diff --git a/CHANGES b/CHANGES index 2344887d8c8..658bd0d65e9 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,10 @@ -*- coding: utf-8 -*- Changes with Apache 2.5.1 + *) mpm_event, mpm_worker, mpm_winnt, mpm_prefork: Added optional functions + to account for connections managed outside the MPM accept loop (e.g., UDP). + [Tarek Ibrahim ] + * mod_ssl: Add support for OpenSSL provider based certificate stores. [Graham Leggett] diff --git a/docs/log-message-tags/next-number b/docs/log-message-tags/next-number index b0267f86665..29cdba2b98b 100644 --- a/docs/log-message-tags/next-number +++ b/docs/log-message-tags/next-number @@ -1 +1 @@ -10622 +10623 diff --git a/include/ap_mmn.h b/include/ap_mmn.h index ccd504d8e82..5389e84a168 100644 --- a/include/ap_mmn.h +++ b/include/ap_mmn.h @@ -737,6 +737,10 @@ * 20211221.29 (2.5.1-dev) Add ap_set_time_process_request() to scoreboard.h * 20211221.30 (2.5.1-dev) Add ap_stat_check() to httpd.h * 20211221.31 (2.5.1-dev) Add ap_*_timingsafe() to httpd.h + * 20211221.32 (2.5.1-dev) Add the optional functions ap_mpm_note_extra_ + * connection_added() and ap_mpm_note_extra_ + * connection_removed(), and ap_mpm_wait_for_extra_ + * connections(), to mpm_common.h */ #define MODULE_MAGIC_COOKIE 0x41503235UL /* "AP25" */ @@ -744,7 +748,7 @@ #ifndef MODULE_MAGIC_NUMBER_MAJOR #define MODULE_MAGIC_NUMBER_MAJOR 20211221 #endif -#define MODULE_MAGIC_NUMBER_MINOR 31 /* 0...n */ +#define MODULE_MAGIC_NUMBER_MINOR 32 /* 0...n */ /** * Determine if the server's current MODULE_MAGIC_NUMBER is at least a diff --git a/include/mpm_common.h b/include/mpm_common.h index f60c473d537..9014b8d9906 100644 --- a/include/mpm_common.h +++ b/include/mpm_common.h @@ -40,6 +40,7 @@ #include "ap_config.h" #include "ap_mpm.h" #include "scoreboard.h" +#include "apr_optional.h" #if APR_HAVE_NETINET_TCP_H #include /* for TCP_NODELAY */ @@ -560,6 +561,34 @@ AP_DECLARE_HOOK(void, child_stopped, */ void mpm_common_pre_config(apr_pool_t *pconf); +/** + * Report a connection which the MPM did not accept itself, so that a child + * stopping gracefully waits for it like for its own connections. Call + * ap_mpm_note_extra_connection_added() when such a connection starts and + * ap_mpm_note_extra_connection_removed() when it ends. + */ +APR_DECLARE_OPTIONAL_FN(void, ap_mpm_note_extra_connection_added, (void)); +APR_DECLARE_OPTIONAL_FN(void, ap_mpm_note_extra_connection_removed, (void)); + +/** + * Make the above optional functions available to modules. To be called from + * the pre-config hook of an MPM which waits for noted connections. + * + * @note An MPM which does not wait for them never calls this, leaving both + * functions unavailable. + */ +AP_DECLARE(void) ap_mpm_register_extra_connection_fns(void); + +/** + * Wait for the connections noted by modules to be gone, for at most + * max(Timeout, GracefulShutdownTimeout). To be called by an MPM child + * that is about to exit gracefully. + * + * @note If tracked connections remain when the timeout expires, a warning is + * logged and the child exits, potentially terminating those connections. + */ +AP_DECLARE(void) ap_mpm_wait_for_extra_connections(void); + #ifdef __cplusplus } #endif diff --git a/server/mpm/event/event.c b/server/mpm/event/event.c index 385532d03b4..d4ab3071dc2 100644 --- a/server/mpm/event/event.c +++ b/server/mpm/event/event.c @@ -973,6 +973,10 @@ static apr_status_t event_request_cleanup(void *dummy) event_conn_state_t *cs = ap_get_module_config(c->conn_config, &mpm_event_module); + if (!cs) { + return APR_SUCCESS; + } + cs->r = NULL; return APR_SUCCESS; } @@ -982,6 +986,10 @@ static void event_pre_read_request(request_rec *r, conn_rec *c) event_conn_state_t *cs = ap_get_module_config(c->conn_config, &mpm_event_module); + if (!cs) { + return; + } + cs->r = r; cs->sc = ap_get_module_config(ap_server_conf->module_config, &mpm_event_module); @@ -999,6 +1007,10 @@ static int event_post_read_request(request_rec *r) event_conn_state_t *cs = ap_get_module_config(c->conn_config, &mpm_event_module); + if (!cs) { + return DECLINED; + } + /* To preserve legacy behaviour (consistent with other MPMs), use * the keepalive timeout from the base server (first on this IP:port) * when none is explicitly configured on this server. @@ -3139,6 +3151,10 @@ static void child_main(int child_num_arg, int child_bucket) rv == AP_MPM_PODX_GRACEFUL ? "graceful" : "ungraceful"); } + if (terminate_mode == ST_GRACEFUL) { + ap_mpm_wait_for_extra_connections(); + } + free(threads); clean_child_exit(resource_shortage ? APEXIT_CHILDSICK : 0); @@ -3876,6 +3892,10 @@ static void setup_slave_conn(conn_rec *c, void *csd) event_conn_state_t *cs; mcs = ap_get_module_config(c->master->conn_config, &mpm_event_module); + if (!mcs) { + /* Master connection is not managed by this MPM; nothing to inherit. */ + return; + } cs = apr_pcalloc(c->pool, sizeof(*cs)); cs->c = c; @@ -3914,7 +3934,9 @@ static int event_protocol_switch(conn_rec *c, request_rec *r, server_rec *s, event_conn_state_t *cs; cs = ap_get_module_config(c->conn_config, &mpm_event_module); - cs->sc = ap_get_module_config(s->module_config, &mpm_event_module); + if (cs) { + cs->sc = ap_get_module_config(s->module_config, &mpm_event_module); + } } return DECLINED; } @@ -3954,6 +3976,8 @@ static int event_pre_config(apr_pool_t * pconf, apr_pool_t * plog, const char *userdata_key = "mpm_event_module"; int test_atomics = 0; + ap_mpm_register_extra_connection_fns(); + debug = ap_exists_config_define("DEBUG"); if (debug) { diff --git a/server/mpm/prefork/prefork.c b/server/mpm/prefork/prefork.c index 37dc2dda4dd..d6a0a2db7d6 100644 --- a/server/mpm/prefork/prefork.c +++ b/server/mpm/prefork/prefork.c @@ -229,6 +229,9 @@ static void clean_child_exit_ex(int code, int from_signal) if (pchild) { if (!code && !from_signal) { ap_run_child_stopping(pchild, !retained->mpm->is_ungraceful); + if (!retained->mpm->is_ungraceful) { + ap_mpm_wait_for_extra_connections(); + } ap_run_child_stopped(pchild, !retained->mpm->is_ungraceful); } apr_pool_destroy(pchild); @@ -1325,6 +1328,8 @@ static int prefork_pre_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp apr_status_t rv; const char *userdata_key = "mpm_prefork_module"; + ap_mpm_register_extra_connection_fns(); + debug = ap_exists_config_define("DEBUG"); if (debug) { diff --git a/server/mpm/winnt/child.c b/server/mpm/winnt/child.c index 1ad8df0374b..27fd53c4b76 100644 --- a/server/mpm/winnt/child.c +++ b/server/mpm/winnt/child.c @@ -1262,6 +1262,10 @@ void child_main(apr_pool_t *pconf, DWORD parent_pid) ap_log_error(APLOG_MARK, APLOG_NOTICE, APR_SUCCESS, ap_server_conf, APLOGNO(00364) "Child: All worker threads have exited."); + if (graceful_shutdown) { + ap_mpm_wait_for_extra_connections(); + } + ap_run_child_stopped(pchild, graceful_shutdown); apr_thread_mutex_destroy(child_lock); diff --git a/server/mpm/winnt/mpm_winnt.c b/server/mpm/winnt/mpm_winnt.c index cbc090fb662..627714adf7f 100644 --- a/server/mpm/winnt/mpm_winnt.c +++ b/server/mpm/winnt/mpm_winnt.c @@ -1368,6 +1368,8 @@ static int winnt_pre_config(apr_pool_t *pconf_, apr_pool_t *plog, apr_pool_t *pt * -k runservice [WinNT errors logged from rewrite_args] */ + ap_mpm_register_extra_connection_fns(); + /* Initialize shared static objects. * TODO: Put config related statics into an sconf structure. */ diff --git a/server/mpm/worker/worker.c b/server/mpm/worker/worker.c index 42b81a8ed1b..8db3f08a983 100644 --- a/server/mpm/worker/worker.c +++ b/server/mpm/worker/worker.c @@ -1328,6 +1328,10 @@ static void child_main(int child_num_arg, int child_bucket) rv == AP_MPM_PODX_GRACEFUL ? ST_GRACEFUL : ST_UNGRACEFUL); } + if (terminate_mode == ST_GRACEFUL) { + ap_mpm_wait_for_extra_connections(); + } + free(threads); clean_child_exit(resource_shortage ? APEXIT_CHILDSICK : 0); @@ -2103,6 +2107,8 @@ static int worker_pre_config(apr_pool_t *pconf, apr_pool_t *plog, apr_status_t rv; const char *userdata_key = "mpm_worker_module"; + ap_mpm_register_extra_connection_fns(); + debug = ap_exists_config_define("DEBUG"); if (debug) { diff --git a/server/mpm_common.c b/server/mpm_common.c index 2973bc9f4f2..b081f860c9e 100644 --- a/server/mpm_common.c +++ b/server/mpm_common.c @@ -34,6 +34,7 @@ #include "apr_getopt.h" #include "apr_optional.h" #include "apr_allocator.h" +#include "apr_atomic.h" #include "httpd.h" #include "http_config.h" @@ -185,6 +186,52 @@ AP_DECLARE_DATA apr_size_t ap_thread_stacksize; #define ALLOCATOR_MAX_FREE_DEFAULT (2048*1024) AP_DECLARE_DATA apr_uint32_t ap_max_mem_free = ALLOCATOR_MAX_FREE_DEFAULT; +static apr_uint32_t extra_connection_count = 0; /* Number of open connections that the MPM does not own */ + +static void ap_mpm_note_extra_connection_added(void) +{ + apr_atomic_inc32(&extra_connection_count); +} + +static void ap_mpm_note_extra_connection_removed(void) +{ + apr_atomic_dec32(&extra_connection_count); +} + +AP_DECLARE(void) ap_mpm_register_extra_connection_fns(void) +{ + APR_REGISTER_OPTIONAL_FN(ap_mpm_note_extra_connection_added); + APR_REGISTER_OPTIONAL_FN(ap_mpm_note_extra_connection_removed); +} + +AP_DECLARE(void) ap_mpm_wait_for_extra_connections(void) +{ + apr_uint32_t count = apr_atomic_read32(&extra_connection_count); + apr_time_t graceful, timeout, deadline; + + if (count == 0) { + return; + } + + graceful = apr_time_from_sec(ap_graceful_shutdown_timeout); + timeout = (graceful > ap_server_conf->timeout) ? graceful : ap_server_conf->timeout; + deadline = apr_time_now() + timeout; + + do { + apr_sleep(apr_time_from_msec(100)); + count = apr_atomic_read32(&extra_connection_count); + } while (count > 0 && apr_time_now() < deadline); + + if (count > 0) { + ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ap_server_conf, + APLOGNO(10622) + "Child: %u connection(s) noted by modules did not " + "finish within %" APR_TIME_T_FMT " seconds, " + "exiting anyway", + count, apr_time_sec(timeout)); + } +} + /* Set defaults for config directives implemented here. This is * called from core's pre-config hook, so MPMs which need to override * one of these should run their pre-config hook after that of core.