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