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