]> git.ipfire.org Git - thirdparty/apache/httpd.git/commitdiff
Backport graceful-stop to the 2.2.x branch.
authorColm MacCarthaigh <colm@apache.org>
Mon, 19 Sep 2005 15:51:22 +0000 (15:51 +0000)
committerColm MacCarthaigh <colm@apache.org>
Mon, 19 Sep 2005 15:51:22 +0000 (15:51 +0000)
git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.2.x@290189 13f79535-47bb-0310-9956-ffa450edef68

CHANGES
include/mpm_common.h
server/main.c
server/mpm/experimental/event/event.c
server/mpm/experimental/event/mpm.h
server/mpm/prefork/mpm.h
server/mpm/prefork/prefork.c
server/mpm/worker/mpm.h
server/mpm/worker/worker.c
server/mpm_common.c
support/apachectl.in

diff --git a/CHANGES b/CHANGES
index f07ddf5806a7eb126946be72e4388c5232f1c0ae..573c61f17decbc0d96984bbc9d2d2c76835e1fa2 100644 (file)
--- 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 <bp thinkpink.com>]
index 161b48e486cc7e3a9449f942fe3ac74eaced445b..bb4aa94d6374da323ca71fae34bff578138cca7e 100644 (file)
@@ -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: <pre>
+ *  MPM_CHILD_PID -- Get the pid from the specified spot in the scoreboard
+ *  MPM_NOTE_CHILD_KILLED -- Note the child died in the scoreboard
+ * </pre>
+ * @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 *);
index 732b10097b61016b1abaeb2a6054404ceaebc883..980858192151bc527ab565aa9fca7eca03cfb3c6 100644 (file)
@@ -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);
index 299cdf856f9630baad7b052058b1a9b6f650b4e9..00ed6501e9296d72b956f193851367f7a4ff5681 100644 (file)
@@ -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}
 };
 
index 67f544eb066d99a34600c28e1028e724b627b4e9..022846573138d030c5343da6fc585bb453ec6d0f 100644 (file)
@@ -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
 
index 811703b16042871422890ec57b6854c209b3c558..cead8b96835269c192f51c2b61c10ab6039c2328 100644 (file)
@@ -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
index 41a54866b3134efd31c8b45c969dabdca4431e6c..2a59800c8e2c3997cf8771229a4013b1b2de0c51 100644 (file)
@@ -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 }
 };
 
index 29c9f452fe6940e721b531574b95f8c2d6a0b6a5..509a80afe2877cb53f9d4bfdd6bc2f0f324343a9 100644 (file)
@@ -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
 
index 904ef4a9028b4bf8fd571a01dd6e0fba3be3d0b0..3dfdcc270a45c8763e601211c5b0fe61daf84b59 100644 (file)
@@ -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 }
 };
 
index 93711f7c192392105b57b1255ba53fd7f5090d9a..c55a79e0968503e92b2603317054bb28df03c6e6 100644 (file)
@@ -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;
                 }
index 8a2b6dba4ac56bad370e7a744118efc95090096d..9f1c0992aa9aa0868cd1a7ee74aee0af62f77c1f 100644 (file)
@@ -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=$?
     ;;