Home | History | Annotate | Download | only in mod_ecs
      1 /*
      2 mod_ecs - Embedded ClearSilver CGI Apache Module
      3 
      4 mod_ecs is a heavily modified version of mod_ecgi from:
      5 http://www.webthing.com/software/mod_ecgi.html
      6 
      7 This version is designed to run with the ClearSilver CGIKit, specifically
      8 with the cgi_wrap calls from that kit.  Those calls wrap the standard CGI
      9 access methods, namely environment variables and stdin/stdout, allowing
     10 those calls to be replaced easily.  mod_ecs provides replacement calls which
     11 interface directly with the Apache internals.
     12 
     13 Additionally, mod_ecs is designed to dlopen() the shared library CGI once,
     14 and keep it in memory, making the CGI almost identical in performance to a
     15 regular Apache module.  The fact that your CGI will be called multiple times
     16 is the biggest difference you can expect from a standard ClearSilver based CGI.
     17 This means your code must be clean!
     18 
     19 ECS - Embedded ClearSilver
     20 
     21 Platform: UNIX only.  Anyone who wants to is welcome to port it elsewhere.
     22 
     23 =======================================================
     24 To COMPILE Apache with embedded CGI support, use
     25 	-ldl in EXTRA_LIBS
     26 	possibly -rdynamic in EXTRA_LFLAGS
     27  I took this out of the config because its not there on freebsd4
     28  = ConfigStart
     29 	LIBS="$LIBS -ldl"
     30  = ConfigEnd
     31 (or as required by your platform)
     32 
     33 OK, here's for APACI:
     34  * MODULE-DEFINITION-START
     35  * Name: ecs_module
     36  * MODULE-DEFINITION-END
     37 
     38 =======================================================
     39 
     40 =======================================================
     41 BUGS
     42 Lots - here are some obvious ones
     43 	- won't work with NPH
     44 	- No mechanism is provided for running from an SSI
     45 	- Can't take part in content-negotiation
     46 	- No graceful cleanup if a CGI program crashes (though it's OK
     47 	  if the CGI fails but returns).
     48 	- Suspected memory leak inherited from Apache (which ignores it
     49 	  because it happens just before exit there).
     50 
     51 */
     52 
     53 #include <dlfcn.h>
     54 #include "mod_ecs.h"
     55 
     56 #include "httpd.h"
     57 #include "http_config.h"
     58 #include "http_request.h"
     59 #include "http_core.h"
     60 #include "http_protocol.h"
     61 #include "http_main.h"
     62 #include "http_log.h"
     63 #include "util_script.h"
     64 #include "http_conf_globals.h"
     65 
     66 module ecs_module;
     67 
     68 /* Configuration stuff */
     69 
     70 #define log_reason(reason,name,r) ap_log_error(APLOG_MARK,APLOG_ERR,(r)->server,(reason),(name))
     71 #define log_scripterror(r,conf,ret,error) (log_reason((error),(r)->filename,(r)),ret)
     72 
     73 char** ecs_create_argv(pool*,char*,char*,char*,char*,const char*);
     74 
     75 /****************************************************************
     76  *
     77  * Actual CGI handling...
     78  */
     79 const int ERROR = 500;
     80 const int INTERNAL_REDIRECT = 3020;
     81 
     82 #undef ECS_DEBUG
     83 
     84 /******************************************************************
     85  * cgiwrap routines
     86  *   We've replaced all the normal CGI api calls with calls to the
     87  *   appropriate cgiwrap routines instead.  Then, we provide versions of
     88  *   the cgiwrap callback here that interface directly with apache.  We
     89  *   need to mimic a bunch of the stuff that apache does in mod_cgi in
     90  *   order to implement the output portion of the CGI spec.
     91  */
     92 typedef struct header_buf {
     93   char *buf;
     94   int len;
     95   int max;
     96   int loc;
     97   int nonl;
     98 } HEADER_BUF;
     99 
    100 typedef struct wrap_data {
    101   HEADER_BUF hbuf;
    102   int end_of_header;
    103   int returns;
    104   request_rec *r;
    105 } WRAPPER_DATA;
    106 
    107 static int buf_getline (const char *idata, int ilen, char *odata, int olen, int *nonl)
    108 {
    109   char *eol;
    110   int len;
    111 
    112   *nonl = 1;
    113   eol = strchr (idata, '\n');
    114   if (eol == NULL)
    115   {
    116     len = ilen;
    117   }
    118   else
    119   {
    120     *nonl = 0;
    121     len = eol - idata + 1;
    122   }
    123   if (len > olen) len = olen;
    124   memcpy (odata, idata, len);
    125   odata[len] = '\0';
    126   return len;
    127 }
    128 
    129 static int h_getline (char *buf, int len, void *h)
    130 {
    131   HEADER_BUF *hbuf = (HEADER_BUF *)h;
    132   int ret;
    133 
    134   buf[0] = '\0';
    135   if (hbuf->loc > hbuf->len)
    136     return 0;
    137 
    138   ret = buf_getline (hbuf->buf + hbuf->loc, hbuf->len - hbuf->loc, buf, len, &(hbuf->nonl));
    139   hbuf->loc += ret;
    140 #if ECS_DEBUG>1
    141   fprintf (stderr, "h_getline: [%d] %s\n", ret, buf);
    142 #endif
    143   return ret;
    144 }
    145 
    146 static int header_write (HEADER_BUF *hbuf, const char *data, int dlen)
    147 {
    148   char buf[1024];
    149   int done, len;
    150   int nonl = hbuf->nonl;
    151 
    152   done = 0;
    153   while (done < dlen)
    154   {
    155     nonl = hbuf->nonl;
    156     len = buf_getline (data + done, dlen - done, buf, sizeof(buf), &(hbuf->nonl));
    157     if (len == 0)
    158       break;
    159     done += len;
    160     if (hbuf->len + len > hbuf->max)
    161     {
    162       hbuf->max *= 2;
    163       if (hbuf->len + len > hbuf->max)
    164       {
    165 	hbuf->max += len + 1;
    166       }
    167       hbuf->buf = (char *) realloc ((void *)(hbuf->buf), hbuf->max);
    168     }
    169     memcpy (hbuf->buf + hbuf->len, buf, len);
    170     hbuf->len += len;
    171     if (!nonl && (buf[0] == '\n' || buf[0] == '\r'))
    172     {
    173       /* end of headers */
    174       return done;
    175     }
    176   }
    177 
    178   return 0;
    179 }
    180 
    181 /* The normal CGI module passes the returned data through
    182  * ap_scan_script_header().  We can't do that directly, since we don't
    183  * have a constant stream of data, so we buffer the header into our own
    184  * structure, and call ap_scan_script_header_err_core() with our own
    185  * getline() function to walk the header buffer we have.  We could
    186  * probably get some speed improvement by keeping the header buffer
    187  * between runs, instead of growing it every time... for later.  Also,
    188  * we currently don't use the pool allocation routines here, so we have
    189  * to be very careful not to leak.  We could probably at least use the
    190  * ap_register_cleanup() function to make sure we clean up our mess...
    191  */
    192 static int wrap_write (void *data, const char *buf, size_t len)
    193 {
    194   WRAPPER_DATA *wrap = (WRAPPER_DATA *)data;
    195   int wl;
    196   int ret;
    197 
    198 #if ECS_DEBUG>1
    199   fprintf (stderr, "wrap_write (%s, %d)\n", buf, len);
    200 #endif
    201   if (!wrap->end_of_header)
    202   {
    203     wl = header_write (&(wrap->hbuf), buf, len);
    204     if (wl == 0)
    205     {
    206       return len;
    207     }
    208     wrap->end_of_header = 1;
    209     wrap->hbuf.loc = 0;
    210 #if ECS_DEBUG>1
    211     fprintf (stderr, "ap_scan_script_header_err_core\n%s\n", wrap->hbuf.buf);
    212 #endif
    213     wrap->returns = ap_scan_script_header_err_core(wrap->r, NULL, h_getline,
    214 	(void *)&(wrap->hbuf));
    215 #if ECS_DEBUG>1
    216     fprintf (stderr, "ap_scan_script_header_err_core.. done\n");
    217 #endif
    218     if (len >= wl)
    219     {
    220       len = len - wl;
    221       buf = buf + wl;
    222     }
    223 
    224     if (wrap->returns == OK)
    225     {
    226       const char* location = ap_table_get (wrap->r->headers_out, "Location");
    227 
    228       if (location && location[0] == '/' && wrap->r->status == 200)
    229       {
    230 	wrap->returns = INTERNAL_REDIRECT;
    231       }
    232       else if (location && wrap->r->status == 200)
    233       {
    234 	/* XX Note that if a script wants to produce its own Redirect
    235 	 * body, it now has to explicitly *say* "Status: 302"
    236 	 */
    237 	wrap->returns = REDIRECT;
    238       }
    239       else
    240       {
    241 #ifdef ECS_DEBUG
    242 	fprintf (stderr, "ap_send_http_header\n");
    243 #endif
    244 	ap_send_http_header(wrap->r);
    245 #ifdef ECS_DEBUG
    246 	fprintf (stderr, "ap_send_http_header.. done\n");
    247 #endif
    248       }
    249     }
    250   }
    251   /* if header didn't return OK, ignore the rest */
    252   if ((wrap->returns != OK) || wrap->r->header_only)
    253   {
    254     return len;
    255   }
    256 #if ECS_DEBUG>1
    257   fprintf (stderr, "ap_rwrite(%s,%d)\n", buf, len);
    258 #endif
    259   ret = ap_rwrite (buf, len, wrap->r);
    260 #if ECS_DEBUG>1
    261   fprintf (stderr, "ap_rwrite.. done\n");
    262 #endif
    263   return ret;
    264 }
    265 
    266 int wrap_vprintf (void *data, const char *fmt, va_list ap)
    267 {
    268   char buf[4096];
    269   int len;
    270 
    271   len = ap_vsnprintf (buf, sizeof(buf), fmt, ap);
    272   return wrap_write (data, buf, len);
    273 }
    274 
    275 static int wrap_read (void *data, char *buf, size_t len)
    276 {
    277   WRAPPER_DATA *wrap = (WRAPPER_DATA *)data;
    278   int ret;
    279   int x = 0;
    280 
    281 #if ECS_DEBUG>1
    282   fprintf (stderr, "wrap_read (%s, %d)\n", buf, len);
    283 #endif
    284   do
    285   {
    286     ret = ap_get_client_block(wrap->r, buf + x, len - x);
    287     if (ret <= 0) break;
    288     x += ret;
    289   } while (x < len);
    290 #if ECS_DEBUG>1
    291   fprintf (stderr, "done ap_get_client_block\n");
    292 #endif
    293   if (ret < 0) return ret;
    294   return x;
    295 }
    296 
    297 static char *wrap_getenv (void *data, const char *s)
    298 {
    299   WRAPPER_DATA *wrap = (WRAPPER_DATA *)data;
    300   char *v;
    301 
    302   v = (char *) ap_table_get (wrap->r->subprocess_env, s);
    303   if (v) return strdup(v);
    304   return NULL;
    305 }
    306 
    307 static int wrap_putenv (void *data, const char *k, const char *v)
    308 {
    309   WRAPPER_DATA *wrap = (WRAPPER_DATA *)data;
    310 
    311   ap_table_set (wrap->r->subprocess_env, k, v);
    312 
    313   return 0;
    314 }
    315 
    316 static char *wrap_iterenv (void *data, int x, char **k, char **v)
    317 {
    318   WRAPPER_DATA *wrap = (WRAPPER_DATA *)data;
    319   array_header *env = ap_table_elts(wrap->r->subprocess_env);
    320   table_entry *entry = (table_entry*)env->elts;
    321 
    322   if (x >= env->nelts) return 0;
    323 
    324   if (entry[x].key == NULL || entry[x].val == NULL)
    325     return 0;
    326 
    327   *k = strdup(entry[x].key);
    328   *v = strdup(entry[x].val);
    329 
    330   return 0;
    331 }
    332 
    333 /*************************************************************************
    334  * Actual mod_ecs data structures for configuration
    335  */
    336 
    337 typedef void (*InitFunc)();
    338 typedef void (*CleanupFunc)();
    339 typedef int (*CGIMainFunc)(int,char**,char**);
    340 typedef int (*WrapInitFunc)(void *,void *,void*,void*,void*,void*,void*);
    341 
    342 typedef struct {
    343   const char *libpath;
    344   ap_os_dso_handle_t dlib;
    345 } ecs_deplibs;
    346 
    347 typedef struct {
    348   const char *libpath;
    349   ap_os_dso_handle_t dlib;
    350   WrapInitFunc wrap_init;
    351   CGIMainFunc start;
    352   time_t mtime;
    353   int loaded;
    354 } ecs_manager;
    355 
    356 typedef struct {
    357   array_header *deplibs;
    358   array_header *handlers;
    359   int fork_enabled;
    360   int reload_enabled;
    361 } ecs_server_conf;
    362 
    363 const char *ECSInit = "ECSInit";
    364 const char *ECSCleanUp = "ECSCleanup";
    365 const char *WrapInit = "cgiwrap_init_emu";
    366 const char *CGIMain = "main";
    367 
    368 static void dummy (ap_os_dso_handle_t dlhandle)
    369 {
    370 }
    371 
    372 static void slib_cleanup (ap_os_dso_handle_t dlhandle)
    373 {
    374   CleanupFunc cleanupFunc;
    375   if ((cleanupFunc = (CleanupFunc)ap_os_dso_sym(dlhandle, ECSCleanUp))) {
    376     (*cleanupFunc)();
    377   }
    378   ap_os_dso_unload(dlhandle);
    379 #ifdef ECS_DEBUG
    380   fprintf(stderr, "Unloading handle %d", dlhandle);
    381 #endif
    382 }
    383 
    384 void *create_ecs_config (pool *p, server_rec *dummy)
    385 {
    386   ecs_server_conf *new = ap_palloc (p, sizeof(ecs_server_conf));
    387   new->deplibs = ap_make_array(p,1,sizeof(ecs_deplibs));
    388   new->handlers = ap_make_array(p,1,sizeof(ecs_manager));
    389   new->fork_enabled = 0;
    390   new->reload_enabled = 0;
    391   return (void *) new;
    392 }
    393 
    394 char** e_setup_cgi_env (request_rec* r)
    395 {
    396   char** env;
    397 
    398   ap_add_common_vars(r);
    399   ap_add_cgi_vars(r);
    400   env = ap_create_environment(r->pool,r->subprocess_env);
    401 
    402   return env;
    403 }
    404 
    405 const char *set_dep_lib (cmd_parms *parms, void *dummy, char *arg)
    406 {
    407   ecs_server_conf *cls = ap_get_module_config (parms->server->module_config,
    408       &ecs_module);
    409   ecs_deplibs *entry;
    410   ap_os_dso_handle_t dlhandle;
    411   InitFunc init_func;
    412 
    413   if ((dlhandle = ap_os_dso_load(arg)) == NULL) {
    414     return ap_os_dso_error();
    415   }
    416 
    417   if ((init_func = (InitFunc)ap_os_dso_sym(dlhandle, ECSInit))) {
    418     (*init_func)();
    419   }
    420 
    421   ap_register_cleanup (cls->deplibs->pool, dlhandle, slib_cleanup, slib_cleanup);
    422 
    423   entry = (ecs_deplibs*)ap_push_array(cls->deplibs);
    424   entry->libpath = ap_pstrdup(cls->deplibs->pool, arg);
    425   entry->dlib = dlhandle;
    426 
    427   return NULL;
    428 }
    429 
    430 /* Load an ecs shared library */
    431 static const char *load_library (ap_pool *p, ecs_manager *entry, int do_stat, char *prefix)
    432 {
    433   ap_os_dso_handle_t dlhandle;
    434   InitFunc init_func;
    435   CGIMainFunc cgi_main;
    436   WrapInitFunc wrap_init;
    437   char *err;
    438   struct stat s;
    439 
    440   if (do_stat)
    441   {
    442     if (stat(entry->libpath, &s) == -1)
    443     {
    444       err = ap_psprintf (p, "Failed to stat library file %s: %d", entry->libpath, errno);
    445       return err;
    446     }
    447     entry->mtime = s.st_mtime;
    448   }
    449 
    450   if (entry->loaded == 1)
    451   {
    452     fprintf (stderr, "Warning: attempting to reload %s but it's already loaded\n", entry->libpath);
    453   }
    454 
    455   /* This does a RTLD_NOW, if we want lazy, we're going to have to do it
    456    * ourselves */
    457   if ((dlhandle = ap_os_dso_load(entry->libpath)) == NULL) {
    458     return ap_os_dso_error();
    459   }
    460 
    461   if (entry->dlib == dlhandle)
    462   {
    463     fprintf (stderr, "Warning: Reload of %s returned same handle\n", entry->libpath);
    464   }
    465 
    466   if ((init_func = (InitFunc)ap_os_dso_sym(dlhandle, ECSInit))) {
    467     (*init_func)();
    468   }
    469   if (!(wrap_init = (WrapInitFunc)ap_os_dso_sym(dlhandle, WrapInit))) {
    470     err = ap_psprintf (p, "Failed to find wrap init function %s in shared object: %s", WrapInit, dlerror());
    471     ap_os_dso_unload(dlhandle);
    472     return err;
    473   }
    474   if (!(cgi_main = (CGIMainFunc)ap_os_dso_sym(dlhandle, CGIMain))) {
    475     err = ap_psprintf (p, "Failed to find entry function %s in shared object: %s", CGIMain, dlerror());
    476     ap_os_dso_unload(dlhandle);
    477     return err;
    478   }
    479 
    480   /* Um, this may be a problem... */
    481   ap_register_cleanup (p, dlhandle, slib_cleanup, dummy);
    482 
    483   entry->dlib = dlhandle;
    484   entry->wrap_init = wrap_init;
    485   entry->start = cgi_main;
    486   entry->loaded = 1;
    487 
    488   fprintf (stderr, "%sLoaded library %s [%d]\n", prefix, entry->libpath, dlhandle);
    489 
    490   return NULL;
    491 }
    492 
    493 const char *set_pre_lib (cmd_parms *parms, void *dummy, char *arg)
    494 {
    495   ecs_server_conf *cls = ap_get_module_config (parms->server->module_config,
    496       &ecs_module);
    497   ecs_manager *entry;
    498 
    499   entry = (ecs_manager*)ap_push_array(cls->handlers);
    500   entry->libpath = ap_pstrdup(cls->handlers->pool, arg);
    501 
    502   return load_library (cls->handlers->pool, entry, 1, "Pre");
    503 }
    504 
    505 const char *set_fork (cmd_parms *parms, void *dummy, int flag)
    506 {
    507   ecs_server_conf *cls = ap_get_module_config (parms->server->module_config,
    508       &ecs_module);
    509 
    510   cls->fork_enabled = (flag ? 1 : 0);
    511 
    512   return NULL;
    513 }
    514 
    515 const char *set_reload (cmd_parms *parms, void *dummy, int flag)
    516 {
    517   ecs_server_conf *cls = ap_get_module_config (parms->server->module_config,
    518       &ecs_module);
    519 
    520   cls->reload_enabled = (flag ? 1 : 0);
    521 
    522   return NULL;
    523 }
    524 
    525 static ecs_manager *findHandler(array_header *a, char *file)
    526 {
    527   ecs_manager *list = (ecs_manager*)(a->elts);
    528   int i;
    529 
    530   for (i = 0; i < a->nelts; i++)
    531   {
    532     if (!strcmp(list[i].libpath, file))
    533       return &(list[i]);
    534   }
    535   return NULL;
    536 }
    537 
    538 static int run_dl_cgi (ecs_server_conf *sconf, request_rec* r, char* argv0)
    539 {
    540   int ret = 0;
    541   void* handle;
    542   int cgi_status;
    543   int argc;
    544   char** argv;
    545   WRAPPER_DATA *wdata;
    546   ecs_manager *handler;
    547   const char *err;
    548 
    549   char** envp = e_setup_cgi_env(r);
    550 
    551   /* Find/open library */
    552   handler = findHandler (sconf->handlers, r->filename);
    553   if (handler == NULL)
    554   {
    555     ecs_manager my_handler;
    556     my_handler.libpath = ap_pstrdup(sconf->handlers->pool, r->filename);
    557     err = load_library(sconf->handlers->pool, &my_handler, 1, "");
    558     if (err != NULL)
    559     {
    560       log_reason("Error opening library:", err, r);
    561       ret = ERROR;
    562     }
    563     else
    564     {
    565       handler = (ecs_manager*)ap_push_array(sconf->handlers);
    566       handler->dlib = my_handler.dlib;
    567       handler->wrap_init = my_handler.wrap_init;
    568       handler->start = my_handler.start;
    569       handler->mtime = my_handler.mtime;
    570       handler->loaded = my_handler.loaded;
    571       handler->libpath = my_handler.libpath;
    572     }
    573   }
    574   else if (sconf->reload_enabled)
    575   {
    576     struct stat s;
    577     if (stat(handler->libpath, &s) == -1)
    578     {
    579       log_reason("Unable to stat file: ", handler->libpath, r);
    580       ret = ERROR;
    581     }
    582     else if (!handler->loaded || (s.st_mtime > handler->mtime))
    583     {
    584       if (handler->loaded)
    585       {
    586 	int x;
    587 	fprintf (stderr, "Unloading %s\n", handler->libpath);
    588 	slib_cleanup(handler->dlib);
    589 	/* Really unload this thing */
    590 	while ((x < 100) && (dlclose(handler->dlib) != -1)) x++;
    591 	if (x == 100)
    592 	  fprintf (stderr, "dlclose() never returned -1");
    593 	handler->loaded = 0;
    594       }
    595       err = load_library(sconf->handlers->pool, handler, 0, "Re");
    596       if (err != NULL)
    597       {
    598 	log_reason("Error opening library:", err, r);
    599 	ret = ERROR;
    600       }
    601       handler->mtime = s.st_mtime;
    602     }
    603   }
    604 
    605   if (!ret) {
    606     if ((!r->args) || (!r->args[0]) || (ap_ind(r->args,'=') >= 0) )
    607     {
    608       argc = 1;
    609       argv = &argv0;
    610     } else {
    611       argv = ecs_create_argv(r->pool, NULL,NULL,NULL,argv0,r->args);
    612       for (argc = 0 ; argv[argc] ; ++argc);
    613     }
    614   }
    615 
    616   /*  Yow ... at last we can go ...
    617 
    618       Now, what to do if CGI crashes (aaargh)
    619       Methinks an atexit ... cleanup perhaps; have to figgerout
    620       what the atexit needs to invoke ... yuk!
    621 
    622       Or maybe better to catch SIGSEGV and SIGBUS ?
    623       - we don't want coredumps from someone else's bugs, do we?
    624       still doesn't guarantee anything very good :-(
    625 
    626       Ugh .. nothing better???
    627    */
    628   if (!ret)
    629   {
    630     wdata = (WRAPPER_DATA *) ap_pcalloc (r->pool, sizeof (WRAPPER_DATA));
    631     /* We use malloc here because there is no pool alloc command for
    632      * realloc... */
    633     wdata->hbuf.buf = (char *) malloc (sizeof(char) * 1024);
    634     wdata->hbuf.max = 1024;
    635     wdata->r = r;
    636 
    637 #ifdef ECS_DEBUG
    638     fprintf (stderr, "wrap_init()\n");
    639 #endif
    640     handler->wrap_init(wdata, wrap_read, wrap_vprintf, wrap_write, wrap_getenv, wrap_putenv, wrap_iterenv);
    641 
    642 #ifdef ECS_DEBUG
    643     fprintf (stderr, "cgi_main()\n");
    644 #endif
    645     cgi_status = handler->start(argc,argv,envp);
    646     if (cgi_status != 0)
    647     {
    648       /*log_reason("CGI returned error status", cgi_status, r) ;*/
    649       ret = ERROR;
    650     }
    651 
    652     if (wdata->returns != OK)
    653       ret = wdata->returns;
    654 
    655     free (wdata->hbuf.buf);
    656   }
    657 
    658   return ret;
    659 }
    660 
    661 int run_xcgi (ecs_server_conf *conf, request_rec* r, char* argv0)
    662 {
    663   int len_read;
    664   char argsbuffer[HUGE_STRING_LEN];
    665   int ret = 0;
    666 
    667   ret = run_dl_cgi (conf, r, argv0);
    668 
    669   if (ret == INTERNAL_REDIRECT)
    670   {
    671     const char* location = ap_table_get (r->headers_out, "Location");
    672 
    673     /* This redirect needs to be a GET no matter what the original
    674      * method was.
    675      */
    676     r->method = ap_pstrdup(r->pool, "GET");
    677     r->method_number = M_GET;
    678 
    679     /* We already read the message body (if any), so don't allow
    680      * the redirected request to think it has one.  We can ignore
    681      * Transfer-Encoding, since we used REQUEST_CHUNKED_ERROR.
    682      */
    683     ap_table_unset(r->headers_in, "Content-Length");
    684 
    685     ap_internal_redirect_handler (location, r);
    686     return OK;
    687   }
    688 
    689   return ret;
    690 }
    691 
    692 int ecs_handler (request_rec* r)
    693 {
    694   int retval;
    695   char *argv0;
    696   int is_included = !strcmp (r->protocol, "INCLUDED");
    697   void *sconf = r->server->module_config;
    698   ecs_server_conf *conf =
    699     (ecs_server_conf *)ap_get_module_config(sconf, &ecs_module);
    700 
    701   ap_error_log2stderr(r->server);
    702 #ifdef ECS_DEBUG
    703   fprintf(stderr, "running ecs_handler %s\n", r->filename);
    704 #endif
    705 
    706   if((argv0 = strrchr(r->filename,'/')) != NULL)
    707     argv0++;
    708   else argv0 = r->filename;
    709 
    710   if (!(ap_allow_options (r) & OPT_EXECCGI) )
    711     return log_scripterror(r, conf, FORBIDDEN,
    712 	"Options ExecCGI is off in this directory");
    713 
    714   if (S_ISDIR(r->finfo.st_mode))
    715     return log_scripterror(r, conf, FORBIDDEN,
    716 	"attempt to invoke directory as script");
    717   if (r->finfo.st_mode == 0)
    718     return log_scripterror(r, conf, NOT_FOUND,
    719 	"file not found or unable to stat");
    720 
    721 #ifdef ECS_DEBUG
    722   fprintf (stderr, "ap_setup_client_block\n");
    723 #endif
    724   if ((retval = ap_setup_client_block(r, REQUEST_CHUNKED_ERROR)))
    725     return retval;
    726 
    727 #ifdef ECS_DEBUG
    728   fprintf (stderr, "before run\n");
    729 #endif
    730   return run_xcgi(conf, r, argv0);
    731 }
    732 
    733 handler_rec ecs_handlers[] = {
    734   { ECS_MAGIC_TYPE, ecs_handler },
    735   { "ecs-cgi", ecs_handler},
    736   { NULL }
    737 };
    738 
    739 command_rec ecs_cmds[] = {
    740  { "ECSFork", set_fork, NULL, OR_FILEINFO, FLAG,
    741    "On or off to enable or disable (default) forking before calling cgi_main" },
    742  { "ECSReload", set_reload, NULL, OR_FILEINFO, FLAG,
    743    "On or off to enable or disable (default) checking if the shared library\n" \
    744    "  has changed and reloading it if it has"},
    745  { "ECSDepLib", set_dep_lib, NULL, RSRC_CONF, TAKE1,
    746    "The location of a dependent lib to dlopen during init"},
    747  { "ECSPreload", set_pre_lib, NULL, RSRC_CONF, TAKE1,
    748    "The location of a shared lib handler to preload during init"},
    749  { NULL }
    750 };
    751 
    752 module ecs_module = {
    753    STANDARD_MODULE_STUFF,
    754    NULL,			/* initializer */
    755    NULL,			/* dir config creater */
    756    NULL,			/* dir merger --- default is to override */
    757    create_ecs_config,		/* server config */
    758    NULL, /*merge_ecs_config,*/	       	/* merge server config */
    759    ecs_cmds,			/* command table */
    760    ecs_handlers,		/* handlers */
    761    NULL,			/* filename translation */
    762    NULL,			/* check_user_id */
    763    NULL,			/* check auth */
    764    NULL,			/* check access */
    765    NULL,			/* type_checker */
    766    NULL,			/* fixups */
    767    NULL,			/* logger */
    768 #if MODULE_MAGIC_NUMBER >= 19970103
    769    NULL,       			/* [3] header parser */
    770 #endif
    771 #if MODULE_MAGIC_NUMBER >= 19970719
    772    NULL,       			/* process initializer */
    773 #endif
    774 #if MODULE_MAGIC_NUMBER >= 19970728
    775    NULL,       			/* process exit/cleanup */
    776 #endif
    777 #if MODULE_MAGIC_NUMBER >= 19970902
    778    NULL,       			/* [1] post read_request handling */
    779 #endif
    780 };
    781 
    782 
    783 /* Here's some stuff that essentially duplicates util_script.c
    784    This really should be merged, but if _I_ do that it'll break
    785    modularity and leave users with a nasty versioning problem.
    786 
    787    If I get a round tuit sometime, I might ask the Apache folks
    788    about integrating some changes in the main source tree.
    789 */
    790 /* If a request includes query info in the URL (stuff after "?"), and
    791  * the query info does not contain "=" (indicative of a FORM submission),
    792  * then this routine is called to create the argument list to be passed
    793  * to the CGI script.  When suexec is enabled, the suexec path, user, and
    794  * group are the first three arguments to be passed; if not, all three
    795  * must be NULL.  The query info is split into separate arguments, where
    796  * "+" is the separator between keyword arguments.
    797  */
    798 char **ecs_create_argv(pool *p, char *path, char *user, char *group,
    799                           char *av0, const char *args)
    800 {
    801     int x, numwords;
    802     char **av;
    803     char *w;
    804     int idx = 0;
    805 
    806     /* count the number of keywords */
    807 
    808     for (x = 0, numwords = 1; args[x]; x++)
    809         if (args[x] == '+') ++numwords;
    810 
    811     if (numwords > APACHE_ARG_MAX - 5) {
    812         numwords = APACHE_ARG_MAX - 5; /* Truncate args to prevent overrun */
    813     }
    814     av = (char **)ap_palloc(p, (numwords + 5) * sizeof(char *));
    815 
    816     if (path)
    817         av[idx++] = path;
    818     if (user)
    819         av[idx++] = user;
    820     if (group)
    821         av[idx++] = group;
    822 
    823     av[idx++] = av0;
    824 
    825     for (x = 1; x <= numwords; x++) {
    826         w = ap_getword_nulls(p, &args, '+');
    827         ap_unescape_url(w);
    828         av[idx++] = ap_escape_shell_cmd(p, w);
    829     }
    830     av[idx] = NULL;
    831     return av;
    832 }
    833