xref: /lighttpd1.4/src/mod_cgi.c (revision db0cd766)
1 #include "first.h"
2 
3 #include "base.h"
4 #include "stat_cache.h"
5 #include "http_kv.h"
6 #include "log.h"
7 #include "connections.h"
8 #include "response.h"
9 #include "http_chunk.h"
10 #include "http_header.h"
11 
12 #include "plugin.h"
13 
14 #include <sys/types.h>
15 #include "sys-socket.h"
16 #ifdef HAVE_SYS_WAIT_H
17 #include <sys/wait.h>
18 #endif
19 
20 #include <unistd.h>
21 #include <errno.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <fdevent.h>
25 
26 #include <fcntl.h>
27 #include <signal.h>
28 
29 static int pipe_cloexec(int pipefd[2]) {
30   #ifdef HAVE_PIPE2
31     if (0 == pipe2(pipefd, O_CLOEXEC)) return 0;
32   #endif
33     return 0 == pipe(pipefd)
34        #ifdef FD_CLOEXEC
35         && 0 == fcntl(pipefd[0], F_SETFD, FD_CLOEXEC)
36         && 0 == fcntl(pipefd[1], F_SETFD, FD_CLOEXEC)
37        #endif
38       ?  0
39       : -1;
40 }
41 
42 typedef struct {
43 	char *ptr;
44 	size_t used;
45 	size_t size;
46 	size_t *offsets;
47 	size_t osize;
48 	size_t oused;
49 	char **eptr;
50 	size_t esize;
51 	buffer *ld_preload;
52 	buffer *ld_library_path;
53       #ifdef __CYGWIN__
54 	buffer *systemroot;
55       #endif
56 } env_accum;
57 
58 typedef struct {
59 	struct { pid_t pid; void *ctx; } *ptr;
60 	size_t used;
61 	size_t size;
62 } buffer_pid_t;
63 
64 typedef struct {
65 	const array *cgi;
66 	unsigned short execute_x_only;
67 	unsigned short local_redir;
68 	unsigned short xsendfile_allow;
69 	unsigned short upgrade;
70 	const array *xsendfile_docroot;
71 } plugin_config;
72 
73 typedef struct {
74 	PLUGIN_DATA;
75 	plugin_config defaults;
76 	plugin_config conf;
77 	buffer_pid_t cgi_pid;
78 	env_accum env;
79 } plugin_data;
80 
81 typedef struct {
82 	pid_t pid;
83 	int fd;
84 	int fdtocgi;
85 	fdnode *fdn;
86 	fdnode *fdntocgi;
87 
88 	request_st *r;
89 	struct fdevents *ev;      /* dumb pointer */
90 	plugin_data *plugin_data; /* dumb pointer */
91 
92 	buffer *response;
93 	buffer *cgi_handler;      /* dumb pointer */
94 	http_response_opts opts;
95 	plugin_config conf;
96 } handler_ctx;
97 
98 static handler_ctx * cgi_handler_ctx_init(void) {
99 	handler_ctx *hctx = calloc(1, sizeof(*hctx));
100 
101 	force_assert(hctx);
102 
103 	hctx->response = chunk_buffer_acquire();
104 	hctx->fd = -1;
105 	hctx->fdtocgi = -1;
106 
107 	return hctx;
108 }
109 
110 static void cgi_handler_ctx_free(handler_ctx *hctx) {
111 	chunk_buffer_release(hctx->response);
112 	free(hctx);
113 }
114 
115 INIT_FUNC(mod_cgi_init) {
116 	plugin_data *p;
117 	const char *s;
118 
119 	p = calloc(1, sizeof(*p));
120 
121 	force_assert(p);
122 
123 	/* for valgrind */
124 	s = getenv("LD_PRELOAD");
125 	if (s) p->env.ld_preload = buffer_init_string(s);
126 	s = getenv("LD_LIBRARY_PATH");
127 	if (s) p->env.ld_library_path = buffer_init_string(s);
128       #ifdef __CYGWIN__
129 	/* CYGWIN needs SYSTEMROOT */
130 	s = getenv("SYSTEMROOT");
131 	if (s) p->env.systemroot = buffer_init_string(s);
132       #endif
133 
134 	return p;
135 }
136 
137 
138 FREE_FUNC(mod_cgi_free) {
139 	plugin_data *p = p_d;
140 	buffer_pid_t *bp = &(p->cgi_pid);
141 	if (bp->ptr) free(bp->ptr);
142 	free(p->env.ptr);
143 	free(p->env.offsets);
144 	free(p->env.eptr);
145 	buffer_free(p->env.ld_preload);
146 	buffer_free(p->env.ld_library_path);
147       #ifdef __CYGWIN__
148 	buffer_free(p->env.systemroot);
149       #endif
150 }
151 
152 static void mod_cgi_merge_config_cpv(plugin_config * const pconf, const config_plugin_value_t * const cpv) {
153     switch (cpv->k_id) { /* index into static config_plugin_keys_t cpk[] */
154       case 0: /* cgi.assign */
155         pconf->cgi = cpv->v.a;
156         break;
157       case 1: /* cgi.execute-x-only */
158         pconf->execute_x_only = (unsigned short)cpv->v.u;
159         break;
160       case 2: /* cgi.x-sendfile */
161         pconf->xsendfile_allow = (unsigned short)cpv->v.u;
162         break;
163       case 3: /* cgi.x-sendfile-docroot */
164         pconf->xsendfile_docroot = cpv->v.a;
165         break;
166       case 4: /* cgi.local-redir */
167         pconf->local_redir = (unsigned short)cpv->v.u;
168         break;
169       case 5: /* cgi.upgrade */
170         pconf->upgrade = (unsigned short)cpv->v.u;
171         break;
172       default:/* should not happen */
173         return;
174     }
175 }
176 
177 static void mod_cgi_merge_config(plugin_config * const pconf, const config_plugin_value_t *cpv) {
178     do {
179         mod_cgi_merge_config_cpv(pconf, cpv);
180     } while ((++cpv)->k_id != -1);
181 }
182 
183 static void mod_cgi_patch_config(request_st * const r, plugin_data * const p) {
184     p->conf = p->defaults; /* copy small struct instead of memcpy() */
185     /*memcpy(&p->conf, &p->defaults, sizeof(plugin_config));*/
186     for (int i = 1, used = p->nconfig; i < used; ++i) {
187         if (config_check_cond(r, (uint32_t)p->cvlist[i].k_id))
188             mod_cgi_merge_config(&p->conf, p->cvlist + p->cvlist[i].v.u2[0]);
189     }
190 }
191 
192 SETDEFAULTS_FUNC(mod_cgi_set_defaults) {
193     static const config_plugin_keys_t cpk[] = {
194       { CONST_STR_LEN("cgi.assign"),
195         T_CONFIG_ARRAY_KVSTRING,
196         T_CONFIG_SCOPE_CONNECTION }
197      ,{ CONST_STR_LEN("cgi.execute-x-only"),
198         T_CONFIG_BOOL,
199         T_CONFIG_SCOPE_CONNECTION }
200      ,{ CONST_STR_LEN("cgi.x-sendfile"),
201         T_CONFIG_BOOL,
202         T_CONFIG_SCOPE_CONNECTION }
203      ,{ CONST_STR_LEN("cgi.x-sendfile-docroot"),
204         T_CONFIG_ARRAY_VLIST,
205         T_CONFIG_SCOPE_CONNECTION }
206      ,{ CONST_STR_LEN("cgi.local-redir"),
207         T_CONFIG_BOOL,
208         T_CONFIG_SCOPE_CONNECTION }
209      ,{ CONST_STR_LEN("cgi.upgrade"),
210         T_CONFIG_BOOL,
211         T_CONFIG_SCOPE_CONNECTION }
212      ,{ NULL, 0,
213         T_CONFIG_UNSET,
214         T_CONFIG_SCOPE_UNSET }
215     };
216 
217     plugin_data * const p = p_d;
218     if (!config_plugin_values_init(srv, p, cpk, "mod_cgi"))
219         return HANDLER_ERROR;
220 
221     /* process and validate config directives
222      * (init i to 0 if global context; to 1 to skip empty global context) */
223     for (int i = !p->cvlist[0].v.u2[1]; i < p->nconfig; ++i) {
224         const config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0];
225         for (; -1 != cpv->k_id; ++cpv) {
226             switch (cpv->k_id) {
227               case 0: /* cgi.assign */
228               case 1: /* cgi.execute-x-only */
229               case 2: /* cgi.x-sendfile */
230                 break;
231               case 3: /* cgi.x-sendfile-docroot */
232                 for (uint32_t j = 0; j < cpv->v.a->used; ++j) {
233                     data_string *ds = (data_string *)cpv->v.a->data[j];
234                     if (ds->value.ptr[0] != '/') {
235                         log_error(srv->errh, __FILE__, __LINE__,
236                           "%s paths must begin with '/'; invalid: \"%s\"",
237                           cpk[cpv->k_id].k, ds->value.ptr);
238                         return HANDLER_ERROR;
239                     }
240                     buffer_path_simplify(&ds->value, &ds->value);
241                     buffer_append_slash(&ds->value);
242                 }
243                 break;
244               case 4: /* cgi.local-redir */
245               case 5: /* cgi.upgrade */
246                 break;
247               default:/* should not happen */
248                 break;
249             }
250         }
251     }
252 
253     /* initialize p->defaults from global config context */
254     if (p->nconfig > 0 && p->cvlist->v.u2[1]) {
255         const config_plugin_value_t *cpv = p->cvlist + p->cvlist->v.u2[0];
256         if (-1 != cpv->k_id)
257             mod_cgi_merge_config(&p->defaults, cpv);
258     }
259 
260     return HANDLER_GO_ON;
261 }
262 
263 
264 static void cgi_pid_add(plugin_data *p, pid_t pid, void *ctx) {
265     buffer_pid_t *bp = &(p->cgi_pid);
266 
267     if (bp->used == bp->size) {
268         bp->size += 16;
269         bp->ptr = realloc(bp->ptr, sizeof(*bp->ptr) * bp->size);
270         force_assert(bp->ptr);
271     }
272 
273     bp->ptr[bp->used].pid = pid;
274     bp->ptr[bp->used].ctx = ctx;
275     ++bp->used;
276 }
277 
278 static void cgi_pid_kill(plugin_data *p, pid_t pid) {
279     buffer_pid_t *bp = &(p->cgi_pid);
280     for (size_t i = 0; i < bp->used; ++i) {
281         if (bp->ptr[i].pid == pid) {
282             bp->ptr[i].ctx = NULL;
283             kill(pid, SIGTERM);
284             return;
285         }
286     }
287 }
288 
289 static void cgi_pid_del(plugin_data *p, size_t i) {
290     buffer_pid_t *bp = &(p->cgi_pid);
291 
292     if (i != bp->used - 1)
293         bp->ptr[i] = bp->ptr[bp->used - 1];
294 
295     --bp->used;
296 }
297 
298 
299 static void cgi_connection_close_fdtocgi(handler_ctx *hctx) {
300 	/*(closes only hctx->fdtocgi)*/
301 	struct fdevents * const ev = hctx->ev;
302 	fdevent_fdnode_event_del(ev, hctx->fdntocgi);
303 	/*fdevent_unregister(ev, hctx->fdtocgi);*//*(handled below)*/
304 	fdevent_sched_close(ev, hctx->fdtocgi, 0);
305 	hctx->fdntocgi = NULL;
306 	hctx->fdtocgi = -1;
307 }
308 
309 static void cgi_connection_close(handler_ctx *hctx) {
310 	/* the connection to the browser went away, but we still have a connection
311 	 * to the CGI script
312 	 *
313 	 * close cgi-connection
314 	 */
315 
316 	if (hctx->fd != -1) {
317 		struct fdevents * const ev = hctx->ev;
318 		/* close connection to the cgi-script */
319 		fdevent_fdnode_event_del(ev, hctx->fdn);
320 		/*fdevent_unregister(ev, hctx->fd);*//*(handled below)*/
321 		fdevent_sched_close(ev, hctx->fd, 0);
322 		hctx->fdn = NULL;
323 	}
324 
325 	if (hctx->fdtocgi != -1) {
326 		cgi_connection_close_fdtocgi(hctx); /*(closes only hctx->fdtocgi)*/
327 	}
328 
329 	plugin_data * const p = hctx->plugin_data;
330 
331 	if (hctx->pid > 0) {
332 		cgi_pid_kill(p, hctx->pid);
333 	}
334 
335 	request_st * const r = hctx->r;
336 
337 	r->plugin_ctx[p->id] = NULL;
338 
339 	cgi_handler_ctx_free(hctx);
340 
341 	/* finish response (if not already r->resp_body_started, r->resp_body_finished) */
342 	if (r->handler_module == p->self) {
343 		http_response_backend_done(r);
344 	}
345 }
346 
347 static handler_t cgi_connection_close_callback(request_st * const r, void *p_d) {
348 	plugin_data *p = p_d;
349 	handler_ctx *hctx = r->plugin_ctx[p->id];
350 	if (hctx) cgi_connection_close(hctx);
351 
352 	return HANDLER_GO_ON;
353 }
354 
355 
356 static int cgi_write_request(handler_ctx *hctx, int fd);
357 
358 
359 static handler_t cgi_handle_fdevent_send (void *ctx, int revents) {
360 	handler_ctx *hctx = ctx;
361 	request_st * const r = hctx->r;
362 
363 	/*(joblist only actually necessary here in mod_cgi fdevent send if returning HANDLER_ERROR)*/
364 	joblist_append(r->con);
365 
366 	if (revents & FDEVENT_OUT) {
367 		if (0 != cgi_write_request(hctx, hctx->fdtocgi)) {
368 			cgi_connection_close(hctx);
369 			return HANDLER_ERROR;
370 		}
371 		/* more request body to be sent to CGI */
372 	}
373 
374 	if (revents & FDEVENT_HUP) {
375 		/* skip sending remaining data to CGI */
376 		if (r->reqbody_length) {
377 			chunkqueue *cq = &r->reqbody_queue;
378 			chunkqueue_mark_written(cq, chunkqueue_length(cq));
379 			if (cq->bytes_in != (off_t)r->reqbody_length) {
380 				r->keep_alive = 0;
381 			}
382 		}
383 
384 		cgi_connection_close_fdtocgi(hctx); /*(closes only hctx->fdtocgi)*/
385 	} else if (revents & FDEVENT_ERR) {
386 		/* kill all connections to the cgi process */
387 #if 1
388 		log_error(r->conf.errh, __FILE__, __LINE__, "cgi-FDEVENT_ERR");
389 #endif
390 		cgi_connection_close(hctx);
391 		return HANDLER_ERROR;
392 	}
393 
394 	return HANDLER_FINISHED;
395 }
396 
397 
398 static handler_t cgi_response_headers(request_st * const r, struct http_response_opts_t *opts) {
399     /* response headers just completed */
400     handler_ctx *hctx = (handler_ctx *)opts->pdata;
401 
402     if (light_btst(r->resp_htags, HTTP_HEADER_UPGRADE)) {
403         if (hctx->conf.upgrade && r->http_status == 101) {
404             /* 101 Switching Protocols; transition to transparent proxy */
405             http_response_upgrade_read_body_unknown(r);
406         }
407         else {
408             light_bclr(r->resp_htags, HTTP_HEADER_UPGRADE);
409           #if 0
410             /* preserve prior questionable behavior; likely broken behavior
411              * anyway if backend thinks connection is being upgraded but client
412              * does not receive Connection: upgrade */
413             http_header_response_unset(r, HTTP_HEADER_UPGRADE,
414                                        CONST_STR_LEN("Upgrade"));
415           #endif
416         }
417     }
418 
419     if (hctx->conf.upgrade
420         && !light_btst(r->resp_htags, HTTP_HEADER_UPGRADE)) {
421         chunkqueue *cq = &r->reqbody_queue;
422         hctx->conf.upgrade = 0;
423         if (cq->bytes_out == (off_t)r->reqbody_length) {
424             cgi_connection_close_fdtocgi(hctx); /*(closes hctx->fdtocgi)*/
425         }
426     }
427 
428     return HANDLER_GO_ON;
429 }
430 
431 
432 static int cgi_recv_response(request_st * const r, handler_ctx * const hctx) {
433 		switch (http_response_read(r, &hctx->opts,
434 					   hctx->response, hctx->fdn)) {
435 		default:
436 			return HANDLER_GO_ON;
437 		case HANDLER_ERROR:
438 			http_response_backend_error(r);
439 			__attribute_fallthrough__
440 		case HANDLER_FINISHED:
441 			cgi_connection_close(hctx);
442 			return HANDLER_FINISHED;
443 		case HANDLER_COMEBACK:
444 			/* flag for mod_cgi_handle_subrequest() */
445 			hctx->conf.local_redir = 2;
446 			buffer_clear(hctx->response);
447 			return HANDLER_COMEBACK;
448 		}
449 }
450 
451 
452 static handler_t cgi_handle_fdevent(void *ctx, int revents) {
453 	handler_ctx *hctx = ctx;
454 	request_st * const r = hctx->r;
455 
456 	joblist_append(r->con);
457 
458 	if (revents & FDEVENT_IN) {
459 		handler_t rc = cgi_recv_response(r, hctx); /*(might invalidate hctx)*/
460 		if (rc != HANDLER_GO_ON) return rc;         /*(unless HANDLER_GO_ON)*/
461 	}
462 
463 	/* perhaps this issue is already handled */
464 	if (revents & (FDEVENT_HUP|FDEVENT_RDHUP)) {
465 		if (r->resp_body_started) {
466 			/* drain any remaining data from kernel pipe buffers
467 			 * even if (r->conf.stream_response_body
468 			 *          & FDEVENT_STREAM_RESPONSE_BUFMIN)
469 			 * since event loop will spin on fd FDEVENT_HUP event
470 			 * until unregistered. */
471 			handler_t rc;
472 			const unsigned short flags = r->conf.stream_response_body;
473 			r->conf.stream_response_body &= ~FDEVENT_STREAM_RESPONSE_BUFMIN;
474 			r->conf.stream_response_body |= FDEVENT_STREAM_RESPONSE_POLLRDHUP;
475 			do {
476 				rc = cgi_recv_response(r,hctx); /*(might invalidate hctx)*/
477 			} while (rc == HANDLER_GO_ON);           /*(unless HANDLER_GO_ON)*/
478 			r->conf.stream_response_body = flags;
479 			return rc; /* HANDLER_FINISHED or HANDLER_COMEBACK or HANDLER_ERROR */
480 		} else if (!buffer_string_is_empty(hctx->response)) {
481 			/* unfinished header package which is a body in reality */
482 			r->resp_body_started = 1;
483 			if (0 != http_chunk_append_buffer(r, hctx->response)) {
484 				cgi_connection_close(hctx);
485 				return HANDLER_ERROR;
486 			}
487 			if (0 == r->http_status) r->http_status = 200; /* OK */
488 		}
489 		cgi_connection_close(hctx);
490 	} else if (revents & FDEVENT_ERR) {
491 		/* kill all connections to the cgi process */
492 		cgi_connection_close(hctx);
493 		return HANDLER_ERROR;
494 	}
495 
496 	return HANDLER_FINISHED;
497 }
498 
499 
500 static int cgi_env_add(void *venv, const char *key, size_t key_len, const char *val, size_t val_len) {
501 	env_accum *env = venv;
502 	char *dst;
503 
504 	if (!key || (!val && val_len)) return -1;
505 
506 	if (env->size - env->used < key_len + val_len + 2) {
507 		if (0 == env->size) env->size = 4096;
508 		do { env->size *= 2; } while (env->size - env->used < key_len + val_len + 2);
509 		env->ptr = realloc(env->ptr, env->size);
510 		force_assert(env->ptr);
511 	}
512 
513 	dst = env->ptr + env->used;
514 	memcpy(dst, key, key_len);
515 	dst[key_len] = '=';
516 	memcpy(dst + key_len + 1, val, val_len);
517 	dst[key_len + 1 + val_len] = '\0';
518 
519 	if (env->osize == env->oused) {
520 		env->osize += 16;
521 		env->offsets = realloc(env->offsets, env->osize * sizeof(*env->offsets));
522 		force_assert(env->offsets);
523 	}
524 	env->offsets[env->oused++] = env->used;
525 	env->used += key_len + val_len + 2;
526 
527 	return 0;
528 }
529 
530 static int cgi_write_request(handler_ctx *hctx, int fd) {
531 	request_st * const r = hctx->r;
532 	chunkqueue *cq = &r->reqbody_queue;
533 	chunk *c;
534 
535 	chunkqueue_remove_finished_chunks(cq); /* unnecessary? */
536 
537 	/* old comment: windows doesn't support select() on pipes - wouldn't be easy to fix for all platforms.
538 	 * solution: if this is still a problem on windows, then substitute
539 	 * socketpair() for pipe() and closesocket() for close() on windows.
540 	 */
541 
542 	for (c = cq->first; c; c = cq->first) {
543 		ssize_t wr = chunkqueue_write_chunk_to_pipe(fd, cq, r->conf.errh);
544 		if (wr > 0) {
545 			chunkqueue_mark_written(cq, wr);
546 			/* continue if wrote whole chunk or wrote 16k block
547 			 * (see chunkqueue_write_chunk_file_intermed()) */
548 			if (c != cq->first || wr == 16384)
549 				continue;
550 			/*(else partial write)*/
551 		}
552 		else if (wr < 0) {
553 				switch(errno) {
554 				case EAGAIN:
555 				case EINTR:
556 					/* ignore and try again later */
557 					break;
558 				case EPIPE:
559 				case ECONNRESET:
560 					/* connection closed */
561 					log_error(r->conf.errh, __FILE__, __LINE__,
562 					  "failed to send post data to cgi, connection closed by CGI");
563 					/* skip all remaining data */
564 					chunkqueue_mark_written(cq, chunkqueue_length(cq));
565 					break;
566 				default:
567 					/* fatal error */
568 					log_perror(r->conf.errh, __FILE__, __LINE__, "write() failed");
569 					return -1;
570 				}
571 		}
572 		/*if (0 == wr) break;*/ /*(might block)*/
573 		break;
574 	}
575 
576 	if (cq->bytes_out == (off_t)r->reqbody_length && !hctx->conf.upgrade) {
577 		/* sent all request body input */
578 		/* close connection to the cgi-script */
579 		if (-1 == hctx->fdtocgi) { /*(received request body sent in initial send to pipe buffer)*/
580 			--r->con->srv->cur_fds;
581 			if (close(fd)) {
582 				log_perror(r->conf.errh, __FILE__, __LINE__, "cgi stdin close %d failed", fd);
583 			}
584 		} else {
585 			cgi_connection_close_fdtocgi(hctx); /*(closes only hctx->fdtocgi)*/
586 		}
587 	} else {
588 		off_t cqlen = chunkqueue_length(cq);
589 		if (cq->bytes_in != r->reqbody_length && cqlen < 65536 - 16384) {
590 			/*(r->conf.stream_request_body & FDEVENT_STREAM_REQUEST)*/
591 			if (!(r->conf.stream_request_body & FDEVENT_STREAM_REQUEST_POLLIN)) {
592 				r->conf.stream_request_body |= FDEVENT_STREAM_REQUEST_POLLIN;
593 				r->con->is_readable = 1; /* trigger optimistic read from client */
594 			}
595 		}
596 		struct fdevents * const ev = hctx->ev;
597 		if (-1 == hctx->fdtocgi) { /*(not registered yet)*/
598 			hctx->fdtocgi = fd;
599 			hctx->fdntocgi = fdevent_register(ev, hctx->fdtocgi, cgi_handle_fdevent_send, hctx);
600 		}
601 		if (0 == cqlen) { /*(chunkqueue_is_empty(cq))*/
602 			if ((fdevent_fdnode_interest(hctx->fdntocgi) & FDEVENT_OUT)) {
603 				fdevent_fdnode_event_set(ev, hctx->fdntocgi, 0);
604 			}
605 		} else {
606 			/* more request body remains to be sent to CGI so register for fdevents */
607 			fdevent_fdnode_event_set(ev, hctx->fdntocgi, FDEVENT_OUT);
608 		}
609 	}
610 
611 	return 0;
612 }
613 
614 static int cgi_create_env(request_st * const r, plugin_data * const p, handler_ctx * const hctx, buffer * const cgi_handler) {
615 	char *args[3];
616 	int to_cgi_fds[2];
617 	int from_cgi_fds[2];
618 	int dfd = -1;
619 	UNUSED(p);
620 
621 	if (!buffer_string_is_empty(cgi_handler)) {
622 		if (NULL == stat_cache_path_stat(cgi_handler)) {
623 			log_perror(r->conf.errh, __FILE__, __LINE__,
624 			  "stat for cgi-handler %s", cgi_handler->ptr);
625 			return -1;
626 		}
627 	}
628 
629 	if (pipe_cloexec(to_cgi_fds)) {
630 		log_perror(r->conf.errh, __FILE__, __LINE__, "pipe failed");
631 		return -1;
632 	}
633 	if (pipe_cloexec(from_cgi_fds)) {
634 		close(to_cgi_fds[0]);
635 		close(to_cgi_fds[1]);
636 		log_perror(r->conf.errh, __FILE__, __LINE__, "pipe failed");
637 		return -1;
638 	}
639 
640 	{
641 		size_t i = 0;
642 		http_cgi_opts opts = { 0, 0, NULL, NULL };
643 		env_accum *env = &p->env;
644 		env->used = 0;
645 		env->oused = 0;
646 
647 		/* create environment */
648 
649 		http_cgi_headers(r, &opts, cgi_env_add, env);
650 
651 		/* for valgrind */
652 		if (p->env.ld_preload) {
653 			cgi_env_add(env, CONST_STR_LEN("LD_PRELOAD"), CONST_BUF_LEN(p->env.ld_preload));
654 		}
655 		if (p->env.ld_library_path) {
656 			cgi_env_add(env, CONST_STR_LEN("LD_LIBRARY_PATH"), CONST_BUF_LEN(p->env.ld_library_path));
657 		}
658 	      #ifdef __CYGWIN__
659 		/* CYGWIN needs SYSTEMROOT */
660 		if (p->env.systemroot) {
661 			cgi_env_add(env, CONST_STR_LEN("SYSTEMROOT"), CONST_BUF_LEN(p->env.systemroot));
662 		}
663 	      #endif
664 
665 		if (env->esize <= env->oused) {
666 			env->esize = (env->oused + 1 + 0xf) & ~(0xfuL);
667 			env->eptr = realloc(env->eptr, env->esize * sizeof(*env->eptr));
668 			force_assert(env->eptr);
669 		}
670 		for (i = 0; i < env->oused; ++i) {
671 			env->eptr[i] = env->ptr + env->offsets[i];
672 		}
673 		env->eptr[env->oused] = NULL;
674 
675 		/* set up args */
676 		i = 0;
677 
678 		if (!buffer_string_is_empty(cgi_handler)) {
679 			args[i++] = cgi_handler->ptr;
680 		}
681 		args[i++] = r->physical.path.ptr;
682 		args[i  ] = NULL;
683 	}
684 
685 	dfd = fdevent_open_dirname(r->physical.path.ptr, r->conf.follow_symlink);
686 	if (-1 == dfd) {
687 		log_perror(r->conf.errh, __FILE__, __LINE__, "open dirname %s failed", r->physical.path.ptr);
688 	}
689 
690 	int serrh_fd = r->conf.serrh ? r->conf.serrh->errorlog_fd : -1;
691 	hctx->pid = (dfd >= 0) ? fdevent_fork_execve(args[0], args, p->env.eptr, to_cgi_fds[0], from_cgi_fds[1], serrh_fd, dfd) : -1;
692 
693 	if (-1 == hctx->pid) {
694 		/* log error with errno prior to calling close() (might change errno) */
695 		log_perror(r->conf.errh, __FILE__, __LINE__, "fork failed");
696 		if (-1 != dfd) close(dfd);
697 		close(from_cgi_fds[0]);
698 		close(from_cgi_fds[1]);
699 		close(to_cgi_fds[0]);
700 		close(to_cgi_fds[1]);
701 		return -1;
702 	} else {
703 		if (-1 != dfd) close(dfd);
704 		close(from_cgi_fds[1]);
705 		close(to_cgi_fds[0]);
706 
707 		hctx->fd = from_cgi_fds[0];
708 
709 		cgi_pid_add(p, hctx->pid, hctx);
710 
711 		if (0 == r->reqbody_length) {
712 			close(to_cgi_fds[1]);
713 		}
714 		else if (0 == fdevent_fcntl_set_nb(to_cgi_fds[1])
715 		         && 0 == cgi_write_request(hctx, to_cgi_fds[1])) {
716 			++r->con->srv->cur_fds;
717 		}
718 		else {
719 			close(to_cgi_fds[1]);
720 			/*(hctx->fd not yet registered with fdevent, so manually
721 			 * cleanup here; see fdevent_register() further below)*/
722 			close(hctx->fd);
723 			hctx->fd = -1;
724 			cgi_connection_close(hctx);
725 			return -1;
726 		}
727 
728 		++r->con->srv->cur_fds;
729 
730 		struct fdevents * const ev = hctx->ev;
731 		hctx->fdn = fdevent_register(ev, hctx->fd, cgi_handle_fdevent, hctx);
732 		if (-1 == fdevent_fcntl_set_nb(hctx->fd)) {
733 			log_perror(r->conf.errh, __FILE__, __LINE__, "fcntl failed");
734 			cgi_connection_close(hctx);
735 			return -1;
736 		}
737 		fdevent_fdnode_event_set(ev, hctx->fdn, FDEVENT_IN | FDEVENT_RDHUP);
738 
739 		return 0;
740 	}
741 }
742 
743 URIHANDLER_FUNC(cgi_is_handled) {
744 	plugin_data *p = p_d;
745 	const stat_cache_st *st;
746 	data_string *ds;
747 
748 	if (NULL != r->handler_module) return HANDLER_GO_ON;
749 	if (buffer_is_empty(&r->physical.path)) return HANDLER_GO_ON;
750 
751 	mod_cgi_patch_config(r, p);
752 	if (NULL == p->conf.cgi) return HANDLER_GO_ON;
753 
754 	ds = (data_string *)array_match_key_suffix(p->conf.cgi, &r->physical.path);
755 	if (NULL == ds) return HANDLER_GO_ON;
756 
757 	st = stat_cache_path_stat(&r->physical.path);
758 	if (NULL == st) return HANDLER_GO_ON;
759 
760 	/* (aside: CGI might be executable even if it is not readable) */
761 	if (!S_ISREG(st->st_mode)) return HANDLER_GO_ON;
762 	if (p->conf.execute_x_only == 1 && (st->st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) == 0) return HANDLER_GO_ON;
763 
764 	{
765 		handler_ctx *hctx = cgi_handler_ctx_init();
766 		hctx->ev = r->con->srv->ev;
767 		hctx->r = r;
768 		hctx->plugin_data = p;
769 		hctx->cgi_handler = &ds->value;
770 		memcpy(&hctx->conf, &p->conf, sizeof(plugin_config));
771 		hctx->conf.upgrade =
772 		  hctx->conf.upgrade
773 		  && r->http_version == HTTP_VERSION_1_1
774 		  && light_btst(r->rqst_htags, HTTP_HEADER_UPGRADE);
775 		hctx->opts.fdfmt = S_IFIFO;
776 		hctx->opts.backend = BACKEND_CGI;
777 		hctx->opts.authorizer = 0;
778 		hctx->opts.local_redir = hctx->conf.local_redir;
779 		hctx->opts.xsendfile_allow = hctx->conf.xsendfile_allow;
780 		hctx->opts.xsendfile_docroot = hctx->conf.xsendfile_docroot;
781 		hctx->opts.pdata = hctx;
782 		hctx->opts.headers = cgi_response_headers;
783 		r->plugin_ctx[p->id] = hctx;
784 		r->handler_module = p->self;
785 	}
786 
787 	return HANDLER_GO_ON;
788 }
789 
790 __attribute_cold__
791 __attribute_noinline__
792 static handler_t mod_cgi_local_redir(request_st * const r) {
793     /* must be called from mod_cgi_handle_subrequest() so that HANDLER_COMEBACK
794      * return value propagates back through connection_state_machine() */
795     http_response_reset(r); /*(includes r->http_status = 0)*/
796     plugins_call_handle_request_reset(r);
797     /*cgi_connection_close(hctx);*//*(already cleaned up and hctx is now invalid)*/
798     return HANDLER_COMEBACK;
799 }
800 
801 /*
802  * - HANDLER_GO_ON : not our job
803  * - HANDLER_FINISHED: got response
804  * - HANDLER_WAIT_FOR_EVENT: waiting for response
805  */
806 SUBREQUEST_FUNC(mod_cgi_handle_subrequest) {
807 	plugin_data * const p = p_d;
808 	handler_ctx * const hctx = r->plugin_ctx[p->id];
809 	if (NULL == hctx) return HANDLER_GO_ON;
810 
811 	if (2 == hctx->conf.local_redir) return mod_cgi_local_redir(r);
812 
813 	if ((r->conf.stream_response_body & FDEVENT_STREAM_RESPONSE_BUFMIN)
814 	    && r->resp_body_started) {
815 		if (chunkqueue_length(&r->write_queue) > 65536 - 4096) {
816 			fdevent_fdnode_event_clr(hctx->ev, hctx->fdn, FDEVENT_IN);
817 		} else if (!(fdevent_fdnode_interest(hctx->fdn) & FDEVENT_IN)) {
818 			/* optimistic read from backend */
819 			handler_t rc = cgi_recv_response(r, hctx);  /*(might invalidate hctx)*/
820 			if (rc == HANDLER_COMEBACK) mod_cgi_local_redir(r);
821 			if (rc != HANDLER_GO_ON) return rc;          /*(unless HANDLER_GO_ON)*/
822 			fdevent_fdnode_event_add(hctx->ev, hctx->fdn, FDEVENT_IN);
823 		}
824 	}
825 
826 	chunkqueue * const cq = &r->reqbody_queue;
827 
828 	if (cq->bytes_in != (off_t)r->reqbody_length) {
829 		/*(64k - 4k to attempt to avoid temporary files
830 		 * in conjunction with FDEVENT_STREAM_REQUEST_BUFMIN)*/
831 		if (chunkqueue_length(cq) > 65536 - 4096
832 		    && (r->conf.stream_request_body & FDEVENT_STREAM_REQUEST_BUFMIN)){
833 			r->conf.stream_request_body &= ~FDEVENT_STREAM_REQUEST_POLLIN;
834 			if (-1 != hctx->fd) return HANDLER_WAIT_FOR_EVENT;
835 		} else {
836 			handler_t rc = r->con->reqbody_read(r);
837 			if (!chunkqueue_is_empty(cq)) {
838 				if (fdevent_fdnode_interest(hctx->fdntocgi) & FDEVENT_OUT) {
839 					return (rc == HANDLER_GO_ON) ? HANDLER_WAIT_FOR_EVENT : rc;
840 				}
841 			}
842 			if (rc != HANDLER_GO_ON) return rc;
843 
844 			/* CGI environment requires that Content-Length be set.
845 			 * Send 411 Length Required if Content-Length missing.
846 			 * (occurs here if client sends Transfer-Encoding: chunked
847 			 *  and module is flagged to stream request body to backend) */
848 			if (-1 == r->reqbody_length) {
849 				return (r->conf.stream_request_body & FDEVENT_STREAM_REQUEST)
850 				  ? http_response_reqbody_read_error(r, 411)
851 				  : HANDLER_WAIT_FOR_EVENT;
852 			}
853 		}
854 	}
855 
856 	if (-1 == hctx->fd) {
857 		if (cgi_create_env(r, p, hctx, hctx->cgi_handler)) {
858 			r->http_status = 500;
859 			r->handler_module = NULL;
860 
861 			return HANDLER_FINISHED;
862 		}
863 	} else if (!chunkqueue_is_empty(cq)) {
864 		if (0 != cgi_write_request(hctx, hctx->fdtocgi)) {
865 			cgi_connection_close(hctx);
866 			return HANDLER_ERROR;
867 		}
868 	}
869 
870 	/* if not done, wait for CGI to close stdout, so we read EOF on pipe */
871 	return HANDLER_WAIT_FOR_EVENT;
872 }
873 
874 
875 static handler_t cgi_waitpid_cb(server *srv, void *p_d, pid_t pid, int status) {
876     plugin_data *p = (plugin_data *)p_d;
877     for (size_t i = 0; i < p->cgi_pid.used; ++i) {
878         handler_ctx *hctx;
879         if (pid != p->cgi_pid.ptr[i].pid) continue;
880 
881         hctx = (handler_ctx *)p->cgi_pid.ptr[i].ctx;
882         if (hctx) hctx->pid = -1;
883         cgi_pid_del(p, i);
884 
885         if (WIFEXITED(status)) {
886             /* (skip logging (non-zero) CGI exit; might be very noisy) */
887         }
888         else if (WIFSIGNALED(status)) {
889             /* ignore SIGTERM if sent by cgi_connection_close() (NULL == hctx)*/
890             if (WTERMSIG(status) != SIGTERM || NULL != hctx) {
891                 log_error_st *errh = hctx ? hctx->r->conf.errh : srv->errh;
892                 log_error(errh, __FILE__, __LINE__,
893                   "CGI pid %d died with signal %d", pid, WTERMSIG(status));
894             }
895         }
896         else {
897             log_error_st *errh = hctx ? hctx->r->conf.errh : srv->errh;
898             log_error(errh, __FILE__, __LINE__,
899               "CGI pid %d ended unexpectedly", pid);
900         }
901 
902         return HANDLER_FINISHED;
903     }
904 
905     return HANDLER_GO_ON;
906 }
907 
908 
909 int mod_cgi_plugin_init(plugin *p);
910 int mod_cgi_plugin_init(plugin *p) {
911 	p->version     = LIGHTTPD_VERSION_ID;
912 	p->name        = "cgi";
913 
914 	p->handle_request_reset = cgi_connection_close_callback;
915 	p->handle_subrequest_start = cgi_is_handled;
916 	p->handle_subrequest = mod_cgi_handle_subrequest;
917 	p->handle_waitpid = cgi_waitpid_cb;
918 	p->init           = mod_cgi_init;
919 	p->cleanup        = mod_cgi_free;
920 	p->set_defaults   = mod_cgi_set_defaults;
921 
922 	return 0;
923 }
924