/src/unit/src/nxt_process.c
Line | Count | Source |
1 | | |
2 | | /* |
3 | | * Copyright (C) Igor Sysoev |
4 | | * Copyright (C) NGINX, Inc. |
5 | | */ |
6 | | |
7 | | #include <nxt_main.h> |
8 | | |
9 | | #include <nxt_application.h> |
10 | | #include <nxt_cgroup.h> |
11 | | |
12 | | #if (NXT_HAVE_LINUX_NS) |
13 | | #include <nxt_clone.h> |
14 | | #endif |
15 | | |
16 | | #include <signal.h> |
17 | | |
18 | | #if (NXT_HAVE_PR_SET_NO_NEW_PRIVS) |
19 | | #include <sys/prctl.h> |
20 | | #endif |
21 | | |
22 | | |
23 | | #if (NXT_HAVE_LINUX_NS) && (NXT_HAVE_CLONE_NEWPID) |
24 | | #define nxt_is_pid_isolated(process) \ |
25 | 0 | nxt_is_clone_flag_set(process->isolation.clone.flags, NEWPID) |
26 | | #else |
27 | | #define nxt_is_pid_isolated(process) \ |
28 | | (0) |
29 | | #endif |
30 | | |
31 | | |
32 | | #if (NXT_HAVE_LINUX_NS) |
33 | | static nxt_int_t nxt_process_pipe_timer(nxt_fd_t fd, short event); |
34 | | static nxt_int_t nxt_process_check_pid_status(const nxt_fd_t *gc_pipe); |
35 | | static nxt_pid_t nxt_process_recv_pid(const nxt_fd_t *pid_pipe, |
36 | | const nxt_fd_t *gc_pipe); |
37 | | static void nxt_process_send_pid(const nxt_fd_t *pid_pipe, nxt_pid_t pid); |
38 | | static nxt_int_t nxt_process_unshare(nxt_task_t *task, nxt_process_t *process, |
39 | | nxt_fd_t *pid_pipe, nxt_fd_t *gc_pipe, nxt_bool_t use_pidns); |
40 | | static nxt_int_t nxt_process_init_pidns(nxt_task_t *task, |
41 | | const nxt_process_t *process, nxt_fd_t *pid_pipe, nxt_fd_t *gc_pipe, |
42 | | nxt_bool_t *use_pidns); |
43 | | #endif |
44 | | |
45 | | static nxt_pid_t nxt_process_create(nxt_task_t *task, nxt_process_t *process); |
46 | | static nxt_int_t nxt_process_do_start(nxt_task_t *task, nxt_process_t *process); |
47 | | static nxt_int_t nxt_process_whoami(nxt_task_t *task, nxt_process_t *process); |
48 | | static nxt_int_t nxt_process_setup(nxt_task_t *task, nxt_process_t *process); |
49 | | static nxt_int_t nxt_process_child_fixup(nxt_task_t *task, |
50 | | nxt_process_t *process); |
51 | | static void nxt_process_whoami_ok(nxt_task_t *task, nxt_port_recv_msg_t *msg, |
52 | | void *data); |
53 | | static void nxt_process_whoami_error(nxt_task_t *task, nxt_port_recv_msg_t *msg, |
54 | | void *data); |
55 | | static nxt_int_t nxt_process_send_created(nxt_task_t *task, |
56 | | nxt_process_t *process); |
57 | | static nxt_int_t nxt_process_send_ready(nxt_task_t *task, |
58 | | nxt_process_t *process); |
59 | | static void nxt_process_created_ok(nxt_task_t *task, nxt_port_recv_msg_t *msg, |
60 | | void *data); |
61 | | static void nxt_process_created_error(nxt_task_t *task, |
62 | | nxt_port_recv_msg_t *msg, void *data); |
63 | | |
64 | | |
65 | | /* A cached process pid. */ |
66 | | nxt_pid_t nxt_pid; |
67 | | |
68 | | /* An original parent process pid. */ |
69 | | nxt_pid_t nxt_ppid; |
70 | | |
71 | | /* A cached process effective uid */ |
72 | | nxt_uid_t nxt_euid; |
73 | | |
74 | | /* A cached process effective gid */ |
75 | | nxt_gid_t nxt_egid; |
76 | | |
77 | | uint8_t nxt_proc_keep_matrix[NXT_PROCESS_MAX][NXT_PROCESS_MAX] = { |
78 | | { 1, 1, 1, 1, 1, 1 }, |
79 | | { 1, 0, 0, 0, 0, 0 }, |
80 | | { 1, 0, 0, 1, 0, 0 }, |
81 | | { 1, 0, 1, 1, 1, 1 }, |
82 | | { 1, 0, 0, 1, 0, 0 }, |
83 | | { 1, 0, 0, 1, 0, 0 }, |
84 | | }; |
85 | | |
86 | | uint8_t nxt_proc_send_matrix[NXT_PROCESS_MAX][NXT_PROCESS_MAX] = { |
87 | | { 1, 1, 1, 1, 1, 1 }, |
88 | | { 1, 0, 0, 0, 0, 0 }, |
89 | | { 1, 0, 0, 1, 0, 0 }, |
90 | | { 1, 0, 1, 1, 1, 1 }, |
91 | | { 1, 0, 0, 0, 0, 0 }, |
92 | | { 1, 0, 0, 0, 0, 0 }, |
93 | | }; |
94 | | |
95 | | uint8_t nxt_proc_remove_notify_matrix[NXT_PROCESS_MAX][NXT_PROCESS_MAX] = { |
96 | | { 0, 0, 0, 0, 0, 0 }, |
97 | | { 0, 0, 0, 0, 0, 0 }, |
98 | | { 0, 0, 0, 1, 0, 0 }, |
99 | | { 0, 0, 1, 0, 1, 1 }, |
100 | | { 0, 0, 0, 1, 0, 0 }, |
101 | | { 1, 0, 0, 1, 0, 0 }, |
102 | | }; |
103 | | |
104 | | |
105 | | static const nxt_port_handlers_t nxt_process_whoami_port_handlers = { |
106 | | .quit = nxt_signal_quit_handler, |
107 | | .rpc_ready = nxt_port_rpc_handler, |
108 | | .rpc_error = nxt_port_rpc_handler, |
109 | | }; |
110 | | |
111 | | |
112 | | nxt_process_t * |
113 | | nxt_process_new(nxt_runtime_t *rt) |
114 | 0 | { |
115 | 0 | nxt_process_t *process; |
116 | |
|
117 | 0 | process = nxt_mp_zalloc(rt->mem_pool, sizeof(nxt_process_t) |
118 | 0 | + sizeof(nxt_process_init_t)); |
119 | |
|
120 | 0 | if (nxt_slow_path(process == NULL)) { |
121 | 0 | return NULL; |
122 | 0 | } |
123 | | |
124 | 0 | nxt_queue_init(&process->ports); |
125 | |
|
126 | 0 | nxt_thread_mutex_create(&process->incoming.mutex); |
127 | |
|
128 | 0 | process->use_count = 1; |
129 | |
|
130 | 0 | nxt_queue_init(&process->children); |
131 | |
|
132 | 0 | return process; |
133 | 0 | } |
134 | | |
135 | | |
136 | | void |
137 | | nxt_process_use(nxt_task_t *task, nxt_process_t *process, int i) |
138 | 0 | { |
139 | 0 | process->use_count += i; |
140 | |
|
141 | 0 | if (process->use_count == 0) { |
142 | 0 | nxt_runtime_process_release(task->thread->runtime, process); |
143 | 0 | } |
144 | 0 | } |
145 | | |
146 | | |
147 | | nxt_int_t |
148 | | nxt_process_init_start(nxt_task_t *task, nxt_process_init_t init) |
149 | 0 | { |
150 | 0 | nxt_int_t ret; |
151 | 0 | nxt_runtime_t *rt; |
152 | 0 | nxt_process_t *process; |
153 | 0 | nxt_process_init_t *pinit; |
154 | |
|
155 | 0 | rt = task->thread->runtime; |
156 | |
|
157 | 0 | process = nxt_process_new(rt); |
158 | 0 | if (nxt_slow_path(process == NULL)) { |
159 | 0 | return NXT_ERROR; |
160 | 0 | } |
161 | | |
162 | 0 | process->parent_port = rt->port_by_type[rt->type]; |
163 | |
|
164 | 0 | process->name = init.name; |
165 | 0 | process->user_cred = &rt->user_cred; |
166 | |
|
167 | 0 | pinit = nxt_process_init(process); |
168 | 0 | *pinit = init; |
169 | |
|
170 | 0 | ret = nxt_process_start(task, process); |
171 | 0 | if (nxt_slow_path(ret == NXT_ERROR)) { |
172 | 0 | nxt_process_use(task, process, -1); |
173 | 0 | } |
174 | |
|
175 | 0 | return ret; |
176 | 0 | } |
177 | | |
178 | | |
179 | | nxt_int_t |
180 | | nxt_process_start(nxt_task_t *task, nxt_process_t *process) |
181 | 0 | { |
182 | 0 | nxt_mp_t *tmp_mp; |
183 | 0 | nxt_int_t ret; |
184 | 0 | nxt_pid_t pid; |
185 | 0 | nxt_port_t *port; |
186 | 0 | nxt_process_init_t *init; |
187 | |
|
188 | 0 | init = nxt_process_init(process); |
189 | |
|
190 | 0 | port = nxt_port_new(task, 0, 0, init->type); |
191 | 0 | if (nxt_slow_path(port == NULL)) { |
192 | 0 | return NXT_ERROR; |
193 | 0 | } |
194 | | |
195 | 0 | nxt_process_port_add(task, process, port); |
196 | |
|
197 | 0 | ret = nxt_port_socket_init(task, port, 0); |
198 | 0 | if (nxt_slow_path(ret != NXT_OK)) { |
199 | 0 | goto free_port; |
200 | 0 | } |
201 | | |
202 | 0 | tmp_mp = nxt_mp_create(1024, 128, 256, 32); |
203 | 0 | if (nxt_slow_path(tmp_mp == NULL)) { |
204 | 0 | ret = NXT_ERROR; |
205 | |
|
206 | 0 | goto close_port; |
207 | 0 | } |
208 | | |
209 | 0 | if (init->prefork) { |
210 | 0 | ret = init->prefork(task, process, tmp_mp); |
211 | 0 | if (nxt_slow_path(ret != NXT_OK)) { |
212 | 0 | goto free_mempool; |
213 | 0 | } |
214 | 0 | } |
215 | | |
216 | 0 | pid = nxt_process_create(task, process); |
217 | |
|
218 | 0 | switch (pid) { |
219 | | |
220 | 0 | case -1: |
221 | 0 | ret = NXT_ERROR; |
222 | 0 | break; |
223 | | |
224 | 0 | case 0: |
225 | | /* The child process: return to the event engine work queue loop. */ |
226 | |
|
227 | 0 | nxt_process_use(task, process, -1); |
228 | |
|
229 | 0 | ret = NXT_AGAIN; |
230 | 0 | break; |
231 | | |
232 | 0 | default: |
233 | | /* The parent process created a new process. */ |
234 | |
|
235 | 0 | nxt_process_use(task, process, -1); |
236 | |
|
237 | 0 | nxt_port_read_close(port); |
238 | 0 | nxt_port_write_enable(task, port); |
239 | |
|
240 | 0 | ret = NXT_OK; |
241 | 0 | break; |
242 | 0 | } |
243 | | |
244 | 0 | free_mempool: |
245 | |
|
246 | 0 | nxt_mp_destroy(tmp_mp); |
247 | |
|
248 | 0 | close_port: |
249 | |
|
250 | 0 | if (nxt_slow_path(ret == NXT_ERROR)) { |
251 | 0 | nxt_port_close(task, port); |
252 | 0 | } |
253 | |
|
254 | 0 | free_port: |
255 | |
|
256 | 0 | nxt_port_use(task, port, -1); |
257 | |
|
258 | 0 | return ret; |
259 | 0 | } |
260 | | |
261 | | |
262 | | static nxt_int_t |
263 | | nxt_process_child_fixup(nxt_task_t *task, nxt_process_t *process) |
264 | 0 | { |
265 | 0 | nxt_process_t *p; |
266 | 0 | nxt_runtime_t *rt; |
267 | 0 | nxt_process_init_t *init; |
268 | 0 | nxt_process_type_t ptype; |
269 | |
|
270 | 0 | init = nxt_process_init(process); |
271 | |
|
272 | 0 | nxt_ppid = nxt_pid; |
273 | |
|
274 | 0 | nxt_pid = getpid(); |
275 | |
|
276 | 0 | process->pid = nxt_pid; |
277 | 0 | process->isolated_pid = nxt_pid; |
278 | | |
279 | | /* Clean inherited cached thread tid. */ |
280 | 0 | task->thread->tid = 0; |
281 | |
|
282 | 0 | ptype = init->type; |
283 | |
|
284 | 0 | nxt_port_reset_next_id(); |
285 | |
|
286 | 0 | nxt_event_engine_thread_adopt(task->thread->engine); |
287 | |
|
288 | 0 | rt = task->thread->runtime; |
289 | | |
290 | | /* Remove not ready processes. */ |
291 | 0 | nxt_runtime_process_each(rt, p) { |
292 | |
|
293 | 0 | if (nxt_proc_keep_matrix[ptype][nxt_process_type(p)] == 0 |
294 | 0 | && p->pid != nxt_ppid) /* Always keep parent's port. */ |
295 | 0 | { |
296 | 0 | nxt_debug(task, "remove not required process %PI", p->pid); |
297 | |
|
298 | 0 | nxt_process_close_ports(task, p); |
299 | |
|
300 | 0 | continue; |
301 | 0 | } |
302 | | |
303 | 0 | if (p->state != NXT_PROCESS_STATE_READY) { |
304 | 0 | nxt_debug(task, "remove not ready process %PI", p->pid); |
305 | |
|
306 | 0 | nxt_process_close_ports(task, p); |
307 | |
|
308 | 0 | continue; |
309 | 0 | } |
310 | | |
311 | 0 | nxt_port_mmaps_destroy(&p->incoming, 0); |
312 | |
|
313 | 0 | } nxt_runtime_process_loop; |
314 | |
|
315 | 0 | if (init->siblings != NULL) { |
316 | 0 | nxt_queue_each(p, init->siblings, nxt_process_t, link) { |
317 | |
|
318 | 0 | nxt_debug(task, "remove sibling process %PI", p->pid); |
319 | |
|
320 | 0 | nxt_process_close_ports(task, p); |
321 | |
|
322 | 0 | } nxt_queue_loop; |
323 | 0 | } |
324 | |
|
325 | 0 | return NXT_OK; |
326 | 0 | } |
327 | | |
328 | | |
329 | | #if (NXT_HAVE_LINUX_NS) |
330 | | |
331 | | static nxt_int_t |
332 | | nxt_process_pipe_timer(nxt_fd_t fd, short event) |
333 | 0 | { |
334 | 0 | int ret; |
335 | 0 | sigset_t mask; |
336 | 0 | struct pollfd pfd; |
337 | |
|
338 | 0 | static const struct timespec ts = { .tv_sec = 5 }; |
339 | | |
340 | | /* |
341 | | * Temporarily block the signals we are handling, (except |
342 | | * for SIGINT & SIGTERM) so that ppoll(2) doesn't get |
343 | | * interrupted. After ppoll(2) returns, our old sigmask |
344 | | * will be back in effect and any pending signals will be |
345 | | * delivered. |
346 | | * |
347 | | * This is because while the kernel ppoll syscall updates |
348 | | * the struct timespec with the time remaining if it got |
349 | | * interrupted with EINTR, the glibc wrapper hides this |
350 | | * from us so we have no way of knowing how long to retry |
351 | | * the ppoll(2) for and if we just retry with the same |
352 | | * timeout we could find ourselves in an infinite loop. |
353 | | */ |
354 | 0 | pthread_sigmask(SIG_SETMASK, NULL, &mask); |
355 | 0 | sigdelset(&mask, SIGINT); |
356 | 0 | sigdelset(&mask, SIGTERM); |
357 | |
|
358 | 0 | pfd.fd = fd; |
359 | 0 | pfd.events = event; |
360 | |
|
361 | 0 | ret = ppoll(&pfd, 1, &ts, &mask); |
362 | 0 | if (ret <= 0 || (ret == 1 && pfd.revents & POLLERR)) { |
363 | 0 | return NXT_ERROR; |
364 | 0 | } |
365 | | |
366 | 0 | return NXT_OK; |
367 | 0 | } |
368 | | |
369 | | |
370 | | static nxt_int_t |
371 | | nxt_process_check_pid_status(const nxt_fd_t *gc_pipe) |
372 | 0 | { |
373 | 0 | int8_t status = -1; |
374 | 0 | ssize_t ret; |
375 | |
|
376 | 0 | close(gc_pipe[1]); |
377 | |
|
378 | 0 | ret = nxt_process_pipe_timer(gc_pipe[0], POLLIN); |
379 | 0 | if (ret == NXT_OK) { |
380 | 0 | read(gc_pipe[0], &status, sizeof(int8_t)); |
381 | 0 | } |
382 | |
|
383 | 0 | close(gc_pipe[0]); |
384 | |
|
385 | 0 | return status; |
386 | 0 | } |
387 | | |
388 | | |
389 | | static nxt_pid_t |
390 | | nxt_process_recv_pid(const nxt_fd_t *pid_pipe, const nxt_fd_t *gc_pipe) |
391 | 0 | { |
392 | 0 | int8_t status; |
393 | 0 | ssize_t ret; |
394 | 0 | nxt_pid_t pid; |
395 | |
|
396 | 0 | close(pid_pipe[1]); |
397 | 0 | close(gc_pipe[0]); |
398 | |
|
399 | 0 | status = 0; |
400 | |
|
401 | 0 | ret = nxt_process_pipe_timer(pid_pipe[0], POLLIN); |
402 | 0 | if (ret == NXT_OK) { |
403 | 0 | ret = read(pid_pipe[0], &pid, sizeof(nxt_pid_t)); |
404 | 0 | } |
405 | |
|
406 | 0 | if (ret <= 0) { |
407 | 0 | status = -1; |
408 | 0 | pid = -1; |
409 | 0 | } |
410 | |
|
411 | 0 | write(gc_pipe[1], &status, sizeof(int8_t)); |
412 | |
|
413 | 0 | close(pid_pipe[0]); |
414 | 0 | close(gc_pipe[1]); |
415 | |
|
416 | 0 | return pid; |
417 | 0 | } |
418 | | |
419 | | |
420 | | static void |
421 | | nxt_process_send_pid(const nxt_fd_t *pid_pipe, nxt_pid_t pid) |
422 | 0 | { |
423 | 0 | nxt_int_t ret; |
424 | |
|
425 | 0 | close(pid_pipe[0]); |
426 | |
|
427 | 0 | ret = nxt_process_pipe_timer(pid_pipe[1], POLLOUT); |
428 | 0 | if (ret == NXT_OK) { |
429 | 0 | write(pid_pipe[1], &pid, sizeof(nxt_pid_t)); |
430 | 0 | } |
431 | |
|
432 | 0 | close(pid_pipe[1]); |
433 | 0 | } |
434 | | |
435 | | |
436 | | static nxt_int_t |
437 | | nxt_process_unshare(nxt_task_t *task, nxt_process_t *process, |
438 | | nxt_fd_t *pid_pipe, nxt_fd_t *gc_pipe, |
439 | | nxt_bool_t use_pidns) |
440 | 0 | { |
441 | 0 | int ret; |
442 | 0 | nxt_pid_t pid; |
443 | |
|
444 | 0 | if (process->isolation.clone.flags == 0) { |
445 | 0 | return NXT_OK; |
446 | 0 | } |
447 | | |
448 | 0 | ret = unshare(process->isolation.clone.flags); |
449 | 0 | if (nxt_slow_path(ret == -1)) { |
450 | 0 | nxt_alert(task, "unshare() failed for %s %E", process->name, |
451 | 0 | nxt_errno); |
452 | |
|
453 | 0 | if (use_pidns) { |
454 | 0 | nxt_pipe_close(task, gc_pipe); |
455 | 0 | nxt_pipe_close(task, pid_pipe); |
456 | 0 | } |
457 | |
|
458 | 0 | return NXT_ERROR; |
459 | 0 | } |
460 | | |
461 | 0 | if (!use_pidns) { |
462 | 0 | return NXT_OK; |
463 | 0 | } |
464 | | |
465 | | /* |
466 | | * PID namespace requested. Employ a double fork(2) technique |
467 | | * so that the prototype process will be placed into the new |
468 | | * namespace and end up with PID 1 (as before with clone). |
469 | | */ |
470 | 0 | pid = fork(); |
471 | 0 | if (nxt_slow_path(pid < 0)) { |
472 | 0 | nxt_alert(task, "fork() failed for %s %E", process->name, nxt_errno); |
473 | 0 | nxt_pipe_close(task, gc_pipe); |
474 | 0 | nxt_pipe_close(task, pid_pipe); |
475 | |
|
476 | 0 | return NXT_ERROR; |
477 | |
|
478 | 0 | } else if (pid > 0) { |
479 | 0 | nxt_pipe_close(task, gc_pipe); |
480 | 0 | nxt_process_send_pid(pid_pipe, pid); |
481 | |
|
482 | 0 | _exit(EXIT_SUCCESS); |
483 | 0 | } |
484 | | |
485 | 0 | nxt_pipe_close(task, pid_pipe); |
486 | 0 | ret = nxt_process_check_pid_status(gc_pipe); |
487 | 0 | if (ret == -1) { |
488 | 0 | return NXT_ERROR; |
489 | 0 | } |
490 | | |
491 | 0 | return NXT_OK; |
492 | 0 | } |
493 | | |
494 | | |
495 | | static nxt_int_t |
496 | | nxt_process_init_pidns(nxt_task_t *task, const nxt_process_t *process, |
497 | | nxt_fd_t *pid_pipe, nxt_fd_t *gc_pipe, |
498 | | nxt_bool_t *use_pidns) |
499 | 0 | { |
500 | 0 | int ret; |
501 | |
|
502 | 0 | *use_pidns = 0; |
503 | |
|
504 | 0 | #if (NXT_HAVE_CLONE_NEWPID) |
505 | 0 | *use_pidns = nxt_is_pid_isolated(process); |
506 | 0 | #endif |
507 | |
|
508 | 0 | if (!*use_pidns) { |
509 | 0 | return NXT_OK; |
510 | 0 | } |
511 | | |
512 | 0 | ret = nxt_pipe_create(task, pid_pipe, 0, 0); |
513 | 0 | if (nxt_slow_path(ret == NXT_ERROR)) { |
514 | 0 | return NXT_ERROR; |
515 | 0 | } |
516 | | |
517 | 0 | ret = nxt_pipe_create(task, gc_pipe, 0, 0); |
518 | 0 | if (nxt_slow_path(ret == NXT_ERROR)) { |
519 | 0 | return NXT_ERROR; |
520 | 0 | } |
521 | | |
522 | 0 | #if (NXT_HAVE_PR_SET_CHILD_SUBREAPER) |
523 | 0 | ret = prctl(PR_SET_CHILD_SUBREAPER, 1, 0, 0, 0); |
524 | 0 | if (nxt_slow_path(ret == -1)) { |
525 | 0 | nxt_alert(task, "prctl(PR_SET_CHILD_SUBREAPER) failed for %s %E", |
526 | 0 | process->name, nxt_errno); |
527 | 0 | } |
528 | 0 | #endif |
529 | |
|
530 | 0 | return NXT_OK; |
531 | 0 | } |
532 | | |
533 | | #endif /* NXT_HAVE_LINUX_NS */ |
534 | | |
535 | | |
536 | | static nxt_pid_t |
537 | | nxt_process_create(nxt_task_t *task, nxt_process_t *process) |
538 | 0 | { |
539 | 0 | nxt_int_t ret; |
540 | 0 | nxt_pid_t pid; |
541 | 0 | nxt_runtime_t *rt; |
542 | |
|
543 | 0 | #if (NXT_HAVE_LINUX_NS) |
544 | 0 | nxt_fd_t pid_pipe[2], gc_pipe[2]; |
545 | 0 | nxt_bool_t use_pidns; |
546 | |
|
547 | 0 | ret = nxt_process_init_pidns(task, process, pid_pipe, gc_pipe, &use_pidns); |
548 | 0 | if (ret == NXT_ERROR) { |
549 | 0 | return -1; |
550 | 0 | } |
551 | 0 | #endif |
552 | | |
553 | 0 | pid = fork(); |
554 | 0 | if (nxt_slow_path(pid < 0)) { |
555 | 0 | nxt_alert(task, "fork() failed for %s %E", process->name, nxt_errno); |
556 | 0 | return pid; |
557 | 0 | } |
558 | | |
559 | 0 | if (pid == 0) { |
560 | | /* Child. */ |
561 | |
|
562 | 0 | #if (NXT_HAVE_LINUX_NS) |
563 | 0 | ret = nxt_process_unshare(task, process, pid_pipe, gc_pipe, use_pidns); |
564 | 0 | if (ret == NXT_ERROR) { |
565 | 0 | _exit(EXIT_FAILURE); |
566 | 0 | } |
567 | 0 | #endif |
568 | | |
569 | 0 | ret = nxt_process_child_fixup(task, process); |
570 | 0 | if (nxt_slow_path(ret != NXT_OK)) { |
571 | 0 | nxt_process_quit(task, 1); |
572 | 0 | return -1; |
573 | 0 | } |
574 | | |
575 | 0 | ret = nxt_process_setup(task, process); |
576 | 0 | if (nxt_slow_path(ret != NXT_OK)) { |
577 | 0 | nxt_process_quit(task, 1); |
578 | 0 | } |
579 | | |
580 | | /* |
581 | | * Explicitly return 0 to notice the caller function this is the child. |
582 | | * The caller must return to the event engine work queue loop. |
583 | | */ |
584 | 0 | return 0; |
585 | 0 | } |
586 | | |
587 | | /* Parent. */ |
588 | | |
589 | 0 | nxt_debug(task, "fork(%s): %PI", process->name, pid); |
590 | |
|
591 | 0 | #if (NXT_HAVE_LINUX_NS) |
592 | 0 | if (use_pidns) { |
593 | 0 | pid = nxt_process_recv_pid(pid_pipe, gc_pipe); |
594 | 0 | if (pid == -1) { |
595 | 0 | return pid; |
596 | 0 | } |
597 | 0 | } |
598 | 0 | #endif |
599 | | |
600 | 0 | process->pid = pid; |
601 | 0 | process->isolated_pid = pid; |
602 | |
|
603 | 0 | rt = task->thread->runtime; |
604 | |
|
605 | 0 | if (rt->is_pid_isolated) { |
606 | | /* |
607 | | * Do not register process in runtime with isolated pid. |
608 | | * Only global pid can be the key to avoid clash. |
609 | | */ |
610 | 0 | nxt_assert(!nxt_queue_is_empty(&process->ports)); |
611 | |
|
612 | 0 | nxt_port_use(task, nxt_process_port_first(process), 1); |
613 | |
|
614 | 0 | } else { |
615 | 0 | nxt_runtime_process_add(task, process); |
616 | 0 | } |
617 | |
|
618 | | #if (NXT_HAVE_CGROUP) |
619 | | ret = nxt_cgroup_proc_add(task, process); |
620 | | if (nxt_slow_path(ret != NXT_OK)) { |
621 | | nxt_alert(task, "cgroup: failed to add process %s to %s %E", |
622 | | process->name, process->isolation.cgroup.path, nxt_errno); |
623 | | nxt_cgroup_cleanup(task, process); |
624 | | kill(pid, SIGTERM); |
625 | | return -1; |
626 | | } |
627 | | #endif |
628 | |
|
629 | 0 | return pid; |
630 | 0 | } |
631 | | |
632 | | |
633 | | static nxt_int_t |
634 | | nxt_process_setup(nxt_task_t *task, nxt_process_t *process) |
635 | 0 | { |
636 | 0 | nxt_int_t ret; |
637 | 0 | nxt_thread_t *thread; |
638 | 0 | nxt_runtime_t *rt; |
639 | 0 | nxt_process_init_t *init; |
640 | 0 | nxt_event_engine_t *engine; |
641 | 0 | const nxt_event_interface_t *interface; |
642 | |
|
643 | 0 | init = nxt_process_init(process); |
644 | |
|
645 | 0 | nxt_debug(task, "%s setup", process->name); |
646 | |
|
647 | 0 | nxt_process_title(task, "unit: %s", process->name); |
648 | |
|
649 | 0 | thread = task->thread; |
650 | 0 | rt = thread->runtime; |
651 | |
|
652 | 0 | if (process->parent_port == rt->port_by_type[NXT_PROCESS_PROTOTYPE]) { |
653 | 0 | nxt_app_set_logs(); |
654 | 0 | } |
655 | |
|
656 | 0 | nxt_random_init(&thread->random); |
657 | |
|
658 | 0 | rt->type = init->type; |
659 | |
|
660 | 0 | engine = thread->engine; |
661 | | |
662 | | /* Update inherited main process event engine and signals processing. */ |
663 | 0 | engine->signals->sigev = init->signals; |
664 | |
|
665 | 0 | interface = nxt_service_get(rt->services, "engine", rt->engine); |
666 | 0 | if (nxt_slow_path(interface == NULL)) { |
667 | 0 | return NXT_ERROR; |
668 | 0 | } |
669 | | |
670 | 0 | if (nxt_event_engine_change(engine, interface, rt->batch) != NXT_OK) { |
671 | 0 | return NXT_ERROR; |
672 | 0 | } |
673 | | |
674 | 0 | ret = nxt_runtime_thread_pool_create(thread, rt, rt->auxiliary_threads, |
675 | 0 | 60000 * 1000000LL); |
676 | 0 | if (nxt_slow_path(ret != NXT_OK)) { |
677 | 0 | return NXT_ERROR; |
678 | 0 | } |
679 | | |
680 | 0 | nxt_port_read_close(process->parent_port); |
681 | 0 | nxt_port_write_enable(task, process->parent_port); |
682 | | |
683 | | /* |
684 | | * If the parent process is already isolated, rt->pid_isolation is already |
685 | | * set to 1 at this point. |
686 | | */ |
687 | 0 | if (nxt_is_pid_isolated(process)) { |
688 | 0 | rt->is_pid_isolated = 1; |
689 | 0 | } |
690 | |
|
691 | 0 | if (rt->is_pid_isolated |
692 | 0 | || process->parent_port != rt->port_by_type[NXT_PROCESS_MAIN]) |
693 | 0 | { |
694 | 0 | ret = nxt_process_whoami(task, process); |
695 | |
|
696 | 0 | } else { |
697 | 0 | ret = nxt_process_do_start(task, process); |
698 | 0 | } |
699 | |
|
700 | 0 | return ret; |
701 | 0 | } |
702 | | |
703 | | |
704 | | static nxt_int_t |
705 | | nxt_process_do_start(nxt_task_t *task, nxt_process_t *process) |
706 | 0 | { |
707 | 0 | nxt_int_t ret; |
708 | 0 | nxt_port_t *port; |
709 | 0 | nxt_process_init_t *init; |
710 | |
|
711 | 0 | nxt_runtime_process_add(task, process); |
712 | |
|
713 | 0 | init = nxt_process_init(process); |
714 | 0 | port = nxt_process_port_first(process); |
715 | |
|
716 | 0 | nxt_port_enable(task, port, init->port_handlers); |
717 | |
|
718 | 0 | ret = init->setup(task, process); |
719 | 0 | if (nxt_slow_path(ret != NXT_OK)) { |
720 | 0 | return NXT_ERROR; |
721 | 0 | } |
722 | | |
723 | 0 | switch (process->state) { |
724 | | |
725 | 0 | case NXT_PROCESS_STATE_CREATED: |
726 | 0 | ret = nxt_process_send_created(task, process); |
727 | 0 | break; |
728 | | |
729 | 0 | case NXT_PROCESS_STATE_READY: |
730 | 0 | ret = nxt_process_send_ready(task, process); |
731 | |
|
732 | 0 | if (nxt_slow_path(ret != NXT_OK)) { |
733 | 0 | break; |
734 | 0 | } |
735 | | |
736 | 0 | ret = init->start(task, &process->data); |
737 | |
|
738 | 0 | nxt_port_write_close(port); |
739 | |
|
740 | 0 | break; |
741 | | |
742 | 0 | default: |
743 | 0 | nxt_assert(0); |
744 | 0 | } |
745 | | |
746 | 0 | if (nxt_slow_path(ret != NXT_OK)) { |
747 | 0 | nxt_alert(task, "%s failed to start", process->name); |
748 | 0 | } |
749 | |
|
750 | 0 | return ret; |
751 | 0 | } |
752 | | |
753 | | |
754 | | static nxt_int_t |
755 | | nxt_process_whoami(nxt_task_t *task, nxt_process_t *process) |
756 | 0 | { |
757 | 0 | uint32_t stream; |
758 | 0 | nxt_fd_t fd; |
759 | 0 | nxt_buf_t *buf; |
760 | 0 | nxt_int_t ret; |
761 | 0 | nxt_port_t *my_port, *main_port; |
762 | 0 | nxt_runtime_t *rt; |
763 | |
|
764 | 0 | rt = task->thread->runtime; |
765 | |
|
766 | 0 | my_port = nxt_process_port_first(process); |
767 | 0 | main_port = rt->port_by_type[NXT_PROCESS_MAIN]; |
768 | |
|
769 | 0 | nxt_assert(my_port != NULL && main_port != NULL); |
770 | |
|
771 | 0 | nxt_port_enable(task, my_port, &nxt_process_whoami_port_handlers); |
772 | |
|
773 | 0 | buf = nxt_buf_mem_alloc(main_port->mem_pool, sizeof(nxt_pid_t), 0); |
774 | 0 | if (nxt_slow_path(buf == NULL)) { |
775 | 0 | return NXT_ERROR; |
776 | 0 | } |
777 | | |
778 | 0 | buf->mem.free = nxt_cpymem(buf->mem.free, &nxt_ppid, sizeof(nxt_pid_t)); |
779 | |
|
780 | 0 | stream = nxt_port_rpc_register_handler(task, my_port, |
781 | 0 | nxt_process_whoami_ok, |
782 | 0 | nxt_process_whoami_error, |
783 | 0 | main_port->pid, process); |
784 | 0 | if (nxt_slow_path(stream == 0)) { |
785 | 0 | nxt_mp_free(main_port->mem_pool, buf); |
786 | |
|
787 | 0 | return NXT_ERROR; |
788 | 0 | } |
789 | | |
790 | 0 | fd = (process->parent_port != main_port) ? my_port->pair[1] : -1; |
791 | |
|
792 | 0 | ret = nxt_port_socket_write(task, main_port, NXT_PORT_MSG_WHOAMI, |
793 | 0 | fd, stream, my_port->id, buf); |
794 | |
|
795 | 0 | if (nxt_slow_path(ret != NXT_OK)) { |
796 | 0 | nxt_alert(task, "%s failed to send WHOAMI message", process->name); |
797 | 0 | nxt_port_rpc_cancel(task, my_port, stream); |
798 | 0 | nxt_mp_free(main_port->mem_pool, buf); |
799 | |
|
800 | 0 | return NXT_ERROR; |
801 | 0 | } |
802 | | |
803 | 0 | return NXT_OK; |
804 | 0 | } |
805 | | |
806 | | |
807 | | static void |
808 | | nxt_process_whoami_ok(nxt_task_t *task, nxt_port_recv_msg_t *msg, void *data) |
809 | 0 | { |
810 | 0 | nxt_pid_t pid, isolated_pid; |
811 | 0 | nxt_buf_t *buf; |
812 | 0 | nxt_port_t *port; |
813 | 0 | nxt_process_t *process; |
814 | 0 | nxt_runtime_t *rt; |
815 | |
|
816 | 0 | process = data; |
817 | |
|
818 | 0 | buf = msg->buf; |
819 | |
|
820 | 0 | nxt_assert(nxt_buf_used_size(buf) == sizeof(nxt_pid_t)); |
821 | |
|
822 | 0 | nxt_memcpy(&pid, buf->mem.pos, sizeof(nxt_pid_t)); |
823 | |
|
824 | 0 | isolated_pid = nxt_pid; |
825 | |
|
826 | 0 | if (isolated_pid != pid) { |
827 | 0 | nxt_pid = pid; |
828 | 0 | process->pid = pid; |
829 | |
|
830 | 0 | nxt_process_port_each(process, port) { |
831 | 0 | port->pid = pid; |
832 | 0 | } nxt_process_port_loop; |
833 | 0 | } |
834 | |
|
835 | 0 | rt = task->thread->runtime; |
836 | |
|
837 | 0 | if (process->parent_port != rt->port_by_type[NXT_PROCESS_MAIN]) { |
838 | 0 | port = process->parent_port; |
839 | |
|
840 | 0 | (void) nxt_port_socket_write(task, port, NXT_PORT_MSG_PROCESS_CREATED, |
841 | 0 | -1, 0, 0, NULL); |
842 | |
|
843 | 0 | nxt_log(task, NXT_LOG_INFO, "%s started", process->name); |
844 | 0 | } |
845 | |
|
846 | 0 | if (nxt_slow_path(nxt_process_do_start(task, process) != NXT_OK)) { |
847 | 0 | nxt_process_quit(task, 1); |
848 | 0 | } |
849 | 0 | } |
850 | | |
851 | | |
852 | | static void |
853 | | nxt_process_whoami_error(nxt_task_t *task, nxt_port_recv_msg_t *msg, void *data) |
854 | 0 | { |
855 | 0 | nxt_alert(task, "WHOAMI error"); |
856 | |
|
857 | 0 | nxt_process_quit(task, 1); |
858 | 0 | } |
859 | | |
860 | | |
861 | | static nxt_int_t |
862 | | nxt_process_send_created(nxt_task_t *task, nxt_process_t *process) |
863 | 0 | { |
864 | 0 | uint32_t stream; |
865 | 0 | nxt_int_t ret; |
866 | 0 | nxt_port_t *my_port, *main_port; |
867 | 0 | nxt_runtime_t *rt; |
868 | |
|
869 | 0 | nxt_assert(process->state == NXT_PROCESS_STATE_CREATED); |
870 | |
|
871 | 0 | rt = task->thread->runtime; |
872 | |
|
873 | 0 | my_port = nxt_process_port_first(process); |
874 | 0 | main_port = rt->port_by_type[NXT_PROCESS_MAIN]; |
875 | |
|
876 | 0 | nxt_assert(my_port != NULL && main_port != NULL); |
877 | |
|
878 | 0 | stream = nxt_port_rpc_register_handler(task, my_port, |
879 | 0 | nxt_process_created_ok, |
880 | 0 | nxt_process_created_error, |
881 | 0 | main_port->pid, process); |
882 | |
|
883 | 0 | if (nxt_slow_path(stream == 0)) { |
884 | 0 | return NXT_ERROR; |
885 | 0 | } |
886 | | |
887 | 0 | ret = nxt_port_socket_write(task, main_port, NXT_PORT_MSG_PROCESS_CREATED, |
888 | 0 | -1, stream, my_port->id, NULL); |
889 | |
|
890 | 0 | if (nxt_slow_path(ret != NXT_OK)) { |
891 | 0 | nxt_alert(task, "%s failed to send CREATED message", process->name); |
892 | 0 | nxt_port_rpc_cancel(task, my_port, stream); |
893 | 0 | return NXT_ERROR; |
894 | 0 | } |
895 | | |
896 | 0 | nxt_debug(task, "%s created", process->name); |
897 | |
|
898 | 0 | return NXT_OK; |
899 | 0 | } |
900 | | |
901 | | |
902 | | static void |
903 | | nxt_process_created_ok(nxt_task_t *task, nxt_port_recv_msg_t *msg, void *data) |
904 | 0 | { |
905 | 0 | nxt_int_t ret; |
906 | 0 | nxt_process_t *process; |
907 | 0 | nxt_process_init_t *init; |
908 | |
|
909 | 0 | process = data; |
910 | |
|
911 | 0 | process->state = NXT_PROCESS_STATE_READY; |
912 | |
|
913 | 0 | init = nxt_process_init(process); |
914 | |
|
915 | 0 | ret = nxt_process_apply_creds(task, process); |
916 | 0 | if (nxt_slow_path(ret != NXT_OK)) { |
917 | 0 | goto fail; |
918 | 0 | } |
919 | | |
920 | 0 | nxt_log(task, NXT_LOG_INFO, "%s started", process->name); |
921 | |
|
922 | 0 | ret = nxt_process_send_ready(task, process); |
923 | 0 | if (nxt_slow_path(ret != NXT_OK)) { |
924 | 0 | goto fail; |
925 | 0 | } |
926 | | |
927 | 0 | ret = init->start(task, &process->data); |
928 | |
|
929 | 0 | if (nxt_process_type(process) != NXT_PROCESS_PROTOTYPE) { |
930 | 0 | nxt_port_write_close(nxt_process_port_first(process)); |
931 | 0 | } |
932 | |
|
933 | 0 | if (nxt_fast_path(ret == NXT_OK)) { |
934 | 0 | return; |
935 | 0 | } |
936 | | |
937 | 0 | fail: |
938 | 0 | nxt_process_quit(task, 1); |
939 | 0 | } |
940 | | |
941 | | |
942 | | static void |
943 | | nxt_process_created_error(nxt_task_t *task, nxt_port_recv_msg_t *msg, |
944 | | void *data) |
945 | 0 | { |
946 | 0 | nxt_process_t *process; |
947 | 0 | nxt_process_init_t *init; |
948 | |
|
949 | 0 | process = data; |
950 | 0 | init = nxt_process_init(process); |
951 | |
|
952 | 0 | nxt_alert(task, "%s failed to start", init->name); |
953 | |
|
954 | 0 | nxt_process_quit(task, 1); |
955 | 0 | } |
956 | | |
957 | | |
958 | | nxt_int_t |
959 | | nxt_process_core_setup(nxt_task_t *task, nxt_process_t *process) |
960 | 0 | { |
961 | 0 | nxt_int_t ret; |
962 | |
|
963 | 0 | ret = nxt_process_apply_creds(task, process); |
964 | 0 | if (nxt_slow_path(ret != NXT_OK)) { |
965 | 0 | return NXT_ERROR; |
966 | 0 | } |
967 | | |
968 | 0 | process->state = NXT_PROCESS_STATE_READY; |
969 | |
|
970 | 0 | return NXT_OK; |
971 | 0 | } |
972 | | |
973 | | |
974 | | nxt_int_t |
975 | | nxt_process_creds_set(nxt_task_t *task, nxt_process_t *process, nxt_str_t *user, |
976 | | nxt_str_t *group) |
977 | 0 | { |
978 | 0 | char *str; |
979 | |
|
980 | 0 | process->user_cred = nxt_mp_zalloc(process->mem_pool, |
981 | 0 | sizeof(nxt_credential_t)); |
982 | |
|
983 | 0 | if (nxt_slow_path(process->user_cred == NULL)) { |
984 | 0 | return NXT_ERROR; |
985 | 0 | } |
986 | | |
987 | 0 | str = nxt_mp_zalloc(process->mem_pool, user->length + 1); |
988 | 0 | if (nxt_slow_path(str == NULL)) { |
989 | 0 | return NXT_ERROR; |
990 | 0 | } |
991 | | |
992 | 0 | nxt_memcpy(str, user->start, user->length); |
993 | 0 | str[user->length] = '\0'; |
994 | |
|
995 | 0 | process->user_cred->user = str; |
996 | |
|
997 | 0 | if (group->start != NULL) { |
998 | 0 | str = nxt_mp_zalloc(process->mem_pool, group->length + 1); |
999 | 0 | if (nxt_slow_path(str == NULL)) { |
1000 | 0 | return NXT_ERROR; |
1001 | 0 | } |
1002 | | |
1003 | 0 | nxt_memcpy(str, group->start, group->length); |
1004 | 0 | str[group->length] = '\0'; |
1005 | |
|
1006 | 0 | } else { |
1007 | 0 | str = NULL; |
1008 | 0 | } |
1009 | | |
1010 | 0 | return nxt_credential_get(task, process->mem_pool, process->user_cred, str); |
1011 | 0 | } |
1012 | | |
1013 | | |
1014 | | nxt_int_t |
1015 | | nxt_process_apply_creds(nxt_task_t *task, nxt_process_t *process) |
1016 | 0 | { |
1017 | 0 | nxt_int_t ret, cap_setid; |
1018 | 0 | nxt_runtime_t *rt; |
1019 | |
|
1020 | 0 | rt = task->thread->runtime; |
1021 | |
|
1022 | 0 | cap_setid = rt->capabilities.setid; |
1023 | |
|
1024 | 0 | #if (NXT_HAVE_LINUX_NS && NXT_HAVE_CLONE_NEWUSER) |
1025 | 0 | if (!cap_setid |
1026 | 0 | && nxt_is_clone_flag_set(process->isolation.clone.flags, NEWUSER)) |
1027 | 0 | { |
1028 | 0 | cap_setid = 1; |
1029 | 0 | } |
1030 | 0 | #endif |
1031 | |
|
1032 | 0 | if (cap_setid) { |
1033 | 0 | ret = nxt_credential_setgids(task, process->user_cred); |
1034 | 0 | if (nxt_slow_path(ret != NXT_OK)) { |
1035 | 0 | return NXT_ERROR; |
1036 | 0 | } |
1037 | | |
1038 | 0 | ret = nxt_credential_setuid(task, process->user_cred); |
1039 | 0 | if (nxt_slow_path(ret != NXT_OK)) { |
1040 | 0 | return NXT_ERROR; |
1041 | 0 | } |
1042 | 0 | } |
1043 | | |
1044 | 0 | #if (NXT_HAVE_PR_SET_NO_NEW_PRIVS) |
1045 | 0 | if (nxt_slow_path(process->isolation.new_privs == 0 |
1046 | 0 | && prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) != 0)) |
1047 | 0 | { |
1048 | 0 | nxt_alert(task, "failed to set no_new_privs %E", nxt_errno); |
1049 | 0 | return NXT_ERROR; |
1050 | 0 | } |
1051 | 0 | #endif |
1052 | | |
1053 | 0 | return NXT_OK; |
1054 | 0 | } |
1055 | | |
1056 | | |
1057 | | static nxt_int_t |
1058 | | nxt_process_send_ready(nxt_task_t *task, nxt_process_t *process) |
1059 | 0 | { |
1060 | 0 | nxt_int_t ret; |
1061 | |
|
1062 | 0 | ret = nxt_port_socket_write(task, process->parent_port, |
1063 | 0 | NXT_PORT_MSG_PROCESS_READY, |
1064 | 0 | -1, process->stream, 0, NULL); |
1065 | |
|
1066 | 0 | if (nxt_slow_path(ret != NXT_OK)) { |
1067 | 0 | nxt_alert(task, "%s failed to send READY message", process->name); |
1068 | 0 | return NXT_ERROR; |
1069 | 0 | } |
1070 | | |
1071 | 0 | nxt_debug(task, "%s sent ready", process->name); |
1072 | |
|
1073 | 0 | return NXT_OK; |
1074 | 0 | } |
1075 | | |
1076 | | |
1077 | | /* |
1078 | | * Linux glibc 2.2 posix_spawn() is implemented via fork()/execve(). |
1079 | | * Linux glibc 2.4 posix_spawn() without file actions and spawn |
1080 | | * attributes uses vfork()/execve(). |
1081 | | * |
1082 | | * On FreeBSD 8.0 posix_spawn() is implemented via vfork()/execve(). |
1083 | | * |
1084 | | * Solaris 10: |
1085 | | * In the Solaris 10 OS, posix_spawn() is currently implemented using |
1086 | | * private-to-libc vfork(), execve(), and exit() functions. They are |
1087 | | * identical to regular vfork(), execve(), and exit() in functionality, |
1088 | | * but they are not exported from libc and therefore don't cause the |
1089 | | * deadlock-in-the-dynamic-linker problem that any multithreaded code |
1090 | | * outside of libc that calls vfork() can cause. |
1091 | | * |
1092 | | * On MacOSX 10.5 (Leoprad) and NetBSD 6.0 posix_spawn() is implemented |
1093 | | * as syscall. |
1094 | | */ |
1095 | | |
1096 | | nxt_pid_t |
1097 | | nxt_process_execute(nxt_task_t *task, char *name, char **argv, char **envp) |
1098 | 0 | { |
1099 | 0 | nxt_pid_t pid; |
1100 | |
|
1101 | 0 | nxt_debug(task, "posix_spawn(\"%s\")", name); |
1102 | |
|
1103 | 0 | if (posix_spawn(&pid, name, NULL, NULL, argv, envp) != 0) { |
1104 | 0 | nxt_alert(task, "posix_spawn(\"%s\") failed %E", name, nxt_errno); |
1105 | 0 | return -1; |
1106 | 0 | } |
1107 | | |
1108 | 0 | return pid; |
1109 | 0 | } |
1110 | | |
1111 | | |
1112 | | nxt_int_t |
1113 | | nxt_process_daemon(nxt_task_t *task) |
1114 | 0 | { |
1115 | 0 | nxt_fd_t fd; |
1116 | 0 | nxt_pid_t pid; |
1117 | 0 | const char *msg; |
1118 | |
|
1119 | 0 | fd = -1; |
1120 | | |
1121 | | /* |
1122 | | * fork() followed by a parent process's exit() detaches a child process |
1123 | | * from an init script or terminal shell process which has started the |
1124 | | * parent process and allows the child process to run in background. |
1125 | | */ |
1126 | |
|
1127 | 0 | pid = fork(); |
1128 | |
|
1129 | 0 | switch (pid) { |
1130 | | |
1131 | 0 | case -1: |
1132 | 0 | msg = "fork() failed %E"; |
1133 | 0 | goto fail; |
1134 | | |
1135 | 0 | case 0: |
1136 | | /* A child. */ |
1137 | 0 | break; |
1138 | | |
1139 | 0 | default: |
1140 | | /* A parent. */ |
1141 | 0 | nxt_debug(task, "fork(): %PI", pid); |
1142 | 0 | exit(0); |
1143 | 0 | nxt_unreachable(); |
1144 | 0 | } |
1145 | | |
1146 | 0 | nxt_pid = getpid(); |
1147 | | |
1148 | | /* Clean inherited cached thread tid. */ |
1149 | 0 | task->thread->tid = 0; |
1150 | |
|
1151 | 0 | nxt_debug(task, "daemon"); |
1152 | | |
1153 | | /* Detach from controlling terminal. */ |
1154 | |
|
1155 | 0 | if (setsid() == -1) { |
1156 | 0 | nxt_alert(task, "setsid() failed %E", nxt_errno); |
1157 | 0 | return NXT_ERROR; |
1158 | 0 | } |
1159 | | |
1160 | | /* |
1161 | | * Set a sefe umask to give at most 755/644 permissions on |
1162 | | * directories/files. |
1163 | | */ |
1164 | 0 | umask(0022); |
1165 | | |
1166 | | /* Redirect STDIN and STDOUT to the "/dev/null". */ |
1167 | |
|
1168 | 0 | fd = open("/dev/null", O_RDWR); |
1169 | 0 | if (fd == -1) { |
1170 | 0 | msg = "open(\"/dev/null\") failed %E"; |
1171 | 0 | goto fail; |
1172 | 0 | } |
1173 | | |
1174 | 0 | if (dup2(fd, STDIN_FILENO) == -1) { |
1175 | 0 | msg = "dup2(\"/dev/null\", STDIN) failed %E"; |
1176 | 0 | goto fail; |
1177 | 0 | } |
1178 | | |
1179 | 0 | if (dup2(fd, STDOUT_FILENO) == -1) { |
1180 | 0 | msg = "dup2(\"/dev/null\", STDOUT) failed %E"; |
1181 | 0 | goto fail; |
1182 | 0 | } |
1183 | | |
1184 | 0 | if (fd > STDERR_FILENO) { |
1185 | 0 | nxt_fd_close(fd); |
1186 | 0 | } |
1187 | |
|
1188 | 0 | return NXT_OK; |
1189 | | |
1190 | 0 | fail: |
1191 | |
|
1192 | 0 | nxt_alert(task, msg, nxt_errno); |
1193 | |
|
1194 | 0 | if (fd != -1) { |
1195 | 0 | nxt_fd_close(fd); |
1196 | 0 | } |
1197 | |
|
1198 | 0 | return NXT_ERROR; |
1199 | 0 | } |
1200 | | |
1201 | | |
1202 | | void |
1203 | | nxt_nanosleep(nxt_nsec_t ns) |
1204 | 0 | { |
1205 | 0 | struct timespec ts; |
1206 | |
|
1207 | 0 | ts.tv_sec = ns / 1000000000; |
1208 | 0 | ts.tv_nsec = ns % 1000000000; |
1209 | |
|
1210 | 0 | (void) nanosleep(&ts, NULL); |
1211 | 0 | } |
1212 | | |
1213 | | |
1214 | | void |
1215 | | nxt_process_port_add(nxt_task_t *task, nxt_process_t *process, nxt_port_t *port) |
1216 | 0 | { |
1217 | 0 | nxt_assert(port->process == NULL); |
1218 | |
|
1219 | 0 | port->process = process; |
1220 | 0 | nxt_queue_insert_tail(&process->ports, &port->link); |
1221 | |
|
1222 | 0 | nxt_process_use(task, process, 1); |
1223 | 0 | } |
1224 | | |
1225 | | |
1226 | | nxt_process_type_t |
1227 | | nxt_process_type(nxt_process_t *process) |
1228 | 0 | { |
1229 | 0 | return nxt_queue_is_empty(&process->ports) ? 0 : |
1230 | 0 | (nxt_process_port_first(process))->type; |
1231 | 0 | } |
1232 | | |
1233 | | |
1234 | | void |
1235 | | nxt_process_close_ports(nxt_task_t *task, nxt_process_t *process) |
1236 | 0 | { |
1237 | 0 | nxt_port_t *port; |
1238 | |
|
1239 | 0 | nxt_process_use(task, process, 1); |
1240 | |
|
1241 | 0 | nxt_process_port_each(process, port) { |
1242 | |
|
1243 | 0 | nxt_port_close(task, port); |
1244 | |
|
1245 | 0 | nxt_runtime_port_remove(task, port); |
1246 | |
|
1247 | 0 | } nxt_process_port_loop; |
1248 | |
|
1249 | 0 | nxt_process_use(task, process, -1); |
1250 | 0 | } |
1251 | | |
1252 | | |
1253 | | void |
1254 | | nxt_process_quit(nxt_task_t *task, nxt_uint_t exit_status) |
1255 | 0 | { |
1256 | 0 | nxt_queue_t *listen; |
1257 | 0 | nxt_queue_link_t *link, *next; |
1258 | 0 | nxt_listen_event_t *lev; |
1259 | |
|
1260 | 0 | nxt_debug(task, "close listen connections"); |
1261 | |
|
1262 | 0 | listen = &task->thread->engine->listen_connections; |
1263 | |
|
1264 | 0 | for (link = nxt_queue_first(listen); |
1265 | 0 | link != nxt_queue_tail(listen); |
1266 | 0 | link = next) |
1267 | 0 | { |
1268 | 0 | next = nxt_queue_next(link); |
1269 | 0 | lev = nxt_queue_link_data(link, nxt_listen_event_t, link); |
1270 | 0 | nxt_queue_remove(link); |
1271 | |
|
1272 | 0 | nxt_fd_event_close(task->thread->engine, &lev->socket); |
1273 | 0 | } |
1274 | |
|
1275 | 0 | nxt_runtime_quit(task, exit_status); |
1276 | 0 | } |