Back to index

php5  5.3.10
fpm_process_ctl.c
Go to the documentation of this file.
00001 
00002        /* $Id: fpm_process_ctl.c,v 1.19.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 <signal.h>
00009 #include <unistd.h>
00010 #include <stdlib.h>
00011 
00012 #include "fpm.h"
00013 #include "fpm_clock.h"
00014 #include "fpm_children.h"
00015 #include "fpm_signals.h"
00016 #include "fpm_events.h"
00017 #include "fpm_process_ctl.h"
00018 #include "fpm_cleanup.h"
00019 #include "fpm_request.h"
00020 #include "fpm_worker_pool.h"
00021 #include "fpm_scoreboard.h"
00022 #include "fpm_sockets.h"
00023 #include "zlog.h"
00024 
00025 
00026 static int fpm_state = FPM_PCTL_STATE_NORMAL;
00027 static int fpm_signal_sent = 0;
00028 
00029 
00030 static const char *fpm_state_names[] = {
00031        [FPM_PCTL_STATE_NORMAL] = "normal",
00032        [FPM_PCTL_STATE_RELOADING] = "reloading",
00033        [FPM_PCTL_STATE_TERMINATING] = "terminating",
00034        [FPM_PCTL_STATE_FINISHING] = "finishing"
00035 };
00036 
00037 static int saved_argc;
00038 static char **saved_argv;
00039 
00040 static void fpm_pctl_cleanup(int which, void *arg) /* {{{ */
00041 {
00042        int i;
00043        if (which != FPM_CLEANUP_PARENT_EXEC) {
00044               for (i = 0; i < saved_argc; i++) {
00045                      free(saved_argv[i]);
00046               }
00047               free(saved_argv);
00048        }
00049 }
00050 /* }}} */
00051 
00052 static struct fpm_event_s pctl_event;
00053 
00054 static void fpm_pctl_action(struct fpm_event_s *ev, short which, void *arg) /* {{{ */
00055 {
00056        fpm_pctl(FPM_PCTL_STATE_UNSPECIFIED, FPM_PCTL_ACTION_TIMEOUT);
00057 }
00058 /* }}} */
00059 
00060 static int fpm_pctl_timeout_set(int sec) /* {{{ */
00061 {
00062        fpm_event_set_timer(&pctl_event, 0, &fpm_pctl_action, NULL);
00063        fpm_event_add(&pctl_event, sec * 1000);
00064        return 0;
00065 }
00066 /* }}} */
00067 
00068 static void fpm_pctl_exit() /* {{{ */
00069 {
00070        zlog(ZLOG_NOTICE, "exiting, bye-bye!");
00071 
00072        fpm_conf_unlink_pid();
00073        fpm_cleanups_run(FPM_CLEANUP_PARENT_EXIT_MAIN);
00074        exit(0);
00075 }
00076 /* }}} */
00077 
00078 #define optional_arg(c) (saved_argc > c ? ", \"" : ""), (saved_argc > c ? saved_argv[c] : ""), (saved_argc > c ? "\"" : "")
00079 
00080 static void fpm_pctl_exec() /* {{{ */
00081 {
00082 
00083        zlog(ZLOG_NOTICE, "reloading: execvp(\"%s\", {\"%s\""
00084                      "%s%s%s" "%s%s%s" "%s%s%s" "%s%s%s" "%s%s%s"
00085                      "%s%s%s" "%s%s%s" "%s%s%s" "%s%s%s" "%s%s%s"
00086               "})",
00087               saved_argv[0], saved_argv[0],
00088               optional_arg(1),
00089               optional_arg(2),
00090               optional_arg(3),
00091               optional_arg(4),
00092               optional_arg(5),
00093               optional_arg(6),
00094               optional_arg(7),
00095               optional_arg(8),
00096               optional_arg(9),
00097               optional_arg(10)
00098        );
00099 
00100        fpm_cleanups_run(FPM_CLEANUP_PARENT_EXEC);
00101        execvp(saved_argv[0], saved_argv);
00102        zlog(ZLOG_SYSERROR, "failed to reload: execvp() failed");
00103        exit(1);
00104 }
00105 /* }}} */
00106 
00107 static void fpm_pctl_action_last() /* {{{ */
00108 {
00109        switch (fpm_state) {
00110               case FPM_PCTL_STATE_RELOADING:
00111                      fpm_pctl_exec();
00112                      break;
00113 
00114               case FPM_PCTL_STATE_FINISHING:
00115               case FPM_PCTL_STATE_TERMINATING:
00116                      fpm_pctl_exit();
00117                      break;
00118        }
00119 }
00120 /* }}} */
00121 
00122 int fpm_pctl_kill(pid_t pid, int how) /* {{{ */
00123 {
00124        int s = 0;
00125 
00126        switch (how) {
00127               case FPM_PCTL_TERM :
00128                      s = SIGTERM;
00129                      break;
00130               case FPM_PCTL_STOP :
00131                      s = SIGSTOP;
00132                      break;
00133               case FPM_PCTL_CONT :
00134                      s = SIGCONT;
00135                      break;
00136               case FPM_PCTL_QUIT :
00137                      s = SIGQUIT;
00138                      break;
00139               default :
00140                      break;
00141        }
00142        return kill(pid, s);
00143 }
00144 /* }}} */
00145 
00146 void fpm_pctl_kill_all(int signo) /* {{{ */
00147 {
00148        struct fpm_worker_pool_s *wp;
00149        int alive_children = 0;
00150 
00151        for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
00152               struct fpm_child_s *child;
00153 
00154               for (child = wp->children; child; child = child->next) {
00155                      int res = kill(child->pid, signo);
00156 
00157                      zlog(ZLOG_DEBUG, "[pool %s] sending signal %d %s to child %d",
00158                             child->wp->config->name, signo,
00159                             fpm_signal_names[signo] ? fpm_signal_names[signo] : "", (int) child->pid);
00160 
00161                      if (res == 0) {
00162                             ++alive_children;
00163                      }
00164               }
00165        }
00166 
00167        if (alive_children) {
00168               zlog(ZLOG_DEBUG, "%d child(ren) still alive", alive_children);
00169        }
00170 }
00171 /* }}} */
00172 
00173 static void fpm_pctl_action_next() /* {{{ */
00174 {
00175        int sig, timeout;
00176 
00177        if (!fpm_globals.running_children) {
00178               fpm_pctl_action_last();
00179        }
00180 
00181        if (fpm_signal_sent == 0) {
00182               if (fpm_state == FPM_PCTL_STATE_TERMINATING) {
00183                      sig = SIGTERM;
00184               } else {
00185                      sig = SIGQUIT;
00186               }
00187               timeout = fpm_global_config.process_control_timeout;
00188        } else {
00189               if (fpm_signal_sent == SIGQUIT) {
00190                      sig = SIGTERM;
00191               } else {
00192                      sig = SIGKILL;
00193               }
00194               timeout = 1;
00195        }
00196 
00197        fpm_pctl_kill_all(sig);
00198        fpm_signal_sent = sig;
00199        fpm_pctl_timeout_set(timeout);
00200 }
00201 /* }}} */
00202 
00203 void fpm_pctl(int new_state, int action) /* {{{ */
00204 {
00205        switch (action) {
00206               case FPM_PCTL_ACTION_SET :
00207                      if (fpm_state == new_state) { /* already in progress - just ignore duplicate signal */
00208                             return;
00209                      }
00210 
00211                      switch (fpm_state) { /* check which states can be overridden */
00212                             case FPM_PCTL_STATE_NORMAL :
00213                                    /* 'normal' can be overridden by any other state */
00214                                    break;
00215                             case FPM_PCTL_STATE_RELOADING :
00216                                    /* 'reloading' can be overridden by 'finishing' */
00217                                    if (new_state == FPM_PCTL_STATE_FINISHING) break;
00218                             case FPM_PCTL_STATE_FINISHING :
00219                                    /* 'reloading' and 'finishing' can be overridden by 'terminating' */
00220                                    if (new_state == FPM_PCTL_STATE_TERMINATING) break;
00221                             case FPM_PCTL_STATE_TERMINATING :
00222                                    /* nothing can override 'terminating' state */
00223                                    zlog(ZLOG_DEBUG, "not switching to '%s' state, because already in '%s' state",
00224                                           fpm_state_names[new_state], fpm_state_names[fpm_state]);
00225                                    return;
00226                      }
00227 
00228                      fpm_signal_sent = 0;
00229                      fpm_state = new_state;
00230 
00231                      zlog(ZLOG_DEBUG, "switching to '%s' state", fpm_state_names[fpm_state]);
00232                      /* fall down */
00233 
00234               case FPM_PCTL_ACTION_TIMEOUT :
00235                      fpm_pctl_action_next();
00236                      break;
00237               case FPM_PCTL_ACTION_LAST_CHILD_EXITED :
00238                      fpm_pctl_action_last();
00239                      break;
00240 
00241        }
00242 }
00243 /* }}} */
00244 
00245 int fpm_pctl_can_spawn_children() /* {{{ */
00246 {
00247        return fpm_state == FPM_PCTL_STATE_NORMAL;
00248 }
00249 /* }}} */
00250 
00251 int fpm_pctl_child_exited() /* {{{ */
00252 {
00253        if (fpm_state == FPM_PCTL_STATE_NORMAL) {
00254               return 0;
00255        }
00256 
00257        if (!fpm_globals.running_children) {
00258               fpm_pctl(FPM_PCTL_STATE_UNSPECIFIED, FPM_PCTL_ACTION_LAST_CHILD_EXITED);
00259        }
00260        return 0;
00261 }
00262 /* }}} */
00263 
00264 int fpm_pctl_init_main() /* {{{ */
00265 {
00266        int i;
00267 
00268        saved_argc = fpm_globals.argc;
00269        saved_argv = malloc(sizeof(char *) * (saved_argc + 1));
00270 
00271        if (!saved_argv) {
00272               return -1;
00273        }
00274 
00275        for (i = 0; i < saved_argc; i++) {
00276               saved_argv[i] = strdup(fpm_globals.argv[i]);
00277 
00278               if (!saved_argv[i]) {
00279                      return -1;
00280               }
00281        }
00282 
00283        saved_argv[i] = 0;
00284 
00285        if (0 > fpm_cleanup_add(FPM_CLEANUP_ALL, fpm_pctl_cleanup, 0)) {
00286               return -1;
00287        }
00288        return 0;
00289 }
00290 /* }}} */
00291 
00292 static void fpm_pctl_check_request_timeout(struct timeval *now) /* {{{ */
00293 {
00294        struct fpm_worker_pool_s *wp;
00295 
00296        for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
00297               int terminate_timeout = wp->config->request_terminate_timeout;
00298               int slowlog_timeout = wp->config->request_slowlog_timeout;
00299               struct fpm_child_s *child;
00300 
00301               if (terminate_timeout || slowlog_timeout) {
00302                      for (child = wp->children; child; child = child->next) {
00303                             fpm_request_check_timed_out(child, now, terminate_timeout, slowlog_timeout);
00304                      }
00305               }
00306        }
00307 }
00308 /* }}} */
00309 
00310 static void fpm_pctl_perform_idle_server_maintenance(struct timeval *now) /* {{{ */
00311 {
00312        struct fpm_worker_pool_s *wp;
00313 
00314        for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
00315               struct fpm_child_s *child;
00316               struct fpm_child_s *last_idle_child = NULL;
00317               int idle = 0;
00318               int active = 0;
00319               int children_to_fork;
00320               unsigned cur_lq = 0;
00321 
00322               if (wp->config == NULL) continue;
00323 
00324               for (child = wp->children; child; child = child->next) {
00325                      if (fpm_request_is_idle(child)) {
00326                             if (last_idle_child == NULL) {
00327                                    last_idle_child = child;
00328                             } else {
00329                                    if (timercmp(&child->started, &last_idle_child->started, <)) {
00330                                           last_idle_child = child;
00331                                    }
00332                             }
00333                             idle++;
00334                      } else {
00335                             active++;
00336                      }
00337               }
00338 
00339               /* update status structure for all PMs */
00340               if (wp->listen_address_domain == FPM_AF_INET) {
00341                      if (0 > fpm_socket_get_listening_queue(wp->listening_socket, &cur_lq, NULL)) {
00342                             cur_lq = 0;
00343 #if 0
00344                      } else {
00345                             if (cur_lq > 0) {
00346                                    if (!wp->warn_lq) {
00347                                           zlog(ZLOG_WARNING, "[pool %s] listening queue is not empty, #%d requests are waiting to be served, consider raising pm.max_children setting (%d)", wp->config->name, cur_lq, wp->config->pm_max_children);
00348                                           wp->warn_lq = 1;
00349                                    }
00350                             } else {
00351                                    wp->warn_lq = 0;
00352                             }
00353 #endif
00354                      }
00355               }
00356               fpm_scoreboard_update(idle, active, cur_lq, -1, -1, -1, FPM_SCOREBOARD_ACTION_SET, wp->scoreboard);
00357 
00358               /* this is specific to PM_STYLE_ONDEMAND */
00359               if (wp->config->pm == PM_STYLE_ONDEMAND) {
00360                      struct timeval last, now;
00361 
00362                      zlog(ZLOG_DEBUG, "[pool %s] currently %d active children, %d spare children", wp->config->name, active, idle);
00363 
00364                      if (!last_idle_child) continue;
00365 
00366                      fpm_request_last_activity(last_idle_child, &last);
00367                      fpm_clock_get(&now);
00368                      if (last.tv_sec < now.tv_sec - wp->config->pm_process_idle_timeout) {
00369                             last_idle_child->idle_kill = 1;
00370                             fpm_pctl_kill(last_idle_child->pid, FPM_PCTL_QUIT);
00371                      }
00372 
00373                      continue;
00374               }
00375 
00376               /* the rest is only used by PM_STYLE_DYNAMIC */
00377               if (wp->config->pm != PM_STYLE_DYNAMIC) continue;
00378 
00379               zlog(ZLOG_DEBUG, "[pool %s] currently %d active children, %d spare children, %d running children. Spawning rate %d", wp->config->name, active, idle, wp->running_children, wp->idle_spawn_rate);
00380 
00381               if (idle > wp->config->pm_max_spare_servers && last_idle_child) {
00382                      last_idle_child->idle_kill = 1;
00383                      fpm_pctl_kill(last_idle_child->pid, FPM_PCTL_QUIT);
00384                      wp->idle_spawn_rate = 1;
00385                      continue;
00386               }
00387 
00388               if (idle < wp->config->pm_min_spare_servers) {
00389                      if (wp->running_children >= wp->config->pm_max_children) {
00390                             if (!wp->warn_max_children) {
00391                                    fpm_scoreboard_update(0, 0, 0, 0, 0, 1, FPM_SCOREBOARD_ACTION_INC, wp->scoreboard);
00392                                    zlog(ZLOG_WARNING, "[pool %s] server reached pm.max_children setting (%d), consider raising it", wp->config->name, wp->config->pm_max_children);
00393                                    wp->warn_max_children = 1;
00394                             }
00395                             wp->idle_spawn_rate = 1;
00396                             continue;
00397                      }
00398 
00399                      if (wp->idle_spawn_rate >= 8) {
00400                             zlog(ZLOG_WARNING, "[pool %s] seems busy (you may need to increase pm.start_servers, or pm.min/max_spare_servers), spawning %d children, there are %d idle, and %d total children", wp->config->name, wp->idle_spawn_rate, idle, wp->running_children);
00401                      }
00402 
00403                      /* compute the number of idle process to spawn */
00404                      children_to_fork = MIN(wp->idle_spawn_rate, wp->config->pm_min_spare_servers - idle);
00405 
00406                      /* get sure it won't exceed max_children */
00407                      children_to_fork = MIN(children_to_fork, wp->config->pm_max_children - wp->running_children);
00408                      if (children_to_fork <= 0) {
00409                             if (!wp->warn_max_children) {
00410                                    fpm_scoreboard_update(0, 0, 0, 0, 0, 1, FPM_SCOREBOARD_ACTION_INC, wp->scoreboard);
00411                                    zlog(ZLOG_WARNING, "[pool %s] server reached pm.max_children setting (%d), consider raising it", wp->config->name, wp->config->pm_max_children);
00412                                    wp->warn_max_children = 1;
00413                             }
00414                             wp->idle_spawn_rate = 1;
00415                             continue;
00416                      }
00417                      wp->warn_max_children = 0;
00418 
00419                      fpm_children_make(wp, 1, children_to_fork, 1);
00420 
00421                      /* if it's a child, stop here without creating the next event
00422                       * this event is reserved to the master process
00423                       */
00424                      if (fpm_globals.is_child) {
00425                             return;
00426                      }
00427 
00428                      zlog(ZLOG_DEBUG, "[pool %s] %d child(ren) have been created dynamically", wp->config->name, children_to_fork);  
00429 
00430                      /* Double the spawn rate for the next iteration */
00431                      if (wp->idle_spawn_rate < FPM_MAX_SPAWN_RATE) {
00432                             wp->idle_spawn_rate *= 2;
00433                      }
00434                      continue;
00435               }
00436               wp->idle_spawn_rate = 1;
00437        }
00438 }
00439 /* }}} */
00440 
00441 void fpm_pctl_heartbeat(struct fpm_event_s *ev, short which, void *arg) /* {{{ */
00442 {
00443        static struct fpm_event_s heartbeat;
00444        struct timeval now;
00445 
00446        if (fpm_globals.parent_pid != getpid()) {
00447               return; /* sanity check */
00448        }
00449 
00450        if (which == FPM_EV_TIMEOUT) {
00451               fpm_clock_get(&now);
00452               fpm_pctl_check_request_timeout(&now);
00453               return;
00454        }
00455 
00456        /* ensure heartbeat is not lower than FPM_PCTL_MIN_HEARTBEAT */
00457        fpm_globals.heartbeat = MAX(fpm_globals.heartbeat, FPM_PCTL_MIN_HEARTBEAT);
00458 
00459        /* first call without setting to initialize the timer */
00460        zlog(ZLOG_DEBUG, "heartbeat have been set up with a timeout of %dms", fpm_globals.heartbeat);
00461        fpm_event_set_timer(&heartbeat, FPM_EV_PERSIST, &fpm_pctl_heartbeat, NULL);
00462        fpm_event_add(&heartbeat, fpm_globals.heartbeat);
00463 }
00464 /* }}} */
00465 
00466 void fpm_pctl_perform_idle_server_maintenance_heartbeat(struct fpm_event_s *ev, short which, void *arg) /* {{{ */
00467 {
00468        static struct fpm_event_s heartbeat;
00469        struct timeval now;
00470 
00471        if (fpm_globals.parent_pid != getpid()) {
00472               return; /* sanity check */
00473        }
00474 
00475        if (which == FPM_EV_TIMEOUT) {
00476               fpm_clock_get(&now);
00477               if (fpm_pctl_can_spawn_children()) {
00478                      fpm_pctl_perform_idle_server_maintenance(&now);
00479 
00480                      /* if it's a child, stop here without creating the next event
00481                       * this event is reserved to the master process
00482                       */
00483                      if (fpm_globals.is_child) {
00484                             return;
00485                      }
00486               }
00487               return;
00488        }
00489 
00490        /* first call without setting which to initialize the timer */
00491        fpm_event_set_timer(&heartbeat, FPM_EV_PERSIST, &fpm_pctl_perform_idle_server_maintenance_heartbeat, NULL);
00492        fpm_event_add(&heartbeat, FPM_IDLE_SERVER_MAINTENANCE_HEARTBEAT);
00493 }
00494 /* }}} */
00495 
00496 void fpm_pctl_on_socket_accept(struct fpm_event_s *ev, short which, void *arg) /* {{{ */
00497 {
00498        struct fpm_worker_pool_s *wp = (struct fpm_worker_pool_s *)arg;
00499        struct fpm_child_s *child;
00500 
00501 
00502        if (fpm_globals.parent_pid != getpid()) {
00503               /* prevent a event race condition when child process
00504                * have not set up its own event loop */
00505               return;
00506        }
00507 
00508        wp->socket_event_set = 0;
00509 
00510 /*     zlog(ZLOG_DEBUG, "[pool %s] heartbeat running_children=%d", wp->config->name, wp->running_children);*/
00511 
00512        if (wp->running_children >= wp->config->pm_max_children) {
00513               if (!wp->warn_max_children) {
00514                      fpm_scoreboard_update(0, 0, 0, 0, 0, 1, FPM_SCOREBOARD_ACTION_INC, wp->scoreboard);
00515                      zlog(ZLOG_WARNING, "[pool %s] server reached max_children setting (%d), consider raising it", wp->config->name, wp->config->pm_max_children);
00516                      wp->warn_max_children = 1;
00517               }
00518 
00519               return;
00520        }
00521 
00522        for (child = wp->children; child; child = child->next) {
00523               /* if there is at least on idle child, it will handle the connection, stop here */
00524               if (fpm_request_is_idle(child)) {
00525                      return;
00526               }
00527        }
00528 
00529        wp->warn_max_children = 0;
00530        fpm_children_make(wp, 1, 1, 1);
00531 
00532        if (fpm_globals.is_child) {
00533               return;
00534        }
00535 
00536        zlog(ZLOG_DEBUG, "[pool %s] got accept without idle child available .... I forked", wp->config->name);
00537 }
00538 /* }}} */
00539