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