From: Colm MacCarthaigh Date: Mon, 19 Sep 2005 15:51:22 +0000 (+0000) Subject: Backport graceful-stop to the 2.2.x branch. X-Git-Tag: 2.1.8~41 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=cbe4551a2ff4392538dd9881bb7dd2a03f4ac8e6;p=thirdparty%2Fapache%2Fhttpd.git Backport graceful-stop to the 2.2.x branch. git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.2.x@290189 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/CHANGES b/CHANGES index f07ddf5806a..573c61f17de 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,11 @@ -*- coding: utf-8 -*- Changes with Apache 2.1.8 + *) prefork, worker and event MPMs: Support a graceful-stop procedure: + Server will wait until existing requests are finished or until + "GracefulShutdownTimeout" number of seconds before exiting. + [Colm MacCarthaigh, Ken Coar, Bill Stoddard] + *) prefork, worker and event MPMs: Prevent children from holding open listening ports upon graceful restart or stop. PR 28167. [Colm MacCarthaigh, Brian Pinkerton ] diff --git a/include/mpm_common.h b/include/mpm_common.h index 161b48e486c..bb4aa94d637 100644 --- a/include/mpm_common.h +++ b/include/mpm_common.h @@ -65,6 +65,15 @@ extern "C" { /* Signal used to gracefully restart (as a quoted string) */ #define AP_SIG_GRACEFUL_STRING "SIGUSR1" +/* Signal used to gracefully stop */ +#define AP_SIG_GRACEFUL_STOP SIGWINCH + +/* Signal used to gracefully stop (without SIG prefix) */ +#define AP_SIG_GRACEFUL_STOP_SHORT WINCH + +/* Signal used to gracefully stop (as a quoted string) */ +#define AP_SIG_GRACEFUL_STOP_STRING "SIGWINCH" + /** * Make sure all child processes that have been spawned by the parent process * have died. This includes process registered as "other_children". @@ -86,8 +95,25 @@ void ap_reclaim_child_processes(int terminate); #endif /** - * Tell ap_reclaim_child_processes() about an MPM child process which has no - * entry in the scoreboard. + * Catch any child processes that have been spawned by the parent process + * which have exited. This includes processes registered as "other_children". + * @warning This is only defined if the MPM defines + * AP_MPM_WANT_RECLAIM_CHILD_PROCESSES + * @tip This function requires that some macros are defined by the MPM:
+ *  MPM_CHILD_PID -- Get the pid from the specified spot in the scoreboard
+ *  MPM_NOTE_CHILD_KILLED -- Note the child died in the scoreboard
+ * 
+ * @tip The MPM child processes which are relieved are those listed + * in the scoreboard as well as those currently registered via + * ap_register_extra_mpm_process(). + */ +#ifdef AP_MPM_WANT_RECLAIM_CHILD_PROCESSES +void ap_relieve_child_processes(void); +#endif + +/** + * Tell ap_reclaim_child_processes() and ap_relieve_child_processes() about + * an MPM child process which has no entry in the scoreboard. * @warning This is only defined if the MPM defines * AP_MPM_WANT_RECLAIM_CHILD_PROCESSES * @param pid The process id of an MPM child process which should be @@ -278,6 +304,20 @@ const char *ap_mpm_set_coredumpdir(cmd_parms *cmd, void *dummy, const char *arg); #endif +/** + * Set the timeout period for a graceful shutdown. + */ +#ifdef AP_MPM_WANT_SET_GRACEFUL_SHUTDOWN +extern int ap_graceful_shutdown_timeout; +const char *ap_mpm_set_graceful_shutdown(cmd_parms *cmd, void *dummy, + const char *arg); +#define AP_GRACEFUL_SHUTDOWN_TIMEOUT_COMMAND \ +AP_INIT_TAKE1("GracefulShutdownTimeout", ap_mpm_set_graceful_shutdown, NULL, \ + RSRC_CONF, "Maximum time in seconds to wait for child " \ + "processes to complete transactions during shutdown") +#endif + + #ifdef AP_MPM_WANT_SIGNAL_SERVER int ap_signal_server(int *, apr_pool_t *); void ap_mpm_rewrite_args(process_rec *); diff --git a/server/main.c b/server/main.c index 732b10097b6..98085819215 100644 --- a/server/main.c +++ b/server/main.c @@ -318,9 +318,15 @@ static void usage(process_rec *process) pad); #endif #ifdef AP_MPM_WANT_SIGNAL_SERVER +#ifdef AP_MPM_WANT_SET_GRACEFUL_SHUTDOWN + ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, + " %s [-k start|restart|graceful|graceful-stop|stop]", + pad); +#else ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, " %s [-k start|restart|graceful|stop]", pad); +#endif /* AP_MPM_WANT_SET_GRACEFUL_SHUTDOWN */ #endif ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, " %s [-v] [-V] [-h] [-l] [-L] [-t] [-S]", pad); diff --git a/server/mpm/experimental/event/event.c b/server/mpm/experimental/event/event.c index 299cdf856f9..00ed6501e92 100644 --- a/server/mpm/experimental/event/event.c +++ b/server/mpm/experimental/event/event.c @@ -416,7 +416,7 @@ ap_generation_t volatile ap_my_generation; * child to force an exit) and so do an exit anyway. */ -static void ap_start_shutdown(void) +static void ap_start_shutdown(int graceful) { mpm_state = AP_MPMQ_STOPPING; if (shutdown_pending == 1) { @@ -427,6 +427,7 @@ static void ap_start_shutdown(void) return; } shutdown_pending = 1; + is_graceful = graceful; } /* do a graceful restart if graceful == 1 */ @@ -443,7 +444,7 @@ static void ap_start_restart(int graceful) static void sig_term(int sig) { - ap_start_shutdown(); + ap_start_shutdown(sig == AP_SIG_GRACEFUL_STOP); } static void restart(int sig) @@ -469,6 +470,11 @@ static void set_signals(void) if (sigaction(SIGTERM, &sa, NULL) < 0) ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGTERM)"); +#ifdef AP_SIG_GRACEFUL_STOP + if (sigaction(AP_SIG_GRACEFUL_STOP, &sa, NULL) < 0) + ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, + "sigaction(" AP_SIG_GRACEFUL_STOP_STRING ")"); +#endif #ifdef SIGINT if (sigaction(SIGINT, &sa, NULL) < 0) ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, @@ -521,6 +527,9 @@ static void set_signals(void) #ifdef AP_SIG_GRACEFUL apr_signal(AP_SIG_GRACEFUL, restart); #endif /* AP_SIG_GRACEFUL */ +#ifdef AP_SIG_GRACEFUL_STOP + apr_signal(AP_SIG_GRACEFUL_STOP, sig_term); +#endif /* AP_SIG_GRACEFUL_STOP */ #ifdef SIGPIPE apr_signal(SIGPIPE, SIG_IGN); #endif /* SIGPIPE */ @@ -598,9 +607,8 @@ static int process_socket(apr_pool_t * p, apr_socket_t * sock, * accept() with a socket readability check, like Win32, * and there are measurable delays before the * socket is readable due to the first data packet arriving, - * it might be better to create the cs on the listener thread, - * set the state to CONN_STATE_CHECK_REQUEST_LINE_READABLE, - * and give it to the event thread. + * it might be better to create the cs on the listener thread + * with the state set to CONN_STATE_CHECK_REQUEST_LINE_READABLE * * FreeBSD users will want to enable the HTTP accept filter * module in their kernel for the highest performance @@ -1925,11 +1933,9 @@ int ap_mpm_run(apr_pool_t * _pconf, apr_pool_t * plog, server_rec * s) server_main_loop(remaining_children_to_start); mpm_state = AP_MPMQ_STOPPING; - if (shutdown_pending) { - /* Time to gracefully shut down: + if (shutdown_pending && !is_graceful) { + /* Time to shut down: * Kill child processes, tell them to call child_exit, etc... - * (By "gracefully" we don't mean graceful in the same sense as - * "apachectl graceful" where we allow old connections to finish.) */ ap_mpm_pod_killpg(pod, ap_daemons_limit, FALSE); ap_reclaim_child_processes(1); /* Start with SIGTERM */ @@ -1948,7 +1954,64 @@ int ap_mpm_run(apr_pool_t * _pconf, apr_pool_t * plog, server_rec * s) ap_server_conf, "caught SIGTERM, shutting down"); } return 1; - } + } else if (shutdown_pending) { + /* Time to gracefully shut down: + * Kill child processes, tell them to call child_exit, etc... + */ + int active_children; + int index; + apr_time_t cutoff = 0; + + /* Close our listeners, and then ask our children to do same */ + ap_close_listeners(); + ap_mpm_pod_killpg(pod, ap_daemons_limit, TRUE); + ap_relieve_child_processes(); + + if (!child_fatal) { + /* cleanup pid file on normal shutdown */ + const char *pidfile = NULL; + pidfile = ap_server_root_relative (pconf, ap_pid_fname); + if ( pidfile != NULL && unlink(pidfile) == 0) + ap_log_error(APLOG_MARK, APLOG_INFO, 0, + ap_server_conf, + "removed PID file %s (pid=%ld)", + pidfile, (long)getpid()); + + ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, + ap_server_conf, "caught SIGTERM, shutting down"); + } + + /* Don't really exit until each child has finished */ + shutdown_pending = 0; + do { + /* Pause for a second */ + apr_sleep(apr_time_from_sec(1)); + + /* Relieve any children which have now exited */ + ap_relieve_child_processes(); + + active_children = 0; + for (index = 0; index < ap_daemons_limit; ++index) { + if (MPM_CHILD_PID(index) != 0) { + if (kill(MPM_CHILD_PID(index), 0) == 0) { + active_children = 1; + /* Having just one child is enough to stay around */ + break; + } + } + } + } while (!shutdown_pending && active_children && + (!ap_graceful_shutdown_timeout || apr_time_now() < cutoff)); + + /* We might be here because we received SIGTERM, either + * way, try and make sure that all of our processes are + * really dead. + */ + ap_mpm_pod_killpg(pod, ap_daemons_limit, FALSE); + ap_reclaim_child_processes(1); + + return 1; + } /* we've been told to restart */ apr_signal(SIGHUP, SIG_IGN); @@ -2371,6 +2434,7 @@ static const command_rec event_cmds[] = { AP_INIT_TAKE1("ThreadLimit", set_thread_limit, NULL, RSRC_CONF, "Maximum number of worker threads per child process for this " "run of Apache - Upper limit for ThreadsPerChild"), + AP_GRACEFUL_SHUTDOWN_TIMEOUT_COMMAND, {NULL} }; diff --git a/server/mpm/experimental/event/mpm.h b/server/mpm/experimental/event/mpm.h index 67f544eb066..02284657313 100644 --- a/server/mpm/experimental/event/mpm.h +++ b/server/mpm/experimental/event/mpm.h @@ -36,6 +36,7 @@ #define AP_MPM_WANT_SIGNAL_SERVER #define AP_MPM_WANT_SET_MAX_MEM_FREE #define AP_MPM_WANT_SET_STACKSIZE +#define AP_MPM_WANT_SET_GRACEFUL_SHUTDOWN #define AP_MPM_WANT_FATAL_SIGNAL_HANDLER #define AP_MPM_DISABLE_NAGLE_ACCEPTED_SOCK diff --git a/server/mpm/prefork/mpm.h b/server/mpm/prefork/mpm.h index 811703b1604..cead8b96835 100644 --- a/server/mpm/prefork/mpm.h +++ b/server/mpm/prefork/mpm.h @@ -38,6 +38,7 @@ #define AP_MPM_WANT_SIGNAL_SERVER #define AP_MPM_WANT_SET_MAX_MEM_FREE #define AP_MPM_WANT_FATAL_SIGNAL_HANDLER +#define AP_MPM_WANT_SET_GRACEFUL_SHUTDOWN #define AP_MPM_DISABLE_NAGLE_ACCEPTED_SOCK #define AP_MPM_USES_POD 1 diff --git a/server/mpm/prefork/prefork.c b/server/mpm/prefork/prefork.c index 41a54866b31..2a59800c8e2 100644 --- a/server/mpm/prefork/prefork.c +++ b/server/mpm/prefork/prefork.c @@ -138,7 +138,7 @@ int tpf_child = 0; char tpf_server_name[INETD_SERVNAME_LENGTH+1]; #endif /* TPF */ -static int die_now = 0; +static volatile int die_now = 0; #ifdef GPROF /* @@ -331,6 +331,9 @@ static void just_die(int sig) static void stop_listening(int sig) { ap_close_listeners(); + + /* For a graceful stop, we want the child to exit when done */ + die_now = 1; } /* volatile just in case */ @@ -348,6 +351,7 @@ static void sig_term(int sig) return; } shutdown_pending = 1; + is_graceful = (sig == AP_SIG_GRACEFUL_STOP); } /* restart() is the signal handler for SIGHUP and AP_SIG_GRACEFUL @@ -380,6 +384,11 @@ static void set_signals(void) sa.sa_handler = sig_term; if (sigaction(SIGTERM, &sa, NULL) < 0) ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGTERM)"); +#ifdef AP_SIG_GRACEFUL_STOP + if (sigaction(AP_SIG_GRACEFUL_STOP, &sa, NULL) < 0) + ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, + "sigaction(" AP_SIG_GRACEFUL_STOP_STRING ")"); +#endif #ifdef SIGINT if (sigaction(SIGINT, &sa, NULL) < 0) ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGINT)"); @@ -427,6 +436,9 @@ static void set_signals(void) #ifdef AP_SIG_GRACEFUL apr_signal(AP_SIG_GRACEFUL, restart); #endif /* AP_SIG_GRACEFUL */ +#ifdef AP_SIG_GRACEFUL_STOP + apr_signal(AP_SIG_GRACEFUL_STOP, sig_term); +#endif /* AP_SIG_GRACEFUL */ #ifdef SIGPIPE apr_signal(SIGPIPE, SIG_IGN); #endif /* SIGPIPE */ @@ -1071,8 +1083,8 @@ int ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s) mpm_state = AP_MPMQ_STOPPING; - if (shutdown_pending) { - /* Time to gracefully shut down: + if (shutdown_pending && !is_graceful) { + /* Time to shut down: * Kill child processes, tell them to call child_exit, etc... */ if (unixd_killpg(getpgrp(), SIGTERM) < 0) { @@ -1094,6 +1106,82 @@ int ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s) ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, "caught SIGTERM, shutting down"); return 1; + } else if (shutdown_pending) { + /* Time to perform a graceful shut down: + * Reap the inactive children, and ask the active ones + * to close their listeners, then wait until they are + * all done to exit. + */ + int active_children; + apr_time_t cutoff = 0; + + /* Stop listening */ + ap_close_listeners(); + + /* kill off the idle ones */ + ap_mpm_pod_killpg(pod, ap_max_daemons_limit); + + /* Send SIGUSR1 to the active children */ + active_children = 0; + for (index = 0; index < ap_daemons_limit; ++index) { + if (ap_scoreboard_image->servers[index][0].status != SERVER_DEAD) { + /* Ask each child to close its listeners. */ + kill(MPM_CHILD_PID(index), AP_SIG_GRACEFUL); + active_children++; + } + } + + /* Allow each child which actually finished to exit */ + ap_relieve_child_processes(); + + /* cleanup pid file */ + { + const char *pidfile = NULL; + pidfile = ap_server_root_relative (pconf, ap_pid_fname); + if ( pidfile != NULL && unlink(pidfile) == 0) + ap_log_error(APLOG_MARK, APLOG_INFO, + 0, ap_server_conf, + "removed PID file %s (pid=%ld)", + pidfile, (long)getpid()); + } + + ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, + "caught " AP_SIG_GRACEFUL_STOP_STRING ", shutting down gracefully"); + + if (ap_graceful_shutdown_timeout) { + cutoff = apr_time_now() + + apr_time_from_sec(ap_graceful_shutdown_timeout); + } + + /* Don't really exit until each child has finished */ + shutdown_pending = 0; + do { + /* Pause for a second */ + sleep(1); + + /* Relieve any children which have now exited */ + ap_relieve_child_processes(); + + active_children = 0; + for (index = 0; index < ap_daemons_limit; ++index) { + if (MPM_CHILD_PID(index) != 0) { + if (kill(MPM_CHILD_PID(index), 0) == 0) { + active_children = 1; + /* Having just one child is enough to stay around */ + break; + } + } + } + } while (!shutdown_pending && active_children && + (!ap_graceful_shutdown_timeout || apr_time_now() < cutoff)); + + /* We might be here because we received SIGTERM, either + * way, try and make sure that all of our processes are + * really dead. + */ + unixd_killpg(getpgrp(), SIGTERM); + + return 1; } /* we've been told to restart */ @@ -1373,6 +1461,7 @@ AP_INIT_TAKE1("MaxClients", set_max_clients, NULL, RSRC_CONF, "Maximum number of children alive at the same time"), AP_INIT_TAKE1("ServerLimit", set_server_limit, NULL, RSRC_CONF, "Maximum value of MaxClients for this run of Apache"), +AP_GRACEFUL_SHUTDOWN_TIMEOUT_COMMAND, { NULL } }; diff --git a/server/mpm/worker/mpm.h b/server/mpm/worker/mpm.h index 29c9f452fe6..509a80afe28 100644 --- a/server/mpm/worker/mpm.h +++ b/server/mpm/worker/mpm.h @@ -36,6 +36,7 @@ #define AP_MPM_WANT_SIGNAL_SERVER #define AP_MPM_WANT_SET_MAX_MEM_FREE #define AP_MPM_WANT_SET_STACKSIZE +#define AP_MPM_WANT_SET_GRACEFUL_SHUTDOWN #define AP_MPM_WANT_FATAL_SIGNAL_HANDLER #define AP_MPM_DISABLE_NAGLE_ACCEPTED_SOCK diff --git a/server/mpm/worker/worker.c b/server/mpm/worker/worker.c index 904ef4a9028..3dfdcc270a4 100644 --- a/server/mpm/worker/worker.c +++ b/server/mpm/worker/worker.c @@ -374,7 +374,7 @@ ap_generation_t volatile ap_my_generation; * child to force an exit) and so do an exit anyway. */ -static void ap_start_shutdown(void) +static void ap_start_shutdown(int graceful) { mpm_state = AP_MPMQ_STOPPING; if (shutdown_pending == 1) { @@ -385,6 +385,7 @@ static void ap_start_shutdown(void) return; } shutdown_pending = 1; + is_graceful = graceful; } /* do a graceful restart if graceful == 1 */ @@ -401,7 +402,7 @@ static void ap_start_restart(int graceful) static void sig_term(int sig) { - ap_start_shutdown(); + ap_start_shutdown(sig == AP_SIG_GRACEFUL_STOP); } static void restart(int sig) @@ -427,6 +428,11 @@ static void set_signals(void) if (sigaction(SIGTERM, &sa, NULL) < 0) ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGTERM)"); +#ifdef AP_SIG_GRACEFUL_STOP + if (sigaction(AP_SIG_GRACEFUL_STOP, &sa, NULL) < 0) + ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, + "sigaction(" AP_SIG_GRACEFUL_STOP_STRING ")"); +#endif #ifdef SIGINT if (sigaction(SIGINT, &sa, NULL) < 0) ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, @@ -479,6 +485,9 @@ static void set_signals(void) #ifdef AP_SIG_GRACEFUL apr_signal(AP_SIG_GRACEFUL, restart); #endif /* AP_SIG_GRACEFUL */ +#ifdef AP_SIG_GRACEFUL_STOP + apr_signal(AP_SIG_GRACEFUL_STOP, sig_term); +#endif /* AP_SIG_GRACEFUL_STOP */ #ifdef SIGPIPE apr_signal(SIGPIPE, SIG_IGN); #endif /* SIGPIPE */ @@ -1714,11 +1723,9 @@ int ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s) server_main_loop(remaining_children_to_start); mpm_state = AP_MPMQ_STOPPING; - if (shutdown_pending) { - /* Time to gracefully shut down: + if (shutdown_pending && !is_graceful) { + /* Time to shut down: * Kill child processes, tell them to call child_exit, etc... - * (By "gracefully" we don't mean graceful in the same sense as - * "apachectl graceful" where we allow old connections to finish.) */ ap_mpm_pod_killpg(pod, ap_daemons_limit, FALSE); ap_reclaim_child_processes(1); /* Start with SIGTERM */ @@ -1730,12 +1737,69 @@ int ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s) if ( pidfile != NULL && unlink(pidfile) == 0) ap_log_error(APLOG_MARK, APLOG_INFO, 0, ap_server_conf, - "removed PID file %s (pid=%ld)", - pidfile, (long)getpid()); + "removed PID file %s (pid=%" APR_PID_T_FMT ")", + pidfile, getpid()); + + ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, + ap_server_conf, "caught SIGTERM, shutting down"); + } + return 1; + } else if (shutdown_pending) { + /* Time to gracefully shut down: + * Kill child processes, tell them to call child_exit, etc... + */ + int active_children; + int index; + apr_time_t cutoff = 0; + + /* Close our listeners, and then ask our children to do same */ + ap_close_listeners(); + ap_mpm_pod_killpg(pod, ap_daemons_limit, TRUE); + ap_relieve_child_processes(); + + if (!child_fatal) { + /* cleanup pid file on normal shutdown */ + const char *pidfile = NULL; + pidfile = ap_server_root_relative (pconf, ap_pid_fname); + if ( pidfile != NULL && unlink(pidfile) == 0) + ap_log_error(APLOG_MARK, APLOG_INFO, 0, + ap_server_conf, + "removed PID file %s (pid=%" APR_PID_T_FMT ")", + pidfile, getpid()); ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, "caught SIGTERM, shutting down"); } + + /* Don't really exit until each child has finished */ + shutdown_pending = 0; + do { + /* Pause for a second */ + apr_sleep(apr_time_from_sec(1)); + + /* Relieve any children which have now exited */ + ap_relieve_child_processes(); + + active_children = 0; + for (index = 0; index < ap_daemons_limit; ++index) { + if (MPM_CHILD_PID(index) != 0) { + if (kill(MPM_CHILD_PID(index), 0) == 0) { + active_children = 1; + /* Having just one child is enough to stay around */ + break; + } + } + } + } while (!shutdown_pending && active_children && + (!ap_graceful_shutdown_timeout || apr_time_now() < cutoff)); + + /* We might be here because we received SIGTERM, either + * way, try and make sure that all of our processes are + * really dead. + */ + ap_mpm_pod_killpg(pod, ap_daemons_limit, FALSE); + ap_reclaim_child_processes(1); + return 1; } @@ -2150,6 +2214,7 @@ AP_INIT_TAKE1("ServerLimit", set_server_limit, NULL, RSRC_CONF, "Maximum number of child processes for this run of Apache"), AP_INIT_TAKE1("ThreadLimit", set_thread_limit, NULL, RSRC_CONF, "Maximum number of worker threads per child process for this run of Apache - Upper limit for ThreadsPerChild"), +AP_GRACEFUL_SHUTDOWN_TIMEOUT_COMMAND, { NULL } }; diff --git a/server/mpm_common.c b/server/mpm_common.c index 93711f7c192..c55a79e0968 100644 --- a/server/mpm_common.c +++ b/server/mpm_common.c @@ -273,6 +273,38 @@ void ap_reclaim_child_processes(int terminate) } while (not_dead_yet > 0 && action_table[cur_action].action != GIVEUP); } + +void ap_relieve_child_processes(void) +{ + int i; + extra_process_t *cur_extra; + int max_daemons; + + ap_mpm_query(AP_MPMQ_MAX_DAEMON_USED, &max_daemons); + + /* now see who is done */ + for (i = 0; i < max_daemons; ++i) { + pid_t pid = MPM_CHILD_PID(i); + + if (pid == 0) { + continue; /* not every scoreboard entry is in use */ + } + + if (reclaim_one_pid(pid, DO_NOTHING)) { + MPM_NOTE_CHILD_KILLED(i); + } + } + + cur_extra = extras; + while (cur_extra) { + extra_process_t *next = cur_extra->next; + + if (reclaim_one_pid(cur_extra->pid, DO_NOTHING)) { + AP_DEBUG_ASSERT(1 == ap_unregister_extra_mpm_process(cur_extra->pid)); + } + cur_extra = next; + } +} #endif /* AP_MPM_WANT_RECLAIM_CHILD_PROCESSES */ #ifdef AP_MPM_WANT_WAIT_OR_TIMEOUT @@ -760,6 +792,21 @@ const char *ap_mpm_set_coredumpdir(cmd_parms *cmd, void *dummy, } #endif +#ifdef AP_MPM_WANT_SET_GRACEFUL_SHUTDOWN +int ap_graceful_shutdown_timeout = 0; + +const char * ap_mpm_set_graceful_shutdown(cmd_parms *cmd, void *dummy, + const char *arg) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + ap_graceful_shutdown_timeout = atoi(arg); + return NULL; +} +#endif + #ifdef AP_MPM_WANT_SET_ACCEPT_LOCK_MECH apr_lockmech_e ap_accept_lock_mech = APR_LOCK_DEFAULT; @@ -921,6 +968,20 @@ int ap_signal_server(int *exit_status, apr_pool_t *pconf) return 1; } } + + if (!strcmp(dash_k_arg, "graceful-stop")) { +#ifdef AP_MPM_WANT_SET_GRACEFUL_SHUTDOWN + if (!running) { + printf("%s\n", status); + } + else { + *exit_status = send_signal(otherpid, AP_SIG_GRACEFUL_STOP); + } +#else + printf("httpd MPM \"" MPM_NAME "\" does not support graceful-stop\n"); +#endif + return 1; + } return 0; } @@ -949,7 +1010,8 @@ void ap_mpm_rewrite_args(process_rec *process) case 'k': if (!dash_k_arg) { if (!strcmp(optarg, "start") || !strcmp(optarg, "stop") || - !strcmp(optarg, "restart") || !strcmp(optarg, "graceful")) { + !strcmp(optarg, "restart") || !strcmp(optarg, "graceful") || + !strcmp(optarg, "graceful-stop")) { dash_k_arg = optarg; break; } diff --git a/support/apachectl.in b/support/apachectl.in index 8a2b6dba4ac..9f1c0992aa9 100644 --- a/support/apachectl.in +++ b/support/apachectl.in @@ -76,7 +76,7 @@ if [ "x$ARGV" = "x" ] ; then fi case $ARGV in -start|stop|restart|graceful) +start|stop|restart|graceful|graceful-stop) $HTTPD -k $ARGV ERROR=$? ;;