With this patch, modules can hook into the CGI fault path and:
  - escalate cgi errors  (ie email, page, self-heal, etc.)
  - write reports to places other than ScriptLog  (ie back to the user)
  - tie into monitoring tools (ie a mod_unicenter.c)


The following patch to mod_cgi.c (off apache2.0.43):
 - all of stderr from a cgi_app is stored into it's own bucket brigade.
 - a hook called ap_hook_cgi_fault() allows modules to access the cgi parameters when 
ap_scan_script_header_err_brigade() returns error (including the stderr brigade).
 - log_script() was converted to use this hook.  log_script() is set as the default 
handler for cgi_fault.
 - log_script_err() was changed to use bucket brigades.


Parameters for the cgi_fault hook are the same as log_script() with exception that 
stderr is a bucket brigade instead of a file_t handle.

I've lightly tested on linux with mod_so.  A "modules/test" application is available.
If approved, I'll convert mod_cgid.


--- httpd-2.0.43/modules/generators/mod_cgi.h  Wed Jun  5 20:17:50 2002
+++ mod_cgi.h   Fri Jan 10 16:57:03 2003
@@ -59,6 +59,7 @@
 #ifndef _MOD_CGI_H
 #define _MOD_CGI_H 1
 
+#include "ap_config.h"
 #include "mod_include.h"
 
 typedef enum {RUN_AS_SSI, RUN_AS_CGI} prog_types;
@@ -76,6 +77,27 @@
     ap_filter_t         *next;
 } cgi_exec_info_t;
 
+typedef struct {
+    const char *logname;
+    long        logbytes;
+    apr_size_t  bufbytes;
+} cgi_server_conf;
+
+/**
+ * A Hook to tie into the path when cgi-apps fail.
+ * @param r Pointer to the request
+ * @param conf Pointer to the cgi config structure
+ * @param ret The reason why the cgi app failed
+ * @param dbuf Pointer to the any data posted to the app
+ * @param sbuf Pointer to the any output received from the cgi app
+ * @param bb The bucket with the stdout from the cgi app
+ * @param ebb The bucket with the stderr from the cgi app
+ * Note the bucket brigades are created and destroyed by mod_cgi.c
+ */ 
+AP_DECLARE_HOOK(void, cgi_fault,(request_rec *r, cgi_server_conf *conf, int ret,
+                        char *dbuf, const char *sbuf, apr_bucket_brigade *bb,
+                        apr_bucket_brigade *ebb))
+
 /**
  * Registerable optional function to override CGI behavior;
  * Reprocess the command and arguments to execute the given CGI script.
@@ -97,4 +119,7 @@
                          request_rec *r, apr_pool_t *p, 
                          cgi_exec_info_t *e_info));
 
+const void *ap_hack_ap_hook_cgi_fault = (const void *)ap_hook_cgi_fault;
+const void *ap_hack_ap_run_cgi_fault  = (const void *)ap_run_cgi_fault;
+
 #endif /* _MOD_CGI_H */

--- httpd-2.0.43/modules/generators/mod_cgi.c  Tue Jul 30 14:18:03 2002
+++ mod_cgi.c   Fri Jan 10 16:57:32 2003
@@ -102,6 +102,11 @@
 static APR_OPTIONAL_FN_TYPE(ap_ssi_parse_string) *cgi_pfn_ps;
 static APR_OPTIONAL_FN_TYPE(ap_cgi_build_command) *cgi_build_command;
 
+/* Allow other modules to hook into the ScriptLog routine */
+APR_HOOK_STRUCT(
+    APR_HOOK_LINK(cgi_fault)
+)
+
 /* Read and discard the data in the brigade produced by a CGI script */
 static void discard_script_output(apr_bucket_brigade *bb);
 
@@ -122,12 +127,6 @@
 #define DEFAULT_LOGBYTES 10385760
 #define DEFAULT_BUFBYTES 1024
 
-typedef struct {
-    const char *logname;
-    long        logbytes;
-    apr_size_t  bufbytes;
-} cgi_server_conf;
-
 static void *create_cgi_config(apr_pool_t *p, server_rec *s)
 {
     cgi_server_conf *c =
@@ -234,25 +233,34 @@
 
 /* Soak up stderr from a script and redirect it to the error log. 
  */
-static void log_script_err(request_rec *r, apr_file_t *script_err)
+static void log_script_err(request_rec *r, apr_bucket_brigade *ebb)
 {
-    char argsbuffer[HUGE_STRING_LEN];
+    const char *buf;
+    apr_size_t len;
+    apr_bucket *e;
+    apr_status_t rv;
     char *newline;
 
-    while (apr_file_gets(argsbuffer, HUGE_STRING_LEN,
-                         script_err) == APR_SUCCESS) {
-        newline = strchr(argsbuffer, '\n');
+    APR_BRIGADE_FOREACH(e, ebb) {
+        if (APR_BUCKET_IS_EOS(e)) {
+            break;
+        }
+        rv = apr_bucket_read(e, &buf, &len, APR_BLOCK_READ);
+        if (rv != APR_SUCCESS || (len == 0)) {
+            break;
+        }
+        newline = strchr(buf, '\n');
         if (newline) {
             *newline = '\0';
         }
         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, 
-                      "%s", argsbuffer);            
+                      "%s", buf);            
     }
 }
 
-static int log_script(request_rec *r, cgi_server_conf * conf, int ret,
+static void log_script(request_rec *r, cgi_server_conf * conf, int ret,
                       char *dbuf, const char *sbuf, apr_bucket_brigade *bb, 
-                      apr_file_t *script_err)
+                      apr_bucket_brigade *ebb)
 {
     const apr_array_header_t *hdrs_arr = apr_table_elts(r->headers_in);
     const apr_table_entry_t *hdrs = (const apr_table_entry_t *) hdrs_arr->elts;
@@ -276,9 +284,8 @@
                        APR_APPEND|APR_WRITE|APR_CREATE, APR_OS_DEFAULT,
                        r->pool) != APR_SUCCESS)) {
         /* Soak up script output */
-        discard_script_output(bb);
-        log_script_err(r, script_err);
-        return ret;
+        log_script_err(r, ebb);
+        return;
     }
 
     /* "%% [Wed Jun 19 10:53:21 1996] GET /cgi-bin/printenv HTTP/1.0" */
@@ -329,21 +336,24 @@
         apr_file_puts("\n", f);
     }
 
-    if (apr_file_gets(argsbuffer, HUGE_STRING_LEN, script_err) == APR_SUCCESS) {
-        apr_file_puts("%stderr\n", f);
-        apr_file_puts(argsbuffer, f);
-        while (apr_file_gets(argsbuffer, HUGE_STRING_LEN,
-                             script_err) == APR_SUCCESS) {
-            apr_file_puts(argsbuffer, f);
+    first = 1;
+    APR_BRIGADE_FOREACH(e, ebb) {
+        if (APR_BUCKET_IS_EOS(e)) {
+            break;
         }
+        rv = apr_bucket_read(e, &buf, &len, APR_BLOCK_READ);
+        if (rv != APR_SUCCESS || (len == 0)) {
+            break;
+        }
+        if (first) {
+            apr_file_puts("%stderr\n", f);
+            first = 0;
+        }
+        apr_file_write(f, buf, &len);
         apr_file_puts("\n", f);
     }
-
-    apr_brigade_destroy(bb);
-    apr_file_close(script_err);
-
     apr_file_close(f);
-    return ret;
+    return;
 }
 
 
@@ -578,6 +588,7 @@
     char *dbuf = NULL;
     apr_file_t *script_out = NULL, *script_in = NULL, *script_err = NULL;
     apr_bucket_brigade *bb;
+    apr_bucket_brigade *ebb;
     apr_bucket *b;
     int is_included;
     int seen_eos, child_stopped_reading;
@@ -585,6 +596,7 @@
     cgi_server_conf *conf;
     apr_status_t rv;
     cgi_exec_info_t e_info;
+    conn_rec *c = r->connection;
 
     if(strcmp(r->handler, CGI_MAGIC_TYPE) && strcmp(r->handler, "cgi-script"))
         return DECLINED;
@@ -666,7 +678,7 @@
     /* Transfer any put/post args, CERN style...
      * Note that we already ignore SIGPIPE in the core server.
      */
-    bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
+    bb = apr_brigade_create(r->pool, c->bucket_alloc);
     seen_eos = 0;
     child_stopped_reading = 0;
     if (conf->logname) {
@@ -739,9 +751,17 @@
     apr_file_flush(script_out);
     apr_file_close(script_out);
 
+    /* Handle cgi-errors and cgi-control messages... */
+    if (script_err) {
+        ebb = apr_brigade_create(r->pool, c->bucket_alloc);
+        b = apr_bucket_pipe_create(script_err, c->bucket_alloc);
+        APR_BRIGADE_INSERT_TAIL(ebb, b);
+        b = apr_bucket_eos_create(c->bucket_alloc);
+        APR_BRIGADE_INSERT_TAIL(ebb, b);
+    }
+
     /* Handle script return... */
     if (script_in && !nph) {
-        conn_rec *c = r->connection;
         const char *location;
         char sbuf[MAX_STRING_LEN];
         int ret;
@@ -751,16 +771,39 @@
         b = apr_bucket_eos_create(c->bucket_alloc);
         APR_BRIGADE_INSERT_TAIL(bb, b);
 
+        /* Run all stderr hooks */
+
         if ((ret = ap_scan_script_header_err_brigade(r, bb, sbuf))) {
-            return log_script(r, conf, ret, dbuf, sbuf, bb, script_err);
+
+            /* cgi-error or cgi-control message found.  Run all hooks */
+            ap_run_cgi_fault(r, conf, ret, dbuf, sbuf, bb, ebb);
+            /* return log_script(r, conf, ret, dbuf, sbuf, bb, script_err); */
+
+            /* Soak up script output */
+            discard_script_output(bb);
+            apr_brigade_destroy(bb);
+
+            /* Soak up script cgi-error output */
+            discard_script_output(ebb);
+            apr_brigade_destroy(ebb);
+
+            apr_file_close(script_err);
+            return ret;
         }
 
         location = apr_table_get(r->headers_out, "Location");
 
         if (location && location[0] == '/' && r->status == 200) {
+            log_script_err(r, ebb);
+            /* Soak up script output */
             discard_script_output(bb);
             apr_brigade_destroy(bb);
-            log_script_err(r, script_err);
+
+            /* Soak up script cgi-error output */
+            discard_script_output(ebb);
+            apr_brigade_destroy(ebb);
+
+            apr_file_close(script_err);
             /* This redirect needs to be a GET no matter what the original
              * method was.
              */
@@ -782,19 +825,27 @@
              */
             discard_script_output(bb);
             apr_brigade_destroy(bb);
+            discard_script_output(ebb);
+            apr_brigade_destroy(ebb);
+            apr_file_close(script_err);
             return HTTP_MOVED_TEMPORARILY;
         }
 
         ap_pass_brigade(r->output_filters, bb);
 
-        log_script_err(r, script_err);
+        log_script_err(r, ebb);
+        discard_script_output(ebb);
+        apr_brigade_destroy(ebb);
         apr_file_close(script_err);
+        return OK;
     }
 
     if (script_in && nph) {
-        conn_rec *c = r->connection;
         struct ap_filter_t *cur;
         
+        discard_script_output(ebb);
+        apr_brigade_destroy(ebb);
+
         /* get rid of all filters up through protocol...  since we
          * haven't parsed off the headers, there is no way they can
          * work
@@ -1060,6 +1111,7 @@
 {
     static const char * const aszPre[] = { "mod_include.c", NULL };
     ap_hook_handler(cgi_handler, NULL, NULL, APR_HOOK_MIDDLE);
+    ap_hook_cgi_fault(log_script, NULL, NULL, APR_HOOK_MIDDLE);
     ap_hook_post_config(cgi_post_config, aszPre, NULL, APR_HOOK_REALLY_FIRST);
 }
 
@@ -1073,3 +1125,10 @@
     cgi_cmds,                    /* command apr_table_t */
     register_hooks               /* register hooks */
 };
+
+
+AP_IMPLEMENT_HOOK_VOID(cgi_fault,
+                        (request_rec *r, cgi_server_conf *conf, int ret,
+                        char *dbuf, const char *sbuf, apr_bucket_brigade *bb, 
+                        apr_bucket_brigade *ebb), (r, conf, ret, dbuf, sbuf, bb, ebb))
+

Reply via email to