xref: /lighttpd1.4/src/mod_cgi.c (revision 3d04bc45)
1 #include "first.h"
2 
3 #include "server.h"
4 #include "stat_cache.h"
5 #include "keyvalue.h"
6 #include "log.h"
7 #include "connections.h"
8 #include "joblist.h"
9 #include "response.h"
10 #include "http_chunk.h"
11 #include "network_backends.h"
12 
13 #include "plugin.h"
14 
15 #include <sys/types.h>
16 #include "sys-mmap.h"
17 
18 #ifdef __WIN32
19 # include <winsock2.h>
20 #else
21 # include <sys/socket.h>
22 # include <sys/wait.h>
23 # include <netinet/in.h>
24 # include <arpa/inet.h>
25 #endif
26 
27 #include <unistd.h>
28 #include <errno.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <fdevent.h>
32 #include <signal.h>
33 #include <ctype.h>
34 #include <assert.h>
35 
36 #include <stdio.h>
37 #include <fcntl.h>
38 
39 #if defined(O_CLOEXEC) && (!defined(__FreeBSD__) || defined(F_DUPFD_CLOEXEC))
40 #define pipe_cloexec(pipefd) pipe2((pipefd), O_CLOEXEC)
41 #elif defined FD_CLOEXEC
42 #define pipe_cloexec(pipefd) \
43   (   0 == pipe(pipefd) \
44    && 0 == fcntl(pipefd[0], F_SETFD, FD_CLOEXEC) \
45    && 0 == fcntl(pipefd[1], F_SETFD, FD_CLOEXEC) \
46     ? 0 \
47     : -1)
48 #else
49 #define pipe_cloexec(pipefd) pipe(pipefd)
50 #endif
51 
52 enum {EOL_UNSET, EOL_N, EOL_RN};
53 
54 typedef struct {
55 	char **ptr;
56 
57 	size_t size;
58 	size_t used;
59 } char_array;
60 
61 typedef struct {
62 	pid_t *ptr;
63 	size_t used;
64 	size_t size;
65 } buffer_pid_t;
66 
67 typedef struct {
68 	array *cgi;
69 	unsigned short execute_x_only;
70 	unsigned short xsendfile_allow;
71 	array *xsendfile_docroot;
72 } plugin_config;
73 
74 typedef struct {
75 	PLUGIN_DATA;
76 	buffer_pid_t cgi_pid;
77 
78 	buffer *parse_response;
79 
80 	plugin_config **config_storage;
81 
82 	plugin_config conf;
83 } plugin_data;
84 
85 typedef struct {
86 	pid_t pid;
87 	int fd;
88 	int fdtocgi;
89 	int fde_ndx; /* index into the fd-event buffer */
90 	int fde_ndx_tocgi; /* index into the fd-event buffer */
91 
92 	connection *remote_conn;  /* dumb pointer */
93 	plugin_data *plugin_data; /* dumb pointer */
94 
95 	buffer *response;
96 	buffer *response_header;
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 = buffer_init();
105 	hctx->response_header = buffer_init();
106 	hctx->fd = -1;
107 	hctx->fdtocgi = -1;
108 
109 	return hctx;
110 }
111 
112 static void cgi_handler_ctx_free(handler_ctx *hctx) {
113 	buffer_free(hctx->response);
114 	buffer_free(hctx->response_header);
115 
116 	free(hctx);
117 }
118 
119 enum {FDEVENT_HANDLED_UNSET, FDEVENT_HANDLED_FINISHED, FDEVENT_HANDLED_NOT_FINISHED, FDEVENT_HANDLED_COMEBACK, FDEVENT_HANDLED_ERROR};
120 
121 INIT_FUNC(mod_cgi_init) {
122 	plugin_data *p;
123 
124 	p = calloc(1, sizeof(*p));
125 
126 	force_assert(p);
127 
128 	p->parse_response = buffer_init();
129 
130 	return p;
131 }
132 
133 
134 FREE_FUNC(mod_cgi_free) {
135 	plugin_data *p = p_d;
136 	buffer_pid_t *r = &(p->cgi_pid);
137 
138 	UNUSED(srv);
139 
140 	if (p->config_storage) {
141 		size_t i;
142 		for (i = 0; i < srv->config_context->used; i++) {
143 			plugin_config *s = p->config_storage[i];
144 
145 			if (NULL == s) continue;
146 
147 			array_free(s->cgi);
148 			array_free(s->xsendfile_docroot);
149 
150 			free(s);
151 		}
152 		free(p->config_storage);
153 	}
154 
155 
156 	if (r->ptr) free(r->ptr);
157 
158 	buffer_free(p->parse_response);
159 
160 	free(p);
161 
162 	return HANDLER_GO_ON;
163 }
164 
165 SETDEFAULTS_FUNC(mod_fastcgi_set_defaults) {
166 	plugin_data *p = p_d;
167 	size_t i = 0;
168 
169 	config_values_t cv[] = {
170 		{ "cgi.assign",                  NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION },       /* 0 */
171 		{ "cgi.execute-x-only",          NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION },     /* 1 */
172 		{ "cgi.x-sendfile",              NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION },     /* 2 */
173 		{ "cgi.x-sendfile-docroot",      NULL, T_CONFIG_ARRAY,   T_CONFIG_SCOPE_CONNECTION },     /* 3 */
174 		{ NULL,                          NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET}
175 	};
176 
177 	if (!p) return HANDLER_ERROR;
178 
179 	p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *));
180 	force_assert(p->config_storage);
181 
182 	for (i = 0; i < srv->config_context->used; i++) {
183 		data_config const* config = (data_config const*)srv->config_context->data[i];
184 		plugin_config *s;
185 
186 		s = calloc(1, sizeof(plugin_config));
187 		force_assert(s);
188 
189 		s->cgi    = array_init();
190 		s->execute_x_only = 0;
191 		s->xsendfile_allow= 0;
192 		s->xsendfile_docroot = array_init();
193 
194 		cv[0].destination = s->cgi;
195 		cv[1].destination = &(s->execute_x_only);
196 		cv[2].destination = &(s->xsendfile_allow);
197 		cv[3].destination = s->xsendfile_docroot;
198 
199 		p->config_storage[i] = s;
200 
201 		if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) {
202 			return HANDLER_ERROR;
203 		}
204 
205 		if (s->xsendfile_docroot->used) {
206 			size_t j;
207 			for (j = 0; j < s->xsendfile_docroot->used; ++j) {
208 				data_string *ds = (data_string *)s->xsendfile_docroot->data[j];
209 				if (ds->type != TYPE_STRING) {
210 					log_error_write(srv, __FILE__, __LINE__, "s",
211 						"unexpected type for key cgi.x-sendfile-docroot; expected: cgi.x-sendfile-docroot = ( \"/allowed/path\", ... )");
212 					return HANDLER_ERROR;
213 				}
214 				if (ds->value->ptr[0] != '/') {
215 					log_error_write(srv, __FILE__, __LINE__, "SBs",
216 						"cgi.x-sendfile-docroot paths must begin with '/'; invalid: \"", ds->value, "\"");
217 					return HANDLER_ERROR;
218 				}
219 				buffer_path_simplify(ds->value, ds->value);
220 				buffer_append_slash(ds->value);
221 			}
222 		}
223 	}
224 
225 	return HANDLER_GO_ON;
226 }
227 
228 
229 static int cgi_pid_add(server *srv, plugin_data *p, pid_t pid) {
230 	int m = -1;
231 	size_t i;
232 	buffer_pid_t *r = &(p->cgi_pid);
233 
234 	UNUSED(srv);
235 
236 	for (i = 0; i < r->used; i++) {
237 		if (r->ptr[i] > m) m = r->ptr[i];
238 	}
239 
240 	if (r->size == 0) {
241 		r->size = 16;
242 		r->ptr = malloc(sizeof(*r->ptr) * r->size);
243 		force_assert(r->ptr);
244 	} else if (r->used == r->size) {
245 		r->size += 16;
246 		r->ptr = realloc(r->ptr, sizeof(*r->ptr) * r->size);
247 		force_assert(r->ptr);
248 	}
249 
250 	r->ptr[r->used++] = pid;
251 
252 	return m;
253 }
254 
255 static int cgi_pid_del(server *srv, plugin_data *p, pid_t pid) {
256 	size_t i;
257 	buffer_pid_t *r = &(p->cgi_pid);
258 
259 	UNUSED(srv);
260 
261 	for (i = 0; i < r->used; i++) {
262 		if (r->ptr[i] == pid) break;
263 	}
264 
265 	if (i != r->used) {
266 		/* found */
267 
268 		if (i != r->used - 1) {
269 			r->ptr[i] = r->ptr[r->used - 1];
270 		}
271 		r->used--;
272 	}
273 
274 	return 0;
275 }
276 
277 static int cgi_response_parse(server *srv, connection *con, plugin_data *p, buffer *in) {
278 	char *ns;
279 	const char *s;
280 	int line = 0;
281 
282 	UNUSED(srv);
283 
284 	buffer_copy_buffer(p->parse_response, in);
285 
286 	for (s = p->parse_response->ptr;
287 	     NULL != (ns = strchr(s, '\n'));
288 	     s = ns + 1, line++) {
289 		const char *key, *value;
290 		int key_len;
291 		data_string *ds;
292 
293 		/* strip the \n */
294 		ns[0] = '\0';
295 
296 		if (ns > s && ns[-1] == '\r') ns[-1] = '\0';
297 
298 		if (line == 0 &&
299 		    0 == strncmp(s, "HTTP/1.", 7)) {
300 			/* non-parsed header ... we parse them anyway */
301 
302 			if ((s[7] == '1' ||
303 			     s[7] == '0') &&
304 			    s[8] == ' ') {
305 				int status;
306 				/* after the space should be a status code for us */
307 
308 				status = strtol(s+9, NULL, 10);
309 
310 				if (status >= 100 &&
311 				    status < 1000) {
312 					/* we expected 3 digits and didn't got them */
313 					con->parsed_response |= HTTP_STATUS;
314 					con->http_status = status;
315 				}
316 			}
317 		} else {
318 			/* parse the headers */
319 			key = s;
320 			if (NULL == (value = strchr(s, ':'))) {
321 				/* we expect: "<key>: <value>\r\n" */
322 				continue;
323 			}
324 
325 			key_len = value - key;
326 			value += 1;
327 
328 			/* skip LWS */
329 			while (*value == ' ' || *value == '\t') value++;
330 
331 			if (NULL == (ds = (data_string *)array_get_unused_element(con->response.headers, TYPE_STRING))) {
332 				ds = data_response_init();
333 			}
334 			buffer_copy_string_len(ds->key, key, key_len);
335 			buffer_copy_string(ds->value, value);
336 
337 			array_insert_unique(con->response.headers, (data_unset *)ds);
338 
339 			switch(key_len) {
340 			case 4:
341 				if (0 == strncasecmp(key, "Date", key_len)) {
342 					con->parsed_response |= HTTP_DATE;
343 				}
344 				break;
345 			case 6:
346 				if (0 == strncasecmp(key, "Status", key_len)) {
347 					int status = strtol(value, NULL, 10);
348 					if (status >= 100 && status < 1000) {
349 						con->http_status = status;
350 						con->parsed_response |= HTTP_STATUS;
351 					} else {
352 						con->http_status = 502;
353 					}
354 				}
355 				break;
356 			case 8:
357 				if (0 == strncasecmp(key, "Location", key_len)) {
358 					con->parsed_response |= HTTP_LOCATION;
359 				}
360 				break;
361 			case 10:
362 				if (0 == strncasecmp(key, "Connection", key_len)) {
363 					con->response.keep_alive = (0 == strcasecmp(value, "Keep-Alive")) ? 1 : 0;
364 					con->parsed_response |= HTTP_CONNECTION;
365 				}
366 				break;
367 			case 14:
368 				if (0 == strncasecmp(key, "Content-Length", key_len)) {
369 					con->response.content_length = strtoul(value, NULL, 10);
370 					con->parsed_response |= HTTP_CONTENT_LENGTH;
371 				}
372 				break;
373 			default:
374 				break;
375 			}
376 		}
377 	}
378 
379 	/* CGI/1.1 rev 03 - 7.2.1.2 */
380 	if ((con->parsed_response & HTTP_LOCATION) &&
381 	    !(con->parsed_response & HTTP_STATUS)) {
382 		con->http_status = 302;
383 	}
384 
385 	return 0;
386 }
387 
388 
389 static int cgi_demux_response(server *srv, handler_ctx *hctx) {
390 	plugin_data *p    = hctx->plugin_data;
391 	connection  *con  = hctx->remote_conn;
392 
393 	while(1) {
394 		int n;
395 		int toread;
396 
397 #if defined(__WIN32)
398 		buffer_string_prepare_copy(hctx->response, 4 * 1024);
399 #else
400 		if (ioctl(con->fd, FIONREAD, &toread) || toread <= 4*1024) {
401 			buffer_string_prepare_copy(hctx->response, 4 * 1024);
402 		} else {
403 			if (toread > MAX_READ_LIMIT) toread = MAX_READ_LIMIT;
404 			buffer_string_prepare_copy(hctx->response, toread);
405 		}
406 #endif
407 
408 		if (-1 == (n = read(hctx->fd, hctx->response->ptr, hctx->response->size - 1))) {
409 			if (errno == EAGAIN || errno == EINTR) {
410 				/* would block, wait for signal */
411 				fdevent_event_add(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN);
412 				return FDEVENT_HANDLED_NOT_FINISHED;
413 			}
414 			/* error */
415 			log_error_write(srv, __FILE__, __LINE__, "sdd", strerror(errno), con->fd, hctx->fd);
416 			return FDEVENT_HANDLED_ERROR;
417 		}
418 
419 		if (n == 0) {
420 			/* read finished */
421 			return FDEVENT_HANDLED_FINISHED;
422 		}
423 
424 		buffer_commit(hctx->response, n);
425 
426 		/* split header from body */
427 
428 		if (con->file_started == 0) {
429 			int is_header = 0;
430 			int is_header_end = 0;
431 			size_t last_eol = 0;
432 			size_t i, header_len;
433 
434 			buffer_append_string_buffer(hctx->response_header, hctx->response);
435 
436 			/**
437 			 * we have to handle a few cases:
438 			 *
439 			 * nph:
440 			 *
441 			 *   HTTP/1.0 200 Ok\n
442 			 *   Header: Value\n
443 			 *   \n
444 			 *
445 			 * CGI:
446 			 *   Header: Value\n
447 			 *   Status: 200\n
448 			 *   \n
449 			 *
450 			 * and different mixes of \n and \r\n combinations
451 			 *
452 			 * Some users also forget about CGI and just send a response and hope
453 			 * we handle it. No headers, no header-content seperator
454 			 *
455 			 */
456 
457 			/* nph (non-parsed headers) */
458 			if (0 == strncmp(hctx->response_header->ptr, "HTTP/1.", 7)) is_header = 1;
459 
460 			header_len = buffer_string_length(hctx->response_header);
461 			for (i = 0; !is_header_end && i < header_len; i++) {
462 				char c = hctx->response_header->ptr[i];
463 
464 				switch (c) {
465 				case ':':
466 					/* we found a colon
467 					 *
468 					 * looks like we have a normal header
469 					 */
470 					is_header = 1;
471 					break;
472 				case '\n':
473 					/* EOL */
474 					if (is_header == 0) {
475 						/* we got a EOL but we don't seem to got a HTTP header */
476 
477 						is_header_end = 1;
478 
479 						break;
480 					}
481 
482 					/**
483 					 * check if we saw a \n(\r)?\n sequence
484 					 */
485 					if (last_eol > 0 &&
486 					    ((i - last_eol == 1) ||
487 					     (i - last_eol == 2 && hctx->response_header->ptr[i - 1] == '\r'))) {
488 						is_header_end = 1;
489 						break;
490 					}
491 
492 					last_eol = i;
493 
494 					break;
495 				}
496 			}
497 
498 			if (is_header_end) {
499 				if (!is_header) {
500 					/* no header, but a body */
501 					if (0 != http_chunk_append_buffer(srv, con, hctx->response_header)) {
502 						return FDEVENT_HANDLED_ERROR;
503 					}
504 				} else {
505 					const char *bstart;
506 					size_t blen;
507 
508 					/* the body starts after the EOL */
509 					bstart = hctx->response_header->ptr + i;
510 					blen = header_len - i;
511 
512 					/**
513 					 * i still points to the char after the terminating EOL EOL
514 					 *
515 					 * put it on the last \n again
516 					 */
517 					i--;
518 
519 					/* string the last \r?\n */
520 					if (i > 0 && (hctx->response_header->ptr[i - 1] == '\r')) {
521 						i--;
522 					}
523 
524 					buffer_string_set_length(hctx->response_header, i);
525 
526 					/* parse the response header */
527 					cgi_response_parse(srv, con, p, hctx->response_header);
528 
529 					if (con->http_status >= 300 && con->http_status < 400) {
530 						/*(con->parsed_response & HTTP_LOCATION)*/
531 						data_string *ds;
532 						if (NULL != (ds = (data_string *) array_get_element(con->response.headers, "Location"))
533 						    && ds->value->ptr[0] == '/') {
534 							if (++con->loops_per_request > 5) {
535 								log_error_write(srv, __FILE__, __LINE__, "sb", "too many internal loops while processing request:", con->request.orig_uri);
536 								con->http_status = 500; /* Internal Server Error */
537 								con->mode = DIRECT;
538 								return FDEVENT_HANDLED_FINISHED;
539 							}
540 
541 							buffer_copy_buffer(con->request.uri, ds->value);
542 
543 							if (con->request.content_length) {
544 								if ((off_t)con->request.content_length != chunkqueue_length(con->request_content_queue)) {
545 									con->keep_alive = 0;
546 								}
547 								con->request.content_length = 0;
548 								chunkqueue_reset(con->request_content_queue);
549 							}
550 
551 							if (con->http_status != 307 && con->http_status != 308) {
552 								/* Note: request body (if any) sent to initial dynamic handler
553 								 * and is not available to the internal redirect */
554 								con->request.http_method = HTTP_METHOD_GET;
555 							}
556 
557 							connection_response_reset(srv, con); /*(includes con->http_status = 0)*/
558 
559 							con->mode = DIRECT;
560 							return FDEVENT_HANDLED_COMEBACK;
561 						}
562 					}
563 
564 					if (p->conf.xsendfile_allow) {
565 						data_string *ds;
566 						if (NULL != (ds = (data_string *) array_get_element(con->response.headers, "X-Sendfile"))) {
567 							http_response_xsendfile(srv, con, ds->value, p->conf.xsendfile_docroot);
568 							return FDEVENT_HANDLED_FINISHED;
569 						}
570 					}
571 
572 					if (blen > 0) {
573 						if (0 != http_chunk_append_mem(srv, con, bstart, blen)) {
574 							return FDEVENT_HANDLED_ERROR;
575 						}
576 					}
577 				}
578 
579 				con->file_started = 1;
580 			} else {
581 				/*(reuse MAX_HTTP_REQUEST_HEADER as max size for response headers from backends)*/
582 				if (header_len > MAX_HTTP_REQUEST_HEADER) {
583 					log_error_write(srv, __FILE__, __LINE__, "sb", "response headers too large for", con->uri.path);
584 					con->http_status = 502; /* Bad Gateway */
585 					con->mode = DIRECT;
586 					return FDEVENT_HANDLED_FINISHED;
587 				}
588 			}
589 		} else {
590 			if (0 != http_chunk_append_buffer(srv, con, hctx->response)) {
591 				return FDEVENT_HANDLED_ERROR;
592 			}
593 			if ((con->conf.stream_response_body & FDEVENT_STREAM_RESPONSE_BUFMIN)
594 			    && chunkqueue_length(con->write_queue) > 65536 - 4096) {
595 				if (!con->is_writable) {
596 					/*(defer removal of FDEVENT_IN interest since
597 					 * connection_state_machine() might be able to send data
598 					 * immediately, unless !con->is_writable, where
599 					 * connection_state_machine() might not loop back to call
600 					 * mod_cgi_handle_subrequest())*/
601 					fdevent_event_clr(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN);
602 				}
603 				break;
604 			}
605 		}
606 
607 #if 0
608 		log_error_write(srv, __FILE__, __LINE__, "ddss", con->fd, hctx->fd, connection_get_state(con->state), b->ptr);
609 #endif
610 	}
611 
612 	return FDEVENT_HANDLED_NOT_FINISHED;
613 }
614 
615 static void cgi_connection_close_fdtocgi(server *srv, handler_ctx *hctx) {
616 	/*(closes only hctx->fdtocgi)*/
617 	fdevent_event_del(srv->ev, &(hctx->fde_ndx_tocgi), hctx->fdtocgi);
618 	fdevent_unregister(srv->ev, hctx->fdtocgi);
619 	fdevent_sched_close(srv->ev, hctx->fdtocgi, 0);
620 	hctx->fdtocgi = -1;
621 }
622 
623 static void cgi_connection_close(server *srv, handler_ctx *hctx) {
624 	int status;
625 	pid_t pid;
626 	plugin_data *p = hctx->plugin_data;
627 	connection *con = hctx->remote_conn;
628 
629 #ifndef __WIN32
630 
631 	/* the connection to the browser went away, but we still have a connection
632 	 * to the CGI script
633 	 *
634 	 * close cgi-connection
635 	 */
636 
637 	if (hctx->fd != -1) {
638 		/* close connection to the cgi-script */
639 		fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd);
640 		fdevent_unregister(srv->ev, hctx->fd);
641 		fdevent_sched_close(srv->ev, hctx->fd, 0);
642 	}
643 
644 	if (hctx->fdtocgi != -1) {
645 		cgi_connection_close_fdtocgi(srv, hctx); /*(closes only hctx->fdtocgi)*/
646 	}
647 
648 	pid = hctx->pid;
649 
650 	con->plugin_ctx[p->id] = NULL;
651 
652 	cgi_handler_ctx_free(hctx);
653 
654 	/* if waitpid hasn't been called by response.c yet, do it here */
655 	if (pid) {
656 		/* check if the CGI-script is already gone */
657 		switch(waitpid(pid, &status, WNOHANG)) {
658 		case 0:
659 			/* not finished yet */
660 #if 0
661 			log_error_write(srv, __FILE__, __LINE__, "sd", "(debug) child isn't done yet, pid:", pid);
662 #endif
663 			break;
664 		case -1:
665 			/* */
666 			if (errno == EINTR) break;
667 
668 			/*
669 			 * errno == ECHILD happens if _subrequest catches the process-status before
670 			 * we have read the response of the cgi process
671 			 *
672 			 * -> catch status
673 			 * -> WAIT_FOR_EVENT
674 			 * -> read response
675 			 * -> we get here with waitpid == ECHILD
676 			 *
677 			 */
678 			if (errno != ECHILD) {
679 				log_error_write(srv, __FILE__, __LINE__, "ss", "waitpid failed: ", strerror(errno));
680 			}
681 			/* anyway: don't wait for it anymore */
682 			pid = 0;
683 			break;
684 		default:
685 			if (WIFEXITED(status)) {
686 #if 0
687 				log_error_write(srv, __FILE__, __LINE__, "sd", "(debug) cgi exited fine, pid:", pid);
688 #endif
689 			} else {
690 				log_error_write(srv, __FILE__, __LINE__, "sd", "cgi died, pid:", pid);
691 			}
692 
693 			pid = 0;
694 			break;
695 		}
696 
697 		if (pid) {
698 			kill(pid, SIGTERM);
699 
700 			/* cgi-script is still alive, queue the PID for removal */
701 			cgi_pid_add(srv, p, pid);
702 		}
703 	}
704 #endif
705 
706 	/* finish response (if not already con->file_started, con->file_finished) */
707 	if (con->mode == p->id) {
708 		http_response_backend_done(srv, con);
709 	}
710 }
711 
712 static handler_t cgi_connection_close_callback(server *srv, connection *con, void *p_d) {
713 	plugin_data *p = p_d;
714 	handler_ctx *hctx = con->plugin_ctx[p->id];
715 	if (hctx) cgi_connection_close(srv, hctx);
716 
717 	return HANDLER_GO_ON;
718 }
719 
720 
721 static int cgi_write_request(server *srv, handler_ctx *hctx, int fd);
722 
723 
724 static handler_t cgi_handle_fdevent_send (server *srv, void *ctx, int revents) {
725 	handler_ctx *hctx = ctx;
726 	connection  *con  = hctx->remote_conn;
727 
728 	/*(joblist only actually necessary here in mod_cgi fdevent send if returning HANDLER_ERROR)*/
729 	joblist_append(srv, con);
730 
731 	if (revents & FDEVENT_OUT) {
732 		if (0 != cgi_write_request(srv, hctx, hctx->fdtocgi)) {
733 			cgi_connection_close(srv, hctx);
734 			return HANDLER_ERROR;
735 		}
736 		/* more request body to be sent to CGI */
737 	}
738 
739 	if (revents & FDEVENT_HUP) {
740 		/* skip sending remaining data to CGI */
741 		if (con->request.content_length) {
742 			chunkqueue *cq = con->request_content_queue;
743 			chunkqueue_mark_written(cq, chunkqueue_length(cq));
744 			if (cq->bytes_in != (off_t)con->request.content_length) {
745 				con->keep_alive = 0;
746 			}
747 		}
748 
749 		cgi_connection_close_fdtocgi(srv, hctx); /*(closes only hctx->fdtocgi)*/
750 	} else if (revents & FDEVENT_ERR) {
751 		/* kill all connections to the cgi process */
752 #if 1
753 		log_error_write(srv, __FILE__, __LINE__, "s", "cgi-FDEVENT_ERR");
754 #endif
755 		cgi_connection_close(srv, hctx);
756 		return HANDLER_ERROR;
757 	}
758 
759 	return HANDLER_FINISHED;
760 }
761 
762 
763 static int cgi_recv_response(server *srv, handler_ctx *hctx) {
764 		switch (cgi_demux_response(srv, hctx)) {
765 		case FDEVENT_HANDLED_NOT_FINISHED:
766 			break;
767 		case FDEVENT_HANDLED_FINISHED:
768 			/* we are done */
769 
770 #if 0
771 			log_error_write(srv, __FILE__, __LINE__, "ddss", con->fd, hctx->fd, connection_get_state(con->state), "finished");
772 #endif
773 			cgi_connection_close(srv, hctx);
774 
775 			/* if we get a IN|HUP and have read everything don't exec the close twice */
776 			return HANDLER_FINISHED;
777 		case FDEVENT_HANDLED_COMEBACK:
778 			cgi_connection_close(srv, hctx);
779 			return HANDLER_COMEBACK;
780 		case FDEVENT_HANDLED_ERROR:
781 			log_error_write(srv, __FILE__, __LINE__, "s", "demuxer failed: ");
782 
783 			cgi_connection_close(srv, hctx);
784 			return HANDLER_FINISHED;
785 		}
786 
787 		return HANDLER_GO_ON;
788 }
789 
790 
791 static handler_t cgi_handle_fdevent(server *srv, void *ctx, int revents) {
792 	handler_ctx *hctx = ctx;
793 	connection  *con  = hctx->remote_conn;
794 
795 	joblist_append(srv, con);
796 
797 	if (revents & FDEVENT_IN) {
798 		handler_t rc = cgi_recv_response(srv, hctx);/*(might invalidate hctx)*/
799 		if (rc != HANDLER_GO_ON) return rc;         /*(unless HANDLER_GO_ON)*/
800 	}
801 
802 	/* perhaps this issue is already handled */
803 	if (revents & FDEVENT_HUP) {
804 		if (con->file_started) {
805 			/* drain any remaining data from kernel pipe buffers
806 			 * even if (con->conf.stream_response_body
807 			 *          & FDEVENT_STREAM_RESPONSE_BUFMIN)
808 			 * since event loop will spin on fd FDEVENT_HUP event
809 			 * until unregistered. */
810 			handler_t rc;
811 			do {
812 				rc = cgi_recv_response(srv,hctx);/*(might invalidate hctx)*/
813 			} while (rc == HANDLER_GO_ON);           /*(unless HANDLER_GO_ON)*/
814 			return rc; /* HANDLER_FINISHED or HANDLER_COMEBACK or HANDLER_ERROR */
815 		} else if (!buffer_string_is_empty(hctx->response_header)) {
816 			/* unfinished header package which is a body in reality */
817 			con->file_started = 1;
818 			if (0 != http_chunk_append_buffer(srv, con, hctx->response_header)) {
819 				cgi_connection_close(srv, hctx);
820 				return HANDLER_ERROR;
821 			}
822 		} else {
823 # if 0
824 			log_error_write(srv, __FILE__, __LINE__, "sddd", "got HUP from cgi", con->fd, hctx->fd, revents);
825 # endif
826 		}
827 		cgi_connection_close(srv, hctx);
828 	} else if (revents & FDEVENT_ERR) {
829 		/* kill all connections to the cgi process */
830 		cgi_connection_close(srv, hctx);
831 #if 1
832 		log_error_write(srv, __FILE__, __LINE__, "s", "cgi-FDEVENT_ERR");
833 #endif
834 		return HANDLER_ERROR;
835 	}
836 
837 	return HANDLER_FINISHED;
838 }
839 
840 
841 static int cgi_env_add(void *venv, const char *key, size_t key_len, const char *val, size_t val_len) {
842 	char_array *env = venv;
843 	char *dst;
844 
845 	if (!key || !val) return -1;
846 
847 	dst = malloc(key_len + val_len + 2);
848 	force_assert(dst);
849 	memcpy(dst, key, key_len);
850 	dst[key_len] = '=';
851 	memcpy(dst + key_len + 1, val, val_len);
852 	dst[key_len + 1 + val_len] = '\0';
853 
854 	if (env->size == 0) {
855 		env->size = 16;
856 		env->ptr = malloc(env->size * sizeof(*env->ptr));
857 		force_assert(env->ptr);
858 	} else if (env->size == env->used) {
859 		env->size += 16;
860 		env->ptr = realloc(env->ptr, env->size * sizeof(*env->ptr));
861 		force_assert(env->ptr);
862 	}
863 
864 	env->ptr[env->used++] = dst;
865 
866 	return 0;
867 }
868 
869 /* returns: 0: continue, -1: fatal error, -2: connection reset */
870 /* similar to network_write_file_chunk_mmap, but doesn't use send on windows (because we're on pipes),
871  * also mmaps and sends complete chunk instead of only small parts - the files
872  * are supposed to be temp files with reasonable chunk sizes.
873  *
874  * Also always use mmap; the files are "trusted", as we created them.
875  */
876 static ssize_t cgi_write_file_chunk_mmap(server *srv, connection *con, int fd, chunkqueue *cq) {
877 	chunk* const c = cq->first;
878 	off_t offset, toSend, file_end;
879 	ssize_t r;
880 	size_t mmap_offset, mmap_avail;
881 	char *data;
882 
883 	force_assert(NULL != c);
884 	force_assert(FILE_CHUNK == c->type);
885 	force_assert(c->offset >= 0 && c->offset <= c->file.length);
886 
887 	offset = c->file.start + c->offset;
888 	toSend = c->file.length - c->offset;
889 	file_end = c->file.start + c->file.length; /* offset to file end in this chunk */
890 
891 	if (0 == toSend) {
892 		chunkqueue_remove_finished_chunks(cq);
893 		return 0;
894 	}
895 
896 	if (0 != network_open_file_chunk(srv, con, cq)) return -1;
897 
898 	/* (re)mmap the buffer if range is not covered completely */
899 	if (MAP_FAILED == c->file.mmap.start
900 		|| offset < c->file.mmap.offset
901 		|| file_end > (off_t)(c->file.mmap.offset + c->file.mmap.length)) {
902 
903 		if (MAP_FAILED != c->file.mmap.start) {
904 			munmap(c->file.mmap.start, c->file.mmap.length);
905 			c->file.mmap.start = MAP_FAILED;
906 		}
907 
908 		c->file.mmap.offset = mmap_align_offset(offset);
909 		c->file.mmap.length = file_end - c->file.mmap.offset;
910 
911 		if (MAP_FAILED == (c->file.mmap.start = mmap(NULL, c->file.mmap.length, PROT_READ, MAP_PRIVATE, c->file.fd, c->file.mmap.offset))) {
912 			if (toSend > 65536) toSend = 65536;
913 			data = malloc(toSend);
914 			force_assert(data);
915 			if (-1 == lseek(c->file.fd, offset, SEEK_SET)
916 			    || 0 >= (toSend = read(c->file.fd, data, toSend))) {
917 				if (-1 == toSend) {
918 					log_error_write(srv, __FILE__, __LINE__, "ssbdo", "lseek/read failed:",
919 						strerror(errno), c->file.name, c->file.fd, offset);
920 				} else { /*(0 == toSend)*/
921 					log_error_write(srv, __FILE__, __LINE__, "sbdo", "unexpected EOF (input truncated?):",
922 						c->file.name, c->file.fd, offset);
923 				}
924 				free(data);
925 				return -1;
926 			}
927 		}
928 	}
929 
930 	if (MAP_FAILED != c->file.mmap.start) {
931 		force_assert(offset >= c->file.mmap.offset);
932 		mmap_offset = offset - c->file.mmap.offset;
933 		force_assert(c->file.mmap.length > mmap_offset);
934 		mmap_avail = c->file.mmap.length - mmap_offset;
935 		force_assert(toSend <= (off_t) mmap_avail);
936 
937 		data = c->file.mmap.start + mmap_offset;
938 	}
939 
940 	r = write(fd, data, toSend);
941 
942 	if (MAP_FAILED == c->file.mmap.start) free(data);
943 
944 	if (r < 0) {
945 		switch (errno) {
946 		case EAGAIN:
947 		case EINTR:
948 			return 0;
949 		case EPIPE:
950 		case ECONNRESET:
951 			return -2;
952 		default:
953 			log_error_write(srv, __FILE__, __LINE__, "ssd",
954 				"write failed:", strerror(errno), fd);
955 			return -1;
956 		}
957 	}
958 
959 	if (r >= 0) {
960 		chunkqueue_mark_written(cq, r);
961 	}
962 
963 	return r;
964 }
965 
966 static int cgi_write_request(server *srv, handler_ctx *hctx, int fd) {
967 	connection *con = hctx->remote_conn;
968 	chunkqueue *cq = con->request_content_queue;
969 	chunk *c;
970 
971 	/* old comment: windows doesn't support select() on pipes - wouldn't be easy to fix for all platforms.
972 	 * solution: if this is still a problem on windows, then substitute
973 	 * socketpair() for pipe() and closesocket() for close() on windows.
974 	 */
975 
976 	for (c = cq->first; c; c = cq->first) {
977 		ssize_t r = -1;
978 
979 		switch(c->type) {
980 		case FILE_CHUNK:
981 			r = cgi_write_file_chunk_mmap(srv, con, fd, cq);
982 			break;
983 
984 		case MEM_CHUNK:
985 			if ((r = write(fd, c->mem->ptr + c->offset, buffer_string_length(c->mem) - c->offset)) < 0) {
986 				switch(errno) {
987 				case EAGAIN:
988 				case EINTR:
989 					/* ignore and try again */
990 					r = 0;
991 					break;
992 				case EPIPE:
993 				case ECONNRESET:
994 					/* connection closed */
995 					r = -2;
996 					break;
997 				default:
998 					/* fatal error */
999 					log_error_write(srv, __FILE__, __LINE__, "ss", "write failed due to: ", strerror(errno));
1000 					r = -1;
1001 					break;
1002 				}
1003 			} else if (r > 0) {
1004 				chunkqueue_mark_written(cq, r);
1005 			}
1006 			break;
1007 		}
1008 
1009 		if (0 == r) break; /*(might block)*/
1010 
1011 		switch (r) {
1012 		case -1:
1013 			/* fatal error */
1014 			return -1;
1015 		case -2:
1016 			/* connection reset */
1017 			log_error_write(srv, __FILE__, __LINE__, "s", "failed to send post data to cgi, connection closed by CGI");
1018 			/* skip all remaining data */
1019 			chunkqueue_mark_written(cq, chunkqueue_length(cq));
1020 			break;
1021 		default:
1022 			break;
1023 		}
1024 	}
1025 
1026 	if (cq->bytes_out == (off_t)con->request.content_length) {
1027 		/* sent all request body input */
1028 		/* close connection to the cgi-script */
1029 		if (-1 == hctx->fdtocgi) { /*(received request body sent in initial send to pipe buffer)*/
1030 			if (close(fd)) {
1031 				log_error_write(srv, __FILE__, __LINE__, "sds", "cgi stdin close failed ", fd, strerror(errno));
1032 			}
1033 		} else {
1034 			cgi_connection_close_fdtocgi(srv, hctx); /*(closes only hctx->fdtocgi)*/
1035 		}
1036 	} else {
1037 		off_t cqlen = cq->bytes_in - cq->bytes_out;
1038 		if (cq->bytes_in < (off_t)con->request.content_length && cqlen < 65536 - 16384) {
1039 			/*(con->conf.stream_request_body & FDEVENT_STREAM_REQUEST)*/
1040 			if (!(con->conf.stream_request_body & FDEVENT_STREAM_REQUEST_POLLIN)) {
1041 				con->conf.stream_request_body |= FDEVENT_STREAM_REQUEST_POLLIN;
1042 				con->is_readable = 1; /* trigger optimistic read from client */
1043 			}
1044 		}
1045 		if (-1 == hctx->fdtocgi) { /*(not registered yet)*/
1046 			hctx->fdtocgi = fd;
1047 			hctx->fde_ndx_tocgi = -1;
1048 			fdevent_register(srv->ev, hctx->fdtocgi, cgi_handle_fdevent_send, hctx);
1049 		}
1050 		if (0 == cqlen) { /*(chunkqueue_is_empty(cq))*/
1051 			if ((fdevent_event_get_interest(srv->ev, hctx->fdtocgi) & FDEVENT_OUT)) {
1052 				fdevent_event_set(srv->ev, &(hctx->fde_ndx_tocgi), hctx->fdtocgi, 0);
1053 			}
1054 		} else {
1055 			/* more request body remains to be sent to CGI so register for fdevents */
1056 			fdevent_event_set(srv->ev, &(hctx->fde_ndx_tocgi), hctx->fdtocgi, FDEVENT_OUT);
1057 		}
1058 	}
1059 
1060 	return 0;
1061 }
1062 
1063 static int cgi_create_env(server *srv, connection *con, plugin_data *p, handler_ctx *hctx, buffer *cgi_handler) {
1064 	pid_t pid;
1065 
1066 	int to_cgi_fds[2];
1067 	int from_cgi_fds[2];
1068 	struct stat st;
1069 	UNUSED(p);
1070 
1071 #ifndef __WIN32
1072 
1073 	if (!buffer_string_is_empty(cgi_handler)) {
1074 		/* stat the exec file */
1075 		if (-1 == (stat(cgi_handler->ptr, &st))) {
1076 			log_error_write(srv, __FILE__, __LINE__, "sbss",
1077 					"stat for cgi-handler", cgi_handler,
1078 					"failed:", strerror(errno));
1079 			return -1;
1080 		}
1081 	}
1082 
1083 	if (pipe_cloexec(to_cgi_fds)) {
1084 		log_error_write(srv, __FILE__, __LINE__, "ss", "pipe failed:", strerror(errno));
1085 		return -1;
1086 	}
1087 
1088 	if (pipe_cloexec(from_cgi_fds)) {
1089 		close(to_cgi_fds[0]);
1090 		close(to_cgi_fds[1]);
1091 		log_error_write(srv, __FILE__, __LINE__, "ss", "pipe failed:", strerror(errno));
1092 		return -1;
1093 	}
1094 
1095 	/* fork, execve */
1096 	switch (pid = fork()) {
1097 	case 0: {
1098 		/* child */
1099 		char **args;
1100 		int argc;
1101 		int i = 0;
1102 		char_array env;
1103 		char *c;
1104 		const char *s;
1105 		http_cgi_opts opts = { 0, 0, NULL, NULL };
1106 
1107 		/* move stdout to from_cgi_fd[1] */
1108 		dup2(from_cgi_fds[1], STDOUT_FILENO);
1109 	      #ifndef FD_CLOEXEC
1110 		close(from_cgi_fds[1]);
1111 		/* not needed */
1112 		close(from_cgi_fds[0]);
1113 	      #endif
1114 
1115 		/* move the stdin to to_cgi_fd[0] */
1116 		dup2(to_cgi_fds[0], STDIN_FILENO);
1117 	      #ifndef FD_CLOEXEC
1118 		close(to_cgi_fds[0]);
1119 		/* not needed */
1120 		close(to_cgi_fds[1]);
1121 	      #endif
1122 
1123 		/* create environment */
1124 		env.ptr = NULL;
1125 		env.size = 0;
1126 		env.used = 0;
1127 
1128 		http_cgi_headers(srv, con, &opts, cgi_env_add, &env);
1129 
1130 		/* for valgrind */
1131 		if (NULL != (s = getenv("LD_PRELOAD"))) {
1132 			cgi_env_add(&env, CONST_STR_LEN("LD_PRELOAD"), s, strlen(s));
1133 		}
1134 
1135 		if (NULL != (s = getenv("LD_LIBRARY_PATH"))) {
1136 			cgi_env_add(&env, CONST_STR_LEN("LD_LIBRARY_PATH"), s, strlen(s));
1137 		}
1138 #ifdef __CYGWIN__
1139 		/* CYGWIN needs SYSTEMROOT */
1140 		if (NULL != (s = getenv("SYSTEMROOT"))) {
1141 			cgi_env_add(&env, CONST_STR_LEN("SYSTEMROOT"), s, strlen(s));
1142 		}
1143 #endif
1144 
1145 		if (env.size == env.used) {
1146 			env.size += 16;
1147 			env.ptr = realloc(env.ptr, env.size * sizeof(*env.ptr));
1148 		}
1149 
1150 		env.ptr[env.used] = NULL;
1151 
1152 		/* set up args */
1153 		argc = 3;
1154 		args = malloc(sizeof(*args) * argc);
1155 		force_assert(args);
1156 		i = 0;
1157 
1158 		if (!buffer_string_is_empty(cgi_handler)) {
1159 			args[i++] = cgi_handler->ptr;
1160 		}
1161 		args[i++] = con->physical.path->ptr;
1162 		args[i  ] = NULL;
1163 
1164 		/* search for the last / */
1165 		if (NULL != (c = strrchr(con->physical.path->ptr, '/'))) {
1166 			/* handle special case of file in root directory */
1167 			const char* physdir = (c == con->physical.path->ptr) ? "/" : con->physical.path->ptr;
1168 
1169 			/* temporarily shorten con->physical.path to directory without terminating '/' */
1170 			*c = '\0';
1171 			/* change to the physical directory */
1172 			if (-1 == chdir(physdir)) {
1173 				log_error_write(srv, __FILE__, __LINE__, "ssb", "chdir failed:", strerror(errno), con->physical.path);
1174 			}
1175 			*c = '/';
1176 		}
1177 
1178 		/* we don't need the client socket */
1179 		for (i = 3; i < 256; i++) {
1180 			if (i != srv->errorlog_fd) close(i);
1181 		}
1182 
1183 		/* exec the cgi */
1184 		execve(args[0], args, env.ptr);
1185 
1186 		/* most log files may have been closed/redirected by this point,
1187 		 * though stderr might still point to lighttpd.breakage.log */
1188 		perror(args[0]);
1189 		_exit(1);
1190 	}
1191 	case -1:
1192 		/* error */
1193 		log_error_write(srv, __FILE__, __LINE__, "ss", "fork failed:", strerror(errno));
1194 		close(from_cgi_fds[0]);
1195 		close(from_cgi_fds[1]);
1196 		close(to_cgi_fds[0]);
1197 		close(to_cgi_fds[1]);
1198 		return -1;
1199 	default: {
1200 		/* parent process */
1201 
1202 		close(from_cgi_fds[1]);
1203 		close(to_cgi_fds[0]);
1204 
1205 		/* register PID and wait for them asynchronously */
1206 
1207 		hctx->pid = pid;
1208 		hctx->fd = from_cgi_fds[0];
1209 		hctx->fde_ndx = -1;
1210 
1211 		++srv->cur_fds;
1212 
1213 		if (0 == con->request.content_length) {
1214 			close(to_cgi_fds[1]);
1215 		} else {
1216 			/* there is content to send */
1217 			if (-1 == fdevent_fcntl_set_nb(srv->ev, to_cgi_fds[1])) {
1218 				log_error_write(srv, __FILE__, __LINE__, "ss", "fcntl failed: ", strerror(errno));
1219 				close(to_cgi_fds[1]);
1220 				cgi_connection_close(srv, hctx);
1221 				return -1;
1222 			}
1223 
1224 			if (0 != cgi_write_request(srv, hctx, to_cgi_fds[1])) {
1225 				close(to_cgi_fds[1]);
1226 				cgi_connection_close(srv, hctx);
1227 				return -1;
1228 			}
1229 
1230 			++srv->cur_fds;
1231 		}
1232 
1233 		fdevent_register(srv->ev, hctx->fd, cgi_handle_fdevent, hctx);
1234 		if (-1 == fdevent_fcntl_set_nb(srv->ev, hctx->fd)) {
1235 			log_error_write(srv, __FILE__, __LINE__, "ss", "fcntl failed: ", strerror(errno));
1236 			cgi_connection_close(srv, hctx);
1237 			return -1;
1238 		}
1239 		fdevent_event_set(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN);
1240 
1241 		break;
1242 	}
1243 	}
1244 
1245 	return 0;
1246 #else
1247 	return -1;
1248 #endif
1249 }
1250 
1251 static buffer * cgi_get_handler(array *a, buffer *fn) {
1252 	size_t k, s_len = buffer_string_length(fn);
1253 	for (k = 0; k < a->used; ++k) {
1254 		data_string *ds = (data_string *)a->data[k];
1255 		size_t ct_len = buffer_string_length(ds->key);
1256 
1257 		if (buffer_is_empty(ds->key)) continue;
1258 		if (s_len < ct_len) continue;
1259 
1260 		if (0 == strncmp(fn->ptr + s_len - ct_len, ds->key->ptr, ct_len)) {
1261 			return ds->value;
1262 		}
1263 	}
1264 
1265 	return NULL;
1266 }
1267 
1268 #define PATCH(x) \
1269 	p->conf.x = s->x;
1270 static int mod_cgi_patch_connection(server *srv, connection *con, plugin_data *p) {
1271 	size_t i, j;
1272 	plugin_config *s = p->config_storage[0];
1273 
1274 	PATCH(cgi);
1275 	PATCH(execute_x_only);
1276 	PATCH(xsendfile_allow);
1277 	PATCH(xsendfile_docroot);
1278 
1279 	/* skip the first, the global context */
1280 	for (i = 1; i < srv->config_context->used; i++) {
1281 		data_config *dc = (data_config *)srv->config_context->data[i];
1282 		s = p->config_storage[i];
1283 
1284 		/* condition didn't match */
1285 		if (!config_check_cond(srv, con, dc)) continue;
1286 
1287 		/* merge config */
1288 		for (j = 0; j < dc->value->used; j++) {
1289 			data_unset *du = dc->value->data[j];
1290 
1291 			if (buffer_is_equal_string(du->key, CONST_STR_LEN("cgi.assign"))) {
1292 				PATCH(cgi);
1293 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("cgi.execute-x-only"))) {
1294 				PATCH(execute_x_only);
1295 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("cgi.x-sendfile"))) {
1296 				PATCH(xsendfile_allow);
1297 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("cgi.x-sendfile-docroot"))) {
1298 				PATCH(xsendfile_docroot);
1299 			}
1300 		}
1301 	}
1302 
1303 	return 0;
1304 }
1305 #undef PATCH
1306 
1307 URIHANDLER_FUNC(cgi_is_handled) {
1308 	plugin_data *p = p_d;
1309 	buffer *fn = con->physical.path;
1310 	stat_cache_entry *sce = NULL;
1311 	struct stat stbuf;
1312 	struct stat *st;
1313 
1314 	if (con->mode != DIRECT) return HANDLER_GO_ON;
1315 
1316 	if (buffer_is_empty(fn)) return HANDLER_GO_ON;
1317 
1318 	mod_cgi_patch_connection(srv, con, p);
1319 
1320 	if (HANDLER_ERROR != stat_cache_get_entry(srv, con, con->physical.path, &sce)) {
1321 		st = &sce->st;
1322 	} else {
1323 		/* CGI might be executable even if it is not readable
1324 		 * (stat_cache_get_entry() currently checks file is readable)*/
1325 		if (0 != stat(con->physical.path->ptr, &stbuf)) return HANDLER_GO_ON;
1326 		st = &stbuf;
1327 	}
1328 
1329 	if (!S_ISREG(st->st_mode)) return HANDLER_GO_ON;
1330 	if (p->conf.execute_x_only == 1 && (st->st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) == 0) return HANDLER_GO_ON;
1331 
1332 	if (NULL != cgi_get_handler(p->conf.cgi, fn)) {
1333 		handler_ctx *hctx = cgi_handler_ctx_init();
1334 		hctx->remote_conn = con;
1335 		hctx->plugin_data = p;
1336 		con->plugin_ctx[p->id] = hctx;
1337 		con->mode = p->id;
1338 	}
1339 
1340 	return HANDLER_GO_ON;
1341 }
1342 
1343 TRIGGER_FUNC(cgi_trigger) {
1344 	plugin_data *p = p_d;
1345 	size_t ndx;
1346 	/* the trigger handle only cares about lonely PID which we have to wait for */
1347 #ifndef __WIN32
1348 
1349 	for (ndx = 0; ndx < p->cgi_pid.used; ndx++) {
1350 		int status;
1351 
1352 		switch(waitpid(p->cgi_pid.ptr[ndx], &status, WNOHANG)) {
1353 		case 0:
1354 			/* not finished yet */
1355 #if 0
1356 			log_error_write(srv, __FILE__, __LINE__, "sd", "(debug) child isn't done yet, pid:", p->cgi_pid.ptr[ndx]);
1357 #endif
1358 			break;
1359 		case -1:
1360 			if (errno == ECHILD) {
1361 				/* someone else called waitpid... remove the pid to stop looping the error each time */
1362 				log_error_write(srv, __FILE__, __LINE__, "s", "cgi child vanished, probably someone else called waitpid");
1363 
1364 				cgi_pid_del(srv, p, p->cgi_pid.ptr[ndx]);
1365 				ndx--;
1366 				continue;
1367 			}
1368 
1369 			log_error_write(srv, __FILE__, __LINE__, "ss", "waitpid failed: ", strerror(errno));
1370 
1371 			return HANDLER_ERROR;
1372 		default:
1373 
1374 			if (WIFEXITED(status)) {
1375 #if 0
1376 				log_error_write(srv, __FILE__, __LINE__, "sd", "(debug) cgi exited fine, pid:", p->cgi_pid.ptr[ndx]);
1377 #endif
1378 			} else if (WIFSIGNALED(status)) {
1379 				/* FIXME: what if we killed the CGI script with a kill(..., SIGTERM) ?
1380 				 */
1381 				if (WTERMSIG(status) != SIGTERM) {
1382 					log_error_write(srv, __FILE__, __LINE__, "sd", "cleaning up CGI: process died with signal", WTERMSIG(status));
1383 				}
1384 			} else {
1385 				log_error_write(srv, __FILE__, __LINE__, "s", "cleaning up CGI: ended unexpectedly");
1386 			}
1387 
1388 			cgi_pid_del(srv, p, p->cgi_pid.ptr[ndx]);
1389 			/* del modified the buffer structure
1390 			 * and copies the last entry to the current one
1391 			 * -> recheck the current index
1392 			 */
1393 			ndx--;
1394 		}
1395 	}
1396 #endif
1397 	return HANDLER_GO_ON;
1398 }
1399 
1400 /*
1401  * - HANDLER_GO_ON : not our job
1402  * - HANDLER_FINISHED: got response
1403  * - HANDLER_WAIT_FOR_EVENT: waiting for response
1404  */
1405 SUBREQUEST_FUNC(mod_cgi_handle_subrequest) {
1406 	plugin_data *p = p_d;
1407 	handler_ctx *hctx = con->plugin_ctx[p->id];
1408 	chunkqueue *cq = con->request_content_queue;
1409 
1410 	if (con->mode != p->id) return HANDLER_GO_ON;
1411 	if (NULL == hctx) return HANDLER_GO_ON;
1412 
1413 	if ((con->conf.stream_response_body & FDEVENT_STREAM_RESPONSE_BUFMIN)
1414 	    && con->file_started) {
1415 		if (chunkqueue_length(con->write_queue) > 65536 - 4096) {
1416 			fdevent_event_clr(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN);
1417 		} else if (!(fdevent_event_get_interest(srv->ev, hctx->fd) & FDEVENT_IN)) {
1418 			/* optimistic read from backend, which might re-enable FDEVENT_IN */
1419 			handler_t rc = cgi_recv_response(srv, hctx); /*(might invalidate hctx)*/
1420 			if (rc != HANDLER_GO_ON) return rc;          /*(unless HANDLER_GO_ON)*/
1421 		}
1422 	}
1423 
1424 	if (cq->bytes_in != (off_t)con->request.content_length) {
1425 		/*(64k - 4k to attempt to avoid temporary files
1426 		 * in conjunction with FDEVENT_STREAM_REQUEST_BUFMIN)*/
1427 		if (cq->bytes_in - cq->bytes_out > 65536 - 4096
1428 		    && (con->conf.stream_request_body & FDEVENT_STREAM_REQUEST_BUFMIN)){
1429 			con->conf.stream_request_body &= ~FDEVENT_STREAM_REQUEST_POLLIN;
1430 			if (-1 != hctx->fd) return HANDLER_WAIT_FOR_EVENT;
1431 		} else {
1432 			handler_t r = connection_handle_read_post_state(srv, con);
1433 			if (!chunkqueue_is_empty(cq)) {
1434 				if (fdevent_event_get_interest(srv->ev, hctx->fdtocgi) & FDEVENT_OUT) {
1435 					return (r == HANDLER_GO_ON) ? HANDLER_WAIT_FOR_EVENT : r;
1436 				}
1437 			}
1438 			if (r != HANDLER_GO_ON) return r;
1439 		}
1440 	}
1441 
1442 	if (-1 == hctx->fd) {
1443 		buffer *handler = cgi_get_handler(p->conf.cgi, con->physical.path);
1444 		if (!handler) return HANDLER_GO_ON; /*(should not happen; checked in cgi_is_handled())*/
1445 		if (cgi_create_env(srv, con, p, hctx, handler)) {
1446 			con->http_status = 500;
1447 			con->mode = DIRECT;
1448 
1449 			return HANDLER_FINISHED;
1450 		}
1451 #if 0
1452 	log_error_write(srv, __FILE__, __LINE__, "sdd", "subrequest, pid =", hctx, hctx->pid);
1453 #endif
1454 	} else if (!chunkqueue_is_empty(con->request_content_queue)) {
1455 		if (0 != cgi_write_request(srv, hctx, hctx->fdtocgi)) {
1456 			cgi_connection_close(srv, hctx);
1457 			return HANDLER_ERROR;
1458 		}
1459 	}
1460 
1461 	/* if not done, wait for CGI to close stdout, so we read EOF on pipe */
1462 	return HANDLER_WAIT_FOR_EVENT;
1463 }
1464 
1465 
1466 int mod_cgi_plugin_init(plugin *p);
1467 int mod_cgi_plugin_init(plugin *p) {
1468 	p->version     = LIGHTTPD_VERSION_ID;
1469 	p->name        = buffer_init_string("cgi");
1470 
1471 	p->connection_reset = cgi_connection_close_callback;
1472 	p->handle_subrequest_start = cgi_is_handled;
1473 	p->handle_subrequest = mod_cgi_handle_subrequest;
1474 	p->handle_trigger = cgi_trigger;
1475 	p->init           = mod_cgi_init;
1476 	p->cleanup        = mod_cgi_free;
1477 	p->set_defaults   = mod_fastcgi_set_defaults;
1478 
1479 	p->data        = NULL;
1480 
1481 	return 0;
1482 }
1483