summaryrefslogtreecommitdiff
path: root/APACHE_1_3_42/src/modules/experimental/mod_whatkilledus.c
diff options
context:
space:
mode:
Diffstat (limited to 'APACHE_1_3_42/src/modules/experimental/mod_whatkilledus.c')
-rw-r--r--APACHE_1_3_42/src/modules/experimental/mod_whatkilledus.c335
1 files changed, 335 insertions, 0 deletions
diff --git a/APACHE_1_3_42/src/modules/experimental/mod_whatkilledus.c b/APACHE_1_3_42/src/modules/experimental/mod_whatkilledus.c
new file mode 100644
index 0000000000..bc861885df
--- /dev/null
+++ b/APACHE_1_3_42/src/modules/experimental/mod_whatkilledus.c
@@ -0,0 +1,335 @@
+/* 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.
+ */
+
+/*
+ * Documentation:
+ *
+ * mod_whatkilledus is an experimental module for Apache httpd 1.3 which
+ * tracks the current request and logs a report of the active request
+ * when a child process crashes. You should verify that it works reasonably
+ * on your system before putting it in production.
+ *
+ * mod_whatkilledus is called during request processing to save information
+ * about the current request. It also implements a fatal exception hook
+ * that will be called when a child process crashes.
+ *
+ * Apache httpd requirements for mod_whatkilledus:
+ *
+ * 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_whatkilledus:
+ *
+ * AIX:
+ * apxs -ci -I/path/to/apache/src/main -Wl,-bE:mod_whatkilledus.exp mod_whatkilledus.c
+ *
+ * other:
+ * apxs -ci -I/path/to/apache/src/main mod_whatkilledus.c
+ *
+ * Activating mod_whatkilledus:
+ *
+ * 1. Load it like any other DSO, but the AddModule should come
+ * last so that if another module causes a crash early in
+ * request processing mod_whatkilledus will have already
+ * had a chance to save information about the request.
+ *
+ * LoadModule whatkilledus_module libexec/mod_whatkilledus.so
+ * ...
+ * AddModule mod_whatkilledus.c
+ *
+ * 2. Enable exception hooks for modules like mod_whatkilledus:
+ * EnableExceptionHook On
+ *
+ * 3. Choose where the report on current activity should be written. If
+ * you want it reported to some place other than the error log, use the
+ * WhatKilledUsLog directive to specify a fully-qualified filename for
+ * the log. 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 "test_char.h" /* an odd one since it is not installed */
+
+/* this module is not thread-safe; it is intended for Apache 1.3 on
+ * platforms that use single-threaded child processes for handling
+ * client connections
+ */
+static char *log_fname;
+static char *local_addr;
+static char *remote_addr;
+static char *request_plus_headers;
+static char buffer[2048];
+
+static void exception_hook(ap_exception_info_t *ei)
+{
+ int msg_len;
+ int logfd;
+ char msg_prefix[60];
+ time_t now;
+ char *newline;
+ int using_errorlog = 1;
+
+ time(&now);
+ ap_snprintf(msg_prefix, sizeof msg_prefix,
+ "[%s pid %ld mod_whatkilledus",
+ 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(buffer, sizeof buffer,
+ "%s error %d opening %s\n",
+ msg_prefix, errno, log_fname);
+ write(logfd, buffer, strlen(buffer));
+ }
+ else {
+ using_errorlog = 0;
+ }
+ }
+ else {
+ logfd = 2;
+ }
+
+ msg_len = ap_snprintf(buffer, sizeof buffer,
+ "%s sig %d crash\n",
+ msg_prefix, ei->sig);
+ write(logfd, buffer, msg_len);
+
+ if (local_addr) {
+ msg_len = ap_snprintf(buffer, sizeof buffer,
+ "%s active connection: %s->%s\n",
+ msg_prefix, remote_addr, local_addr);
+ }
+ else {
+ msg_len = ap_snprintf(buffer, sizeof buffer,
+ "%s no active connection at crash\n",
+ msg_prefix);
+ }
+
+ write(logfd, buffer, msg_len);
+
+ if (request_plus_headers) {
+ msg_len = ap_snprintf(buffer, sizeof buffer,
+ "%s active request:\n",
+ msg_prefix);
+ write(logfd, buffer, msg_len);
+ write(logfd, request_plus_headers, strlen(request_plus_headers));
+ }
+ else {
+ msg_len = ap_snprintf(buffer, sizeof buffer,
+ "%s no request active at crash\n",
+ msg_prefix);
+ write(logfd, buffer, msg_len);
+ }
+ msg_len = ap_snprintf(buffer, sizeof buffer,
+ "%s end of report\n",
+ msg_prefix);
+ write(logfd, buffer, msg_len);
+ if (!using_errorlog) {
+ close(logfd);
+ }
+}
+
+static void init(server_rec *s, pool *p)
+{
+ int rc = ap_add_fatal_exception_hook(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_whatkilledus");
+ }
+}
+
+static void clear_conn_info(void *ignored)
+{
+ local_addr = remote_addr = NULL;
+}
+
+static void save_conn_info(request_rec *r)
+{
+ conn_rec *c = r->connection;
+ local_addr = ap_psprintf(c->pool, "%pI", &c->local_addr);
+ remote_addr = ap_psprintf(c->pool, "%pI", &c->remote_addr);
+
+ ap_register_cleanup(c->pool, NULL, clear_conn_info, ap_null_cleanup);
+}
+
+static void clear_req_info(void *ignored)
+{
+ request_plus_headers = NULL;
+}
+
+#define FIELD_SEPARATOR "|"
+#define KEYVAL_SEPARATOR ":"
+
+/* graciously lifted from mod_log_forensic */
+
+static int count_string(const char *p)
+{
+ int n;
+
+ for (n = 0; *p; ++p, ++n) {
+ if (test_char_table[*(unsigned char *)p] & T_ESCAPE_FORENSIC) {
+ n += 2;
+ }
+ }
+ return n;
+}
+
+static int count_headers(void *in_len, const char *key, const char *value)
+{
+ int *len = in_len;
+
+ *len += strlen(FIELD_SEPARATOR);
+ *len += count_string(key);
+ *len += strlen(KEYVAL_SEPARATOR);
+ *len += count_string(value);
+
+ return 1;
+}
+
+static char *copy_and_escape(char *loc, const char *str) {
+ /* mod_log_forensic will SIGABRT here if it messed up the count
+ * and overflowed; mod_whatkilledus will segfault here or will
+ * SIGABRT back in the caller if that happens
+ */
+ for ( ; *str; ++str) {
+ if (test_char_table[*(unsigned char *)str] & T_ESCAPE_FORENSIC) {
+ *loc++ = '%';
+ sprintf(loc, "%02x", *(unsigned char *)str);
+ loc += 2;
+ }
+ else {
+ *loc++ = *str;
+ }
+ }
+ *loc = '\0';
+ return loc;
+}
+
+static int copy_headers(void *in_ch, const char *key, const char *value)
+{
+ char **ch = in_ch;
+
+ strcpy(*ch, FIELD_SEPARATOR);
+ *ch += strlen(FIELD_SEPARATOR);
+
+ *ch = copy_and_escape(*ch, key);
+
+ strcpy(*ch, KEYVAL_SEPARATOR);
+ *ch += strlen(KEYVAL_SEPARATOR);
+
+ *ch = copy_and_escape(*ch, value);
+
+ return 1;
+}
+
+static void save_req_info(request_rec *r)
+{
+ /* to save for the request:
+ * r->the_request +
+ * foreach header:
+ * '|' + header field
+ */
+ int len = strlen(r->the_request);
+ char *ch;
+ ap_table_do(count_headers, &len, r->headers_in, NULL);
+
+ request_plus_headers = ap_palloc(r->pool, len + 2 /* 2 for the '\n' + '\0' at end */);
+ ch = request_plus_headers;
+ strcpy(ch, r->the_request);
+ ch += strlen(ch);
+
+ ap_table_do(copy_headers, &ch, r->headers_in, NULL);
+ *ch = '\n';
+ *(ch + 1) = '\0';
+
+ ap_assert(ch == request_plus_headers + len);
+
+ ap_register_cleanup(r->pool, NULL, clear_req_info, ap_null_cleanup);
+}
+
+static int post_read(request_rec *r)
+{
+ if (r->prev) { /* we were already called for this internal redirect */
+ return DECLINED;
+ }
+
+ /* save whatever info, like client, which vhost, which port
+ * (to know SSL or not), etc.
+ */
+ if (!local_addr) { /* first request on this connection */
+ save_conn_info(r);
+ }
+
+ save_req_info(r);
+
+ return DECLINED;
+}
+
+static const char *cmd_file(cmd_parms *cmd, void *dconf, char *fname)
+{
+ log_fname = ap_pstrdup(cmd->pool, fname);
+ return NULL;
+}
+
+static const command_rec command_table[] = {
+ {
+ "WhatKilledUsLog", cmd_file, NULL, RSRC_CONF, TAKE1, "the fully-qualified filename of the mod_whatkilledus logfile"
+ }
+ ,
+ {
+ NULL
+ }
+};
+
+module MODULE_VAR_EXPORT whatkilledus_module = {
+ STANDARD_MODULE_STUFF,
+ init, /* initializer */
+ NULL, /* create per-dir config */
+ NULL, /* merge per-dir config */
+ NULL, /* server config */
+ NULL, /* merge server config */
+ 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 */
+ post_read /* post read-request */
+};