| /* Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #if !defined(__linux__) && !defined(__FreeBSD__) |
| #error This module is currently only implemented for Linux and FreeBSD. |
| #endif |
| |
| /* |
| * Documentation: |
| * |
| * mod_backtrace is an experimental module for Apache httpd 1.3 which |
| * collects backtraces when a child process crashes. Currently it is |
| * implemented only on Linux and FreeBSD, but other platforms could be |
| * supported in the future. You should verify that it works reasonably |
| * on your system before putting it in production. |
| * |
| * It implements a fatal exception hook that will be called when a child |
| * process crashes. In the exception hook it uses system library routines |
| * to obtain information about the call stack, and it writes the call |
| * stack to a log file or the web server error log. The backtrace is a |
| * critical piece of information when determining the failing software |
| * component that caused the crash. Note that the backtrace written by |
| * mod_backtrace may not have as much information as a debugger can |
| * display from a core dump. |
| * |
| * Apache httpd requirements for mod_backtrace: |
| * |
| * Apache httpd >= 1.3.30 must be built with the AP_ENABLE_EXCEPTION_HOOK |
| * symbol defined and mod_so enabled. AP_ENABLE_EXCEPTION_HOOK is already |
| * defined in ap_config.h for some platforms, including AIX, Linux, |
| * Solaris, and HP-UX. It can be enabled for other platforms by including |
| * -DAP_ENABLE_EXCEPTION_HOOK in CFLAGS when the configure script is |
| * invoked. |
| * |
| * Compiling mod_backtrace: |
| * |
| * Linux: |
| * apxs -ci mod_backtrace.c |
| * |
| * FreeBSD: |
| * install libexecinfo from the Ports system then |
| * apxs -ci -L/usr/local/lib -lexecinfo mod_backtrace.c |
| * |
| * Activating mod_backtrace: |
| * |
| * 1. Load it like any other DSO: |
| * LoadModule backtrace_module libexec/mod_backtrace.so |
| * ... |
| * AddModule mod_backtrace.c |
| * |
| * 2. Enable exception hooks for modules like mod_backtrace: |
| * EnableExceptionHook On |
| * |
| * 3. Choose where backtrace information should be written. |
| * If you want backtraces from crashes to be reported some place other |
| * than the error log, use the BacktraceLog directive to specify a |
| * fully-qualified filename for the log to which backtraces will be |
| * written. Note that the web server user id (e.g., "nobody") must |
| * be able to create or append to this log file, as the log file is |
| * not opened until a crash occurs. |
| */ |
| |
| #include "httpd.h" |
| #include "http_config.h" |
| #include "http_log.h" |
| |
| #include <fcntl.h> |
| #include <unistd.h> |
| #include <execinfo.h> |
| |
| static char *log_fname; |
| |
| static void bt_show_backtrace(int sig) |
| { |
| char msgbuf[128]; |
| size_t size; |
| void *array[20]; |
| extern int main(); |
| int logfd; |
| time_t now; |
| char msg_prefix[60]; |
| char *newline; |
| int using_errorlog = 1; |
| |
| time(&now); |
| ap_snprintf(msg_prefix, sizeof msg_prefix, |
| "[%s pid %ld mod_backtrace", |
| asctime(localtime(&now)), |
| (long)getpid()); |
| newline = strchr(msg_prefix, '\n'); /* dang asctime() */ |
| if (newline) { /* silly we are */ |
| *newline = ']'; |
| } |
| |
| if (log_fname) { |
| logfd = open(log_fname, O_WRONLY|O_APPEND|O_CREAT, 0644); |
| if (logfd == -1) { |
| logfd = 2; /* unix, so fd 2 is the web server error log */ |
| ap_snprintf(msgbuf, sizeof msgbuf, |
| "%s error %d opening %s\n", |
| msg_prefix, errno, log_fname); |
| write(logfd, msgbuf, strlen(msgbuf)); |
| } |
| else { |
| using_errorlog = 0; |
| } |
| } |
| else { |
| logfd = 2; |
| } |
| |
| ap_snprintf(msgbuf, sizeof msgbuf, |
| "%s backtrace for signal %d\n", |
| msg_prefix, sig); |
| write(logfd, msgbuf, strlen(msgbuf)); |
| |
| /* the address of main() can be useful if we're on old |
| * glibc and get only addresses for stack frames... knowing |
| * where main() is then is a useful clue |
| */ |
| ap_snprintf(msgbuf, sizeof msgbuf, |
| "%s main() is at %pp\n", |
| msg_prefix, |
| main);/* don't you DARE put parens after "main" */ |
| write(logfd, msgbuf, strlen(msgbuf)); |
| |
| size = backtrace(array, sizeof array / sizeof array[0]); |
| backtrace_symbols_fd(array, size, logfd); |
| ap_snprintf(msgbuf, sizeof msgbuf, |
| "%s end of report\n", |
| msg_prefix); |
| write(logfd, msgbuf, strlen(msgbuf)); |
| if (!using_errorlog) { |
| close(logfd); |
| } |
| } |
| |
| static void bt_exception_hook(ap_exception_info_t *ei) |
| { |
| bt_show_backtrace(ei->sig); |
| } |
| |
| static void bt_init(server_rec *s, pool *p) |
| { |
| int rc = ap_add_fatal_exception_hook(bt_exception_hook); |
| |
| if (rc) { |
| ap_log_error(APLOG_MARK, APLOG_ALERT|APLOG_NOERRNO, s, |
| "fatal exception hooks are not enabled; please " |
| "enable them with the EnableExceptionHook directive " |
| "or disable mod_backtrace"); |
| } |
| } |
| |
| static const char *bt_cmd_file(cmd_parms *cmd, void *dconf, char *fname) |
| { |
| log_fname = ap_pstrdup(cmd->pool, fname); |
| return NULL; |
| } |
| |
| static const command_rec bt_command_table[] = { |
| { |
| "BacktraceLog", bt_cmd_file, NULL, RSRC_CONF, TAKE1, "the fully-qualified filename of the mod_backtrace logfile" |
| } |
| , |
| { |
| NULL |
| } |
| }; |
| |
| module MODULE_VAR_EXPORT backtrace_module = { |
| STANDARD_MODULE_STUFF, |
| bt_init, /* initializer */ |
| NULL, /* dir config creater */ |
| NULL, /* dir merger --- default is to override */ |
| NULL, /* server config */ |
| NULL, /* merge server config */ |
| bt_command_table, /* command table */ |
| NULL, /* handlers */ |
| NULL, /* filename translation */ |
| NULL, /* check_user_id */ |
| NULL, /* check auth */ |
| NULL, /* check access */ |
| NULL, /* type_checker */ |
| NULL, /* fixups */ |
| NULL, /* logger */ |
| NULL, /* header parser */ |
| NULL, /* child init */ |
| NULL, /* child exit */ |
| NULL /* post read request */ |
| }; |