root/sapi/fpm/fpm/fpm_process_ctl.c

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

DEFINITIONS

This source file includes following definitions.
  1. fpm_pctl_cleanup
  2. fpm_pctl_action
  3. fpm_pctl_timeout_set
  4. fpm_pctl_exit
  5. fpm_pctl_exec
  6. fpm_pctl_action_last
  7. fpm_pctl_kill
  8. fpm_pctl_kill_all
  9. fpm_pctl_action_next
  10. fpm_pctl
  11. fpm_pctl_can_spawn_children
  12. fpm_pctl_child_exited
  13. fpm_pctl_init_main
  14. fpm_pctl_check_request_timeout
  15. fpm_pctl_perform_idle_server_maintenance
  16. fpm_pctl_heartbeat
  17. fpm_pctl_perform_idle_server_maintenance_heartbeat
  18. fpm_pctl_on_socket_accept

   1 
   2         /* $Id: fpm_process_ctl.c,v 1.19.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 <signal.h>
   9 #include <unistd.h>
  10 #include <stdlib.h>
  11 
  12 #include "fpm.h"
  13 #include "fpm_clock.h"
  14 #include "fpm_children.h"
  15 #include "fpm_signals.h"
  16 #include "fpm_events.h"
  17 #include "fpm_process_ctl.h"
  18 #include "fpm_cleanup.h"
  19 #include "fpm_request.h"
  20 #include "fpm_worker_pool.h"
  21 #include "fpm_scoreboard.h"
  22 #include "fpm_sockets.h"
  23 #include "zlog.h"
  24 
  25 
  26 static int fpm_state = FPM_PCTL_STATE_NORMAL;
  27 static int fpm_signal_sent = 0;
  28 
  29 
  30 static const char *fpm_state_names[] = {
  31         [FPM_PCTL_STATE_NORMAL] = "normal",
  32         [FPM_PCTL_STATE_RELOADING] = "reloading",
  33         [FPM_PCTL_STATE_TERMINATING] = "terminating",
  34         [FPM_PCTL_STATE_FINISHING] = "finishing"
  35 };
  36 
  37 static int saved_argc;
  38 static char **saved_argv;
  39 
  40 static void fpm_pctl_cleanup(int which, void *arg) /* {{{ */
  41 {
  42         int i;
  43         if (which != FPM_CLEANUP_PARENT_EXEC) {
  44                 for (i = 0; i < saved_argc; i++) {
  45                         free(saved_argv[i]);
  46                 }
  47                 free(saved_argv);
  48         }
  49 }
  50 /* }}} */
  51 
  52 static struct fpm_event_s pctl_event;
  53 
  54 static void fpm_pctl_action(struct fpm_event_s *ev, short which, void *arg) /* {{{ */
  55 {
  56         fpm_pctl(FPM_PCTL_STATE_UNSPECIFIED, FPM_PCTL_ACTION_TIMEOUT);
  57 }
  58 /* }}} */
  59 
  60 static int fpm_pctl_timeout_set(int sec) /* {{{ */
  61 {
  62         fpm_event_set_timer(&pctl_event, 0, &fpm_pctl_action, NULL);
  63         fpm_event_add(&pctl_event, sec * 1000);
  64         return 0;
  65 }
  66 /* }}} */
  67 
  68 static void fpm_pctl_exit() /* {{{ */
  69 {
  70         zlog(ZLOG_NOTICE, "exiting, bye-bye!");
  71 
  72         fpm_conf_unlink_pid();
  73         fpm_cleanups_run(FPM_CLEANUP_PARENT_EXIT_MAIN);
  74         exit(FPM_EXIT_OK);
  75 }
  76 /* }}} */
  77 
  78 #define optional_arg(c) (saved_argc > c ? ", \"" : ""), (saved_argc > c ? saved_argv[c] : ""), (saved_argc > c ? "\"" : "")
  79 
  80 static void fpm_pctl_exec() /* {{{ */
  81 {
  82 
  83         zlog(ZLOG_NOTICE, "reloading: execvp(\"%s\", {\"%s\""
  84                         "%s%s%s" "%s%s%s" "%s%s%s" "%s%s%s" "%s%s%s"
  85                         "%s%s%s" "%s%s%s" "%s%s%s" "%s%s%s" "%s%s%s"
  86                 "})",
  87                 saved_argv[0], saved_argv[0],
  88                 optional_arg(1),
  89                 optional_arg(2),
  90                 optional_arg(3),
  91                 optional_arg(4),
  92                 optional_arg(5),
  93                 optional_arg(6),
  94                 optional_arg(7),
  95                 optional_arg(8),
  96                 optional_arg(9),
  97                 optional_arg(10)
  98         );
  99 
 100         fpm_cleanups_run(FPM_CLEANUP_PARENT_EXEC);
 101         execvp(saved_argv[0], saved_argv);
 102         zlog(ZLOG_SYSERROR, "failed to reload: execvp() failed");
 103         exit(FPM_EXIT_SOFTWARE);
 104 }
 105 /* }}} */
 106 
 107 static void fpm_pctl_action_last() /* {{{ */
 108 {
 109         switch (fpm_state) {
 110                 case FPM_PCTL_STATE_RELOADING:
 111                         fpm_pctl_exec();
 112                         break;
 113 
 114                 case FPM_PCTL_STATE_FINISHING:
 115                 case FPM_PCTL_STATE_TERMINATING:
 116                         fpm_pctl_exit();
 117                         break;
 118         }
 119 }
 120 /* }}} */
 121 
 122 int fpm_pctl_kill(pid_t pid, int how) /* {{{ */
 123 {
 124         int s = 0;
 125 
 126         switch (how) {
 127                 case FPM_PCTL_TERM :
 128                         s = SIGTERM;
 129                         break;
 130                 case FPM_PCTL_STOP :
 131                         s = SIGSTOP;
 132                         break;
 133                 case FPM_PCTL_CONT :
 134                         s = SIGCONT;
 135                         break;
 136                 case FPM_PCTL_QUIT :
 137                         s = SIGQUIT;
 138                         break;
 139                 default :
 140                         break;
 141         }
 142         return kill(pid, s);
 143 }
 144 /* }}} */
 145 
 146 void fpm_pctl_kill_all(int signo) /* {{{ */
 147 {
 148         struct fpm_worker_pool_s *wp;
 149         int alive_children = 0;
 150 
 151         for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
 152                 struct fpm_child_s *child;
 153 
 154                 for (child = wp->children; child; child = child->next) {
 155                         int res = kill(child->pid, signo);
 156 
 157                         zlog(ZLOG_DEBUG, "[pool %s] sending signal %d %s to child %d",
 158                                 child->wp->config->name, signo,
 159                                 fpm_signal_names[signo] ? fpm_signal_names[signo] : "", (int) child->pid);
 160 
 161                         if (res == 0) {
 162                                 ++alive_children;
 163                         }
 164                 }
 165         }
 166 
 167         if (alive_children) {
 168                 zlog(ZLOG_DEBUG, "%d child(ren) still alive", alive_children);
 169         }
 170 }
 171 /* }}} */
 172 
 173 static void fpm_pctl_action_next() /* {{{ */
 174 {
 175         int sig, timeout;
 176 
 177         if (!fpm_globals.running_children) {
 178                 fpm_pctl_action_last();
 179         }
 180 
 181         if (fpm_signal_sent == 0) {
 182                 if (fpm_state == FPM_PCTL_STATE_TERMINATING) {
 183                         sig = SIGTERM;
 184                 } else {
 185                         sig = SIGQUIT;
 186                 }
 187                 timeout = fpm_global_config.process_control_timeout;
 188         } else {
 189                 if (fpm_signal_sent == SIGQUIT) {
 190                         sig = SIGTERM;
 191                 } else {
 192                         sig = SIGKILL;
 193                 }
 194                 timeout = 1;
 195         }
 196 
 197         fpm_pctl_kill_all(sig);
 198         fpm_signal_sent = sig;
 199         fpm_pctl_timeout_set(timeout);
 200 }
 201 /* }}} */
 202 
 203 void fpm_pctl(int new_state, int action) /* {{{ */
 204 {
 205         switch (action) {
 206                 case FPM_PCTL_ACTION_SET :
 207                         if (fpm_state == new_state) { /* already in progress - just ignore duplicate signal */
 208                                 return;
 209                         }
 210 
 211                         switch (fpm_state) { /* check which states can be overridden */
 212                                 case FPM_PCTL_STATE_NORMAL :
 213                                         /* 'normal' can be overridden by any other state */
 214                                         break;
 215                                 case FPM_PCTL_STATE_RELOADING :
 216                                         /* 'reloading' can be overridden by 'finishing' */
 217                                         if (new_state == FPM_PCTL_STATE_FINISHING) break;
 218                                 case FPM_PCTL_STATE_FINISHING :
 219                                         /* 'reloading' and 'finishing' can be overridden by 'terminating' */
 220                                         if (new_state == FPM_PCTL_STATE_TERMINATING) break;
 221                                 case FPM_PCTL_STATE_TERMINATING :
 222                                         /* nothing can override 'terminating' state */
 223                                         zlog(ZLOG_DEBUG, "not switching to '%s' state, because already in '%s' state",
 224                                                 fpm_state_names[new_state], fpm_state_names[fpm_state]);
 225                                         return;
 226                         }
 227 
 228                         fpm_signal_sent = 0;
 229                         fpm_state = new_state;
 230 
 231                         zlog(ZLOG_DEBUG, "switching to '%s' state", fpm_state_names[fpm_state]);
 232                         /* fall down */
 233 
 234                 case FPM_PCTL_ACTION_TIMEOUT :
 235                         fpm_pctl_action_next();
 236                         break;
 237                 case FPM_PCTL_ACTION_LAST_CHILD_EXITED :
 238                         fpm_pctl_action_last();
 239                         break;
 240 
 241         }
 242 }
 243 /* }}} */
 244 
 245 int fpm_pctl_can_spawn_children() /* {{{ */
 246 {
 247         return fpm_state == FPM_PCTL_STATE_NORMAL;
 248 }
 249 /* }}} */
 250 
 251 int fpm_pctl_child_exited() /* {{{ */
 252 {
 253         if (fpm_state == FPM_PCTL_STATE_NORMAL) {
 254                 return 0;
 255         }
 256 
 257         if (!fpm_globals.running_children) {
 258                 fpm_pctl(FPM_PCTL_STATE_UNSPECIFIED, FPM_PCTL_ACTION_LAST_CHILD_EXITED);
 259         }
 260         return 0;
 261 }
 262 /* }}} */
 263 
 264 int fpm_pctl_init_main() /* {{{ */
 265 {
 266         int i;
 267 
 268         saved_argc = fpm_globals.argc;
 269         saved_argv = malloc(sizeof(char *) * (saved_argc + 1));
 270 
 271         if (!saved_argv) {
 272                 return -1;
 273         }
 274 
 275         for (i = 0; i < saved_argc; i++) {
 276                 saved_argv[i] = strdup(fpm_globals.argv[i]);
 277 
 278                 if (!saved_argv[i]) {
 279                         return -1;
 280                 }
 281         }
 282 
 283         saved_argv[i] = 0;
 284 
 285         if (0 > fpm_cleanup_add(FPM_CLEANUP_ALL, fpm_pctl_cleanup, 0)) {
 286                 return -1;
 287         }
 288         return 0;
 289 }
 290 /* }}} */
 291 
 292 static void fpm_pctl_check_request_timeout(struct timeval *now) /* {{{ */
 293 {
 294         struct fpm_worker_pool_s *wp;
 295 
 296         for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
 297                 int terminate_timeout = wp->config->request_terminate_timeout;
 298                 int slowlog_timeout = wp->config->request_slowlog_timeout;
 299                 struct fpm_child_s *child;
 300 
 301                 if (terminate_timeout || slowlog_timeout) {
 302                         for (child = wp->children; child; child = child->next) {
 303                                 fpm_request_check_timed_out(child, now, terminate_timeout, slowlog_timeout);
 304                         }
 305                 }
 306         }
 307 }
 308 /* }}} */
 309 
 310 static void fpm_pctl_perform_idle_server_maintenance(struct timeval *now) /* {{{ */
 311 {
 312         struct fpm_worker_pool_s *wp;
 313 
 314         for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
 315                 struct fpm_child_s *child;
 316                 struct fpm_child_s *last_idle_child = NULL;
 317                 int idle = 0;
 318                 int active = 0;
 319                 int children_to_fork;
 320                 unsigned cur_lq = 0;
 321 
 322                 if (wp->config == NULL) continue;
 323 
 324                 for (child = wp->children; child; child = child->next) {
 325                         if (fpm_request_is_idle(child)) {
 326                                 if (last_idle_child == NULL) {
 327                                         last_idle_child = child;
 328                                 } else {
 329                                         if (timercmp(&child->started, &last_idle_child->started, <)) {
 330                                                 last_idle_child = child;
 331                                         }
 332                                 }
 333                                 idle++;
 334                         } else {
 335                                 active++;
 336                         }
 337                 }
 338 
 339                 /* update status structure for all PMs */
 340                 if (wp->listen_address_domain == FPM_AF_INET) {
 341                         if (0 > fpm_socket_get_listening_queue(wp->listening_socket, &cur_lq, NULL)) {
 342                                 cur_lq = 0;
 343 #if 0
 344                         } else {
 345                                 if (cur_lq > 0) {
 346                                         if (!wp->warn_lq) {
 347                                                 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);
 348                                                 wp->warn_lq = 1;
 349                                         }
 350                                 } else {
 351                                         wp->warn_lq = 0;
 352                                 }
 353 #endif
 354                         }
 355                 }
 356                 fpm_scoreboard_update(idle, active, cur_lq, -1, -1, -1, 0, FPM_SCOREBOARD_ACTION_SET, wp->scoreboard);
 357 
 358                 /* this is specific to PM_STYLE_ONDEMAND */
 359                 if (wp->config->pm == PM_STYLE_ONDEMAND) {
 360                         struct timeval last, now;
 361 
 362                         zlog(ZLOG_DEBUG, "[pool %s] currently %d active children, %d spare children", wp->config->name, active, idle);
 363 
 364                         if (!last_idle_child) continue;
 365 
 366                         fpm_request_last_activity(last_idle_child, &last);
 367                         fpm_clock_get(&now);
 368                         if (last.tv_sec < now.tv_sec - wp->config->pm_process_idle_timeout) {
 369                                 last_idle_child->idle_kill = 1;
 370                                 fpm_pctl_kill(last_idle_child->pid, FPM_PCTL_QUIT);
 371                         }
 372 
 373                         continue;
 374                 }
 375 
 376                 /* the rest is only used by PM_STYLE_DYNAMIC */
 377                 if (wp->config->pm != PM_STYLE_DYNAMIC) continue;
 378 
 379                 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);
 380 
 381                 if (idle > wp->config->pm_max_spare_servers && last_idle_child) {
 382                         last_idle_child->idle_kill = 1;
 383                         fpm_pctl_kill(last_idle_child->pid, FPM_PCTL_QUIT);
 384                         wp->idle_spawn_rate = 1;
 385                         continue;
 386                 }
 387 
 388                 if (idle < wp->config->pm_min_spare_servers) {
 389                         if (wp->running_children >= wp->config->pm_max_children) {
 390                                 if (!wp->warn_max_children) {
 391                                         fpm_scoreboard_update(0, 0, 0, 0, 0, 1, 0, FPM_SCOREBOARD_ACTION_INC, wp->scoreboard);
 392                                         zlog(ZLOG_WARNING, "[pool %s] server reached pm.max_children setting (%d), consider raising it", wp->config->name, wp->config->pm_max_children);
 393                                         wp->warn_max_children = 1;
 394                                 }
 395                                 wp->idle_spawn_rate = 1;
 396                                 continue;
 397                         }
 398 
 399                         if (wp->idle_spawn_rate >= 8) {
 400                                 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);
 401                         }
 402 
 403                         /* compute the number of idle process to spawn */
 404                         children_to_fork = MIN(wp->idle_spawn_rate, wp->config->pm_min_spare_servers - idle);
 405 
 406                         /* get sure it won't exceed max_children */
 407                         children_to_fork = MIN(children_to_fork, wp->config->pm_max_children - wp->running_children);
 408                         if (children_to_fork <= 0) {
 409                                 if (!wp->warn_max_children) {
 410                                         fpm_scoreboard_update(0, 0, 0, 0, 0, 1, 0, FPM_SCOREBOARD_ACTION_INC, wp->scoreboard);
 411                                         zlog(ZLOG_WARNING, "[pool %s] server reached pm.max_children setting (%d), consider raising it", wp->config->name, wp->config->pm_max_children);
 412                                         wp->warn_max_children = 1;
 413                                 }
 414                                 wp->idle_spawn_rate = 1;
 415                                 continue;
 416                         }
 417                         wp->warn_max_children = 0;
 418 
 419                         fpm_children_make(wp, 1, children_to_fork, 1);
 420 
 421                         /* if it's a child, stop here without creating the next event
 422                          * this event is reserved to the master process
 423                          */
 424                         if (fpm_globals.is_child) {
 425                                 return;
 426                         }
 427 
 428                         zlog(ZLOG_DEBUG, "[pool %s] %d child(ren) have been created dynamically", wp->config->name, children_to_fork);
 429 
 430                         /* Double the spawn rate for the next iteration */
 431                         if (wp->idle_spawn_rate < FPM_MAX_SPAWN_RATE) {
 432                                 wp->idle_spawn_rate *= 2;
 433                         }
 434                         continue;
 435                 }
 436                 wp->idle_spawn_rate = 1;
 437         }
 438 }
 439 /* }}} */
 440 
 441 void fpm_pctl_heartbeat(struct fpm_event_s *ev, short which, void *arg) /* {{{ */
 442 {
 443         static struct fpm_event_s heartbeat;
 444         struct timeval now;
 445 
 446         if (fpm_globals.parent_pid != getpid()) {
 447                 return; /* sanity check */
 448         }
 449 
 450         if (which == FPM_EV_TIMEOUT) {
 451                 fpm_clock_get(&now);
 452                 fpm_pctl_check_request_timeout(&now);
 453                 return;
 454         }
 455 
 456         /* ensure heartbeat is not lower than FPM_PCTL_MIN_HEARTBEAT */
 457         fpm_globals.heartbeat = MAX(fpm_globals.heartbeat, FPM_PCTL_MIN_HEARTBEAT);
 458 
 459         /* first call without setting to initialize the timer */
 460         zlog(ZLOG_DEBUG, "heartbeat have been set up with a timeout of %dms", fpm_globals.heartbeat);
 461         fpm_event_set_timer(&heartbeat, FPM_EV_PERSIST, &fpm_pctl_heartbeat, NULL);
 462         fpm_event_add(&heartbeat, fpm_globals.heartbeat);
 463 }
 464 /* }}} */
 465 
 466 void fpm_pctl_perform_idle_server_maintenance_heartbeat(struct fpm_event_s *ev, short which, void *arg) /* {{{ */
 467 {
 468         static struct fpm_event_s heartbeat;
 469         struct timeval now;
 470 
 471         if (fpm_globals.parent_pid != getpid()) {
 472                 return; /* sanity check */
 473         }
 474 
 475         if (which == FPM_EV_TIMEOUT) {
 476                 fpm_clock_get(&now);
 477                 if (fpm_pctl_can_spawn_children()) {
 478                         fpm_pctl_perform_idle_server_maintenance(&now);
 479 
 480                         /* if it's a child, stop here without creating the next event
 481                          * this event is reserved to the master process
 482                          */
 483                         if (fpm_globals.is_child) {
 484                                 return;
 485                         }
 486                 }
 487                 return;
 488         }
 489 
 490         /* first call without setting which to initialize the timer */
 491         fpm_event_set_timer(&heartbeat, FPM_EV_PERSIST, &fpm_pctl_perform_idle_server_maintenance_heartbeat, NULL);
 492         fpm_event_add(&heartbeat, FPM_IDLE_SERVER_MAINTENANCE_HEARTBEAT);
 493 }
 494 /* }}} */
 495 
 496 void fpm_pctl_on_socket_accept(struct fpm_event_s *ev, short which, void *arg) /* {{{ */
 497 {
 498         struct fpm_worker_pool_s *wp = (struct fpm_worker_pool_s *)arg;
 499         struct fpm_child_s *child;
 500 
 501 
 502         if (fpm_globals.parent_pid != getpid()) {
 503                 /* prevent a event race condition when child process
 504                  * have not set up its own event loop */
 505                 return;
 506         }
 507 
 508         wp->socket_event_set = 0;
 509 
 510 /*      zlog(ZLOG_DEBUG, "[pool %s] heartbeat running_children=%d", wp->config->name, wp->running_children);*/
 511 
 512         if (wp->running_children >= wp->config->pm_max_children) {
 513                 if (!wp->warn_max_children) {
 514                         fpm_scoreboard_update(0, 0, 0, 0, 0, 1, 0, FPM_SCOREBOARD_ACTION_INC, wp->scoreboard);
 515                         zlog(ZLOG_WARNING, "[pool %s] server reached max_children setting (%d), consider raising it", wp->config->name, wp->config->pm_max_children);
 516                         wp->warn_max_children = 1;
 517                 }
 518 
 519                 return;
 520         }
 521 
 522         for (child = wp->children; child; child = child->next) {
 523                 /* if there is at least on idle child, it will handle the connection, stop here */
 524                 if (fpm_request_is_idle(child)) {
 525                         return;
 526                 }
 527         }
 528 
 529         wp->warn_max_children = 0;
 530         fpm_children_make(wp, 1, 1, 1);
 531 
 532         if (fpm_globals.is_child) {
 533                 return;
 534         }
 535 
 536         zlog(ZLOG_DEBUG, "[pool %s] got accept without idle child available .... I forked", wp->config->name);
 537 }
 538 /* }}} */
 539 

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