1 #include "first.h" 2 3 #include "base.h" 4 #include "stat_cache.h" 5 #include "http_kv.h" 6 #include "fdlog.h" 7 #include "log.h" 8 #include "response.h" 9 #include "http_cgi.h" 10 #include "http_chunk.h" 11 #include "http_header.h" 12 13 #include "plugin.h" 14 15 #include <sys/types.h> 16 #include "sys-socket.h" 17 #ifdef HAVE_SYS_WAIT_H 18 #include <sys/wait.h> 19 #endif 20 21 #include <unistd.h> 22 #include <errno.h> 23 #include <stdlib.h> 24 #include <string.h> 25 #include <fdevent.h> 26 27 #include <fcntl.h> 28 #include <signal.h> 29 30 typedef struct { 31 uintptr_t *offsets; 32 size_t osize; 33 size_t oused; 34 buffer *b; 35 buffer *boffsets; 36 buffer *ld_preload; 37 buffer *ld_library_path; 38 #ifdef __CYGWIN__ 39 buffer *systemroot; 40 #endif 41 } env_accum; 42 43 typedef struct { 44 unix_time64_t read_timeout; 45 unix_time64_t write_timeout; 46 int signal_fin; 47 } cgi_limits; 48 49 typedef struct { 50 const array *cgi; 51 const cgi_limits *limits; 52 unsigned short execute_x_only; 53 unsigned short local_redir; 54 unsigned short xsendfile_allow; 55 unsigned short upgrade; 56 const array *xsendfile_docroot; 57 } plugin_config; 58 59 struct cgi_pid_t; 60 61 typedef struct { 62 PLUGIN_DATA; 63 plugin_config defaults; 64 plugin_config conf; 65 int tempfile_accum; 66 struct cgi_pid_t *cgi_pid; 67 env_accum env; 68 } plugin_data; 69 70 typedef struct { 71 struct cgi_pid_t *cgi_pid; 72 int fd; 73 int fdtocgi; 74 int rd_revents; 75 int wr_revents; 76 fdnode *fdn; 77 fdnode *fdntocgi; 78 79 request_st *r; 80 connection *con; /* dumb pointer */ 81 struct fdevents *ev; /* dumb pointer */ 82 plugin_data *plugin_data; /* dumb pointer */ 83 84 buffer *response; 85 unix_time64_t read_ts; 86 unix_time64_t write_ts; 87 buffer *cgi_handler; /* dumb pointer */ 88 http_response_opts opts; 89 plugin_config conf; 90 } handler_ctx; 91 92 typedef struct cgi_pid_t { 93 pid_t pid; 94 int signal_sent; 95 handler_ctx *hctx; 96 struct cgi_pid_t *next; 97 struct cgi_pid_t *prev; 98 } cgi_pid_t; 99 100 static handler_ctx * cgi_handler_ctx_init(void) { 101 handler_ctx *hctx = calloc(1, sizeof(*hctx)); 102 103 force_assert(hctx); 104 105 hctx->response = chunk_buffer_acquire(); 106 hctx->fd = -1; 107 hctx->fdtocgi = -1; 108 109 return hctx; 110 } 111 112 static void cgi_handler_ctx_free(handler_ctx *hctx) { 113 chunk_buffer_release(hctx->response); 114 free(hctx); 115 } 116 117 INIT_FUNC(mod_cgi_init) { 118 plugin_data *p; 119 const char *s; 120 121 p = calloc(1, sizeof(*p)); 122 123 force_assert(p); 124 125 /* for valgrind */ 126 s = getenv("LD_PRELOAD"); 127 if (s) p->env.ld_preload = buffer_init_string(s); 128 s = getenv("LD_LIBRARY_PATH"); 129 if (s) p->env.ld_library_path = buffer_init_string(s); 130 #ifdef __CYGWIN__ 131 /* CYGWIN needs SYSTEMROOT */ 132 s = getenv("SYSTEMROOT"); 133 if (s) p->env.systemroot = buffer_init_string(s); 134 #endif 135 136 return p; 137 } 138 139 140 FREE_FUNC(mod_cgi_free) { 141 plugin_data *p = p_d; 142 buffer_free(p->env.ld_preload); 143 buffer_free(p->env.ld_library_path); 144 #ifdef __CYGWIN__ 145 buffer_free(p->env.systemroot); 146 #endif 147 148 for (cgi_pid_t *cgi_pid = p->cgi_pid, *next; cgi_pid; cgi_pid = next) { 149 next = cgi_pid->next; 150 free(cgi_pid); 151 } 152 153 if (NULL == p->cvlist) return; 154 /* (init i to 0 if global context; to 1 to skip empty global context) */ 155 for (int i = !p->cvlist[0].v.u2[1], used = p->nconfig; i < used; ++i) { 156 config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0]; 157 for (; -1 != cpv->k_id; ++cpv) { 158 if (cpv->vtype != T_CONFIG_LOCAL || NULL == cpv->v.v) continue; 159 switch (cpv->k_id) { 160 case 6: /* cgi.limits */ 161 free(cpv->v.v); 162 break; 163 default: 164 break; 165 } 166 } 167 } 168 } 169 170 static void mod_cgi_merge_config_cpv(plugin_config * const pconf, const config_plugin_value_t * const cpv) { 171 switch (cpv->k_id) { /* index into static config_plugin_keys_t cpk[] */ 172 case 0: /* cgi.assign */ 173 pconf->cgi = cpv->v.a; 174 break; 175 case 1: /* cgi.execute-x-only */ 176 pconf->execute_x_only = (unsigned short)cpv->v.u; 177 break; 178 case 2: /* cgi.x-sendfile */ 179 pconf->xsendfile_allow = (unsigned short)cpv->v.u; 180 break; 181 case 3: /* cgi.x-sendfile-docroot */ 182 pconf->xsendfile_docroot = cpv->v.a; 183 break; 184 case 4: /* cgi.local-redir */ 185 pconf->local_redir = (unsigned short)cpv->v.u; 186 break; 187 case 5: /* cgi.upgrade */ 188 pconf->upgrade = (unsigned short)cpv->v.u; 189 break; 190 case 6: /* cgi.limits */ 191 if (cpv->vtype != T_CONFIG_LOCAL) break; 192 pconf->limits = cpv->v.v; 193 break; 194 default:/* should not happen */ 195 return; 196 } 197 } 198 199 static void mod_cgi_merge_config(plugin_config * const pconf, const config_plugin_value_t *cpv) { 200 do { 201 mod_cgi_merge_config_cpv(pconf, cpv); 202 } while ((++cpv)->k_id != -1); 203 } 204 205 static void mod_cgi_patch_config(request_st * const r, plugin_data * const p) { 206 p->conf = p->defaults; /* copy small struct instead of memcpy() */ 207 /*memcpy(&p->conf, &p->defaults, sizeof(plugin_config));*/ 208 for (int i = 1, used = p->nconfig; i < used; ++i) { 209 if (config_check_cond(r, (uint32_t)p->cvlist[i].k_id)) 210 mod_cgi_merge_config(&p->conf, p->cvlist + p->cvlist[i].v.u2[0]); 211 } 212 } 213 214 __attribute_cold__ 215 __attribute_pure__ 216 static int mod_cgi_str_to_signal (const char *s, int default_sig) { 217 static const struct { const char *name; int sig; } sigs[] = { 218 { "HUP", SIGHUP } 219 ,{ "INT", SIGINT } 220 ,{ "QUIT", SIGQUIT } 221 ,{ "ILL", SIGILL } 222 ,{ "TRAP", SIGTRAP } 223 ,{ "ABRT", SIGABRT } 224 #ifdef SIGBUS 225 ,{ "BUS", SIGBUS } 226 #endif 227 ,{ "FPE", SIGFPE } 228 ,{ "KILL", SIGKILL } 229 #ifdef SIGUSR1 230 ,{ "USR1", SIGUSR1 } 231 #endif 232 ,{ "SEGV", SIGSEGV } 233 #ifdef SIGUSR2 234 ,{ "USR2", SIGUSR2 } 235 #endif 236 ,{ "PIPE", SIGPIPE } 237 ,{ "ALRM", SIGALRM } 238 ,{ "TERM", SIGTERM } 239 #ifdef SIGCHLD 240 ,{ "CHLD", SIGCHLD } 241 #endif 242 #ifdef SIGCONT 243 ,{ "CONT", SIGCONT } 244 #endif 245 #ifdef SIGURG 246 ,{ "URG", SIGURG } 247 #endif 248 #ifdef SIGXCPU 249 ,{ "XCPU", SIGXCPU } 250 #endif 251 #ifdef SIGXFSZ 252 ,{ "XFSZ", SIGXFSZ } 253 #endif 254 #ifdef SIGWINCH 255 ,{ "WINCH",SIGWINCH} 256 #endif 257 #ifdef SIGPOLL 258 ,{ "POLL", SIGPOLL } 259 #endif 260 #ifdef SIGIO 261 ,{ "IO", SIGIO } 262 #endif 263 }; 264 265 if (s[0] == 'S' && s[1] == 'I' && s[2] == 'G') s += 3; /*("SIG" prefix)*/ 266 for (uint32_t i = 0; i < sizeof(sigs)/sizeof(*sigs); ++i) { 267 if (0 == strcmp(s, sigs[i].name)) return sigs[i].sig; 268 } 269 return default_sig; 270 } 271 272 static cgi_limits * mod_cgi_parse_limits(const array * const a, log_error_st * const errh) { 273 cgi_limits * const limits = calloc(1, sizeof(cgi_limits)); 274 force_assert(limits); 275 for (uint32_t i = 0; i < a->used; ++i) { 276 const data_unset * const du = a->data[i]; 277 int32_t v = config_plugin_value_to_int32(du, -1); 278 if (buffer_eq_icase_slen(&du->key, CONST_STR_LEN("read-timeout"))) { 279 limits->read_timeout = (unix_time64_t)v; 280 continue; 281 } 282 if (buffer_eq_icase_slen(&du->key, CONST_STR_LEN("write-timeout"))) { 283 limits->write_timeout = (unix_time64_t)v; 284 continue; 285 } 286 if (buffer_eq_icase_slen(&du->key, CONST_STR_LEN("tcp-fin-propagate"))) { 287 if (-1 == v) { 288 v = SIGTERM; 289 if (du->type == TYPE_STRING) { 290 buffer * const vstr = &((data_string *)du)->value; 291 buffer_to_upper(vstr); 292 v = mod_cgi_str_to_signal(vstr->ptr, SIGTERM); 293 } 294 } 295 limits->signal_fin = v; 296 continue; 297 } 298 log_error(errh, __FILE__, __LINE__, 299 "unrecognized cgi.limits param: %s", du->key.ptr); 300 } 301 return limits; 302 } 303 304 SETDEFAULTS_FUNC(mod_cgi_set_defaults) { 305 static const config_plugin_keys_t cpk[] = { 306 { CONST_STR_LEN("cgi.assign"), 307 T_CONFIG_ARRAY_KVSTRING, 308 T_CONFIG_SCOPE_CONNECTION } 309 ,{ CONST_STR_LEN("cgi.execute-x-only"), 310 T_CONFIG_BOOL, 311 T_CONFIG_SCOPE_CONNECTION } 312 ,{ CONST_STR_LEN("cgi.x-sendfile"), 313 T_CONFIG_BOOL, 314 T_CONFIG_SCOPE_CONNECTION } 315 ,{ CONST_STR_LEN("cgi.x-sendfile-docroot"), 316 T_CONFIG_ARRAY_VLIST, 317 T_CONFIG_SCOPE_CONNECTION } 318 ,{ CONST_STR_LEN("cgi.local-redir"), 319 T_CONFIG_BOOL, 320 T_CONFIG_SCOPE_CONNECTION } 321 ,{ CONST_STR_LEN("cgi.upgrade"), 322 T_CONFIG_BOOL, 323 T_CONFIG_SCOPE_CONNECTION } 324 ,{ CONST_STR_LEN("cgi.limits"), 325 T_CONFIG_ARRAY_KVANY, 326 T_CONFIG_SCOPE_CONNECTION } 327 ,{ NULL, 0, 328 T_CONFIG_UNSET, 329 T_CONFIG_SCOPE_UNSET } 330 }; 331 332 plugin_data * const p = p_d; 333 if (!config_plugin_values_init(srv, p, cpk, "mod_cgi")) 334 return HANDLER_ERROR; 335 336 /* process and validate config directives 337 * (init i to 0 if global context; to 1 to skip empty global context) */ 338 for (int i = !p->cvlist[0].v.u2[1]; i < p->nconfig; ++i) { 339 config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0]; 340 for (; -1 != cpv->k_id; ++cpv) { 341 switch (cpv->k_id) { 342 case 0: /* cgi.assign */ 343 case 1: /* cgi.execute-x-only */ 344 case 2: /* cgi.x-sendfile */ 345 break; 346 case 3: /* cgi.x-sendfile-docroot */ 347 for (uint32_t j = 0; j < cpv->v.a->used; ++j) { 348 data_string *ds = (data_string *)cpv->v.a->data[j]; 349 if (ds->value.ptr[0] != '/') { 350 log_error(srv->errh, __FILE__, __LINE__, 351 "%s paths must begin with '/'; invalid: \"%s\"", 352 cpk[cpv->k_id].k, ds->value.ptr); 353 return HANDLER_ERROR; 354 } 355 buffer_path_simplify(&ds->value); 356 buffer_append_slash(&ds->value); 357 } 358 break; 359 case 4: /* cgi.local-redir */ 360 case 5: /* cgi.upgrade */ 361 break; 362 case 6: /* cgi.limits */ 363 cpv->v.v = mod_cgi_parse_limits(cpv->v.a, srv->errh); 364 if (NULL == cpv->v.v) return HANDLER_ERROR; 365 cpv->vtype = T_CONFIG_LOCAL; 366 break; 367 default:/* should not happen */ 368 break; 369 } 370 } 371 } 372 373 /* initialize p->defaults from global config context */ 374 if (p->nconfig > 0 && p->cvlist->v.u2[1]) { 375 const config_plugin_value_t *cpv = p->cvlist + p->cvlist->v.u2[0]; 376 if (-1 != cpv->k_id) 377 mod_cgi_merge_config(&p->defaults, cpv); 378 } 379 380 p->tempfile_accum = config_feature_bool(srv, "cgi.tempfile-accum", 1); 381 382 return HANDLER_GO_ON; 383 } 384 385 386 static cgi_pid_t * cgi_pid_add(plugin_data *p, pid_t pid, handler_ctx *hctx) { 387 cgi_pid_t *cgi_pid = malloc(sizeof(cgi_pid_t)); 388 force_assert(cgi_pid); 389 cgi_pid->pid = pid; 390 cgi_pid->signal_sent = 0; 391 cgi_pid->hctx = hctx; 392 cgi_pid->prev = NULL; 393 cgi_pid->next = p->cgi_pid; 394 p->cgi_pid = cgi_pid; 395 return cgi_pid; 396 } 397 398 static void cgi_pid_kill(cgi_pid_t *cgi_pid, int sig) { 399 cgi_pid->signal_sent = sig; /*(save last signal sent)*/ 400 kill(cgi_pid->pid, sig); 401 } 402 403 static void cgi_pid_del(plugin_data *p, cgi_pid_t *cgi_pid) { 404 if (cgi_pid->prev) 405 cgi_pid->prev->next = cgi_pid->next; 406 else 407 p->cgi_pid = cgi_pid->next; 408 409 if (cgi_pid->next) 410 cgi_pid->next->prev = cgi_pid->prev; 411 412 free(cgi_pid); 413 } 414 415 416 static void cgi_connection_close_fdtocgi(handler_ctx *hctx) { 417 /*(closes only hctx->fdtocgi)*/ 418 struct fdevents * const ev = hctx->ev; 419 fdevent_fdnode_event_del(ev, hctx->fdntocgi); 420 /*fdevent_unregister(ev, hctx->fdtocgi);*//*(handled below)*/ 421 fdevent_sched_close(ev, hctx->fdtocgi, 0); 422 hctx->fdntocgi = NULL; 423 hctx->fdtocgi = -1; 424 } 425 426 static void cgi_connection_close(handler_ctx *hctx) { 427 /* the connection to the browser went away, but we still have a connection 428 * to the CGI script 429 * 430 * close cgi-connection 431 */ 432 433 if (hctx->fd != -1) { 434 struct fdevents * const ev = hctx->ev; 435 /* close connection to the cgi-script */ 436 fdevent_fdnode_event_del(ev, hctx->fdn); 437 /*fdevent_unregister(ev, hctx->fd);*//*(handled below)*/ 438 fdevent_sched_close(ev, hctx->fd, 0); 439 hctx->fdn = NULL; 440 } 441 442 if (hctx->fdtocgi != -1) { 443 cgi_connection_close_fdtocgi(hctx); /*(closes only hctx->fdtocgi)*/ 444 } 445 446 plugin_data * const p = hctx->plugin_data; 447 request_st * const r = hctx->r; 448 r->plugin_ctx[p->id] = NULL; 449 450 if (hctx->cgi_pid) { 451 cgi_pid_kill(hctx->cgi_pid, SIGTERM); 452 hctx->cgi_pid->hctx = NULL; 453 } 454 cgi_handler_ctx_free(hctx); 455 456 /* finish response (if not already r->resp_body_started, r->resp_body_finished) */ 457 if (r->handler_module == p->self) { 458 http_response_backend_done(r); 459 } 460 } 461 462 static handler_t cgi_connection_close_callback(request_st * const r, void *p_d) { 463 handler_ctx *hctx = r->plugin_ctx[((plugin_data *)p_d)->id]; 464 if (hctx) { 465 chunkqueue_set_tempdirs(&r->reqbody_queue, /* reset sz */ 466 r->reqbody_queue.tempdirs, 0); 467 cgi_connection_close(hctx); 468 } 469 return HANDLER_GO_ON; 470 } 471 472 473 static int cgi_write_request(handler_ctx *hctx, int fd); 474 475 476 static handler_t cgi_handle_fdevent_send (void *ctx, int revents) { 477 handler_ctx *hctx = ctx; 478 hctx->wr_revents |= revents; 479 joblist_append(hctx->con); 480 return HANDLER_FINISHED; 481 } 482 483 484 static handler_t cgi_process_wr_revents (handler_ctx * const hctx, request_st * const r, int revents) { 485 if (revents & FDEVENT_OUT) { 486 if (0 != cgi_write_request(hctx, hctx->fdtocgi)) { 487 cgi_connection_close(hctx); 488 return HANDLER_ERROR; 489 } 490 /* more request body to be sent to CGI */ 491 } 492 493 if (revents & FDEVENT_HUP) { 494 /* skip sending remaining data to CGI */ 495 if (r->reqbody_length) { 496 chunkqueue *cq = &r->reqbody_queue; 497 chunkqueue_mark_written(cq, chunkqueue_length(cq)); 498 if (cq->bytes_in != (off_t)r->reqbody_length) { 499 r->keep_alive = 0; 500 } 501 } 502 503 cgi_connection_close_fdtocgi(hctx); /*(closes only hctx->fdtocgi)*/ 504 } else if (revents & FDEVENT_ERR) { 505 /* kill all connections to the cgi process */ 506 #if 1 507 log_error(r->conf.errh, __FILE__, __LINE__, "cgi-FDEVENT_ERR"); 508 #endif 509 cgi_connection_close(hctx); 510 return HANDLER_ERROR; 511 } 512 513 return HANDLER_GO_ON; 514 } 515 516 517 static handler_t cgi_response_headers(request_st * const r, struct http_response_opts_t *opts) { 518 /* response headers just completed */ 519 handler_ctx *hctx = (handler_ctx *)opts->pdata; 520 521 if (light_btst(r->resp_htags, HTTP_HEADER_UPGRADE)) { 522 if (hctx->conf.upgrade && r->http_status == 101) { 523 /* 101 Switching Protocols; transition to transparent proxy */ 524 http_response_upgrade_read_body_unknown(r); 525 } 526 else { 527 light_bclr(r->resp_htags, HTTP_HEADER_UPGRADE); 528 #if 0 529 /* preserve prior questionable behavior; likely broken behavior 530 * anyway if backend thinks connection is being upgraded but client 531 * does not receive Connection: upgrade */ 532 http_header_response_unset(r, HTTP_HEADER_UPGRADE, 533 CONST_STR_LEN("Upgrade")); 534 #endif 535 } 536 } 537 538 if (hctx->conf.upgrade 539 && !light_btst(r->resp_htags, HTTP_HEADER_UPGRADE)) { 540 chunkqueue *cq = &r->reqbody_queue; 541 hctx->conf.upgrade = 0; 542 if (cq->bytes_out == (off_t)r->reqbody_length) { 543 cgi_connection_close_fdtocgi(hctx); /*(closes hctx->fdtocgi)*/ 544 } 545 } 546 547 return HANDLER_GO_ON; 548 } 549 550 551 __attribute_cold__ 552 __attribute_noinline__ 553 static handler_t cgi_local_redir(request_st * const r, handler_ctx * const hctx) { 554 buffer_clear(hctx->response); 555 chunk_buffer_yield(hctx->response); 556 http_response_reset(r); /*(includes r->http_status = 0)*/ 557 plugins_call_handle_request_reset(r); 558 /*cgi_connection_close(hctx);*//*(already cleaned up and hctx is now invalid)*/ 559 return HANDLER_COMEBACK; 560 } 561 562 563 static int cgi_recv_response(request_st * const r, handler_ctx * const hctx) { 564 const off_t bytes_in = r->write_queue.bytes_in; 565 switch (http_response_read(r, &hctx->opts, 566 hctx->response, hctx->fdn)) { 567 default: 568 if (r->write_queue.bytes_in > bytes_in) 569 hctx->read_ts = log_monotonic_secs; 570 return HANDLER_GO_ON; 571 case HANDLER_ERROR: 572 http_response_backend_error(r); 573 __attribute_fallthrough__ 574 case HANDLER_FINISHED: 575 cgi_connection_close(hctx); 576 return HANDLER_FINISHED; 577 case HANDLER_COMEBACK: 578 return cgi_local_redir(r, hctx); /* HANDLER_COMEBACK */ 579 } 580 } 581 582 583 static handler_t cgi_handle_fdevent(void *ctx, int revents) { 584 handler_ctx *hctx = ctx; 585 hctx->rd_revents |= revents; 586 joblist_append(hctx->con); 587 return HANDLER_FINISHED; 588 } 589 590 591 static handler_t cgi_process_rd_revents(handler_ctx * const hctx, request_st * const r, int revents) { 592 if (revents & FDEVENT_IN) { 593 handler_t rc = cgi_recv_response(r, hctx); /*(might invalidate hctx)*/ 594 if (rc != HANDLER_GO_ON) return rc; /*(unless HANDLER_GO_ON)*/ 595 } 596 597 /* perhaps this issue is already handled */ 598 if (revents & (FDEVENT_HUP|FDEVENT_RDHUP)) { 599 if (r->resp_body_started) { 600 /* drain any remaining data from kernel pipe buffers 601 * even if (r->conf.stream_response_body 602 * & FDEVENT_STREAM_RESPONSE_BUFMIN) 603 * since event loop will spin on fd FDEVENT_HUP event 604 * until unregistered. */ 605 handler_t rc; 606 const unsigned short flags = r->conf.stream_response_body; 607 r->conf.stream_response_body &= ~FDEVENT_STREAM_RESPONSE_BUFMIN; 608 r->conf.stream_response_body |= FDEVENT_STREAM_RESPONSE_POLLRDHUP; 609 do { 610 rc = cgi_recv_response(r,hctx); /*(might invalidate hctx)*/ 611 } while (rc == HANDLER_GO_ON); /*(unless HANDLER_GO_ON)*/ 612 r->conf.stream_response_body = flags; 613 return rc; /* HANDLER_FINISHED or HANDLER_COMEBACK or HANDLER_ERROR */ 614 } else if (!buffer_is_blank(hctx->response)) { 615 /* unfinished header package which is a body in reality */ 616 r->resp_body_started = 1; 617 if (0 != http_chunk_append_buffer(r, hctx->response)) { 618 cgi_connection_close(hctx); 619 return HANDLER_ERROR; 620 } 621 if (0 == r->http_status) r->http_status = 200; /* OK */ 622 } 623 cgi_connection_close(hctx); 624 } else if (revents & FDEVENT_ERR) { 625 /* kill all connections to the cgi process */ 626 cgi_connection_close(hctx); 627 return HANDLER_ERROR; 628 } 629 630 return HANDLER_GO_ON; 631 } 632 633 634 __attribute_cold__ 635 __attribute_noinline__ 636 static void cgi_env_offset_resize(env_accum *env) { 637 chunk_buffer_prepare_append(env->boffsets, env->boffsets->size*2); 638 env->offsets = (uintptr_t *)(void *)env->boffsets->ptr; 639 env->osize = env->boffsets->size/sizeof(*env->offsets); 640 } 641 642 static int cgi_env_add(void *venv, const char *key, size_t key_len, const char *val, size_t val_len) { 643 env_accum *env = venv; 644 645 if (!key || (!val && val_len)) return -1; 646 647 if (__builtin_expect( (env->osize == env->oused), 0)) 648 cgi_env_offset_resize(env); 649 env->offsets[env->oused++] = env->b->used-1; 650 651 char * const dst = buffer_extend(env->b, key_len + val_len + 2); 652 memcpy(dst, key, key_len); 653 dst[key_len] = '='; 654 if (val_len) memcpy(dst + key_len + 1, val, val_len); 655 dst[key_len + 1 + val_len] = '\0'; 656 657 return 0; 658 } 659 660 static int cgi_write_request(handler_ctx *hctx, int fd) { 661 request_st * const r = hctx->r; 662 chunkqueue *cq = &r->reqbody_queue; 663 chunk *c; 664 665 chunkqueue_remove_finished_chunks(cq); /* unnecessary? */ 666 667 /* old comment: windows doesn't support select() on pipes - wouldn't be easy to fix for all platforms. 668 */ 669 670 for (c = cq->first; c; c = cq->first) { 671 ssize_t wr = chunkqueue_write_chunk_to_pipe(fd, cq, r->conf.errh); 672 if (wr > 0) { 673 hctx->write_ts = log_monotonic_secs; 674 chunkqueue_mark_written(cq, wr); 675 /* continue if wrote whole chunk or wrote 16k block 676 * (see chunkqueue_write_chunk_file_intermed()) */ 677 if (c != cq->first || wr == 16384) 678 continue; 679 /*(else partial write)*/ 680 } 681 else if (wr < 0) { 682 switch(errno) { 683 case EAGAIN: 684 case EINTR: 685 /* ignore and try again later */ 686 break; 687 case EPIPE: 688 case ECONNRESET: 689 /* connection closed */ 690 log_error(r->conf.errh, __FILE__, __LINE__, 691 "failed to send post data to cgi, connection closed by CGI"); 692 /* skip all remaining data */ 693 chunkqueue_mark_written(cq, chunkqueue_length(cq)); 694 break; 695 default: 696 /* fatal error */ 697 log_perror(r->conf.errh, __FILE__, __LINE__, "write() failed"); 698 return -1; 699 } 700 } 701 /*if (0 == wr) break;*/ /*(might block)*/ 702 break; 703 } 704 705 if (cq->bytes_out == (off_t)r->reqbody_length && !hctx->conf.upgrade) { 706 /* sent all request body input */ 707 /* close connection to the cgi-script */ 708 if (-1 == hctx->fdtocgi) { /*(received request body sent in initial send to pipe buffer)*/ 709 --r->con->srv->cur_fds; 710 if (close(fd)) { 711 log_perror(r->conf.errh, __FILE__, __LINE__, "cgi stdin close %d failed", fd); 712 } 713 } else { 714 cgi_connection_close_fdtocgi(hctx); /*(closes only hctx->fdtocgi)*/ 715 } 716 } else { 717 off_t cqlen = chunkqueue_length(cq); 718 if (cq->bytes_in != r->reqbody_length && cqlen < 65536 - 16384) { 719 /*(r->conf.stream_request_body & FDEVENT_STREAM_REQUEST)*/ 720 if (!(r->conf.stream_request_body & FDEVENT_STREAM_REQUEST_POLLIN)) { 721 r->conf.stream_request_body |= FDEVENT_STREAM_REQUEST_POLLIN; 722 r->con->is_readable = 1; /* trigger optimistic read from client */ 723 } 724 } 725 struct fdevents * const ev = hctx->ev; 726 if (-1 == hctx->fdtocgi) { /*(not registered yet)*/ 727 hctx->fdtocgi = fd; 728 hctx->fdntocgi = fdevent_register(ev, hctx->fdtocgi, cgi_handle_fdevent_send, hctx); 729 } 730 if (0 == cqlen) { /*(chunkqueue_is_empty(cq))*/ 731 if ((fdevent_fdnode_interest(hctx->fdntocgi) & FDEVENT_OUT)) { 732 fdevent_fdnode_event_set(ev, hctx->fdntocgi, 0); 733 } 734 } else { 735 /* more request body remains to be sent to CGI so register for fdevents */ 736 hctx->write_ts = log_monotonic_secs; 737 fdevent_fdnode_event_set(ev, hctx->fdntocgi, FDEVENT_OUT); 738 } 739 } 740 741 return 0; 742 } 743 744 static int cgi_create_env(request_st * const r, plugin_data * const p, handler_ctx * const hctx, buffer * const cgi_handler) { 745 char *args[3]; 746 int to_cgi_fds[2]; 747 int from_cgi_fds[2]; 748 UNUSED(p); 749 750 if (!buffer_is_blank(cgi_handler)) { 751 if (NULL == stat_cache_path_stat(cgi_handler)) { 752 log_perror(r->conf.errh, __FILE__, __LINE__, 753 "stat for cgi-handler %s", cgi_handler->ptr); 754 return -1; 755 } 756 } 757 758 to_cgi_fds[0] = -1; 759 #ifndef __CYGWIN__ 760 if (0 == r->reqbody_length) { 761 /* future: might keep fd open in p->devnull for reuse 762 * and dup() here, or do not close() (later in this func) */ 763 to_cgi_fds[0] = fdevent_open_devnull(); 764 if (-1 == to_cgi_fds[0]) { 765 log_perror(r->conf.errh, __FILE__, __LINE__, "open /dev/null"); 766 return -1; 767 } 768 } 769 else if (!(r->conf.stream_request_body /*(if not streaming request body)*/ 770 & (FDEVENT_STREAM_REQUEST|FDEVENT_STREAM_REQUEST_BUFMIN))) { 771 chunkqueue * const cq = &r->reqbody_queue; 772 chunk * const c = cq->first; 773 if (c && c == cq->last && c->type == FILE_CHUNK && c->file.is_temp) { 774 /* request body in single tempfile if not streaming req body */ 775 if (-1 == c->file.fd 776 && 0 != chunkqueue_open_file_chunk(cq, r->conf.errh)) 777 return -1; 778 #ifdef __COVERITY__ 779 force_assert(-1 != c->file.fd); 780 #endif 781 if (-1 == lseek(c->file.fd, 0, SEEK_SET)) { 782 log_perror(r->conf.errh, __FILE__, __LINE__, 783 "lseek %s", c->mem->ptr); 784 return -1; 785 } 786 to_cgi_fds[0] = c->file.fd; 787 to_cgi_fds[1] = -1; 788 } 789 } 790 #endif 791 792 unsigned int bufsz_hint = 16384; 793 #ifdef _WIN32 794 if (r->reqbody_length <= 1048576) 795 bufsz_hint = (unsigned int)r->reqbody_length; 796 #endif 797 if (-1 == to_cgi_fds[0] && fdevent_pipe_cloexec(to_cgi_fds, bufsz_hint)) { 798 log_perror(r->conf.errh, __FILE__, __LINE__, "pipe failed"); 799 return -1; 800 } 801 if (fdevent_pipe_cloexec(from_cgi_fds, bufsz_hint)) { 802 if (0 == r->reqbody_length) { 803 close(to_cgi_fds[0]); 804 } 805 else if (-1 != to_cgi_fds[1]) { 806 close(to_cgi_fds[0]); 807 close(to_cgi_fds[1]); 808 } 809 log_perror(r->conf.errh, __FILE__, __LINE__, "pipe failed"); 810 return -1; 811 } 812 813 env_accum * const env = &p->env; 814 env->b = chunk_buffer_acquire(); 815 env->boffsets = chunk_buffer_acquire(); 816 buffer_truncate(env->b, 0); 817 char **envp; 818 { 819 size_t i = 0; 820 http_cgi_opts opts = { 0, 0, NULL, NULL }; 821 env->offsets = (uintptr_t *)(void *)env->boffsets->ptr; 822 env->osize = env->boffsets->size/sizeof(*env->offsets); 823 env->oused = 0; 824 825 /* create environment */ 826 827 http_cgi_headers(r, &opts, cgi_env_add, env); 828 829 /* for valgrind */ 830 if (p->env.ld_preload) { 831 cgi_env_add(env, CONST_STR_LEN("LD_PRELOAD"), BUF_PTR_LEN(p->env.ld_preload)); 832 } 833 if (p->env.ld_library_path) { 834 cgi_env_add(env, CONST_STR_LEN("LD_LIBRARY_PATH"), BUF_PTR_LEN(p->env.ld_library_path)); 835 } 836 #ifdef __CYGWIN__ 837 /* CYGWIN needs SYSTEMROOT */ 838 if (p->env.systemroot) { 839 cgi_env_add(env, CONST_STR_LEN("SYSTEMROOT"), BUF_PTR_LEN(p->env.systemroot)); 840 } 841 #endif 842 843 /* adjust (uintptr_t) offsets to (char *) ptr 844 * (stored as offsets while accumulating in buffer, 845 * in case buffer is reallocated during env creation) */ 846 if (__builtin_expect( (env->osize == env->oused), 0)) 847 cgi_env_offset_resize(env); 848 envp = (char **)env->offsets; 849 envp[env->oused] = NULL; 850 const uintptr_t baseptr = (uintptr_t)env->b->ptr; 851 for (i = 0; i < env->oused; ++i) 852 envp[i] += baseptr; 853 854 /* set up args */ 855 i = 0; 856 857 if (!buffer_is_blank(cgi_handler)) { 858 args[i++] = cgi_handler->ptr; 859 } 860 args[i++] = r->physical.path.ptr; 861 args[i ] = NULL; 862 } 863 864 int dfd = fdevent_open_dirname(r->physical.path.ptr,r->conf.follow_symlink); 865 if (-1 == dfd) { 866 log_perror(r->conf.errh, __FILE__, __LINE__, "open dirname %s failed", r->physical.path.ptr); 867 } 868 869 int serrh_fd = r->conf.serrh ? r->conf.serrh->fd : -1; 870 pid_t pid = (dfd >= 0) 871 ? fdevent_fork_execve(args[0], args, envp, 872 to_cgi_fds[0], from_cgi_fds[1], serrh_fd, dfd) 873 : -1; 874 875 chunk_buffer_release(env->boffsets); 876 chunk_buffer_release(env->b); 877 env->boffsets = NULL; 878 env->b = NULL; 879 880 if (-1 == pid) { 881 /* log error with errno prior to calling close() (might change errno) */ 882 log_perror(r->conf.errh, __FILE__, __LINE__, "fork failed"); 883 if (-1 != dfd) close(dfd); 884 close(from_cgi_fds[0]); 885 close(from_cgi_fds[1]); 886 if (0 == r->reqbody_length) { 887 close(to_cgi_fds[0]); 888 } 889 else if (-1 != to_cgi_fds[1]) { 890 close(to_cgi_fds[0]); 891 close(to_cgi_fds[1]); 892 } 893 return -1; 894 } else { 895 if (-1 != dfd) close(dfd); 896 close(from_cgi_fds[1]); 897 898 hctx->fd = from_cgi_fds[0]; 899 hctx->cgi_pid = cgi_pid_add(p, pid, hctx); 900 901 if (0 == r->reqbody_length) { 902 close(to_cgi_fds[0]); 903 } 904 else if (-1 == to_cgi_fds[1]) { 905 chunkqueue * const cq = &r->reqbody_queue; 906 chunkqueue_mark_written(cq, chunkqueue_length(cq)); 907 } 908 else if (0 == fdevent_fcntl_set_nb(to_cgi_fds[1]) 909 && 0 == cgi_write_request(hctx, to_cgi_fds[1])) { 910 close(to_cgi_fds[0]); 911 ++r->con->srv->cur_fds; 912 } 913 else { 914 close(to_cgi_fds[0]); 915 close(to_cgi_fds[1]); 916 /*(hctx->fd not yet registered with fdevent, so manually 917 * cleanup here; see fdevent_register() further below)*/ 918 close(hctx->fd); 919 hctx->fd = -1; 920 cgi_connection_close(hctx); 921 return -1; 922 } 923 924 ++r->con->srv->cur_fds; 925 926 struct fdevents * const ev = hctx->ev; 927 hctx->fdn = fdevent_register(ev, hctx->fd, cgi_handle_fdevent, hctx); 928 if (-1 == fdevent_fcntl_set_nb(hctx->fd)) { 929 log_perror(r->conf.errh, __FILE__, __LINE__, "fcntl failed"); 930 cgi_connection_close(hctx); 931 return -1; 932 } 933 hctx->read_ts = log_monotonic_secs; 934 fdevent_fdnode_event_set(ev, hctx->fdn, FDEVENT_IN | FDEVENT_RDHUP); 935 936 return 0; 937 } 938 } 939 940 URIHANDLER_FUNC(cgi_is_handled) { 941 plugin_data *p = p_d; 942 const stat_cache_st *st; 943 data_string *ds; 944 945 if (NULL != r->handler_module) return HANDLER_GO_ON; 946 /* r->physical.path is non-empty for handle_subrequest_start */ 947 /*if (buffer_is_blank(&r->physical.path)) return HANDLER_GO_ON;*/ 948 949 mod_cgi_patch_config(r, p); 950 if (NULL == p->conf.cgi) return HANDLER_GO_ON; 951 952 ds = (data_string *)array_match_key_suffix(p->conf.cgi, &r->physical.path); 953 if (NULL == ds) return HANDLER_GO_ON; 954 955 /* r->tmp_sce is set in http_response_physical_path_check() and is valid 956 * in handle_subrequest_start callback -- handle_subrequest_start callbacks 957 * should not change r->physical.path (or should invalidate r->tmp_sce) */ 958 st = r->tmp_sce && buffer_is_equal(&r->tmp_sce->name, &r->physical.path) 959 ? &r->tmp_sce->st 960 : stat_cache_path_stat(&r->physical.path); 961 if (NULL == st) return HANDLER_GO_ON; 962 963 /* (aside: CGI might be executable even if it is not readable) */ 964 if (!S_ISREG(st->st_mode)) return HANDLER_GO_ON; 965 if (p->conf.execute_x_only == 1 && (st->st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) == 0) return HANDLER_GO_ON; 966 967 if (r->reqbody_length 968 && p->tempfile_accum 969 && !(r->conf.stream_request_body /*(if not streaming request body)*/ 970 & (FDEVENT_STREAM_REQUEST|FDEVENT_STREAM_REQUEST_BUFMIN))) { 971 /* store request body in single tempfile if not streaming request body*/ 972 r->reqbody_queue.upload_temp_file_size = INTMAX_MAX; 973 } 974 975 { 976 handler_ctx *hctx = cgi_handler_ctx_init(); 977 hctx->ev = r->con->srv->ev; 978 hctx->r = r; 979 hctx->con = r->con; 980 hctx->plugin_data = p; 981 hctx->cgi_handler = &ds->value; 982 memcpy(&hctx->conf, &p->conf, sizeof(plugin_config)); 983 hctx->conf.upgrade = 984 hctx->conf.upgrade 985 && r->http_version == HTTP_VERSION_1_1 986 && light_btst(r->rqst_htags, HTTP_HEADER_UPGRADE); 987 hctx->opts.max_per_read = 988 !(r->conf.stream_response_body /*(if not streaming response body)*/ 989 & (FDEVENT_STREAM_RESPONSE|FDEVENT_STREAM_RESPONSE_BUFMIN)) 990 ? 262144 991 : (r->conf.stream_response_body & FDEVENT_STREAM_RESPONSE_BUFMIN) 992 ? 16384 /* FDEVENT_STREAM_RESPONSE_BUFMIN */ 993 : 65536; /* FDEVENT_STREAM_RESPONSE */ 994 hctx->opts.fdfmt = S_IFIFO; 995 hctx->opts.backend = BACKEND_CGI; 996 hctx->opts.authorizer = 0; 997 hctx->opts.local_redir = hctx->conf.local_redir; 998 hctx->opts.xsendfile_allow = hctx->conf.xsendfile_allow; 999 hctx->opts.xsendfile_docroot = hctx->conf.xsendfile_docroot; 1000 hctx->opts.pdata = hctx; 1001 hctx->opts.headers = cgi_response_headers; 1002 r->plugin_ctx[p->id] = hctx; 1003 r->handler_module = p->self; 1004 } 1005 1006 return HANDLER_GO_ON; 1007 } 1008 1009 /* 1010 * - HANDLER_GO_ON : not our job 1011 * - HANDLER_FINISHED: got response 1012 * - HANDLER_WAIT_FOR_EVENT: waiting for response 1013 */ 1014 SUBREQUEST_FUNC(mod_cgi_handle_subrequest) { 1015 plugin_data * const p = p_d; 1016 handler_ctx * const hctx = r->plugin_ctx[p->id]; 1017 if (NULL == hctx) return HANDLER_GO_ON; 1018 1019 if (__builtin_expect( 1020 (r->conf.stream_request_body & FDEVENT_STREAM_REQUEST_TCP_FIN), 0) 1021 && hctx->conf.limits && hctx->conf.limits->signal_fin) { 1022 /* XXX: consider setting r->http_status = 499 if (0 == r->http_status) 1023 * (499 is nginx custom status to indicate client closed connection) */ 1024 if (-1 == hctx->fd) return HANDLER_ERROR; /*(CGI not yet spawned)*/ 1025 if (hctx->cgi_pid) /* send signal to notify CGI about TCP FIN */ 1026 cgi_pid_kill(hctx->cgi_pid, hctx->conf.limits->signal_fin); 1027 } 1028 1029 const int rd_revents = hctx->rd_revents; 1030 const int wr_revents = hctx->wr_revents; 1031 if (rd_revents) { 1032 hctx->rd_revents = 0; 1033 handler_t rc = cgi_process_rd_revents(hctx, r, rd_revents); 1034 if (rc != HANDLER_GO_ON) return rc; /*(might invalidate hctx)*/ 1035 } 1036 if (wr_revents) { 1037 hctx->wr_revents = 0; 1038 handler_t rc = cgi_process_wr_revents(hctx, r, wr_revents); 1039 if (rc != HANDLER_GO_ON) return rc; /*(might invalidate hctx)*/ 1040 } 1041 1042 if ((r->conf.stream_response_body & FDEVENT_STREAM_RESPONSE_BUFMIN) 1043 && r->resp_body_started) { 1044 if (chunkqueue_length(&r->write_queue) > 65536 - 4096) { 1045 fdevent_fdnode_event_clr(hctx->ev, hctx->fdn, FDEVENT_IN); 1046 } else if (!(fdevent_fdnode_interest(hctx->fdn) & FDEVENT_IN)) { 1047 /* optimistic read from backend */ 1048 handler_t rc = cgi_recv_response(r, hctx); /*(might invalidate hctx)*/ 1049 if (rc != HANDLER_GO_ON) return rc; /*(unless HANDLER_GO_ON)*/ 1050 hctx->read_ts = log_monotonic_secs; 1051 fdevent_fdnode_event_add(hctx->ev, hctx->fdn, FDEVENT_IN); 1052 } 1053 } 1054 1055 chunkqueue * const cq = &r->reqbody_queue; 1056 1057 if (cq->bytes_in != (off_t)r->reqbody_length) { 1058 /*(64k - 4k to attempt to avoid temporary files 1059 * in conjunction with FDEVENT_STREAM_REQUEST_BUFMIN)*/ 1060 if (chunkqueue_length(cq) > 65536 - 4096 1061 && (r->conf.stream_request_body & FDEVENT_STREAM_REQUEST_BUFMIN)){ 1062 r->conf.stream_request_body &= ~FDEVENT_STREAM_REQUEST_POLLIN; 1063 if (-1 != hctx->fd) return HANDLER_WAIT_FOR_EVENT; 1064 } else { 1065 handler_t rc = r->con->reqbody_read(r); 1066 if (!chunkqueue_is_empty(cq)) { 1067 if (fdevent_fdnode_interest(hctx->fdntocgi) & FDEVENT_OUT) { 1068 return (rc == HANDLER_GO_ON) ? HANDLER_WAIT_FOR_EVENT : rc; 1069 } 1070 } 1071 if (rc != HANDLER_GO_ON) return rc; 1072 1073 /* CGI environment requires that Content-Length be set. 1074 * Send 411 Length Required if Content-Length missing. 1075 * (occurs here if client sends Transfer-Encoding: chunked 1076 * and module is flagged to stream request body to backend) */ 1077 if (-1 == r->reqbody_length) { 1078 return (r->conf.stream_request_body & FDEVENT_STREAM_REQUEST) 1079 ? http_response_reqbody_read_error(r, 411) 1080 : HANDLER_WAIT_FOR_EVENT; 1081 } 1082 } 1083 } 1084 1085 if (-1 == hctx->fd) { 1086 if (cgi_create_env(r, p, hctx, hctx->cgi_handler)) { 1087 r->http_status = 500; 1088 r->handler_module = NULL; 1089 1090 return HANDLER_FINISHED; 1091 } 1092 } else if (!chunkqueue_is_empty(cq)) { 1093 if (0 != cgi_write_request(hctx, hctx->fdtocgi)) { 1094 cgi_connection_close(hctx); 1095 return HANDLER_ERROR; 1096 } 1097 } 1098 1099 /* if not done, wait for CGI to close stdout, so we read EOF on pipe */ 1100 return HANDLER_WAIT_FOR_EVENT; 1101 } 1102 1103 1104 __attribute_cold__ 1105 __attribute_noinline__ 1106 static void cgi_trigger_hctx_timeout(handler_ctx * const hctx, const char * const msg) { 1107 request_st * const r = hctx->r; 1108 joblist_append(r->con); 1109 1110 log_error(r->conf.errh, __FILE__, __LINE__, 1111 "%s timeout on CGI: %s (pid: %lld)", 1112 msg, r->physical.path.ptr, (long long)hctx->cgi_pid->pid); 1113 1114 if (*msg == 'w') { /* "write" */ 1115 /* theoretically, response might be waiting on hctx->fdn pipe 1116 * if it arrived since we last checked for event, and if CGI 1117 * timeout out while reading (or did not read) request body */ 1118 handler_t rc = cgi_recv_response(r, hctx); /*(might invalidate hctx)*/ 1119 if (rc != HANDLER_GO_ON) return; /*(unless HANDLER_GO_ON)*/ 1120 } 1121 1122 if (0 == r->http_status) r->http_status = 504; /* Gateway Timeout */ 1123 cgi_connection_close(hctx); 1124 } 1125 1126 1127 static handler_t cgi_trigger_cb(server *srv, void *p_d) { 1128 UNUSED(srv); 1129 const unix_time64_t mono = log_monotonic_secs; 1130 plugin_data * const p = p_d; 1131 for (cgi_pid_t *cgi_pid = p->cgi_pid; cgi_pid; cgi_pid = cgi_pid->next) { 1132 /*(hctx stays in cgi_pid list until process pid is reaped, 1133 * so p->cgi_pid[] is not modified during this loop)*/ 1134 handler_ctx * const hctx = cgi_pid->hctx; 1135 if (!hctx) continue; /*(already called cgi_pid_kill())*/ 1136 const cgi_limits * const limits = hctx->conf.limits; 1137 if (NULL == limits) continue; 1138 if (limits->read_timeout && hctx->fdn 1139 && (fdevent_fdnode_interest(hctx->fdn) & FDEVENT_IN) 1140 && mono - hctx->read_ts > limits->read_timeout) { 1141 cgi_trigger_hctx_timeout(hctx, "read"); 1142 continue; 1143 } 1144 if (limits->write_timeout && hctx->fdntocgi 1145 && (fdevent_fdnode_interest(hctx->fdntocgi) & FDEVENT_OUT) 1146 && mono - hctx->write_ts > limits->write_timeout) { 1147 cgi_trigger_hctx_timeout(hctx, "write"); 1148 continue; 1149 } 1150 } 1151 return HANDLER_GO_ON; 1152 } 1153 1154 1155 static handler_t cgi_waitpid_cb(server *srv, void *p_d, pid_t pid, int status) { 1156 /*(XXX: if supporting a large number of CGI, might use a different algorithm 1157 * instead of linked list, e.g. splaytree indexed with pid)*/ 1158 plugin_data *p = (plugin_data *)p_d; 1159 for (cgi_pid_t *cgi_pid = p->cgi_pid; cgi_pid; cgi_pid = cgi_pid->next) { 1160 if (pid != cgi_pid->pid) continue; 1161 1162 handler_ctx * const hctx = cgi_pid->hctx; 1163 if (hctx) hctx->cgi_pid = NULL; 1164 1165 if (WIFEXITED(status)) { 1166 /* (skip logging (non-zero) CGI exit; might be very noisy) */ 1167 } 1168 else if (WIFSIGNALED(status)) { 1169 /* ignore SIGTERM if sent by cgi_connection_close() (NULL == hctx)*/ 1170 if (WTERMSIG(status) != cgi_pid->signal_sent) { 1171 log_error_st *errh = hctx ? hctx->r->conf.errh : srv->errh; 1172 log_error(errh, __FILE__, __LINE__, 1173 "CGI pid %d died with signal %d", pid, WTERMSIG(status)); 1174 } 1175 } 1176 else { 1177 log_error_st *errh = hctx ? hctx->r->conf.errh : srv->errh; 1178 log_error(errh, __FILE__, __LINE__, 1179 "CGI pid %d ended unexpectedly", pid); 1180 } 1181 1182 cgi_pid_del(p, cgi_pid); 1183 return HANDLER_FINISHED; 1184 } 1185 1186 return HANDLER_GO_ON; 1187 } 1188 1189 1190 int mod_cgi_plugin_init(plugin *p); 1191 int mod_cgi_plugin_init(plugin *p) { 1192 p->version = LIGHTTPD_VERSION_ID; 1193 p->name = "cgi"; 1194 1195 p->handle_request_reset = cgi_connection_close_callback; 1196 p->handle_subrequest_start = cgi_is_handled; 1197 p->handle_subrequest = mod_cgi_handle_subrequest; 1198 p->handle_trigger = cgi_trigger_cb; 1199 p->handle_waitpid = cgi_waitpid_cb; 1200 p->init = mod_cgi_init; 1201 p->cleanup = mod_cgi_free; 1202 p->set_defaults = mod_cgi_set_defaults; 1203 1204 return 0; 1205 } 1206