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