Annotation of embedaddon/php/sapi/fpm/fpm/fpm_children.c, revision 1.1
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);
! 159: exit(255);
! 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:
! 201: if (WEXITSTATUS(status) != 0) {
! 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>