• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/router/lighttpd-1.4.39/src/
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
42enum {EOL_UNSET, EOL_N, EOL_RN};
43
44typedef struct {
45	char **ptr;
46
47	size_t size;
48	size_t used;
49} char_array;
50
51typedef struct {
52	pid_t *ptr;
53	size_t used;
54	size_t size;
55} buffer_pid_t;
56
57typedef struct {
58	array *cgi;
59	unsigned short execute_x_only;
60} plugin_config;
61
62typedef 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
74typedef 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
86static 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
97static 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
104enum {FDEVENT_HANDLED_UNSET, FDEVENT_HANDLED_FINISHED, FDEVENT_HANDLED_NOT_FINISHED, FDEVENT_HANDLED_ERROR};
105
106INIT_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
120FREE_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
151SETDEFAULTS_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
190static 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
216static 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
238static 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
345static 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
522static 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
622static 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
629static 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
710static 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 */
744static 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
816static 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;
1230static 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
1261URIHANDLER_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
1302TRIGGER_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 */
1364SUBREQUEST_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
1456int mod_cgi_plugin_init(plugin *p);
1457int 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