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