xref: /unit/src/nxt_process.c (revision 138)
10Sigor@sysoev.ru 
20Sigor@sysoev.ru /*
30Sigor@sysoev.ru  * Copyright (C) Igor Sysoev
40Sigor@sysoev.ru  * Copyright (C) NGINX, Inc.
50Sigor@sysoev.ru  */
60Sigor@sysoev.ru 
70Sigor@sysoev.ru #include <nxt_main.h>
820Sigor@sysoev.ru #include <nxt_master_process.h>
90Sigor@sysoev.ru 
100Sigor@sysoev.ru 
1120Sigor@sysoev.ru static void nxt_process_start(nxt_task_t *task, nxt_process_init_t *process);
1220Sigor@sysoev.ru static nxt_int_t nxt_user_groups_get(nxt_task_t *task, nxt_user_cred_t *uc);
130Sigor@sysoev.ru 
140Sigor@sysoev.ru 
150Sigor@sysoev.ru /* A cached process pid. */
160Sigor@sysoev.ru nxt_pid_t  nxt_pid;
170Sigor@sysoev.ru 
180Sigor@sysoev.ru /* An original parent process pid. */
190Sigor@sysoev.ru nxt_pid_t  nxt_ppid;
200Sigor@sysoev.ru 
210Sigor@sysoev.ru 
220Sigor@sysoev.ru nxt_pid_t
2378Smax.romanov@nginx.com nxt_process_create(nxt_task_t *task, nxt_process_t *process)
240Sigor@sysoev.ru {
2578Smax.romanov@nginx.com     nxt_pid_t      pid;
2678Smax.romanov@nginx.com     nxt_runtime_t  *rt;
2778Smax.romanov@nginx.com 
2878Smax.romanov@nginx.com     rt = task->thread->runtime;
290Sigor@sysoev.ru 
300Sigor@sysoev.ru     pid = fork();
310Sigor@sysoev.ru 
320Sigor@sysoev.ru     switch (pid) {
330Sigor@sysoev.ru 
340Sigor@sysoev.ru     case -1:
3520Sigor@sysoev.ru         nxt_log(task, NXT_LOG_CRIT, "fork() failed while creating \"%s\" %E",
3678Smax.romanov@nginx.com                 process->init->name, nxt_errno);
370Sigor@sysoev.ru         break;
380Sigor@sysoev.ru 
390Sigor@sysoev.ru     case 0:
400Sigor@sysoev.ru         /* A child. */
410Sigor@sysoev.ru         nxt_pid = getpid();
420Sigor@sysoev.ru 
430Sigor@sysoev.ru         /* Clean inherited cached thread tid. */
4420Sigor@sysoev.ru         task->thread->tid = 0;
450Sigor@sysoev.ru 
4678Smax.romanov@nginx.com         process->pid = nxt_pid;
4778Smax.romanov@nginx.com         process->init->port->pid = nxt_pid;
4878Smax.romanov@nginx.com 
49125Smax.romanov@nginx.com         rt->types = 0;
50125Smax.romanov@nginx.com 
5178Smax.romanov@nginx.com         nxt_runtime_process_add(rt, process);
5278Smax.romanov@nginx.com 
5378Smax.romanov@nginx.com         nxt_process_start(task, process->init);
540Sigor@sysoev.ru         break;
550Sigor@sysoev.ru 
560Sigor@sysoev.ru     default:
570Sigor@sysoev.ru         /* A parent. */
5878Smax.romanov@nginx.com         nxt_debug(task, "fork(\"%s\"): %PI", process->init->name, pid);
5978Smax.romanov@nginx.com 
6078Smax.romanov@nginx.com         process->pid = pid;
6178Smax.romanov@nginx.com         process->init->port->pid = pid;
6278Smax.romanov@nginx.com 
6378Smax.romanov@nginx.com         nxt_runtime_process_add(rt, process);
6478Smax.romanov@nginx.com 
650Sigor@sysoev.ru         break;
660Sigor@sysoev.ru     }
670Sigor@sysoev.ru 
680Sigor@sysoev.ru     return pid;
690Sigor@sysoev.ru }
700Sigor@sysoev.ru 
710Sigor@sysoev.ru 
7220Sigor@sysoev.ru static void
7320Sigor@sysoev.ru nxt_process_start(nxt_task_t *task, nxt_process_init_t *process)
7420Sigor@sysoev.ru {
7520Sigor@sysoev.ru     nxt_int_t                    ret;
7620Sigor@sysoev.ru     nxt_thread_t                 *thread;
7720Sigor@sysoev.ru     nxt_runtime_t                *rt;
7820Sigor@sysoev.ru     nxt_event_engine_t           *engine;
7920Sigor@sysoev.ru     const nxt_event_interface_t  *interface;
8020Sigor@sysoev.ru 
8120Sigor@sysoev.ru     nxt_log(task, NXT_LOG_INFO, "%s started", process->name);
8220Sigor@sysoev.ru 
8320Sigor@sysoev.ru     nxt_process_title(task, "nginext: %s", process->name);
8420Sigor@sysoev.ru 
85*138Sigor@sysoev.ru     thread = task->thread;
86*138Sigor@sysoev.ru 
87*138Sigor@sysoev.ru     nxt_random_init(&thread->random);
8820Sigor@sysoev.ru 
8920Sigor@sysoev.ru     if (process->user_cred != NULL && getuid() == 0) {
9020Sigor@sysoev.ru         /* Super-user. */
9120Sigor@sysoev.ru 
9220Sigor@sysoev.ru         ret = nxt_user_cred_set(task, process->user_cred);
9320Sigor@sysoev.ru         if (ret != NXT_OK) {
9420Sigor@sysoev.ru             goto fail;
9520Sigor@sysoev.ru         }
9620Sigor@sysoev.ru     }
9720Sigor@sysoev.ru 
9820Sigor@sysoev.ru     rt = thread->runtime;
9920Sigor@sysoev.ru 
100125Smax.romanov@nginx.com     rt->types |= (1U << process->type);
10120Sigor@sysoev.ru 
10220Sigor@sysoev.ru     engine = thread->engine;
10320Sigor@sysoev.ru 
10420Sigor@sysoev.ru     /* Update inherited master process event engine and signals processing. */
10520Sigor@sysoev.ru     engine->signals->sigev = process->signals;
10620Sigor@sysoev.ru 
10720Sigor@sysoev.ru     interface = nxt_service_get(rt->services, "engine", rt->engine);
10820Sigor@sysoev.ru     if (interface == NULL) {
10920Sigor@sysoev.ru         goto fail;
11020Sigor@sysoev.ru     }
11120Sigor@sysoev.ru 
11220Sigor@sysoev.ru     if (nxt_event_engine_change(engine, interface, rt->batch) != NXT_OK) {
11320Sigor@sysoev.ru         goto fail;
11420Sigor@sysoev.ru     }
11520Sigor@sysoev.ru 
11620Sigor@sysoev.ru     nxt_port_read_close(process->master_port);
11720Sigor@sysoev.ru     nxt_port_write_enable(task, process->master_port);
11820Sigor@sysoev.ru 
11920Sigor@sysoev.ru     /* A worker process port. */
12077Smax.romanov@nginx.com     nxt_port_write_close(process->port);
12177Smax.romanov@nginx.com     nxt_port_create(task, process->port, process->port_handlers);
12220Sigor@sysoev.ru 
12320Sigor@sysoev.ru     ret = nxt_runtime_thread_pool_create(thread, rt, rt->auxiliary_threads,
12420Sigor@sysoev.ru                                          60000 * 1000000LL);
12520Sigor@sysoev.ru     if (ret != NXT_OK) {
12620Sigor@sysoev.ru         goto fail;
12720Sigor@sysoev.ru     }
12820Sigor@sysoev.ru 
12920Sigor@sysoev.ru     ret = process->start(task, rt);
13020Sigor@sysoev.ru 
13120Sigor@sysoev.ru     if (ret == NXT_OK) {
13220Sigor@sysoev.ru         return;
13320Sigor@sysoev.ru     }
13420Sigor@sysoev.ru 
13520Sigor@sysoev.ru fail:
13620Sigor@sysoev.ru 
13720Sigor@sysoev.ru     exit(1);
13820Sigor@sysoev.ru }
13920Sigor@sysoev.ru 
14020Sigor@sysoev.ru 
1410Sigor@sysoev.ru #if (NXT_HAVE_POSIX_SPAWN)
1420Sigor@sysoev.ru 
1430Sigor@sysoev.ru /*
1440Sigor@sysoev.ru  * Linux glibc 2.2 posix_spawn() is implemented via fork()/execve().
1450Sigor@sysoev.ru  * Linux glibc 2.4 posix_spawn() without file actions and spawn
1460Sigor@sysoev.ru  * attributes uses vfork()/execve().
1470Sigor@sysoev.ru  *
1480Sigor@sysoev.ru  * On FreeBSD 8.0 posix_spawn() is implemented via vfork()/execve().
1490Sigor@sysoev.ru  *
1500Sigor@sysoev.ru  * Solaris 10:
1510Sigor@sysoev.ru  *   In the Solaris 10 OS, posix_spawn() is currently implemented using
1520Sigor@sysoev.ru  *   private-to-libc vfork(), execve(), and exit() functions.  They are
1530Sigor@sysoev.ru  *   identical to regular vfork(), execve(), and exit() in functionality,
1540Sigor@sysoev.ru  *   but they are not exported from libc and therefore don't cause the
1550Sigor@sysoev.ru  *   deadlock-in-the-dynamic-linker problem that any multithreaded code
1560Sigor@sysoev.ru  *   outside of libc that calls vfork() can cause.
1570Sigor@sysoev.ru  *
1580Sigor@sysoev.ru  * On MacOSX 10.5 (Leoprad) and NetBSD 6.0 posix_spawn() is implemented
1590Sigor@sysoev.ru  * as syscall.
1600Sigor@sysoev.ru  */
1610Sigor@sysoev.ru 
1620Sigor@sysoev.ru nxt_pid_t
16320Sigor@sysoev.ru nxt_process_execute(nxt_task_t *task, char *name, char **argv, char **envp)
1640Sigor@sysoev.ru {
1650Sigor@sysoev.ru     nxt_pid_t  pid;
1660Sigor@sysoev.ru 
16720Sigor@sysoev.ru     nxt_debug(task, "posix_spawn(\"%s\")", name);
1680Sigor@sysoev.ru 
1690Sigor@sysoev.ru     if (posix_spawn(&pid, name, NULL, NULL, argv, envp) != 0) {
17020Sigor@sysoev.ru         nxt_log(task, NXT_LOG_CRIT, "posix_spawn(\"%s\") failed %E",
17120Sigor@sysoev.ru                 name, nxt_errno);
1720Sigor@sysoev.ru         return -1;
1730Sigor@sysoev.ru     }
1740Sigor@sysoev.ru 
1750Sigor@sysoev.ru     return pid;
1760Sigor@sysoev.ru }
1770Sigor@sysoev.ru 
1780Sigor@sysoev.ru #else
1790Sigor@sysoev.ru 
1800Sigor@sysoev.ru nxt_pid_t
18120Sigor@sysoev.ru nxt_process_execute(nxt_task_t *task, char *name, char **argv, char **envp)
1820Sigor@sysoev.ru {
1830Sigor@sysoev.ru     nxt_pid_t  pid;
1840Sigor@sysoev.ru 
1850Sigor@sysoev.ru     /*
1860Sigor@sysoev.ru      * vfork() is better than fork() because:
1870Sigor@sysoev.ru      *   it is faster several times;
1880Sigor@sysoev.ru      *   its execution time does not depend on private memory mapping size;
1890Sigor@sysoev.ru      *   it has lesser chances to fail due to the ENOMEM error.
1900Sigor@sysoev.ru      */
1910Sigor@sysoev.ru 
1920Sigor@sysoev.ru     pid = vfork();
1930Sigor@sysoev.ru 
1940Sigor@sysoev.ru     switch (pid) {
1950Sigor@sysoev.ru 
1960Sigor@sysoev.ru     case -1:
19720Sigor@sysoev.ru         nxt_log(task, NXT_LOG_CRIT, "vfork() failed while executing \"%s\" %E",
19820Sigor@sysoev.ru                 name, nxt_errno);
1990Sigor@sysoev.ru         break;
2000Sigor@sysoev.ru 
2010Sigor@sysoev.ru     case 0:
2020Sigor@sysoev.ru         /* A child. */
20320Sigor@sysoev.ru         nxt_debug(task, "execve(\"%s\")", name);
2040Sigor@sysoev.ru 
2050Sigor@sysoev.ru         (void) execve(name, argv, envp);
2060Sigor@sysoev.ru 
20720Sigor@sysoev.ru         nxt_log(task, NXT_LOG_CRIT, "execve(\"%s\") failed %E",
20820Sigor@sysoev.ru                 name, nxt_errno);
2090Sigor@sysoev.ru 
2100Sigor@sysoev.ru         exit(1);
21120Sigor@sysoev.ru         nxt_unreachable();
2120Sigor@sysoev.ru         break;
2130Sigor@sysoev.ru 
2140Sigor@sysoev.ru     default:
2150Sigor@sysoev.ru         /* A parent. */
21620Sigor@sysoev.ru         nxt_debug(task, "vfork(): %PI", pid);
2170Sigor@sysoev.ru         break;
2180Sigor@sysoev.ru     }
2190Sigor@sysoev.ru 
2200Sigor@sysoev.ru     return pid;
2210Sigor@sysoev.ru }
2220Sigor@sysoev.ru 
2230Sigor@sysoev.ru #endif
2240Sigor@sysoev.ru 
2250Sigor@sysoev.ru 
2260Sigor@sysoev.ru nxt_int_t
22720Sigor@sysoev.ru nxt_process_daemon(nxt_task_t *task)
2280Sigor@sysoev.ru {
2290Sigor@sysoev.ru     nxt_fd_t      fd;
2300Sigor@sysoev.ru     nxt_pid_t     pid;
2310Sigor@sysoev.ru     const char    *msg;
2320Sigor@sysoev.ru 
2330Sigor@sysoev.ru     /*
2340Sigor@sysoev.ru      * fork() followed by a parent process's exit() detaches a child process
2350Sigor@sysoev.ru      * from an init script or terminal shell process which has started the
2360Sigor@sysoev.ru      * parent process and allows the child process to run in background.
2370Sigor@sysoev.ru      */
2380Sigor@sysoev.ru 
2390Sigor@sysoev.ru     pid = fork();
2400Sigor@sysoev.ru 
2410Sigor@sysoev.ru     switch (pid) {
2420Sigor@sysoev.ru 
2430Sigor@sysoev.ru     case -1:
2440Sigor@sysoev.ru         msg = "fork() failed %E";
2450Sigor@sysoev.ru         goto fail;
2460Sigor@sysoev.ru 
2470Sigor@sysoev.ru     case 0:
2480Sigor@sysoev.ru         /* A child. */
2490Sigor@sysoev.ru         break;
2500Sigor@sysoev.ru 
2510Sigor@sysoev.ru     default:
2520Sigor@sysoev.ru         /* A parent. */
25320Sigor@sysoev.ru         nxt_debug(task, "fork(): %PI", pid);
2540Sigor@sysoev.ru         exit(0);
2550Sigor@sysoev.ru         nxt_unreachable();
2560Sigor@sysoev.ru     }
2570Sigor@sysoev.ru 
2580Sigor@sysoev.ru     nxt_pid = getpid();
2590Sigor@sysoev.ru 
2600Sigor@sysoev.ru     /* Clean inherited cached thread tid. */
26120Sigor@sysoev.ru     task->thread->tid = 0;
2620Sigor@sysoev.ru 
26320Sigor@sysoev.ru     nxt_debug(task, "daemon");
2640Sigor@sysoev.ru 
2650Sigor@sysoev.ru     /* Detach from controlling terminal. */
2660Sigor@sysoev.ru 
2670Sigor@sysoev.ru     if (setsid() == -1) {
26820Sigor@sysoev.ru         nxt_log(task, NXT_LOG_CRIT, "setsid() failed %E", nxt_errno);
2690Sigor@sysoev.ru         return NXT_ERROR;
2700Sigor@sysoev.ru     }
2710Sigor@sysoev.ru 
2720Sigor@sysoev.ru     /*
2730Sigor@sysoev.ru      * Reset file mode creation mask: any access
2740Sigor@sysoev.ru      * rights can be set on file creation.
2750Sigor@sysoev.ru      */
2760Sigor@sysoev.ru     umask(0);
2770Sigor@sysoev.ru 
2780Sigor@sysoev.ru     /* Redirect STDIN and STDOUT to the "/dev/null". */
2790Sigor@sysoev.ru 
2800Sigor@sysoev.ru     fd = open("/dev/null", O_RDWR);
2810Sigor@sysoev.ru     if (fd == -1) {
2820Sigor@sysoev.ru         msg = "open(\"/dev/null\") failed %E";
2830Sigor@sysoev.ru         goto fail;
2840Sigor@sysoev.ru     }
2850Sigor@sysoev.ru 
2860Sigor@sysoev.ru     if (dup2(fd, STDIN_FILENO) == -1) {
2870Sigor@sysoev.ru         msg = "dup2(\"/dev/null\", STDIN) failed %E";
2880Sigor@sysoev.ru         goto fail;
2890Sigor@sysoev.ru     }
2900Sigor@sysoev.ru 
2910Sigor@sysoev.ru     if (dup2(fd, STDOUT_FILENO) == -1) {
2920Sigor@sysoev.ru         msg = "dup2(\"/dev/null\", STDOUT) failed %E";
2930Sigor@sysoev.ru         goto fail;
2940Sigor@sysoev.ru     }
2950Sigor@sysoev.ru 
2960Sigor@sysoev.ru     if (fd > STDERR_FILENO) {
2970Sigor@sysoev.ru         nxt_fd_close(fd);
2980Sigor@sysoev.ru     }
2990Sigor@sysoev.ru 
3000Sigor@sysoev.ru     return NXT_OK;
3010Sigor@sysoev.ru 
3020Sigor@sysoev.ru fail:
3030Sigor@sysoev.ru 
30420Sigor@sysoev.ru     nxt_log(task, NXT_LOG_CRIT, msg, nxt_errno);
3050Sigor@sysoev.ru 
3060Sigor@sysoev.ru     return NXT_ERROR;
3070Sigor@sysoev.ru }
3080Sigor@sysoev.ru 
3090Sigor@sysoev.ru 
3100Sigor@sysoev.ru void
3110Sigor@sysoev.ru nxt_nanosleep(nxt_nsec_t ns)
3120Sigor@sysoev.ru {
3130Sigor@sysoev.ru     struct timespec  ts;
3140Sigor@sysoev.ru 
3150Sigor@sysoev.ru     ts.tv_sec = ns / 1000000000;
3160Sigor@sysoev.ru     ts.tv_nsec = ns % 1000000000;
3170Sigor@sysoev.ru 
3180Sigor@sysoev.ru     (void) nanosleep(&ts, NULL);
3190Sigor@sysoev.ru }
3200Sigor@sysoev.ru 
3210Sigor@sysoev.ru 
3220Sigor@sysoev.ru nxt_int_t
32320Sigor@sysoev.ru nxt_user_cred_get(nxt_task_t *task, nxt_user_cred_t *uc, const char *group)
3240Sigor@sysoev.ru {
3250Sigor@sysoev.ru     struct group   *grp;
3260Sigor@sysoev.ru     struct passwd  *pwd;
3270Sigor@sysoev.ru 
3280Sigor@sysoev.ru     pwd = getpwnam(uc->user);
3290Sigor@sysoev.ru 
3300Sigor@sysoev.ru     if (nxt_slow_path(pwd == NULL)) {
33120Sigor@sysoev.ru         nxt_log(task, NXT_LOG_CRIT, "getpwnam(%s) failed %E",
33220Sigor@sysoev.ru                 uc->user, nxt_errno);
3330Sigor@sysoev.ru         return NXT_ERROR;
3340Sigor@sysoev.ru     }
3350Sigor@sysoev.ru 
3360Sigor@sysoev.ru     uc->uid = pwd->pw_uid;
3370Sigor@sysoev.ru     uc->base_gid = pwd->pw_gid;
3380Sigor@sysoev.ru 
3390Sigor@sysoev.ru     if (group != NULL) {
3400Sigor@sysoev.ru         grp = getgrnam(group);
3410Sigor@sysoev.ru 
3420Sigor@sysoev.ru         if (nxt_slow_path(grp == NULL)) {
34320Sigor@sysoev.ru             nxt_log(task, NXT_LOG_CRIT, "getgrnam(%s) failed %E",
34420Sigor@sysoev.ru                     group, nxt_errno);
3450Sigor@sysoev.ru             return NXT_ERROR;
3460Sigor@sysoev.ru         }
3470Sigor@sysoev.ru 
3480Sigor@sysoev.ru         uc->base_gid = grp->gr_gid;
3490Sigor@sysoev.ru     }
3500Sigor@sysoev.ru 
3510Sigor@sysoev.ru     if (getuid() == 0) {
35220Sigor@sysoev.ru         return nxt_user_groups_get(task, uc);
3530Sigor@sysoev.ru     }
3540Sigor@sysoev.ru 
3550Sigor@sysoev.ru     return NXT_OK;
3560Sigor@sysoev.ru }
3570Sigor@sysoev.ru 
3580Sigor@sysoev.ru 
3590Sigor@sysoev.ru /*
3600Sigor@sysoev.ru  * nxt_user_groups_get() stores an array of groups IDs which should be
3610Sigor@sysoev.ru  * set by the initgroups() function for a given user.  The initgroups()
3620Sigor@sysoev.ru  * may block a just forked worker process for some time if LDAP or NDIS+
3630Sigor@sysoev.ru  * is used, so nxt_user_groups_get() allows to get worker user groups in
3640Sigor@sysoev.ru  * master process.  In a nutshell the initgroups() calls getgrouplist()
3650Sigor@sysoev.ru  * followed by setgroups().  However Solaris lacks the getgrouplist().
3660Sigor@sysoev.ru  * Besides getgrouplist() does not allow to query the exact number of
3670Sigor@sysoev.ru  * groups while NGROUPS_MAX can be quite large (e.g. 65536 on Linux).
3680Sigor@sysoev.ru  * So nxt_user_groups_get() emulates getgrouplist(): at first the function
3690Sigor@sysoev.ru  * saves the super-user groups IDs, then calls initgroups() and saves the
3700Sigor@sysoev.ru  * specified user groups IDs, and then restores the super-user groups IDs.
3710Sigor@sysoev.ru  * This works at least on Linux, FreeBSD, and Solaris, but does not work
3720Sigor@sysoev.ru  * on MacOSX, getgroups(2):
3730Sigor@sysoev.ru  *
3740Sigor@sysoev.ru  *   To provide compatibility with applications that use getgroups() in
3750Sigor@sysoev.ru  *   environments where users may be in more than {NGROUPS_MAX} groups,
3760Sigor@sysoev.ru  *   a variant of getgroups(), obtained when compiling with either the
3770Sigor@sysoev.ru  *   macros _DARWIN_UNLIMITED_GETGROUPS or _DARWIN_C_SOURCE defined, can
3780Sigor@sysoev.ru  *   be used that is not limited to {NGROUPS_MAX} groups.  However, this
3790Sigor@sysoev.ru  *   variant only returns the user's default group access list and not
3800Sigor@sysoev.ru  *   the group list modified by a call to setgroups(2).
3810Sigor@sysoev.ru  *
3820Sigor@sysoev.ru  * For such cases initgroups() is used in worker process as fallback.
3830Sigor@sysoev.ru  */
3840Sigor@sysoev.ru 
3850Sigor@sysoev.ru static nxt_int_t
38620Sigor@sysoev.ru nxt_user_groups_get(nxt_task_t *task, nxt_user_cred_t *uc)
3870Sigor@sysoev.ru {
3880Sigor@sysoev.ru     int        nsaved, ngroups;
3890Sigor@sysoev.ru     nxt_int_t  ret;
3900Sigor@sysoev.ru     nxt_gid_t  *saved;
3910Sigor@sysoev.ru 
3920Sigor@sysoev.ru     nsaved = getgroups(0, NULL);
3930Sigor@sysoev.ru 
3940Sigor@sysoev.ru     if (nsaved == -1) {
39520Sigor@sysoev.ru         nxt_log(task, NXT_LOG_CRIT, "getgroups(0, NULL) failed %E", nxt_errno);
3960Sigor@sysoev.ru         return NXT_ERROR;
3970Sigor@sysoev.ru     }
3980Sigor@sysoev.ru 
39920Sigor@sysoev.ru     nxt_debug(task, "getgroups(0, NULL): %d", nsaved);
4000Sigor@sysoev.ru 
4010Sigor@sysoev.ru     if (nsaved > NGROUPS_MAX) {
4020Sigor@sysoev.ru         /* MacOSX case. */
4030Sigor@sysoev.ru         return NXT_OK;
4040Sigor@sysoev.ru     }
4050Sigor@sysoev.ru 
4060Sigor@sysoev.ru     saved = nxt_malloc(nsaved * sizeof(nxt_gid_t));
4070Sigor@sysoev.ru 
4080Sigor@sysoev.ru     if (saved == NULL) {
4090Sigor@sysoev.ru         return NXT_ERROR;
4100Sigor@sysoev.ru     }
4110Sigor@sysoev.ru 
4120Sigor@sysoev.ru     ret = NXT_ERROR;
4130Sigor@sysoev.ru 
4140Sigor@sysoev.ru     nsaved = getgroups(nsaved, saved);
4150Sigor@sysoev.ru 
4160Sigor@sysoev.ru     if (nsaved == -1) {
41720Sigor@sysoev.ru         nxt_log(task, NXT_LOG_CRIT, "getgroups(%d) failed %E",
41820Sigor@sysoev.ru                 nsaved, nxt_errno);
4190Sigor@sysoev.ru         goto fail;
4200Sigor@sysoev.ru     }
4210Sigor@sysoev.ru 
42220Sigor@sysoev.ru     nxt_debug(task, "getgroups(): %d", nsaved);
4230Sigor@sysoev.ru 
4240Sigor@sysoev.ru     if (initgroups(uc->user, uc->base_gid) != 0) {
42520Sigor@sysoev.ru         nxt_log(task, NXT_LOG_CRIT, "initgroups(%s, %d) failed",
4260Sigor@sysoev.ru                              uc->user, uc->base_gid);
4270Sigor@sysoev.ru         goto restore;
4280Sigor@sysoev.ru     }
4290Sigor@sysoev.ru 
4300Sigor@sysoev.ru     ngroups = getgroups(0, NULL);
4310Sigor@sysoev.ru 
4320Sigor@sysoev.ru     if (ngroups == -1) {
43320Sigor@sysoev.ru         nxt_log(task, NXT_LOG_CRIT, "getgroups(0, NULL) failed %E", nxt_errno);
4340Sigor@sysoev.ru         goto restore;
4350Sigor@sysoev.ru     }
4360Sigor@sysoev.ru 
43720Sigor@sysoev.ru     nxt_debug(task, "getgroups(0, NULL): %d", ngroups);
4380Sigor@sysoev.ru 
4390Sigor@sysoev.ru     uc->gids = nxt_malloc(ngroups * sizeof(nxt_gid_t));
4400Sigor@sysoev.ru 
4410Sigor@sysoev.ru     if (uc->gids == NULL) {
4420Sigor@sysoev.ru         goto restore;
4430Sigor@sysoev.ru     }
4440Sigor@sysoev.ru 
4450Sigor@sysoev.ru     ngroups = getgroups(ngroups, uc->gids);
4460Sigor@sysoev.ru 
4470Sigor@sysoev.ru     if (ngroups == -1) {
44820Sigor@sysoev.ru         nxt_log(task, NXT_LOG_CRIT, "getgroups(%d) failed %E",
44920Sigor@sysoev.ru                 ngroups, nxt_errno);
4500Sigor@sysoev.ru         goto restore;
4510Sigor@sysoev.ru     }
4520Sigor@sysoev.ru 
4530Sigor@sysoev.ru     uc->ngroups = ngroups;
4540Sigor@sysoev.ru 
4550Sigor@sysoev.ru #if (NXT_DEBUG)
4560Sigor@sysoev.ru     {
4570Sigor@sysoev.ru         u_char      *p, *end;
4580Sigor@sysoev.ru         nxt_uint_t  i;
4590Sigor@sysoev.ru         u_char      msg[NXT_MAX_ERROR_STR];
4600Sigor@sysoev.ru 
4610Sigor@sysoev.ru         p = msg;
4620Sigor@sysoev.ru         end = msg + NXT_MAX_ERROR_STR;
4630Sigor@sysoev.ru 
4640Sigor@sysoev.ru         for (i = 0; i < uc->ngroups; i++) {
4650Sigor@sysoev.ru             p = nxt_sprintf(p, end, "%uL:", (uint64_t) uc->gids[i]);
4660Sigor@sysoev.ru         }
4670Sigor@sysoev.ru 
46820Sigor@sysoev.ru         nxt_debug(task, "user \"%s\" cred: uid:%uL base gid:%uL, gids:%*s",
46920Sigor@sysoev.ru                   uc->user, (uint64_t) uc->uid, (uint64_t) uc->base_gid,
47020Sigor@sysoev.ru                   p - msg, msg);
4710Sigor@sysoev.ru     }
4720Sigor@sysoev.ru #endif
4730Sigor@sysoev.ru 
4740Sigor@sysoev.ru     ret = NXT_OK;
4750Sigor@sysoev.ru 
4760Sigor@sysoev.ru restore:
4770Sigor@sysoev.ru 
4780Sigor@sysoev.ru     if (setgroups(nsaved, saved) != 0) {
47920Sigor@sysoev.ru         nxt_log(task, NXT_LOG_CRIT, "setgroups(%d) failed %E",
48020Sigor@sysoev.ru                 nsaved, nxt_errno);
4810Sigor@sysoev.ru         ret = NXT_ERROR;
4820Sigor@sysoev.ru     }
4830Sigor@sysoev.ru 
4840Sigor@sysoev.ru fail:
4850Sigor@sysoev.ru 
4860Sigor@sysoev.ru     nxt_free(saved);
4870Sigor@sysoev.ru 
4880Sigor@sysoev.ru     return ret;
4890Sigor@sysoev.ru }
4900Sigor@sysoev.ru 
4910Sigor@sysoev.ru 
4920Sigor@sysoev.ru nxt_int_t
49320Sigor@sysoev.ru nxt_user_cred_set(nxt_task_t *task, nxt_user_cred_t *uc)
4940Sigor@sysoev.ru {
49520Sigor@sysoev.ru     nxt_debug(task, "user cred set: \"%s\" uid:%uL base gid:%uL",
49620Sigor@sysoev.ru               uc->user, (uint64_t) uc->uid, uc->base_gid);
4970Sigor@sysoev.ru 
4980Sigor@sysoev.ru     if (setgid(uc->base_gid) != 0) {
49920Sigor@sysoev.ru         nxt_log(task, NXT_LOG_CRIT, "setgid(%d) failed %E",
50020Sigor@sysoev.ru                 uc->base_gid, nxt_errno);
5010Sigor@sysoev.ru         return NXT_ERROR;
5020Sigor@sysoev.ru     }
5030Sigor@sysoev.ru 
5040Sigor@sysoev.ru     if (uc->gids != NULL) {
5050Sigor@sysoev.ru         if (setgroups(uc->ngroups, uc->gids) != 0) {
50620Sigor@sysoev.ru             nxt_log(task, NXT_LOG_CRIT, "setgroups(%i) failed %E",
50720Sigor@sysoev.ru                     uc->ngroups, nxt_errno);
5080Sigor@sysoev.ru             return NXT_ERROR;
5090Sigor@sysoev.ru         }
5100Sigor@sysoev.ru 
5110Sigor@sysoev.ru     } else {
5120Sigor@sysoev.ru         /* MacOSX fallback. */
5130Sigor@sysoev.ru         if (initgroups(uc->user, uc->base_gid) != 0) {
51420Sigor@sysoev.ru             nxt_log(task, NXT_LOG_CRIT, "initgroups(%s, %d) failed",
51520Sigor@sysoev.ru                     uc->user, uc->base_gid);
5160Sigor@sysoev.ru             return NXT_ERROR;
5170Sigor@sysoev.ru         }
5180Sigor@sysoev.ru     }
5190Sigor@sysoev.ru 
5200Sigor@sysoev.ru     if (setuid(uc->uid) != 0) {
52120Sigor@sysoev.ru         nxt_log(task, NXT_LOG_CRIT, "setuid(%d) failed %E", uc->uid, nxt_errno);
5220Sigor@sysoev.ru         return NXT_ERROR;
5230Sigor@sysoev.ru     }
5240Sigor@sysoev.ru 
5250Sigor@sysoev.ru     return NXT_OK;
5260Sigor@sysoev.ru }
52742Smax.romanov@nginx.com 
52842Smax.romanov@nginx.com 
52942Smax.romanov@nginx.com nxt_port_t *
53042Smax.romanov@nginx.com nxt_process_port_new(nxt_process_t *process)
53142Smax.romanov@nginx.com {
53242Smax.romanov@nginx.com     nxt_port_t  *port;
53342Smax.romanov@nginx.com 
53465Sigor@sysoev.ru     port = nxt_mp_zalloc(process->mem_pool, sizeof(nxt_port_t));
53542Smax.romanov@nginx.com     if (nxt_fast_path(port != NULL)) {
53642Smax.romanov@nginx.com         port->id = process->last_port_id++;
53742Smax.romanov@nginx.com         port->pid = process->pid;
53842Smax.romanov@nginx.com         port->process = process;
53942Smax.romanov@nginx.com 
54042Smax.romanov@nginx.com         nxt_process_port_add(process, port);
54142Smax.romanov@nginx.com     }
54242Smax.romanov@nginx.com 
54342Smax.romanov@nginx.com     return port;
54442Smax.romanov@nginx.com }
54542Smax.romanov@nginx.com 
54688Smax.romanov@nginx.com 
54788Smax.romanov@nginx.com void
54888Smax.romanov@nginx.com nxt_process_connected_port_add(nxt_process_t *process, nxt_port_t *port)
54988Smax.romanov@nginx.com {
55088Smax.romanov@nginx.com     /* TODO lock ports */
55188Smax.romanov@nginx.com 
55288Smax.romanov@nginx.com     nxt_port_hash_add(&process->connected_ports, process->mem_pool, port);
55388Smax.romanov@nginx.com }
55488Smax.romanov@nginx.com 
55588Smax.romanov@nginx.com void
55688Smax.romanov@nginx.com nxt_process_connected_port_remove(nxt_process_t *process, nxt_port_t *port)
55788Smax.romanov@nginx.com {
55888Smax.romanov@nginx.com     /* TODO lock ports */
55988Smax.romanov@nginx.com 
56088Smax.romanov@nginx.com     nxt_port_hash_remove(&process->connected_ports, process->mem_pool, port);
56188Smax.romanov@nginx.com }
56288Smax.romanov@nginx.com 
56388Smax.romanov@nginx.com nxt_port_t *
56488Smax.romanov@nginx.com nxt_process_connected_port_find(nxt_process_t *process, nxt_pid_t pid,
56588Smax.romanov@nginx.com     nxt_port_id_t port_id)
56688Smax.romanov@nginx.com {
56788Smax.romanov@nginx.com     /* TODO lock ports */
56888Smax.romanov@nginx.com 
56988Smax.romanov@nginx.com     return nxt_port_hash_find(&process->connected_ports, pid, port_id);
57088Smax.romanov@nginx.com }
57188Smax.romanov@nginx.com 
572