You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@httpd.apache.org by co...@apache.org on 2005/09/19 17:51:34 UTC

svn commit: r290189 - in /httpd/httpd: branches/2.2.x/ branches/2.2.x/include/ branches/2.2.x/server/ branches/2.2.x/server/mpm/experimental/event/ branches/2.2.x/server/mpm/prefork/ branches/2.2.x/server/mpm/worker/ branches/2.2.x/support/ trunk/

Author: colm
Date: Mon Sep 19 08:51:22 2005
New Revision: 290189

URL: http://svn.apache.org/viewcvs?rev=290189&view=rev
Log:

Backport graceful-stop to the 2.2.x branch.


Modified:
    httpd/httpd/branches/2.2.x/CHANGES
    httpd/httpd/branches/2.2.x/include/mpm_common.h
    httpd/httpd/branches/2.2.x/server/main.c
    httpd/httpd/branches/2.2.x/server/mpm/experimental/event/event.c
    httpd/httpd/branches/2.2.x/server/mpm/experimental/event/mpm.h
    httpd/httpd/branches/2.2.x/server/mpm/prefork/mpm.h
    httpd/httpd/branches/2.2.x/server/mpm/prefork/prefork.c
    httpd/httpd/branches/2.2.x/server/mpm/worker/mpm.h
    httpd/httpd/branches/2.2.x/server/mpm/worker/worker.c
    httpd/httpd/branches/2.2.x/server/mpm_common.c
    httpd/httpd/branches/2.2.x/support/apachectl.in
    httpd/httpd/trunk/CHANGES

Modified: httpd/httpd/branches/2.2.x/CHANGES
URL: http://svn.apache.org/viewcvs/httpd/httpd/branches/2.2.x/CHANGES?rev=290189&r1=290188&r2=290189&view=diff
==============================================================================
--- httpd/httpd/branches/2.2.x/CHANGES [utf-8] (original)
+++ httpd/httpd/branches/2.2.x/CHANGES [utf-8] Mon Sep 19 08:51:22 2005
@@ -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>]

Modified: httpd/httpd/branches/2.2.x/include/mpm_common.h
URL: http://svn.apache.org/viewcvs/httpd/httpd/branches/2.2.x/include/mpm_common.h?rev=290189&r1=290188&r2=290189&view=diff
==============================================================================
--- httpd/httpd/branches/2.2.x/include/mpm_common.h (original)
+++ httpd/httpd/branches/2.2.x/include/mpm_common.h Mon Sep 19 08:51:22 2005
@@ -65,6 +65,15 @@
 /* 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 @@
 #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
@@ -277,6 +303,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 *);

Modified: httpd/httpd/branches/2.2.x/server/main.c
URL: http://svn.apache.org/viewcvs/httpd/httpd/branches/2.2.x/server/main.c?rev=290189&r1=290188&r2=290189&view=diff
==============================================================================
--- httpd/httpd/branches/2.2.x/server/main.c (original)
+++ httpd/httpd/branches/2.2.x/server/main.c Mon Sep 19 08:51:22 2005
@@ -318,9 +318,15 @@
                  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);

Modified: httpd/httpd/branches/2.2.x/server/mpm/experimental/event/event.c
URL: http://svn.apache.org/viewcvs/httpd/httpd/branches/2.2.x/server/mpm/experimental/event/event.c?rev=290189&r1=290188&r2=290189&view=diff
==============================================================================
--- httpd/httpd/branches/2.2.x/server/mpm/experimental/event/event.c (original)
+++ httpd/httpd/branches/2.2.x/server/mpm/experimental/event/event.c Mon Sep 19 08:51:22 2005
@@ -416,7 +416,7 @@
  * 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 @@
         return;
     }
     shutdown_pending = 1;
+    is_graceful = graceful;
 }
 
 /* do a graceful restart if graceful == 1 */
@@ -443,7 +444,7 @@
 
 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 @@
     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 @@
 #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 @@
          * 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 @@
     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 @@
                          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 @@
     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}
 };
 

Modified: httpd/httpd/branches/2.2.x/server/mpm/experimental/event/mpm.h
URL: http://svn.apache.org/viewcvs/httpd/httpd/branches/2.2.x/server/mpm/experimental/event/mpm.h?rev=290189&r1=290188&r2=290189&view=diff
==============================================================================
--- httpd/httpd/branches/2.2.x/server/mpm/experimental/event/mpm.h (original)
+++ httpd/httpd/branches/2.2.x/server/mpm/experimental/event/mpm.h Mon Sep 19 08:51:22 2005
@@ -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
 

Modified: httpd/httpd/branches/2.2.x/server/mpm/prefork/mpm.h
URL: http://svn.apache.org/viewcvs/httpd/httpd/branches/2.2.x/server/mpm/prefork/mpm.h?rev=290189&r1=290188&r2=290189&view=diff
==============================================================================
--- httpd/httpd/branches/2.2.x/server/mpm/prefork/mpm.h (original)
+++ httpd/httpd/branches/2.2.x/server/mpm/prefork/mpm.h Mon Sep 19 08:51:22 2005
@@ -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

Modified: httpd/httpd/branches/2.2.x/server/mpm/prefork/prefork.c
URL: http://svn.apache.org/viewcvs/httpd/httpd/branches/2.2.x/server/mpm/prefork/prefork.c?rev=290189&r1=290188&r2=290189&view=diff
==============================================================================
--- httpd/httpd/branches/2.2.x/server/mpm/prefork/prefork.c (original)
+++ httpd/httpd/branches/2.2.x/server/mpm/prefork/prefork.c Mon Sep 19 08:51:22 2005
@@ -138,7 +138,7 @@
 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 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 @@
         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 @@
     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 @@
 #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 @@
 
     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 @@
         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 @@
               "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 }
 };
 

Modified: httpd/httpd/branches/2.2.x/server/mpm/worker/mpm.h
URL: http://svn.apache.org/viewcvs/httpd/httpd/branches/2.2.x/server/mpm/worker/mpm.h?rev=290189&r1=290188&r2=290189&view=diff
==============================================================================
--- httpd/httpd/branches/2.2.x/server/mpm/worker/mpm.h (original)
+++ httpd/httpd/branches/2.2.x/server/mpm/worker/mpm.h Mon Sep 19 08:51:22 2005
@@ -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
 

Modified: httpd/httpd/branches/2.2.x/server/mpm/worker/worker.c
URL: http://svn.apache.org/viewcvs/httpd/httpd/branches/2.2.x/server/mpm/worker/worker.c?rev=290189&r1=290188&r2=290189&view=diff
==============================================================================
--- httpd/httpd/branches/2.2.x/server/mpm/worker/worker.c (original)
+++ httpd/httpd/branches/2.2.x/server/mpm/worker/worker.c Mon Sep 19 08:51:22 2005
@@ -374,7 +374,7 @@
  * 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 @@
         return;
     }
     shutdown_pending = 1;
+    is_graceful = graceful;
 }
 
 /* do a graceful restart if graceful == 1 */
@@ -401,7 +402,7 @@
 
 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 @@
     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 @@
 #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 @@
     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 @@
             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 @@
   "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 }
 };
 

Modified: httpd/httpd/branches/2.2.x/server/mpm_common.c
URL: http://svn.apache.org/viewcvs/httpd/httpd/branches/2.2.x/server/mpm_common.c?rev=290189&r1=290188&r2=290189&view=diff
==============================================================================
--- httpd/httpd/branches/2.2.x/server/mpm_common.c (original)
+++ httpd/httpd/branches/2.2.x/server/mpm_common.c Mon Sep 19 08:51:22 2005
@@ -273,6 +273,38 @@
     } 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 @@
 }
 #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 @@
             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 @@
         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;
                 }

Modified: httpd/httpd/branches/2.2.x/support/apachectl.in
URL: http://svn.apache.org/viewcvs/httpd/httpd/branches/2.2.x/support/apachectl.in?rev=290189&r1=290188&r2=290189&view=diff
==============================================================================
--- httpd/httpd/branches/2.2.x/support/apachectl.in (original)
+++ httpd/httpd/branches/2.2.x/support/apachectl.in Mon Sep 19 08:51:22 2005
@@ -76,7 +76,7 @@
 fi
 
 case $ARGV in
-start|stop|restart|graceful)
+start|stop|restart|graceful|graceful-stop)
     $HTTPD -k $ARGV
     ERROR=$?
     ;;

Modified: httpd/httpd/trunk/CHANGES
URL: http://svn.apache.org/viewcvs/httpd/httpd/trunk/CHANGES?rev=290189&r1=290188&r2=290189&view=diff
==============================================================================
--- httpd/httpd/trunk/CHANGES [utf-8] (original)
+++ httpd/httpd/trunk/CHANGES [utf-8] Mon Sep 19 08:51:22 2005
@@ -23,17 +23,17 @@
 
   *) Doxygen fixup [Neale Ranns <neale ranns.org>, Ian Holsman]
 
-  *) 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]
-
   *) Teach mod_ssl to use arbitrary OIDs in an SSLRequire directive,
      allowing string-valued client certificate attributes to be used for
      access control, as in: SSLRequire "value" in OID("1.3.6.1.4.1.18060.1")
      [Martin Kraemer, David Reid]
 
 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.