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