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
16#include <sys/types.h>
17#include <unistd.h>
18#include <errno.h>
19#include <fcntl.h>
20#include <string.h>
21#include <stdlib.h>
22#include <ctype.h>
23#include <assert.h>
24#include <signal.h>
25
26#include <stdio.h>
27
28#ifdef HAVE_SYS_FILIO_H
29# include <sys/filio.h>
30#endif
31
32#include "sys-socket.h"
33
34#ifdef HAVE_SYS_UIO_H
35# include <sys/uio.h>
36#endif
37
38#ifdef HAVE_SYS_WAIT_H
39# include <sys/wait.h>
40#endif
41
42#include "version.h"
43
44enum {EOL_UNSET, EOL_N, EOL_RN};
45
46/*
47 *
48 * TODO:
49 *
50 * - add timeout for a connect to a non-scgi process
51 *   (use state_timestamp + state)
52 *
53 */
54
55typedef struct scgi_proc {
56	size_t id; /* id will be between 1 and max_procs */
57	buffer *socket; /* config.socket + "-" + id */
58	unsigned port;  /* config.port + pno */
59
60	pid_t pid;   /* PID of the spawned process (0 if not spawned locally) */
61
62
63	size_t load; /* number of requests waiting on this process */
64
65	time_t last_used; /* see idle_timeout */
66	size_t requests;  /* see max_requests */
67	struct scgi_proc *prev, *next; /* see first */
68
69	time_t disable_ts; /* replace by host->something */
70
71	int is_local;
72
73	enum { PROC_STATE_UNSET,            /* init-phase */
74			PROC_STATE_RUNNING, /* alive */
75			PROC_STATE_DIED_WAIT_FOR_PID,
76			PROC_STATE_KILLED,  /* was killed as we don't have the load anymore */
77			PROC_STATE_DIED,    /* marked as dead, should be restarted */
78			PROC_STATE_DISABLED /* proc disabled as it resulted in an error */
79	} state;
80} scgi_proc;
81
82typedef struct {
83	/* list of processes handling this extension
84	 * sorted by lowest load
85	 *
86	 * whenever a job is done move it up in the list
87	 * until it is sorted, move it down as soon as the
88	 * job is started
89	 */
90	scgi_proc *first;
91	scgi_proc *unused_procs;
92
93	/*
94	 * spawn at least min_procs, at max_procs.
95	 *
96	 * as soon as the load of the first entry
97	 * is max_load_per_proc we spawn a new one
98	 * and add it to the first entry and give it
99	 * the load
100	 *
101	 */
102
103	unsigned short min_procs;
104	unsigned short max_procs;
105	size_t num_procs;    /* how many procs are started */
106	size_t active_procs; /* how many of them are really running */
107
108	unsigned short max_load_per_proc;
109
110	/*
111	 * kick the process from the list if it was not
112	 * used for idle_timeout until min_procs is
113	 * reached. this helps to get the processlist
114	 * small again we had a small peak load.
115	 *
116	 */
117
118	unsigned short idle_timeout;
119
120	/*
121	 * time after a disabled remote connection is tried to be re-enabled
122	 *
123	 *
124	 */
125
126	unsigned short disable_time;
127
128	/*
129	 * same scgi processes get a little bit larger
130	 * than wanted. max_requests_per_proc kills a
131	 * process after a number of handled requests.
132	 *
133	 */
134	size_t max_requests_per_proc;
135
136
137	/* config */
138
139	/*
140	 * host:port
141	 *
142	 * if host is one of the local IP adresses the
143	 * whole connection is local
144	 *
145	 * if tcp/ip should be used host AND port have
146	 * to be specified
147	 *
148	 */
149	buffer *host;
150	unsigned short port;
151
152	/*
153	 * Unix Domain Socket
154	 *
155	 * instead of TCP/IP we can use Unix Domain Sockets
156	 * - more secure (you have fileperms to play with)
157	 * - more control (on locally)
158	 * - more speed (no extra overhead)
159	 */
160	buffer *unixsocket;
161
162	/* if socket is local we can start the scgi
163	 * process ourself
164	 *
165	 * bin-path is the path to the binary
166	 *
167	 * check min_procs and max_procs for the number
168	 * of process to start-up
169	 */
170	buffer *bin_path;
171
172	/* bin-path is set bin-environment is taken to
173	 * create the environement before starting the
174	 * FastCGI process
175	 *
176	 */
177	array *bin_env;
178
179	array *bin_env_copy;
180
181	/*
182	 * docroot-translation between URL->phys and the
183	 * remote host
184	 *
185	 * reasons:
186	 * - different dir-layout if remote
187	 * - chroot if local
188	 *
189	 */
190	buffer *docroot;
191
192	/*
193	 * check_local tell you if the phys file is stat()ed
194	 * or not. FastCGI doesn't care if the service is
195	 * remote. If the web-server side doesn't contain
196	 * the scgi-files we should not stat() for them
197	 * and say '404 not found'.
198	 */
199	unsigned short check_local;
200
201	/*
202	 * append PATH_INFO to SCRIPT_FILENAME
203	 *
204	 * php needs this if cgi.fix_pathinfo is provied
205	 *
206	 */
207
208	/*
209	 * workaround for program when prefix="/"
210	 *
211	 * rule to build PATH_INFO is hardcoded for when check_local is disabled
212	 * enable this option to use the workaround
213	 *
214	 */
215
216	unsigned short fix_root_path_name;
217	ssize_t load; /* replace by host->load */
218
219	size_t max_id; /* corresponds most of the time to
220	num_procs.
221
222	only if a process is killed max_id waits for the process itself
223	to die and decrements its afterwards */
224} scgi_extension_host;
225
226/*
227 * one extension can have multiple hosts assigned
228 * one host can spawn additional processes on the same
229 *   socket (if we control it)
230 *
231 * ext -> host -> procs
232 *    1:n     1:n
233 *
234 * if the scgi process is remote that whole goes down
235 * to
236 *
237 * ext -> host -> procs
238 *    1:n     1:1
239 *
240 * in case of PHP and FCGI_CHILDREN we have again a procs
241 * but we don't control it directly.
242 *
243 */
244
245typedef struct {
246	buffer *key; /* like .php */
247
248	int note_is_sent;
249	scgi_extension_host **hosts;
250
251	size_t used;
252	size_t size;
253} scgi_extension;
254
255typedef struct {
256	scgi_extension **exts;
257
258	size_t used;
259	size_t size;
260} scgi_exts;
261
262
263typedef struct {
264	scgi_exts *exts;
265
266	int debug;
267} plugin_config;
268
269typedef struct {
270	char **ptr;
271
272	size_t size;
273	size_t used;
274} char_array;
275
276/* generic plugin data, shared between all connections */
277typedef struct {
278	PLUGIN_DATA;
279
280	buffer *scgi_env;
281
282	buffer *path;
283	buffer *parse_response;
284
285	plugin_config **config_storage;
286
287	plugin_config conf; /* this is only used as long as no handler_ctx is setup */
288} plugin_data;
289
290/* connection specific data */
291typedef enum { FCGI_STATE_INIT, FCGI_STATE_CONNECT, FCGI_STATE_PREPARE_WRITE,
292		FCGI_STATE_WRITE, FCGI_STATE_READ
293} scgi_connection_state_t;
294
295typedef struct {
296	buffer  *response;
297	size_t   response_len;
298	int      response_type;
299	int      response_padding;
300
301	scgi_proc *proc;
302	scgi_extension_host *host;
303
304	scgi_connection_state_t state;
305	time_t   state_timestamp;
306
307	int      reconnects; /* number of reconnect attempts */
308
309	chunkqueue *wb;
310
311	buffer   *response_header;
312
313	int       delayed;   /* flag to mark that the connect() is delayed */
314
315	size_t    request_id;
316	int       fd;        /* fd to the scgi process */
317	int       fde_ndx;   /* index into the fd-event buffer */
318
319	pid_t     pid;
320	int       got_proc;
321
322	plugin_config conf;
323
324	connection *remote_conn;  /* dumb pointer */
325	plugin_data *plugin_data; /* dumb pointer */
326} handler_ctx;
327
328
329/* ok, we need a prototype */
330static handler_t scgi_handle_fdevent(server *srv, void *ctx, int revents);
331
332int scgi_proclist_sort_down(server *srv, scgi_extension_host *host, scgi_proc *proc);
333
334static void reset_signals(void) {
335#ifdef SIGTTOU
336	signal(SIGTTOU, SIG_DFL);
337#endif
338#ifdef SIGTTIN
339	signal(SIGTTIN, SIG_DFL);
340#endif
341#ifdef SIGTSTP
342	signal(SIGTSTP, SIG_DFL);
343#endif
344	signal(SIGHUP, SIG_DFL);
345	signal(SIGPIPE, SIG_DFL);
346	signal(SIGUSR1, SIG_DFL);
347}
348
349static handler_ctx * handler_ctx_init(void) {
350	handler_ctx * hctx;
351
352	hctx = calloc(1, sizeof(*hctx));
353	force_assert(hctx);
354
355	hctx->fde_ndx = -1;
356
357	hctx->response = buffer_init();
358	hctx->response_header = buffer_init();
359
360	hctx->request_id = 0;
361	hctx->state = FCGI_STATE_INIT;
362	hctx->proc = NULL;
363
364	hctx->response_len = 0;
365	hctx->response_type = 0;
366	hctx->response_padding = 0;
367	hctx->fd = -1;
368
369	hctx->reconnects = 0;
370
371	hctx->wb = chunkqueue_init();
372
373	return hctx;
374}
375
376static void handler_ctx_free(handler_ctx *hctx) {
377	buffer_free(hctx->response);
378	buffer_free(hctx->response_header);
379
380	chunkqueue_free(hctx->wb);
381
382	free(hctx);
383}
384
385static scgi_proc *scgi_process_init(void) {
386	scgi_proc *f;
387
388	f = calloc(1, sizeof(*f));
389	force_assert(f);
390	f->socket = buffer_init();
391
392	f->prev = NULL;
393	f->next = NULL;
394
395	return f;
396}
397
398static void scgi_process_free(scgi_proc *f) {
399	if (!f) return;
400
401	scgi_process_free(f->next);
402
403	buffer_free(f->socket);
404
405	free(f);
406}
407
408static scgi_extension_host *scgi_host_init(void) {
409	scgi_extension_host *f;
410
411	f = calloc(1, sizeof(*f));
412
413	f->host = buffer_init();
414	f->unixsocket = buffer_init();
415	f->docroot = buffer_init();
416	f->bin_path = buffer_init();
417	f->bin_env = array_init();
418	f->bin_env_copy = array_init();
419
420	return f;
421}
422
423static void scgi_host_free(scgi_extension_host *h) {
424	if (!h) return;
425
426	buffer_free(h->host);
427	buffer_free(h->unixsocket);
428	buffer_free(h->docroot);
429	buffer_free(h->bin_path);
430	array_free(h->bin_env);
431	array_free(h->bin_env_copy);
432
433	scgi_process_free(h->first);
434	scgi_process_free(h->unused_procs);
435
436	free(h);
437
438}
439
440static scgi_exts *scgi_extensions_init(void) {
441	scgi_exts *f;
442
443	f = calloc(1, sizeof(*f));
444	force_assert(f);
445
446	return f;
447}
448
449static void scgi_extensions_free(scgi_exts *f) {
450	size_t i;
451
452	if (!f) return;
453
454	for (i = 0; i < f->used; i++) {
455		scgi_extension *fe;
456		size_t j;
457
458		fe = f->exts[i];
459
460		for (j = 0; j < fe->used; j++) {
461			scgi_extension_host *h;
462
463			h = fe->hosts[j];
464
465			scgi_host_free(h);
466		}
467
468		buffer_free(fe->key);
469		free(fe->hosts);
470
471		free(fe);
472	}
473
474	free(f->exts);
475
476	free(f);
477}
478
479static int scgi_extension_insert(scgi_exts *ext, buffer *key, scgi_extension_host *fh) {
480	scgi_extension *fe;
481	size_t i;
482
483	/* there is something */
484
485	for (i = 0; i < ext->used; i++) {
486		if (buffer_is_equal(key, ext->exts[i]->key)) {
487			break;
488		}
489	}
490
491	if (i == ext->used) {
492		/* filextension is new */
493		fe = calloc(1, sizeof(*fe));
494		force_assert(fe);
495		fe->key = buffer_init();
496		buffer_copy_buffer(fe->key, key);
497
498		/* */
499
500		if (ext->size == 0) {
501			ext->size = 8;
502			ext->exts = malloc(ext->size * sizeof(*(ext->exts)));
503			force_assert(ext->exts);
504		} else if (ext->used == ext->size) {
505			ext->size += 8;
506			ext->exts = realloc(ext->exts, ext->size * sizeof(*(ext->exts)));
507			force_assert(ext->exts);
508		}
509		ext->exts[ext->used++] = fe;
510	} else {
511		fe = ext->exts[i];
512	}
513
514	if (fe->size == 0) {
515		fe->size = 4;
516		fe->hosts = malloc(fe->size * sizeof(*(fe->hosts)));
517		force_assert(fe->hosts);
518	} else if (fe->size == fe->used) {
519		fe->size += 4;
520		fe->hosts = realloc(fe->hosts, fe->size * sizeof(*(fe->hosts)));
521		force_assert(fe->hosts);
522	}
523
524	fe->hosts[fe->used++] = fh;
525
526	return 0;
527
528}
529
530INIT_FUNC(mod_scgi_init) {
531	plugin_data *p;
532
533	p = calloc(1, sizeof(*p));
534	force_assert(p);
535
536	p->scgi_env = buffer_init();
537
538	p->path = buffer_init();
539	p->parse_response = buffer_init();
540
541	return p;
542}
543
544
545FREE_FUNC(mod_scgi_free) {
546	plugin_data *p = p_d;
547
548	UNUSED(srv);
549
550	buffer_free(p->scgi_env);
551	buffer_free(p->path);
552	buffer_free(p->parse_response);
553
554	if (p->config_storage) {
555		size_t i, j, n;
556		for (i = 0; i < srv->config_context->used; i++) {
557			plugin_config *s = p->config_storage[i];
558			scgi_exts *exts;
559
560			if (NULL == s) continue;
561
562			exts = s->exts;
563
564			for (j = 0; j < exts->used; j++) {
565				scgi_extension *ex;
566
567				ex = exts->exts[j];
568
569				for (n = 0; n < ex->used; n++) {
570					scgi_proc *proc;
571					scgi_extension_host *host;
572
573					host = ex->hosts[n];
574
575					for (proc = host->first; proc; proc = proc->next) {
576						if (proc->pid != 0) kill(proc->pid, SIGTERM);
577
578						if (proc->is_local &&
579						    !buffer_string_is_empty(proc->socket)) {
580							unlink(proc->socket->ptr);
581						}
582					}
583
584					for (proc = host->unused_procs; proc; proc = proc->next) {
585						if (proc->pid != 0) kill(proc->pid, SIGTERM);
586
587						if (proc->is_local &&
588						    !buffer_string_is_empty(proc->socket)) {
589							unlink(proc->socket->ptr);
590						}
591					}
592				}
593			}
594
595			scgi_extensions_free(s->exts);
596
597			free(s);
598		}
599		free(p->config_storage);
600	}
601
602	free(p);
603
604	return HANDLER_GO_ON;
605}
606
607static int env_add(char_array *env, const char *key, size_t key_len, const char *val, size_t val_len) {
608	char *dst;
609	size_t i;
610
611	if (!key || !val) return -1;
612
613	dst = malloc(key_len + val_len + 3);
614	force_assert(dst);
615	memcpy(dst, key, key_len);
616	dst[key_len] = '=';
617	/* add the \0 from the value */
618	memcpy(dst + key_len + 1, val, val_len + 1);
619
620	for (i = 0; i < env->used; i++) {
621		if (0 == strncmp(dst, env->ptr[i], key_len + 1)) {
622			/* don't care about free as we are in a forked child which is going to exec(...) */
623			/* free(env->ptr[i]); */
624			env->ptr[i] = dst;
625			return 0;
626		}
627	}
628
629	if (env->size == 0) {
630		env->size = 16;
631		env->ptr = malloc(env->size * sizeof(*env->ptr));
632		force_assert(env->ptr);
633	} else if (env->size == env->used) {
634		env->size += 16;
635		env->ptr = realloc(env->ptr, env->size * sizeof(*env->ptr));
636		force_assert(env->ptr);
637	}
638
639	env->ptr[env->used++] = dst;
640
641	return 0;
642}
643
644static int scgi_spawn_connection(server *srv,
645				 plugin_data *p,
646				 scgi_extension_host *host,
647				 scgi_proc *proc) {
648	int scgi_fd;
649	int socket_type, status;
650	struct timeval tv = { 0, 100 * 1000 };
651#ifdef HAVE_SYS_UN_H
652	struct sockaddr_un scgi_addr_un;
653#endif
654	struct sockaddr_in scgi_addr_in;
655	struct sockaddr *scgi_addr;
656
657	socklen_t servlen;
658
659#ifndef HAVE_FORK
660	return -1;
661#endif
662
663	if (p->conf.debug) {
664		log_error_write(srv, __FILE__, __LINE__, "sdb",
665				"new proc, socket:", proc->port, proc->socket);
666	}
667
668	if (!buffer_string_is_empty(proc->socket)) {
669#ifdef HAVE_SYS_UN_H
670		memset(&scgi_addr_un, 0, sizeof(scgi_addr_un));
671		scgi_addr_un.sun_family = AF_UNIX;
672		if (buffer_string_length(proc->socket) + 1 > sizeof(scgi_addr_un.sun_path)) {
673			log_error_write(srv, __FILE__, __LINE__, "sB",
674					"ERROR: Unix Domain socket filename too long:",
675					proc->socket);
676			return -1;
677		}
678		memcpy(scgi_addr_un.sun_path, proc->socket->ptr, buffer_string_length(proc->socket) + 1);
679
680#ifdef SUN_LEN
681		servlen = SUN_LEN(&scgi_addr_un);
682#else
683		/* stevens says: */
684		servlen = buffer_string_length(proc->socket) + 1 + sizeof(scgi_addr_un.sun_family);
685#endif
686		socket_type = AF_UNIX;
687		scgi_addr = (struct sockaddr *) &scgi_addr_un;
688#else
689		log_error_write(srv, __FILE__, __LINE__, "s",
690				"ERROR: Unix Domain sockets are not supported.");
691		return -1;
692#endif
693	} else {
694		memset(&scgi_addr_in, 0, sizeof(scgi_addr_in));
695		scgi_addr_in.sin_family = AF_INET;
696
697		if (buffer_string_is_empty(host->host)) {
698			scgi_addr_in.sin_addr.s_addr = htonl(INADDR_ANY);
699		} else {
700			struct hostent *he;
701
702			/* set a usefull default */
703			scgi_addr_in.sin_addr.s_addr = htonl(INADDR_ANY);
704
705
706			if (NULL == (he = gethostbyname(host->host->ptr))) {
707				log_error_write(srv, __FILE__, __LINE__,
708						"sdb", "gethostbyname failed: ",
709						h_errno, host->host);
710				return -1;
711			}
712
713			if (he->h_addrtype != AF_INET) {
714				log_error_write(srv, __FILE__, __LINE__, "sd", "addr-type != AF_INET: ", he->h_addrtype);
715				return -1;
716			}
717
718			if (he->h_length != sizeof(struct in_addr)) {
719				log_error_write(srv, __FILE__, __LINE__, "sd", "addr-length != sizeof(in_addr): ", he->h_length);
720				return -1;
721			}
722
723			memcpy(&(scgi_addr_in.sin_addr.s_addr), he->h_addr_list[0], he->h_length);
724
725		}
726		scgi_addr_in.sin_port = htons(proc->port);
727		servlen = sizeof(scgi_addr_in);
728
729		socket_type = AF_INET;
730		scgi_addr = (struct sockaddr *) &scgi_addr_in;
731	}
732
733	if (-1 == (scgi_fd = socket(socket_type, SOCK_STREAM, 0))) {
734		log_error_write(srv, __FILE__, __LINE__, "ss",
735				"failed:", strerror(errno));
736		return -1;
737	}
738
739	if (-1 == connect(scgi_fd, scgi_addr, servlen)) {
740		/* server is not up, spawn in  */
741		pid_t child;
742		int val;
743
744		if (!buffer_string_is_empty(proc->socket)) {
745			unlink(proc->socket->ptr);
746		}
747
748		close(scgi_fd);
749
750		/* reopen socket */
751		if (-1 == (scgi_fd = socket(socket_type, SOCK_STREAM, 0))) {
752			log_error_write(srv, __FILE__, __LINE__, "ss",
753				"socket failed:", strerror(errno));
754			return -1;
755		}
756
757		val = 1;
758		if (setsockopt(scgi_fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) < 0) {
759			log_error_write(srv, __FILE__, __LINE__, "ss",
760					"socketsockopt failed:", strerror(errno));
761			close(scgi_fd);
762			return -1;
763		}
764
765		/* create socket */
766		if (-1 == bind(scgi_fd, scgi_addr, servlen)) {
767			log_error_write(srv, __FILE__, __LINE__, "sbds",
768				"bind failed for:",
769				proc->socket,
770				proc->port,
771				strerror(errno));
772			close(scgi_fd);
773			return -1;
774		}
775
776		if (-1 == listen(scgi_fd, 1024)) {
777			log_error_write(srv, __FILE__, __LINE__, "ss",
778				"listen failed:", strerror(errno));
779			close(scgi_fd);
780			return -1;
781		}
782
783#ifdef HAVE_FORK
784		switch ((child = fork())) {
785		case 0: {
786			buffer *b;
787			size_t i = 0;
788			int fd = 0;
789			char_array env;
790
791
792			/* create environment */
793			env.ptr = NULL;
794			env.size = 0;
795			env.used = 0;
796
797			if (scgi_fd != 0) {
798				dup2(scgi_fd, 0);
799				close(scgi_fd);
800			}
801
802			/* we don't need the client socket */
803			for (fd = 3; fd < 256; fd++) {
804				close(fd);
805			}
806
807			/* build clean environment */
808			if (host->bin_env_copy->used) {
809				for (i = 0; i < host->bin_env_copy->used; i++) {
810					data_string *ds = (data_string *)host->bin_env_copy->data[i];
811					char *ge;
812
813					if (NULL != (ge = getenv(ds->value->ptr))) {
814						env_add(&env, CONST_BUF_LEN(ds->value), ge, strlen(ge));
815					}
816				}
817			} else {
818				for (i = 0; environ[i]; i++) {
819					char *eq;
820
821					if (NULL != (eq = strchr(environ[i], '='))) {
822						env_add(&env, environ[i], eq - environ[i], eq+1, strlen(eq+1));
823					}
824				}
825			}
826
827			/* create environment */
828			for (i = 0; i < host->bin_env->used; i++) {
829				data_string *ds = (data_string *)host->bin_env->data[i];
830
831				env_add(&env, CONST_BUF_LEN(ds->key), CONST_BUF_LEN(ds->value));
832			}
833
834			for (i = 0; i < env.used; i++) {
835				/* search for PHP_FCGI_CHILDREN */
836				if (0 == strncmp(env.ptr[i], "PHP_FCGI_CHILDREN=", sizeof("PHP_FCGI_CHILDREN=") - 1)) break;
837			}
838
839			/* not found, add a default */
840			if (i == env.used) {
841				env_add(&env, CONST_STR_LEN("PHP_FCGI_CHILDREN"), CONST_STR_LEN("1"));
842			}
843
844			env.ptr[env.used] = NULL;
845
846			b = buffer_init();
847			buffer_copy_string_len(b, CONST_STR_LEN("exec "));
848			buffer_append_string_buffer(b, host->bin_path);
849
850			reset_signals();
851
852			/* exec the cgi */
853			execle("/bin/sh", "sh", "-c", b->ptr, (char *)NULL, env.ptr);
854
855			log_error_write(srv, __FILE__, __LINE__, "sbs",
856					"execl failed for:", host->bin_path, strerror(errno));
857
858			exit(errno);
859
860			break;
861		}
862		case -1:
863			/* error */
864			break;
865		default:
866			/* father */
867
868			/* wait */
869			select(0, NULL, NULL, NULL, &tv);
870
871			switch (waitpid(child, &status, WNOHANG)) {
872			case 0:
873				/* child still running after timeout, good */
874				break;
875			case -1:
876				/* no PID found ? should never happen */
877				log_error_write(srv, __FILE__, __LINE__, "ss",
878						"pid not found:", strerror(errno));
879				return -1;
880			default:
881				/* the child should not terminate at all */
882				if (WIFEXITED(status)) {
883					log_error_write(srv, __FILE__, __LINE__, "sd",
884							"child exited (is this a SCGI binary ?):",
885							WEXITSTATUS(status));
886				} else if (WIFSIGNALED(status)) {
887					log_error_write(srv, __FILE__, __LINE__, "sd",
888							"child signaled:",
889							WTERMSIG(status));
890				} else {
891					log_error_write(srv, __FILE__, __LINE__, "sd",
892							"child died somehow:",
893							status);
894				}
895				return -1;
896			}
897
898			/* register process */
899			proc->pid = child;
900			proc->last_used = srv->cur_ts;
901			proc->is_local = 1;
902
903			break;
904		}
905#endif
906	} else {
907		proc->is_local = 0;
908		proc->pid = 0;
909
910		if (p->conf.debug) {
911			log_error_write(srv, __FILE__, __LINE__, "sb",
912					"(debug) socket is already used, won't spawn:",
913					proc->socket);
914		}
915	}
916
917	proc->state = PROC_STATE_RUNNING;
918	host->active_procs++;
919
920	close(scgi_fd);
921
922	return 0;
923}
924
925
926SETDEFAULTS_FUNC(mod_scgi_set_defaults) {
927	plugin_data *p = p_d;
928	data_unset *du;
929	size_t i = 0;
930	scgi_extension_host *df = NULL;
931
932	config_values_t cv[] = {
933		{ "scgi.server",              NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION },       /* 0 */
934		{ "scgi.debug",               NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION },       /* 1 */
935		{ NULL,                          NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
936	};
937
938	p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *));
939	force_assert(p->config_storage);
940
941	for (i = 0; i < srv->config_context->used; i++) {
942		data_config const* config = (data_config const*)srv->config_context->data[i];
943		plugin_config *s;
944
945		s = malloc(sizeof(plugin_config));
946		force_assert(s);
947		s->exts          = scgi_extensions_init();
948		s->debug         = 0;
949
950		cv[0].destination = s->exts;
951		cv[1].destination = &(s->debug);
952
953		p->config_storage[i] = s;
954
955		if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) {
956			goto error;
957		}
958
959		/*
960		 * <key> = ( ... )
961		 */
962
963		if (NULL != (du = array_get_element(config->value, "scgi.server"))) {
964			size_t j;
965			data_array *da = (data_array *)du;
966
967			if (du->type != TYPE_ARRAY) {
968				log_error_write(srv, __FILE__, __LINE__, "sss",
969						"unexpected type for key: ", "scgi.server", "array of strings");
970
971				goto error;
972			}
973
974
975			/*
976			 * scgi.server = ( "<ext>" => ( ... ),
977			 *                    "<ext>" => ( ... ) )
978			 */
979
980			for (j = 0; j < da->value->used; j++) {
981				size_t n;
982				data_array *da_ext = (data_array *)da->value->data[j];
983
984				if (da->value->data[j]->type != TYPE_ARRAY) {
985					log_error_write(srv, __FILE__, __LINE__, "sssbs",
986							"unexpected type for key: ", "scgi.server",
987							"[", da->value->data[j]->key, "](string)");
988
989					goto error;
990				}
991
992				/*
993				 * da_ext->key == name of the extension
994				 */
995
996				/*
997				 * scgi.server = ( "<ext>" =>
998				 *                     ( "<host>" => ( ... ),
999				 *                       "<host>" => ( ... )
1000				 *                     ),
1001				 *                    "<ext>" => ... )
1002				 */
1003
1004				for (n = 0; n < da_ext->value->used; n++) {
1005					data_array *da_host = (data_array *)da_ext->value->data[n];
1006
1007					config_values_t fcv[] = {
1008						{ "host",              NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },       /* 0 */
1009						{ "docroot",           NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },       /* 1 */
1010						{ "socket",            NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },       /* 2 */
1011						{ "bin-path",          NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },       /* 3 */
1012
1013						{ "check-local",       NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION },      /* 4 */
1014						{ "port",              NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION },        /* 5 */
1015						{ "min-procs-not-working",         NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION },        /* 7 this is broken for now */
1016						{ "max-procs",         NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION },        /* 7 */
1017						{ "max-load-per-proc", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION },        /* 8 */
1018						{ "idle-timeout",      NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION },        /* 9 */
1019						{ "disable-time",      NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION },        /* 10 */
1020
1021						{ "bin-environment",   NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION },        /* 11 */
1022						{ "bin-copy-environment", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION },     /* 12 */
1023						{ "fix-root-scriptname",  NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION },   /* 13 */
1024
1025
1026						{ NULL,                NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
1027					};
1028
1029					if (da_host->type != TYPE_ARRAY) {
1030						log_error_write(srv, __FILE__, __LINE__, "ssSBS",
1031								"unexpected type for key:",
1032								"scgi.server",
1033								"[", da_host->key, "](string)");
1034
1035						goto error;
1036					}
1037
1038					df = scgi_host_init();
1039
1040					df->check_local  = 1;
1041					df->min_procs    = 4;
1042					df->max_procs    = 4;
1043					df->max_load_per_proc = 1;
1044					df->idle_timeout = 60;
1045					df->disable_time = 60;
1046					df->fix_root_path_name = 0;
1047
1048					fcv[0].destination = df->host;
1049					fcv[1].destination = df->docroot;
1050					fcv[2].destination = df->unixsocket;
1051					fcv[3].destination = df->bin_path;
1052
1053					fcv[4].destination = &(df->check_local);
1054					fcv[5].destination = &(df->port);
1055					fcv[6].destination = &(df->min_procs);
1056					fcv[7].destination = &(df->max_procs);
1057					fcv[8].destination = &(df->max_load_per_proc);
1058					fcv[9].destination = &(df->idle_timeout);
1059					fcv[10].destination = &(df->disable_time);
1060
1061					fcv[11].destination = df->bin_env;
1062					fcv[12].destination = df->bin_env_copy;
1063					fcv[13].destination = &(df->fix_root_path_name);
1064
1065
1066					if (0 != config_insert_values_internal(srv, da_host->value, fcv, T_CONFIG_SCOPE_CONNECTION)) {
1067						goto error;
1068					}
1069
1070					if ((!buffer_string_is_empty(df->host) || df->port) &&
1071					    !buffer_string_is_empty(df->unixsocket)) {
1072						log_error_write(srv, __FILE__, __LINE__, "s",
1073								"either host+port or socket");
1074
1075						goto error;
1076					}
1077
1078					if (!buffer_string_is_empty(df->unixsocket)) {
1079						/* unix domain socket */
1080						struct sockaddr_un un;
1081
1082						if (buffer_string_length(df->unixsocket) + 1 > sizeof(un.sun_path) - 2) {
1083							log_error_write(srv, __FILE__, __LINE__, "s",
1084									"path of the unixdomain socket is too large");
1085							goto error;
1086						}
1087					} else {
1088						/* tcp/ip */
1089
1090						if (buffer_string_is_empty(df->host) &&
1091						    buffer_string_is_empty(df->bin_path)) {
1092							log_error_write(srv, __FILE__, __LINE__, "sbbbs",
1093									"missing key (string):",
1094									da->key,
1095									da_ext->key,
1096									da_host->key,
1097									"host");
1098
1099							goto error;
1100						} else if (df->port == 0) {
1101							log_error_write(srv, __FILE__, __LINE__, "sbbbs",
1102									"missing key (short):",
1103									da->key,
1104									da_ext->key,
1105									da_host->key,
1106									"port");
1107							goto error;
1108						}
1109					}
1110
1111					if (!buffer_string_is_empty(df->bin_path)) {
1112						/* a local socket + self spawning */
1113						size_t pno;
1114
1115						/* HACK:  just to make sure the adaptive spawing is disabled */
1116						df->min_procs = df->max_procs;
1117
1118						if (df->min_procs > df->max_procs) df->max_procs = df->min_procs;
1119						if (df->max_load_per_proc < 1) df->max_load_per_proc = 0;
1120
1121						if (s->debug) {
1122							log_error_write(srv, __FILE__, __LINE__, "ssbsdsbsdsd",
1123									"--- scgi spawning local",
1124									"\n\tproc:", df->bin_path,
1125									"\n\tport:", df->port,
1126									"\n\tsocket", df->unixsocket,
1127									"\n\tmin-procs:", df->min_procs,
1128									"\n\tmax-procs:", df->max_procs);
1129						}
1130
1131						for (pno = 0; pno < df->min_procs; pno++) {
1132							scgi_proc *proc;
1133
1134							proc = scgi_process_init();
1135							proc->id = df->num_procs++;
1136							df->max_id++;
1137
1138							if (buffer_string_is_empty(df->unixsocket)) {
1139								proc->port = df->port + pno;
1140							} else {
1141								buffer_copy_buffer(proc->socket, df->unixsocket);
1142								buffer_append_string_len(proc->socket, CONST_STR_LEN("-"));
1143								buffer_append_int(proc->socket, pno);
1144							}
1145
1146							if (s->debug) {
1147								log_error_write(srv, __FILE__, __LINE__, "ssdsbsdsd",
1148										"--- scgi spawning",
1149										"\n\tport:", df->port,
1150										"\n\tsocket", df->unixsocket,
1151										"\n\tcurrent:", pno, "/", df->min_procs);
1152							}
1153
1154							if (scgi_spawn_connection(srv, p, df, proc)) {
1155								log_error_write(srv, __FILE__, __LINE__, "s",
1156										"[ERROR]: spawning fcgi failed.");
1157								scgi_process_free(proc);
1158								goto error;
1159							}
1160
1161							proc->next = df->first;
1162							if (df->first) 	df->first->prev = proc;
1163
1164							df->first = proc;
1165						}
1166					} else {
1167						scgi_proc *fp;
1168
1169						fp = scgi_process_init();
1170						fp->id = df->num_procs++;
1171						df->max_id++;
1172						df->active_procs++;
1173						fp->state = PROC_STATE_RUNNING;
1174
1175						if (buffer_string_is_empty(df->unixsocket)) {
1176							fp->port = df->port;
1177						} else {
1178							buffer_copy_buffer(fp->socket, df->unixsocket);
1179						}
1180
1181						df->first = fp;
1182
1183						df->min_procs = 1;
1184						df->max_procs = 1;
1185					}
1186
1187					/* if extension already exists, take it */
1188					scgi_extension_insert(s->exts, da_ext->key, df);
1189					df = NULL;
1190				}
1191			}
1192		}
1193	}
1194
1195	return HANDLER_GO_ON;
1196
1197error:
1198	if (NULL != df) scgi_host_free(df);
1199	return HANDLER_ERROR;
1200}
1201
1202static int scgi_set_state(server *srv, handler_ctx *hctx, scgi_connection_state_t state) {
1203	hctx->state = state;
1204	hctx->state_timestamp = srv->cur_ts;
1205
1206	return 0;
1207}
1208
1209
1210static void scgi_connection_cleanup(server *srv, handler_ctx *hctx) {
1211	plugin_data *p;
1212	connection  *con;
1213
1214	if (NULL == hctx) return;
1215
1216	p    = hctx->plugin_data;
1217	con  = hctx->remote_conn;
1218
1219	if (hctx->fd != -1) {
1220		fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd);
1221		fdevent_unregister(srv->ev, hctx->fd);
1222		close(hctx->fd);
1223		srv->cur_fds--;
1224	}
1225
1226	if (hctx->host && hctx->proc) {
1227		hctx->host->load--;
1228
1229		if (hctx->got_proc) {
1230			/* after the connect the process gets a load */
1231			hctx->proc->load--;
1232
1233			if (p->conf.debug) {
1234				log_error_write(srv, __FILE__, __LINE__, "sddb",
1235						"release proc:",
1236						hctx->fd,
1237						hctx->proc->pid, hctx->proc->socket);
1238			}
1239		}
1240
1241		scgi_proclist_sort_down(srv, hctx->host, hctx->proc);
1242	}
1243
1244
1245	handler_ctx_free(hctx);
1246	con->plugin_ctx[p->id] = NULL;
1247}
1248
1249static int scgi_reconnect(server *srv, handler_ctx *hctx) {
1250	plugin_data *p    = hctx->plugin_data;
1251
1252	/* child died
1253	 *
1254	 * 1.
1255	 *
1256	 * connect was ok, connection was accepted
1257	 * but the php accept loop checks after the accept if it should die or not.
1258	 *
1259	 * if yes we can only detect it at a write()
1260	 *
1261	 * next step is resetting this attemp and setup a connection again
1262	 *
1263	 * if we have more then 5 reconnects for the same request, die
1264	 *
1265	 * 2.
1266	 *
1267	 * we have a connection but the child died by some other reason
1268	 *
1269	 */
1270
1271	fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd);
1272	fdevent_unregister(srv->ev, hctx->fd);
1273	close(hctx->fd);
1274	srv->cur_fds--;
1275
1276	scgi_set_state(srv, hctx, FCGI_STATE_INIT);
1277
1278	hctx->request_id = 0;
1279	hctx->reconnects++;
1280
1281	if (p->conf.debug) {
1282		log_error_write(srv, __FILE__, __LINE__, "sddb",
1283				"release proc:",
1284				hctx->fd,
1285				hctx->proc->pid, hctx->proc->socket);
1286	}
1287
1288	hctx->proc->load--;
1289	scgi_proclist_sort_down(srv, hctx->host, hctx->proc);
1290
1291	return 0;
1292}
1293
1294
1295static handler_t scgi_connection_reset(server *srv, connection *con, void *p_d) {
1296	plugin_data *p = p_d;
1297
1298	scgi_connection_cleanup(srv, con->plugin_ctx[p->id]);
1299
1300	return HANDLER_GO_ON;
1301}
1302
1303
1304static int scgi_env_add(buffer *env, const char *key, size_t key_len, const char *val, size_t val_len) {
1305	size_t len;
1306
1307	if (!key || !val) return -1;
1308
1309	len = key_len + val_len + 2;
1310
1311	buffer_string_prepare_append(env, len);
1312
1313	buffer_append_string_len(env, key, key_len);
1314	buffer_append_string_len(env, "", 1);
1315	buffer_append_string_len(env, val, val_len);
1316	buffer_append_string_len(env, "", 1);
1317
1318	return 0;
1319}
1320
1321
1322/**
1323 *
1324 * returns
1325 *   -1 error
1326 *    0 connected
1327 *    1 not connected yet
1328 */
1329
1330static int scgi_establish_connection(server *srv, handler_ctx *hctx) {
1331	struct sockaddr *scgi_addr;
1332	struct sockaddr_in scgi_addr_in;
1333#ifdef HAVE_SYS_UN_H
1334	struct sockaddr_un scgi_addr_un;
1335#endif
1336	socklen_t servlen;
1337
1338	scgi_extension_host *host = hctx->host;
1339	scgi_proc *proc   = hctx->proc;
1340	int scgi_fd       = hctx->fd;
1341
1342	if (!buffer_string_is_empty(proc->socket)) {
1343#ifdef HAVE_SYS_UN_H
1344		/* use the unix domain socket */
1345		memset(&scgi_addr_un, 0, sizeof(scgi_addr_un));
1346		scgi_addr_un.sun_family = AF_UNIX;
1347		if (buffer_string_length(proc->socket) + 1 > sizeof(scgi_addr_un.sun_path)) {
1348			log_error_write(srv, __FILE__, __LINE__, "sB",
1349					"ERROR: Unix Domain socket filename too long:",
1350					proc->socket);
1351			return -1;
1352		}
1353		memcpy(scgi_addr_un.sun_path, proc->socket->ptr, buffer_string_length(proc->socket) + 1);
1354
1355#ifdef SUN_LEN
1356		servlen = SUN_LEN(&scgi_addr_un);
1357#else
1358		/* stevens says: */
1359		servlen = buffer_string_length(proc->socket) + 1 + sizeof(scgi_addr_un.sun_family);
1360#endif
1361		scgi_addr = (struct sockaddr *) &scgi_addr_un;
1362#else
1363		return -1;
1364#endif
1365	} else {
1366		memset(&scgi_addr_in, 0, sizeof(scgi_addr_in));
1367		scgi_addr_in.sin_family = AF_INET;
1368		if (0 == inet_aton(host->host->ptr, &(scgi_addr_in.sin_addr))) {
1369			log_error_write(srv, __FILE__, __LINE__, "sbs",
1370					"converting IP-adress failed for", host->host,
1371					"\nBe sure to specify an IP address here");
1372
1373			return -1;
1374		}
1375		scgi_addr_in.sin_port = htons(proc->port);
1376		servlen = sizeof(scgi_addr_in);
1377
1378		scgi_addr = (struct sockaddr *) &scgi_addr_in;
1379	}
1380
1381	if (-1 == connect(scgi_fd, scgi_addr, servlen)) {
1382		if (errno == EINPROGRESS ||
1383		    errno == EALREADY ||
1384		    errno == EINTR) {
1385			if (hctx->conf.debug) {
1386				log_error_write(srv, __FILE__, __LINE__, "sd",
1387						"connect delayed, will continue later:", scgi_fd);
1388			}
1389
1390			return 1;
1391		} else {
1392			log_error_write(srv, __FILE__, __LINE__, "sdsddb",
1393					"connect failed:", scgi_fd,
1394					strerror(errno), errno,
1395					proc->port, proc->socket);
1396
1397			if (errno == EAGAIN) {
1398				/* this is Linux only */
1399
1400				log_error_write(srv, __FILE__, __LINE__, "s",
1401						"If this happend on Linux: You have been run out of local ports. "
1402						"Check the manual, section Performance how to handle this.");
1403			}
1404
1405			return -1;
1406		}
1407	}
1408	if (hctx->conf.debug > 1) {
1409		log_error_write(srv, __FILE__, __LINE__, "sd",
1410				"connect succeeded: ", scgi_fd);
1411	}
1412
1413
1414
1415	return 0;
1416}
1417
1418static int scgi_env_add_request_headers(server *srv, connection *con, plugin_data *p) {
1419	size_t i;
1420
1421	for (i = 0; i < con->request.headers->used; i++) {
1422		data_string *ds;
1423
1424		ds = (data_string *)con->request.headers->data[i];
1425
1426		if (!buffer_is_empty(ds->value) && !buffer_is_empty(ds->key)) {
1427			buffer_copy_string_encoded_cgi_varnames(srv->tmp_buf, CONST_BUF_LEN(ds->key), 1);
1428
1429			scgi_env_add(p->scgi_env, CONST_BUF_LEN(srv->tmp_buf), CONST_BUF_LEN(ds->value));
1430		}
1431	}
1432
1433	for (i = 0; i < con->environment->used; i++) {
1434		data_string *ds;
1435
1436		ds = (data_string *)con->environment->data[i];
1437
1438		if (!buffer_is_empty(ds->value) && !buffer_is_empty(ds->key)) {
1439			buffer_copy_string_encoded_cgi_varnames(srv->tmp_buf, CONST_BUF_LEN(ds->key), 0);
1440
1441			scgi_env_add(p->scgi_env, CONST_BUF_LEN(srv->tmp_buf), CONST_BUF_LEN(ds->value));
1442		}
1443	}
1444
1445	return 0;
1446}
1447
1448
1449static int scgi_create_env(server *srv, handler_ctx *hctx) {
1450	char buf[LI_ITOSTRING_LENGTH];
1451	const char *s;
1452#ifdef HAVE_IPV6
1453	char b2[INET6_ADDRSTRLEN + 1];
1454#endif
1455	buffer *b;
1456
1457	plugin_data *p    = hctx->plugin_data;
1458	scgi_extension_host *host= hctx->host;
1459
1460	connection *con   = hctx->remote_conn;
1461	server_socket *srv_sock = con->srv_socket;
1462
1463	sock_addr our_addr;
1464	socklen_t our_addr_len;
1465
1466	buffer_string_prepare_copy(p->scgi_env, 1023);
1467
1468	/* CGI-SPEC 6.1.2, FastCGI spec 6.3 and SCGI spec */
1469
1470	li_itostr(buf, con->request.content_length);
1471	scgi_env_add(p->scgi_env, CONST_STR_LEN("CONTENT_LENGTH"), buf, strlen(buf));
1472	scgi_env_add(p->scgi_env, CONST_STR_LEN("SCGI"), CONST_STR_LEN("1"));
1473
1474
1475	if (buffer_is_empty(con->conf.server_tag)) {
1476		scgi_env_add(p->scgi_env, CONST_STR_LEN("SERVER_SOFTWARE"), CONST_STR_LEN(PACKAGE_DESC));
1477	} else {
1478		scgi_env_add(p->scgi_env, CONST_STR_LEN("SERVER_SOFTWARE"), CONST_BUF_LEN(con->conf.server_tag));
1479	}
1480
1481	if (!buffer_is_empty(con->server_name)) {
1482		size_t len = buffer_string_length(con->server_name);
1483
1484		if (con->server_name->ptr[0] == '[') {
1485			const char *colon = strstr(con->server_name->ptr, "]:");
1486			if (colon) len = (colon + 1) - con->server_name->ptr;
1487		} else {
1488			const char *colon = strchr(con->server_name->ptr, ':');
1489			if (colon) len = colon - con->server_name->ptr;
1490		}
1491
1492		scgi_env_add(p->scgi_env, CONST_STR_LEN("SERVER_NAME"), con->server_name->ptr, len);
1493	} else {
1494#ifdef HAVE_IPV6
1495		s = inet_ntop(srv_sock->addr.plain.sa_family,
1496			      srv_sock->addr.plain.sa_family == AF_INET6 ?
1497			      (const void *) &(srv_sock->addr.ipv6.sin6_addr) :
1498			      (const void *) &(srv_sock->addr.ipv4.sin_addr),
1499			      b2, sizeof(b2)-1);
1500#else
1501		s = inet_ntoa(srv_sock->addr.ipv4.sin_addr);
1502#endif
1503		force_assert(s);
1504		scgi_env_add(p->scgi_env, CONST_STR_LEN("SERVER_NAME"), s, strlen(s));
1505	}
1506
1507	scgi_env_add(p->scgi_env, CONST_STR_LEN("GATEWAY_INTERFACE"), CONST_STR_LEN("CGI/1.1"));
1508
1509	li_utostr(buf,
1510#ifdef HAVE_IPV6
1511	       ntohs(srv_sock->addr.plain.sa_family ? srv_sock->addr.ipv6.sin6_port : srv_sock->addr.ipv4.sin_port)
1512#else
1513	       ntohs(srv_sock->addr.ipv4.sin_port)
1514#endif
1515	       );
1516
1517	scgi_env_add(p->scgi_env, CONST_STR_LEN("SERVER_PORT"), buf, strlen(buf));
1518
1519	/* get the server-side of the connection to the client */
1520	our_addr_len = sizeof(our_addr);
1521
1522	if (-1 == getsockname(con->fd, &(our_addr.plain), &our_addr_len)) {
1523		s = inet_ntop_cache_get_ip(srv, &(srv_sock->addr));
1524	} else {
1525		s = inet_ntop_cache_get_ip(srv, &(our_addr));
1526	}
1527	scgi_env_add(p->scgi_env, CONST_STR_LEN("SERVER_ADDR"), s, strlen(s));
1528
1529	li_utostr(buf,
1530#ifdef HAVE_IPV6
1531	       ntohs(con->dst_addr.plain.sa_family ? con->dst_addr.ipv6.sin6_port : con->dst_addr.ipv4.sin_port)
1532#else
1533	       ntohs(con->dst_addr.ipv4.sin_port)
1534#endif
1535	       );
1536
1537	scgi_env_add(p->scgi_env, CONST_STR_LEN("REMOTE_PORT"), buf, strlen(buf));
1538
1539	s = inet_ntop_cache_get_ip(srv, &(con->dst_addr));
1540	force_assert(s);
1541	scgi_env_add(p->scgi_env, CONST_STR_LEN("REMOTE_ADDR"), s, strlen(s));
1542
1543	/*
1544	 * SCRIPT_NAME, PATH_INFO and PATH_TRANSLATED according to
1545	 * http://cgi-spec.golux.com/draft-coar-cgi-v11-03-clean.html
1546	 * (6.1.14, 6.1.6, 6.1.7)
1547	 */
1548
1549	scgi_env_add(p->scgi_env, CONST_STR_LEN("SCRIPT_NAME"), CONST_BUF_LEN(con->uri.path));
1550
1551	if (!buffer_string_is_empty(con->request.pathinfo)) {
1552		scgi_env_add(p->scgi_env, CONST_STR_LEN("PATH_INFO"), CONST_BUF_LEN(con->request.pathinfo));
1553
1554		/* PATH_TRANSLATED is only defined if PATH_INFO is set */
1555
1556		if (!buffer_string_is_empty(host->docroot)) {
1557			buffer_copy_buffer(p->path, host->docroot);
1558		} else {
1559			buffer_copy_buffer(p->path, con->physical.basedir);
1560		}
1561		buffer_append_string_buffer(p->path, con->request.pathinfo);
1562		scgi_env_add(p->scgi_env, CONST_STR_LEN("PATH_TRANSLATED"), CONST_BUF_LEN(p->path));
1563	} else {
1564		scgi_env_add(p->scgi_env, CONST_STR_LEN("PATH_INFO"), CONST_STR_LEN(""));
1565	}
1566
1567	/*
1568	 * SCRIPT_FILENAME and DOCUMENT_ROOT for php. The PHP manual
1569	 * http://www.php.net/manual/en/reserved.variables.php
1570	 * treatment of PATH_TRANSLATED is different from the one of CGI specs.
1571	 * TODO: this code should be checked against cgi.fix_pathinfo php
1572	 * parameter.
1573	 */
1574
1575	if (!buffer_string_is_empty(host->docroot)) {
1576		/*
1577		 * rewrite SCRIPT_FILENAME
1578		 *
1579		 */
1580
1581		buffer_copy_buffer(p->path, host->docroot);
1582		buffer_append_string_buffer(p->path, con->uri.path);
1583
1584		scgi_env_add(p->scgi_env, CONST_STR_LEN("SCRIPT_FILENAME"), CONST_BUF_LEN(p->path));
1585		scgi_env_add(p->scgi_env, CONST_STR_LEN("DOCUMENT_ROOT"), CONST_BUF_LEN(host->docroot));
1586	} else {
1587		buffer_copy_buffer(p->path, con->physical.path);
1588
1589		scgi_env_add(p->scgi_env, CONST_STR_LEN("SCRIPT_FILENAME"), CONST_BUF_LEN(p->path));
1590		scgi_env_add(p->scgi_env, CONST_STR_LEN("DOCUMENT_ROOT"), CONST_BUF_LEN(con->physical.basedir));
1591	}
1592	scgi_env_add(p->scgi_env, CONST_STR_LEN("REQUEST_URI"), CONST_BUF_LEN(con->request.orig_uri));
1593	if (!buffer_is_equal(con->request.uri, con->request.orig_uri)) {
1594		scgi_env_add(p->scgi_env, CONST_STR_LEN("REDIRECT_URI"), CONST_BUF_LEN(con->request.uri));
1595	}
1596	if (!buffer_string_is_empty(con->uri.query)) {
1597		scgi_env_add(p->scgi_env, CONST_STR_LEN("QUERY_STRING"), CONST_BUF_LEN(con->uri.query));
1598	} else {
1599		scgi_env_add(p->scgi_env, CONST_STR_LEN("QUERY_STRING"), CONST_STR_LEN(""));
1600	}
1601
1602	s = get_http_method_name(con->request.http_method);
1603	force_assert(s);
1604	scgi_env_add(p->scgi_env, CONST_STR_LEN("REQUEST_METHOD"), s, strlen(s));
1605	scgi_env_add(p->scgi_env, CONST_STR_LEN("REDIRECT_STATUS"), CONST_STR_LEN("200")); /* if php is compiled with --force-redirect */
1606	s = get_http_version_name(con->request.http_version);
1607	force_assert(s);
1608	scgi_env_add(p->scgi_env, CONST_STR_LEN("SERVER_PROTOCOL"), s, strlen(s));
1609
1610#ifdef USE_OPENSSL
1611	if (buffer_is_equal_caseless_string(con->uri.scheme, CONST_STR_LEN("https"))) {
1612		scgi_env_add(p->scgi_env, CONST_STR_LEN("HTTPS"), CONST_STR_LEN("on"));
1613	}
1614#endif
1615
1616	scgi_env_add_request_headers(srv, con, p);
1617
1618	b = buffer_init();
1619
1620	buffer_append_int(b, buffer_string_length(p->scgi_env));
1621	buffer_append_string_len(b, CONST_STR_LEN(":"));
1622	buffer_append_string_buffer(b, p->scgi_env);
1623	buffer_append_string_len(b, CONST_STR_LEN(","));
1624
1625	chunkqueue_append_buffer(hctx->wb, b);
1626	buffer_free(b);
1627
1628	if (con->request.content_length) {
1629		chunkqueue *req_cq = con->request_content_queue;
1630
1631		chunkqueue_steal(hctx->wb, req_cq, req_cq->bytes_in);
1632	}
1633
1634	return 0;
1635}
1636
1637static int scgi_response_parse(server *srv, connection *con, plugin_data *p, buffer *in, int eol) {
1638	char *ns;
1639	const char *s;
1640	int line = 0;
1641
1642	UNUSED(srv);
1643
1644	buffer_copy_buffer(p->parse_response, in);
1645
1646	for (s = p->parse_response->ptr;
1647	     NULL != (ns = (eol == EOL_RN ? strstr(s, "\r\n") : strchr(s, '\n')));
1648	     s = ns + (eol == EOL_RN ? 2 : 1), line++) {
1649		const char *key, *value;
1650		int key_len;
1651		data_string *ds;
1652
1653		ns[0] = '\0';
1654
1655		if (line == 0 &&
1656		    0 == strncmp(s, "HTTP/1.", 7)) {
1657			/* non-parsed header ... we parse them anyway */
1658
1659			if ((s[7] == '1' ||
1660			     s[7] == '0') &&
1661			    s[8] == ' ') {
1662				int status;
1663				/* after the space should be a status code for us */
1664
1665				status = strtol(s+9, NULL, 10);
1666
1667				if (status >= 100 && status < 1000) {
1668					/* we expected 3 digits got them */
1669					con->parsed_response |= HTTP_STATUS;
1670					con->http_status = status;
1671				}
1672			}
1673		} else {
1674
1675			key = s;
1676			if (NULL == (value = strchr(s, ':'))) {
1677				/* we expect: "<key>: <value>\r\n" */
1678				continue;
1679			}
1680
1681			key_len = value - key;
1682			value += 1;
1683
1684			/* skip LWS */
1685			while (*value == ' ' || *value == '\t') value++;
1686
1687			if (NULL == (ds = (data_string *)array_get_unused_element(con->response.headers, TYPE_STRING))) {
1688				ds = data_response_init();
1689			}
1690			buffer_copy_string_len(ds->key, key, key_len);
1691			buffer_copy_string(ds->value, value);
1692
1693			array_insert_unique(con->response.headers, (data_unset *)ds);
1694
1695			switch(key_len) {
1696			case 4:
1697				if (0 == strncasecmp(key, "Date", key_len)) {
1698					con->parsed_response |= HTTP_DATE;
1699				}
1700				break;
1701			case 6:
1702				if (0 == strncasecmp(key, "Status", key_len)) {
1703					con->http_status = strtol(value, NULL, 10);
1704					con->parsed_response |= HTTP_STATUS;
1705				}
1706				break;
1707			case 8:
1708				if (0 == strncasecmp(key, "Location", key_len)) {
1709					con->parsed_response |= HTTP_LOCATION;
1710				}
1711				break;
1712			case 10:
1713				if (0 == strncasecmp(key, "Connection", key_len)) {
1714					con->response.keep_alive = (0 == strcasecmp(value, "Keep-Alive")) ? 1 : 0;
1715					con->parsed_response |= HTTP_CONNECTION;
1716				}
1717				break;
1718			case 14:
1719				if (0 == strncasecmp(key, "Content-Length", key_len)) {
1720					con->response.content_length = strtol(value, NULL, 10);
1721					con->parsed_response |= HTTP_CONTENT_LENGTH;
1722				}
1723				break;
1724			default:
1725				break;
1726			}
1727		}
1728	}
1729
1730	/* CGI/1.1 rev 03 - 7.2.1.2 */
1731	if ((con->parsed_response & HTTP_LOCATION) &&
1732	    !(con->parsed_response & HTTP_STATUS)) {
1733		con->http_status = 302;
1734	}
1735
1736	return 0;
1737}
1738
1739
1740static int scgi_demux_response(server *srv, handler_ctx *hctx) {
1741	plugin_data *p    = hctx->plugin_data;
1742	connection  *con  = hctx->remote_conn;
1743
1744	while(1) {
1745		int n;
1746
1747		buffer_string_prepare_copy(hctx->response, 1023);
1748		if (-1 == (n = read(hctx->fd, hctx->response->ptr, hctx->response->size - 1))) {
1749			if (errno == EAGAIN || errno == EINTR) {
1750				/* would block, wait for signal */
1751				return 0;
1752			}
1753			/* error */
1754			log_error_write(srv, __FILE__, __LINE__, "sdd", strerror(errno), con->fd, hctx->fd);
1755			return -1;
1756		}
1757
1758		if (n == 0) {
1759			/* read finished */
1760
1761			con->file_finished = 1;
1762
1763			/* send final chunk */
1764			http_chunk_close(srv, con);
1765			joblist_append(srv, con);
1766
1767			return 1;
1768		}
1769
1770		buffer_commit(hctx->response, n);
1771
1772		/* split header from body */
1773
1774		if (con->file_started == 0) {
1775			char *c;
1776			int in_header = 0;
1777			int header_end = 0;
1778			int cp, eol = EOL_UNSET;
1779			size_t used = 0;
1780			size_t hlen = 0;
1781
1782			buffer_append_string_buffer(hctx->response_header, hctx->response);
1783
1784			/* nph (non-parsed headers) */
1785			if (0 == strncmp(hctx->response_header->ptr, "HTTP/1.", 7)) in_header = 1;
1786
1787			/* search for the \r\n\r\n or \n\n in the string */
1788			for (c = hctx->response_header->ptr, cp = 0, used = buffer_string_length(hctx->response_header); used; c++, cp++, used--) {
1789				if (*c == ':') in_header = 1;
1790				else if (*c == '\n') {
1791					if (in_header == 0) {
1792						/* got a response without a response header */
1793
1794						c = NULL;
1795						header_end = 1;
1796						break;
1797					}
1798
1799					if (eol == EOL_UNSET) eol = EOL_N;
1800
1801					if (*(c+1) == '\n') {
1802						header_end = 1;
1803						hlen = cp + 2;
1804						break;
1805					}
1806
1807				} else if (used > 1 && *c == '\r' && *(c+1) == '\n') {
1808					if (in_header == 0) {
1809						/* got a response without a response header */
1810
1811						c = NULL;
1812						header_end = 1;
1813						break;
1814					}
1815
1816					if (eol == EOL_UNSET) eol = EOL_RN;
1817
1818					if (used > 3 &&
1819					    *(c+2) == '\r' &&
1820					    *(c+3) == '\n') {
1821						header_end = 1;
1822						hlen = cp + 4;
1823						break;
1824					}
1825
1826					/* skip the \n */
1827					c++;
1828					cp++;
1829					used--;
1830				}
1831			}
1832
1833			if (header_end) {
1834				if (c == NULL) {
1835					/* no header, but a body */
1836
1837					if (con->request.http_version == HTTP_VERSION_1_1) {
1838						con->response.transfer_encoding = HTTP_TRANSFER_ENCODING_CHUNKED;
1839					}
1840
1841					http_chunk_append_buffer(srv, con, hctx->response_header);
1842					joblist_append(srv, con);
1843				} else {
1844					size_t blen = buffer_string_length(hctx->response_header) - hlen;
1845
1846					/* a small hack: terminate after at the second \r */
1847					buffer_string_set_length(hctx->response_header, hlen - 1);
1848
1849					/* parse the response header */
1850					scgi_response_parse(srv, con, p, hctx->response_header, eol);
1851
1852					/* enable chunked-transfer-encoding */
1853					if (con->request.http_version == HTTP_VERSION_1_1 &&
1854					    !(con->parsed_response & HTTP_CONTENT_LENGTH)) {
1855						con->response.transfer_encoding = HTTP_TRANSFER_ENCODING_CHUNKED;
1856					}
1857
1858					if (blen > 0) {
1859						http_chunk_append_mem(srv, con, hctx->response_header->ptr + hlen, blen);
1860						joblist_append(srv, con);
1861					}
1862				}
1863
1864				con->file_started = 1;
1865			}
1866		} else {
1867			http_chunk_append_buffer(srv, con, hctx->response);
1868			joblist_append(srv, con);
1869		}
1870
1871#if 0
1872		log_error_write(srv, __FILE__, __LINE__, "ddss", con->fd, hctx->fd, connection_get_state(con->state), b->ptr);
1873#endif
1874	}
1875
1876	return 0;
1877}
1878
1879
1880static int scgi_proclist_sort_up(server *srv, scgi_extension_host *host, scgi_proc *proc) {
1881	scgi_proc *p;
1882
1883	UNUSED(srv);
1884
1885	/* we have been the smallest of the current list
1886	 * and we want to insert the node sorted as soon
1887	 * possible
1888	 *
1889	 * 1 0 0 0 1 1 1
1890	 * |      ^
1891	 * |      |
1892	 * +------+
1893	 *
1894	 */
1895
1896	/* nothing to sort, only one element */
1897	if (host->first == proc && proc->next == NULL) return 0;
1898
1899	for (p = proc; p->next && p->next->load < proc->load; p = p->next);
1900
1901	/* no need to move something
1902	 *
1903	 * 1 2 2 2 3 3 3
1904	 * ^
1905	 * |
1906	 * +
1907	 *
1908	 */
1909	if (p == proc) return 0;
1910
1911	if (host->first == proc) {
1912		/* we have been the first elememt */
1913
1914		host->first = proc->next;
1915		host->first->prev = NULL;
1916	}
1917
1918	/* disconnect proc */
1919
1920	if (proc->prev) proc->prev->next = proc->next;
1921	if (proc->next) proc->next->prev = proc->prev;
1922
1923	/* proc should be right of p */
1924
1925	proc->next = p->next;
1926	proc->prev = p;
1927	if (p->next) p->next->prev = proc;
1928	p->next = proc;
1929#if 0
1930	for(p = host->first; p; p = p->next) {
1931		log_error_write(srv, __FILE__, __LINE__, "dd",
1932				p->pid, p->load);
1933	}
1934#else
1935	UNUSED(srv);
1936#endif
1937
1938	return 0;
1939}
1940
1941int scgi_proclist_sort_down(server *srv, scgi_extension_host *host, scgi_proc *proc) {
1942	scgi_proc *p;
1943
1944	UNUSED(srv);
1945
1946	/* we have been the smallest of the current list
1947	 * and we want to insert the node sorted as soon
1948	 * possible
1949	 *
1950	 *  0 0 0 0 1 0 1
1951	 * ^          |
1952	 * |          |
1953	 * +----------+
1954	 *
1955	 *
1956	 * the basic is idea is:
1957	 * - the last active scgi process should be still
1958	 *   in ram and is not swapped out yet
1959	 * - processes that are not reused will be killed
1960	 *   after some time by the trigger-handler
1961	 * - remember it as:
1962	 *   everything > 0 is hot
1963	 *   all unused procs are colder the more right they are
1964	 *   ice-cold processes are propably unused since more
1965	 *   than 'unused-timeout', are swaped out and won't be
1966	 *   reused in the next seconds anyway.
1967	 *
1968	 */
1969
1970	/* nothing to sort, only one element */
1971	if (host->first == proc && proc->next == NULL) return 0;
1972
1973	for (p = host->first; p != proc && p->load < proc->load; p = p->next);
1974
1975
1976	/* no need to move something
1977	 *
1978	 * 1 2 2 2 3 3 3
1979	 * ^
1980	 * |
1981	 * +
1982	 *
1983	 */
1984	if (p == proc) return 0;
1985
1986	/* we have to move left. If we are already the first element
1987	 * we are done */
1988	if (host->first == proc) return 0;
1989
1990	/* release proc */
1991	if (proc->prev) proc->prev->next = proc->next;
1992	if (proc->next) proc->next->prev = proc->prev;
1993
1994	/* proc should be left of p */
1995	proc->next = p;
1996	proc->prev = p->prev;
1997	if (p->prev) p->prev->next = proc;
1998	p->prev = proc;
1999
2000	if (proc->prev == NULL) host->first = proc;
2001#if 0
2002	for(p = host->first; p; p = p->next) {
2003		log_error_write(srv, __FILE__, __LINE__, "dd",
2004				p->pid, p->load);
2005	}
2006#else
2007	UNUSED(srv);
2008#endif
2009
2010	return 0;
2011}
2012
2013static int scgi_restart_dead_procs(server *srv, plugin_data *p, scgi_extension_host *host) {
2014	scgi_proc *proc;
2015
2016	for (proc = host->first; proc; proc = proc->next) {
2017		if (p->conf.debug) {
2018			log_error_write(srv, __FILE__, __LINE__,  "sbdbdddd",
2019					"proc:",
2020					host->host, proc->port,
2021					proc->socket,
2022					proc->state,
2023					proc->is_local,
2024					proc->load,
2025					proc->pid);
2026		}
2027
2028		if (0 == proc->is_local) {
2029			/*
2030			 * external servers might get disabled
2031			 *
2032			 * enable the server again, perhaps it is back again
2033			 */
2034
2035			if ((proc->state == PROC_STATE_DISABLED) &&
2036			    (srv->cur_ts - proc->disable_ts > host->disable_time)) {
2037				proc->state = PROC_STATE_RUNNING;
2038				host->active_procs++;
2039
2040				log_error_write(srv, __FILE__, __LINE__,  "sbdb",
2041						"fcgi-server re-enabled:",
2042						host->host, host->port,
2043						host->unixsocket);
2044			}
2045		} else {
2046			/* the child should not terminate at all */
2047			int status;
2048
2049			if (proc->state == PROC_STATE_DIED_WAIT_FOR_PID) {
2050				switch(waitpid(proc->pid, &status, WNOHANG)) {
2051				case 0:
2052					/* child is still alive */
2053					break;
2054				case -1:
2055					break;
2056				default:
2057					if (WIFEXITED(status)) {
2058#if 0
2059						log_error_write(srv, __FILE__, __LINE__, "sdsd",
2060								"child exited, pid:", proc->pid,
2061								"status:", WEXITSTATUS(status));
2062#endif
2063					} else if (WIFSIGNALED(status)) {
2064						log_error_write(srv, __FILE__, __LINE__, "sd",
2065								"child signaled:",
2066								WTERMSIG(status));
2067					} else {
2068						log_error_write(srv, __FILE__, __LINE__, "sd",
2069								"child died somehow:",
2070								status);
2071					}
2072
2073					proc->state = PROC_STATE_DIED;
2074					break;
2075				}
2076			}
2077
2078			/*
2079			 * local servers might died, but we restart them
2080			 *
2081			 */
2082			if (proc->state == PROC_STATE_DIED &&
2083			    proc->load == 0) {
2084				/* restart the child */
2085
2086				if (p->conf.debug) {
2087					log_error_write(srv, __FILE__, __LINE__, "ssdsbsdsd",
2088							"--- scgi spawning",
2089							"\n\tport:", host->port,
2090							"\n\tsocket", host->unixsocket,
2091							"\n\tcurrent:", 1, "/", host->min_procs);
2092				}
2093
2094				if (scgi_spawn_connection(srv, p, host, proc)) {
2095					log_error_write(srv, __FILE__, __LINE__, "s",
2096							"ERROR: spawning fcgi failed.");
2097					return HANDLER_ERROR;
2098				}
2099
2100				scgi_proclist_sort_down(srv, host, proc);
2101			}
2102		}
2103	}
2104
2105	return 0;
2106}
2107
2108
2109static handler_t scgi_write_request(server *srv, handler_ctx *hctx) {
2110	plugin_data *p    = hctx->plugin_data;
2111	scgi_extension_host *host= hctx->host;
2112	connection *con   = hctx->remote_conn;
2113
2114	int ret;
2115
2116	/* sanity check */
2117	if (!host) {
2118		log_error_write(srv, __FILE__, __LINE__, "s", "fatal error: host = NULL");
2119		return HANDLER_ERROR;
2120	}
2121	if (((buffer_string_is_empty(host->host) || !host->port) && buffer_string_is_empty(host->unixsocket))) {
2122		log_error_write(srv, __FILE__, __LINE__, "sxddd",
2123				"write-req: error",
2124				host,
2125				buffer_string_length(host->host),
2126				host->port,
2127				buffer_string_length(host->unixsocket));
2128		return HANDLER_ERROR;
2129	}
2130
2131
2132	switch(hctx->state) {
2133	case FCGI_STATE_INIT:
2134		ret = buffer_string_is_empty(host->unixsocket) ? AF_INET : AF_UNIX;
2135
2136		if (-1 == (hctx->fd = socket(ret, SOCK_STREAM, 0))) {
2137			if (errno == EMFILE ||
2138			    errno == EINTR) {
2139				log_error_write(srv, __FILE__, __LINE__, "sd",
2140						"wait for fd at connection:", con->fd);
2141
2142				return HANDLER_WAIT_FOR_FD;
2143			}
2144
2145			log_error_write(srv, __FILE__, __LINE__, "ssdd",
2146					"socket failed:", strerror(errno), srv->cur_fds, srv->max_fds);
2147			return HANDLER_ERROR;
2148		}
2149		hctx->fde_ndx = -1;
2150
2151		srv->cur_fds++;
2152
2153		fdevent_register(srv->ev, hctx->fd, scgi_handle_fdevent, hctx);
2154
2155		if (-1 == fdevent_fcntl_set(srv->ev, hctx->fd)) {
2156			log_error_write(srv, __FILE__, __LINE__, "ss",
2157					"fcntl failed: ", strerror(errno));
2158
2159			return HANDLER_ERROR;
2160		}
2161
2162		/* fall through */
2163	case FCGI_STATE_CONNECT:
2164		if (hctx->state == FCGI_STATE_INIT) {
2165			for (hctx->proc = hctx->host->first;
2166			     hctx->proc && hctx->proc->state != PROC_STATE_RUNNING;
2167			     hctx->proc = hctx->proc->next);
2168
2169			/* all childs are dead */
2170			if (hctx->proc == NULL) {
2171				hctx->fde_ndx = -1;
2172
2173				return HANDLER_ERROR;
2174			}
2175
2176			if (hctx->proc->is_local) {
2177				hctx->pid = hctx->proc->pid;
2178			}
2179
2180			switch (scgi_establish_connection(srv, hctx)) {
2181			case 1:
2182				scgi_set_state(srv, hctx, FCGI_STATE_CONNECT);
2183
2184				/* connection is in progress, wait for an event and call getsockopt() below */
2185
2186				fdevent_event_set(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_OUT);
2187
2188				return HANDLER_WAIT_FOR_EVENT;
2189			case -1:
2190				/* if ECONNREFUSED choose another connection -> FIXME */
2191				hctx->fde_ndx = -1;
2192
2193				return HANDLER_ERROR;
2194			default:
2195				/* everything is ok, go on */
2196				break;
2197			}
2198
2199
2200		} else {
2201			int socket_error;
2202			socklen_t socket_error_len = sizeof(socket_error);
2203
2204			/* try to finish the connect() */
2205			if (0 != getsockopt(hctx->fd, SOL_SOCKET, SO_ERROR, &socket_error, &socket_error_len)) {
2206				log_error_write(srv, __FILE__, __LINE__, "ss",
2207						"getsockopt failed:", strerror(errno));
2208
2209				return HANDLER_ERROR;
2210			}
2211			if (socket_error != 0) {
2212				if (!hctx->proc->is_local || p->conf.debug) {
2213					/* local procs get restarted */
2214
2215					log_error_write(srv, __FILE__, __LINE__, "ss",
2216							"establishing connection failed:", strerror(socket_error),
2217							"port:", hctx->proc->port);
2218				}
2219
2220				return HANDLER_ERROR;
2221			}
2222		}
2223
2224		/* ok, we have the connection */
2225
2226		hctx->proc->load++;
2227		hctx->proc->last_used = srv->cur_ts;
2228		hctx->got_proc = 1;
2229
2230		if (p->conf.debug) {
2231			log_error_write(srv, __FILE__, __LINE__, "sddbdd",
2232					"got proc:",
2233					hctx->fd,
2234					hctx->proc->pid,
2235					hctx->proc->socket,
2236					hctx->proc->port,
2237					hctx->proc->load);
2238		}
2239
2240		/* move the proc-list entry down the list */
2241		scgi_proclist_sort_up(srv, hctx->host, hctx->proc);
2242
2243		scgi_set_state(srv, hctx, FCGI_STATE_PREPARE_WRITE);
2244		/* fall through */
2245	case FCGI_STATE_PREPARE_WRITE:
2246		scgi_create_env(srv, hctx);
2247
2248		scgi_set_state(srv, hctx, FCGI_STATE_WRITE);
2249
2250		/* fall through */
2251	case FCGI_STATE_WRITE:
2252		ret = srv->network_backend_write(srv, con, hctx->fd, hctx->wb, MAX_WRITE_LIMIT);
2253
2254		chunkqueue_remove_finished_chunks(hctx->wb);
2255
2256		if (ret < 0) {
2257			if (errno == ENOTCONN || ret == -2) {
2258				/* the connection got dropped after accept()
2259				 *
2260				 * this is most of the time a PHP which dies
2261				 * after PHP_FCGI_MAX_REQUESTS
2262				 *
2263				 */
2264				if (hctx->wb->bytes_out == 0 &&
2265				    hctx->reconnects < 5) {
2266					usleep(10000); /* take away the load of the webserver
2267							* to let the php a chance to restart
2268							*/
2269
2270					scgi_reconnect(srv, hctx);
2271
2272					return HANDLER_WAIT_FOR_FD;
2273				}
2274
2275				/* not reconnected ... why
2276				 *
2277				 * far@#lighttpd report this for FreeBSD
2278				 *
2279				 */
2280
2281				log_error_write(srv, __FILE__, __LINE__, "ssosd",
2282						"connection was dropped after accept(). reconnect() denied:",
2283						"write-offset:", hctx->wb->bytes_out,
2284						"reconnect attempts:", hctx->reconnects);
2285
2286				return HANDLER_ERROR;
2287			} else {
2288				/* -1 == ret => error on our side */
2289				log_error_write(srv, __FILE__, __LINE__, "ssd",
2290					"write failed:", strerror(errno), errno);
2291
2292				return HANDLER_ERROR;
2293			}
2294		}
2295
2296		if (hctx->wb->bytes_out == hctx->wb->bytes_in) {
2297			/* we don't need the out event anymore */
2298			fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd);
2299			fdevent_event_set(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN);
2300			scgi_set_state(srv, hctx, FCGI_STATE_READ);
2301		} else {
2302			fdevent_event_set(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_OUT);
2303
2304			return HANDLER_WAIT_FOR_EVENT;
2305		}
2306
2307		break;
2308	case FCGI_STATE_READ:
2309		/* waiting for a response */
2310		break;
2311	default:
2312		log_error_write(srv, __FILE__, __LINE__, "s", "(debug) unknown state");
2313		return HANDLER_ERROR;
2314	}
2315
2316	return HANDLER_WAIT_FOR_EVENT;
2317}
2318
2319SUBREQUEST_FUNC(mod_scgi_handle_subrequest) {
2320	plugin_data *p = p_d;
2321
2322	handler_ctx *hctx = con->plugin_ctx[p->id];
2323	scgi_proc *proc;
2324	scgi_extension_host *host;
2325
2326	if (NULL == hctx) return HANDLER_GO_ON;
2327
2328	/* not my job */
2329	if (con->mode != p->id) return HANDLER_GO_ON;
2330
2331	/* ok, create the request */
2332	switch(scgi_write_request(srv, hctx)) {
2333	case HANDLER_ERROR:
2334		proc = hctx->proc;
2335		host = hctx->host;
2336
2337		if (proc &&
2338		    0 == proc->is_local &&
2339		    proc->state != PROC_STATE_DISABLED) {
2340			/* only disable remote servers as we don't manage them*/
2341
2342			log_error_write(srv, __FILE__, __LINE__,  "sbdb", "fcgi-server disabled:",
2343					host->host,
2344					proc->port,
2345					proc->socket);
2346
2347			/* disable this server */
2348			proc->disable_ts = srv->cur_ts;
2349			proc->state = PROC_STATE_DISABLED;
2350			host->active_procs--;
2351		}
2352
2353		if (hctx->state == FCGI_STATE_INIT ||
2354		    hctx->state == FCGI_STATE_CONNECT) {
2355			/* connect() or getsockopt() failed,
2356			 * restart the request-handling
2357			 */
2358			if (proc && proc->is_local) {
2359
2360				if (p->conf.debug) {
2361					log_error_write(srv, __FILE__, __LINE__,  "sbdb", "connect() to scgi failed, restarting the request-handling:",
2362							host->host,
2363							proc->port,
2364							proc->socket);
2365				}
2366
2367				/*
2368				 * several hctx might reference the same proc
2369				 *
2370				 * Only one of them should mark the proc as dead all the other
2371				 * ones should just take a new one.
2372				 *
2373				 * If a new proc was started with the old struct this might lead
2374				 * the mark a perfect proc as dead otherwise
2375				 *
2376				 */
2377				if (proc->state == PROC_STATE_RUNNING &&
2378				    hctx->pid == proc->pid) {
2379					proc->state = PROC_STATE_DIED_WAIT_FOR_PID;
2380				}
2381			}
2382			scgi_restart_dead_procs(srv, p, host);
2383
2384			scgi_connection_cleanup(srv, hctx);
2385
2386			buffer_reset(con->physical.path);
2387			con->mode = DIRECT;
2388			joblist_append(srv, con);
2389
2390			/* mis-using HANDLER_WAIT_FOR_FD to break out of the loop
2391			 * and hope that the childs will be restarted
2392			 *
2393			 */
2394			return HANDLER_WAIT_FOR_FD;
2395		} else {
2396			scgi_connection_cleanup(srv, hctx);
2397
2398			buffer_reset(con->physical.path);
2399			con->mode = DIRECT;
2400			con->http_status = 503;
2401
2402			return HANDLER_FINISHED;
2403		}
2404	case HANDLER_WAIT_FOR_EVENT:
2405		if (con->file_started == 1) {
2406			return HANDLER_FINISHED;
2407		} else {
2408			return HANDLER_WAIT_FOR_EVENT;
2409		}
2410	case HANDLER_WAIT_FOR_FD:
2411		return HANDLER_WAIT_FOR_FD;
2412	default:
2413		log_error_write(srv, __FILE__, __LINE__, "s", "subrequest write-req default");
2414		return HANDLER_ERROR;
2415	}
2416}
2417
2418static handler_t scgi_connection_close(server *srv, handler_ctx *hctx) {
2419	connection  *con;
2420
2421	if (NULL == hctx) return HANDLER_GO_ON;
2422
2423	con  = hctx->remote_conn;
2424
2425	log_error_write(srv, __FILE__, __LINE__, "ssdsd",
2426			"emergency exit: scgi:",
2427			"connection-fd:", con->fd,
2428			"fcgi-fd:", hctx->fd);
2429
2430	scgi_connection_cleanup(srv, hctx);
2431
2432	return HANDLER_FINISHED;
2433}
2434
2435
2436static handler_t scgi_handle_fdevent(server *srv, void *ctx, int revents) {
2437	handler_ctx *hctx = ctx;
2438	connection  *con  = hctx->remote_conn;
2439	plugin_data *p    = hctx->plugin_data;
2440
2441	scgi_proc *proc   = hctx->proc;
2442	scgi_extension_host *host= hctx->host;
2443
2444	if ((revents & FDEVENT_IN) &&
2445	    hctx->state == FCGI_STATE_READ) {
2446		switch (scgi_demux_response(srv, hctx)) {
2447		case 0:
2448			break;
2449		case 1:
2450			/* we are done */
2451			scgi_connection_cleanup(srv, hctx);
2452
2453			joblist_append(srv, con);
2454			return HANDLER_FINISHED;
2455		case -1:
2456			if (proc->pid && proc->state != PROC_STATE_DIED) {
2457				int status;
2458
2459				/* only fetch the zombie if it is not already done */
2460
2461				switch(waitpid(proc->pid, &status, WNOHANG)) {
2462				case 0:
2463					/* child is still alive */
2464					break;
2465				case -1:
2466					break;
2467				default:
2468					/* the child should not terminate at all */
2469					if (WIFEXITED(status)) {
2470						log_error_write(srv, __FILE__, __LINE__, "sdsd",
2471								"child exited, pid:", proc->pid,
2472								"status:", WEXITSTATUS(status));
2473					} else if (WIFSIGNALED(status)) {
2474						log_error_write(srv, __FILE__, __LINE__, "sd",
2475								"child signaled:",
2476								WTERMSIG(status));
2477					} else {
2478						log_error_write(srv, __FILE__, __LINE__, "sd",
2479								"child died somehow:",
2480								status);
2481					}
2482
2483					if (p->conf.debug) {
2484						log_error_write(srv, __FILE__, __LINE__, "ssdsbsdsd",
2485								"--- scgi spawning",
2486								"\n\tport:", host->port,
2487								"\n\tsocket", host->unixsocket,
2488								"\n\tcurrent:", 1, "/", host->min_procs);
2489					}
2490
2491					if (scgi_spawn_connection(srv, p, host, proc)) {
2492						/* child died */
2493						proc->state = PROC_STATE_DIED;
2494					} else {
2495						scgi_proclist_sort_down(srv, host, proc);
2496					}
2497
2498					break;
2499				}
2500			}
2501
2502			if (con->file_started == 0) {
2503				/* nothing has been send out yet, try to use another child */
2504
2505				if (hctx->wb->bytes_out == 0 &&
2506				    hctx->reconnects < 5) {
2507					scgi_reconnect(srv, hctx);
2508
2509					log_error_write(srv, __FILE__, __LINE__, "ssdsd",
2510						"response not sent, request not sent, reconnection.",
2511						"connection-fd:", con->fd,
2512						"fcgi-fd:", hctx->fd);
2513
2514					return HANDLER_WAIT_FOR_FD;
2515				}
2516
2517				log_error_write(srv, __FILE__, __LINE__, "sosdsd",
2518						"response not sent, request sent:", hctx->wb->bytes_out,
2519						"connection-fd:", con->fd,
2520						"fcgi-fd:", hctx->fd);
2521
2522				scgi_connection_cleanup(srv, hctx);
2523
2524				connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST);
2525				buffer_reset(con->physical.path);
2526				con->http_status = 500;
2527				con->mode = DIRECT;
2528			} else {
2529				/* response might have been already started, kill the connection */
2530				log_error_write(srv, __FILE__, __LINE__, "ssdsd",
2531						"response already sent out, termination connection",
2532						"connection-fd:", con->fd,
2533						"fcgi-fd:", hctx->fd);
2534
2535				scgi_connection_cleanup(srv, hctx);
2536
2537				connection_set_state(srv, con, CON_STATE_ERROR);
2538			}
2539
2540			/* */
2541
2542
2543			joblist_append(srv, con);
2544			return HANDLER_FINISHED;
2545		}
2546	}
2547
2548	if (revents & FDEVENT_OUT) {
2549		if (hctx->state == FCGI_STATE_CONNECT ||
2550		    hctx->state == FCGI_STATE_WRITE) {
2551			/* we are allowed to send something out
2552			 *
2553			 * 1. in a unfinished connect() call
2554			 * 2. in a unfinished write() call (long POST request)
2555			 */
2556			return mod_scgi_handle_subrequest(srv, con, p);
2557		} else {
2558			log_error_write(srv, __FILE__, __LINE__, "sd",
2559					"got a FDEVENT_OUT and didn't know why:",
2560					hctx->state);
2561		}
2562	}
2563
2564	/* perhaps this issue is already handled */
2565	if (revents & FDEVENT_HUP) {
2566		if (hctx->state == FCGI_STATE_CONNECT) {
2567			/* getoptsock will catch this one (right ?)
2568			 *
2569			 * if we are in connect we might get a EINPROGRESS
2570			 * in the first call and a FDEVENT_HUP in the
2571			 * second round
2572			 *
2573			 * FIXME: as it is a bit ugly.
2574			 *
2575			 */
2576			return mod_scgi_handle_subrequest(srv, con, p);
2577		} else if (hctx->state == FCGI_STATE_READ &&
2578			   hctx->proc->port == 0) {
2579			/* FIXME:
2580			 *
2581			 * ioctl says 8192 bytes to read from PHP and we receive directly a HUP for the socket
2582			 * even if the FCGI_FIN packet is not received yet
2583			 */
2584		} else {
2585			log_error_write(srv, __FILE__, __LINE__, "sbSBSDSd",
2586					"error: unexpected close of scgi connection for",
2587					con->uri.path,
2588					"(no scgi process on host: ",
2589					host->host,
2590					", port: ",
2591					host->port,
2592					" ?)",
2593					hctx->state);
2594
2595			connection_set_state(srv, con, CON_STATE_ERROR);
2596			scgi_connection_close(srv, hctx);
2597			joblist_append(srv, con);
2598		}
2599	} else if (revents & FDEVENT_ERR) {
2600		log_error_write(srv, __FILE__, __LINE__, "s",
2601				"fcgi: got a FDEVENT_ERR. Don't know why.");
2602		/* kill all connections to the scgi process */
2603
2604
2605		connection_set_state(srv, con, CON_STATE_ERROR);
2606		scgi_connection_close(srv, hctx);
2607		joblist_append(srv, con);
2608	}
2609
2610	return HANDLER_FINISHED;
2611}
2612#define PATCH(x) \
2613	p->conf.x = s->x;
2614static int scgi_patch_connection(server *srv, connection *con, plugin_data *p) {
2615	size_t i, j;
2616	plugin_config *s = p->config_storage[0];
2617
2618	PATCH(exts);
2619	PATCH(debug);
2620
2621	/* skip the first, the global context */
2622	for (i = 1; i < srv->config_context->used; i++) {
2623		data_config *dc = (data_config *)srv->config_context->data[i];
2624		s = p->config_storage[i];
2625
2626		/* condition didn't match */
2627		if (!config_check_cond(srv, con, dc)) continue;
2628
2629		/* merge config */
2630		for (j = 0; j < dc->value->used; j++) {
2631			data_unset *du = dc->value->data[j];
2632
2633			if (buffer_is_equal_string(du->key, CONST_STR_LEN("scgi.server"))) {
2634				PATCH(exts);
2635			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("scgi.debug"))) {
2636				PATCH(debug);
2637			}
2638		}
2639	}
2640
2641	return 0;
2642}
2643#undef PATCH
2644
2645
2646static handler_t scgi_check_extension(server *srv, connection *con, void *p_d, int uri_path_handler) {
2647	plugin_data *p = p_d;
2648	size_t s_len;
2649	int used = -1;
2650	size_t k;
2651	buffer *fn;
2652	scgi_extension *extension = NULL;
2653	scgi_extension_host *host = NULL;
2654
2655	if (con->mode != DIRECT) return HANDLER_GO_ON;
2656
2657	/* Possibly, we processed already this request */
2658	if (con->file_started == 1) return HANDLER_GO_ON;
2659
2660	fn = uri_path_handler ? con->uri.path : con->physical.path;
2661
2662	if (buffer_string_is_empty(fn)) return HANDLER_GO_ON;
2663
2664	s_len = buffer_string_length(fn);
2665
2666	scgi_patch_connection(srv, con, p);
2667
2668	/* check if extension matches */
2669	for (k = 0; k < p->conf.exts->used; k++) {
2670		size_t ct_len;
2671		scgi_extension *ext = p->conf.exts->exts[k];
2672
2673		if (buffer_is_empty(ext->key)) continue;
2674
2675		ct_len = buffer_string_length(ext->key);
2676
2677		if (s_len < ct_len) continue;
2678
2679		/* check extension in the form "/scgi_pattern" */
2680		if (*(ext->key->ptr) == '/') {
2681			if (strncmp(fn->ptr, ext->key->ptr, ct_len) == 0) {
2682				extension = ext;
2683				break;
2684			}
2685		} else if (0 == strncmp(fn->ptr + s_len - ct_len, ext->key->ptr, ct_len)) {
2686			/* check extension in the form ".fcg" */
2687			extension = ext;
2688			break;
2689		}
2690	}
2691
2692	/* extension doesn't match */
2693	if (NULL == extension) {
2694		return HANDLER_GO_ON;
2695	}
2696
2697	/* get best server */
2698	for (k = 0; k < extension->used; k++) {
2699		scgi_extension_host *h = extension->hosts[k];
2700
2701		/* we should have at least one proc that can do something */
2702		if (h->active_procs == 0) {
2703			continue;
2704		}
2705
2706		if (used == -1 || h->load < used) {
2707			used = h->load;
2708
2709			host = h;
2710		}
2711	}
2712
2713	if (!host) {
2714		/* sorry, we don't have a server alive for this ext */
2715		buffer_reset(con->physical.path);
2716		con->http_status = 500;
2717
2718		/* only send the 'no handler' once */
2719		if (!extension->note_is_sent) {
2720			extension->note_is_sent = 1;
2721
2722			log_error_write(srv, __FILE__, __LINE__, "sbsbs",
2723					"all handlers for ", con->uri.path,
2724					"on", extension->key,
2725					"are down.");
2726		}
2727
2728		return HANDLER_FINISHED;
2729	}
2730
2731	/* a note about no handler is not sent yet */
2732	extension->note_is_sent = 0;
2733
2734	/*
2735	 * if check-local is disabled, use the uri.path handler
2736	 *
2737	 */
2738
2739	/* init handler-context */
2740	if (uri_path_handler) {
2741		if (host->check_local == 0) {
2742			handler_ctx *hctx;
2743			char *pathinfo;
2744
2745			hctx = handler_ctx_init();
2746
2747			hctx->remote_conn      = con;
2748			hctx->plugin_data      = p;
2749			hctx->host             = host;
2750			hctx->proc	       = NULL;
2751
2752			hctx->conf.exts        = p->conf.exts;
2753			hctx->conf.debug       = p->conf.debug;
2754
2755			con->plugin_ctx[p->id] = hctx;
2756
2757			host->load++;
2758
2759			con->mode = p->id;
2760
2761			if (con->conf.log_request_handling) {
2762				log_error_write(srv, __FILE__, __LINE__, "s",
2763				"handling it in mod_scgi");
2764			}
2765
2766			/* the prefix is the SCRIPT_NAME,
2767			 * everything from start to the next slash
2768			 * this is important for check-local = "disable"
2769			 *
2770			 * if prefix = /admin.fcgi
2771			 *
2772			 * /admin.fcgi/foo/bar
2773			 *
2774			 * SCRIPT_NAME = /admin.fcgi
2775			 * PATH_INFO   = /foo/bar
2776			 *
2777			 * if prefix = /fcgi-bin/
2778			 *
2779			 * /fcgi-bin/foo/bar
2780			 *
2781			 * SCRIPT_NAME = /fcgi-bin/foo
2782			 * PATH_INFO   = /bar
2783			 *
2784			 */
2785
2786			/* the rewrite is only done for /prefix/? matches */
2787			if (host->fix_root_path_name && extension->key->ptr[0] == '/' && extension->key->ptr[1] == '\0') {
2788				buffer_copy_string(con->request.pathinfo, con->uri.path->ptr);
2789				buffer_string_set_length(con->uri.path, 0);
2790			} else if (extension->key->ptr[0] == '/' &&
2791			    buffer_string_length(con->uri.path) > buffer_string_length(extension->key) &&
2792			    NULL != (pathinfo = strchr(con->uri.path->ptr + buffer_string_length(extension->key), '/'))) {
2793				/* rewrite uri.path and pathinfo */
2794
2795				buffer_copy_string(con->request.pathinfo, pathinfo);
2796				buffer_string_set_length(con->uri.path, buffer_string_length(con->uri.path) - buffer_string_length(con->request.pathinfo));
2797			}
2798		}
2799	} else {
2800		handler_ctx *hctx;
2801		hctx = handler_ctx_init();
2802
2803		hctx->remote_conn      = con;
2804		hctx->plugin_data      = p;
2805		hctx->host             = host;
2806		hctx->proc             = NULL;
2807
2808		hctx->conf.exts        = p->conf.exts;
2809		hctx->conf.debug       = p->conf.debug;
2810
2811		con->plugin_ctx[p->id] = hctx;
2812
2813		host->load++;
2814
2815		con->mode = p->id;
2816
2817		if (con->conf.log_request_handling) {
2818			log_error_write(srv, __FILE__, __LINE__, "s", "handling it in mod_scgi");
2819		}
2820	}
2821
2822	return HANDLER_GO_ON;
2823}
2824
2825/* uri-path handler */
2826static handler_t scgi_check_extension_1(server *srv, connection *con, void *p_d) {
2827	return scgi_check_extension(srv, con, p_d, 1);
2828}
2829
2830/* start request handler */
2831static handler_t scgi_check_extension_2(server *srv, connection *con, void *p_d) {
2832	return scgi_check_extension(srv, con, p_d, 0);
2833}
2834
2835JOBLIST_FUNC(mod_scgi_handle_joblist) {
2836	plugin_data *p = p_d;
2837	handler_ctx *hctx = con->plugin_ctx[p->id];
2838
2839	if (hctx == NULL) return HANDLER_GO_ON;
2840
2841	if (hctx->fd != -1) {
2842		switch (hctx->state) {
2843		case FCGI_STATE_READ:
2844			fdevent_event_set(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN);
2845
2846			break;
2847		case FCGI_STATE_CONNECT:
2848		case FCGI_STATE_WRITE:
2849			fdevent_event_set(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_OUT);
2850
2851			break;
2852		case FCGI_STATE_INIT:
2853			/* at reconnect */
2854			break;
2855		default:
2856			log_error_write(srv, __FILE__, __LINE__, "sd", "unhandled fcgi.state", hctx->state);
2857			break;
2858		}
2859	}
2860
2861	return HANDLER_GO_ON;
2862}
2863
2864
2865static handler_t scgi_connection_close_callback(server *srv, connection *con, void *p_d) {
2866	plugin_data *p = p_d;
2867
2868	return scgi_connection_close(srv, con->plugin_ctx[p->id]);
2869}
2870
2871TRIGGER_FUNC(mod_scgi_handle_trigger) {
2872	plugin_data *p = p_d;
2873	size_t i, j, n;
2874
2875
2876	/* perhaps we should kill a connect attempt after 10-15 seconds
2877	 *
2878	 * currently we wait for the TCP timeout which is on Linux 180 seconds
2879	 *
2880	 *
2881	 *
2882	 */
2883
2884	/* check all childs if they are still up */
2885
2886	for (i = 0; i < srv->config_context->used; i++) {
2887		plugin_config *conf;
2888		scgi_exts *exts;
2889
2890		conf = p->config_storage[i];
2891
2892		exts = conf->exts;
2893
2894		for (j = 0; j < exts->used; j++) {
2895			scgi_extension *ex;
2896
2897			ex = exts->exts[j];
2898
2899			for (n = 0; n < ex->used; n++) {
2900
2901				scgi_proc *proc;
2902				unsigned long sum_load = 0;
2903				scgi_extension_host *host;
2904
2905				host = ex->hosts[n];
2906
2907				scgi_restart_dead_procs(srv, p, host);
2908
2909				for (proc = host->first; proc; proc = proc->next) {
2910					sum_load += proc->load;
2911				}
2912
2913				if (host->num_procs &&
2914				    host->num_procs < host->max_procs &&
2915				    (sum_load / host->num_procs) > host->max_load_per_proc) {
2916					/* overload, spawn new child */
2917					scgi_proc *fp = NULL;
2918
2919					if (p->conf.debug) {
2920						log_error_write(srv, __FILE__, __LINE__, "s",
2921								"overload detected, spawning a new child");
2922					}
2923
2924					for (fp = host->unused_procs; fp && fp->pid != 0; fp = fp->next);
2925
2926					if (fp) {
2927						if (fp == host->unused_procs) host->unused_procs = fp->next;
2928
2929						if (fp->next) fp->next->prev = NULL;
2930
2931						host->max_id++;
2932					} else {
2933						fp = scgi_process_init();
2934						fp->id = host->max_id++;
2935					}
2936
2937					host->num_procs++;
2938
2939					if (buffer_string_is_empty(host->unixsocket)) {
2940						fp->port = host->port + fp->id;
2941					} else {
2942						buffer_copy_buffer(fp->socket, host->unixsocket);
2943						buffer_append_string_len(fp->socket, CONST_STR_LEN("-"));
2944						buffer_append_int(fp->socket, fp->id);
2945					}
2946
2947					if (scgi_spawn_connection(srv, p, host, fp)) {
2948						log_error_write(srv, __FILE__, __LINE__, "s",
2949								"ERROR: spawning fcgi failed.");
2950						scgi_process_free(fp);
2951						return HANDLER_ERROR;
2952					}
2953
2954					fp->prev = NULL;
2955					fp->next = host->first;
2956					if (host->first) {
2957						host->first->prev = fp;
2958					}
2959					host->first = fp;
2960				}
2961
2962				for (proc = host->first; proc; proc = proc->next) {
2963					if (proc->load != 0) break;
2964					if (host->num_procs <= host->min_procs) break;
2965					if (proc->pid == 0) continue;
2966
2967					if (srv->cur_ts - proc->last_used > host->idle_timeout) {
2968						/* a proc is idling for a long time now,
2969						 * terminated it */
2970
2971						if (p->conf.debug) {
2972							log_error_write(srv, __FILE__, __LINE__, "ssbsd",
2973									"idle-timeout reached, terminating child:",
2974									"socket:", proc->socket,
2975									"pid", proc->pid);
2976						}
2977
2978
2979						if (proc->next) proc->next->prev = proc->prev;
2980						if (proc->prev) proc->prev->next = proc->next;
2981
2982						if (proc->prev == NULL) host->first = proc->next;
2983
2984						proc->prev = NULL;
2985						proc->next = host->unused_procs;
2986
2987						if (host->unused_procs) host->unused_procs->prev = proc;
2988						host->unused_procs = proc;
2989
2990						kill(proc->pid, SIGTERM);
2991
2992						proc->state = PROC_STATE_KILLED;
2993
2994						log_error_write(srv, __FILE__, __LINE__, "ssbsd",
2995									"killed:",
2996									"socket:", proc->socket,
2997									"pid", proc->pid);
2998
2999						host->num_procs--;
3000
3001						/* proc is now in unused, let the next second handle the next process */
3002						break;
3003					}
3004				}
3005
3006				for (proc = host->unused_procs; proc; proc = proc->next) {
3007					int status;
3008
3009					if (proc->pid == 0) continue;
3010
3011					switch (waitpid(proc->pid, &status, WNOHANG)) {
3012					case 0:
3013						/* child still running after timeout, good */
3014						break;
3015					case -1:
3016						if (errno != EINTR) {
3017							/* no PID found ? should never happen */
3018							log_error_write(srv, __FILE__, __LINE__, "sddss",
3019									"pid ", proc->pid, proc->state,
3020									"not found:", strerror(errno));
3021
3022#if 0
3023							if (errno == ECHILD) {
3024								/* someone else has cleaned up for us */
3025								proc->pid = 0;
3026								proc->state = PROC_STATE_UNSET;
3027							}
3028#endif
3029						}
3030						break;
3031					default:
3032						/* the child should not terminate at all */
3033						if (WIFEXITED(status)) {
3034							if (proc->state != PROC_STATE_KILLED) {
3035								log_error_write(srv, __FILE__, __LINE__, "sdb",
3036										"child exited:",
3037										WEXITSTATUS(status), proc->socket);
3038							}
3039						} else if (WIFSIGNALED(status)) {
3040							if (WTERMSIG(status) != SIGTERM) {
3041								log_error_write(srv, __FILE__, __LINE__, "sd",
3042										"child signaled:",
3043										WTERMSIG(status));
3044							}
3045						} else {
3046							log_error_write(srv, __FILE__, __LINE__, "sd",
3047									"child died somehow:",
3048									status);
3049						}
3050						proc->pid = 0;
3051						proc->state = PROC_STATE_UNSET;
3052						host->max_id--;
3053					}
3054				}
3055			}
3056		}
3057	}
3058
3059	return HANDLER_GO_ON;
3060}
3061
3062
3063int mod_scgi_plugin_init(plugin *p);
3064int mod_scgi_plugin_init(plugin *p) {
3065	p->version     = LIGHTTPD_VERSION_ID;
3066	p->name         = buffer_init_string("scgi");
3067
3068	p->init         = mod_scgi_init;
3069	p->cleanup      = mod_scgi_free;
3070	p->set_defaults = mod_scgi_set_defaults;
3071	p->connection_reset        = scgi_connection_reset;
3072	p->handle_connection_close = scgi_connection_close_callback;
3073	p->handle_uri_clean        = scgi_check_extension_1;
3074	p->handle_subrequest_start = scgi_check_extension_2;
3075	p->handle_subrequest       = mod_scgi_handle_subrequest;
3076	p->handle_joblist          = mod_scgi_handle_joblist;
3077	p->handle_trigger          = mod_scgi_handle_trigger;
3078
3079	p->data         = NULL;
3080
3081	return 0;
3082}
3083