[core] no SOCK_CLOEXEC on Linux kernel < 2.6.27
[lighttpd.git] / src / mod_fastcgi.c
blob4decdaa633bae28a36adfdfb1964d1b405f4c643
1 #include "first.h"
3 #include "buffer.h"
4 #include "server.h"
5 #include "keyvalue.h"
6 #include "log.h"
8 #include "http_chunk.h"
9 #include "fdevent.h"
10 #include "connections.h"
11 #include "response.h"
12 #include "joblist.h"
14 #include "plugin.h"
16 #include "stat_cache.h"
17 #include "status_counter.h"
19 #include <sys/types.h>
20 #include <unistd.h>
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <string.h>
24 #include <stdlib.h>
25 #include <signal.h>
27 #ifdef HAVE_FASTCGI_FASTCGI_H
28 # include <fastcgi/fastcgi.h>
29 #else
30 # ifdef HAVE_FASTCGI_H
31 # include <fastcgi.h>
32 # else
33 # include "fastcgi.h"
34 # endif
35 #endif /* HAVE_FASTCGI_FASTCGI_H */
37 #include "sys-socket.h"
39 #ifdef HAVE_SYS_UIO_H
40 #include <sys/uio.h>
41 #endif
42 #ifdef HAVE_SYS_WAIT_H
43 #include <sys/wait.h>
44 #endif
48 * TODO:
50 * - add timeout for a connect to a non-fastcgi process
51 * (use state_timestamp + state)
55 typedef struct fcgi_proc {
56 size_t id; /* id will be between 1 and max_procs */
57 buffer *unixsocket; /* config.socket + "-" + id */
58 unsigned port; /* config.port + pno */
60 buffer *connection_name; /* either tcp:<host>:<port> or unix:<socket> for debugging purposes */
62 pid_t pid; /* PID of the spawned process (0 if not spawned locally) */
65 size_t load; /* number of requests waiting on this process */
67 size_t requests; /* see max_requests */
68 struct fcgi_proc *prev, *next; /* see first */
70 time_t disabled_until; /* this proc is disabled until, use something else until then */
72 int is_local;
74 enum {
75 PROC_STATE_RUNNING, /* alive */
76 PROC_STATE_OVERLOADED, /* listen-queue is full,
77 don't send anything to this proc for the next 2 seconds */
78 PROC_STATE_DIED_WAIT_FOR_PID, /* */
79 PROC_STATE_DIED, /* marked as dead, should be restarted */
80 PROC_STATE_KILLED /* was killed as we don't have the load anymore */
81 } state;
82 } fcgi_proc;
84 typedef struct {
85 /* the key that is used to reference this value */
86 buffer *id;
88 /* list of processes handling this extension
89 * sorted by lowest load
91 * whenever a job is done move it up in the list
92 * until it is sorted, move it down as soon as the
93 * job is started
95 fcgi_proc *first;
96 fcgi_proc *unused_procs;
99 * spawn at least min_procs, at max_procs.
101 * as soon as the load of the first entry
102 * is max_load_per_proc we spawn a new one
103 * and add it to the first entry and give it
104 * the load
108 unsigned short max_procs;
109 size_t num_procs; /* how many procs are started */
110 size_t active_procs; /* how many of them are really running, i.e. state = PROC_STATE_RUNNING */
113 * time after a disabled remote connection is tried to be re-enabled
118 unsigned short disable_time;
121 * some fastcgi processes get a little bit larger
122 * than wanted. max_requests_per_proc kills a
123 * process after a number of handled requests.
126 size_t max_requests_per_proc;
129 /* config */
132 * host:port
134 * if host is one of the local IP adresses the
135 * whole connection is local
137 * if port is not 0, and host is not specified,
138 * "localhost" (INADDR_LOOPBACK) is assumed.
141 buffer *host;
142 unsigned short port;
143 sa_family_t family;
146 * Unix Domain Socket
148 * instead of TCP/IP we can use Unix Domain Sockets
149 * - more secure (you have fileperms to play with)
150 * - more control (on locally)
151 * - more speed (no extra overhead)
153 buffer *unixsocket;
155 /* if socket is local we can start the fastcgi
156 * process ourself
158 * bin-path is the path to the binary
160 * check min_procs and max_procs for the number
161 * of process to start up
163 buffer *bin_path;
165 /* bin-path is set bin-environment is taken to
166 * create the environement before starting the
167 * FastCGI process
170 array *bin_env;
172 array *bin_env_copy;
175 * docroot-translation between URL->phys and the
176 * remote host
178 * reasons:
179 * - different dir-layout if remote
180 * - chroot if local
183 buffer *docroot;
186 * check_local tells you if the phys file is stat()ed
187 * or not. FastCGI doesn't care if the service is
188 * remote. If the web-server side doesn't contain
189 * the fastcgi-files we should not stat() for them
190 * and say '404 not found'.
192 unsigned short check_local;
195 * append PATH_INFO to SCRIPT_FILENAME
197 * php needs this if cgi.fix_pathinfo is provided
201 unsigned short break_scriptfilename_for_php;
204 * workaround for program when prefix="/"
206 * rule to build PATH_INFO is hardcoded for when check_local is disabled
207 * enable this option to use the workaround
211 unsigned short fix_root_path_name;
214 * If the backend includes X-Sendfile in the response
215 * we use the value as filename and ignore the content.
218 unsigned short xsendfile_allow;
219 array *xsendfile_docroot;
221 ssize_t load; /* replace by host->load */
223 size_t max_id; /* corresponds most of the time to num_procs */
225 buffer *strip_request_uri;
227 unsigned short kill_signal; /* we need a setting for this as libfcgi
228 applications prefer SIGUSR1 while the
229 rest of the world would use SIGTERM
230 *sigh* */
232 int listen_backlog;
233 int refcount;
234 } fcgi_extension_host;
237 * one extension can have multiple hosts assigned
238 * one host can spawn additional processes on the same
239 * socket (if we control it)
241 * ext -> host -> procs
242 * 1:n 1:n
244 * if the fastcgi process is remote that whole goes down
245 * to
247 * ext -> host -> procs
248 * 1:n 1:1
250 * in case of PHP and FCGI_CHILDREN we have again a procs
251 * but we don't control it directly.
255 typedef struct {
256 buffer *key; /* like .php */
258 int note_is_sent;
259 int last_used_ndx;
261 fcgi_extension_host **hosts;
263 size_t used;
264 size_t size;
265 } fcgi_extension;
267 typedef struct {
268 fcgi_extension **exts;
270 size_t used;
271 size_t size;
272 } fcgi_exts;
275 typedef struct {
276 fcgi_exts *exts;
277 fcgi_exts *exts_auth;
278 fcgi_exts *exts_resp;
280 array *ext_mapping;
282 unsigned int debug;
283 } plugin_config;
285 typedef struct {
286 char **ptr;
288 size_t size;
289 size_t used;
290 } char_array;
292 /* generic plugin data, shared between all connections */
293 typedef struct {
294 PLUGIN_DATA;
296 buffer *fcgi_env;
298 buffer *statuskey;
300 plugin_config **config_storage;
302 plugin_config conf; /* this is only used as long as no handler_ctx is setup */
303 } plugin_data;
305 /* connection specific data */
306 typedef enum {
307 FCGI_STATE_INIT,
308 FCGI_STATE_CONNECT_DELAYED,
309 FCGI_STATE_PREPARE_WRITE,
310 FCGI_STATE_WRITE,
311 FCGI_STATE_READ
312 } fcgi_connection_state_t;
314 typedef struct {
315 fcgi_proc *proc;
316 fcgi_extension_host *host;
317 fcgi_extension *ext;
318 fcgi_extension *ext_auth; /* (might be used in future to allow multiple authorizers)*/
319 unsigned short fcgi_mode; /* FastCGI mode: FCGI_AUTHORIZER or FCGI_RESPONDER */
321 fcgi_connection_state_t state;
322 time_t state_timestamp;
324 chunkqueue *rb; /* read queue */
325 chunkqueue *wb; /* write queue */
326 off_t wb_reqlen;
328 buffer *response_header;
330 int fd; /* fd to the fastcgi process */
331 int fde_ndx; /* index into the fd-event buffer */
333 pid_t pid;
334 int got_proc;
335 int reconnects; /* number of reconnect attempts */
337 int request_id;
338 int send_content_body;
340 http_response_opts opts;
341 plugin_config conf;
343 connection *remote_conn; /* dumb pointer */
344 plugin_data *plugin_data; /* dumb pointer */
345 } handler_ctx;
348 /* ok, we need a prototype */
349 static handler_t fcgi_handle_fdevent(server *srv, void *ctx, int revents);
351 static void reset_signals(void) {
352 #ifdef SIGTTOU
353 signal(SIGTTOU, SIG_DFL);
354 #endif
355 #ifdef SIGTTIN
356 signal(SIGTTIN, SIG_DFL);
357 #endif
358 #ifdef SIGTSTP
359 signal(SIGTSTP, SIG_DFL);
360 #endif
361 signal(SIGHUP, SIG_DFL);
362 signal(SIGPIPE, SIG_DFL);
363 signal(SIGUSR1, SIG_DFL);
366 static void fastcgi_status_copy_procname(buffer *b, fcgi_extension_host *host, fcgi_proc *proc) {
367 buffer_copy_string_len(b, CONST_STR_LEN("fastcgi.backend."));
368 buffer_append_string_buffer(b, host->id);
369 if (proc) {
370 buffer_append_string_len(b, CONST_STR_LEN("."));
371 buffer_append_int(b, proc->id);
375 static void fcgi_proc_load_inc(server *srv, handler_ctx *hctx) {
376 plugin_data *p = hctx->plugin_data;
377 hctx->proc->load++;
379 status_counter_inc(srv, CONST_STR_LEN("fastcgi.active-requests"));
381 fastcgi_status_copy_procname(p->statuskey, hctx->host, hctx->proc);
382 buffer_append_string_len(p->statuskey, CONST_STR_LEN(".load"));
384 status_counter_set(srv, CONST_BUF_LEN(p->statuskey), hctx->proc->load);
387 static void fcgi_proc_load_dec(server *srv, handler_ctx *hctx) {
388 plugin_data *p = hctx->plugin_data;
389 hctx->proc->load--;
391 status_counter_dec(srv, CONST_STR_LEN("fastcgi.active-requests"));
393 fastcgi_status_copy_procname(p->statuskey, hctx->host, hctx->proc);
394 buffer_append_string_len(p->statuskey, CONST_STR_LEN(".load"));
396 status_counter_set(srv, CONST_BUF_LEN(p->statuskey), hctx->proc->load);
399 static void fcgi_host_assign(server *srv, handler_ctx *hctx, fcgi_extension_host *host) {
400 plugin_data *p = hctx->plugin_data;
401 hctx->host = host;
402 hctx->host->load++;
404 fastcgi_status_copy_procname(p->statuskey, hctx->host, NULL);
405 buffer_append_string_len(p->statuskey, CONST_STR_LEN(".load"));
407 status_counter_set(srv, CONST_BUF_LEN(p->statuskey), hctx->host->load);
410 static void fcgi_host_reset(server *srv, handler_ctx *hctx) {
411 plugin_data *p = hctx->plugin_data;
412 hctx->host->load--;
414 fastcgi_status_copy_procname(p->statuskey, hctx->host, NULL);
415 buffer_append_string_len(p->statuskey, CONST_STR_LEN(".load"));
417 status_counter_set(srv, CONST_BUF_LEN(p->statuskey), hctx->host->load);
419 hctx->host = NULL;
422 static int fastcgi_status_init(server *srv, buffer *b, fcgi_extension_host *host, fcgi_proc *proc) {
423 #define CLEAN(x) \
424 fastcgi_status_copy_procname(b, host, proc); \
425 buffer_append_string_len(b, CONST_STR_LEN(x)); \
426 status_counter_set(srv, CONST_BUF_LEN(b), 0);
428 CLEAN(".disabled");
429 CLEAN(".died");
430 CLEAN(".overloaded");
431 CLEAN(".connected");
432 CLEAN(".load");
434 #undef CLEAN
436 #define CLEAN(x) \
437 fastcgi_status_copy_procname(b, host, NULL); \
438 buffer_append_string_len(b, CONST_STR_LEN(x)); \
439 status_counter_set(srv, CONST_BUF_LEN(b), 0);
441 CLEAN(".load");
443 #undef CLEAN
445 return 0;
448 static handler_ctx * handler_ctx_init(void) {
449 handler_ctx * hctx;
451 hctx = calloc(1, sizeof(*hctx));
452 force_assert(hctx);
454 hctx->fde_ndx = -1;
456 /*hctx->response_header = buffer_init();*//*(allocated when needed)*/
458 hctx->request_id = 0;
459 hctx->fcgi_mode = FCGI_RESPONDER;
460 hctx->state = FCGI_STATE_INIT;
461 hctx->proc = NULL;
463 hctx->fd = -1;
465 hctx->reconnects = 0;
466 hctx->send_content_body = 1;
468 hctx->rb = chunkqueue_init();
469 hctx->wb = chunkqueue_init();
470 hctx->wb_reqlen = 0;
472 return hctx;
475 static void handler_ctx_free(handler_ctx *hctx) {
476 /* caller MUST have called fcgi_backend_close(srv, hctx) if necessary */
477 buffer_free(hctx->response_header);
479 chunkqueue_free(hctx->rb);
480 chunkqueue_free(hctx->wb);
482 free(hctx);
485 static void handler_ctx_clear(handler_ctx *hctx) {
486 /* caller MUST have called fcgi_backend_close(srv, hctx) if necessary */
488 hctx->proc = NULL;
489 hctx->host = NULL;
490 hctx->ext = NULL;
491 /*hctx->ext_auth is intentionally preserved to flag prior authorizer*/
493 hctx->fcgi_mode = FCGI_RESPONDER;
494 hctx->state = FCGI_STATE_INIT;
495 /*hctx->state_timestamp = 0;*//*(unused; left as-is)*/
497 chunkqueue_reset(hctx->rb);
498 chunkqueue_reset(hctx->wb);
499 hctx->wb_reqlen = 0;
501 buffer_reset(hctx->response_header);
503 hctx->fd = -1;
504 hctx->fde_ndx = -1;
505 hctx->got_proc = 0;
506 hctx->reconnects = 0;
507 hctx->request_id = 0;
508 hctx->send_content_body = 1;
510 /*plugin_config conf;*//*(no need to reset for same request)*/
512 /*hctx->remote_conn = NULL;*//*(no need to reset for same request)*/
513 /*hctx->plugin_data = NULL;*//*(no need to reset for same request)*/
516 static fcgi_proc *fastcgi_process_init(void) {
517 fcgi_proc *f;
519 f = calloc(1, sizeof(*f));
520 f->unixsocket = buffer_init();
521 f->connection_name = buffer_init();
523 f->prev = NULL;
524 f->next = NULL;
525 f->state = PROC_STATE_DIED;
527 return f;
530 static void fastcgi_process_free(fcgi_proc *f) {
531 if (!f) return;
533 fastcgi_process_free(f->next);
535 buffer_free(f->unixsocket);
536 buffer_free(f->connection_name);
538 free(f);
541 static fcgi_extension_host *fastcgi_host_init(void) {
542 fcgi_extension_host *f;
544 f = calloc(1, sizeof(*f));
546 f->id = buffer_init();
547 f->host = buffer_init();
548 f->unixsocket = buffer_init();
549 f->docroot = buffer_init();
550 f->bin_path = buffer_init();
551 f->bin_env = array_init();
552 f->bin_env_copy = array_init();
553 f->strip_request_uri = buffer_init();
554 f->xsendfile_docroot = array_init();
556 return f;
559 static void fastcgi_host_free(fcgi_extension_host *h) {
560 if (!h) return;
561 if (h->refcount) {
562 --h->refcount;
563 return;
566 buffer_free(h->id);
567 buffer_free(h->host);
568 buffer_free(h->unixsocket);
569 buffer_free(h->docroot);
570 buffer_free(h->bin_path);
571 buffer_free(h->strip_request_uri);
572 array_free(h->bin_env);
573 array_free(h->bin_env_copy);
574 array_free(h->xsendfile_docroot);
576 fastcgi_process_free(h->first);
577 fastcgi_process_free(h->unused_procs);
579 free(h);
583 static fcgi_exts *fastcgi_extensions_init(void) {
584 fcgi_exts *f;
586 f = calloc(1, sizeof(*f));
588 return f;
591 static void fastcgi_extensions_free(fcgi_exts *f) {
592 size_t i;
594 if (!f) return;
596 for (i = 0; i < f->used; i++) {
597 fcgi_extension *fe;
598 size_t j;
600 fe = f->exts[i];
602 for (j = 0; j < fe->used; j++) {
603 fcgi_extension_host *h;
605 h = fe->hosts[j];
607 fastcgi_host_free(h);
610 buffer_free(fe->key);
611 free(fe->hosts);
613 free(fe);
616 free(f->exts);
618 free(f);
621 static int fastcgi_extension_insert(fcgi_exts *ext, buffer *key, fcgi_extension_host *fh) {
622 fcgi_extension *fe;
623 size_t i;
625 /* there is something */
627 for (i = 0; i < ext->used; i++) {
628 if (buffer_is_equal(key, ext->exts[i]->key)) {
629 break;
633 if (i == ext->used) {
634 /* filextension is new */
635 fe = calloc(1, sizeof(*fe));
636 force_assert(fe);
637 fe->key = buffer_init();
638 fe->last_used_ndx = -1;
639 buffer_copy_buffer(fe->key, key);
641 /* */
643 if (ext->size == 0) {
644 ext->size = 8;
645 ext->exts = malloc(ext->size * sizeof(*(ext->exts)));
646 force_assert(ext->exts);
647 } else if (ext->used == ext->size) {
648 ext->size += 8;
649 ext->exts = realloc(ext->exts, ext->size * sizeof(*(ext->exts)));
650 force_assert(ext->exts);
652 ext->exts[ext->used++] = fe;
653 } else {
654 fe = ext->exts[i];
657 if (fe->size == 0) {
658 fe->size = 4;
659 fe->hosts = malloc(fe->size * sizeof(*(fe->hosts)));
660 force_assert(fe->hosts);
661 } else if (fe->size == fe->used) {
662 fe->size += 4;
663 fe->hosts = realloc(fe->hosts, fe->size * sizeof(*(fe->hosts)));
664 force_assert(fe->hosts);
667 fe->hosts[fe->used++] = fh;
669 return 0;
673 static void fcgi_proc_set_state(fcgi_extension_host *host, fcgi_proc *proc, int state) {
674 if ((int)proc->state == state) return;
675 if (proc->state == PROC_STATE_RUNNING) {
676 --host->active_procs;
677 } else if (state == PROC_STATE_RUNNING) {
678 ++host->active_procs;
680 proc->state = state;
683 static void fcgi_proc_disable(server *srv, fcgi_extension_host *host, fcgi_proc *proc, handler_ctx *hctx) {
684 if (host->disable_time || (proc->is_local && proc->pid == hctx->pid)) {
685 proc->disabled_until = srv->cur_ts + host->disable_time;
686 fcgi_proc_set_state(host, proc, proc->is_local ? PROC_STATE_DIED_WAIT_FOR_PID : PROC_STATE_DIED);
688 if (hctx->conf.debug) {
689 log_error_write(srv, __FILE__, __LINE__, "sds",
690 "backend disabled for", host->disable_time, "seconds");
695 static void fcgi_proc_check_enable(server *srv, fcgi_extension_host *host, fcgi_proc *proc) {
696 if (srv->cur_ts <= proc->disabled_until) return;
697 if (proc->state == PROC_STATE_RUNNING) return;
699 fcgi_proc_set_state(host, proc, PROC_STATE_RUNNING);
701 log_error_write(srv, __FILE__, __LINE__, "sbbdb",
702 "fcgi-server re-enabled:", proc->connection_name,
703 host->host, host->port, host->unixsocket);
706 static int fcgi_proc_waitpid(server *srv, fcgi_extension_host *host, fcgi_proc *proc) {
707 int rc, status;
709 if (!proc->is_local) return 0;
710 if (proc->pid <= 0) return 0;
712 do {
713 rc = waitpid(proc->pid, &status, WNOHANG);
714 } while (-1 == rc && errno == EINTR);
715 if (0 == rc) return 0; /* child still running */
717 /* child terminated */
718 if (-1 == rc) {
719 /* EINVAL or ECHILD no child processes */
720 /* should not happen; someone else has cleaned up for us */
721 log_error_write(srv, __FILE__, __LINE__, "sddss",
722 "pid ", proc->pid, proc->state,
723 "not found:", strerror(errno));
724 } else if (WIFEXITED(status)) {
725 if (proc->state != PROC_STATE_KILLED) {
726 log_error_write(srv, __FILE__, __LINE__, "sdb",
727 "child exited:",
728 WEXITSTATUS(status), proc->connection_name);
730 } else if (WIFSIGNALED(status)) {
731 if (WTERMSIG(status) != SIGTERM && WTERMSIG(status) != SIGINT) {
732 log_error_write(srv, __FILE__, __LINE__, "sd",
733 "child signalled:", WTERMSIG(status));
735 } else {
736 log_error_write(srv, __FILE__, __LINE__, "sd",
737 "child died somehow:", status);
740 proc->pid = 0;
741 fcgi_proc_set_state(host, proc, PROC_STATE_DIED);
742 return 1;
745 INIT_FUNC(mod_fastcgi_init) {
746 plugin_data *p;
748 p = calloc(1, sizeof(*p));
750 p->fcgi_env = buffer_init();
752 p->statuskey = buffer_init();
754 return p;
758 FREE_FUNC(mod_fastcgi_free) {
759 plugin_data *p = p_d;
761 UNUSED(srv);
763 buffer_free(p->fcgi_env);
764 buffer_free(p->statuskey);
766 if (p->config_storage) {
767 size_t i, j, n;
768 for (i = 0; i < srv->config_context->used; i++) {
769 plugin_config *s = p->config_storage[i];
770 fcgi_exts *exts;
772 if (NULL == s) continue;
774 exts = s->exts;
776 if (exts) {
777 for (j = 0; j < exts->used; j++) {
778 fcgi_extension *ex;
780 ex = exts->exts[j];
782 for (n = 0; n < ex->used; n++) {
783 fcgi_proc *proc;
784 fcgi_extension_host *host;
786 host = ex->hosts[n];
788 for (proc = host->first; proc; proc = proc->next) {
789 if (proc->pid != 0) {
790 kill(proc->pid, host->kill_signal);
793 if (proc->is_local &&
794 !buffer_string_is_empty(proc->unixsocket)) {
795 unlink(proc->unixsocket->ptr);
799 for (proc = host->unused_procs; proc; proc = proc->next) {
800 if (proc->pid != 0) {
801 kill(proc->pid, host->kill_signal);
803 if (proc->is_local &&
804 !buffer_string_is_empty(proc->unixsocket)) {
805 unlink(proc->unixsocket->ptr);
811 fastcgi_extensions_free(s->exts);
812 fastcgi_extensions_free(s->exts_auth);
813 fastcgi_extensions_free(s->exts_resp);
815 array_free(s->ext_mapping);
817 free(s);
819 free(p->config_storage);
822 free(p);
824 return HANDLER_GO_ON;
827 static int env_add(char_array *env, const char *key, size_t key_len, const char *val, size_t val_len) {
828 char *dst;
829 size_t i;
831 if (!key || !val) return -1;
833 dst = malloc(key_len + val_len + 3);
834 memcpy(dst, key, key_len);
835 dst[key_len] = '=';
836 memcpy(dst + key_len + 1, val, val_len);
837 dst[key_len + 1 + val_len] = '\0';
839 for (i = 0; i < env->used; i++) {
840 if (0 == strncmp(dst, env->ptr[i], key_len + 1)) {
841 /* don't care about free as we are in a forked child which is going to exec(...) */
842 /* free(env->ptr[i]); */
843 env->ptr[i] = dst;
844 return 0;
848 if (env->size == 0) {
849 env->size = 16;
850 env->ptr = malloc(env->size * sizeof(*env->ptr));
851 } else if (env->size == env->used + 1) {
852 env->size += 16;
853 env->ptr = realloc(env->ptr, env->size * sizeof(*env->ptr));
856 env->ptr[env->used++] = dst;
858 return 0;
861 static int parse_binpath(char_array *env, buffer *b) {
862 char *start;
863 size_t i;
864 /* search for spaces */
866 start = b->ptr;
867 for (i = 0; i < buffer_string_length(b); i++) {
868 switch(b->ptr[i]) {
869 case ' ':
870 case '\t':
871 /* a WS, stop here and copy the argument */
873 if (env->size == 0) {
874 env->size = 16;
875 env->ptr = malloc(env->size * sizeof(*env->ptr));
876 } else if (env->size == env->used) {
877 env->size += 16;
878 env->ptr = realloc(env->ptr, env->size * sizeof(*env->ptr));
881 b->ptr[i] = '\0';
883 env->ptr[env->used++] = start;
885 start = b->ptr + i + 1;
886 break;
887 default:
888 break;
892 if (env->size == 0) {
893 env->size = 16;
894 env->ptr = malloc(env->size * sizeof(*env->ptr));
895 } else if (env->size == env->used) { /* we need one extra for the terminating NULL */
896 env->size += 16;
897 env->ptr = realloc(env->ptr, env->size * sizeof(*env->ptr));
900 /* the rest */
901 env->ptr[env->used++] = start;
903 if (env->size == 0) {
904 env->size = 16;
905 env->ptr = malloc(env->size * sizeof(*env->ptr));
906 } else if (env->size == env->used) { /* we need one extra for the terminating NULL */
907 env->size += 16;
908 env->ptr = realloc(env->ptr, env->size * sizeof(*env->ptr));
911 /* terminate */
912 env->ptr[env->used++] = NULL;
914 return 0;
917 #if !defined(HAVE_FORK)
918 static int fcgi_spawn_connection(server *srv,
919 plugin_data *p,
920 fcgi_extension_host *host,
921 fcgi_proc *proc) {
922 UNUSED(srv);
923 UNUSED(p);
924 UNUSED(host);
925 UNUSED(proc);
926 return -1;
929 #else /* -> defined(HAVE_FORK) */
931 static int fcgi_spawn_connection(server *srv,
932 plugin_data *p,
933 fcgi_extension_host *host,
934 fcgi_proc *proc) {
935 int fcgi_fd;
936 int status;
937 struct timeval tv = { 0, 10 * 1000 };
938 #ifdef HAVE_SYS_UN_H
939 struct sockaddr_un fcgi_addr_un;
940 #endif
941 #if defined(HAVE_IPV6) && defined(HAVE_INET_PTON)
942 struct sockaddr_in6 fcgi_addr_in6;
943 #endif
944 struct sockaddr_in fcgi_addr_in;
945 struct sockaddr *fcgi_addr;
947 socklen_t servlen;
949 if (p->conf.debug) {
950 log_error_write(srv, __FILE__, __LINE__, "sdb",
951 "new proc, socket:", proc->port, proc->unixsocket);
954 if (!buffer_string_is_empty(proc->unixsocket)) {
955 #ifdef HAVE_SYS_UN_H
956 memset(&fcgi_addr_un, 0, sizeof(fcgi_addr_un));
957 fcgi_addr_un.sun_family = AF_UNIX;
958 if (buffer_string_length(proc->unixsocket) + 1 > sizeof(fcgi_addr_un.sun_path)) {
959 log_error_write(srv, __FILE__, __LINE__, "sB",
960 "ERROR: Unix Domain socket filename too long:",
961 proc->unixsocket);
962 return -1;
964 memcpy(fcgi_addr_un.sun_path, proc->unixsocket->ptr, buffer_string_length(proc->unixsocket) + 1);
966 #ifdef SUN_LEN
967 servlen = SUN_LEN(&fcgi_addr_un);
968 #else
969 /* stevens says: */
970 servlen = buffer_string_length(proc->unixsocket) + 1 + sizeof(fcgi_addr_un.sun_family);
971 #endif
972 fcgi_addr = (struct sockaddr *) &fcgi_addr_un;
974 buffer_copy_string_len(proc->connection_name, CONST_STR_LEN("unix:"));
975 buffer_append_string_buffer(proc->connection_name, proc->unixsocket);
977 #else
978 log_error_write(srv, __FILE__, __LINE__, "s",
979 "ERROR: Unix Domain sockets are not supported.");
980 return -1;
981 #endif
982 } else if (buffer_string_is_empty(host->host)) {
983 memset(&fcgi_addr_in, 0, sizeof(fcgi_addr_in));
984 fcgi_addr_in.sin_family = AF_INET;
985 fcgi_addr_in.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
986 fcgi_addr_in.sin_port = htons(proc->port);
987 servlen = sizeof(fcgi_addr_in);
988 fcgi_addr = (struct sockaddr *) &fcgi_addr_in;
989 #if defined(HAVE_IPV6) && defined(HAVE_INET_PTON)
990 } else if (host->family == AF_INET6) {
991 memset(&fcgi_addr_in6, 0, sizeof(fcgi_addr_in6));
992 fcgi_addr_in6.sin6_family = AF_INET6;
993 inet_pton(AF_INET6, host->host->ptr, (char *) &fcgi_addr_in6.sin6_addr);
994 fcgi_addr_in6.sin6_port = htons(proc->port);
995 servlen = sizeof(fcgi_addr_in6);
996 fcgi_addr = (struct sockaddr *) &fcgi_addr_in6;
997 #endif
998 } else {
999 memset(&fcgi_addr_in, 0, sizeof(fcgi_addr_in));
1000 fcgi_addr_in.sin_family = AF_INET;
1001 #if defined(HAVE_INET_PTON)
1002 inet_pton(AF_INET, host->host->ptr, (char *) &fcgi_addr_in.sin_addr);
1003 #else
1005 struct hostent *he;
1007 /* set a useful default */
1008 fcgi_addr_in.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
1010 if (NULL == (he = gethostbyname(host->host->ptr))) {
1011 log_error_write(srv, __FILE__, __LINE__,
1012 "sdb", "gethostbyname failed: ",
1013 h_errno, host->host);
1014 return -1;
1017 if (he->h_addrtype != AF_INET) {
1018 log_error_write(srv, __FILE__, __LINE__, "sd", "addr-type != AF_INET: ", he->h_addrtype);
1019 return -1;
1022 if (he->h_length != sizeof(struct in_addr)) {
1023 log_error_write(srv, __FILE__, __LINE__, "sd", "addr-length != sizeof(in_addr): ", he->h_length);
1024 return -1;
1027 memcpy(&(fcgi_addr_in.sin_addr.s_addr), he->h_addr_list[0], he->h_length);
1030 #endif
1031 fcgi_addr_in.sin_port = htons(proc->port);
1032 servlen = sizeof(fcgi_addr_in);
1034 fcgi_addr = (struct sockaddr *) &fcgi_addr_in;
1037 if (buffer_string_is_empty(proc->unixsocket)) {
1038 buffer_copy_string_len(proc->connection_name, CONST_STR_LEN("tcp:"));
1039 if (!buffer_string_is_empty(host->host)) {
1040 buffer_append_string_buffer(proc->connection_name, host->host);
1041 } else {
1042 buffer_append_string_len(proc->connection_name, CONST_STR_LEN("localhost"));
1044 buffer_append_string_len(proc->connection_name, CONST_STR_LEN(":"));
1045 buffer_append_int(proc->connection_name, proc->port);
1048 if (-1 == (fcgi_fd = fdevent_socket_cloexec(fcgi_addr->sa_family, SOCK_STREAM, 0))) {
1049 log_error_write(srv, __FILE__, __LINE__, "ss",
1050 "failed:", strerror(errno));
1051 return -1;
1054 do {
1055 status = connect(fcgi_fd, fcgi_addr, servlen);
1056 } while (-1 == status && errno == EINTR);
1058 if (-1 == status && errno != ENOENT
1059 && !buffer_string_is_empty(proc->unixsocket)) {
1060 log_error_write(srv, __FILE__, __LINE__, "sbss",
1061 "unlink", proc->unixsocket,
1062 "after connect failed:", strerror(errno));
1063 unlink(proc->unixsocket->ptr);
1066 close(fcgi_fd);
1068 if (-1 == status) {
1069 /* server is not up, spawn it */
1070 pid_t child;
1071 int val;
1073 /* reopen socket */
1074 if (-1 == (fcgi_fd = fdevent_socket_cloexec(fcgi_addr->sa_family, SOCK_STREAM, 0))) {
1075 log_error_write(srv, __FILE__, __LINE__, "ss",
1076 "socket failed:", strerror(errno));
1077 return -1;
1080 val = 1;
1081 if (setsockopt(fcgi_fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) < 0) {
1082 log_error_write(srv, __FILE__, __LINE__, "ss",
1083 "socketsockopt failed:", strerror(errno));
1084 close(fcgi_fd);
1085 return -1;
1088 /* create socket */
1089 if (-1 == bind(fcgi_fd, fcgi_addr, servlen)) {
1090 log_error_write(srv, __FILE__, __LINE__, "sbs",
1091 "bind failed for:",
1092 proc->connection_name,
1093 strerror(errno));
1094 close(fcgi_fd);
1095 return -1;
1098 if (-1 == listen(fcgi_fd, host->listen_backlog)) {
1099 log_error_write(srv, __FILE__, __LINE__, "ss",
1100 "listen failed:", strerror(errno));
1101 close(fcgi_fd);
1102 return -1;
1105 switch ((child = fork())) {
1106 case 0: {
1107 size_t i = 0;
1108 char *c;
1109 char_array env;
1110 char_array arg;
1112 /* create environment */
1113 env.ptr = NULL;
1114 env.size = 0;
1115 env.used = 0;
1117 arg.ptr = NULL;
1118 arg.size = 0;
1119 arg.used = 0;
1121 if(fcgi_fd != FCGI_LISTENSOCK_FILENO) {
1122 dup2(fcgi_fd, FCGI_LISTENSOCK_FILENO);
1123 close(fcgi_fd);
1125 else {
1126 fdevent_clrfd_cloexec(fcgi_fd);
1129 /* we don't need the client socket */
1130 for (i = 3; i < 256; i++) {
1131 close(i);
1134 /* build clean environment */
1135 if (host->bin_env_copy->used) {
1136 for (i = 0; i < host->bin_env_copy->used; i++) {
1137 data_string *ds = (data_string *)host->bin_env_copy->data[i];
1138 char *ge;
1140 if (NULL != (ge = getenv(ds->value->ptr))) {
1141 env_add(&env, CONST_BUF_LEN(ds->value), ge, strlen(ge));
1144 } else {
1145 char ** const e = environ;
1146 for (i = 0; e[i]; ++i) {
1147 char *eq;
1149 if (NULL != (eq = strchr(e[i], '='))) {
1150 env_add(&env, e[i], eq - e[i], eq+1, strlen(eq+1));
1155 /* create environment */
1156 for (i = 0; i < host->bin_env->used; i++) {
1157 data_string *ds = (data_string *)host->bin_env->data[i];
1159 env_add(&env, CONST_BUF_LEN(ds->key), CONST_BUF_LEN(ds->value));
1162 for (i = 0; i < env.used; i++) {
1163 /* search for PHP_FCGI_CHILDREN */
1164 if (0 == strncmp(env.ptr[i], "PHP_FCGI_CHILDREN=", sizeof("PHP_FCGI_CHILDREN=") - 1)) break;
1167 /* not found, add a default */
1168 if (i == env.used) {
1169 env_add(&env, CONST_STR_LEN("PHP_FCGI_CHILDREN"), CONST_STR_LEN("1"));
1172 env.ptr[env.used] = NULL;
1174 parse_binpath(&arg, host->bin_path);
1176 /* chdir into the base of the bin-path,
1177 * search for the last / */
1178 if (NULL != (c = strrchr(arg.ptr[0], '/'))) {
1179 *c = '\0';
1181 /* change to the physical directory */
1182 if (-1 == chdir(arg.ptr[0])) {
1183 *c = '/';
1184 log_error_write(srv, __FILE__, __LINE__, "sss", "chdir failed:", strerror(errno), arg.ptr[0]);
1186 *c = '/';
1189 reset_signals();
1191 /* exec the cgi */
1192 execve(arg.ptr[0], arg.ptr, env.ptr);
1194 /* log_error_write(srv, __FILE__, __LINE__, "sbs",
1195 "execve failed for:", host->bin_path, strerror(errno)); */
1197 _exit(errno);
1199 break;
1201 case -1:
1202 /* error */
1203 close(fcgi_fd);
1204 break;
1205 default:
1206 /* father */
1207 close(fcgi_fd);
1209 /* register process */
1210 proc->is_local = 1;
1211 proc->pid = child;
1213 /* wait */
1214 select(0, NULL, NULL, NULL, &tv);
1216 if (0 != fcgi_proc_waitpid(srv, host, proc)) {
1217 log_error_write(srv, __FILE__, __LINE__, "sb",
1218 "fastcgi-backend failed to start:", host->bin_path);
1219 log_error_write(srv, __FILE__, __LINE__, "s",
1220 "If you're trying to run your app as a FastCGI backend, make sure you're using the FastCGI-enabled version. "
1221 "If this is PHP on Gentoo, add 'fastcgi' to the USE flags. "
1222 "If this is PHP, try removing the bytecode caches for now and try again.");
1223 return -1;
1226 break;
1228 } else {
1229 proc->is_local = 0;
1230 proc->pid = 0;
1232 if (p->conf.debug) {
1233 log_error_write(srv, __FILE__, __LINE__, "sb",
1234 "(debug) socket is already used; won't spawn:",
1235 proc->connection_name);
1239 fcgi_proc_set_state(host, proc, PROC_STATE_RUNNING);
1240 return 0;
1243 #endif /* HAVE_FORK */
1245 static fcgi_extension_host * unixsocket_is_dup(plugin_data *p, size_t used, buffer *unixsocket) {
1246 size_t i, j, n;
1247 for (i = 0; i < used; ++i) {
1248 fcgi_exts *exts = p->config_storage[i]->exts;
1249 if (NULL == exts) continue;
1250 for (j = 0; j < exts->used; ++j) {
1251 fcgi_extension *ex = exts->exts[j];
1252 for (n = 0; n < ex->used; ++n) {
1253 fcgi_extension_host *host = ex->hosts[n];
1254 if (!buffer_string_is_empty(host->unixsocket)
1255 && buffer_is_equal(host->unixsocket, unixsocket)
1256 && !buffer_string_is_empty(host->bin_path))
1257 return host;
1262 return NULL;
1265 SETDEFAULTS_FUNC(mod_fastcgi_set_defaults) {
1266 plugin_data *p = p_d;
1267 data_unset *du;
1268 size_t i = 0;
1269 buffer *fcgi_mode = buffer_init();
1270 fcgi_extension_host *host = NULL;
1272 config_values_t cv[] = {
1273 { "fastcgi.server", NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 0 */
1274 { "fastcgi.debug", NULL, T_CONFIG_INT , T_CONFIG_SCOPE_CONNECTION }, /* 1 */
1275 { "fastcgi.map-extensions", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 2 */
1276 { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
1279 p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *));
1281 for (i = 0; i < srv->config_context->used; i++) {
1282 data_config const* config = (data_config const*)srv->config_context->data[i];
1283 plugin_config *s;
1285 s = malloc(sizeof(plugin_config));
1286 s->exts = NULL;
1287 s->exts_auth = NULL;
1288 s->exts_resp = NULL;
1289 s->debug = 0;
1290 s->ext_mapping = array_init();
1292 cv[0].destination = s->exts; /* not used; T_CONFIG_LOCAL */
1293 cv[1].destination = &(s->debug);
1294 cv[2].destination = s->ext_mapping;
1296 p->config_storage[i] = s;
1298 if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) {
1299 goto error;
1303 * <key> = ( ... )
1306 if (NULL != (du = array_get_element(config->value, "fastcgi.server"))) {
1307 size_t j;
1308 data_array *da = (data_array *)du;
1310 if (du->type != TYPE_ARRAY || !array_is_kvarray(da->value)) {
1311 log_error_write(srv, __FILE__, __LINE__, "s",
1312 "unexpected value for fastcgi.server; expected ( \"ext\" => ( \"backend-label\" => ( \"key\" => \"value\" )))");
1314 goto error;
1317 s->exts = fastcgi_extensions_init();
1318 s->exts_auth = fastcgi_extensions_init();
1319 s->exts_resp = fastcgi_extensions_init();
1322 * fastcgi.server = ( "<ext>" => ( ... ),
1323 * "<ext>" => ( ... ) )
1326 for (j = 0; j < da->value->used; j++) {
1327 size_t n;
1328 data_array *da_ext = (data_array *)da->value->data[j];
1331 * da_ext->key == name of the extension
1335 * fastcgi.server = ( "<ext>" =>
1336 * ( "<host>" => ( ... ),
1337 * "<host>" => ( ... )
1338 * ),
1339 * "<ext>" => ... )
1342 for (n = 0; n < da_ext->value->used; n++) {
1343 data_array *da_host = (data_array *)da_ext->value->data[n];
1345 config_values_t fcv[] = {
1346 { "host", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 0 */
1347 { "docroot", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 1 */
1348 { "mode", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 2 */
1349 { "socket", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 3 */
1350 { "bin-path", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 4 */
1352 { "check-local", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 5 */
1353 { "port", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 6 */
1354 { "max-procs", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 7 */
1355 { "disable-time", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 8 */
1357 { "bin-environment", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 9 */
1358 { "bin-copy-environment", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 10 */
1360 { "broken-scriptfilename", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 11 */
1361 { "allow-x-send-file", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 12 */
1362 { "strip-request-uri", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 13 */
1363 { "kill-signal", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 14 */
1364 { "fix-root-scriptname", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 15 */
1365 { "listen-backlog", NULL, T_CONFIG_INT, T_CONFIG_SCOPE_CONNECTION }, /* 16 */
1366 { "x-sendfile", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 17 */
1367 { "x-sendfile-docroot",NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 18 */
1369 { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
1371 unsigned short host_mode = FCGI_RESPONDER;
1373 if (da_host->type != TYPE_ARRAY || !array_is_kvany(da_host->value)) {
1374 log_error_write(srv, __FILE__, __LINE__, "SBS",
1375 "unexpected value for fastcgi.server near [",
1376 da_host->key, "](string); expected ( \"ext\" => ( \"backend-label\" => ( \"key\" => \"value\" )))");
1378 goto error;
1381 host = fastcgi_host_init();
1382 buffer_reset(fcgi_mode);
1384 buffer_copy_buffer(host->id, da_host->key);
1386 host->check_local = 1;
1387 host->max_procs = 4;
1388 host->disable_time = 1;
1389 host->break_scriptfilename_for_php = 0;
1390 host->xsendfile_allow = 0;
1391 host->kill_signal = SIGTERM;
1392 host->fix_root_path_name = 0;
1393 host->listen_backlog = 1024;
1394 host->refcount = 0;
1396 fcv[0].destination = host->host;
1397 fcv[1].destination = host->docroot;
1398 fcv[2].destination = fcgi_mode;
1399 fcv[3].destination = host->unixsocket;
1400 fcv[4].destination = host->bin_path;
1402 fcv[5].destination = &(host->check_local);
1403 fcv[6].destination = &(host->port);
1404 fcv[7].destination = &(host->max_procs);
1405 fcv[8].destination = &(host->disable_time);
1407 fcv[9].destination = host->bin_env;
1408 fcv[10].destination = host->bin_env_copy;
1409 fcv[11].destination = &(host->break_scriptfilename_for_php);
1410 fcv[12].destination = &(host->xsendfile_allow);
1411 fcv[13].destination = host->strip_request_uri;
1412 fcv[14].destination = &(host->kill_signal);
1413 fcv[15].destination = &(host->fix_root_path_name);
1414 fcv[16].destination = &(host->listen_backlog);
1415 fcv[17].destination = &(host->xsendfile_allow);
1416 fcv[18].destination = host->xsendfile_docroot;
1418 if (0 != config_insert_values_internal(srv, da_host->value, fcv, T_CONFIG_SCOPE_CONNECTION)) {
1419 goto error;
1422 if ((!buffer_string_is_empty(host->host) || host->port) &&
1423 !buffer_string_is_empty(host->unixsocket)) {
1424 log_error_write(srv, __FILE__, __LINE__, "sbsbsbs",
1425 "either host/port or socket have to be set in:",
1426 da->key, "= (",
1427 da_ext->key, " => (",
1428 da_host->key, " ( ...");
1430 goto error;
1433 if (!buffer_string_is_empty(host->unixsocket)) {
1434 /* unix domain socket */
1435 struct sockaddr_un un;
1437 if (buffer_string_length(host->unixsocket) + 1 > sizeof(un.sun_path) - 2) {
1438 log_error_write(srv, __FILE__, __LINE__, "sbsbsbs",
1439 "unixsocket is too long in:",
1440 da->key, "= (",
1441 da_ext->key, " => (",
1442 da_host->key, " ( ...");
1444 goto error;
1447 if (!buffer_string_is_empty(host->bin_path)) {
1448 fcgi_extension_host *duplicate = unixsocket_is_dup(p, i+1, host->unixsocket);
1449 if (NULL != duplicate) {
1450 if (!buffer_is_equal(host->bin_path, duplicate->bin_path)) {
1451 log_error_write(srv, __FILE__, __LINE__, "sb",
1452 "duplicate unixsocket path:",
1453 host->unixsocket);
1454 goto error;
1456 fastcgi_host_free(host);
1457 host = duplicate;
1458 ++host->refcount;
1462 host->family = AF_UNIX;
1463 } else {
1464 /* tcp/ip */
1466 if (buffer_string_is_empty(host->host) &&
1467 buffer_string_is_empty(host->bin_path)) {
1468 log_error_write(srv, __FILE__, __LINE__, "sbsbsbs",
1469 "host or binpath have to be set in:",
1470 da->key, "= (",
1471 da_ext->key, " => (",
1472 da_host->key, " ( ...");
1474 goto error;
1475 } else if (host->port == 0) {
1476 log_error_write(srv, __FILE__, __LINE__, "sbsbsbs",
1477 "port has to be set in:",
1478 da->key, "= (",
1479 da_ext->key, " => (",
1480 da_host->key, " ( ...");
1482 goto error;
1485 host->family = (!buffer_string_is_empty(host->host) && NULL != strchr(host->host->ptr, ':')) ? AF_INET6 : AF_INET;
1488 if (host->refcount) {
1489 /* already init'd; skip spawning */
1490 } else if (!buffer_string_is_empty(host->bin_path)) {
1491 /* a local socket + self spawning */
1492 size_t pno;
1494 struct stat st;
1495 size_t nchars = strcspn(host->bin_path->ptr, " \t");
1496 char c = host->bin_path->ptr[nchars];
1497 host->bin_path->ptr[nchars] = '\0';
1498 if (0 == nchars || 0 != stat(host->bin_path->ptr, &st) || !S_ISREG(st.st_mode) || !(st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) {
1499 host->bin_path->ptr[nchars] = c;
1500 log_error_write(srv, __FILE__, __LINE__, "SSs",
1501 "invalid \"bin-path\" => \"", host->bin_path->ptr,
1502 "\" (check that file exists, is regular file, and is executable by lighttpd)");
1504 host->bin_path->ptr[nchars] = c;
1506 if (s->debug) {
1507 log_error_write(srv, __FILE__, __LINE__, "ssbsdsbsd",
1508 "--- fastcgi spawning local",
1509 "\n\tproc:", host->bin_path,
1510 "\n\tport:", host->port,
1511 "\n\tsocket", host->unixsocket,
1512 "\n\tmax-procs:", host->max_procs);
1515 for (pno = 0; pno < host->max_procs; pno++) {
1516 fcgi_proc *proc;
1518 proc = fastcgi_process_init();
1519 proc->id = host->num_procs++;
1520 host->max_id++;
1522 if (buffer_string_is_empty(host->unixsocket)) {
1523 proc->port = host->port + pno;
1524 } else {
1525 buffer_copy_buffer(proc->unixsocket, host->unixsocket);
1526 buffer_append_string_len(proc->unixsocket, CONST_STR_LEN("-"));
1527 buffer_append_int(proc->unixsocket, pno);
1530 if (s->debug) {
1531 log_error_write(srv, __FILE__, __LINE__, "ssdsbsdsd",
1532 "--- fastcgi spawning",
1533 "\n\tport:", host->port,
1534 "\n\tsocket", host->unixsocket,
1535 "\n\tcurrent:", pno, "/", host->max_procs);
1538 if (!srv->srvconf.preflight_check
1539 && fcgi_spawn_connection(srv, p, host, proc)) {
1540 log_error_write(srv, __FILE__, __LINE__, "s",
1541 "[ERROR]: spawning fcgi failed.");
1542 fastcgi_process_free(proc);
1543 goto error;
1546 fastcgi_status_init(srv, p->statuskey, host, proc);
1548 proc->next = host->first;
1549 if (host->first) host->first->prev = proc;
1551 host->first = proc;
1553 } else {
1554 fcgi_proc *proc;
1556 proc = fastcgi_process_init();
1557 proc->id = host->num_procs++;
1558 host->max_id++;
1559 fcgi_proc_set_state(host, proc, PROC_STATE_RUNNING);
1561 if (buffer_string_is_empty(host->unixsocket)) {
1562 proc->port = host->port;
1563 } else {
1564 buffer_copy_buffer(proc->unixsocket, host->unixsocket);
1567 fastcgi_status_init(srv, p->statuskey, host, proc);
1569 host->first = proc;
1571 host->max_procs = 1;
1574 if (!buffer_string_is_empty(fcgi_mode)) {
1575 if (strcmp(fcgi_mode->ptr, "responder") == 0) {
1576 host_mode = FCGI_RESPONDER;
1577 } else if (strcmp(fcgi_mode->ptr, "authorizer") == 0) {
1578 host_mode = FCGI_AUTHORIZER;
1579 } else {
1580 log_error_write(srv, __FILE__, __LINE__, "sbs",
1581 "WARNING: unknown fastcgi mode:",
1582 fcgi_mode, "(ignored, mode set to responder)");
1586 if (host->xsendfile_docroot->used) {
1587 size_t k;
1588 for (k = 0; k < host->xsendfile_docroot->used; ++k) {
1589 data_string *ds = (data_string *)host->xsendfile_docroot->data[k];
1590 if (ds->type != TYPE_STRING) {
1591 log_error_write(srv, __FILE__, __LINE__, "s",
1592 "unexpected type for x-sendfile-docroot; expected: \"x-sendfile-docroot\" => ( \"/allowed/path\", ... )");
1593 goto error;
1595 if (ds->value->ptr[0] != '/') {
1596 log_error_write(srv, __FILE__, __LINE__, "SBs",
1597 "x-sendfile-docroot paths must begin with '/'; invalid: \"", ds->value, "\"");
1598 goto error;
1600 buffer_path_simplify(ds->value, ds->value);
1601 buffer_append_slash(ds->value);
1605 /* s->exts is list of exts -> hosts
1606 * s->exts now used as combined list of authorizer and responder hosts (for backend maintenance)
1607 * s->exts_auth is list of exts -> authorizer hosts
1608 * s->exts_resp is list of exts -> responder hosts
1609 * For each path/extension, there may be an independent FCGI_AUTHORIZER and FCGI_RESPONDER
1610 * (The FCGI_AUTHORIZER and FCGI_RESPONDER could be handled by the same host,
1611 * and an admin might want to do that for large uploads, since FCGI_AUTHORIZER
1612 * runs prior to receiving (potentially large) request body from client and can
1613 * authorizer or deny request prior to receiving the full upload)
1615 fastcgi_extension_insert(s->exts, da_ext->key, host);
1617 if (host_mode == FCGI_AUTHORIZER) {
1618 ++host->refcount;
1619 fastcgi_extension_insert(s->exts_auth, da_ext->key, host);
1620 } else if (host_mode == FCGI_RESPONDER) {
1621 ++host->refcount;
1622 fastcgi_extension_insert(s->exts_resp, da_ext->key, host);
1623 } /*(else should have been rejected above)*/
1625 host = NULL;
1631 buffer_free(fcgi_mode);
1632 return HANDLER_GO_ON;
1634 error:
1635 if (NULL != host) fastcgi_host_free(host);
1636 buffer_free(fcgi_mode);
1637 return HANDLER_ERROR;
1640 static int fcgi_set_state(server *srv, handler_ctx *hctx, fcgi_connection_state_t state) {
1641 hctx->state = state;
1642 hctx->state_timestamp = srv->cur_ts;
1644 return 0;
1648 static void fcgi_backend_close(server *srv, handler_ctx *hctx) {
1649 if (hctx->fd != -1) {
1650 fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd);
1651 fdevent_unregister(srv->ev, hctx->fd);
1652 fdevent_sched_close(srv->ev, hctx->fd, 1);
1653 hctx->fd = -1;
1654 hctx->fde_ndx = -1;
1657 if (hctx->host) {
1658 if (hctx->proc && hctx->got_proc) {
1659 /* after the connect the process gets a load */
1660 fcgi_proc_load_dec(srv, hctx);
1662 if (hctx->conf.debug) {
1663 log_error_write(srv, __FILE__, __LINE__, "ssdsbsd",
1664 "released proc:",
1665 "pid:", hctx->proc->pid,
1666 "socket:", hctx->proc->connection_name,
1667 "load:", hctx->proc->load);
1671 fcgi_host_reset(srv, hctx);
1675 static fcgi_extension_host * fcgi_extension_host_get(server *srv, connection *con, plugin_data *p, fcgi_extension *extension) {
1676 fcgi_extension_host *host;
1677 int ndx = extension->last_used_ndx + 1;
1678 if (ndx >= (int) extension->used || ndx < 0) ndx = 0;
1679 UNUSED(p);
1681 /* check if the next server has no load */
1682 host = extension->hosts[ndx];
1683 if (host->load > 0 || host->active_procs == 0) {
1684 /* get backend with the least load */
1685 size_t k;
1686 int used = -1;
1687 for (k = 0, ndx = -1; k < extension->used; k++) {
1688 host = extension->hosts[k];
1690 /* we should have at least one proc that can do something */
1691 if (host->active_procs == 0) continue;
1693 if (used == -1 || host->load < used) {
1694 used = host->load;
1695 ndx = k;
1700 if (ndx == -1) {
1701 /* all hosts are down */
1702 /* sorry, we don't have a server alive for this ext */
1703 con->http_status = 503; /* Service Unavailable */
1704 con->mode = DIRECT;
1706 /* only send the 'no handler' once */
1707 if (!extension->note_is_sent) {
1708 extension->note_is_sent = 1;
1710 log_error_write(srv, __FILE__, __LINE__, "sBSbsbs",
1711 "all handlers for", con->uri.path, "?", con->uri.query,
1712 "on", extension->key,
1713 "are down.");
1716 return NULL;
1719 /* found a server */
1720 extension->last_used_ndx = ndx;
1721 return extension->hosts[ndx];
1724 static void fcgi_connection_close(server *srv, handler_ctx *hctx) {
1725 plugin_data *p;
1726 connection *con;
1728 p = hctx->plugin_data;
1729 con = hctx->remote_conn;
1731 fcgi_backend_close(srv, hctx);
1732 handler_ctx_free(hctx);
1733 con->plugin_ctx[p->id] = NULL;
1735 /* finish response (if not already con->file_started, con->file_finished) */
1736 if (con->mode == p->id) {
1737 http_response_backend_done(srv, con);
1741 static handler_t fcgi_reconnect(server *srv, handler_ctx *hctx) {
1742 fcgi_backend_close(srv, hctx);
1744 hctx->host = fcgi_extension_host_get(srv, hctx->remote_conn, hctx->plugin_data, hctx->ext);
1745 if (NULL == hctx->host) return HANDLER_FINISHED;
1747 fcgi_host_assign(srv, hctx, hctx->host);
1748 hctx->request_id = 0;
1749 hctx->opts.xsendfile_allow = hctx->host->xsendfile_allow;
1750 hctx->opts.xsendfile_docroot = hctx->host->xsendfile_docroot;
1751 fcgi_set_state(srv, hctx, FCGI_STATE_INIT);
1752 return HANDLER_COMEBACK;
1756 static handler_t fcgi_connection_reset(server *srv, connection *con, void *p_d) {
1757 plugin_data *p = p_d;
1758 handler_ctx *hctx = con->plugin_ctx[p->id];
1759 if (hctx) fcgi_connection_close(srv, hctx);
1761 return HANDLER_GO_ON;
1765 static int fcgi_env_add(void *venv, const char *key, size_t key_len, const char *val, size_t val_len) {
1766 buffer *env = venv;
1767 size_t len;
1768 char len_enc[8];
1769 size_t len_enc_len = 0;
1771 if (!key || !val) return -1;
1773 len = key_len + val_len;
1775 len += key_len > 127 ? 4 : 1;
1776 len += val_len > 127 ? 4 : 1;
1778 if (buffer_string_length(env) + len >= FCGI_MAX_LENGTH) {
1780 * we can't append more headers, ignore it
1782 return -1;
1786 * field length can be 31bit max
1788 * HINT: this can't happen as FCGI_MAX_LENGTH is only 16bit
1790 force_assert(key_len < 0x7fffffffu);
1791 force_assert(val_len < 0x7fffffffu);
1793 buffer_string_prepare_append(env, len);
1795 if (key_len > 127) {
1796 len_enc[len_enc_len++] = ((key_len >> 24) & 0xff) | 0x80;
1797 len_enc[len_enc_len++] = (key_len >> 16) & 0xff;
1798 len_enc[len_enc_len++] = (key_len >> 8) & 0xff;
1799 len_enc[len_enc_len++] = (key_len >> 0) & 0xff;
1800 } else {
1801 len_enc[len_enc_len++] = (key_len >> 0) & 0xff;
1804 if (val_len > 127) {
1805 len_enc[len_enc_len++] = ((val_len >> 24) & 0xff) | 0x80;
1806 len_enc[len_enc_len++] = (val_len >> 16) & 0xff;
1807 len_enc[len_enc_len++] = (val_len >> 8) & 0xff;
1808 len_enc[len_enc_len++] = (val_len >> 0) & 0xff;
1809 } else {
1810 len_enc[len_enc_len++] = (val_len >> 0) & 0xff;
1813 buffer_append_string_len(env, len_enc, len_enc_len);
1814 buffer_append_string_len(env, key, key_len);
1815 buffer_append_string_len(env, val, val_len);
1817 return 0;
1820 static int fcgi_header(FCGI_Header * header, unsigned char type, int request_id, int contentLength, unsigned char paddingLength) {
1821 force_assert(contentLength <= FCGI_MAX_LENGTH);
1823 header->version = FCGI_VERSION_1;
1824 header->type = type;
1825 header->requestIdB0 = request_id & 0xff;
1826 header->requestIdB1 = (request_id >> 8) & 0xff;
1827 header->contentLengthB0 = contentLength & 0xff;
1828 header->contentLengthB1 = (contentLength >> 8) & 0xff;
1829 header->paddingLength = paddingLength;
1830 header->reserved = 0;
1832 return 0;
1835 typedef enum {
1836 CONNECTION_OK,
1837 CONNECTION_DELAYED, /* retry after event, take same host */
1838 CONNECTION_OVERLOADED, /* disable for 1 second, take another backend */
1839 CONNECTION_DEAD /* disable for 60 seconds, take another backend */
1840 } connection_result_t;
1842 static connection_result_t fcgi_establish_connection(server *srv, handler_ctx *hctx) {
1843 struct sockaddr *fcgi_addr;
1844 struct sockaddr_in fcgi_addr_in;
1845 #if defined(HAVE_IPV6) && defined(HAVE_INET_PTON)
1846 struct sockaddr_in6 fcgi_addr_in6;
1847 #endif
1848 #ifdef HAVE_SYS_UN_H
1849 struct sockaddr_un fcgi_addr_un;
1850 #endif
1851 socklen_t servlen;
1853 fcgi_extension_host *host = hctx->host;
1854 fcgi_proc *proc = hctx->proc;
1855 int fcgi_fd = hctx->fd;
1857 if (!buffer_string_is_empty(proc->unixsocket)) {
1858 #ifdef HAVE_SYS_UN_H
1859 /* use the unix domain socket */
1860 memset(&fcgi_addr_un, 0, sizeof(fcgi_addr_un));
1861 fcgi_addr_un.sun_family = AF_UNIX;
1862 if (buffer_string_length(proc->unixsocket) + 1 > sizeof(fcgi_addr_un.sun_path)) {
1863 log_error_write(srv, __FILE__, __LINE__, "sB",
1864 "ERROR: Unix Domain socket filename too long:",
1865 proc->unixsocket);
1866 return CONNECTION_DEAD;
1868 memcpy(fcgi_addr_un.sun_path, proc->unixsocket->ptr, buffer_string_length(proc->unixsocket) + 1);
1870 #ifdef SUN_LEN
1871 servlen = SUN_LEN(&fcgi_addr_un);
1872 #else
1873 /* stevens says: */
1874 servlen = buffer_string_length(proc->unixsocket) + 1 + sizeof(fcgi_addr_un.sun_family);
1875 #endif
1876 fcgi_addr = (struct sockaddr *) &fcgi_addr_un;
1878 if (buffer_string_is_empty(proc->connection_name)) {
1879 /* on remote spawing we have to set the connection-name now */
1880 buffer_copy_string_len(proc->connection_name, CONST_STR_LEN("unix:"));
1881 buffer_append_string_buffer(proc->connection_name, proc->unixsocket);
1883 #else
1884 return CONNECTION_DEAD;
1885 #endif
1886 #if defined(HAVE_IPV6) && defined(HAVE_INET_PTON)
1887 } else if (host->family == AF_INET6 && !buffer_string_is_empty(host->host)) {
1888 memset(&fcgi_addr_in6, 0, sizeof(fcgi_addr_in6));
1889 fcgi_addr_in6.sin6_family = AF_INET6;
1890 inet_pton(AF_INET6, host->host->ptr, (char *) &fcgi_addr_in6.sin6_addr);
1891 fcgi_addr_in6.sin6_port = htons(proc->port);
1892 servlen = sizeof(fcgi_addr_in6);
1893 fcgi_addr = (struct sockaddr *) &fcgi_addr_in6;
1894 #endif
1895 } else {
1896 memset(&fcgi_addr_in, 0, sizeof(fcgi_addr_in));
1897 fcgi_addr_in.sin_family = AF_INET;
1898 if (!buffer_string_is_empty(host->host)) {
1899 if (0 == inet_aton(host->host->ptr, &(fcgi_addr_in.sin_addr))) {
1900 log_error_write(srv, __FILE__, __LINE__, "sbs",
1901 "converting IP address failed for", host->host,
1902 "\nBe sure to specify an IP address here");
1904 return CONNECTION_DEAD;
1906 } else {
1907 fcgi_addr_in.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
1909 fcgi_addr_in.sin_port = htons(proc->port);
1910 servlen = sizeof(fcgi_addr_in);
1912 fcgi_addr = (struct sockaddr *) &fcgi_addr_in;
1915 if (buffer_string_is_empty(proc->unixsocket)) {
1916 if (buffer_string_is_empty(proc->connection_name)) {
1917 /* on remote spawing we have to set the connection-name now */
1918 buffer_copy_string_len(proc->connection_name, CONST_STR_LEN("tcp:"));
1919 if (!buffer_string_is_empty(host->host)) {
1920 buffer_append_string_buffer(proc->connection_name, host->host);
1921 } else {
1922 buffer_append_string_len(proc->connection_name, CONST_STR_LEN("localhost"));
1924 buffer_append_string_len(proc->connection_name, CONST_STR_LEN(":"));
1925 buffer_append_int(proc->connection_name, proc->port);
1929 if (-1 == connect(fcgi_fd, fcgi_addr, servlen)) {
1930 if (errno == EINPROGRESS ||
1931 errno == EALREADY ||
1932 errno == EINTR) {
1933 if (hctx->conf.debug > 2) {
1934 log_error_write(srv, __FILE__, __LINE__, "sb",
1935 "connect delayed; will continue later:", proc->connection_name);
1938 return CONNECTION_DELAYED;
1939 } else if (errno == EAGAIN) {
1940 if (hctx->conf.debug) {
1941 log_error_write(srv, __FILE__, __LINE__, "sbsd",
1942 "This means that you have more incoming requests than your FastCGI backend can handle in parallel."
1943 "It might help to spawn more FastCGI backends or PHP children; if not, decrease server.max-connections."
1944 "The load for this FastCGI backend", proc->connection_name, "is", proc->load);
1947 return CONNECTION_OVERLOADED;
1948 } else {
1949 log_error_write(srv, __FILE__, __LINE__, "sssb",
1950 "connect failed:",
1951 strerror(errno), "on",
1952 proc->connection_name);
1954 return CONNECTION_DEAD;
1958 hctx->reconnects = 0;
1959 if (hctx->conf.debug > 1) {
1960 log_error_write(srv, __FILE__, __LINE__, "sd",
1961 "connect succeeded: ", fcgi_fd);
1964 return CONNECTION_OK;
1967 static void fcgi_stdin_append(server *srv, connection *con, handler_ctx *hctx, int request_id) {
1968 FCGI_Header header;
1969 chunkqueue *req_cq = con->request_content_queue;
1970 off_t offset, weWant;
1971 const off_t req_cqlen = req_cq->bytes_in - req_cq->bytes_out;
1973 /* something to send ? */
1974 for (offset = 0; offset != req_cqlen; offset += weWant) {
1975 weWant = req_cqlen - offset > FCGI_MAX_LENGTH ? FCGI_MAX_LENGTH : req_cqlen - offset;
1977 /* we announce toWrite octets
1978 * now take all request_content chunks available
1979 * */
1981 fcgi_header(&(header), FCGI_STDIN, request_id, weWant, 0);
1982 chunkqueue_append_mem(hctx->wb, (const char *)&header, sizeof(header));
1983 hctx->wb_reqlen += sizeof(header);
1985 if (hctx->conf.debug > 10) {
1986 log_error_write(srv, __FILE__, __LINE__, "soso", "tosend:", offset, "/", req_cqlen);
1989 chunkqueue_steal(hctx->wb, req_cq, weWant);
1990 /*(hctx->wb_reqlen already includes content_length)*/
1993 if (hctx->wb->bytes_in == hctx->wb_reqlen) {
1994 /* terminate STDIN */
1995 fcgi_header(&(header), FCGI_STDIN, request_id, 0, 0);
1996 chunkqueue_append_mem(hctx->wb, (const char *)&header, sizeof(header));
1997 hctx->wb_reqlen += (int)sizeof(header);
2001 static int fcgi_create_env(server *srv, handler_ctx *hctx, int request_id) {
2002 FCGI_BeginRequestRecord beginRecord;
2003 FCGI_Header header;
2005 plugin_data *p = hctx->plugin_data;
2006 fcgi_extension_host *host= hctx->host;
2008 connection *con = hctx->remote_conn;
2010 http_cgi_opts opts = {
2011 (hctx->fcgi_mode == FCGI_AUTHORIZER),
2012 host->break_scriptfilename_for_php,
2013 host->docroot,
2014 host->strip_request_uri
2017 /* send FCGI_BEGIN_REQUEST */
2019 fcgi_header(&(beginRecord.header), FCGI_BEGIN_REQUEST, request_id, sizeof(beginRecord.body), 0);
2020 beginRecord.body.roleB0 = hctx->fcgi_mode;
2021 beginRecord.body.roleB1 = 0;
2022 beginRecord.body.flags = 0;
2023 memset(beginRecord.body.reserved, 0, sizeof(beginRecord.body.reserved));
2025 /* send FCGI_PARAMS */
2026 buffer_string_prepare_copy(p->fcgi_env, 1023);
2028 if (0 != http_cgi_headers(srv, con, &opts, fcgi_env_add, p->fcgi_env)) {
2029 con->http_status = 400;
2030 return -1;
2031 } else {
2032 buffer *b = buffer_init();
2034 buffer_copy_string_len(b, (const char *)&beginRecord, sizeof(beginRecord));
2036 fcgi_header(&(header), FCGI_PARAMS, request_id, buffer_string_length(p->fcgi_env), 0);
2037 buffer_append_string_len(b, (const char *)&header, sizeof(header));
2038 buffer_append_string_buffer(b, p->fcgi_env);
2040 fcgi_header(&(header), FCGI_PARAMS, request_id, 0, 0);
2041 buffer_append_string_len(b, (const char *)&header, sizeof(header));
2043 hctx->wb_reqlen = buffer_string_length(b);
2044 chunkqueue_append_buffer(hctx->wb, b);
2045 buffer_free(b);
2048 hctx->wb_reqlen += con->request.content_length;/* (eventual) (minimal) total request size, not necessarily including all fcgi_headers around content length yet */
2049 fcgi_stdin_append(srv, con, hctx, request_id);
2051 return 0;
2054 typedef struct {
2055 buffer *b;
2056 unsigned int len;
2057 int type;
2058 int padding;
2059 int request_id;
2060 } fastcgi_response_packet;
2062 static int fastcgi_get_packet(server *srv, handler_ctx *hctx, fastcgi_response_packet *packet) {
2063 chunk *c;
2064 size_t offset;
2065 size_t toread;
2066 FCGI_Header *header;
2068 if (!hctx->rb->first) return -1;
2070 packet->b = buffer_init();
2071 packet->len = 0;
2072 packet->type = 0;
2073 packet->padding = 0;
2074 packet->request_id = 0;
2076 offset = 0; toread = 8;
2077 /* get at least the FastCGI header */
2078 for (c = hctx->rb->first; c; c = c->next) {
2079 size_t weHave = buffer_string_length(c->mem) - c->offset;
2081 if (weHave > toread) weHave = toread;
2083 buffer_append_string_len(packet->b, c->mem->ptr + c->offset, weHave);
2084 toread -= weHave;
2085 offset = weHave; /* skip offset bytes in chunk for "real" data */
2087 if (0 == toread) break;
2090 if (buffer_string_length(packet->b) < sizeof(FCGI_Header)) {
2091 /* no header */
2092 if (hctx->conf.debug) {
2093 log_error_write(srv, __FILE__, __LINE__, "sdsds", "FastCGI: header too small:", buffer_string_length(packet->b), "bytes <", sizeof(FCGI_Header), "bytes, waiting for more data");
2096 buffer_free(packet->b);
2098 return -1;
2101 /* we have at least a header, now check how much me have to fetch */
2102 header = (FCGI_Header *)(packet->b->ptr);
2104 packet->len = (header->contentLengthB0 | (header->contentLengthB1 << 8)) + header->paddingLength;
2105 packet->request_id = (header->requestIdB0 | (header->requestIdB1 << 8));
2106 packet->type = header->type;
2107 packet->padding = header->paddingLength;
2109 /* ->b should only be the content */
2110 buffer_string_set_length(packet->b, 0);
2112 if (packet->len) {
2113 /* copy the content */
2114 for (; c && (buffer_string_length(packet->b) < packet->len); c = c->next) {
2115 size_t weWant = packet->len - buffer_string_length(packet->b);
2116 size_t weHave = buffer_string_length(c->mem) - c->offset - offset;
2118 if (weHave > weWant) weHave = weWant;
2120 buffer_append_string_len(packet->b, c->mem->ptr + c->offset + offset, weHave);
2122 /* we only skipped the first bytes as they belonged to the fcgi header */
2123 offset = 0;
2126 if (buffer_string_length(packet->b) < packet->len) {
2127 /* we didn't get the full packet */
2129 buffer_free(packet->b);
2130 return -1;
2133 buffer_string_set_length(packet->b, buffer_string_length(packet->b) - packet->padding);
2136 chunkqueue_mark_written(hctx->rb, packet->len + sizeof(FCGI_Header));
2138 return 0;
2141 static int fcgi_demux_response(server *srv, handler_ctx *hctx) {
2142 int fin = 0;
2143 int toread;
2144 ssize_t r = 0;
2146 connection *con = hctx->remote_conn;
2147 int fcgi_fd = hctx->fd;
2148 fcgi_proc *proc = hctx->proc;
2151 * check how much we have to read
2153 #ifndef __CYGWIN__ /*(cygwin does not support FIONREAD on sockets)*/
2154 if (0 != fdevent_ioctl_fionread(hctx->fd, S_IFSOCK, &toread)) {
2155 if (errno == EAGAIN) {
2156 return 0;
2158 log_error_write(srv, __FILE__, __LINE__, "sd",
2159 "unexpected end-of-file (perhaps the fastcgi process died):",
2160 fcgi_fd);
2161 return -1;
2162 } else if (0 == toread) {
2163 if (!(fdevent_event_get_interest(srv->ev, hctx->fd) & FDEVENT_IN))
2164 return HANDLER_GO_ON; /* optimistic read; data not ready */
2165 toread = 4096; /* let read() below indicate if EOF or EAGAIN */
2167 #else
2168 toread = 4096;
2169 #endif
2171 if (toread > 0) {
2172 char *mem;
2173 size_t mem_len;
2175 if ((con->conf.stream_response_body & FDEVENT_STREAM_RESPONSE_BUFMIN)) {
2176 off_t cqlen = chunkqueue_length(hctx->rb);
2177 if (cqlen + toread > 65536 + (int)sizeof(FCGI_Header)) { /*(max size of FastCGI packet + 1)*/
2178 if (cqlen < 65536 + (int)sizeof(FCGI_Header)) {
2179 toread = 65536 + (int)sizeof(FCGI_Header) - cqlen;
2180 } else { /* should not happen */
2181 toread = toread < 1024 ? toread : 1024;
2186 chunkqueue_get_memory(hctx->rb, &mem, &mem_len, 0, toread);
2187 r = read(hctx->fd, mem, mem_len);
2188 chunkqueue_use_memory(hctx->rb, r > 0 ? r : 0);
2190 if (-1 == r) {
2191 if (errno == EAGAIN) {
2192 return 0;
2194 log_error_write(srv, __FILE__, __LINE__, "sds",
2195 "unexpected end-of-file (perhaps the fastcgi process died):",
2196 fcgi_fd, strerror(errno));
2197 return -1;
2200 if (0 == r) {
2201 if (!(fdevent_event_get_interest(srv->ev, hctx->fd) & FDEVENT_IN)) return 0;
2202 log_error_write(srv, __FILE__, __LINE__, "ssdsb",
2203 "unexpected end-of-file (perhaps the fastcgi process died):",
2204 "pid:", proc->pid,
2205 "socket:", proc->connection_name);
2207 return -1;
2211 * parse the fastcgi packets and forward the content to the write-queue
2214 while (fin == 0) {
2215 fastcgi_response_packet packet;
2217 /* check if we have at least one packet */
2218 if (0 != fastcgi_get_packet(srv, hctx, &packet)) {
2219 /* no full packet */
2220 break;
2223 switch(packet.type) {
2224 case FCGI_STDOUT:
2225 if (packet.len == 0) break;
2227 /* is the header already finished */
2228 if (0 == con->file_started) {
2229 /* split header from body */
2230 buffer *hdrs = (!hctx->response_header)
2231 ? packet.b
2232 : (buffer_append_string_buffer(hctx->response_header, packet.b), hctx->response_header);
2233 handler_t rc = http_response_parse_headers(srv, con, &hctx->opts, hdrs);
2234 if (rc != HANDLER_GO_ON) {
2235 hctx->send_content_body = 0;
2236 fin = 1;
2237 break;
2239 if (0 == con->file_started) {
2240 if (!hctx->response_header) {
2241 hctx->response_header = packet.b;
2242 packet.b = NULL;
2245 else if (hctx->fcgi_mode == FCGI_AUTHORIZER &&
2246 (con->http_status == 0 || con->http_status == 200)) {
2247 /* authorizer approved request; ignore the content here */
2248 hctx->send_content_body = 0;
2250 } else if (hctx->send_content_body && !buffer_string_is_empty(packet.b)) {
2251 if (0 != http_chunk_append_buffer(srv, con, packet.b)) {
2252 /* error writing to tempfile;
2253 * truncate response or send 500 if nothing sent yet */
2254 fin = 1;
2255 break;
2257 if ((con->conf.stream_response_body & FDEVENT_STREAM_RESPONSE_BUFMIN)
2258 && chunkqueue_length(con->write_queue) > 65536 - 4096) {
2259 if (!con->is_writable) {
2260 /*(defer removal of FDEVENT_IN interest since
2261 * connection_state_machine() might be able to send data
2262 * immediately, unless !con->is_writable, where
2263 * connection_state_machine() might not loop back to call
2264 * mod_fastcgi_handle_subrequest())*/
2265 fdevent_event_clr(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN);
2269 break;
2270 case FCGI_STDERR:
2271 if (packet.len == 0) break;
2273 log_error_write_multiline_buffer(srv, __FILE__, __LINE__, packet.b, "s",
2274 "FastCGI-stderr:");
2276 break;
2277 case FCGI_END_REQUEST:
2278 fin = 1;
2279 break;
2280 default:
2281 log_error_write(srv, __FILE__, __LINE__, "sd",
2282 "FastCGI: header.type not handled: ", packet.type);
2283 break;
2285 buffer_free(packet.b);
2288 return fin;
2291 static int fcgi_restart_dead_procs(server *srv, plugin_data *p, fcgi_extension_host *host) {
2292 fcgi_proc *proc;
2294 for (proc = host->first; proc; proc = proc->next) {
2295 if (p->conf.debug > 2) {
2296 log_error_write(srv, __FILE__, __LINE__, "sbdddd",
2297 "proc:",
2298 proc->connection_name,
2299 proc->state,
2300 proc->is_local,
2301 proc->load,
2302 proc->pid);
2306 * if the remote side is overloaded, we check back after <n> seconds
2309 switch (proc->state) {
2310 case PROC_STATE_KILLED:
2311 /* this should never happen as long as adaptive spawing is disabled */
2312 force_assert(0);
2314 break;
2315 case PROC_STATE_RUNNING:
2316 break;
2317 case PROC_STATE_OVERLOADED:
2318 case PROC_STATE_DIED_WAIT_FOR_PID:
2319 if (0 == fcgi_proc_waitpid(srv, host, proc)) {
2320 fcgi_proc_check_enable(srv, host, proc);
2323 /* fall through if we have a dead proc now */
2324 if (proc->state != PROC_STATE_DIED) break;
2326 case PROC_STATE_DIED:
2327 /* local procs get restarted by us,
2328 * remote ones hopefully by the admin */
2330 if (!buffer_string_is_empty(host->bin_path)) {
2331 /* we still have connections bound to this proc,
2332 * let them terminate first */
2333 if (proc->load != 0) break;
2335 /* restart the child */
2337 if (p->conf.debug) {
2338 log_error_write(srv, __FILE__, __LINE__, "ssbsdsd",
2339 "--- fastcgi spawning",
2340 "\n\tsocket", proc->connection_name,
2341 "\n\tcurrent:", 1, "/", host->max_procs);
2344 if (fcgi_spawn_connection(srv, p, host, proc)) {
2345 log_error_write(srv, __FILE__, __LINE__, "s",
2346 "ERROR: spawning fcgi failed.");
2347 return HANDLER_ERROR;
2349 } else {
2350 fcgi_proc_check_enable(srv, host, proc);
2352 break;
2356 return 0;
2359 static handler_t fcgi_write_request(server *srv, handler_ctx *hctx) {
2360 plugin_data *p = hctx->plugin_data;
2361 fcgi_extension_host *host= hctx->host;
2362 connection *con = hctx->remote_conn;
2363 fcgi_proc *proc;
2365 int ret;
2367 /* we can't handle this in the switch as we have to fall through in it */
2368 if (hctx->state == FCGI_STATE_CONNECT_DELAYED) {
2369 int socket_error;
2370 socklen_t socket_error_len = sizeof(socket_error);
2372 /* try to finish the connect() */
2373 if (0 != getsockopt(hctx->fd, SOL_SOCKET, SO_ERROR, &socket_error, &socket_error_len)) {
2374 log_error_write(srv, __FILE__, __LINE__, "ss",
2375 "getsockopt failed:", strerror(errno));
2377 fcgi_proc_disable(srv, hctx->host, hctx->proc, hctx);
2379 return HANDLER_ERROR;
2381 if (socket_error != 0) {
2382 if (!hctx->proc->is_local || hctx->conf.debug) {
2383 /* local procs get restarted */
2385 log_error_write(srv, __FILE__, __LINE__, "sssb",
2386 "establishing connection failed:", strerror(socket_error),
2387 "socket:", hctx->proc->connection_name);
2390 fcgi_proc_disable(srv, hctx->host, hctx->proc, hctx);
2391 log_error_write(srv, __FILE__, __LINE__, "sdssdsd",
2392 "backend is overloaded; we'll disable it for", hctx->host->disable_time, "seconds and send the request to another backend instead:",
2393 "reconnects:", hctx->reconnects,
2394 "load:", host->load);
2396 fastcgi_status_copy_procname(p->statuskey, hctx->host, hctx->proc);
2397 buffer_append_string_len(p->statuskey, CONST_STR_LEN(".died"));
2399 status_counter_inc(srv, CONST_BUF_LEN(p->statuskey));
2401 return HANDLER_ERROR;
2403 /* go on with preparing the request */
2404 hctx->state = FCGI_STATE_PREPARE_WRITE;
2408 switch(hctx->state) {
2409 case FCGI_STATE_CONNECT_DELAYED:
2410 /* should never happen */
2411 return HANDLER_WAIT_FOR_EVENT;
2412 case FCGI_STATE_INIT:
2413 /* do we have a running process for this host (max-procs) ? */
2414 hctx->proc = NULL;
2416 for (proc = hctx->host->first;
2417 proc && proc->state != PROC_STATE_RUNNING;
2418 proc = proc->next);
2420 /* all children are dead */
2421 if (proc == NULL) {
2422 hctx->fde_ndx = -1;
2424 return HANDLER_ERROR;
2427 hctx->proc = proc;
2429 /* check the other procs if they have a lower load */
2430 for (proc = proc->next; proc; proc = proc->next) {
2431 if (proc->state != PROC_STATE_RUNNING) continue;
2432 if (proc->load < hctx->proc->load) hctx->proc = proc;
2435 if (-1 == (hctx->fd = fdevent_socket_nb_cloexec(host->family, SOCK_STREAM, 0))) {
2436 if (errno == EMFILE ||
2437 errno == EINTR) {
2438 log_error_write(srv, __FILE__, __LINE__, "sd",
2439 "wait for fd at connection:", con->fd);
2441 return HANDLER_WAIT_FOR_FD;
2444 log_error_write(srv, __FILE__, __LINE__, "ssdd",
2445 "socket failed:", strerror(errno), srv->cur_fds, srv->max_fds);
2446 return HANDLER_ERROR;
2448 hctx->fde_ndx = -1;
2450 srv->cur_fds++;
2452 fdevent_register(srv->ev, hctx->fd, fcgi_handle_fdevent, hctx);
2454 if (-1 == fdevent_fcntl_set(srv->ev, hctx->fd)) {
2455 log_error_write(srv, __FILE__, __LINE__, "ss",
2456 "fcntl failed:", strerror(errno));
2458 return HANDLER_ERROR;
2461 if (hctx->proc->is_local) {
2462 hctx->pid = hctx->proc->pid;
2465 switch (fcgi_establish_connection(srv, hctx)) {
2466 case CONNECTION_DELAYED:
2467 /* connection is in progress, wait for an event and call getsockopt() below */
2469 fdevent_event_set(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_OUT);
2471 fcgi_set_state(srv, hctx, FCGI_STATE_CONNECT_DELAYED);
2472 return HANDLER_WAIT_FOR_EVENT;
2473 case CONNECTION_OVERLOADED:
2474 /* cool down the backend, it is overloaded
2475 * -> EAGAIN */
2477 if (hctx->host->disable_time) {
2478 log_error_write(srv, __FILE__, __LINE__, "sdssdsd",
2479 "backend is overloaded; we'll disable it for", hctx->host->disable_time, "seconds and send the request to another backend instead:",
2480 "reconnects:", hctx->reconnects,
2481 "load:", host->load);
2483 hctx->proc->disabled_until = srv->cur_ts + hctx->host->disable_time;
2484 fcgi_proc_set_state(hctx->host, hctx->proc, PROC_STATE_OVERLOADED);
2487 fastcgi_status_copy_procname(p->statuskey, hctx->host, hctx->proc);
2488 buffer_append_string_len(p->statuskey, CONST_STR_LEN(".overloaded"));
2490 status_counter_inc(srv, CONST_BUF_LEN(p->statuskey));
2492 return HANDLER_ERROR;
2493 case CONNECTION_DEAD:
2494 /* we got a hard error from the backend like
2495 * - ECONNREFUSED for tcp-ip sockets
2496 * - ENOENT for unix-domain-sockets
2498 * for check if the host is back in hctx->host->disable_time seconds
2499 * */
2501 fcgi_proc_disable(srv, hctx->host, hctx->proc, hctx);
2503 log_error_write(srv, __FILE__, __LINE__, "sdssdsd",
2504 "backend died; we'll disable it for", hctx->host->disable_time, "seconds and send the request to another backend instead:",
2505 "reconnects:", hctx->reconnects,
2506 "load:", host->load);
2508 fastcgi_status_copy_procname(p->statuskey, hctx->host, hctx->proc);
2509 buffer_append_string_len(p->statuskey, CONST_STR_LEN(".died"));
2511 status_counter_inc(srv, CONST_BUF_LEN(p->statuskey));
2513 return HANDLER_ERROR;
2514 case CONNECTION_OK:
2515 /* everything is ok, go on */
2517 fcgi_set_state(srv, hctx, FCGI_STATE_PREPARE_WRITE);
2519 break;
2521 /* fallthrough */
2522 case FCGI_STATE_PREPARE_WRITE:
2523 /* ok, we have the connection */
2525 fcgi_proc_load_inc(srv, hctx);
2526 hctx->got_proc = 1;
2528 status_counter_inc(srv, CONST_STR_LEN("fastcgi.requests"));
2530 fastcgi_status_copy_procname(p->statuskey, hctx->host, hctx->proc);
2531 buffer_append_string_len(p->statuskey, CONST_STR_LEN(".connected"));
2533 status_counter_inc(srv, CONST_BUF_LEN(p->statuskey));
2535 if (hctx->conf.debug) {
2536 log_error_write(srv, __FILE__, __LINE__, "ssdsbsd",
2537 "got proc:",
2538 "pid:", hctx->proc->pid,
2539 "socket:", hctx->proc->connection_name,
2540 "load:", hctx->proc->load);
2543 /* move the proc-list entry down the list */
2544 if (hctx->request_id == 0) {
2545 hctx->request_id = 1; /* always use id 1 as we don't use multiplexing */
2546 } else {
2547 log_error_write(srv, __FILE__, __LINE__, "sd",
2548 "fcgi-request is already in use:", hctx->request_id);
2551 if (-1 == fcgi_create_env(srv, hctx, hctx->request_id)) return HANDLER_ERROR;
2553 fdevent_event_add(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN);
2554 fcgi_set_state(srv, hctx, FCGI_STATE_WRITE);
2555 /* fall through */
2556 case FCGI_STATE_WRITE:
2557 ret = srv->network_backend_write(srv, con, hctx->fd, hctx->wb, MAX_WRITE_LIMIT);
2559 chunkqueue_remove_finished_chunks(hctx->wb);
2561 if (ret < 0) {
2562 switch(errno) {
2563 case EPIPE:
2564 case ENOTCONN:
2565 case ECONNRESET:
2566 /* the connection got dropped after accept()
2567 * we don't care about that - if you accept() it, you have to handle it.
2570 log_error_write(srv, __FILE__, __LINE__, "ssosb",
2571 "connection was dropped after accept() (perhaps the fastcgi process died),",
2572 "write-offset:", hctx->wb->bytes_out,
2573 "socket:", hctx->proc->connection_name);
2575 return HANDLER_ERROR;
2576 default:
2577 log_error_write(srv, __FILE__, __LINE__, "ssd",
2578 "write failed:", strerror(errno), errno);
2580 return HANDLER_ERROR;
2584 if (hctx->wb->bytes_out == hctx->wb_reqlen) {
2585 fdevent_event_clr(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_OUT);
2586 fcgi_set_state(srv, hctx, FCGI_STATE_READ);
2587 } else {
2588 off_t wblen = hctx->wb->bytes_in - hctx->wb->bytes_out;
2589 if (hctx->wb->bytes_in < hctx->wb_reqlen && wblen < 65536 - 16384) {
2590 /*(con->conf.stream_request_body & FDEVENT_STREAM_REQUEST)*/
2591 if (!(con->conf.stream_request_body & FDEVENT_STREAM_REQUEST_POLLIN)) {
2592 con->conf.stream_request_body |= FDEVENT_STREAM_REQUEST_POLLIN;
2593 con->is_readable = 1; /* trigger optimistic read from client */
2596 if (0 == wblen) {
2597 fdevent_event_clr(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_OUT);
2598 } else {
2599 fdevent_event_add(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_OUT);
2603 return HANDLER_WAIT_FOR_EVENT;
2604 case FCGI_STATE_READ:
2605 /* waiting for a response */
2606 return HANDLER_WAIT_FOR_EVENT;
2607 default:
2608 log_error_write(srv, __FILE__, __LINE__, "s", "(debug) unknown state");
2609 return HANDLER_ERROR;
2614 /* might be called on fdevent after a connect() is delay too
2615 * */
2616 static handler_t fcgi_send_request(server *srv, handler_ctx *hctx) {
2617 /* ok, create the request */
2618 fcgi_extension_host *host = hctx->host;
2619 handler_t rc = fcgi_write_request(srv, hctx);
2620 if (HANDLER_ERROR != rc) {
2621 return rc;
2622 } else {
2623 plugin_data *p = hctx->plugin_data;
2624 connection *con = hctx->remote_conn;
2626 if (hctx->state == FCGI_STATE_INIT ||
2627 hctx->state == FCGI_STATE_CONNECT_DELAYED) {
2628 fcgi_restart_dead_procs(srv, p, host);
2630 /* cleanup this request and let the request handler start this request again */
2631 if (hctx->reconnects++ < 5) {
2632 return fcgi_reconnect(srv, hctx);
2633 } else {
2634 fcgi_connection_close(srv, hctx);
2635 con->http_status = 503;
2637 return HANDLER_FINISHED;
2639 } else {
2640 int status = con->http_status;
2641 fcgi_connection_close(srv, hctx);
2642 con->http_status = (status == 400) ? 400 : 503;
2644 return HANDLER_FINISHED;
2650 static handler_t fcgi_recv_response(server *srv, handler_ctx *hctx);
2653 SUBREQUEST_FUNC(mod_fastcgi_handle_subrequest) {
2654 plugin_data *p = p_d;
2656 handler_ctx *hctx = con->plugin_ctx[p->id];
2658 if (NULL == hctx) return HANDLER_GO_ON;
2660 /* not my job */
2661 if (con->mode != p->id) return HANDLER_GO_ON;
2663 if ((con->conf.stream_response_body & FDEVENT_STREAM_RESPONSE_BUFMIN)
2664 && con->file_started) {
2665 if (chunkqueue_length(con->write_queue) > 65536 - 4096) {
2666 fdevent_event_clr(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN);
2667 } else if (!(fdevent_event_get_interest(srv->ev, hctx->fd) & FDEVENT_IN)) {
2668 /* optimistic read from backend */
2669 handler_t rc = fcgi_recv_response(srv, hctx); /*(might invalidate hctx)*/
2670 if (rc != HANDLER_GO_ON) return rc; /*(unless HANDLER_GO_ON)*/
2671 fdevent_event_add(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN);
2675 /* (do not receive request body before FCGI_AUTHORIZER has run or else
2676 * the request body is discarded with handler_ctx_clear() after running
2677 * the FastCGI Authorizer) */
2679 if (hctx->fcgi_mode != FCGI_AUTHORIZER
2680 && (0 == hctx->wb->bytes_in
2681 ? con->state == CON_STATE_READ_POST
2682 : hctx->wb->bytes_in < hctx->wb_reqlen)) {
2683 /*(64k - 4k to attempt to avoid temporary files
2684 * in conjunction with FDEVENT_STREAM_REQUEST_BUFMIN)*/
2685 if (hctx->wb->bytes_in - hctx->wb->bytes_out > 65536 - 4096
2686 && (con->conf.stream_request_body & FDEVENT_STREAM_REQUEST_BUFMIN)){
2687 con->conf.stream_request_body &= ~FDEVENT_STREAM_REQUEST_POLLIN;
2688 if (0 != hctx->wb->bytes_in) return HANDLER_WAIT_FOR_EVENT;
2689 } else {
2690 handler_t r = connection_handle_read_post_state(srv, con);
2691 chunkqueue *req_cq = con->request_content_queue;
2692 if (0 != hctx->wb->bytes_in && !chunkqueue_is_empty(req_cq)) {
2693 fcgi_stdin_append(srv, con, hctx, hctx->request_id);
2694 if (fdevent_event_get_interest(srv->ev, hctx->fd) & FDEVENT_OUT) {
2695 return (r == HANDLER_GO_ON) ? HANDLER_WAIT_FOR_EVENT : r;
2698 if (r != HANDLER_GO_ON) return r;
2700 /* CGI environment requires that Content-Length be set.
2701 * Send 411 Length Required if Content-Length missing.
2702 * (occurs here if client sends Transfer-Encoding: chunked
2703 * and module is flagged to stream request body to backend) */
2704 if (-1 == con->request.content_length) {
2705 return connection_handle_read_post_error(srv, con, 411);
2710 return ((0 == hctx->wb->bytes_in || !chunkqueue_is_empty(hctx->wb))
2711 && hctx->state != FCGI_STATE_CONNECT_DELAYED)
2712 ? fcgi_send_request(srv, hctx)
2713 : HANDLER_WAIT_FOR_EVENT;
2717 static handler_t fcgi_recv_response(server *srv, handler_ctx *hctx) {
2718 connection *con = hctx->remote_conn;
2719 plugin_data *p = hctx->plugin_data;
2721 fcgi_proc *proc = hctx->proc;
2722 fcgi_extension_host *host= hctx->host;
2724 switch (fcgi_demux_response(srv, hctx)) {
2725 case 0:
2726 break;
2727 case 1:
2729 if (hctx->fcgi_mode == FCGI_AUTHORIZER &&
2730 (con->http_status == 200 ||
2731 con->http_status == 0)) {
2733 * If we are here in AUTHORIZER mode then a request for authorizer
2734 * was processed already, and status 200 has been returned. We need
2735 * now to handle authorized request.
2737 buffer *physpath = NULL;
2739 if (!buffer_string_is_empty(host->docroot)) {
2740 buffer_copy_buffer(con->physical.doc_root, host->docroot);
2741 buffer_copy_buffer(con->physical.basedir, host->docroot);
2743 buffer_copy_buffer(con->physical.path, host->docroot);
2744 buffer_append_string_buffer(con->physical.path, con->uri.path);
2745 physpath = con->physical.path;
2748 fcgi_backend_close(srv, hctx);
2749 handler_ctx_clear(hctx);
2751 /* don't do more than 6 loops here, that normally shouldn't happen */
2752 if (++con->loops_per_request > 5) {
2753 log_error_write(srv, __FILE__, __LINE__, "sb", "too many loops while processing request:", con->request.orig_uri);
2754 con->http_status = 500; /* Internal Server Error */
2755 con->mode = DIRECT;
2756 return HANDLER_FINISHED;
2759 /* restart the request so other handlers can process it */
2761 if (physpath) con->physical.path = NULL;
2762 connection_response_reset(srv, con); /*(includes con->http_status = 0)*/
2763 if (physpath) con->physical.path = physpath; /* preserve con->physical.path with modified docroot */
2765 /*(FYI: if multiple FastCGI authorizers were to be supported,
2766 * next one could be started here instead of restarting request)*/
2768 con->mode = DIRECT;
2769 return HANDLER_COMEBACK;
2770 } else {
2771 /* we are done */
2772 fcgi_connection_close(srv, hctx);
2775 return HANDLER_FINISHED;
2776 case -1:
2777 if (proc->is_local && 1 == proc->load && proc->pid == hctx->pid && proc->state != PROC_STATE_DIED) {
2778 if (0 != fcgi_proc_waitpid(srv, host, proc)) {
2779 if (hctx->conf.debug) {
2780 log_error_write(srv, __FILE__, __LINE__, "ssbsdsd",
2781 "--- fastcgi spawning",
2782 "\n\tsocket", proc->connection_name,
2783 "\n\tcurrent:", 1, "/", host->max_procs);
2786 if (fcgi_spawn_connection(srv, p, host, proc)) {
2787 log_error_write(srv, __FILE__, __LINE__, "s",
2788 "respawning failed, will retry later");
2793 if (con->file_started == 0) {
2794 /* nothing has been sent out yet, try to use another child */
2796 if (hctx->wb->bytes_out == 0 &&
2797 hctx->reconnects++ < 5) {
2799 log_error_write(srv, __FILE__, __LINE__, "ssbsBSBs",
2800 "response not received, request not sent",
2801 "on socket:", proc->connection_name,
2802 "for", con->uri.path, "?", con->uri.query, ", reconnecting");
2804 return fcgi_reconnect(srv, hctx);
2807 log_error_write(srv, __FILE__, __LINE__, "sosbsBSBs",
2808 "response not received, request sent:", hctx->wb->bytes_out,
2809 "on socket:", proc->connection_name,
2810 "for", con->uri.path, "?", con->uri.query, ", closing connection");
2811 } else {
2812 log_error_write(srv, __FILE__, __LINE__, "ssbsBSBs",
2813 "response already sent out, but backend returned error",
2814 "on socket:", proc->connection_name,
2815 "for", con->uri.path, "?", con->uri.query, ", terminating connection");
2818 http_response_backend_error(srv, con);
2819 fcgi_connection_close(srv, hctx);
2820 return HANDLER_FINISHED;
2823 return HANDLER_GO_ON;
2827 static handler_t fcgi_handle_fdevent(server *srv, void *ctx, int revents) {
2828 handler_ctx *hctx = ctx;
2829 connection *con = hctx->remote_conn;
2831 joblist_append(srv, con);
2833 if (revents & FDEVENT_IN) {
2834 handler_t rc = fcgi_recv_response(srv, hctx);/*(might invalidate hctx)*/
2835 if (rc != HANDLER_GO_ON) return rc; /*(unless HANDLER_GO_ON)*/
2838 if (revents & FDEVENT_OUT) {
2839 return fcgi_send_request(srv, hctx); /*(might invalidate hctx)*/
2842 /* perhaps this issue is already handled */
2843 if (revents & FDEVENT_HUP) {
2844 if (hctx->state == FCGI_STATE_CONNECT_DELAYED) {
2845 /* getoptsock will catch this one (right ?)
2847 * if we are in connect we might get an EINPROGRESS
2848 * in the first call and an FDEVENT_HUP in the
2849 * second round
2851 * FIXME: as it is a bit ugly.
2854 fcgi_send_request(srv, hctx);
2855 } else if (con->file_started) {
2856 /* drain any remaining data from kernel pipe buffers
2857 * even if (con->conf.stream_response_body
2858 * & FDEVENT_STREAM_RESPONSE_BUFMIN)
2859 * since event loop will spin on fd FDEVENT_HUP event
2860 * until unregistered. */
2861 handler_t rc;
2862 do {
2863 rc = fcgi_recv_response(srv,hctx);/*(might invalidate hctx)*/
2864 } while (rc == HANDLER_GO_ON); /*(unless HANDLER_GO_ON)*/
2865 return rc; /* HANDLER_FINISHED or HANDLER_ERROR */
2866 } else {
2867 fcgi_proc *proc = hctx->proc;
2868 log_error_write(srv, __FILE__, __LINE__, "sBSbsbsd",
2869 "error: unexpected close of fastcgi connection for",
2870 con->uri.path, "?", con->uri.query,
2871 "(no fastcgi process on socket:", proc->connection_name, "?)",
2872 hctx->state);
2874 fcgi_connection_close(srv, hctx);
2876 } else if (revents & FDEVENT_ERR) {
2877 log_error_write(srv, __FILE__, __LINE__, "s",
2878 "fcgi: got a FDEVENT_ERR. Don't know why.");
2880 http_response_backend_error(srv, con);
2881 fcgi_connection_close(srv, hctx);
2884 return HANDLER_FINISHED;
2887 #define PATCH(x) \
2888 p->conf.x = s->x;
2889 static int fcgi_patch_connection(server *srv, connection *con, plugin_data *p) {
2890 size_t i, j;
2891 plugin_config *s = p->config_storage[0];
2893 PATCH(exts);
2894 PATCH(exts_auth);
2895 PATCH(exts_resp);
2896 PATCH(debug);
2897 PATCH(ext_mapping);
2899 /* skip the first, the global context */
2900 for (i = 1; i < srv->config_context->used; i++) {
2901 data_config *dc = (data_config *)srv->config_context->data[i];
2902 s = p->config_storage[i];
2904 /* condition didn't match */
2905 if (!config_check_cond(srv, con, dc)) continue;
2907 /* merge config */
2908 for (j = 0; j < dc->value->used; j++) {
2909 data_unset *du = dc->value->data[j];
2911 if (buffer_is_equal_string(du->key, CONST_STR_LEN("fastcgi.server"))) {
2912 PATCH(exts);
2913 PATCH(exts_auth);
2914 PATCH(exts_resp);
2915 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("fastcgi.debug"))) {
2916 PATCH(debug);
2917 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("fastcgi.map-extensions"))) {
2918 PATCH(ext_mapping);
2923 return 0;
2925 #undef PATCH
2928 static handler_t fcgi_check_extension(server *srv, connection *con, void *p_d, int uri_path_handler) {
2929 plugin_data *p = p_d;
2930 size_t s_len;
2931 size_t k;
2932 buffer *fn;
2933 fcgi_extension *extension = NULL;
2934 fcgi_extension_host *host = NULL;
2935 handler_ctx *hctx;
2936 unsigned short fcgi_mode;
2938 if (con->mode != DIRECT) return HANDLER_GO_ON;
2940 fn = uri_path_handler ? con->uri.path : con->physical.path;
2942 if (buffer_string_is_empty(fn)) return HANDLER_GO_ON;
2944 s_len = buffer_string_length(fn);
2946 fcgi_patch_connection(srv, con, p);
2947 if (NULL == p->conf.exts) return HANDLER_GO_ON;
2949 /* check p->conf.exts_auth list and then p->conf.ext_resp list
2950 * (skip p->conf.exts_auth if array is empty or if FCGI_AUTHORIZER already ran in this request */
2951 hctx = con->plugin_ctx[p->id]; /*(not NULL if FCGI_AUTHORIZER ran; hctx->ext-auth check is redundant)*/
2952 fcgi_mode = (NULL == hctx || NULL == hctx->ext_auth)
2953 ? 0 /* FCGI_AUTHORIZER p->conf.exts_auth will be searched next */
2954 : FCGI_AUTHORIZER; /* FCGI_RESPONDER p->conf.exts_resp will be searched next */
2956 do {
2958 fcgi_exts *exts;
2959 if (0 == fcgi_mode) {
2960 fcgi_mode = FCGI_AUTHORIZER;
2961 exts = p->conf.exts_auth;
2962 } else {
2963 fcgi_mode = FCGI_RESPONDER;
2964 exts = p->conf.exts_resp;
2967 if (0 == exts->used) continue;
2969 /* fastcgi.map-extensions maps extensions to existing fastcgi.server entries
2971 * fastcgi.map-extensions = ( ".php3" => ".php" )
2973 * fastcgi.server = ( ".php" => ... )
2975 * */
2977 /* check if extension-mapping matches */
2978 for (k = 0; k < p->conf.ext_mapping->used; k++) {
2979 data_string *ds = (data_string *)p->conf.ext_mapping->data[k];
2980 size_t ct_len; /* length of the config entry */
2982 if (buffer_is_empty(ds->key)) continue;
2984 ct_len = buffer_string_length(ds->key);
2986 if (s_len < ct_len) continue;
2988 /* found a mapping */
2989 if (0 == strncmp(fn->ptr + s_len - ct_len, ds->key->ptr, ct_len)) {
2990 /* check if we know the extension */
2992 /* we can reuse k here */
2993 for (k = 0; k < exts->used; k++) {
2994 extension = exts->exts[k];
2996 if (buffer_is_equal(ds->value, extension->key)) {
2997 break;
3001 if (k == exts->used) {
3002 /* found nothing */
3003 extension = NULL;
3005 break;
3009 if (extension == NULL) {
3010 size_t uri_path_len = buffer_string_length(con->uri.path);
3012 /* check if extension matches */
3013 for (k = 0; k < exts->used; k++) {
3014 size_t ct_len; /* length of the config entry */
3015 fcgi_extension *ext = exts->exts[k];
3017 if (buffer_is_empty(ext->key)) continue;
3019 ct_len = buffer_string_length(ext->key);
3021 /* check _url_ in the form "/fcgi_pattern" */
3022 if (ext->key->ptr[0] == '/') {
3023 if ((ct_len <= uri_path_len) &&
3024 (strncmp(con->uri.path->ptr, ext->key->ptr, ct_len) == 0)) {
3025 extension = ext;
3026 break;
3028 } else if ((ct_len <= s_len) && (0 == strncmp(fn->ptr + s_len - ct_len, ext->key->ptr, ct_len))) {
3029 /* check extension in the form ".fcg" */
3030 extension = ext;
3031 break;
3036 } while (NULL == extension && fcgi_mode != FCGI_RESPONDER);
3038 /* extension doesn't match */
3039 if (NULL == extension) {
3040 return HANDLER_GO_ON;
3043 /* check if we have at least one server for this extension up and running */
3044 host = fcgi_extension_host_get(srv, con, p, extension);
3045 if (NULL == host) {
3046 return HANDLER_FINISHED;
3049 /* a note about no handler is not sent yet */
3050 extension->note_is_sent = 0;
3053 * if check-local is disabled, use the uri.path handler
3057 /* init handler-context */
3058 if (uri_path_handler) {
3059 if (host->check_local != 0) {
3060 return HANDLER_GO_ON;
3061 } else {
3062 /* do not split path info for authorizer */
3063 if (fcgi_mode != FCGI_AUTHORIZER) {
3064 /* the prefix is the SCRIPT_NAME,
3065 * everything from start to the next slash
3066 * this is important for check-local = "disable"
3068 * if prefix = /admin.fcgi
3070 * /admin.fcgi/foo/bar
3072 * SCRIPT_NAME = /admin.fcgi
3073 * PATH_INFO = /foo/bar
3075 * if prefix = /fcgi-bin/
3077 * /fcgi-bin/foo/bar
3079 * SCRIPT_NAME = /fcgi-bin/foo
3080 * PATH_INFO = /bar
3082 * if prefix = /, and fix-root-path-name is enable
3084 * /fcgi-bin/foo/bar
3086 * SCRIPT_NAME = /fcgi-bin/foo
3087 * PATH_INFO = /bar
3090 char *pathinfo;
3092 /* the rewrite is only done for /prefix/? matches */
3093 if (host->fix_root_path_name && extension->key->ptr[0] == '/' && extension->key->ptr[1] == '\0') {
3094 buffer_copy_string(con->request.pathinfo, con->uri.path->ptr);
3095 buffer_string_set_length(con->uri.path, 0);
3096 } else if (extension->key->ptr[0] == '/' &&
3097 buffer_string_length(con->uri.path) > buffer_string_length(extension->key) &&
3098 NULL != (pathinfo = strchr(con->uri.path->ptr + buffer_string_length(extension->key), '/'))) {
3099 /* rewrite uri.path and pathinfo */
3101 buffer_copy_string(con->request.pathinfo, pathinfo);
3102 buffer_string_set_length(con->uri.path, buffer_string_length(con->uri.path) - buffer_string_length(con->request.pathinfo));
3108 if (!hctx) hctx = handler_ctx_init();
3110 hctx->remote_conn = con;
3111 hctx->plugin_data = p;
3112 hctx->proc = NULL;
3113 hctx->ext = extension;
3114 fcgi_host_assign(srv, hctx, host);
3116 hctx->fcgi_mode = fcgi_mode;
3117 if (fcgi_mode == FCGI_AUTHORIZER) {
3118 hctx->ext_auth = hctx->ext;
3121 /*hctx->conf.exts = p->conf.exts;*/
3122 /*hctx->conf.exts_auth = p->conf.exts_auth;*/
3123 /*hctx->conf.exts_resp = p->conf.exts_resp;*/
3124 /*hctx->conf.ext_mapping = p->conf.ext_mapping;*/
3125 hctx->conf.debug = p->conf.debug;
3127 hctx->opts.fdfmt = S_IFSOCK;
3128 hctx->opts.backend = BACKEND_FASTCGI;
3129 hctx->opts.authorizer = (fcgi_mode == FCGI_AUTHORIZER);
3130 hctx->opts.local_redir = 0;
3131 hctx->opts.xsendfile_allow = host->xsendfile_allow;
3132 hctx->opts.xsendfile_docroot = host->xsendfile_docroot;
3134 con->plugin_ctx[p->id] = hctx;
3136 con->mode = p->id;
3138 if (con->conf.log_request_handling) {
3139 log_error_write(srv, __FILE__, __LINE__, "s", "handling it in mod_fastcgi");
3142 return HANDLER_GO_ON;
3145 /* uri-path handler */
3146 static handler_t fcgi_check_extension_1(server *srv, connection *con, void *p_d) {
3147 return fcgi_check_extension(srv, con, p_d, 1);
3150 /* start request handler */
3151 static handler_t fcgi_check_extension_2(server *srv, connection *con, void *p_d) {
3152 return fcgi_check_extension(srv, con, p_d, 0);
3156 TRIGGER_FUNC(mod_fastcgi_handle_trigger) {
3157 plugin_data *p = p_d;
3158 size_t i, j, n;
3161 /* perhaps we should kill a connect attempt after 10-15 seconds
3163 * currently we wait for the TCP timeout which is 180 seconds on Linux
3169 /* check all children if they are still up */
3171 for (i = 0; i < srv->config_context->used; i++) {
3172 plugin_config *conf;
3173 fcgi_exts *exts;
3175 conf = p->config_storage[i];
3177 exts = conf->exts;
3178 if (NULL == exts) continue;
3180 for (j = 0; j < exts->used; j++) {
3181 fcgi_extension *ex;
3183 ex = exts->exts[j];
3185 for (n = 0; n < ex->used; n++) {
3187 fcgi_proc *proc;
3188 fcgi_extension_host *host;
3190 host = ex->hosts[n];
3192 for (proc = host->first; proc; proc = proc->next) {
3193 fcgi_proc_waitpid(srv, host, proc);
3196 fcgi_restart_dead_procs(srv, p, host);
3198 for (proc = host->unused_procs; proc; proc = proc->next) {
3199 fcgi_proc_waitpid(srv, host, proc);
3205 return HANDLER_GO_ON;
3209 int mod_fastcgi_plugin_init(plugin *p);
3210 int mod_fastcgi_plugin_init(plugin *p) {
3211 p->version = LIGHTTPD_VERSION_ID;
3212 p->name = buffer_init_string("fastcgi");
3214 p->init = mod_fastcgi_init;
3215 p->cleanup = mod_fastcgi_free;
3216 p->set_defaults = mod_fastcgi_set_defaults;
3217 p->connection_reset = fcgi_connection_reset;
3218 p->handle_uri_clean = fcgi_check_extension_1;
3219 p->handle_subrequest_start = fcgi_check_extension_2;
3220 p->handle_subrequest = mod_fastcgi_handle_subrequest;
3221 p->handle_trigger = mod_fastcgi_handle_trigger;
3223 p->data = NULL;
3225 return 0;