From 931fe85b8df29fdbd47c6d3f6b4f724d8c6b8645 Mon Sep 17 00:00:00 2001 From: Ryan Bloom Date: Thu, 7 Jun 2001 00:09:16 +0000 Subject: [PATCH] First pass at the pipe_of_death logic for the prefork MPM. This does pass some initial testing, but it needs to be banged on more. It looks like if the server gets a lot of requests to restart all at once, there are potential problems, but other than that this does seem to solve our current restart issues. git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@89282 13f79535-47bb-0310-9956-ffa450edef68 --- include/httpd.h | 2 + include/mpm_common.h | 43 ++++++++++++++++++ server/mpm/prefork/mpm.h | 1 + server/mpm/prefork/prefork.c | 67 +++++++++++++--------------- server/mpm_common.c | 84 ++++++++++++++++++++++++++++++++++++ 5 files changed, 161 insertions(+), 36 deletions(-) diff --git a/include/httpd.h b/include/httpd.h index 9c6edf919c5..0bb3e32e58b 100644 --- a/include/httpd.h +++ b/include/httpd.h @@ -1542,6 +1542,8 @@ const char *ap_strstr_c(const char *s, const char *c); #endif +#define AP_NORESTART APR_OS_START_USEERR + 1 + #ifdef __cplusplus } #endif diff --git a/include/mpm_common.h b/include/mpm_common.h index 1873dd9778a..f029225320c 100644 --- a/include/mpm_common.h +++ b/include/mpm_common.h @@ -164,6 +164,49 @@ AP_DECLARE(gid_t) ap_gname2id(const char *name); #define AP_MPM_HARD_LIMITS_FILE APACHE_MPM_DIR "/mpm_default.h" +#ifdef AP_MPM_USES_POD + +typedef struct ap_pod_t ap_pod_t; + +struct ap_pod_t { + apr_file_t *pod_in; + apr_file_t *pod_out; + apr_pool_t *p; +}; + +/** + * Open the pipe-of-death. The pipe of death is used to tell all child + * processes that it is time to die gracefully. + * @param p The pool to use for allocating the pipe + */ +AP_DECLARE(apr_status_t) ap_mpm_pod_open(apr_pool_t *p, ap_pod_t **pod); + +/** + * Check the pipe to determine if the process has been signalled to die. + */ +AP_DECLARE(apr_status_t) ap_mpm_pod_check(ap_pod_t *pod); + +/** + * Close the pipe-of-death + */ +AP_DECLARE(apr_status_t) ap_mpm_pod_close(ap_pod_t *pod); + +/** + * Write data to the pipe-of-death, signalling that one child process + * should die. + * @param p The pool to use when allocating any required structures. + */ +AP_DECLARE(apr_status_t) ap_mpm_pod_signal(ap_pod_t *pod); + +/** + * Write data to the pipe-of-death, signalling that all child process + * should die. + * @param p The pool to use when allocating any required structures. + * @param num The number of child processes to kill + */ +AP_DECLARE(void) ap_mpm_pod_killpg(ap_pod_t *pod, int num); +#endif + #ifdef __cplusplus } #endif diff --git a/server/mpm/prefork/mpm.h b/server/mpm/prefork/mpm.h index 6a923b06363..efad24f9eba 100644 --- a/server/mpm/prefork/mpm.h +++ b/server/mpm/prefork/mpm.h @@ -68,6 +68,7 @@ #define MPM_NAME "Prefork" +#define AP_MPM_USES_POD 1 #define AP_MPM_NEEDS_RECLAIM_CHILD_PROCESSES 1 #define MPM_SYNC_CHILD_TABLE() (ap_sync_scoreboard_image()) #define MPM_CHILD_PID(i) (ap_scoreboard_image->parent[i].pid) diff --git a/server/mpm/prefork/prefork.c b/server/mpm/prefork/prefork.c index dcd8135c334..f3b1d13a93e 100644 --- a/server/mpm/prefork/prefork.c +++ b/server/mpm/prefork/prefork.c @@ -140,6 +140,8 @@ static int ap_daemons_min_free=0; static int ap_daemons_max_free=0; static int ap_daemons_limit=0; +static ap_pod_t *pod; + /* * The max child slot ever assigned, preserved across restarts. Necessary * to deal with MaxClients changes across SIGWINCH restarts. We use this @@ -182,6 +184,8 @@ int tpf_child = 0; char tpf_server_name[INETD_SERVNAME_LENGTH+1]; #endif /* TPF */ +int die_now = 0; + #ifdef GPROF /* * change directory for gprof to plop the gmon.out file @@ -232,7 +236,7 @@ static void clean_child_exit(int code) if (pchild) { apr_pool_destroy(pchild); } - ap_scoreboard_image->parent[my_child_num].process_status = SB_WORKING; + ap_mpm_pod_close(pod); chdir_for_gprof(); exit(code); } @@ -378,15 +382,7 @@ static void just_die(int sig) static void please_die_gracefully(int sig) { /* clean_child_exit(0); */ - ap_scoreboard_image->parent[my_child_num].process_status = SB_IDLE_DIE; - if (sig == SIGHUP) { - (void) ap_update_child_status(AP_CHILD_THREAD_FROM_ID(my_child_num), - SERVER_GRACEFUL, (request_rec *) NULL); - } - else { - (void) ap_update_child_status(AP_CHILD_THREAD_FROM_ID(my_child_num), - SERVER_IDLE_KILL, (request_rec *) NULL); - } + die_now = 1; } /* volatile just in case */ @@ -414,9 +410,11 @@ static void restart(int sig) return; } restart_pending = 1; +#if 0 if ((is_graceful = (sig == SIGWINCH))) { apr_pool_cleanup_kill(pconf, NULL, ap_cleanup_scoreboard); } +#endif } static void set_signals(void) @@ -479,12 +477,9 @@ static void set_signals(void) /* we want to ignore HUPs and WINCH while we're busy processing one */ sigaddset(&sa.sa_mask, SIGHUP); - sigaddset(&sa.sa_mask, SIGWINCH); sa.sa_handler = restart; if (sigaction(SIGHUP, &sa, NULL) < 0) ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGHUP)"); - if (sigaction(SIGWINCH, &sa, NULL) < 0) - ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGWINCH)"); #else if (!one_process) { apr_signal(SIGSEGV, sig_coredump); @@ -513,7 +508,7 @@ static void set_signals(void) apr_signal(SIGHUP, restart); #endif /* SIGHUP */ #ifdef SIGWINCH - apr_signal(SIGWINCH, restart); + apr_signal(SIGWINCH, SIG_IGN); #endif /* SIGWINCH */ #ifdef SIGPIPE apr_signal(SIGPIPE, SIG_IGN); @@ -533,9 +528,6 @@ static apr_socket_t *csd; static int requests_this_child; static fd_set main_fds; -#define I_AM_TO_SHUTDOWN() \ -(ap_scoreboard_image->parent[my_child_num].process_status != SB_WORKING) - int ap_graceful_stop_signalled(void) { /* not ever called anymore... */ @@ -581,12 +573,11 @@ static void child_main(int child_num_arg) apr_signal(SIGHUP, please_die_gracefully); ap_sync_scoreboard_image(); - while (!I_AM_TO_SHUTDOWN()) { + while (!die_now) { /* Prepare to receive a SIGWINCH due to graceful restart so that * we can exit cleanly. */ - apr_signal(SIGWINCH, please_die_gracefully); apr_signal(SIGTERM, just_die); /* @@ -669,13 +660,20 @@ static void child_main(int child_num_arg) */ for (;;) { ap_sync_scoreboard_image(); - if (I_AM_TO_SHUTDOWN()) { + if (die_now) { /* we didn't get a socket, and we were told to die */ clean_child_exit(0); } stat = apr_accept(&csd, sd, ptrans); if (stat == APR_SUCCESS || !APR_STATUS_IS_EINTR(stat)) break; + /* In reality, this could be done later, but to keep it + * consistent with MPMs that have a thread race-condition, + * we will do it here. + */ + if (!ap_mpm_pod_check(pod)) { + die_now = 1; + } } if (stat == APR_SUCCESS) @@ -777,19 +775,13 @@ static void child_main(int child_num_arg) } ap_sync_scoreboard_image(); - if (I_AM_TO_SHUTDOWN()) { + if (die_now) { clean_child_exit(0); } } SAFE_ACCEPT(accept_mutex_off()); /* unlock after "accept" */ - /* We've got a socket, let's at least process one request off the - * socket before we accept a graceful restart request. We set - * the signal to ignore because we don't want to disturb any - * third party code. - */ - apr_signal(SIGWINCH, SIG_IGN); /* * We now have a connection, so set it up with the appropriate * socket options, file descriptors, and read/write buffers. @@ -894,7 +886,6 @@ static int make_child(server_rec *s, int slot) * requested there's no race condition here. */ apr_signal(SIGHUP, please_die_gracefully); - apr_signal(SIGWINCH, please_die_gracefully); apr_signal(SIGTERM, just_die); ap_scoreboard_image->parent[slot].process_status = SB_WORKING; child_main(slot); @@ -940,7 +931,7 @@ static int idle_spawn_rate = 1; #endif static int hold_off_on_exponential_spawning; -static void perform_idle_server_maintenance(void) +static void perform_idle_server_maintenance(apr_pool_t *p) { int i; int to_kill; @@ -1002,7 +993,7 @@ static void perform_idle_server_maintenance(void) * shut down gracefully, in case it happened to pick up a request * while we were counting */ - kill(ap_scoreboard_image->parent[to_kill].pid, SIGWINCH); + ap_mpm_pod_signal(pod); idle_spawn_rate = 1; } else if (idle_count < ap_daemons_min_free) { @@ -1088,6 +1079,7 @@ int ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s) { int index; int remaining_children_to_start; + apr_status_t rv; pconf = _pconf; @@ -1099,6 +1091,11 @@ int ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s) /* XXX: hey, what's the right way for the mpm to indicate a fatal error? */ return 1; } + if ((rv = ap_mpm_pod_open(pconf, &pod))) { + ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s, + "Could not open pipe-of-death."); + return 1; + } SAFE_ACCEPT(accept_mutex_init(pconf)); if (!is_graceful) { @@ -1209,7 +1206,7 @@ int ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s) continue; } - perform_idle_server_maintenance(); + perform_idle_server_maintenance(pconf); #ifdef TPF shutdown_pending = os_check_server(tpf_server_name); ap_check_signals(); @@ -1244,7 +1241,6 @@ int ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s) /* we've been told to restart */ apr_signal(SIGHUP, SIG_IGN); - apr_signal(SIGWINCH, SIG_IGN); if (one_process) { /* not worth thinking about */ return 1; @@ -1264,12 +1260,11 @@ int ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s) if (is_graceful) { ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, 0, ap_server_conf, - "SIGWINCH received. Doing graceful restart"); + "Graceful restart requested, doing restart"); /* kill off the idle ones */ - if (unixd_killpg(getpgrp(), SIGWINCH) < 0) { - ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "killpg SIGWINCH"); - } + ap_mpm_pod_killpg(pod, ap_daemons_limit); + #ifndef SCOREBOARD_FILE /* This is mostly for debugging... so that we know what is still * gracefully dealing with existing request. But we can't really diff --git a/server/mpm_common.c b/server/mpm_common.c index edf28680d99..8a9fcf3fa39 100644 --- a/server/mpm_common.c +++ b/server/mpm_common.c @@ -78,6 +78,7 @@ #include "mpm.h" #include "mpm_common.h" #include "ap_mpm.h" +#include "ap_listen.h" #ifdef HAVE_PWD_H #include @@ -338,4 +339,87 @@ int initgroups(const char *name, gid_t basegid) } #endif /* def NEED_INITGROUPS */ +#ifdef AP_MPM_USES_POD +AP_DECLARE(apr_status_t) ap_mpm_pod_open(apr_pool_t *p, ap_pod_t **pod) +{ + apr_status_t rv; + + *pod = apr_palloc(p, sizeof(**pod)); + rv = apr_file_pipe_create(&((*pod)->pod_in), &((*pod)->pod_out), p); + apr_file_pipe_timeout_set((*pod)->pod_out, 0); + (*pod)->p = p; + return rv; +} + +AP_DECLARE(apr_status_t) ap_mpm_pod_check(ap_pod_t *pod) +{ + char c; + apr_size_t len = 1; + apr_status_t rv; + + rv = apr_file_read(pod->pod_in, &c, &len); + + if ((rv == APR_SUCCESS) && (len == 1)) { + return APR_SUCCESS; + } + if (rv != APR_SUCCESS) { + return rv; + } + return AP_NORESTART; +} + +AP_DECLARE(apr_status_t) ap_mpm_pod_close(ap_pod_t *pod) +{ + apr_status_t rv; + + rv = apr_file_close(pod->pod_out); + if (rv != APR_SUCCESS) { + return rv; + } + + rv = apr_file_close(pod->pod_in); + if (rv != APR_SUCCESS) { + return rv; + } + return rv; +} + +AP_DECLARE(apr_status_t) ap_mpm_pod_signal(ap_pod_t *pod) +{ + apr_socket_t *sock; + apr_sockaddr_t *sa; + apr_status_t rv; + char char_of_death = '!'; + apr_size_t one = 1; + + do { + if ((rv = apr_file_write(pod->pod_out, &char_of_death, &one)) + != APR_SUCCESS) { + if (APR_STATUS_IS_EINTR(rv)) { + continue; + } + else { + ap_log_error(APLOG_MARK, APLOG_WARNING, rv, ap_server_conf, + "write pipe_of_death"); + return rv; + } + } + } while (1); + + apr_sockaddr_info_get(&sa, "127.0.0.1", APR_UNSPEC, ap_listeners->bind_addr->port, 0, pod->p); + apr_socket_create(&sock, sa->family, SOCK_STREAM, pod->p); + apr_connect(sock, sa); + apr_socket_close(sock); + + return APR_SUCCESS; +} + +AP_DECLARE(void) ap_mpm_pod_killpg(ap_pod_t *pod, int num) +{ + int i; + for (i = 0; i < num; i++) { + ap_mpm_pod_signal(pod); + } +} +#endif -- 2.47.2