• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
buf_getline(const char * idata,int ilen,char * odata,int olen,int * nonl)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 
h_getline(char * buf,int len,void * h)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 
header_write(HEADER_BUF * hbuf,const char * data,int dlen)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  */
wrap_write(void * data,const char * buf,size_t len)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 
wrap_vprintf(void * data,const char * fmt,va_list ap)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 
wrap_read(void * data,char * buf,size_t len)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 
wrap_getenv(void * data,const char * s)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 
wrap_putenv(void * data,const char * k,const char * v)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 
wrap_iterenv(void * data,int x,char ** k,char ** v)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 
dummy(ap_os_dso_handle_t dlhandle)368 static void dummy (ap_os_dso_handle_t dlhandle)
369 {
370 }
371 
slib_cleanup(ap_os_dso_handle_t dlhandle)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 
create_ecs_config(pool * p,server_rec * dummy)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 
e_setup_cgi_env(request_rec * r)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 
set_dep_lib(cmd_parms * parms,void * dummy,char * arg)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 */
load_library(ap_pool * p,ecs_manager * entry,int do_stat,char * prefix)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 
set_pre_lib(cmd_parms * parms,void * dummy,char * arg)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 
set_fork(cmd_parms * parms,void * dummy,int flag)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 
set_reload(cmd_parms * parms,void * dummy,int flag)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 
findHandler(array_header * a,char * file)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 
run_dl_cgi(ecs_server_conf * sconf,request_rec * r,char * argv0)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 
run_xcgi(ecs_server_conf * conf,request_rec * r,char * argv0)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 
ecs_handler(request_rec * r)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  */
ecs_create_argv(pool * p,char * path,char * user,char * group,char * av0,const char * args)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