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