xref: /unit/src/nxt_process.c (revision 1014:8e802fcac511)
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 
11 static void nxt_process_start(nxt_task_t *task, nxt_process_t *process);
12 static nxt_int_t nxt_user_groups_get(nxt_task_t *task, nxt_user_cred_t *uc);
13 
14 
15 /* A cached process pid. */
16 nxt_pid_t  nxt_pid;
17 
18 /* An original parent process pid. */
19 nxt_pid_t  nxt_ppid;
20 
21 nxt_bool_t  nxt_proc_conn_matrix[NXT_PROCESS_MAX][NXT_PROCESS_MAX] = {
22     { 1, 1, 1, 1, 1 },
23     { 1, 0, 0, 0, 0 },
24     { 1, 0, 0, 1, 0 },
25     { 1, 0, 1, 0, 1 },
26     { 1, 0, 0, 0, 0 },
27 };
28 
29 nxt_bool_t  nxt_proc_remove_notify_matrix[NXT_PROCESS_MAX][NXT_PROCESS_MAX] = {
30     { 0, 0, 0, 0, 0 },
31     { 0, 0, 0, 0, 0 },
32     { 0, 0, 0, 1, 0 },
33     { 0, 0, 1, 0, 1 },
34     { 0, 0, 0, 1, 0 },
35 };
36 
37 nxt_pid_t
38 nxt_process_create(nxt_task_t *task, nxt_process_t *process)
39 {
40     nxt_pid_t           pid;
41     nxt_process_t       *p;
42     nxt_runtime_t       *rt;
43     nxt_process_type_t  ptype;
44 
45     rt = task->thread->runtime;
46 
47     pid = fork();
48 
49     switch (pid) {
50 
51     case -1:
52         nxt_alert(task, "fork() failed while creating \"%s\" %E",
53                   process->init->name, nxt_errno);
54         break;
55 
56     case 0:
57         /* A child. */
58         nxt_pid = getpid();
59 
60         /* Clean inherited cached thread tid. */
61         task->thread->tid = 0;
62 
63         process->pid = nxt_pid;
64 
65         ptype = process->init->type;
66 
67         nxt_port_reset_next_id();
68 
69         nxt_event_engine_thread_adopt(task->thread->engine);
70 
71         /* Remove not ready processes */
72         nxt_runtime_process_each(rt, p) {
73 
74             if (nxt_proc_conn_matrix[ptype][nxt_process_type(p)] == 0) {
75                 nxt_debug(task, "remove not required process %PI", p->pid);
76 
77                 nxt_process_close_ports(task, p);
78 
79                 continue;
80             }
81 
82             if (!p->ready) {
83                 nxt_debug(task, "remove not ready process %PI", p->pid);
84 
85                 nxt_process_close_ports(task, p);
86 
87                 continue;
88             }
89 
90             nxt_port_mmaps_destroy(&p->incoming, 0);
91             nxt_port_mmaps_destroy(&p->outgoing, 0);
92 
93         } nxt_runtime_process_loop;
94 
95         nxt_runtime_process_add(task, process);
96 
97         nxt_process_start(task, process);
98 
99         process->ready = 1;
100 
101         break;
102 
103     default:
104         /* A parent. */
105         nxt_debug(task, "fork(\"%s\"): %PI", process->init->name, pid);
106 
107         process->pid = pid;
108 
109         nxt_runtime_process_add(task, process);
110 
111         break;
112     }
113 
114     return pid;
115 }
116 
117 
118 static void
119 nxt_process_start(nxt_task_t *task, nxt_process_t *process)
120 {
121     nxt_int_t                    ret;
122     nxt_port_t                   *port, *main_port;
123     nxt_thread_t                 *thread;
124     nxt_runtime_t                *rt;
125     nxt_process_init_t           *init;
126     nxt_event_engine_t           *engine;
127     const nxt_event_interface_t  *interface;
128 
129     init = process->init;
130 
131     nxt_log(task, NXT_LOG_INFO, "%s started", init->name);
132 
133     nxt_process_title(task, "unit: %s", init->name);
134 
135     thread = task->thread;
136 
137     nxt_random_init(&thread->random);
138 
139     if (init->user_cred != NULL) {
140         /*
141          * Changing user credentials requires either root privileges
142          * or CAP_SETUID and CAP_SETGID capabilities on Linux.
143          */
144         ret = nxt_user_cred_set(task, init->user_cred);
145         if (ret != NXT_OK) {
146             goto fail;
147         }
148     }
149 
150     rt = thread->runtime;
151 
152     rt->type = init->type;
153 
154     engine = thread->engine;
155 
156     /* Update inherited main process event engine and signals processing. */
157     engine->signals->sigev = init->signals;
158 
159     interface = nxt_service_get(rt->services, "engine", rt->engine);
160     if (nxt_slow_path(interface == NULL)) {
161         goto fail;
162     }
163 
164     if (nxt_event_engine_change(engine, interface, rt->batch) != NXT_OK) {
165         goto fail;
166     }
167 
168     ret = nxt_runtime_thread_pool_create(thread, rt, rt->auxiliary_threads,
169                                          60000 * 1000000LL);
170     if (nxt_slow_path(ret != NXT_OK)) {
171         goto fail;
172     }
173 
174     main_port = rt->port_by_type[NXT_PROCESS_MAIN];
175 
176     nxt_port_read_close(main_port);
177     nxt_port_write_enable(task, main_port);
178 
179     port = nxt_process_port_first(process);
180 
181     nxt_port_write_close(port);
182 
183     ret = init->start(task, init->data);
184 
185     if (nxt_slow_path(ret != NXT_OK)) {
186         goto fail;
187     }
188 
189     nxt_port_enable(task, port, init->port_handlers);
190 
191     ret = nxt_port_socket_write(task, main_port, NXT_PORT_MSG_PROCESS_READY,
192                                 -1, init->stream, 0, NULL);
193 
194     if (nxt_slow_path(ret != NXT_OK)) {
195         nxt_log(task, NXT_LOG_ERR, "failed to send READY message to main");
196 
197         goto fail;
198     }
199 
200     return;
201 
202 fail:
203 
204     exit(1);
205 }
206 
207 
208 #if (NXT_HAVE_POSIX_SPAWN)
209 
210 /*
211  * Linux glibc 2.2 posix_spawn() is implemented via fork()/execve().
212  * Linux glibc 2.4 posix_spawn() without file actions and spawn
213  * attributes uses vfork()/execve().
214  *
215  * On FreeBSD 8.0 posix_spawn() is implemented via vfork()/execve().
216  *
217  * Solaris 10:
218  *   In the Solaris 10 OS, posix_spawn() is currently implemented using
219  *   private-to-libc vfork(), execve(), and exit() functions.  They are
220  *   identical to regular vfork(), execve(), and exit() in functionality,
221  *   but they are not exported from libc and therefore don't cause the
222  *   deadlock-in-the-dynamic-linker problem that any multithreaded code
223  *   outside of libc that calls vfork() can cause.
224  *
225  * On MacOSX 10.5 (Leoprad) and NetBSD 6.0 posix_spawn() is implemented
226  * as syscall.
227  */
228 
229 nxt_pid_t
230 nxt_process_execute(nxt_task_t *task, char *name, char **argv, char **envp)
231 {
232     nxt_pid_t  pid;
233 
234     nxt_debug(task, "posix_spawn(\"%s\")", name);
235 
236     if (posix_spawn(&pid, name, NULL, NULL, argv, envp) != 0) {
237         nxt_alert(task, "posix_spawn(\"%s\") failed %E", name, nxt_errno);
238         return -1;
239     }
240 
241     return pid;
242 }
243 
244 #else
245 
246 nxt_pid_t
247 nxt_process_execute(nxt_task_t *task, char *name, char **argv, char **envp)
248 {
249     nxt_pid_t  pid;
250 
251     /*
252      * vfork() is better than fork() because:
253      *   it is faster several times;
254      *   its execution time does not depend on private memory mapping size;
255      *   it has lesser chances to fail due to the ENOMEM error.
256      */
257 
258     pid = vfork();
259 
260     switch (pid) {
261 
262     case -1:
263         nxt_alert(task, "vfork() failed while executing \"%s\" %E",
264                   name, nxt_errno);
265         break;
266 
267     case 0:
268         /* A child. */
269         nxt_debug(task, "execve(\"%s\")", name);
270 
271         (void) execve(name, argv, envp);
272 
273         nxt_alert(task, "execve(\"%s\") failed %E", name, nxt_errno);
274 
275         exit(1);
276         nxt_unreachable();
277         break;
278 
279     default:
280         /* A parent. */
281         nxt_debug(task, "vfork(): %PI", pid);
282         break;
283     }
284 
285     return pid;
286 }
287 
288 #endif
289 
290 
291 nxt_int_t
292 nxt_process_daemon(nxt_task_t *task)
293 {
294     nxt_fd_t      fd;
295     nxt_pid_t     pid;
296     const char    *msg;
297 
298     fd = -1;
299 
300     /*
301      * fork() followed by a parent process's exit() detaches a child process
302      * from an init script or terminal shell process which has started the
303      * parent process and allows the child process to run in background.
304      */
305 
306     pid = fork();
307 
308     switch (pid) {
309 
310     case -1:
311         msg = "fork() failed %E";
312         goto fail;
313 
314     case 0:
315         /* A child. */
316         break;
317 
318     default:
319         /* A parent. */
320         nxt_debug(task, "fork(): %PI", pid);
321         exit(0);
322         nxt_unreachable();
323     }
324 
325     nxt_pid = getpid();
326 
327     /* Clean inherited cached thread tid. */
328     task->thread->tid = 0;
329 
330     nxt_debug(task, "daemon");
331 
332     /* Detach from controlling terminal. */
333 
334     if (setsid() == -1) {
335         nxt_alert(task, "setsid() failed %E", nxt_errno);
336         return NXT_ERROR;
337     }
338 
339     /*
340      * Reset file mode creation mask: any access
341      * rights can be set on file creation.
342      */
343     umask(0);
344 
345     /* Redirect STDIN and STDOUT to the "/dev/null". */
346 
347     fd = open("/dev/null", O_RDWR);
348     if (fd == -1) {
349         msg = "open(\"/dev/null\") failed %E";
350         goto fail;
351     }
352 
353     if (dup2(fd, STDIN_FILENO) == -1) {
354         msg = "dup2(\"/dev/null\", STDIN) failed %E";
355         goto fail;
356     }
357 
358     if (dup2(fd, STDOUT_FILENO) == -1) {
359         msg = "dup2(\"/dev/null\", STDOUT) failed %E";
360         goto fail;
361     }
362 
363     if (fd > STDERR_FILENO) {
364         nxt_fd_close(fd);
365     }
366 
367     return NXT_OK;
368 
369 fail:
370 
371     nxt_alert(task, msg, nxt_errno);
372 
373     if (fd != -1) {
374         nxt_fd_close(fd);
375     }
376 
377     return NXT_ERROR;
378 }
379 
380 
381 void
382 nxt_nanosleep(nxt_nsec_t ns)
383 {
384     struct timespec  ts;
385 
386     ts.tv_sec = ns / 1000000000;
387     ts.tv_nsec = ns % 1000000000;
388 
389     (void) nanosleep(&ts, NULL);
390 }
391 
392 
393 nxt_int_t
394 nxt_user_cred_get(nxt_task_t *task, nxt_user_cred_t *uc, const char *group)
395 {
396     struct group   *grp;
397     struct passwd  *pwd;
398 
399     nxt_errno = 0;
400 
401     pwd = getpwnam(uc->user);
402 
403     if (nxt_slow_path(pwd == NULL)) {
404 
405         if (nxt_errno == 0) {
406             nxt_alert(task, "getpwnam(\"%s\") failed, user \"%s\" not found",
407                       uc->user, uc->user);
408         } else {
409             nxt_alert(task, "getpwnam(\"%s\") failed %E", uc->user, nxt_errno);
410         }
411 
412         return NXT_ERROR;
413     }
414 
415     uc->uid = pwd->pw_uid;
416     uc->base_gid = pwd->pw_gid;
417 
418     if (group != NULL && group[0] != '\0') {
419         nxt_errno = 0;
420 
421         grp = getgrnam(group);
422 
423         if (nxt_slow_path(grp == NULL)) {
424 
425             if (nxt_errno == 0) {
426                 nxt_alert(task,
427                           "getgrnam(\"%s\") failed, group \"%s\" not found",
428                           group, group);
429             } else {
430                 nxt_alert(task, "getgrnam(\"%s\") failed %E", group, nxt_errno);
431             }
432 
433             return NXT_ERROR;
434         }
435 
436         uc->base_gid = grp->gr_gid;
437     }
438 
439     return nxt_user_groups_get(task, uc);
440 }
441 
442 
443 /*
444  * nxt_user_groups_get() stores an array of groups IDs which should be
445  * set by the initgroups() function for a given user.  The initgroups()
446  * may block a just forked worker process for some time if LDAP or NDIS+
447  * is used, so nxt_user_groups_get() allows to get worker user groups in
448  * main process.  In a nutshell the initgroups() calls getgrouplist()
449  * followed by setgroups().  However Solaris lacks the getgrouplist().
450  * Besides getgrouplist() does not allow to query the exact number of
451  * groups while NGROUPS_MAX can be quite large (e.g. 65536 on Linux).
452  * So nxt_user_groups_get() emulates getgrouplist(): at first the function
453  * saves the super-user groups IDs, then calls initgroups() and saves the
454  * specified user groups IDs, and then restores the super-user groups IDs.
455  * This works at least on Linux, FreeBSD, and Solaris, but does not work
456  * on MacOSX, getgroups(2):
457  *
458  *   To provide compatibility with applications that use getgroups() in
459  *   environments where users may be in more than {NGROUPS_MAX} groups,
460  *   a variant of getgroups(), obtained when compiling with either the
461  *   macros _DARWIN_UNLIMITED_GETGROUPS or _DARWIN_C_SOURCE defined, can
462  *   be used that is not limited to {NGROUPS_MAX} groups.  However, this
463  *   variant only returns the user's default group access list and not
464  *   the group list modified by a call to setgroups(2).
465  *
466  * For such cases initgroups() is used in worker process as fallback.
467  */
468 
469 static nxt_int_t
470 nxt_user_groups_get(nxt_task_t *task, nxt_user_cred_t *uc)
471 {
472     int        nsaved, ngroups;
473     nxt_int_t  ret;
474     nxt_gid_t  *saved;
475 
476     nsaved = getgroups(0, NULL);
477 
478     if (nsaved == -1) {
479         nxt_alert(task, "getgroups(0, NULL) failed %E", nxt_errno);
480         return NXT_ERROR;
481     }
482 
483     nxt_debug(task, "getgroups(0, NULL): %d", nsaved);
484 
485     if (nsaved > NGROUPS_MAX) {
486         /* MacOSX case. */
487 
488         uc->gids = NULL;
489         uc->ngroups = 0;
490 
491         return NXT_OK;
492     }
493 
494     saved = nxt_malloc(nsaved * sizeof(nxt_gid_t));
495 
496     if (saved == NULL) {
497         return NXT_ERROR;
498     }
499 
500     ret = NXT_ERROR;
501 
502     nsaved = getgroups(nsaved, saved);
503 
504     if (nsaved == -1) {
505         nxt_alert(task, "getgroups(%d) failed %E", nsaved, nxt_errno);
506         goto free;
507     }
508 
509     nxt_debug(task, "getgroups(): %d", nsaved);
510 
511     if (initgroups(uc->user, uc->base_gid) != 0) {
512         if (nxt_errno == NXT_EPERM) {
513             nxt_log(task, NXT_LOG_NOTICE,
514                     "initgroups(%s, %d) failed %E, ignored",
515                     uc->user, uc->base_gid, nxt_errno);
516 
517             ret = NXT_OK;
518 
519             goto free;
520 
521         } else {
522             nxt_alert(task, "initgroups(%s, %d) failed %E",
523                       uc->user, uc->base_gid, nxt_errno);
524             goto restore;
525         }
526     }
527 
528     ngroups = getgroups(0, NULL);
529 
530     if (ngroups == -1) {
531         nxt_alert(task, "getgroups(0, NULL) failed %E", nxt_errno);
532         goto restore;
533     }
534 
535     nxt_debug(task, "getgroups(0, NULL): %d", ngroups);
536 
537     uc->gids = nxt_malloc(ngroups * sizeof(nxt_gid_t));
538 
539     if (uc->gids == NULL) {
540         goto restore;
541     }
542 
543     ngroups = getgroups(ngroups, uc->gids);
544 
545     if (ngroups == -1) {
546         nxt_alert(task, "getgroups(%d) failed %E", ngroups, nxt_errno);
547         goto restore;
548     }
549 
550     uc->ngroups = ngroups;
551 
552 #if (NXT_DEBUG)
553     {
554         u_char      *p, *end;
555         nxt_uint_t  i;
556         u_char      msg[NXT_MAX_ERROR_STR];
557 
558         p = msg;
559         end = msg + NXT_MAX_ERROR_STR;
560 
561         for (i = 0; i < uc->ngroups; i++) {
562             p = nxt_sprintf(p, end, "%uL:", (uint64_t) uc->gids[i]);
563         }
564 
565         nxt_debug(task, "user \"%s\" cred: uid:%uL base gid:%uL, gids:%*s",
566                   uc->user, (uint64_t) uc->uid, (uint64_t) uc->base_gid,
567                   p - msg, msg);
568     }
569 #endif
570 
571     ret = NXT_OK;
572 
573 restore:
574 
575     if (setgroups(nsaved, saved) != 0) {
576         nxt_alert(task, "setgroups(%d) failed %E", nsaved, nxt_errno);
577         ret = NXT_ERROR;
578     }
579 
580 free:
581 
582     nxt_free(saved);
583 
584     return ret;
585 }
586 
587 
588 nxt_int_t
589 nxt_user_cred_set(nxt_task_t *task, nxt_user_cred_t *uc)
590 {
591     nxt_debug(task, "user cred set: \"%s\" uid:%uL base gid:%uL",
592               uc->user, (uint64_t) uc->uid, (uint64_t) uc->base_gid);
593 
594     if (setgid(uc->base_gid) != 0) {
595         if (nxt_errno == NXT_EPERM) {
596             nxt_log(task, NXT_LOG_NOTICE, "setgid(%d) failed %E, ignored",
597                     uc->base_gid, nxt_errno);
598             return NXT_OK;
599 
600         } else {
601             nxt_alert(task, "setgid(%d) failed %E", uc->base_gid, nxt_errno);
602             return NXT_ERROR;
603         }
604     }
605 
606     if (uc->gids != NULL) {
607         if (setgroups(uc->ngroups, uc->gids) != 0) {
608             nxt_alert(task, "setgroups(%i) failed %E", uc->ngroups, nxt_errno);
609             return NXT_ERROR;
610         }
611 
612     } else {
613         /* MacOSX fallback. */
614         if (initgroups(uc->user, uc->base_gid) != 0) {
615             nxt_alert(task, "initgroups(%s, %d) failed %E",
616                       uc->user, uc->base_gid, nxt_errno);
617             return NXT_ERROR;
618         }
619     }
620 
621     if (setuid(uc->uid) != 0) {
622         nxt_alert(task, "setuid(%d) failed %E", uc->uid, nxt_errno);
623         return NXT_ERROR;
624     }
625 
626     return NXT_OK;
627 }
628 
629 
630 void
631 nxt_process_port_add(nxt_task_t *task, nxt_process_t *process, nxt_port_t *port)
632 {
633     nxt_assert(port->process == NULL);
634 
635     port->process = process;
636     nxt_queue_insert_tail(&process->ports, &port->link);
637 
638     nxt_process_use(task, process, 1);
639 }
640 
641 
642 nxt_process_type_t
643 nxt_process_type(nxt_process_t *process)
644 {
645     return nxt_queue_is_empty(&process->ports) ? 0 :
646         (nxt_process_port_first(process))->type;
647 }
648 
649 
650 void
651 nxt_process_close_ports(nxt_task_t *task, nxt_process_t *process)
652 {
653     nxt_port_t  *port;
654 
655     nxt_process_port_each(process, port) {
656 
657         nxt_port_close(task, port);
658 
659         nxt_runtime_port_remove(task, port);
660 
661     } nxt_process_port_loop;
662 }
663 
664 
665 void
666 nxt_process_connected_port_add(nxt_process_t *process, nxt_port_t *port)
667 {
668     nxt_thread_mutex_lock(&process->cp_mutex);
669 
670     nxt_port_hash_add(&process->connected_ports, port);
671 
672     nxt_thread_mutex_unlock(&process->cp_mutex);
673 }
674 
675 
676 void
677 nxt_process_connected_port_remove(nxt_process_t *process, nxt_port_t *port)
678 {
679     nxt_thread_mutex_lock(&process->cp_mutex);
680 
681     nxt_port_hash_remove(&process->connected_ports, port);
682 
683     nxt_thread_mutex_unlock(&process->cp_mutex);
684 }
685 
686 
687 nxt_port_t *
688 nxt_process_connected_port_find(nxt_process_t *process, nxt_pid_t pid,
689     nxt_port_id_t port_id)
690 {
691     nxt_port_t  *res;
692 
693     nxt_thread_mutex_lock(&process->cp_mutex);
694 
695     res = nxt_port_hash_find(&process->connected_ports, pid, port_id);
696 
697     nxt_thread_mutex_unlock(&process->cp_mutex);
698 
699     return res;
700 }
701