1 /* $NetBSD: cgi-bozo.c,v 1.59 2026/10/07 14:39:30 nia Exp $ */ 2 3 /* $eterna: cgi-bozo.c,v 1.40 2011/11/18 09:21:15 mrg Exp $ */ 4 5 /* 6 * Copyright (c) 1997-2021 Matthew R. Green 7 * All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer and 16 * dedication in the documentation and/or other materials provided 17 * with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 24 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 26 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 27 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 * 31 */ 32 33 /* this code implements CGI/1.2 for bozohttpd */ 34 35 #ifndef NO_CGIBIN_SUPPORT 36 37 #include <sys/param.h> 38 #include <sys/socket.h> 39 #include <sys/wait.h> 40 41 #include <ctype.h> 42 #include <errno.h> 43 #include <paths.h> 44 #include <signal.h> 45 #include <stdlib.h> 46 #include <string.h> 47 #include <syslog.h> 48 #include <unistd.h> 49 #include <assert.h> 50 51 #include <netinet/in.h> 52 53 #include "bozohttpd.h" 54 55 #define CGIBIN_PREFIX "cgi-bin/" 56 #define CGIBIN_PREFIX_LEN (sizeof(CGIBIN_PREFIX)-1) 57 58 #ifndef USE_ARG 59 #define USE_ARG(x) /*LINTED*/(void)&(x) 60 #endif 61 62 /* 63 * given the file name, return a CGI interpreter 64 */ 65 static const char * 66 content_cgihandler(bozohttpd_t *httpd, bozo_httpreq_t *request, 67 const char *file) 68 { 69 bozo_content_map_t *map; 70 71 USE_ARG(request); 72 debug((httpd, DEBUG_FAT, "content_cgihandler: trying file %s", file)); 73 map = bozo_match_content_map(httpd, file, 0); 74 if (map) 75 return map->cgihandler; 76 return NULL; 77 } 78 79 static int 80 parse_header(bozo_httpreq_t *request, const char *str, ssize_t len, 81 char **hdr_str, char **hdr_val) 82 { 83 struct bozohttpd_t *httpd = request->hr_httpd; 84 char *name, *value; 85 86 /* if the string passed is zero-length bail out */ 87 if (*str == '\0') 88 return -1; 89 90 value = bozostrdup(httpd, request, str); 91 92 /* locate the ':' separator in the header/value */ 93 name = bozostrnsep(&value, ":", &len); 94 95 if (NULL == name || -1 == len) { 96 free(value); 97 return -1; 98 } 99 100 /* skip leading space/tab */ 101 while (*value == ' ' || *value == '\t') 102 len--, value++; 103 104 *hdr_str = name; 105 *hdr_val = value; 106 107 return 0; 108 } 109 110 /* 111 * handle parsing a CGI header output, transposing a Status: header 112 * into the HTTP reply (ie, instead of "200 OK"). 113 */ 114 static void 115 finish_cgi_output(bozohttpd_t *httpd, bozo_httpreq_t *request, int in, int nph) 116 { 117 char buf[BOZO_WRSZ]; 118 char *str; 119 ssize_t len; 120 ssize_t rbytes; 121 SIMPLEQ_HEAD(, bozoheaders) headers; 122 bozoheaders_t *hdr, *nhdr; 123 int write_header, nheaders = 0; 124 125 /* much of this code is like bozo_read_request()'s header loop. */ 126 SIMPLEQ_INIT(&headers); 127 write_header = nph == 0; 128 while (nph == 0 && 129 (str = bozodgetln(httpd, in, &len, bozo_read)) != NULL) { 130 char *hdr_name, *hdr_value; 131 132 if (parse_header(request, str, len, &hdr_name, &hdr_value)) 133 break; 134 135 /* 136 * The CGI 1.{1,2} spec both say that if the cgi program 137 * returns a `Status:' header field then the server MUST 138 * return it in the response. If the cgi program does 139 * not return any `Status:' header then the server should 140 * respond with 200 OK. 141 * The CGI 1.1 and 1.2 specification differ slightly on 142 * this in that v1.2 says that the script MUST NOT return a 143 * `Status:' header if it is returning a `Location:' header. 144 * For compatibility we are going with the CGI 1.1 behavior. 145 */ 146 if (strcasecmp(hdr_name, "status") == 0) { 147 debug((httpd, DEBUG_OBESE, 148 "%s: writing HTTP header " 149 "from status %s ..", __func__, hdr_value)); 150 bozo_printf(httpd, "%s %s\r\n", request->hr_proto, 151 hdr_value); 152 bozo_flush(httpd, stdout); 153 write_header = 0; 154 free(hdr_name); 155 break; 156 } 157 158 hdr = bozomalloc(httpd, sizeof *hdr); 159 hdr->h_header = hdr_name; 160 hdr->h_value = hdr_value; 161 SIMPLEQ_INSERT_TAIL(&headers, hdr, h_next); 162 nheaders++; 163 } 164 165 if (write_header) { 166 debug((httpd, DEBUG_OBESE, 167 "%s: writing HTTP header ..", __func__)); 168 bozo_printf(httpd, 169 "%s 200 OK\r\n", request->hr_proto); 170 bozo_flush(httpd, stdout); 171 } 172 173 if (nheaders) { 174 debug((httpd, DEBUG_OBESE, 175 "%s: writing delayed HTTP headers ..", __func__)); 176 SIMPLEQ_FOREACH_SAFE(hdr, &headers, h_next, nhdr) { 177 bozo_printf(httpd, "%s: %s\r\n", hdr->h_header, 178 hdr->h_value); 179 free(hdr->h_header); 180 free(hdr); 181 } 182 bozo_printf(httpd, "\r\n"); 183 bozo_flush(httpd, stdout); 184 } 185 186 /* CGI programs should perform their own timeouts */ 187 while ((rbytes = read(in, buf, sizeof buf)) > 0) { 188 ssize_t wbytes; 189 190 while (rbytes) { 191 wbytes = bozo_write(httpd, STDOUT_FILENO, buf, 192 (size_t)rbytes); 193 if (wbytes > 0) 194 rbytes -= wbytes; 195 else 196 bozoerr(httpd, 1, 197 "cgi output write failed: %s", 198 strerror(errno)); 199 } 200 } 201 } 202 203 static void 204 append_index_html(bozohttpd_t *httpd, char **url) 205 { 206 *url = bozorealloc(httpd, *url, 207 strlen(*url) + strlen(httpd->index_html) + 1); 208 strcat(*url, httpd->index_html); 209 debug((httpd, DEBUG_NORMAL, 210 "append_index_html: url adjusted to `%s'", *url)); 211 } 212 213 /* This function parse search-string according to section 4.4 of RFC3875 */ 214 static char ** 215 parse_search_string(bozo_httpreq_t *request, const char *query, size_t *args_len) 216 { 217 struct bozohttpd_t *httpd = request->hr_httpd; 218 size_t i; 219 char *s, *str, **args; 220 221 *args_len = 0; 222 223 /* URI MUST not contain any unencoded '=' - RFC3875, section 4.4 */ 224 if (strchr(query, '=')) 225 return NULL; 226 227 str = bozostrdup(httpd, request, query); 228 229 /* 230 * there's no more arguments than '+' chars in the query string as it's 231 * the separator 232 */ 233 *args_len = 1; 234 /* count '+' in str */ 235 for (s = str; (s = strchr(s, '+')) != NULL; (*args_len)++) 236 s++; 237 238 args = bozomalloc(httpd, sizeof(*args) * (*args_len + 1)); 239 240 args[0] = str; 241 args[*args_len] = NULL; 242 for (s = str, i = 1; (s = strchr(s, '+')) != NULL; i++) { 243 *s = '\0'; 244 s++; 245 args[i] = s; 246 } 247 248 /* 249 * check if search-strings are valid: 250 * 251 * RFC3875, section 4.4: 252 * 253 * search-string = search-word *( "+" search-word ) 254 * search-word = 1*schar 255 * schar = unreserved | escaped | xreserved 256 * xreserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "," | 257 * "$" 258 * 259 * section 2.3: 260 * 261 * hex = digit | "A" | "B" | "C" | "D" | "E" | "F" | "a" | 262 * "b" | "c" | "d" | "e" | "f" 263 * escaped = "%" hex hex 264 * unreserved = alpha | digit | mark 265 * mark = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")" 266 * 267 * section 2.2: 268 * 269 * alpha = lowalpha | hialpha 270 * lowalpha = "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | 271 * "i" | "j" | "k" | "l" | "m" | "n" | "o" | "p" | 272 * "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" | 273 * "y" | "z" 274 * hialpha = "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | 275 * "I" | "J" | "K" | "L" | "M" | "N" | "O" | "P" | 276 * "Q" | "R" | "S" | "T" | "U" | "V" | "W" | "X" | 277 * "Y" | "Z" 278 * digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | 279 * "8" | "9" 280 */ 281 #define UNRESERVED_CHAR "-_.!~*'()" 282 #define XRESERVED_CHAR ";/?:@&=,$" 283 284 for (i = 0; i < *args_len; i++) { 285 s = args[i]; 286 /* search-word MUST have at least one schar */ 287 if (*s == '\0') 288 goto parse_err; 289 while (*s) { 290 /* check if it's unreserved */ 291 if (isalpha((unsigned char)*s) || 292 isdigit((unsigned char)*s) || 293 strchr(UNRESERVED_CHAR, *s)) { 294 s++; 295 continue; 296 } 297 298 /* check if it's escaped */ 299 if (*s == '%') { 300 if (s[1] == '\0' || s[2] == '\0') 301 goto parse_err; 302 if (!isxdigit((unsigned char)s[1]) || 303 !isxdigit((unsigned char)s[2])) 304 goto parse_err; 305 s += 3; 306 continue; 307 } 308 309 /* check if it's xreserved */ 310 311 if (strchr(XRESERVED_CHAR, *s)) { 312 s++; 313 continue; 314 } 315 316 goto parse_err; 317 } 318 } 319 320 /* decode percent encoding */ 321 for (i = 0; i < *args_len; i++) { 322 if (bozo_decode_url_percent(request, args[i])) 323 goto parse_err; 324 } 325 326 /* allocate each arg separately */ 327 for (i = 0; i < *args_len; i++) 328 args[i] = bozostrdup(httpd, request, args[i]); 329 free(str); 330 331 return args; 332 333 parse_err: 334 335 free(str); 336 free(args); 337 *args_len = 0; 338 339 return NULL; 340 341 } 342 343 void 344 bozo_cgi_setbin(bozohttpd_t *httpd, const char *path) 345 { 346 httpd->cgibin = bozostrdup(httpd, NULL, path); 347 debug((httpd, DEBUG_OBESE, "cgibin (cgi-bin directory) is %s", 348 httpd->cgibin)); 349 } 350 351 /* help build up the environ pointer */ 352 void 353 bozo_setenv(bozohttpd_t *httpd, const char *env, const char *val, 354 char **envp) 355 { 356 char *s1 = bozomalloc(httpd, strlen(env) + strlen(val) + 2); 357 358 strcpy(s1, env); 359 strcat(s1, "="); 360 strcat(s1, val); 361 debug((httpd, DEBUG_OBESE, "bozo_setenv: %s", s1)); 362 *envp = s1; 363 } 364 365 /* 366 * Checks if the request has asked for a cgi-bin. Should only be called if 367 * cgibin is set. If it starts CGIBIN_PREFIX or has a ncontent handler, 368 * process the cgi, otherwise just return. Returns 0 if it did not handle 369 * the request. 370 */ 371 int 372 bozo_process_cgi(bozo_httpreq_t *request) 373 { 374 bozohttpd_t *httpd = request->hr_httpd; 375 char buf[BOZO_WRSZ]; 376 char date[40]; 377 bozoheaders_t *headp; 378 const char *type, *clen, *info, *cgihandler; 379 char *query, *s, *t, *path, *env, *command, *file, *url; 380 char **envp, **curenvp, **argv, **search_string_argv = NULL; 381 char **lastenvp; 382 char *uri; 383 size_t i, len, search_string_argc = 0; 384 ssize_t rbytes; 385 pid_t pid; 386 int envpsize, ix, nph; 387 int sv[2]; 388 389 if (!httpd->cgibin && !httpd->process_cgi) 390 return 0; 391 392 #ifndef NO_USER_SUPPORT 393 if (request->hr_user && !httpd->enable_cgi_users) 394 return 0; 395 #endif /* !NO_USER_SUPPORT */ 396 397 if (request->hr_oldfile && strcmp(request->hr_oldfile, "/") != 0) 398 uri = request->hr_oldfile; 399 else 400 uri = request->hr_file; 401 402 if (uri[0] == '/') 403 file = bozostrdup(httpd, request, uri); 404 else 405 bozoasprintf(httpd, &file, "/%s", uri); 406 407 if (request->hr_query && strlen(request->hr_query)) 408 query = bozostrdup(httpd, request, request->hr_query); 409 else 410 query = NULL; 411 412 bozoasprintf(httpd, &url, "%s%s%s", 413 file, 414 query ? "?" : "", 415 query ? query : ""); 416 debug((httpd, DEBUG_NORMAL, "%s: url `%s'", __func__, url)); 417 418 path = NULL; 419 envp = NULL; 420 cgihandler = NULL; 421 command = NULL; 422 info = NULL; 423 424 len = strlen(url); 425 426 if (bozo_auth_check(request, url + 1)) 427 goto out; 428 429 if (!httpd->cgibin || 430 strncmp(url + 1, CGIBIN_PREFIX, CGIBIN_PREFIX_LEN) != 0) { 431 cgihandler = content_cgihandler(httpd, request, file + 1); 432 if (cgihandler == NULL) { 433 debug((httpd, DEBUG_FAT, 434 "%s: no handler, returning", __func__)); 435 goto out; 436 } 437 if (len == 0 || file[len - 1] == '/') 438 append_index_html(httpd, &file); 439 debug((httpd, DEBUG_NORMAL, "%s: cgihandler `%s'", 440 __func__, cgihandler)); 441 } else if (len - 1 == CGIBIN_PREFIX_LEN) /* url is "/cgi-bin/" */ 442 append_index_html(httpd, &file); 443 444 /* RFC3875 sect. 4.4. - search-string support */ 445 if (query != NULL) { 446 search_string_argv = parse_search_string(request, query, 447 &search_string_argc); 448 } 449 450 debug((httpd, DEBUG_NORMAL, "parse_search_string args no: %zu", 451 search_string_argc)); 452 for (i = 0; i < search_string_argc; i++) { 453 debug((httpd, DEBUG_FAT, 454 "search_string[%zu]: `%s'", i, search_string_argv[i])); 455 } 456 457 argv = bozomalloc(httpd, sizeof(*argv) * (3 + search_string_argc)); 458 459 ix = 0; 460 if (cgihandler) { 461 command = file + 1; 462 path = bozostrdup(httpd, request, cgihandler); 463 } else { 464 command = file + CGIBIN_PREFIX_LEN + 1; 465 if ((s = strchr(command, '/')) != NULL) { 466 info = bozostrdup(httpd, request, s); 467 *s = '\0'; 468 } 469 path = bozomalloc(httpd, 470 strlen(httpd->cgibin) + 1 + strlen(command) + 1); 471 strcpy(path, httpd->cgibin); 472 strcat(path, "/"); 473 strcat(path, command); 474 } 475 476 argv[ix++] = path; 477 478 /* copy search-string args */ 479 for (i = 0; i < search_string_argc; i++) 480 argv[ix++] = search_string_argv[i]; 481 482 argv[ix++] = NULL; 483 nph = strncmp(command, "nph-", 4) == 0; 484 485 type = request->hr_content_type; 486 clen = request->hr_content_length; 487 488 envpsize = 13 + request->hr_nheaders + 489 (info && *info ? 1 : 0) + 490 (query && *query ? 1 : 0) + 491 (type && *type ? 1 : 0) + 492 (clen && *clen ? 1 : 0) + 493 (request->hr_remotehost && *request->hr_remotehost ? 1 : 0) + 494 (request->hr_remoteaddr && *request->hr_remoteaddr ? 1 : 0) + 495 (cgihandler ? 1 : 0) + 496 bozo_auth_cgi_count(request) + 497 (request->hr_serverport && *request->hr_serverport ? 1 : 0); 498 499 debug((httpd, DEBUG_FAT, 500 "%s: path `%s', cmd `%s', info `%s', " 501 "query `%s', nph `%d', envpsize `%d'", __func__, 502 path, command, strornull(info), 503 strornull(query), nph, envpsize)); 504 505 envp = bozomalloc(httpd, sizeof(*envp) * envpsize); 506 for (ix = 0; ix < envpsize; ix++) 507 envp[ix] = NULL; 508 curenvp = envp; 509 lastenvp = envp + envpsize; 510 511 SIMPLEQ_FOREACH(headp, &request->hr_headers, h_next) { 512 const char *s2; 513 env = bozomalloc(httpd, 6 + strlen(headp->h_header) + 1 + 514 strlen(headp->h_value)); 515 516 t = env; 517 strcpy(t, "HTTP_"); 518 t += strlen(t); 519 for (s2 = headp->h_header; *s2; t++, s2++) 520 if (islower((unsigned char)*s2)) 521 *t = toupper((unsigned char)*s2); 522 else if (*s2 == '-') 523 *t = '_'; 524 else 525 *t = *s2; 526 *t = '\0'; 527 debug((httpd, DEBUG_OBESE, "setting header %s as %s = %s", 528 headp->h_header, env, headp->h_value)); 529 bozo_setenv(httpd, env, headp->h_value, curenvp++); 530 free(env); 531 } 532 533 #ifndef _PATH_DEFPATH 534 #define _PATH_DEFPATH "/usr/bin:/bin" 535 #endif 536 537 bozo_setenv(httpd, "PATH", _PATH_DEFPATH, curenvp++); 538 bozo_setenv(httpd, "IFS", " \t\n", curenvp++); 539 bozo_setenv(httpd, "SERVER_NAME", BOZOHOST(httpd,request), curenvp++); 540 bozo_setenv(httpd, "GATEWAY_INTERFACE", "CGI/1.1", curenvp++); 541 bozo_setenv(httpd, "SERVER_PROTOCOL", request->hr_proto, curenvp++); 542 bozo_setenv(httpd, "REQUEST_METHOD", request->hr_methodstr, curenvp++); 543 bozo_setenv(httpd, "SCRIPT_NAME", file, curenvp++); 544 bozo_setenv(httpd, "SCRIPT_FILENAME", file + 1, curenvp++); 545 bozo_setenv(httpd, "SERVER_SOFTWARE", httpd->server_software, 546 curenvp++); 547 bozo_setenv(httpd, "REQUEST_URI", uri, curenvp++); 548 bozo_setenv(httpd, "DATE_GMT", bozo_http_date(date, sizeof(date)), 549 curenvp++); 550 /* RFC3875 section 4.1.7 says that QUERY_STRING MUST be defined. */ 551 if (query && *query) 552 bozo_setenv(httpd, "QUERY_STRING", query, curenvp++); 553 else 554 bozo_setenv(httpd, "QUERY_STRING", "", curenvp++); 555 if (info && *info) 556 bozo_setenv(httpd, "PATH_INFO", info, curenvp++); 557 if (type && *type) 558 bozo_setenv(httpd, "CONTENT_TYPE", type, curenvp++); 559 if (clen && *clen) 560 bozo_setenv(httpd, "CONTENT_LENGTH", clen, curenvp++); 561 if (request->hr_serverport && *request->hr_serverport) 562 bozo_setenv(httpd, "SERVER_PORT", request->hr_serverport, 563 curenvp++); 564 if (request->hr_remotehost && *request->hr_remotehost) 565 bozo_setenv(httpd, "REMOTE_HOST", request->hr_remotehost, 566 curenvp++); 567 if (request->hr_remoteaddr && *request->hr_remoteaddr) 568 bozo_setenv(httpd, "REMOTE_ADDR", request->hr_remoteaddr, 569 curenvp++); 570 /* 571 * Apache does this when invoking content handlers, and PHP 572 * 5.3 requires it as a "security" measure. 573 */ 574 if (cgihandler) 575 bozo_setenv(httpd, "REDIRECT_STATUS", "200", curenvp++); 576 bozo_auth_cgi_setenv(request, &curenvp); 577 578 debug((httpd, DEBUG_FAT, "%s: going exec %s with args:", __func__, 579 path)); 580 581 for (i = 0; argv[i] != NULL; i++) { 582 debug((httpd, DEBUG_FAT, "%s: argv[%zu] = `%s'", __func__, 583 i, argv[i])); 584 } 585 586 if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, sv) == -1) 587 bozoerr(httpd, 1, "child socketpair failed: %s", 588 strerror(errno)); 589 590 *curenvp = 0; 591 assert(lastenvp > curenvp); 592 593 /* 594 * We create 2 procs: one to become the CGI, one to continue 595 * reading from the network and writing to the CGI process. 596 * This parent will read from the CGI and output to the network. 597 */ 598 switch (fork()) { 599 case -1: /* eep, failure */ 600 bozoerr(httpd, 1, "child fork failed: %s", strerror(errno)); 601 /*NOTREACHED*/ 602 case 0: 603 close(sv[0]); 604 dup2(sv[1], STDIN_FILENO); 605 dup2(sv[1], STDOUT_FILENO); 606 close(2); 607 close(sv[1]); 608 closelog(); 609 bozo_daemon_closefds(httpd); 610 611 if (httpd->cgibin && chdir(httpd->cgibin) == -1) 612 bozoerr(httpd, 1, "failed to chdir(2)"); 613 614 if (-1 == execve(path, argv, envp)) { 615 int saveerrno = errno; 616 bozo_http_error(httpd, 404, request, 617 "Cannot execute CGI"); 618 /* don't log easy to trigger events */ 619 if (saveerrno != ENOENT && 620 saveerrno != EISDIR && 621 saveerrno != EACCES) 622 bozoerr(httpd, 1, "child exec failed: %s: %s", 623 path, strerror(saveerrno)); 624 _exit(1); 625 } 626 /* NOT REACHED */ 627 bozoerr(httpd, 1, "child execve returned?!"); 628 } 629 630 free(query); 631 free(file); 632 free(url); 633 for (i = 0; i < search_string_argc; i++) 634 free(search_string_argv[i]); 635 free(search_string_argv); 636 637 close(sv[1]); 638 639 /* parent: read from sv[0] (bozo_write()) write to stdout */ 640 /* child: read from stdin (bozo_read()) write to sv[0] */ 641 pid = fork(); 642 if (pid == -1) 643 bozoerr(httpd, 1, "io child fork failed: %s", strerror(errno)); 644 else if (pid == 0) { 645 close(STDOUT_FILENO); 646 /* CGI programs should perform their own timeouts */ 647 while ((rbytes = bozo_read(httpd, STDIN_FILENO, buf, 648 sizeof buf)) > 0) { 649 ssize_t wbytes; 650 while (rbytes) { 651 wbytes = write(sv[0], buf, (size_t)rbytes); 652 if (wbytes > 0) 653 rbytes -= wbytes; 654 else 655 bozoerr(httpd, 1, "write failed: %s", 656 strerror(errno)); 657 } 658 } 659 debug((httpd, DEBUG_FAT, "done processing cgi input")); 660 _exit(0); 661 } 662 663 /* child reader/writer */ 664 close(STDIN_FILENO); 665 finish_cgi_output(httpd, request, sv[0], nph); 666 /* if we do SSL, send a SSL_shutdown now */ 667 bozo_ssl_shutdown(request->hr_httpd); 668 /* if we're done output, our child is useless... */ 669 kill(pid, SIGKILL); 670 debug((httpd, DEBUG_FAT, "done processing cgi output")); 671 /* 672 * Collect input child, but leave CGI child running 673 * to allow some overlap. 674 */ 675 waitpid(pid, NULL, 0); 676 exit(0); 677 678 out: 679 680 for (i = 0; i < search_string_argc; i++) 681 free(search_string_argv[i]); 682 free(search_string_argv); 683 free(query); 684 free(file); 685 free(url); 686 return 0; 687 } 688 689 #ifndef NO_DYNAMIC_CONTENT 690 /* cgi maps are simple ".postfix /path/to/prog" */ 691 void 692 bozo_add_content_map_cgi(bozohttpd_t *httpd, const char *arg, 693 const char *cgihandler) 694 { 695 bozo_content_map_t *map; 696 697 debug((httpd, DEBUG_NORMAL, "bozo_add_content_map_cgi: name %s cgi %s", 698 arg, cgihandler)); 699 700 httpd->process_cgi = 1; 701 702 map = bozo_get_content_map(httpd, arg); 703 map->name = arg; 704 map->type = map->encoding = map->encoding11 = NULL; 705 map->cgihandler = cgihandler; 706 } 707 #endif /* NO_DYNAMIC_CONTENT */ 708 709 #endif /* NO_CGIBIN_SUPPORT */ 710