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))
+