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