xref: /lighttpd1.4/src/mod_cgi.c (revision 3392e8fb)
1 #include "first.h"
2 
3 #include "base.h"
4 #include "stat_cache.h"
5 #include "http_kv.h"
6 #include "log.h"
7 #include "connections.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 static int pipe_cloexec(int pipefd[2]) {
31   #ifdef HAVE_PIPE2
32     if (0 == pipe2(pipefd, O_CLOEXEC)) return 0;
33   #endif
34     return 0 == pipe(pipefd)
35        #ifdef FD_CLOEXEC
36         && 0 == fcntl(pipefd[0], F_SETFD, FD_CLOEXEC)
37         && 0 == fcntl(pipefd[1], F_SETFD, FD_CLOEXEC)
38        #endif
39       ?  0
40       : -1;
41 }
42 
43 typedef struct {
44 	char *ptr;
45 	size_t used;
46 	size_t size;
47 	size_t *offsets;
48 	size_t osize;
49 	size_t oused;
50 	char **eptr;
51 	size_t esize;
52 	buffer *ld_preload;
53 	buffer *ld_library_path;
54       #ifdef __CYGWIN__
55 	buffer *systemroot;
56       #endif
57 } env_accum;
58 
59 typedef struct {
60 	struct { pid_t pid; void *ctx; } *ptr;
61 	size_t used;
62 	size_t size;
63 } buffer_pid_t;
64 
65 typedef struct {
66 	const array *cgi;
67 	unsigned short execute_x_only;
68 	unsigned short local_redir;
69 	unsigned short xsendfile_allow;
70 	unsigned short upgrade;
71 	const array *xsendfile_docroot;
72 } plugin_config;
73 
74 typedef struct {
75 	PLUGIN_DATA;
76 	plugin_config defaults;
77 	plugin_config conf;
78 	buffer_pid_t cgi_pid;
79 	env_accum env;
80 } plugin_data;
81 
82 typedef struct {
83 	pid_t pid;
84 	int fd;
85 	int fdtocgi;
86 	fdnode *fdn;
87 	fdnode *fdntocgi;
88 
89 	request_st *r;
90 	struct fdevents *ev;      /* dumb pointer */
91 	plugin_data *plugin_data; /* dumb pointer */
92 
93 	buffer *response;
94 	buffer *cgi_handler;      /* dumb pointer */
95 	http_response_opts opts;
96 	plugin_config conf;
97 } handler_ctx;
98 
99 static handler_ctx * cgi_handler_ctx_init(void) {
100 	handler_ctx *hctx = calloc(1, sizeof(*hctx));
101 
102 	force_assert(hctx);
103 
104 	hctx->response = chunk_buffer_acquire();
105 	hctx->fd = -1;
106 	hctx->fdtocgi = -1;
107 
108 	return hctx;
109 }
110 
111 static void cgi_handler_ctx_free(handler_ctx *hctx) {
112 	chunk_buffer_release(hctx->response);
113 	free(hctx);
114 }
115 
116 INIT_FUNC(mod_cgi_init) {
117 	plugin_data *p;
118 	const char *s;
119 
120 	p = calloc(1, sizeof(*p));
121 
122 	force_assert(p);
123 
124 	/* for valgrind */
125 	s = getenv("LD_PRELOAD");
126 	if (s) p->env.ld_preload = buffer_init_string(s);
127 	s = getenv("LD_LIBRARY_PATH");
128 	if (s) p->env.ld_library_path = buffer_init_string(s);
129       #ifdef __CYGWIN__
130 	/* CYGWIN needs SYSTEMROOT */
131 	s = getenv("SYSTEMROOT");
132 	if (s) p->env.systemroot = buffer_init_string(s);
133       #endif
134 
135 	return p;
136 }
137 
138 
139 FREE_FUNC(mod_cgi_free) {
140 	plugin_data *p = p_d;
141 	buffer_pid_t *bp = &(p->cgi_pid);
142 	if (bp->ptr) free(bp->ptr);
143 	free(p->env.ptr);
144 	free(p->env.offsets);
145 	free(p->env.eptr);
146 	buffer_free(p->env.ld_preload);
147 	buffer_free(p->env.ld_library_path);
148       #ifdef __CYGWIN__
149 	buffer_free(p->env.systemroot);
150       #endif
151 }
152 
153 static void mod_cgi_merge_config_cpv(plugin_config * const pconf, const config_plugin_value_t * const cpv) {
154     switch (cpv->k_id) { /* index into static config_plugin_keys_t cpk[] */
155       case 0: /* cgi.assign */
156         pconf->cgi = cpv->v.a;
157         break;
158       case 1: /* cgi.execute-x-only */
159         pconf->execute_x_only = (unsigned short)cpv->v.u;
160         break;
161       case 2: /* cgi.x-sendfile */
162         pconf->xsendfile_allow = (unsigned short)cpv->v.u;
163         break;
164       case 3: /* cgi.x-sendfile-docroot */
165         pconf->xsendfile_docroot = cpv->v.a;
166         break;
167       case 4: /* cgi.local-redir */
168         pconf->local_redir = (unsigned short)cpv->v.u;
169         break;
170       case 5: /* cgi.upgrade */
171         pconf->upgrade = (unsigned short)cpv->v.u;
172         break;
173       default:/* should not happen */
174         return;
175     }
176 }
177 
178 static void mod_cgi_merge_config(plugin_config * const pconf, const config_plugin_value_t *cpv) {
179     do {
180         mod_cgi_merge_config_cpv(pconf, cpv);
181     } while ((++cpv)->k_id != -1);
182 }
183 
184 static void mod_cgi_patch_config(request_st * const r, plugin_data * const p) {
185     p->conf = p->defaults; /* copy small struct instead of memcpy() */
186     /*memcpy(&p->conf, &p->defaults, sizeof(plugin_config));*/
187     for (int i = 1, used = p->nconfig; i < used; ++i) {
188         if (config_check_cond(r, (uint32_t)p->cvlist[i].k_id))
189             mod_cgi_merge_config(&p->conf, p->cvlist + p->cvlist[i].v.u2[0]);
190     }
191 }
192 
193 SETDEFAULTS_FUNC(mod_cgi_set_defaults) {
194     static const config_plugin_keys_t cpk[] = {
195       { CONST_STR_LEN("cgi.assign"),
196         T_CONFIG_ARRAY_KVSTRING,
197         T_CONFIG_SCOPE_CONNECTION }
198      ,{ CONST_STR_LEN("cgi.execute-x-only"),
199         T_CONFIG_BOOL,
200         T_CONFIG_SCOPE_CONNECTION }
201      ,{ CONST_STR_LEN("cgi.x-sendfile"),
202         T_CONFIG_BOOL,
203         T_CONFIG_SCOPE_CONNECTION }
204      ,{ CONST_STR_LEN("cgi.x-sendfile-docroot"),
205         T_CONFIG_ARRAY_VLIST,
206         T_CONFIG_SCOPE_CONNECTION }
207      ,{ CONST_STR_LEN("cgi.local-redir"),
208         T_CONFIG_BOOL,
209         T_CONFIG_SCOPE_CONNECTION }
210      ,{ CONST_STR_LEN("cgi.upgrade"),
211         T_CONFIG_BOOL,
212         T_CONFIG_SCOPE_CONNECTION }
213      ,{ NULL, 0,
214         T_CONFIG_UNSET,
215         T_CONFIG_SCOPE_UNSET }
216     };
217 
218     plugin_data * const p = p_d;
219     if (!config_plugin_values_init(srv, p, cpk, "mod_cgi"))
220         return HANDLER_ERROR;
221 
222     /* process and validate config directives
223      * (init i to 0 if global context; to 1 to skip empty global context) */
224     for (int i = !p->cvlist[0].v.u2[1]; i < p->nconfig; ++i) {
225         const config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0];
226         for (; -1 != cpv->k_id; ++cpv) {
227             switch (cpv->k_id) {
228               case 0: /* cgi.assign */
229               case 1: /* cgi.execute-x-only */
230               case 2: /* cgi.x-sendfile */
231                 break;
232               case 3: /* cgi.x-sendfile-docroot */
233                 for (uint32_t j = 0; j < cpv->v.a->used; ++j) {
234                     data_string *ds = (data_string *)cpv->v.a->data[j];
235                     if (ds->value.ptr[0] != '/') {
236                         log_error(srv->errh, __FILE__, __LINE__,
237                           "%s paths must begin with '/'; invalid: \"%s\"",
238                           cpk[cpv->k_id].k, ds->value.ptr);
239                         return HANDLER_ERROR;
240                     }
241                     buffer_path_simplify(&ds->value, &ds->value);
242                     buffer_append_slash(&ds->value);
243                 }
244                 break;
245               case 4: /* cgi.local-redir */
246               case 5: /* cgi.upgrade */
247                 break;
248               default:/* should not happen */
249                 break;
250             }
251         }
252     }
253 
254     /* initialize p->defaults from global config context */
255     if (p->nconfig > 0 && p->cvlist->v.u2[1]) {
256         const config_plugin_value_t *cpv = p->cvlist + p->cvlist->v.u2[0];
257         if (-1 != cpv->k_id)
258             mod_cgi_merge_config(&p->defaults, cpv);
259     }
260 
261     return HANDLER_GO_ON;
262 }
263 
264 
265 static void cgi_pid_add(plugin_data *p, pid_t pid, void *ctx) {
266     buffer_pid_t *bp = &(p->cgi_pid);
267 
268     if (bp->used == bp->size) {
269         bp->size += 16;
270         bp->ptr = realloc(bp->ptr, sizeof(*bp->ptr) * bp->size);
271         force_assert(bp->ptr);
272     }
273 
274     bp->ptr[bp->used].pid = pid;
275     bp->ptr[bp->used].ctx = ctx;
276     ++bp->used;
277 }
278 
279 static void cgi_pid_kill(plugin_data *p, pid_t pid) {
280     buffer_pid_t *bp = &(p->cgi_pid);
281     for (size_t i = 0; i < bp->used; ++i) {
282         if (bp->ptr[i].pid == pid) {
283             bp->ptr[i].ctx = NULL;
284             kill(pid, SIGTERM);
285             return;
286         }
287     }
288 }
289 
290 static void cgi_pid_del(plugin_data *p, size_t i) {
291     buffer_pid_t *bp = &(p->cgi_pid);
292 
293     if (i != bp->used - 1)
294         bp->ptr[i] = bp->ptr[bp->used - 1];
295 
296     --bp->used;
297 }
298 
299 
300 static void cgi_connection_close_fdtocgi(handler_ctx *hctx) {
301 	/*(closes only hctx->fdtocgi)*/
302 	struct fdevents * const ev = hctx->ev;
303 	fdevent_fdnode_event_del(ev, hctx->fdntocgi);
304 	/*fdevent_unregister(ev, hctx->fdtocgi);*//*(handled below)*/
305 	fdevent_sched_close(ev, hctx->fdtocgi, 0);
306 	hctx->fdntocgi = NULL;
307 	hctx->fdtocgi = -1;
308 }
309 
310 static void cgi_connection_close(handler_ctx *hctx) {
311 	/* the connection to the browser went away, but we still have a connection
312 	 * to the CGI script
313 	 *
314 	 * close cgi-connection
315 	 */
316 
317 	if (hctx->fd != -1) {
318 		struct fdevents * const ev = hctx->ev;
319 		/* close connection to the cgi-script */
320 		fdevent_fdnode_event_del(ev, hctx->fdn);
321 		/*fdevent_unregister(ev, hctx->fd);*//*(handled below)*/
322 		fdevent_sched_close(ev, hctx->fd, 0);
323 		hctx->fdn = NULL;
324 	}
325 
326 	if (hctx->fdtocgi != -1) {
327 		cgi_connection_close_fdtocgi(hctx); /*(closes only hctx->fdtocgi)*/
328 	}
329 
330 	plugin_data * const p = hctx->plugin_data;
331 
332 	if (hctx->pid > 0) {
333 		cgi_pid_kill(p, hctx->pid);
334 	}
335 
336 	request_st * const r = hctx->r;
337 
338 	r->plugin_ctx[p->id] = NULL;
339 
340 	cgi_handler_ctx_free(hctx);
341 
342 	/* finish response (if not already r->resp_body_started, r->resp_body_finished) */
343 	if (r->handler_module == p->self) {
344 		http_response_backend_done(r);
345 	}
346 }
347 
348 static handler_t cgi_connection_close_callback(request_st * const r, void *p_d) {
349 	plugin_data *p = p_d;
350 	handler_ctx *hctx = r->plugin_ctx[p->id];
351 	if (hctx) cgi_connection_close(hctx);
352 
353 	return HANDLER_GO_ON;
354 }
355 
356 
357 static int cgi_write_request(handler_ctx *hctx, int fd);
358 
359 
360 static handler_t cgi_handle_fdevent_send (void *ctx, int revents) {
361 	handler_ctx *hctx = ctx;
362 	request_st * const r = hctx->r;
363 
364 	/*(joblist only actually necessary here in mod_cgi fdevent send if returning HANDLER_ERROR)*/
365 	joblist_append(r->con);
366 
367 	if (revents & FDEVENT_OUT) {
368 		if (0 != cgi_write_request(hctx, hctx->fdtocgi)) {
369 			cgi_connection_close(hctx);
370 			return HANDLER_ERROR;
371 		}
372 		/* more request body to be sent to CGI */
373 	}
374 
375 	if (revents & FDEVENT_HUP) {
376 		/* skip sending remaining data to CGI */
377 		if (r->reqbody_length) {
378 			chunkqueue *cq = &r->reqbody_queue;
379 			chunkqueue_mark_written(cq, chunkqueue_length(cq));
380 			if (cq->bytes_in != (off_t)r->reqbody_length) {
381 				r->keep_alive = 0;
382 			}
383 		}
384 
385 		cgi_connection_close_fdtocgi(hctx); /*(closes only hctx->fdtocgi)*/
386 	} else if (revents & FDEVENT_ERR) {
387 		/* kill all connections to the cgi process */
388 #if 1
389 		log_error(r->conf.errh, __FILE__, __LINE__, "cgi-FDEVENT_ERR");
390 #endif
391 		cgi_connection_close(hctx);
392 		return HANDLER_ERROR;
393 	}
394 
395 	return HANDLER_FINISHED;
396 }
397 
398 
399 static handler_t cgi_response_headers(request_st * const r, struct http_response_opts_t *opts) {
400     /* response headers just completed */
401     handler_ctx *hctx = (handler_ctx *)opts->pdata;
402 
403     if (light_btst(r->resp_htags, HTTP_HEADER_UPGRADE)) {
404         if (hctx->conf.upgrade && r->http_status == 101) {
405             /* 101 Switching Protocols; transition to transparent proxy */
406             http_response_upgrade_read_body_unknown(r);
407         }
408         else {
409             light_bclr(r->resp_htags, HTTP_HEADER_UPGRADE);
410           #if 0
411             /* preserve prior questionable behavior; likely broken behavior
412              * anyway if backend thinks connection is being upgraded but client
413              * does not receive Connection: upgrade */
414             http_header_response_unset(r, HTTP_HEADER_UPGRADE,
415                                        CONST_STR_LEN("Upgrade"));
416           #endif
417         }
418     }
419 
420     if (hctx->conf.upgrade
421         && !light_btst(r->resp_htags, HTTP_HEADER_UPGRADE)) {
422         chunkqueue *cq = &r->reqbody_queue;
423         hctx->conf.upgrade = 0;
424         if (cq->bytes_out == (off_t)r->reqbody_length) {
425             cgi_connection_close_fdtocgi(hctx); /*(closes hctx->fdtocgi)*/
426         }
427     }
428 
429     return HANDLER_GO_ON;
430 }
431 
432 
433 static int cgi_recv_response(request_st * const r, handler_ctx * const hctx) {
434 		switch (http_response_read(r, &hctx->opts,
435 					   hctx->response, hctx->fdn)) {
436 		default:
437 			return HANDLER_GO_ON;
438 		case HANDLER_ERROR:
439 			http_response_backend_error(r);
440 			__attribute_fallthrough__
441 		case HANDLER_FINISHED:
442 			cgi_connection_close(hctx);
443 			return HANDLER_FINISHED;
444 		case HANDLER_COMEBACK:
445 			/* flag for mod_cgi_handle_subrequest() */
446 			hctx->conf.local_redir = 2;
447 			buffer_clear(hctx->response);
448 			return HANDLER_COMEBACK;
449 		}
450 }
451 
452 
453 static handler_t cgi_handle_fdevent(void *ctx, int revents) {
454 	handler_ctx *hctx = ctx;
455 	request_st * const r = hctx->r;
456 
457 	joblist_append(r->con);
458 
459 	if (revents & FDEVENT_IN) {
460 		handler_t rc = cgi_recv_response(r, hctx); /*(might invalidate hctx)*/
461 		if (rc != HANDLER_GO_ON) return rc;         /*(unless HANDLER_GO_ON)*/
462 	}
463 
464 	/* perhaps this issue is already handled */
465 	if (revents & (FDEVENT_HUP|FDEVENT_RDHUP)) {
466 		if (r->resp_body_started) {
467 			/* drain any remaining data from kernel pipe buffers
468 			 * even if (r->conf.stream_response_body
469 			 *          & FDEVENT_STREAM_RESPONSE_BUFMIN)
470 			 * since event loop will spin on fd FDEVENT_HUP event
471 			 * until unregistered. */
472 			handler_t rc;
473 			const unsigned short flags = r->conf.stream_response_body;
474 			r->conf.stream_response_body &= ~FDEVENT_STREAM_RESPONSE_BUFMIN;
475 			r->conf.stream_response_body |= FDEVENT_STREAM_RESPONSE_POLLRDHUP;
476 			do {
477 				rc = cgi_recv_response(r,hctx); /*(might invalidate hctx)*/
478 			} while (rc == HANDLER_GO_ON);           /*(unless HANDLER_GO_ON)*/
479 			r->conf.stream_response_body = flags;
480 			return rc; /* HANDLER_FINISHED or HANDLER_COMEBACK or HANDLER_ERROR */
481 		} else if (!buffer_string_is_empty(hctx->response)) {
482 			/* unfinished header package which is a body in reality */
483 			r->resp_body_started = 1;
484 			if (0 != http_chunk_append_buffer(r, hctx->response)) {
485 				cgi_connection_close(hctx);
486 				return HANDLER_ERROR;
487 			}
488 			if (0 == r->http_status) r->http_status = 200; /* OK */
489 		}
490 		cgi_connection_close(hctx);
491 	} else if (revents & FDEVENT_ERR) {
492 		/* kill all connections to the cgi process */
493 		cgi_connection_close(hctx);
494 		return HANDLER_ERROR;
495 	}
496 
497 	return HANDLER_FINISHED;
498 }
499 
500 
501 static int cgi_env_add(void *venv, const char *key, size_t key_len, const char *val, size_t val_len) {
502 	env_accum *env = venv;
503 	char *dst;
504 
505 	if (!key || (!val && val_len)) return -1;
506 
507 	if (env->size - env->used < key_len + val_len + 2) {
508 		if (0 == env->size) env->size = 4096;
509 		do { env->size *= 2; } while (env->size - env->used < key_len + val_len + 2);
510 		env->ptr = realloc(env->ptr, env->size);
511 		force_assert(env->ptr);
512 	}
513 
514 	dst = env->ptr + env->used;
515 	memcpy(dst, key, key_len);
516 	dst[key_len] = '=';
517 	if (val_len) memcpy(dst + key_len + 1, val, val_len);
518 	dst[key_len + 1 + val_len] = '\0';
519 
520 	if (env->osize == env->oused) {
521 		env->osize += 16;
522 		env->offsets = realloc(env->offsets, env->osize * sizeof(*env->offsets));
523 		force_assert(env->offsets);
524 	}
525 	env->offsets[env->oused++] = env->used;
526 	env->used += key_len + val_len + 2;
527 
528 	return 0;
529 }
530 
531 static int cgi_write_request(handler_ctx *hctx, int fd) {
532 	request_st * const r = hctx->r;
533 	chunkqueue *cq = &r->reqbody_queue;
534 	chunk *c;
535 
536 	chunkqueue_remove_finished_chunks(cq); /* unnecessary? */
537 
538 	/* old comment: windows doesn't support select() on pipes - wouldn't be easy to fix for all platforms.
539 	 * solution: if this is still a problem on windows, then substitute
540 	 * socketpair() for pipe() and closesocket() for close() on windows.
541 	 */
542 
543 	for (c = cq->first; c; c = cq->first) {
544 		ssize_t wr = chunkqueue_write_chunk_to_pipe(fd, cq, r->conf.errh);
545 		if (wr > 0) {
546 			chunkqueue_mark_written(cq, wr);
547 			/* continue if wrote whole chunk or wrote 16k block
548 			 * (see chunkqueue_write_chunk_file_intermed()) */
549 			if (c != cq->first || wr == 16384)
550 				continue;
551 			/*(else partial write)*/
552 		}
553 		else if (wr < 0) {
554 				switch(errno) {
555 				case EAGAIN:
556 				case EINTR:
557 					/* ignore and try again later */
558 					break;
559 				case EPIPE:
560 				case ECONNRESET:
561 					/* connection closed */
562 					log_error(r->conf.errh, __FILE__, __LINE__,
563 					  "failed to send post data to cgi, connection closed by CGI");
564 					/* skip all remaining data */
565 					chunkqueue_mark_written(cq, chunkqueue_length(cq));
566 					break;
567 				default:
568 					/* fatal error */
569 					log_perror(r->conf.errh, __FILE__, __LINE__, "write() failed");
570 					return -1;
571 				}
572 		}
573 		/*if (0 == wr) break;*/ /*(might block)*/
574 		break;
575 	}
576 
577 	if (cq->bytes_out == (off_t)r->reqbody_length && !hctx->conf.upgrade) {
578 		/* sent all request body input */
579 		/* close connection to the cgi-script */
580 		if (-1 == hctx->fdtocgi) { /*(received request body sent in initial send to pipe buffer)*/
581 			--r->con->srv->cur_fds;
582 			if (close(fd)) {
583 				log_perror(r->conf.errh, __FILE__, __LINE__, "cgi stdin close %d failed", fd);
584 			}
585 		} else {
586 			cgi_connection_close_fdtocgi(hctx); /*(closes only hctx->fdtocgi)*/
587 		}
588 	} else {
589 		off_t cqlen = chunkqueue_length(cq);
590 		if (cq->bytes_in != r->reqbody_length && cqlen < 65536 - 16384) {
591 			/*(r->conf.stream_request_body & FDEVENT_STREAM_REQUEST)*/
592 			if (!(r->conf.stream_request_body & FDEVENT_STREAM_REQUEST_POLLIN)) {
593 				r->conf.stream_request_body |= FDEVENT_STREAM_REQUEST_POLLIN;
594 				r->con->is_readable = 1; /* trigger optimistic read from client */
595 			}
596 		}
597 		struct fdevents * const ev = hctx->ev;
598 		if (-1 == hctx->fdtocgi) { /*(not registered yet)*/
599 			hctx->fdtocgi = fd;
600 			hctx->fdntocgi = fdevent_register(ev, hctx->fdtocgi, cgi_handle_fdevent_send, hctx);
601 		}
602 		if (0 == cqlen) { /*(chunkqueue_is_empty(cq))*/
603 			if ((fdevent_fdnode_interest(hctx->fdntocgi) & FDEVENT_OUT)) {
604 				fdevent_fdnode_event_set(ev, hctx->fdntocgi, 0);
605 			}
606 		} else {
607 			/* more request body remains to be sent to CGI so register for fdevents */
608 			fdevent_fdnode_event_set(ev, hctx->fdntocgi, FDEVENT_OUT);
609 		}
610 	}
611 
612 	return 0;
613 }
614 
615 static int cgi_create_env(request_st * const r, plugin_data * const p, handler_ctx * const hctx, buffer * const cgi_handler) {
616 	char *args[3];
617 	int to_cgi_fds[2];
618 	int from_cgi_fds[2];
619 	int dfd = -1;
620 	UNUSED(p);
621 
622 	if (!buffer_string_is_empty(cgi_handler)) {
623 		if (NULL == stat_cache_path_stat(cgi_handler)) {
624 			log_perror(r->conf.errh, __FILE__, __LINE__,
625 			  "stat for cgi-handler %s", cgi_handler->ptr);
626 			return -1;
627 		}
628 	}
629 
630 	if (pipe_cloexec(to_cgi_fds)) {
631 		log_perror(r->conf.errh, __FILE__, __LINE__, "pipe failed");
632 		return -1;
633 	}
634 	if (pipe_cloexec(from_cgi_fds)) {
635 		close(to_cgi_fds[0]);
636 		close(to_cgi_fds[1]);
637 		log_perror(r->conf.errh, __FILE__, __LINE__, "pipe failed");
638 		return -1;
639 	}
640 
641 	{
642 		size_t i = 0;
643 		http_cgi_opts opts = { 0, 0, NULL, NULL };
644 		env_accum *env = &p->env;
645 		env->used = 0;
646 		env->oused = 0;
647 
648 		/* create environment */
649 
650 		http_cgi_headers(r, &opts, cgi_env_add, env);
651 
652 		/* for valgrind */
653 		if (p->env.ld_preload) {
654 			cgi_env_add(env, CONST_STR_LEN("LD_PRELOAD"), CONST_BUF_LEN(p->env.ld_preload));
655 		}
656 		if (p->env.ld_library_path) {
657 			cgi_env_add(env, CONST_STR_LEN("LD_LIBRARY_PATH"), CONST_BUF_LEN(p->env.ld_library_path));
658 		}
659 	      #ifdef __CYGWIN__
660 		/* CYGWIN needs SYSTEMROOT */
661 		if (p->env.systemroot) {
662 			cgi_env_add(env, CONST_STR_LEN("SYSTEMROOT"), CONST_BUF_LEN(p->env.systemroot));
663 		}
664 	      #endif
665 
666 		if (env->esize <= env->oused) {
667 			env->esize = (env->oused + 1 + 0xf) & ~(0xfuL);
668 			env->eptr = realloc(env->eptr, env->esize * sizeof(*env->eptr));
669 			force_assert(env->eptr);
670 		}
671 		for (i = 0; i < env->oused; ++i) {
672 			env->eptr[i] = env->ptr + env->offsets[i];
673 		}
674 		env->eptr[env->oused] = NULL;
675 
676 		/* set up args */
677 		i = 0;
678 
679 		if (!buffer_string_is_empty(cgi_handler)) {
680 			args[i++] = cgi_handler->ptr;
681 		}
682 		args[i++] = r->physical.path.ptr;
683 		args[i  ] = NULL;
684 	}
685 
686 	dfd = fdevent_open_dirname(r->physical.path.ptr, r->conf.follow_symlink);
687 	if (-1 == dfd) {
688 		log_perror(r->conf.errh, __FILE__, __LINE__, "open dirname %s failed", r->physical.path.ptr);
689 	}
690 
691 	int serrh_fd = r->conf.serrh ? r->conf.serrh->errorlog_fd : -1;
692 	hctx->pid = (dfd >= 0) ? fdevent_fork_execve(args[0], args, p->env.eptr, to_cgi_fds[0], from_cgi_fds[1], serrh_fd, dfd) : -1;
693 
694 	if (-1 == hctx->pid) {
695 		/* log error with errno prior to calling close() (might change errno) */
696 		log_perror(r->conf.errh, __FILE__, __LINE__, "fork failed");
697 		if (-1 != dfd) close(dfd);
698 		close(from_cgi_fds[0]);
699 		close(from_cgi_fds[1]);
700 		close(to_cgi_fds[0]);
701 		close(to_cgi_fds[1]);
702 		return -1;
703 	} else {
704 		if (-1 != dfd) close(dfd);
705 		close(from_cgi_fds[1]);
706 		close(to_cgi_fds[0]);
707 
708 		hctx->fd = from_cgi_fds[0];
709 
710 		cgi_pid_add(p, hctx->pid, hctx);
711 
712 		if (0 == r->reqbody_length) {
713 			close(to_cgi_fds[1]);
714 		}
715 		else if (0 == fdevent_fcntl_set_nb(to_cgi_fds[1])
716 		         && 0 == cgi_write_request(hctx, to_cgi_fds[1])) {
717 			++r->con->srv->cur_fds;
718 		}
719 		else {
720 			close(to_cgi_fds[1]);
721 			/*(hctx->fd not yet registered with fdevent, so manually
722 			 * cleanup here; see fdevent_register() further below)*/
723 			close(hctx->fd);
724 			hctx->fd = -1;
725 			cgi_connection_close(hctx);
726 			return -1;
727 		}
728 
729 		++r->con->srv->cur_fds;
730 
731 		struct fdevents * const ev = hctx->ev;
732 		hctx->fdn = fdevent_register(ev, hctx->fd, cgi_handle_fdevent, hctx);
733 		if (-1 == fdevent_fcntl_set_nb(hctx->fd)) {
734 			log_perror(r->conf.errh, __FILE__, __LINE__, "fcntl failed");
735 			cgi_connection_close(hctx);
736 			return -1;
737 		}
738 		fdevent_fdnode_event_set(ev, hctx->fdn, FDEVENT_IN | FDEVENT_RDHUP);
739 
740 		return 0;
741 	}
742 }
743 
744 URIHANDLER_FUNC(cgi_is_handled) {
745 	plugin_data *p = p_d;
746 	const stat_cache_st *st;
747 	data_string *ds;
748 
749 	if (NULL != r->handler_module) return HANDLER_GO_ON;
750 	if (buffer_is_empty(&r->physical.path)) return HANDLER_GO_ON;
751 
752 	mod_cgi_patch_config(r, p);
753 	if (NULL == p->conf.cgi) return HANDLER_GO_ON;
754 
755 	ds = (data_string *)array_match_key_suffix(p->conf.cgi, &r->physical.path);
756 	if (NULL == ds) return HANDLER_GO_ON;
757 
758 	st = stat_cache_path_stat(&r->physical.path);
759 	if (NULL == st) return HANDLER_GO_ON;
760 
761 	/* (aside: CGI might be executable even if it is not readable) */
762 	if (!S_ISREG(st->st_mode)) return HANDLER_GO_ON;
763 	if (p->conf.execute_x_only == 1 && (st->st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) == 0) return HANDLER_GO_ON;
764 
765 	{
766 		handler_ctx *hctx = cgi_handler_ctx_init();
767 		hctx->ev = r->con->srv->ev;
768 		hctx->r = r;
769 		hctx->plugin_data = p;
770 		hctx->cgi_handler = &ds->value;
771 		memcpy(&hctx->conf, &p->conf, sizeof(plugin_config));
772 		hctx->conf.upgrade =
773 		  hctx->conf.upgrade
774 		  && r->http_version == HTTP_VERSION_1_1
775 		  && light_btst(r->rqst_htags, HTTP_HEADER_UPGRADE);
776 		hctx->opts.fdfmt = S_IFIFO;
777 		hctx->opts.backend = BACKEND_CGI;
778 		hctx->opts.authorizer = 0;
779 		hctx->opts.local_redir = hctx->conf.local_redir;
780 		hctx->opts.xsendfile_allow = hctx->conf.xsendfile_allow;
781 		hctx->opts.xsendfile_docroot = hctx->conf.xsendfile_docroot;
782 		hctx->opts.pdata = hctx;
783 		hctx->opts.headers = cgi_response_headers;
784 		r->plugin_ctx[p->id] = hctx;
785 		r->handler_module = p->self;
786 	}
787 
788 	return HANDLER_GO_ON;
789 }
790 
791 __attribute_cold__
792 __attribute_noinline__
793 static handler_t mod_cgi_local_redir(request_st * const r) {
794     /* must be called from mod_cgi_handle_subrequest() so that HANDLER_COMEBACK
795      * return value propagates back through connection_state_machine() */
796     http_response_reset(r); /*(includes r->http_status = 0)*/
797     plugins_call_handle_request_reset(r);
798     /*cgi_connection_close(hctx);*//*(already cleaned up and hctx is now invalid)*/
799     return HANDLER_COMEBACK;
800 }
801 
802 /*
803  * - HANDLER_GO_ON : not our job
804  * - HANDLER_FINISHED: got response
805  * - HANDLER_WAIT_FOR_EVENT: waiting for response
806  */
807 SUBREQUEST_FUNC(mod_cgi_handle_subrequest) {
808 	plugin_data * const p = p_d;
809 	handler_ctx * const hctx = r->plugin_ctx[p->id];
810 	if (NULL == hctx) return HANDLER_GO_ON;
811 
812 	if (2 == hctx->conf.local_redir) return mod_cgi_local_redir(r);
813 
814 	if ((r->conf.stream_response_body & FDEVENT_STREAM_RESPONSE_BUFMIN)
815 	    && r->resp_body_started) {
816 		if (chunkqueue_length(&r->write_queue) > 65536 - 4096) {
817 			fdevent_fdnode_event_clr(hctx->ev, hctx->fdn, FDEVENT_IN);
818 		} else if (!(fdevent_fdnode_interest(hctx->fdn) & FDEVENT_IN)) {
819 			/* optimistic read from backend */
820 			handler_t rc = cgi_recv_response(r, hctx);  /*(might invalidate hctx)*/
821 			if (rc == HANDLER_COMEBACK) mod_cgi_local_redir(r);
822 			if (rc != HANDLER_GO_ON) return rc;          /*(unless HANDLER_GO_ON)*/
823 			fdevent_fdnode_event_add(hctx->ev, hctx->fdn, FDEVENT_IN);
824 		}
825 	}
826 
827 	chunkqueue * const cq = &r->reqbody_queue;
828 
829 	if (cq->bytes_in != (off_t)r->reqbody_length) {
830 		/*(64k - 4k to attempt to avoid temporary files
831 		 * in conjunction with FDEVENT_STREAM_REQUEST_BUFMIN)*/
832 		if (chunkqueue_length(cq) > 65536 - 4096
833 		    && (r->conf.stream_request_body & FDEVENT_STREAM_REQUEST_BUFMIN)){
834 			r->conf.stream_request_body &= ~FDEVENT_STREAM_REQUEST_POLLIN;
835 			if (-1 != hctx->fd) return HANDLER_WAIT_FOR_EVENT;
836 		} else {
837 			handler_t rc = r->con->reqbody_read(r);
838 			if (!chunkqueue_is_empty(cq)) {
839 				if (fdevent_fdnode_interest(hctx->fdntocgi) & FDEVENT_OUT) {
840 					return (rc == HANDLER_GO_ON) ? HANDLER_WAIT_FOR_EVENT : rc;
841 				}
842 			}
843 			if (rc != HANDLER_GO_ON) return rc;
844 
845 			/* CGI environment requires that Content-Length be set.
846 			 * Send 411 Length Required if Content-Length missing.
847 			 * (occurs here if client sends Transfer-Encoding: chunked
848 			 *  and module is flagged to stream request body to backend) */
849 			if (-1 == r->reqbody_length) {
850 				return (r->conf.stream_request_body & FDEVENT_STREAM_REQUEST)
851 				  ? http_response_reqbody_read_error(r, 411)
852 				  : HANDLER_WAIT_FOR_EVENT;
853 			}
854 		}
855 	}
856 
857 	if (-1 == hctx->fd) {
858 		if (cgi_create_env(r, p, hctx, hctx->cgi_handler)) {
859 			r->http_status = 500;
860 			r->handler_module = NULL;
861 
862 			return HANDLER_FINISHED;
863 		}
864 	} else if (!chunkqueue_is_empty(cq)) {
865 		if (0 != cgi_write_request(hctx, hctx->fdtocgi)) {
866 			cgi_connection_close(hctx);
867 			return HANDLER_ERROR;
868 		}
869 	}
870 
871 	/* if not done, wait for CGI to close stdout, so we read EOF on pipe */
872 	return HANDLER_WAIT_FOR_EVENT;
873 }
874 
875 
876 static handler_t cgi_waitpid_cb(server *srv, void *p_d, pid_t pid, int status) {
877     plugin_data *p = (plugin_data *)p_d;
878     for (size_t i = 0; i < p->cgi_pid.used; ++i) {
879         handler_ctx *hctx;
880         if (pid != p->cgi_pid.ptr[i].pid) continue;
881 
882         hctx = (handler_ctx *)p->cgi_pid.ptr[i].ctx;
883         if (hctx) hctx->pid = -1;
884         cgi_pid_del(p, i);
885 
886         if (WIFEXITED(status)) {
887             /* (skip logging (non-zero) CGI exit; might be very noisy) */
888         }
889         else if (WIFSIGNALED(status)) {
890             /* ignore SIGTERM if sent by cgi_connection_close() (NULL == hctx)*/
891             if (WTERMSIG(status) != SIGTERM || NULL != hctx) {
892                 log_error_st *errh = hctx ? hctx->r->conf.errh : srv->errh;
893                 log_error(errh, __FILE__, __LINE__,
894                   "CGI pid %d died with signal %d", pid, WTERMSIG(status));
895             }
896         }
897         else {
898             log_error_st *errh = hctx ? hctx->r->conf.errh : srv->errh;
899             log_error(errh, __FILE__, __LINE__,
900               "CGI pid %d ended unexpectedly", pid);
901         }
902 
903         return HANDLER_FINISHED;
904     }
905 
906     return HANDLER_GO_ON;
907 }
908 
909 
910 int mod_cgi_plugin_init(plugin *p);
911 int mod_cgi_plugin_init(plugin *p) {
912 	p->version     = LIGHTTPD_VERSION_ID;
913 	p->name        = "cgi";
914 
915 	p->handle_request_reset = cgi_connection_close_callback;
916 	p->handle_subrequest_start = cgi_is_handled;
917 	p->handle_subrequest = mod_cgi_handle_subrequest;
918 	p->handle_waitpid = cgi_waitpid_cb;
919 	p->init           = mod_cgi_init;
920 	p->cleanup        = mod_cgi_free;
921 	p->set_defaults   = mod_cgi_set_defaults;
922 
923 	return 0;
924 }
925