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