xref: /unit/src/nxt_process.c (revision 1305:966d691dab2c)
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_process_worker_setup(nxt_task_t *task,
18     nxt_process_t *process, int parentfd);
19 
20 /* A cached process pid. */
21 nxt_pid_t  nxt_pid;
22 
23 /* An original parent process pid. */
24 nxt_pid_t  nxt_ppid;
25 
26 nxt_bool_t  nxt_proc_conn_matrix[NXT_PROCESS_MAX][NXT_PROCESS_MAX] = {
27     { 1, 1, 1, 1, 1 },
28     { 1, 0, 0, 0, 0 },
29     { 1, 0, 0, 1, 0 },
30     { 1, 0, 1, 0, 1 },
31     { 1, 0, 0, 0, 0 },
32 };
33 
34 nxt_bool_t  nxt_proc_remove_notify_matrix[NXT_PROCESS_MAX][NXT_PROCESS_MAX] = {
35     { 0, 0, 0, 0, 0 },
36     { 0, 0, 0, 0, 0 },
37     { 0, 0, 0, 1, 0 },
38     { 0, 0, 1, 0, 1 },
39     { 0, 0, 0, 1, 0 },
40 };
41 
42 
43 static nxt_int_t
44 nxt_process_worker_setup(nxt_task_t *task, nxt_process_t *process, int parentfd)
45 {
46     pid_t               rpid, pid;
47     ssize_t             n;
48     nxt_int_t           parent_status;
49     nxt_process_t       *p;
50     nxt_runtime_t       *rt;
51     nxt_process_init_t  *init;
52     nxt_process_type_t  ptype;
53 
54     pid  = getpid();
55     rpid = 0;
56     rt   = task->thread->runtime;
57     init = process->init;
58 
59     /* Setup the worker process. */
60 
61     n = read(parentfd, &rpid, sizeof(rpid));
62     if (nxt_slow_path(n == -1 || n != sizeof(rpid))) {
63         nxt_alert(task, "failed to read real pid");
64         return NXT_ERROR;
65     }
66 
67     if (nxt_slow_path(rpid == 0)) {
68         nxt_alert(task, "failed to get real pid from parent");
69         return NXT_ERROR;
70     }
71 
72     nxt_pid = rpid;
73 
74     /* Clean inherited cached thread tid. */
75     task->thread->tid = 0;
76 
77     process->pid = nxt_pid;
78 
79     if (nxt_pid != pid) {
80         nxt_debug(task, "app \"%s\" real pid %d", init->name, nxt_pid);
81         nxt_debug(task, "app \"%s\" isolated pid: %d", init->name, pid);
82     }
83 
84     n = read(parentfd, &parent_status, sizeof(parent_status));
85     if (nxt_slow_path(n == -1 || n != sizeof(parent_status))) {
86         nxt_alert(task, "failed to read parent status");
87         return NXT_ERROR;
88     }
89 
90     if (nxt_slow_path(parent_status != NXT_OK)) {
91         return parent_status;
92     }
93 
94     ptype = init->type;
95 
96     nxt_port_reset_next_id();
97 
98     nxt_event_engine_thread_adopt(task->thread->engine);
99 
100     /* Remove not ready processes. */
101     nxt_runtime_process_each(rt, p) {
102 
103         if (nxt_proc_conn_matrix[ptype][nxt_process_type(p)] == 0) {
104             nxt_debug(task, "remove not required process %PI", p->pid);
105 
106             nxt_process_close_ports(task, p);
107 
108             continue;
109         }
110 
111         if (!p->ready) {
112             nxt_debug(task, "remove not ready process %PI", p->pid);
113 
114             nxt_process_close_ports(task, p);
115 
116             continue;
117         }
118 
119         nxt_port_mmaps_destroy(&p->incoming, 0);
120         nxt_port_mmaps_destroy(&p->outgoing, 0);
121 
122     } nxt_runtime_process_loop;
123 
124     nxt_runtime_process_add(task, process);
125 
126     nxt_process_start(task, process);
127 
128     process->ready = 1;
129 
130     return NXT_OK;
131 }
132 
133 
134 nxt_pid_t
135 nxt_process_create(nxt_task_t *task, nxt_process_t *process)
136 {
137     int                 pipefd[2];
138     nxt_int_t           ret;
139     nxt_pid_t           pid;
140     nxt_process_init_t  *init;
141 
142     if (nxt_slow_path(pipe(pipefd) == -1)) {
143         nxt_alert(task, "failed to create process pipe for passing rpid");
144         return -1;
145     }
146 
147     init = process->init;
148 
149 #if (NXT_HAVE_CLONE)
150     pid = nxt_clone(SIGCHLD | init->isolation.clone.flags);
151     if (nxt_slow_path(pid < 0)) {
152         nxt_alert(task, "clone() failed while creating \"%s\" %E",
153                   init->name, nxt_errno);
154         goto cleanup;
155     }
156 #else
157     pid = fork();
158     if (nxt_slow_path(pid < 0)) {
159         nxt_alert(task, "fork() failed while creating \"%s\" %E",
160                   init->name, nxt_errno);
161         goto cleanup;
162     }
163 #endif
164 
165     if (pid == 0) {
166         /* Child. */
167 
168         if (nxt_slow_path(close(pipefd[1]) == -1)) {
169             nxt_alert(task, "failed to close writer pipe fd");
170         }
171 
172         ret = nxt_process_worker_setup(task, process, pipefd[0]);
173         if (nxt_slow_path(ret != NXT_OK)) {
174             exit(1);
175         }
176 
177         if (nxt_slow_path(close(pipefd[0]) == -1)) {
178             nxt_alert(task, "failed to close writer pipe fd");
179         }
180 
181         /*
182          * Explicitly return 0 to notice the caller function this is the child.
183          * The caller must return to the event engine work queue loop.
184          */
185         return 0;
186     }
187 
188     /* Parent. */
189 
190     /*
191      * At this point, the child process is blocked reading the
192      * pipe fd to get its real pid (rpid).
193      *
194      * If anything goes wrong now, we need to terminate the child
195      * process by sending a NXT_ERROR in the pipe.
196      */
197 
198 #if (NXT_HAVE_CLONE)
199     nxt_debug(task, "clone(\"%s\"): %PI", init->name, pid);
200 #else
201     nxt_debug(task, "fork(\"%s\"): %PI", init->name, pid);
202 #endif
203 
204     if (nxt_slow_path(write(pipefd[1], &pid, sizeof(pid)) == -1)) {
205         nxt_alert(task, "failed to write real pid");
206         goto fail;
207     }
208 
209 #if (NXT_HAVE_CLONE && NXT_HAVE_CLONE_NEWUSER)
210     if ((init->isolation.clone.flags & CLONE_NEWUSER) == CLONE_NEWUSER) {
211         ret = nxt_clone_proc_map(task, pid, &init->isolation.clone);
212         if (nxt_slow_path(ret != NXT_OK)) {
213             goto fail;
214         }
215     }
216 #endif
217 
218     ret = NXT_OK;
219 
220     if (nxt_slow_path(write(pipefd[1], &ret, sizeof(ret)) == -1)) {
221         nxt_alert(task, "failed to write status");
222         goto fail;
223     }
224 
225     process->pid = pid;
226 
227     nxt_runtime_process_add(task, process);
228 
229     goto cleanup;
230 
231 fail:
232 
233     ret = NXT_ERROR;
234 
235     if (nxt_slow_path(write(pipefd[1], &ret, sizeof(ret)) == -1)) {
236         nxt_alert(task, "failed to write status");
237     }
238 
239     waitpid(pid, NULL, 0);
240 
241     pid = -1;
242 
243 cleanup:
244 
245     if (nxt_slow_path(close(pipefd[0]) != 0)) {
246         nxt_alert(task, "failed to close pipe: %E", nxt_errno);
247     }
248 
249     if (nxt_slow_path(close(pipefd[1]) != 0)) {
250         nxt_alert(task, "failed to close pipe: %E", nxt_errno);
251     }
252 
253     return pid;
254 }
255 
256 
257 static void
258 nxt_process_start(nxt_task_t *task, nxt_process_t *process)
259 {
260     nxt_int_t                    ret;
261     nxt_port_t                   *port, *main_port;
262     nxt_thread_t                 *thread;
263     nxt_runtime_t                *rt;
264     nxt_process_init_t           *init;
265     nxt_event_engine_t           *engine;
266     const nxt_event_interface_t  *interface;
267 
268     init = process->init;
269 
270     nxt_log(task, NXT_LOG_INFO, "%s started", init->name);
271 
272     nxt_process_title(task, "unit: %s", init->name);
273 
274     thread = task->thread;
275     rt     = thread->runtime;
276 
277     nxt_random_init(&thread->random);
278 
279     if (rt->capabilities.setid && init->user_cred != NULL) {
280         ret = nxt_credential_set(task, init->user_cred);
281         if (ret != NXT_OK) {
282             goto fail;
283         }
284     }
285 
286     rt->type = init->type;
287 
288     engine = thread->engine;
289 
290     /* Update inherited main process event engine and signals processing. */
291     engine->signals->sigev = init->signals;
292 
293     interface = nxt_service_get(rt->services, "engine", rt->engine);
294     if (nxt_slow_path(interface == NULL)) {
295         goto fail;
296     }
297 
298     if (nxt_event_engine_change(engine, interface, rt->batch) != NXT_OK) {
299         goto fail;
300     }
301 
302     ret = nxt_runtime_thread_pool_create(thread, rt, rt->auxiliary_threads,
303                                          60000 * 1000000LL);
304     if (nxt_slow_path(ret != NXT_OK)) {
305         goto fail;
306     }
307 
308     main_port = rt->port_by_type[NXT_PROCESS_MAIN];
309 
310     nxt_port_read_close(main_port);
311     nxt_port_write_enable(task, main_port);
312 
313     port = nxt_process_port_first(process);
314 
315     nxt_port_write_close(port);
316 
317     ret = init->start(task, init->data);
318 
319     if (nxt_slow_path(ret != NXT_OK)) {
320         goto fail;
321     }
322 
323     nxt_port_enable(task, port, init->port_handlers);
324 
325     ret = nxt_port_socket_write(task, main_port, NXT_PORT_MSG_PROCESS_READY,
326                                 -1, init->stream, 0, NULL);
327 
328     if (nxt_slow_path(ret != NXT_OK)) {
329         nxt_log(task, NXT_LOG_ERR, "failed to send READY message to main");
330 
331         goto fail;
332     }
333 
334     return;
335 
336 fail:
337 
338     exit(1);
339 }
340 
341 
342 #if (NXT_HAVE_POSIX_SPAWN)
343 
344 /*
345  * Linux glibc 2.2 posix_spawn() is implemented via fork()/execve().
346  * Linux glibc 2.4 posix_spawn() without file actions and spawn
347  * attributes uses vfork()/execve().
348  *
349  * On FreeBSD 8.0 posix_spawn() is implemented via vfork()/execve().
350  *
351  * Solaris 10:
352  *   In the Solaris 10 OS, posix_spawn() is currently implemented using
353  *   private-to-libc vfork(), execve(), and exit() functions.  They are
354  *   identical to regular vfork(), execve(), and exit() in functionality,
355  *   but they are not exported from libc and therefore don't cause the
356  *   deadlock-in-the-dynamic-linker problem that any multithreaded code
357  *   outside of libc that calls vfork() can cause.
358  *
359  * On MacOSX 10.5 (Leoprad) and NetBSD 6.0 posix_spawn() is implemented
360  * as syscall.
361  */
362 
363 nxt_pid_t
364 nxt_process_execute(nxt_task_t *task, char *name, char **argv, char **envp)
365 {
366     nxt_pid_t  pid;
367 
368     nxt_debug(task, "posix_spawn(\"%s\")", name);
369 
370     if (posix_spawn(&pid, name, NULL, NULL, argv, envp) != 0) {
371         nxt_alert(task, "posix_spawn(\"%s\") failed %E", name, nxt_errno);
372         return -1;
373     }
374 
375     return pid;
376 }
377 
378 #else
379 
380 nxt_pid_t
381 nxt_process_execute(nxt_task_t *task, char *name, char **argv, char **envp)
382 {
383     nxt_pid_t  pid;
384 
385     /*
386      * vfork() is better than fork() because:
387      *   it is faster several times;
388      *   its execution time does not depend on private memory mapping size;
389      *   it has lesser chances to fail due to the ENOMEM error.
390      */
391 
392     pid = vfork();
393 
394     switch (pid) {
395 
396     case -1:
397         nxt_alert(task, "vfork() failed while executing \"%s\" %E",
398                   name, nxt_errno);
399         break;
400 
401     case 0:
402         /* A child. */
403         nxt_debug(task, "execve(\"%s\")", name);
404 
405         (void) execve(name, argv, envp);
406 
407         nxt_alert(task, "execve(\"%s\") failed %E", name, nxt_errno);
408 
409         exit(1);
410         nxt_unreachable();
411         break;
412 
413     default:
414         /* A parent. */
415         nxt_debug(task, "vfork(): %PI", pid);
416         break;
417     }
418 
419     return pid;
420 }
421 
422 #endif
423 
424 
425 nxt_int_t
426 nxt_process_daemon(nxt_task_t *task)
427 {
428     nxt_fd_t      fd;
429     nxt_pid_t     pid;
430     const char    *msg;
431 
432     fd = -1;
433 
434     /*
435      * fork() followed by a parent process's exit() detaches a child process
436      * from an init script or terminal shell process which has started the
437      * parent process and allows the child process to run in background.
438      */
439 
440     pid = fork();
441 
442     switch (pid) {
443 
444     case -1:
445         msg = "fork() failed %E";
446         goto fail;
447 
448     case 0:
449         /* A child. */
450         break;
451 
452     default:
453         /* A parent. */
454         nxt_debug(task, "fork(): %PI", pid);
455         exit(0);
456         nxt_unreachable();
457     }
458 
459     nxt_pid = getpid();
460 
461     /* Clean inherited cached thread tid. */
462     task->thread->tid = 0;
463 
464     nxt_debug(task, "daemon");
465 
466     /* Detach from controlling terminal. */
467 
468     if (setsid() == -1) {
469         nxt_alert(task, "setsid() failed %E", nxt_errno);
470         return NXT_ERROR;
471     }
472 
473     /*
474      * Reset file mode creation mask: any access
475      * rights can be set on file creation.
476      */
477     umask(0);
478 
479     /* Redirect STDIN and STDOUT to the "/dev/null". */
480 
481     fd = open("/dev/null", O_RDWR);
482     if (fd == -1) {
483         msg = "open(\"/dev/null\") failed %E";
484         goto fail;
485     }
486 
487     if (dup2(fd, STDIN_FILENO) == -1) {
488         msg = "dup2(\"/dev/null\", STDIN) failed %E";
489         goto fail;
490     }
491 
492     if (dup2(fd, STDOUT_FILENO) == -1) {
493         msg = "dup2(\"/dev/null\", STDOUT) failed %E";
494         goto fail;
495     }
496 
497     if (fd > STDERR_FILENO) {
498         nxt_fd_close(fd);
499     }
500 
501     return NXT_OK;
502 
503 fail:
504 
505     nxt_alert(task, msg, nxt_errno);
506 
507     if (fd != -1) {
508         nxt_fd_close(fd);
509     }
510 
511     return NXT_ERROR;
512 }
513 
514 
515 void
516 nxt_nanosleep(nxt_nsec_t ns)
517 {
518     struct timespec  ts;
519 
520     ts.tv_sec = ns / 1000000000;
521     ts.tv_nsec = ns % 1000000000;
522 
523     (void) nanosleep(&ts, NULL);
524 }
525 
526 
527 void
528 nxt_process_use(nxt_task_t *task, nxt_process_t *process, int i)
529 {
530     process->use_count += i;
531 
532     if (process->use_count == 0) {
533         nxt_runtime_process_release(task->thread->runtime, process);
534     }
535 }
536 
537 
538 void
539 nxt_process_port_add(nxt_task_t *task, nxt_process_t *process, nxt_port_t *port)
540 {
541     nxt_assert(port->process == NULL);
542 
543     port->process = process;
544     nxt_queue_insert_tail(&process->ports, &port->link);
545 
546     nxt_process_use(task, process, 1);
547 }
548 
549 
550 nxt_process_type_t
551 nxt_process_type(nxt_process_t *process)
552 {
553     return nxt_queue_is_empty(&process->ports) ? 0 :
554         (nxt_process_port_first(process))->type;
555 }
556 
557 
558 void
559 nxt_process_close_ports(nxt_task_t *task, nxt_process_t *process)
560 {
561     nxt_port_t  *port;
562 
563     nxt_process_port_each(process, port) {
564 
565         nxt_port_close(task, port);
566 
567         nxt_runtime_port_remove(task, port);
568 
569     } nxt_process_port_loop;
570 }
571 
572 
573 void
574 nxt_process_connected_port_add(nxt_process_t *process, nxt_port_t *port)
575 {
576     nxt_thread_mutex_lock(&process->cp_mutex);
577 
578     nxt_port_hash_add(&process->connected_ports, port);
579 
580     nxt_thread_mutex_unlock(&process->cp_mutex);
581 }
582 
583 
584 void
585 nxt_process_connected_port_remove(nxt_process_t *process, nxt_port_t *port)
586 {
587     nxt_thread_mutex_lock(&process->cp_mutex);
588 
589     nxt_port_hash_remove(&process->connected_ports, port);
590 
591     nxt_thread_mutex_unlock(&process->cp_mutex);
592 }
593 
594 
595 nxt_port_t *
596 nxt_process_connected_port_find(nxt_process_t *process, nxt_pid_t pid,
597     nxt_port_id_t port_id)
598 {
599     nxt_port_t  *res;
600 
601     nxt_thread_mutex_lock(&process->cp_mutex);
602 
603     res = nxt_port_hash_find(&process->connected_ports, pid, port_id);
604 
605     nxt_thread_mutex_unlock(&process->cp_mutex);
606 
607     return res;
608 }
609