use buffer_string_set_length() to truncate strings
[lighttpd.git] / src / mod_scgi.c
blobb47096a4edffc84e86c15ed56c57feef3af970cf
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 "inet_ntop_cache.h"
18 #include <sys/types.h>
19 #include <unistd.h>
20 #include <errno.h>
21 #include <fcntl.h>
22 #include <string.h>
23 #include <stdlib.h>
24 #include <ctype.h>
25 #include <assert.h>
26 #include <signal.h>
28 #include <stdio.h>
30 #include "sys-socket.h"
32 #ifdef HAVE_SYS_UIO_H
33 # include <sys/uio.h>
34 #endif
36 #ifdef HAVE_SYS_WAIT_H
37 # include <sys/wait.h>
38 #endif
40 #include "version.h"
42 enum {EOL_UNSET, EOL_N, EOL_RN};
46 * TODO:
48 * - add timeout for a connect to a non-scgi process
49 * (use state_timestamp + state)
53 typedef struct scgi_proc {
54 size_t id; /* id will be between 1 and max_procs */
55 buffer *socket; /* config.socket + "-" + id */
56 unsigned port; /* config.port + pno */
58 pid_t pid; /* PID of the spawned process (0 if not spawned locally) */
61 size_t load; /* number of requests waiting on this process */
63 time_t last_used; /* see idle_timeout */
64 size_t requests; /* see max_requests */
65 struct scgi_proc *prev, *next; /* see first */
67 time_t disable_ts; /* replace by host->something */
69 int is_local;
71 enum { PROC_STATE_UNSET, /* init-phase */
72 PROC_STATE_RUNNING, /* alive */
73 PROC_STATE_DIED_WAIT_FOR_PID,
74 PROC_STATE_KILLED, /* was killed as we don't have the load anymore */
75 PROC_STATE_DIED, /* marked as dead, should be restarted */
76 PROC_STATE_DISABLED /* proc disabled as it resulted in an error */
77 } state;
78 } scgi_proc;
80 typedef struct {
81 /* list of processes handling this extension
82 * sorted by lowest load
84 * whenever a job is done move it up in the list
85 * until it is sorted, move it down as soon as the
86 * job is started
88 scgi_proc *first;
89 scgi_proc *unused_procs;
92 * spawn at least min_procs, at max_procs.
94 * as soon as the load of the first entry
95 * is max_load_per_proc we spawn a new one
96 * and add it to the first entry and give it
97 * the load
101 unsigned short min_procs;
102 unsigned short max_procs;
103 size_t num_procs; /* how many procs are started */
104 size_t active_procs; /* how many of them are really running */
106 unsigned short max_load_per_proc;
109 * kick the process from the list if it was not
110 * used for idle_timeout until min_procs is
111 * reached. this helps to get the processlist
112 * small again we had a small peak load.
116 unsigned short idle_timeout;
119 * time after a disabled remote connection is tried to be re-enabled
124 unsigned short disable_time;
127 * same scgi processes get a little bit larger
128 * than wanted. max_requests_per_proc kills a
129 * process after a number of handled requests.
132 size_t max_requests_per_proc;
135 /* config */
138 * host:port
140 * if host is one of the local IP adresses the
141 * whole connection is local
143 * if tcp/ip should be used host AND port have
144 * to be specified
147 buffer *host;
148 unsigned short port;
149 sa_family_t family;
152 * Unix Domain Socket
154 * instead of TCP/IP we can use Unix Domain Sockets
155 * - more secure (you have fileperms to play with)
156 * - more control (on locally)
157 * - more speed (no extra overhead)
159 buffer *unixsocket;
161 /* if socket is local we can start the scgi
162 * process ourself
164 * bin-path is the path to the binary
166 * check min_procs and max_procs for the number
167 * of process to start-up
169 buffer *bin_path;
171 /* bin-path is set bin-environment is taken to
172 * create the environement before starting the
173 * FastCGI process
176 array *bin_env;
178 array *bin_env_copy;
181 * docroot-translation between URL->phys and the
182 * remote host
184 * reasons:
185 * - different dir-layout if remote
186 * - chroot if local
189 buffer *docroot;
192 * check_local tell you if the phys file is stat()ed
193 * or not. FastCGI doesn't care if the service is
194 * remote. If the web-server side doesn't contain
195 * the scgi-files we should not stat() for them
196 * and say '404 not found'.
198 unsigned short check_local;
201 * append PATH_INFO to SCRIPT_FILENAME
203 * php needs this if cgi.fix_pathinfo is provied
208 * workaround for program when prefix="/"
210 * rule to build PATH_INFO is hardcoded for when check_local is disabled
211 * enable this option to use the workaround
215 unsigned short fix_root_path_name;
218 * If the backend includes X-Sendfile in the response
219 * we use the value as filename and ignore the content.
222 unsigned short xsendfile_allow;
223 array *xsendfile_docroot;
225 ssize_t load; /* replace by host->load */
227 size_t max_id; /* corresponds most of the time to
228 num_procs.
230 only if a process is killed max_id waits for the process itself
231 to die and decrements its afterwards */
233 int listen_backlog;
234 } scgi_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 scgi 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 scgi_extension_host **hosts;
261 size_t used;
262 size_t size;
263 } scgi_extension;
265 typedef struct {
266 scgi_extension **exts;
268 size_t used;
269 size_t size;
270 } scgi_exts;
273 typedef struct {
274 scgi_exts *exts;
276 int debug;
277 } plugin_config;
279 typedef struct {
280 char **ptr;
282 size_t size;
283 size_t used;
284 } char_array;
286 /* generic plugin data, shared between all connections */
287 typedef struct {
288 PLUGIN_DATA;
290 buffer *scgi_env;
292 buffer *path;
293 buffer *parse_response;
295 plugin_config **config_storage;
297 plugin_config conf; /* this is only used as long as no handler_ctx is setup */
298 } plugin_data;
300 /* connection specific data */
301 typedef enum { FCGI_STATE_INIT, FCGI_STATE_CONNECT, FCGI_STATE_PREPARE_WRITE,
302 FCGI_STATE_WRITE, FCGI_STATE_READ
303 } scgi_connection_state_t;
305 typedef struct {
306 buffer *response;
307 size_t response_len;
308 int response_type;
309 int response_padding;
311 scgi_proc *proc;
312 scgi_extension_host *host;
314 scgi_connection_state_t state;
315 time_t state_timestamp;
317 int reconnects; /* number of reconnect attempts */
319 chunkqueue *wb;
321 buffer *response_header;
323 int delayed; /* flag to mark that the connect() is delayed */
325 size_t request_id;
326 int fd; /* fd to the scgi process */
327 int fde_ndx; /* index into the fd-event buffer */
329 pid_t pid;
330 int got_proc;
332 plugin_config conf;
334 connection *remote_conn; /* dumb pointer */
335 plugin_data *plugin_data; /* dumb pointer */
336 } handler_ctx;
339 /* ok, we need a prototype */
340 static handler_t scgi_handle_fdevent(server *srv, void *ctx, int revents);
342 int scgi_proclist_sort_down(server *srv, scgi_extension_host *host, scgi_proc *proc);
344 static void reset_signals(void) {
345 #ifdef SIGTTOU
346 signal(SIGTTOU, SIG_DFL);
347 #endif
348 #ifdef SIGTTIN
349 signal(SIGTTIN, SIG_DFL);
350 #endif
351 #ifdef SIGTSTP
352 signal(SIGTSTP, SIG_DFL);
353 #endif
354 signal(SIGHUP, SIG_DFL);
355 signal(SIGPIPE, SIG_DFL);
356 signal(SIGUSR1, SIG_DFL);
359 static handler_ctx * handler_ctx_init(void) {
360 handler_ctx * hctx;
362 hctx = calloc(1, sizeof(*hctx));
363 force_assert(hctx);
365 hctx->fde_ndx = -1;
367 hctx->response = buffer_init();
368 hctx->response_header = buffer_init();
370 hctx->request_id = 0;
371 hctx->state = FCGI_STATE_INIT;
372 hctx->proc = NULL;
374 hctx->response_len = 0;
375 hctx->response_type = 0;
376 hctx->response_padding = 0;
377 hctx->fd = -1;
379 hctx->reconnects = 0;
381 hctx->wb = chunkqueue_init();
383 return hctx;
386 static void handler_ctx_free(handler_ctx *hctx) {
387 buffer_free(hctx->response);
388 buffer_free(hctx->response_header);
390 chunkqueue_free(hctx->wb);
392 free(hctx);
395 static scgi_proc *scgi_process_init(void) {
396 scgi_proc *f;
398 f = calloc(1, sizeof(*f));
399 force_assert(f);
400 f->socket = buffer_init();
402 f->prev = NULL;
403 f->next = NULL;
405 return f;
408 static void scgi_process_free(scgi_proc *f) {
409 if (!f) return;
411 scgi_process_free(f->next);
413 buffer_free(f->socket);
415 free(f);
418 static scgi_extension_host *scgi_host_init(void) {
419 scgi_extension_host *f;
421 f = calloc(1, sizeof(*f));
423 f->host = buffer_init();
424 f->unixsocket = buffer_init();
425 f->docroot = buffer_init();
426 f->bin_path = buffer_init();
427 f->bin_env = array_init();
428 f->bin_env_copy = array_init();
429 f->xsendfile_docroot = array_init();
431 return f;
434 static void scgi_host_free(scgi_extension_host *h) {
435 if (!h) return;
437 buffer_free(h->host);
438 buffer_free(h->unixsocket);
439 buffer_free(h->docroot);
440 buffer_free(h->bin_path);
441 array_free(h->bin_env);
442 array_free(h->bin_env_copy);
443 array_free(h->xsendfile_docroot);
445 scgi_process_free(h->first);
446 scgi_process_free(h->unused_procs);
448 free(h);
452 static scgi_exts *scgi_extensions_init(void) {
453 scgi_exts *f;
455 f = calloc(1, sizeof(*f));
456 force_assert(f);
458 return f;
461 static void scgi_extensions_free(scgi_exts *f) {
462 size_t i;
464 if (!f) return;
466 for (i = 0; i < f->used; i++) {
467 scgi_extension *fe;
468 size_t j;
470 fe = f->exts[i];
472 for (j = 0; j < fe->used; j++) {
473 scgi_extension_host *h;
475 h = fe->hosts[j];
477 scgi_host_free(h);
480 buffer_free(fe->key);
481 free(fe->hosts);
483 free(fe);
486 free(f->exts);
488 free(f);
491 static int scgi_extension_insert(scgi_exts *ext, buffer *key, scgi_extension_host *fh) {
492 scgi_extension *fe;
493 size_t i;
495 /* there is something */
497 for (i = 0; i < ext->used; i++) {
498 if (buffer_is_equal(key, ext->exts[i]->key)) {
499 break;
503 if (i == ext->used) {
504 /* filextension is new */
505 fe = calloc(1, sizeof(*fe));
506 force_assert(fe);
507 fe->key = buffer_init();
508 buffer_copy_buffer(fe->key, key);
510 /* */
512 if (ext->size == 0) {
513 ext->size = 8;
514 ext->exts = malloc(ext->size * sizeof(*(ext->exts)));
515 force_assert(ext->exts);
516 } else if (ext->used == ext->size) {
517 ext->size += 8;
518 ext->exts = realloc(ext->exts, ext->size * sizeof(*(ext->exts)));
519 force_assert(ext->exts);
521 ext->exts[ext->used++] = fe;
522 } else {
523 fe = ext->exts[i];
526 if (fe->size == 0) {
527 fe->size = 4;
528 fe->hosts = malloc(fe->size * sizeof(*(fe->hosts)));
529 force_assert(fe->hosts);
530 } else if (fe->size == fe->used) {
531 fe->size += 4;
532 fe->hosts = realloc(fe->hosts, fe->size * sizeof(*(fe->hosts)));
533 force_assert(fe->hosts);
536 fe->hosts[fe->used++] = fh;
538 return 0;
542 INIT_FUNC(mod_scgi_init) {
543 plugin_data *p;
545 p = calloc(1, sizeof(*p));
546 force_assert(p);
548 p->scgi_env = buffer_init();
550 p->path = buffer_init();
551 p->parse_response = buffer_init();
553 return p;
557 FREE_FUNC(mod_scgi_free) {
558 plugin_data *p = p_d;
560 UNUSED(srv);
562 buffer_free(p->scgi_env);
563 buffer_free(p->path);
564 buffer_free(p->parse_response);
566 if (p->config_storage) {
567 size_t i, j, n;
568 for (i = 0; i < srv->config_context->used; i++) {
569 plugin_config *s = p->config_storage[i];
570 scgi_exts *exts;
572 if (NULL == s) continue;
574 exts = s->exts;
576 for (j = 0; j < exts->used; j++) {
577 scgi_extension *ex;
579 ex = exts->exts[j];
581 for (n = 0; n < ex->used; n++) {
582 scgi_proc *proc;
583 scgi_extension_host *host;
585 host = ex->hosts[n];
587 for (proc = host->first; proc; proc = proc->next) {
588 if (proc->pid != 0) kill(proc->pid, SIGTERM);
590 if (proc->is_local &&
591 !buffer_string_is_empty(proc->socket)) {
592 unlink(proc->socket->ptr);
596 for (proc = host->unused_procs; proc; proc = proc->next) {
597 if (proc->pid != 0) kill(proc->pid, SIGTERM);
599 if (proc->is_local &&
600 !buffer_string_is_empty(proc->socket)) {
601 unlink(proc->socket->ptr);
607 scgi_extensions_free(s->exts);
609 free(s);
611 free(p->config_storage);
614 free(p);
616 return HANDLER_GO_ON;
619 static int env_add(char_array *env, const char *key, size_t key_len, const char *val, size_t val_len) {
620 char *dst;
621 size_t i;
623 if (!key || !val) return -1;
625 dst = malloc(key_len + val_len + 3);
626 force_assert(dst);
627 memcpy(dst, key, key_len);
628 dst[key_len] = '=';
629 /* add the \0 from the value */
630 memcpy(dst + key_len + 1, val, val_len + 1);
632 for (i = 0; i < env->used; i++) {
633 if (0 == strncmp(dst, env->ptr[i], key_len + 1)) {
634 /* don't care about free as we are in a forked child which is going to exec(...) */
635 /* free(env->ptr[i]); */
636 env->ptr[i] = dst;
637 return 0;
641 if (env->size == 0) {
642 env->size = 16;
643 env->ptr = malloc(env->size * sizeof(*env->ptr));
644 force_assert(env->ptr);
645 } else if (env->size == env->used) {
646 env->size += 16;
647 env->ptr = realloc(env->ptr, env->size * sizeof(*env->ptr));
648 force_assert(env->ptr);
651 env->ptr[env->used++] = dst;
653 return 0;
656 #if !defined(HAVE_FORK)
657 static int scgi_spawn_connection(server *srv,
658 plugin_data *p,
659 scgi_extension_host *host,
660 scgi_proc *proc) {
661 UNUSED(srv);
662 UNUSED(p);
663 UNUSED(host);
664 UNUSED(proc);
665 return -1;
668 #else /* -> defined(HAVE_FORK) */
670 static int scgi_spawn_connection(server *srv,
671 plugin_data *p,
672 scgi_extension_host *host,
673 scgi_proc *proc) {
674 int scgi_fd;
675 int status;
676 struct timeval tv = { 0, 100 * 1000 };
677 #ifdef HAVE_SYS_UN_H
678 struct sockaddr_un scgi_addr_un;
679 #endif
680 #if defined(HAVE_IPV6) && defined(HAVE_INET_PTON)
681 struct sockaddr_in6 scgi_addr_in6;
682 #endif
683 struct sockaddr_in scgi_addr_in;
684 struct sockaddr *scgi_addr;
686 socklen_t servlen;
688 if (p->conf.debug) {
689 log_error_write(srv, __FILE__, __LINE__, "sdb",
690 "new proc, socket:", proc->port, proc->socket);
694 if (!buffer_string_is_empty(proc->socket)) {
695 #ifdef HAVE_SYS_UN_H
696 memset(&scgi_addr_un, 0, sizeof(scgi_addr_un));
697 scgi_addr_un.sun_family = AF_UNIX;
698 if (buffer_string_length(proc->socket) + 1 > sizeof(scgi_addr_un.sun_path)) {
699 log_error_write(srv, __FILE__, __LINE__, "sB",
700 "ERROR: Unix Domain socket filename too long:",
701 proc->socket);
702 return -1;
704 memcpy(scgi_addr_un.sun_path, proc->socket->ptr, buffer_string_length(proc->socket) + 1);
706 #ifdef SUN_LEN
707 servlen = SUN_LEN(&scgi_addr_un);
708 #else
709 /* stevens says: */
710 servlen = buffer_string_length(proc->socket) + 1 + sizeof(scgi_addr_un.sun_family);
711 #endif
712 scgi_addr = (struct sockaddr *) &scgi_addr_un;
713 #else
714 log_error_write(srv, __FILE__, __LINE__, "s",
715 "ERROR: Unix Domain sockets are not supported.");
716 return -1;
717 #endif
718 #if defined(HAVE_IPV6) && defined(HAVE_INET_PTON)
719 } else if (host->family == AF_INET6 && !buffer_string_is_empty(host->host)) {
720 memset(&scgi_addr_in6, 0, sizeof(scgi_addr_in6));
721 scgi_addr_in6.sin6_family = AF_INET6;
722 inet_pton(AF_INET6, host->host->ptr, (char *) &scgi_addr_in6.sin6_addr);
723 scgi_addr_in6.sin6_port = htons(proc->port);
724 servlen = sizeof(scgi_addr_in6);
725 scgi_addr = (struct sockaddr *) &scgi_addr_in6;
726 #endif
727 } else {
728 memset(&scgi_addr_in, 0, sizeof(scgi_addr_in));
729 scgi_addr_in.sin_family = AF_INET;
731 if (buffer_string_is_empty(host->host)) {
732 scgi_addr_in.sin_addr.s_addr = htonl(INADDR_ANY);
733 } else {
734 struct hostent *he;
736 /* set a usefull default */
737 scgi_addr_in.sin_addr.s_addr = htonl(INADDR_ANY);
740 if (NULL == (he = gethostbyname(host->host->ptr))) {
741 log_error_write(srv, __FILE__, __LINE__,
742 "sdb", "gethostbyname failed: ",
743 h_errno, host->host);
744 return -1;
747 if (he->h_addrtype != AF_INET) {
748 log_error_write(srv, __FILE__, __LINE__, "sd", "addr-type != AF_INET: ", he->h_addrtype);
749 return -1;
752 if (he->h_length != sizeof(struct in_addr)) {
753 log_error_write(srv, __FILE__, __LINE__, "sd", "addr-length != sizeof(in_addr): ", he->h_length);
754 return -1;
757 memcpy(&(scgi_addr_in.sin_addr.s_addr), he->h_addr_list[0], he->h_length);
760 scgi_addr_in.sin_port = htons(proc->port);
761 servlen = sizeof(scgi_addr_in);
763 scgi_addr = (struct sockaddr *) &scgi_addr_in;
766 if (-1 == (scgi_fd = socket(scgi_addr->sa_family, SOCK_STREAM, 0))) {
767 log_error_write(srv, __FILE__, __LINE__, "ss",
768 "failed:", strerror(errno));
769 return -1;
772 if (-1 == connect(scgi_fd, scgi_addr, servlen)) {
773 /* server is not up, spawn in */
774 pid_t child;
775 int val;
777 if (!buffer_string_is_empty(proc->socket)) {
778 unlink(proc->socket->ptr);
781 close(scgi_fd);
783 /* reopen socket */
784 if (-1 == (scgi_fd = socket(scgi_addr->sa_family, SOCK_STREAM, 0))) {
785 log_error_write(srv, __FILE__, __LINE__, "ss",
786 "socket failed:", strerror(errno));
787 return -1;
790 val = 1;
791 if (setsockopt(scgi_fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) < 0) {
792 log_error_write(srv, __FILE__, __LINE__, "ss",
793 "socketsockopt failed:", strerror(errno));
794 close(scgi_fd);
795 return -1;
798 /* create socket */
799 if (-1 == bind(scgi_fd, scgi_addr, servlen)) {
800 log_error_write(srv, __FILE__, __LINE__, "sbds",
801 "bind failed for:",
802 proc->socket,
803 proc->port,
804 strerror(errno));
805 close(scgi_fd);
806 return -1;
809 if (-1 == listen(scgi_fd, host->listen_backlog)) {
810 log_error_write(srv, __FILE__, __LINE__, "ss",
811 "listen failed:", strerror(errno));
812 close(scgi_fd);
813 return -1;
816 switch ((child = fork())) {
817 case 0: {
818 buffer *b;
819 size_t i = 0;
820 int fd = 0;
821 char_array env;
824 /* create environment */
825 env.ptr = NULL;
826 env.size = 0;
827 env.used = 0;
829 if (scgi_fd != 0) {
830 dup2(scgi_fd, 0);
831 close(scgi_fd);
834 /* we don't need the client socket */
835 for (fd = 3; fd < 256; fd++) {
836 close(fd);
839 /* build clean environment */
840 if (host->bin_env_copy->used) {
841 for (i = 0; i < host->bin_env_copy->used; i++) {
842 data_string *ds = (data_string *)host->bin_env_copy->data[i];
843 char *ge;
845 if (NULL != (ge = getenv(ds->value->ptr))) {
846 env_add(&env, CONST_BUF_LEN(ds->value), ge, strlen(ge));
849 } else {
850 char ** const e = environ;
851 for (i = 0; e[i]; ++i) {
852 char *eq;
854 if (NULL != (eq = strchr(e[i], '='))) {
855 env_add(&env, e[i], eq - e[i], eq+1, strlen(eq+1));
860 /* create environment */
861 for (i = 0; i < host->bin_env->used; i++) {
862 data_string *ds = (data_string *)host->bin_env->data[i];
864 env_add(&env, CONST_BUF_LEN(ds->key), CONST_BUF_LEN(ds->value));
867 for (i = 0; i < env.used; i++) {
868 /* search for PHP_FCGI_CHILDREN */
869 if (0 == strncmp(env.ptr[i], "PHP_FCGI_CHILDREN=", sizeof("PHP_FCGI_CHILDREN=") - 1)) break;
872 /* not found, add a default */
873 if (i == env.used) {
874 env_add(&env, CONST_STR_LEN("PHP_FCGI_CHILDREN"), CONST_STR_LEN("1"));
877 env.ptr[env.used] = NULL;
879 b = buffer_init();
880 buffer_copy_string_len(b, CONST_STR_LEN("exec "));
881 buffer_append_string_buffer(b, host->bin_path);
883 reset_signals();
885 /* exec the cgi */
886 execle("/bin/sh", "sh", "-c", b->ptr, (char *)NULL, env.ptr);
888 log_error_write(srv, __FILE__, __LINE__, "sbs",
889 "execl failed for:", host->bin_path, strerror(errno));
891 _exit(errno);
893 break;
895 case -1:
896 /* error */
897 close(scgi_fd);
898 break;
899 default:
900 /* father */
901 close(scgi_fd);
903 /* wait */
904 select(0, NULL, NULL, NULL, &tv);
906 switch (waitpid(child, &status, WNOHANG)) {
907 case 0:
908 /* child still running after timeout, good */
909 break;
910 case -1:
911 /* no PID found ? should never happen */
912 log_error_write(srv, __FILE__, __LINE__, "ss",
913 "pid not found:", strerror(errno));
914 return -1;
915 default:
916 /* the child should not terminate at all */
917 if (WIFEXITED(status)) {
918 log_error_write(srv, __FILE__, __LINE__, "sd",
919 "child exited (is this a SCGI binary ?):",
920 WEXITSTATUS(status));
921 } else if (WIFSIGNALED(status)) {
922 log_error_write(srv, __FILE__, __LINE__, "sd",
923 "child signaled:",
924 WTERMSIG(status));
925 } else {
926 log_error_write(srv, __FILE__, __LINE__, "sd",
927 "child died somehow:",
928 status);
930 return -1;
933 /* register process */
934 proc->pid = child;
935 proc->last_used = srv->cur_ts;
936 proc->is_local = 1;
938 break;
940 } else {
941 close(scgi_fd);
943 proc->is_local = 0;
944 proc->pid = 0;
946 if (p->conf.debug) {
947 log_error_write(srv, __FILE__, __LINE__, "sb",
948 "(debug) socket is already used, won't spawn:",
949 proc->socket);
953 proc->state = PROC_STATE_RUNNING;
954 host->active_procs++;
956 return 0;
959 #endif /* HAVE_FORK */
961 static int unixsocket_is_dup(plugin_data *p, size_t used, buffer *unixsocket) {
962 size_t i, j, n;
963 for (i = 0; i < used; ++i) {
964 scgi_exts *exts = p->config_storage[i]->exts;
965 for (j = 0; j < exts->used; ++j) {
966 scgi_extension *ex = exts->exts[j];
967 for (n = 0; n < ex->used; ++n) {
968 scgi_extension_host *host = ex->hosts[n];
969 if (!buffer_string_is_empty(host->unixsocket)
970 && buffer_is_equal(host->unixsocket, unixsocket))
971 return 1;
976 return 0;
979 SETDEFAULTS_FUNC(mod_scgi_set_defaults) {
980 plugin_data *p = p_d;
981 data_unset *du;
982 size_t i = 0;
983 scgi_extension_host *df = NULL;
985 config_values_t cv[] = {
986 { "scgi.server", NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 0 */
987 { "scgi.debug", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 1 */
988 { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
991 p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *));
992 force_assert(p->config_storage);
994 for (i = 0; i < srv->config_context->used; i++) {
995 data_config const* config = (data_config const*)srv->config_context->data[i];
996 plugin_config *s;
998 s = malloc(sizeof(plugin_config));
999 force_assert(s);
1000 s->exts = scgi_extensions_init();
1001 s->debug = 0;
1003 cv[0].destination = s->exts;
1004 cv[1].destination = &(s->debug);
1006 p->config_storage[i] = s;
1008 if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) {
1009 goto error;
1013 * <key> = ( ... )
1016 if (NULL != (du = array_get_element(config->value, "scgi.server"))) {
1017 size_t j;
1018 data_array *da = (data_array *)du;
1020 if (du->type != TYPE_ARRAY) {
1021 log_error_write(srv, __FILE__, __LINE__, "sss",
1022 "unexpected type for key: ", "scgi.server", "expected ( \"ext\" => ( \"backend-label\" => ( \"key\" => \"value\" )))");
1024 goto error;
1029 * scgi.server = ( "<ext>" => ( ... ),
1030 * "<ext>" => ( ... ) )
1033 for (j = 0; j < da->value->used; j++) {
1034 size_t n;
1035 data_array *da_ext = (data_array *)da->value->data[j];
1037 if (da->value->data[j]->type != TYPE_ARRAY) {
1038 log_error_write(srv, __FILE__, __LINE__, "sssbs",
1039 "unexpected type for key: ", "scgi.server",
1040 "[", da->value->data[j]->key, "](string); expected ( \"ext\" => ( \"backend-label\" => ( \"key\" => \"value\" )))");
1042 goto error;
1046 * da_ext->key == name of the extension
1050 * scgi.server = ( "<ext>" =>
1051 * ( "<host>" => ( ... ),
1052 * "<host>" => ( ... )
1053 * ),
1054 * "<ext>" => ... )
1057 for (n = 0; n < da_ext->value->used; n++) {
1058 data_array *da_host = (data_array *)da_ext->value->data[n];
1060 config_values_t fcv[] = {
1061 { "host", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 0 */
1062 { "docroot", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 1 */
1063 { "socket", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 2 */
1064 { "bin-path", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 3 */
1066 { "check-local", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 4 */
1067 { "port", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 5 */
1068 { "min-procs-not-working", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 7 this is broken for now */
1069 { "max-procs", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 7 */
1070 { "max-load-per-proc", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 8 */
1071 { "idle-timeout", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 9 */
1072 { "disable-time", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 10 */
1074 { "bin-environment", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 11 */
1075 { "bin-copy-environment", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 12 */
1076 { "fix-root-scriptname", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 13 */
1077 { "listen-backlog", NULL, T_CONFIG_INT, T_CONFIG_SCOPE_CONNECTION }, /* 14 */
1078 { "x-sendfile", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 15 */
1079 { "x-sendfile-docroot",NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 16 */
1082 { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
1085 if (da_host->type != TYPE_ARRAY) {
1086 log_error_write(srv, __FILE__, __LINE__, "ssSBS",
1087 "unexpected type for key:",
1088 "scgi.server",
1089 "[", da_host->key, "](string); expected ( \"ext\" => ( \"backend-label\" => ( \"key\" => \"value\" )))");
1091 goto error;
1094 df = scgi_host_init();
1096 df->check_local = 1;
1097 df->min_procs = 4;
1098 df->max_procs = 4;
1099 df->max_load_per_proc = 1;
1100 df->idle_timeout = 60;
1101 df->disable_time = 60;
1102 df->fix_root_path_name = 0;
1103 df->listen_backlog = 1024;
1104 df->xsendfile_allow = 0;
1106 fcv[0].destination = df->host;
1107 fcv[1].destination = df->docroot;
1108 fcv[2].destination = df->unixsocket;
1109 fcv[3].destination = df->bin_path;
1111 fcv[4].destination = &(df->check_local);
1112 fcv[5].destination = &(df->port);
1113 fcv[6].destination = &(df->min_procs);
1114 fcv[7].destination = &(df->max_procs);
1115 fcv[8].destination = &(df->max_load_per_proc);
1116 fcv[9].destination = &(df->idle_timeout);
1117 fcv[10].destination = &(df->disable_time);
1119 fcv[11].destination = df->bin_env;
1120 fcv[12].destination = df->bin_env_copy;
1121 fcv[13].destination = &(df->fix_root_path_name);
1122 fcv[14].destination = &(df->listen_backlog);
1123 fcv[15].destination = &(df->xsendfile_allow);
1124 fcv[16].destination = df->xsendfile_docroot;
1127 if (0 != config_insert_values_internal(srv, da_host->value, fcv, T_CONFIG_SCOPE_CONNECTION)) {
1128 goto error;
1131 if ((!buffer_string_is_empty(df->host) || df->port) &&
1132 !buffer_string_is_empty(df->unixsocket)) {
1133 log_error_write(srv, __FILE__, __LINE__, "s",
1134 "either host+port or socket");
1136 goto error;
1139 if (!buffer_string_is_empty(df->unixsocket)) {
1140 /* unix domain socket */
1141 struct sockaddr_un un;
1143 if (buffer_string_length(df->unixsocket) + 1 > sizeof(un.sun_path) - 2) {
1144 log_error_write(srv, __FILE__, __LINE__, "s",
1145 "path of the unixdomain socket is too large");
1146 goto error;
1149 if (!buffer_string_is_empty(df->bin_path)
1150 && unixsocket_is_dup(p, i+1, df->unixsocket)) {
1151 log_error_write(srv, __FILE__, __LINE__, "sb",
1152 "duplicate unixsocket path:",
1153 df->unixsocket);
1154 goto error;
1157 df->family = AF_UNIX;
1158 } else {
1159 /* tcp/ip */
1161 if (buffer_string_is_empty(df->host) &&
1162 buffer_string_is_empty(df->bin_path)) {
1163 log_error_write(srv, __FILE__, __LINE__, "sbbbs",
1164 "missing key (string):",
1165 da->key,
1166 da_ext->key,
1167 da_host->key,
1168 "host");
1170 goto error;
1171 } else if (df->port == 0) {
1172 log_error_write(srv, __FILE__, __LINE__, "sbbbs",
1173 "missing key (short):",
1174 da->key,
1175 da_ext->key,
1176 da_host->key,
1177 "port");
1178 goto error;
1181 df->family = (!buffer_string_is_empty(df->host) && NULL != strchr(df->host->ptr, ':')) ? AF_INET6 : AF_INET;
1184 if (!buffer_string_is_empty(df->bin_path)) {
1185 /* a local socket + self spawning */
1186 size_t pno;
1188 /* HACK: just to make sure the adaptive spawing is disabled */
1189 df->min_procs = df->max_procs;
1191 if (df->min_procs > df->max_procs) df->max_procs = df->min_procs;
1192 if (df->max_load_per_proc < 1) df->max_load_per_proc = 0;
1194 if (s->debug) {
1195 log_error_write(srv, __FILE__, __LINE__, "ssbsdsbsdsd",
1196 "--- scgi spawning local",
1197 "\n\tproc:", df->bin_path,
1198 "\n\tport:", df->port,
1199 "\n\tsocket", df->unixsocket,
1200 "\n\tmin-procs:", df->min_procs,
1201 "\n\tmax-procs:", df->max_procs);
1204 for (pno = 0; pno < df->min_procs; pno++) {
1205 scgi_proc *proc;
1207 proc = scgi_process_init();
1208 proc->id = df->num_procs++;
1209 df->max_id++;
1211 if (buffer_string_is_empty(df->unixsocket)) {
1212 proc->port = df->port + pno;
1213 } else {
1214 buffer_copy_buffer(proc->socket, df->unixsocket);
1215 buffer_append_string_len(proc->socket, CONST_STR_LEN("-"));
1216 buffer_append_int(proc->socket, pno);
1219 if (s->debug) {
1220 log_error_write(srv, __FILE__, __LINE__, "ssdsbsdsd",
1221 "--- scgi spawning",
1222 "\n\tport:", df->port,
1223 "\n\tsocket", df->unixsocket,
1224 "\n\tcurrent:", pno, "/", df->min_procs);
1227 if (!srv->srvconf.preflight_check
1228 && scgi_spawn_connection(srv, p, df, proc)) {
1229 log_error_write(srv, __FILE__, __LINE__, "s",
1230 "[ERROR]: spawning fcgi failed.");
1231 scgi_process_free(proc);
1232 goto error;
1235 proc->next = df->first;
1236 if (df->first) df->first->prev = proc;
1238 df->first = proc;
1240 } else {
1241 scgi_proc *fp;
1243 fp = scgi_process_init();
1244 fp->id = df->num_procs++;
1245 df->max_id++;
1246 df->active_procs++;
1247 fp->state = PROC_STATE_RUNNING;
1249 if (buffer_string_is_empty(df->unixsocket)) {
1250 fp->port = df->port;
1251 } else {
1252 buffer_copy_buffer(fp->socket, df->unixsocket);
1255 df->first = fp;
1257 df->min_procs = 1;
1258 df->max_procs = 1;
1261 if (df->xsendfile_docroot->used) {
1262 size_t k;
1263 for (k = 0; k < df->xsendfile_docroot->used; ++k) {
1264 data_string *ds = (data_string *)df->xsendfile_docroot->data[k];
1265 if (ds->type != TYPE_STRING) {
1266 log_error_write(srv, __FILE__, __LINE__, "s",
1267 "unexpected type for x-sendfile-docroot; expected: \"x-sendfile-docroot\" => ( \"/allowed/path\", ... )");
1268 goto error;
1270 if (ds->value->ptr[0] != '/') {
1271 log_error_write(srv, __FILE__, __LINE__, "SBs",
1272 "x-sendfile-docroot paths must begin with '/'; invalid: \"", ds->value, "\"");
1273 goto error;
1275 buffer_path_simplify(ds->value, ds->value);
1276 buffer_append_slash(ds->value);
1280 /* if extension already exists, take it */
1281 scgi_extension_insert(s->exts, da_ext->key, df);
1282 df = NULL;
1288 return HANDLER_GO_ON;
1290 error:
1291 if (NULL != df) scgi_host_free(df);
1292 return HANDLER_ERROR;
1295 static int scgi_set_state(server *srv, handler_ctx *hctx, scgi_connection_state_t state) {
1296 hctx->state = state;
1297 hctx->state_timestamp = srv->cur_ts;
1299 return 0;
1303 static void scgi_connection_close(server *srv, handler_ctx *hctx) {
1304 plugin_data *p;
1305 connection *con;
1307 p = hctx->plugin_data;
1308 con = hctx->remote_conn;
1310 if (hctx->fd != -1) {
1311 fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd);
1312 fdevent_unregister(srv->ev, hctx->fd);
1313 close(hctx->fd);
1314 srv->cur_fds--;
1317 if (hctx->host && hctx->proc) {
1318 hctx->host->load--;
1320 if (hctx->got_proc) {
1321 /* after the connect the process gets a load */
1322 hctx->proc->load--;
1324 if (p->conf.debug) {
1325 log_error_write(srv, __FILE__, __LINE__, "sddb",
1326 "release proc:",
1327 hctx->fd,
1328 hctx->proc->pid, hctx->proc->socket);
1332 scgi_proclist_sort_down(srv, hctx->host, hctx->proc);
1336 handler_ctx_free(hctx);
1337 con->plugin_ctx[p->id] = NULL;
1339 /* finish response (if not already finished) */
1340 if (con->mode == p->id
1341 && (con->state == CON_STATE_HANDLE_REQUEST || con->state == CON_STATE_READ_POST)) {
1342 /* (not CON_STATE_ERROR and not CON_STATE_RESPONSE_END,
1343 * i.e. not called from scgi_connection_reset()) */
1345 /* Send an error if we haven't sent any data yet */
1346 if (0 == con->file_started) {
1347 con->http_status = 500;
1348 con->mode = DIRECT;
1350 else if (!con->file_finished) {
1351 http_chunk_close(srv, con);
1352 con->file_finished = 1;
1357 static int scgi_reconnect(server *srv, handler_ctx *hctx) {
1358 plugin_data *p = hctx->plugin_data;
1360 /* child died
1362 * 1.
1364 * connect was ok, connection was accepted
1365 * but the php accept loop checks after the accept if it should die or not.
1367 * if yes we can only detect it at a write()
1369 * next step is resetting this attemp and setup a connection again
1371 * if we have more then 5 reconnects for the same request, die
1373 * 2.
1375 * we have a connection but the child died by some other reason
1379 fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd);
1380 fdevent_unregister(srv->ev, hctx->fd);
1381 close(hctx->fd);
1382 srv->cur_fds--;
1384 scgi_set_state(srv, hctx, FCGI_STATE_INIT);
1386 hctx->request_id = 0;
1387 hctx->reconnects++;
1389 if (p->conf.debug) {
1390 log_error_write(srv, __FILE__, __LINE__, "sddb",
1391 "release proc:",
1392 hctx->fd,
1393 hctx->proc->pid, hctx->proc->socket);
1396 hctx->proc->load--;
1397 scgi_proclist_sort_down(srv, hctx->host, hctx->proc);
1399 return 0;
1403 static handler_t scgi_connection_reset(server *srv, connection *con, void *p_d) {
1404 plugin_data *p = p_d;
1405 handler_ctx *hctx = con->plugin_ctx[p->id];
1406 if (hctx) scgi_connection_close(srv, hctx);
1408 return HANDLER_GO_ON;
1412 static int scgi_env_add(buffer *env, const char *key, size_t key_len, const char *val, size_t val_len) {
1413 size_t len;
1415 if (!key || !val) return -1;
1417 len = key_len + val_len + 2;
1419 buffer_string_prepare_append(env, len);
1421 buffer_append_string_len(env, key, key_len);
1422 buffer_append_string_len(env, "", 1);
1423 buffer_append_string_len(env, val, val_len);
1424 buffer_append_string_len(env, "", 1);
1426 return 0;
1432 * returns
1433 * -1 error
1434 * 0 connected
1435 * 1 not connected yet
1438 static int scgi_establish_connection(server *srv, handler_ctx *hctx) {
1439 struct sockaddr *scgi_addr;
1440 struct sockaddr_in scgi_addr_in;
1441 #if defined(HAVE_IPV6) && defined(HAVE_INET_PTON)
1442 struct sockaddr_in6 scgi_addr_in6;
1443 #endif
1444 #ifdef HAVE_SYS_UN_H
1445 struct sockaddr_un scgi_addr_un;
1446 #endif
1447 socklen_t servlen;
1449 scgi_extension_host *host = hctx->host;
1450 scgi_proc *proc = hctx->proc;
1451 int scgi_fd = hctx->fd;
1453 if (!buffer_string_is_empty(proc->socket)) {
1454 #ifdef HAVE_SYS_UN_H
1455 /* use the unix domain socket */
1456 memset(&scgi_addr_un, 0, sizeof(scgi_addr_un));
1457 scgi_addr_un.sun_family = AF_UNIX;
1458 if (buffer_string_length(proc->socket) + 1 > sizeof(scgi_addr_un.sun_path)) {
1459 log_error_write(srv, __FILE__, __LINE__, "sB",
1460 "ERROR: Unix Domain socket filename too long:",
1461 proc->socket);
1462 return -1;
1464 memcpy(scgi_addr_un.sun_path, proc->socket->ptr, buffer_string_length(proc->socket) + 1);
1466 #ifdef SUN_LEN
1467 servlen = SUN_LEN(&scgi_addr_un);
1468 #else
1469 /* stevens says: */
1470 servlen = buffer_string_length(proc->socket) + 1 + sizeof(scgi_addr_un.sun_family);
1471 #endif
1472 scgi_addr = (struct sockaddr *) &scgi_addr_un;
1473 #else
1474 return -1;
1475 #endif
1476 #if defined(HAVE_IPV6) && defined(HAVE_INET_PTON)
1477 } else if (host->family == AF_INET6 && !buffer_string_is_empty(host->host)) {
1478 memset(&scgi_addr_in6, 0, sizeof(scgi_addr_in6));
1479 scgi_addr_in6.sin6_family = AF_INET6;
1480 inet_pton(AF_INET6, host->host->ptr, (char *) &scgi_addr_in6.sin6_addr);
1481 scgi_addr_in6.sin6_port = htons(proc->port);
1482 servlen = sizeof(scgi_addr_in6);
1483 scgi_addr = (struct sockaddr *) &scgi_addr_in6;
1484 #endif
1485 } else {
1486 memset(&scgi_addr_in, 0, sizeof(scgi_addr_in));
1487 scgi_addr_in.sin_family = AF_INET;
1488 if (0 == inet_aton(host->host->ptr, &(scgi_addr_in.sin_addr))) {
1489 log_error_write(srv, __FILE__, __LINE__, "sbs",
1490 "converting IP-adress failed for", host->host,
1491 "\nBe sure to specify an IP address here");
1493 return -1;
1495 scgi_addr_in.sin_port = htons(proc->port);
1496 servlen = sizeof(scgi_addr_in);
1498 scgi_addr = (struct sockaddr *) &scgi_addr_in;
1501 if (-1 == connect(scgi_fd, scgi_addr, servlen)) {
1502 if (errno == EINPROGRESS ||
1503 errno == EALREADY ||
1504 errno == EINTR) {
1505 if (hctx->conf.debug) {
1506 log_error_write(srv, __FILE__, __LINE__, "sd",
1507 "connect delayed, will continue later:", scgi_fd);
1510 return 1;
1511 } else {
1512 log_error_write(srv, __FILE__, __LINE__, "sdsddb",
1513 "connect failed:", scgi_fd,
1514 strerror(errno), errno,
1515 proc->port, proc->socket);
1517 if (errno == EAGAIN) {
1518 /* this is Linux only */
1520 log_error_write(srv, __FILE__, __LINE__, "s",
1521 "If this happend on Linux: You have been run out of local ports. "
1522 "Check the manual, section Performance how to handle this.");
1525 return -1;
1528 if (hctx->conf.debug > 1) {
1529 log_error_write(srv, __FILE__, __LINE__, "sd",
1530 "connect succeeded: ", scgi_fd);
1535 return 0;
1538 static int scgi_env_add_request_headers(server *srv, connection *con, plugin_data *p) {
1539 size_t i;
1541 for (i = 0; i < con->request.headers->used; i++) {
1542 data_string *ds;
1544 ds = (data_string *)con->request.headers->data[i];
1546 if (!buffer_is_empty(ds->value) && !buffer_is_empty(ds->key)) {
1547 buffer_copy_string_encoded_cgi_varnames(srv->tmp_buf, CONST_BUF_LEN(ds->key), 1);
1549 scgi_env_add(p->scgi_env, CONST_BUF_LEN(srv->tmp_buf), CONST_BUF_LEN(ds->value));
1553 for (i = 0; i < con->environment->used; i++) {
1554 data_string *ds;
1556 ds = (data_string *)con->environment->data[i];
1558 if (!buffer_is_empty(ds->value) && !buffer_is_empty(ds->key)) {
1559 buffer_copy_string_encoded_cgi_varnames(srv->tmp_buf, CONST_BUF_LEN(ds->key), 0);
1561 scgi_env_add(p->scgi_env, CONST_BUF_LEN(srv->tmp_buf), CONST_BUF_LEN(ds->value));
1565 return 0;
1569 static int scgi_create_env(server *srv, handler_ctx *hctx) {
1570 char buf[LI_ITOSTRING_LENGTH];
1571 const char *s;
1572 #ifdef HAVE_IPV6
1573 char b2[INET6_ADDRSTRLEN + 1];
1574 #endif
1575 buffer *b;
1577 plugin_data *p = hctx->plugin_data;
1578 scgi_extension_host *host= hctx->host;
1580 connection *con = hctx->remote_conn;
1581 server_socket *srv_sock = con->srv_socket;
1583 sock_addr our_addr;
1584 socklen_t our_addr_len;
1586 buffer_string_prepare_copy(p->scgi_env, 1023);
1588 /* CGI-SPEC 6.1.2, FastCGI spec 6.3 and SCGI spec */
1590 li_itostrn(buf, sizeof(buf), con->request.content_length);
1591 scgi_env_add(p->scgi_env, CONST_STR_LEN("CONTENT_LENGTH"), buf, strlen(buf));
1592 scgi_env_add(p->scgi_env, CONST_STR_LEN("SCGI"), CONST_STR_LEN("1"));
1595 if (buffer_is_empty(con->conf.server_tag)) {
1596 scgi_env_add(p->scgi_env, CONST_STR_LEN("SERVER_SOFTWARE"), CONST_STR_LEN(PACKAGE_DESC));
1597 } else {
1598 scgi_env_add(p->scgi_env, CONST_STR_LEN("SERVER_SOFTWARE"), CONST_BUF_LEN(con->conf.server_tag));
1601 if (!buffer_is_empty(con->server_name)) {
1602 size_t len = buffer_string_length(con->server_name);
1604 if (con->server_name->ptr[0] == '[') {
1605 const char *colon = strstr(con->server_name->ptr, "]:");
1606 if (colon) len = (colon + 1) - con->server_name->ptr;
1607 } else {
1608 const char *colon = strchr(con->server_name->ptr, ':');
1609 if (colon) len = colon - con->server_name->ptr;
1612 scgi_env_add(p->scgi_env, CONST_STR_LEN("SERVER_NAME"), con->server_name->ptr, len);
1613 } else {
1614 #ifdef HAVE_IPV6
1615 s = inet_ntop(srv_sock->addr.plain.sa_family,
1616 srv_sock->addr.plain.sa_family == AF_INET6 ?
1617 (const void *) &(srv_sock->addr.ipv6.sin6_addr) :
1618 (const void *) &(srv_sock->addr.ipv4.sin_addr),
1619 b2, sizeof(b2)-1);
1620 #else
1621 s = inet_ntoa(srv_sock->addr.ipv4.sin_addr);
1622 #endif
1623 force_assert(s);
1624 scgi_env_add(p->scgi_env, CONST_STR_LEN("SERVER_NAME"), s, strlen(s));
1627 scgi_env_add(p->scgi_env, CONST_STR_LEN("GATEWAY_INTERFACE"), CONST_STR_LEN("CGI/1.1"));
1629 li_utostrn(buf, sizeof(buf),
1630 #ifdef HAVE_IPV6
1631 ntohs(srv_sock->addr.plain.sa_family ? srv_sock->addr.ipv6.sin6_port : srv_sock->addr.ipv4.sin_port)
1632 #else
1633 ntohs(srv_sock->addr.ipv4.sin_port)
1634 #endif
1637 scgi_env_add(p->scgi_env, CONST_STR_LEN("SERVER_PORT"), buf, strlen(buf));
1639 /* get the server-side of the connection to the client */
1640 our_addr_len = sizeof(our_addr);
1642 if (-1 == getsockname(con->fd, (struct sockaddr *)&our_addr, &our_addr_len)
1643 || our_addr_len > sizeof(our_addr)) {
1644 s = inet_ntop_cache_get_ip(srv, &(srv_sock->addr));
1645 } else {
1646 s = inet_ntop_cache_get_ip(srv, &(our_addr));
1648 scgi_env_add(p->scgi_env, CONST_STR_LEN("SERVER_ADDR"), s, strlen(s));
1650 li_utostrn(buf, sizeof(buf),
1651 #ifdef HAVE_IPV6
1652 ntohs(con->dst_addr.plain.sa_family ? con->dst_addr.ipv6.sin6_port : con->dst_addr.ipv4.sin_port)
1653 #else
1654 ntohs(con->dst_addr.ipv4.sin_port)
1655 #endif
1658 scgi_env_add(p->scgi_env, CONST_STR_LEN("REMOTE_PORT"), buf, strlen(buf));
1660 s = inet_ntop_cache_get_ip(srv, &(con->dst_addr));
1661 force_assert(s);
1662 scgi_env_add(p->scgi_env, CONST_STR_LEN("REMOTE_ADDR"), s, strlen(s));
1665 * SCRIPT_NAME, PATH_INFO and PATH_TRANSLATED according to
1666 * http://cgi-spec.golux.com/draft-coar-cgi-v11-03-clean.html
1667 * (6.1.14, 6.1.6, 6.1.7)
1670 scgi_env_add(p->scgi_env, CONST_STR_LEN("SCRIPT_NAME"), CONST_BUF_LEN(con->uri.path));
1672 if (!buffer_string_is_empty(con->request.pathinfo)) {
1673 scgi_env_add(p->scgi_env, CONST_STR_LEN("PATH_INFO"), CONST_BUF_LEN(con->request.pathinfo));
1675 /* PATH_TRANSLATED is only defined if PATH_INFO is set */
1677 if (!buffer_string_is_empty(host->docroot)) {
1678 buffer_copy_buffer(p->path, host->docroot);
1679 } else {
1680 buffer_copy_buffer(p->path, con->physical.basedir);
1682 buffer_append_string_buffer(p->path, con->request.pathinfo);
1683 scgi_env_add(p->scgi_env, CONST_STR_LEN("PATH_TRANSLATED"), CONST_BUF_LEN(p->path));
1684 } else {
1685 scgi_env_add(p->scgi_env, CONST_STR_LEN("PATH_INFO"), CONST_STR_LEN(""));
1689 * SCRIPT_FILENAME and DOCUMENT_ROOT for php. The PHP manual
1690 * http://www.php.net/manual/en/reserved.variables.php
1691 * treatment of PATH_TRANSLATED is different from the one of CGI specs.
1692 * TODO: this code should be checked against cgi.fix_pathinfo php
1693 * parameter.
1696 if (!buffer_string_is_empty(host->docroot)) {
1698 * rewrite SCRIPT_FILENAME
1702 buffer_copy_buffer(p->path, host->docroot);
1703 buffer_append_string_buffer(p->path, con->uri.path);
1705 scgi_env_add(p->scgi_env, CONST_STR_LEN("SCRIPT_FILENAME"), CONST_BUF_LEN(p->path));
1706 scgi_env_add(p->scgi_env, CONST_STR_LEN("DOCUMENT_ROOT"), CONST_BUF_LEN(host->docroot));
1707 } else {
1708 buffer_copy_buffer(p->path, con->physical.path);
1710 scgi_env_add(p->scgi_env, CONST_STR_LEN("SCRIPT_FILENAME"), CONST_BUF_LEN(p->path));
1711 scgi_env_add(p->scgi_env, CONST_STR_LEN("DOCUMENT_ROOT"), CONST_BUF_LEN(con->physical.basedir));
1713 if (con->error_handler_saved_status >= 0) {
1714 scgi_env_add(p->scgi_env, CONST_STR_LEN("REQUEST_URI"), CONST_BUF_LEN(con->request.uri));
1715 } else {
1716 scgi_env_add(p->scgi_env, CONST_STR_LEN("REQUEST_URI"), CONST_BUF_LEN(con->request.orig_uri));
1718 if (!buffer_string_is_empty(con->uri.query)) {
1719 scgi_env_add(p->scgi_env, CONST_STR_LEN("QUERY_STRING"), CONST_BUF_LEN(con->uri.query));
1720 } else {
1721 scgi_env_add(p->scgi_env, CONST_STR_LEN("QUERY_STRING"), CONST_STR_LEN(""));
1724 s = get_http_method_name(con->request.http_method);
1725 force_assert(s);
1726 scgi_env_add(p->scgi_env, CONST_STR_LEN("REQUEST_METHOD"), s, strlen(s));
1727 /* set REDIRECT_STATUS for php compiled with --force-redirect
1728 * (if REDIRECT_STATUS has not already been set by error handler) */
1729 if (0 == con->error_handler_saved_status) {
1730 scgi_env_add(p->scgi_env, CONST_STR_LEN("REDIRECT_STATUS"), CONST_STR_LEN("200"));
1732 s = get_http_version_name(con->request.http_version);
1733 force_assert(s);
1734 scgi_env_add(p->scgi_env, CONST_STR_LEN("SERVER_PROTOCOL"), s, strlen(s));
1736 #ifdef USE_OPENSSL
1737 if (buffer_is_equal_caseless_string(con->uri.scheme, CONST_STR_LEN("https"))) {
1738 scgi_env_add(p->scgi_env, CONST_STR_LEN("HTTPS"), CONST_STR_LEN("on"));
1740 #endif
1742 scgi_env_add_request_headers(srv, con, p);
1744 b = buffer_init();
1746 buffer_append_int(b, buffer_string_length(p->scgi_env));
1747 buffer_append_string_len(b, CONST_STR_LEN(":"));
1748 buffer_append_string_buffer(b, p->scgi_env);
1749 buffer_append_string_len(b, CONST_STR_LEN(","));
1751 chunkqueue_append_buffer(hctx->wb, b);
1752 buffer_free(b);
1754 if (con->request.content_length) {
1755 chunkqueue *req_cq = con->request_content_queue;
1757 chunkqueue_steal(hctx->wb, req_cq, req_cq->bytes_in);
1760 return 0;
1763 static int scgi_response_parse(server *srv, connection *con, plugin_data *p, buffer *in, int eol) {
1764 char *ns;
1765 const char *s;
1766 int line = 0;
1768 UNUSED(srv);
1770 buffer_copy_buffer(p->parse_response, in);
1772 for (s = p->parse_response->ptr;
1773 NULL != (ns = (eol == EOL_RN ? strstr(s, "\r\n") : strchr(s, '\n')));
1774 s = ns + (eol == EOL_RN ? 2 : 1), line++) {
1775 const char *key, *value;
1776 int key_len;
1777 data_string *ds;
1779 ns[0] = '\0';
1781 if (line == 0 &&
1782 0 == strncmp(s, "HTTP/1.", 7)) {
1783 /* non-parsed header ... we parse them anyway */
1785 if ((s[7] == '1' ||
1786 s[7] == '0') &&
1787 s[8] == ' ') {
1788 int status;
1789 /* after the space should be a status code for us */
1791 status = strtol(s+9, NULL, 10);
1793 if (status >= 100 && status < 1000) {
1794 /* we expected 3 digits got them */
1795 con->parsed_response |= HTTP_STATUS;
1796 con->http_status = status;
1799 } else {
1801 key = s;
1802 if (NULL == (value = strchr(s, ':'))) {
1803 /* we expect: "<key>: <value>\r\n" */
1804 continue;
1807 key_len = value - key;
1808 value += 1;
1810 /* skip LWS */
1811 while (*value == ' ' || *value == '\t') value++;
1813 if (NULL == (ds = (data_string *)array_get_unused_element(con->response.headers, TYPE_STRING))) {
1814 ds = data_response_init();
1816 buffer_copy_string_len(ds->key, key, key_len);
1817 buffer_copy_string(ds->value, value);
1819 array_insert_unique(con->response.headers, (data_unset *)ds);
1821 switch(key_len) {
1822 case 4:
1823 if (0 == strncasecmp(key, "Date", key_len)) {
1824 con->parsed_response |= HTTP_DATE;
1826 break;
1827 case 6:
1828 if (0 == strncasecmp(key, "Status", key_len)) {
1829 int status = strtol(value, NULL, 10);
1830 if (status >= 100 && status < 1000) {
1831 con->http_status = status;
1832 con->parsed_response |= HTTP_STATUS;
1833 } else {
1834 con->http_status = 502;
1837 break;
1838 case 8:
1839 if (0 == strncasecmp(key, "Location", key_len)) {
1840 con->parsed_response |= HTTP_LOCATION;
1842 break;
1843 case 10:
1844 if (0 == strncasecmp(key, "Connection", key_len)) {
1845 con->response.keep_alive = (0 == strcasecmp(value, "Keep-Alive")) ? 1 : 0;
1846 con->parsed_response |= HTTP_CONNECTION;
1848 break;
1849 case 14:
1850 if (0 == strncasecmp(key, "Content-Length", key_len)) {
1851 con->response.content_length = strtoul(value, NULL, 10);
1852 con->parsed_response |= HTTP_CONTENT_LENGTH;
1854 break;
1855 default:
1856 break;
1861 /* CGI/1.1 rev 03 - 7.2.1.2 */
1862 if ((con->parsed_response & HTTP_LOCATION) &&
1863 !(con->parsed_response & HTTP_STATUS)) {
1864 con->http_status = 302;
1867 return 0;
1871 static int scgi_demux_response(server *srv, handler_ctx *hctx) {
1872 plugin_data *p = hctx->plugin_data;
1873 connection *con = hctx->remote_conn;
1875 while(1) {
1876 int n;
1878 buffer_string_prepare_copy(hctx->response, 1023);
1879 if (-1 == (n = read(hctx->fd, hctx->response->ptr, hctx->response->size - 1))) {
1880 if (errno == EAGAIN || errno == EINTR) {
1881 /* would block, wait for signal */
1882 return 0;
1884 /* error */
1885 log_error_write(srv, __FILE__, __LINE__, "sdd", strerror(errno), con->fd, hctx->fd);
1886 return -1;
1889 if (n == 0) {
1890 /* read finished */
1892 con->file_finished = 1;
1894 /* send final chunk */
1895 http_chunk_close(srv, con);
1897 return 1;
1900 buffer_commit(hctx->response, n);
1902 /* split header from body */
1904 if (con->file_started == 0) {
1905 char *c;
1906 int in_header = 0;
1907 int header_end = 0;
1908 int cp, eol = EOL_UNSET;
1909 size_t used = 0;
1910 size_t hlen = 0;
1912 buffer_append_string_buffer(hctx->response_header, hctx->response);
1914 /* nph (non-parsed headers) */
1915 if (0 == strncmp(hctx->response_header->ptr, "HTTP/1.", 7)) in_header = 1;
1917 /* search for the \r\n\r\n or \n\n in the string */
1918 for (c = hctx->response_header->ptr, cp = 0, used = buffer_string_length(hctx->response_header); used; c++, cp++, used--) {
1919 if (*c == ':') in_header = 1;
1920 else if (*c == '\n') {
1921 if (in_header == 0) {
1922 /* got a response without a response header */
1924 c = NULL;
1925 header_end = 1;
1926 break;
1929 if (eol == EOL_UNSET) eol = EOL_N;
1931 if (*(c+1) == '\n') {
1932 header_end = 1;
1933 hlen = cp + 2;
1934 break;
1937 } else if (used > 1 && *c == '\r' && *(c+1) == '\n') {
1938 if (in_header == 0) {
1939 /* got a response without a response header */
1941 c = NULL;
1942 header_end = 1;
1943 break;
1946 if (eol == EOL_UNSET) eol = EOL_RN;
1948 if (used > 3 &&
1949 *(c+2) == '\r' &&
1950 *(c+3) == '\n') {
1951 header_end = 1;
1952 hlen = cp + 4;
1953 break;
1956 /* skip the \n */
1957 c++;
1958 cp++;
1959 used--;
1963 if (header_end) {
1964 if (c == NULL) {
1965 /* no header, but a body */
1967 if (con->request.http_version == HTTP_VERSION_1_1) {
1968 con->response.transfer_encoding = HTTP_TRANSFER_ENCODING_CHUNKED;
1971 http_chunk_append_buffer(srv, con, hctx->response_header);
1972 } else {
1973 size_t blen = buffer_string_length(hctx->response_header) - hlen;
1975 /* a small hack: terminate after at the second \r */
1976 buffer_string_set_length(hctx->response_header, hlen - 1);
1978 /* parse the response header */
1979 scgi_response_parse(srv, con, p, hctx->response_header, eol);
1981 if (hctx->host->xsendfile_allow) {
1982 data_string *ds;
1983 if (NULL != (ds = (data_string *) array_get_element(con->response.headers, "X-Sendfile"))) {
1984 http_response_xsendfile(srv, con, ds->value, hctx->host->xsendfile_docroot);
1985 return 1;
1989 /* enable chunked-transfer-encoding */
1990 if (con->request.http_version == HTTP_VERSION_1_1 &&
1991 !(con->parsed_response & HTTP_CONTENT_LENGTH)) {
1992 con->response.transfer_encoding = HTTP_TRANSFER_ENCODING_CHUNKED;
1995 if (blen > 0) {
1996 http_chunk_append_mem(srv, con, hctx->response_header->ptr + hlen, blen);
2000 con->file_started = 1;
2002 } else {
2003 http_chunk_append_buffer(srv, con, hctx->response);
2006 #if 0
2007 log_error_write(srv, __FILE__, __LINE__, "ddss", con->fd, hctx->fd, connection_get_state(con->state), b->ptr);
2008 #endif
2011 return 0;
2015 static int scgi_proclist_sort_up(server *srv, scgi_extension_host *host, scgi_proc *proc) {
2016 scgi_proc *p;
2018 UNUSED(srv);
2020 /* we have been the smallest of the current list
2021 * and we want to insert the node sorted as soon
2022 * possible
2024 * 1 0 0 0 1 1 1
2025 * | ^
2026 * | |
2027 * +------+
2031 /* nothing to sort, only one element */
2032 if (host->first == proc && proc->next == NULL) return 0;
2034 for (p = proc; p->next && p->next->load < proc->load; p = p->next);
2036 /* no need to move something
2038 * 1 2 2 2 3 3 3
2044 if (p == proc) return 0;
2046 if (host->first == proc) {
2047 /* we have been the first elememt */
2049 host->first = proc->next;
2050 host->first->prev = NULL;
2053 /* disconnect proc */
2055 if (proc->prev) proc->prev->next = proc->next;
2056 if (proc->next) proc->next->prev = proc->prev;
2058 /* proc should be right of p */
2060 proc->next = p->next;
2061 proc->prev = p;
2062 if (p->next) p->next->prev = proc;
2063 p->next = proc;
2064 #if 0
2065 for(p = host->first; p; p = p->next) {
2066 log_error_write(srv, __FILE__, __LINE__, "dd",
2067 p->pid, p->load);
2069 #else
2070 UNUSED(srv);
2071 #endif
2073 return 0;
2076 int scgi_proclist_sort_down(server *srv, scgi_extension_host *host, scgi_proc *proc) {
2077 scgi_proc *p;
2079 UNUSED(srv);
2081 /* we have been the smallest of the current list
2082 * and we want to insert the node sorted as soon
2083 * possible
2085 * 0 0 0 0 1 0 1
2086 * ^ |
2087 * | |
2088 * +----------+
2091 * the basic is idea is:
2092 * - the last active scgi process should be still
2093 * in ram and is not swapped out yet
2094 * - processes that are not reused will be killed
2095 * after some time by the trigger-handler
2096 * - remember it as:
2097 * everything > 0 is hot
2098 * all unused procs are colder the more right they are
2099 * ice-cold processes are propably unused since more
2100 * than 'unused-timeout', are swaped out and won't be
2101 * reused in the next seconds anyway.
2105 /* nothing to sort, only one element */
2106 if (host->first == proc && proc->next == NULL) return 0;
2108 for (p = host->first; p != proc && p->load < proc->load; p = p->next);
2111 /* no need to move something
2113 * 1 2 2 2 3 3 3
2119 if (p == proc) return 0;
2121 /* we have to move left. If we are already the first element
2122 * we are done */
2123 if (host->first == proc) return 0;
2125 /* release proc */
2126 if (proc->prev) proc->prev->next = proc->next;
2127 if (proc->next) proc->next->prev = proc->prev;
2129 /* proc should be left of p */
2130 proc->next = p;
2131 proc->prev = p->prev;
2132 if (p->prev) p->prev->next = proc;
2133 p->prev = proc;
2135 if (proc->prev == NULL) host->first = proc;
2136 #if 0
2137 for(p = host->first; p; p = p->next) {
2138 log_error_write(srv, __FILE__, __LINE__, "dd",
2139 p->pid, p->load);
2141 #else
2142 UNUSED(srv);
2143 #endif
2145 return 0;
2148 static int scgi_restart_dead_procs(server *srv, plugin_data *p, scgi_extension_host *host) {
2149 scgi_proc *proc;
2151 for (proc = host->first; proc; proc = proc->next) {
2152 if (p->conf.debug) {
2153 log_error_write(srv, __FILE__, __LINE__, "sbdbdddd",
2154 "proc:",
2155 host->host, proc->port,
2156 proc->socket,
2157 proc->state,
2158 proc->is_local,
2159 proc->load,
2160 proc->pid);
2163 if (0 == proc->is_local) {
2165 * external servers might get disabled
2167 * enable the server again, perhaps it is back again
2170 if ((proc->state == PROC_STATE_DISABLED) &&
2171 (srv->cur_ts - proc->disable_ts > host->disable_time)) {
2172 proc->state = PROC_STATE_RUNNING;
2173 host->active_procs++;
2175 log_error_write(srv, __FILE__, __LINE__, "sbdb",
2176 "fcgi-server re-enabled:",
2177 host->host, host->port,
2178 host->unixsocket);
2180 } else {
2181 /* the child should not terminate at all */
2182 int status;
2184 if (proc->state == PROC_STATE_DIED_WAIT_FOR_PID) {
2185 switch(waitpid(proc->pid, &status, WNOHANG)) {
2186 case 0:
2187 /* child is still alive */
2188 break;
2189 case -1:
2190 break;
2191 default:
2192 if (WIFEXITED(status)) {
2193 #if 0
2194 log_error_write(srv, __FILE__, __LINE__, "sdsd",
2195 "child exited, pid:", proc->pid,
2196 "status:", WEXITSTATUS(status));
2197 #endif
2198 } else if (WIFSIGNALED(status)) {
2199 log_error_write(srv, __FILE__, __LINE__, "sd",
2200 "child signaled:",
2201 WTERMSIG(status));
2202 } else {
2203 log_error_write(srv, __FILE__, __LINE__, "sd",
2204 "child died somehow:",
2205 status);
2208 proc->state = PROC_STATE_DIED;
2209 break;
2214 * local servers might died, but we restart them
2217 if (proc->state == PROC_STATE_DIED &&
2218 proc->load == 0) {
2219 /* restart the child */
2221 if (p->conf.debug) {
2222 log_error_write(srv, __FILE__, __LINE__, "ssdsbsdsd",
2223 "--- scgi spawning",
2224 "\n\tport:", host->port,
2225 "\n\tsocket", host->unixsocket,
2226 "\n\tcurrent:", 1, "/", host->min_procs);
2229 if (scgi_spawn_connection(srv, p, host, proc)) {
2230 log_error_write(srv, __FILE__, __LINE__, "s",
2231 "ERROR: spawning fcgi failed.");
2232 return HANDLER_ERROR;
2235 scgi_proclist_sort_down(srv, host, proc);
2240 return 0;
2244 static handler_t scgi_write_request(server *srv, handler_ctx *hctx) {
2245 plugin_data *p = hctx->plugin_data;
2246 scgi_extension_host *host= hctx->host;
2247 connection *con = hctx->remote_conn;
2249 int ret;
2251 /* sanity check */
2252 if (!host) {
2253 log_error_write(srv, __FILE__, __LINE__, "s", "fatal error: host = NULL");
2254 return HANDLER_ERROR;
2256 if (((buffer_string_is_empty(host->host) || !host->port) && buffer_string_is_empty(host->unixsocket))) {
2257 log_error_write(srv, __FILE__, __LINE__, "sxddd",
2258 "write-req: error",
2259 host,
2260 buffer_string_length(host->host),
2261 host->port,
2262 buffer_string_length(host->unixsocket));
2263 return HANDLER_ERROR;
2267 switch(hctx->state) {
2268 case FCGI_STATE_INIT:
2269 if (-1 == (hctx->fd = socket(host->family, SOCK_STREAM, 0))) {
2270 if (errno == EMFILE ||
2271 errno == EINTR) {
2272 log_error_write(srv, __FILE__, __LINE__, "sd",
2273 "wait for fd at connection:", con->fd);
2275 return HANDLER_WAIT_FOR_FD;
2278 log_error_write(srv, __FILE__, __LINE__, "ssdd",
2279 "socket failed:", strerror(errno), srv->cur_fds, srv->max_fds);
2280 return HANDLER_ERROR;
2282 hctx->fde_ndx = -1;
2284 srv->cur_fds++;
2286 fdevent_register(srv->ev, hctx->fd, scgi_handle_fdevent, hctx);
2288 if (-1 == fdevent_fcntl_set(srv->ev, hctx->fd)) {
2289 log_error_write(srv, __FILE__, __LINE__, "ss",
2290 "fcntl failed: ", strerror(errno));
2292 return HANDLER_ERROR;
2295 /* fall through */
2296 case FCGI_STATE_CONNECT:
2297 if (hctx->state == FCGI_STATE_INIT) {
2298 for (hctx->proc = hctx->host->first;
2299 hctx->proc && hctx->proc->state != PROC_STATE_RUNNING;
2300 hctx->proc = hctx->proc->next);
2302 /* all childs are dead */
2303 if (hctx->proc == NULL) {
2304 hctx->fde_ndx = -1;
2306 return HANDLER_ERROR;
2309 if (hctx->proc->is_local) {
2310 hctx->pid = hctx->proc->pid;
2313 switch (scgi_establish_connection(srv, hctx)) {
2314 case 1:
2315 scgi_set_state(srv, hctx, FCGI_STATE_CONNECT);
2317 /* connection is in progress, wait for an event and call getsockopt() below */
2319 fdevent_event_set(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_OUT);
2321 return HANDLER_WAIT_FOR_EVENT;
2322 case -1:
2323 /* if ECONNREFUSED choose another connection -> FIXME */
2324 hctx->fde_ndx = -1;
2326 return HANDLER_ERROR;
2327 default:
2328 /* everything is ok, go on */
2329 break;
2333 } else {
2334 int socket_error;
2335 socklen_t socket_error_len = sizeof(socket_error);
2337 /* try to finish the connect() */
2338 if (0 != getsockopt(hctx->fd, SOL_SOCKET, SO_ERROR, &socket_error, &socket_error_len)) {
2339 log_error_write(srv, __FILE__, __LINE__, "ss",
2340 "getsockopt failed:", strerror(errno));
2342 return HANDLER_ERROR;
2344 if (socket_error != 0) {
2345 if (!hctx->proc->is_local || p->conf.debug) {
2346 /* local procs get restarted */
2348 log_error_write(srv, __FILE__, __LINE__, "ss",
2349 "establishing connection failed:", strerror(socket_error),
2350 "port:", hctx->proc->port);
2353 return HANDLER_ERROR;
2357 /* ok, we have the connection */
2359 hctx->proc->load++;
2360 hctx->proc->last_used = srv->cur_ts;
2361 hctx->got_proc = 1;
2363 if (p->conf.debug) {
2364 log_error_write(srv, __FILE__, __LINE__, "sddbdd",
2365 "got proc:",
2366 hctx->fd,
2367 hctx->proc->pid,
2368 hctx->proc->socket,
2369 hctx->proc->port,
2370 hctx->proc->load);
2373 /* move the proc-list entry down the list */
2374 scgi_proclist_sort_up(srv, hctx->host, hctx->proc);
2376 scgi_set_state(srv, hctx, FCGI_STATE_PREPARE_WRITE);
2377 /* fall through */
2378 case FCGI_STATE_PREPARE_WRITE:
2379 scgi_create_env(srv, hctx);
2381 scgi_set_state(srv, hctx, FCGI_STATE_WRITE);
2383 /* fall through */
2384 case FCGI_STATE_WRITE:
2385 ret = srv->network_backend_write(srv, con, hctx->fd, hctx->wb, MAX_WRITE_LIMIT);
2387 chunkqueue_remove_finished_chunks(hctx->wb);
2389 if (ret < 0) {
2390 if (errno == ENOTCONN || ret == -2) {
2391 /* the connection got dropped after accept()
2393 * this is most of the time a PHP which dies
2394 * after PHP_FCGI_MAX_REQUESTS
2397 if (hctx->wb->bytes_out == 0 &&
2398 hctx->reconnects < 5) {
2399 usleep(10000); /* take away the load of the webserver
2400 * to let the php a chance to restart
2403 scgi_reconnect(srv, hctx);
2405 return HANDLER_COMEBACK;
2408 /* not reconnected ... why
2410 * far@#lighttpd report this for FreeBSD
2414 log_error_write(srv, __FILE__, __LINE__, "ssosd",
2415 "connection was dropped after accept(). reconnect() denied:",
2416 "write-offset:", hctx->wb->bytes_out,
2417 "reconnect attempts:", hctx->reconnects);
2419 return HANDLER_ERROR;
2420 } else {
2421 /* -1 == ret => error on our side */
2422 log_error_write(srv, __FILE__, __LINE__, "ssd",
2423 "write failed:", strerror(errno), errno);
2425 return HANDLER_ERROR;
2429 if (hctx->wb->bytes_out == hctx->wb->bytes_in) {
2430 fdevent_event_set(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN);
2431 scgi_set_state(srv, hctx, FCGI_STATE_READ);
2432 } else {
2433 fdevent_event_set(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN|FDEVENT_OUT);
2436 return HANDLER_WAIT_FOR_EVENT;
2437 case FCGI_STATE_READ:
2438 /* waiting for a response */
2439 return HANDLER_WAIT_FOR_EVENT;
2440 default:
2441 log_error_write(srv, __FILE__, __LINE__, "s", "(debug) unknown state");
2442 return HANDLER_ERROR;
2446 static handler_t scgi_send_request(server *srv, handler_ctx *hctx) {
2447 /* ok, create the request */
2448 handler_t rc = scgi_write_request(srv, hctx);
2449 if (HANDLER_ERROR != rc) {
2450 return rc;
2451 } else {
2452 scgi_proc *proc = hctx->proc;
2453 scgi_extension_host *host = hctx->host;
2454 plugin_data *p = hctx->plugin_data;
2455 connection *con = hctx->remote_conn;
2457 if (proc &&
2458 0 == proc->is_local &&
2459 proc->state != PROC_STATE_DISABLED) {
2460 /* only disable remote servers as we don't manage them*/
2462 log_error_write(srv, __FILE__, __LINE__, "sbdb", "fcgi-server disabled:",
2463 host->host,
2464 proc->port,
2465 proc->socket);
2467 /* disable this server */
2468 proc->disable_ts = srv->cur_ts;
2469 proc->state = PROC_STATE_DISABLED;
2470 host->active_procs--;
2473 if (hctx->state == FCGI_STATE_INIT ||
2474 hctx->state == FCGI_STATE_CONNECT) {
2475 /* connect() or getsockopt() failed,
2476 * restart the request-handling
2478 if (proc && proc->is_local) {
2480 if (p->conf.debug) {
2481 log_error_write(srv, __FILE__, __LINE__, "sbdb", "connect() to scgi failed, restarting the request-handling:",
2482 host->host,
2483 proc->port,
2484 proc->socket);
2488 * several hctx might reference the same proc
2490 * Only one of them should mark the proc as dead all the other
2491 * ones should just take a new one.
2493 * If a new proc was started with the old struct this might lead
2494 * the mark a perfect proc as dead otherwise
2497 if (proc->state == PROC_STATE_RUNNING &&
2498 hctx->pid == proc->pid) {
2499 proc->state = PROC_STATE_DIED_WAIT_FOR_PID;
2502 scgi_restart_dead_procs(srv, p, host);
2504 con->mode = DIRECT;/*(avoid changing con->state, con->http_status)*/
2505 scgi_connection_close(srv, hctx);
2506 con->mode = p->id;
2508 return HANDLER_COMEBACK;
2509 } else {
2510 scgi_connection_close(srv, hctx);
2511 con->http_status = 503;
2513 return HANDLER_FINISHED;
2518 SUBREQUEST_FUNC(mod_scgi_handle_subrequest) {
2519 plugin_data *p = p_d;
2521 handler_ctx *hctx = con->plugin_ctx[p->id];
2523 if (NULL == hctx) return HANDLER_GO_ON;
2525 /* not my job */
2526 if (con->mode != p->id) return HANDLER_GO_ON;
2528 if (con->state == CON_STATE_READ_POST) {
2529 handler_t r = connection_handle_read_post_state(srv, con);
2530 if (r != HANDLER_GO_ON) return r;
2533 return (hctx->state != FCGI_STATE_READ)
2534 ? scgi_send_request(srv, hctx)
2535 : HANDLER_WAIT_FOR_EVENT;
2539 static handler_t scgi_handle_fdevent(server *srv, void *ctx, int revents) {
2540 handler_ctx *hctx = ctx;
2541 connection *con = hctx->remote_conn;
2542 plugin_data *p = hctx->plugin_data;
2544 scgi_proc *proc = hctx->proc;
2545 scgi_extension_host *host= hctx->host;
2547 joblist_append(srv, con);
2549 if ((revents & FDEVENT_IN) &&
2550 hctx->state == FCGI_STATE_READ) {
2551 switch (scgi_demux_response(srv, hctx)) {
2552 case 0:
2553 break;
2554 case 1:
2555 /* we are done */
2556 scgi_connection_close(srv, hctx);
2558 return HANDLER_FINISHED;
2559 case -1:
2560 if (proc->pid && proc->state != PROC_STATE_DIED) {
2561 int status;
2563 /* only fetch the zombie if it is not already done */
2565 switch(waitpid(proc->pid, &status, WNOHANG)) {
2566 case 0:
2567 /* child is still alive */
2568 break;
2569 case -1:
2570 break;
2571 default:
2572 /* the child should not terminate at all */
2573 if (WIFEXITED(status)) {
2574 log_error_write(srv, __FILE__, __LINE__, "sdsd",
2575 "child exited, pid:", proc->pid,
2576 "status:", WEXITSTATUS(status));
2577 } else if (WIFSIGNALED(status)) {
2578 log_error_write(srv, __FILE__, __LINE__, "sd",
2579 "child signaled:",
2580 WTERMSIG(status));
2581 } else {
2582 log_error_write(srv, __FILE__, __LINE__, "sd",
2583 "child died somehow:",
2584 status);
2587 if (p->conf.debug) {
2588 log_error_write(srv, __FILE__, __LINE__, "ssdsbsdsd",
2589 "--- scgi spawning",
2590 "\n\tport:", host->port,
2591 "\n\tsocket", host->unixsocket,
2592 "\n\tcurrent:", 1, "/", host->min_procs);
2595 if (scgi_spawn_connection(srv, p, host, proc)) {
2596 /* child died */
2597 proc->state = PROC_STATE_DIED;
2598 } else {
2599 scgi_proclist_sort_down(srv, host, proc);
2602 break;
2606 if (con->file_started == 0) {
2607 /* nothing has been send out yet, try to use another child */
2609 if (hctx->wb->bytes_out == 0 &&
2610 hctx->reconnects < 5) {
2612 log_error_write(srv, __FILE__, __LINE__, "ssdsd",
2613 "response not sent, request not sent, reconnection.",
2614 "connection-fd:", con->fd,
2615 "fcgi-fd:", hctx->fd);
2617 scgi_reconnect(srv, hctx);
2619 return HANDLER_COMEBACK;
2622 log_error_write(srv, __FILE__, __LINE__, "sosdsd",
2623 "response not sent, request sent:", hctx->wb->bytes_out,
2624 "connection-fd:", con->fd,
2625 "fcgi-fd:", hctx->fd);
2627 scgi_connection_close(srv, hctx);
2628 } else {
2629 /* response might have been already started, kill the connection */
2630 log_error_write(srv, __FILE__, __LINE__, "ssdsd",
2631 "response already sent out, termination connection",
2632 "connection-fd:", con->fd,
2633 "fcgi-fd:", hctx->fd);
2635 con->keep_alive = 0;
2636 con->file_finished = 1;
2637 con->mode = DIRECT; /*(avoid sending final chunked block)*/
2638 scgi_connection_close(srv, hctx);
2641 return HANDLER_FINISHED;
2645 if (revents & FDEVENT_OUT) {
2646 if (hctx->state == FCGI_STATE_CONNECT ||
2647 hctx->state == FCGI_STATE_WRITE) {
2648 /* we are allowed to send something out
2650 * 1. in a unfinished connect() call
2651 * 2. in a unfinished write() call (long POST request)
2653 return scgi_send_request(srv, hctx); /*(might invalidate hctx)*/
2654 } else {
2655 log_error_write(srv, __FILE__, __LINE__, "sd",
2656 "got a FDEVENT_OUT and didn't know why:",
2657 hctx->state);
2661 /* perhaps this issue is already handled */
2662 if (revents & FDEVENT_HUP) {
2663 if (hctx->state == FCGI_STATE_CONNECT) {
2664 /* getoptsock will catch this one (right ?)
2666 * if we are in connect we might get a EINPROGRESS
2667 * in the first call and a FDEVENT_HUP in the
2668 * second round
2670 * FIXME: as it is a bit ugly.
2673 scgi_send_request(srv, hctx);
2674 } else if (hctx->state == FCGI_STATE_READ &&
2675 hctx->proc->port == 0) {
2676 /* FIXME:
2678 * ioctl says 8192 bytes to read from PHP and we receive directly a HUP for the socket
2679 * even if the FCGI_FIN packet is not received yet
2681 } else {
2682 log_error_write(srv, __FILE__, __LINE__, "sbSBSDSd",
2683 "error: unexpected close of scgi connection for",
2684 con->uri.path,
2685 "(no scgi process on host: ",
2686 host->host,
2687 ", port: ",
2688 host->port,
2689 " ?)",
2690 hctx->state);
2692 scgi_connection_close(srv, hctx);
2694 } else if (revents & FDEVENT_ERR) {
2695 log_error_write(srv, __FILE__, __LINE__, "s",
2696 "fcgi: got a FDEVENT_ERR. Don't know why.");
2698 if (con->file_started) {
2699 con->keep_alive = 0;
2700 con->file_finished = 1;
2701 con->mode = DIRECT; /*(avoid sending final chunked block)*/
2703 scgi_connection_close(srv, hctx);
2706 return HANDLER_FINISHED;
2708 #define PATCH(x) \
2709 p->conf.x = s->x;
2710 static int scgi_patch_connection(server *srv, connection *con, plugin_data *p) {
2711 size_t i, j;
2712 plugin_config *s = p->config_storage[0];
2714 PATCH(exts);
2715 PATCH(debug);
2717 /* skip the first, the global context */
2718 for (i = 1; i < srv->config_context->used; i++) {
2719 data_config *dc = (data_config *)srv->config_context->data[i];
2720 s = p->config_storage[i];
2722 /* condition didn't match */
2723 if (!config_check_cond(srv, con, dc)) continue;
2725 /* merge config */
2726 for (j = 0; j < dc->value->used; j++) {
2727 data_unset *du = dc->value->data[j];
2729 if (buffer_is_equal_string(du->key, CONST_STR_LEN("scgi.server"))) {
2730 PATCH(exts);
2731 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("scgi.debug"))) {
2732 PATCH(debug);
2737 return 0;
2739 #undef PATCH
2742 static handler_t scgi_check_extension(server *srv, connection *con, void *p_d, int uri_path_handler) {
2743 plugin_data *p = p_d;
2744 size_t s_len;
2745 int used = -1;
2746 size_t k;
2747 buffer *fn;
2748 scgi_extension *extension = NULL;
2749 scgi_extension_host *host = NULL;
2751 if (con->mode != DIRECT) return HANDLER_GO_ON;
2753 /* Possibly, we processed already this request */
2754 if (con->file_started == 1) return HANDLER_GO_ON;
2756 fn = uri_path_handler ? con->uri.path : con->physical.path;
2758 if (buffer_string_is_empty(fn)) return HANDLER_GO_ON;
2760 s_len = buffer_string_length(fn);
2762 scgi_patch_connection(srv, con, p);
2764 /* check if extension matches */
2765 for (k = 0; k < p->conf.exts->used; k++) {
2766 size_t ct_len;
2767 scgi_extension *ext = p->conf.exts->exts[k];
2769 if (buffer_is_empty(ext->key)) continue;
2771 ct_len = buffer_string_length(ext->key);
2773 if (s_len < ct_len) continue;
2775 /* check extension in the form "/scgi_pattern" */
2776 if (*(ext->key->ptr) == '/') {
2777 if (strncmp(fn->ptr, ext->key->ptr, ct_len) == 0) {
2778 extension = ext;
2779 break;
2781 } else if (0 == strncmp(fn->ptr + s_len - ct_len, ext->key->ptr, ct_len)) {
2782 /* check extension in the form ".fcg" */
2783 extension = ext;
2784 break;
2788 /* extension doesn't match */
2789 if (NULL == extension) {
2790 return HANDLER_GO_ON;
2793 /* get best server */
2794 for (k = 0; k < extension->used; k++) {
2795 scgi_extension_host *h = extension->hosts[k];
2797 /* we should have at least one proc that can do something */
2798 if (h->active_procs == 0) {
2799 continue;
2802 if (used == -1 || h->load < used) {
2803 used = h->load;
2805 host = h;
2809 if (!host) {
2810 /* sorry, we don't have a server alive for this ext */
2811 con->http_status = 500;
2812 con->mode = DIRECT;
2814 /* only send the 'no handler' once */
2815 if (!extension->note_is_sent) {
2816 extension->note_is_sent = 1;
2818 log_error_write(srv, __FILE__, __LINE__, "sbsbs",
2819 "all handlers for ", con->uri.path,
2820 "on", extension->key,
2821 "are down.");
2824 return HANDLER_FINISHED;
2827 /* a note about no handler is not sent yet */
2828 extension->note_is_sent = 0;
2831 * if check-local is disabled, use the uri.path handler
2835 /* init handler-context */
2836 if (uri_path_handler) {
2837 if (host->check_local == 0) {
2838 handler_ctx *hctx;
2839 char *pathinfo;
2841 hctx = handler_ctx_init();
2843 hctx->remote_conn = con;
2844 hctx->plugin_data = p;
2845 hctx->host = host;
2846 hctx->proc = NULL;
2848 hctx->conf.exts = p->conf.exts;
2849 hctx->conf.debug = p->conf.debug;
2851 con->plugin_ctx[p->id] = hctx;
2853 host->load++;
2855 con->mode = p->id;
2857 if (con->conf.log_request_handling) {
2858 log_error_write(srv, __FILE__, __LINE__, "s",
2859 "handling it in mod_scgi");
2862 /* the prefix is the SCRIPT_NAME,
2863 * everything from start to the next slash
2864 * this is important for check-local = "disable"
2866 * if prefix = /admin.fcgi
2868 * /admin.fcgi/foo/bar
2870 * SCRIPT_NAME = /admin.fcgi
2871 * PATH_INFO = /foo/bar
2873 * if prefix = /fcgi-bin/
2875 * /fcgi-bin/foo/bar
2877 * SCRIPT_NAME = /fcgi-bin/foo
2878 * PATH_INFO = /bar
2882 /* the rewrite is only done for /prefix/? matches */
2883 if (host->fix_root_path_name && extension->key->ptr[0] == '/' && extension->key->ptr[1] == '\0') {
2884 buffer_copy_string(con->request.pathinfo, con->uri.path->ptr);
2885 buffer_string_set_length(con->uri.path, 0);
2886 } else if (extension->key->ptr[0] == '/' &&
2887 buffer_string_length(con->uri.path) > buffer_string_length(extension->key) &&
2888 NULL != (pathinfo = strchr(con->uri.path->ptr + buffer_string_length(extension->key), '/'))) {
2889 /* rewrite uri.path and pathinfo */
2891 buffer_copy_string(con->request.pathinfo, pathinfo);
2892 buffer_string_set_length(con->uri.path, buffer_string_length(con->uri.path) - buffer_string_length(con->request.pathinfo));
2895 } else {
2896 handler_ctx *hctx;
2897 hctx = handler_ctx_init();
2899 hctx->remote_conn = con;
2900 hctx->plugin_data = p;
2901 hctx->host = host;
2902 hctx->proc = NULL;
2904 hctx->conf.exts = p->conf.exts;
2905 hctx->conf.debug = p->conf.debug;
2907 con->plugin_ctx[p->id] = hctx;
2909 host->load++;
2911 con->mode = p->id;
2913 if (con->conf.log_request_handling) {
2914 log_error_write(srv, __FILE__, __LINE__, "s", "handling it in mod_scgi");
2918 return HANDLER_GO_ON;
2921 /* uri-path handler */
2922 static handler_t scgi_check_extension_1(server *srv, connection *con, void *p_d) {
2923 return scgi_check_extension(srv, con, p_d, 1);
2926 /* start request handler */
2927 static handler_t scgi_check_extension_2(server *srv, connection *con, void *p_d) {
2928 return scgi_check_extension(srv, con, p_d, 0);
2932 TRIGGER_FUNC(mod_scgi_handle_trigger) {
2933 plugin_data *p = p_d;
2934 size_t i, j, n;
2937 /* perhaps we should kill a connect attempt after 10-15 seconds
2939 * currently we wait for the TCP timeout which is on Linux 180 seconds
2945 /* check all childs if they are still up */
2947 for (i = 0; i < srv->config_context->used; i++) {
2948 plugin_config *conf;
2949 scgi_exts *exts;
2951 conf = p->config_storage[i];
2953 exts = conf->exts;
2955 for (j = 0; j < exts->used; j++) {
2956 scgi_extension *ex;
2958 ex = exts->exts[j];
2960 for (n = 0; n < ex->used; n++) {
2962 scgi_proc *proc;
2963 unsigned long sum_load = 0;
2964 scgi_extension_host *host;
2966 host = ex->hosts[n];
2968 scgi_restart_dead_procs(srv, p, host);
2970 for (proc = host->first; proc; proc = proc->next) {
2971 sum_load += proc->load;
2974 if (host->num_procs &&
2975 host->num_procs < host->max_procs &&
2976 (sum_load / host->num_procs) > host->max_load_per_proc) {
2977 /* overload, spawn new child */
2978 scgi_proc *fp = NULL;
2980 if (p->conf.debug) {
2981 log_error_write(srv, __FILE__, __LINE__, "s",
2982 "overload detected, spawning a new child");
2985 for (fp = host->unused_procs; fp && fp->pid != 0; fp = fp->next);
2987 if (fp) {
2988 if (fp == host->unused_procs) host->unused_procs = fp->next;
2990 if (fp->next) fp->next->prev = NULL;
2992 host->max_id++;
2993 } else {
2994 fp = scgi_process_init();
2995 fp->id = host->max_id++;
2998 host->num_procs++;
3000 if (buffer_string_is_empty(host->unixsocket)) {
3001 fp->port = host->port + fp->id;
3002 } else {
3003 buffer_copy_buffer(fp->socket, host->unixsocket);
3004 buffer_append_string_len(fp->socket, CONST_STR_LEN("-"));
3005 buffer_append_int(fp->socket, fp->id);
3008 if (scgi_spawn_connection(srv, p, host, fp)) {
3009 log_error_write(srv, __FILE__, __LINE__, "s",
3010 "ERROR: spawning fcgi failed.");
3011 scgi_process_free(fp);
3012 return HANDLER_ERROR;
3015 fp->prev = NULL;
3016 fp->next = host->first;
3017 if (host->first) {
3018 host->first->prev = fp;
3020 host->first = fp;
3023 for (proc = host->first; proc; proc = proc->next) {
3024 if (proc->load != 0) break;
3025 if (host->num_procs <= host->min_procs) break;
3026 if (proc->pid == 0) continue;
3028 if (srv->cur_ts - proc->last_used > host->idle_timeout) {
3029 /* a proc is idling for a long time now,
3030 * terminated it */
3032 if (p->conf.debug) {
3033 log_error_write(srv, __FILE__, __LINE__, "ssbsd",
3034 "idle-timeout reached, terminating child:",
3035 "socket:", proc->socket,
3036 "pid", proc->pid);
3040 if (proc->next) proc->next->prev = proc->prev;
3041 if (proc->prev) proc->prev->next = proc->next;
3043 if (proc->prev == NULL) host->first = proc->next;
3045 proc->prev = NULL;
3046 proc->next = host->unused_procs;
3048 if (host->unused_procs) host->unused_procs->prev = proc;
3049 host->unused_procs = proc;
3051 kill(proc->pid, SIGTERM);
3053 proc->state = PROC_STATE_KILLED;
3055 log_error_write(srv, __FILE__, __LINE__, "ssbsd",
3056 "killed:",
3057 "socket:", proc->socket,
3058 "pid", proc->pid);
3060 host->num_procs--;
3062 /* proc is now in unused, let the next second handle the next process */
3063 break;
3067 for (proc = host->unused_procs; proc; proc = proc->next) {
3068 int status;
3070 if (proc->pid == 0) continue;
3072 switch (waitpid(proc->pid, &status, WNOHANG)) {
3073 case 0:
3074 /* child still running after timeout, good */
3075 break;
3076 case -1:
3077 if (errno != EINTR) {
3078 /* no PID found ? should never happen */
3079 log_error_write(srv, __FILE__, __LINE__, "sddss",
3080 "pid ", proc->pid, proc->state,
3081 "not found:", strerror(errno));
3083 #if 0
3084 if (errno == ECHILD) {
3085 /* someone else has cleaned up for us */
3086 proc->pid = 0;
3087 proc->state = PROC_STATE_UNSET;
3089 #endif
3091 break;
3092 default:
3093 /* the child should not terminate at all */
3094 if (WIFEXITED(status)) {
3095 if (proc->state != PROC_STATE_KILLED) {
3096 log_error_write(srv, __FILE__, __LINE__, "sdb",
3097 "child exited:",
3098 WEXITSTATUS(status), proc->socket);
3100 } else if (WIFSIGNALED(status)) {
3101 if (WTERMSIG(status) != SIGTERM) {
3102 log_error_write(srv, __FILE__, __LINE__, "sd",
3103 "child signaled:",
3104 WTERMSIG(status));
3106 } else {
3107 log_error_write(srv, __FILE__, __LINE__, "sd",
3108 "child died somehow:",
3109 status);
3111 proc->pid = 0;
3112 proc->state = PROC_STATE_UNSET;
3113 host->max_id--;
3120 return HANDLER_GO_ON;
3124 int mod_scgi_plugin_init(plugin *p);
3125 int mod_scgi_plugin_init(plugin *p) {
3126 p->version = LIGHTTPD_VERSION_ID;
3127 p->name = buffer_init_string("scgi");
3129 p->init = mod_scgi_init;
3130 p->cleanup = mod_scgi_free;
3131 p->set_defaults = mod_scgi_set_defaults;
3132 p->connection_reset = scgi_connection_reset;
3133 p->handle_connection_close = scgi_connection_reset;
3134 p->handle_uri_clean = scgi_check_extension_1;
3135 p->handle_subrequest_start = scgi_check_extension_2;
3136 p->handle_subrequest = mod_scgi_handle_subrequest;
3137 p->handle_trigger = mod_scgi_handle_trigger;
3139 p->data = NULL;
3141 return 0;