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 if (-1 == hctx->fdtocgi) return; 419 struct fdevents * const ev = hctx->ev; 420 fdevent_fdnode_event_del(ev, hctx->fdntocgi); 421 /*fdevent_unregister(ev, hctx->fdtocgi);*//*(handled below)*/ 422 fdevent_sched_close(ev, hctx->fdtocgi, 0); 423 hctx->fdntocgi = NULL; 424 hctx->fdtocgi = -1; 425 } 426 427 static void cgi_connection_close(handler_ctx *hctx) { 428 /* the connection to the browser went away, but we still have a connection 429 * to the CGI script 430 * 431 * close cgi-connection 432 */ 433 434 if (hctx->fd != -1) { 435 struct fdevents * const ev = hctx->ev; 436 /* close connection to the cgi-script */ 437 fdevent_fdnode_event_del(ev, hctx->fdn); 438 /*fdevent_unregister(ev, hctx->fd);*//*(handled below)*/ 439 fdevent_sched_close(ev, hctx->fd, 0); 440 hctx->fdn = NULL; 441 } 442 443 if (hctx->fdtocgi != -1) { 444 cgi_connection_close_fdtocgi(hctx); /*(closes only hctx->fdtocgi)*/ 445 } 446 447 plugin_data * const p = hctx->plugin_data; 448 request_st * const r = hctx->r; 449 r->plugin_ctx[p->id] = NULL; 450 451 if (hctx->cgi_pid) { 452 cgi_pid_kill(hctx->cgi_pid, SIGTERM); 453 hctx->cgi_pid->hctx = NULL; 454 } 455 cgi_handler_ctx_free(hctx); 456 457 /* finish response (if not already r->resp_body_started, r->resp_body_finished) */ 458 if (r->handler_module == p->self) { 459 http_response_backend_done(r); 460 } 461 } 462 463 static handler_t cgi_connection_close_callback(request_st * const r, void *p_d) { 464 handler_ctx *hctx = r->plugin_ctx[((plugin_data *)p_d)->id]; 465 if (hctx) { 466 chunkqueue_set_tempdirs(&r->reqbody_queue, /* reset sz */ 467 r->reqbody_queue.tempdirs, 0); 468 cgi_connection_close(hctx); 469 } 470 return HANDLER_GO_ON; 471 } 472 473 474 static int cgi_write_request(handler_ctx *hctx, int fd); 475 476 477 static handler_t cgi_handle_fdevent_send (void *ctx, int revents) { 478 handler_ctx *hctx = ctx; 479 hctx->wr_revents |= revents; 480 joblist_append(hctx->con); 481 return HANDLER_FINISHED; 482 } 483 484 485 static handler_t cgi_process_wr_revents (handler_ctx * const hctx, request_st * const r, int revents) { 486 if (revents & FDEVENT_OUT) { 487 if (0 != cgi_write_request(hctx, hctx->fdtocgi)) { 488 cgi_connection_close(hctx); 489 return HANDLER_ERROR; 490 } 491 /* more request body to be sent to CGI */ 492 } 493 494 if (revents & FDEVENT_HUP) { 495 /* skip sending remaining data to CGI */ 496 if (r->reqbody_length) { 497 chunkqueue *cq = &r->reqbody_queue; 498 chunkqueue_mark_written(cq, chunkqueue_length(cq)); 499 if (cq->bytes_in != (off_t)r->reqbody_length) { 500 r->keep_alive = 0; 501 } 502 } 503 504 cgi_connection_close_fdtocgi(hctx); /*(closes only hctx->fdtocgi)*/ 505 } else if (revents & FDEVENT_ERR) { 506 /* kill all connections to the cgi process */ 507 #if 1 508 log_error(r->conf.errh, __FILE__, __LINE__, "cgi-FDEVENT_ERR"); 509 #endif 510 cgi_connection_close(hctx); 511 return HANDLER_ERROR; 512 } 513 514 return HANDLER_GO_ON; 515 } 516 517 518 static handler_t cgi_response_headers(request_st * const r, struct http_response_opts_t *opts) { 519 /* response headers just completed */ 520 handler_ctx *hctx = (handler_ctx *)opts->pdata; 521 522 if (light_btst(r->resp_htags, HTTP_HEADER_UPGRADE)) { 523 if (hctx->conf.upgrade && r->http_status == 101) { 524 /* 101 Switching Protocols; transition to transparent proxy */ 525 http_response_upgrade_read_body_unknown(r); 526 } 527 else { 528 light_bclr(r->resp_htags, HTTP_HEADER_UPGRADE); 529 #if 0 530 /* preserve prior questionable behavior; likely broken behavior 531 * anyway if backend thinks connection is being upgraded but client 532 * does not receive Connection: upgrade */ 533 http_header_response_unset(r, HTTP_HEADER_UPGRADE, 534 CONST_STR_LEN("Upgrade")); 535 #endif 536 } 537 } 538 539 if (hctx->conf.upgrade 540 && !light_btst(r->resp_htags, HTTP_HEADER_UPGRADE)) { 541 chunkqueue *cq = &r->reqbody_queue; 542 hctx->conf.upgrade = 0; 543 if (cq->bytes_out == (off_t)r->reqbody_length) { 544 cgi_connection_close_fdtocgi(hctx); /*(closes hctx->fdtocgi)*/ 545 } 546 } 547 548 return HANDLER_GO_ON; 549 } 550 551 552 __attribute_cold__ 553 __attribute_noinline__ 554 static handler_t cgi_local_redir(request_st * const r, handler_ctx * const hctx) { 555 buffer_clear(hctx->response); 556 chunk_buffer_yield(hctx->response); 557 http_response_reset(r); /*(includes r->http_status = 0)*/ 558 plugins_call_handle_request_reset(r); 559 /*cgi_connection_close(hctx);*//*(already cleaned up and hctx is now invalid)*/ 560 return HANDLER_COMEBACK; 561 } 562 563 564 static int cgi_recv_response(request_st * const r, handler_ctx * const hctx) { 565 const off_t bytes_in = r->write_queue.bytes_in; 566 switch (http_response_read(r, &hctx->opts, 567 hctx->response, hctx->fdn)) { 568 default: 569 if (r->write_queue.bytes_in > bytes_in) 570 hctx->read_ts = log_monotonic_secs; 571 return HANDLER_GO_ON; 572 case HANDLER_ERROR: 573 http_response_backend_error(r); 574 __attribute_fallthrough__ 575 case HANDLER_FINISHED: 576 cgi_connection_close(hctx); 577 return HANDLER_FINISHED; 578 case HANDLER_COMEBACK: 579 return cgi_local_redir(r, hctx); /* HANDLER_COMEBACK */ 580 } 581 } 582 583 584 static handler_t cgi_handle_fdevent(void *ctx, int revents) { 585 handler_ctx *hctx = ctx; 586 hctx->rd_revents |= revents; 587 joblist_append(hctx->con); 588 return HANDLER_FINISHED; 589 } 590 591 592 static handler_t cgi_process_rd_revents(handler_ctx * const hctx, request_st * const r, int revents) { 593 if (revents & FDEVENT_IN) { 594 handler_t rc = cgi_recv_response(r, hctx); /*(might invalidate hctx)*/ 595 if (rc != HANDLER_GO_ON) return rc; /*(unless HANDLER_GO_ON)*/ 596 } 597 598 /* perhaps this issue is already handled */ 599 if (revents & (FDEVENT_HUP|FDEVENT_RDHUP)) { 600 if (r->resp_body_started) { 601 /* drain any remaining data from kernel pipe buffers 602 * even if (r->conf.stream_response_body 603 * & FDEVENT_STREAM_RESPONSE_BUFMIN) 604 * since event loop will spin on fd FDEVENT_HUP event 605 * until unregistered. */ 606 handler_t rc; 607 const unsigned short flags = r->conf.stream_response_body; 608 r->conf.stream_response_body &= ~FDEVENT_STREAM_RESPONSE_BUFMIN; 609 r->conf.stream_response_body |= FDEVENT_STREAM_RESPONSE_POLLRDHUP; 610 do { 611 rc = cgi_recv_response(r,hctx); /*(might invalidate hctx)*/ 612 } while (rc == HANDLER_GO_ON); /*(unless HANDLER_GO_ON)*/ 613 r->conf.stream_response_body = flags; 614 return rc; /* HANDLER_FINISHED or HANDLER_COMEBACK or HANDLER_ERROR */ 615 } else if (!buffer_is_blank(hctx->response)) { 616 /* unfinished header package which is a body in reality */ 617 r->resp_body_started = 1; 618 if (0 != http_chunk_append_buffer(r, hctx->response)) { 619 cgi_connection_close(hctx); 620 return HANDLER_ERROR; 621 } 622 if (0 == r->http_status) r->http_status = 200; /* OK */ 623 } 624 cgi_connection_close(hctx); 625 } else if (revents & FDEVENT_ERR) { 626 /* kill all connections to the cgi process */ 627 cgi_connection_close(hctx); 628 return HANDLER_ERROR; 629 } 630 631 return HANDLER_GO_ON; 632 } 633 634 635 __attribute_cold__ 636 __attribute_noinline__ 637 static void cgi_env_offset_resize(env_accum *env) { 638 chunk_buffer_prepare_append(env->boffsets, env->boffsets->size*2); 639 env->offsets = (uintptr_t *)(void *)env->boffsets->ptr; 640 env->osize = env->boffsets->size/sizeof(*env->offsets); 641 } 642 643 static int cgi_env_add(void *venv, const char *key, size_t key_len, const char *val, size_t val_len) { 644 env_accum *env = venv; 645 646 if (!key || (!val && val_len)) return -1; 647 648 if (__builtin_expect( (env->osize == env->oused), 0)) 649 cgi_env_offset_resize(env); 650 env->offsets[env->oused++] = env->b->used-1; 651 652 char * const dst = buffer_extend(env->b, key_len + val_len + 2); 653 memcpy(dst, key, key_len); 654 dst[key_len] = '='; 655 if (val_len) memcpy(dst + key_len + 1, val, val_len); 656 dst[key_len + 1 + val_len] = '\0'; 657 658 return 0; 659 } 660 661 static int cgi_write_request(handler_ctx *hctx, int fd) { 662 request_st * const r = hctx->r; 663 chunkqueue *cq = &r->reqbody_queue; 664 chunk *c; 665 666 chunkqueue_remove_finished_chunks(cq); /* unnecessary? */ 667 668 /* old comment: windows doesn't support select() on pipes - wouldn't be easy to fix for all platforms. 669 */ 670 671 for (c = cq->first; c; c = cq->first) { 672 ssize_t wr = chunkqueue_write_chunk_to_pipe(fd, cq, r->conf.errh); 673 if (wr > 0) { 674 hctx->write_ts = log_monotonic_secs; 675 chunkqueue_mark_written(cq, wr); 676 /* continue if wrote whole chunk or wrote 16k block 677 * (see chunkqueue_write_chunk_file_intermed()) */ 678 if (c != cq->first || wr == 16384) 679 continue; 680 /*(else partial write)*/ 681 } 682 else if (wr < 0) { 683 switch(errno) { 684 case EAGAIN: 685 case EINTR: 686 /* ignore and try again later */ 687 break; 688 case EPIPE: 689 case ECONNRESET: 690 /* connection closed */ 691 log_error(r->conf.errh, __FILE__, __LINE__, 692 "failed to send post data to cgi, connection closed by CGI"); 693 /* skip all remaining data */ 694 chunkqueue_mark_written(cq, chunkqueue_length(cq)); 695 break; 696 default: 697 /* fatal error */ 698 log_perror(r->conf.errh, __FILE__, __LINE__, "write() failed"); 699 return -1; 700 } 701 } 702 /*if (0 == wr) break;*/ /*(might block)*/ 703 break; 704 } 705 706 if (cq->bytes_out == (off_t)r->reqbody_length && !hctx->conf.upgrade) { 707 /* sent all request body input */ 708 /* close connection to the cgi-script */ 709 if (-1 == hctx->fdtocgi) { /*(received request body sent in initial send to pipe buffer)*/ 710 --r->con->srv->cur_fds; 711 if (close(fd)) { 712 log_perror(r->conf.errh, __FILE__, __LINE__, "cgi stdin close %d failed", fd); 713 } 714 } else { 715 cgi_connection_close_fdtocgi(hctx); /*(closes only hctx->fdtocgi)*/ 716 } 717 } else { 718 off_t cqlen = chunkqueue_length(cq); 719 if (cq->bytes_in != r->reqbody_length && cqlen < 65536 - 16384) { 720 /*(r->conf.stream_request_body & FDEVENT_STREAM_REQUEST)*/ 721 if (!(r->conf.stream_request_body & FDEVENT_STREAM_REQUEST_POLLIN)) { 722 r->conf.stream_request_body |= FDEVENT_STREAM_REQUEST_POLLIN; 723 r->con->is_readable = 1; /* trigger optimistic read from client */ 724 } 725 } 726 struct fdevents * const ev = hctx->ev; 727 if (-1 == hctx->fdtocgi) { /*(not registered yet)*/ 728 hctx->fdtocgi = fd; 729 hctx->fdntocgi = fdevent_register(ev, hctx->fdtocgi, cgi_handle_fdevent_send, hctx); 730 } 731 if (0 == cqlen) { /*(chunkqueue_is_empty(cq))*/ 732 if ((fdevent_fdnode_interest(hctx->fdntocgi) & FDEVENT_OUT)) { 733 fdevent_fdnode_event_set(ev, hctx->fdntocgi, 0); 734 } 735 } else { 736 /* more request body remains to be sent to CGI so register for fdevents */ 737 hctx->write_ts = log_monotonic_secs; 738 fdevent_fdnode_event_set(ev, hctx->fdntocgi, FDEVENT_OUT); 739 } 740 } 741 742 return 0; 743 } 744 745 static int cgi_create_env(request_st * const r, plugin_data * const p, handler_ctx * const hctx, buffer * const cgi_handler) { 746 char *args[3]; 747 int to_cgi_fds[2]; 748 int from_cgi_fds[2]; 749 UNUSED(p); 750 751 if (!buffer_is_blank(cgi_handler)) { 752 if (NULL == stat_cache_path_stat(cgi_handler)) { 753 log_perror(r->conf.errh, __FILE__, __LINE__, 754 "stat for cgi-handler %s", cgi_handler->ptr); 755 return -1; 756 } 757 } 758 759 to_cgi_fds[0] = -1; 760 #ifndef __CYGWIN__ 761 if (0 == r->reqbody_length) { 762 /* future: might keep fd open in p->devnull for reuse 763 * and dup() here, or do not close() (later in this func) */ 764 to_cgi_fds[0] = fdevent_open_devnull(); 765 if (-1 == to_cgi_fds[0]) { 766 log_perror(r->conf.errh, __FILE__, __LINE__, "open /dev/null"); 767 return -1; 768 } 769 } 770 else if (!(r->conf.stream_request_body /*(if not streaming request body)*/ 771 & (FDEVENT_STREAM_REQUEST|FDEVENT_STREAM_REQUEST_BUFMIN))) { 772 chunkqueue * const cq = &r->reqbody_queue; 773 chunk * const c = cq->first; 774 if (c && c == cq->last && c->type == FILE_CHUNK && c->file.is_temp) { 775 /* request body in single tempfile if not streaming req body */ 776 if (-1 == c->file.fd 777 && 0 != chunkqueue_open_file_chunk(cq, r->conf.errh)) 778 return -1; 779 #ifdef __COVERITY__ 780 force_assert(-1 != c->file.fd); 781 #endif 782 if (-1 == lseek(c->file.fd, 0, SEEK_SET)) { 783 log_perror(r->conf.errh, __FILE__, __LINE__, 784 "lseek %s", c->mem->ptr); 785 return -1; 786 } 787 to_cgi_fds[0] = c->file.fd; 788 to_cgi_fds[1] = -1; 789 } 790 } 791 #endif 792 793 unsigned int bufsz_hint = 16384; 794 #ifdef _WIN32 795 if (r->reqbody_length <= 1048576) 796 bufsz_hint = (unsigned int)r->reqbody_length; 797 #endif 798 if (-1 == to_cgi_fds[0] && fdevent_pipe_cloexec(to_cgi_fds, bufsz_hint)) { 799 log_perror(r->conf.errh, __FILE__, __LINE__, "pipe failed"); 800 return -1; 801 } 802 if (fdevent_pipe_cloexec(from_cgi_fds, bufsz_hint)) { 803 if (0 == r->reqbody_length) { 804 close(to_cgi_fds[0]); 805 } 806 else if (-1 != to_cgi_fds[1]) { 807 close(to_cgi_fds[0]); 808 close(to_cgi_fds[1]); 809 } 810 log_perror(r->conf.errh, __FILE__, __LINE__, "pipe failed"); 811 return -1; 812 } 813 814 env_accum * const env = &p->env; 815 env->b = chunk_buffer_acquire(); 816 env->boffsets = chunk_buffer_acquire(); 817 buffer_truncate(env->b, 0); 818 char **envp; 819 { 820 size_t i = 0; 821 http_cgi_opts opts = { 0, 0, NULL, NULL }; 822 env->offsets = (uintptr_t *)(void *)env->boffsets->ptr; 823 env->osize = env->boffsets->size/sizeof(*env->offsets); 824 env->oused = 0; 825 826 /* create environment */ 827 828 http_cgi_headers(r, &opts, cgi_env_add, env); 829 830 /* for valgrind */ 831 if (p->env.ld_preload) { 832 cgi_env_add(env, CONST_STR_LEN("LD_PRELOAD"), BUF_PTR_LEN(p->env.ld_preload)); 833 } 834 if (p->env.ld_library_path) { 835 cgi_env_add(env, CONST_STR_LEN("LD_LIBRARY_PATH"), BUF_PTR_LEN(p->env.ld_library_path)); 836 } 837 #ifdef __CYGWIN__ 838 /* CYGWIN needs SYSTEMROOT */ 839 if (p->env.systemroot) { 840 cgi_env_add(env, CONST_STR_LEN("SYSTEMROOT"), BUF_PTR_LEN(p->env.systemroot)); 841 } 842 #endif 843 844 /* adjust (uintptr_t) offsets to (char *) ptr 845 * (stored as offsets while accumulating in buffer, 846 * in case buffer is reallocated during env creation) */ 847 if (__builtin_expect( (env->osize == env->oused), 0)) 848 cgi_env_offset_resize(env); 849 envp = (char **)env->offsets; 850 envp[env->oused] = NULL; 851 const uintptr_t baseptr = (uintptr_t)env->b->ptr; 852 for (i = 0; i < env->oused; ++i) 853 envp[i] += baseptr; 854 855 /* set up args */ 856 i = 0; 857 858 if (!buffer_is_blank(cgi_handler)) { 859 args[i++] = cgi_handler->ptr; 860 } 861 args[i++] = r->physical.path.ptr; 862 args[i ] = NULL; 863 } 864 865 int dfd = fdevent_open_dirname(r->physical.path.ptr,r->conf.follow_symlink); 866 if (-1 == dfd) { 867 log_perror(r->conf.errh, __FILE__, __LINE__, "open dirname %s failed", r->physical.path.ptr); 868 } 869 870 int serrh_fd = r->conf.serrh ? r->conf.serrh->fd : -1; 871 pid_t pid = (dfd >= 0) 872 ? fdevent_fork_execve(args[0], args, envp, 873 to_cgi_fds[0], from_cgi_fds[1], serrh_fd, dfd) 874 : -1; 875 876 chunk_buffer_release(env->boffsets); 877 chunk_buffer_release(env->b); 878 env->boffsets = NULL; 879 env->b = NULL; 880 881 if (-1 == pid) { 882 /* log error with errno prior to calling close() (might change errno) */ 883 log_perror(r->conf.errh, __FILE__, __LINE__, "fork failed"); 884 if (-1 != dfd) close(dfd); 885 close(from_cgi_fds[0]); 886 close(from_cgi_fds[1]); 887 if (0 == r->reqbody_length) { 888 close(to_cgi_fds[0]); 889 } 890 else if (-1 != to_cgi_fds[1]) { 891 close(to_cgi_fds[0]); 892 close(to_cgi_fds[1]); 893 } 894 return -1; 895 } else { 896 if (-1 != dfd) close(dfd); 897 close(from_cgi_fds[1]); 898 899 hctx->fd = from_cgi_fds[0]; 900 hctx->cgi_pid = cgi_pid_add(p, pid, hctx); 901 902 if (0 == r->reqbody_length) { 903 close(to_cgi_fds[0]); 904 } 905 else if (-1 == to_cgi_fds[1]) { 906 chunkqueue * const cq = &r->reqbody_queue; 907 chunkqueue_mark_written(cq, chunkqueue_length(cq)); 908 } 909 else if (0 == fdevent_fcntl_set_nb(to_cgi_fds[1]) 910 && 0 == cgi_write_request(hctx, to_cgi_fds[1])) { 911 close(to_cgi_fds[0]); 912 ++r->con->srv->cur_fds; 913 } 914 else { 915 close(to_cgi_fds[0]); 916 close(to_cgi_fds[1]); 917 /*(hctx->fd not yet registered with fdevent, so manually 918 * cleanup here; see fdevent_register() further below)*/ 919 close(hctx->fd); 920 hctx->fd = -1; 921 cgi_connection_close(hctx); 922 return -1; 923 } 924 925 ++r->con->srv->cur_fds; 926 927 struct fdevents * const ev = hctx->ev; 928 hctx->fdn = fdevent_register(ev, hctx->fd, cgi_handle_fdevent, hctx); 929 if (-1 == fdevent_fcntl_set_nb(hctx->fd)) { 930 log_perror(r->conf.errh, __FILE__, __LINE__, "fcntl failed"); 931 cgi_connection_close(hctx); 932 return -1; 933 } 934 hctx->read_ts = log_monotonic_secs; 935 fdevent_fdnode_event_set(ev, hctx->fdn, FDEVENT_IN | FDEVENT_RDHUP); 936 937 return 0; 938 } 939 } 940 941 URIHANDLER_FUNC(cgi_is_handled) { 942 plugin_data *p = p_d; 943 const stat_cache_st *st; 944 data_string *ds; 945 946 if (NULL != r->handler_module) return HANDLER_GO_ON; 947 /* r->physical.path is non-empty for handle_subrequest_start */ 948 /*if (buffer_is_blank(&r->physical.path)) return HANDLER_GO_ON;*/ 949 950 mod_cgi_patch_config(r, p); 951 if (NULL == p->conf.cgi) return HANDLER_GO_ON; 952 953 ds = (data_string *)array_match_key_suffix(p->conf.cgi, &r->physical.path); 954 if (NULL == ds) return HANDLER_GO_ON; 955 956 /* r->tmp_sce is set in http_response_physical_path_check() and is valid 957 * in handle_subrequest_start callback -- handle_subrequest_start callbacks 958 * should not change r->physical.path (or should invalidate r->tmp_sce) */ 959 st = r->tmp_sce && buffer_is_equal(&r->tmp_sce->name, &r->physical.path) 960 ? &r->tmp_sce->st 961 : stat_cache_path_stat(&r->physical.path); 962 if (NULL == st) return HANDLER_GO_ON; 963 964 /* (aside: CGI might be executable even if it is not readable) */ 965 if (!S_ISREG(st->st_mode)) return HANDLER_GO_ON; 966 if (p->conf.execute_x_only == 1 && (st->st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) == 0) return HANDLER_GO_ON; 967 968 if (r->reqbody_length 969 && p->tempfile_accum 970 && !(r->conf.stream_request_body /*(if not streaming request body)*/ 971 & (FDEVENT_STREAM_REQUEST|FDEVENT_STREAM_REQUEST_BUFMIN))) { 972 /* store request body in single tempfile if not streaming request body*/ 973 r->reqbody_queue.upload_temp_file_size = INTMAX_MAX; 974 } 975 976 { 977 handler_ctx *hctx = cgi_handler_ctx_init(); 978 hctx->ev = r->con->srv->ev; 979 hctx->r = r; 980 hctx->con = r->con; 981 hctx->plugin_data = p; 982 hctx->cgi_handler = &ds->value; 983 memcpy(&hctx->conf, &p->conf, sizeof(plugin_config)); 984 if (!light_btst(r->rqst_htags, HTTP_HEADER_UPGRADE)) 985 hctx->conf.upgrade = 0; 986 else if (!hctx->conf.upgrade || r->http_version != HTTP_VERSION_1_1) { 987 hctx->conf.upgrade = 0; 988 http_header_request_unset(r, HTTP_HEADER_UPGRADE, 989 CONST_STR_LEN("Upgrade")); 990 } 991 hctx->opts.max_per_read = 992 !(r->conf.stream_response_body /*(if not streaming response body)*/ 993 & (FDEVENT_STREAM_RESPONSE|FDEVENT_STREAM_RESPONSE_BUFMIN)) 994 ? 262144 995 : (r->conf.stream_response_body & FDEVENT_STREAM_RESPONSE_BUFMIN) 996 ? 16384 /* FDEVENT_STREAM_RESPONSE_BUFMIN */ 997 : 65536; /* FDEVENT_STREAM_RESPONSE */ 998 hctx->opts.fdfmt = S_IFIFO; 999 hctx->opts.backend = BACKEND_CGI; 1000 hctx->opts.authorizer = 0; 1001 hctx->opts.local_redir = hctx->conf.local_redir; 1002 hctx->opts.xsendfile_allow = hctx->conf.xsendfile_allow; 1003 hctx->opts.xsendfile_docroot = hctx->conf.xsendfile_docroot; 1004 hctx->opts.pdata = hctx; 1005 hctx->opts.headers = cgi_response_headers; 1006 r->plugin_ctx[p->id] = hctx; 1007 r->handler_module = p->self; 1008 } 1009 1010 return HANDLER_GO_ON; 1011 } 1012 1013 /* 1014 * - HANDLER_GO_ON : not our job 1015 * - HANDLER_FINISHED: got response 1016 * - HANDLER_WAIT_FOR_EVENT: waiting for response 1017 */ 1018 SUBREQUEST_FUNC(mod_cgi_handle_subrequest) { 1019 plugin_data * const p = p_d; 1020 handler_ctx * const hctx = r->plugin_ctx[p->id]; 1021 if (NULL == hctx) return HANDLER_GO_ON; 1022 1023 if (__builtin_expect( 1024 (r->conf.stream_request_body & FDEVENT_STREAM_REQUEST_TCP_FIN), 0) 1025 && hctx->conf.limits && hctx->conf.limits->signal_fin) { 1026 /* XXX: consider setting r->http_status = 499 if (0 == r->http_status) 1027 * (499 is nginx custom status to indicate client closed connection) */ 1028 if (-1 == hctx->fd) return HANDLER_ERROR; /*(CGI not yet spawned)*/ 1029 if (hctx->cgi_pid) /* send signal to notify CGI about TCP FIN */ 1030 cgi_pid_kill(hctx->cgi_pid, hctx->conf.limits->signal_fin); 1031 } 1032 1033 const int rd_revents = hctx->rd_revents; 1034 const int wr_revents = hctx->wr_revents; 1035 if (rd_revents) { 1036 hctx->rd_revents = 0; 1037 handler_t rc = cgi_process_rd_revents(hctx, r, rd_revents); 1038 if (rc != HANDLER_GO_ON) return rc; /*(might invalidate hctx)*/ 1039 } 1040 if (wr_revents) { 1041 hctx->wr_revents = 0; 1042 handler_t rc = cgi_process_wr_revents(hctx, r, wr_revents); 1043 if (rc != HANDLER_GO_ON) return rc; /*(might invalidate hctx)*/ 1044 } 1045 1046 if ((r->conf.stream_response_body & FDEVENT_STREAM_RESPONSE_BUFMIN) 1047 && r->resp_body_started) { 1048 if (chunkqueue_length(&r->write_queue) > 65536 - 4096) { 1049 fdevent_fdnode_event_clr(hctx->ev, hctx->fdn, FDEVENT_IN); 1050 } else if (!(fdevent_fdnode_interest(hctx->fdn) & FDEVENT_IN)) { 1051 /* optimistic read from backend */ 1052 handler_t rc = cgi_recv_response(r, hctx); /*(might invalidate hctx)*/ 1053 if (rc != HANDLER_GO_ON) return rc; /*(unless HANDLER_GO_ON)*/ 1054 hctx->read_ts = log_monotonic_secs; 1055 fdevent_fdnode_event_add(hctx->ev, hctx->fdn, FDEVENT_IN); 1056 } 1057 } 1058 1059 chunkqueue * const cq = &r->reqbody_queue; 1060 1061 if (cq->bytes_in != (off_t)r->reqbody_length) { 1062 /*(64k - 4k to attempt to avoid temporary files 1063 * in conjunction with FDEVENT_STREAM_REQUEST_BUFMIN)*/ 1064 if (chunkqueue_length(cq) > 65536 - 4096 1065 && (r->conf.stream_request_body & FDEVENT_STREAM_REQUEST_BUFMIN)){ 1066 r->conf.stream_request_body &= ~FDEVENT_STREAM_REQUEST_POLLIN; 1067 if (-1 != hctx->fd) return HANDLER_WAIT_FOR_EVENT; 1068 } else { 1069 handler_t rc = r->con->reqbody_read(r); 1070 if (!chunkqueue_is_empty(cq)) { 1071 if (fdevent_fdnode_interest(hctx->fdntocgi) & FDEVENT_OUT) { 1072 return (rc == HANDLER_GO_ON) ? HANDLER_WAIT_FOR_EVENT : rc; 1073 } 1074 } 1075 if (rc != HANDLER_GO_ON) return rc; 1076 1077 /* CGI environment requires that Content-Length be set. 1078 * Send 411 Length Required if Content-Length missing. 1079 * (occurs here if client sends Transfer-Encoding: chunked 1080 * and module is flagged to stream request body to backend) */ 1081 if (-1 == r->reqbody_length) { 1082 return (r->conf.stream_request_body & FDEVENT_STREAM_REQUEST) 1083 ? http_response_reqbody_read_error(r, 411) 1084 : HANDLER_WAIT_FOR_EVENT; 1085 } 1086 } 1087 } 1088 1089 if (-1 == hctx->fd) { 1090 if (cgi_create_env(r, p, hctx, hctx->cgi_handler)) { 1091 r->http_status = 500; 1092 r->handler_module = NULL; 1093 1094 return HANDLER_FINISHED; 1095 } 1096 } else if (!chunkqueue_is_empty(cq)) { 1097 if (0 != cgi_write_request(hctx, hctx->fdtocgi)) { 1098 cgi_connection_close(hctx); 1099 return HANDLER_ERROR; 1100 } 1101 } 1102 1103 /* if not done, wait for CGI to close stdout, so we read EOF on pipe */ 1104 return HANDLER_WAIT_FOR_EVENT; 1105 } 1106 1107 1108 __attribute_cold__ 1109 __attribute_noinline__ 1110 static void cgi_trigger_hctx_timeout(handler_ctx * const hctx, const char * const msg) { 1111 request_st * const r = hctx->r; 1112 joblist_append(r->con); 1113 1114 log_error(r->conf.errh, __FILE__, __LINE__, 1115 "%s timeout on CGI: %s (pid: %lld)", 1116 msg, r->physical.path.ptr, (long long)hctx->cgi_pid->pid); 1117 1118 if (*msg == 'w') { /* "write" */ 1119 /* theoretically, response might be waiting on hctx->fdn pipe 1120 * if it arrived since we last checked for event, and if CGI 1121 * timeout out while reading (or did not read) request body */ 1122 handler_t rc = cgi_recv_response(r, hctx); /*(might invalidate hctx)*/ 1123 if (rc != HANDLER_GO_ON) return; /*(unless HANDLER_GO_ON)*/ 1124 } 1125 1126 if (0 == r->http_status) r->http_status = 504; /* Gateway Timeout */ 1127 cgi_connection_close(hctx); 1128 } 1129 1130 1131 static handler_t cgi_trigger_cb(server *srv, void *p_d) { 1132 UNUSED(srv); 1133 const unix_time64_t mono = log_monotonic_secs; 1134 plugin_data * const p = p_d; 1135 for (cgi_pid_t *cgi_pid = p->cgi_pid; cgi_pid; cgi_pid = cgi_pid->next) { 1136 /*(hctx stays in cgi_pid list until process pid is reaped, 1137 * so p->cgi_pid[] is not modified during this loop)*/ 1138 handler_ctx * const hctx = cgi_pid->hctx; 1139 if (!hctx) continue; /*(already called cgi_pid_kill())*/ 1140 const cgi_limits * const limits = hctx->conf.limits; 1141 if (NULL == limits) continue; 1142 if (limits->read_timeout && hctx->fdn 1143 && (fdevent_fdnode_interest(hctx->fdn) & FDEVENT_IN) 1144 && mono - hctx->read_ts > limits->read_timeout) { 1145 cgi_trigger_hctx_timeout(hctx, "read"); 1146 continue; 1147 } 1148 if (limits->write_timeout && hctx->fdntocgi 1149 && (fdevent_fdnode_interest(hctx->fdntocgi) & FDEVENT_OUT) 1150 && mono - hctx->write_ts > limits->write_timeout) { 1151 cgi_trigger_hctx_timeout(hctx, "write"); 1152 continue; 1153 } 1154 } 1155 return HANDLER_GO_ON; 1156 } 1157 1158 1159 static handler_t cgi_waitpid_cb(server *srv, void *p_d, pid_t pid, int status) { 1160 /*(XXX: if supporting a large number of CGI, might use a different algorithm 1161 * instead of linked list, e.g. splaytree indexed with pid)*/ 1162 plugin_data *p = (plugin_data *)p_d; 1163 for (cgi_pid_t *cgi_pid = p->cgi_pid; cgi_pid; cgi_pid = cgi_pid->next) { 1164 if (pid != cgi_pid->pid) continue; 1165 1166 handler_ctx * const hctx = cgi_pid->hctx; 1167 if (hctx) hctx->cgi_pid = NULL; 1168 1169 if (WIFEXITED(status)) { 1170 /* (skip logging (non-zero) CGI exit; might be very noisy) */ 1171 } 1172 else if (WIFSIGNALED(status)) { 1173 /* ignore SIGTERM if sent by cgi_connection_close() (NULL == hctx)*/ 1174 if (WTERMSIG(status) != cgi_pid->signal_sent) { 1175 log_error_st *errh = hctx ? hctx->r->conf.errh : srv->errh; 1176 log_error(errh, __FILE__, __LINE__, 1177 "CGI pid %d died with signal %d", pid, WTERMSIG(status)); 1178 } 1179 } 1180 else { 1181 log_error_st *errh = hctx ? hctx->r->conf.errh : srv->errh; 1182 log_error(errh, __FILE__, __LINE__, 1183 "CGI pid %d ended unexpectedly", pid); 1184 } 1185 1186 cgi_pid_del(p, cgi_pid); 1187 return HANDLER_FINISHED; 1188 } 1189 1190 return HANDLER_GO_ON; 1191 } 1192 1193 1194 int mod_cgi_plugin_init(plugin *p); 1195 int mod_cgi_plugin_init(plugin *p) { 1196 p->version = LIGHTTPD_VERSION_ID; 1197 p->name = "cgi"; 1198 1199 p->handle_request_reset = cgi_connection_close_callback; 1200 p->handle_subrequest_start = cgi_is_handled; 1201 p->handle_subrequest = mod_cgi_handle_subrequest; 1202 p->handle_trigger = cgi_trigger_cb; 1203 p->handle_waitpid = cgi_waitpid_cb; 1204 p->init = mod_cgi_init; 1205 p->cleanup = mod_cgi_free; 1206 p->set_defaults = mod_cgi_set_defaults; 1207 1208 return 0; 1209 } 1210