X-Git-Url: https://granicus.if.org/sourcecode?a=blobdiff_plain;ds=sidebyside;f=server%2Fmpm_unix.c;h=1a7f9359c73b51e0a7dbe178f15114f0464baf7c;hb=c20d5efb4c6f4fca3713c98f98d475bb022523f9;hp=a9cde5c0bb47124994fb4e5c0d73a30d9c84efec;hpb=ebb62867fb8a335f70c30e01280cc30f5c4d0597;p=apache diff --git a/server/mpm_unix.c b/server/mpm_unix.c index a9cde5c0bb..1a7f9359c7 100644 --- a/server/mpm_unix.c +++ b/server/mpm_unix.c @@ -25,6 +25,8 @@ * does not belong in src/os/unix */ +#ifndef WIN32 + #include "apr.h" #include "apr_thread_proc.h" #include "apr_signal.h" @@ -37,6 +39,7 @@ #include "httpd.h" #include "http_config.h" +#include "http_core.h" #include "http_log.h" #include "http_main.h" #include "mpm_common.h" @@ -56,27 +59,31 @@ #endif -APLOG_USE_MODULE(core); +/* we know core's module_index is 0 */ +#undef APLOG_MODULE_INDEX +#define APLOG_MODULE_INDEX AP_CORE_MODULE_INDEX typedef enum {DO_NOTHING, SEND_SIGTERM, SEND_SIGKILL, GIVEUP} action_t; typedef struct extra_process_t { struct extra_process_t *next; pid_t pid; + ap_generation_t gen; } extra_process_t; static extra_process_t *extras; -void ap_register_extra_mpm_process(pid_t pid) +AP_DECLARE(void) ap_register_extra_mpm_process(pid_t pid, ap_generation_t gen) { - extra_process_t *p = (extra_process_t *)malloc(sizeof(extra_process_t)); + extra_process_t *p = (extra_process_t *)ap_malloc(sizeof(extra_process_t)); p->next = extras; p->pid = pid; + p->gen = gen; extras = p; } -int ap_unregister_extra_mpm_process(pid_t pid) +AP_DECLARE(int) ap_unregister_extra_mpm_process(pid_t pid, ap_generation_t *old_gen) { extra_process_t *cur = extras; extra_process_t *prev = NULL; @@ -93,6 +100,7 @@ int ap_unregister_extra_mpm_process(pid_t pid) else { extras = cur->next; } + *old_gen = cur->gen; free(cur); return 1; /* found */ } @@ -112,7 +120,7 @@ static int reclaim_one_pid(pid_t pid, action_t action) /* Ensure pid sanity. */ if (pid < 1) { return 1; - } + } proc.pid = pid; waitret = apr_proc_wait(&proc, &status, &why, APR_NOWAIT); @@ -129,7 +137,7 @@ static int reclaim_one_pid(pid_t pid, action_t action) case SEND_SIGTERM: /* ok, now it's being annoying */ ap_log_error(APLOG_MARK, APLOG_WARNING, - 0, ap_server_conf, + 0, ap_server_conf, APLOGNO(00045) "child process %" APR_PID_T_FMT " still did not exit, " "sending a SIGTERM", @@ -139,7 +147,7 @@ static int reclaim_one_pid(pid_t pid, action_t action) case SEND_SIGKILL: ap_log_error(APLOG_MARK, APLOG_ERR, - 0, ap_server_conf, + 0, ap_server_conf, APLOGNO(00046) "child process %" APR_PID_T_FMT " still did not exit, " "sending a SIGKILL", @@ -154,7 +162,7 @@ static int reclaim_one_pid(pid_t pid, action_t action) * after the restart. */ ap_log_error(APLOG_MARK, APLOG_ERR, - 0, ap_server_conf, + 0, ap_server_conf, APLOGNO(00047) "could not make child process %" APR_PID_T_FMT " exit, " "attempting to continue anyway", @@ -165,7 +173,8 @@ static int reclaim_one_pid(pid_t pid, action_t action) return 0; } -void ap_reclaim_child_processes(int terminate) +AP_DECLARE(void) ap_reclaim_child_processes(int terminate, + ap_reclaim_callback_fn_t *mpm_callback) { apr_time_t waittime = 1024 * 16; int i; @@ -228,7 +237,7 @@ void ap_reclaim_child_processes(int terminate) } if (reclaim_one_pid(pid, action_table[cur_action].action)) { - ap_mpm_note_child_killed(i); + mpm_callback(i, 0, 0); } else { ++not_dead_yet; @@ -237,10 +246,16 @@ void ap_reclaim_child_processes(int terminate) cur_extra = extras; while (cur_extra) { + ap_generation_t old_gen; extra_process_t *next = cur_extra->next; if (reclaim_one_pid(cur_extra->pid, action_table[cur_action].action)) { - AP_DEBUG_ASSERT(1 == ap_unregister_extra_mpm_process(cur_extra->pid)); + if (ap_unregister_extra_mpm_process(cur_extra->pid, &old_gen) == 1) { + mpm_callback(-1, cur_extra->pid, old_gen); + } + else { + AP_DEBUG_ASSERT(1 == 0); + } } else { ++not_dead_yet; @@ -255,7 +270,7 @@ void ap_reclaim_child_processes(int terminate) action_table[cur_action].action != GIVEUP); } -void ap_relieve_child_processes(void) +AP_DECLARE(void) ap_relieve_child_processes(ap_reclaim_callback_fn_t *mpm_callback) { int i; extra_process_t *cur_extra; @@ -273,16 +288,22 @@ void ap_relieve_child_processes(void) } if (reclaim_one_pid(pid, DO_NOTHING)) { - ap_mpm_note_child_killed(i); + mpm_callback(i, 0, 0); } } cur_extra = extras; while (cur_extra) { + ap_generation_t old_gen; extra_process_t *next = cur_extra->next; if (reclaim_one_pid(cur_extra->pid, DO_NOTHING)) { - AP_DEBUG_ASSERT(1 == ap_unregister_extra_mpm_process(cur_extra->pid)); + if (ap_unregister_extra_mpm_process(cur_extra->pid, &old_gen) == 1) { + mpm_callback(-1, cur_extra->pid, old_gen); + } + else { + AP_DEBUG_ASSERT(1 == 0); + } } cur_extra = next; } @@ -292,7 +313,7 @@ void ap_relieve_child_processes(void) * the pid is a member of the current process group; either using * apr_proc_wait(), where waitpid() guarantees to fail for non-child * processes; or by using getpgid() directly, if available. */ -apr_status_t ap_mpm_safe_kill(pid_t pid, int sig) +AP_DECLARE(apr_status_t) ap_mpm_safe_kill(pid_t pid, int sig) { #ifndef HAVE_GETPGID apr_proc_t proc; @@ -316,7 +337,7 @@ apr_status_t ap_mpm_safe_kill(pid_t pid, int sig) else if (rv != APR_CHILD_NOTDONE) { /* The child is already dead and reaped, or was a bogus pid - * log this either way. */ - ap_log_error(APLOG_MARK, APLOG_NOTICE, rv, ap_server_conf, + ap_log_error(APLOG_MARK, APLOG_NOTICE, rv, ap_server_conf, APLOGNO(00048) "cannot send signal %d to pid %ld (non-child or " "already dead)", sig, (long)pid); return APR_EINVAL; @@ -329,25 +350,26 @@ apr_status_t ap_mpm_safe_kill(pid_t pid, int sig) return APR_EINVAL; } - pg = getpgid(pid); + pg = getpgid(pid); if (pg == -1) { /* Process already dead... */ return errno; } if (pg != getpgrp()) { - ap_log_error(APLOG_MARK, APLOG_ALERT, 0, ap_server_conf, + ap_log_error(APLOG_MARK, APLOG_ALERT, 0, ap_server_conf, APLOGNO(00049) "refusing to send signal %d to pid %ld outside " "process group", sig, (long)pid); return APR_EINVAL; } -#endif +#endif return kill(pid, sig) ? errno : APR_SUCCESS; } -int ap_process_child_status(apr_proc_t *pid, apr_exit_why_e why, int status) +AP_DECLARE(int) ap_process_child_status(apr_proc_t *pid, apr_exit_why_e why, + int status) { int signum = status; const char *sigdesc; @@ -367,7 +389,7 @@ int ap_process_child_status(apr_proc_t *pid, apr_exit_why_e why, int status) if (status == APEXIT_CHILDFATAL) { ap_log_error(APLOG_MARK, APLOG_ALERT, - 0, ap_server_conf, + 0, ap_server_conf, APLOGNO(00050) "Child %" APR_PID_T_FMT " returned a Fatal error... Apache is exiting!", pid->pid); @@ -390,7 +412,7 @@ int ap_process_child_status(apr_proc_t *pid, apr_exit_why_e why, int status) default: if (APR_PROC_CHECK_CORE_DUMP(why)) { ap_log_error(APLOG_MARK, APLOG_NOTICE, - 0, ap_server_conf, + 0, ap_server_conf, APLOGNO(00051) "child pid %ld exit signal %s (%d), " "possible coredump in %s", (long)pid->pid, sigdesc, signum, @@ -398,7 +420,7 @@ int ap_process_child_status(apr_proc_t *pid, apr_exit_why_e why, int status) } else { ap_log_error(APLOG_MARK, APLOG_NOTICE, - 0, ap_server_conf, + 0, ap_server_conf, APLOGNO(00052) "child pid %ld exit signal %s (%d)", (long)pid->pid, sigdesc, signum); } @@ -472,21 +494,122 @@ static apr_status_t pod_signal_internal(ap_pod_t *pod) rv = apr_file_write(pod->pod_out, &char_of_death, &one); if (rv != APR_SUCCESS) { - ap_log_error(APLOG_MARK, APLOG_WARNING, rv, ap_server_conf, + ap_log_error(APLOG_MARK, APLOG_WARNING, rv, ap_server_conf, APLOGNO(00053) "write pipe_of_death"); } return rv; } -/* This function connects to the server, then immediately closes the connection. - * This permits the MPM to skip the poll when there is only one listening - * socket, because it provides a alternate way to unblock an accept() when - * the pod is used. - */ +AP_DECLARE(apr_status_t) ap_mpm_podx_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); + if (rv != APR_SUCCESS) { + return rv; + } + /* + apr_file_pipe_timeout_set((*pod)->pod_in, 0); + */ + (*pod)->p = p; + + /* close these before exec. */ + apr_file_inherit_unset((*pod)->pod_in); + apr_file_inherit_unset((*pod)->pod_out); + + return APR_SUCCESS; +} + +AP_DECLARE(int) ap_mpm_podx_check(ap_pod_t *pod) +{ + char c; + apr_os_file_t fd; + int rc; + + /* we need to surface EINTR so we'll have to grab the + * native file descriptor and do the OS read() ourselves + */ + apr_os_file_get(&fd, pod->pod_in); + rc = read(fd, &c, 1); + if (rc == 1) { + switch (c) { + case AP_MPM_PODX_RESTART_CHAR: + return AP_MPM_PODX_RESTART; + case AP_MPM_PODX_GRACEFUL_CHAR: + return AP_MPM_PODX_GRACEFUL; + } + } + return AP_MPM_PODX_NORESTART; +} + +AP_DECLARE(apr_status_t) ap_mpm_podx_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; +} + +static apr_status_t podx_signal_internal(ap_pod_t *pod, + ap_podx_restart_t graceful) +{ + apr_status_t rv; + apr_size_t one = 1; + char char_of_death = ' '; + switch (graceful) { + case AP_MPM_PODX_RESTART: + char_of_death = AP_MPM_PODX_RESTART_CHAR; + break; + case AP_MPM_PODX_GRACEFUL: + char_of_death = AP_MPM_PODX_GRACEFUL_CHAR; + break; + case AP_MPM_PODX_NORESTART: + break; + } + + rv = apr_file_write(pod->pod_out, &char_of_death, &one); + if (rv != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_WARNING, rv, ap_server_conf, APLOGNO(02404) + "write pipe_of_death"); + } + return rv; +} + +AP_DECLARE(apr_status_t) ap_mpm_podx_signal(ap_pod_t * pod, + ap_podx_restart_t graceful) +{ + return podx_signal_internal(pod, graceful); +} + +AP_DECLARE(void) ap_mpm_podx_killpg(ap_pod_t * pod, int num, + ap_podx_restart_t graceful) +{ + int i; + apr_status_t rv = APR_SUCCESS; + + for (i = 0; i < num && rv == APR_SUCCESS; i++) { + rv = podx_signal_internal(pod, graceful); + } +} + +/* This function connects to the server and sends enough data to + * ensure the child wakes up and processes a new connection. This + * permits the MPM to skip the poll when there is only one listening + * socket, because it provides a alternate way to unblock an accept() + * when the pod is used. */ static apr_status_t dummy_connection(ap_pod_t *pod) { - char *srequest; + const char *data; apr_status_t rv; apr_socket_t *sock; apr_pool_t *p; @@ -513,7 +636,7 @@ static apr_status_t dummy_connection(ap_pod_t *pod) rv = apr_socket_create(&sock, lp->bind_addr->family, SOCK_STREAM, 0, p); if (rv != APR_SUCCESS) { - ap_log_error(APLOG_MARK, APLOG_WARNING, rv, ap_server_conf, + ap_log_error(APLOG_MARK, APLOG_WARNING, rv, ap_server_conf, APLOGNO(00054) "get socket to connect to listener"); apr_pool_destroy(p); return rv; @@ -527,7 +650,7 @@ static apr_status_t dummy_connection(ap_pod_t *pod) */ rv = apr_socket_timeout_set(sock, apr_time_from_sec(3)); if (rv != APR_SUCCESS) { - ap_log_error(APLOG_MARK, APLOG_WARNING, rv, ap_server_conf, + ap_log_error(APLOG_MARK, APLOG_WARNING, rv, ap_server_conf, APLOGNO(00055) "set timeout on socket to connect to listener"); apr_socket_close(sock); apr_pool_destroy(p); @@ -546,28 +669,43 @@ static apr_status_t dummy_connection(ap_pod_t *pod) log_level = APLOG_DEBUG; } - ap_log_error(APLOG_MARK, log_level, rv, ap_server_conf, + ap_log_error(APLOG_MARK, log_level, rv, ap_server_conf, APLOGNO(00056) "connect to listener on %pI", lp->bind_addr); + apr_pool_destroy(p); + return rv; } - /* Create the request string. We include a User-Agent so that - * adminstrators can track down the cause of the odd-looking - * requests in their logs. - */ - srequest = apr_pstrcat(p, "OPTIONS * HTTP/1.0\r\nUser-Agent: ", + if (lp->protocol && strcasecmp(lp->protocol, "https") == 0) { + /* Send a TLS 1.0 close_notify alert. This is perhaps the + * "least wrong" way to open and cleanly terminate an SSL + * connection. It should "work" without noisy error logs if + * the server actually expects SSLv3/TLSv1. With + * SSLv23_server_method() OpenSSL's SSL_accept() fails + * ungracefully on receipt of this message, since it requires + * an 11-byte ClientHello message and this is too short. */ + static const unsigned char tls10_close_notify[7] = { + '\x15', /* TLSPlainText.type = Alert (21) */ + '\x03', '\x01', /* TLSPlainText.version = {3, 1} */ + '\x00', '\x02', /* TLSPlainText.length = 2 */ + '\x01', /* Alert.level = warning (1) */ + '\x00' /* Alert.description = close_notify (0) */ + }; + data = (const char *)tls10_close_notify; + len = sizeof(tls10_close_notify); + } + else /* ... XXX other request types here? */ { + /* Create an HTTP request string. We include a User-Agent so + * that adminstrators can track down the cause of the + * odd-looking requests in their logs. A complete request is + * used since kernel-level filtering may require that much + * data before returning from accept(). */ + data = apr_pstrcat(p, "OPTIONS * HTTP/1.0\r\nUser-Agent: ", ap_get_server_description(), " (internal dummy connection)\r\n\r\n", NULL); + len = strlen(data); + } - /* Since some operating systems support buffering of data or entire - * requests in the kernel, we send a simple request, to make sure - * the server pops out of a blocking accept(). - */ - /* XXX: This is HTTP specific. We should look at the Protocol for each - * listener, and send the correct type of request to trigger any Accept - * Filters. - */ - len = strlen(srequest); - apr_socket_send(sock, srequest, &len); + apr_socket_send(sock, data, &len); apr_socket_close(sock); apr_pool_destroy(p); @@ -604,17 +742,23 @@ void ap_mpm_pod_killpg(ap_pod_t *pod, int num) * readers stranded (a number of them could be tied up for * a while serving time-consuming requests) */ + /* Recall: we only worry about IDLE child processes here */ for (i = 0; i < num && rv == APR_SUCCESS; i++) { + if (ap_scoreboard_image->servers[i][0].status != SERVER_READY || + ap_scoreboard_image->servers[i][0].pid == 0) { + continue; + } rv = dummy_connection(pod); } } static const char *dash_k_arg = NULL; +static const char *dash_k_arg_noarg = "noarg"; static int send_signal(pid_t pid, int sig) { if (kill(pid, sig) < 0) { - ap_log_error(APLOG_MARK, APLOG_STARTUP, errno, NULL, + ap_log_error(APLOG_MARK, APLOG_STARTUP, errno, NULL, APLOGNO(00057) "sending signal to server"); return 1; } @@ -626,17 +770,16 @@ int ap_signal_server(int *exit_status, apr_pool_t *pconf) apr_status_t rv; pid_t otherpid; int running = 0; - int have_pid_file = 0; const char *status; *exit_status = 0; rv = ap_read_pid(pconf, ap_pid_fname, &otherpid); if (rv != APR_SUCCESS) { - if (rv != APR_ENOENT) { - ap_log_error(APLOG_MARK, APLOG_STARTUP, rv, NULL, + if (!APR_STATUS_IS_ENOENT(rv)) { + ap_log_error(APLOG_MARK, APLOG_STARTUP, rv, NULL, APLOGNO(00058) "Error retrieving pid file %s", ap_pid_fname); - ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, + ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00059) "Remove it before continuing if it is corrupted."); *exit_status = 1; return 1; @@ -644,7 +787,6 @@ int ap_signal_server(int *exit_status, apr_pool_t *pconf) status = "httpd (no pid file) not running"; } else { - have_pid_file = 1; if (kill(otherpid, 0) == 0) { running = 1; status = apr_psprintf(pconf, @@ -658,7 +800,7 @@ int ap_signal_server(int *exit_status, apr_pool_t *pconf) } } - if (!strcmp(dash_k_arg, "start")) { + if (!strcmp(dash_k_arg, "start") || dash_k_arg == dash_k_arg_noarg) { if (running) { printf("%s\n", status); return 1; @@ -715,12 +857,10 @@ void ap_mpm_rewrite_args(process_rec *process) apr_getopt_t *opt; char optbuf[3]; const char *optarg; - int fixed_args; mpm_new_argv = apr_array_make(process->pool, process->argc, sizeof(const char **)); *(const char **)apr_array_push(mpm_new_argv) = process->argv[0]; - fixed_args = mpm_new_argv->nelts; apr_getopt_init(&opt, process->pool, process->argc, process->argv); opt->errfn = NULL; optbuf[0] = '-'; @@ -760,9 +900,11 @@ void ap_mpm_rewrite_args(process_rec *process) process->argc = mpm_new_argv->nelts; process->argv = (const char * const *)mpm_new_argv->elts; - if (dash_k_arg) { - APR_REGISTER_OPTIONAL_FN(ap_signal_server); + if (NULL == dash_k_arg) { + dash_k_arg = dash_k_arg_noarg; } + + APR_REGISTER_OPTIONAL_FN(ap_signal_server); } static pid_t parent_pid, my_pid; @@ -828,7 +970,7 @@ static void sig_coredump(int sig) */ if (getpid() == parent_pid) { ap_log_error(APLOG_MARK, APLOG_NOTICE, - 0, ap_server_conf, + 0, ap_server_conf, APLOGNO(00060) "seg fault or similar nasty error detected " "in the parent process"); /* XXX we can probably add some rudimentary cleanup code here, @@ -847,13 +989,14 @@ static void sig_coredump(int sig) */ } -apr_status_t ap_fatal_signal_child_setup(server_rec *s) +AP_DECLARE(apr_status_t) ap_fatal_signal_child_setup(server_rec *s) { my_pid = getpid(); return APR_SUCCESS; } -apr_status_t ap_fatal_signal_setup(server_rec *s, apr_pool_t *in_pconf) +AP_DECLARE(apr_status_t) ap_fatal_signal_setup(server_rec *s, + apr_pool_t *in_pconf) { #ifndef NO_USE_SIGACTION struct sigaction sa; @@ -870,26 +1013,26 @@ apr_status_t ap_fatal_signal_setup(server_rec *s, apr_pool_t *in_pconf) sa.sa_handler = sig_coredump; if (sigaction(SIGSEGV, &sa, NULL) < 0) - ap_log_error(APLOG_MARK, APLOG_WARNING, errno, s, "sigaction(SIGSEGV)"); + ap_log_error(APLOG_MARK, APLOG_WARNING, errno, s, APLOGNO(00061) "sigaction(SIGSEGV)"); #ifdef SIGBUS if (sigaction(SIGBUS, &sa, NULL) < 0) - ap_log_error(APLOG_MARK, APLOG_WARNING, errno, s, "sigaction(SIGBUS)"); + ap_log_error(APLOG_MARK, APLOG_WARNING, errno, s, APLOGNO(00062) "sigaction(SIGBUS)"); #endif #ifdef SIGABORT if (sigaction(SIGABORT, &sa, NULL) < 0) - ap_log_error(APLOG_MARK, APLOG_WARNING, errno, s, "sigaction(SIGABORT)"); + ap_log_error(APLOG_MARK, APLOG_WARNING, errno, s, APLOGNO(00063) "sigaction(SIGABORT)"); #endif #ifdef SIGABRT if (sigaction(SIGABRT, &sa, NULL) < 0) - ap_log_error(APLOG_MARK, APLOG_WARNING, errno, s, "sigaction(SIGABRT)"); + ap_log_error(APLOG_MARK, APLOG_WARNING, errno, s, APLOGNO(00064) "sigaction(SIGABRT)"); #endif #ifdef SIGILL if (sigaction(SIGILL, &sa, NULL) < 0) - ap_log_error(APLOG_MARK, APLOG_WARNING, errno, s, "sigaction(SIGILL)"); + ap_log_error(APLOG_MARK, APLOG_WARNING, errno, s, APLOGNO(00065) "sigaction(SIGILL)"); #endif #ifdef SIGFPE if (sigaction(SIGFPE, &sa, NULL) < 0) - ap_log_error(APLOG_MARK, APLOG_WARNING, errno, s, "sigaction(SIGFPE)"); + ap_log_error(APLOG_MARK, APLOG_WARNING, errno, s, APLOGNO(00066) "sigaction(SIGFPE)"); #endif #else /* NO_USE_SIGACTION */ @@ -918,3 +1061,5 @@ apr_status_t ap_fatal_signal_setup(server_rec *s, apr_pool_t *in_pconf) return APR_SUCCESS; } + +#endif /* WIN32 */