From: Jeff Trawick Date: Thu, 26 Feb 2004 20:32:23 +0000 (+0000) Subject: merge this feature, and code cleanups for signal handlers, X-Git-Tag: 2.0.49~73 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=86e4f7f90155e019fa0b4685c697a5ddd195912c;p=thirdparty%2Fapache%2Fhttpd.git merge this feature, and code cleanups for signal handlers, from 2.1-dev: Add fatal exception hook for use by diagnostic modules. The hook is only available if the --enable-exception-hook configure parm is used and the EnableExceptionHook directive has been set to "on". Reviewed by: stoddard, jerenkrantz, gregames git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/APACHE_2_0_BRANCH@102786 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/CHANGES b/CHANGES index 921c46ab445..e4ee2c533c9 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,10 @@ Changes with Apache 2.0.49 + *) Add fatal exception hook for use by diagnostic modules. The hook + is only available if the --enable-exception-hook configure parm + is used and the EnableExceptionHook directive has been set to + "on". [Jeff Trawick] + *) Allow mod_auth_digest to work with sub-requests with different methods than the original request. PR 25040. [Josh Dady ] diff --git a/STATUS b/STATUS index dc7c1981152..0205c420ec7 100644 --- a/STATUS +++ b/STATUS @@ -1,5 +1,5 @@ APACHE 2.0 STATUS: -*-text-*- -Last modified at [$Date: 2004/02/26 20:30:26 $] +Last modified at [$Date: 2004/02/26 20:32:19 $] Release: @@ -276,15 +276,6 @@ PATCHES TO BACKPORT FROM 2.1 http://cvs.apache.org/viewcvs.cgi/httpd-2.0/modules/arch/win32/mod_isapi.c?r1=1.98&r2=1.99 +1: trawick, stoddard - * fatal exception hook and related signal handler cleanups - This also fixes an issue for me on Solaris where the cgid daemon - won't restart multiple times because of something different - about the signal handling the second time it is started. With - this patch I can "kill -SEGV" the cgid daemon process a number - of times and the daemon will be restarted each time. - http://www.apache.org/~trawick/fatal_exception_20.patch - +1: trawick, stoddard, jerenkrantz, gregames - CURRENT RELEASE NOTES: * Backwards compatibility is expected of future Apache 2.0 releases, diff --git a/configure.in b/configure.in index 687d83a4819..aa4dceabfd4 100644 --- a/configure.in +++ b/configure.in @@ -368,6 +368,12 @@ else nonssl_listen_stmt_2="Listen [[::]]:@@Port@@" fi +AC_ARG_ENABLE(exception-hook,APACHE_HELP_STRING(--enable-exception-hook,Enable fatal exception hook), +[ + AC_DEFINE(AP_ENABLE_EXCEPTION_HOOK, 1, + [Allow modules to run hook after a fatal exception]) +])dnl + AC_ARG_ENABLE(maintainer-mode,APACHE_HELP_STRING(--enable-maintainer-mode,Turn on debugging and compile time warnings), [ APR_ADDTO(CPPFLAGS, -DAP_DEBUG) diff --git a/include/ap_mpm.h b/include/ap_mpm.h index afe030678f5..ce351aff31d 100644 --- a/include/ap_mpm.h +++ b/include/ap_mpm.h @@ -163,4 +163,13 @@ extern void moncontrol(int); #define AP_MONCONTROL(x) #endif +#if AP_ENABLE_EXCEPTION_HOOK +typedef struct ap_exception_info_t { + int sig; + pid_t pid; +} ap_exception_info_t; + +AP_DECLARE_HOOK(int,fatal_exception,(ap_exception_info_t *ei)) +#endif /*AP_ENABLE_EXCEPTION_HOOK*/ + #endif diff --git a/include/mpm_common.h b/include/mpm_common.h index 0605131093a..13d7afd9bb8 100644 --- a/include/mpm_common.h +++ b/include/mpm_common.h @@ -249,6 +249,16 @@ extern const char *ap_mpm_set_max_mem_free(cmd_parms *cmd, void *dummy, const char *arg); #endif +#ifdef AP_MPM_WANT_FATAL_SIGNAL_HANDLER +extern apr_status_t ap_fatal_signal_setup(server_rec *s, apr_pool_t *pconf); +extern apr_status_t ap_fatal_signal_child_setup(server_rec *s); +#endif + +#if AP_ENABLE_EXCEPTION_HOOK +extern const char *ap_mpm_set_exception_hook(cmd_parms *cmd, void *dummy, + const char *arg); +#endif + #ifdef __cplusplus } #endif diff --git a/server/core.c b/server/core.c index abc0befac71..5ca95130b85 100644 --- a/server/core.c +++ b/server/core.c @@ -3242,6 +3242,10 @@ AP_INIT_TAKE1("AcceptMutex", ap_mpm_set_accept_lock_mech, NULL, RSRC_CONF, AP_INIT_TAKE1("MaxMemFree", ap_mpm_set_max_mem_free, NULL, RSRC_CONF, "Maximum number of 1k blocks a particular childs allocator may hold."), #endif +#if AP_ENABLE_EXCEPTION_HOOK +AP_INIT_TAKE1("EnableExceptionHook", ap_mpm_set_exception_hook, NULL, RSRC_CONF, + "Controls whether exception hook may be called after a crash"), +#endif { NULL } }; diff --git a/server/mpm/experimental/leader/leader.c b/server/mpm/experimental/leader/leader.c index af427c4322e..6142b326385 100644 --- a/server/mpm/experimental/leader/leader.c +++ b/server/mpm/experimental/leader/leader.c @@ -422,40 +422,6 @@ static void clean_child_exit(int code) exit(code); } -/* handle all varieties of core dumping signals */ -static void sig_coredump(int sig) -{ - apr_filepath_set(ap_coredump_dir, pconf); - apr_signal(sig, SIG_DFL); - /* linuxthreads issue calling getpid() here: - * This comparison won't match if the crashing thread is - * some module's thread that runs in the parent process. - * The fallout, which is limited to linuxthreads: - * The special log message won't be written when such a - * thread in the parent causes the parent to crash. - */ - if (getpid() == parent_pid) { - ap_log_error(APLOG_MARK, APLOG_NOTICE, - 0, ap_server_conf, - "seg fault or similar nasty error detected " - "in the parent process"); - - /* XXX we can probably add some rudimentary cleanup code here, - * like getting rid of the pid file. If any additional bad stuff - * happens, we are protected from recursive errors taking down the - * system since this function is no longer the signal handler GLA - */ - } - kill(ap_my_pid, sig); - /* At this point we've got sig blocked, because we're still inside - * the signal handler. When we leave the signal handler it will - * be unblocked, and we'll take the signal... and coredump or whatever - * is appropriate for this particular Unix. In addition the parent - * will see the real signal we received -- whereas if we called - * abort() here, the parent would only see SIGABRT. - */ -} - static void just_die(int sig) { clean_child_exit(0); @@ -535,42 +501,16 @@ 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; - if (!one_process) { - sa.sa_handler = sig_coredump; -#if defined(SA_ONESHOT) - sa.sa_flags = SA_ONESHOT; -#elif defined(SA_RESETHAND) - sa.sa_flags = SA_RESETHAND; -#endif - if (sigaction(SIGSEGV, &sa, NULL) < 0) - ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, - "sigaction(SIGSEGV)"); -#ifdef SIGBUS - if (sigaction(SIGBUS, &sa, NULL) < 0) - ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, - "sigaction(SIGBUS)"); -#endif -#ifdef SIGABORT - if (sigaction(SIGABORT, &sa, NULL) < 0) - ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, - "sigaction(SIGABORT)"); -#endif -#ifdef SIGABRT - if (sigaction(SIGABRT, &sa, NULL) < 0) - ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, - "sigaction(SIGABRT)"); -#endif -#ifdef SIGILL - if (sigaction(SIGILL, &sa, NULL) < 0) - ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, - "sigaction(SIGILL)"); -#endif - 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, @@ -612,19 +552,6 @@ static void set_signals(void) "sigaction(" AP_SIG_GRACEFUL_STRING ")"); #else if (!one_process) { - apr_signal(SIGSEGV, sig_coredump); -#ifdef SIGBUS - apr_signal(SIGBUS, sig_coredump); -#endif /* SIGBUS */ -#ifdef SIGABORT - apr_signal(SIGABORT, sig_coredump); -#endif /* SIGABORT */ -#ifdef SIGABRT - apr_signal(SIGABRT, sig_coredump); -#endif /* SIGABRT */ -#ifdef SIGILL - apr_signal(SIGILL, sig_coredump); -#endif /* SIGILL */ #ifdef SIGXCPU apr_signal(SIGXCPU, SIG_DFL); #endif /* SIGXCPU */ @@ -1080,6 +1007,7 @@ static void child_main(int child_num_arg) */ ap_my_pid = getpid(); + ap_fatal_signal_child_setup(ap_server_conf); apr_pool_create(&pchild, pconf); /*stuff to do before we switch id's, so we have permissions.*/ diff --git a/server/mpm/experimental/leader/mpm.h b/server/mpm/experimental/leader/mpm.h index ec4129a7b14..e9e51438dce 100644 --- a/server/mpm/experimental/leader/mpm.h +++ b/server/mpm/experimental/leader/mpm.h @@ -34,6 +34,7 @@ #define AP_MPM_WANT_SET_ACCEPT_LOCK_MECH #define AP_MPM_WANT_SIGNAL_SERVER #define AP_MPM_WANT_SET_MAX_MEM_FREE +#define AP_MPM_WANT_FATAL_SIGNAL_HANDLER #define AP_MPM_DISABLE_NAGLE_ACCEPTED_SOCK #define AP_MPM_USES_POD 1 diff --git a/server/mpm/experimental/perchild/mpm.h b/server/mpm/experimental/perchild/mpm.h index 2ae76146caf..95f3eb35f3e 100644 --- a/server/mpm/experimental/perchild/mpm.h +++ b/server/mpm/experimental/perchild/mpm.h @@ -34,6 +34,7 @@ #define AP_MPM_WANT_SET_COREDUMPDIR #define AP_MPM_WANT_SET_ACCEPT_LOCK_MECH #define AP_MPM_WANT_SIGNAL_SERVER +#define AP_MPM_WANT_FATAL_SIGNAL_HANDLER #define AP_MPM_USES_POD #define MPM_CHILD_PID(i) (ap_scoreboard_image->parent[i].pid) diff --git a/server/mpm/experimental/perchild/perchild.c b/server/mpm/experimental/perchild/perchild.c index 4ddaee6a4f2..d068152f91b 100644 --- a/server/mpm/experimental/perchild/perchild.c +++ b/server/mpm/experimental/perchild/perchild.c @@ -281,21 +281,6 @@ static void clean_child_exit(int code) exit(code); } -/* handle all varieties of core dumping signals */ -static void sig_coredump(int sig) -{ - chdir(ap_coredump_dir); - apr_signal(sig, SIG_DFL); - kill(getpid(), sig); - /* At this point we've got sig blocked, because we're still inside - * the signal handler. When we leave the signal handler it will - * be unblocked, and we'll take the signal... and coredump or whatever - * is appropriate for this particular Unix. In addition the parent - * will see the real signal we received -- whereas if we called - * abort() here, the parent would only see SIGABRT. - */ -} - static void just_die(int sig) { clean_child_exit(0); @@ -375,42 +360,16 @@ 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; - if (!one_process) { - sa.sa_handler = sig_coredump; -#if defined(SA_ONESHOT) - sa.sa_flags = SA_ONESHOT; -#elif defined(SA_RESETHAND) - sa.sa_flags = SA_RESETHAND; -#endif - if (sigaction(SIGSEGV, &sa, NULL) < 0) - ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, - "sigaction(SIGSEGV)"); -#ifdef SIGBUS - if (sigaction(SIGBUS, &sa, NULL) < 0) - ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, - "sigaction(SIGBUS)"); -#endif -#ifdef SIGABORT - if (sigaction(SIGABORT, &sa, NULL) < 0) - ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, - "sigaction(SIGABORT)"); -#endif -#ifdef SIGABRT - if (sigaction(SIGABRT, &sa, NULL) < 0) - ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, - "sigaction(SIGABRT)"); -#endif -#ifdef SIGILL - if (sigaction(SIGILL, &sa, NULL) < 0) - ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, - "sigaction(SIGILL)"); -#endif - 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, @@ -452,19 +411,6 @@ static void set_signals(void) "sigaction(" AP_SIG_GRACEFUL_STRING ")"); #else if (!one_process) { - apr_signal(SIGSEGV, sig_coredump); -#ifdef SIGBUS - apr_signal(SIGBUS, sig_coredump); -#endif /* SIGBUS */ -#ifdef SIGABORT - apr_signal(SIGABORT, sig_coredump); -#endif /* SIGABORT */ -#ifdef SIGABRT - apr_signal(SIGABRT, sig_coredump); -#endif /* SIGABRT */ -#ifdef SIGILL - apr_signal(SIGILL, sig_coredump); -#endif /* SIGILL */ #ifdef SIGXCPU apr_signal(SIGXCPU, SIG_DFL); #endif /* SIGXCPU */ @@ -982,6 +928,7 @@ static void child_main(int child_num_arg) ap_listen_rec *lr; my_pid = getpid(); + ap_fatal_signal_child_setup(ap_server_conf); child_num = child_num_arg; apr_pool_create(&pchild, pconf); diff --git a/server/mpm/experimental/threadpool/mpm.h b/server/mpm/experimental/threadpool/mpm.h index 7423c4c3670..afdcce52100 100644 --- a/server/mpm/experimental/threadpool/mpm.h +++ b/server/mpm/experimental/threadpool/mpm.h @@ -34,6 +34,7 @@ #define AP_MPM_WANT_SET_ACCEPT_LOCK_MECH #define AP_MPM_WANT_SIGNAL_SERVER #define AP_MPM_WANT_SET_MAX_MEM_FREE +#define AP_MPM_WANT_FATAL_SIGNAL_HANDLER #define AP_MPM_DISABLE_NAGLE_ACCEPTED_SOCK #define MPM_CHILD_PID(i) (ap_scoreboard_image->parent[i].pid) diff --git a/server/mpm/experimental/threadpool/threadpool.c b/server/mpm/experimental/threadpool/threadpool.c index a821d8eec73..a0df66ceeac 100644 --- a/server/mpm/experimental/threadpool/threadpool.c +++ b/server/mpm/experimental/threadpool/threadpool.c @@ -498,40 +498,6 @@ static void clean_child_exit(int code) exit(code); } -/* handle all varieties of core dumping signals */ -static void sig_coredump(int sig) -{ - apr_filepath_set(ap_coredump_dir, pconf); - apr_signal(sig, SIG_DFL); - /* linuxthreads issue calling getpid() here: - * This comparison won't match if the crashing thread is - * some module's thread that runs in the parent process. - * The fallout, which is limited to linuxthreads: - * The special log message won't be written when such a - * thread in the parent causes the parent to crash. - */ - if (getpid() == parent_pid) { - ap_log_error(APLOG_MARK, APLOG_NOTICE, - 0, ap_server_conf, - "seg fault or similar nasty error detected " - "in the parent process"); - - /* XXX we can probably add some rudimentary cleanup code here, - * like getting rid of the pid file. If any additional bad stuff - * happens, we are protected from recursive errors taking down the - * system since this function is no longer the signal handler GLA - */ - } - kill(ap_my_pid, sig); - /* At this point we've got sig blocked, because we're still inside - * the signal handler. When we leave the signal handler it will - * be unblocked, and we'll take the signal... and coredump or whatever - * is appropriate for this particular Unix. In addition the parent - * will see the real signal we received -- whereas if we called - * abort() here, the parent would only see SIGABRT. - */ -} - static void just_die(int sig) { clean_child_exit(0); @@ -606,42 +572,16 @@ 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; - if (!one_process) { - sa.sa_handler = sig_coredump; -#if defined(SA_ONESHOT) - sa.sa_flags = SA_ONESHOT; -#elif defined(SA_RESETHAND) - sa.sa_flags = SA_RESETHAND; -#endif - if (sigaction(SIGSEGV, &sa, NULL) < 0) - ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, - "sigaction(SIGSEGV)"); -#ifdef SIGBUS - if (sigaction(SIGBUS, &sa, NULL) < 0) - ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, - "sigaction(SIGBUS)"); -#endif -#ifdef SIGABORT - if (sigaction(SIGABORT, &sa, NULL) < 0) - ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, - "sigaction(SIGABORT)"); -#endif -#ifdef SIGABRT - if (sigaction(SIGABRT, &sa, NULL) < 0) - ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, - "sigaction(SIGABRT)"); -#endif -#ifdef SIGILL - if (sigaction(SIGILL, &sa, NULL) < 0) - ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, - "sigaction(SIGILL)"); -#endif - 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, @@ -683,19 +623,6 @@ static void set_signals(void) "sigaction(" AP_SIG_GRACEFUL_STRING ")"); #else if (!one_process) { - apr_signal(SIGSEGV, sig_coredump); -#ifdef SIGBUS - apr_signal(SIGBUS, sig_coredump); -#endif /* SIGBUS */ -#ifdef SIGABORT - apr_signal(SIGABORT, sig_coredump); -#endif /* SIGABORT */ -#ifdef SIGABRT - apr_signal(SIGABRT, sig_coredump); -#endif /* SIGABRT */ -#ifdef SIGILL - apr_signal(SIGILL, sig_coredump); -#endif /* SIGILL */ #ifdef SIGXCPU apr_signal(SIGXCPU, SIG_DFL); #endif /* SIGXCPU */ @@ -1294,6 +1221,7 @@ static void child_main(int child_num_arg) * child initializes */ ap_my_pid = getpid(); + ap_fatal_signal_child_setup(ap_server_conf); apr_pool_create(&pchild, pconf); /*stuff to do before we switch id's, so we have permissions.*/ diff --git a/server/mpm/prefork/mpm.h b/server/mpm/prefork/mpm.h index cb4385a666d..64c1114866c 100644 --- a/server/mpm/prefork/mpm.h +++ b/server/mpm/prefork/mpm.h @@ -36,6 +36,7 @@ #define AP_MPM_WANT_SET_ACCEPT_LOCK_MECH #define AP_MPM_WANT_SIGNAL_SERVER #define AP_MPM_WANT_SET_MAX_MEM_FREE +#define AP_MPM_WANT_FATAL_SIGNAL_HANDLER #define AP_MPM_DISABLE_NAGLE_ACCEPTED_SOCK #define AP_MPM_USES_POD 1 diff --git a/server/mpm/prefork/prefork.c b/server/mpm/prefork/prefork.c index 7e42acede88..cd14da135b2 100644 --- a/server/mpm/prefork/prefork.c +++ b/server/mpm/prefork/prefork.c @@ -315,34 +315,6 @@ int reap_children(int *exitcode, apr_exit_why_e *status) } #endif -/* handle all varieties of core dumping signals */ -static void sig_coredump(int sig) -{ - chdir(ap_coredump_dir); - apr_signal(sig, SIG_DFL); - /* linuxthreads issue calling getpid() here: - * This comparison won't match if the crashing thread is - * some module's thread that runs in the parent process. - * The fallout, which is limited to linuxthreads: - * The special log message won't be written when such a - * thread in the parent causes the parent to crash. - */ - if (getpid() == parent_pid) { - ap_log_error(APLOG_MARK, APLOG_NOTICE, - 0, ap_server_conf, - "seg fault or similar nasty error detected " - "in the parent process"); - } - kill(getpid(), sig); - /* At this point we've got sig blocked, because we're still inside - * the signal handler. When we leave the signal handler it will - * be unblocked, and we'll take the signal... and coredump or whatever - * is appropriate for this particular Unix. In addition the parent - * will see the real signal we received -- whereas if we called - * abort() here, the parent would only see SIGABRT. - */ -} - /***************************************************************** * Connection structures and accounting... */ @@ -386,37 +358,16 @@ 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; - if (!one_process) { - sa.sa_handler = sig_coredump; -#if defined(SA_ONESHOT) - sa.sa_flags = SA_ONESHOT; -#elif defined(SA_RESETHAND) - sa.sa_flags = SA_RESETHAND; -#endif - if (sigaction(SIGSEGV, &sa, NULL) < 0) - ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGSEGV)"); -#ifdef SIGBUS - if (sigaction(SIGBUS, &sa, NULL) < 0) - ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGBUS)"); -#endif -#ifdef SIGABORT - if (sigaction(SIGABORT, &sa, NULL) < 0) - ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGABORT)"); -#endif -#ifdef SIGABRT - if (sigaction(SIGABRT, &sa, NULL) < 0) - ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGABRT)"); -#endif -#ifdef SIGILL - if (sigaction(SIGILL, &sa, NULL) < 0) - ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGILL)"); -#endif - 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, "sigaction(SIGTERM)"); @@ -451,19 +402,6 @@ static void set_signals(void) ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(" AP_SIG_GRACEFUL_STRING ")"); #else if (!one_process) { - apr_signal(SIGSEGV, sig_coredump); -#ifdef SIGBUS - apr_signal(SIGBUS, sig_coredump); -#endif /* SIGBUS */ -#ifdef SIGABORT - apr_signal(SIGABORT, sig_coredump); -#endif /* SIGABORT */ -#ifdef SIGABRT - apr_signal(SIGABRT, sig_coredump); -#endif /* SIGABRT */ -#ifdef SIGILL - apr_signal(SIGILL, sig_coredump); -#endif /* SIGILL */ #ifdef SIGXCPU apr_signal(SIGXCPU, SIG_DFL); #endif /* SIGXCPU */ @@ -528,6 +466,8 @@ static void child_main(int child_num_arg) csd = NULL; requests_this_child = 0; + ap_fatal_signal_child_setup(ap_server_conf); + /* Get a sub context for global allocations in this child, so that * we can have cleanups occur when the child exits. */ diff --git a/server/mpm/worker/mpm.h b/server/mpm/worker/mpm.h index 4a926ab251c..b152569dd2c 100644 --- a/server/mpm/worker/mpm.h +++ b/server/mpm/worker/mpm.h @@ -34,6 +34,7 @@ #define AP_MPM_WANT_SET_ACCEPT_LOCK_MECH #define AP_MPM_WANT_SIGNAL_SERVER #define AP_MPM_WANT_SET_MAX_MEM_FREE +#define AP_MPM_WANT_FATAL_SIGNAL_HANDLER #define AP_MPM_DISABLE_NAGLE_ACCEPTED_SOCK #define MPM_CHILD_PID(i) (ap_scoreboard_image->parent[i].pid) diff --git a/server/mpm/worker/worker.c b/server/mpm/worker/worker.c index 9c874973da2..d9e463c4fa3 100644 --- a/server/mpm/worker/worker.c +++ b/server/mpm/worker/worker.c @@ -337,40 +337,6 @@ static void clean_child_exit(int code) exit(code); } -/* handle all varieties of core dumping signals */ -static void sig_coredump(int sig) -{ - apr_filepath_set(ap_coredump_dir, pconf); - apr_signal(sig, SIG_DFL); - /* linuxthreads issue calling getpid() here: - * This comparison won't match if the crashing thread is - * some module's thread that runs in the parent process. - * The fallout, which is limited to linuxthreads: - * The special log message won't be written when such a - * thread in the parent causes the parent to crash. - */ - if (getpid() == parent_pid) { - ap_log_error(APLOG_MARK, APLOG_NOTICE, - 0, ap_server_conf, - "seg fault or similar nasty error detected " - "in the parent process"); - - /* XXX we can probably add some rudimentary cleanup code here, - * like getting rid of the pid file. If any additional bad stuff - * happens, we are protected from recursive errors taking down the - * system since this function is no longer the signal handler GLA - */ - } - kill(ap_my_pid, sig); - /* At this point we've got sig blocked, because we're still inside - * the signal handler. When we leave the signal handler it will - * be unblocked, and we'll take the signal... and coredump or whatever - * is appropriate for this particular Unix. In addition the parent - * will see the real signal we received -- whereas if we called - * abort() here, the parent would only see SIGABRT. - */ -} - static void just_die(int sig) { clean_child_exit(0); @@ -445,42 +411,16 @@ 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; - if (!one_process) { - sa.sa_handler = sig_coredump; -#if defined(SA_ONESHOT) - sa.sa_flags = SA_ONESHOT; -#elif defined(SA_RESETHAND) - sa.sa_flags = SA_RESETHAND; -#endif - if (sigaction(SIGSEGV, &sa, NULL) < 0) - ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, - "sigaction(SIGSEGV)"); -#ifdef SIGBUS - if (sigaction(SIGBUS, &sa, NULL) < 0) - ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, - "sigaction(SIGBUS)"); -#endif -#ifdef SIGABORT - if (sigaction(SIGABORT, &sa, NULL) < 0) - ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, - "sigaction(SIGABORT)"); -#endif -#ifdef SIGABRT - if (sigaction(SIGABRT, &sa, NULL) < 0) - ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, - "sigaction(SIGABRT)"); -#endif -#ifdef SIGILL - if (sigaction(SIGILL, &sa, NULL) < 0) - ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, - "sigaction(SIGILL)"); -#endif - 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, @@ -522,19 +462,6 @@ static void set_signals(void) "sigaction(" AP_SIG_GRACEFUL_STRING ")"); #else if (!one_process) { - apr_signal(SIGSEGV, sig_coredump); -#ifdef SIGBUS - apr_signal(SIGBUS, sig_coredump); -#endif /* SIGBUS */ -#ifdef SIGABORT - apr_signal(SIGABORT, sig_coredump); -#endif /* SIGABORT */ -#ifdef SIGABRT - apr_signal(SIGABRT, sig_coredump); -#endif /* SIGABRT */ -#ifdef SIGILL - apr_signal(SIGILL, sig_coredump); -#endif /* SIGILL */ #ifdef SIGXCPU apr_signal(SIGXCPU, SIG_DFL); #endif /* SIGXCPU */ @@ -1165,6 +1092,7 @@ static void child_main(int child_num_arg) * child initializes */ ap_my_pid = getpid(); + ap_fatal_signal_child_setup(ap_server_conf); apr_pool_create(&pchild, pconf); /*stuff to do before we switch id's, so we have permissions.*/ diff --git a/server/mpm_common.c b/server/mpm_common.c index 15da034db31..e17b552f1fb 100644 --- a/server/mpm_common.c +++ b/server/mpm_common.c @@ -54,6 +54,9 @@ #ifdef HAVE_GRP_H #include #endif +#if APR_HAVE_UNISTD_H +#include +#endif #ifdef AP_MPM_WANT_RECLAIM_CHILD_PROCESSES void ap_reclaim_child_processes(int terminate) @@ -867,3 +870,161 @@ const char *ap_mpm_set_max_mem_free(cmd_parms *cmd, void *dummy, } #endif /* AP_MPM_WANT_SET_MAX_MEM_FREE */ + +#ifdef AP_MPM_WANT_FATAL_SIGNAL_HANDLER + +static pid_t parent_pid, my_pid; +apr_pool_t *pconf; + +#if AP_ENABLE_EXCEPTION_HOOK + +static int exception_hook_enabled; + +const char *ap_mpm_set_exception_hook(cmd_parms *cmd, void *dummy, + const char *arg) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + if (cmd->server->is_virtual) { + return "EnableExceptionHook directive not allowed in "; + } + + if (strcasecmp(arg, "on") == 0) { + exception_hook_enabled = 1; + } + else if (strcasecmp(arg, "off") == 0) { + exception_hook_enabled = 0; + } + else { + return "parameter must be 'on' or 'off'"; + } + + return NULL; +} + +APR_HOOK_STRUCT( + APR_HOOK_LINK(fatal_exception) +) + +AP_IMPLEMENT_HOOK_RUN_ALL(int, fatal_exception, + (ap_exception_info_t *ei), (ei), OK, DECLINED) + +static void run_fatal_exception_hook(int sig) +{ + ap_exception_info_t ei = {0}; + + if (exception_hook_enabled && + geteuid() != 0 && + my_pid != parent_pid) { + ei.sig = sig; + ei.pid = my_pid; + ap_run_fatal_exception(&ei); + } +} +#endif /* AP_ENABLE_EXCEPTION_HOOK */ + +/* handle all varieties of core dumping signals */ +static void sig_coredump(int sig) +{ + apr_filepath_set(ap_coredump_dir, pconf); + apr_signal(sig, SIG_DFL); +#if AP_ENABLE_EXCEPTION_HOOK + run_fatal_exception_hook(sig); +#endif + /* linuxthreads issue calling getpid() here: + * This comparison won't match if the crashing thread is + * some module's thread that runs in the parent process. + * The fallout, which is limited to linuxthreads: + * The special log message won't be written when such a + * thread in the parent causes the parent to crash. + */ + if (getpid() == parent_pid) { + ap_log_error(APLOG_MARK, APLOG_NOTICE, + 0, ap_server_conf, + "seg fault or similar nasty error detected " + "in the parent process"); + /* XXX we can probably add some rudimentary cleanup code here, + * like getting rid of the pid file. If any additional bad stuff + * happens, we are protected from recursive errors taking down the + * system since this function is no longer the signal handler GLA + */ + } + kill(getpid(), sig); + /* At this point we've got sig blocked, because we're still inside + * the signal handler. When we leave the signal handler it will + * be unblocked, and we'll take the signal... and coredump or whatever + * is appropriate for this particular Unix. In addition the parent + * will see the real signal we received -- whereas if we called + * abort() here, the parent would only see SIGABRT. + */ +} + +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) +{ +#ifndef NO_USE_SIGACTION + struct sigaction 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; + if (sigaction(SIGSEGV, &sa, NULL) < 0) + ap_log_error(APLOG_MARK, APLOG_WARNING, errno, s, "sigaction(SIGSEGV)"); +#ifdef SIGBUS + if (sigaction(SIGBUS, &sa, NULL) < 0) + ap_log_error(APLOG_MARK, APLOG_WARNING, errno, s, "sigaction(SIGBUS)"); +#endif +#ifdef SIGABORT + if (sigaction(SIGABORT, &sa, NULL) < 0) + ap_log_error(APLOG_MARK, APLOG_WARNING, errno, s, "sigaction(SIGABORT)"); +#endif +#ifdef SIGABRT + if (sigaction(SIGABRT, &sa, NULL) < 0) + ap_log_error(APLOG_MARK, APLOG_WARNING, errno, s, "sigaction(SIGABRT)"); +#endif +#ifdef SIGILL + if (sigaction(SIGILL, &sa, NULL) < 0) + ap_log_error(APLOG_MARK, APLOG_WARNING, errno, s, "sigaction(SIGILL)"); +#endif + +#else /* NO_USE_SIGACTION */ + + apr_signal(SIGSEGV, sig_coredump); +#ifdef SIGBUS + apr_signal(SIGBUS, sig_coredump); +#endif /* SIGBUS */ +#ifdef SIGABORT + apr_signal(SIGABORT, sig_coredump); +#endif /* SIGABORT */ +#ifdef SIGABRT + apr_signal(SIGABRT, sig_coredump); +#endif /* SIGABRT */ +#ifdef SIGILL + apr_signal(SIGILL, sig_coredump); +#endif /* SIGILL */ + +#endif /* NO_USE_SIGACTION */ + + pconf = in_pconf; + parent_pid = my_pid = getpid(); + + return APR_SUCCESS; +} + +#endif /* AP_MPM_WANT_FATAL_SIGNAL_HANDLER */