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