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