httpd-cvs mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From jor...@apache.org
Subject cvs commit: httpd-2.0 CHANGES
Date Sun, 18 Apr 2004 16:02:58 GMT
jorton      2004/04/18 09:02:58

  Modified:    modules/generators mod_cgi.c
               .        CHANGES
  Log:
  Implement the "CGI bucket" in mod_cgi to handle stderr output during
  script execution, preventing deadlock if stderr output fills the pipe
  buffer:
  
  * modules/generators/mod_cgi.c (log_script_err): Return a read error.
  (cgi_bucket_create, cgi_bucket_dup, cgi_read_stdout, cgi_bucket_read):
  New functions.
  (cgi_handler): Use new CGI bucket rather than a pipe bucket if
  APR_FILES_AS_SOCKETS; use zero read timeout from stdout/stderr during
  script execution.
  
  PR: 22030
  Submitted by: Joe Orton, Jeff Trawick
  
  Revision  Changes    Path
  1.161     +182 -3    httpd-2.0/modules/generators/mod_cgi.c
  
  Index: mod_cgi.c
  ===================================================================
  RCS file: /home/cvs/httpd-2.0/modules/generators/mod_cgi.c,v
  retrieving revision 1.160
  retrieving revision 1.161
  diff -w -d -u -r1.160 -r1.161
  --- mod_cgi.c	9 Feb 2004 20:29:19 -0000	1.160
  +++ mod_cgi.c	18 Apr 2004 16:02:57 -0000	1.161
  @@ -32,6 +32,7 @@
   #include "apr_optional.h"
   #include "apr_buckets.h"
   #include "apr_lib.h"
  +#include "apr_poll.h"
   
   #define APR_WANT_STRFUNC
   #define APR_WANT_MEMFUNC
  @@ -192,13 +193,14 @@
   
   /* 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 apr_status_t log_script_err(request_rec *r, apr_file_t *script_err)
   {
       char argsbuffer[HUGE_STRING_LEN];
       char *newline;
  +    apr_status_t rv;
   
  -    while (apr_file_gets(argsbuffer, HUGE_STRING_LEN,
  -                         script_err) == APR_SUCCESS) {
  +    while ((rv = apr_file_gets(argsbuffer, HUGE_STRING_LEN,
  +                               script_err)) == APR_SUCCESS) {
           newline = strchr(argsbuffer, '\n');
           if (newline) {
               *newline = '\0';
  @@ -206,6 +208,8 @@
           ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, 
                         "%s", argsbuffer);            
       }
  +
  +    return rv;
   }
   
   static int log_script(request_rec *r, cgi_server_conf * conf, int ret,
  @@ -539,6 +543,172 @@
       }
   }
   
  +#if APR_FILES_AS_SOCKETS
  +
  +/* A CGI bucket type is needed to catch any output to stderr from the
  + * script; see PR 22030. */
  +static const apr_bucket_type_t bucket_type_cgi;
  +
  +struct cgi_bucket_data {
  +    apr_pollset_t *pollset;
  +    request_rec *r;
  +};
  +
  +/* Create a CGI bucket using pipes from script stdout 'out'
  + * and stderr 'err', for request 'r'. */
  +static apr_bucket *cgi_bucket_create(request_rec *r,
  +                                     apr_file_t *out, apr_file_t *err,
  +                                     apr_bucket_alloc_t *list)
  +{
  +    apr_bucket *b = apr_bucket_alloc(sizeof(*b), list);
  +    apr_status_t rv;
  +    apr_pollfd_t fd;
  +    struct cgi_bucket_data *data = apr_palloc(r->pool, sizeof *data);
  +    
  +    APR_BUCKET_INIT(b);
  +    b->free = apr_bucket_free;
  +    b->list = list;
  +    b->type = &bucket_type_cgi;
  +    b->length = (apr_size_t)(-1);
  +    b->start = -1;
  +
  +    /* Create the pollset */
  +    rv = apr_pollset_create(&data->pollset, 2, r->pool, 0);
  +    AP_DEBUG_ASSERT(rv == APR_SUCCESS);
  +
  +    fd.desc_type = APR_POLL_FILE;
  +    fd.reqevents = APR_POLLIN;
  +    fd.p = r->pool;
  +    fd.desc.f = out; /* script's stdout */
  +    fd.client_data = (void *)1;
  +    rv = apr_pollset_add(data->pollset, &fd);
  +    AP_DEBUG_ASSERT(rv == APR_SUCCESS);
  +    
  +    fd.desc.f = err; /* script's stderr */
  +    fd.client_data = (void *)2;
  +    rv = apr_pollset_add(data->pollset, &fd);
  +    AP_DEBUG_ASSERT(rv == APR_SUCCESS);
  +    
  +    data->r = r;
  +    b->data = data;
  +    return b;
  +}
  +
  +/* Create a duplicate CGI bucket using given bucket data */
  +static apr_bucket *cgi_bucket_dup(struct cgi_bucket_data *data,
  +                                  apr_bucket_alloc_t *list)
  +{
  +    apr_bucket *b = apr_bucket_alloc(sizeof(*b), list);
  +    APR_BUCKET_INIT(b);
  +    b->free = apr_bucket_free;
  +    b->list = list;
  +    b->type = &bucket_type_cgi;
  +    b->length = (apr_size_t)(-1);
  +    b->start = -1;
  +    b->data = data;
  +    return b;
  +}
  +
  +/* Handle stdout from CGI child.  Duplicate of logic from the _read
  + * method of the real APR pipe bucket implementation. */
  +static apr_status_t cgi_read_stdout(apr_bucket *a, apr_file_t *out,
  +                                    const char **str, apr_size_t *len)
  +{
  +    char *buf;
  +    apr_status_t rv;
  +
  +    *str = NULL;
  +    *len = APR_BUCKET_BUFF_SIZE;
  +    buf = apr_bucket_alloc(*len, a->list); /* XXX: check for failure? */
  +
  +    rv = apr_file_read(out, buf, len);
  +
  +    if (rv != APR_SUCCESS && rv != APR_EOF) {
  +        apr_bucket_free(buf);
  +        return rv;
  +    }
  +
  +    if (*len > 0) {
  +        struct cgi_bucket_data *data = a->data;
  +        apr_bucket_heap *h;
  +
  +        /* Change the current bucket to refer to what we read */
  +        a = apr_bucket_heap_make(a, buf, *len, apr_bucket_free);
  +        h = a->data;
  +        h->alloc_len = APR_BUCKET_BUFF_SIZE; /* note the real buffer size */
  +        *str = buf;
  +        APR_BUCKET_INSERT_AFTER(a, cgi_bucket_dup(data, a->list));
  +    }
  +    else {
  +        apr_bucket_free(buf);
  +        a = apr_bucket_immortal_make(a, "", 0);
  +        *str = a->data;
  +    }
  +    return rv;
  +}
  +
  +/* Read method of CGI bucket: polls on stderr and stdout of the child,
  + * sending any stderr output immediately away to the error log. */
  +static apr_status_t cgi_bucket_read(apr_bucket *b, const char **str,
  +                                    apr_size_t *len, apr_read_type_e block)
  +{
  +    struct cgi_bucket_data *data = b->data;
  +    apr_interval_time_t timeout;
  +    apr_status_t rv;
  +    int gotdata = 0;
  +
  +    timeout = block == APR_NONBLOCK_READ ? 0 : data->r->server->timeout;
  +
  +    do {
  +        const apr_pollfd_t *results;
  +        apr_int32_t num;
  +
  +        rv = apr_pollset_poll(data->pollset, timeout, &num, &results);
  +        if (APR_STATUS_IS_TIMEUP(rv)) {
  +            return timeout == 0 ? APR_EAGAIN : rv;
  +        }
  +        else if (APR_STATUS_IS_EINTR(rv)) {
  +            continue;
  +        }
  +        else if (rv != APR_SUCCESS) {
  +            ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, data->r,
  +                          "poll failed waiting for CGI child");
  +            return rv;
  +        }
  +        
  +        for (; num; num--, results++) {
  +            if (results[0].client_data == (void *)1) {
  +                /* stdout */
  +                rv = cgi_read_stdout(b, results[0].desc.f, str, len);
  +                if (APR_STATUS_IS_EOF(rv)) {
  +                    rv = APR_SUCCESS;
  +                }
  +                gotdata = 1;
  +            } else {
  +                /* stderr */
  +                apr_status_t rv2 = log_script_err(data->r, results[0].desc.f);
  +                if (APR_STATUS_IS_EOF(rv2)) {
  +                    apr_pollset_remove(data->pollset, &results[0]);
  +                }
  +            }
  +        }
  +
  +    } while (!gotdata);
  +
  +    return rv;
  +}
  +
  +static const apr_bucket_type_t bucket_type_cgi = {
  +    "CGI", 5, APR_BUCKET_DATA,
  +    apr_bucket_destroy_noop,
  +    cgi_bucket_read,
  +    apr_bucket_setaside_notimpl,
  +    apr_bucket_split_notimpl,
  +    apr_bucket_copy_notimpl
  +};
  +
  +#endif
  +
   static int cgi_handler(request_rec *r)
   {
       int nph;
  @@ -720,7 +890,14 @@
           char sbuf[MAX_STRING_LEN];
           int ret;
   
  +#if APR_FILES_AS_SOCKETS
  +        apr_file_pipe_timeout_set(script_in, 0);
  +        apr_file_pipe_timeout_set(script_err, 0);
  +
  +        b = cgi_bucket_create(r, script_in, script_err, c->bucket_alloc);
  +#else
           b = apr_bucket_pipe_create(script_in, c->bucket_alloc);
  +#endif
           APR_BRIGADE_INSERT_TAIL(bb, b);
           b = apr_bucket_eos_create(c->bucket_alloc);
           APR_BRIGADE_INSERT_TAIL(bb, b);
  @@ -734,6 +911,7 @@
           if (location && location[0] == '/' && r->status == 200) {
               discard_script_output(bb);
               apr_brigade_destroy(bb);
  +            apr_file_pipe_timeout_set(script_err, r->server->timeout);
               log_script_err(r, script_err);
               /* This redirect needs to be a GET no matter what the original
                * method was.
  @@ -768,6 +946,7 @@
            * reason
            */
           if (rv == APR_SUCCESS && !r->connection->aborted) {
  +            apr_file_pipe_timeout_set(script_err, r->server->timeout);
               log_script_err(r, script_err);
           }
   
  
  
  
  1.1459    +4 -0      httpd-2.0/CHANGES
  
  Index: CHANGES
  ===================================================================
  RCS file: /home/cvs/httpd-2.0/CHANGES,v
  retrieving revision 1.1458
  retrieving revision 1.1459
  diff -w -d -u -r1.1458 -r1.1459
  --- CHANGES	16 Apr 2004 05:12:22 -0000	1.1458
  +++ CHANGES	18 Apr 2004 16:02:57 -0000	1.1459
  @@ -2,6 +2,10 @@
   
     [Remove entries to the current 2.0 section below, when backported]
   
  +  *) mod_cgi: Handle output on stderr during script execution on Unix
  +     platforms; preventing deadlock when stderr output fills pipe buffer.
  +     PR 22030.  [Joe Orton, Jeff Trawick]
  +
     *) mod_deflate: New option for DEFLATE output file (force-gzip),
        new output filter 'INFLATE' for uncompressing responses.
        [Nick Kew <Nick at WebThing dot com>, Ian Holsman]
  
  
  

Mime
View raw message