1 #include "server.h" 2 #include "stat_cache.h" 3 #include "keyvalue.h" 4 #include "log.h" 5 #include "connections.h" 6 #include "joblist.h" 7 #include "http_chunk.h" 8 9 #include "plugin.h" 10 11 #include <sys/types.h> 12 13 #ifdef __WIN32 14 # include <winsock2.h> 15 #else 16 # include <sys/socket.h> 17 # include <sys/wait.h> 18 # include <sys/mman.h> 19 # include <netinet/in.h> 20 # include <arpa/inet.h> 21 #endif 22 23 #include <unistd.h> 24 #include <errno.h> 25 #include <stdlib.h> 26 #include <string.h> 27 #include <fdevent.h> 28 #include <signal.h> 29 #include <ctype.h> 30 #include <assert.h> 31 32 #include <stdio.h> 33 #include <fcntl.h> 34 35 #ifdef HAVE_SYS_FILIO_H 36 # include <sys/filio.h> 37 #endif 38 39 #include "version.h" 40 41 enum {EOL_UNSET, EOL_N, EOL_RN}; 42 43 typedef struct { 44 char **ptr; 45 46 size_t size; 47 size_t used; 48 } char_array; 49 50 typedef struct { 51 pid_t *ptr; 52 size_t used; 53 size_t size; 54 } buffer_pid_t; 55 56 typedef struct { 57 array *cgi; 58 unsigned short execute_x_only; 59 } plugin_config; 60 61 typedef struct { 62 PLUGIN_DATA; 63 buffer_pid_t cgi_pid; 64 65 buffer *tmp_buf; 66 buffer *parse_response; 67 68 plugin_config **config_storage; 69 70 plugin_config conf; 71 } plugin_data; 72 73 typedef struct { 74 pid_t pid; 75 int fd; 76 int fde_ndx; /* index into the fd-event buffer */ 77 78 connection *remote_conn; /* dumb pointer */ 79 plugin_data *plugin_data; /* dumb pointer */ 80 81 buffer *response; 82 buffer *response_header; 83 } handler_ctx; 84 85 static handler_ctx * cgi_handler_ctx_init(void) { 86 handler_ctx *hctx = calloc(1, sizeof(*hctx)); 87 88 assert(hctx); 89 90 hctx->response = buffer_init(); 91 hctx->response_header = buffer_init(); 92 93 return hctx; 94 } 95 96 static void cgi_handler_ctx_free(handler_ctx *hctx) { 97 buffer_free(hctx->response); 98 buffer_free(hctx->response_header); 99 100 free(hctx); 101 } 102 103 enum {FDEVENT_HANDLED_UNSET, FDEVENT_HANDLED_FINISHED, FDEVENT_HANDLED_NOT_FINISHED, FDEVENT_HANDLED_ERROR}; 104 105 INIT_FUNC(mod_cgi_init) { 106 plugin_data *p; 107 108 p = calloc(1, sizeof(*p)); 109 110 assert(p); 111 112 p->tmp_buf = buffer_init(); 113 p->parse_response = buffer_init(); 114 115 return p; 116 } 117 118 119 FREE_FUNC(mod_cgi_free) { 120 plugin_data *p = p_d; 121 buffer_pid_t *r = &(p->cgi_pid); 122 123 UNUSED(srv); 124 125 if (p->config_storage) { 126 size_t i; 127 for (i = 0; i < srv->config_context->used; i++) { 128 plugin_config *s = p->config_storage[i]; 129 130 array_free(s->cgi); 131 132 free(s); 133 } 134 free(p->config_storage); 135 } 136 137 138 if (r->ptr) free(r->ptr); 139 140 buffer_free(p->tmp_buf); 141 buffer_free(p->parse_response); 142 143 free(p); 144 145 return HANDLER_GO_ON; 146 } 147 148 SETDEFAULTS_FUNC(mod_fastcgi_set_defaults) { 149 plugin_data *p = p_d; 150 size_t i = 0; 151 152 config_values_t cv[] = { 153 { "cgi.assign", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ 154 { "cgi.execute-x-only", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ 155 { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET} 156 }; 157 158 if (!p) return HANDLER_ERROR; 159 160 p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *)); 161 162 for (i = 0; i < srv->config_context->used; i++) { 163 plugin_config *s; 164 165 s = calloc(1, sizeof(plugin_config)); 166 assert(s); 167 168 s->cgi = array_init(); 169 s->execute_x_only = 0; 170 171 cv[0].destination = s->cgi; 172 cv[1].destination = &(s->execute_x_only); 173 174 p->config_storage[i] = s; 175 176 if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) { 177 return HANDLER_ERROR; 178 } 179 } 180 181 return HANDLER_GO_ON; 182 } 183 184 185 static int cgi_pid_add(server *srv, plugin_data *p, pid_t pid) { 186 int m = -1; 187 size_t i; 188 buffer_pid_t *r = &(p->cgi_pid); 189 190 UNUSED(srv); 191 192 for (i = 0; i < r->used; i++) { 193 if (r->ptr[i] > m) m = r->ptr[i]; 194 } 195 196 if (r->size == 0) { 197 r->size = 16; 198 r->ptr = malloc(sizeof(*r->ptr) * r->size); 199 } else if (r->used == r->size) { 200 r->size += 16; 201 r->ptr = realloc(r->ptr, sizeof(*r->ptr) * r->size); 202 } 203 204 r->ptr[r->used++] = pid; 205 206 return m; 207 } 208 209 static int cgi_pid_del(server *srv, plugin_data *p, pid_t pid) { 210 size_t i; 211 buffer_pid_t *r = &(p->cgi_pid); 212 213 UNUSED(srv); 214 215 for (i = 0; i < r->used; i++) { 216 if (r->ptr[i] == pid) break; 217 } 218 219 if (i != r->used) { 220 /* found */ 221 222 if (i != r->used - 1) { 223 r->ptr[i] = r->ptr[r->used - 1]; 224 } 225 r->used--; 226 } 227 228 return 0; 229 } 230 231 static int cgi_response_parse(server *srv, connection *con, plugin_data *p, buffer *in) { 232 char *ns; 233 const char *s; 234 int line = 0; 235 236 UNUSED(srv); 237 238 buffer_copy_string_buffer(p->parse_response, in); 239 240 for (s = p->parse_response->ptr; 241 NULL != (ns = strchr(s, '\n')); 242 s = ns + 1, line++) { 243 const char *key, *value; 244 int key_len; 245 data_string *ds; 246 247 /* strip the \n */ 248 ns[0] = '\0'; 249 250 if (ns > s && ns[-1] == '\r') ns[-1] = '\0'; 251 252 if (line == 0 && 253 0 == strncmp(s, "HTTP/1.", 7)) { 254 /* non-parsed header ... we parse them anyway */ 255 256 if ((s[7] == '1' || 257 s[7] == '0') && 258 s[8] == ' ') { 259 int status; 260 /* after the space should be a status code for us */ 261 262 status = strtol(s+9, NULL, 10); 263 264 if (status >= 100 && 265 status < 1000) { 266 /* we expected 3 digits and didn't got them */ 267 con->parsed_response |= HTTP_STATUS; 268 con->http_status = status; 269 } 270 } 271 } else { 272 /* parse the headers */ 273 key = s; 274 if (NULL == (value = strchr(s, ':'))) { 275 /* we expect: "<key>: <value>\r\n" */ 276 continue; 277 } 278 279 key_len = value - key; 280 value += 1; 281 282 /* skip LWS */ 283 while (*value == ' ' || *value == '\t') value++; 284 285 if (NULL == (ds = (data_string *)array_get_unused_element(con->response.headers, TYPE_STRING))) { 286 ds = data_response_init(); 287 } 288 buffer_copy_string_len(ds->key, key, key_len); 289 buffer_copy_string(ds->value, value); 290 291 array_insert_unique(con->response.headers, (data_unset *)ds); 292 293 switch(key_len) { 294 case 4: 295 if (0 == strncasecmp(key, "Date", key_len)) { 296 con->parsed_response |= HTTP_DATE; 297 } 298 break; 299 case 6: 300 if (0 == strncasecmp(key, "Status", key_len)) { 301 con->http_status = strtol(value, NULL, 10); 302 con->parsed_response |= HTTP_STATUS; 303 } 304 break; 305 case 8: 306 if (0 == strncasecmp(key, "Location", key_len)) { 307 con->parsed_response |= HTTP_LOCATION; 308 } 309 break; 310 case 10: 311 if (0 == strncasecmp(key, "Connection", key_len)) { 312 con->response.keep_alive = (0 == strcasecmp(value, "Keep-Alive")) ? 1 : 0; 313 con->parsed_response |= HTTP_CONNECTION; 314 } 315 break; 316 case 14: 317 if (0 == strncasecmp(key, "Content-Length", key_len)) { 318 con->response.content_length = strtol(value, NULL, 10); 319 con->parsed_response |= HTTP_CONTENT_LENGTH; 320 } 321 break; 322 default: 323 break; 324 } 325 } 326 } 327 328 /* CGI/1.1 rev 03 - 7.2.1.2 */ 329 if ((con->parsed_response & HTTP_LOCATION) && 330 !(con->parsed_response & HTTP_STATUS)) { 331 con->http_status = 302; 332 } 333 334 return 0; 335 } 336 337 338 static int cgi_demux_response(server *srv, handler_ctx *hctx) { 339 plugin_data *p = hctx->plugin_data; 340 connection *con = hctx->remote_conn; 341 342 while(1) { 343 int n; 344 int toread; 345 346 #if defined(__WIN32) 347 buffer_prepare_copy(hctx->response, 4 * 1024); 348 #else 349 if (ioctl(con->fd, FIONREAD, &toread) || toread == 0 || toread <= 4*1024) { 350 buffer_prepare_copy(hctx->response, 4 * 1024); 351 } else { 352 if (toread > MAX_READ_LIMIT) toread = MAX_READ_LIMIT; 353 buffer_prepare_copy(hctx->response, toread + 1); 354 } 355 #endif 356 357 if (-1 == (n = read(hctx->fd, hctx->response->ptr, hctx->response->size - 1))) { 358 if (errno == EAGAIN || errno == EINTR) { 359 /* would block, wait for signal */ 360 return FDEVENT_HANDLED_NOT_FINISHED; 361 } 362 /* error */ 363 log_error_write(srv, __FILE__, __LINE__, "sdd", strerror(errno), con->fd, hctx->fd); 364 return FDEVENT_HANDLED_ERROR; 365 } 366 367 if (n == 0) { 368 /* read finished */ 369 370 con->file_finished = 1; 371 372 /* send final chunk */ 373 http_chunk_append_mem(srv, con, NULL, 0); 374 joblist_append(srv, con); 375 376 return FDEVENT_HANDLED_FINISHED; 377 } 378 379 hctx->response->ptr[n] = '\0'; 380 hctx->response->used = n+1; 381 382 /* split header from body */ 383 384 if (con->file_started == 0) { 385 int is_header = 0; 386 int is_header_end = 0; 387 size_t last_eol = 0; 388 size_t i; 389 390 buffer_append_string_buffer(hctx->response_header, hctx->response); 391 392 /** 393 * we have to handle a few cases: 394 * 395 * nph: 396 * 397 * HTTP/1.0 200 Ok\n 398 * Header: Value\n 399 * \n 400 * 401 * CGI: 402 * Header: Value\n 403 * Status: 200\n 404 * \n 405 * 406 * and different mixes of \n and \r\n combinations 407 * 408 * Some users also forget about CGI and just send a response and hope 409 * we handle it. No headers, no header-content seperator 410 * 411 */ 412 413 /* nph (non-parsed headers) */ 414 if (0 == strncmp(hctx->response_header->ptr, "HTTP/1.", 7)) is_header = 1; 415 416 for (i = 0; !is_header_end && i < hctx->response_header->used - 1; i++) { 417 char c = hctx->response_header->ptr[i]; 418 419 switch (c) { 420 case ':': 421 /* we found a colon 422 * 423 * looks like we have a normal header 424 */ 425 is_header = 1; 426 break; 427 case '\n': 428 /* EOL */ 429 if (is_header == 0) { 430 /* we got a EOL but we don't seem to got a HTTP header */ 431 432 is_header_end = 1; 433 434 break; 435 } 436 437 /** 438 * check if we saw a \n(\r)?\n sequence 439 */ 440 if (last_eol > 0 && 441 ((i - last_eol == 1) || 442 (i - last_eol == 2 && hctx->response_header->ptr[i - 1] == '\r'))) { 443 is_header_end = 1; 444 break; 445 } 446 447 last_eol = i; 448 449 break; 450 } 451 } 452 453 if (is_header_end) { 454 if (!is_header) { 455 /* no header, but a body */ 456 457 if (con->request.http_version == HTTP_VERSION_1_1) { 458 con->response.transfer_encoding = HTTP_TRANSFER_ENCODING_CHUNKED; 459 } 460 461 http_chunk_append_mem(srv, con, hctx->response_header->ptr, hctx->response_header->used); 462 joblist_append(srv, con); 463 } else { 464 const char *bstart; 465 size_t blen; 466 467 /** 468 * i still points to the char after the terminating EOL EOL 469 * 470 * put it on the last \n again 471 */ 472 i--; 473 474 /* the body starts after the EOL */ 475 bstart = hctx->response_header->ptr + (i + 1); 476 blen = (hctx->response_header->used - 1) - (i + 1); 477 478 /* string the last \r?\n */ 479 if (i > 0 && (hctx->response_header->ptr[i - 1] == '\r')) { 480 i--; 481 } 482 483 hctx->response_header->ptr[i] = '\0'; 484 hctx->response_header->used = i + 1; /* the string + \0 */ 485 486 /* parse the response header */ 487 cgi_response_parse(srv, con, p, hctx->response_header); 488 489 /* enable chunked-transfer-encoding */ 490 if (con->request.http_version == HTTP_VERSION_1_1 && 491 !(con->parsed_response & HTTP_CONTENT_LENGTH)) { 492 con->response.transfer_encoding = HTTP_TRANSFER_ENCODING_CHUNKED; 493 } 494 495 if (blen > 0) { 496 http_chunk_append_mem(srv, con, bstart, blen + 1); 497 joblist_append(srv, con); 498 } 499 } 500 501 con->file_started = 1; 502 } 503 } else { 504 http_chunk_append_mem(srv, con, hctx->response->ptr, hctx->response->used); 505 joblist_append(srv, con); 506 } 507 508 #if 0 509 log_error_write(srv, __FILE__, __LINE__, "ddss", con->fd, hctx->fd, connection_get_state(con->state), b->ptr); 510 #endif 511 } 512 513 return FDEVENT_HANDLED_NOT_FINISHED; 514 } 515 516 static handler_t cgi_connection_close(server *srv, handler_ctx *hctx) { 517 int status; 518 pid_t pid; 519 plugin_data *p; 520 connection *con; 521 522 if (NULL == hctx) return HANDLER_GO_ON; 523 524 p = hctx->plugin_data; 525 con = hctx->remote_conn; 526 527 if (con->mode != p->id) return HANDLER_GO_ON; 528 529 #ifndef __WIN32 530 531 /* the connection to the browser went away, but we still have a connection 532 * to the CGI script 533 * 534 * close cgi-connection 535 */ 536 537 if (hctx->fd != -1) { 538 /* close connection to the cgi-script */ 539 fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd); 540 fdevent_unregister(srv->ev, hctx->fd); 541 542 if (close(hctx->fd)) { 543 log_error_write(srv, __FILE__, __LINE__, "sds", "cgi close failed ", hctx->fd, strerror(errno)); 544 } 545 546 hctx->fd = -1; 547 hctx->fde_ndx = -1; 548 } 549 550 pid = hctx->pid; 551 552 con->plugin_ctx[p->id] = NULL; 553 554 /* is this a good idea ? */ 555 cgi_handler_ctx_free(hctx); 556 557 /* if waitpid hasn't been called by response.c yet, do it here */ 558 if (pid) { 559 /* check if the CGI-script is already gone */ 560 switch(waitpid(pid, &status, WNOHANG)) { 561 case 0: 562 /* not finished yet */ 563 #if 0 564 log_error_write(srv, __FILE__, __LINE__, "sd", "(debug) child isn't done yet, pid:", pid); 565 #endif 566 break; 567 case -1: 568 /* */ 569 if (errno == EINTR) break; 570 571 /* 572 * errno == ECHILD happens if _subrequest catches the process-status before 573 * we have read the response of the cgi process 574 * 575 * -> catch status 576 * -> WAIT_FOR_EVENT 577 * -> read response 578 * -> we get here with waitpid == ECHILD 579 * 580 */ 581 if (errno == ECHILD) return HANDLER_GO_ON; 582 583 log_error_write(srv, __FILE__, __LINE__, "ss", "waitpid failed: ", strerror(errno)); 584 return HANDLER_ERROR; 585 default: 586 /* Send an error if we haven't sent any data yet */ 587 if (0 == con->file_started) { 588 connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST); 589 con->http_status = 500; 590 con->mode = DIRECT; 591 } else { 592 con->file_finished = 1; 593 } 594 595 if (WIFEXITED(status)) { 596 #if 0 597 log_error_write(srv, __FILE__, __LINE__, "sd", "(debug) cgi exited fine, pid:", pid); 598 #endif 599 return HANDLER_GO_ON; 600 } else { 601 log_error_write(srv, __FILE__, __LINE__, "sd", "cgi died, pid:", pid); 602 return HANDLER_GO_ON; 603 } 604 } 605 606 607 kill(pid, SIGTERM); 608 609 /* cgi-script is still alive, queue the PID for removal */ 610 cgi_pid_add(srv, p, pid); 611 } 612 #endif 613 return HANDLER_GO_ON; 614 } 615 616 static handler_t cgi_connection_close_callback(server *srv, connection *con, void *p_d) { 617 plugin_data *p = p_d; 618 619 return cgi_connection_close(srv, con->plugin_ctx[p->id]); 620 } 621 622 623 static handler_t cgi_handle_fdevent(server *srv, void *ctx, int revents) { 624 handler_ctx *hctx = ctx; 625 connection *con = hctx->remote_conn; 626 627 joblist_append(srv, con); 628 629 if (hctx->fd == -1) { 630 log_error_write(srv, __FILE__, __LINE__, "ddss", con->fd, hctx->fd, connection_get_state(con->state), "invalid cgi-fd"); 631 632 return HANDLER_ERROR; 633 } 634 635 if (revents & FDEVENT_IN) { 636 switch (cgi_demux_response(srv, hctx)) { 637 case FDEVENT_HANDLED_NOT_FINISHED: 638 break; 639 case FDEVENT_HANDLED_FINISHED: 640 /* we are done */ 641 642 #if 0 643 log_error_write(srv, __FILE__, __LINE__, "ddss", con->fd, hctx->fd, connection_get_state(con->state), "finished"); 644 #endif 645 cgi_connection_close(srv, hctx); 646 647 /* if we get a IN|HUP and have read everything don't exec the close twice */ 648 return HANDLER_FINISHED; 649 case FDEVENT_HANDLED_ERROR: 650 /* Send an error if we haven't sent any data yet */ 651 if (0 == con->file_started) { 652 connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST); 653 con->http_status = 500; 654 con->mode = DIRECT; 655 } else { 656 con->file_finished = 1; 657 } 658 659 log_error_write(srv, __FILE__, __LINE__, "s", "demuxer failed: "); 660 break; 661 } 662 } 663 664 if (revents & FDEVENT_OUT) { 665 /* nothing to do */ 666 } 667 668 /* perhaps this issue is already handled */ 669 if (revents & FDEVENT_HUP) { 670 /* check if we still have a unfinished header package which is a body in reality */ 671 if (con->file_started == 0 && 672 hctx->response_header->used) { 673 con->file_started = 1; 674 http_chunk_append_mem(srv, con, hctx->response_header->ptr, hctx->response_header->used); 675 joblist_append(srv, con); 676 } 677 678 if (con->file_finished == 0) { 679 http_chunk_append_mem(srv, con, NULL, 0); 680 joblist_append(srv, con); 681 } 682 683 con->file_finished = 1; 684 685 if (chunkqueue_is_empty(con->write_queue)) { 686 /* there is nothing left to write */ 687 connection_set_state(srv, con, CON_STATE_RESPONSE_END); 688 } else { 689 /* used the write-handler to finish the request on demand */ 690 691 } 692 693 # if 0 694 log_error_write(srv, __FILE__, __LINE__, "sddd", "got HUP from cgi", con->fd, hctx->fd, revents); 695 # endif 696 697 /* rtsigs didn't liked the close */ 698 cgi_connection_close(srv, hctx); 699 } else if (revents & FDEVENT_ERR) { 700 con->file_finished = 1; 701 702 /* kill all connections to the cgi process */ 703 cgi_connection_close(srv, hctx); 704 #if 1 705 log_error_write(srv, __FILE__, __LINE__, "s", "cgi-FDEVENT_ERR"); 706 #endif 707 return HANDLER_ERROR; 708 } 709 710 return HANDLER_FINISHED; 711 } 712 713 714 static int cgi_env_add(char_array *env, const char *key, size_t key_len, const char *val, size_t val_len) { 715 char *dst; 716 717 if (!key || !val) return -1; 718 719 dst = malloc(key_len + val_len + 2); 720 memcpy(dst, key, key_len); 721 dst[key_len] = '='; 722 memcpy(dst + key_len + 1, val, val_len); 723 dst[key_len + 1 + val_len] = '\0'; 724 725 if (env->size == 0) { 726 env->size = 16; 727 env->ptr = malloc(env->size * sizeof(*env->ptr)); 728 } else if (env->size == env->used) { 729 env->size += 16; 730 env->ptr = realloc(env->ptr, env->size * sizeof(*env->ptr)); 731 } 732 733 env->ptr[env->used++] = dst; 734 735 return 0; 736 } 737 738 static int cgi_create_env(server *srv, connection *con, plugin_data *p, buffer *cgi_handler) { 739 pid_t pid; 740 741 #ifdef HAVE_IPV6 742 char b2[INET6_ADDRSTRLEN + 1]; 743 #endif 744 745 int to_cgi_fds[2]; 746 int from_cgi_fds[2]; 747 struct stat st; 748 749 #ifndef __WIN32 750 751 if (cgi_handler->used > 1) { 752 /* stat the exec file */ 753 if (-1 == (stat(cgi_handler->ptr, &st))) { 754 log_error_write(srv, __FILE__, __LINE__, "sbss", 755 "stat for cgi-handler", cgi_handler, 756 "failed:", strerror(errno)); 757 return -1; 758 } 759 } 760 761 if (pipe(to_cgi_fds)) { 762 log_error_write(srv, __FILE__, __LINE__, "ss", "pipe failed:", strerror(errno)); 763 return -1; 764 } 765 766 if (pipe(from_cgi_fds)) { 767 close(to_cgi_fds[0]); 768 close(to_cgi_fds[1]); 769 log_error_write(srv, __FILE__, __LINE__, "ss", "pipe failed:", strerror(errno)); 770 return -1; 771 } 772 773 /* fork, execve */ 774 switch (pid = fork()) { 775 case 0: { 776 /* child */ 777 char **args; 778 int argc; 779 int i = 0; 780 char buf[32]; 781 size_t n; 782 char_array env; 783 char *c; 784 const char *s; 785 server_socket *srv_sock = con->srv_socket; 786 787 /* move stdout to from_cgi_fd[1] */ 788 close(STDOUT_FILENO); 789 dup2(from_cgi_fds[1], STDOUT_FILENO); 790 close(from_cgi_fds[1]); 791 /* not needed */ 792 close(from_cgi_fds[0]); 793 794 /* move the stdin to to_cgi_fd[0] */ 795 close(STDIN_FILENO); 796 dup2(to_cgi_fds[0], STDIN_FILENO); 797 close(to_cgi_fds[0]); 798 /* not needed */ 799 close(to_cgi_fds[1]); 800 801 /* create environment */ 802 env.ptr = NULL; 803 env.size = 0; 804 env.used = 0; 805 806 if (buffer_is_empty(con->conf.server_tag)) { 807 cgi_env_add(&env, CONST_STR_LEN("SERVER_SOFTWARE"), CONST_STR_LEN(PACKAGE_DESC)); 808 } else { 809 cgi_env_add(&env, CONST_STR_LEN("SERVER_SOFTWARE"), CONST_BUF_LEN(con->conf.server_tag)); 810 } 811 812 if (!buffer_is_empty(con->server_name)) { 813 size_t len = con->server_name->used - 1; 814 815 if (con->server_name->ptr[0] == '[') { 816 const char *colon = strstr(con->server_name->ptr, "]:"); 817 if (colon) len = (colon + 1) - con->server_name->ptr; 818 } else { 819 const char *colon = strchr(con->server_name->ptr, ':'); 820 if (colon) len = colon - con->server_name->ptr; 821 } 822 823 cgi_env_add(&env, CONST_STR_LEN("SERVER_NAME"), con->server_name->ptr, len); 824 } else { 825 #ifdef HAVE_IPV6 826 s = inet_ntop(srv_sock->addr.plain.sa_family, 827 srv_sock->addr.plain.sa_family == AF_INET6 ? 828 (const void *) &(srv_sock->addr.ipv6.sin6_addr) : 829 (const void *) &(srv_sock->addr.ipv4.sin_addr), 830 b2, sizeof(b2)-1); 831 #else 832 s = inet_ntoa(srv_sock->addr.ipv4.sin_addr); 833 #endif 834 cgi_env_add(&env, CONST_STR_LEN("SERVER_NAME"), s, strlen(s)); 835 } 836 cgi_env_add(&env, CONST_STR_LEN("GATEWAY_INTERFACE"), CONST_STR_LEN("CGI/1.1")); 837 838 s = get_http_version_name(con->request.http_version); 839 840 cgi_env_add(&env, CONST_STR_LEN("SERVER_PROTOCOL"), s, strlen(s)); 841 842 LI_ltostr(buf, 843 #ifdef HAVE_IPV6 844 ntohs(srv_sock->addr.plain.sa_family == AF_INET6 ? srv_sock->addr.ipv6.sin6_port : srv_sock->addr.ipv4.sin_port) 845 #else 846 ntohs(srv_sock->addr.ipv4.sin_port) 847 #endif 848 ); 849 cgi_env_add(&env, CONST_STR_LEN("SERVER_PORT"), buf, strlen(buf)); 850 851 switch (srv_sock->addr.plain.sa_family) { 852 #ifdef HAVE_IPV6 853 case AF_INET6: 854 s = inet_ntop(srv_sock->addr.plain.sa_family, 855 (const void *) &(srv_sock->addr.ipv6.sin6_addr), 856 b2, sizeof(b2)-1); 857 break; 858 case AF_INET: 859 s = inet_ntop(srv_sock->addr.plain.sa_family, 860 (const void *) &(srv_sock->addr.ipv4.sin_addr), 861 b2, sizeof(b2)-1); 862 break; 863 #else 864 case AF_INET: 865 s = inet_ntoa(srv_sock->addr.ipv4.sin_addr); 866 break; 867 #endif 868 default: 869 s = ""; 870 break; 871 } 872 cgi_env_add(&env, CONST_STR_LEN("SERVER_ADDR"), s, strlen(s)); 873 874 s = get_http_method_name(con->request.http_method); 875 cgi_env_add(&env, CONST_STR_LEN("REQUEST_METHOD"), s, strlen(s)); 876 877 if (!buffer_is_empty(con->request.pathinfo)) { 878 cgi_env_add(&env, CONST_STR_LEN("PATH_INFO"), CONST_BUF_LEN(con->request.pathinfo)); 879 } 880 cgi_env_add(&env, CONST_STR_LEN("REDIRECT_STATUS"), CONST_STR_LEN("200")); 881 if (!buffer_is_empty(con->uri.query)) { 882 cgi_env_add(&env, CONST_STR_LEN("QUERY_STRING"), CONST_BUF_LEN(con->uri.query)); 883 } 884 if (!buffer_is_empty(con->request.orig_uri)) { 885 cgi_env_add(&env, CONST_STR_LEN("REQUEST_URI"), CONST_BUF_LEN(con->request.orig_uri)); 886 } 887 888 889 switch (con->dst_addr.plain.sa_family) { 890 #ifdef HAVE_IPV6 891 case AF_INET6: 892 s = inet_ntop(con->dst_addr.plain.sa_family, 893 (const void *) &(con->dst_addr.ipv6.sin6_addr), 894 b2, sizeof(b2)-1); 895 break; 896 case AF_INET: 897 s = inet_ntop(con->dst_addr.plain.sa_family, 898 (const void *) &(con->dst_addr.ipv4.sin_addr), 899 b2, sizeof(b2)-1); 900 break; 901 #else 902 case AF_INET: 903 s = inet_ntoa(con->dst_addr.ipv4.sin_addr); 904 break; 905 #endif 906 default: 907 s = ""; 908 break; 909 } 910 cgi_env_add(&env, CONST_STR_LEN("REMOTE_ADDR"), s, strlen(s)); 911 912 LI_ltostr(buf, 913 #ifdef HAVE_IPV6 914 ntohs(con->dst_addr.plain.sa_family == AF_INET6 ? con->dst_addr.ipv6.sin6_port : con->dst_addr.ipv4.sin_port) 915 #else 916 ntohs(con->dst_addr.ipv4.sin_port) 917 #endif 918 ); 919 cgi_env_add(&env, CONST_STR_LEN("REMOTE_PORT"), buf, strlen(buf)); 920 921 if (!buffer_is_empty(con->authed_user)) { 922 cgi_env_add(&env, CONST_STR_LEN("REMOTE_USER"), 923 CONST_BUF_LEN(con->authed_user)); 924 } 925 926 #ifdef USE_OPENSSL 927 if (srv_sock->is_ssl) { 928 cgi_env_add(&env, CONST_STR_LEN("HTTPS"), CONST_STR_LEN("on")); 929 } 930 #endif 931 932 /* request.content_length < SSIZE_MAX, see request.c */ 933 LI_ltostr(buf, con->request.content_length); 934 cgi_env_add(&env, CONST_STR_LEN("CONTENT_LENGTH"), buf, strlen(buf)); 935 cgi_env_add(&env, CONST_STR_LEN("SCRIPT_FILENAME"), CONST_BUF_LEN(con->physical.path)); 936 cgi_env_add(&env, CONST_STR_LEN("SCRIPT_NAME"), CONST_BUF_LEN(con->uri.path)); 937 cgi_env_add(&env, CONST_STR_LEN("DOCUMENT_ROOT"), CONST_BUF_LEN(con->physical.basedir)); 938 939 /* for valgrind */ 940 if (NULL != (s = getenv("LD_PRELOAD"))) { 941 cgi_env_add(&env, CONST_STR_LEN("LD_PRELOAD"), s, strlen(s)); 942 } 943 944 if (NULL != (s = getenv("LD_LIBRARY_PATH"))) { 945 cgi_env_add(&env, CONST_STR_LEN("LD_LIBRARY_PATH"), s, strlen(s)); 946 } 947 #ifdef __CYGWIN__ 948 /* CYGWIN needs SYSTEMROOT */ 949 if (NULL != (s = getenv("SYSTEMROOT"))) { 950 cgi_env_add(&env, CONST_STR_LEN("SYSTEMROOT"), s, strlen(s)); 951 } 952 #endif 953 954 for (n = 0; n < con->request.headers->used; n++) { 955 data_string *ds; 956 957 ds = (data_string *)con->request.headers->data[n]; 958 959 if (ds->value->used && ds->key->used) { 960 size_t j; 961 962 buffer_reset(p->tmp_buf); 963 964 if (0 != strcasecmp(ds->key->ptr, "CONTENT-TYPE")) { 965 buffer_copy_string_len(p->tmp_buf, CONST_STR_LEN("HTTP_")); 966 p->tmp_buf->used--; /* strip \0 after HTTP_ */ 967 } 968 969 buffer_prepare_append(p->tmp_buf, ds->key->used + 2); 970 971 for (j = 0; j < ds->key->used - 1; j++) { 972 char cr = '_'; 973 if (light_isalpha(ds->key->ptr[j])) { 974 /* upper-case */ 975 cr = ds->key->ptr[j] & ~32; 976 } else if (light_isdigit(ds->key->ptr[j])) { 977 /* copy */ 978 cr = ds->key->ptr[j]; 979 } 980 p->tmp_buf->ptr[p->tmp_buf->used++] = cr; 981 } 982 p->tmp_buf->ptr[p->tmp_buf->used++] = '\0'; 983 984 cgi_env_add(&env, CONST_BUF_LEN(p->tmp_buf), CONST_BUF_LEN(ds->value)); 985 } 986 } 987 988 for (n = 0; n < con->environment->used; n++) { 989 data_string *ds; 990 991 ds = (data_string *)con->environment->data[n]; 992 993 if (ds->value->used && ds->key->used) { 994 size_t j; 995 996 buffer_reset(p->tmp_buf); 997 998 buffer_prepare_append(p->tmp_buf, ds->key->used + 2); 999 1000 for (j = 0; j < ds->key->used - 1; j++) { 1001 char cr = '_'; 1002 if (light_isalpha(ds->key->ptr[j])) { 1003 /* upper-case */ 1004 cr = ds->key->ptr[j] & ~32; 1005 } else if (light_isdigit(ds->key->ptr[j])) { 1006 /* copy */ 1007 cr = ds->key->ptr[j]; 1008 } 1009 p->tmp_buf->ptr[p->tmp_buf->used++] = cr; 1010 } 1011 p->tmp_buf->ptr[p->tmp_buf->used++] = '\0'; 1012 1013 cgi_env_add(&env, CONST_BUF_LEN(p->tmp_buf), CONST_BUF_LEN(ds->value)); 1014 } 1015 } 1016 1017 if (env.size == env.used) { 1018 env.size += 16; 1019 env.ptr = realloc(env.ptr, env.size * sizeof(*env.ptr)); 1020 } 1021 1022 env.ptr[env.used] = NULL; 1023 1024 /* set up args */ 1025 argc = 3; 1026 args = malloc(sizeof(*args) * argc); 1027 i = 0; 1028 1029 if (cgi_handler->used > 1) { 1030 args[i++] = cgi_handler->ptr; 1031 } 1032 args[i++] = con->physical.path->ptr; 1033 args[i ] = NULL; 1034 1035 /* search for the last / */ 1036 if (NULL != (c = strrchr(con->physical.path->ptr, '/'))) { 1037 *c = '\0'; 1038 1039 /* change to the physical directory */ 1040 if (-1 == chdir(con->physical.path->ptr)) { 1041 log_error_write(srv, __FILE__, __LINE__, "ssb", "chdir failed:", strerror(errno), con->physical.path); 1042 } 1043 *c = '/'; 1044 } 1045 1046 /* we don't need the client socket */ 1047 for (i = 3; i < 256; i++) { 1048 if (i != srv->errorlog_fd) close(i); 1049 } 1050 1051 /* exec the cgi */ 1052 execve(args[0], args, env.ptr); 1053 1054 /* log_error_write(srv, __FILE__, __LINE__, "sss", "CGI failed:", strerror(errno), args[0]); */ 1055 1056 /* */ 1057 SEGFAULT(); 1058 break; 1059 } 1060 case -1: 1061 /* error */ 1062 log_error_write(srv, __FILE__, __LINE__, "ss", "fork failed:", strerror(errno)); 1063 close(from_cgi_fds[0]); 1064 close(from_cgi_fds[1]); 1065 close(to_cgi_fds[0]); 1066 close(to_cgi_fds[1]); 1067 return -1; 1068 break; 1069 default: { 1070 handler_ctx *hctx; 1071 /* father */ 1072 1073 close(from_cgi_fds[1]); 1074 close(to_cgi_fds[0]); 1075 1076 if (con->request.content_length) { 1077 chunkqueue *cq = con->request_content_queue; 1078 chunk *c; 1079 1080 assert(chunkqueue_length(cq) == (off_t)con->request.content_length); 1081 1082 /* there is content to send */ 1083 for (c = cq->first; c; c = cq->first) { 1084 int r = 0; 1085 1086 /* copy all chunks */ 1087 switch(c->type) { 1088 case FILE_CHUNK: 1089 1090 if (c->file.mmap.start == MAP_FAILED) { 1091 if (-1 == c->file.fd && /* open the file if not already open */ 1092 -1 == (c->file.fd = open(c->file.name->ptr, O_RDONLY))) { 1093 log_error_write(srv, __FILE__, __LINE__, "ss", "open failed: ", strerror(errno)); 1094 1095 close(from_cgi_fds[0]); 1096 close(to_cgi_fds[1]); 1097 return -1; 1098 } 1099 1100 c->file.mmap.length = c->file.length; 1101 1102 if (MAP_FAILED == (c->file.mmap.start = mmap(NULL, c->file.mmap.length, PROT_READ, MAP_SHARED, c->file.fd, 0))) { 1103 log_error_write(srv, __FILE__, __LINE__, "ssbd", "mmap failed: ", 1104 strerror(errno), c->file.name, c->file.fd); 1105 1106 close(from_cgi_fds[0]); 1107 close(to_cgi_fds[1]); 1108 return -1; 1109 } 1110 1111 close(c->file.fd); 1112 c->file.fd = -1; 1113 1114 /* chunk_reset() or chunk_free() will cleanup for us */ 1115 } 1116 1117 if ((r = write(to_cgi_fds[1], c->file.mmap.start + c->offset, c->file.length - c->offset)) < 0) { 1118 switch(errno) { 1119 case ENOSPC: 1120 con->http_status = 507; 1121 break; 1122 case EINTR: 1123 continue; 1124 default: 1125 con->http_status = 403; 1126 break; 1127 } 1128 } 1129 break; 1130 case MEM_CHUNK: 1131 if ((r = write(to_cgi_fds[1], c->mem->ptr + c->offset, c->mem->used - c->offset - 1)) < 0) { 1132 switch(errno) { 1133 case ENOSPC: 1134 con->http_status = 507; 1135 break; 1136 case EINTR: 1137 continue; 1138 default: 1139 con->http_status = 403; 1140 break; 1141 } 1142 } 1143 break; 1144 case UNUSED_CHUNK: 1145 break; 1146 } 1147 1148 if (r > 0) { 1149 c->offset += r; 1150 cq->bytes_out += r; 1151 } else { 1152 log_error_write(srv, __FILE__, __LINE__, "ss", "write() failed due to: ", strerror(errno)); 1153 con->http_status = 500; 1154 break; 1155 } 1156 chunkqueue_remove_finished_chunks(cq); 1157 } 1158 } 1159 1160 close(to_cgi_fds[1]); 1161 1162 /* register PID and wait for them asyncronously */ 1163 con->mode = p->id; 1164 buffer_reset(con->physical.path); 1165 1166 hctx = cgi_handler_ctx_init(); 1167 1168 hctx->remote_conn = con; 1169 hctx->plugin_data = p; 1170 hctx->pid = pid; 1171 hctx->fd = from_cgi_fds[0]; 1172 hctx->fde_ndx = -1; 1173 1174 con->plugin_ctx[p->id] = hctx; 1175 1176 fdevent_register(srv->ev, hctx->fd, cgi_handle_fdevent, hctx); 1177 fdevent_event_set(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN); 1178 1179 if (-1 == fdevent_fcntl_set(srv->ev, hctx->fd)) { 1180 log_error_write(srv, __FILE__, __LINE__, "ss", "fcntl failed: ", strerror(errno)); 1181 1182 fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd); 1183 fdevent_unregister(srv->ev, hctx->fd); 1184 1185 log_error_write(srv, __FILE__, __LINE__, "sd", "cgi close:", hctx->fd); 1186 1187 close(hctx->fd); 1188 1189 cgi_handler_ctx_free(hctx); 1190 1191 con->plugin_ctx[p->id] = NULL; 1192 1193 return -1; 1194 } 1195 1196 break; 1197 } 1198 } 1199 1200 return 0; 1201 #else 1202 return -1; 1203 #endif 1204 } 1205 1206 #define PATCH(x) \ 1207 p->conf.x = s->x; 1208 static int mod_cgi_patch_connection(server *srv, connection *con, plugin_data *p) { 1209 size_t i, j; 1210 plugin_config *s = p->config_storage[0]; 1211 1212 PATCH(cgi); 1213 PATCH(execute_x_only); 1214 1215 /* skip the first, the global context */ 1216 for (i = 1; i < srv->config_context->used; i++) { 1217 data_config *dc = (data_config *)srv->config_context->data[i]; 1218 s = p->config_storage[i]; 1219 1220 /* condition didn't match */ 1221 if (!config_check_cond(srv, con, dc)) continue; 1222 1223 /* merge config */ 1224 for (j = 0; j < dc->value->used; j++) { 1225 data_unset *du = dc->value->data[j]; 1226 1227 if (buffer_is_equal_string(du->key, CONST_STR_LEN("cgi.assign"))) { 1228 PATCH(cgi); 1229 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("cgi.execute-x-only"))) { 1230 PATCH(execute_x_only); 1231 } 1232 } 1233 } 1234 1235 return 0; 1236 } 1237 #undef PATCH 1238 1239 URIHANDLER_FUNC(cgi_is_handled) { 1240 size_t k, s_len; 1241 plugin_data *p = p_d; 1242 buffer *fn = con->physical.path; 1243 stat_cache_entry *sce = NULL; 1244 1245 if (con->mode != DIRECT) return HANDLER_GO_ON; 1246 1247 if (fn->used == 0) return HANDLER_GO_ON; 1248 1249 mod_cgi_patch_connection(srv, con, p); 1250 1251 if (HANDLER_ERROR == stat_cache_get_entry(srv, con, con->physical.path, &sce)) return HANDLER_GO_ON; 1252 if (!S_ISREG(sce->st.st_mode)) return HANDLER_GO_ON; 1253 if (p->conf.execute_x_only == 1 && (sce->st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) == 0) return HANDLER_GO_ON; 1254 1255 s_len = fn->used - 1; 1256 1257 for (k = 0; k < p->conf.cgi->used; k++) { 1258 data_string *ds = (data_string *)p->conf.cgi->data[k]; 1259 size_t ct_len = ds->key->used - 1; 1260 1261 if (ds->key->used == 0) continue; 1262 if (s_len < ct_len) continue; 1263 1264 if (0 == strncmp(fn->ptr + s_len - ct_len, ds->key->ptr, ct_len)) { 1265 if (cgi_create_env(srv, con, p, ds->value)) { 1266 con->mode = DIRECT; 1267 con->http_status = 500; 1268 1269 buffer_reset(con->physical.path); 1270 return HANDLER_FINISHED; 1271 } 1272 /* one handler is enough for the request */ 1273 break; 1274 } 1275 } 1276 1277 return HANDLER_GO_ON; 1278 } 1279 1280 TRIGGER_FUNC(cgi_trigger) { 1281 plugin_data *p = p_d; 1282 size_t ndx; 1283 /* the trigger handle only cares about lonely PID which we have to wait for */ 1284 #ifndef __WIN32 1285 1286 for (ndx = 0; ndx < p->cgi_pid.used; ndx++) { 1287 int status; 1288 1289 switch(waitpid(p->cgi_pid.ptr[ndx], &status, WNOHANG)) { 1290 case 0: 1291 /* not finished yet */ 1292 #if 0 1293 log_error_write(srv, __FILE__, __LINE__, "sd", "(debug) child isn't done yet, pid:", p->cgi_pid.ptr[ndx]); 1294 #endif 1295 break; 1296 case -1: 1297 if (errno == ECHILD) { 1298 /* someone else called waitpid... remove the pid to stop looping the error each time */ 1299 log_error_write(srv, __FILE__, __LINE__, "s", "cgi child vanished, probably someone else called waitpid"); 1300 1301 cgi_pid_del(srv, p, p->cgi_pid.ptr[ndx]); 1302 ndx--; 1303 continue; 1304 } 1305 1306 log_error_write(srv, __FILE__, __LINE__, "ss", "waitpid failed: ", strerror(errno)); 1307 1308 return HANDLER_ERROR; 1309 default: 1310 1311 if (WIFEXITED(status)) { 1312 #if 0 1313 log_error_write(srv, __FILE__, __LINE__, "sd", "(debug) cgi exited fine, pid:", p->cgi_pid.ptr[ndx]); 1314 #endif 1315 } else if (WIFSIGNALED(status)) { 1316 /* FIXME: what if we killed the CGI script with a kill(..., SIGTERM) ? 1317 */ 1318 if (WTERMSIG(status) != SIGTERM) { 1319 log_error_write(srv, __FILE__, __LINE__, "sd", "cleaning up CGI: process died with signal", WTERMSIG(status)); 1320 } 1321 } else { 1322 log_error_write(srv, __FILE__, __LINE__, "s", "cleaning up CGI: ended unexpectedly"); 1323 } 1324 1325 cgi_pid_del(srv, p, p->cgi_pid.ptr[ndx]); 1326 /* del modified the buffer structure 1327 * and copies the last entry to the current one 1328 * -> recheck the current index 1329 */ 1330 ndx--; 1331 } 1332 } 1333 #endif 1334 return HANDLER_GO_ON; 1335 } 1336 1337 /* 1338 * - HANDLER_GO_ON : not our job 1339 * - HANDLER_FINISHED: got response header 1340 * - HANDLER_WAIT_FOR_EVENT: waiting for response header 1341 */ 1342 SUBREQUEST_FUNC(mod_cgi_handle_subrequest) { 1343 int status; 1344 plugin_data *p = p_d; 1345 handler_ctx *hctx = con->plugin_ctx[p->id]; 1346 1347 if (con->mode != p->id) return HANDLER_GO_ON; 1348 if (NULL == hctx) return HANDLER_GO_ON; 1349 1350 #if 0 1351 log_error_write(srv, __FILE__, __LINE__, "sdd", "subrequest, pid =", hctx, hctx->pid); 1352 #endif 1353 1354 if (hctx->pid == 0) { 1355 /* cgi already dead */ 1356 if (!con->file_started) return HANDLER_WAIT_FOR_EVENT; 1357 return HANDLER_FINISHED; 1358 } 1359 1360 #ifndef __WIN32 1361 switch(waitpid(hctx->pid, &status, WNOHANG)) { 1362 case 0: 1363 /* we only have for events here if we don't have the header yet, 1364 * otherwise the event-handler will send us the incoming data */ 1365 if (con->file_started) return HANDLER_FINISHED; 1366 1367 return HANDLER_WAIT_FOR_EVENT; 1368 case -1: 1369 if (errno == EINTR) return HANDLER_WAIT_FOR_EVENT; 1370 1371 if (errno == ECHILD && con->file_started == 0) { 1372 /* 1373 * second round but still not response 1374 */ 1375 return HANDLER_WAIT_FOR_EVENT; 1376 } 1377 1378 log_error_write(srv, __FILE__, __LINE__, "ss", "waitpid failed: ", strerror(errno)); 1379 con->mode = DIRECT; 1380 con->http_status = 500; 1381 1382 hctx->pid = 0; 1383 1384 fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd); 1385 fdevent_unregister(srv->ev, hctx->fd); 1386 1387 if (close(hctx->fd)) { 1388 log_error_write(srv, __FILE__, __LINE__, "sds", "cgi close failed ", hctx->fd, strerror(errno)); 1389 } 1390 1391 cgi_handler_ctx_free(hctx); 1392 1393 con->plugin_ctx[p->id] = NULL; 1394 1395 return HANDLER_FINISHED; 1396 default: 1397 /* cgi process exited 1398 */ 1399 1400 hctx->pid = 0; 1401 1402 /* we already have response headers? just continue */ 1403 if (con->file_started) return HANDLER_FINISHED; 1404 1405 if (WIFEXITED(status)) { 1406 /* clean exit - just continue */ 1407 return HANDLER_WAIT_FOR_EVENT; 1408 } 1409 1410 /* cgi proc died, and we didn't get any data yet - send error message and close cgi con */ 1411 log_error_write(srv, __FILE__, __LINE__, "s", "cgi died ?"); 1412 1413 con->http_status = 500; 1414 con->mode = DIRECT; 1415 1416 fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd); 1417 fdevent_unregister(srv->ev, hctx->fd); 1418 1419 if (close(hctx->fd)) { 1420 log_error_write(srv, __FILE__, __LINE__, "sds", "cgi close failed ", hctx->fd, strerror(errno)); 1421 } 1422 1423 cgi_handler_ctx_free(hctx); 1424 1425 con->plugin_ctx[p->id] = NULL; 1426 return HANDLER_FINISHED; 1427 } 1428 #else 1429 return HANDLER_ERROR; 1430 #endif 1431 } 1432 1433 1434 int mod_cgi_plugin_init(plugin *p); 1435 int mod_cgi_plugin_init(plugin *p) { 1436 p->version = LIGHTTPD_VERSION_ID; 1437 p->name = buffer_init_string("cgi"); 1438 1439 p->connection_reset = cgi_connection_close_callback; 1440 p->handle_subrequest_start = cgi_is_handled; 1441 p->handle_subrequest = mod_cgi_handle_subrequest; 1442 #if 0 1443 p->handle_fdevent = cgi_handle_fdevent; 1444 #endif 1445 p->handle_trigger = cgi_trigger; 1446 p->init = mod_cgi_init; 1447 p->cleanup = mod_cgi_free; 1448 p->set_defaults = mod_fastcgi_set_defaults; 1449 1450 p->data = NULL; 1451 1452 return 0; 1453 } 1454