1 #include "buffer.h"
2 #include "server.h"
3 #include "keyvalue.h"
4 #include "log.h"
5 
6 #include "http_chunk.h"
7 #include "fdevent.h"
8 #include "connections.h"
9 #include "response.h"
10 #include "joblist.h"
11 
12 #include "plugin.h"
13 
14 #include "inet_ntop_cache.h"
15 #include "stat_cache.h"
16 #include "status_counter.h"
17 
18 #include <sys/types.h>
19 #include <unistd.h>
20 #include <errno.h>
21 #include <fcntl.h>
22 #include <string.h>
23 #include <stdlib.h>
24 #include <ctype.h>
25 #include <assert.h>
26 #include <signal.h>
27 
28 #ifdef HAVE_FASTCGI_FASTCGI_H
29 # include <fastcgi/fastcgi.h>
30 #else
31 # ifdef HAVE_FASTCGI_H
32 #  include <fastcgi.h>
33 # else
34 #  include "fastcgi.h"
35 # endif
36 #endif /* HAVE_FASTCGI_FASTCGI_H */
37 
38 #include <stdio.h>
39 
40 #ifdef HAVE_SYS_FILIO_H
41 # include <sys/filio.h>
42 #endif
43 
44 #include "sys-socket.h"
45 
46 #ifdef HAVE_SYS_UIO_H
47 #include <sys/uio.h>
48 #endif
49 #ifdef HAVE_SYS_WAIT_H
50 #include <sys/wait.h>
51 #endif
52 
53 #include "version.h"
54 
55 #define FCGI_ENV_ADD_CHECK(ret, con) \
56 	if (ret == -1) { \
57 		con->http_status = 400; \
58 		con->file_finished = 1; \
59 		return -1; \
60 	};
61 
62 /*
63  *
64  * TODO:
65  *
66  * - add timeout for a connect to a non-fastcgi process
67  *   (use state_timestamp + state)
68  *
69  */
70 
71 typedef struct fcgi_proc {
72 	size_t id; /* id will be between 1 and max_procs */
73 	buffer *unixsocket; /* config.socket + "-" + id */
74 	unsigned port;  /* config.port + pno */
75 
76 	buffer *connection_name; /* either tcp:<host>:<port> or unix:<socket> for debugging purposes */
77 
78 	pid_t pid;   /* PID of the spawned process (0 if not spawned locally) */
79 
80 
81 	size_t load; /* number of requests waiting on this process */
82 
83 	size_t requests;  /* see max_requests */
84 	struct fcgi_proc *prev, *next; /* see first */
85 
86 	time_t disabled_until; /* this proc is disabled until, use something else until then */
87 
88 	int is_local;
89 
90 	enum {
91 		PROC_STATE_UNSET,    /* init-phase */
92 		PROC_STATE_RUNNING,  /* alive */
93 		PROC_STATE_OVERLOADED, /* listen-queue is full,
94 					  don't send anything to this proc for the next 2 seconds */
95 		PROC_STATE_DIED_WAIT_FOR_PID, /* */
96 		PROC_STATE_DIED,     /* marked as dead, should be restarted */
97 		PROC_STATE_KILLED    /* was killed as we don't have the load anymore */
98 	} state;
99 } fcgi_proc;
100 
101 typedef struct {
102 	/* the key that is used to reference this value */
103 	buffer *id;
104 
105 	/* list of processes handling this extension
106 	 * sorted by lowest load
107 	 *
108 	 * whenever a job is done move it up in the list
109 	 * until it is sorted, move it down as soon as the
110 	 * job is started
111 	 */
112 	fcgi_proc *first;
113 	fcgi_proc *unused_procs;
114 
115 	/*
116 	 * spawn at least min_procs, at max_procs.
117 	 *
118 	 * as soon as the load of the first entry
119 	 * is max_load_per_proc we spawn a new one
120 	 * and add it to the first entry and give it
121 	 * the load
122 	 *
123 	 */
124 
125 	unsigned short max_procs;
126 	size_t num_procs;    /* how many procs are started */
127 	size_t active_procs; /* how many of them are really running, i.e. state = PROC_STATE_RUNNING */
128 
129 	/*
130 	 * time after a disabled remote connection is tried to be re-enabled
131 	 *
132 	 *
133 	 */
134 
135 	unsigned short disable_time;
136 
137 	/*
138 	 * some fastcgi processes get a little bit larger
139 	 * than wanted. max_requests_per_proc kills a
140 	 * process after a number of handled requests.
141 	 *
142 	 */
143 	size_t max_requests_per_proc;
144 
145 
146 	/* config */
147 
148 	/*
149 	 * host:port
150 	 *
151 	 * if host is one of the local IP adresses the
152 	 * whole connection is local
153 	 *
154 	 * if port is not 0, and host is not specified,
155 	 * "localhost" (INADDR_LOOPBACK) is assumed.
156 	 *
157 	 */
158 	buffer *host;
159 	unsigned short port;
160 
161 	/*
162 	 * Unix Domain Socket
163 	 *
164 	 * instead of TCP/IP we can use Unix Domain Sockets
165 	 * - more secure (you have fileperms to play with)
166 	 * - more control (on locally)
167 	 * - more speed (no extra overhead)
168 	 */
169 	buffer *unixsocket;
170 
171 	/* if socket is local we can start the fastcgi
172 	 * process ourself
173 	 *
174 	 * bin-path is the path to the binary
175 	 *
176 	 * check min_procs and max_procs for the number
177 	 * of process to start up
178 	 */
179 	buffer *bin_path;
180 
181 	/* bin-path is set bin-environment is taken to
182 	 * create the environement before starting the
183 	 * FastCGI process
184 	 *
185 	 */
186 	array *bin_env;
187 
188 	array *bin_env_copy;
189 
190 	/*
191 	 * docroot-translation between URL->phys and the
192 	 * remote host
193 	 *
194 	 * reasons:
195 	 * - different dir-layout if remote
196 	 * - chroot if local
197 	 *
198 	 */
199 	buffer *docroot;
200 
201 	/*
202 	 * fastcgi-mode:
203 	 * - responser
204 	 * - authorizer
205 	 *
206 	 */
207 	unsigned short mode;
208 
209 	/*
210 	 * check_local tells you if the phys file is stat()ed
211 	 * or not. FastCGI doesn't care if the service is
212 	 * remote. If the web-server side doesn't contain
213 	 * the fastcgi-files we should not stat() for them
214 	 * and say '404 not found'.
215 	 */
216 	unsigned short check_local;
217 
218 	/*
219 	 * append PATH_INFO to SCRIPT_FILENAME
220 	 *
221 	 * php needs this if cgi.fix_pathinfo is provided
222 	 *
223 	 */
224 
225 	unsigned short break_scriptfilename_for_php;
226 
227 	/*
228 	 * workaround for program when prefix="/"
229 	 *
230 	 * rule to build PATH_INFO is hardcoded for when check_local is disabled
231 	 * enable this option to use the workaround
232 	 *
233 	 */
234 
235 	unsigned short fix_root_path_name;
236 
237 	/*
238 	 * If the backend includes X-LIGHTTPD-send-file in the response
239 	 * we use the value as filename and ignore the content.
240 	 *
241 	 */
242 	unsigned short allow_xsendfile;
243 
244 	ssize_t load; /* replace by host->load */
245 
246 	size_t max_id; /* corresponds most of the time to
247 	num_procs.
248 
249 	only if a process is killed max_id waits for the process itself
250 	to die and decrements it afterwards */
251 
252 	buffer *strip_request_uri;
253 
254 	unsigned short kill_signal; /* we need a setting for this as libfcgi
255 				       applications prefer SIGUSR1 while the
256 				       rest of the world would use SIGTERM
257 				       *sigh* */
258 } fcgi_extension_host;
259 
260 /*
261  * one extension can have multiple hosts assigned
262  * one host can spawn additional processes on the same
263  *   socket (if we control it)
264  *
265  * ext -> host -> procs
266  *    1:n     1:n
267  *
268  * if the fastcgi process is remote that whole goes down
269  * to
270  *
271  * ext -> host -> procs
272  *    1:n     1:1
273  *
274  * in case of PHP and FCGI_CHILDREN we have again a procs
275  * but we don't control it directly.
276  *
277  */
278 
279 typedef struct {
280 	buffer *key; /* like .php */
281 
282 	int note_is_sent;
283 	int last_used_ndx;
284 
285 	fcgi_extension_host **hosts;
286 
287 	size_t used;
288 	size_t size;
289 } fcgi_extension;
290 
291 typedef struct {
292 	fcgi_extension **exts;
293 
294 	size_t used;
295 	size_t size;
296 } fcgi_exts;
297 
298 
299 typedef struct {
300 	fcgi_exts *exts;
301 
302 	array *ext_mapping;
303 
304 	unsigned int debug;
305 } plugin_config;
306 
307 typedef struct {
308 	char **ptr;
309 
310 	size_t size;
311 	size_t used;
312 } char_array;
313 
314 /* generic plugin data, shared between all connections */
315 typedef struct {
316 	PLUGIN_DATA;
317 
318 	buffer *fcgi_env;
319 
320 	buffer *path;
321 	buffer *parse_response;
322 
323 	buffer *statuskey;
324 
325 	plugin_config **config_storage;
326 
327 	plugin_config conf; /* this is only used as long as no handler_ctx is setup */
328 } plugin_data;
329 
330 /* connection specific data */
331 typedef enum {
332 	FCGI_STATE_UNSET,
333 	FCGI_STATE_INIT,
334 	FCGI_STATE_CONNECT_DELAYED,
335 	FCGI_STATE_PREPARE_WRITE,
336 	FCGI_STATE_WRITE,
337 	FCGI_STATE_READ
338 } fcgi_connection_state_t;
339 
340 typedef struct {
341 	fcgi_proc *proc;
342 	fcgi_extension_host *host;
343 	fcgi_extension *ext;
344 
345 	fcgi_connection_state_t state;
346 	time_t   state_timestamp;
347 
348 	int      reconnects; /* number of reconnect attempts */
349 
350 	chunkqueue *rb; /* read queue */
351 	chunkqueue *wb; /* write queue */
352 
353 	buffer   *response_header;
354 
355 	size_t    request_id;
356 	int       fd;        /* fd to the fastcgi process */
357 	int       fde_ndx;   /* index into the fd-event buffer */
358 
359 	pid_t     pid;
360 	int       got_proc;
361 
362 	int       send_content_body;
363 
364 	plugin_config conf;
365 
366 	connection *remote_conn;  /* dumb pointer */
367 	plugin_data *plugin_data; /* dumb pointer */
368 } handler_ctx;
369 
370 
371 /* ok, we need a prototype */
372 static handler_t fcgi_handle_fdevent(server *srv, void *ctx, int revents);
373 
reset_signals(void)374 static void reset_signals(void) {
375 #ifdef SIGTTOU
376 	signal(SIGTTOU, SIG_DFL);
377 #endif
378 #ifdef SIGTTIN
379 	signal(SIGTTIN, SIG_DFL);
380 #endif
381 #ifdef SIGTSTP
382 	signal(SIGTSTP, SIG_DFL);
383 #endif
384 	signal(SIGHUP, SIG_DFL);
385 	signal(SIGPIPE, SIG_DFL);
386 	signal(SIGUSR1, SIG_DFL);
387 }
388 
fastcgi_status_copy_procname(buffer * b,fcgi_extension_host * host,fcgi_proc * proc)389 static void fastcgi_status_copy_procname(buffer *b, fcgi_extension_host *host, fcgi_proc *proc) {
390 	buffer_copy_string_len(b, CONST_STR_LEN("fastcgi.backend."));
391 	buffer_append_string_buffer(b, host->id);
392 	if (proc) {
393 		buffer_append_string_len(b, CONST_STR_LEN("."));
394 		buffer_append_long(b, proc->id);
395 	}
396 }
397 
fcgi_proc_load_inc(server * srv,handler_ctx * hctx)398 static void fcgi_proc_load_inc(server *srv, handler_ctx *hctx) {
399 	plugin_data *p = hctx->plugin_data;
400 	hctx->proc->load++;
401 
402 	status_counter_inc(srv, CONST_STR_LEN("fastcgi.active-requests"));
403 
404 	fastcgi_status_copy_procname(p->statuskey, hctx->host, hctx->proc);
405 	buffer_append_string_len(p->statuskey, CONST_STR_LEN(".load"));
406 
407 	status_counter_set(srv, CONST_BUF_LEN(p->statuskey), hctx->proc->load);
408 }
409 
fcgi_proc_load_dec(server * srv,handler_ctx * hctx)410 static void fcgi_proc_load_dec(server *srv, handler_ctx *hctx) {
411 	plugin_data *p = hctx->plugin_data;
412 	hctx->proc->load--;
413 
414 	status_counter_dec(srv, CONST_STR_LEN("fastcgi.active-requests"));
415 
416 	fastcgi_status_copy_procname(p->statuskey, hctx->host, hctx->proc);
417 	buffer_append_string_len(p->statuskey, CONST_STR_LEN(".load"));
418 
419 	status_counter_set(srv, CONST_BUF_LEN(p->statuskey), hctx->proc->load);
420 }
421 
fcgi_host_assign(server * srv,handler_ctx * hctx,fcgi_extension_host * host)422 static void fcgi_host_assign(server *srv, handler_ctx *hctx, fcgi_extension_host *host) {
423 	plugin_data *p = hctx->plugin_data;
424 	hctx->host = host;
425 	hctx->host->load++;
426 
427 	fastcgi_status_copy_procname(p->statuskey, hctx->host, NULL);
428 	buffer_append_string_len(p->statuskey, CONST_STR_LEN(".load"));
429 
430 	status_counter_set(srv, CONST_BUF_LEN(p->statuskey), hctx->host->load);
431 }
432 
fcgi_host_reset(server * srv,handler_ctx * hctx)433 static void fcgi_host_reset(server *srv, handler_ctx *hctx) {
434 	plugin_data *p = hctx->plugin_data;
435 	hctx->host->load--;
436 
437 	fastcgi_status_copy_procname(p->statuskey, hctx->host, NULL);
438 	buffer_append_string_len(p->statuskey, CONST_STR_LEN(".load"));
439 
440 	status_counter_set(srv, CONST_BUF_LEN(p->statuskey), hctx->host->load);
441 
442 	hctx->host = NULL;
443 }
444 
fcgi_host_disable(server * srv,handler_ctx * hctx)445 static void fcgi_host_disable(server *srv, handler_ctx *hctx) {
446 	plugin_data *p    = hctx->plugin_data;
447 
448 	if (hctx->host->disable_time || hctx->proc->is_local) {
449 		if (hctx->proc->state == PROC_STATE_RUNNING) hctx->host->active_procs--;
450 		hctx->proc->disabled_until = srv->cur_ts + hctx->host->disable_time;
451 		hctx->proc->state = hctx->proc->is_local ? PROC_STATE_DIED_WAIT_FOR_PID : PROC_STATE_DIED;
452 
453 		if (p->conf.debug) {
454 			log_error_write(srv, __FILE__, __LINE__, "sds",
455 				"backend disabled for", hctx->host->disable_time, "seconds");
456 		}
457 	}
458 }
459 
fastcgi_status_init(server * srv,buffer * b,fcgi_extension_host * host,fcgi_proc * proc)460 static int fastcgi_status_init(server *srv, buffer *b, fcgi_extension_host *host, fcgi_proc *proc) {
461 #define CLEAN(x) \
462 	fastcgi_status_copy_procname(b, host, proc); \
463 	buffer_append_string_len(b, CONST_STR_LEN(x)); \
464 	status_counter_set(srv, CONST_BUF_LEN(b), 0);
465 
466 	CLEAN(".disabled");
467 	CLEAN(".died");
468 	CLEAN(".overloaded");
469 	CLEAN(".connected");
470 	CLEAN(".load");
471 
472 #undef CLEAN
473 
474 #define CLEAN(x) \
475 	fastcgi_status_copy_procname(b, host, NULL); \
476 	buffer_append_string_len(b, CONST_STR_LEN(x)); \
477 	status_counter_set(srv, CONST_BUF_LEN(b), 0);
478 
479 	CLEAN(".load");
480 
481 #undef CLEAN
482 
483 	return 0;
484 }
485 
handler_ctx_init(void)486 static handler_ctx * handler_ctx_init(void) {
487 	handler_ctx * hctx;
488 
489 	hctx = calloc(1, sizeof(*hctx));
490 	assert(hctx);
491 
492 	hctx->fde_ndx = -1;
493 
494 	hctx->response_header = buffer_init();
495 
496 	hctx->request_id = 0;
497 	hctx->state = FCGI_STATE_INIT;
498 	hctx->proc = NULL;
499 
500 	hctx->fd = -1;
501 
502 	hctx->reconnects = 0;
503 	hctx->send_content_body = 1;
504 
505 	hctx->rb = chunkqueue_init();
506 	hctx->wb = chunkqueue_init();
507 
508 	return hctx;
509 }
510 
handler_ctx_free(server * srv,handler_ctx * hctx)511 static void handler_ctx_free(server *srv, handler_ctx *hctx) {
512 	if (hctx->host) {
513 		fcgi_host_reset(srv, hctx);
514 	}
515 
516 	buffer_free(hctx->response_header);
517 
518 	chunkqueue_free(hctx->rb);
519 	chunkqueue_free(hctx->wb);
520 
521 	free(hctx);
522 }
523 
fastcgi_process_init(void)524 static fcgi_proc *fastcgi_process_init(void) {
525 	fcgi_proc *f;
526 
527 	f = calloc(1, sizeof(*f));
528 	f->unixsocket = buffer_init();
529 	f->connection_name = buffer_init();
530 
531 	f->prev = NULL;
532 	f->next = NULL;
533 
534 	return f;
535 }
536 
fastcgi_process_free(fcgi_proc * f)537 static void fastcgi_process_free(fcgi_proc *f) {
538 	if (!f) return;
539 
540 	fastcgi_process_free(f->next);
541 
542 	buffer_free(f->unixsocket);
543 	buffer_free(f->connection_name);
544 
545 	free(f);
546 }
547 
fastcgi_host_init(void)548 static fcgi_extension_host *fastcgi_host_init(void) {
549 	fcgi_extension_host *f;
550 
551 	f = calloc(1, sizeof(*f));
552 
553 	f->id = buffer_init();
554 	f->host = buffer_init();
555 	f->unixsocket = buffer_init();
556 	f->docroot = buffer_init();
557 	f->bin_path = buffer_init();
558 	f->bin_env = array_init();
559 	f->bin_env_copy = array_init();
560 	f->strip_request_uri = buffer_init();
561 
562 	return f;
563 }
564 
fastcgi_host_free(fcgi_extension_host * h)565 static void fastcgi_host_free(fcgi_extension_host *h) {
566 	if (!h) return;
567 
568 	buffer_free(h->id);
569 	buffer_free(h->host);
570 	buffer_free(h->unixsocket);
571 	buffer_free(h->docroot);
572 	buffer_free(h->bin_path);
573 	buffer_free(h->strip_request_uri);
574 	array_free(h->bin_env);
575 	array_free(h->bin_env_copy);
576 
577 	fastcgi_process_free(h->first);
578 	fastcgi_process_free(h->unused_procs);
579 
580 	free(h);
581 
582 }
583 
fastcgi_extensions_init(void)584 static fcgi_exts *fastcgi_extensions_init(void) {
585 	fcgi_exts *f;
586 
587 	f = calloc(1, sizeof(*f));
588 
589 	return f;
590 }
591 
fastcgi_extensions_free(fcgi_exts * f)592 static void fastcgi_extensions_free(fcgi_exts *f) {
593 	size_t i;
594 
595 	if (!f) return;
596 
597 	for (i = 0; i < f->used; i++) {
598 		fcgi_extension *fe;
599 		size_t j;
600 
601 		fe = f->exts[i];
602 
603 		for (j = 0; j < fe->used; j++) {
604 			fcgi_extension_host *h;
605 
606 			h = fe->hosts[j];
607 
608 			fastcgi_host_free(h);
609 		}
610 
611 		buffer_free(fe->key);
612 		free(fe->hosts);
613 
614 		free(fe);
615 	}
616 
617 	free(f->exts);
618 
619 	free(f);
620 }
621 
fastcgi_extension_insert(fcgi_exts * ext,buffer * key,fcgi_extension_host * fh)622 static int fastcgi_extension_insert(fcgi_exts *ext, buffer *key, fcgi_extension_host *fh) {
623 	fcgi_extension *fe;
624 	size_t i;
625 
626 	/* there is something */
627 
628 	for (i = 0; i < ext->used; i++) {
629 		if (buffer_is_equal(key, ext->exts[i]->key)) {
630 			break;
631 		}
632 	}
633 
634 	if (i == ext->used) {
635 		/* filextension is new */
636 		fe = calloc(1, sizeof(*fe));
637 		assert(fe);
638 		fe->key = buffer_init();
639 		fe->last_used_ndx = -1;
640 		buffer_copy_string_buffer(fe->key, key);
641 
642 		/* */
643 
644 		if (ext->size == 0) {
645 			ext->size = 8;
646 			ext->exts = malloc(ext->size * sizeof(*(ext->exts)));
647 			assert(ext->exts);
648 		} else if (ext->used == ext->size) {
649 			ext->size += 8;
650 			ext->exts = realloc(ext->exts, ext->size * sizeof(*(ext->exts)));
651 			assert(ext->exts);
652 		}
653 		ext->exts[ext->used++] = fe;
654 	} else {
655 		fe = ext->exts[i];
656 	}
657 
658 	if (fe->size == 0) {
659 		fe->size = 4;
660 		fe->hosts = malloc(fe->size * sizeof(*(fe->hosts)));
661 		assert(fe->hosts);
662 	} else if (fe->size == fe->used) {
663 		fe->size += 4;
664 		fe->hosts = realloc(fe->hosts, fe->size * sizeof(*(fe->hosts)));
665 		assert(fe->hosts);
666 	}
667 
668 	fe->hosts[fe->used++] = fh;
669 
670 	return 0;
671 
672 }
673 
INIT_FUNC(mod_fastcgi_init)674 INIT_FUNC(mod_fastcgi_init) {
675 	plugin_data *p;
676 
677 	p = calloc(1, sizeof(*p));
678 
679 	p->fcgi_env = buffer_init();
680 
681 	p->path = buffer_init();
682 	p->parse_response = buffer_init();
683 
684 	p->statuskey = buffer_init();
685 
686 	return p;
687 }
688 
689 
FREE_FUNC(mod_fastcgi_free)690 FREE_FUNC(mod_fastcgi_free) {
691 	plugin_data *p = p_d;
692 
693 	UNUSED(srv);
694 
695 	buffer_free(p->fcgi_env);
696 	buffer_free(p->path);
697 	buffer_free(p->parse_response);
698 	buffer_free(p->statuskey);
699 
700 	if (p->config_storage) {
701 		size_t i, j, n;
702 		for (i = 0; i < srv->config_context->used; i++) {
703 			plugin_config *s = p->config_storage[i];
704 			fcgi_exts *exts;
705 
706 			if (!s) continue;
707 
708 			exts = s->exts;
709 
710 			for (j = 0; j < exts->used; j++) {
711 				fcgi_extension *ex;
712 
713 				ex = exts->exts[j];
714 
715 				for (n = 0; n < ex->used; n++) {
716 					fcgi_proc *proc;
717 					fcgi_extension_host *host;
718 
719 					host = ex->hosts[n];
720 
721 					for (proc = host->first; proc; proc = proc->next) {
722 						if (proc->pid != 0) {
723 							kill(proc->pid, host->kill_signal);
724 						}
725 
726 						if (proc->is_local &&
727 						    !buffer_is_empty(proc->unixsocket)) {
728 							unlink(proc->unixsocket->ptr);
729 						}
730 					}
731 
732 					for (proc = host->unused_procs; proc; proc = proc->next) {
733 						if (proc->pid != 0) {
734 							kill(proc->pid, host->kill_signal);
735 						}
736 						if (proc->is_local &&
737 						    !buffer_is_empty(proc->unixsocket)) {
738 							unlink(proc->unixsocket->ptr);
739 						}
740 					}
741 				}
742 			}
743 
744 			fastcgi_extensions_free(s->exts);
745 			array_free(s->ext_mapping);
746 
747 			free(s);
748 		}
749 		free(p->config_storage);
750 	}
751 
752 	free(p);
753 
754 	return HANDLER_GO_ON;
755 }
756 
env_add(char_array * env,const char * key,size_t key_len,const char * val,size_t val_len)757 static int env_add(char_array *env, const char *key, size_t key_len, const char *val, size_t val_len) {
758 	char *dst;
759 	size_t i;
760 
761 	if (!key || !val) return -1;
762 
763 	dst = malloc(key_len + val_len + 3);
764 	memcpy(dst, key, key_len);
765 	dst[key_len] = '=';
766 	memcpy(dst + key_len + 1, val, val_len);
767 	dst[key_len + 1 + val_len] = '\0';
768 
769 	for (i = 0; i < env->used; i++) {
770 		if (0 == strncmp(dst, env->ptr[i], key_len + 1)) {
771 			/* don't care about free as we are in a forked child which is going to exec(...) */
772 			/* free(env->ptr[i]); */
773 			env->ptr[i] = dst;
774 			return 0;
775 		}
776 	}
777 
778 	if (env->size == 0) {
779 		env->size = 16;
780 		env->ptr = malloc(env->size * sizeof(*env->ptr));
781 	} else if (env->size == env->used + 1) {
782 		env->size += 16;
783 		env->ptr = realloc(env->ptr, env->size * sizeof(*env->ptr));
784 	}
785 
786 	env->ptr[env->used++] = dst;
787 
788 	return 0;
789 }
790 
parse_binpath(char_array * env,buffer * b)791 static int parse_binpath(char_array *env, buffer *b) {
792 	char *start;
793 	size_t i;
794 	/* search for spaces */
795 
796 	start = b->ptr;
797 	for (i = 0; i < b->used - 1; i++) {
798 		switch(b->ptr[i]) {
799 		case ' ':
800 		case '\t':
801 			/* a WS, stop here and copy the argument */
802 
803 			if (env->size == 0) {
804 				env->size = 16;
805 				env->ptr = malloc(env->size * sizeof(*env->ptr));
806 			} else if (env->size == env->used) {
807 				env->size += 16;
808 				env->ptr = realloc(env->ptr, env->size * sizeof(*env->ptr));
809 			}
810 
811 			b->ptr[i] = '\0';
812 
813 			env->ptr[env->used++] = start;
814 
815 			start = b->ptr + i + 1;
816 			break;
817 		default:
818 			break;
819 		}
820 	}
821 
822 	if (env->size == 0) {
823 		env->size = 16;
824 		env->ptr = malloc(env->size * sizeof(*env->ptr));
825 	} else if (env->size == env->used) { /* we need one extra for the terminating NULL */
826 		env->size += 16;
827 		env->ptr = realloc(env->ptr, env->size * sizeof(*env->ptr));
828 	}
829 
830 	/* the rest */
831 	env->ptr[env->used++] = start;
832 
833 	if (env->size == 0) {
834 		env->size = 16;
835 		env->ptr = malloc(env->size * sizeof(*env->ptr));
836 	} else if (env->size == env->used) { /* we need one extra for the terminating NULL */
837 		env->size += 16;
838 		env->ptr = realloc(env->ptr, env->size * sizeof(*env->ptr));
839 	}
840 
841 	/* terminate */
842 	env->ptr[env->used++] = NULL;
843 
844 	return 0;
845 }
846 
fcgi_spawn_connection(server * srv,plugin_data * p,fcgi_extension_host * host,fcgi_proc * proc)847 static int fcgi_spawn_connection(server *srv,
848 				 plugin_data *p,
849 				 fcgi_extension_host *host,
850 				 fcgi_proc *proc) {
851 	int fcgi_fd;
852 	int socket_type, status;
853 	struct timeval tv = { 0, 100 * 1000 };
854 #ifdef HAVE_SYS_UN_H
855 	struct sockaddr_un fcgi_addr_un;
856 #endif
857 	struct sockaddr_in fcgi_addr_in;
858 	struct sockaddr *fcgi_addr;
859 
860 	socklen_t servlen;
861 
862 #ifndef HAVE_FORK
863 	return -1;
864 #endif
865 
866 	if (p->conf.debug) {
867 		log_error_write(srv, __FILE__, __LINE__, "sdb",
868 				"new proc, socket:", proc->port, proc->unixsocket);
869 	}
870 
871 	if (!buffer_is_empty(proc->unixsocket)) {
872 		memset(&fcgi_addr, 0, sizeof(fcgi_addr));
873 
874 #ifdef HAVE_SYS_UN_H
875 		fcgi_addr_un.sun_family = AF_UNIX;
876 		strcpy(fcgi_addr_un.sun_path, proc->unixsocket->ptr);
877 
878 #ifdef SUN_LEN
879 		servlen = SUN_LEN(&fcgi_addr_un);
880 #else
881 		/* stevens says: */
882 		servlen = proc->unixsocket->used + sizeof(fcgi_addr_un.sun_family);
883 #endif
884 		socket_type = AF_UNIX;
885 		fcgi_addr = (struct sockaddr *) &fcgi_addr_un;
886 
887 		buffer_copy_string_len(proc->connection_name, CONST_STR_LEN("unix:"));
888 		buffer_append_string_buffer(proc->connection_name, proc->unixsocket);
889 
890 #else
891 		log_error_write(srv, __FILE__, __LINE__, "s",
892 				"ERROR: Unix Domain sockets are not supported.");
893 		return -1;
894 #endif
895 	} else {
896 		fcgi_addr_in.sin_family = AF_INET;
897 
898 		if (buffer_is_empty(host->host)) {
899 			fcgi_addr_in.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
900 		} else {
901 			struct hostent *he;
902 
903 			/* set a useful default */
904 			fcgi_addr_in.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
905 
906 
907 			if (NULL == (he = gethostbyname(host->host->ptr))) {
908 				log_error_write(srv, __FILE__, __LINE__,
909 						"sdb", "gethostbyname failed: ",
910 						h_errno, host->host);
911 				return -1;
912 			}
913 
914 			if (he->h_addrtype != AF_INET) {
915 				log_error_write(srv, __FILE__, __LINE__, "sd", "addr-type != AF_INET: ", he->h_addrtype);
916 				return -1;
917 			}
918 
919 			if (he->h_length != sizeof(struct in_addr)) {
920 				log_error_write(srv, __FILE__, __LINE__, "sd", "addr-length != sizeof(in_addr): ", he->h_length);
921 				return -1;
922 			}
923 
924 			memcpy(&(fcgi_addr_in.sin_addr.s_addr), he->h_addr_list[0], he->h_length);
925 
926 		}
927 		fcgi_addr_in.sin_port = htons(proc->port);
928 		servlen = sizeof(fcgi_addr_in);
929 
930 		socket_type = AF_INET;
931 		fcgi_addr = (struct sockaddr *) &fcgi_addr_in;
932 
933 		buffer_copy_string_len(proc->connection_name, CONST_STR_LEN("tcp:"));
934 		if (!buffer_is_empty(host->host)) {
935 			buffer_append_string_buffer(proc->connection_name, host->host);
936 		} else {
937 			buffer_append_string_len(proc->connection_name, CONST_STR_LEN("localhost"));
938 		}
939 		buffer_append_string_len(proc->connection_name, CONST_STR_LEN(":"));
940 		buffer_append_long(proc->connection_name, proc->port);
941 	}
942 
943 	if (-1 == (fcgi_fd = socket(socket_type, SOCK_STREAM, 0))) {
944 		log_error_write(srv, __FILE__, __LINE__, "ss",
945 				"failed:", strerror(errno));
946 		return -1;
947 	}
948 
949 	if (-1 == connect(fcgi_fd, fcgi_addr, servlen)) {
950 		/* server is not up, spawn it  */
951 		pid_t child;
952 		int val;
953 
954 		if (errno != ENOENT &&
955 		    !buffer_is_empty(proc->unixsocket)) {
956 			unlink(proc->unixsocket->ptr);
957 		}
958 
959 		close(fcgi_fd);
960 
961 		/* reopen socket */
962 		if (-1 == (fcgi_fd = socket(socket_type, SOCK_STREAM, 0))) {
963 			log_error_write(srv, __FILE__, __LINE__, "ss",
964 				"socket failed:", strerror(errno));
965 			return -1;
966 		}
967 
968 		val = 1;
969 		if (setsockopt(fcgi_fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) < 0) {
970 			log_error_write(srv, __FILE__, __LINE__, "ss",
971 					"socketsockopt failed:", strerror(errno));
972 			return -1;
973 		}
974 
975 		/* create socket */
976 		if (-1 == bind(fcgi_fd, fcgi_addr, servlen)) {
977 			log_error_write(srv, __FILE__, __LINE__, "sbs",
978 				"bind failed for:",
979 				proc->connection_name,
980 				strerror(errno));
981 			return -1;
982 		}
983 
984 		if (-1 == listen(fcgi_fd, 1024)) {
985 			log_error_write(srv, __FILE__, __LINE__, "ss",
986 				"listen failed:", strerror(errno));
987 			return -1;
988 		}
989 
990 #ifdef HAVE_FORK
991 		switch ((child = fork())) {
992 		case 0: {
993 			size_t i = 0;
994 			char *c;
995 			char_array env;
996 			char_array arg;
997 
998 			/* create environment */
999 			env.ptr = NULL;
1000 			env.size = 0;
1001 			env.used = 0;
1002 
1003 			arg.ptr = NULL;
1004 			arg.size = 0;
1005 			arg.used = 0;
1006 
1007 			if(fcgi_fd != FCGI_LISTENSOCK_FILENO) {
1008 				close(FCGI_LISTENSOCK_FILENO);
1009 				dup2(fcgi_fd, FCGI_LISTENSOCK_FILENO);
1010 				close(fcgi_fd);
1011 			}
1012 
1013 			/* we don't need the client socket */
1014 			for (i = 3; i < 256; i++) {
1015 				close(i);
1016 			}
1017 
1018 			/* build clean environment */
1019 			if (host->bin_env_copy->used) {
1020 				for (i = 0; i < host->bin_env_copy->used; i++) {
1021 					data_string *ds = (data_string *)host->bin_env_copy->data[i];
1022 					char *ge;
1023 
1024 					if (NULL != (ge = getenv(ds->value->ptr))) {
1025 						env_add(&env, CONST_BUF_LEN(ds->value), ge, strlen(ge));
1026 					}
1027 				}
1028 			} else {
1029 				for (i = 0; environ[i]; i++) {
1030 					char *eq;
1031 
1032 					if (NULL != (eq = strchr(environ[i], '='))) {
1033 						env_add(&env, environ[i], eq - environ[i], eq+1, strlen(eq+1));
1034 					}
1035 				}
1036 			}
1037 
1038 			/* create environment */
1039 			for (i = 0; i < host->bin_env->used; i++) {
1040 				data_string *ds = (data_string *)host->bin_env->data[i];
1041 
1042 				env_add(&env, CONST_BUF_LEN(ds->key), CONST_BUF_LEN(ds->value));
1043 			}
1044 
1045 			for (i = 0; i < env.used; i++) {
1046 				/* search for PHP_FCGI_CHILDREN */
1047 				if (0 == strncmp(env.ptr[i], "PHP_FCGI_CHILDREN=", sizeof("PHP_FCGI_CHILDREN=") - 1)) break;
1048 			}
1049 
1050 			/* not found, add a default */
1051 			if (i == env.used) {
1052 				env_add(&env, CONST_STR_LEN("PHP_FCGI_CHILDREN"), CONST_STR_LEN("1"));
1053 			}
1054 
1055 			env.ptr[env.used] = NULL;
1056 
1057 			parse_binpath(&arg, host->bin_path);
1058 
1059 			/* chdir into the base of the bin-path,
1060 			 * search for the last / */
1061 			if (NULL != (c = strrchr(arg.ptr[0], '/'))) {
1062 				*c = '\0';
1063 
1064 				/* change to the physical directory */
1065 				if (-1 == chdir(arg.ptr[0])) {
1066 					*c = '/';
1067 					log_error_write(srv, __FILE__, __LINE__, "sss", "chdir failed:", strerror(errno), arg.ptr[0]);
1068 				}
1069 				*c = '/';
1070 			}
1071 
1072 			reset_signals();
1073 
1074 			/* exec the cgi */
1075 			execve(arg.ptr[0], arg.ptr, env.ptr);
1076 
1077 			/* log_error_write(srv, __FILE__, __LINE__, "sbs",
1078 					"execve failed for:", host->bin_path, strerror(errno)); */
1079 
1080 			exit(errno);
1081 
1082 			break;
1083 		}
1084 		case -1:
1085 			/* error */
1086 			break;
1087 		default:
1088 			/* father */
1089 
1090 			/* wait */
1091 			select(0, NULL, NULL, NULL, &tv);
1092 
1093 			switch (waitpid(child, &status, WNOHANG)) {
1094 			case 0:
1095 				/* child still running after timeout, good */
1096 				break;
1097 			case -1:
1098 				/* no PID found ? should never happen */
1099 				log_error_write(srv, __FILE__, __LINE__, "ss",
1100 						"pid not found:", strerror(errno));
1101 				return -1;
1102 			default:
1103 				log_error_write(srv, __FILE__, __LINE__, "sbs",
1104 						"the fastcgi-backend", host->bin_path, "failed to start:");
1105 				/* the child should not terminate at all */
1106 				if (WIFEXITED(status)) {
1107 					log_error_write(srv, __FILE__, __LINE__, "sdb",
1108 							"child exited with status",
1109 							WEXITSTATUS(status), host->bin_path);
1110 					log_error_write(srv, __FILE__, __LINE__, "s",
1111 							"If you're trying to run your app as a FastCGI backend, make sure you're using the FastCGI-enabled version.\n"
1112 							"If this is PHP on Gentoo, add 'fastcgi' to the USE flags.");
1113 				} else if (WIFSIGNALED(status)) {
1114 					log_error_write(srv, __FILE__, __LINE__, "sd",
1115 							"terminated by signal:",
1116 							WTERMSIG(status));
1117 
1118 					if (WTERMSIG(status) == 11) {
1119 						log_error_write(srv, __FILE__, __LINE__, "s",
1120 								"to be exact: it segfaulted, crashed, died, ... you get the idea." );
1121 						log_error_write(srv, __FILE__, __LINE__, "s",
1122 								"If this is PHP, try removing the bytecode caches for now and try again.");
1123 					}
1124 				} else {
1125 					log_error_write(srv, __FILE__, __LINE__, "sd",
1126 							"child died somehow:",
1127 							status);
1128 				}
1129 				return -1;
1130 			}
1131 
1132 			/* register process */
1133 			proc->pid = child;
1134 			proc->is_local = 1;
1135 
1136 			break;
1137 		}
1138 #endif
1139 	} else {
1140 		proc->is_local = 0;
1141 		proc->pid = 0;
1142 
1143 		if (p->conf.debug) {
1144 			log_error_write(srv, __FILE__, __LINE__, "sb",
1145 					"(debug) socket is already used; won't spawn:",
1146 					proc->connection_name);
1147 		}
1148 	}
1149 
1150 	proc->state = PROC_STATE_RUNNING;
1151 	host->active_procs++;
1152 
1153 	close(fcgi_fd);
1154 
1155 	return 0;
1156 }
1157 
1158 
SETDEFAULTS_FUNC(mod_fastcgi_set_defaults)1159 SETDEFAULTS_FUNC(mod_fastcgi_set_defaults) {
1160 	plugin_data *p = p_d;
1161 	data_unset *du;
1162 	size_t i = 0;
1163 	buffer *fcgi_mode = buffer_init();
1164 
1165 	config_values_t cv[] = {
1166 		{ "fastcgi.server",              NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION },       /* 0 */
1167 		{ "fastcgi.debug",               NULL, T_CONFIG_INT  , T_CONFIG_SCOPE_CONNECTION },       /* 1 */
1168 		{ "fastcgi.map-extensions",      NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION },       /* 2 */
1169 		{ NULL,                          NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
1170 	};
1171 
1172 	p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *));
1173 
1174 	for (i = 0; i < srv->config_context->used; i++) {
1175 		plugin_config *s;
1176 		array *ca;
1177 
1178 		s = malloc(sizeof(plugin_config));
1179 		s->exts          = fastcgi_extensions_init();
1180 		s->debug         = 0;
1181 		s->ext_mapping   = array_init();
1182 
1183 		cv[0].destination = s->exts;
1184 		cv[1].destination = &(s->debug);
1185 		cv[2].destination = s->ext_mapping;
1186 
1187 		p->config_storage[i] = s;
1188 		ca = ((data_config *)srv->config_context->data[i])->value;
1189 
1190 		if (0 != config_insert_values_global(srv, ca, cv)) {
1191 			return HANDLER_ERROR;
1192 		}
1193 
1194 		/*
1195 		 * <key> = ( ... )
1196 		 */
1197 
1198 		if (NULL != (du = array_get_element(ca, "fastcgi.server"))) {
1199 			size_t j;
1200 			data_array *da = (data_array *)du;
1201 
1202 			if (du->type != TYPE_ARRAY) {
1203 				log_error_write(srv, __FILE__, __LINE__, "sss",
1204 						"unexpected type for key: ", "fastcgi.server", "array of strings");
1205 
1206 				return HANDLER_ERROR;
1207 			}
1208 
1209 
1210 			/*
1211 			 * fastcgi.server = ( "<ext>" => ( ... ),
1212 			 *                    "<ext>" => ( ... ) )
1213 			 */
1214 
1215 			for (j = 0; j < da->value->used; j++) {
1216 				size_t n;
1217 				data_array *da_ext = (data_array *)da->value->data[j];
1218 
1219 				if (da->value->data[j]->type != TYPE_ARRAY) {
1220 					log_error_write(srv, __FILE__, __LINE__, "sssbs",
1221 							"unexpected type for key: ", "fastcgi.server",
1222 							"[", da->value->data[j]->key, "](string)");
1223 
1224 					return HANDLER_ERROR;
1225 				}
1226 
1227 				/*
1228 				 * da_ext->key == name of the extension
1229 				 */
1230 
1231 				/*
1232 				 * fastcgi.server = ( "<ext>" =>
1233 				 *                     ( "<host>" => ( ... ),
1234 				 *                       "<host>" => ( ... )
1235 				 *                     ),
1236 				 *                    "<ext>" => ... )
1237 				 */
1238 
1239 				for (n = 0; n < da_ext->value->used; n++) {
1240 					data_array *da_host = (data_array *)da_ext->value->data[n];
1241 
1242 					fcgi_extension_host *host;
1243 
1244 					config_values_t fcv[] = {
1245 						{ "host",              NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },       /* 0 */
1246 						{ "docroot",           NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },       /* 1 */
1247 						{ "mode",              NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },       /* 2 */
1248 						{ "socket",            NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },       /* 3 */
1249 						{ "bin-path",          NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },       /* 4 */
1250 
1251 						{ "check-local",       NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION },      /* 5 */
1252 						{ "port",              NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION },        /* 6 */
1253 						{ "max-procs",         NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION },        /* 7 */
1254 						{ "disable-time",      NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION },        /* 8 */
1255 
1256 						{ "bin-environment",   NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION },        /* 9 */
1257 						{ "bin-copy-environment", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION },     /* 10 */
1258 
1259 						{ "broken-scriptfilename", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION },  /* 11 */
1260 						{ "allow-x-send-file",  NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION },     /* 12 */
1261 						{ "strip-request-uri",  NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },      /* 13 */
1262 						{ "kill-signal",        NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION },       /* 14 */
1263 						{ "fix-root-scriptname",   NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION },  /* 15 */
1264 
1265 						{ NULL,                NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
1266 					};
1267 
1268 					if (da_host->type != TYPE_ARRAY) {
1269 						log_error_write(srv, __FILE__, __LINE__, "ssSBS",
1270 								"unexpected type for key:",
1271 								"fastcgi.server",
1272 								"[", da_host->key, "](string)");
1273 
1274 						return HANDLER_ERROR;
1275 					}
1276 
1277 					host = fastcgi_host_init();
1278 
1279 					buffer_copy_string_buffer(host->id, da_host->key);
1280 
1281 					host->check_local  = 1;
1282 					host->max_procs    = 4;
1283 					host->mode = FCGI_RESPONDER;
1284 					host->disable_time = 1;
1285 					host->break_scriptfilename_for_php = 0;
1286 					host->allow_xsendfile = 0; /* handle X-LIGHTTPD-send-file */
1287 					host->kill_signal = SIGTERM;
1288 					host->fix_root_path_name = 0;
1289 
1290 					fcv[0].destination = host->host;
1291 					fcv[1].destination = host->docroot;
1292 					fcv[2].destination = fcgi_mode;
1293 					fcv[3].destination = host->unixsocket;
1294 					fcv[4].destination = host->bin_path;
1295 
1296 					fcv[5].destination = &(host->check_local);
1297 					fcv[6].destination = &(host->port);
1298 					fcv[7].destination = &(host->max_procs);
1299 					fcv[8].destination = &(host->disable_time);
1300 
1301 					fcv[9].destination = host->bin_env;
1302 					fcv[10].destination = host->bin_env_copy;
1303 					fcv[11].destination = &(host->break_scriptfilename_for_php);
1304 					fcv[12].destination = &(host->allow_xsendfile);
1305 					fcv[13].destination = host->strip_request_uri;
1306 					fcv[14].destination = &(host->kill_signal);
1307 					fcv[15].destination = &(host->fix_root_path_name);
1308 
1309 					if (0 != config_insert_values_internal(srv, da_host->value, fcv)) {
1310 						return HANDLER_ERROR;
1311 					}
1312 
1313 					if ((!buffer_is_empty(host->host) || host->port) &&
1314 					    !buffer_is_empty(host->unixsocket)) {
1315 						log_error_write(srv, __FILE__, __LINE__, "sbsbsbs",
1316 								"either host/port or socket have to be set in:",
1317 								da->key, "= (",
1318 								da_ext->key, " => (",
1319 								da_host->key, " ( ...");
1320 
1321 						return HANDLER_ERROR;
1322 					}
1323 
1324 					if (!buffer_is_empty(host->unixsocket)) {
1325 						/* unix domain socket */
1326 						struct sockaddr_un un;
1327 
1328 						if (host->unixsocket->used > sizeof(un.sun_path) - 2) {
1329 							log_error_write(srv, __FILE__, __LINE__, "sbsbsbs",
1330 									"unixsocket is too long in:",
1331 									da->key, "= (",
1332 									da_ext->key, " => (",
1333 									da_host->key, " ( ...");
1334 
1335 							return HANDLER_ERROR;
1336 						}
1337 					} else {
1338 						/* tcp/ip */
1339 
1340 						if (buffer_is_empty(host->host) &&
1341 						    buffer_is_empty(host->bin_path)) {
1342 							log_error_write(srv, __FILE__, __LINE__, "sbsbsbs",
1343 									"host or binpath have to be set in:",
1344 									da->key, "= (",
1345 									da_ext->key, " => (",
1346 									da_host->key, " ( ...");
1347 
1348 							return HANDLER_ERROR;
1349 						} else if (host->port == 0) {
1350 							log_error_write(srv, __FILE__, __LINE__, "sbsbsbs",
1351 									"port has to be set in:",
1352 									da->key, "= (",
1353 									da_ext->key, " => (",
1354 									da_host->key, " ( ...");
1355 
1356 							return HANDLER_ERROR;
1357 						}
1358 					}
1359 
1360 					if (!buffer_is_empty(host->bin_path)) {
1361 						/* a local socket + self spawning */
1362 						size_t pno;
1363 
1364 						if (s->debug) {
1365 							log_error_write(srv, __FILE__, __LINE__, "ssbsdsbsd",
1366 									"--- fastcgi spawning local",
1367 									"\n\tproc:", host->bin_path,
1368 									"\n\tport:", host->port,
1369 									"\n\tsocket", host->unixsocket,
1370 									"\n\tmax-procs:", host->max_procs);
1371 						}
1372 
1373 						for (pno = 0; pno < host->max_procs; pno++) {
1374 							fcgi_proc *proc;
1375 
1376 							proc = fastcgi_process_init();
1377 							proc->id = host->num_procs++;
1378 							host->max_id++;
1379 
1380 							if (buffer_is_empty(host->unixsocket)) {
1381 								proc->port = host->port + pno;
1382 							} else {
1383 								buffer_copy_string_buffer(proc->unixsocket, host->unixsocket);
1384 								buffer_append_string_len(proc->unixsocket, CONST_STR_LEN("-"));
1385 								buffer_append_long(proc->unixsocket, pno);
1386 							}
1387 
1388 							if (s->debug) {
1389 								log_error_write(srv, __FILE__, __LINE__, "ssdsbsdsd",
1390 										"--- fastcgi spawning",
1391 										"\n\tport:", host->port,
1392 										"\n\tsocket", host->unixsocket,
1393 										"\n\tcurrent:", pno, "/", host->max_procs);
1394 							}
1395 
1396 							if (fcgi_spawn_connection(srv, p, host, proc)) {
1397 								log_error_write(srv, __FILE__, __LINE__, "s",
1398 										"[ERROR]: spawning fcgi failed.");
1399 								return HANDLER_ERROR;
1400 							}
1401 
1402 							fastcgi_status_init(srv, p->statuskey, host, proc);
1403 
1404 							proc->next = host->first;
1405 							if (host->first) 	host->first->prev = proc;
1406 
1407 							host->first = proc;
1408 						}
1409 					} else {
1410 						fcgi_proc *proc;
1411 
1412 						proc = fastcgi_process_init();
1413 						proc->id = host->num_procs++;
1414 						host->max_id++;
1415 						host->active_procs++;
1416 						proc->state = PROC_STATE_RUNNING;
1417 
1418 						if (buffer_is_empty(host->unixsocket)) {
1419 							proc->port = host->port;
1420 						} else {
1421 							buffer_copy_string_buffer(proc->unixsocket, host->unixsocket);
1422 						}
1423 
1424 						fastcgi_status_init(srv, p->statuskey, host, proc);
1425 
1426 						host->first = proc;
1427 
1428 						host->max_procs = 1;
1429 					}
1430 
1431 					if (!buffer_is_empty(fcgi_mode)) {
1432 						if (strcmp(fcgi_mode->ptr, "responder") == 0) {
1433 							host->mode = FCGI_RESPONDER;
1434 						} else if (strcmp(fcgi_mode->ptr, "authorizer") == 0) {
1435 							host->mode = FCGI_AUTHORIZER;
1436 							if (buffer_is_empty(host->docroot)) {
1437 								log_error_write(srv, __FILE__, __LINE__, "s",
1438 										"ERROR: docroot is required for authorizer mode.");
1439 								return HANDLER_ERROR;
1440 							}
1441 						} else {
1442 							log_error_write(srv, __FILE__, __LINE__, "sbs",
1443 									"WARNING: unknown fastcgi mode:",
1444 									fcgi_mode, "(ignored, mode set to responder)");
1445 						}
1446 					}
1447 
1448 					/* if extension already exists, take it */
1449 					fastcgi_extension_insert(s->exts, da_ext->key, host);
1450 				}
1451 			}
1452 		}
1453 	}
1454 
1455 	buffer_free(fcgi_mode);
1456 
1457 	return HANDLER_GO_ON;
1458 }
1459 
fcgi_set_state(server * srv,handler_ctx * hctx,fcgi_connection_state_t state)1460 static int fcgi_set_state(server *srv, handler_ctx *hctx, fcgi_connection_state_t state) {
1461 	hctx->state = state;
1462 	hctx->state_timestamp = srv->cur_ts;
1463 
1464 	return 0;
1465 }
1466 
1467 
fcgi_connection_close(server * srv,handler_ctx * hctx)1468 static void fcgi_connection_close(server *srv, handler_ctx *hctx) {
1469 	plugin_data *p;
1470 	connection  *con;
1471 
1472 	if (NULL == hctx) return;
1473 
1474 	p    = hctx->plugin_data;
1475 	con  = hctx->remote_conn;
1476 
1477 	if (hctx->fd != -1) {
1478 		fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd);
1479 		fdevent_unregister(srv->ev, hctx->fd);
1480 		close(hctx->fd);
1481 		srv->cur_fds--;
1482 	}
1483 
1484 	if (hctx->host && hctx->proc) {
1485 		if (hctx->got_proc) {
1486 			/* after the connect the process gets a load */
1487 			fcgi_proc_load_dec(srv, hctx);
1488 
1489 			if (p->conf.debug) {
1490 				log_error_write(srv, __FILE__, __LINE__, "ssdsbsd",
1491 						"released proc:",
1492 						"pid:", hctx->proc->pid,
1493 						"socket:", hctx->proc->connection_name,
1494 						"load:", hctx->proc->load);
1495 			}
1496 		}
1497 	}
1498 
1499 
1500 	handler_ctx_free(srv, hctx);
1501 	con->plugin_ctx[p->id] = NULL;
1502 }
1503 
fcgi_reconnect(server * srv,handler_ctx * hctx)1504 static int fcgi_reconnect(server *srv, handler_ctx *hctx) {
1505 	plugin_data *p    = hctx->plugin_data;
1506 
1507 	/* child died
1508 	 *
1509 	 * 1.
1510 	 *
1511 	 * connect was ok, connection was accepted
1512 	 * but the php accept loop checks after the accept if it should die or not.
1513 	 *
1514 	 * if yes we can only detect it at a write()
1515 	 *
1516 	 * next step is resetting this attemp and setup a connection again
1517 	 *
1518 	 * if we have more than 5 reconnects for the same request, die
1519 	 *
1520 	 * 2.
1521 	 *
1522 	 * we have a connection but the child died by some other reason
1523 	 *
1524 	 */
1525 
1526 	if (hctx->fd != -1) {
1527 		fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd);
1528 		fdevent_unregister(srv->ev, hctx->fd);
1529 		close(hctx->fd);
1530 		srv->cur_fds--;
1531 		hctx->fd = -1;
1532 	}
1533 
1534 	fcgi_set_state(srv, hctx, FCGI_STATE_INIT);
1535 
1536 	hctx->request_id = 0;
1537 	hctx->reconnects++;
1538 
1539 	if (p->conf.debug > 2) {
1540 		if (hctx->proc) {
1541 			log_error_write(srv, __FILE__, __LINE__, "sdb",
1542 					"release proc for reconnect:",
1543 					hctx->proc->pid, hctx->proc->connection_name);
1544 		} else {
1545 			log_error_write(srv, __FILE__, __LINE__, "sb",
1546 					"release proc for reconnect:",
1547 					hctx->host->unixsocket);
1548 		}
1549 	}
1550 
1551 	if (hctx->proc && hctx->got_proc) {
1552 		fcgi_proc_load_dec(srv, hctx);
1553 	}
1554 
1555 	/* perhaps another host gives us more luck */
1556 	fcgi_host_reset(srv, hctx);
1557 
1558 	return 0;
1559 }
1560 
1561 
fcgi_connection_reset(server * srv,connection * con,void * p_d)1562 static handler_t fcgi_connection_reset(server *srv, connection *con, void *p_d) {
1563 	plugin_data *p = p_d;
1564 
1565 	fcgi_connection_close(srv, con->plugin_ctx[p->id]);
1566 
1567 	return HANDLER_GO_ON;
1568 }
1569 
1570 
fcgi_env_add(buffer * env,const char * key,size_t key_len,const char * val,size_t val_len)1571 static int fcgi_env_add(buffer *env, const char *key, size_t key_len, const char *val, size_t val_len) {
1572 	size_t len;
1573 
1574 	if (!key || !val) return -1;
1575 
1576 	len = key_len + val_len;
1577 
1578 	len += key_len > 127 ? 4 : 1;
1579 	len += val_len > 127 ? 4 : 1;
1580 
1581 	if (env->used + len >= FCGI_MAX_LENGTH) {
1582 		/**
1583 		 * we can't append more headers, ignore it
1584 		 */
1585 		return -1;
1586 	}
1587 
1588 	/**
1589 	 * field length can be 31bit max
1590 	 *
1591 	 * HINT: this can't happen as FCGI_MAX_LENGTH is only 16bit
1592 	 */
1593 	if (key_len > 0x7fffffff) key_len = 0x7fffffff;
1594 	if (val_len > 0x7fffffff) val_len = 0x7fffffff;
1595 
1596 	buffer_prepare_append(env, len);
1597 
1598 	if (key_len > 127) {
1599 		env->ptr[env->used++] = ((key_len >> 24) & 0xff) | 0x80;
1600 		env->ptr[env->used++] = (key_len >> 16) & 0xff;
1601 		env->ptr[env->used++] = (key_len >> 8) & 0xff;
1602 		env->ptr[env->used++] = (key_len >> 0) & 0xff;
1603 	} else {
1604 		env->ptr[env->used++] = (key_len >> 0) & 0xff;
1605 	}
1606 
1607 	if (val_len > 127) {
1608 		env->ptr[env->used++] = ((val_len >> 24) & 0xff) | 0x80;
1609 		env->ptr[env->used++] = (val_len >> 16) & 0xff;
1610 		env->ptr[env->used++] = (val_len >> 8) & 0xff;
1611 		env->ptr[env->used++] = (val_len >> 0) & 0xff;
1612 	} else {
1613 		env->ptr[env->used++] = (val_len >> 0) & 0xff;
1614 	}
1615 
1616 	memcpy(env->ptr + env->used, key, key_len);
1617 	env->used += key_len;
1618 	memcpy(env->ptr + env->used, val, val_len);
1619 	env->used += val_len;
1620 
1621 	return 0;
1622 }
1623 
fcgi_header(FCGI_Header * header,unsigned char type,size_t request_id,int contentLength,unsigned char paddingLength)1624 static int fcgi_header(FCGI_Header * header, unsigned char type, size_t request_id, int contentLength, unsigned char paddingLength) {
1625 	assert(contentLength <= FCGI_MAX_LENGTH);
1626 
1627 	header->version = FCGI_VERSION_1;
1628 	header->type = type;
1629 	header->requestIdB0 = request_id & 0xff;
1630 	header->requestIdB1 = (request_id >> 8) & 0xff;
1631 	header->contentLengthB0 = contentLength & 0xff;
1632 	header->contentLengthB1 = (contentLength >> 8) & 0xff;
1633 	header->paddingLength = paddingLength;
1634 	header->reserved = 0;
1635 
1636 	return 0;
1637 }
1638 
1639 typedef enum {
1640 	CONNECTION_OK,
1641 	CONNECTION_DELAYED, /* retry after event, take same host */
1642 	CONNECTION_OVERLOADED, /* disable for 1 second, take another backend */
1643 	CONNECTION_DEAD /* disable for 60 seconds, take another backend */
1644 } connection_result_t;
1645 
fcgi_establish_connection(server * srv,handler_ctx * hctx)1646 static connection_result_t fcgi_establish_connection(server *srv, handler_ctx *hctx) {
1647 	struct sockaddr *fcgi_addr;
1648 	struct sockaddr_in fcgi_addr_in;
1649 #ifdef HAVE_SYS_UN_H
1650 	struct sockaddr_un fcgi_addr_un;
1651 #endif
1652 	socklen_t servlen;
1653 
1654 	fcgi_extension_host *host = hctx->host;
1655 	fcgi_proc *proc   = hctx->proc;
1656 	int fcgi_fd       = hctx->fd;
1657 
1658 	memset(&fcgi_addr, 0, sizeof(fcgi_addr));
1659 
1660 	if (!buffer_is_empty(proc->unixsocket)) {
1661 #ifdef HAVE_SYS_UN_H
1662 		/* use the unix domain socket */
1663 		fcgi_addr_un.sun_family = AF_UNIX;
1664 		strcpy(fcgi_addr_un.sun_path, proc->unixsocket->ptr);
1665 #ifdef SUN_LEN
1666 		servlen = SUN_LEN(&fcgi_addr_un);
1667 #else
1668 		/* stevens says: */
1669 		servlen = proc->unixsocket->used + sizeof(fcgi_addr_un.sun_family);
1670 #endif
1671 		fcgi_addr = (struct sockaddr *) &fcgi_addr_un;
1672 
1673 		if (buffer_is_empty(proc->connection_name)) {
1674 			/* on remote spawing we have to set the connection-name now */
1675 			buffer_copy_string_len(proc->connection_name, CONST_STR_LEN("unix:"));
1676 			buffer_append_string_buffer(proc->connection_name, proc->unixsocket);
1677 		}
1678 #else
1679 		return CONNECTION_DEAD;
1680 #endif
1681 	} else {
1682 		fcgi_addr_in.sin_family = AF_INET;
1683 		if (!buffer_is_empty(host->host)) {
1684 			if (0 == inet_aton(host->host->ptr, &(fcgi_addr_in.sin_addr))) {
1685 				log_error_write(srv, __FILE__, __LINE__, "sbs",
1686 						"converting IP address failed for", host->host,
1687 						"\nBe sure to specify an IP address here");
1688 
1689 				return CONNECTION_DEAD;
1690 			}
1691 		} else {
1692 			fcgi_addr_in.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
1693 		}
1694 		fcgi_addr_in.sin_port = htons(proc->port);
1695 		servlen = sizeof(fcgi_addr_in);
1696 
1697 		fcgi_addr = (struct sockaddr *) &fcgi_addr_in;
1698 
1699 		if (buffer_is_empty(proc->connection_name)) {
1700 			/* on remote spawing we have to set the connection-name now */
1701 			buffer_copy_string_len(proc->connection_name, CONST_STR_LEN("tcp:"));
1702 			if (!buffer_is_empty(host->host)) {
1703 				buffer_append_string_buffer(proc->connection_name, host->host);
1704 			} else {
1705 				buffer_append_string_len(proc->connection_name, CONST_STR_LEN("localhost"));
1706 			}
1707 			buffer_append_string_len(proc->connection_name, CONST_STR_LEN(":"));
1708 			buffer_append_long(proc->connection_name, proc->port);
1709 		}
1710 	}
1711 
1712 	if (-1 == connect(fcgi_fd, fcgi_addr, servlen)) {
1713 		if (errno == EINPROGRESS ||
1714 		    errno == EALREADY ||
1715 		    errno == EINTR) {
1716 			if (hctx->conf.debug > 2) {
1717 				log_error_write(srv, __FILE__, __LINE__, "sb",
1718 					"connect delayed; will continue later:", proc->connection_name);
1719 			}
1720 
1721 			return CONNECTION_DELAYED;
1722 		} else if (errno == EAGAIN) {
1723 			if (hctx->conf.debug) {
1724 				log_error_write(srv, __FILE__, __LINE__, "sbsd",
1725 					"This means that you have more incoming requests than your FastCGI backend can handle in parallel."
1726 					"It might help to spawn more FastCGI backends or PHP children; if not, decrease server.max-connections."
1727 					"The load for this FastCGI backend", proc->connection_name, "is", proc->load);
1728 			}
1729 
1730 			return CONNECTION_OVERLOADED;
1731 		} else {
1732 			log_error_write(srv, __FILE__, __LINE__, "sssb",
1733 					"connect failed:",
1734 					strerror(errno), "on",
1735 					proc->connection_name);
1736 
1737 			return CONNECTION_DEAD;
1738 		}
1739 	}
1740 
1741 	hctx->reconnects = 0;
1742 	if (hctx->conf.debug > 1) {
1743 		log_error_write(srv, __FILE__, __LINE__, "sd",
1744 				"connect succeeded: ", fcgi_fd);
1745 	}
1746 
1747 	return CONNECTION_OK;
1748 }
1749 
fcgi_env_add_request_headers(server * srv,connection * con,plugin_data * p)1750 static int fcgi_env_add_request_headers(server *srv, connection *con, plugin_data *p) {
1751 	size_t i;
1752 
1753 	for (i = 0; i < con->request.headers->used; i++) {
1754 		data_string *ds;
1755 
1756 		ds = (data_string *)con->request.headers->data[i];
1757 
1758 		if (ds->value->used && ds->key->used) {
1759 			size_t j;
1760 			buffer_reset(srv->tmp_buf);
1761 
1762 			if (0 != strcasecmp(ds->key->ptr, "CONTENT-TYPE")) {
1763 				buffer_copy_string_len(srv->tmp_buf, CONST_STR_LEN("HTTP_"));
1764 				srv->tmp_buf->used--;
1765 			}
1766 
1767 			buffer_prepare_append(srv->tmp_buf, ds->key->used + 2);
1768 			for (j = 0; j < ds->key->used - 1; j++) {
1769 				char c = '_';
1770 				if (light_isalpha(ds->key->ptr[j])) {
1771 					/* upper-case */
1772 					c = ds->key->ptr[j] & ~32;
1773 				} else if (light_isdigit(ds->key->ptr[j])) {
1774 					/* copy */
1775 					c = ds->key->ptr[j];
1776 				}
1777 				srv->tmp_buf->ptr[srv->tmp_buf->used++] = c;
1778 			}
1779 			srv->tmp_buf->ptr[srv->tmp_buf->used++] = '\0';
1780 
1781 			FCGI_ENV_ADD_CHECK(fcgi_env_add(p->fcgi_env, CONST_BUF_LEN(srv->tmp_buf), CONST_BUF_LEN(ds->value)),con);
1782 		}
1783 	}
1784 
1785 	for (i = 0; i < con->environment->used; i++) {
1786 		data_string *ds;
1787 
1788 		ds = (data_string *)con->environment->data[i];
1789 
1790 		if (ds->value->used && ds->key->used) {
1791 			size_t j;
1792 			buffer_reset(srv->tmp_buf);
1793 
1794 			buffer_prepare_append(srv->tmp_buf, ds->key->used + 2);
1795 			for (j = 0; j < ds->key->used - 1; j++) {
1796 				char c = '_';
1797 				if (light_isalpha(ds->key->ptr[j])) {
1798 					/* upper-case */
1799 					c = ds->key->ptr[j] & ~32;
1800 				} else if (light_isdigit(ds->key->ptr[j])) {
1801 					/* copy */
1802 					c = ds->key->ptr[j];
1803 				}
1804 				srv->tmp_buf->ptr[srv->tmp_buf->used++] = c;
1805 			}
1806 			srv->tmp_buf->ptr[srv->tmp_buf->used++] = '\0';
1807 
1808 			FCGI_ENV_ADD_CHECK(fcgi_env_add(p->fcgi_env, CONST_BUF_LEN(srv->tmp_buf), CONST_BUF_LEN(ds->value)), con);
1809 		}
1810 	}
1811 
1812 	return 0;
1813 }
1814 
1815 
fcgi_create_env(server * srv,handler_ctx * hctx,size_t request_id)1816 static int fcgi_create_env(server *srv, handler_ctx *hctx, size_t request_id) {
1817 	FCGI_BeginRequestRecord beginRecord;
1818 	FCGI_Header header;
1819 	buffer *b;
1820 
1821 	char buf[32];
1822 	const char *s;
1823 #ifdef HAVE_IPV6
1824 	char b2[INET6_ADDRSTRLEN + 1];
1825 #endif
1826 
1827 	plugin_data *p    = hctx->plugin_data;
1828 	fcgi_extension_host *host= hctx->host;
1829 
1830 	connection *con   = hctx->remote_conn;
1831 	server_socket *srv_sock = con->srv_socket;
1832 
1833 	sock_addr our_addr;
1834 	socklen_t our_addr_len;
1835 
1836 	/* send FCGI_BEGIN_REQUEST */
1837 
1838 	fcgi_header(&(beginRecord.header), FCGI_BEGIN_REQUEST, request_id, sizeof(beginRecord.body), 0);
1839 	beginRecord.body.roleB0 = host->mode;
1840 	beginRecord.body.roleB1 = 0;
1841 	beginRecord.body.flags = 0;
1842 	memset(beginRecord.body.reserved, 0, sizeof(beginRecord.body.reserved));
1843 
1844 	b = chunkqueue_get_append_buffer(hctx->wb);
1845 
1846 	buffer_copy_memory(b, (const char *)&beginRecord, sizeof(beginRecord));
1847 
1848 	/* send FCGI_PARAMS */
1849 	buffer_prepare_copy(p->fcgi_env, 1024);
1850 
1851 
1852 	if (buffer_is_empty(con->conf.server_tag)) {
1853 		FCGI_ENV_ADD_CHECK(fcgi_env_add(p->fcgi_env, CONST_STR_LEN("SERVER_SOFTWARE"), CONST_STR_LEN(PACKAGE_DESC)),con)
1854 	} else {
1855 		FCGI_ENV_ADD_CHECK(fcgi_env_add(p->fcgi_env, CONST_STR_LEN("SERVER_SOFTWARE"), CONST_BUF_LEN(con->conf.server_tag)),con)
1856 	}
1857 
1858 	if (con->server_name->used) {
1859 		size_t len = con->server_name->used - 1;
1860 
1861 		if (con->server_name->ptr[0] == '[') {
1862 			const char *colon = strstr(con->server_name->ptr, "]:");
1863 			if (colon) len = (colon + 1) - con->server_name->ptr;
1864 		} else {
1865 			const char *colon = strchr(con->server_name->ptr, ':');
1866 			if (colon) len = colon - con->server_name->ptr;
1867 		}
1868 
1869 		FCGI_ENV_ADD_CHECK(fcgi_env_add(p->fcgi_env, CONST_STR_LEN("SERVER_NAME"), con->server_name->ptr, len),con)
1870 	} else {
1871 #ifdef HAVE_IPV6
1872 		s = inet_ntop(srv_sock->addr.plain.sa_family,
1873 			      srv_sock->addr.plain.sa_family == AF_INET6 ?
1874 			      (const void *) &(srv_sock->addr.ipv6.sin6_addr) :
1875 			      (const void *) &(srv_sock->addr.ipv4.sin_addr),
1876 			      b2, sizeof(b2)-1);
1877 #else
1878 		s = inet_ntoa(srv_sock->addr.ipv4.sin_addr);
1879 #endif
1880 		FCGI_ENV_ADD_CHECK(fcgi_env_add(p->fcgi_env, CONST_STR_LEN("SERVER_NAME"), s, strlen(s)),con)
1881 	}
1882 
1883 	FCGI_ENV_ADD_CHECK(fcgi_env_add(p->fcgi_env, CONST_STR_LEN("GATEWAY_INTERFACE"), CONST_STR_LEN("CGI/1.1")),con)
1884 
1885 	LI_ltostr(buf,
1886 #ifdef HAVE_IPV6
1887 	       ntohs(srv_sock->addr.plain.sa_family ? srv_sock->addr.ipv6.sin6_port : srv_sock->addr.ipv4.sin_port)
1888 #else
1889 	       ntohs(srv_sock->addr.ipv4.sin_port)
1890 #endif
1891 	       );
1892 
1893 	FCGI_ENV_ADD_CHECK(fcgi_env_add(p->fcgi_env, CONST_STR_LEN("SERVER_PORT"), buf, strlen(buf)),con)
1894 
1895 	/* get the server-side of the connection to the client */
1896 	our_addr_len = sizeof(our_addr);
1897 
1898 	if (-1 == getsockname(con->fd, &(our_addr.plain), &our_addr_len)) {
1899 		s = inet_ntop_cache_get_ip(srv, &(srv_sock->addr));
1900 	} else {
1901 		s = inet_ntop_cache_get_ip(srv, &(our_addr));
1902 	}
1903 	FCGI_ENV_ADD_CHECK(fcgi_env_add(p->fcgi_env, CONST_STR_LEN("SERVER_ADDR"), s, strlen(s)),con)
1904 
1905 	LI_ltostr(buf,
1906 #ifdef HAVE_IPV6
1907 	       ntohs(con->dst_addr.plain.sa_family ? con->dst_addr.ipv6.sin6_port : con->dst_addr.ipv4.sin_port)
1908 #else
1909 	       ntohs(con->dst_addr.ipv4.sin_port)
1910 #endif
1911 	       );
1912 
1913 	FCGI_ENV_ADD_CHECK(fcgi_env_add(p->fcgi_env, CONST_STR_LEN("REMOTE_PORT"), buf, strlen(buf)),con)
1914 
1915 	s = inet_ntop_cache_get_ip(srv, &(con->dst_addr));
1916 	FCGI_ENV_ADD_CHECK(fcgi_env_add(p->fcgi_env, CONST_STR_LEN("REMOTE_ADDR"), s, strlen(s)),con)
1917 
1918 	if (!buffer_is_empty(con->authed_user)) {
1919 		FCGI_ENV_ADD_CHECK(fcgi_env_add(p->fcgi_env, CONST_STR_LEN("REMOTE_USER"), CONST_BUF_LEN(con->authed_user)),con)
1920 	}
1921 
1922 	if (con->request.content_length > 0 && host->mode != FCGI_AUTHORIZER) {
1923 		/* CGI-SPEC 6.1.2 and FastCGI spec 6.3 */
1924 
1925 		/* request.content_length < SSIZE_MAX, see request.c */
1926 		LI_ltostr(buf, con->request.content_length);
1927 		FCGI_ENV_ADD_CHECK(fcgi_env_add(p->fcgi_env, CONST_STR_LEN("CONTENT_LENGTH"), buf, strlen(buf)),con)
1928 	}
1929 
1930 	if (host->mode != FCGI_AUTHORIZER) {
1931 		/*
1932 		 * SCRIPT_NAME, PATH_INFO and PATH_TRANSLATED according to
1933 		 * http://cgi-spec.golux.com/draft-coar-cgi-v11-03-clean.html
1934 		 * (6.1.14, 6.1.6, 6.1.7)
1935 		 * For AUTHORIZER mode these headers should be omitted.
1936 		 */
1937 
1938 		FCGI_ENV_ADD_CHECK(fcgi_env_add(p->fcgi_env, CONST_STR_LEN("SCRIPT_NAME"), CONST_BUF_LEN(con->uri.path)),con)
1939 
1940 		if (!buffer_is_empty(con->request.pathinfo)) {
1941 			FCGI_ENV_ADD_CHECK(fcgi_env_add(p->fcgi_env, CONST_STR_LEN("PATH_INFO"), CONST_BUF_LEN(con->request.pathinfo)),con)
1942 
1943 			/* PATH_TRANSLATED is only defined if PATH_INFO is set */
1944 
1945 			if (!buffer_is_empty(host->docroot)) {
1946 				buffer_copy_string_buffer(p->path, host->docroot);
1947 			} else {
1948 				buffer_copy_string_buffer(p->path, con->physical.basedir);
1949 			}
1950 			buffer_append_string_buffer(p->path, con->request.pathinfo);
1951 			FCGI_ENV_ADD_CHECK(fcgi_env_add(p->fcgi_env, CONST_STR_LEN("PATH_TRANSLATED"), CONST_BUF_LEN(p->path)),con)
1952 		} else {
1953 			FCGI_ENV_ADD_CHECK(fcgi_env_add(p->fcgi_env, CONST_STR_LEN("PATH_INFO"), CONST_STR_LEN("")),con)
1954 		}
1955 	}
1956 
1957 	/*
1958 	 * SCRIPT_FILENAME and DOCUMENT_ROOT for php. The PHP manual
1959 	 * http://www.php.net/manual/en/reserved.variables.php
1960 	 * treatment of PATH_TRANSLATED is different from the one of CGI specs.
1961 	 * TODO: this code should be checked against cgi.fix_pathinfo php
1962 	 * parameter.
1963 	 */
1964 
1965 	if (!buffer_is_empty(host->docroot)) {
1966 		/*
1967 		 * rewrite SCRIPT_FILENAME
1968 		 *
1969 		 */
1970 
1971 		buffer_copy_string_buffer(p->path, host->docroot);
1972 		buffer_append_string_buffer(p->path, con->uri.path);
1973 
1974 		FCGI_ENV_ADD_CHECK(fcgi_env_add(p->fcgi_env, CONST_STR_LEN("SCRIPT_FILENAME"), CONST_BUF_LEN(p->path)),con)
1975 		FCGI_ENV_ADD_CHECK(fcgi_env_add(p->fcgi_env, CONST_STR_LEN("DOCUMENT_ROOT"), CONST_BUF_LEN(host->docroot)),con)
1976 	} else {
1977 		buffer_copy_string_buffer(p->path, con->physical.path);
1978 
1979 		/* cgi.fix_pathinfo need a broken SCRIPT_FILENAME to find out what PATH_INFO is itself
1980 		 *
1981 		 * see src/sapi/cgi_main.c, init_request_info()
1982 		 */
1983 		if (host->break_scriptfilename_for_php) {
1984 			buffer_append_string_buffer(p->path, con->request.pathinfo);
1985 		}
1986 
1987 		FCGI_ENV_ADD_CHECK(fcgi_env_add(p->fcgi_env, CONST_STR_LEN("SCRIPT_FILENAME"), CONST_BUF_LEN(p->path)),con)
1988 		FCGI_ENV_ADD_CHECK(fcgi_env_add(p->fcgi_env, CONST_STR_LEN("DOCUMENT_ROOT"), CONST_BUF_LEN(con->physical.basedir)),con)
1989 	}
1990 
1991 	if (host->strip_request_uri->used > 1) {
1992 		/* we need at least one char to strip off */
1993 		/**
1994 		 * /app1/index/list
1995 		 *
1996 		 * stripping /app1 or /app1/ should lead to
1997 		 *
1998 		 * /index/list
1999 		 *
2000 		 */
2001 		if ('/' != host->strip_request_uri->ptr[host->strip_request_uri->used - 2]) {
2002 			/* fix the user-input to have / as last char */
2003 			buffer_append_string_len(host->strip_request_uri, CONST_STR_LEN("/"));
2004 		}
2005 
2006 		if (con->request.orig_uri->used >= host->strip_request_uri->used &&
2007 		    0 == strncmp(con->request.orig_uri->ptr, host->strip_request_uri->ptr, host->strip_request_uri->used - 1)) {
2008 			/* the left is the same */
2009 
2010 			fcgi_env_add(p->fcgi_env, CONST_STR_LEN("REQUEST_URI"),
2011 					con->request.orig_uri->ptr + (host->strip_request_uri->used - 2),
2012 					con->request.orig_uri->used - (host->strip_request_uri->used - 2) - 1);
2013 		} else {
2014 			FCGI_ENV_ADD_CHECK(fcgi_env_add(p->fcgi_env, CONST_STR_LEN("REQUEST_URI"), CONST_BUF_LEN(con->request.orig_uri)),con)
2015 		}
2016 	} else {
2017 		FCGI_ENV_ADD_CHECK(fcgi_env_add(p->fcgi_env, CONST_STR_LEN("REQUEST_URI"), CONST_BUF_LEN(con->request.orig_uri)),con)
2018 	}
2019 	if (!buffer_is_equal(con->request.uri, con->request.orig_uri)) {
2020 		FCGI_ENV_ADD_CHECK(fcgi_env_add(p->fcgi_env, CONST_STR_LEN("REDIRECT_URI"), CONST_BUF_LEN(con->request.uri)),con)
2021 	}
2022 	if (!buffer_is_empty(con->uri.query)) {
2023 		FCGI_ENV_ADD_CHECK(fcgi_env_add(p->fcgi_env, CONST_STR_LEN("QUERY_STRING"), CONST_BUF_LEN(con->uri.query)),con)
2024 	} else {
2025 		FCGI_ENV_ADD_CHECK(fcgi_env_add(p->fcgi_env, CONST_STR_LEN("QUERY_STRING"), CONST_STR_LEN("")),con)
2026 	}
2027 
2028 	s = get_http_method_name(con->request.http_method);
2029 	FCGI_ENV_ADD_CHECK(fcgi_env_add(p->fcgi_env, CONST_STR_LEN("REQUEST_METHOD"), s, strlen(s)),con)
2030 	FCGI_ENV_ADD_CHECK(fcgi_env_add(p->fcgi_env, CONST_STR_LEN("REDIRECT_STATUS"), CONST_STR_LEN("200")),con) /* if php is compiled with --force-redirect */
2031 	s = get_http_version_name(con->request.http_version);
2032 	FCGI_ENV_ADD_CHECK(fcgi_env_add(p->fcgi_env, CONST_STR_LEN("SERVER_PROTOCOL"), s, strlen(s)),con)
2033 
2034     if (srv_sock->is_ssl || srv_sock->is_proxy_ssl) {
2035 		FCGI_ENV_ADD_CHECK(fcgi_env_add(p->fcgi_env, CONST_STR_LEN("HTTPS"), CONST_STR_LEN("on")),con)
2036 	}
2037 
2038 	FCGI_ENV_ADD_CHECK(fcgi_env_add_request_headers(srv, con, p), con);
2039 
2040 	fcgi_header(&(header), FCGI_PARAMS, request_id, p->fcgi_env->used, 0);
2041 	buffer_append_memory(b, (const char *)&header, sizeof(header));
2042 	buffer_append_memory(b, (const char *)p->fcgi_env->ptr, p->fcgi_env->used);
2043 
2044 	fcgi_header(&(header), FCGI_PARAMS, request_id, 0, 0);
2045 	buffer_append_memory(b, (const char *)&header, sizeof(header));
2046 
2047 	b->used++; /* add virtual \0 */
2048 	hctx->wb->bytes_in += b->used - 1;
2049 
2050 	if (con->request.content_length) {
2051 		chunkqueue *req_cq = con->request_content_queue;
2052 		chunk *req_c;
2053 		off_t offset;
2054 
2055 		/* something to send ? */
2056 		for (offset = 0, req_c = req_cq->first; offset != req_cq->bytes_in; ) {
2057 			off_t weWant = req_cq->bytes_in - offset > FCGI_MAX_LENGTH ? FCGI_MAX_LENGTH : req_cq->bytes_in - offset;
2058 			off_t written = 0;
2059 			off_t weHave = 0;
2060 
2061 			/* we announce toWrite octets
2062 			 * now take all the request_content chunks that we need to fill this request
2063 			 * */
2064 
2065 			b = chunkqueue_get_append_buffer(hctx->wb);
2066 			fcgi_header(&(header), FCGI_STDIN, request_id, weWant, 0);
2067 			buffer_copy_memory(b, (const char *)&header, sizeof(header));
2068 			hctx->wb->bytes_in += sizeof(header);
2069 
2070 			if (p->conf.debug > 10) {
2071 				log_error_write(srv, __FILE__, __LINE__, "soso", "tosend:", offset, "/", req_cq->bytes_in);
2072 			}
2073 
2074 			for (written = 0; written != weWant; ) {
2075 				if (p->conf.debug > 10) {
2076 					log_error_write(srv, __FILE__, __LINE__, "soso", "chunk:", written, "/", weWant);
2077 				}
2078 
2079 				switch (req_c->type) {
2080 				case FILE_CHUNK:
2081 					weHave = req_c->file.length - req_c->offset;
2082 
2083 					if (weHave > weWant - written) weHave = weWant - written;
2084 
2085 					if (p->conf.debug > 10) {
2086 						log_error_write(srv, __FILE__, __LINE__, "soSosOsb",
2087 							"sending", weHave, "bytes from (",
2088 							req_c->offset, "/", req_c->file.length, ")",
2089 							req_c->file.name);
2090 					}
2091 
2092 					assert(weHave != 0);
2093 
2094 					chunkqueue_append_file(hctx->wb, req_c->file.name, req_c->offset, weHave);
2095 
2096 					req_c->offset += weHave;
2097 					req_cq->bytes_out += weHave;
2098 					written += weHave;
2099 
2100 					hctx->wb->bytes_in += weHave;
2101 
2102 					/* steal the tempfile
2103 					 *
2104 					 * This is tricky:
2105 					 * - we reference the tempfile from the request-content-queue several times
2106 					 *   if the req_c is larger than FCGI_MAX_LENGTH
2107 					 * - we can't simply cleanup the request-content-queue as soon as possible
2108 					 *   as it would remove the tempfiles
2109 					 * - the idea is to 'steal' the tempfiles and attach the is_temp flag to the last
2110 					 *   referencing chunk of the fastcgi-write-queue
2111 					 *
2112 					 *  */
2113 
2114 					if (req_c->offset == req_c->file.length) {
2115 						chunk *c;
2116 
2117 						if (p->conf.debug > 10) {
2118 							log_error_write(srv, __FILE__, __LINE__, "s", "next chunk");
2119 						}
2120 						c = hctx->wb->last;
2121 
2122 						assert(c->type == FILE_CHUNK);
2123 						assert(req_c->file.is_temp == 1);
2124 
2125 						c->file.is_temp = 1;
2126 						req_c->file.is_temp = 0;
2127 
2128 						chunkqueue_remove_finished_chunks(req_cq);
2129 
2130 						req_c = req_cq->first;
2131 					}
2132 
2133 					break;
2134 				case MEM_CHUNK:
2135 					/* append to the buffer */
2136 					weHave = req_c->mem->used - 1 - req_c->offset;
2137 
2138 					if (weHave > weWant - written) weHave = weWant - written;
2139 
2140 					buffer_append_memory(b, req_c->mem->ptr + req_c->offset, weHave);
2141 
2142 					req_c->offset += weHave;
2143 					req_cq->bytes_out += weHave;
2144 					written += weHave;
2145 
2146 					hctx->wb->bytes_in += weHave;
2147 
2148 					if (req_c->offset == (off_t) req_c->mem->used - 1) {
2149 						chunkqueue_remove_finished_chunks(req_cq);
2150 
2151 						req_c = req_cq->first;
2152 					}
2153 
2154 					break;
2155 				default:
2156 					break;
2157 				}
2158 			}
2159 
2160 			b->used++; /* add virtual \0 */
2161 			offset += weWant;
2162 		}
2163 	}
2164 
2165 	b = chunkqueue_get_append_buffer(hctx->wb);
2166 	/* terminate STDIN */
2167 	fcgi_header(&(header), FCGI_STDIN, request_id, 0, 0);
2168 	buffer_copy_memory(b, (const char *)&header, sizeof(header));
2169 	b->used++; /* add virtual \0 */
2170 
2171 	hctx->wb->bytes_in += sizeof(header);
2172 
2173 	return 0;
2174 }
2175 
fcgi_response_parse(server * srv,connection * con,plugin_data * p,buffer * in)2176 static int fcgi_response_parse(server *srv, connection *con, plugin_data *p, buffer *in) {
2177 	char *s, *ns;
2178 
2179 	handler_ctx *hctx = con->plugin_ctx[p->id];
2180 	fcgi_extension_host *host= hctx->host;
2181 	int have_sendfile2 = 0;
2182 	off_t sendfile2_content_length = 0;
2183 
2184 	UNUSED(srv);
2185 
2186 	buffer_copy_string_buffer(p->parse_response, in);
2187 
2188 	/* search for \n */
2189 	for (s = p->parse_response->ptr; NULL != (ns = strchr(s, '\n')); s = ns + 1) {
2190 		char *key, *value;
2191 		int key_len;
2192 		data_string *ds = NULL;
2193 
2194 		/* a good day. Someone has read the specs and is sending a \r\n to us */
2195 
2196 		if (ns > p->parse_response->ptr &&
2197 		    *(ns-1) == '\r') {
2198 			*(ns-1) = '\0';
2199 		}
2200 
2201 		ns[0] = '\0';
2202 
2203 		key = s;
2204 		if (NULL == (value = strchr(s, ':'))) {
2205 			/* we expect: "<key>: <value>\n" */
2206 			continue;
2207 		}
2208 
2209 		key_len = value - key;
2210 
2211 		value++;
2212 		/* strip WS */
2213 		while (*value == ' ' || *value == '\t') value++;
2214 
2215 		if (host->mode != FCGI_AUTHORIZER ||
2216 		    !(con->http_status == 0 ||
2217 		      con->http_status == 200)) {
2218 			/* authorizers shouldn't affect the response headers sent back to the client */
2219 
2220 			/* don't forward Status: */
2221 			if (0 != strncasecmp(key, "Status", key_len)) {
2222 				if (NULL == (ds = (data_string *)array_get_unused_element(con->response.headers, TYPE_STRING))) {
2223 					ds = data_response_init();
2224 				}
2225 				buffer_copy_string_len(ds->key, key, key_len);
2226 				buffer_copy_string(ds->value, value);
2227 
2228 				array_insert_unique(con->response.headers, (data_unset *)ds);
2229 			}
2230 		}
2231 
2232 		switch(key_len) {
2233 		case 4:
2234 			if (0 == strncasecmp(key, "Date", key_len)) {
2235 				con->parsed_response |= HTTP_DATE;
2236 			}
2237 			break;
2238 		case 6:
2239 			if (0 == strncasecmp(key, "Status", key_len)) {
2240 				con->http_status = strtol(value, NULL, 10);
2241 				con->parsed_response |= HTTP_STATUS;
2242 			}
2243 			break;
2244 		case 8:
2245 			if (0 == strncasecmp(key, "Location", key_len)) {
2246 				con->parsed_response |= HTTP_LOCATION;
2247 			}
2248 			break;
2249 		case 10:
2250 			if (0 == strncasecmp(key, "Connection", key_len)) {
2251 				con->response.keep_alive = (0 == strcasecmp(value, "Keep-Alive")) ? 1 : 0;
2252 				con->parsed_response |= HTTP_CONNECTION;
2253 			}
2254 			break;
2255 		case 11:
2256 			if (host->allow_xsendfile && 0 == strncasecmp(key, "X-Sendfile2", key_len)&& hctx->send_content_body) {
2257 				char *pos = value;
2258 				have_sendfile2 = 1;
2259 
2260 				while (*pos) {
2261 					char *filename, *range;
2262 					stat_cache_entry *sce;
2263 					off_t begin_range, end_range, range_len;
2264 
2265 					while (' ' == *pos) pos++;
2266 					if (!*pos) break;
2267 
2268 					filename = pos;
2269 					if (NULL == (range = strchr(pos, ' '))) {
2270 						/* missing range */
2271 						if (p->conf.debug) {
2272 							log_error_write(srv, __FILE__, __LINE__, "ss", "Couldn't find range after filename:", filename);
2273 						}
2274 						return 1;
2275 					}
2276 					buffer_copy_string_len(srv->tmp_buf, filename, range - filename);
2277 
2278 					/* find end of range */
2279 					for (pos = ++range; *pos && *pos != ' ' && *pos != ','; pos++) ;
2280 
2281 					buffer_urldecode_path(srv->tmp_buf);
2282 					if (HANDLER_ERROR == stat_cache_get_entry(srv, con, srv->tmp_buf, &sce)) {
2283 						if (p->conf.debug) {
2284 							log_error_write(srv, __FILE__, __LINE__, "sb",
2285 								"send-file error: couldn't get stat_cache entry for X-Sendfile2:",
2286 								srv->tmp_buf);
2287 						}
2288 						return 1;
2289 					} else if (!S_ISREG(sce->st.st_mode)) {
2290 						if (p->conf.debug) {
2291 							log_error_write(srv, __FILE__, __LINE__, "sb",
2292 								"send-file error: wrong filetype for X-Sendfile2:",
2293 								srv->tmp_buf);
2294 						}
2295 						return 1;
2296 					}
2297 					/* found the file */
2298 
2299 					/* parse range */
2300 					begin_range = 0; end_range = sce->st.st_size - 1;
2301 					{
2302 						char *rpos = NULL;
2303 						errno = 0;
2304 						begin_range = strtoll(range, &rpos, 10);
2305 						if (errno != 0 || begin_range < 0 || rpos == range) goto range_failed;
2306 						if ('-' != *rpos++) goto range_failed;
2307 						if (rpos != pos) {
2308 							range = rpos;
2309 							end_range = strtoll(range, &rpos, 10);
2310 							if (errno != 0 || end_range < 0 || rpos == range) goto range_failed;
2311 						}
2312 						if (rpos != pos) goto range_failed;
2313 
2314 						goto range_success;
2315 
2316 range_failed:
2317 						if (p->conf.debug) {
2318 							log_error_write(srv, __FILE__, __LINE__, "ss", "Couldn't decode range after filename:", filename);
2319 						}
2320 						return 1;
2321 
2322 range_success: ;
2323 					}
2324 
2325 					/* no parameters accepted */
2326 
2327 					while (*pos == ' ') pos++;
2328 					if (*pos != '\0' && *pos != ',') return 1;
2329 
2330 					range_len = end_range - begin_range + 1;
2331 					if (range_len < 0) return 1;
2332 					if (range_len != 0) {
2333 						http_chunk_append_file(srv, con, srv->tmp_buf, begin_range, range_len);
2334 					}
2335 					sendfile2_content_length += range_len;
2336 
2337 					if (*pos == ',') pos++;
2338 				}
2339 			}
2340 			break;
2341 		case 14:
2342 			if (0 == strncasecmp(key, "Content-Length", key_len)) {
2343 				con->response.content_length = strtol(value, NULL, 10);
2344 				con->parsed_response |= HTTP_CONTENT_LENGTH;
2345 
2346 				if (con->response.content_length < 0) con->response.content_length = 0;
2347 			}
2348 			break;
2349 		default:
2350 			break;
2351 		}
2352 	}
2353 
2354 	if (have_sendfile2) {
2355 		data_string *dcls;
2356 
2357 		hctx->send_content_body = 0;
2358 		joblist_append(srv, con);
2359 
2360 		/* fix content-length */
2361 		if (NULL == (dcls = (data_string *)array_get_unused_element(con->response.headers, TYPE_STRING))) {
2362 			dcls = data_response_init();
2363 		}
2364 
2365 		buffer_copy_string_len(dcls->key, "Content-Length", sizeof("Content-Length")-1);
2366 		buffer_copy_off_t(dcls->value, sendfile2_content_length);
2367 		dcls = (data_string*) array_replace(con->response.headers, (data_unset *)dcls);
2368 		if (dcls) dcls->free((data_unset*)dcls);
2369 
2370 		con->parsed_response |= HTTP_CONTENT_LENGTH;
2371 		con->response.content_length = sendfile2_content_length;
2372 	}
2373 
2374 	/* CGI/1.1 rev 03 - 7.2.1.2 */
2375 	if ((con->parsed_response & HTTP_LOCATION) &&
2376 	    !(con->parsed_response & HTTP_STATUS)) {
2377 		con->http_status = 302;
2378 	}
2379 
2380 	return 0;
2381 }
2382 
2383 typedef struct {
2384 	buffer  *b;
2385 	size_t   len;
2386 	int      type;
2387 	int      padding;
2388 	size_t   request_id;
2389 } fastcgi_response_packet;
2390 
fastcgi_get_packet(server * srv,handler_ctx * hctx,fastcgi_response_packet * packet)2391 static int fastcgi_get_packet(server *srv, handler_ctx *hctx, fastcgi_response_packet *packet) {
2392 	chunk *	c;
2393 	size_t offset;
2394 	size_t toread;
2395 	FCGI_Header *header;
2396 
2397 	if (!hctx->rb->first) return -1;
2398 
2399 	packet->b = buffer_init();
2400 	packet->len = 0;
2401 	packet->type = 0;
2402 	packet->padding = 0;
2403 	packet->request_id = 0;
2404 
2405 	offset = 0; toread = 8;
2406 	/* get at least the FastCGI header */
2407 	for (c = hctx->rb->first; c; c = c->next) {
2408 		size_t weHave = c->mem->used - c->offset - 1;
2409 
2410 		if (weHave > toread) weHave = toread;
2411 
2412 		if (packet->b->used == 0) {
2413 			buffer_copy_string_len(packet->b, c->mem->ptr + c->offset, weHave);
2414 		} else {
2415 			buffer_append_string_len(packet->b, c->mem->ptr + c->offset, weHave);
2416 		}
2417 		toread -= weHave;
2418 		offset = weHave; /* skip offset bytes in chunk for "real" data */
2419 
2420 		if (0 == toread) break;
2421 	}
2422 
2423 	if ((packet->b->used == 0) ||
2424 	    (packet->b->used - 1 < sizeof(FCGI_Header))) {
2425 		/* no header */
2426 		buffer_free(packet->b);
2427 
2428 		if (hctx->plugin_data->conf.debug) {
2429 			log_error_write(srv, __FILE__, __LINE__, "sdsds", "FastCGI: header too small:", packet->b->used, "bytes <", sizeof(FCGI_Header), "bytes, waiting for more data");
2430 		}
2431 		return -1;
2432 	}
2433 
2434 	/* we have at least a header, now check how much me have to fetch */
2435 	header = (FCGI_Header *)(packet->b->ptr);
2436 
2437 	packet->len = (header->contentLengthB0 | (header->contentLengthB1 << 8)) + header->paddingLength;
2438 	packet->request_id = (header->requestIdB0 | (header->requestIdB1 << 8));
2439 	packet->type = header->type;
2440 	packet->padding = header->paddingLength;
2441 
2442 	/* ->b should only be the content */
2443 	buffer_copy_string_len(packet->b, CONST_STR_LEN("")); /* used == 1 */
2444 
2445 	if (packet->len) {
2446 		/* copy the content */
2447 		for (; c && (packet->b->used < packet->len + 1); c = c->next) {
2448 			size_t weWant = packet->len - (packet->b->used - 1);
2449 			size_t weHave = c->mem->used - c->offset - offset - 1;
2450 
2451 			if (weHave > weWant) weHave = weWant;
2452 
2453 			buffer_append_string_len(packet->b, c->mem->ptr + c->offset + offset, weHave);
2454 
2455 			/* we only skipped the first bytes as they belonged to the fcgi header */
2456 			offset = 0;
2457 		}
2458 
2459 		if (packet->b->used < packet->len + 1) {
2460 			/* we didn't get the full packet */
2461 
2462 			buffer_free(packet->b);
2463 			return -1;
2464 		}
2465 
2466 		packet->b->used -= packet->padding;
2467 		packet->b->ptr[packet->b->used - 1] = '\0';
2468 	}
2469 
2470 	/* tag the chunks as read */
2471 	toread = packet->len + sizeof(FCGI_Header);
2472 	for (c = hctx->rb->first; c && toread; c = c->next) {
2473 		if (c->mem->used - c->offset - 1 <= toread) {
2474 			/* we read this whole buffer, move it to unused */
2475 			toread -= c->mem->used - c->offset - 1;
2476 			c->offset = c->mem->used - 1; /* everthing has been written */
2477 		} else {
2478 			c->offset += toread;
2479 			toread = 0;
2480 		}
2481 	}
2482 
2483 	chunkqueue_remove_finished_chunks(hctx->rb);
2484 
2485 	return 0;
2486 }
2487 
fcgi_demux_response(server * srv,handler_ctx * hctx)2488 static int fcgi_demux_response(server *srv, handler_ctx *hctx) {
2489 	int fin = 0;
2490 	int toread;
2491 	ssize_t r;
2492 
2493 	plugin_data *p    = hctx->plugin_data;
2494 	connection *con   = hctx->remote_conn;
2495 	int fcgi_fd       = hctx->fd;
2496 	fcgi_extension_host *host= hctx->host;
2497 	fcgi_proc *proc   = hctx->proc;
2498 
2499 	/*
2500 	 * check how much we have to read
2501 	 */
2502 	if (ioctl(hctx->fd, FIONREAD, &toread)) {
2503 		if (errno == EAGAIN) return 0;
2504 		log_error_write(srv, __FILE__, __LINE__, "sd",
2505 				"unexpected end-of-file (perhaps the fastcgi process died):",
2506 				fcgi_fd);
2507 		return -1;
2508 	}
2509 
2510 	/* init read-buffer */
2511 
2512 	if (toread > 0) {
2513 		buffer *b;
2514 		chunk *cq_first = hctx->rb->first;
2515 		chunk *cq_last = hctx->rb->last;
2516 
2517 		b = chunkqueue_get_append_buffer(hctx->rb);
2518 		buffer_prepare_copy(b, toread + 1);
2519 
2520 		/* append to read-buffer */
2521 		if (-1 == (r = read(hctx->fd, b->ptr, toread))) {
2522 			if (errno == EAGAIN) {
2523 				/* roll back the last chunk allocation,
2524                                    and continue on next iteration        */
2525 				buffer_free(hctx->rb->last->mem);
2526 				free(hctx->rb->last);
2527 				hctx->rb->first = cq_first;
2528 				hctx->rb->last = cq_last;
2529 				return 0;
2530 			}
2531 			log_error_write(srv, __FILE__, __LINE__, "sds",
2532 					"unexpected end-of-file (perhaps the fastcgi process died):",
2533 					fcgi_fd, strerror(errno));
2534 			return -1;
2535 		}
2536 
2537 		/* this should be catched by the b > 0 above */
2538 		assert(r);
2539 
2540 		b->used = r + 1; /* one extra for the fake \0 */
2541 		b->ptr[b->used - 1] = '\0';
2542 	} else {
2543 		log_error_write(srv, __FILE__, __LINE__, "ssdsb",
2544 				"unexpected end-of-file (perhaps the fastcgi process died):",
2545 				"pid:", proc->pid,
2546 				"socket:", proc->connection_name);
2547 
2548 		return -1;
2549 	}
2550 
2551 	/*
2552 	 * parse the fastcgi packets and forward the content to the write-queue
2553 	 *
2554 	 */
2555 	while (fin == 0) {
2556 		fastcgi_response_packet packet;
2557 
2558 		/* check if we have at least one packet */
2559 		if (0 != fastcgi_get_packet(srv, hctx, &packet)) {
2560 			/* no full packet */
2561 			break;
2562 		}
2563 
2564 		switch(packet.type) {
2565 		case FCGI_STDOUT:
2566 			if (packet.len == 0) break;
2567 
2568 			/* is the header already finished */
2569 			if (0 == con->file_started) {
2570 				char *c;
2571 				size_t blen;
2572 				data_string *ds;
2573 
2574 				/* search for header terminator
2575 				 *
2576 				 * if we start with \r\n check if last packet terminated with \r\n
2577 				 * if we start with \n check if last packet terminated with \n
2578 				 * search for \r\n\r\n
2579 				 * search for \n\n
2580 				 */
2581 
2582 				if (hctx->response_header->used == 0) {
2583 					buffer_copy_string_buffer(hctx->response_header, packet.b);
2584 				} else {
2585 					buffer_append_string_buffer(hctx->response_header, packet.b);
2586 				}
2587 
2588 				if (NULL != (c = buffer_search_string_len(hctx->response_header, CONST_STR_LEN("\r\n\r\n")))) {
2589 					blen = hctx->response_header->used - (c - hctx->response_header->ptr) - 4;
2590 					hctx->response_header->used = (c - hctx->response_header->ptr) + 3;
2591 					c += 4; /* point the the start of the response */
2592 				} else if (NULL != (c = buffer_search_string_len(hctx->response_header, CONST_STR_LEN("\n\n")))) {
2593 					blen = hctx->response_header->used - (c - hctx->response_header->ptr) - 2;
2594 					hctx->response_header->used = c - hctx->response_header->ptr + 2;
2595 					c += 2; /* point the the start of the response */
2596 				} else {
2597 					/* no luck, no header found */
2598 					break;
2599 				}
2600 
2601 				/* parse the response header */
2602 				if (fcgi_response_parse(srv, con, p, hctx->response_header)) {
2603 					con->http_status = 502;
2604 					hctx->send_content_body = 0;
2605 					con->file_started = 1;
2606 					break;
2607 				}
2608 
2609 				con->file_started = 1;
2610 
2611 				if (host->mode == FCGI_AUTHORIZER &&
2612 				    (con->http_status == 0 ||
2613 				     con->http_status == 200)) {
2614 					/* a authorizer with approved the static request, ignore the content here */
2615 					hctx->send_content_body = 0;
2616 				}
2617 
2618 				if (host->allow_xsendfile && hctx->send_content_body &&
2619 				    (NULL != (ds = (data_string *) array_get_element(con->response.headers, "X-LIGHTTPD-send-file"))
2620 					  || NULL != (ds = (data_string *) array_get_element(con->response.headers, "X-Sendfile")))) {
2621 					stat_cache_entry *sce;
2622 
2623 					if (HANDLER_ERROR != stat_cache_get_entry(srv, con, ds->value, &sce)) {
2624 						data_string *dcls;
2625 						if (NULL == (dcls = (data_string *)array_get_unused_element(con->response.headers, TYPE_STRING))) {
2626 							dcls = data_response_init();
2627 						}
2628 						/* found */
2629 						http_chunk_append_file(srv, con, ds->value, 0, sce->st.st_size);
2630 						hctx->send_content_body = 0; /* ignore the content */
2631 						joblist_append(srv, con);
2632 
2633 						buffer_copy_string_len(dcls->key, "Content-Length", sizeof("Content-Length")-1);
2634 						buffer_copy_off_t(dcls->value, sce->st.st_size);
2635 						dcls = (data_string*) array_replace(con->response.headers, (data_unset *)dcls);
2636 						if (dcls) dcls->free((data_unset*)dcls);
2637 
2638 						con->parsed_response |= HTTP_CONTENT_LENGTH;
2639 						con->response.content_length = sce->st.st_size;
2640 					} else {
2641 						log_error_write(srv, __FILE__, __LINE__, "sb",
2642 							"send-file error: couldn't get stat_cache entry for:",
2643 							ds->value);
2644 						con->http_status = 502;
2645 						hctx->send_content_body = 0;
2646 						con->file_started = 1;
2647 						break;
2648 					}
2649 				}
2650 
2651 
2652 				if (hctx->send_content_body && blen > 1) {
2653 					/* enable chunked-transfer-encoding */
2654 					if (con->request.http_version == HTTP_VERSION_1_1 &&
2655 					    !(con->parsed_response & HTTP_CONTENT_LENGTH)) {
2656 						con->response.transfer_encoding = HTTP_TRANSFER_ENCODING_CHUNKED;
2657 					}
2658 
2659 					http_chunk_append_mem(srv, con, c, blen);
2660 					joblist_append(srv, con);
2661 				}
2662 			} else if (hctx->send_content_body && packet.b->used > 1) {
2663 				if (con->request.http_version == HTTP_VERSION_1_1 &&
2664 				    !(con->parsed_response & HTTP_CONTENT_LENGTH)) {
2665 					/* enable chunked-transfer-encoding */
2666 					con->response.transfer_encoding = HTTP_TRANSFER_ENCODING_CHUNKED;
2667 				}
2668 
2669 				http_chunk_append_mem(srv, con, packet.b->ptr, packet.b->used);
2670 				joblist_append(srv, con);
2671 			}
2672 			break;
2673 		case FCGI_STDERR:
2674 			if (packet.len == 0) break;
2675 
2676 			log_error_write(srv, __FILE__, __LINE__, "sb",
2677 					"FastCGI-stderr:", packet.b);
2678 
2679 			break;
2680 		case FCGI_END_REQUEST:
2681 			con->file_finished = 1;
2682 
2683 			if (host->mode != FCGI_AUTHORIZER ||
2684 			    !(con->http_status == 0 ||
2685 			      con->http_status == 200)) {
2686 				/* send chunk-end if necessary */
2687 				http_chunk_append_mem(srv, con, NULL, 0);
2688 				joblist_append(srv, con);
2689 			}
2690 
2691 			fin = 1;
2692 			break;
2693 		default:
2694 			log_error_write(srv, __FILE__, __LINE__, "sd",
2695 					"FastCGI: header.type not handled: ", packet.type);
2696 			break;
2697 		}
2698 		buffer_free(packet.b);
2699 	}
2700 
2701 	return fin;
2702 }
2703 
fcgi_restart_dead_procs(server * srv,plugin_data * p,fcgi_extension_host * host)2704 static int fcgi_restart_dead_procs(server *srv, plugin_data *p, fcgi_extension_host *host) {
2705 	fcgi_proc *proc;
2706 
2707 	for (proc = host->first; proc; proc = proc->next) {
2708 		int status;
2709 
2710 		if (p->conf.debug > 2) {
2711 			log_error_write(srv, __FILE__, __LINE__,  "sbdddd",
2712 					"proc:",
2713 					proc->connection_name,
2714 					proc->state,
2715 					proc->is_local,
2716 					proc->load,
2717 					proc->pid);
2718 		}
2719 
2720 		/*
2721 		 * if the remote side is overloaded, we check back after <n> seconds
2722 		 *
2723 		 */
2724 		switch (proc->state) {
2725 		case PROC_STATE_KILLED:
2726 		case PROC_STATE_UNSET:
2727 			/* this should never happen as long as adaptive spawing is disabled */
2728 			assert(0);
2729 
2730 			break;
2731 		case PROC_STATE_RUNNING:
2732 			break;
2733 		case PROC_STATE_OVERLOADED:
2734 			if (srv->cur_ts <= proc->disabled_until) break;
2735 
2736 			proc->state = PROC_STATE_RUNNING;
2737 			host->active_procs++;
2738 
2739 			log_error_write(srv, __FILE__, __LINE__,  "sbdb",
2740 					"fcgi-server re-enabled:",
2741 					host->host, host->port,
2742 					host->unixsocket);
2743 			break;
2744 		case PROC_STATE_DIED_WAIT_FOR_PID:
2745 			/* non-local procs don't have PIDs to wait for */
2746 			if (!proc->is_local) {
2747 				proc->state = PROC_STATE_DIED;
2748 			} else {
2749 				/* the child should not terminate at all */
2750 
2751 				for ( ;; ) {
2752 					switch(waitpid(proc->pid, &status, WNOHANG)) {
2753 					case 0:
2754 						/* child is still alive */
2755 						if (srv->cur_ts <= proc->disabled_until) break;
2756 
2757 						proc->state = PROC_STATE_RUNNING;
2758 						host->active_procs++;
2759 
2760 						log_error_write(srv, __FILE__, __LINE__,  "sbdb",
2761 							"fcgi-server re-enabled:",
2762 							host->host, host->port,
2763 							host->unixsocket);
2764 						break;
2765 					case -1:
2766 						if (errno == EINTR) continue;
2767 
2768 						log_error_write(srv, __FILE__, __LINE__, "sd",
2769 							"child died somehow, waitpid failed:",
2770 							errno);
2771 						proc->state = PROC_STATE_DIED;
2772 						break;
2773 					default:
2774 						if (WIFEXITED(status)) {
2775 #if 0
2776 							log_error_write(srv, __FILE__, __LINE__, "sdsd",
2777 								"child exited, pid:", proc->pid,
2778 								"status:", WEXITSTATUS(status));
2779 #endif
2780 						} else if (WIFSIGNALED(status)) {
2781 							log_error_write(srv, __FILE__, __LINE__, "sd",
2782 								"child signaled:",
2783 								WTERMSIG(status));
2784 						} else {
2785 							log_error_write(srv, __FILE__, __LINE__, "sd",
2786 								"child died somehow:",
2787 								status);
2788 						}
2789 
2790 						proc->state = PROC_STATE_DIED;
2791 						break;
2792 					}
2793 					break;
2794 				}
2795 			}
2796 
2797 			/* fall through if we have a dead proc now */
2798 			if (proc->state != PROC_STATE_DIED) break;
2799 
2800 		case PROC_STATE_DIED:
2801 			/* local procs get restarted by us,
2802 			 * remote ones hopefully by the admin */
2803 
2804 			if (!buffer_is_empty(host->bin_path)) {
2805 				/* we still have connections bound to this proc,
2806 				 * let them terminate first */
2807 				if (proc->load != 0) break;
2808 
2809 				/* restart the child */
2810 
2811 				if (p->conf.debug) {
2812 					log_error_write(srv, __FILE__, __LINE__, "ssbsdsd",
2813 							"--- fastcgi spawning",
2814 							"\n\tsocket", proc->connection_name,
2815 							"\n\tcurrent:", 1, "/", host->max_procs);
2816 				}
2817 
2818 				if (fcgi_spawn_connection(srv, p, host, proc)) {
2819 					log_error_write(srv, __FILE__, __LINE__, "s",
2820 							"ERROR: spawning fcgi failed.");
2821 					return HANDLER_ERROR;
2822 				}
2823 			} else {
2824 				if (srv->cur_ts <= proc->disabled_until) break;
2825 
2826 				proc->state = PROC_STATE_RUNNING;
2827 				host->active_procs++;
2828 
2829 				log_error_write(srv, __FILE__, __LINE__,  "sb",
2830 						"fcgi-server re-enabled:",
2831 						proc->connection_name);
2832 			}
2833 			break;
2834 		}
2835 	}
2836 
2837 	return 0;
2838 }
2839 
fcgi_write_request(server * srv,handler_ctx * hctx)2840 static handler_t fcgi_write_request(server *srv, handler_ctx *hctx) {
2841 	plugin_data *p    = hctx->plugin_data;
2842 	fcgi_extension_host *host= hctx->host;
2843 	connection *con   = hctx->remote_conn;
2844 	fcgi_proc  *proc;
2845 
2846 	int ret;
2847 
2848 	/* sanity check:
2849 	 *  - host != NULL
2850 	 *  - either:
2851 	 *     - tcp socket (do not check host->host->uses, as it may be not set which means INADDR_LOOPBACK)
2852 	 *     - unix socket
2853 	 */
2854 	if (!host) {
2855 		log_error_write(srv, __FILE__, __LINE__, "s", "fatal error: host = NULL");
2856 		return HANDLER_ERROR;
2857 	}
2858 	if ((!host->port && !host->unixsocket->used)) {
2859 		log_error_write(srv, __FILE__, __LINE__, "s", "fatal error: neither host->port nor host->unixsocket is set");
2860 		return HANDLER_ERROR;
2861 	}
2862 
2863 	/* we can't handle this in the switch as we have to fall through in it */
2864 	if (hctx->state == FCGI_STATE_CONNECT_DELAYED) {
2865 		int socket_error;
2866 		socklen_t socket_error_len = sizeof(socket_error);
2867 
2868 		/* try to finish the connect() */
2869 		if (0 != getsockopt(hctx->fd, SOL_SOCKET, SO_ERROR, &socket_error, &socket_error_len)) {
2870 			log_error_write(srv, __FILE__, __LINE__, "ss",
2871 					"getsockopt failed:", strerror(errno));
2872 
2873 			fcgi_host_disable(srv, hctx);
2874 
2875 			return HANDLER_ERROR;
2876 		}
2877 		if (socket_error != 0) {
2878 			if (!hctx->proc->is_local || p->conf.debug) {
2879 				/* local procs get restarted */
2880 
2881 				log_error_write(srv, __FILE__, __LINE__, "sssb",
2882 						"establishing connection failed:", strerror(socket_error),
2883 						"socket:", hctx->proc->connection_name);
2884 			}
2885 
2886 			fcgi_host_disable(srv, hctx);
2887 			log_error_write(srv, __FILE__, __LINE__, "sdssdsd",
2888 				"backend is overloaded; we'll disable it for", hctx->host->disable_time, "seconds and send the request to another backend instead:",
2889 				"reconnects:", hctx->reconnects,
2890 				"load:", host->load);
2891 
2892 			fastcgi_status_copy_procname(p->statuskey, hctx->host, hctx->proc);
2893 			buffer_append_string_len(p->statuskey, CONST_STR_LEN(".died"));
2894 
2895 			status_counter_inc(srv, CONST_BUF_LEN(p->statuskey));
2896 
2897 			return HANDLER_ERROR;
2898 		}
2899 		/* go on with preparing the request */
2900 		hctx->state = FCGI_STATE_PREPARE_WRITE;
2901 	}
2902 
2903 
2904 	switch(hctx->state) {
2905 	case FCGI_STATE_CONNECT_DELAYED:
2906 		/* should never happen */
2907 		break;
2908 	case FCGI_STATE_INIT:
2909 		/* do we have a running process for this host (max-procs) ? */
2910 		hctx->proc = NULL;
2911 
2912 		for (proc = hctx->host->first;
2913 		     proc && proc->state != PROC_STATE_RUNNING;
2914 		     proc = proc->next);
2915 
2916 		/* all children are dead */
2917 		if (proc == NULL) {
2918 			hctx->fde_ndx = -1;
2919 
2920 			return HANDLER_ERROR;
2921 		}
2922 
2923 		hctx->proc = proc;
2924 
2925 		/* check the other procs if they have a lower load */
2926 		for (proc = proc->next; proc; proc = proc->next) {
2927 			if (proc->state != PROC_STATE_RUNNING) continue;
2928 			if (proc->load < hctx->proc->load) hctx->proc = proc;
2929 		}
2930 
2931 		ret = host->unixsocket->used ? AF_UNIX : AF_INET;
2932 
2933 		if (-1 == (hctx->fd = socket(ret, SOCK_STREAM, 0))) {
2934 			if (errno == EMFILE ||
2935 			    errno == EINTR) {
2936 				log_error_write(srv, __FILE__, __LINE__, "sd",
2937 						"wait for fd at connection:", con->fd);
2938 
2939 				return HANDLER_WAIT_FOR_FD;
2940 			}
2941 
2942 			log_error_write(srv, __FILE__, __LINE__, "ssdd",
2943 					"socket failed:", strerror(errno), srv->cur_fds, srv->max_fds);
2944 			return HANDLER_ERROR;
2945 		}
2946 		hctx->fde_ndx = -1;
2947 
2948 		srv->cur_fds++;
2949 
2950 		fdevent_register(srv->ev, hctx->fd, fcgi_handle_fdevent, hctx);
2951 
2952 		if (-1 == fdevent_fcntl_set(srv->ev, hctx->fd)) {
2953 			log_error_write(srv, __FILE__, __LINE__, "ss",
2954 					"fcntl failed:", strerror(errno));
2955 
2956 			return HANDLER_ERROR;
2957 		}
2958 
2959 		if (hctx->proc->is_local) {
2960 			hctx->pid = hctx->proc->pid;
2961 		}
2962 
2963 		switch (fcgi_establish_connection(srv, hctx)) {
2964 		case CONNECTION_DELAYED:
2965 			/* connection is in progress, wait for an event and call getsockopt() below */
2966 
2967 			fdevent_event_set(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_OUT);
2968 
2969 			fcgi_set_state(srv, hctx, FCGI_STATE_CONNECT_DELAYED);
2970 			return HANDLER_WAIT_FOR_EVENT;
2971 		case CONNECTION_OVERLOADED:
2972 			/* cool down the backend, it is overloaded
2973 			 * -> EAGAIN */
2974 
2975 			if (hctx->host->disable_time) {
2976 				log_error_write(srv, __FILE__, __LINE__, "sdssdsd",
2977 					"backend is overloaded; we'll disable it for", hctx->host->disable_time, "seconds and send the request to another backend instead:",
2978 					"reconnects:", hctx->reconnects,
2979 					"load:", host->load);
2980 
2981 				hctx->proc->disabled_until = srv->cur_ts + hctx->host->disable_time;
2982 				if (hctx->proc->state == PROC_STATE_RUNNING) hctx->host->active_procs--;
2983 				hctx->proc->state = PROC_STATE_OVERLOADED;
2984 			}
2985 
2986 			fastcgi_status_copy_procname(p->statuskey, hctx->host, hctx->proc);
2987 			buffer_append_string_len(p->statuskey, CONST_STR_LEN(".overloaded"));
2988 
2989 			status_counter_inc(srv, CONST_BUF_LEN(p->statuskey));
2990 
2991 			return HANDLER_ERROR;
2992 		case CONNECTION_DEAD:
2993 			/* we got a hard error from the backend like
2994 			 * - ECONNREFUSED for tcp-ip sockets
2995 			 * - ENOENT for unix-domain-sockets
2996 			 *
2997 			 * for check if the host is back in hctx->host->disable_time seconds
2998 			 *  */
2999 
3000 			fcgi_host_disable(srv, hctx);
3001 
3002 			log_error_write(srv, __FILE__, __LINE__, "sdssdsd",
3003 				"backend died; we'll disable it for", hctx->host->disable_time, "seconds and send the request to another backend instead:",
3004 				"reconnects:", hctx->reconnects,
3005 				"load:", host->load);
3006 
3007 			fastcgi_status_copy_procname(p->statuskey, hctx->host, hctx->proc);
3008 			buffer_append_string_len(p->statuskey, CONST_STR_LEN(".died"));
3009 
3010 			status_counter_inc(srv, CONST_BUF_LEN(p->statuskey));
3011 
3012 			return HANDLER_ERROR;
3013 		case CONNECTION_OK:
3014 			/* everything is ok, go on */
3015 
3016 			fcgi_set_state(srv, hctx, FCGI_STATE_PREPARE_WRITE);
3017 
3018 			break;
3019 		}
3020 
3021 	case FCGI_STATE_PREPARE_WRITE:
3022 		/* ok, we have the connection */
3023 
3024 		fcgi_proc_load_inc(srv, hctx);
3025 		hctx->got_proc = 1;
3026 
3027 		status_counter_inc(srv, CONST_STR_LEN("fastcgi.requests"));
3028 
3029 		fastcgi_status_copy_procname(p->statuskey, hctx->host, hctx->proc);
3030 		buffer_append_string_len(p->statuskey, CONST_STR_LEN(".connected"));
3031 
3032 		status_counter_inc(srv, CONST_BUF_LEN(p->statuskey));
3033 
3034 		if (p->conf.debug) {
3035 			log_error_write(srv, __FILE__, __LINE__, "ssdsbsd",
3036 				"got proc:",
3037 				"pid:", hctx->proc->pid,
3038 				"socket:", hctx->proc->connection_name,
3039 				"load:", hctx->proc->load);
3040 		}
3041 
3042 		/* move the proc-list entry down the list */
3043 		if (hctx->request_id == 0) {
3044 			hctx->request_id = 1; /* always use id 1 as we don't use multiplexing */
3045 		} else {
3046 			log_error_write(srv, __FILE__, __LINE__, "sd",
3047 					"fcgi-request is already in use:", hctx->request_id);
3048 		}
3049 
3050 		/* fall through */
3051 		if (-1 == fcgi_create_env(srv, hctx, hctx->request_id)) return HANDLER_ERROR;
3052 		fcgi_set_state(srv, hctx, FCGI_STATE_WRITE);
3053 		/* fall through */
3054 	case FCGI_STATE_WRITE:
3055 		ret = srv->network_backend_write(srv, con, hctx->fd, hctx->wb, MAX_WRITE_LIMIT);
3056 
3057 		chunkqueue_remove_finished_chunks(hctx->wb);
3058 
3059 		if (ret < 0) {
3060 			switch(errno) {
3061 			case EPIPE:
3062 			case ENOTCONN:
3063 			case ECONNRESET:
3064 				/* the connection got dropped after accept()
3065 				 * we don't care about that - if you accept() it, you have to handle it.
3066 				 */
3067 
3068 				log_error_write(srv, __FILE__, __LINE__, "ssosb",
3069 							"connection was dropped after accept() (perhaps the fastcgi process died),",
3070 						"write-offset:", hctx->wb->bytes_out,
3071 						"socket:", hctx->proc->connection_name);
3072 
3073 				return HANDLER_ERROR;
3074 			default:
3075 				log_error_write(srv, __FILE__, __LINE__, "ssd",
3076 						"write failed:", strerror(errno), errno);
3077 
3078 				return HANDLER_ERROR;
3079 			}
3080 		}
3081 
3082 		if (hctx->wb->bytes_out == hctx->wb->bytes_in) {
3083 			/* we don't need the out event anymore */
3084 			fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd);
3085 			fdevent_event_set(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN);
3086 			fcgi_set_state(srv, hctx, FCGI_STATE_READ);
3087 		} else {
3088 			fdevent_event_set(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_OUT);
3089 
3090 			return HANDLER_WAIT_FOR_EVENT;
3091 		}
3092 
3093 		break;
3094 	case FCGI_STATE_READ:
3095 		/* waiting for a response */
3096 		break;
3097 	default:
3098 		log_error_write(srv, __FILE__, __LINE__, "s", "(debug) unknown state");
3099 		return HANDLER_ERROR;
3100 	}
3101 
3102 	return HANDLER_WAIT_FOR_EVENT;
3103 }
3104 
3105 
3106 /* might be called on fdevent after a connect() is delay too
3107  * */
SUBREQUEST_FUNC(mod_fastcgi_handle_subrequest)3108 SUBREQUEST_FUNC(mod_fastcgi_handle_subrequest) {
3109 	plugin_data *p = p_d;
3110 
3111 	handler_ctx *hctx = con->plugin_ctx[p->id];
3112 	fcgi_extension_host *host;
3113 
3114 	if (NULL == hctx) return HANDLER_GO_ON;
3115 
3116 	/* not my job */
3117 	if (con->mode != p->id) return HANDLER_GO_ON;
3118 
3119 	/* we don't have a host yet, choose one
3120 	 * -> this happens in the first round
3121 	 *    and when the host died and we have to select a new one */
3122 	if (hctx->host == NULL) {
3123 		size_t k;
3124 		int ndx, used = -1;
3125 
3126 		/* check if the next server has no load. */
3127 		ndx = hctx->ext->last_used_ndx + 1;
3128 		if(ndx >= (int) hctx->ext->used || ndx < 0) ndx = 0;
3129 		host = hctx->ext->hosts[ndx];
3130 		if (host->load > 0) {
3131 			/* get backend with the least load. */
3132 			for (k = 0, ndx = -1; k < hctx->ext->used; k++) {
3133 				host = hctx->ext->hosts[k];
3134 
3135 				/* we should have at least one proc that can do something */
3136 				if (host->active_procs == 0) continue;
3137 
3138 				if (used == -1 || host->load < used) {
3139 					used = host->load;
3140 
3141 					ndx = k;
3142 				}
3143 			}
3144 		}
3145 
3146 		/* found a server */
3147 		if (ndx == -1) {
3148 			/* all hosts are down */
3149 
3150 			fcgi_connection_close(srv, hctx);
3151 
3152 			con->http_status = 500;
3153 			con->mode = DIRECT;
3154 
3155 			return HANDLER_FINISHED;
3156 		}
3157 
3158 		hctx->ext->last_used_ndx = ndx;
3159 		host = hctx->ext->hosts[ndx];
3160 
3161 		/*
3162 		 * if check-local is disabled, use the uri.path handler
3163 		 *
3164 		 */
3165 
3166 		/* init handler-context */
3167 
3168 		/* we put a connection on this host, move the other new connections to other hosts
3169 		 *
3170 		 * as soon as hctx->host is unassigned, decrease the load again */
3171 		fcgi_host_assign(srv, hctx, host);
3172 		hctx->proc = NULL;
3173 	} else {
3174 		host = hctx->host;
3175 	}
3176 
3177 	/* ok, create the request */
3178 	switch(fcgi_write_request(srv, hctx)) {
3179 	case HANDLER_ERROR:
3180 		host = hctx->host;
3181 
3182 		if (hctx->state == FCGI_STATE_INIT ||
3183 		    hctx->state == FCGI_STATE_CONNECT_DELAYED) {
3184 			fcgi_restart_dead_procs(srv, p, host);
3185 
3186 			/* cleanup this request and let the request handler start this request again */
3187 			if (hctx->reconnects < 5) {
3188 				fcgi_reconnect(srv, hctx);
3189 				joblist_append(srv, con); /* in case we come from the event-handler */
3190 
3191 				return HANDLER_WAIT_FOR_FD;
3192 			} else {
3193 				fcgi_connection_close(srv, hctx);
3194 
3195 				buffer_reset(con->physical.path);
3196 				con->mode = DIRECT;
3197 				con->http_status = 503;
3198 				joblist_append(srv, con); /* in case we come from the event-handler */
3199 
3200 				return HANDLER_FINISHED;
3201 			}
3202 		} else {
3203 			fcgi_connection_close(srv, hctx);
3204 
3205 			buffer_reset(con->physical.path);
3206 			con->mode = DIRECT;
3207 			if (con->http_status != 400) con->http_status = 503;
3208 			joblist_append(srv, con); /* really ? */
3209 
3210 			return HANDLER_FINISHED;
3211 		}
3212 	case HANDLER_WAIT_FOR_EVENT:
3213 		if (con->file_started == 1) {
3214 			return HANDLER_FINISHED;
3215 		} else {
3216 			return HANDLER_WAIT_FOR_EVENT;
3217 		}
3218 	case HANDLER_WAIT_FOR_FD:
3219 		return HANDLER_WAIT_FOR_FD;
3220 	default:
3221 		log_error_write(srv, __FILE__, __LINE__, "s", "subrequest write-req default");
3222 		return HANDLER_ERROR;
3223 	}
3224 }
3225 
fcgi_handle_fdevent(server * srv,void * ctx,int revents)3226 static handler_t fcgi_handle_fdevent(server *srv, void *ctx, int revents) {
3227 	handler_ctx *hctx = ctx;
3228 	connection  *con  = hctx->remote_conn;
3229 	plugin_data *p    = hctx->plugin_data;
3230 
3231 	fcgi_proc *proc   = hctx->proc;
3232 	fcgi_extension_host *host= hctx->host;
3233 
3234 	if ((revents & FDEVENT_IN) &&
3235 	    hctx->state == FCGI_STATE_READ) {
3236 		switch (fcgi_demux_response(srv, hctx)) {
3237 		case 0:
3238 			break;
3239 		case 1:
3240 
3241 			if (host->mode == FCGI_AUTHORIZER &&
3242 		   	    (con->http_status == 200 ||
3243 			     con->http_status == 0)) {
3244 				/*
3245 				 * If we are here in AUTHORIZER mode then a request for authorizer
3246 				 * was processed already, and status 200 has been returned. We need
3247 				 * now to handle authorized request.
3248 				 */
3249 
3250 				buffer_copy_string_buffer(con->physical.doc_root, host->docroot);
3251 				buffer_copy_string_buffer(con->physical.basedir, host->docroot);
3252 
3253 				buffer_copy_string_buffer(con->physical.path, host->docroot);
3254 				buffer_append_string_buffer(con->physical.path, con->uri.path);
3255 				fcgi_connection_close(srv, hctx);
3256 
3257 				con->mode = DIRECT;
3258 				con->http_status = 0;
3259 				con->file_started = 1; /* fcgi_extension won't touch the request afterwards */
3260 			} else {
3261 				/* we are done */
3262 				fcgi_connection_close(srv, hctx);
3263 			}
3264 
3265 			joblist_append(srv, con);
3266 			return HANDLER_FINISHED;
3267 		case -1:
3268 			if (proc->pid && proc->state != PROC_STATE_DIED) {
3269 				int status;
3270 
3271 				/* only fetch the zombie if it is not already done */
3272 
3273 				switch(waitpid(proc->pid, &status, WNOHANG)) {
3274 				case 0:
3275 					/* child is still alive */
3276 					break;
3277 				case -1:
3278 					break;
3279 				default:
3280 					/* the child should not terminate at all */
3281 					if (WIFEXITED(status)) {
3282 						log_error_write(srv, __FILE__, __LINE__, "sdsd",
3283 								"child exited, pid:", proc->pid,
3284 								"status:", WEXITSTATUS(status));
3285 					} else if (WIFSIGNALED(status)) {
3286 						log_error_write(srv, __FILE__, __LINE__, "sd",
3287 								"child signaled:",
3288 								WTERMSIG(status));
3289 					} else {
3290 						log_error_write(srv, __FILE__, __LINE__, "sd",
3291 								"child died somehow:",
3292 								status);
3293 					}
3294 
3295 					if (p->conf.debug) {
3296 						log_error_write(srv, __FILE__, __LINE__, "ssbsdsd",
3297 								"--- fastcgi spawning",
3298 								"\n\tsocket", proc->connection_name,
3299 								"\n\tcurrent:", 1, "/", host->max_procs);
3300 					}
3301 
3302 					if (fcgi_spawn_connection(srv, p, host, proc)) {
3303 						/* respawning failed, retry later */
3304 						proc->state = PROC_STATE_DIED;
3305 
3306 						log_error_write(srv, __FILE__, __LINE__, "s",
3307 								"respawning failed, will retry later");
3308 					}
3309 
3310 					break;
3311 				}
3312 			}
3313 
3314 			if (con->file_started == 0) {
3315 				/* nothing has been sent out yet, try to use another child */
3316 
3317 				if (hctx->wb->bytes_out == 0 &&
3318 				    hctx->reconnects < 5) {
3319 					fcgi_reconnect(srv, hctx);
3320 
3321 					log_error_write(srv, __FILE__, __LINE__, "ssbsBSBs",
3322 						"response not received, request not sent",
3323 						"on socket:", proc->connection_name,
3324 						"for", con->uri.path, "?", con->uri.query, ", reconnecting");
3325 
3326 					return HANDLER_WAIT_FOR_FD;
3327 				}
3328 
3329 				log_error_write(srv, __FILE__, __LINE__, "sosbsBSBs",
3330 						"response not received, request sent:", hctx->wb->bytes_out,
3331 						"on socket:", proc->connection_name,
3332 						"for", con->uri.path, "?", con->uri.query, ", closing connection");
3333 
3334 				fcgi_connection_close(srv, hctx);
3335 
3336 				connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST);
3337 				buffer_reset(con->physical.path);
3338 				con->http_status = 500;
3339 				con->mode = DIRECT;
3340 			} else {
3341 				/* response might have been already started, kill the connection */
3342 				fcgi_connection_close(srv, hctx);
3343 
3344 				log_error_write(srv, __FILE__, __LINE__, "ssbsBSBs",
3345 						"response already sent out, but backend returned error",
3346 						"on socket:", proc->connection_name,
3347 						"for", con->uri.path, "?", con->uri.query, ", terminating connection");
3348 
3349 				connection_set_state(srv, con, CON_STATE_ERROR);
3350 			}
3351 
3352 			/* */
3353 
3354 
3355 			joblist_append(srv, con);
3356 			return HANDLER_FINISHED;
3357 		}
3358 	}
3359 
3360 	if (revents & FDEVENT_OUT) {
3361 		if (hctx->state == FCGI_STATE_CONNECT_DELAYED ||
3362 		    hctx->state == FCGI_STATE_WRITE) {
3363 			/* we are allowed to send something out
3364 			 *
3365 			 * 1. in an unfinished connect() call
3366 			 * 2. in an unfinished write() call (long POST request)
3367 			 */
3368 			return mod_fastcgi_handle_subrequest(srv, con, p);
3369 		} else {
3370 			log_error_write(srv, __FILE__, __LINE__, "sd",
3371 					"got a FDEVENT_OUT and didn't know why:",
3372 					hctx->state);
3373 		}
3374 	}
3375 
3376 	/* perhaps this issue is already handled */
3377 	if (revents & FDEVENT_HUP) {
3378 		if (hctx->state == FCGI_STATE_CONNECT_DELAYED) {
3379 			/* getoptsock will catch this one (right ?)
3380 			 *
3381 			 * if we are in connect we might get an EINPROGRESS
3382 			 * in the first call and an FDEVENT_HUP in the
3383 			 * second round
3384 			 *
3385 			 * FIXME: as it is a bit ugly.
3386 			 *
3387 			 */
3388 			return mod_fastcgi_handle_subrequest(srv, con, p);
3389 		} else if (hctx->state == FCGI_STATE_READ &&
3390 			   hctx->proc->port == 0) {
3391 			/* FIXME:
3392 			 *
3393 			 * ioctl says 8192 bytes to read from PHP and we receive directly a HUP for the socket
3394 			 * even if the FCGI_FIN packet is not received yet
3395 			 */
3396 		} else {
3397 			log_error_write(srv, __FILE__, __LINE__, "sBSbsbsd",
3398 					"error: unexpected close of fastcgi connection for",
3399 					con->uri.path, "?", con->uri.query,
3400 					"(no fastcgi process on socket:", proc->connection_name, "?)",
3401 					hctx->state);
3402 
3403 			connection_set_state(srv, con, CON_STATE_ERROR);
3404 			fcgi_connection_close(srv, hctx);
3405 			joblist_append(srv, con);
3406 		}
3407 	} else if (revents & FDEVENT_ERR) {
3408 		log_error_write(srv, __FILE__, __LINE__, "s",
3409 				"fcgi: got a FDEVENT_ERR. Don't know why.");
3410 		/* kill all connections to the fastcgi process */
3411 
3412 
3413 		connection_set_state(srv, con, CON_STATE_ERROR);
3414 		fcgi_connection_close(srv, hctx);
3415 		joblist_append(srv, con);
3416 	}
3417 
3418 	return HANDLER_FINISHED;
3419 }
3420 #define PATCH(x) \
3421 	p->conf.x = s->x;
fcgi_patch_connection(server * srv,connection * con,plugin_data * p)3422 static int fcgi_patch_connection(server *srv, connection *con, plugin_data *p) {
3423 	size_t i, j;
3424 	plugin_config *s = p->config_storage[0];
3425 
3426 	PATCH(exts);
3427 	PATCH(debug);
3428 	PATCH(ext_mapping);
3429 
3430 	/* skip the first, the global context */
3431 	for (i = 1; i < srv->config_context->used; i++) {
3432 		data_config *dc = (data_config *)srv->config_context->data[i];
3433 		s = p->config_storage[i];
3434 
3435 		/* condition didn't match */
3436 		if (!config_check_cond(srv, con, dc)) continue;
3437 
3438 		/* merge config */
3439 		for (j = 0; j < dc->value->used; j++) {
3440 			data_unset *du = dc->value->data[j];
3441 
3442 			if (buffer_is_equal_string(du->key, CONST_STR_LEN("fastcgi.server"))) {
3443 				PATCH(exts);
3444 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("fastcgi.debug"))) {
3445 				PATCH(debug);
3446 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("fastcgi.map-extensions"))) {
3447 				PATCH(ext_mapping);
3448 			}
3449 		}
3450 	}
3451 
3452 	return 0;
3453 }
3454 #undef PATCH
3455 
3456 
fcgi_check_extension(server * srv,connection * con,void * p_d,int uri_path_handler)3457 static handler_t fcgi_check_extension(server *srv, connection *con, void *p_d, int uri_path_handler) {
3458 	plugin_data *p = p_d;
3459 	size_t s_len;
3460 	size_t k;
3461 	buffer *fn;
3462 	fcgi_extension *extension = NULL;
3463 	fcgi_extension_host *host = NULL;
3464 
3465 	if (con->mode != DIRECT) return HANDLER_GO_ON;
3466 
3467 	/* Possibly, we processed already this request */
3468 	if (con->file_started == 1) return HANDLER_GO_ON;
3469 
3470 	fn = uri_path_handler ? con->uri.path : con->physical.path;
3471 
3472 	if (buffer_is_empty(fn)) return HANDLER_GO_ON;
3473 
3474 	s_len = fn->used - 1;
3475 
3476 	fcgi_patch_connection(srv, con, p);
3477 
3478 	/* fastcgi.map-extensions maps extensions to existing fastcgi.server entries
3479 	 *
3480 	 * fastcgi.map-extensions = ( ".php3" => ".php" )
3481 	 *
3482 	 * fastcgi.server = ( ".php" => ... )
3483 	 *
3484 	 * */
3485 
3486 	/* check if extension-mapping matches */
3487 	for (k = 0; k < p->conf.ext_mapping->used; k++) {
3488 		data_string *ds = (data_string *)p->conf.ext_mapping->data[k];
3489 		size_t ct_len; /* length of the config entry */
3490 
3491 		if (ds->key->used == 0) continue;
3492 
3493 		ct_len = ds->key->used - 1;
3494 
3495 		if (s_len < ct_len) continue;
3496 
3497 		/* found a mapping */
3498 		if (0 == strncmp(fn->ptr + s_len - ct_len, ds->key->ptr, ct_len)) {
3499 			/* check if we know the extension */
3500 
3501 			/* we can reuse k here */
3502 			for (k = 0; k < p->conf.exts->used; k++) {
3503 				extension = p->conf.exts->exts[k];
3504 
3505 				if (buffer_is_equal(ds->value, extension->key)) {
3506 					break;
3507 				}
3508 			}
3509 
3510 			if (k == p->conf.exts->used) {
3511 				/* found nothign */
3512 				extension = NULL;
3513 			}
3514 			break;
3515 		}
3516 	}
3517 
3518 	if (extension == NULL) {
3519 		/* check if extension matches */
3520 		for (k = 0; k < p->conf.exts->used; k++) {
3521 			size_t ct_len; /* length of the config entry */
3522 			fcgi_extension *ext = p->conf.exts->exts[k];
3523 
3524 			if (ext->key->used == 0) continue;
3525 
3526 			ct_len = ext->key->used - 1;
3527 
3528 			/* check _url_ in the form "/fcgi_pattern" */
3529 			if (ext->key->ptr[0] == '/') {
3530 				if ((ct_len <= con->uri.path->used -1) &&
3531 				    (strncmp(con->uri.path->ptr, ext->key->ptr, ct_len) == 0)) {
3532 					extension = ext;
3533 					break;
3534 				}
3535 			} else if ((ct_len <= s_len) && (0 == strncmp(fn->ptr + s_len - ct_len, ext->key->ptr, ct_len))) {
3536 				/* check extension in the form ".fcg" */
3537 				extension = ext;
3538 				break;
3539 			}
3540 		}
3541 		/* extension doesn't match */
3542 		if (NULL == extension) {
3543 			return HANDLER_GO_ON;
3544 		}
3545 	}
3546 
3547 	/* check if we have at least one server for this extension up and running */
3548 	for (k = 0; k < extension->used; k++) {
3549 		fcgi_extension_host *h = extension->hosts[k];
3550 
3551 		/* we should have at least one proc that can do something */
3552 		if (h->active_procs == 0) {
3553 			continue;
3554 		}
3555 
3556 		/* we found one host that is alive */
3557 		host = h;
3558 		break;
3559 	}
3560 
3561 	if (!host) {
3562 		/* sorry, we don't have a server alive for this ext */
3563 		buffer_reset(con->physical.path);
3564 		con->http_status = 500;
3565 
3566 		/* only send the 'no handler' once */
3567 		if (!extension->note_is_sent) {
3568 			extension->note_is_sent = 1;
3569 
3570 			log_error_write(srv, __FILE__, __LINE__, "sBSbsbs",
3571 					"all handlers for", con->uri.path, "?", con->uri.query,
3572 					"on", extension->key,
3573 					"are down.");
3574 		}
3575 
3576 		return HANDLER_FINISHED;
3577 	}
3578 
3579 	/* a note about no handler is not sent yet */
3580 	extension->note_is_sent = 0;
3581 
3582 	/*
3583 	 * if check-local is disabled, use the uri.path handler
3584 	 *
3585 	 */
3586 
3587 	/* init handler-context */
3588 	if (uri_path_handler) {
3589 		if (host->check_local == 0) {
3590 			handler_ctx *hctx;
3591 			char *pathinfo;
3592 
3593 			hctx = handler_ctx_init();
3594 
3595 			hctx->remote_conn      = con;
3596 			hctx->plugin_data      = p;
3597 			hctx->proc	       = NULL;
3598 			hctx->ext              = extension;
3599 
3600 
3601 			hctx->conf.exts        = p->conf.exts;
3602 			hctx->conf.debug       = p->conf.debug;
3603 
3604 			con->plugin_ctx[p->id] = hctx;
3605 
3606 			con->mode = p->id;
3607 
3608 			if (con->conf.log_request_handling) {
3609 				log_error_write(srv, __FILE__, __LINE__, "s",
3610 				"handling it in mod_fastcgi");
3611 			}
3612 
3613 			/* do not split path info for authorizer */
3614 			if (host->mode != FCGI_AUTHORIZER) {
3615 				/* the prefix is the SCRIPT_NAME,
3616 				* everything from start to the next slash
3617 				* this is important for check-local = "disable"
3618 				*
3619 				* if prefix = /admin.fcgi
3620 				*
3621 				* /admin.fcgi/foo/bar
3622 				*
3623 				* SCRIPT_NAME = /admin.fcgi
3624 				* PATH_INFO   = /foo/bar
3625 				*
3626 				* if prefix = /fcgi-bin/
3627 				*
3628 				* /fcgi-bin/foo/bar
3629 				*
3630 				* SCRIPT_NAME = /fcgi-bin/foo
3631 				* PATH_INFO   = /bar
3632 				*
3633 				* if prefix = /, and fix-root-path-name is enable
3634 				*
3635 				* /fcgi-bin/foo/bar
3636 				*
3637 				* SCRIPT_NAME = /fcgi-bin/foo
3638 				* PATH_INFO   = /bar
3639 				*
3640 				*/
3641 
3642 				/* the rewrite is only done for /prefix/? matches */
3643 				if (host->fix_root_path_name && extension->key->ptr[0] == '/' && extension->key->ptr[1] == '\0') {
3644 					buffer_copy_string(con->request.pathinfo, con->uri.path->ptr);
3645 					con->uri.path->used = 1;
3646 					con->uri.path->ptr[con->uri.path->used - 1] = '\0';
3647 				} else if (extension->key->ptr[0] == '/' &&
3648 					con->uri.path->used > extension->key->used &&
3649 					NULL != (pathinfo = strchr(con->uri.path->ptr + extension->key->used - 1, '/'))) {
3650 					/* rewrite uri.path and pathinfo */
3651 
3652 					buffer_copy_string(con->request.pathinfo, pathinfo);
3653 
3654 					con->uri.path->used -= con->request.pathinfo->used - 1;
3655 					con->uri.path->ptr[con->uri.path->used - 1] = '\0';
3656 				}
3657 			}
3658 		}
3659 	} else {
3660 		handler_ctx *hctx;
3661 		hctx = handler_ctx_init();
3662 
3663 		hctx->remote_conn      = con;
3664 		hctx->plugin_data      = p;
3665 		hctx->proc             = NULL;
3666 		hctx->ext              = extension;
3667 
3668 		hctx->conf.exts        = p->conf.exts;
3669 		hctx->conf.debug       = p->conf.debug;
3670 
3671 		con->plugin_ctx[p->id] = hctx;
3672 
3673 		con->mode = p->id;
3674 
3675 		if (con->conf.log_request_handling) {
3676 			log_error_write(srv, __FILE__, __LINE__, "s", "handling it in mod_fastcgi");
3677 		}
3678 	}
3679 
3680 	return HANDLER_GO_ON;
3681 }
3682 
3683 /* uri-path handler */
fcgi_check_extension_1(server * srv,connection * con,void * p_d)3684 static handler_t fcgi_check_extension_1(server *srv, connection *con, void *p_d) {
3685 	return fcgi_check_extension(srv, con, p_d, 1);
3686 }
3687 
3688 /* start request handler */
fcgi_check_extension_2(server * srv,connection * con,void * p_d)3689 static handler_t fcgi_check_extension_2(server *srv, connection *con, void *p_d) {
3690 	return fcgi_check_extension(srv, con, p_d, 0);
3691 }
3692 
JOBLIST_FUNC(mod_fastcgi_handle_joblist)3693 JOBLIST_FUNC(mod_fastcgi_handle_joblist) {
3694 	plugin_data *p = p_d;
3695 	handler_ctx *hctx = con->plugin_ctx[p->id];
3696 
3697 	if (hctx == NULL) return HANDLER_GO_ON;
3698 
3699 	if (hctx->fd != -1) {
3700 		switch (hctx->state) {
3701 		case FCGI_STATE_READ:
3702 			fdevent_event_set(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN);
3703 
3704 			break;
3705 		case FCGI_STATE_CONNECT_DELAYED:
3706 		case FCGI_STATE_WRITE:
3707 			fdevent_event_set(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_OUT);
3708 
3709 			break;
3710 		case FCGI_STATE_INIT:
3711 			/* at reconnect */
3712 			break;
3713 		default:
3714 			log_error_write(srv, __FILE__, __LINE__, "sd", "unhandled fcgi.state", hctx->state);
3715 			break;
3716 		}
3717 	}
3718 
3719 	return HANDLER_GO_ON;
3720 }
3721 
3722 
fcgi_connection_close_callback(server * srv,connection * con,void * p_d)3723 static handler_t fcgi_connection_close_callback(server *srv, connection *con, void *p_d) {
3724 	plugin_data *p = p_d;
3725 
3726 	fcgi_connection_close(srv, con->plugin_ctx[p->id]);
3727 
3728 	return HANDLER_GO_ON;
3729 }
3730 
TRIGGER_FUNC(mod_fastcgi_handle_trigger)3731 TRIGGER_FUNC(mod_fastcgi_handle_trigger) {
3732 	plugin_data *p = p_d;
3733 	size_t i, j, n;
3734 
3735 
3736 	/* perhaps we should kill a connect attempt after 10-15 seconds
3737 	 *
3738 	 * currently we wait for the TCP timeout which is 180 seconds on Linux
3739 	 *
3740 	 *
3741 	 *
3742 	 */
3743 
3744 	/* check all children if they are still up */
3745 
3746 	for (i = 0; i < srv->config_context->used; i++) {
3747 		plugin_config *conf;
3748 		fcgi_exts *exts;
3749 
3750 		conf = p->config_storage[i];
3751 
3752 		exts = conf->exts;
3753 
3754 		for (j = 0; j < exts->used; j++) {
3755 			fcgi_extension *ex;
3756 
3757 			ex = exts->exts[j];
3758 
3759 			for (n = 0; n < ex->used; n++) {
3760 
3761 				fcgi_proc *proc;
3762 				fcgi_extension_host *host;
3763 
3764 				host = ex->hosts[n];
3765 
3766 				fcgi_restart_dead_procs(srv, p, host);
3767 
3768 				for (proc = host->unused_procs; proc; proc = proc->next) {
3769 					int status;
3770 
3771 					if (proc->pid == 0) continue;
3772 
3773 					switch (waitpid(proc->pid, &status, WNOHANG)) {
3774 					case 0:
3775 						/* child still running after timeout, good */
3776 						break;
3777 					case -1:
3778 						if (errno != EINTR) {
3779 							/* no PID found ? should never happen */
3780 							log_error_write(srv, __FILE__, __LINE__, "sddss",
3781 									"pid ", proc->pid, proc->state,
3782 									"not found:", strerror(errno));
3783 
3784 #if 0
3785 							if (errno == ECHILD) {
3786 								/* someone else has cleaned up for us */
3787 								proc->pid = 0;
3788 								proc->state = PROC_STATE_UNSET;
3789 							}
3790 #endif
3791 						}
3792 						break;
3793 					default:
3794 						/* the child should not terminate at all */
3795 						if (WIFEXITED(status)) {
3796 							if (proc->state != PROC_STATE_KILLED) {
3797 								log_error_write(srv, __FILE__, __LINE__, "sdb",
3798 										"child exited:",
3799 										WEXITSTATUS(status), proc->connection_name);
3800 							}
3801 						} else if (WIFSIGNALED(status)) {
3802 							if (WTERMSIG(status) != SIGTERM) {
3803 								log_error_write(srv, __FILE__, __LINE__, "sd",
3804 										"child signaled:",
3805 										WTERMSIG(status));
3806 							}
3807 						} else {
3808 							log_error_write(srv, __FILE__, __LINE__, "sd",
3809 									"child died somehow:",
3810 									status);
3811 						}
3812 						proc->pid = 0;
3813 						if (proc->state == PROC_STATE_RUNNING) host->active_procs--;
3814 						proc->state = PROC_STATE_UNSET;
3815 						host->max_id--;
3816 					}
3817 				}
3818 			}
3819 		}
3820 	}
3821 
3822 	return HANDLER_GO_ON;
3823 }
3824 
3825 
3826 int mod_fastcgi_plugin_init(plugin *p);
mod_fastcgi_plugin_init(plugin * p)3827 int mod_fastcgi_plugin_init(plugin *p) {
3828 	p->version      = LIGHTTPD_VERSION_ID;
3829 	p->name         = buffer_init_string("fastcgi");
3830 
3831 	p->init         = mod_fastcgi_init;
3832 	p->cleanup      = mod_fastcgi_free;
3833 	p->set_defaults = mod_fastcgi_set_defaults;
3834 	p->connection_reset        = fcgi_connection_reset;
3835 	p->handle_connection_close = fcgi_connection_close_callback;
3836 	p->handle_uri_clean        = fcgi_check_extension_1;
3837 	p->handle_subrequest_start = fcgi_check_extension_2;
3838 	p->handle_subrequest       = mod_fastcgi_handle_subrequest;
3839 	p->handle_joblist          = mod_fastcgi_handle_joblist;
3840 	p->handle_trigger          = mod_fastcgi_handle_trigger;
3841 
3842 	p->data         = NULL;
3843 
3844 	return 0;
3845 }
3846