Changes with Apache 2.4.26
+ *) MPMs unix: Place signals handlers and helpers out of DSOs to avoid
+ a possible crash if a signal is caught during (graceful) restart.
+ PR 60487. [Yann Ylavic]
+
*) core: Deprecate ap_get_basic_auth_pw() and add
ap_get_basic_auth_components().
[Emmanuel Dreyfus <manu netbsd.org>, Jacob Champion, Eric Covener]
PATCHES ACCEPTED TO BACKPORT FROM TRUNK:
[ start all new proposals below, under PATCHES PROPOSED. ]
- *) MPMs unix: Place signals handlers and helpers out of DSOs to avoid a
- possible crash if a signal is caught during (graceful) restart. PR 60487.
- trunk patch: http://svn.apache.org/r1783849
- 2.4.x patch: svn merge -c 1783849 ^/httpd/httpd/trunk .
- (trunk works, modulo CHANGES)
- +1: ylavic, wrowe, icing
-
PATCHES PROPOSED TO BACKPORT FROM TRUNK:
[ New proposals should be added at the end of the list ]
}
+/* Unixes MPMs' */
+
+static ap_unixd_mpm_retained_data *retained_data = NULL;
+AP_DECLARE(ap_unixd_mpm_retained_data *) ap_unixd_mpm_get_retained_data()
+{
+ if (!retained_data) {
+ retained_data = ap_retained_data_create("ap_unixd_mpm_retained_data",
+ sizeof(*retained_data));
+ retained_data->mpm_state = AP_MPMQ_STARTING;
+ }
+ return retained_data;
+}
+
+static void sig_term(int sig)
+{
+ retained_data->mpm_state = AP_MPMQ_STOPPING;
+ if (retained_data->shutdown_pending
+ && (retained_data->is_ungraceful
+ || sig == AP_SIG_GRACEFUL_STOP)) {
+ /* Already handled */
+ return;
+ }
+
+ retained_data->shutdown_pending = 1;
+ if (sig != AP_SIG_GRACEFUL_STOP) {
+ retained_data->is_ungraceful = 1;
+ }
+}
+
+static void sig_restart(int sig)
+{
+ retained_data->mpm_state = AP_MPMQ_STOPPING;
+ if (retained_data->restart_pending
+ && (retained_data->is_ungraceful
+ || sig == AP_SIG_GRACEFUL)) {
+ /* Already handled */
+ return;
+ }
+
+ retained_data->restart_pending = 1;
+ if (sig != AP_SIG_GRACEFUL) {
+ retained_data->is_ungraceful = 1;
+ }
+}
+
+static apr_status_t unset_signals(void *unused)
+{
+ retained_data->shutdown_pending = retained_data->restart_pending = 0;
+ retained_data->was_graceful = !retained_data->is_ungraceful;
+ retained_data->is_ungraceful = 0;
+
+ return APR_SUCCESS;
+}
+
+AP_DECLARE(void) ap_unixd_mpm_set_signals(apr_pool_t *pconf, int one_process)
+{
+#ifndef NO_USE_SIGACTION
+ struct sigaction sa;
+#endif
+
+ /* Signals' handlers depend on retained data */
+ (void)ap_unixd_mpm_get_retained_data();
+
+#ifndef NO_USE_SIGACTION
+ memset(&sa, 0, sizeof sa);
+ sigemptyset(&sa.sa_mask);
+
+#ifdef SIGPIPE
+ sa.sa_handler = SIG_IGN;
+ if (sigaction(SIGPIPE, &sa, NULL) < 0)
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, APLOGNO(00269)
+ "sigaction(SIGPIPE)");
+#endif
+#ifdef SIGXCPU
+ sa.sa_handler = SIG_DFL;
+ if (sigaction(SIGXCPU, &sa, NULL) < 0)
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, APLOGNO(00267)
+ "sigaction(SIGXCPU)");
+#endif
+#ifdef SIGXFSZ
+ /* 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, APLOGNO(00268)
+ "sigaction(SIGXFSZ)");
+#endif
+
+ sa.sa_handler = sig_term;
+ if (sigaction(SIGTERM, &sa, NULL) < 0)
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, APLOGNO(00264)
+ "sigaction(SIGTERM)");
+#ifdef SIGINT
+ if (sigaction(SIGINT, &sa, NULL) < 0)
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, APLOGNO(00266)
+ "sigaction(SIGINT)");
+#endif
+#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, APLOGNO(00265)
+ "sigaction(" AP_SIG_GRACEFUL_STOP_STRING ")");
+#endif
+
+ /* Don't catch restart signals in ONE_PROCESS mode :) */
+ if (!one_process) {
+ sa.sa_handler = sig_restart;
+ if (sigaction(SIGHUP, &sa, NULL) < 0)
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, APLOGNO(00270)
+ "sigaction(SIGHUP)");
+ if (sigaction(AP_SIG_GRACEFUL, &sa, NULL) < 0)
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, APLOGNO(00271)
+ "sigaction(" AP_SIG_GRACEFUL_STRING ")");
+ }
+
+#else /* NO_USE_SIGACTION */
+
+#ifdef SIGPIPE
+ apr_signal(SIGPIPE, SIG_IGN);
+#endif /* SIGPIPE */
+#ifdef SIGXCPU
+ apr_signal(SIGXCPU, SIG_DFL);
+#endif /* SIGXCPU */
+#ifdef SIGXFSZ
+ apr_signal(SIGXFSZ, SIG_IGN);
+#endif /* SIGXFSZ */
+
+ apr_signal(SIGTERM, sig_term);
+#ifdef AP_SIG_GRACEFUL_STOP
+ apr_signal(AP_SIG_GRACEFUL_STOP, sig_term);
+#endif /* AP_SIG_GRACEFUL_STOP */
+
+ if (!one_process) {
+ /* Don't restart in ONE_PROCESS mode :) */
+#ifdef SIGHUP
+ apr_signal(SIGHUP, sig_restart);
+#endif /* SIGHUP */
+#ifdef AP_SIG_GRACEFUL
+ apr_signal(AP_SIG_GRACEFUL, sig_restart);
+#endif /* AP_SIG_GRACEFUL */
+ }
+
+#endif /* NO_USE_SIGACTION */
+
+ apr_pool_cleanup_register(pconf, NULL, unset_signals,
+ apr_pool_cleanup_null);
+}
+
+
#ifdef _OSD_POSIX
#include "apr_lib.h"
#include "httpd.h"
#include "http_config.h"
+#include "scoreboard.h"
#include "ap_listen.h"
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#define ap_os_killpg(x, y) (kill (-(x), (y)))
#endif /* HAVE_KILLPG */
+typedef struct {
+ void *baton; /* MPM's */
+
+ /* volatile because they're updated from signals' handlers */
+ int volatile mpm_state;
+ int volatile shutdown_pending;
+ int volatile restart_pending;
+ int volatile is_ungraceful;
+
+ ap_generation_t my_generation;
+ int module_loads;
+ int was_graceful;
+
+ /*
+ * Current number of listeners buckets and maximum reached across
+ * restarts (to size retained data according to dynamic num_buckets,
+ * eg. idle_spawn_rate).
+ */
+ int num_buckets, max_buckets;
+} ap_unixd_mpm_retained_data;
+
+AP_DECLARE(ap_unixd_mpm_retained_data *) ap_unixd_mpm_get_retained_data(void);
+AP_DECLARE(void) ap_unixd_mpm_set_signals(apr_pool_t *pconf, int once_process);
+
#ifdef __cplusplus
}
#endif
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 apr_thread_mutex_t *timeout_mutex;
* subsequent calls to pre-config hook
*/
typedef struct event_retained_data {
+ ap_unixd_mpm_retained_data *mpm;
+
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;
/*
* The max child slot ever assigned, preserved across restarts. Necessary
#define MAX_SPAWN_RATE (32)
#endif
int hold_off_on_exponential_spawning;
- /*
- * Current number of listeners buckets and maximum reached across
- * restarts (to size retained data according to dynamic num_buckets,
- * eg. idle_spawn_rate).
- */
- int num_buckets, max_buckets;
} event_retained_data;
static event_retained_data *retained;
return;
}
terminate_mode = mode;
- mpm_state = AP_MPMQ_STOPPING;
+ retained->mpm->mpm_state = AP_MPMQ_STOPPING;
/* in case we weren't called from the listener thread, wake up the
* listener thread
*result = active_daemons_limit;
break;
case AP_MPMQ_MPM_STATE:
- *result = mpm_state;
+ *result = retained->mpm->mpm_state;
break;
case AP_MPMQ_GENERATION:
- *result = retained->my_generation;
+ *result = retained->mpm->my_generation;
break;
default:
*rv = APR_ENOTIMPL;
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);
+ retained->mpm->my_generation, slot, MPM_CHILD_STARTED);
}
static const char *event_get_name(void)
static void clean_child_exit(int code) __attribute__ ((noreturn));
static void clean_child_exit(int code)
{
- mpm_state = AP_MPMQ_STOPPING;
+ retained->mpm->mpm_state = AP_MPMQ_STOPPING;
if (pchild) {
apr_pool_destroy(pchild);
}
static int child_fatal;
-/* volatile because they're updated from a signal handler */
-static int volatile shutdown_pending;
-static int volatile restart_pending;
-
static apr_status_t decrement_connection_count(void *cs_)
{
event_conn_state_t *cs = cs_;
return APR_SUCCESS;
}
-/*
- * ap_start_shutdown() and ap_start_restart(), below, are a first stab at
- * functions to initiate shutdown or restart without relying on signals.
- * Previously this was initiated in sig_term() and restart() signal handlers,
- * but we want to be able to start a shutdown/restart from other sources --
- * e.g. on Win32, from the service manager. Now the service manager can
- * call ap_start_shutdown() or ap_start_restart() as appropriate. Note that
- * these functions can also be called by the child processes, since global
- * variables are no longer used to pass on the required action to the parent.
- *
- * These should only be called from the parent process itself, since the
- * parent process will use the shutdown_pending and restart_pending variables
- * to determine whether to shutdown or restart. The child process should
- * call signal_parent() directly to tell the parent to die -- this will
- * cause neither of those variable to be set, which the parent will
- * assume means something serious is wrong (which it will be, for the
- * child to force an exit) and so do an exit anyway.
- */
-
-static void ap_start_shutdown(int graceful)
-{
- mpm_state = AP_MPMQ_STOPPING;
- if (shutdown_pending == 1) {
- /* Um, is this _probably_ not an error, if the user has
- * tried to do a shutdown twice quickly, so we won't
- * worry about reporting it.
- */
- return;
- }
- shutdown_pending = 1;
- retained->is_graceful = graceful;
-}
-
-/* do a graceful restart if graceful == 1 */
-static void ap_start_restart(int graceful)
-{
- mpm_state = AP_MPMQ_STOPPING;
- if (restart_pending == 1) {
- /* Probably not an error - don't bother reporting it */
- return;
- }
- restart_pending = 1;
- retained->is_graceful = graceful;
-}
-
-static void sig_term(int sig)
-{
- ap_start_shutdown(sig == AP_SIG_GRACEFUL_STOP);
-}
-
-static void restart(int sig)
-{
- ap_start_restart(sig == AP_SIG_GRACEFUL);
-}
-
-static void set_signals(void)
-{
-#ifndef NO_USE_SIGACTION
- struct sigaction sa;
-#endif
-
- if (!one_process) {
- ap_fatal_signal_setup(ap_server_conf, pconf);
- }
-
-#ifndef NO_USE_SIGACTION
- sigemptyset(&sa.sa_mask);
- sa.sa_flags = 0;
-
- sa.sa_handler = sig_term;
- if (sigaction(SIGTERM, &sa, NULL) < 0)
- ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, APLOGNO(00459)
- "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, APLOGNO(00460)
- "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, APLOGNO(00461)
- "sigaction(SIGINT)");
-#endif
-#ifdef SIGXCPU
- sa.sa_handler = SIG_DFL;
- if (sigaction(SIGXCPU, &sa, NULL) < 0)
- ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, APLOGNO(00462)
- "sigaction(SIGXCPU)");
-#endif
-#ifdef SIGXFSZ
- /* 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, APLOGNO(00463)
- "sigaction(SIGXFSZ)");
-#endif
-#ifdef SIGPIPE
- sa.sa_handler = SIG_IGN;
- if (sigaction(SIGPIPE, &sa, NULL) < 0)
- ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, APLOGNO(00464)
- "sigaction(SIGPIPE)");
-#endif
-
- /* we want to ignore HUPs and AP_SIG_GRACEFUL while we're busy
- * processing one */
- sigaddset(&sa.sa_mask, SIGHUP);
- sigaddset(&sa.sa_mask, AP_SIG_GRACEFUL);
- sa.sa_handler = restart;
- if (sigaction(SIGHUP, &sa, NULL) < 0)
- ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, APLOGNO(00465)
- "sigaction(SIGHUP)");
- if (sigaction(AP_SIG_GRACEFUL, &sa, NULL) < 0)
- ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, APLOGNO(00466)
- "sigaction(" AP_SIG_GRACEFUL_STRING ")");
-#else
- if (!one_process) {
-#ifdef SIGXCPU
- apr_signal(SIGXCPU, SIG_DFL);
-#endif /* SIGXCPU */
-#ifdef SIGXFSZ
- apr_signal(SIGXFSZ, SIG_IGN);
-#endif /* SIGXFSZ */
- }
-
- apr_signal(SIGTERM, sig_term);
-#ifdef SIGHUP
- apr_signal(SIGHUP, restart);
-#endif /* SIGHUP */
-#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 */
-
-#endif
-}
-
static void notify_suspend(event_conn_state_t *cs)
{
ap_run_suspend_connection(cs->c, cs->r);
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 = retained->my_generation;
+ ap_scoreboard_image->servers[process_slot][thread_slot].generation = retained->mpm->my_generation;
ap_update_child_status_from_indexes(process_slot, thread_slot,
SERVER_STARTING, NULL);
apr_thread_t *start_thread_id;
int i;
- mpm_state = AP_MPMQ_STARTING; /* for benefit of any hooks that run as this
- * child initializes
- */
+ /* for benefit of any hooks that run as this child initializes */
+ retained->mpm->mpm_state = AP_MPMQ_STARTING;
+
ap_my_pid = getpid();
ap_fatal_signal_child_setup(ap_server_conf);
apr_pool_create(&pchild, pconf);
/* close unused listeners and pods */
- for (i = 0; i < retained->num_buckets; i++) {
+ for (i = 0; i < retained->mpm->num_buckets; i++) {
if (i != child_bucket) {
ap_close_listeners_ex(all_buckets[i].listeners);
ap_mpm_podx_close(all_buckets[i].pod);
clean_child_exit(APEXIT_CHILDSICK);
}
- mpm_state = AP_MPMQ_RUNNING;
+ retained->mpm->mpm_state = AP_MPMQ_RUNNING;
/* If we are only running in one_process mode, we will want to
* still handle signals. */
if (one_process) {
my_bucket = &all_buckets[0];
- set_signals();
event_note_child_started(slot, getpid());
child_main(slot, 0);
/* NOTREACHED */
if (ap_scoreboard_image->parent[i].pid != 0) {
continue;
}
- if (make_child(ap_server_conf, i, i % retained->num_buckets) < 0) {
+ if (make_child(ap_server_conf, i, i % retained->mpm->num_buckets) < 0) {
break;
}
--number_to_start;
* This depends on the ordering of SERVER_READY and SERVER_STARTING.
*/
if (status <= SERVER_READY && !ps->quiescing && !ps->not_accepting
- && ps->generation == retained->my_generation
+ && ps->generation == retained->mpm->my_generation
&& ps->bucket == child_bucket)
{
++idle_thread_count;
else {
/* looks like a basket case, as no child ever fully initialized; give up.
*/
- shutdown_pending = 1;
+ retained->mpm->shutdown_pending = 1;
child_fatal = 1;
ap_log_error(APLOG_MARK, APLOG_ALERT, 0,
ap_server_conf, APLOGNO(02324)
apr_proc_t pid;
int i;
- while (!restart_pending && !shutdown_pending) {
+ while (!retained->mpm->restart_pending && !retained->mpm->shutdown_pending) {
ap_wait_or_timeout(&exitwhy, &status, &pid, pconf, ap_server_conf);
if (pid.pid != -1) {
*/
if (child_slot < 0
|| ap_get_scoreboard_process(child_slot)->generation
- == retained->my_generation) {
- shutdown_pending = 1;
+ == retained->mpm->my_generation) {
+ retained->mpm->shutdown_pending = 1;
child_fatal = 1;
/*
* total_daemons counting will be off now, but as we
/* handled */
}
#endif
- else if (retained->is_graceful) {
+ else if (retained->mpm->was_graceful) {
/* Great, we've probably just lost a slot in the
* scoreboard. Somehow we don't know about this child.
*/
static int event_run(apr_pool_t * _pconf, apr_pool_t * plog, server_rec * s)
{
- int num_buckets = retained->num_buckets;
+ int num_buckets = retained->mpm->num_buckets;
int remaining_children_to_start;
int i;
ap_log_pid(pconf, ap_pid_fname);
- if (!retained->is_graceful) {
+ if (!retained->mpm->was_graceful) {
if (ap_run_pre_mpm(s->process->pool, SB_SHARED) != OK) {
- mpm_state = AP_MPMQ_STOPPING;
+ retained->mpm->mpm_state = AP_MPMQ_STOPPING;
return !OK;
}
/* fix the generation number in the global score; we just got a new,
* cleared scoreboard
*/
- ap_scoreboard_image->global->running_generation = retained->my_generation;
+ ap_scoreboard_image->global->running_generation = retained->mpm->my_generation;
}
- restart_pending = shutdown_pending = 0;
- set_signals();
+ if (!one_process) {
+ ap_fatal_signal_setup(ap_server_conf, pconf);
+ }
+ ap_unixd_mpm_set_signals(pconf, one_process);
/* Don't thrash since num_buckets depends on the
* system and the number of online CPU cores...
if (remaining_children_to_start > active_daemons_limit) {
remaining_children_to_start = active_daemons_limit;
}
- if (!retained->is_graceful) {
+ if (!retained->mpm->was_graceful) {
startup_children(remaining_children_to_start);
remaining_children_to_start = 0;
}
ap_log_command_line(plog, s);
ap_log_mpm_common(s);
- mpm_state = AP_MPMQ_RUNNING;
+ retained->mpm->mpm_state = AP_MPMQ_RUNNING;
server_main_loop(remaining_children_to_start, num_buckets);
- mpm_state = AP_MPMQ_STOPPING;
+ retained->mpm->mpm_state = AP_MPMQ_STOPPING;
- if (shutdown_pending && !retained->is_graceful) {
+ if (retained->mpm->shutdown_pending && retained->mpm->is_ungraceful) {
/* Time to shut down:
* Kill child processes, tell them to call child_exit, etc...
*/
ap_log_error(APLOG_MARK, APLOG_NOTICE, 0,
ap_server_conf, APLOGNO(00491) "caught SIGTERM, shutting down");
}
+
return DONE;
- } else if (shutdown_pending) {
+ }
+
+ if (retained->mpm->shutdown_pending) {
/* Time to gracefully shut down:
* Kill child processes, tell them to call child_exit, etc...
*/
}
/* Don't really exit until each child has finished */
- shutdown_pending = 0;
+ retained->mpm->shutdown_pending = 0;
do {
/* Pause for a second */
apr_sleep(apr_time_from_sec(1));
break;
}
}
- } while (!shutdown_pending && active_children &&
+ } while (!retained->mpm->shutdown_pending && active_children &&
(!ap_graceful_shutdown_timeout || apr_time_now() < cutoff));
/* We might be here because we received SIGTERM, either
}
/* we've been told to restart */
- apr_signal(SIGHUP, SIG_IGN);
-
if (one_process) {
/* not worth thinking about */
return DONE;
/* XXX: we really need to make sure this new generation number isn't in
* use by any of the children.
*/
- ++retained->my_generation;
- ap_scoreboard_image->global->running_generation = retained->my_generation;
+ ++retained->mpm->my_generation;
+ ap_scoreboard_image->global->running_generation = retained->mpm->my_generation;
- if (retained->is_graceful) {
+ if (!retained->mpm->is_ungraceful) {
ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, APLOGNO(00493)
AP_SIG_GRACEFUL_STRING
" received. Doing graceful restart");
pconf = p;
/* the reverse of pre_config, we want this only the first time around */
- if (retained->module_loads == 1) {
+ if (retained->mpm->module_loads == 1) {
startup = 1;
level_flags |= APLOG_STARTUP;
}
if (one_process) {
num_buckets = 1;
}
- else if (retained->is_graceful) {
+ else if (retained->mpm->was_graceful) {
/* Preserve the number of buckets on graceful restarts. */
- num_buckets = retained->num_buckets;
+ num_buckets = retained->mpm->num_buckets;
}
if ((rv = ap_duplicate_listeners(pconf, ap_server_conf,
&listen_buckets, &num_buckets))) {
all_buckets[i].listeners = listen_buckets[i];
}
- if (retained->max_buckets < num_buckets) {
+ if (retained->mpm->max_buckets < num_buckets) {
int new_max, *new_ptr;
- new_max = retained->max_buckets * 2;
+ new_max = retained->mpm->max_buckets * 2;
if (new_max < num_buckets) {
new_max = num_buckets;
}
new_ptr = (int *)apr_palloc(ap_pglobal, new_max * sizeof(int));
memcpy(new_ptr, retained->idle_spawn_rate,
- retained->num_buckets * sizeof(int));
+ retained->mpm->num_buckets * sizeof(int));
retained->idle_spawn_rate = new_ptr;
- retained->max_buckets = new_max;
+ retained->mpm->max_buckets = new_max;
}
- if (retained->num_buckets < num_buckets) {
+ if (retained->mpm->num_buckets < num_buckets) {
int rate_max = 1;
/* If new buckets are added, set their idle spawn rate to
* the highest so far, so that they get filled as quickly
* as the existing ones.
*/
- for (i = 0; i < retained->num_buckets; i++) {
+ for (i = 0; i < retained->mpm->num_buckets; i++) {
if (rate_max < retained->idle_spawn_rate[i]) {
rate_max = retained->idle_spawn_rate[i];
}
retained->idle_spawn_rate[i] = rate_max;
}
}
- retained->num_buckets = num_buckets;
+ retained->mpm->num_buckets = num_buckets;
/* for skiplist */
srand((unsigned int)apr_time_now());
int no_detach, debug, foreground;
apr_status_t rv;
const char *userdata_key = "mpm_event_module";
-
- mpm_state = AP_MPMQ_STARTING;
+ int test_atomics = 0;
debug = ap_exists_config_define("DEBUG");
foreground = ap_exists_config_define("FOREGROUND");
}
- /* sigh, want this only the second time around */
retained = ap_retained_data_get(userdata_key);
if (!retained) {
retained = ap_retained_data_create(userdata_key, sizeof(*retained));
+ retained->mpm = ap_unixd_mpm_get_retained_data();
retained->max_daemons_limit = -1;
+ if (retained->mpm->module_loads) {
+ test_atomics = 1;
+ }
+ }
+ retained->mpm->mpm_state = AP_MPMQ_STARTING;
+ if (retained->mpm->baton != retained) {
+ retained->mpm->was_graceful = 0;
+ retained->mpm->baton = retained;
}
- ++retained->module_loads;
- if (retained->module_loads == 2) {
- /* test for correct operation of fdqueue */
+ ++retained->mpm->module_loads;
+
+ /* test once for correct operation of fdqueue */
+ if (test_atomics || retained->mpm->module_loads == 2) {
static apr_uint32_t foo1, foo2;
apr_atomic_set32(&foo1, 100);
"atomics not working as expected - add32 of negative number");
return HTTP_INTERNAL_SERVER_ERROR;
}
+ }
+ /* sigh, want this only the second time around */
+ if (retained->mpm->module_loads == 2) {
rv = apr_pollset_create(&event_pollset, 1, plog,
APR_POLLSET_THREADSAFE | APR_POLLSET_NOCOPY);
if (rv != APR_SUCCESS) {
int startup = 0;
/* the reverse of pre_config, we want this only the first time around */
- if (retained->module_loads == 1) {
+ if (retained->mpm->module_loads == 1) {
startup = 1;
}
static int ap_daemons_max_free=0;
static int ap_daemons_limit=0; /* MaxRequestWorkers */
static int server_limit = 0;
-static int mpm_state = AP_MPMQ_STARTING;
/* data retained by prefork across load/unload of the module
* allocated on first call to pre-config hook; located on
* subsequent calls to pre-config hook
*/
typedef struct prefork_retained_data {
+ ap_unixd_mpm_retained_data *mpm;
+
int first_server_limit;
- int module_loads;
- ap_generation_t my_generation;
- int volatile is_graceful; /* set from signal handler */
int maxclients_reported;
/*
* The max child slot ever assigned, preserved across restarts. Necessary
ap_listen_rec *listeners;
apr_proc_mutex_t *mutex;
} prefork_child_bucket;
-static int num_buckets; /* Number of listeners buckets */
static prefork_child_bucket *all_buckets, /* All listeners buckets */
*my_bucket; /* Current child bucket */
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);
+ retained->mpm->my_generation, slot, MPM_CHILD_STARTED);
}
/* a clean exit from a child with proper cleanup */
static void clean_child_exit(int code) __attribute__ ((noreturn));
static void clean_child_exit(int code)
{
- mpm_state = AP_MPMQ_STOPPING;
+ retained->mpm->mpm_state = AP_MPMQ_STOPPING;
if (pchild) {
apr_pool_destroy(pchild);
if (rv != APR_SUCCESS) {
const char *msg = "couldn't grab the accept mutex";
- if (retained->my_generation !=
+ if (retained->mpm->my_generation !=
ap_scoreboard_image->global->running_generation) {
ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, ap_server_conf, APLOGNO(00143) "%s", msg);
clean_child_exit(0);
if (rv != APR_SUCCESS) {
const char *msg = "couldn't release the accept mutex";
- if (retained->my_generation !=
+ if (retained->mpm->my_generation !=
ap_scoreboard_image->global->running_generation) {
ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, ap_server_conf, APLOGNO(00145) "%s", msg);
/* don't exit here... we have a connection to
*result = ap_daemons_limit;
break;
case AP_MPMQ_MPM_STATE:
- *result = mpm_state;
+ *result = retained->mpm->mpm_state;
break;
case AP_MPMQ_GENERATION:
- *result = retained->my_generation;
+ *result = retained->mpm->my_generation;
break;
default:
*rv = APR_ENOTIMPL;
clean_child_exit(0);
}
-/* volatile because they're updated from a signal handler */
-static int volatile shutdown_pending;
-static int volatile restart_pending;
+/* volatile because it's updated from a signal handler */
static int volatile die_now = 0;
static void stop_listening(int sig)
{
- mpm_state = AP_MPMQ_STOPPING;
+ retained->mpm->mpm_state = AP_MPMQ_STOPPING;
ap_close_listeners_ex(my_bucket->listeners);
/* For a graceful stop, we want the child to exit when done */
die_now = 1;
}
-static void sig_term(int sig)
-{
- if (shutdown_pending == 1) {
- /* Um, is this _probably_ not an error, if the user has
- * tried to do a shutdown twice quickly, so we won't
- * worry about reporting it.
- */
- return;
- }
- mpm_state = AP_MPMQ_STOPPING;
- shutdown_pending = 1;
- retained->is_graceful = (sig == AP_SIG_GRACEFUL_STOP);
-}
-
-/* restart() is the signal handler for SIGHUP and AP_SIG_GRACEFUL
- * in the parent process, unless running in ONE_PROCESS mode
- */
-static void restart(int sig)
-{
- if (restart_pending == 1) {
- /* Probably not an error - don't bother reporting it */
- return;
- }
- mpm_state = AP_MPMQ_STOPPING;
- restart_pending = 1;
- retained->is_graceful = (sig == AP_SIG_GRACEFUL);
-}
-
-static void set_signals(void)
-{
-#ifndef NO_USE_SIGACTION
- struct sigaction sa;
-#endif
-
- if (!one_process) {
- ap_fatal_signal_setup(ap_server_conf, pconf);
- }
-
-#ifndef NO_USE_SIGACTION
- sigemptyset(&sa.sa_mask);
- sa.sa_flags = 0;
-
- sa.sa_handler = sig_term;
- if (sigaction(SIGTERM, &sa, NULL) < 0)
- ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, APLOGNO(00147) "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, APLOGNO(00148)
- "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, APLOGNO(00149) "sigaction(SIGINT)");
-#endif
-#ifdef SIGXCPU
- sa.sa_handler = SIG_DFL;
- if (sigaction(SIGXCPU, &sa, NULL) < 0)
- ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, APLOGNO(00150) "sigaction(SIGXCPU)");
-#endif
-#ifdef SIGXFSZ
- /* 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, APLOGNO(00151) "sigaction(SIGXFSZ)");
-#endif
-#ifdef SIGPIPE
- sa.sa_handler = SIG_IGN;
- if (sigaction(SIGPIPE, &sa, NULL) < 0)
- ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, APLOGNO(00152) "sigaction(SIGPIPE)");
-#endif
-
- /* we want to ignore HUPs and AP_SIG_GRACEFUL while we're busy
- * processing one
- */
- sigaddset(&sa.sa_mask, SIGHUP);
- sigaddset(&sa.sa_mask, AP_SIG_GRACEFUL);
- sa.sa_handler = restart;
- if (sigaction(SIGHUP, &sa, NULL) < 0)
- ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, APLOGNO(00153) "sigaction(SIGHUP)");
- if (sigaction(AP_SIG_GRACEFUL, &sa, NULL) < 0)
- ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, APLOGNO(00154) "sigaction(" AP_SIG_GRACEFUL_STRING ")");
-#else
- if (!one_process) {
-#ifdef SIGXCPU
- apr_signal(SIGXCPU, SIG_DFL);
-#endif /* SIGXCPU */
-#ifdef SIGXFSZ
- apr_signal(SIGXFSZ, SIG_IGN);
-#endif /* SIGXFSZ */
- }
-
- apr_signal(SIGTERM, sig_term);
-#ifdef SIGHUP
- apr_signal(SIGHUP, restart);
-#endif /* SIGHUP */
-#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 */
-
-#endif
-}
-
/*****************************************************************
* Child process main loop.
* The following vars are static to avoid getting clobbered by longjmp();
int last_poll_idx = 0;
const char *lockfile;
- mpm_state = AP_MPMQ_STARTING; /* for benefit of any hooks that run as this
- * child initializes
- */
+ /* for benefit of any hooks that run as this child initializes */
+ retained->mpm->mpm_state = AP_MPMQ_STARTING;
my_child_num = child_num_arg;
ap_my_pid = getpid();
apr_pool_tag(ptrans, "transaction");
/* close unused listeners and pods */
- for (i = 0; i < num_buckets; i++) {
+ for (i = 0; i < retained->mpm->num_buckets; i++) {
if (i != child_bucket) {
ap_close_listeners_ex(all_buckets[i].listeners);
ap_mpm_pod_close(all_buckets[i].pod);
lr->accept_func = ap_unixd_accept;
}
- mpm_state = AP_MPMQ_RUNNING;
+ retained->mpm->mpm_state = AP_MPMQ_RUNNING;
bucket_alloc = apr_bucket_alloc_create(pchild);
/* die_now is set when AP_SIG_GRACEFUL is received in the child;
- * shutdown_pending is set when SIGTERM is received when running
- * in single process mode. */
- while (!die_now && !shutdown_pending) {
+ * {shutdown,restart}_pending are set when a signal is received while
+ * running in single process mode.
+ */
+ while (!die_now
+ && !retained->mpm->shutdown_pending
+ && !retained->mpm->restart_pending) {
conn_rec *current_conn;
void *csd;
* poll if already signalled
*/
if (die_now /* in graceful stop/restart */
- || (one_process && shutdown_pending)) {
+ || retained->mpm->shutdown_pending
+ || retained->mpm->restart_pending) {
SAFE_ACCEPT(accept_mutex_off());
clean_child_exit(0);
}
if (ap_mpm_pod_check(my_bucket->pod) == APR_SUCCESS) { /* selected as idle? */
die_now = 1;
}
- else if (retained->my_generation !=
+ else if (retained->mpm->my_generation !=
ap_scoreboard_image->global->running_generation) { /* restart? */
/* yeah, this could be non-graceful restart, in which case the
* parent will kill us soon enough, but why bother checking?
if (one_process) {
my_bucket = &all_buckets[0];
- apr_signal(SIGHUP, sig_term);
- /* Don't catch AP_SIG_GRACEFUL in ONE_PROCESS mode :) */
- apr_signal(SIGINT, sig_term);
-#ifdef SIGQUIT
- apr_signal(SIGQUIT, SIG_DFL);
-#endif
- apr_signal(SIGTERM, sig_term);
prefork_note_child_started(slot, getpid());
child_main(slot, 0);
/* NOTREACHED */
if (ap_scoreboard_image->servers[i][0].status != SERVER_DEAD) {
continue;
}
- if (make_child(ap_server_conf, i, i % num_buckets) < 0) {
+ if (make_child(ap_server_conf, i, i % retained->mpm->num_buckets) < 0) {
break;
}
--number_to_start;
* shut down gracefully, in case it happened to pick up a request
* while we were counting
*/
- bucket_kill_child_record = (bucket_kill_child_record + 1) % num_buckets;
+ bucket_kill_child_record = (bucket_kill_child_record + 1) % retained->mpm->num_buckets;
ap_mpm_pod_signal(all_buckets[bucket_kill_child_record].pod);
retained->idle_spawn_rate = 1;
}
}
for (i = 0; i < free_length; ++i) {
bucket_make_child_record++;
- bucket_make_child_record %= num_buckets;
+ bucket_make_child_record %= retained->mpm->num_buckets;
make_child(ap_server_conf, free_slots[i],
bucket_make_child_record);
}
ap_log_pid(pconf, ap_pid_fname);
- if (!retained->is_graceful) {
+ if (!retained->mpm->was_graceful) {
if (ap_run_pre_mpm(s->process->pool, SB_SHARED) != OK) {
- mpm_state = AP_MPMQ_STOPPING;
+ retained->mpm->mpm_state = AP_MPMQ_STOPPING;
return !OK;
}
/* fix the generation number in the global score; we just got a new,
* cleared scoreboard
*/
- ap_scoreboard_image->global->running_generation = retained->my_generation;
+ ap_scoreboard_image->global->running_generation = retained->mpm->my_generation;
}
- restart_pending = shutdown_pending = 0;
- set_signals();
+ if (!one_process) {
+ ap_fatal_signal_setup(ap_server_conf, pconf);
+ }
+ ap_unixd_mpm_set_signals(pconf, one_process);
if (one_process) {
AP_MONCONTROL(1);
/* Don't thrash since num_buckets depends on the
* system and the number of online CPU cores...
*/
- if (ap_daemons_limit < num_buckets)
- ap_daemons_limit = num_buckets;
- if (ap_daemons_to_start < num_buckets)
- ap_daemons_to_start = num_buckets;
- if (ap_daemons_min_free < num_buckets)
- ap_daemons_min_free = num_buckets;
- if (ap_daemons_max_free < ap_daemons_min_free + num_buckets)
- ap_daemons_max_free = ap_daemons_min_free + num_buckets;
+ if (ap_daemons_limit < retained->mpm->num_buckets)
+ ap_daemons_limit = retained->mpm->num_buckets;
+ if (ap_daemons_to_start < retained->mpm->num_buckets)
+ ap_daemons_to_start = retained->mpm->num_buckets;
+ if (ap_daemons_min_free < retained->mpm->num_buckets)
+ ap_daemons_min_free = retained->mpm->num_buckets;
+ if (ap_daemons_max_free < ap_daemons_min_free + retained->mpm->num_buckets)
+ ap_daemons_max_free = ap_daemons_min_free + retained->mpm->num_buckets;
/* If we're doing a graceful_restart then we're going to see a lot
* of children exiting immediately when we get into the main loop
if (remaining_children_to_start > ap_daemons_limit) {
remaining_children_to_start = ap_daemons_limit;
}
- if (!retained->is_graceful) {
+ if (!retained->mpm->was_graceful) {
startup_children(remaining_children_to_start);
remaining_children_to_start = 0;
}
: "none",
apr_proc_mutex_defname());
- mpm_state = AP_MPMQ_RUNNING;
+ retained->mpm->mpm_state = AP_MPMQ_RUNNING;
- while (!restart_pending && !shutdown_pending) {
+ while (!retained->mpm->restart_pending && !retained->mpm->shutdown_pending) {
int child_slot;
apr_exit_why_e exitwhy;
int status, processed_status;
*/
if (child_slot < 0
|| ap_get_scoreboard_process(child_slot)->generation
- == retained->my_generation) {
- mpm_state = AP_MPMQ_STOPPING;
+ == retained->mpm->my_generation) {
+ retained->mpm->mpm_state = AP_MPMQ_STOPPING;
return !OK;
}
else {
/* handled */
#endif
}
- else if (retained->is_graceful) {
+ else if (retained->mpm->was_graceful) {
/* Great, we've probably just lost a slot in the
* scoreboard. Somehow we don't know about this
* child.
perform_idle_server_maintenance(pconf);
}
- mpm_state = AP_MPMQ_STOPPING;
+ retained->mpm->mpm_state = AP_MPMQ_STOPPING;
- if (shutdown_pending && !retained->is_graceful) {
+ if (retained->mpm->shutdown_pending && retained->mpm->is_ungraceful) {
/* Time to shut down:
* Kill child processes, tell them to call child_exit, etc...
*/
"caught SIGTERM, shutting down");
return DONE;
- } else if (shutdown_pending) {
+ }
+
+ if (retained->mpm->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
ap_close_listeners();
/* kill off the idle ones */
- for (i = 0; i < num_buckets; i++) {
+ for (i = 0; i < retained->mpm->num_buckets; i++) {
ap_mpm_pod_killpg(all_buckets[i].pod, retained->max_daemons_limit);
}
}
/* Don't really exit until each child has finished */
- shutdown_pending = 0;
+ retained->mpm->shutdown_pending = 0;
do {
/* Pause for a second */
sleep(1);
break;
}
}
- } while (!shutdown_pending && active_children &&
+ } while (!retained->mpm->shutdown_pending && active_children &&
(!ap_graceful_shutdown_timeout || apr_time_now() < cutoff));
/* We might be here because we received SIGTERM, either
}
/* we've been told to restart */
- apr_signal(SIGHUP, SIG_IGN);
- apr_signal(AP_SIG_GRACEFUL, SIG_IGN);
if (one_process) {
/* not worth thinking about */
return DONE;
/* XXX: we really need to make sure this new generation number isn't in
* use by any of the children.
*/
- ++retained->my_generation;
- ap_scoreboard_image->global->running_generation = retained->my_generation;
+ ++retained->mpm->my_generation;
+ ap_scoreboard_image->global->running_generation = retained->mpm->my_generation;
- if (retained->is_graceful) {
+ if (!retained->mpm->is_ungraceful) {
ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, APLOGNO(00171)
"Graceful restart requested, doing restart");
/* kill off the idle ones */
- for (i = 0; i < num_buckets; i++) {
+ for (i = 0; i < retained->mpm->num_buckets; i++) {
ap_mpm_pod_killpg(all_buckets[i].pod, retained->max_daemons_limit);
}
pconf = p;
/* the reverse of pre_config, we want this only the first time around */
- if (retained->module_loads == 1) {
+ if (retained->mpm->module_loads == 1) {
startup = 1;
level_flags |= APLOG_STARTUP;
}
}
if (one_process) {
- num_buckets = 1;
+ retained->mpm->num_buckets = 1;
}
- else if (!retained->is_graceful) { /* Preserve the number of buckets
- on graceful restarts. */
- num_buckets = 0;
+ else if (!retained->mpm->was_graceful) {
+ /* Preserve the number of buckets on graceful restarts. */
+ retained->mpm->num_buckets = 0;
}
if ((rv = ap_duplicate_listeners(pconf, ap_server_conf,
- &listen_buckets, &num_buckets))) {
+ &listen_buckets, &retained->mpm->num_buckets))) {
ap_log_error(APLOG_MARK, APLOG_CRIT | level_flags, rv,
(startup ? NULL : s),
"could not duplicate listeners");
return !OK;
}
- all_buckets = apr_pcalloc(pconf, num_buckets *
+ all_buckets = apr_pcalloc(pconf, retained->mpm->num_buckets *
sizeof(prefork_child_bucket));
- for (i = 0; i < num_buckets; i++) {
+ for (i = 0; i < retained->mpm->num_buckets; i++) {
if ((rv = ap_mpm_pod_open(pconf, &all_buckets[i].pod))) {
ap_log_error(APLOG_MARK, APLOG_CRIT | level_flags, rv,
(startup ? NULL : s),
apr_status_t rv;
const char *userdata_key = "mpm_prefork_module";
- mpm_state = AP_MPMQ_STARTING;
-
debug = ap_exists_config_define("DEBUG");
if (debug) {
ap_mutex_register(p, AP_ACCEPT_MUTEX_TYPE, NULL, APR_LOCK_DEFAULT, 0);
- /* sigh, want this only the second time around */
retained = ap_retained_data_get(userdata_key);
if (!retained) {
retained = ap_retained_data_create(userdata_key, sizeof(*retained));
+ retained->mpm = ap_unixd_mpm_get_retained_data();
retained->max_daemons_limit = -1;
retained->idle_spawn_rate = 1;
}
- ++retained->module_loads;
- if (retained->module_loads == 2) {
+ retained->mpm->mpm_state = AP_MPMQ_STARTING;
+ if (retained->mpm->baton != retained) {
+ retained->mpm->was_graceful = 0;
+ retained->mpm->baton = retained;
+ }
+ ++retained->mpm->module_loads;
+
+ /* sigh, want this only the second time around */
+ if (retained->mpm->module_loads == 2) {
if (!one_process && !foreground) {
/* before we detach, setup crash handlers to log to errorlog */
ap_fatal_signal_setup(ap_server_conf, pconf);
int startup = 0;
/* the reverse of pre_config, we want this only the first time around */
- if (retained->module_loads == 1) {
+ if (retained->mpm->module_loads == 1) {
startup = 1;
}
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;
/* data retained by worker across load/unload of the module
* allocated on first call to pre-config hook; located on
* subsequent calls to pre-config hook
*/
typedef struct worker_retained_data {
+ ap_unixd_mpm_retained_data *mpm;
+
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;
/*
#define MAX_SPAWN_RATE (32)
#endif
int hold_off_on_exponential_spawning;
- /*
- * Current number of listeners buckets and maximum reached across
- * restarts (to size retained data according to dynamic num_buckets,
- * eg. idle_spawn_rate).
- */
- int num_buckets, max_buckets;
} worker_retained_data;
static worker_retained_data *retained;
return;
}
terminate_mode = mode;
- mpm_state = AP_MPMQ_STOPPING;
+ retained->mpm->mpm_state = AP_MPMQ_STOPPING;
/* in case we weren't called from the listener thread, wake up the
* listener thread
*result = ap_daemons_limit;
break;
case AP_MPMQ_MPM_STATE:
- *result = mpm_state;
+ *result = retained->mpm->mpm_state;
break;
case AP_MPMQ_GENERATION:
- *result = retained->my_generation;
+ *result = retained->mpm->my_generation;
break;
default:
*rv = APR_ENOTIMPL;
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);
+ retained->mpm->my_generation, slot, MPM_CHILD_STARTED);
}
static void worker_note_child_lost_slot(int slot, pid_t newpid)
static void clean_child_exit(int code) __attribute__ ((noreturn));
static void clean_child_exit(int code)
{
- mpm_state = AP_MPMQ_STOPPING;
+ retained->mpm->mpm_state = AP_MPMQ_STOPPING;
if (pchild) {
apr_pool_destroy(pchild);
}
static int child_fatal;
-/* volatile because they're updated from a signal handler */
-static int volatile shutdown_pending;
-static int volatile restart_pending;
-
-/*
- * ap_start_shutdown() and ap_start_restart(), below, are a first stab at
- * functions to initiate shutdown or restart without relying on signals.
- * Previously this was initiated in sig_term() and restart() signal handlers,
- * but we want to be able to start a shutdown/restart from other sources --
- * e.g. on Win32, from the service manager. Now the service manager can
- * call ap_start_shutdown() or ap_start_restart() as appropriate. Note that
- * these functions can also be called by the child processes, since global
- * variables are no longer used to pass on the required action to the parent.
- *
- * These should only be called from the parent process itself, since the
- * parent process will use the shutdown_pending and restart_pending variables
- * to determine whether to shutdown or restart. The child process should
- * call signal_parent() directly to tell the parent to die -- this will
- * cause neither of those variable to be set, which the parent will
- * assume means something serious is wrong (which it will be, for the
- * child to force an exit) and so do an exit anyway.
- */
-
-static void ap_start_shutdown(int graceful)
-{
- mpm_state = AP_MPMQ_STOPPING;
- if (shutdown_pending == 1) {
- /* Um, is this _probably_ not an error, if the user has
- * tried to do a shutdown twice quickly, so we won't
- * worry about reporting it.
- */
- return;
- }
- shutdown_pending = 1;
- retained->is_graceful = graceful;
-}
-
-/* do a graceful restart if graceful == 1 */
-static void ap_start_restart(int graceful)
-{
- mpm_state = AP_MPMQ_STOPPING;
- if (restart_pending == 1) {
- /* Probably not an error - don't bother reporting it */
- return;
- }
- restart_pending = 1;
- retained->is_graceful = graceful;
-}
-
-static void sig_term(int sig)
-{
- ap_start_shutdown(sig == AP_SIG_GRACEFUL_STOP);
-}
-
-static void restart(int sig)
-{
- ap_start_restart(sig == AP_SIG_GRACEFUL);
-}
-
-static void set_signals(void)
-{
-#ifndef NO_USE_SIGACTION
- struct sigaction sa;
-#endif
-
- if (!one_process) {
- ap_fatal_signal_setup(ap_server_conf, pconf);
- }
-
-#ifndef NO_USE_SIGACTION
- sigemptyset(&sa.sa_mask);
- sa.sa_flags = 0;
-
- sa.sa_handler = sig_term;
- if (sigaction(SIGTERM, &sa, NULL) < 0)
- ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, APLOGNO(00264)
- "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, APLOGNO(00265)
- "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, APLOGNO(00266)
- "sigaction(SIGINT)");
-#endif
-#ifdef SIGXCPU
- sa.sa_handler = SIG_DFL;
- if (sigaction(SIGXCPU, &sa, NULL) < 0)
- ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, APLOGNO(00267)
- "sigaction(SIGXCPU)");
-#endif
-#ifdef SIGXFSZ
- /* 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, APLOGNO(00268)
- "sigaction(SIGXFSZ)");
-#endif
-#ifdef SIGPIPE
- sa.sa_handler = SIG_IGN;
- if (sigaction(SIGPIPE, &sa, NULL) < 0)
- ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, APLOGNO(00269)
- "sigaction(SIGPIPE)");
-#endif
-
- /* we want to ignore HUPs and AP_SIG_GRACEFUL while we're busy
- * processing one */
- sigaddset(&sa.sa_mask, SIGHUP);
- sigaddset(&sa.sa_mask, AP_SIG_GRACEFUL);
- sa.sa_handler = restart;
- if (sigaction(SIGHUP, &sa, NULL) < 0)
- ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, APLOGNO(00270)
- "sigaction(SIGHUP)");
- if (sigaction(AP_SIG_GRACEFUL, &sa, NULL) < 0)
- ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, APLOGNO(00271)
- "sigaction(" AP_SIG_GRACEFUL_STRING ")");
-#else
- if (!one_process) {
-#ifdef SIGXCPU
- apr_signal(SIGXCPU, SIG_DFL);
-#endif /* SIGXCPU */
-#ifdef SIGXFSZ
- apr_signal(SIGXFSZ, SIG_IGN);
-#endif /* SIGXFSZ */
- }
-
- apr_signal(SIGTERM, sig_term);
-#ifdef SIGHUP
- apr_signal(SIGHUP, restart);
-#endif /* SIGHUP */
-#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 */
-
-#endif
-}
-
/*****************************************************************
* Here follows a long bunch of generic server bookkeeping stuff...
*/
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 = retained->my_generation;
+ ap_scoreboard_image->servers[process_slot][thread_slot].generation = retained->mpm->my_generation;
ap_update_child_status_from_indexes(process_slot, thread_slot,
SERVER_STARTING, NULL);
apr_thread_t *start_thread_id;
int i;
- mpm_state = AP_MPMQ_STARTING; /* for benefit of any hooks that run as this
- * child initializes
- */
+ /* for benefit of any hooks that run as this child initializes */
+ retained->mpm->mpm_state = AP_MPMQ_STARTING;
+
ap_my_pid = getpid();
ap_fatal_signal_child_setup(ap_server_conf);
apr_pool_create(&pchild, pconf);
/* close unused listeners and pods */
- for (i = 0; i < retained->num_buckets; i++) {
+ for (i = 0; i < retained->mpm->num_buckets; i++) {
if (i != child_bucket) {
ap_close_listeners_ex(all_buckets[i].listeners);
ap_mpm_podx_close(all_buckets[i].pod);
clean_child_exit(APEXIT_CHILDSICK);
}
- mpm_state = AP_MPMQ_RUNNING;
+ retained->mpm->mpm_state = AP_MPMQ_RUNNING;
/* If we are only running in one_process mode, we will want to
* still handle signals. */
if (one_process) {
my_bucket = &all_buckets[0];
- set_signals();
worker_note_child_started(slot, getpid());
child_main(slot, 0);
/* NOTREACHED */
if (ap_scoreboard_image->parent[i].pid != 0) {
continue;
}
- if (make_child(ap_server_conf, i, i % retained->num_buckets) < 0) {
+ if (make_child(ap_server_conf, i, i % retained->mpm->num_buckets) < 0) {
break;
}
--number_to_start;
loop if no pid? not much else matters */
if (status <= SERVER_READY &&
!ps->quiescing &&
- ps->generation == retained->my_generation &&
+ ps->generation == retained->mpm->my_generation &&
ps->bucket == child_bucket) {
++idle_thread_count;
}
else {
/* looks like a basket case, as no child ever fully initialized; give up.
*/
- shutdown_pending = 1;
+ retained->mpm->shutdown_pending = 1;
child_fatal = 1;
ap_log_error(APLOG_MARK, APLOG_ALERT, 0,
ap_server_conf, APLOGNO(02325)
apr_proc_t pid;
int i;
- while (!restart_pending && !shutdown_pending) {
+ while (!retained->mpm->restart_pending && !retained->mpm->shutdown_pending) {
ap_wait_or_timeout(&exitwhy, &status, &pid, pconf, ap_server_conf);
if (pid.pid != -1) {
*/
if (child_slot < 0
|| ap_get_scoreboard_process(child_slot)->generation
- == retained->my_generation) {
- shutdown_pending = 1;
+ == retained->mpm->my_generation) {
+ retained->mpm->shutdown_pending = 1;
child_fatal = 1;
return;
}
worker_note_child_killed(-1, /* already out of the scoreboard */
pid.pid, old_gen);
if (processed_status == APEXIT_CHILDSICK
- && old_gen == retained->my_generation) {
+ && old_gen == retained->mpm->my_generation) {
/* resource shortage, minimize the fork rate */
for (i = 0; i < num_buckets; i++) {
retained->idle_spawn_rate[i] = 1;
/* handled */
#endif
}
- else if (retained->is_graceful) {
+ else if (retained->mpm->was_graceful) {
/* Great, we've probably just lost a slot in the
* scoreboard. Somehow we don't know about this child.
*/
static int worker_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
{
- int num_buckets = retained->num_buckets;
+ int num_buckets = retained->mpm->num_buckets;
int remaining_children_to_start;
int i;
ap_log_pid(pconf, ap_pid_fname);
- if (!retained->is_graceful) {
+ if (!retained->mpm->was_graceful) {
if (ap_run_pre_mpm(s->process->pool, SB_SHARED) != OK) {
- mpm_state = AP_MPMQ_STOPPING;
+ retained->mpm->mpm_state = AP_MPMQ_STOPPING;
return !OK;
}
/* fix the generation number in the global score; we just got a new,
* cleared scoreboard
*/
- ap_scoreboard_image->global->running_generation = retained->my_generation;
+ ap_scoreboard_image->global->running_generation = retained->mpm->my_generation;
}
- restart_pending = shutdown_pending = 0;
- set_signals();
+ if (!one_process) {
+ ap_fatal_signal_setup(ap_server_conf, pconf);
+ }
+ ap_unixd_mpm_set_signals(pconf, one_process);
/* Don't thrash since num_buckets depends on the
* system and the number of online CPU cores...
if (remaining_children_to_start > ap_daemons_limit) {
remaining_children_to_start = ap_daemons_limit;
}
- if (!retained->is_graceful) {
+ if (!retained->mpm->was_graceful) {
startup_children(remaining_children_to_start);
remaining_children_to_start = 0;
}
? apr_proc_mutex_name(all_buckets[0].mutex)
: "none",
apr_proc_mutex_defname());
- mpm_state = AP_MPMQ_RUNNING;
+ retained->mpm->mpm_state = AP_MPMQ_RUNNING;
server_main_loop(remaining_children_to_start, num_buckets);
- mpm_state = AP_MPMQ_STOPPING;
+ retained->mpm->mpm_state = AP_MPMQ_STOPPING;
- if (shutdown_pending && !retained->is_graceful) {
+ if (retained->mpm->shutdown_pending && retained->mpm->is_ungraceful) {
/* Time to shut down:
* Kill child processes, tell them to call child_exit, etc...
*/
ap_server_conf, APLOGNO(00295) "caught SIGTERM, shutting down");
}
return DONE;
- } else if (shutdown_pending) {
+ }
+
+ if (retained->mpm->shutdown_pending) {
/* Time to gracefully shut down:
* Kill child processes, tell them to call child_exit, etc...
*/
}
/* Don't really exit until each child has finished */
- shutdown_pending = 0;
+ retained->mpm->shutdown_pending = 0;
do {
/* Pause for a second */
apr_sleep(apr_time_from_sec(1));
break;
}
}
- } while (!shutdown_pending && active_children &&
+ } while (!retained->mpm->shutdown_pending && active_children &&
(!ap_graceful_shutdown_timeout || apr_time_now() < cutoff));
/* We might be here because we received SIGTERM, either
}
/* we've been told to restart */
- apr_signal(SIGHUP, SIG_IGN);
-
if (one_process) {
/* not worth thinking about */
return DONE;
/* XXX: we really need to make sure this new generation number isn't in
* use by any of the children.
*/
- ++retained->my_generation;
- ap_scoreboard_image->global->running_generation = retained->my_generation;
+ ++retained->mpm->my_generation;
+ ap_scoreboard_image->global->running_generation = retained->mpm->my_generation;
- if (retained->is_graceful) {
+ if (!retained->mpm->is_ungraceful) {
ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, APLOGNO(00297)
AP_SIG_GRACEFUL_STRING " received. Doing graceful restart");
/* wake up the children...time to die. But we'll have more soon */
pconf = p;
/* the reverse of pre_config, we want this only the first time around */
- if (retained->module_loads == 1) {
+ if (retained->mpm->module_loads == 1) {
startup = 1;
level_flags |= APLOG_STARTUP;
}
if (one_process) {
num_buckets = 1;
}
- else if (retained->is_graceful) {
+ else if (retained->mpm->was_graceful) {
/* Preserve the number of buckets on graceful restarts. */
- num_buckets = retained->num_buckets;
+ num_buckets = retained->mpm->num_buckets;
}
if ((rv = ap_duplicate_listeners(pconf, ap_server_conf,
&listen_buckets, &num_buckets))) {
all_buckets[i].listeners = listen_buckets[i];
}
- if (retained->max_buckets < num_buckets) {
+ if (retained->mpm->max_buckets < num_buckets) {
int new_max, *new_ptr;
- new_max = retained->max_buckets * 2;
+ new_max = retained->mpm->max_buckets * 2;
if (new_max < num_buckets) {
new_max = num_buckets;
}
new_ptr = (int *)apr_palloc(ap_pglobal, new_max * sizeof(int));
memcpy(new_ptr, retained->idle_spawn_rate,
- retained->num_buckets * sizeof(int));
+ retained->mpm->num_buckets * sizeof(int));
retained->idle_spawn_rate = new_ptr;
- retained->max_buckets = new_max;
+ retained->mpm->max_buckets = new_max;
}
- if (retained->num_buckets < num_buckets) {
+ if (retained->mpm->num_buckets < num_buckets) {
int rate_max = 1;
/* If new buckets are added, set their idle spawn rate to
* the highest so far, so that they get filled as quickly
* as the existing ones.
*/
- for (i = 0; i < retained->num_buckets; i++) {
+ for (i = 0; i < retained->mpm->num_buckets; i++) {
if (rate_max < retained->idle_spawn_rate[i]) {
rate_max = retained->idle_spawn_rate[i];
}
retained->idle_spawn_rate[i] = rate_max;
}
}
- retained->num_buckets = num_buckets;
+ retained->mpm->num_buckets = num_buckets;
return OK;
}
apr_status_t rv;
const char *userdata_key = "mpm_worker_module";
- mpm_state = AP_MPMQ_STARTING;
-
debug = ap_exists_config_define("DEBUG");
if (debug) {
ap_mutex_register(pconf, AP_ACCEPT_MUTEX_TYPE, NULL, APR_LOCK_DEFAULT, 0);
- /* sigh, want this only the second time around */
retained = ap_retained_data_get(userdata_key);
if (!retained) {
retained = ap_retained_data_create(userdata_key, sizeof(*retained));
+ retained->mpm = ap_unixd_mpm_get_retained_data();
retained->max_daemons_limit = -1;
}
- ++retained->module_loads;
- if (retained->module_loads == 2) {
+ retained->mpm->mpm_state = AP_MPMQ_STARTING;
+ if (retained->mpm->baton != retained) {
+ retained->mpm->was_graceful = 0;
+ retained->mpm->baton = retained;
+ }
+ ++retained->mpm->module_loads;
+
+ /* sigh, want this only the second time around */
+ if (retained->mpm->module_loads == 2) {
if (!one_process && !foreground) {
/* before we detach, setup crash handlers to log to errorlog */
ap_fatal_signal_setup(ap_server_conf, pconf);
int startup = 0;
/* the reverse of pre_config, we want this only the first time around */
- if (retained->module_loads == 1) {
+ if (retained->mpm->module_loads == 1) {
startup = 1;
}
#ifndef NO_USE_SIGACTION
struct sigaction sa;
+ memset(&sa, 0, sizeof sa);
sigemptyset(&sa.sa_mask);
#if defined(SA_ONESHOT)
sa.sa_flags = SA_ONESHOT;
#elif defined(SA_RESETHAND)
sa.sa_flags = SA_RESETHAND;
-#else
- sa.sa_flags = 0;
#endif
sa.sa_handler = sig_coredump;