]> granicus.if.org Git - apache/blobdiff - server/mpm/worker/worker.c
Add child_status hook for tracking creation/termination of MPM child
[apache] / server / mpm / worker / worker.c
index c35ca94fa8af474a70e97d93ca10315401499fb8..752679d4589492ae234c06839399436eff46b6bd 100644 (file)
@@ -132,8 +132,6 @@ static int resource_shortage = 0;
 static fd_queue_t *worker_queue;
 static fd_queue_info_t *worker_queue_info;
 static int mpm_state = AP_MPMQ_STARTING;
-static int sick_child_detected;
-static ap_generation_t volatile my_generation = 0;
 
 /* data retained by worker across load/unload of the module
  * allocated on first call to pre-config hook; located on
@@ -143,6 +141,29 @@ typedef struct worker_retained_data {
     int first_server_limit;
     int first_thread_limit;
     int module_loads;
+    int sick_child_detected;
+    ap_generation_t my_generation;
+    int volatile is_graceful; /* set from signal handler */
+    int maxclients_reported;
+    int near_maxclients_reported;
+    /*
+     * The max child slot ever assigned, preserved across restarts.  Necessary
+     * to deal with MaxClients changes across AP_SIG_GRACEFUL restarts.  We
+     * use this value to optimize routines that have to scan the entire
+     * scoreboard.
+     */
+    int max_daemons_limit;
+    /*
+     * idle_spawn_rate is the number of children that will be spawned on the
+     * next maintenance cycle if there aren't enough idle servers.  It is
+     * doubled up to MAX_SPAWN_RATE, and reset only when a cycle goes by
+     * without the need to spawn.
+     */
+    int idle_spawn_rate;
+#ifndef MAX_SPAWN_RATE
+#define MAX_SPAWN_RATE        (32)
+#endif
+    int hold_off_on_exponential_spawning;
 } worker_retained_data;
 static worker_retained_data *retained;
 
@@ -167,14 +188,6 @@ typedef struct {
 
 #define ID_FROM_CHILD_THREAD(c, t)    ((c * thread_limit) + t)
 
-/*
- * The max child slot ever assigned, preserved across restarts.  Necessary
- * to deal with MaxClients changes across AP_SIG_GRACEFUL restarts.  We
- * use this value to optimize routines that have to scan the entire
- * scoreboard.
- */
-static int max_daemons_limit = -1;
-
 static ap_worker_pod_t *pod;
 
 /* The worker MPM respects a couple of runtime flags that can aid
@@ -311,7 +324,7 @@ static int worker_query(int query_code, int *result, apr_status_t *rv)
     *rv = APR_SUCCESS;
     switch (query_code) {
         case AP_MPMQ_MAX_DAEMON_USED:
-            *result = max_daemons_limit;
+            *result = retained->max_daemons_limit;
             break;
         case AP_MPMQ_IS_THREADED:
             *result = AP_MPMQ_STATIC;
@@ -350,7 +363,7 @@ static int worker_query(int query_code, int *result, apr_status_t *rv)
             *result = mpm_state;
             break;
         case AP_MPMQ_GENERATION:
-            *result = my_generation;
+            *result = retained->my_generation;
             break;
         default:
             *rv = APR_ENOTIMPL;
@@ -359,10 +372,47 @@ static int worker_query(int query_code, int *result, apr_status_t *rv)
     return OK;
 }
 
-static apr_status_t worker_note_child_killed(int childnum)
+static void worker_note_child_killed(int childnum, pid_t pid, ap_generation_t gen)
 {
-    ap_scoreboard_image->parent[childnum].pid = 0;
-    return APR_SUCCESS;
+    if (childnum != -1) { /* child had a scoreboard slot? */
+        ap_run_child_status(ap_server_conf,
+                            ap_scoreboard_image->parent[childnum].pid,
+                            ap_scoreboard_image->parent[childnum].generation,
+                            childnum, MPM_CHILD_EXITED);
+        ap_scoreboard_image->parent[childnum].pid = 0;
+    }
+    else {
+        ap_run_child_status(ap_server_conf, pid, gen, -1, MPM_CHILD_EXITED);
+    }
+}
+
+static void worker_note_child_started(int slot, pid_t pid)
+{
+    ap_scoreboard_image->parent[slot].pid = pid;
+    ap_run_child_status(ap_server_conf,
+                        ap_scoreboard_image->parent[slot].pid,
+                        retained->my_generation, slot, MPM_CHILD_STARTED);
+}
+
+static void worker_note_child_lost_slot(int slot, pid_t newpid)
+{
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf,
+                 "pid %" APR_PID_T_FMT " taking over scoreboard slot from "
+                 "%" APR_PID_T_FMT "%s",
+                 newpid,
+                 ap_scoreboard_image->parent[slot].pid,
+                 ap_scoreboard_image->parent[slot].quiescing ?
+                 " (quiescing)" : "");
+    ap_run_child_status(ap_server_conf,
+                        ap_scoreboard_image->parent[slot].pid,
+                        ap_scoreboard_image->parent[slot].generation,
+                        slot, MPM_CHILD_LOST_SLOT);
+    /* Don't forget about this exiting child process, or we
+     * won't be able to kill it if it doesn't exit by the
+     * time the server is shut down.
+     */
+    ap_register_extra_mpm_process(ap_scoreboard_image->parent[slot].pid,
+                                  ap_scoreboard_image->parent[slot].generation);
 }
 
 static const char *worker_get_name(void)
@@ -378,6 +428,11 @@ static void clean_child_exit(int code)
     if (pchild) {
         apr_pool_destroy(pchild);
     }
+
+    if (one_process) {
+        worker_note_child_killed(/* slot */ 0, 0, 0);
+    }
+
     exit(code);
 }
 
@@ -390,11 +445,11 @@ static void just_die(int sig)
  * Connection structures and accounting...
  */
 
-/* volatile just in case */
+static int child_fatal;
+
+/* volatile because they're updated from a signal handler */
 static int volatile shutdown_pending;
 static int volatile restart_pending;
-static int volatile is_graceful;
-static volatile int child_fatal;
 
 /*
  * ap_start_shutdown() and ap_start_restart(), below, are a first stab at
@@ -426,7 +481,7 @@ static void ap_start_shutdown(int graceful)
         return;
     }
     shutdown_pending = 1;
-    is_graceful = graceful;
+    retained->is_graceful = graceful;
 }
 
 /* do a graceful restart if graceful == 1 */
@@ -438,7 +493,7 @@ static void ap_start_restart(int graceful)
         return;
     }
     restart_pending = 1;
-    is_graceful = graceful;
+    retained->is_graceful = graceful;
 }
 
 static void sig_term(int sig)
@@ -486,7 +541,10 @@ static void set_signals(void)
                      "sigaction(SIGXCPU)");
 #endif
 #ifdef SIGXFSZ
-    sa.sa_handler = SIG_DFL;
+    /* For systems following the LFS standard, ignoring SIGXFSZ allows
+     * a write() beyond the 2GB limit to fail gracefully with E2BIG
+     * rather than terminate the process. */
+    sa.sa_handler = SIG_IGN;
     if (sigaction(SIGXFSZ, &sa, NULL) < 0)
         ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf,
                      "sigaction(SIGXFSZ)");
@@ -515,7 +573,7 @@ static void set_signals(void)
         apr_signal(SIGXCPU, SIG_DFL);
 #endif /* SIGXCPU */
 #ifdef SIGXFSZ
-        apr_signal(SIGXFSZ, SIG_DFL);
+        apr_signal(SIGXFSZ, SIG_IGN);
 #endif /* SIGXFSZ */
     }
 
@@ -564,7 +622,7 @@ static void process_socket(apr_thread_t *thd, apr_pool_t *p, apr_socket_t *sock,
 }
 
 /* requests_this_child has gone to zero or below.  See if the admin coded
-   "MaxRequestsPerChild 0", and keep going in that case.  Doing it this way
+   "MaxConnectionsPerChild 0", and keep going in that case.  Doing it this way
    simplifies the hot path in worker_thread */
 static void check_infinite_requests(void)
 {
@@ -726,8 +784,7 @@ static void * APR_THREAD_FUNC listener_thread(apr_thread_t *thd, void * dummy)
 
                     /* apr_pollset_poll() will only return errors in catastrophic
                      * circumstances. Let's try exiting gracefully, for now. */
-                    ap_log_error(APLOG_MARK, APLOG_ERR, rv,
-                                 (const server_rec *) ap_server_conf,
+                    ap_log_error(APLOG_MARK, APLOG_ERR, rv, ap_server_conf,
                                  "apr_pollset_poll: (listen)");
                     signal_threads(ST_GRACEFUL);
                 }
@@ -858,7 +915,7 @@ static void * APR_THREAD_FUNC worker_thread(apr_thread_t *thd, void * dummy)
 
     ap_scoreboard_image->servers[process_slot][thread_slot].pid = ap_my_pid;
     ap_scoreboard_image->servers[process_slot][thread_slot].tid = apr_os_thread_current();
-    ap_scoreboard_image->servers[process_slot][thread_slot].generation = my_generation;
+    ap_scoreboard_image->servers[process_slot][thread_slot].generation = retained->my_generation;
     ap_update_child_status_from_indexes(process_slot, thread_slot, SERVER_STARTING, NULL);
 
 #ifdef HAVE_PTHREAD_KILL
@@ -1277,7 +1334,7 @@ static void child_main(int child_num_arg)
     else { /* !one_process */
         /* remove SIGTERM from the set of blocked signals...  if one of
          * the other threads in the process needs to take us down
-         * (e.g., for MaxRequestsPerChild) it will send us SIGTERM
+         * (e.g., for MaxConnectionsPerChild) it will send us SIGTERM
          */
         unblock_signal(SIGTERM);
         apr_signal(SIGTERM, dummy_signal_handler);
@@ -1324,14 +1381,15 @@ static int make_child(server_rec *s, int slot)
 {
     int pid;
 
-    if (slot + 1 > max_daemons_limit) {
-        max_daemons_limit = slot + 1;
+    if (slot + 1 > retained->max_daemons_limit) {
+        retained->max_daemons_limit = slot + 1;
     }
 
     if (one_process) {
         set_signals();
-        ap_scoreboard_image->parent[slot].pid = getpid();
+        worker_note_child_started(slot, getpid());
         child_main(slot);
+        /* NOTREACHED */
     }
 
     if ((pid = fork()) == -1) {
@@ -1368,27 +1426,18 @@ static int make_child(server_rec *s, int slot)
 
         apr_signal(SIGTERM, just_die);
         child_main(slot);
-
-        clean_child_exit(0);
+        /* NOTREACHED */
     }
     /* else */
     if (ap_scoreboard_image->parent[slot].pid != 0) {
         /* This new child process is squatting on the scoreboard
          * entry owned by an exiting child process, which cannot
          * exit until all active requests complete.
-         * Don't forget about this exiting child process, or we
-         * won't be able to kill it if it doesn't exit by the
-         * time the server is shut down.
          */
-        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf,
-                     "taking over scoreboard slot from %" APR_PID_T_FMT "%s",
-                     ap_scoreboard_image->parent[slot].pid,
-                     ap_scoreboard_image->parent[slot].quiescing ?
-                         " (quiescing)" : "");
-        ap_register_extra_mpm_process(ap_scoreboard_image->parent[slot].pid);
+        worker_note_child_lost_slot(slot, pid);
     }
     ap_scoreboard_image->parent[slot].quiescing = 0;
-    ap_scoreboard_image->parent[slot].pid = pid;
+    worker_note_child_started(slot, pid);
     return 0;
 }
 
@@ -1408,19 +1457,6 @@ static void startup_children(int number_to_start)
     }
 }
 
-
-/*
- * idle_spawn_rate is the number of children that will be spawned on the
- * next maintenance cycle if there aren't enough idle servers.  It is
- * doubled up to MAX_SPAWN_RATE, and reset only when a cycle goes by
- * without the need to spawn.
- */
-static int idle_spawn_rate = 1;
-#ifndef MAX_SPAWN_RATE
-#define MAX_SPAWN_RATE        (32)
-#endif
-static int hold_off_on_exponential_spawning;
-
 static void perform_idle_server_maintenance(void)
 {
     int i, j;
@@ -1449,7 +1485,7 @@ static void perform_idle_server_maintenance(void)
         int any_dead_threads = 0;
         int all_dead_threads = 1;
 
-        if (i >= max_daemons_limit && totally_free_length == idle_spawn_rate)
+        if (i >= retained->max_daemons_limit && totally_free_length == retained->idle_spawn_rate)
             /* short cut if all active processes have been examined and
              * enough empty scoreboard slots have been found
              */
@@ -1477,7 +1513,7 @@ static void perform_idle_server_maintenance(void)
                                    loop if no pid?  not much else matters */
                 if (status <= SERVER_READY && 
                         !ps->quiescing &&
-                        ps->generation == my_generation) {
+                        ps->generation == retained->my_generation) {
                     ++idle_thread_count;
                 }
                 if (status >= SERVER_READY && status < SERVER_GRACEFUL) {
@@ -1485,7 +1521,7 @@ static void perform_idle_server_maintenance(void)
                 }
             }
         }
-        if (any_dead_threads && totally_free_length < idle_spawn_rate
+        if (any_dead_threads && totally_free_length < retained->idle_spawn_rate
                 && free_length < MAX_SPAWN_RATE
                 && (!ps->pid               /* no process in the slot */
                     || ps->quiescing)) {   /* or at least one is going away */
@@ -1514,12 +1550,12 @@ static void perform_idle_server_maintenance(void)
         }
     }
 
-    if (sick_child_detected) {
+    if (retained->sick_child_detected) {
         if (active_thread_count > 0) {
             /* some child processes appear to be working.  don't kill the
              * whole server.
              */
-            sick_child_detected = 0;
+            retained->sick_child_detected = 0;
         }
         else {
             /* looks like a basket case.  give up.
@@ -1535,12 +1571,12 @@ static void perform_idle_server_maintenance(void)
         }
     }
 
-    max_daemons_limit = last_non_dead + 1;
+    retained->max_daemons_limit = last_non_dead + 1;
 
     if (idle_thread_count > max_spare_threads) {
         /* Kill off one child */
         ap_worker_pod_signal(pod, TRUE);
-        idle_spawn_rate = 1;
+        retained->idle_spawn_rate = 1;
     }
     else if (idle_thread_count < min_spare_threads) {
         /* terminate the free list */
@@ -1550,23 +1586,21 @@ static void perform_idle_server_maintenance(void)
                 /* no threads are "inactive" - starting, stopping, etc. */
                 /* have we reached MaxClients, or just getting close? */
                 if (0 == idle_thread_count) {
-                    static int reported = 0;
-                    if (!reported) {
+                    if (!retained->maxclients_reported) {
                         /* only report this condition once */
                         ap_log_error(APLOG_MARK, APLOG_ERR, 0,
                                      ap_server_conf,
                                      "server reached MaxClients setting, consider"
                                      " raising the MaxClients setting");
-                        reported = 1;
+                        retained->maxclients_reported = 1;
                     }
                 } else {
-                    static int reported = 0;
-                    if (!reported) {
+                    if (!retained->near_maxclients_reported) {
                         ap_log_error(APLOG_MARK, APLOG_ERR, 0,
                                      ap_server_conf,
                                      "server is within MinSpareThreads of MaxClients, "
                                      "consider raising the MaxClients setting");
-                        reported = 1;
+                        retained->near_maxclients_reported = 1;
                     }
                 }
             }
@@ -1575,13 +1609,13 @@ static void perform_idle_server_maintenance(void)
                              ap_server_conf,
                              "scoreboard is full, not at MaxClients");
             }
-            idle_spawn_rate = 1;
+            retained->idle_spawn_rate = 1;
         }
         else {
-            if (free_length > idle_spawn_rate) {
-                free_length = idle_spawn_rate;
+            if (free_length > retained->idle_spawn_rate) {
+                free_length = retained->idle_spawn_rate;
             }
-            if (idle_spawn_rate >= 8) {
+            if (retained->idle_spawn_rate >= 8) {
                 ap_log_error(APLOG_MARK, APLOG_INFO, 0,
                              ap_server_conf,
                              "server seems busy, (you may need "
@@ -1597,21 +1631,22 @@ static void perform_idle_server_maintenance(void)
             /* the next time around we want to spawn twice as many if this
              * wasn't good enough, but not if we've just done a graceful
              */
-            if (hold_off_on_exponential_spawning) {
-                --hold_off_on_exponential_spawning;
+            if (retained->hold_off_on_exponential_spawning) {
+                --retained->hold_off_on_exponential_spawning;
             }
-            else if (idle_spawn_rate < MAX_SPAWN_RATE) {
-                idle_spawn_rate *= 2;
+            else if (retained->idle_spawn_rate < MAX_SPAWN_RATE) {
+                retained->idle_spawn_rate *= 2;
             }
         }
     }
     else {
-      idle_spawn_rate = 1;
+      retained->idle_spawn_rate = 1;
     }
 }
 
 static void server_main_loop(int remaining_children_to_start)
 {
+    ap_generation_t old_gen;
     int child_slot;
     apr_exit_why_e exitwhy;
     int status, processed_status;
@@ -1632,7 +1667,7 @@ static void server_main_loop(int remaining_children_to_start)
                 /* tell perform_idle_server_maintenance to check into this
                  * on the next timer pop
                  */
-                sick_child_detected = 1;
+                retained->sick_child_detected = 1;
             }
             /* non-fatal death... note that it's gone in the scoreboard. */
             child_slot = ap_find_child_by_pid(&pid);
@@ -1641,11 +1676,11 @@ static void server_main_loop(int remaining_children_to_start)
                     ap_update_child_status_from_indexes(child_slot, i, SERVER_DEAD,
                                                         (request_rec *) NULL);
 
-                ap_scoreboard_image->parent[child_slot].pid = 0;
+                worker_note_child_killed(child_slot, 0, 0);
                 ap_scoreboard_image->parent[child_slot].quiescing = 0;
                 if (processed_status == APEXIT_CHILDSICK) {
                     /* resource shortage, minimize the fork rate */
-                    idle_spawn_rate = 1;
+                    retained->idle_spawn_rate = 1;
                 }
                 else if (remaining_children_to_start
                     && child_slot < ap_daemons_limit) {
@@ -1656,8 +1691,9 @@ static void server_main_loop(int remaining_children_to_start)
                     --remaining_children_to_start;
                 }
             }
-            else if (ap_unregister_extra_mpm_process(pid.pid) == 1) {
-                /* handled */
+            else if (ap_unregister_extra_mpm_process(pid.pid, &old_gen) == 1) {
+                worker_note_child_killed(-1, /* already out of the scoreboard */
+                                         pid.pid, old_gen);
 #if APR_HAS_OTHER_CHILD
             }
             else if (apr_proc_other_child_alert(&pid, APR_OC_REASON_DEATH,
@@ -1665,7 +1701,7 @@ static void server_main_loop(int remaining_children_to_start)
                 /* handled */
 #endif
             }
-            else if (is_graceful) {
+            else if (retained->is_graceful) {
                 /* Great, we've probably just lost a slot in the
                  * scoreboard.  Somehow we don't know about this child.
                  */
@@ -1707,14 +1743,14 @@ static int worker_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
     ap_log_pid(pconf, ap_pid_fname);
 
     /* Initialize cross-process accept lock */
-    rv = ap_proc_mutex_create(&accept_mutex, AP_ACCEPT_MUTEX_TYPE, NULL, s,
-                              _pconf, 0);
+    rv = ap_proc_mutex_create(&accept_mutex, NULL, AP_ACCEPT_MUTEX_TYPE, NULL,
+                              s, _pconf, 0);
     if (rv != APR_SUCCESS) {
         mpm_state = AP_MPMQ_STOPPING;
         return DONE;
     }
 
-    if (!is_graceful) {
+    if (!retained->is_graceful) {
         if (ap_run_pre_mpm(s->process->pool, SB_SHARED) != OK) {
             mpm_state = AP_MPMQ_STOPPING;
             return DONE;
@@ -1722,7 +1758,7 @@ static int worker_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
         /* fix the generation number in the global score; we just got a new,
          * cleared scoreboard
          */
-        ap_scoreboard_image->global->running_generation = my_generation;
+        ap_scoreboard_image->global->running_generation = retained->my_generation;
     }
 
     set_signals();
@@ -1743,14 +1779,14 @@ static int worker_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
     if (remaining_children_to_start > ap_daemons_limit) {
         remaining_children_to_start = ap_daemons_limit;
     }
-    if (!is_graceful) {
+    if (!retained->is_graceful) {
         startup_children(remaining_children_to_start);
         remaining_children_to_start = 0;
     }
     else {
         /* give the system some time to recover before kicking into
             * exponential mode */
-        hold_off_on_exponential_spawning = 10;
+        retained->hold_off_on_exponential_spawning = 10;
     }
 
     ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf,
@@ -1769,23 +1805,17 @@ static int worker_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 && !is_graceful) {
+    if (shutdown_pending && !retained->is_graceful) {
         /* Time to shut down:
          * Kill child processes, tell them to call child_exit, etc...
          */
         ap_worker_pod_killpg(pod, ap_daemons_limit, FALSE);
-        ap_reclaim_child_processes(1);                /* Start with SIGTERM */
+        ap_reclaim_child_processes(1, /* Start with SIGTERM */
+                                   worker_note_child_killed);
 
         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_remove_pid(pconf, ap_pid_fname);
             ap_log_error(APLOG_MARK, APLOG_NOTICE, 0,
                          ap_server_conf, "caught SIGTERM, shutting down");
         }
@@ -1801,18 +1831,11 @@ static int worker_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
         /* Close our listeners, and then ask our children to do same */
         ap_close_listeners();
         ap_worker_pod_killpg(pod, ap_daemons_limit, TRUE);
-        ap_relieve_child_processes();
+        ap_relieve_child_processes(worker_note_child_killed);
 
         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_remove_pid(pconf, ap_pid_fname);
             ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf,
                          "caught " AP_SIG_GRACEFUL_STOP_STRING
                          ", shutting down gracefully");
@@ -1830,7 +1853,7 @@ static int worker_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
             apr_sleep(apr_time_from_sec(1));
 
             /* Relieve any children which have now exited */
-            ap_relieve_child_processes();
+            ap_relieve_child_processes(worker_note_child_killed);
 
             active_children = 0;
             for (index = 0; index < ap_daemons_limit; ++index) {
@@ -1848,7 +1871,7 @@ static int worker_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
          * really dead.
          */
         ap_worker_pod_killpg(pod, ap_daemons_limit, FALSE);
-        ap_reclaim_child_processes(1);
+        ap_reclaim_child_processes(1, worker_note_child_killed);
 
         return DONE;
     }
@@ -1865,10 +1888,10 @@ static int worker_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
     /* XXX: we really need to make sure this new generation number isn't in
      * use by any of the children.
      */
-    ++my_generation;
-    ap_scoreboard_image->global->running_generation = my_generation;
+    ++retained->my_generation;
+    ap_scoreboard_image->global->running_generation = retained->my_generation;
 
-    if (is_graceful) {
+    if (retained->is_graceful) {
         ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf,
                      AP_SIG_GRACEFUL_STRING " received.  Doing graceful restart");
         /* wake up the children...time to die.  But we'll have more soon */
@@ -1887,7 +1910,8 @@ static int worker_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
          */
         ap_worker_pod_killpg(pod, ap_daemons_limit, FALSE);
 
-        ap_reclaim_child_processes(1);                /* Start with SIGTERM */
+        ap_reclaim_child_processes(1, /* Start with SIGTERM */
+                                   worker_note_child_killed);
         ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf,
                     "SIGHUP received.  Attempting to restart");
     }
@@ -1957,11 +1981,11 @@ static int worker_pre_config(apr_pool_t *pconf, apr_pool_t *plog,
     retained = ap_retained_data_get(userdata_key);
     if (!retained) {
         retained = ap_retained_data_create(userdata_key, sizeof(*retained));
+        retained->max_daemons_limit = -1;
+        retained->idle_spawn_rate = 1;
     }
     ++retained->module_loads;
     if (retained->module_loads == 2) {
-        is_graceful = 0;
-
         if (!one_process && !foreground) {
             rv = apr_proc_detach(no_detach ? APR_PROC_DETACH_FOREGROUND
                                            : APR_PROC_DETACH_DAEMONIZE);
@@ -1971,9 +1995,10 @@ static int worker_pre_config(apr_pool_t *pconf, apr_pool_t *plog,
                 return HTTP_INTERNAL_SERVER_ERROR;
             }
         }
-        parent_pid = ap_my_pid = getpid();
     }
 
+    parent_pid = ap_my_pid = getpid();
+
     ap_listen_pre_config();
     ap_daemons_to_start = DEFAULT_START_DAEMON;
     min_spare_threads = DEFAULT_MIN_FREE_DAEMON * DEFAULT_THREADS_PER_CHILD;
@@ -1983,12 +2008,7 @@ static int worker_pre_config(apr_pool_t *pconf, apr_pool_t *plog,
     ap_daemons_limit = server_limit;
     threads_per_child = DEFAULT_THREADS_PER_CHILD;
     max_clients = ap_daemons_limit * threads_per_child;
-    ap_pid_fname = DEFAULT_PIDLOG;
-    ap_max_requests_per_child = DEFAULT_MAX_REQUESTS_PER_CHILD;
     ap_extended_status = 0;
-    ap_max_mem_free = APR_ALLOCATOR_MAX_FREE_UNLIMITED;
-
-    apr_cpystrn(ap_coredump_dir, ap_server_root, sizeof(ap_coredump_dir));
 
     return OK;
 }
@@ -1996,11 +2016,10 @@ static int worker_pre_config(apr_pool_t *pconf, apr_pool_t *plog,
 static int worker_check_config(apr_pool_t *p, apr_pool_t *plog,
                                apr_pool_t *ptemp, server_rec *s)
 {
-    static int restart_num = 0;
     int startup = 0;
 
     /* the reverse of pre_config, we want this only the first time around */
-    if (restart_num++ == 0) {
+    if (retained->module_loads == 1) {
         startup = 1;
     }
 
@@ -2246,7 +2265,6 @@ static void worker_hooks(apr_pool_t *p)
     ap_hook_check_config(worker_check_config, NULL, NULL, APR_HOOK_MIDDLE);
     ap_hook_mpm(worker_run, NULL, NULL, APR_HOOK_MIDDLE);
     ap_hook_mpm_query(worker_query, NULL, NULL, APR_HOOK_MIDDLE);
-    ap_hook_mpm_note_child_killed(worker_note_child_killed, NULL, NULL, APR_HOOK_MIDDLE);
     ap_hook_mpm_get_name(worker_get_name, NULL, NULL, APR_HOOK_MIDDLE);
 }
 
@@ -2352,7 +2370,7 @@ AP_GRACEFUL_SHUTDOWN_TIMEOUT_COMMAND,
 { NULL }
 };
 
-module AP_MODULE_DECLARE_DATA mpm_worker_module = {
+AP_DECLARE_MODULE(mpm_worker) = {
     MPM20_MODULE_STUFF,
     NULL,                       /* hook to run before apache parses args */
     NULL,                       /* create per-directory config structure */