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