Annotation of embedaddon/lighttpd/src/mod_cgi.c, revision 1.1
1.1 ! misho 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_equal_caseless_string(con->uri.scheme, CONST_STR_LEN("https"))) {
! 922: cgi_env_add(&env, CONST_STR_LEN("HTTPS"), CONST_STR_LEN("on"));
! 923: }
! 924:
! 925: /* request.content_length < SSIZE_MAX, see request.c */
! 926: LI_ltostr(buf, con->request.content_length);
! 927: cgi_env_add(&env, CONST_STR_LEN("CONTENT_LENGTH"), buf, strlen(buf));
! 928: cgi_env_add(&env, CONST_STR_LEN("SCRIPT_FILENAME"), CONST_BUF_LEN(con->physical.path));
! 929: cgi_env_add(&env, CONST_STR_LEN("SCRIPT_NAME"), CONST_BUF_LEN(con->uri.path));
! 930: cgi_env_add(&env, CONST_STR_LEN("DOCUMENT_ROOT"), CONST_BUF_LEN(con->physical.basedir));
! 931:
! 932: /* for valgrind */
! 933: if (NULL != (s = getenv("LD_PRELOAD"))) {
! 934: cgi_env_add(&env, CONST_STR_LEN("LD_PRELOAD"), s, strlen(s));
! 935: }
! 936:
! 937: if (NULL != (s = getenv("LD_LIBRARY_PATH"))) {
! 938: cgi_env_add(&env, CONST_STR_LEN("LD_LIBRARY_PATH"), s, strlen(s));
! 939: }
! 940: #ifdef __CYGWIN__
! 941: /* CYGWIN needs SYSTEMROOT */
! 942: if (NULL != (s = getenv("SYSTEMROOT"))) {
! 943: cgi_env_add(&env, CONST_STR_LEN("SYSTEMROOT"), s, strlen(s));
! 944: }
! 945: #endif
! 946:
! 947: for (n = 0; n < con->request.headers->used; n++) {
! 948: data_string *ds;
! 949:
! 950: ds = (data_string *)con->request.headers->data[n];
! 951:
! 952: if (ds->value->used && ds->key->used) {
! 953: size_t j;
! 954:
! 955: buffer_reset(p->tmp_buf);
! 956:
! 957: if (0 != strcasecmp(ds->key->ptr, "CONTENT-TYPE")) {
! 958: buffer_copy_string_len(p->tmp_buf, CONST_STR_LEN("HTTP_"));
! 959: p->tmp_buf->used--; /* strip \0 after HTTP_ */
! 960: }
! 961:
! 962: buffer_prepare_append(p->tmp_buf, ds->key->used + 2);
! 963:
! 964: for (j = 0; j < ds->key->used - 1; j++) {
! 965: char cr = '_';
! 966: if (light_isalpha(ds->key->ptr[j])) {
! 967: /* upper-case */
! 968: cr = ds->key->ptr[j] & ~32;
! 969: } else if (light_isdigit(ds->key->ptr[j])) {
! 970: /* copy */
! 971: cr = ds->key->ptr[j];
! 972: }
! 973: p->tmp_buf->ptr[p->tmp_buf->used++] = cr;
! 974: }
! 975: p->tmp_buf->ptr[p->tmp_buf->used++] = '\0';
! 976:
! 977: cgi_env_add(&env, CONST_BUF_LEN(p->tmp_buf), CONST_BUF_LEN(ds->value));
! 978: }
! 979: }
! 980:
! 981: for (n = 0; n < con->environment->used; n++) {
! 982: data_string *ds;
! 983:
! 984: ds = (data_string *)con->environment->data[n];
! 985:
! 986: if (ds->value->used && ds->key->used) {
! 987: size_t j;
! 988:
! 989: buffer_reset(p->tmp_buf);
! 990:
! 991: buffer_prepare_append(p->tmp_buf, ds->key->used + 2);
! 992:
! 993: for (j = 0; j < ds->key->used - 1; j++) {
! 994: char cr = '_';
! 995: if (light_isalpha(ds->key->ptr[j])) {
! 996: /* upper-case */
! 997: cr = ds->key->ptr[j] & ~32;
! 998: } else if (light_isdigit(ds->key->ptr[j])) {
! 999: /* copy */
! 1000: cr = ds->key->ptr[j];
! 1001: }
! 1002: p->tmp_buf->ptr[p->tmp_buf->used++] = cr;
! 1003: }
! 1004: p->tmp_buf->ptr[p->tmp_buf->used++] = '\0';
! 1005:
! 1006: cgi_env_add(&env, CONST_BUF_LEN(p->tmp_buf), CONST_BUF_LEN(ds->value));
! 1007: }
! 1008: }
! 1009:
! 1010: if (env.size == env.used) {
! 1011: env.size += 16;
! 1012: env.ptr = realloc(env.ptr, env.size * sizeof(*env.ptr));
! 1013: }
! 1014:
! 1015: env.ptr[env.used] = NULL;
! 1016:
! 1017: /* set up args */
! 1018: argc = 3;
! 1019: args = malloc(sizeof(*args) * argc);
! 1020: i = 0;
! 1021:
! 1022: if (cgi_handler->used > 1) {
! 1023: args[i++] = cgi_handler->ptr;
! 1024: }
! 1025: args[i++] = con->physical.path->ptr;
! 1026: args[i ] = NULL;
! 1027:
! 1028: /* search for the last / */
! 1029: if (NULL != (c = strrchr(con->physical.path->ptr, '/'))) {
! 1030: *c = '\0';
! 1031:
! 1032: /* change to the physical directory */
! 1033: if (-1 == chdir(con->physical.path->ptr)) {
! 1034: log_error_write(srv, __FILE__, __LINE__, "ssb", "chdir failed:", strerror(errno), con->physical.path);
! 1035: }
! 1036: *c = '/';
! 1037: }
! 1038:
! 1039: /* we don't need the client socket */
! 1040: for (i = 3; i < 256; i++) {
! 1041: if (i != srv->errorlog_fd) close(i);
! 1042: }
! 1043:
! 1044: /* exec the cgi */
! 1045: execve(args[0], args, env.ptr);
! 1046:
! 1047: /* log_error_write(srv, __FILE__, __LINE__, "sss", "CGI failed:", strerror(errno), args[0]); */
! 1048:
! 1049: /* */
! 1050: SEGFAULT();
! 1051: break;
! 1052: }
! 1053: case -1:
! 1054: /* error */
! 1055: log_error_write(srv, __FILE__, __LINE__, "ss", "fork failed:", strerror(errno));
! 1056: close(from_cgi_fds[0]);
! 1057: close(from_cgi_fds[1]);
! 1058: close(to_cgi_fds[0]);
! 1059: close(to_cgi_fds[1]);
! 1060: return -1;
! 1061: break;
! 1062: default: {
! 1063: handler_ctx *hctx;
! 1064: /* father */
! 1065:
! 1066: close(from_cgi_fds[1]);
! 1067: close(to_cgi_fds[0]);
! 1068:
! 1069: if (con->request.content_length) {
! 1070: chunkqueue *cq = con->request_content_queue;
! 1071: chunk *c;
! 1072:
! 1073: assert(chunkqueue_length(cq) == (off_t)con->request.content_length);
! 1074:
! 1075: /* there is content to send */
! 1076: for (c = cq->first; c; c = cq->first) {
! 1077: int r = 0;
! 1078:
! 1079: /* copy all chunks */
! 1080: switch(c->type) {
! 1081: case FILE_CHUNK:
! 1082:
! 1083: if (c->file.mmap.start == MAP_FAILED) {
! 1084: if (-1 == c->file.fd && /* open the file if not already open */
! 1085: -1 == (c->file.fd = open(c->file.name->ptr, O_RDONLY))) {
! 1086: log_error_write(srv, __FILE__, __LINE__, "ss", "open failed: ", strerror(errno));
! 1087:
! 1088: close(from_cgi_fds[0]);
! 1089: close(to_cgi_fds[1]);
! 1090: return -1;
! 1091: }
! 1092:
! 1093: c->file.mmap.length = c->file.length;
! 1094:
! 1095: if (MAP_FAILED == (c->file.mmap.start = mmap(NULL, c->file.mmap.length, PROT_READ, MAP_SHARED, c->file.fd, 0))) {
! 1096: log_error_write(srv, __FILE__, __LINE__, "ssbd", "mmap failed: ",
! 1097: strerror(errno), c->file.name, c->file.fd);
! 1098:
! 1099: close(from_cgi_fds[0]);
! 1100: close(to_cgi_fds[1]);
! 1101: return -1;
! 1102: }
! 1103:
! 1104: close(c->file.fd);
! 1105: c->file.fd = -1;
! 1106:
! 1107: /* chunk_reset() or chunk_free() will cleanup for us */
! 1108: }
! 1109:
! 1110: if ((r = write(to_cgi_fds[1], c->file.mmap.start + c->offset, c->file.length - c->offset)) < 0) {
! 1111: switch(errno) {
! 1112: case ENOSPC:
! 1113: con->http_status = 507;
! 1114: break;
! 1115: case EINTR:
! 1116: continue;
! 1117: default:
! 1118: con->http_status = 403;
! 1119: break;
! 1120: }
! 1121: }
! 1122: break;
! 1123: case MEM_CHUNK:
! 1124: if ((r = write(to_cgi_fds[1], c->mem->ptr + c->offset, c->mem->used - c->offset - 1)) < 0) {
! 1125: switch(errno) {
! 1126: case ENOSPC:
! 1127: con->http_status = 507;
! 1128: break;
! 1129: case EINTR:
! 1130: continue;
! 1131: default:
! 1132: con->http_status = 403;
! 1133: break;
! 1134: }
! 1135: }
! 1136: break;
! 1137: case UNUSED_CHUNK:
! 1138: break;
! 1139: }
! 1140:
! 1141: if (r > 0) {
! 1142: c->offset += r;
! 1143: cq->bytes_out += r;
! 1144: } else {
! 1145: log_error_write(srv, __FILE__, __LINE__, "ss", "write() failed due to: ", strerror(errno));
! 1146: con->http_status = 500;
! 1147: break;
! 1148: }
! 1149: chunkqueue_remove_finished_chunks(cq);
! 1150: }
! 1151: }
! 1152:
! 1153: close(to_cgi_fds[1]);
! 1154:
! 1155: /* register PID and wait for them asyncronously */
! 1156: con->mode = p->id;
! 1157: buffer_reset(con->physical.path);
! 1158:
! 1159: hctx = cgi_handler_ctx_init();
! 1160:
! 1161: hctx->remote_conn = con;
! 1162: hctx->plugin_data = p;
! 1163: hctx->pid = pid;
! 1164: hctx->fd = from_cgi_fds[0];
! 1165: hctx->fde_ndx = -1;
! 1166:
! 1167: con->plugin_ctx[p->id] = hctx;
! 1168:
! 1169: fdevent_register(srv->ev, hctx->fd, cgi_handle_fdevent, hctx);
! 1170: fdevent_event_set(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN);
! 1171:
! 1172: if (-1 == fdevent_fcntl_set(srv->ev, hctx->fd)) {
! 1173: log_error_write(srv, __FILE__, __LINE__, "ss", "fcntl failed: ", strerror(errno));
! 1174:
! 1175: fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd);
! 1176: fdevent_unregister(srv->ev, hctx->fd);
! 1177:
! 1178: log_error_write(srv, __FILE__, __LINE__, "sd", "cgi close:", hctx->fd);
! 1179:
! 1180: close(hctx->fd);
! 1181:
! 1182: cgi_handler_ctx_free(hctx);
! 1183:
! 1184: con->plugin_ctx[p->id] = NULL;
! 1185:
! 1186: return -1;
! 1187: }
! 1188:
! 1189: break;
! 1190: }
! 1191: }
! 1192:
! 1193: return 0;
! 1194: #else
! 1195: return -1;
! 1196: #endif
! 1197: }
! 1198:
! 1199: #define PATCH(x) \
! 1200: p->conf.x = s->x;
! 1201: static int mod_cgi_patch_connection(server *srv, connection *con, plugin_data *p) {
! 1202: size_t i, j;
! 1203: plugin_config *s = p->config_storage[0];
! 1204:
! 1205: PATCH(cgi);
! 1206: PATCH(execute_x_only);
! 1207:
! 1208: /* skip the first, the global context */
! 1209: for (i = 1; i < srv->config_context->used; i++) {
! 1210: data_config *dc = (data_config *)srv->config_context->data[i];
! 1211: s = p->config_storage[i];
! 1212:
! 1213: /* condition didn't match */
! 1214: if (!config_check_cond(srv, con, dc)) continue;
! 1215:
! 1216: /* merge config */
! 1217: for (j = 0; j < dc->value->used; j++) {
! 1218: data_unset *du = dc->value->data[j];
! 1219:
! 1220: if (buffer_is_equal_string(du->key, CONST_STR_LEN("cgi.assign"))) {
! 1221: PATCH(cgi);
! 1222: } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("cgi.execute-x-only"))) {
! 1223: PATCH(execute_x_only);
! 1224: }
! 1225: }
! 1226: }
! 1227:
! 1228: return 0;
! 1229: }
! 1230: #undef PATCH
! 1231:
! 1232: URIHANDLER_FUNC(cgi_is_handled) {
! 1233: size_t k, s_len;
! 1234: plugin_data *p = p_d;
! 1235: buffer *fn = con->physical.path;
! 1236: stat_cache_entry *sce = NULL;
! 1237:
! 1238: if (con->mode != DIRECT) return HANDLER_GO_ON;
! 1239:
! 1240: if (fn->used == 0) return HANDLER_GO_ON;
! 1241:
! 1242: mod_cgi_patch_connection(srv, con, p);
! 1243:
! 1244: if (HANDLER_ERROR == stat_cache_get_entry(srv, con, con->physical.path, &sce)) return HANDLER_GO_ON;
! 1245: if (!S_ISREG(sce->st.st_mode)) return HANDLER_GO_ON;
! 1246: if (p->conf.execute_x_only == 1 && (sce->st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) == 0) return HANDLER_GO_ON;
! 1247:
! 1248: s_len = fn->used - 1;
! 1249:
! 1250: for (k = 0; k < p->conf.cgi->used; k++) {
! 1251: data_string *ds = (data_string *)p->conf.cgi->data[k];
! 1252: size_t ct_len = ds->key->used - 1;
! 1253:
! 1254: if (ds->key->used == 0) continue;
! 1255: if (s_len < ct_len) continue;
! 1256:
! 1257: if (0 == strncmp(fn->ptr + s_len - ct_len, ds->key->ptr, ct_len)) {
! 1258: if (cgi_create_env(srv, con, p, ds->value)) {
! 1259: con->mode = DIRECT;
! 1260: con->http_status = 500;
! 1261:
! 1262: buffer_reset(con->physical.path);
! 1263: return HANDLER_FINISHED;
! 1264: }
! 1265: /* one handler is enough for the request */
! 1266: break;
! 1267: }
! 1268: }
! 1269:
! 1270: return HANDLER_GO_ON;
! 1271: }
! 1272:
! 1273: TRIGGER_FUNC(cgi_trigger) {
! 1274: plugin_data *p = p_d;
! 1275: size_t ndx;
! 1276: /* the trigger handle only cares about lonely PID which we have to wait for */
! 1277: #ifndef __WIN32
! 1278:
! 1279: for (ndx = 0; ndx < p->cgi_pid.used; ndx++) {
! 1280: int status;
! 1281:
! 1282: switch(waitpid(p->cgi_pid.ptr[ndx], &status, WNOHANG)) {
! 1283: case 0:
! 1284: /* not finished yet */
! 1285: #if 0
! 1286: log_error_write(srv, __FILE__, __LINE__, "sd", "(debug) child isn't done yet, pid:", p->cgi_pid.ptr[ndx]);
! 1287: #endif
! 1288: break;
! 1289: case -1:
! 1290: if (errno == ECHILD) {
! 1291: /* someone else called waitpid... remove the pid to stop looping the error each time */
! 1292: log_error_write(srv, __FILE__, __LINE__, "s", "cgi child vanished, probably someone else called waitpid");
! 1293:
! 1294: cgi_pid_del(srv, p, p->cgi_pid.ptr[ndx]);
! 1295: ndx--;
! 1296: continue;
! 1297: }
! 1298:
! 1299: log_error_write(srv, __FILE__, __LINE__, "ss", "waitpid failed: ", strerror(errno));
! 1300:
! 1301: return HANDLER_ERROR;
! 1302: default:
! 1303:
! 1304: if (WIFEXITED(status)) {
! 1305: #if 0
! 1306: log_error_write(srv, __FILE__, __LINE__, "sd", "(debug) cgi exited fine, pid:", p->cgi_pid.ptr[ndx]);
! 1307: #endif
! 1308: } else if (WIFSIGNALED(status)) {
! 1309: /* FIXME: what if we killed the CGI script with a kill(..., SIGTERM) ?
! 1310: */
! 1311: if (WTERMSIG(status) != SIGTERM) {
! 1312: log_error_write(srv, __FILE__, __LINE__, "sd", "cleaning up CGI: process died with signal", WTERMSIG(status));
! 1313: }
! 1314: } else {
! 1315: log_error_write(srv, __FILE__, __LINE__, "s", "cleaning up CGI: ended unexpectedly");
! 1316: }
! 1317:
! 1318: cgi_pid_del(srv, p, p->cgi_pid.ptr[ndx]);
! 1319: /* del modified the buffer structure
! 1320: * and copies the last entry to the current one
! 1321: * -> recheck the current index
! 1322: */
! 1323: ndx--;
! 1324: }
! 1325: }
! 1326: #endif
! 1327: return HANDLER_GO_ON;
! 1328: }
! 1329:
! 1330: /*
! 1331: * - HANDLER_GO_ON : not our job
! 1332: * - HANDLER_FINISHED: got response header
! 1333: * - HANDLER_WAIT_FOR_EVENT: waiting for response header
! 1334: */
! 1335: SUBREQUEST_FUNC(mod_cgi_handle_subrequest) {
! 1336: int status;
! 1337: plugin_data *p = p_d;
! 1338: handler_ctx *hctx = con->plugin_ctx[p->id];
! 1339:
! 1340: if (con->mode != p->id) return HANDLER_GO_ON;
! 1341: if (NULL == hctx) return HANDLER_GO_ON;
! 1342:
! 1343: #if 0
! 1344: log_error_write(srv, __FILE__, __LINE__, "sdd", "subrequest, pid =", hctx, hctx->pid);
! 1345: #endif
! 1346:
! 1347: if (hctx->pid == 0) {
! 1348: /* cgi already dead */
! 1349: if (!con->file_started) return HANDLER_WAIT_FOR_EVENT;
! 1350: return HANDLER_FINISHED;
! 1351: }
! 1352:
! 1353: #ifndef __WIN32
! 1354: switch(waitpid(hctx->pid, &status, WNOHANG)) {
! 1355: case 0:
! 1356: /* we only have for events here if we don't have the header yet,
! 1357: * otherwise the event-handler will send us the incoming data */
! 1358: if (con->file_started) return HANDLER_FINISHED;
! 1359:
! 1360: return HANDLER_WAIT_FOR_EVENT;
! 1361: case -1:
! 1362: if (errno == EINTR) return HANDLER_WAIT_FOR_EVENT;
! 1363:
! 1364: if (errno == ECHILD && con->file_started == 0) {
! 1365: /*
! 1366: * second round but still not response
! 1367: */
! 1368: return HANDLER_WAIT_FOR_EVENT;
! 1369: }
! 1370:
! 1371: log_error_write(srv, __FILE__, __LINE__, "ss", "waitpid failed: ", strerror(errno));
! 1372: con->mode = DIRECT;
! 1373: con->http_status = 500;
! 1374:
! 1375: hctx->pid = 0;
! 1376:
! 1377: fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd);
! 1378: fdevent_unregister(srv->ev, hctx->fd);
! 1379:
! 1380: if (close(hctx->fd)) {
! 1381: log_error_write(srv, __FILE__, __LINE__, "sds", "cgi close failed ", hctx->fd, strerror(errno));
! 1382: }
! 1383:
! 1384: cgi_handler_ctx_free(hctx);
! 1385:
! 1386: con->plugin_ctx[p->id] = NULL;
! 1387:
! 1388: return HANDLER_FINISHED;
! 1389: default:
! 1390: /* cgi process exited
! 1391: */
! 1392:
! 1393: hctx->pid = 0;
! 1394:
! 1395: /* we already have response headers? just continue */
! 1396: if (con->file_started) return HANDLER_FINISHED;
! 1397:
! 1398: if (WIFEXITED(status)) {
! 1399: /* clean exit - just continue */
! 1400: return HANDLER_WAIT_FOR_EVENT;
! 1401: }
! 1402:
! 1403: /* cgi proc died, and we didn't get any data yet - send error message and close cgi con */
! 1404: log_error_write(srv, __FILE__, __LINE__, "s", "cgi died ?");
! 1405:
! 1406: con->http_status = 500;
! 1407: con->mode = DIRECT;
! 1408:
! 1409: fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd);
! 1410: fdevent_unregister(srv->ev, hctx->fd);
! 1411:
! 1412: if (close(hctx->fd)) {
! 1413: log_error_write(srv, __FILE__, __LINE__, "sds", "cgi close failed ", hctx->fd, strerror(errno));
! 1414: }
! 1415:
! 1416: cgi_handler_ctx_free(hctx);
! 1417:
! 1418: con->plugin_ctx[p->id] = NULL;
! 1419: return HANDLER_FINISHED;
! 1420: }
! 1421: #else
! 1422: return HANDLER_ERROR;
! 1423: #endif
! 1424: }
! 1425:
! 1426:
! 1427: int mod_cgi_plugin_init(plugin *p);
! 1428: int mod_cgi_plugin_init(plugin *p) {
! 1429: p->version = LIGHTTPD_VERSION_ID;
! 1430: p->name = buffer_init_string("cgi");
! 1431:
! 1432: p->connection_reset = cgi_connection_close_callback;
! 1433: p->handle_subrequest_start = cgi_is_handled;
! 1434: p->handle_subrequest = mod_cgi_handle_subrequest;
! 1435: #if 0
! 1436: p->handle_fdevent = cgi_handle_fdevent;
! 1437: #endif
! 1438: p->handle_trigger = cgi_trigger;
! 1439: p->init = mod_cgi_init;
! 1440: p->cleanup = mod_cgi_free;
! 1441: p->set_defaults = mod_fastcgi_set_defaults;
! 1442:
! 1443: p->data = NULL;
! 1444:
! 1445: return 0;
! 1446: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>