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