Back to index

php5  5.3.10
fpm_children.c
Go to the documentation of this file.
00001 
00002        /* $Id: fpm_children.c,v 1.32.2.2 2008/12/13 03:21:18 anight Exp $ */
00003        /* (c) 2007,2008 Andrei Nigmatulin */
00004 
00005 #include "fpm_config.h"
00006 
00007 #include <sys/types.h>
00008 #include <sys/wait.h>
00009 #include <time.h>
00010 #include <unistd.h>
00011 #include <string.h>
00012 #include <stdio.h>
00013 
00014 #include "fpm.h"
00015 #include "fpm_children.h"
00016 #include "fpm_signals.h"
00017 #include "fpm_worker_pool.h"
00018 #include "fpm_sockets.h"
00019 #include "fpm_process_ctl.h"
00020 #include "fpm_php.h"
00021 #include "fpm_conf.h"
00022 #include "fpm_cleanup.h"
00023 #include "fpm_events.h"
00024 #include "fpm_clock.h"
00025 #include "fpm_stdio.h"
00026 #include "fpm_unix.h"
00027 #include "fpm_env.h"
00028 #include "fpm_scoreboard.h"
00029 #include "fpm_status.h"
00030 #include "fpm_log.h"
00031 
00032 #include "zlog.h"
00033 
00034 static time_t *last_faults;
00035 static int fault;
00036 
00037 static void fpm_children_cleanup(int which, void *arg) /* {{{ */
00038 {
00039        free(last_faults);
00040 }
00041 /* }}} */
00042 
00043 static struct fpm_child_s *fpm_child_alloc() /* {{{ */
00044 {
00045        struct fpm_child_s *ret;
00046 
00047        ret = malloc(sizeof(struct fpm_child_s));
00048 
00049        if (!ret) {
00050               return 0;
00051        }
00052 
00053        memset(ret, 0, sizeof(*ret));
00054        ret->scoreboard_i = -1;
00055        return ret;
00056 }
00057 /* }}} */
00058 
00059 static void fpm_child_free(struct fpm_child_s *child) /* {{{ */
00060 {
00061        free(child);
00062 }
00063 /* }}} */
00064 
00065 static void fpm_child_close(struct fpm_child_s *child, int in_event_loop) /* {{{ */
00066 {
00067        if (child->fd_stdout != -1) {
00068               if (in_event_loop) {
00069                      fpm_event_fire(&child->ev_stdout);
00070               }
00071               if (child->fd_stdout != -1) {
00072                      close(child->fd_stdout);
00073               }
00074        }
00075 
00076        if (child->fd_stderr != -1) {
00077               if (in_event_loop) {
00078                      fpm_event_fire(&child->ev_stderr);
00079               }
00080               if (child->fd_stderr != -1) {
00081                      close(child->fd_stderr);
00082               }
00083        }
00084 
00085        fpm_child_free(child);
00086 }
00087 /* }}} */
00088 
00089 static void fpm_child_link(struct fpm_child_s *child) /* {{{ */
00090 {
00091        struct fpm_worker_pool_s *wp = child->wp;
00092 
00093        ++wp->running_children;
00094        ++fpm_globals.running_children;
00095 
00096        child->next = wp->children;
00097        if (child->next) {
00098               child->next->prev = child;
00099        }
00100        child->prev = 0;
00101        wp->children = child;
00102 }
00103 /* }}} */
00104 
00105 static void fpm_child_unlink(struct fpm_child_s *child) /* {{{ */
00106 {
00107        --child->wp->running_children;
00108        --fpm_globals.running_children;
00109 
00110        if (child->prev) {
00111               child->prev->next = child->next;
00112        } else {
00113               child->wp->children = child->next;
00114        }
00115 
00116        if (child->next) {
00117               child->next->prev = child->prev;
00118        }
00119 }
00120 /* }}} */
00121 
00122 static struct fpm_child_s *fpm_child_find(pid_t pid) /* {{{ */
00123 {
00124        struct fpm_worker_pool_s *wp;
00125        struct fpm_child_s *child = 0;
00126 
00127        for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
00128 
00129               for (child = wp->children; child; child = child->next) {
00130                      if (child->pid == pid) {
00131                             break;
00132                      }
00133               }
00134 
00135               if (child) break;
00136        }
00137 
00138        if (!child) {
00139               return 0;
00140        }
00141 
00142        return child;
00143 }
00144 /* }}} */
00145 
00146 static void fpm_child_init(struct fpm_worker_pool_s *wp) /* {{{ */
00147 {
00148        fpm_globals.max_requests = wp->config->pm_max_requests;
00149 
00150        if (0 > fpm_stdio_init_child(wp)  ||
00151            0 > fpm_log_init_child(wp)    ||
00152            0 > fpm_status_init_child(wp) ||
00153            0 > fpm_unix_init_child(wp)   ||
00154            0 > fpm_signals_init_child()  ||
00155            0 > fpm_env_init_child(wp)    ||
00156            0 > fpm_php_init_child(wp)) {
00157 
00158               zlog(ZLOG_ERROR, "[pool %s] child failed to initialize", wp->config->name);
00159               exit(255);
00160        }
00161 }
00162 /* }}} */
00163 
00164 int fpm_children_free(struct fpm_child_s *child) /* {{{ */
00165 {
00166        struct fpm_child_s *next;
00167 
00168        for (; child; child = next) {
00169               next = child->next;
00170               fpm_child_close(child, 0 /* in_event_loop */);
00171        }
00172 
00173        return 0;
00174 }
00175 /* }}} */
00176 
00177 void fpm_children_bury() /* {{{ */
00178 {
00179        int status;
00180        pid_t pid;
00181        struct fpm_child_s *child;
00182 
00183        while ( (pid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) {
00184               char buf[128];
00185               int severity = ZLOG_NOTICE;
00186               int restart_child = 1;
00187 
00188               child = fpm_child_find(pid);
00189 
00190               if (WIFEXITED(status)) {
00191 
00192                      snprintf(buf, sizeof(buf), "with code %d", WEXITSTATUS(status));
00193 
00194                      /* if it's been killed because of dynamic process management
00195                       * don't restart it automaticaly
00196                       */
00197                      if (child && child->idle_kill) {
00198                             restart_child = 0;
00199                      }
00200 
00201                      if (WEXITSTATUS(status) != 0) {
00202                             severity = ZLOG_WARNING;
00203                      }
00204 
00205               } else if (WIFSIGNALED(status)) {
00206                      const char *signame = fpm_signal_names[WTERMSIG(status)];
00207                      const char *have_core = WCOREDUMP(status) ? " - core dumped" : "";
00208 
00209                      if (signame == NULL) {
00210                             signame = "";
00211                      }
00212 
00213                      snprintf(buf, sizeof(buf), "on signal %d (%s%s)", WTERMSIG(status), signame, have_core);
00214 
00215                      /* if it's been killed because of dynamic process management
00216                       * don't restart it automaticaly
00217                       */
00218                      if (child && child->idle_kill && WTERMSIG(status) == SIGQUIT) {
00219                             restart_child = 0;
00220                      }
00221 
00222                      if (WTERMSIG(status) != SIGQUIT) { /* possible request loss */
00223                             severity = ZLOG_WARNING;
00224                      }
00225               } else if (WIFSTOPPED(status)) {
00226 
00227                      zlog(ZLOG_NOTICE, "child %d stopped for tracing", (int) pid);
00228 
00229                      if (child && child->tracer) {
00230                             child->tracer(child);
00231                      }
00232 
00233                      continue;
00234               }
00235 
00236               if (child) {
00237                      struct fpm_worker_pool_s *wp = child->wp;
00238                      struct timeval tv1, tv2;
00239 
00240                      fpm_child_unlink(child);
00241 
00242                      fpm_scoreboard_proc_free(wp->scoreboard, child->scoreboard_i);
00243 
00244                      fpm_clock_get(&tv1);
00245 
00246                      timersub(&tv1, &child->started, &tv2);
00247 
00248                      if (restart_child) {
00249                             if (!fpm_pctl_can_spawn_children()) {
00250                                    severity = ZLOG_DEBUG;
00251                             }
00252                             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);
00253                      } else {
00254                             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);
00255                      }
00256 
00257                      fpm_child_close(child, 1 /* in event_loop */);
00258 
00259                      fpm_pctl_child_exited();
00260 
00261                      if (last_faults && (WTERMSIG(status) == SIGSEGV || WTERMSIG(status) == SIGBUS)) {
00262                             time_t now = tv1.tv_sec;
00263                             int restart_condition = 1;
00264                             int i;
00265 
00266                             last_faults[fault++] = now;
00267 
00268                             if (fault == fpm_global_config.emergency_restart_threshold) {
00269                                    fault = 0;
00270                             }
00271 
00272                             for (i = 0; i < fpm_global_config.emergency_restart_threshold; i++) {
00273                                    if (now - last_faults[i] > fpm_global_config.emergency_restart_interval) {
00274                                           restart_condition = 0;
00275                                           break;
00276                                    }
00277                             }
00278 
00279                             if (restart_condition) {
00280 
00281                                    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);
00282 
00283                                    fpm_pctl(FPM_PCTL_STATE_RELOADING, FPM_PCTL_ACTION_SET);
00284                             }
00285                      }
00286 
00287                      if (restart_child) {
00288                             fpm_children_make(wp, 1 /* in event loop */, 1, 0);
00289 
00290                             if (fpm_globals.is_child) {
00291                                    break;
00292                             }
00293                      }
00294               } else {
00295                      zlog(ZLOG_ALERT, "oops, unknown child (%d) exited %s. Please open a bug report (https://bugs.php.net).", pid, buf);
00296               }
00297        }
00298 }
00299 /* }}} */
00300 
00301 static struct fpm_child_s *fpm_resources_prepare(struct fpm_worker_pool_s *wp) /* {{{ */
00302 {
00303        struct fpm_child_s *c;
00304 
00305        c = fpm_child_alloc();
00306 
00307        if (!c) {
00308               zlog(ZLOG_ERROR, "[pool %s] unable to malloc new child", wp->config->name);
00309               return 0;
00310        }
00311 
00312        c->wp = wp;
00313        c->fd_stdout = -1; c->fd_stderr = -1;
00314 
00315        if (0 > fpm_stdio_prepare_pipes(c)) {
00316               fpm_child_free(c);
00317               return 0;
00318        }
00319 
00320        if (0 > fpm_scoreboard_proc_alloc(wp->scoreboard, &c->scoreboard_i)) {
00321               fpm_stdio_discard_pipes(c);
00322               fpm_child_free(c);
00323               return 0;
00324        }
00325 
00326        return c;
00327 }
00328 /* }}} */
00329 
00330 static void fpm_resources_discard(struct fpm_child_s *child) /* {{{ */
00331 {
00332        fpm_scoreboard_proc_free(child->wp->scoreboard, child->scoreboard_i);
00333        fpm_stdio_discard_pipes(child);
00334        fpm_child_free(child);
00335 }
00336 /* }}} */
00337 
00338 static void fpm_child_resources_use(struct fpm_child_s *child) /* {{{ */
00339 {
00340        struct fpm_worker_pool_s *wp;
00341        for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
00342               if (wp == child->wp) {
00343                      continue;
00344               }
00345               fpm_scoreboard_free(wp->scoreboard);
00346        }
00347 
00348        fpm_scoreboard_child_use(child->wp->scoreboard, child->scoreboard_i, getpid());
00349        fpm_stdio_child_use_pipes(child);
00350        fpm_child_free(child);
00351 }
00352 /* }}} */
00353 
00354 static void fpm_parent_resources_use(struct fpm_child_s *child) /* {{{ */
00355 {
00356        fpm_stdio_parent_use_pipes(child);
00357        fpm_child_link(child);
00358 }
00359 /* }}} */
00360 
00361 int fpm_children_make(struct fpm_worker_pool_s *wp, int in_event_loop, int nb_to_spawn, int is_debug) /* {{{ */
00362 {
00363        pid_t pid;
00364        struct fpm_child_s *child;
00365        int max;
00366        static int warned = 0;
00367 
00368        if (wp->config->pm == PM_STYLE_DYNAMIC) {
00369               if (!in_event_loop) { /* starting */
00370                      max = wp->config->pm_start_servers;
00371               } else {
00372                      max = wp->running_children + nb_to_spawn;
00373               }
00374        } else if (wp->config->pm == PM_STYLE_ONDEMAND) {
00375               if (!in_event_loop) { /* starting */
00376                      max = 0; /* do not create any child at startup */
00377               } else {
00378                      max = wp->running_children + nb_to_spawn;
00379               }
00380        } else { /* PM_STYLE_STATIC */
00381               max = wp->config->pm_max_children;
00382        }
00383 
00384        /*
00385         * fork children while:
00386         *   - fpm_pctl_can_spawn_children : FPM is running in a NORMAL state (aka not restart, stop or reload)
00387         *   - wp->running_children < max  : there is less than the max process for the current pool
00388         *   - (fpm_global_config.process_max < 1 || fpm_globals.running_children < fpm_global_config.process_max):
00389         *     if fpm_global_config.process_max is set, FPM has not fork this number of processes (globaly)
00390         */
00391        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)) {
00392 
00393               warned = 0;
00394               child = fpm_resources_prepare(wp);
00395 
00396               if (!child) {
00397                      return 2;
00398               }
00399 
00400               pid = fork();
00401 
00402               switch (pid) {
00403 
00404                      case 0 :
00405                             fpm_child_resources_use(child);
00406                             fpm_globals.is_child = 1;
00407                             fpm_child_init(wp);
00408                             return 0;
00409 
00410                      case -1 :
00411                             zlog(ZLOG_SYSERROR, "fork() failed");
00412 
00413                             fpm_resources_discard(child);
00414                             return 2;
00415 
00416                      default :
00417                             child->pid = pid;
00418                             fpm_clock_get(&child->started);
00419                             fpm_parent_resources_use(child);
00420 
00421                             zlog(is_debug ? ZLOG_DEBUG : ZLOG_NOTICE, "[pool %s] child %d started", wp->config->name, (int) pid);
00422               }
00423 
00424        }
00425 
00426        if (!warned && fpm_global_config.process_max > 0 && fpm_globals.running_children >= fpm_global_config.process_max) {
00427               warned = 1;
00428               zlog(ZLOG_WARNING, "The maximum number of processes has been reached. Please review your configuration and consider raising 'process.max'");
00429        }
00430 
00431        return 1; /* we are done */
00432 }
00433 /* }}} */
00434 
00435 int fpm_children_create_initial(struct fpm_worker_pool_s *wp) /* {{{ */
00436 {
00437        if (wp->config->pm == PM_STYLE_ONDEMAND) {
00438               wp->ondemand_event = (struct fpm_event_s *)malloc(sizeof(struct fpm_event_s));
00439 
00440               if (!wp->ondemand_event) {
00441                      zlog(ZLOG_ERROR, "[pool %s] unable to malloc the ondemand socket event", wp->config->name);
00442                      // FIXME handle crash
00443                      return 1;
00444               }
00445 
00446               memset(wp->ondemand_event, 0, sizeof(struct fpm_event_s));
00447               fpm_event_set(wp->ondemand_event, wp->listening_socket, FPM_EV_READ | FPM_EV_EDGE, fpm_pctl_on_socket_accept, wp);
00448               wp->socket_event_set = 1;
00449               fpm_event_add(wp->ondemand_event, 0);
00450 
00451               return 1;
00452        }
00453        return fpm_children_make(wp, 0 /* not in event loop yet */, 0, 1);
00454 }
00455 /* }}} */
00456 
00457 int fpm_children_init_main() /* {{{ */
00458 {
00459        if (fpm_global_config.emergency_restart_threshold &&
00460               fpm_global_config.emergency_restart_interval) {
00461 
00462               last_faults = malloc(sizeof(time_t) * fpm_global_config.emergency_restart_threshold);
00463 
00464               if (!last_faults) {
00465                      return -1;
00466               }
00467 
00468               memset(last_faults, 0, sizeof(time_t) * fpm_global_config.emergency_restart_threshold);
00469        }
00470 
00471        if (0 > fpm_cleanup_add(FPM_CLEANUP_ALL, fpm_children_cleanup, 0)) {
00472               return -1;
00473        }
00474 
00475        return 0;
00476 }
00477 /* }}} */
00478