Annotation of embedaddon/lighttpd/src/mod_scgi.c, revision 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>