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