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