0
|
1
|
|
2 /*
|
|
3 * Copyright (C) Igor Sysoev
|
644
|
4 * Copyright (C) Nginx, Inc.
|
0
|
5 */
|
|
6
|
|
7
|
|
8 #include <ngx_config.h>
|
|
9 #include <ngx_core.h>
|
|
10 #include <ngx_event.h>
|
|
11 #include <ngx_channel.h>
|
|
12
|
|
13
|
88
|
14 typedef struct {
|
586
|
15 int signo;
|
|
16 char *signame;
|
|
17 char *name;
|
|
18 void (*handler)(int signo);
|
88
|
19 } ngx_signal_t;
|
|
20
|
|
21
|
|
22
|
0
|
23 static void ngx_execute_proc(ngx_cycle_t *cycle, void *data);
|
88
|
24 static void ngx_signal_handler(int signo);
|
|
25 static void ngx_process_get_status(void);
|
0
|
26
|
2
|
27
|
|
28 int ngx_argc;
|
|
29 char **ngx_argv;
|
|
30 char **ngx_os_argv;
|
|
31
|
|
32 ngx_int_t ngx_process_slot;
|
|
33 ngx_socket_t ngx_channel;
|
|
34 ngx_int_t ngx_last_process;
|
|
35 ngx_process_t ngx_processes[NGX_MAX_PROCESSES];
|
0
|
36
|
|
37
|
88
|
38 ngx_signal_t signals[] = {
|
|
39 { ngx_signal_value(NGX_RECONFIGURE_SIGNAL),
|
|
40 "SIG" ngx_value(NGX_RECONFIGURE_SIGNAL),
|
482
|
41 "reload",
|
88
|
42 ngx_signal_handler },
|
|
43
|
|
44 { ngx_signal_value(NGX_REOPEN_SIGNAL),
|
|
45 "SIG" ngx_value(NGX_REOPEN_SIGNAL),
|
482
|
46 "reopen",
|
88
|
47 ngx_signal_handler },
|
|
48
|
|
49 { ngx_signal_value(NGX_NOACCEPT_SIGNAL),
|
|
50 "SIG" ngx_value(NGX_NOACCEPT_SIGNAL),
|
482
|
51 "",
|
88
|
52 ngx_signal_handler },
|
|
53
|
|
54 { ngx_signal_value(NGX_TERMINATE_SIGNAL),
|
|
55 "SIG" ngx_value(NGX_TERMINATE_SIGNAL),
|
482
|
56 "stop",
|
88
|
57 ngx_signal_handler },
|
|
58
|
|
59 { ngx_signal_value(NGX_SHUTDOWN_SIGNAL),
|
|
60 "SIG" ngx_value(NGX_SHUTDOWN_SIGNAL),
|
482
|
61 "quit",
|
88
|
62 ngx_signal_handler },
|
|
63
|
|
64 { ngx_signal_value(NGX_CHANGEBIN_SIGNAL),
|
|
65 "SIG" ngx_value(NGX_CHANGEBIN_SIGNAL),
|
482
|
66 "",
|
88
|
67 ngx_signal_handler },
|
|
68
|
482
|
69 { SIGALRM, "SIGALRM", "", ngx_signal_handler },
|
88
|
70
|
482
|
71 { SIGINT, "SIGINT", "", ngx_signal_handler },
|
88
|
72
|
482
|
73 { SIGIO, "SIGIO", "", ngx_signal_handler },
|
88
|
74
|
482
|
75 { SIGCHLD, "SIGCHLD", "", ngx_signal_handler },
|
88
|
76
|
518
|
77 { SIGSYS, "SIGSYS, SIG_IGN", "", SIG_IGN },
|
|
78
|
482
|
79 { SIGPIPE, "SIGPIPE, SIG_IGN", "", SIG_IGN },
|
88
|
80
|
482
|
81 { 0, NULL, "", NULL }
|
88
|
82 };
|
|
83
|
|
84
|
64
|
85 ngx_pid_t
|
|
86 ngx_spawn_process(ngx_cycle_t *cycle, ngx_spawn_proc_pt proc, void *data,
|
|
87 char *name, ngx_int_t respawn)
|
0
|
88 {
|
|
89 u_long on;
|
|
90 ngx_pid_t pid;
|
|
91 ngx_int_t s;
|
|
92
|
|
93 if (respawn >= 0) {
|
|
94 s = respawn;
|
|
95
|
|
96 } else {
|
|
97 for (s = 0; s < ngx_last_process; s++) {
|
|
98 if (ngx_processes[s].pid == -1) {
|
|
99 break;
|
|
100 }
|
|
101 }
|
|
102
|
|
103 if (s == NGX_MAX_PROCESSES) {
|
|
104 ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
|
|
105 "no more than %d processes can be spawned",
|
|
106 NGX_MAX_PROCESSES);
|
92
|
107 return NGX_INVALID_PID;
|
0
|
108 }
|
|
109 }
|
|
110
|
|
111
|
|
112 if (respawn != NGX_PROCESS_DETACHED) {
|
|
113
|
|
114 /* Solaris 9 still has no AF_LOCAL */
|
|
115
|
|
116 if (socketpair(AF_UNIX, SOCK_STREAM, 0, ngx_processes[s].channel) == -1)
|
|
117 {
|
|
118 ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
|
|
119 "socketpair() failed while spawning \"%s\"", name);
|
92
|
120 return NGX_INVALID_PID;
|
0
|
121 }
|
|
122
|
|
123 ngx_log_debug2(NGX_LOG_DEBUG_CORE, cycle->log, 0,
|
|
124 "channel %d:%d",
|
|
125 ngx_processes[s].channel[0],
|
|
126 ngx_processes[s].channel[1]);
|
|
127
|
|
128 if (ngx_nonblocking(ngx_processes[s].channel[0]) == -1) {
|
|
129 ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
|
|
130 ngx_nonblocking_n " failed while spawning \"%s\"",
|
|
131 name);
|
|
132 ngx_close_channel(ngx_processes[s].channel, cycle->log);
|
92
|
133 return NGX_INVALID_PID;
|
0
|
134 }
|
|
135
|
|
136 if (ngx_nonblocking(ngx_processes[s].channel[1]) == -1) {
|
|
137 ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
|
|
138 ngx_nonblocking_n " failed while spawning \"%s\"",
|
|
139 name);
|
|
140 ngx_close_channel(ngx_processes[s].channel, cycle->log);
|
92
|
141 return NGX_INVALID_PID;
|
0
|
142 }
|
|
143
|
|
144 on = 1;
|
|
145 if (ioctl(ngx_processes[s].channel[0], FIOASYNC, &on) == -1) {
|
|
146 ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
|
|
147 "ioctl(FIOASYNC) failed while spawning \"%s\"", name);
|
|
148 ngx_close_channel(ngx_processes[s].channel, cycle->log);
|
92
|
149 return NGX_INVALID_PID;
|
0
|
150 }
|
|
151
|
|
152 if (fcntl(ngx_processes[s].channel[0], F_SETOWN, ngx_pid) == -1) {
|
|
153 ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
|
|
154 "fcntl(F_SETOWN) failed while spawning \"%s\"", name);
|
|
155 ngx_close_channel(ngx_processes[s].channel, cycle->log);
|
92
|
156 return NGX_INVALID_PID;
|
0
|
157 }
|
|
158
|
|
159 if (fcntl(ngx_processes[s].channel[0], F_SETFD, FD_CLOEXEC) == -1) {
|
|
160 ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
|
|
161 "fcntl(FD_CLOEXEC) failed while spawning \"%s\"",
|
|
162 name);
|
|
163 ngx_close_channel(ngx_processes[s].channel, cycle->log);
|
92
|
164 return NGX_INVALID_PID;
|
0
|
165 }
|
|
166
|
|
167 if (fcntl(ngx_processes[s].channel[1], F_SETFD, FD_CLOEXEC) == -1) {
|
|
168 ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
|
|
169 "fcntl(FD_CLOEXEC) failed while spawning \"%s\"",
|
|
170 name);
|
|
171 ngx_close_channel(ngx_processes[s].channel, cycle->log);
|
92
|
172 return NGX_INVALID_PID;
|
0
|
173 }
|
|
174
|
|
175 ngx_channel = ngx_processes[s].channel[1];
|
|
176
|
|
177 } else {
|
|
178 ngx_processes[s].channel[0] = -1;
|
|
179 ngx_processes[s].channel[1] = -1;
|
|
180 }
|
|
181
|
|
182 ngx_process_slot = s;
|
|
183
|
|
184
|
|
185 pid = fork();
|
|
186
|
|
187 switch (pid) {
|
|
188
|
|
189 case -1:
|
|
190 ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
|
|
191 "fork() failed while spawning \"%s\"", name);
|
|
192 ngx_close_channel(ngx_processes[s].channel, cycle->log);
|
92
|
193 return NGX_INVALID_PID;
|
0
|
194
|
|
195 case 0:
|
|
196 ngx_pid = ngx_getpid();
|
|
197 proc(cycle, data);
|
|
198 break;
|
|
199
|
|
200 default:
|
|
201 break;
|
|
202 }
|
|
203
|
26
|
204 ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "start %s %P", name, pid);
|
0
|
205
|
|
206 ngx_processes[s].pid = pid;
|
|
207 ngx_processes[s].exited = 0;
|
|
208
|
|
209 if (respawn >= 0) {
|
|
210 return pid;
|
|
211 }
|
|
212
|
|
213 ngx_processes[s].proc = proc;
|
|
214 ngx_processes[s].data = data;
|
|
215 ngx_processes[s].name = name;
|
|
216 ngx_processes[s].exiting = 0;
|
|
217
|
|
218 switch (respawn) {
|
|
219
|
514
|
220 case NGX_PROCESS_NORESPAWN:
|
|
221 ngx_processes[s].respawn = 0;
|
|
222 ngx_processes[s].just_spawn = 0;
|
|
223 ngx_processes[s].detached = 0;
|
|
224 break;
|
|
225
|
|
226 case NGX_PROCESS_JUST_SPAWN:
|
|
227 ngx_processes[s].respawn = 0;
|
|
228 ngx_processes[s].just_spawn = 1;
|
|
229 ngx_processes[s].detached = 0;
|
|
230 break;
|
|
231
|
0
|
232 case NGX_PROCESS_RESPAWN:
|
|
233 ngx_processes[s].respawn = 1;
|
514
|
234 ngx_processes[s].just_spawn = 0;
|
0
|
235 ngx_processes[s].detached = 0;
|
|
236 break;
|
|
237
|
|
238 case NGX_PROCESS_JUST_RESPAWN:
|
|
239 ngx_processes[s].respawn = 1;
|
514
|
240 ngx_processes[s].just_spawn = 1;
|
0
|
241 ngx_processes[s].detached = 0;
|
|
242 break;
|
|
243
|
|
244 case NGX_PROCESS_DETACHED:
|
|
245 ngx_processes[s].respawn = 0;
|
514
|
246 ngx_processes[s].just_spawn = 0;
|
0
|
247 ngx_processes[s].detached = 1;
|
|
248 break;
|
|
249 }
|
|
250
|
|
251 if (s == ngx_last_process) {
|
|
252 ngx_last_process++;
|
|
253 }
|
|
254
|
|
255 return pid;
|
|
256 }
|
|
257
|
|
258
|
64
|
259 ngx_pid_t
|
|
260 ngx_execute(ngx_cycle_t *cycle, ngx_exec_ctx_t *ctx)
|
0
|
261 {
|
|
262 return ngx_spawn_process(cycle, ngx_execute_proc, ctx, ctx->name,
|
|
263 NGX_PROCESS_DETACHED);
|
|
264 }
|
|
265
|
|
266
|
64
|
267 static void
|
|
268 ngx_execute_proc(ngx_cycle_t *cycle, void *data)
|
0
|
269 {
|
|
270 ngx_exec_ctx_t *ctx = data;
|
|
271
|
|
272 if (execve(ctx->path, ctx->argv, ctx->envp) == -1) {
|
|
273 ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
|
|
274 "execve() failed while executing %s \"%s\"",
|
|
275 ctx->name, ctx->path);
|
|
276 }
|
|
277
|
|
278 exit(1);
|
|
279 }
|
|
280
|
|
281
|
88
|
282 ngx_int_t
|
|
283 ngx_init_signals(ngx_log_t *log)
|
|
284 {
|
|
285 ngx_signal_t *sig;
|
|
286 struct sigaction sa;
|
|
287
|
|
288 for (sig = signals; sig->signo != 0; sig++) {
|
|
289 ngx_memzero(&sa, sizeof(struct sigaction));
|
|
290 sa.sa_handler = sig->handler;
|
|
291 sigemptyset(&sa.sa_mask);
|
|
292 if (sigaction(sig->signo, &sa, NULL) == -1) {
|
|
293 ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,
|
|
294 "sigaction(%s) failed", sig->signame);
|
|
295 return NGX_ERROR;
|
|
296 }
|
|
297 }
|
|
298
|
|
299 return NGX_OK;
|
|
300 }
|
|
301
|
|
302
|
64
|
303 void
|
88
|
304 ngx_signal_handler(int signo)
|
|
305 {
|
|
306 char *action;
|
|
307 ngx_int_t ignore;
|
|
308 ngx_err_t err;
|
|
309 ngx_signal_t *sig;
|
|
310
|
|
311 ignore = 0;
|
|
312
|
|
313 err = ngx_errno;
|
|
314
|
|
315 for (sig = signals; sig->signo != 0; sig++) {
|
|
316 if (sig->signo == signo) {
|
|
317 break;
|
|
318 }
|
|
319 }
|
|
320
|
566
|
321 ngx_time_sigsafe_update();
|
88
|
322
|
|
323 action = "";
|
|
324
|
|
325 switch (ngx_process) {
|
|
326
|
|
327 case NGX_PROCESS_MASTER:
|
|
328 case NGX_PROCESS_SINGLE:
|
|
329 switch (signo) {
|
|
330
|
|
331 case ngx_signal_value(NGX_SHUTDOWN_SIGNAL):
|
|
332 ngx_quit = 1;
|
|
333 action = ", shutting down";
|
|
334 break;
|
|
335
|
|
336 case ngx_signal_value(NGX_TERMINATE_SIGNAL):
|
|
337 case SIGINT:
|
|
338 ngx_terminate = 1;
|
|
339 action = ", exiting";
|
|
340 break;
|
|
341
|
|
342 case ngx_signal_value(NGX_NOACCEPT_SIGNAL):
|
638
|
343 if (ngx_daemonized) {
|
|
344 ngx_noaccept = 1;
|
|
345 action = ", stop accepting connections";
|
|
346 }
|
88
|
347 break;
|
|
348
|
|
349 case ngx_signal_value(NGX_RECONFIGURE_SIGNAL):
|
|
350 ngx_reconfigure = 1;
|
|
351 action = ", reconfiguring";
|
|
352 break;
|
|
353
|
|
354 case ngx_signal_value(NGX_REOPEN_SIGNAL):
|
|
355 ngx_reopen = 1;
|
|
356 action = ", reopening logs";
|
|
357 break;
|
|
358
|
|
359 case ngx_signal_value(NGX_CHANGEBIN_SIGNAL):
|
|
360 if (getppid() > 1 || ngx_new_binary > 0) {
|
|
361
|
|
362 /*
|
|
363 * Ignore the signal in the new binary if its parent is
|
|
364 * not the init process, i.e. the old binary's process
|
236
|
365 * is still running. Or ignore the signal in the old binary's
|
88
|
366 * process if the new binary's process is already running.
|
|
367 */
|
|
368
|
|
369 action = ", ignoring";
|
|
370 ignore = 1;
|
|
371 break;
|
|
372 }
|
|
373
|
|
374 ngx_change_binary = 1;
|
|
375 action = ", changing binary";
|
|
376 break;
|
|
377
|
|
378 case SIGALRM:
|
518
|
379 ngx_sigalrm = 1;
|
88
|
380 break;
|
|
381
|
|
382 case SIGIO:
|
|
383 ngx_sigio = 1;
|
|
384 break;
|
|
385
|
|
386 case SIGCHLD:
|
|
387 ngx_reap = 1;
|
|
388 break;
|
|
389 }
|
|
390
|
|
391 break;
|
|
392
|
|
393 case NGX_PROCESS_WORKER:
|
552
|
394 case NGX_PROCESS_HELPER:
|
88
|
395 switch (signo) {
|
|
396
|
|
397 case ngx_signal_value(NGX_NOACCEPT_SIGNAL):
|
638
|
398 if (!ngx_daemonized) {
|
|
399 break;
|
|
400 }
|
88
|
401 ngx_debug_quit = 1;
|
|
402 case ngx_signal_value(NGX_SHUTDOWN_SIGNAL):
|
|
403 ngx_quit = 1;
|
|
404 action = ", shutting down";
|
|
405 break;
|
|
406
|
|
407 case ngx_signal_value(NGX_TERMINATE_SIGNAL):
|
|
408 case SIGINT:
|
|
409 ngx_terminate = 1;
|
|
410 action = ", exiting";
|
|
411 break;
|
|
412
|
|
413 case ngx_signal_value(NGX_REOPEN_SIGNAL):
|
|
414 ngx_reopen = 1;
|
|
415 action = ", reopening logs";
|
|
416 break;
|
|
417
|
|
418 case ngx_signal_value(NGX_RECONFIGURE_SIGNAL):
|
|
419 case ngx_signal_value(NGX_CHANGEBIN_SIGNAL):
|
|
420 case SIGIO:
|
|
421 action = ", ignoring";
|
|
422 break;
|
|
423 }
|
|
424
|
|
425 break;
|
|
426 }
|
|
427
|
|
428 ngx_log_error(NGX_LOG_NOTICE, ngx_cycle->log, 0,
|
|
429 "signal %d (%s) received%s", signo, sig->signame, action);
|
|
430
|
|
431 if (ignore) {
|
|
432 ngx_log_error(NGX_LOG_CRIT, ngx_cycle->log, 0,
|
|
433 "the changing binary signal is ignored: "
|
|
434 "you should shutdown or terminate "
|
|
435 "before either old or new binary's process");
|
|
436 }
|
|
437
|
|
438 if (signo == SIGCHLD) {
|
|
439 ngx_process_get_status();
|
|
440 }
|
|
441
|
|
442 ngx_set_errno(err);
|
|
443 }
|
|
444
|
|
445
|
|
446 static void
|
64
|
447 ngx_process_get_status(void)
|
0
|
448 {
|
|
449 int status;
|
|
450 char *process;
|
|
451 ngx_pid_t pid;
|
|
452 ngx_err_t err;
|
|
453 ngx_int_t i;
|
|
454 ngx_uint_t one;
|
26
|
455
|
0
|
456 one = 0;
|
|
457
|
|
458 for ( ;; ) {
|
|
459 pid = waitpid(-1, &status, WNOHANG);
|
|
460
|
|
461 if (pid == 0) {
|
|
462 return;
|
|
463 }
|
|
464
|
|
465 if (pid == -1) {
|
|
466 err = ngx_errno;
|
|
467
|
|
468 if (err == NGX_EINTR) {
|
|
469 continue;
|
|
470 }
|
|
471
|
|
472 if (err == NGX_ECHILD && one) {
|
|
473 return;
|
|
474 }
|
|
475
|
270
|
476 #if (NGX_SOLARIS || NGX_FREEBSD)
|
2
|
477
|
|
478 /*
|
|
479 * Solaris always calls the signal handler for each exited process
|
270
|
480 * despite waitpid() may be already called for this process.
|
|
481 *
|
|
482 * When several processes exit at the same time FreeBSD may
|
|
483 * erroneously call the signal handler for exited process
|
372
|
484 * despite waitpid() may be already called for this process.
|
2
|
485 */
|
|
486
|
|
487 if (err == NGX_ECHILD) {
|
604
|
488 ngx_log_error(NGX_LOG_INFO, ngx_cycle->log, err,
|
|
489 "waitpid() failed");
|
112
|
490 return;
|
2
|
491 }
|
|
492
|
|
493 #endif
|
|
494
|
604
|
495 ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, err,
|
|
496 "waitpid() failed");
|
0
|
497 return;
|
|
498 }
|
|
499
|
|
500
|
|
501 if (ngx_accept_mutex_ptr) {
|
|
502
|
|
503 /*
|
|
504 * unlock the accept mutex if the abnormally exited process
|
|
505 * held it
|
|
506 */
|
|
507
|
|
508 ngx_atomic_cmp_set(ngx_accept_mutex_ptr, pid, 0);
|
|
509 }
|
|
510
|
|
511
|
|
512 one = 1;
|
|
513 process = "unknown process";
|
|
514
|
|
515 for (i = 0; i < ngx_last_process; i++) {
|
|
516 if (ngx_processes[i].pid == pid) {
|
|
517 ngx_processes[i].status = status;
|
|
518 ngx_processes[i].exited = 1;
|
|
519 process = ngx_processes[i].name;
|
|
520 break;
|
|
521 }
|
|
522 }
|
|
523
|
|
524 if (WTERMSIG(status)) {
|
444
|
525 #ifdef WCOREDUMP
|
0
|
526 ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
|
10
|
527 "%s %P exited on signal %d%s",
|
0
|
528 process, pid, WTERMSIG(status),
|
|
529 WCOREDUMP(status) ? " (core dumped)" : "");
|
444
|
530 #else
|
|
531 ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
|
|
532 "%s %P exited on signal %d",
|
|
533 process, pid, WTERMSIG(status));
|
|
534 #endif
|
0
|
535
|
|
536 } else {
|
26
|
537 ngx_log_error(NGX_LOG_NOTICE, ngx_cycle->log, 0,
|
10
|
538 "%s %P exited with code %d",
|
0
|
539 process, pid, WEXITSTATUS(status));
|
|
540 }
|
|
541
|
|
542 if (WEXITSTATUS(status) == 2 && ngx_processes[i].respawn) {
|
|
543 ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
|
372
|
544 "%s %P exited with fatal code %d "
|
638
|
545 "and cannot be respawned",
|
372
|
546 process, pid, WEXITSTATUS(status));
|
0
|
547 ngx_processes[i].respawn = 0;
|
|
548 }
|
|
549 }
|
|
550 }
|
10
|
551
|
|
552
|
64
|
553 void
|
|
554 ngx_debug_point(void)
|
10
|
555 {
|
|
556 ngx_core_conf_t *ccf;
|
|
557
|
|
558 ccf = (ngx_core_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx,
|
|
559 ngx_core_module);
|
|
560
|
|
561 switch (ccf->debug_points) {
|
|
562
|
|
563 case NGX_DEBUG_POINTS_STOP:
|
|
564 raise(SIGSTOP);
|
|
565 break;
|
|
566
|
|
567 case NGX_DEBUG_POINTS_ABORT:
|
112
|
568 ngx_abort();
|
10
|
569 }
|
|
570 }
|
482
|
571
|
|
572
|
|
573 ngx_int_t
|
|
574 ngx_os_signal_process(ngx_cycle_t *cycle, char *name, ngx_int_t pid)
|
|
575 {
|
|
576 ngx_signal_t *sig;
|
|
577
|
|
578 for (sig = signals; sig->signo != 0; sig++) {
|
|
579 if (ngx_strcmp(name, sig->name) == 0) {
|
|
580 if (kill(pid, sig->signo) != -1) {
|
|
581 return 0;
|
|
582 }
|
|
583
|
|
584 ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
|
|
585 "kill(%P, %d) failed", pid, sig->signo);
|
|
586 }
|
|
587 }
|
|
588
|
|
589 return 1;
|
|
590 }
|