xref: /unit/src/nxt_process.c (revision 1302:d4c6a91f7091)
1 
2 /*
3  * Copyright (C) Igor Sysoev
4  * Copyright (C) NGINX, Inc.
5  */
6 
7 #include <nxt_main.h>
8 #include <nxt_main_process.h>
9 
10 #if (NXT_HAVE_CLONE)
11 #include <nxt_clone.h>
12 #endif
13 
14 #include <signal.h>
15 
16 static void nxt_process_start(nxt_task_t *task, nxt_process_t *process);
17 static nxt_int_t nxt_user_groups_get(nxt_task_t *task, nxt_mp_t *mp,
18     nxt_user_cred_t *uc);
19 static nxt_int_t nxt_process_worker_setup(nxt_task_t *task,
20     nxt_process_t *process, int parentfd);
21 
22 /* A cached process pid. */
23 nxt_pid_t  nxt_pid;
24 
25 /* An original parent process pid. */
26 nxt_pid_t  nxt_ppid;
27 
28 nxt_bool_t  nxt_proc_conn_matrix[NXT_PROCESS_MAX][NXT_PROCESS_MAX] = {
29     { 1, 1, 1, 1, 1 },
30     { 1, 0, 0, 0, 0 },
31     { 1, 0, 0, 1, 0 },
32     { 1, 0, 1, 0, 1 },
33     { 1, 0, 0, 0, 0 },
34 };
35 
36 nxt_bool_t  nxt_proc_remove_notify_matrix[NXT_PROCESS_MAX][NXT_PROCESS_MAX] = {
37     { 0, 0, 0, 0, 0 },
38     { 0, 0, 0, 0, 0 },
39     { 0, 0, 0, 1, 0 },
40     { 0, 0, 1, 0, 1 },
41     { 0, 0, 0, 1, 0 },
42 };
43 
44 
45 static nxt_int_t
46 nxt_process_worker_setup(nxt_task_t *task, nxt_process_t *process, int parentfd)
47 {
48     pid_t               rpid, pid;
49     ssize_t             n;
50     nxt_int_t           parent_status;
51     nxt_process_t       *p;
52     nxt_runtime_t       *rt;
53     nxt_process_init_t  *init;
54     nxt_process_type_t  ptype;
55 
56     pid  = getpid();
57     rpid = 0;
58     rt   = task->thread->runtime;
59     init = process->init;
60 
61     /* Setup the worker process. */
62 
63     n = read(parentfd, &rpid, sizeof(rpid));
64     if (nxt_slow_path(n == -1 || n != sizeof(rpid))) {
65         nxt_alert(task, "failed to read real pid");
66         return NXT_ERROR;
67     }
68 
69     if (nxt_slow_path(rpid == 0)) {
70         nxt_alert(task, "failed to get real pid from parent");
71         return NXT_ERROR;
72     }
73 
74     nxt_pid = rpid;
75 
76     /* Clean inherited cached thread tid. */
77     task->thread->tid = 0;
78 
79     process->pid = nxt_pid;
80 
81     if (nxt_pid != pid) {
82         nxt_debug(task, "app \"%s\" real pid %d", init->name, nxt_pid);
83         nxt_debug(task, "app \"%s\" isolated pid: %d", init->name, pid);
84     }
85 
86     n = read(parentfd, &parent_status, sizeof(parent_status));
87     if (nxt_slow_path(n == -1 || n != sizeof(parent_status))) {
88         nxt_alert(task, "failed to read parent status");
89         return NXT_ERROR;
90     }
91 
92     if (nxt_slow_path(parent_status != NXT_OK)) {
93         return parent_status;
94     }
95 
96     ptype = init->type;
97 
98     nxt_port_reset_next_id();
99 
100     nxt_event_engine_thread_adopt(task->thread->engine);
101 
102     /* Remove not ready processes. */
103     nxt_runtime_process_each(rt, p) {
104 
105         if (nxt_proc_conn_matrix[ptype][nxt_process_type(p)] == 0) {
106             nxt_debug(task, "remove not required process %PI", p->pid);
107 
108             nxt_process_close_ports(task, p);
109 
110             continue;
111         }
112 
113         if (!p->ready) {
114             nxt_debug(task, "remove not ready process %PI", p->pid);
115 
116             nxt_process_close_ports(task, p);
117 
118             continue;
119         }
120 
121         nxt_port_mmaps_destroy(&p->incoming, 0);
122         nxt_port_mmaps_destroy(&p->outgoing, 0);
123 
124     } nxt_runtime_process_loop;
125 
126     nxt_runtime_process_add(task, process);
127 
128     nxt_process_start(task, process);
129 
130     process->ready = 1;
131 
132     return NXT_OK;
133 }
134 
135 
136 nxt_pid_t
137 nxt_process_create(nxt_task_t *task, nxt_process_t *process)
138 {
139     int                 pipefd[2];
140     nxt_int_t           ret;
141     nxt_pid_t           pid;
142     nxt_process_init_t  *init;
143 
144     if (nxt_slow_path(pipe(pipefd) == -1)) {
145         nxt_alert(task, "failed to create process pipe for passing rpid");
146         return -1;
147     }
148 
149     init = process->init;
150 
151 #if (NXT_HAVE_CLONE)
152     pid = nxt_clone(SIGCHLD | init->isolation.clone.flags);
153     if (nxt_slow_path(pid < 0)) {
154         nxt_alert(task, "clone() failed while creating \"%s\" %E",
155                   init->name, nxt_errno);
156         goto cleanup;
157     }
158 #else
159     pid = fork();
160     if (nxt_slow_path(pid < 0)) {
161         nxt_alert(task, "fork() failed while creating \"%s\" %E",
162                   init->name, nxt_errno);
163         goto cleanup;
164     }
165 #endif
166 
167     if (pid == 0) {
168         /* Child. */
169 
170         if (nxt_slow_path(close(pipefd[1]) == -1)) {
171             nxt_alert(task, "failed to close writer pipe fd");
172         }
173 
174         ret = nxt_process_worker_setup(task, process, pipefd[0]);
175         if (nxt_slow_path(ret != NXT_OK)) {
176             exit(1);
177         }
178 
179         if (nxt_slow_path(close(pipefd[0]) == -1)) {
180             nxt_alert(task, "failed to close writer pipe fd");
181         }
182 
183         /*
184          * Explicitly return 0 to notice the caller function this is the child.
185          * The caller must return to the event engine work queue loop.
186          */
187         return 0;
188     }
189 
190     /* Parent. */
191 
192     /*
193      * At this point, the child process is blocked reading the
194      * pipe fd to get its real pid (rpid).
195      *
196      * If anything goes wrong now, we need to terminate the child
197      * process by sending a NXT_ERROR in the pipe.
198      */
199 
200 #if (NXT_HAVE_CLONE)
201     nxt_debug(task, "clone(\"%s\"): %PI", init->name, pid);
202 #else
203     nxt_debug(task, "fork(\"%s\"): %PI", init->name, pid);
204 #endif
205 
206     if (nxt_slow_path(write(pipefd[1], &pid, sizeof(pid)) == -1)) {
207         nxt_alert(task, "failed to write real pid");
208         goto fail;
209     }
210 
211 #if (NXT_HAVE_CLONE && NXT_HAVE_CLONE_NEWUSER)
212     if ((init->isolation.clone.flags & CLONE_NEWUSER) == CLONE_NEWUSER) {
213         ret = nxt_clone_proc_map(task, pid, &init->isolation.clone);
214         if (nxt_slow_path(ret != NXT_OK)) {
215             goto fail;
216         }
217     }
218 #endif
219 
220     ret = NXT_OK;
221 
222     if (nxt_slow_path(write(pipefd[1], &ret, sizeof(ret)) == -1)) {
223         nxt_alert(task, "failed to write status");
224         goto fail;
225     }
226 
227     process->pid = pid;
228 
229     nxt_runtime_process_add(task, process);
230 
231     goto cleanup;
232 
233 fail:
234 
235     ret = NXT_ERROR;
236 
237     if (nxt_slow_path(write(pipefd[1], &ret, sizeof(ret)) == -1)) {
238         nxt_alert(task, "failed to write status");
239     }
240 
241     waitpid(pid, NULL, 0);
242 
243     pid = -1;
244 
245 cleanup:
246 
247     if (nxt_slow_path(close(pipefd[0]) != 0)) {
248         nxt_alert(task, "failed to close pipe: %E", nxt_errno);
249     }
250 
251     if (nxt_slow_path(close(pipefd[1]) != 0)) {
252         nxt_alert(task, "failed to close pipe: %E", nxt_errno);
253     }
254 
255     return pid;
256 }
257 
258 
259 static void
260 nxt_process_start(nxt_task_t *task, nxt_process_t *process)
261 {
262     nxt_int_t                    ret;
263     nxt_port_t                   *port, *main_port;
264     nxt_thread_t                 *thread;
265     nxt_runtime_t                *rt;
266     nxt_process_init_t           *init;
267     nxt_event_engine_t           *engine;
268     const nxt_event_interface_t  *interface;
269 
270     init = process->init;
271 
272     nxt_log(task, NXT_LOG_INFO, "%s started", init->name);
273 
274     nxt_process_title(task, "unit: %s", init->name);
275 
276     thread = task->thread;
277     rt     = thread->runtime;
278 
279     nxt_random_init(&thread->random);
280 
281     if (rt->capabilities.setid && init->user_cred != NULL) {
282         ret = nxt_user_cred_set(task, init->user_cred);
283         if (ret != NXT_OK) {
284             goto fail;
285         }
286     }
287 
288     rt->type = init->type;
289 
290     engine = thread->engine;
291 
292     /* Update inherited main process event engine and signals processing. */
293     engine->signals->sigev = init->signals;
294 
295     interface = nxt_service_get(rt->services, "engine", rt->engine);
296     if (nxt_slow_path(interface == NULL)) {
297         goto fail;
298     }
299 
300     if (nxt_event_engine_change(engine, interface, rt->batch) != NXT_OK) {
301         goto fail;
302     }
303 
304     ret = nxt_runtime_thread_pool_create(thread, rt, rt->auxiliary_threads,
305                                          60000 * 1000000LL);
306     if (nxt_slow_path(ret != NXT_OK)) {
307         goto fail;
308     }
309 
310     main_port = rt->port_by_type[NXT_PROCESS_MAIN];
311 
312     nxt_port_read_close(main_port);
313     nxt_port_write_enable(task, main_port);
314 
315     port = nxt_process_port_first(process);
316 
317     nxt_port_write_close(port);
318 
319     ret = init->start(task, init->data);
320 
321     if (nxt_slow_path(ret != NXT_OK)) {
322         goto fail;
323     }
324 
325     nxt_port_enable(task, port, init->port_handlers);
326 
327     ret = nxt_port_socket_write(task, main_port, NXT_PORT_MSG_PROCESS_READY,
328                                 -1, init->stream, 0, NULL);
329 
330     if (nxt_slow_path(ret != NXT_OK)) {
331         nxt_log(task, NXT_LOG_ERR, "failed to send READY message to main");
332 
333         goto fail;
334     }
335 
336     return;
337 
338 fail:
339 
340     exit(1);
341 }
342 
343 
344 #if (NXT_HAVE_POSIX_SPAWN)
345 
346 /*
347  * Linux glibc 2.2 posix_spawn() is implemented via fork()/execve().
348  * Linux glibc 2.4 posix_spawn() without file actions and spawn
349  * attributes uses vfork()/execve().
350  *
351  * On FreeBSD 8.0 posix_spawn() is implemented via vfork()/execve().
352  *
353  * Solaris 10:
354  *   In the Solaris 10 OS, posix_spawn() is currently implemented using
355  *   private-to-libc vfork(), execve(), and exit() functions.  They are
356  *   identical to regular vfork(), execve(), and exit() in functionality,
357  *   but they are not exported from libc and therefore don't cause the
358  *   deadlock-in-the-dynamic-linker problem that any multithreaded code
359  *   outside of libc that calls vfork() can cause.
360  *
361  * On MacOSX 10.5 (Leoprad) and NetBSD 6.0 posix_spawn() is implemented
362  * as syscall.
363  */
364 
365 nxt_pid_t
366 nxt_process_execute(nxt_task_t *task, char *name, char **argv, char **envp)
367 {
368     nxt_pid_t  pid;
369 
370     nxt_debug(task, "posix_spawn(\"%s\")", name);
371 
372     if (posix_spawn(&pid, name, NULL, NULL, argv, envp) != 0) {
373         nxt_alert(task, "posix_spawn(\"%s\") failed %E", name, nxt_errno);
374         return -1;
375     }
376 
377     return pid;
378 }
379 
380 #else
381 
382 nxt_pid_t
383 nxt_process_execute(nxt_task_t *task, char *name, char **argv, char **envp)
384 {
385     nxt_pid_t  pid;
386 
387     /*
388      * vfork() is better than fork() because:
389      *   it is faster several times;
390      *   its execution time does not depend on private memory mapping size;
391      *   it has lesser chances to fail due to the ENOMEM error.
392      */
393 
394     pid = vfork();
395 
396     switch (pid) {
397 
398     case -1:
399         nxt_alert(task, "vfork() failed while executing \"%s\" %E",
400                   name, nxt_errno);
401         break;
402 
403     case 0:
404         /* A child. */
405         nxt_debug(task, "execve(\"%s\")", name);
406 
407         (void) execve(name, argv, envp);
408 
409         nxt_alert(task, "execve(\"%s\") failed %E", name, nxt_errno);
410 
411         exit(1);
412         nxt_unreachable();
413         break;
414 
415     default:
416         /* A parent. */
417         nxt_debug(task, "vfork(): %PI", pid);
418         break;
419     }
420 
421     return pid;
422 }
423 
424 #endif
425 
426 
427 nxt_int_t
428 nxt_process_daemon(nxt_task_t *task)
429 {
430     nxt_fd_t      fd;
431     nxt_pid_t     pid;
432     const char    *msg;
433 
434     fd = -1;
435 
436     /*
437      * fork() followed by a parent process's exit() detaches a child process
438      * from an init script or terminal shell process which has started the
439      * parent process and allows the child process to run in background.
440      */
441 
442     pid = fork();
443 
444     switch (pid) {
445 
446     case -1:
447         msg = "fork() failed %E";
448         goto fail;
449 
450     case 0:
451         /* A child. */
452         break;
453 
454     default:
455         /* A parent. */
456         nxt_debug(task, "fork(): %PI", pid);
457         exit(0);
458         nxt_unreachable();
459     }
460 
461     nxt_pid = getpid();
462 
463     /* Clean inherited cached thread tid. */
464     task->thread->tid = 0;
465 
466     nxt_debug(task, "daemon");
467 
468     /* Detach from controlling terminal. */
469 
470     if (setsid() == -1) {
471         nxt_alert(task, "setsid() failed %E", nxt_errno);
472         return NXT_ERROR;
473     }
474 
475     /*
476      * Reset file mode creation mask: any access
477      * rights can be set on file creation.
478      */
479     umask(0);
480 
481     /* Redirect STDIN and STDOUT to the "/dev/null". */
482 
483     fd = open("/dev/null", O_RDWR);
484     if (fd == -1) {
485         msg = "open(\"/dev/null\") failed %E";
486         goto fail;
487     }
488 
489     if (dup2(fd, STDIN_FILENO) == -1) {
490         msg = "dup2(\"/dev/null\", STDIN) failed %E";
491         goto fail;
492     }
493 
494     if (dup2(fd, STDOUT_FILENO) == -1) {
495         msg = "dup2(\"/dev/null\", STDOUT) failed %E";
496         goto fail;
497     }
498 
499     if (fd > STDERR_FILENO) {
500         nxt_fd_close(fd);
501     }
502 
503     return NXT_OK;
504 
505 fail:
506 
507     nxt_alert(task, msg, nxt_errno);
508 
509     if (fd != -1) {
510         nxt_fd_close(fd);
511     }
512 
513     return NXT_ERROR;
514 }
515 
516 
517 void
518 nxt_nanosleep(nxt_nsec_t ns)
519 {
520     struct timespec  ts;
521 
522     ts.tv_sec = ns / 1000000000;
523     ts.tv_nsec = ns % 1000000000;
524 
525     (void) nanosleep(&ts, NULL);
526 }
527 
528 
529 nxt_int_t
530 nxt_user_cred_get(nxt_task_t *task, nxt_mp_t *mp, nxt_user_cred_t *uc,
531     const char *group)
532 {
533     struct group   *grp;
534     struct passwd  *pwd;
535 
536     nxt_errno = 0;
537 
538     pwd = getpwnam(uc->user);
539 
540     if (nxt_slow_path(pwd == NULL)) {
541 
542         if (nxt_errno == 0) {
543             nxt_alert(task, "getpwnam(\"%s\") failed, user \"%s\" not found",
544                       uc->user, uc->user);
545         } else {
546             nxt_alert(task, "getpwnam(\"%s\") failed %E", uc->user, nxt_errno);
547         }
548 
549         return NXT_ERROR;
550     }
551 
552     uc->uid = pwd->pw_uid;
553     uc->base_gid = pwd->pw_gid;
554 
555     if (group != NULL && group[0] != '\0') {
556         nxt_errno = 0;
557 
558         grp = getgrnam(group);
559 
560         if (nxt_slow_path(grp == NULL)) {
561 
562             if (nxt_errno == 0) {
563                 nxt_alert(task,
564                           "getgrnam(\"%s\") failed, group \"%s\" not found",
565                           group, group);
566             } else {
567                 nxt_alert(task, "getgrnam(\"%s\") failed %E", group, nxt_errno);
568             }
569 
570             return NXT_ERROR;
571         }
572 
573         uc->base_gid = grp->gr_gid;
574     }
575 
576     nxt_debug(task, "about to get \"%s\" groups (uid:%d, base gid:%d)",
577               uc->user, uc->uid, uc->base_gid);
578 
579     if (nxt_user_groups_get(task, mp, uc) != NXT_OK) {
580         return NXT_ERROR;
581     }
582 
583 #if (NXT_DEBUG)
584     {
585         u_char      *p, *end;
586         nxt_uint_t  i;
587         u_char      msg[NXT_MAX_ERROR_STR];
588 
589         p = msg;
590         end = msg + NXT_MAX_ERROR_STR;
591 
592         for (i = 0; i < uc->ngroups; i++) {
593             p = nxt_sprintf(p, end, "%d%c", uc->gids[i],
594                             i+1 < uc->ngroups ? ',' : '\0');
595         }
596 
597         nxt_debug(task, "user \"%s\" has gids:%*s", uc->user, p - msg, msg);
598     }
599 #endif
600 
601     return NXT_OK;
602 }
603 
604 
605 #if (NXT_HAVE_GETGROUPLIST && !NXT_MACOSX)
606 
607 #define NXT_NGROUPS nxt_min(256, NGROUPS_MAX)
608 
609 
610 static nxt_int_t
611 nxt_user_groups_get(nxt_task_t *task, nxt_mp_t *mp, nxt_user_cred_t *uc)
612 {
613     int    ngroups;
614     gid_t  groups[NXT_NGROUPS];
615 
616     ngroups = NXT_NGROUPS;
617 
618     if (getgrouplist(uc->user, uc->base_gid, groups, &ngroups) < 0) {
619         if (nxt_slow_path(ngroups <= NXT_NGROUPS)) {
620             nxt_alert(task, "getgrouplist(\"%s\", %d, ...) failed %E", uc->user,
621                       uc->base_gid, nxt_errno);
622 
623             return NXT_ERROR;
624         }
625     }
626 
627     if (ngroups > NXT_NGROUPS) {
628         if (ngroups > NGROUPS_MAX) {
629             ngroups = NGROUPS_MAX;
630         }
631 
632         uc->ngroups = ngroups;
633 
634         uc->gids = nxt_mp_alloc(mp, ngroups * sizeof(gid_t));
635         if (nxt_slow_path(uc->gids == NULL)) {
636             return NXT_ERROR;
637         }
638 
639         if (nxt_slow_path(getgrouplist(uc->user, uc->base_gid, uc->gids,
640                                        &ngroups) < 0)) {
641 
642             nxt_alert(task, "getgrouplist(\"%s\", %d) failed %E", uc->user,
643                       uc->base_gid, nxt_errno);
644 
645             return NXT_ERROR;
646         }
647 
648         return NXT_OK;
649     }
650 
651     uc->ngroups = ngroups;
652 
653     uc->gids = nxt_mp_alloc(mp, ngroups * sizeof(gid_t));
654     if (nxt_slow_path(uc->gids == NULL)) {
655         return NXT_ERROR;
656     }
657 
658     nxt_memcpy(uc->gids, groups, ngroups * sizeof(gid_t));
659 
660     return NXT_OK;
661 }
662 
663 
664 #else
665 
666 /*
667  * For operating systems that lack getgrouplist(3) or it's buggy (MacOS),
668  * nxt_user_groups_get() stores an array of groups IDs which should be
669  * set by the setgroups() function for a given user.  The initgroups()
670  * may block a just forked worker process for some time if LDAP or NDIS+
671  * is used, so nxt_user_groups_get() allows to get worker user groups in
672  * main process.  In a nutshell the initgroups() calls getgrouplist()
673  * followed by setgroups().  However older Solaris lacks the getgrouplist().
674  * Besides getgrouplist() does not allow to query the exact number of
675  * groups in some platforms, while NGROUPS_MAX can be quite large (e.g.
676  * 65536 on Linux).
677  * So nxt_user_groups_get() emulates getgrouplist(): at first the function
678  * saves the super-user groups IDs, then calls initgroups() and saves the
679  * specified user groups IDs, and then restores the super-user groups IDs.
680  * This works at least on Linux, FreeBSD, and Solaris, but does not work
681  * on MacOSX, getgroups(2):
682  *
683  *   To provide compatibility with applications that use getgroups() in
684  *   environments where users may be in more than {NGROUPS_MAX} groups,
685  *   a variant of getgroups(), obtained when compiling with either the
686  *   macros _DARWIN_UNLIMITED_GETGROUPS or _DARWIN_C_SOURCE defined, can
687  *   be used that is not limited to {NGROUPS_MAX} groups.  However, this
688  *   variant only returns the user's default group access list and not
689  *   the group list modified by a call to setgroups(2).
690  *
691  * For such cases initgroups() is used in worker process as fallback.
692  */
693 
694 static nxt_int_t
695 nxt_user_groups_get(nxt_task_t *task, nxt_mp_t *mp, nxt_user_cred_t *uc)
696 {
697     int        nsaved, ngroups;
698     nxt_int_t  ret;
699     nxt_gid_t  *saved;
700 
701     nsaved = getgroups(0, NULL);
702 
703     if (nxt_slow_path(nsaved == -1)) {
704         nxt_alert(task, "getgroups(0, NULL) failed %E", nxt_errno);
705         return NXT_ERROR;
706     }
707 
708     nxt_debug(task, "getgroups(0, NULL): %d", nsaved);
709 
710     if (nsaved > NGROUPS_MAX) {
711         /* MacOSX case. */
712 
713         uc->gids = NULL;
714         uc->ngroups = 0;
715 
716         return NXT_OK;
717     }
718 
719     saved = nxt_mp_alloc(mp, nsaved * sizeof(nxt_gid_t));
720 
721     if (nxt_slow_path(saved == NULL)) {
722         return NXT_ERROR;
723     }
724 
725     ret = NXT_ERROR;
726 
727     nsaved = getgroups(nsaved, saved);
728 
729     if (nxt_slow_path(nsaved == -1)) {
730         nxt_alert(task, "getgroups(%d) failed %E", nsaved, nxt_errno);
731         goto free;
732     }
733 
734     nxt_debug(task, "getgroups(): %d", nsaved);
735 
736     if (initgroups(uc->user, uc->base_gid) != 0) {
737         if (nxt_errno == NXT_EPERM) {
738             nxt_log(task, NXT_LOG_NOTICE,
739                     "initgroups(%s, %d) failed %E, ignored",
740                     uc->user, uc->base_gid, nxt_errno);
741 
742             ret = NXT_OK;
743 
744             goto free;
745 
746         } else {
747             nxt_alert(task, "initgroups(%s, %d) failed %E",
748                       uc->user, uc->base_gid, nxt_errno);
749             goto restore;
750         }
751     }
752 
753     ngroups = getgroups(0, NULL);
754 
755     if (nxt_slow_path(ngroups == -1)) {
756         nxt_alert(task, "getgroups(0, NULL) failed %E", nxt_errno);
757         goto restore;
758     }
759 
760     nxt_debug(task, "getgroups(0, NULL): %d", ngroups);
761 
762     uc->gids = nxt_mp_alloc(mp, ngroups * sizeof(nxt_gid_t));
763 
764     if (nxt_slow_path(uc->gids == NULL)) {
765         goto restore;
766     }
767 
768     ngroups = getgroups(ngroups, uc->gids);
769 
770     if (nxt_slow_path(ngroups == -1)) {
771         nxt_alert(task, "getgroups(%d) failed %E", ngroups, nxt_errno);
772         goto restore;
773     }
774 
775     uc->ngroups = ngroups;
776 
777     ret = NXT_OK;
778 
779 restore:
780 
781     if (nxt_slow_path(setgroups(nsaved, saved) != 0)) {
782         nxt_alert(task, "setgroups(%d) failed %E", nsaved, nxt_errno);
783         ret = NXT_ERROR;
784     }
785 
786 free:
787 
788     nxt_mp_free(mp, saved);
789 
790     return ret;
791 }
792 
793 
794 #endif
795 
796 
797 nxt_int_t
798 nxt_user_cred_set(nxt_task_t *task, nxt_user_cred_t *uc)
799 {
800     nxt_debug(task, "user cred set: \"%s\" uid:%d base gid:%d",
801               uc->user, uc->uid, uc->base_gid);
802 
803     if (setgid(uc->base_gid) != 0) {
804 
805 #if (NXT_HAVE_CLONE)
806         if (nxt_errno == EINVAL) {
807             nxt_log(task, NXT_LOG_ERR, "The gid %d isn't valid in the "
808                     "application namespace.", uc->base_gid);
809             return NXT_ERROR;
810         }
811 #endif
812 
813         nxt_alert(task, "setgid(%d) failed %E", uc->base_gid, nxt_errno);
814         return NXT_ERROR;
815     }
816 
817     if (uc->gids != NULL) {
818         if (setgroups(uc->ngroups, uc->gids) != 0) {
819 
820 #if (NXT_HAVE_CLONE)
821             if (nxt_errno == EINVAL) {
822                 nxt_log(task, NXT_LOG_ERR, "The user \"%s\" (uid: %d) has "
823                         "supplementary group ids not valid in the application "
824                         "namespace.", uc->user, uc->uid);
825                 return NXT_ERROR;
826             }
827 #endif
828 
829             nxt_alert(task, "setgroups(%i) failed %E", uc->ngroups, nxt_errno);
830             return NXT_ERROR;
831         }
832 
833     } else {
834         /* MacOSX fallback. */
835         if (initgroups(uc->user, uc->base_gid) != 0) {
836             nxt_alert(task, "initgroups(%s, %d) failed %E",
837                       uc->user, uc->base_gid, nxt_errno);
838             return NXT_ERROR;
839         }
840     }
841 
842     if (setuid(uc->uid) != 0) {
843 
844 #if (NXT_HAVE_CLONE)
845         if (nxt_errno == EINVAL) {
846             nxt_log(task, NXT_LOG_ERR, "The uid %d (user \"%s\") isn't "
847                     "valid in the application namespace.", uc->uid, uc->user);
848             return NXT_ERROR;
849         }
850 #endif
851 
852         nxt_alert(task, "setuid(%d) failed %E", uc->uid, nxt_errno);
853         return NXT_ERROR;
854     }
855 
856     return NXT_OK;
857 }
858 
859 
860 void
861 nxt_process_use(nxt_task_t *task, nxt_process_t *process, int i)
862 {
863     process->use_count += i;
864 
865     if (process->use_count == 0) {
866         nxt_runtime_process_release(task->thread->runtime, process);
867     }
868 }
869 
870 
871 void
872 nxt_process_port_add(nxt_task_t *task, nxt_process_t *process, nxt_port_t *port)
873 {
874     nxt_assert(port->process == NULL);
875 
876     port->process = process;
877     nxt_queue_insert_tail(&process->ports, &port->link);
878 
879     nxt_process_use(task, process, 1);
880 }
881 
882 
883 nxt_process_type_t
884 nxt_process_type(nxt_process_t *process)
885 {
886     return nxt_queue_is_empty(&process->ports) ? 0 :
887         (nxt_process_port_first(process))->type;
888 }
889 
890 
891 void
892 nxt_process_close_ports(nxt_task_t *task, nxt_process_t *process)
893 {
894     nxt_port_t  *port;
895 
896     nxt_process_port_each(process, port) {
897 
898         nxt_port_close(task, port);
899 
900         nxt_runtime_port_remove(task, port);
901 
902     } nxt_process_port_loop;
903 }
904 
905 
906 void
907 nxt_process_connected_port_add(nxt_process_t *process, nxt_port_t *port)
908 {
909     nxt_thread_mutex_lock(&process->cp_mutex);
910 
911     nxt_port_hash_add(&process->connected_ports, port);
912 
913     nxt_thread_mutex_unlock(&process->cp_mutex);
914 }
915 
916 
917 void
918 nxt_process_connected_port_remove(nxt_process_t *process, nxt_port_t *port)
919 {
920     nxt_thread_mutex_lock(&process->cp_mutex);
921 
922     nxt_port_hash_remove(&process->connected_ports, port);
923 
924     nxt_thread_mutex_unlock(&process->cp_mutex);
925 }
926 
927 
928 nxt_port_t *
929 nxt_process_connected_port_find(nxt_process_t *process, nxt_pid_t pid,
930     nxt_port_id_t port_id)
931 {
932     nxt_port_t  *res;
933 
934     nxt_thread_mutex_lock(&process->cp_mutex);
935 
936     res = nxt_port_hash_find(&process->connected_ports, pid, port_id);
937 
938     nxt_thread_mutex_unlock(&process->cp_mutex);
939 
940     return res;
941 }
942