xref: /lighttpd1.4/src/mod_cgi.c (revision 7967fdf5)
18abd06a7SGlenn Strauss #include "first.h"
28abd06a7SGlenn Strauss 
304d76e7aSGlenn Strauss #include "base.h"
422e8b456SStefan Bühler #include "stat_cache.h"
5c56b2108SGlenn Strauss #include "http_kv.h"
67b615d5dSGlenn Strauss #include "fdlog.h"
722e8b456SStefan Bühler #include "log.h"
81f23ba9aSGlenn Strauss #include "response.h"
9c95f832fSGlenn Strauss #include "http_cgi.h"
1022e8b456SStefan Bühler #include "http_chunk.h"
113dd3cde9SGlenn Strauss #include "http_header.h"
1222e8b456SStefan Bühler 
1322e8b456SStefan Bühler #include "plugin.h"
1422e8b456SStefan Bühler 
15bcdc6a3bSJan Kneschke #include <sys/types.h>
16a53f662aSGlenn Strauss #include "sys-socket.h"
1729f50bdeSGlenn Strauss #ifdef HAVE_SYS_WAIT_H
18bcdc6a3bSJan Kneschke #include <sys/wait.h>
1929f50bdeSGlenn Strauss #endif
20bcdc6a3bSJan Kneschke 
21bcdc6a3bSJan Kneschke #include <unistd.h>
22bcdc6a3bSJan Kneschke #include <errno.h>
23bcdc6a3bSJan Kneschke #include <stdlib.h>
24bcdc6a3bSJan Kneschke #include <string.h>
25bcdc6a3bSJan Kneschke #include <fdevent.h>
26bcdc6a3bSJan Kneschke 
276e193e40SJan Kneschke #include <fcntl.h>
28c5865d4fSGlenn Strauss #include <signal.h>
29bcdc6a3bSJan Kneschke 
30bcdc6a3bSJan Kneschke typedef struct {
317b3a4f13SGlenn Strauss 	uintptr_t *offsets;
32407b4d14SGlenn Strauss 	size_t osize;
33407b4d14SGlenn Strauss 	size_t oused;
347b3a4f13SGlenn Strauss 	buffer *b;
357b3a4f13SGlenn Strauss 	buffer *boffsets;
362d914758SGlenn Strauss 	buffer *ld_preload;
372d914758SGlenn Strauss 	buffer *ld_library_path;
382d914758SGlenn Strauss       #ifdef __CYGWIN__
392d914758SGlenn Strauss 	buffer *systemroot;
402d914758SGlenn Strauss       #endif
41407b4d14SGlenn Strauss } env_accum;
42bcdc6a3bSJan Kneschke 
43bcdc6a3bSJan Kneschke typedef struct {
448c36615fSGlenn Strauss 	unix_time64_t read_timeout;
458c36615fSGlenn Strauss 	unix_time64_t write_timeout;
4633ffec35SGlenn Strauss 	int signal_fin;
478c36615fSGlenn Strauss } cgi_limits;
488c36615fSGlenn Strauss 
498c36615fSGlenn Strauss typedef struct {
50131450daSGlenn Strauss 	const array *cgi;
518c36615fSGlenn Strauss 	const cgi_limits *limits;
523c117059SStefan Bühler 	unsigned short execute_x_only;
5357ab20acSGlenn Strauss 	unsigned short local_redir;
541f23ba9aSGlenn Strauss 	unsigned short xsendfile_allow;
55574fb562SGlenn Strauss 	unsigned short upgrade;
56131450daSGlenn Strauss 	const array *xsendfile_docroot;
57bcdc6a3bSJan Kneschke } plugin_config;
58bcdc6a3bSJan Kneschke 
597e000de0SGlenn Strauss struct cgi_pid_t;
607e000de0SGlenn Strauss 
61bcdc6a3bSJan Kneschke typedef struct {
62bcdc6a3bSJan Kneschke 	PLUGIN_DATA;
63131450daSGlenn Strauss 	plugin_config defaults;
64bcdc6a3bSJan Kneschke 	plugin_config conf;
6570195d06SGlenn Strauss 	int tempfile_accum;
667e000de0SGlenn Strauss 	struct cgi_pid_t *cgi_pid;
67407b4d14SGlenn Strauss 	env_accum env;
68bcdc6a3bSJan Kneschke } plugin_data;
69bcdc6a3bSJan Kneschke 
70bcdc6a3bSJan Kneschke typedef struct {
717e000de0SGlenn Strauss 	struct cgi_pid_t *cgi_pid;
72bcdc6a3bSJan Kneschke 	int fd;
737c0f8a77SGlenn Strauss 	int fdtocgi;
74dd23fcb2SGlenn Strauss 	int rd_revents;
75dd23fcb2SGlenn Strauss 	int wr_revents;
769113011dSGlenn Strauss 	fdnode *fdn;
779113011dSGlenn Strauss 	fdnode *fdntocgi;
78bcdc6a3bSJan Kneschke 
797c7f8c46SGlenn Strauss 	request_st *r;
80dd23fcb2SGlenn Strauss 	connection *con;          /* dumb pointer */
817c7f8c46SGlenn Strauss 	struct fdevents *ev;      /* dumb pointer */
82bcdc6a3bSJan Kneschke 	plugin_data *plugin_data; /* dumb pointer */
83bcdc6a3bSJan Kneschke 
84bcdc6a3bSJan Kneschke 	buffer *response;
858c36615fSGlenn Strauss 	unix_time64_t read_ts;
868c36615fSGlenn Strauss 	unix_time64_t write_ts;
874d7f5737SGlenn Strauss 	buffer *cgi_handler;      /* dumb pointer */
880a635fc8SGlenn Strauss 	http_response_opts opts;
895bf5e1adSGlenn Strauss 	plugin_config conf;
90ce9e0dfcSGlenn Strauss 	off_t orig_reqbody_length;
91bcdc6a3bSJan Kneschke } handler_ctx;
92bcdc6a3bSJan Kneschke 
937e000de0SGlenn Strauss typedef struct cgi_pid_t {
947e000de0SGlenn Strauss 	pid_t pid;
9533ffec35SGlenn Strauss 	int signal_sent;
967e000de0SGlenn Strauss 	handler_ctx *hctx;
977e000de0SGlenn Strauss 	struct cgi_pid_t *next;
987e000de0SGlenn Strauss 	struct cgi_pid_t *prev;
997e000de0SGlenn Strauss } cgi_pid_t;
1007e000de0SGlenn Strauss 
cgi_handler_ctx_init(void)1010c6a5645SCyril Brulebois static handler_ctx * cgi_handler_ctx_init(void) {
1025e14db43SGlenn Strauss 	handler_ctx *hctx = ck_calloc(1, sizeof(*hctx));
1033d8d56d8SGlenn Strauss 	hctx->response = chunk_buffer_acquire();
104bbbbfb3dSGlenn Strauss 	hctx->fd = -1;
1057c0f8a77SGlenn Strauss 	hctx->fdtocgi = -1;
106bcdc6a3bSJan Kneschke 	return hctx;
107bcdc6a3bSJan Kneschke }
108bcdc6a3bSJan Kneschke 
cgi_handler_ctx_free(handler_ctx * hctx)109bcdc6a3bSJan Kneschke static void cgi_handler_ctx_free(handler_ctx *hctx) {
1103d8d56d8SGlenn Strauss 	chunk_buffer_release(hctx->response);
111bcdc6a3bSJan Kneschke 	free(hctx);
112bcdc6a3bSJan Kneschke }
113bcdc6a3bSJan Kneschke 
INIT_FUNC(mod_cgi_init)114bcdc6a3bSJan Kneschke INIT_FUNC(mod_cgi_init) {
1155e14db43SGlenn Strauss 	plugin_data * const p = ck_calloc(1, sizeof(*p));
1162d914758SGlenn Strauss 	const char *s;
117bcdc6a3bSJan Kneschke 
1182d914758SGlenn Strauss 	/* for valgrind */
1192d914758SGlenn Strauss 	s = getenv("LD_PRELOAD");
120bade1c03SGlenn Strauss 	if (s) buffer_copy_string((p->env.ld_preload = buffer_init()), s);
1212d914758SGlenn Strauss 	s = getenv("LD_LIBRARY_PATH");
122bade1c03SGlenn Strauss 	if (s) buffer_copy_string((p->env.ld_library_path = buffer_init()), s);
1232d914758SGlenn Strauss       #ifdef __CYGWIN__
1242d914758SGlenn Strauss 	/* CYGWIN needs SYSTEMROOT */
1252d914758SGlenn Strauss 	s = getenv("SYSTEMROOT");
126bade1c03SGlenn Strauss 	if (s) buffer_copy_string((p->env.systemroot = buffer_init()), s);
1272d914758SGlenn Strauss       #endif
1282d914758SGlenn Strauss 
129bcdc6a3bSJan Kneschke 	return p;
130bcdc6a3bSJan Kneschke }
131bcdc6a3bSJan Kneschke 
132bcdc6a3bSJan Kneschke 
FREE_FUNC(mod_cgi_free)133bcdc6a3bSJan Kneschke FREE_FUNC(mod_cgi_free) {
134bcdc6a3bSJan Kneschke 	plugin_data *p = p_d;
1352d914758SGlenn Strauss 	buffer_free(p->env.ld_preload);
1362d914758SGlenn Strauss 	buffer_free(p->env.ld_library_path);
1372d914758SGlenn Strauss       #ifdef __CYGWIN__
1382d914758SGlenn Strauss 	buffer_free(p->env.systemroot);
1392d914758SGlenn Strauss       #endif
1408c36615fSGlenn Strauss 
1417e000de0SGlenn Strauss     for (cgi_pid_t *cgi_pid = p->cgi_pid, *next; cgi_pid; cgi_pid = next) {
1427e000de0SGlenn Strauss         next = cgi_pid->next;
1437e000de0SGlenn Strauss         free(cgi_pid);
1447e000de0SGlenn Strauss     }
1457e000de0SGlenn Strauss 
1468c36615fSGlenn Strauss     if (NULL == p->cvlist) return;
1478c36615fSGlenn Strauss     /* (init i to 0 if global context; to 1 to skip empty global context) */
1488c36615fSGlenn Strauss     for (int i = !p->cvlist[0].v.u2[1], used = p->nconfig; i < used; ++i) {
1498c36615fSGlenn Strauss         config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0];
1508c36615fSGlenn Strauss         for (; -1 != cpv->k_id; ++cpv) {
1518c36615fSGlenn Strauss             if (cpv->vtype != T_CONFIG_LOCAL || NULL == cpv->v.v) continue;
1528c36615fSGlenn Strauss             switch (cpv->k_id) {
1538c36615fSGlenn Strauss               case 6: /* cgi.limits */
1548c36615fSGlenn Strauss                 free(cpv->v.v);
1558c36615fSGlenn Strauss                 break;
1568c36615fSGlenn Strauss               default:
1578c36615fSGlenn Strauss                 break;
1588c36615fSGlenn Strauss             }
1598c36615fSGlenn Strauss         }
1608c36615fSGlenn Strauss     }
161bcdc6a3bSJan Kneschke }
162bcdc6a3bSJan Kneschke 
mod_cgi_merge_config_cpv(plugin_config * const pconf,const config_plugin_value_t * const cpv)163131450daSGlenn Strauss static void mod_cgi_merge_config_cpv(plugin_config * const pconf, const config_plugin_value_t * const cpv) {
164131450daSGlenn Strauss     switch (cpv->k_id) { /* index into static config_plugin_keys_t cpk[] */
165131450daSGlenn Strauss       case 0: /* cgi.assign */
166131450daSGlenn Strauss         pconf->cgi = cpv->v.a;
167131450daSGlenn Strauss         break;
168131450daSGlenn Strauss       case 1: /* cgi.execute-x-only */
169131450daSGlenn Strauss         pconf->execute_x_only = (unsigned short)cpv->v.u;
170131450daSGlenn Strauss         break;
171131450daSGlenn Strauss       case 2: /* cgi.x-sendfile */
172131450daSGlenn Strauss         pconf->xsendfile_allow = (unsigned short)cpv->v.u;
173131450daSGlenn Strauss         break;
174131450daSGlenn Strauss       case 3: /* cgi.x-sendfile-docroot */
175131450daSGlenn Strauss         pconf->xsendfile_docroot = cpv->v.a;
176131450daSGlenn Strauss         break;
177131450daSGlenn Strauss       case 4: /* cgi.local-redir */
178131450daSGlenn Strauss         pconf->local_redir = (unsigned short)cpv->v.u;
179131450daSGlenn Strauss         break;
180131450daSGlenn Strauss       case 5: /* cgi.upgrade */
181131450daSGlenn Strauss         pconf->upgrade = (unsigned short)cpv->v.u;
182131450daSGlenn Strauss         break;
1838c36615fSGlenn Strauss       case 6: /* cgi.limits */
1848c36615fSGlenn Strauss         if (cpv->vtype != T_CONFIG_LOCAL) break;
1858c36615fSGlenn Strauss         pconf->limits = cpv->v.v;
1868c36615fSGlenn Strauss         break;
187131450daSGlenn Strauss       default:/* should not happen */
188131450daSGlenn Strauss         return;
189131450daSGlenn Strauss     }
190131450daSGlenn Strauss }
191bcdc6a3bSJan Kneschke 
mod_cgi_merge_config(plugin_config * const pconf,const config_plugin_value_t * cpv)192131450daSGlenn Strauss static void mod_cgi_merge_config(plugin_config * const pconf, const config_plugin_value_t *cpv) {
193131450daSGlenn Strauss     do {
194131450daSGlenn Strauss         mod_cgi_merge_config_cpv(pconf, cpv);
195131450daSGlenn Strauss     } while ((++cpv)->k_id != -1);
196131450daSGlenn Strauss }
197131450daSGlenn Strauss 
mod_cgi_patch_config(request_st * const r,plugin_data * const p)1987c7f8c46SGlenn Strauss static void mod_cgi_patch_config(request_st * const r, plugin_data * const p) {
199cc2134c8SGlenn Strauss     p->conf = p->defaults; /* copy small struct instead of memcpy() */
200cc2134c8SGlenn Strauss     /*memcpy(&p->conf, &p->defaults, sizeof(plugin_config));*/
201131450daSGlenn Strauss     for (int i = 1, used = p->nconfig; i < used; ++i) {
2027c7f8c46SGlenn Strauss         if (config_check_cond(r, (uint32_t)p->cvlist[i].k_id))
203131450daSGlenn Strauss             mod_cgi_merge_config(&p->conf, p->cvlist + p->cvlist[i].v.u2[0]);
204131450daSGlenn Strauss     }
205131450daSGlenn Strauss }
206131450daSGlenn Strauss 
20733ffec35SGlenn Strauss __attribute_cold__
20833ffec35SGlenn Strauss __attribute_pure__
mod_cgi_str_to_signal(const char * s,int default_sig)20933ffec35SGlenn Strauss static int mod_cgi_str_to_signal (const char *s, int default_sig) {
21033ffec35SGlenn Strauss     static const struct { const char *name; int sig; } sigs[] = {
21133ffec35SGlenn Strauss       { "HUP",  SIGHUP  }
21233ffec35SGlenn Strauss      ,{ "INT",  SIGINT  }
21333ffec35SGlenn Strauss      ,{ "QUIT", SIGQUIT }
21433ffec35SGlenn Strauss      ,{ "ILL",  SIGILL  }
21533ffec35SGlenn Strauss      ,{ "TRAP", SIGTRAP }
21633ffec35SGlenn Strauss      ,{ "ABRT", SIGABRT }
21733ffec35SGlenn Strauss      #ifdef SIGBUS
21833ffec35SGlenn Strauss      ,{ "BUS",  SIGBUS  }
21933ffec35SGlenn Strauss      #endif
22033ffec35SGlenn Strauss      ,{ "FPE",  SIGFPE  }
22133ffec35SGlenn Strauss      ,{ "KILL", SIGKILL }
22233ffec35SGlenn Strauss      #ifdef SIGUSR1
22333ffec35SGlenn Strauss      ,{ "USR1", SIGUSR1 }
22433ffec35SGlenn Strauss      #endif
22533ffec35SGlenn Strauss      ,{ "SEGV", SIGSEGV }
22633ffec35SGlenn Strauss      #ifdef SIGUSR2
22733ffec35SGlenn Strauss      ,{ "USR2", SIGUSR2 }
22833ffec35SGlenn Strauss      #endif
22933ffec35SGlenn Strauss      ,{ "PIPE", SIGPIPE }
23033ffec35SGlenn Strauss      ,{ "ALRM", SIGALRM }
23133ffec35SGlenn Strauss      ,{ "TERM", SIGTERM }
23233ffec35SGlenn Strauss      #ifdef SIGCHLD
23333ffec35SGlenn Strauss      ,{ "CHLD", SIGCHLD }
23433ffec35SGlenn Strauss      #endif
23533ffec35SGlenn Strauss      #ifdef SIGCONT
23633ffec35SGlenn Strauss      ,{ "CONT", SIGCONT }
23733ffec35SGlenn Strauss      #endif
23833ffec35SGlenn Strauss      #ifdef SIGURG
23933ffec35SGlenn Strauss      ,{ "URG",  SIGURG  }
24033ffec35SGlenn Strauss      #endif
24133ffec35SGlenn Strauss      #ifdef SIGXCPU
24233ffec35SGlenn Strauss      ,{ "XCPU", SIGXCPU }
24333ffec35SGlenn Strauss      #endif
24433ffec35SGlenn Strauss      #ifdef SIGXFSZ
24533ffec35SGlenn Strauss      ,{ "XFSZ", SIGXFSZ }
24633ffec35SGlenn Strauss      #endif
24733ffec35SGlenn Strauss      #ifdef SIGWINCH
24833ffec35SGlenn Strauss      ,{ "WINCH",SIGWINCH}
24933ffec35SGlenn Strauss      #endif
25033ffec35SGlenn Strauss      #ifdef SIGPOLL
25133ffec35SGlenn Strauss      ,{ "POLL", SIGPOLL }
25233ffec35SGlenn Strauss      #endif
25333ffec35SGlenn Strauss      #ifdef SIGIO
25433ffec35SGlenn Strauss      ,{ "IO",   SIGIO   }
25533ffec35SGlenn Strauss      #endif
25633ffec35SGlenn Strauss     };
25733ffec35SGlenn Strauss 
25833ffec35SGlenn Strauss     if (s[0] == 'S' && s[1] == 'I' && s[2] == 'G') s += 3; /*("SIG" prefix)*/
25933ffec35SGlenn Strauss     for (uint32_t i = 0; i < sizeof(sigs)/sizeof(*sigs); ++i) {
26033ffec35SGlenn Strauss         if (0 == strcmp(s, sigs[i].name)) return sigs[i].sig;
26133ffec35SGlenn Strauss     }
26233ffec35SGlenn Strauss     return default_sig;
26333ffec35SGlenn Strauss }
26433ffec35SGlenn Strauss 
mod_cgi_parse_limits(const array * const a,log_error_st * const errh)2658c36615fSGlenn Strauss static cgi_limits * mod_cgi_parse_limits(const array * const a, log_error_st * const errh) {
2665e14db43SGlenn Strauss     cgi_limits * const limits = ck_calloc(1, sizeof(cgi_limits));
2678c36615fSGlenn Strauss     for (uint32_t i = 0; i < a->used; ++i) {
2688c36615fSGlenn Strauss         const data_unset * const du = a->data[i];
2698c36615fSGlenn Strauss         int32_t v = config_plugin_value_to_int32(du, -1);
2708c36615fSGlenn Strauss         if (buffer_eq_icase_slen(&du->key, CONST_STR_LEN("read-timeout"))) {
2718c36615fSGlenn Strauss             limits->read_timeout = (unix_time64_t)v;
2728c36615fSGlenn Strauss             continue;
2738c36615fSGlenn Strauss         }
2748c36615fSGlenn Strauss         if (buffer_eq_icase_slen(&du->key, CONST_STR_LEN("write-timeout"))) {
2758c36615fSGlenn Strauss             limits->write_timeout = (unix_time64_t)v;
2768c36615fSGlenn Strauss             continue;
2778c36615fSGlenn Strauss         }
27833ffec35SGlenn Strauss         if (buffer_eq_icase_slen(&du->key, CONST_STR_LEN("tcp-fin-propagate"))) {
27933ffec35SGlenn Strauss             if (-1 == v) {
28033ffec35SGlenn Strauss                 v = SIGTERM;
28133ffec35SGlenn Strauss                 if (du->type == TYPE_STRING) {
28233ffec35SGlenn Strauss                     buffer * const vstr = &((data_string *)du)->value;
28333ffec35SGlenn Strauss                     buffer_to_upper(vstr);
28433ffec35SGlenn Strauss                     v = mod_cgi_str_to_signal(vstr->ptr, SIGTERM);
28533ffec35SGlenn Strauss                 }
28633ffec35SGlenn Strauss             }
28733ffec35SGlenn Strauss             limits->signal_fin = v;
28833ffec35SGlenn Strauss             continue;
28933ffec35SGlenn Strauss         }
2908c36615fSGlenn Strauss         log_error(errh, __FILE__, __LINE__,
2918c36615fSGlenn Strauss                   "unrecognized cgi.limits param: %s", du->key.ptr);
2928c36615fSGlenn Strauss     }
2938c36615fSGlenn Strauss     return limits;
2948c36615fSGlenn Strauss }
2958c36615fSGlenn Strauss 
SETDEFAULTS_FUNC(mod_cgi_set_defaults)296131450daSGlenn Strauss SETDEFAULTS_FUNC(mod_cgi_set_defaults) {
297131450daSGlenn Strauss     static const config_plugin_keys_t cpk[] = {
298131450daSGlenn Strauss       { CONST_STR_LEN("cgi.assign"),
29903b4c993SGlenn Strauss         T_CONFIG_ARRAY_KVSTRING,
300131450daSGlenn Strauss         T_CONFIG_SCOPE_CONNECTION }
301131450daSGlenn Strauss      ,{ CONST_STR_LEN("cgi.execute-x-only"),
302131450daSGlenn Strauss         T_CONFIG_BOOL,
303131450daSGlenn Strauss         T_CONFIG_SCOPE_CONNECTION }
304131450daSGlenn Strauss      ,{ CONST_STR_LEN("cgi.x-sendfile"),
305131450daSGlenn Strauss         T_CONFIG_BOOL,
306131450daSGlenn Strauss         T_CONFIG_SCOPE_CONNECTION }
307131450daSGlenn Strauss      ,{ CONST_STR_LEN("cgi.x-sendfile-docroot"),
30803b4c993SGlenn Strauss         T_CONFIG_ARRAY_VLIST,
309131450daSGlenn Strauss         T_CONFIG_SCOPE_CONNECTION }
310131450daSGlenn Strauss      ,{ CONST_STR_LEN("cgi.local-redir"),
311131450daSGlenn Strauss         T_CONFIG_BOOL,
312131450daSGlenn Strauss         T_CONFIG_SCOPE_CONNECTION }
313131450daSGlenn Strauss      ,{ CONST_STR_LEN("cgi.upgrade"),
314131450daSGlenn Strauss         T_CONFIG_BOOL,
315131450daSGlenn Strauss         T_CONFIG_SCOPE_CONNECTION }
3168c36615fSGlenn Strauss      ,{ CONST_STR_LEN("cgi.limits"),
3178c36615fSGlenn Strauss         T_CONFIG_ARRAY_KVANY,
3188c36615fSGlenn Strauss         T_CONFIG_SCOPE_CONNECTION }
319131450daSGlenn Strauss      ,{ NULL, 0,
320131450daSGlenn Strauss         T_CONFIG_UNSET,
321131450daSGlenn Strauss         T_CONFIG_SCOPE_UNSET }
322bcdc6a3bSJan Kneschke     };
323bcdc6a3bSJan Kneschke 
324131450daSGlenn Strauss     plugin_data * const p = p_d;
325131450daSGlenn Strauss     if (!config_plugin_values_init(srv, p, cpk, "mod_cgi"))
326131450daSGlenn Strauss         return HANDLER_ERROR;
327bcdc6a3bSJan Kneschke 
328131450daSGlenn Strauss     /* process and validate config directives
329131450daSGlenn Strauss      * (init i to 0 if global context; to 1 to skip empty global context) */
330131450daSGlenn Strauss     for (int i = !p->cvlist[0].v.u2[1]; i < p->nconfig; ++i) {
3318c36615fSGlenn Strauss         config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0];
332131450daSGlenn Strauss         for (; -1 != cpv->k_id; ++cpv) {
333131450daSGlenn Strauss             switch (cpv->k_id) {
334131450daSGlenn Strauss               case 0: /* cgi.assign */
335131450daSGlenn Strauss               case 1: /* cgi.execute-x-only */
336131450daSGlenn Strauss               case 2: /* cgi.x-sendfile */
337131450daSGlenn Strauss                 break;
338131450daSGlenn Strauss               case 3: /* cgi.x-sendfile-docroot */
339131450daSGlenn Strauss                 for (uint32_t j = 0; j < cpv->v.a->used; ++j) {
340131450daSGlenn Strauss                     data_string *ds = (data_string *)cpv->v.a->data[j];
341601c572cSGlenn Strauss                     if (ds->value.ptr[0] != '/') {
342131450daSGlenn Strauss                         log_error(srv->errh, __FILE__, __LINE__,
343131450daSGlenn Strauss                           "%s paths must begin with '/'; invalid: \"%s\"",
344131450daSGlenn Strauss                           cpk[cpv->k_id].k, ds->value.ptr);
3451f23ba9aSGlenn Strauss                         return HANDLER_ERROR;
3461f23ba9aSGlenn Strauss                     }
347980554bcSGlenn Strauss                     buffer_path_simplify(&ds->value);
348601c572cSGlenn Strauss                     buffer_append_slash(&ds->value);
3491f23ba9aSGlenn Strauss                 }
350131450daSGlenn Strauss                 break;
351131450daSGlenn Strauss               case 4: /* cgi.local-redir */
352131450daSGlenn Strauss               case 5: /* cgi.upgrade */
353131450daSGlenn Strauss                 break;
3548c36615fSGlenn Strauss               case 6: /* cgi.limits */
3558c36615fSGlenn Strauss                 cpv->v.v = mod_cgi_parse_limits(cpv->v.a, srv->errh);
3568c36615fSGlenn Strauss                 if (NULL == cpv->v.v) return HANDLER_ERROR;
3578c36615fSGlenn Strauss                 cpv->vtype = T_CONFIG_LOCAL;
3588c36615fSGlenn Strauss                 break;
359131450daSGlenn Strauss               default:/* should not happen */
360131450daSGlenn Strauss                 break;
3611f23ba9aSGlenn Strauss             }
362bcdc6a3bSJan Kneschke         }
363131450daSGlenn Strauss     }
364131450daSGlenn Strauss 
365131450daSGlenn Strauss     /* initialize p->defaults from global config context */
366131450daSGlenn Strauss     if (p->nconfig > 0 && p->cvlist->v.u2[1]) {
367131450daSGlenn Strauss         const config_plugin_value_t *cpv = p->cvlist + p->cvlist->v.u2[0];
368131450daSGlenn Strauss         if (-1 != cpv->k_id)
369131450daSGlenn Strauss             mod_cgi_merge_config(&p->defaults, cpv);
370131450daSGlenn Strauss     }
371bcdc6a3bSJan Kneschke 
3724d99d9b7SGlenn Strauss     p->tempfile_accum = config_feature_bool(srv, "cgi.tempfile-accum", 1);
37370195d06SGlenn Strauss 
374bcdc6a3bSJan Kneschke     return HANDLER_GO_ON;
375bcdc6a3bSJan Kneschke }
376bcdc6a3bSJan Kneschke 
377bcdc6a3bSJan Kneschke 
cgi_pid_add(plugin_data * p,pid_t pid,handler_ctx * hctx)3787e000de0SGlenn Strauss static cgi_pid_t * cgi_pid_add(plugin_data *p, pid_t pid, handler_ctx *hctx) {
3795e14db43SGlenn Strauss     cgi_pid_t *cgi_pid = ck_malloc(sizeof(cgi_pid_t));
3807e000de0SGlenn Strauss     cgi_pid->pid = pid;
38133ffec35SGlenn Strauss     cgi_pid->signal_sent = 0;
3827e000de0SGlenn Strauss     cgi_pid->hctx = hctx;
3837e000de0SGlenn Strauss     cgi_pid->prev = NULL;
3847e000de0SGlenn Strauss     cgi_pid->next = p->cgi_pid;
3857e000de0SGlenn Strauss     p->cgi_pid = cgi_pid;
3867e000de0SGlenn Strauss     return cgi_pid;
387bcdc6a3bSJan Kneschke }
388bcdc6a3bSJan Kneschke 
cgi_pid_kill(cgi_pid_t * cgi_pid,int sig)38933ffec35SGlenn Strauss static void cgi_pid_kill(cgi_pid_t *cgi_pid, int sig) {
39033ffec35SGlenn Strauss     cgi_pid->signal_sent = sig; /*(save last signal sent)*/
39133ffec35SGlenn Strauss     kill(cgi_pid->pid, sig);
392bcdc6a3bSJan Kneschke }
393bcdc6a3bSJan Kneschke 
cgi_pid_del(plugin_data * p,cgi_pid_t * cgi_pid)3947e000de0SGlenn Strauss static void cgi_pid_del(plugin_data *p, cgi_pid_t *cgi_pid) {
3957e000de0SGlenn Strauss     if (cgi_pid->prev)
3967e000de0SGlenn Strauss         cgi_pid->prev->next = cgi_pid->next;
3977e000de0SGlenn Strauss     else
3987e000de0SGlenn Strauss         p->cgi_pid = cgi_pid->next;
399bcdc6a3bSJan Kneschke 
4007e000de0SGlenn Strauss     if (cgi_pid->next)
4017e000de0SGlenn Strauss         cgi_pid->next->prev = cgi_pid->prev;
402bcdc6a3bSJan Kneschke 
4037e000de0SGlenn Strauss     free(cgi_pid);
404bcdc6a3bSJan Kneschke }
405bcdc6a3bSJan Kneschke 
406bcdc6a3bSJan Kneschke 
cgi_connection_close_fdtocgi(handler_ctx * hctx)4077c7f8c46SGlenn Strauss static void cgi_connection_close_fdtocgi(handler_ctx *hctx) {
4087c0f8a77SGlenn Strauss 	/*(closes only hctx->fdtocgi)*/
409ec50657eSGlenn Strauss 	if (-1 == hctx->fdtocgi) return;
4107c7f8c46SGlenn Strauss 	struct fdevents * const ev = hctx->ev;
411b5775b99SGlenn Strauss 	fdevent_fdnode_event_del(ev, hctx->fdntocgi);
412*f0786a75SGlenn Strauss 	/*fdevent_unregister(ev, hctx->fdntocgi);*//*(handled below)*/
413*f0786a75SGlenn Strauss 	fdevent_sched_close(ev, hctx->fdntocgi);
4149113011dSGlenn Strauss 	hctx->fdntocgi = NULL;
4157c0f8a77SGlenn Strauss 	hctx->fdtocgi = -1;
4167c0f8a77SGlenn Strauss }
4177c0f8a77SGlenn Strauss 
cgi_connection_close(handler_ctx * hctx)4187c7f8c46SGlenn Strauss static void cgi_connection_close(handler_ctx *hctx) {
419bcdc6a3bSJan Kneschke 	/* the connection to the browser went away, but we still have a connection
420bcdc6a3bSJan Kneschke 	 * to the CGI script
421bcdc6a3bSJan Kneschke 	 *
422bcdc6a3bSJan Kneschke 	 * close cgi-connection
423bcdc6a3bSJan Kneschke 	 */
424bcdc6a3bSJan Kneschke 
425efa87f47SJan Kneschke 	if (hctx->fd != -1) {
4267c7f8c46SGlenn Strauss 		struct fdevents * const ev = hctx->ev;
427bcdc6a3bSJan Kneschke 		/* close connection to the cgi-script */
428b5775b99SGlenn Strauss 		fdevent_fdnode_event_del(ev, hctx->fdn);
429*f0786a75SGlenn Strauss 		/*fdevent_unregister(ev, hctx->fdn);*//*(handled below)*/
430*f0786a75SGlenn Strauss 		fdevent_sched_close(ev, hctx->fdn);
4319113011dSGlenn Strauss 		hctx->fdn = NULL;
4327c0f8a77SGlenn Strauss 	}
433bcdc6a3bSJan Kneschke 
4347c0f8a77SGlenn Strauss 	if (hctx->fdtocgi != -1) {
4357c7f8c46SGlenn Strauss 		cgi_connection_close_fdtocgi(hctx); /*(closes only hctx->fdtocgi)*/
436efa87f47SJan Kneschke 	}
437bcdc6a3bSJan Kneschke 
4387c7f8c46SGlenn Strauss 	plugin_data * const p = hctx->plugin_data;
4397c7f8c46SGlenn Strauss 	request_st * const r = hctx->r;
4407c7f8c46SGlenn Strauss 	r->plugin_ctx[p->id] = NULL;
441bcdc6a3bSJan Kneschke 
44233ffec35SGlenn Strauss 	if (hctx->cgi_pid) {
44333ffec35SGlenn Strauss 		cgi_pid_kill(hctx->cgi_pid, SIGTERM);
44433ffec35SGlenn Strauss 		hctx->cgi_pid->hctx = NULL;
44533ffec35SGlenn Strauss 	}
446bcdc6a3bSJan Kneschke 	cgi_handler_ctx_free(hctx);
447bcdc6a3bSJan Kneschke 
4487c7f8c46SGlenn Strauss 	/* finish response (if not already r->resp_body_started, r->resp_body_finished) */
4497c7f8c46SGlenn Strauss 	if (r->handler_module == p->self) {
4507c7f8c46SGlenn Strauss 		http_response_backend_done(r);
45194647804SGlenn Strauss 	}
452bcdc6a3bSJan Kneschke }
453bcdc6a3bSJan Kneschke 
cgi_connection_close_callback(request_st * const r,void * p_d)4547c7f8c46SGlenn Strauss static handler_t cgi_connection_close_callback(request_st * const r, void *p_d) {
45570195d06SGlenn Strauss     handler_ctx *hctx = r->plugin_ctx[((plugin_data *)p_d)->id];
45670195d06SGlenn Strauss     if (hctx) {
45770195d06SGlenn Strauss         chunkqueue_set_tempdirs(&r->reqbody_queue, /* reset sz */
45870195d06SGlenn Strauss                                 r->reqbody_queue.tempdirs, 0);
45970195d06SGlenn Strauss         cgi_connection_close(hctx);
46070195d06SGlenn Strauss     }
461bbbbfb3dSGlenn Strauss     return HANDLER_GO_ON;
462bcdc6a3bSJan Kneschke }
463bcdc6a3bSJan Kneschke 
464bcdc6a3bSJan Kneschke 
46550bdb55dSGlenn Strauss static int cgi_write_request(handler_ctx *hctx, int fd);
4667c0f8a77SGlenn Strauss 
4677c0f8a77SGlenn Strauss 
cgi_handle_fdevent_send(void * ctx,int revents)46805cc88ddSGlenn Strauss static handler_t cgi_handle_fdevent_send (void *ctx, int revents) {
4697c0f8a77SGlenn Strauss 	handler_ctx *hctx = ctx;
470dd23fcb2SGlenn Strauss 	hctx->wr_revents |= revents;
471dd23fcb2SGlenn Strauss 	joblist_append(hctx->con);
472dd23fcb2SGlenn Strauss 	return HANDLER_FINISHED;
473dd23fcb2SGlenn Strauss }
4747c0f8a77SGlenn Strauss 
4757c0f8a77SGlenn Strauss 
cgi_process_wr_revents(handler_ctx * const hctx,request_st * const r,int revents)476dd23fcb2SGlenn Strauss static handler_t cgi_process_wr_revents (handler_ctx * const hctx, request_st * const r, int revents) {
4777c0f8a77SGlenn Strauss 	if (revents & FDEVENT_OUT) {
47850bdb55dSGlenn Strauss 		if (0 != cgi_write_request(hctx, hctx->fdtocgi)) {
4797c7f8c46SGlenn Strauss 			cgi_connection_close(hctx);
4807c0f8a77SGlenn Strauss 			return HANDLER_ERROR;
4817c0f8a77SGlenn Strauss 		}
4827c0f8a77SGlenn Strauss 		/* more request body to be sent to CGI */
4837c0f8a77SGlenn Strauss 	}
4847c0f8a77SGlenn Strauss 
4857c0f8a77SGlenn Strauss 	if (revents & FDEVENT_HUP) {
4867c0f8a77SGlenn Strauss 		/* skip sending remaining data to CGI */
4877c7f8c46SGlenn Strauss 		if (r->reqbody_length) {
48881029b8bSGlenn Strauss 			chunkqueue *cq = &r->reqbody_queue;
4897c0f8a77SGlenn Strauss 			chunkqueue_mark_written(cq, chunkqueue_length(cq));
4907c7f8c46SGlenn Strauss 			if (cq->bytes_in != (off_t)r->reqbody_length) {
4917c7f8c46SGlenn Strauss 				r->keep_alive = 0;
492f69f209eSGlenn Strauss 			}
493f69f209eSGlenn Strauss 		}
4947c0f8a77SGlenn Strauss 
4957c7f8c46SGlenn Strauss 		cgi_connection_close_fdtocgi(hctx); /*(closes only hctx->fdtocgi)*/
4967c0f8a77SGlenn Strauss 	} else if (revents & FDEVENT_ERR) {
4977c0f8a77SGlenn Strauss 		/* kill all connections to the cgi process */
4987c0f8a77SGlenn Strauss #if 1
4997c7f8c46SGlenn Strauss 		log_error(r->conf.errh, __FILE__, __LINE__, "cgi-FDEVENT_ERR");
5007c0f8a77SGlenn Strauss #endif
5017c7f8c46SGlenn Strauss 		cgi_connection_close(hctx);
5027c0f8a77SGlenn Strauss 		return HANDLER_ERROR;
5037c0f8a77SGlenn Strauss 	}
5047c0f8a77SGlenn Strauss 
505dd23fcb2SGlenn Strauss 	return HANDLER_GO_ON;
5067c0f8a77SGlenn Strauss }
5077c0f8a77SGlenn Strauss 
5087c0f8a77SGlenn Strauss 
cgi_response_headers(request_st * const r,struct http_response_opts_t * opts)5097c7f8c46SGlenn Strauss static handler_t cgi_response_headers(request_st * const r, struct http_response_opts_t *opts) {
510574fb562SGlenn Strauss     /* response headers just completed */
51145b970e6SGlenn Strauss     handler_ctx *hctx = (handler_ctx *)opts->pdata;
512574fb562SGlenn Strauss 
5139c8981a7SGlenn Strauss     if (light_btst(r->resp_htags, HTTP_HEADER_UPGRADE)) {
5147c7f8c46SGlenn Strauss         if (hctx->conf.upgrade && r->http_status == 101) {
515574fb562SGlenn Strauss             /* 101 Switching Protocols; transition to transparent proxy */
5165d1aa5d0SGlenn Strauss             if (r->h2_connect_ext) {
5175d1aa5d0SGlenn Strauss                 r->http_status = 200; /* OK (response status for CONNECT) */
5185d1aa5d0SGlenn Strauss                 http_header_response_unset(r, HTTP_HEADER_UPGRADE,
5195d1aa5d0SGlenn Strauss                                            CONST_STR_LEN("Upgrade"));
5205d1aa5d0SGlenn Strauss                 http_header_response_unset(r, HTTP_HEADER_OTHER,
5215d1aa5d0SGlenn Strauss                                          CONST_STR_LEN("Sec-WebSocket-Accept"));
5225d1aa5d0SGlenn Strauss             }
5237c7f8c46SGlenn Strauss             http_response_upgrade_read_body_unknown(r);
524574fb562SGlenn Strauss         }
525574fb562SGlenn Strauss         else {
5269c8981a7SGlenn Strauss             light_bclr(r->resp_htags, HTTP_HEADER_UPGRADE);
527574fb562SGlenn Strauss           #if 0
528574fb562SGlenn Strauss             /* preserve prior questionable behavior; likely broken behavior
529574fb562SGlenn Strauss              * anyway if backend thinks connection is being upgraded but client
530574fb562SGlenn Strauss              * does not receive Connection: upgrade */
5317c7f8c46SGlenn Strauss             http_header_response_unset(r, HTTP_HEADER_UPGRADE,
532f13db690SGlenn Strauss                                        CONST_STR_LEN("Upgrade"));
533574fb562SGlenn Strauss           #endif
534574fb562SGlenn Strauss         }
535574fb562SGlenn Strauss     }
5365d1aa5d0SGlenn Strauss     else if (__builtin_expect( (r->h2_connect_ext != 0), 0)
5375d1aa5d0SGlenn Strauss              && r->http_status < 300) {
5385d1aa5d0SGlenn Strauss         /*(not handling other 1xx intermediate responses here; not expected)*/
5395d1aa5d0SGlenn Strauss         http_response_body_clear(r, 0);
5405d1aa5d0SGlenn Strauss         r->handler_module = NULL;
5415d1aa5d0SGlenn Strauss         r->http_status = 405; /* Method Not Allowed */
5425d1aa5d0SGlenn Strauss         return HANDLER_FINISHED;
5435d1aa5d0SGlenn Strauss     }
544574fb562SGlenn Strauss 
5459c8981a7SGlenn Strauss     if (hctx->conf.upgrade
546ce9e0dfcSGlenn Strauss         && !light_btst(r->resp_htags, HTTP_HEADER_UPGRADE)) {
54781029b8bSGlenn Strauss         chunkqueue *cq = &r->reqbody_queue;
548574fb562SGlenn Strauss         hctx->conf.upgrade = 0;
549ce9e0dfcSGlenn Strauss         r->reqbody_length = hctx->orig_reqbody_length;
5507c7f8c46SGlenn Strauss         if (cq->bytes_out == (off_t)r->reqbody_length) {
5517c7f8c46SGlenn Strauss             cgi_connection_close_fdtocgi(hctx); /*(closes hctx->fdtocgi)*/
552574fb562SGlenn Strauss         }
553574fb562SGlenn Strauss     }
554574fb562SGlenn Strauss 
55545b970e6SGlenn Strauss     return HANDLER_GO_ON;
556574fb562SGlenn Strauss }
557574fb562SGlenn Strauss 
558574fb562SGlenn Strauss 
559dd23fcb2SGlenn Strauss __attribute_cold__
560dd23fcb2SGlenn Strauss __attribute_noinline__
cgi_local_redir(request_st * const r,handler_ctx * const hctx)561dd23fcb2SGlenn Strauss static handler_t cgi_local_redir(request_st * const r, handler_ctx * const hctx) {
562dd23fcb2SGlenn Strauss     buffer_clear(hctx->response);
563dd23fcb2SGlenn Strauss     chunk_buffer_yield(hctx->response);
564dd23fcb2SGlenn Strauss     http_response_reset(r); /*(includes r->http_status = 0)*/
565cc65a21eSGlenn Strauss     r->con->srv->plugins_request_reset(r);
566dd23fcb2SGlenn Strauss     /*cgi_connection_close(hctx);*//*(already cleaned up and hctx is now invalid)*/
567dd23fcb2SGlenn Strauss     return HANDLER_COMEBACK;
568dd23fcb2SGlenn Strauss }
569dd23fcb2SGlenn Strauss 
570dd23fcb2SGlenn Strauss 
cgi_recv_response(request_st * const r,handler_ctx * const hctx)5717c7f8c46SGlenn Strauss static int cgi_recv_response(request_st * const r, handler_ctx * const hctx) {
5728c36615fSGlenn Strauss 		const off_t bytes_in = r->write_queue.bytes_in;
5737c7f8c46SGlenn Strauss 		switch (http_response_read(r, &hctx->opts,
5749113011dSGlenn Strauss 					   hctx->response, hctx->fdn)) {
5750a635fc8SGlenn Strauss 		default:
5768c36615fSGlenn Strauss 			if (r->write_queue.bytes_in > bytes_in)
5778c36615fSGlenn Strauss 				hctx->read_ts = log_monotonic_secs;
5780a635fc8SGlenn Strauss 			return HANDLER_GO_ON;
5790a635fc8SGlenn Strauss 		case HANDLER_ERROR:
5807c7f8c46SGlenn Strauss 			http_response_backend_error(r);
58176faed91SGlenn Strauss 			__attribute_fallthrough__
5820a635fc8SGlenn Strauss 		case HANDLER_FINISHED:
5837c7f8c46SGlenn Strauss 			cgi_connection_close(hctx);
584efa87f47SJan Kneschke 			return HANDLER_FINISHED;
5850a635fc8SGlenn Strauss 		case HANDLER_COMEBACK:
586dd23fcb2SGlenn Strauss 			return cgi_local_redir(r, hctx); /* HANDLER_COMEBACK */
587bcdc6a3bSJan Kneschke 		}
588ddfae019SGlenn Strauss }
589ddfae019SGlenn Strauss 
590ddfae019SGlenn Strauss 
cgi_handle_fdevent(void * ctx,int revents)59105cc88ddSGlenn Strauss static handler_t cgi_handle_fdevent(void *ctx, int revents) {
592ddfae019SGlenn Strauss 	handler_ctx *hctx = ctx;
593dd23fcb2SGlenn Strauss 	hctx->rd_revents |= revents;
594dd23fcb2SGlenn Strauss 	joblist_append(hctx->con);
595dd23fcb2SGlenn Strauss 	return HANDLER_FINISHED;
596dd23fcb2SGlenn Strauss }
597ddfae019SGlenn Strauss 
598ddfae019SGlenn Strauss 
cgi_process_rd_revents(handler_ctx * const hctx,request_st * const r,int revents)599dd23fcb2SGlenn Strauss static handler_t cgi_process_rd_revents(handler_ctx * const hctx, request_st * const r, int revents) {
600ddfae019SGlenn Strauss 	if (revents & FDEVENT_IN) {
6017c7f8c46SGlenn Strauss 		handler_t rc = cgi_recv_response(r, hctx); /*(might invalidate hctx)*/
602ddfae019SGlenn Strauss 		if (rc != HANDLER_GO_ON) return rc;         /*(unless HANDLER_GO_ON)*/
603bcdc6a3bSJan Kneschke 	}
604bcdc6a3bSJan Kneschke 
605bcdc6a3bSJan Kneschke 	/* perhaps this issue is already handled */
606d5d02583SGlenn Strauss 	if (revents & (FDEVENT_HUP|FDEVENT_RDHUP)) {
6077c7f8c46SGlenn Strauss 		if (r->resp_body_started) {
608923688d2SGlenn Strauss 			/* drain any remaining data from kernel pipe buffers
6097c7f8c46SGlenn Strauss 			 * even if (r->conf.stream_response_body
610923688d2SGlenn Strauss 			 *          & FDEVENT_STREAM_RESPONSE_BUFMIN)
611923688d2SGlenn Strauss 			 * since event loop will spin on fd FDEVENT_HUP event
612923688d2SGlenn Strauss 			 * until unregistered. */
613923688d2SGlenn Strauss 			handler_t rc;
6147c7f8c46SGlenn Strauss 			const unsigned short flags = r->conf.stream_response_body;
6157c7f8c46SGlenn Strauss 			r->conf.stream_response_body &= ~FDEVENT_STREAM_RESPONSE_BUFMIN;
6167c7f8c46SGlenn Strauss 			r->conf.stream_response_body |= FDEVENT_STREAM_RESPONSE_POLLRDHUP;
617923688d2SGlenn Strauss 			do {
6187c7f8c46SGlenn Strauss 				rc = cgi_recv_response(r,hctx); /*(might invalidate hctx)*/
619923688d2SGlenn Strauss 			} while (rc == HANDLER_GO_ON);           /*(unless HANDLER_GO_ON)*/
6207c7f8c46SGlenn Strauss 			r->conf.stream_response_body = flags;
6218861c2bbSGlenn Strauss 			return rc; /* HANDLER_FINISHED or HANDLER_COMEBACK or HANDLER_ERROR */
622af3df29aSGlenn Strauss 		} else if (!buffer_is_blank(hctx->response)) {
623923688d2SGlenn Strauss 			/* unfinished header package which is a body in reality */
6247c7f8c46SGlenn Strauss 			r->resp_body_started = 1;
6257c7f8c46SGlenn Strauss 			if (0 != http_chunk_append_buffer(r, hctx->response)) {
6267c7f8c46SGlenn Strauss 				cgi_connection_close(hctx);
6275a91fd4bSGlenn Strauss 				return HANDLER_ERROR;
6285a91fd4bSGlenn Strauss 			}
6297c7f8c46SGlenn Strauss 			if (0 == r->http_status) r->http_status = 200; /* OK */
630923688d2SGlenn Strauss 		}
6317c7f8c46SGlenn Strauss 		cgi_connection_close(hctx);
632467fb2bfSGlenn Strauss 		return HANDLER_FINISHED;
633bcdc6a3bSJan Kneschke 	} else if (revents & FDEVENT_ERR) {
634bcdc6a3bSJan Kneschke 		/* kill all connections to the cgi process */
6357c7f8c46SGlenn Strauss 		cgi_connection_close(hctx);
636bcdc6a3bSJan Kneschke 		return HANDLER_ERROR;
637bcdc6a3bSJan Kneschke 	}
638bcdc6a3bSJan Kneschke 
639dd23fcb2SGlenn Strauss 	return HANDLER_GO_ON;
640bcdc6a3bSJan Kneschke }
641bcdc6a3bSJan Kneschke 
642bcdc6a3bSJan Kneschke 
6437b3a4f13SGlenn Strauss __attribute_cold__
6447b3a4f13SGlenn Strauss __attribute_noinline__
cgi_env_offset_resize(env_accum * env)6457b3a4f13SGlenn Strauss static void cgi_env_offset_resize(env_accum *env) {
6467b3a4f13SGlenn Strauss     chunk_buffer_prepare_append(env->boffsets, env->boffsets->size*2);
6477b3a4f13SGlenn Strauss     env->offsets = (uintptr_t *)(void *)env->boffsets->ptr;
6487b3a4f13SGlenn Strauss     env->osize = env->boffsets->size/sizeof(*env->offsets);
6497b3a4f13SGlenn Strauss }
6507b3a4f13SGlenn Strauss 
cgi_env_add(void * venv,const char * key,size_t key_len,const char * val,size_t val_len)6517fa5bfc9SGlenn Strauss static int cgi_env_add(void *venv, const char *key, size_t key_len, const char *val, size_t val_len) {
652407b4d14SGlenn Strauss 	env_accum *env = venv;
653bcdc6a3bSJan Kneschke 
654c0e2667bSGlenn Strauss 	if (!key || (!val && val_len)) return -1;
655bcdc6a3bSJan Kneschke 
6567b3a4f13SGlenn Strauss 	if (__builtin_expect( (env->osize == env->oused), 0))
6577b3a4f13SGlenn Strauss 		cgi_env_offset_resize(env);
6587b3a4f13SGlenn Strauss 	env->offsets[env->oused++] = env->b->used-1;
659407b4d14SGlenn Strauss 
6607b3a4f13SGlenn Strauss 	char * const dst = buffer_extend(env->b, key_len + val_len + 2);
661bcdc6a3bSJan Kneschke 	memcpy(dst, key, key_len);
662bcdc6a3bSJan Kneschke 	dst[key_len] = '=';
6635ccebbf0SGlenn Strauss 	if (val_len) memcpy(dst + key_len + 1, val, val_len);
664ecfdc609SStefan Bühler 	dst[key_len + 1 + val_len] = '\0';
665bcdc6a3bSJan Kneschke 
666bcdc6a3bSJan Kneschke 	return 0;
667bcdc6a3bSJan Kneschke }
668bcdc6a3bSJan Kneschke 
cgi_write_request(handler_ctx * hctx,int fd)66950bdb55dSGlenn Strauss static int cgi_write_request(handler_ctx *hctx, int fd) {
6707c7f8c46SGlenn Strauss 	request_st * const r = hctx->r;
67181029b8bSGlenn Strauss 	chunkqueue *cq = &r->reqbody_queue;
6727c0f8a77SGlenn Strauss 	chunk *c;
6737c0f8a77SGlenn Strauss 
6742639e5aeSGlenn Strauss 	chunkqueue_remove_finished_chunks(cq); /* unnecessary? */
6752639e5aeSGlenn Strauss 
6767c0f8a77SGlenn Strauss 	/* old comment: windows doesn't support select() on pipes - wouldn't be easy to fix for all platforms.
6777c0f8a77SGlenn Strauss 	 */
6787c0f8a77SGlenn Strauss 
6797c0f8a77SGlenn Strauss 	for (c = cq->first; c; c = cq->first) {
6802639e5aeSGlenn Strauss 		ssize_t wr = chunkqueue_write_chunk_to_pipe(fd, cq, r->conf.errh);
6812639e5aeSGlenn Strauss 		if (wr > 0) {
6828c36615fSGlenn Strauss 			hctx->write_ts = log_monotonic_secs;
6832639e5aeSGlenn Strauss 			chunkqueue_mark_written(cq, wr);
6842639e5aeSGlenn Strauss 			/* continue if wrote whole chunk or wrote 16k block
6852639e5aeSGlenn Strauss 			 * (see chunkqueue_write_chunk_file_intermed()) */
6862639e5aeSGlenn Strauss 			if (c != cq->first || wr == 16384)
6872639e5aeSGlenn Strauss 				continue;
6882639e5aeSGlenn Strauss 			/*(else partial write)*/
6892639e5aeSGlenn Strauss 		}
6902639e5aeSGlenn Strauss 		else if (wr < 0) {
6917c0f8a77SGlenn Strauss 				switch(errno) {
6927c0f8a77SGlenn Strauss 				case EAGAIN:
6937c0f8a77SGlenn Strauss 				case EINTR:
6942639e5aeSGlenn Strauss 					/* ignore and try again later */
6957c0f8a77SGlenn Strauss 					break;
6967c0f8a77SGlenn Strauss 				case EPIPE:
6977c0f8a77SGlenn Strauss 				case ECONNRESET:
6987c0f8a77SGlenn Strauss 					/* connection closed */
6997c7f8c46SGlenn Strauss 					log_error(r->conf.errh, __FILE__, __LINE__,
700010c2894SGlenn Strauss 					  "failed to send post data to cgi, connection closed by CGI");
7017c0f8a77SGlenn Strauss 					/* skip all remaining data */
7027c0f8a77SGlenn Strauss 					chunkqueue_mark_written(cq, chunkqueue_length(cq));
7037c0f8a77SGlenn Strauss 					break;
7047c0f8a77SGlenn Strauss 				default:
7052639e5aeSGlenn Strauss 					/* fatal error */
7062639e5aeSGlenn Strauss 					log_perror(r->conf.errh, __FILE__, __LINE__, "write() failed");
7072639e5aeSGlenn Strauss 					return -1;
7087c0f8a77SGlenn Strauss 				}
7097c0f8a77SGlenn Strauss 		}
7102639e5aeSGlenn Strauss 		/*if (0 == wr) break;*/ /*(might block)*/
7112639e5aeSGlenn Strauss 		break;
7122639e5aeSGlenn Strauss 	}
7137c0f8a77SGlenn Strauss 
7147c7f8c46SGlenn Strauss 	if (cq->bytes_out == (off_t)r->reqbody_length && !hctx->conf.upgrade) {
7157c0f8a77SGlenn Strauss 		/* sent all request body input */
7167c0f8a77SGlenn Strauss 		/* close connection to the cgi-script */
717f69f209eSGlenn Strauss 		if (-1 == hctx->fdtocgi) { /*(received request body sent in initial send to pipe buffer)*/
7187c7f8c46SGlenn Strauss 			--r->con->srv->cur_fds;
7197c0f8a77SGlenn Strauss 			if (close(fd)) {
7207c7f8c46SGlenn Strauss 				log_perror(r->conf.errh, __FILE__, __LINE__, "cgi stdin close %d failed", fd);
7217c0f8a77SGlenn Strauss 			}
7227c0f8a77SGlenn Strauss 		} else {
7237c7f8c46SGlenn Strauss 			cgi_connection_close_fdtocgi(hctx); /*(closes only hctx->fdtocgi)*/
7247c0f8a77SGlenn Strauss 		}
7257c0f8a77SGlenn Strauss 	} else {
72697e314fcSGlenn Strauss 		off_t cqlen = chunkqueue_length(cq);
7277c7f8c46SGlenn Strauss 		if (cq->bytes_in != r->reqbody_length && cqlen < 65536 - 16384) {
7287c7f8c46SGlenn Strauss 			/*(r->conf.stream_request_body & FDEVENT_STREAM_REQUEST)*/
7297c7f8c46SGlenn Strauss 			if (!(r->conf.stream_request_body & FDEVENT_STREAM_REQUEST_POLLIN)) {
7307c7f8c46SGlenn Strauss 				r->conf.stream_request_body |= FDEVENT_STREAM_REQUEST_POLLIN;
7317c7f8c46SGlenn Strauss 				r->con->is_readable = 1; /* trigger optimistic read from client */
732f69f209eSGlenn Strauss 			}
733f69f209eSGlenn Strauss 		}
7347c7f8c46SGlenn Strauss 		struct fdevents * const ev = hctx->ev;
7357c0f8a77SGlenn Strauss 		if (-1 == hctx->fdtocgi) { /*(not registered yet)*/
7367c0f8a77SGlenn Strauss 			hctx->fdtocgi = fd;
737b5775b99SGlenn Strauss 			hctx->fdntocgi = fdevent_register(ev, hctx->fdtocgi, cgi_handle_fdevent_send, hctx);
738f69f209eSGlenn Strauss 		}
739f69f209eSGlenn Strauss 		if (0 == cqlen) { /*(chunkqueue_is_empty(cq))*/
7409113011dSGlenn Strauss 			if ((fdevent_fdnode_interest(hctx->fdntocgi) & FDEVENT_OUT)) {
741b5775b99SGlenn Strauss 				fdevent_fdnode_event_set(ev, hctx->fdntocgi, 0);
742f69f209eSGlenn Strauss 			}
743f69f209eSGlenn Strauss 		} else {
744f69f209eSGlenn Strauss 			/* more request body remains to be sent to CGI so register for fdevents */
7458c36615fSGlenn Strauss 			hctx->write_ts = log_monotonic_secs;
746b5775b99SGlenn Strauss 			fdevent_fdnode_event_set(ev, hctx->fdntocgi, FDEVENT_OUT);
7477c0f8a77SGlenn Strauss 		}
7487c0f8a77SGlenn Strauss 	}
7497c0f8a77SGlenn Strauss 
7506d6dbadbSStefan Bühler 	return 0;
7516d6dbadbSStefan Bühler }
7526d6dbadbSStefan Bühler 
cgi_create_env(request_st * const r,plugin_data * const p,handler_ctx * const hctx,buffer * const cgi_handler)7537c7f8c46SGlenn Strauss static int cgi_create_env(request_st * const r, plugin_data * const p, handler_ctx * const hctx, buffer * const cgi_handler) {
754a9970fecSGlenn Strauss 	char *args[3];
755bcdc6a3bSJan Kneschke 	int to_cgi_fds[2];
756bcdc6a3bSJan Kneschke 	int from_cgi_fds[2];
7577fa5bfc9SGlenn Strauss 	UNUSED(p);
758bcdc6a3bSJan Kneschke 
759af3df29aSGlenn Strauss 	if (!buffer_is_blank(cgi_handler)) {
760fe021118SGlenn Strauss 		if (NULL == stat_cache_path_stat(cgi_handler)) {
7617c7f8c46SGlenn Strauss 			log_perror(r->conf.errh, __FILE__, __LINE__,
762010c2894SGlenn Strauss 			  "stat for cgi-handler %s", cgi_handler->ptr);
763bcdc6a3bSJan Kneschke 			return -1;
764bcdc6a3bSJan Kneschke 		}
76591066a8bSJan Kneschke 	}
766bcdc6a3bSJan Kneschke 
76770195d06SGlenn Strauss 	to_cgi_fds[0] = -1;
76870195d06SGlenn Strauss 	if (0 == r->reqbody_length) {
76970195d06SGlenn Strauss 		/* future: might keep fd open in p->devnull for reuse
77070195d06SGlenn Strauss 		 * and dup() here, or do not close() (later in this func) */
77170195d06SGlenn Strauss 		to_cgi_fds[0] = fdevent_open_devnull();
77270195d06SGlenn Strauss 		if (-1 == to_cgi_fds[0]) {
77370195d06SGlenn Strauss 			log_perror(r->conf.errh, __FILE__, __LINE__, "open /dev/null");
77470195d06SGlenn Strauss 			return -1;
77570195d06SGlenn Strauss 		}
77670195d06SGlenn Strauss 	}
77770195d06SGlenn Strauss 	else if (!(r->conf.stream_request_body /*(if not streaming request body)*/
778317b4ba1SGlenn Strauss 	           & (FDEVENT_STREAM_REQUEST|FDEVENT_STREAM_REQUEST_BUFMIN))
779317b4ba1SGlenn Strauss 	         && !hctx->conf.upgrade) {
78070195d06SGlenn Strauss 		chunkqueue * const cq = &r->reqbody_queue;
78170195d06SGlenn Strauss 		chunk * const c = cq->first;
78270195d06SGlenn Strauss 		if (c && c == cq->last && c->type == FILE_CHUNK && c->file.is_temp) {
78370195d06SGlenn Strauss 			/* request body in single tempfile if not streaming req body */
78470195d06SGlenn Strauss 			if (-1 == c->file.fd
78570195d06SGlenn Strauss 			    && 0 != chunkqueue_open_file_chunk(cq, r->conf.errh))
78670195d06SGlenn Strauss 				return -1;
787a3e9faa4SGlenn Strauss 		  #ifdef __COVERITY__
788a3e9faa4SGlenn Strauss 			force_assert(-1 != c->file.fd);
789a3e9faa4SGlenn Strauss 		  #endif
79070195d06SGlenn Strauss 			if (-1 == lseek(c->file.fd, 0, SEEK_SET)) {
79170195d06SGlenn Strauss 				log_perror(r->conf.errh, __FILE__, __LINE__,
79270195d06SGlenn Strauss 				  "lseek %s", c->mem->ptr);
79370195d06SGlenn Strauss 				return -1;
79470195d06SGlenn Strauss 			}
79570195d06SGlenn Strauss 			to_cgi_fds[0] = c->file.fd;
79670195d06SGlenn Strauss 			to_cgi_fds[1] = -1;
79770195d06SGlenn Strauss 		}
79870195d06SGlenn Strauss 	}
79970195d06SGlenn Strauss 
800cc2fcd3eSGlenn Strauss 	unsigned int bufsz_hint = 16384;
801cc2fcd3eSGlenn Strauss   #ifdef _WIN32
802ce9e0dfcSGlenn Strauss 	if (r->reqbody_length <= 1048576 && r->reqbody_length > 0)
803cc2fcd3eSGlenn Strauss 		bufsz_hint = (unsigned int)r->reqbody_length;
804cc2fcd3eSGlenn Strauss   #endif
805cc2fcd3eSGlenn Strauss 	if (-1 == to_cgi_fds[0] && fdevent_pipe_cloexec(to_cgi_fds, bufsz_hint)) {
8067c7f8c46SGlenn Strauss 		log_perror(r->conf.errh, __FILE__, __LINE__, "pipe failed");
807bcdc6a3bSJan Kneschke 		return -1;
808bcdc6a3bSJan Kneschke 	}
809cc2fcd3eSGlenn Strauss 	if (fdevent_pipe_cloexec(from_cgi_fds, bufsz_hint)) {
81070195d06SGlenn Strauss 		if (0 == r->reqbody_length) {
81170195d06SGlenn Strauss 			close(to_cgi_fds[0]);
81270195d06SGlenn Strauss 		}
81370195d06SGlenn Strauss 		else if (-1 != to_cgi_fds[1]) {
81485b1f6afSStefan Bühler 			close(to_cgi_fds[0]);
81585b1f6afSStefan Bühler 			close(to_cgi_fds[1]);
81670195d06SGlenn Strauss 		}
8177c7f8c46SGlenn Strauss 		log_perror(r->conf.errh, __FILE__, __LINE__, "pipe failed");
818bcdc6a3bSJan Kneschke 		return -1;
819bcdc6a3bSJan Kneschke 	}
820bcdc6a3bSJan Kneschke 
8217b3a4f13SGlenn Strauss 	env_accum * const env = &p->env;
8227b3a4f13SGlenn Strauss 	env->b = chunk_buffer_acquire();
8237b3a4f13SGlenn Strauss 	env->boffsets = chunk_buffer_acquire();
8247b3a4f13SGlenn Strauss 	buffer_truncate(env->b, 0);
8257b3a4f13SGlenn Strauss 	char **envp;
826a9970fecSGlenn Strauss 	{
827407b4d14SGlenn Strauss 		size_t i = 0;
8287fa5bfc9SGlenn Strauss 		http_cgi_opts opts = { 0, 0, NULL, NULL };
8297b3a4f13SGlenn Strauss 		env->offsets = (uintptr_t *)(void *)env->boffsets->ptr;
8307b3a4f13SGlenn Strauss 		env->osize = env->boffsets->size/sizeof(*env->offsets);
831407b4d14SGlenn Strauss 		env->oused = 0;
832bcdc6a3bSJan Kneschke 
833bcdc6a3bSJan Kneschke 		/* create environment */
834bcdc6a3bSJan Kneschke 
8355d1aa5d0SGlenn Strauss 		if (r->h2_connect_ext) {
8365d1aa5d0SGlenn Strauss 			/*(SERVER_PROTOCOL=HTTP/1.1 instead of HTTP/2.0)*/
8375d1aa5d0SGlenn Strauss 			r->http_version = HTTP_VERSION_1_1;
8385d1aa5d0SGlenn Strauss 			r->http_method = HTTP_METHOD_GET;
839ce9e0dfcSGlenn Strauss 		}
840ce9e0dfcSGlenn Strauss 		if (hctx->conf.upgrade) {
841ce9e0dfcSGlenn Strauss 			r->reqbody_length = hctx->orig_reqbody_length;
842ce9e0dfcSGlenn Strauss 			if (r->reqbody_length < 0)
8435d1aa5d0SGlenn Strauss 				r->reqbody_length = 0;
8445d1aa5d0SGlenn Strauss 		}
8455d1aa5d0SGlenn Strauss 
8467c7f8c46SGlenn Strauss 		http_cgi_headers(r, &opts, cgi_env_add, env);
847bcdc6a3bSJan Kneschke 
848ce9e0dfcSGlenn Strauss 		if (hctx->conf.upgrade)
849ce9e0dfcSGlenn Strauss 			r->reqbody_length = -1;
8505d1aa5d0SGlenn Strauss 		if (r->h2_connect_ext) {
8515d1aa5d0SGlenn Strauss 			r->http_version = HTTP_VERSION_2;
8525d1aa5d0SGlenn Strauss 			r->http_method = HTTP_METHOD_CONNECT;
8535d1aa5d0SGlenn Strauss 			/* https://datatracker.ietf.org/doc/html/rfc6455#section-4.1
8545d1aa5d0SGlenn Strauss 			 * 7. The request MUST include a header field with the name
8555d1aa5d0SGlenn Strauss 			 *    |Sec-WebSocket-Key|.  The value of this header field MUST be a
8565d1aa5d0SGlenn Strauss 			 *    nonce consisting of a randomly selected 16-byte value that has
8575d1aa5d0SGlenn Strauss 			 *    been base64-encoded (see Section 4 of [RFC4648]).  The nonce
8585d1aa5d0SGlenn Strauss 			 *    MUST be selected randomly for each connection.
8595d1aa5d0SGlenn Strauss 			 * Note: Sec-WebSocket-Key is not used in RFC8441;
8605d1aa5d0SGlenn Strauss 			 *       include Sec-WebSocket-Key for HTTP/1.1 compatibility;
8615d1aa5d0SGlenn Strauss 			 *       !!not random!! base64-encoded "0000000000000000" */
8625d1aa5d0SGlenn Strauss 			if (!http_header_request_get(r, HTTP_HEADER_OTHER,
8635d1aa5d0SGlenn Strauss 			                             CONST_STR_LEN("Sec-WebSocket-Key")))
8645d1aa5d0SGlenn Strauss 				cgi_env_add(env, CONST_STR_LEN("HTTP_SEC_WEBSOCKET_KEY"),
865cda9b716SShulyaka 				                 CONST_STR_LEN("MDAwMDAwMDAwMDAwMDAwMA=="));
8665d1aa5d0SGlenn Strauss 			/*(Upgrade and Connection should not exist for HTTP/2 request)*/
8675d1aa5d0SGlenn Strauss 			cgi_env_add(env, CONST_STR_LEN("HTTP_UPGRADE"), CONST_STR_LEN("websocket"));
8685d1aa5d0SGlenn Strauss 			cgi_env_add(env, CONST_STR_LEN("HTTP_CONNECTION"), CONST_STR_LEN("upgrade"));
8695d1aa5d0SGlenn Strauss 		}
8705d1aa5d0SGlenn Strauss 
871bcdc6a3bSJan Kneschke 		/* for valgrind */
8722d914758SGlenn Strauss 		if (p->env.ld_preload) {
873af3df29aSGlenn Strauss 			cgi_env_add(env, CONST_STR_LEN("LD_PRELOAD"), BUF_PTR_LEN(p->env.ld_preload));
874433f1883SJan Kneschke 		}
8752d914758SGlenn Strauss 		if (p->env.ld_library_path) {
876af3df29aSGlenn Strauss 			cgi_env_add(env, CONST_STR_LEN("LD_LIBRARY_PATH"), BUF_PTR_LEN(p->env.ld_library_path));
877433f1883SJan Kneschke 		}
878bcdc6a3bSJan Kneschke 	      #ifdef __CYGWIN__
879bcdc6a3bSJan Kneschke 		/* CYGWIN needs SYSTEMROOT */
8802d914758SGlenn Strauss 		if (p->env.systemroot) {
881af3df29aSGlenn Strauss 			cgi_env_add(env, CONST_STR_LEN("SYSTEMROOT"), BUF_PTR_LEN(p->env.systemroot));
882433f1883SJan Kneschke 		}
883bcdc6a3bSJan Kneschke 	      #endif
884bcdc6a3bSJan Kneschke 
8857b3a4f13SGlenn Strauss 		/* adjust (uintptr_t) offsets to (char *) ptr
8867b3a4f13SGlenn Strauss 		 * (stored as offsets while accumulating in buffer,
8877b3a4f13SGlenn Strauss 		 *  in case buffer is reallocated during env creation) */
8887b3a4f13SGlenn Strauss 		if (__builtin_expect( (env->osize == env->oused), 0))
8897b3a4f13SGlenn Strauss 			cgi_env_offset_resize(env);
8907b3a4f13SGlenn Strauss 		envp = (char **)env->offsets;
8917b3a4f13SGlenn Strauss 		envp[env->oused] = NULL;
8927b3a4f13SGlenn Strauss 		const uintptr_t baseptr = (uintptr_t)env->b->ptr;
8937b3a4f13SGlenn Strauss 		for (i = 0; i < env->oused; ++i)
8947b3a4f13SGlenn Strauss 			envp[i] += baseptr;
895bcdc6a3bSJan Kneschke 
896bcdc6a3bSJan Kneschke 		/* set up args */
897bcdc6a3bSJan Kneschke 		i = 0;
898bcdc6a3bSJan Kneschke 
899af3df29aSGlenn Strauss 		if (!buffer_is_blank(cgi_handler)) {
900bcdc6a3bSJan Kneschke 			args[i++] = cgi_handler->ptr;
901bcdc6a3bSJan Kneschke 		}
9027c7f8c46SGlenn Strauss 		args[i++] = r->physical.path.ptr;
9034df22f2aSStefan Bühler 		args[i  ] = NULL;
904bcdc6a3bSJan Kneschke 	}
905bcdc6a3bSJan Kneschke 
9069a2404ceSGlenn Strauss 	int dfd = fdevent_open_dirname(r->physical.path.ptr,r->conf.follow_symlink);
907a9970fecSGlenn Strauss 	if (-1 == dfd) {
9087c7f8c46SGlenn Strauss 		log_perror(r->conf.errh, __FILE__, __LINE__, "open dirname %s failed", r->physical.path.ptr);
909bcdc6a3bSJan Kneschke 	}
910bcdc6a3bSJan Kneschke 
9117b615d5dSGlenn Strauss 	int serrh_fd = r->conf.serrh ? r->conf.serrh->fd : -1;
9127e000de0SGlenn Strauss 	pid_t pid = (dfd >= 0)
9137b3a4f13SGlenn Strauss 	  ? fdevent_fork_execve(args[0], args, envp,
9147b3a4f13SGlenn Strauss 	                        to_cgi_fds[0], from_cgi_fds[1], serrh_fd, dfd)
9157b3a4f13SGlenn Strauss 	  : -1;
9167b3a4f13SGlenn Strauss 
9177b3a4f13SGlenn Strauss 	chunk_buffer_release(env->boffsets);
9187b3a4f13SGlenn Strauss 	chunk_buffer_release(env->b);
9197b3a4f13SGlenn Strauss 	env->boffsets = NULL;
9207b3a4f13SGlenn Strauss 	env->b = NULL;
921a9970fecSGlenn Strauss 
9227e000de0SGlenn Strauss 	if (-1 == pid) {
923a9970fecSGlenn Strauss 		/* log error with errno prior to calling close() (might change errno) */
9247c7f8c46SGlenn Strauss 		log_perror(r->conf.errh, __FILE__, __LINE__, "fork failed");
925a9970fecSGlenn Strauss 		if (-1 != dfd) close(dfd);
92685b1f6afSStefan Bühler 		close(from_cgi_fds[0]);
92785b1f6afSStefan Bühler 		close(from_cgi_fds[1]);
92870195d06SGlenn Strauss 		if (0 == r->reqbody_length) {
92970195d06SGlenn Strauss 			close(to_cgi_fds[0]);
93070195d06SGlenn Strauss 		}
93170195d06SGlenn Strauss 		else if (-1 != to_cgi_fds[1]) {
93285b1f6afSStefan Bühler 			close(to_cgi_fds[0]);
93385b1f6afSStefan Bühler 			close(to_cgi_fds[1]);
93470195d06SGlenn Strauss 		}
935c065f36bSStefan Bühler 		return -1;
936a9970fecSGlenn Strauss 	} else {
937a9970fecSGlenn Strauss 		if (-1 != dfd) close(dfd);
9381982fda2SJan Kneschke 		close(from_cgi_fds[1]);
9391982fda2SJan Kneschke 
940bcdc6a3bSJan Kneschke 		hctx->fd = from_cgi_fds[0];
9417e000de0SGlenn Strauss 		hctx->cgi_pid = cgi_pid_add(p, pid, hctx);
9429030cfaeSGlenn Strauss 
9437c7f8c46SGlenn Strauss 		if (0 == r->reqbody_length) {
94470195d06SGlenn Strauss 			close(to_cgi_fds[0]);
94570195d06SGlenn Strauss 		}
94670195d06SGlenn Strauss 		else if (-1 == to_cgi_fds[1]) {
94770195d06SGlenn Strauss 			chunkqueue * const cq = &r->reqbody_queue;
94870195d06SGlenn Strauss 			chunkqueue_mark_written(cq, chunkqueue_length(cq));
9497c0f8a77SGlenn Strauss 		}
950fc19558fSGlenn Strauss 		else if (0 == fdevent_fcntl_set_nb(to_cgi_fds[1])
951fc19558fSGlenn Strauss 		         && 0 == cgi_write_request(hctx, to_cgi_fds[1])) {
95270195d06SGlenn Strauss 			close(to_cgi_fds[0]);
953fc19558fSGlenn Strauss 			++r->con->srv->cur_fds;
954fc19558fSGlenn Strauss 		}
955fc19558fSGlenn Strauss 		else {
95670195d06SGlenn Strauss 			close(to_cgi_fds[0]);
9577c0f8a77SGlenn Strauss 			close(to_cgi_fds[1]);
958fc19558fSGlenn Strauss 			/*(hctx->fd not yet registered with fdevent, so manually
959fc19558fSGlenn Strauss 			 * cleanup here; see fdevent_register() further below)*/
960fc19558fSGlenn Strauss 			close(hctx->fd);
961fc19558fSGlenn Strauss 			hctx->fd = -1;
9627c7f8c46SGlenn Strauss 			cgi_connection_close(hctx);
9637c0f8a77SGlenn Strauss 			return -1;
9647c0f8a77SGlenn Strauss 		}
96540f16d52SGlenn Strauss 
9667c7f8c46SGlenn Strauss 		++r->con->srv->cur_fds;
9677c0f8a77SGlenn Strauss 
9687c7f8c46SGlenn Strauss 		struct fdevents * const ev = hctx->ev;
969b5775b99SGlenn Strauss 		hctx->fdn = fdevent_register(ev, hctx->fd, cgi_handle_fdevent, hctx);
97048004c6aSGlenn Strauss 		if (-1 == fdevent_fcntl_set_nb(hctx->fd)) {
9717c7f8c46SGlenn Strauss 			log_perror(r->conf.errh, __FILE__, __LINE__, "fcntl failed");
9727c7f8c46SGlenn Strauss 			cgi_connection_close(hctx);
973bcdc6a3bSJan Kneschke 			return -1;
974bcdc6a3bSJan Kneschke 		}
9758c36615fSGlenn Strauss 		hctx->read_ts = log_monotonic_secs;
976b5775b99SGlenn Strauss 		fdevent_fdnode_event_set(ev, hctx->fdn, FDEVENT_IN | FDEVENT_RDHUP);
977bcdc6a3bSJan Kneschke 
978bcdc6a3bSJan Kneschke 		return 0;
979a9970fecSGlenn Strauss 	}
980bcdc6a3bSJan Kneschke }
981bcdc6a3bSJan Kneschke 
URIHANDLER_FUNC(cgi_is_handled)982bcdc6a3bSJan Kneschke URIHANDLER_FUNC(cgi_is_handled) {
983bcdc6a3bSJan Kneschke 	plugin_data *p = p_d;
984fe021118SGlenn Strauss 	const stat_cache_st *st;
985d61f3381SGlenn Strauss 	data_string *ds;
986bcdc6a3bSJan Kneschke 
9877c7f8c46SGlenn Strauss 	if (NULL != r->handler_module) return HANDLER_GO_ON;
9889a5e1652SGlenn Strauss 	/* r->physical.path is non-empty for handle_subrequest_start */
989af3df29aSGlenn Strauss 	/*if (buffer_is_blank(&r->physical.path)) return HANDLER_GO_ON;*/
990bcdc6a3bSJan Kneschke 
9917c7f8c46SGlenn Strauss 	mod_cgi_patch_config(r, p);
992131450daSGlenn Strauss 	if (NULL == p->conf.cgi) return HANDLER_GO_ON;
993bcdc6a3bSJan Kneschke 
9947c7f8c46SGlenn Strauss 	ds = (data_string *)array_match_key_suffix(p->conf.cgi, &r->physical.path);
995d61f3381SGlenn Strauss 	if (NULL == ds) return HANDLER_GO_ON;
996d61f3381SGlenn Strauss 
9979a5e1652SGlenn Strauss 	/* r->tmp_sce is set in http_response_physical_path_check() and is valid
9989a5e1652SGlenn Strauss 	 * in handle_subrequest_start callback -- handle_subrequest_start callbacks
9999a5e1652SGlenn Strauss 	 * should not change r->physical.path (or should invalidate r->tmp_sce) */
10009a5e1652SGlenn Strauss 	st = r->tmp_sce && buffer_is_equal(&r->tmp_sce->name, &r->physical.path)
10019a5e1652SGlenn Strauss 	   ? &r->tmp_sce->st
10029a5e1652SGlenn Strauss 	   : stat_cache_path_stat(&r->physical.path);
1003aa00359eSGlenn Strauss 	if (NULL == st) return HANDLER_GO_ON;
1004b9f245f2SGlenn Strauss 
1005fe021118SGlenn Strauss 	/* (aside: CGI might be executable even if it is not readable) */
1006b9f245f2SGlenn Strauss 	if (!S_ISREG(st->st_mode)) return HANDLER_GO_ON;
1007b9f245f2SGlenn Strauss 	if (p->conf.execute_x_only == 1 && (st->st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) == 0) return HANDLER_GO_ON;
1008ff5c18beSStefan Bühler 
10095d1aa5d0SGlenn Strauss 	if (__builtin_expect( (r->h2_connect_ext != 0), 0) && !p->conf.upgrade) {
10105d1aa5d0SGlenn Strauss 		r->http_status = 405; /* Method Not Allowed */
10115d1aa5d0SGlenn Strauss 		return HANDLER_FINISHED;
10125d1aa5d0SGlenn Strauss 	}
10135d1aa5d0SGlenn Strauss 
101470195d06SGlenn Strauss 	if (r->reqbody_length
101570195d06SGlenn Strauss 	    && p->tempfile_accum
101670195d06SGlenn Strauss 	    && !(r->conf.stream_request_body /*(if not streaming request body)*/
101770195d06SGlenn Strauss 	         & (FDEVENT_STREAM_REQUEST|FDEVENT_STREAM_REQUEST_BUFMIN))) {
101870195d06SGlenn Strauss 		/* store request body in single tempfile if not streaming request body*/
101973df7b64SGlenn Strauss 		r->reqbody_queue.upload_temp_file_size =
102073df7b64SGlenn Strauss 		  (off_t)((1uLL << (sizeof(off_t)*8-1))-1);
102170195d06SGlenn Strauss 	}
102270195d06SGlenn Strauss 
1023d61f3381SGlenn Strauss 	{
1024bbbbfb3dSGlenn Strauss 		handler_ctx *hctx = cgi_handler_ctx_init();
10257c7f8c46SGlenn Strauss 		hctx->ev = r->con->srv->ev;
10267c7f8c46SGlenn Strauss 		hctx->r = r;
1027dd23fcb2SGlenn Strauss 		hctx->con = r->con;
1028bbbbfb3dSGlenn Strauss 		hctx->plugin_data = p;
1029601c572cSGlenn Strauss 		hctx->cgi_handler = &ds->value;
10305bf5e1adSGlenn Strauss 		memcpy(&hctx->conf, &p->conf, sizeof(plugin_config));
10315d1aa5d0SGlenn Strauss 		if (__builtin_expect( (r->h2_connect_ext != 0), 0)) {
10325d1aa5d0SGlenn Strauss 		}
10335d1aa5d0SGlenn Strauss 		else if (!light_btst(r->rqst_htags, HTTP_HEADER_UPGRADE))
1034e8a6ed6eSGlenn Strauss 			hctx->conf.upgrade = 0;
1035e8a6ed6eSGlenn Strauss 		else if (!hctx->conf.upgrade || r->http_version != HTTP_VERSION_1_1) {
1036e8a6ed6eSGlenn Strauss 			hctx->conf.upgrade = 0;
1037e8a6ed6eSGlenn Strauss 			http_header_request_unset(r, HTTP_HEADER_UPGRADE,
1038e8a6ed6eSGlenn Strauss 			                          CONST_STR_LEN("Upgrade"));
1039e8a6ed6eSGlenn Strauss 		}
1040ce9e0dfcSGlenn Strauss 		if (hctx->conf.upgrade) {
1041ce9e0dfcSGlenn Strauss 			hctx->orig_reqbody_length = r->reqbody_length;
1042ce9e0dfcSGlenn Strauss 			r->reqbody_length = -1;
1043ce9e0dfcSGlenn Strauss 		}
1044f19f7162SGlenn Strauss 		hctx->opts.max_per_read =
1045f19f7162SGlenn Strauss 		  !(r->conf.stream_response_body /*(if not streaming response body)*/
1046f19f7162SGlenn Strauss 		    & (FDEVENT_STREAM_RESPONSE|FDEVENT_STREAM_RESPONSE_BUFMIN))
1047f19f7162SGlenn Strauss 		    ? 262144
1048f19f7162SGlenn Strauss 		    : (r->conf.stream_response_body & FDEVENT_STREAM_RESPONSE_BUFMIN)
1049f19f7162SGlenn Strauss 		      ? 16384  /* FDEVENT_STREAM_RESPONSE_BUFMIN */
1050f19f7162SGlenn Strauss 		      : 65536; /* FDEVENT_STREAM_RESPONSE */
10514796313eSGlenn Strauss 		hctx->opts.fdfmt = S_IFIFO;
10520a635fc8SGlenn Strauss 		hctx->opts.backend = BACKEND_CGI;
10530a635fc8SGlenn Strauss 		hctx->opts.authorizer = 0;
10540a635fc8SGlenn Strauss 		hctx->opts.local_redir = hctx->conf.local_redir;
10550a635fc8SGlenn Strauss 		hctx->opts.xsendfile_allow = hctx->conf.xsendfile_allow;
10560a635fc8SGlenn Strauss 		hctx->opts.xsendfile_docroot = hctx->conf.xsendfile_docroot;
105745b970e6SGlenn Strauss 		hctx->opts.pdata = hctx;
105845b970e6SGlenn Strauss 		hctx->opts.headers = cgi_response_headers;
10597c7f8c46SGlenn Strauss 		r->plugin_ctx[p->id] = hctx;
10607c7f8c46SGlenn Strauss 		r->handler_module = p->self;
10612bc0d288SJan Kneschke 	}
1062bcdc6a3bSJan Kneschke 
1063bcdc6a3bSJan Kneschke 	return HANDLER_GO_ON;
1064bcdc6a3bSJan Kneschke }
1065bcdc6a3bSJan Kneschke 
106673e0bb27SStefan Bühler /*
106773e0bb27SStefan Bühler  * - HANDLER_GO_ON : not our job
1068e5e66f79SGlenn Strauss  * - HANDLER_FINISHED: got response
1069e5e66f79SGlenn Strauss  * - HANDLER_WAIT_FOR_EVENT: waiting for response
107073e0bb27SStefan Bühler  */
SUBREQUEST_FUNC(mod_cgi_handle_subrequest)1071bcdc6a3bSJan Kneschke SUBREQUEST_FUNC(mod_cgi_handle_subrequest) {
107250bdb55dSGlenn Strauss 	plugin_data * const p = p_d;
10737c7f8c46SGlenn Strauss 	handler_ctx * const hctx = r->plugin_ctx[p->id];
1074bcdc6a3bSJan Kneschke 	if (NULL == hctx) return HANDLER_GO_ON;
1075bcdc6a3bSJan Kneschke 
107633ffec35SGlenn Strauss 	if (__builtin_expect(
107733ffec35SGlenn Strauss 	     (r->conf.stream_request_body & FDEVENT_STREAM_REQUEST_TCP_FIN), 0)
107833ffec35SGlenn Strauss 	    && hctx->conf.limits && hctx->conf.limits->signal_fin) {
107933ffec35SGlenn Strauss 		/* XXX: consider setting r->http_status = 499 if (0 == r->http_status)
108033ffec35SGlenn Strauss 		 * (499 is nginx custom status to indicate client closed connection) */
108133ffec35SGlenn Strauss 		if (-1 == hctx->fd) return HANDLER_ERROR; /*(CGI not yet spawned)*/
108233ffec35SGlenn Strauss 		if (hctx->cgi_pid) /* send signal to notify CGI about TCP FIN */
108333ffec35SGlenn Strauss 			cgi_pid_kill(hctx->cgi_pid, hctx->conf.limits->signal_fin);
108433ffec35SGlenn Strauss 	}
108533ffec35SGlenn Strauss 
1086dd23fcb2SGlenn Strauss 	const int rd_revents = hctx->rd_revents;
1087dd23fcb2SGlenn Strauss 	const int wr_revents = hctx->wr_revents;
1088dd23fcb2SGlenn Strauss 	if (rd_revents) {
1089dd23fcb2SGlenn Strauss 		hctx->rd_revents = 0;
1090dd23fcb2SGlenn Strauss 		handler_t rc = cgi_process_rd_revents(hctx, r, rd_revents);
1091dd23fcb2SGlenn Strauss 		if (rc != HANDLER_GO_ON) return rc; /*(might invalidate hctx)*/
1092dd23fcb2SGlenn Strauss 	}
1093dd23fcb2SGlenn Strauss 	if (wr_revents) {
1094dd23fcb2SGlenn Strauss 		hctx->wr_revents = 0;
1095dd23fcb2SGlenn Strauss 		handler_t rc = cgi_process_wr_revents(hctx, r, wr_revents);
1096dd23fcb2SGlenn Strauss 		if (rc != HANDLER_GO_ON) return rc; /*(might invalidate hctx)*/
1097dd23fcb2SGlenn Strauss 	}
10983f4f9344SGlenn Strauss 
10997c7f8c46SGlenn Strauss 	if ((r->conf.stream_response_body & FDEVENT_STREAM_RESPONSE_BUFMIN)
11007c7f8c46SGlenn Strauss 	    && r->resp_body_started) {
110181029b8bSGlenn Strauss 		if (chunkqueue_length(&r->write_queue) > 65536 - 4096) {
11027c7f8c46SGlenn Strauss 			fdevent_fdnode_event_clr(hctx->ev, hctx->fdn, FDEVENT_IN);
11039113011dSGlenn Strauss 		} else if (!(fdevent_fdnode_interest(hctx->fdn) & FDEVENT_IN)) {
1104e4bb5622SGlenn Strauss 			/* optimistic read from backend */
11057c7f8c46SGlenn Strauss 			handler_t rc = cgi_recv_response(r, hctx);  /*(might invalidate hctx)*/
110618a7b2beSGlenn Strauss 			if (rc != HANDLER_GO_ON) return rc;          /*(unless HANDLER_GO_ON)*/
11078c36615fSGlenn Strauss 			hctx->read_ts = log_monotonic_secs;
11087c7f8c46SGlenn Strauss 			fdevent_fdnode_event_add(hctx->ev, hctx->fdn, FDEVENT_IN);
110918a7b2beSGlenn Strauss 		}
111018a7b2beSGlenn Strauss 	}
111118a7b2beSGlenn Strauss 
111281029b8bSGlenn Strauss 	chunkqueue * const cq = &r->reqbody_queue;
111350bdb55dSGlenn Strauss 
11147c7f8c46SGlenn Strauss 	if (cq->bytes_in != (off_t)r->reqbody_length) {
1115f69f209eSGlenn Strauss 		/*(64k - 4k to attempt to avoid temporary files
1116f69f209eSGlenn Strauss 		 * in conjunction with FDEVENT_STREAM_REQUEST_BUFMIN)*/
111797e314fcSGlenn Strauss 		if (chunkqueue_length(cq) > 65536 - 4096
11187c7f8c46SGlenn Strauss 		    && (r->conf.stream_request_body & FDEVENT_STREAM_REQUEST_BUFMIN)){
11197c7f8c46SGlenn Strauss 			r->conf.stream_request_body &= ~FDEVENT_STREAM_REQUEST_POLLIN;
1120f69f209eSGlenn Strauss 		} else {
11218940fec8SGlenn Strauss 			handler_t rc = r->con->reqbody_read(r);
1122fa5e9b53SGlenn Strauss 			if (rc != HANDLER_GO_ON
1123317b4ba1SGlenn Strauss 			    && !(hctx->conf.upgrade && -1 == hctx->fd
1124fa5e9b53SGlenn Strauss 			         && rc == HANDLER_WAIT_FOR_EVENT))
1125fa5e9b53SGlenn Strauss 				return rc;
1126f69f209eSGlenn Strauss 		}
1127f69f209eSGlenn Strauss 	}
11284d7f5737SGlenn Strauss 
1129fa5e9b53SGlenn Strauss 	if (-1 == hctx->fd) {
11304d7f5737SGlenn Strauss 			/* CGI environment requires that Content-Length be set.
11314d7f5737SGlenn Strauss 			 * Send 411 Length Required if Content-Length missing.
11324d7f5737SGlenn Strauss 			 * (occurs here if client sends Transfer-Encoding: chunked
11334d7f5737SGlenn Strauss 			 *  and module is flagged to stream request body to backend) */
1134317b4ba1SGlenn Strauss 			if (-1 == r->reqbody_length && !hctx->conf.upgrade) {
1135bcddbe18SGlenn Strauss 				return (r->conf.stream_request_body & FDEVENT_STREAM_REQUEST)
11362f2eec18SGlenn Strauss 				  ? http_response_reqbody_read_error(r, 411)
1137bcddbe18SGlenn Strauss 				  : HANDLER_WAIT_FOR_EVENT;
11384d7f5737SGlenn Strauss 			}
11397c7f8c46SGlenn Strauss 		if (cgi_create_env(r, p, hctx, hctx->cgi_handler)) {
11407c7f8c46SGlenn Strauss 			r->http_status = 500;
11417c7f8c46SGlenn Strauss 			r->handler_module = NULL;
1142bbbbfb3dSGlenn Strauss 
1143bbbbfb3dSGlenn Strauss 			return HANDLER_FINISHED;
1144bbbbfb3dSGlenn Strauss 		}
11451474be78SGlenn Strauss 	} else if (!chunkqueue_is_empty(cq)) {
1146fa5e9b53SGlenn Strauss 		if (fdevent_fdnode_interest(hctx->fdntocgi) & FDEVENT_OUT)
1147fa5e9b53SGlenn Strauss 			return HANDLER_WAIT_FOR_EVENT;
114850bdb55dSGlenn Strauss 		if (0 != cgi_write_request(hctx, hctx->fdtocgi)) {
11497c7f8c46SGlenn Strauss 			cgi_connection_close(hctx);
1150f69f209eSGlenn Strauss 			return HANDLER_ERROR;
1151f69f209eSGlenn Strauss 		}
1152f69f209eSGlenn Strauss 	}
115373e0bb27SStefan Bühler 
1154f2cbd0a3SGlenn Strauss 	/* if not done, wait for CGI to close stdout, so we read EOF on pipe */
11554ef4baa5SGlenn Strauss 	return HANDLER_WAIT_FOR_EVENT;
1156bcdc6a3bSJan Kneschke }
1157bcdc6a3bSJan Kneschke 
1158bcdc6a3bSJan Kneschke 
11598c36615fSGlenn Strauss __attribute_cold__
11608c36615fSGlenn Strauss __attribute_noinline__
cgi_trigger_hctx_timeout(handler_ctx * const hctx,const char * const msg)11618c36615fSGlenn Strauss static void cgi_trigger_hctx_timeout(handler_ctx * const hctx, const char * const msg) {
11628c36615fSGlenn Strauss     request_st * const r = hctx->r;
11638c36615fSGlenn Strauss     joblist_append(r->con);
11648c36615fSGlenn Strauss 
11658c36615fSGlenn Strauss     log_error(r->conf.errh, __FILE__, __LINE__,
11668c36615fSGlenn Strauss       "%s timeout on CGI: %s (pid: %lld)",
11677e000de0SGlenn Strauss       msg, r->physical.path.ptr, (long long)hctx->cgi_pid->pid);
11688c36615fSGlenn Strauss 
11698c36615fSGlenn Strauss     if (*msg == 'w') { /* "write" */
11708c36615fSGlenn Strauss         /* theoretically, response might be waiting on hctx->fdn pipe
11718c36615fSGlenn Strauss          * if it arrived since we last checked for event, and if CGI
11728c36615fSGlenn Strauss          * timeout out while reading (or did not read) request body */
11738c36615fSGlenn Strauss         handler_t rc = cgi_recv_response(r, hctx); /*(might invalidate hctx)*/
11748c36615fSGlenn Strauss         if (rc != HANDLER_GO_ON) return;            /*(unless HANDLER_GO_ON)*/
11758c36615fSGlenn Strauss     }
11768c36615fSGlenn Strauss 
11778c36615fSGlenn Strauss     if (0 == r->http_status) r->http_status = 504; /* Gateway Timeout */
11788c36615fSGlenn Strauss     cgi_connection_close(hctx);
11798c36615fSGlenn Strauss }
11808c36615fSGlenn Strauss 
11818c36615fSGlenn Strauss 
cgi_trigger_cb(server * srv,void * p_d)11828c36615fSGlenn Strauss static handler_t cgi_trigger_cb(server *srv, void *p_d) {
11838c36615fSGlenn Strauss     UNUSED(srv);
11848c36615fSGlenn Strauss     const unix_time64_t mono = log_monotonic_secs;
11857e000de0SGlenn Strauss     plugin_data * const p = p_d;
11867e000de0SGlenn Strauss     for (cgi_pid_t *cgi_pid = p->cgi_pid; cgi_pid; cgi_pid = cgi_pid->next) {
11878c36615fSGlenn Strauss         /*(hctx stays in cgi_pid list until process pid is reaped,
11888c36615fSGlenn Strauss          * so p->cgi_pid[] is not modified during this loop)*/
11897e000de0SGlenn Strauss         handler_ctx * const hctx = cgi_pid->hctx;
11908c36615fSGlenn Strauss         if (!hctx) continue; /*(already called cgi_pid_kill())*/
11918c36615fSGlenn Strauss         const cgi_limits * const limits = hctx->conf.limits;
11928c36615fSGlenn Strauss         if (NULL == limits) continue;
11938c36615fSGlenn Strauss         if (limits->read_timeout && hctx->fdn
11948c36615fSGlenn Strauss             && (fdevent_fdnode_interest(hctx->fdn) & FDEVENT_IN)
11958c36615fSGlenn Strauss             && mono - hctx->read_ts > limits->read_timeout) {
11968c36615fSGlenn Strauss             cgi_trigger_hctx_timeout(hctx, "read");
11978c36615fSGlenn Strauss             continue;
11988c36615fSGlenn Strauss         }
11998c36615fSGlenn Strauss         if (limits->write_timeout && hctx->fdntocgi
12008c36615fSGlenn Strauss             && (fdevent_fdnode_interest(hctx->fdntocgi) & FDEVENT_OUT)
12018c36615fSGlenn Strauss             && mono - hctx->write_ts > limits->write_timeout) {
12028c36615fSGlenn Strauss             cgi_trigger_hctx_timeout(hctx, "write");
12038c36615fSGlenn Strauss             continue;
12048c36615fSGlenn Strauss         }
12058c36615fSGlenn Strauss     }
12068c36615fSGlenn Strauss     return HANDLER_GO_ON;
12078c36615fSGlenn Strauss }
12088c36615fSGlenn Strauss 
12098c36615fSGlenn Strauss 
cgi_waitpid_cb(server * srv,void * p_d,pid_t pid,int status)12109030cfaeSGlenn Strauss static handler_t cgi_waitpid_cb(server *srv, void *p_d, pid_t pid, int status) {
12117e000de0SGlenn Strauss     /*(XXX: if supporting a large number of CGI, might use a different algorithm
12127e000de0SGlenn Strauss      * instead of linked list, e.g. splaytree indexed with pid)*/
12139030cfaeSGlenn Strauss     plugin_data *p = (plugin_data *)p_d;
12147e000de0SGlenn Strauss     for (cgi_pid_t *cgi_pid = p->cgi_pid; cgi_pid; cgi_pid = cgi_pid->next) {
12157e000de0SGlenn Strauss         if (pid != cgi_pid->pid) continue;
12169030cfaeSGlenn Strauss 
12177e000de0SGlenn Strauss         handler_ctx * const hctx = cgi_pid->hctx;
12187e000de0SGlenn Strauss         if (hctx) hctx->cgi_pid = NULL;
12199030cfaeSGlenn Strauss 
12209030cfaeSGlenn Strauss         if (WIFEXITED(status)) {
12219030cfaeSGlenn Strauss             /* (skip logging (non-zero) CGI exit; might be very noisy) */
12229030cfaeSGlenn Strauss         }
12239030cfaeSGlenn Strauss         else if (WIFSIGNALED(status)) {
12249030cfaeSGlenn Strauss             /* ignore SIGTERM if sent by cgi_connection_close() (NULL == hctx)*/
122533ffec35SGlenn Strauss             if (WTERMSIG(status) != cgi_pid->signal_sent) {
12267c7f8c46SGlenn Strauss                 log_error_st *errh = hctx ? hctx->r->conf.errh : srv->errh;
1227010c2894SGlenn Strauss                 log_error(errh, __FILE__, __LINE__,
1228010c2894SGlenn Strauss                   "CGI pid %d died with signal %d", pid, WTERMSIG(status));
12299030cfaeSGlenn Strauss             }
12309030cfaeSGlenn Strauss         }
12319030cfaeSGlenn Strauss         else {
12327c7f8c46SGlenn Strauss             log_error_st *errh = hctx ? hctx->r->conf.errh : srv->errh;
1233010c2894SGlenn Strauss             log_error(errh, __FILE__, __LINE__,
1234010c2894SGlenn Strauss               "CGI pid %d ended unexpectedly", pid);
12359030cfaeSGlenn Strauss         }
12369030cfaeSGlenn Strauss 
123733ffec35SGlenn Strauss         cgi_pid_del(p, cgi_pid);
12389030cfaeSGlenn Strauss         return HANDLER_FINISHED;
12399030cfaeSGlenn Strauss     }
12409030cfaeSGlenn Strauss 
12419030cfaeSGlenn Strauss     return HANDLER_GO_ON;
12429030cfaeSGlenn Strauss }
12439030cfaeSGlenn Strauss 
12449030cfaeSGlenn Strauss 
1245b82d7b8aSGlenn Strauss __attribute_cold__
124663f785a2SStefan Bühler int mod_cgi_plugin_init(plugin *p);
mod_cgi_plugin_init(plugin * p)1247bcdc6a3bSJan Kneschke int mod_cgi_plugin_init(plugin *p) {
1248bcdc6a3bSJan Kneschke 	p->version     = LIGHTTPD_VERSION_ID;
1249e2de4e58SGlenn Strauss 	p->name        = "cgi";
1250bcdc6a3bSJan Kneschke 
125133c8cf41SGlenn Strauss 	p->handle_request_reset = cgi_connection_close_callback;
1252bcdc6a3bSJan Kneschke 	p->handle_subrequest_start = cgi_is_handled;
1253bcdc6a3bSJan Kneschke 	p->handle_subrequest = mod_cgi_handle_subrequest;
12548c36615fSGlenn Strauss 	p->handle_trigger = cgi_trigger_cb;
12559030cfaeSGlenn Strauss 	p->handle_waitpid = cgi_waitpid_cb;
1256bcdc6a3bSJan Kneschke 	p->init           = mod_cgi_init;
1257bcdc6a3bSJan Kneschke 	p->cleanup        = mod_cgi_free;
1258131450daSGlenn Strauss 	p->set_defaults   = mod_cgi_set_defaults;
1259bcdc6a3bSJan Kneschke 
1260bcdc6a3bSJan Kneschke 	return 0;
1261bcdc6a3bSJan Kneschke }
1262