root/sapi/fpm/fpm/fpm_children.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. fpm_children_cleanup
  2. fpm_child_alloc
  3. fpm_child_free
  4. fpm_child_close
  5. fpm_child_link
  6. fpm_child_unlink
  7. fpm_child_find
  8. fpm_child_init
  9. fpm_children_free
  10. fpm_children_bury
  11. fpm_resources_prepare
  12. fpm_resources_discard
  13. fpm_child_resources_use
  14. fpm_parent_resources_use
  15. fpm_children_make
  16. fpm_children_create_initial
  17. fpm_children_init_main

   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(FPM_EXIT_SOFTWARE);
 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) != FPM_EXIT_OK) {
 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 management 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 

/* [<][>][^][v][top][bottom][index][help] */