Annotation of embedaddon/php/sapi/fpm/fpm/fpm_children.c, revision 1.1.1.2
1.1 misho 1:
2: /* $Id: fpm_children.c,v 1.32.2.2 2008/12/13 03:21:18 anight Exp $ */
3: /* (c) 2007,2008 Andrei Nigmatulin */
4:
5: #include "fpm_config.h"
6:
7: #include <sys/types.h>
8: #include <sys/wait.h>
9: #include <time.h>
10: #include <unistd.h>
11: #include <string.h>
12: #include <stdio.h>
13:
14: #include "fpm.h"
15: #include "fpm_children.h"
16: #include "fpm_signals.h"
17: #include "fpm_worker_pool.h"
18: #include "fpm_sockets.h"
19: #include "fpm_process_ctl.h"
20: #include "fpm_php.h"
21: #include "fpm_conf.h"
22: #include "fpm_cleanup.h"
23: #include "fpm_events.h"
24: #include "fpm_clock.h"
25: #include "fpm_stdio.h"
26: #include "fpm_unix.h"
27: #include "fpm_env.h"
28: #include "fpm_scoreboard.h"
29: #include "fpm_status.h"
30: #include "fpm_log.h"
31:
32: #include "zlog.h"
33:
34: static time_t *last_faults;
35: static int fault;
36:
37: static void fpm_children_cleanup(int which, void *arg) /* {{{ */
38: {
39: free(last_faults);
40: }
41: /* }}} */
42:
43: static struct fpm_child_s *fpm_child_alloc() /* {{{ */
44: {
45: struct fpm_child_s *ret;
46:
47: ret = malloc(sizeof(struct fpm_child_s));
48:
49: if (!ret) {
50: return 0;
51: }
52:
53: memset(ret, 0, sizeof(*ret));
54: ret->scoreboard_i = -1;
55: return ret;
56: }
57: /* }}} */
58:
59: static void fpm_child_free(struct fpm_child_s *child) /* {{{ */
60: {
61: free(child);
62: }
63: /* }}} */
64:
65: static void fpm_child_close(struct fpm_child_s *child, int in_event_loop) /* {{{ */
66: {
67: if (child->fd_stdout != -1) {
68: if (in_event_loop) {
69: fpm_event_fire(&child->ev_stdout);
70: }
71: if (child->fd_stdout != -1) {
72: close(child->fd_stdout);
73: }
74: }
75:
76: if (child->fd_stderr != -1) {
77: if (in_event_loop) {
78: fpm_event_fire(&child->ev_stderr);
79: }
80: if (child->fd_stderr != -1) {
81: close(child->fd_stderr);
82: }
83: }
84:
85: fpm_child_free(child);
86: }
87: /* }}} */
88:
89: static void fpm_child_link(struct fpm_child_s *child) /* {{{ */
90: {
91: struct fpm_worker_pool_s *wp = child->wp;
92:
93: ++wp->running_children;
94: ++fpm_globals.running_children;
95:
96: child->next = wp->children;
97: if (child->next) {
98: child->next->prev = child;
99: }
100: child->prev = 0;
101: wp->children = child;
102: }
103: /* }}} */
104:
105: static void fpm_child_unlink(struct fpm_child_s *child) /* {{{ */
106: {
107: --child->wp->running_children;
108: --fpm_globals.running_children;
109:
110: if (child->prev) {
111: child->prev->next = child->next;
112: } else {
113: child->wp->children = child->next;
114: }
115:
116: if (child->next) {
117: child->next->prev = child->prev;
118: }
119: }
120: /* }}} */
121:
122: static struct fpm_child_s *fpm_child_find(pid_t pid) /* {{{ */
123: {
124: struct fpm_worker_pool_s *wp;
125: struct fpm_child_s *child = 0;
126:
127: for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
128:
129: for (child = wp->children; child; child = child->next) {
130: if (child->pid == pid) {
131: break;
132: }
133: }
134:
135: if (child) break;
136: }
137:
138: if (!child) {
139: return 0;
140: }
141:
142: return child;
143: }
144: /* }}} */
145:
146: static void fpm_child_init(struct fpm_worker_pool_s *wp) /* {{{ */
147: {
148: fpm_globals.max_requests = wp->config->pm_max_requests;
149:
150: if (0 > fpm_stdio_init_child(wp) ||
151: 0 > fpm_log_init_child(wp) ||
152: 0 > fpm_status_init_child(wp) ||
153: 0 > fpm_unix_init_child(wp) ||
154: 0 > fpm_signals_init_child() ||
155: 0 > fpm_env_init_child(wp) ||
156: 0 > fpm_php_init_child(wp)) {
157:
158: zlog(ZLOG_ERROR, "[pool %s] child failed to initialize", wp->config->name);
1.1.1.2 ! misho 159: exit(FPM_EXIT_SOFTWARE);
1.1 misho 160: }
161: }
162: /* }}} */
163:
164: int fpm_children_free(struct fpm_child_s *child) /* {{{ */
165: {
166: struct fpm_child_s *next;
167:
168: for (; child; child = next) {
169: next = child->next;
170: fpm_child_close(child, 0 /* in_event_loop */);
171: }
172:
173: return 0;
174: }
175: /* }}} */
176:
177: void fpm_children_bury() /* {{{ */
178: {
179: int status;
180: pid_t pid;
181: struct fpm_child_s *child;
182:
183: while ( (pid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) {
184: char buf[128];
185: int severity = ZLOG_NOTICE;
186: int restart_child = 1;
187:
188: child = fpm_child_find(pid);
189:
190: if (WIFEXITED(status)) {
191:
192: snprintf(buf, sizeof(buf), "with code %d", WEXITSTATUS(status));
193:
194: /* if it's been killed because of dynamic process management
195: * don't restart it automaticaly
196: */
197: if (child && child->idle_kill) {
198: restart_child = 0;
199: }
200:
1.1.1.2 ! misho 201: if (WEXITSTATUS(status) != FPM_EXIT_OK) {
1.1 misho 202: severity = ZLOG_WARNING;
203: }
204:
205: } else if (WIFSIGNALED(status)) {
206: const char *signame = fpm_signal_names[WTERMSIG(status)];
207: const char *have_core = WCOREDUMP(status) ? " - core dumped" : "";
208:
209: if (signame == NULL) {
210: signame = "";
211: }
212:
213: snprintf(buf, sizeof(buf), "on signal %d (%s%s)", WTERMSIG(status), signame, have_core);
214:
215: /* if it's been killed because of dynamic process management
216: * don't restart it automaticaly
217: */
218: if (child && child->idle_kill && WTERMSIG(status) == SIGQUIT) {
219: restart_child = 0;
220: }
221:
222: if (WTERMSIG(status) != SIGQUIT) { /* possible request loss */
223: severity = ZLOG_WARNING;
224: }
225: } else if (WIFSTOPPED(status)) {
226:
227: zlog(ZLOG_NOTICE, "child %d stopped for tracing", (int) pid);
228:
229: if (child && child->tracer) {
230: child->tracer(child);
231: }
232:
233: continue;
234: }
235:
236: if (child) {
237: struct fpm_worker_pool_s *wp = child->wp;
238: struct timeval tv1, tv2;
239:
240: fpm_child_unlink(child);
241:
242: fpm_scoreboard_proc_free(wp->scoreboard, child->scoreboard_i);
243:
244: fpm_clock_get(&tv1);
245:
246: timersub(&tv1, &child->started, &tv2);
247:
248: if (restart_child) {
249: if (!fpm_pctl_can_spawn_children()) {
250: severity = ZLOG_DEBUG;
251: }
252: zlog(severity, "[pool %s] child %d exited %s after %ld.%06d seconds from start", child->wp->config->name, (int) pid, buf, tv2.tv_sec, (int) tv2.tv_usec);
253: } else {
254: zlog(ZLOG_DEBUG, "[pool %s] child %d has been killed by the process managment after %ld.%06d seconds from start", child->wp->config->name, (int) pid, tv2.tv_sec, (int) tv2.tv_usec);
255: }
256:
257: fpm_child_close(child, 1 /* in event_loop */);
258:
259: fpm_pctl_child_exited();
260:
261: if (last_faults && (WTERMSIG(status) == SIGSEGV || WTERMSIG(status) == SIGBUS)) {
262: time_t now = tv1.tv_sec;
263: int restart_condition = 1;
264: int i;
265:
266: last_faults[fault++] = now;
267:
268: if (fault == fpm_global_config.emergency_restart_threshold) {
269: fault = 0;
270: }
271:
272: for (i = 0; i < fpm_global_config.emergency_restart_threshold; i++) {
273: if (now - last_faults[i] > fpm_global_config.emergency_restart_interval) {
274: restart_condition = 0;
275: break;
276: }
277: }
278:
279: if (restart_condition) {
280:
281: zlog(ZLOG_WARNING, "failed processes threshold (%d in %d sec) is reached, initiating reload", fpm_global_config.emergency_restart_threshold, fpm_global_config.emergency_restart_interval);
282:
283: fpm_pctl(FPM_PCTL_STATE_RELOADING, FPM_PCTL_ACTION_SET);
284: }
285: }
286:
287: if (restart_child) {
288: fpm_children_make(wp, 1 /* in event loop */, 1, 0);
289:
290: if (fpm_globals.is_child) {
291: break;
292: }
293: }
294: } else {
295: zlog(ZLOG_ALERT, "oops, unknown child (%d) exited %s. Please open a bug report (https://bugs.php.net).", pid, buf);
296: }
297: }
298: }
299: /* }}} */
300:
301: static struct fpm_child_s *fpm_resources_prepare(struct fpm_worker_pool_s *wp) /* {{{ */
302: {
303: struct fpm_child_s *c;
304:
305: c = fpm_child_alloc();
306:
307: if (!c) {
308: zlog(ZLOG_ERROR, "[pool %s] unable to malloc new child", wp->config->name);
309: return 0;
310: }
311:
312: c->wp = wp;
313: c->fd_stdout = -1; c->fd_stderr = -1;
314:
315: if (0 > fpm_stdio_prepare_pipes(c)) {
316: fpm_child_free(c);
317: return 0;
318: }
319:
320: if (0 > fpm_scoreboard_proc_alloc(wp->scoreboard, &c->scoreboard_i)) {
321: fpm_stdio_discard_pipes(c);
322: fpm_child_free(c);
323: return 0;
324: }
325:
326: return c;
327: }
328: /* }}} */
329:
330: static void fpm_resources_discard(struct fpm_child_s *child) /* {{{ */
331: {
332: fpm_scoreboard_proc_free(child->wp->scoreboard, child->scoreboard_i);
333: fpm_stdio_discard_pipes(child);
334: fpm_child_free(child);
335: }
336: /* }}} */
337:
338: static void fpm_child_resources_use(struct fpm_child_s *child) /* {{{ */
339: {
340: struct fpm_worker_pool_s *wp;
341: for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
342: if (wp == child->wp) {
343: continue;
344: }
345: fpm_scoreboard_free(wp->scoreboard);
346: }
347:
348: fpm_scoreboard_child_use(child->wp->scoreboard, child->scoreboard_i, getpid());
349: fpm_stdio_child_use_pipes(child);
350: fpm_child_free(child);
351: }
352: /* }}} */
353:
354: static void fpm_parent_resources_use(struct fpm_child_s *child) /* {{{ */
355: {
356: fpm_stdio_parent_use_pipes(child);
357: fpm_child_link(child);
358: }
359: /* }}} */
360:
361: int fpm_children_make(struct fpm_worker_pool_s *wp, int in_event_loop, int nb_to_spawn, int is_debug) /* {{{ */
362: {
363: pid_t pid;
364: struct fpm_child_s *child;
365: int max;
366: static int warned = 0;
367:
368: if (wp->config->pm == PM_STYLE_DYNAMIC) {
369: if (!in_event_loop) { /* starting */
370: max = wp->config->pm_start_servers;
371: } else {
372: max = wp->running_children + nb_to_spawn;
373: }
374: } else if (wp->config->pm == PM_STYLE_ONDEMAND) {
375: if (!in_event_loop) { /* starting */
376: max = 0; /* do not create any child at startup */
377: } else {
378: max = wp->running_children + nb_to_spawn;
379: }
380: } else { /* PM_STYLE_STATIC */
381: max = wp->config->pm_max_children;
382: }
383:
384: /*
385: * fork children while:
386: * - fpm_pctl_can_spawn_children : FPM is running in a NORMAL state (aka not restart, stop or reload)
387: * - wp->running_children < max : there is less than the max process for the current pool
388: * - (fpm_global_config.process_max < 1 || fpm_globals.running_children < fpm_global_config.process_max):
389: * if fpm_global_config.process_max is set, FPM has not fork this number of processes (globaly)
390: */
391: while (fpm_pctl_can_spawn_children() && wp->running_children < max && (fpm_global_config.process_max < 1 || fpm_globals.running_children < fpm_global_config.process_max)) {
392:
393: warned = 0;
394: child = fpm_resources_prepare(wp);
395:
396: if (!child) {
397: return 2;
398: }
399:
400: pid = fork();
401:
402: switch (pid) {
403:
404: case 0 :
405: fpm_child_resources_use(child);
406: fpm_globals.is_child = 1;
407: fpm_child_init(wp);
408: return 0;
409:
410: case -1 :
411: zlog(ZLOG_SYSERROR, "fork() failed");
412:
413: fpm_resources_discard(child);
414: return 2;
415:
416: default :
417: child->pid = pid;
418: fpm_clock_get(&child->started);
419: fpm_parent_resources_use(child);
420:
421: zlog(is_debug ? ZLOG_DEBUG : ZLOG_NOTICE, "[pool %s] child %d started", wp->config->name, (int) pid);
422: }
423:
424: }
425:
426: if (!warned && fpm_global_config.process_max > 0 && fpm_globals.running_children >= fpm_global_config.process_max) {
427: warned = 1;
428: zlog(ZLOG_WARNING, "The maximum number of processes has been reached. Please review your configuration and consider raising 'process.max'");
429: }
430:
431: return 1; /* we are done */
432: }
433: /* }}} */
434:
435: int fpm_children_create_initial(struct fpm_worker_pool_s *wp) /* {{{ */
436: {
437: if (wp->config->pm == PM_STYLE_ONDEMAND) {
438: wp->ondemand_event = (struct fpm_event_s *)malloc(sizeof(struct fpm_event_s));
439:
440: if (!wp->ondemand_event) {
441: zlog(ZLOG_ERROR, "[pool %s] unable to malloc the ondemand socket event", wp->config->name);
442: // FIXME handle crash
443: return 1;
444: }
445:
446: memset(wp->ondemand_event, 0, sizeof(struct fpm_event_s));
447: fpm_event_set(wp->ondemand_event, wp->listening_socket, FPM_EV_READ | FPM_EV_EDGE, fpm_pctl_on_socket_accept, wp);
448: wp->socket_event_set = 1;
449: fpm_event_add(wp->ondemand_event, 0);
450:
451: return 1;
452: }
453: return fpm_children_make(wp, 0 /* not in event loop yet */, 0, 1);
454: }
455: /* }}} */
456:
457: int fpm_children_init_main() /* {{{ */
458: {
459: if (fpm_global_config.emergency_restart_threshold &&
460: fpm_global_config.emergency_restart_interval) {
461:
462: last_faults = malloc(sizeof(time_t) * fpm_global_config.emergency_restart_threshold);
463:
464: if (!last_faults) {
465: return -1;
466: }
467:
468: memset(last_faults, 0, sizeof(time_t) * fpm_global_config.emergency_restart_threshold);
469: }
470:
471: if (0 > fpm_cleanup_add(FPM_CLEANUP_ALL, fpm_children_cleanup, 0)) {
472: return -1;
473: }
474:
475: return 0;
476: }
477: /* }}} */
478:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>