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