1 #include "first.h" 2 3 #include "gw_backend.h" 4 5 #include <sys/types.h> 6 #include <sys/stat.h> 7 #include "sys-socket.h" 8 #ifdef HAVE_SYS_UIO_H 9 #include <sys/uio.h> 10 #endif 11 #ifdef HAVE_SYS_WAIT_H 12 #include <sys/wait.h> 13 #endif 14 15 #include <errno.h> 16 #include <fcntl.h> 17 #include <limits.h> 18 #include <stdlib.h> 19 #include <signal.h> 20 #include <string.h> 21 #include <unistd.h> 22 23 #include "base.h" 24 #include "array.h" 25 #include "buffer.h" 26 #include "crc32.h" 27 #include "fdevent.h" 28 #include "log.h" 29 #include "sock_addr.h" 30 31 32 33 34 #include "status_counter.h" 35 36 static int * gw_status_get_counter(server *srv, gw_host *host, gw_proc *proc, const char *tag, size_t len) { 37 buffer *b = srv->tmp_buf; 38 buffer_copy_string_len(b, CONST_STR_LEN("gw.backend.")); 39 buffer_append_string_buffer(b, host->id); 40 if (proc) { 41 buffer_append_string_len(b, CONST_STR_LEN(".")); 42 buffer_append_int(b, proc->id); 43 } 44 buffer_append_string_len(b, tag, len); 45 return status_counter_get_counter(srv, CONST_BUF_LEN(b)); 46 } 47 48 static void gw_proc_tag_inc(server *srv, gw_host *host, gw_proc *proc, const char *tag, size_t len) { 49 ++(*gw_status_get_counter(srv, host, proc, tag, len)); 50 } 51 52 static void gw_proc_load_inc(server *srv, gw_host *host, gw_proc *proc) { 53 *gw_status_get_counter(srv,host,proc,CONST_STR_LEN(".load")) = ++proc->load; 54 55 status_counter_inc(srv, CONST_STR_LEN("gw.active-requests")); 56 } 57 58 static void gw_proc_load_dec(server *srv, gw_host *host, gw_proc *proc) { 59 *gw_status_get_counter(srv,host,proc,CONST_STR_LEN(".load")) = --proc->load; 60 61 status_counter_dec(srv, CONST_STR_LEN("gw.active-requests")); 62 } 63 64 static void gw_host_assign(server *srv, gw_host *host) { 65 *gw_status_get_counter(srv,host,NULL,CONST_STR_LEN(".load")) = ++host->load; 66 } 67 68 static void gw_host_reset(server *srv, gw_host *host) { 69 *gw_status_get_counter(srv,host,NULL,CONST_STR_LEN(".load")) = --host->load; 70 } 71 72 static int gw_status_init(server *srv, gw_host *host, gw_proc *proc) { 73 *gw_status_get_counter(srv, host, proc, CONST_STR_LEN(".disabled")) = 0; 74 *gw_status_get_counter(srv, host, proc, CONST_STR_LEN(".died")) = 0; 75 *gw_status_get_counter(srv, host, proc, CONST_STR_LEN(".overloaded")) = 0; 76 *gw_status_get_counter(srv, host, proc, CONST_STR_LEN(".connected")) = 0; 77 *gw_status_get_counter(srv, host, proc, CONST_STR_LEN(".load")) = 0; 78 79 *gw_status_get_counter(srv, host, NULL, CONST_STR_LEN(".load")) = 0; 80 81 return 0; 82 } 83 84 85 86 87 static void gw_proc_set_state(gw_host *host, gw_proc *proc, int state) { 88 if ((int)proc->state == state) return; 89 if (proc->state == PROC_STATE_RUNNING) { 90 --host->active_procs; 91 } else if (state == PROC_STATE_RUNNING) { 92 ++host->active_procs; 93 } 94 proc->state = state; 95 } 96 97 98 static gw_proc *gw_proc_init(void) { 99 gw_proc *f = calloc(1, sizeof(*f)); 100 force_assert(f); 101 102 f->unixsocket = buffer_init(); 103 f->connection_name = buffer_init(); 104 105 f->prev = NULL; 106 f->next = NULL; 107 f->state = PROC_STATE_DIED; 108 109 return f; 110 } 111 112 static void gw_proc_free(gw_proc *f) { 113 if (!f) return; 114 115 gw_proc_free(f->next); 116 117 buffer_free(f->unixsocket); 118 buffer_free(f->connection_name); 119 free(f->saddr); 120 121 free(f); 122 } 123 124 static gw_host *gw_host_init(void) { 125 gw_host *f = calloc(1, sizeof(*f)); 126 force_assert(f); 127 128 f->id = buffer_init(); 129 f->host = buffer_init(); 130 f->unixsocket = buffer_init(); 131 f->docroot = buffer_init(); 132 f->bin_path = buffer_init(); 133 f->bin_env = array_init(); 134 f->bin_env_copy = array_init(); 135 f->strip_request_uri = buffer_init(); 136 f->xsendfile_docroot = array_init(); 137 138 return f; 139 } 140 141 static void gw_host_free(gw_host *h) { 142 if (!h) return; 143 if (h->refcount) { 144 --h->refcount; 145 return; 146 } 147 148 buffer_free(h->id); 149 buffer_free(h->host); 150 buffer_free(h->unixsocket); 151 buffer_free(h->docroot); 152 buffer_free(h->bin_path); 153 buffer_free(h->strip_request_uri); 154 array_free(h->bin_env); 155 array_free(h->bin_env_copy); 156 array_free(h->xsendfile_docroot); 157 158 gw_proc_free(h->first); 159 gw_proc_free(h->unused_procs); 160 161 for (size_t i = 0; i < h->args.used; ++i) free(h->args.ptr[i]); 162 free(h->args.ptr); 163 free(h); 164 } 165 166 static gw_exts *gw_extensions_init(void) { 167 gw_exts *f = calloc(1, sizeof(*f)); 168 force_assert(f); 169 return f; 170 } 171 172 static void gw_extensions_free(gw_exts *f) { 173 if (!f) return; 174 for (size_t i = 0; i < f->used; ++i) { 175 gw_extension *fe = f->exts[i]; 176 for (size_t j = 0; j < fe->used; ++j) { 177 gw_host_free(fe->hosts[j]); 178 } 179 buffer_free(fe->key); 180 free(fe->hosts); 181 free(fe); 182 } 183 free(f->exts); 184 free(f); 185 } 186 187 static int gw_extension_insert(gw_exts *ext, buffer *key, gw_host *fh) { 188 gw_extension *fe = NULL; 189 for (size_t i = 0; i < ext->used; ++i) { 190 if (buffer_is_equal(key, ext->exts[i]->key)) { 191 fe = ext->exts[i]; 192 break; 193 } 194 } 195 196 if (NULL == fe) { 197 fe = calloc(1, sizeof(*fe)); 198 force_assert(fe); 199 fe->key = buffer_init(); 200 fe->last_used_ndx = -1; 201 buffer_copy_buffer(fe->key, key); 202 203 if (ext->used == ext->size) { 204 ext->size += 8; 205 ext->exts = realloc(ext->exts, ext->size * sizeof(*(ext->exts))); 206 force_assert(ext->exts); 207 } 208 ext->exts[ext->used++] = fe; 209 fe->size = 4; 210 fe->hosts = malloc(fe->size * sizeof(*(fe->hosts))); 211 force_assert(fe->hosts); 212 } else if (fe->size == fe->used) { 213 fe->size += 4; 214 fe->hosts = realloc(fe->hosts, fe->size * sizeof(*(fe->hosts))); 215 force_assert(fe->hosts); 216 } 217 218 fe->hosts[fe->used++] = fh; 219 return 0; 220 } 221 222 static void gw_proc_connect_success(server *srv, gw_host *host, gw_proc *proc, int debug) { 223 gw_proc_tag_inc(srv, host, proc, CONST_STR_LEN(".connected")); 224 proc->last_used = srv->cur_ts; 225 226 if (debug) { 227 log_error_write(srv, __FILE__, __LINE__, "ssdsbsd", 228 "got proc:", 229 "pid:", proc->pid, 230 "socket:", proc->connection_name, 231 "load:", proc->load); 232 } 233 } 234 235 static void gw_proc_connect_error(server *srv, gw_host *host, gw_proc *proc, pid_t pid, int errnum, int debug) { 236 log_error_write(srv, __FILE__, __LINE__, "sssb", 237 "establishing connection failed:", strerror(errnum), 238 "socket:", proc->connection_name); 239 240 if (!proc->is_local) { 241 proc->disabled_until = srv->cur_ts + host->disable_time; 242 gw_proc_set_state(host, proc, PROC_STATE_OVERLOADED); 243 } 244 else if (proc->pid == pid && proc->state == PROC_STATE_RUNNING) { 245 /* several requests from lighttpd might reference the same proc 246 * 247 * Only one of them should mark the proc 248 * and all other ones should just take a new one. 249 * 250 * If a new proc was started with the old struct, this might 251 * otherwise lead to marking a perfectly good proc as dead 252 */ 253 log_error_write(srv, __FILE__, __LINE__, "sdssd", 254 "backend error; we'll disable for", host->disable_time, 255 "secs and send the request to another backend instead:", 256 "load:", host->load); 257 if (EAGAIN == errnum) { 258 /* - EAGAIN: cool down the backend; it is overloaded */ 259 #ifdef __linux__ 260 log_error_write(srv, __FILE__, __LINE__, "s", 261 "If this happened on Linux: You have run out of local ports. " 262 "Check the manual, section Performance how to handle this."); 263 #endif 264 if (debug) { 265 log_error_write(srv, __FILE__, __LINE__, "sbsd", 266 "This means that you have more incoming requests than your " 267 "FastCGI backend can handle in parallel. It might help to " 268 "spawn more FastCGI backends or PHP children; if not, " 269 "decrease server.max-connections. The load for this FastCGI " 270 "backend", proc->connection_name, "is", proc->load); 271 } 272 proc->disabled_until = srv->cur_ts + host->disable_time; 273 gw_proc_set_state(host, proc, PROC_STATE_OVERLOADED); 274 } 275 else { 276 /* we got a hard error from the backend like 277 * - ECONNREFUSED for tcp-ip sockets 278 * - ENOENT for unix-domain-sockets 279 */ 280 #if 0 281 gw_proc_set_state(host, proc, PROC_STATE_DIED_WAIT_FOR_PID); 282 #else /* treat as overloaded (future: unless we send kill() signal)*/ 283 proc->disabled_until = srv->cur_ts + host->disable_time; 284 gw_proc_set_state(host, proc, PROC_STATE_OVERLOADED); 285 #endif 286 } 287 } 288 289 if (EAGAIN == errnum) { 290 gw_proc_tag_inc(srv, host, proc, CONST_STR_LEN(".overloaded")); 291 } 292 else { 293 gw_proc_tag_inc(srv, host, proc, CONST_STR_LEN(".died")); 294 } 295 } 296 297 static void gw_proc_release(server *srv, gw_host *host, gw_proc *proc, int debug) { 298 gw_proc_load_dec(srv, host, proc); 299 300 if (debug) { 301 log_error_write(srv, __FILE__, __LINE__, "ssdsbsd", 302 "released proc:", 303 "pid:", proc->pid, 304 "socket:", proc->connection_name, 305 "load:", proc->load); 306 } 307 } 308 309 static void gw_proc_check_enable(server *srv, gw_host *host, gw_proc *proc) { 310 if (srv->cur_ts <= proc->disabled_until) return; 311 if (proc->state != PROC_STATE_OVERLOADED) return; 312 313 gw_proc_set_state(host, proc, PROC_STATE_RUNNING); 314 315 log_error_write(srv, __FILE__, __LINE__, "sbbdb", 316 "gw-server re-enabled:", proc->connection_name, 317 host->host, host->port, host->unixsocket); 318 } 319 320 static void gw_proc_waitpid_log(server *srv, gw_host *host, gw_proc *proc, int status) { 321 UNUSED(host); 322 if (WIFEXITED(status)) { 323 if (proc->state != PROC_STATE_KILLED) { 324 log_error_write(srv, __FILE__, __LINE__, "sdb", 325 "child exited:", 326 WEXITSTATUS(status), proc->connection_name); 327 } 328 } else if (WIFSIGNALED(status)) { 329 if (WTERMSIG(status) != SIGTERM && WTERMSIG(status) != SIGINT 330 && WTERMSIG(status) != host->kill_signal) { 331 log_error_write(srv, __FILE__, __LINE__, "sd", 332 "child signalled:", WTERMSIG(status)); 333 } 334 } else { 335 log_error_write(srv, __FILE__, __LINE__, "sd", 336 "child died somehow:", status); 337 } 338 } 339 340 static int gw_proc_waitpid(server *srv, gw_host *host, gw_proc *proc) { 341 int rc, status; 342 343 if (!proc->is_local) return 0; 344 if (proc->pid <= 0) return 0; 345 346 do { 347 rc = waitpid(proc->pid, &status, WNOHANG); 348 } while (-1 == rc && errno == EINTR); 349 if (0 == rc) return 0; /* child still running */ 350 351 /* child terminated */ 352 if (-1 == rc) { 353 /* EINVAL or ECHILD no child processes */ 354 /* should not happen; someone else has cleaned up for us */ 355 log_error_write(srv, __FILE__, __LINE__, "sddss", 356 "pid ", proc->pid, proc->state, 357 "not found:", strerror(errno)); 358 } 359 else { 360 gw_proc_waitpid_log(srv, host, proc, status); 361 } 362 363 proc->pid = 0; 364 if (proc->state != PROC_STATE_KILLED) 365 proc->disabled_until = srv->cur_ts; 366 gw_proc_set_state(host, proc, PROC_STATE_DIED); 367 return 1; 368 } 369 370 static int gw_proc_sockaddr_init(server *srv, gw_host *host, gw_proc *proc) { 371 sock_addr addr; 372 socklen_t addrlen; 373 374 if (!buffer_string_is_empty(proc->unixsocket)) { 375 if (1 != sock_addr_from_str_hints(srv, &addr, &addrlen, 376 proc->unixsocket->ptr, AF_UNIX, 0)) { 377 errno = EINVAL; 378 return -1; 379 } 380 buffer_copy_string_len(proc->connection_name, CONST_STR_LEN("unix:")); 381 buffer_append_string_buffer(proc->connection_name, proc->unixsocket); 382 } 383 else { 384 /*(note: name resolution here is *blocking* if IP string not supplied)*/ 385 if (1 != sock_addr_from_str_hints(srv, &addr, &addrlen, 386 host->host->ptr, 0, proc->port)) { 387 errno = EINVAL; 388 return -1; 389 } 390 else { 391 /* overwrite host->host buffer with IP addr string so that 392 * any further use of gw_host does not block on DNS lookup */ 393 sock_addr_inet_ntop_copy_buffer(host->host, &addr); 394 host->family = sock_addr_get_family(&addr); 395 } 396 buffer_copy_string_len(proc->connection_name, CONST_STR_LEN("tcp:")); 397 buffer_append_string_buffer(proc->connection_name, host->host); 398 buffer_append_string_len(proc->connection_name, CONST_STR_LEN(":")); 399 buffer_append_int(proc->connection_name, proc->port); 400 } 401 402 if (NULL != proc->saddr && proc->saddrlen < addrlen) { 403 free(proc->saddr); 404 proc->saddr = NULL; 405 } 406 if (NULL == proc->saddr) { 407 proc->saddr = (struct sockaddr *)malloc(addrlen); 408 force_assert(proc->saddr); 409 } 410 proc->saddrlen = addrlen; 411 memcpy(proc->saddr, &addr, addrlen); 412 return 0; 413 } 414 415 static int env_add(char_array *env, const char *key, size_t key_len, const char *val, size_t val_len) { 416 char *dst; 417 418 if (!key || !val) return -1; 419 420 dst = malloc(key_len + val_len + 3); 421 force_assert(dst); 422 memcpy(dst, key, key_len); 423 dst[key_len] = '='; 424 memcpy(dst + key_len + 1, val, val_len + 1); /* add the \0 from the value */ 425 426 for (size_t i = 0; i < env->used; ++i) { 427 if (0 == strncmp(dst, env->ptr[i], key_len + 1)) { 428 free(env->ptr[i]); 429 env->ptr[i] = dst; 430 return 0; 431 } 432 } 433 434 if (env->size <= env->used + 1) { 435 env->size += 16; 436 env->ptr = realloc(env->ptr, env->size * sizeof(*env->ptr)); 437 force_assert(env->ptr); 438 } 439 440 env->ptr[env->used++] = dst; 441 442 return 0; 443 } 444 445 static int gw_spawn_connection(server *srv, gw_host *host, gw_proc *proc, int debug) { 446 int gw_fd; 447 int status; 448 struct timeval tv = { 0, 10 * 1000 }; 449 450 if (debug) { 451 log_error_write(srv, __FILE__, __LINE__, "sdb", 452 "new proc, socket:", proc->port, proc->unixsocket); 453 } 454 455 gw_fd = fdevent_socket_cloexec(proc->saddr->sa_family, SOCK_STREAM, 0); 456 if (-1 == gw_fd) { 457 log_error_write(srv, __FILE__, __LINE__, "ss", 458 "failed:", strerror(errno)); 459 return -1; 460 } 461 462 do { 463 status = connect(gw_fd, proc->saddr, proc->saddrlen); 464 } while (-1 == status && errno == EINTR); 465 466 if (-1 == status && errno != ENOENT 467 && !buffer_string_is_empty(proc->unixsocket)) { 468 log_error_write(srv, __FILE__, __LINE__, "sbss", 469 "unlink", proc->unixsocket, 470 "after connect failed:", strerror(errno)); 471 unlink(proc->unixsocket->ptr); 472 } 473 474 close(gw_fd); 475 476 if (-1 == status) { 477 /* server is not up, spawn it */ 478 char_array env; 479 size_t i; 480 int dfd = -1; 481 482 /* reopen socket */ 483 gw_fd = fdevent_socket_cloexec(proc->saddr->sa_family, SOCK_STREAM, 0); 484 if (-1 == gw_fd) { 485 log_error_write(srv, __FILE__, __LINE__, "ss", 486 "socket failed:", strerror(errno)); 487 return -1; 488 } 489 490 if (fdevent_set_so_reuseaddr(gw_fd, 1) < 0) { 491 log_error_write(srv, __FILE__, __LINE__, "ss", 492 "socketsockopt failed:", strerror(errno)); 493 close(gw_fd); 494 return -1; 495 } 496 497 /* create socket */ 498 if (-1 == bind(gw_fd, proc->saddr, proc->saddrlen)) { 499 log_error_write(srv, __FILE__, __LINE__, "sbs", 500 "bind failed for:", 501 proc->connection_name, 502 strerror(errno)); 503 close(gw_fd); 504 return -1; 505 } 506 507 if (-1 == listen(gw_fd, host->listen_backlog)) { 508 log_error_write(srv, __FILE__, __LINE__, "ss", 509 "listen failed:", strerror(errno)); 510 close(gw_fd); 511 return -1; 512 } 513 514 { 515 /* create environment */ 516 env.ptr = NULL; 517 env.size = 0; 518 env.used = 0; 519 520 /* build clean environment */ 521 if (host->bin_env_copy->used) { 522 for (i = 0; i < host->bin_env_copy->used; ++i) { 523 data_string *ds=(data_string *)host->bin_env_copy->data[i]; 524 char *ge; 525 526 if (NULL != (ge = getenv(ds->value->ptr))) { 527 env_add(&env, CONST_BUF_LEN(ds->value), ge, strlen(ge)); 528 } 529 } 530 } else { 531 char ** const e = fdevent_environ(); 532 for (i = 0; e[i]; ++i) { 533 char *eq; 534 535 if (NULL != (eq = strchr(e[i], '='))) { 536 env_add(&env, e[i], eq - e[i], eq+1, strlen(eq+1)); 537 } 538 } 539 } 540 541 /* create environment */ 542 for (i = 0; i < host->bin_env->used; ++i) { 543 data_string *ds = (data_string *)host->bin_env->data[i]; 544 545 env_add(&env, CONST_BUF_LEN(ds->key), CONST_BUF_LEN(ds->value)); 546 } 547 548 for (i = 0; i < env.used; ++i) { 549 /* search for PHP_FCGI_CHILDREN */ 550 if (0 == strncmp(env.ptr[i], "PHP_FCGI_CHILDREN=", 551 sizeof("PHP_FCGI_CHILDREN=")-1)) { 552 break; 553 } 554 } 555 556 /* not found, add a default */ 557 if (i == env.used) { 558 env_add(&env, CONST_STR_LEN("PHP_FCGI_CHILDREN"), 559 CONST_STR_LEN("1")); 560 } 561 562 env.ptr[env.used] = NULL; 563 } 564 565 dfd = fdevent_open_dirname(host->args.ptr[0], 1); /* permit symlinks */ 566 if (-1 == dfd) { 567 log_error_write(srv, __FILE__, __LINE__, "sss", 568 "open dirname failed:", strerror(errno), 569 host->args.ptr[0]); 570 } 571 572 /*(FCGI_LISTENSOCK_FILENO == STDIN_FILENO == 0)*/ 573 proc->pid = (dfd >= 0) 574 ? fdevent_fork_execve(host->args.ptr[0], host->args.ptr, 575 env.ptr, gw_fd, -1, -1, dfd) 576 : -1; 577 578 for (i = 0; i < env.used; ++i) free(env.ptr[i]); 579 free(env.ptr); 580 if (-1 != dfd) close(dfd); 581 close(gw_fd); 582 583 if (-1 == proc->pid) { 584 log_error_write(srv, __FILE__, __LINE__, "sb", 585 "gw-backend failed to start:", host->bin_path); 586 proc->pid = 0; 587 proc->disabled_until = srv->cur_ts; 588 return -1; 589 } 590 591 /* register process */ 592 proc->last_used = srv->cur_ts; 593 proc->is_local = 1; 594 595 /* wait */ 596 select(0, NULL, NULL, NULL, &tv); 597 598 if (0 != gw_proc_waitpid(srv, host, proc)) { 599 log_error_write(srv, __FILE__, __LINE__, "sb", 600 "gw-backend failed to start:", host->bin_path); 601 log_error_write(srv, __FILE__, __LINE__, "s", 602 "If you're trying to run your app as a FastCGI backend, make " 603 "sure you're using the FastCGI-enabled version. If this is PHP " 604 "on Gentoo, add 'fastcgi' to the USE flags. If this is PHP, try " 605 "removing the bytecode caches for now and try again."); 606 return -1; 607 } 608 } else { 609 proc->is_local = 0; 610 proc->pid = 0; 611 612 if (debug) { 613 log_error_write(srv, __FILE__, __LINE__, "sb", 614 "(debug) socket is already used; won't spawn:", 615 proc->connection_name); 616 } 617 } 618 619 gw_proc_set_state(host, proc, PROC_STATE_RUNNING); 620 return 0; 621 } 622 623 static void gw_proc_spawn(server *srv, gw_host *host, int debug) { 624 gw_proc *proc; 625 for (proc = host->unused_procs; proc; proc = proc->next) { 626 /* (proc->pid <= 0 indicates PROC_STATE_DIED, not PROC_STATE_KILLED) */ 627 if (proc->pid > 0) continue; 628 /* (do not attempt to spawn another proc if a proc just exited) */ 629 if (proc->disabled_until >= srv->cur_ts) return; 630 break; 631 } 632 if (proc) { 633 if (proc == host->unused_procs) 634 host->unused_procs = proc->next; 635 else 636 proc->prev->next = proc->next; 637 638 if (proc->next) { 639 proc->next->prev = proc->prev; 640 proc->next = NULL; 641 } 642 643 proc->prev = NULL; 644 } else { 645 proc = gw_proc_init(); 646 proc->id = host->max_id++; 647 } 648 649 ++host->num_procs; 650 651 if (buffer_string_is_empty(host->unixsocket)) { 652 proc->port = host->port + proc->id; 653 } else { 654 buffer_copy_buffer(proc->unixsocket, host->unixsocket); 655 buffer_append_string_len(proc->unixsocket, CONST_STR_LEN("-")); 656 buffer_append_int(proc->unixsocket, proc->id); 657 } 658 659 if (0 != gw_proc_sockaddr_init(srv, host, proc)) { 660 /*(should not happen if host->host validated at startup, 661 * and translated from name to IP address at startup)*/ 662 log_error_write(srv, __FILE__, __LINE__, "s", 663 "ERROR: spawning backend failed."); 664 --host->num_procs; 665 if (proc->id == host->max_id-1) --host->max_id; 666 gw_proc_free(proc); 667 } else if (gw_spawn_connection(srv, host, proc, debug)) { 668 log_error_write(srv, __FILE__, __LINE__, "s", 669 "ERROR: spawning backend failed."); 670 proc->next = host->unused_procs; 671 if (host->unused_procs) 672 host->unused_procs->prev = proc; 673 host->unused_procs = proc; 674 } else { 675 proc->next = host->first; 676 if (host->first) 677 host->first->prev = proc; 678 host->first = proc; 679 } 680 } 681 682 static void gw_proc_kill(server *srv, gw_host *host, gw_proc *proc) { 683 UNUSED(srv); 684 if (proc->next) proc->next->prev = proc->prev; 685 if (proc->prev) proc->prev->next = proc->next; 686 687 if (proc->prev == NULL) host->first = proc->next; 688 689 proc->prev = NULL; 690 proc->next = host->unused_procs; 691 proc->disabled_until = 0; 692 693 if (host->unused_procs) 694 host->unused_procs->prev = proc; 695 host->unused_procs = proc; 696 697 kill(proc->pid, host->kill_signal); 698 699 gw_proc_set_state(host, proc, PROC_STATE_KILLED); 700 701 --host->num_procs; 702 } 703 704 static gw_host * unixsocket_is_dup(gw_plugin_data *p, size_t used, buffer *unixsocket) { 705 for (size_t i = 0; i < used; ++i) { 706 gw_exts *exts = p->config_storage[i]->exts; 707 if (NULL == exts) continue; 708 for (size_t j = 0; j < exts->used; ++j) { 709 gw_extension *ex = exts->exts[j]; 710 for (size_t n = 0; n < ex->used; ++n) { 711 gw_host *host = ex->hosts[n]; 712 if (!buffer_string_is_empty(host->unixsocket) 713 && buffer_is_equal(host->unixsocket, unixsocket) 714 && !buffer_string_is_empty(host->bin_path)) 715 return host; 716 } 717 } 718 } 719 720 return NULL; 721 } 722 723 static int parse_binpath(char_array *env, buffer *b) { 724 char *start = b->ptr; 725 char c; 726 /* search for spaces */ 727 for (size_t i = 0; i < buffer_string_length(b); ++i) { 728 switch(b->ptr[i]) { 729 case ' ': 730 case '\t': 731 /* a WS, stop here and copy the argument */ 732 733 if (env->size == env->used) { 734 env->size += 16; 735 env->ptr = realloc(env->ptr, env->size * sizeof(*env->ptr)); 736 } 737 738 c = b->ptr[i]; 739 b->ptr[i] = '\0'; 740 env->ptr[env->used++] = strdup(start); 741 b->ptr[i] = c; 742 743 start = b->ptr + i + 1; 744 break; 745 default: 746 break; 747 } 748 } 749 750 if (env->size == env->used) { /*need one extra for terminating NULL*/ 751 env->size += 16; 752 env->ptr = realloc(env->ptr, env->size * sizeof(*env->ptr)); 753 } 754 755 /* the rest */ 756 env->ptr[env->used++] = strdup(start); 757 758 if (env->size == env->used) { /*need one extra for terminating NULL*/ 759 env->size += 16; 760 env->ptr = realloc(env->ptr, env->size * sizeof(*env->ptr)); 761 } 762 763 /* terminate */ 764 env->ptr[env->used++] = NULL; 765 766 return 0; 767 } 768 769 enum { 770 GW_BALANCE_LEAST_CONNECTION, 771 GW_BALANCE_RR, 772 GW_BALANCE_HASH, 773 GW_BALANCE_STICKY 774 }; 775 776 static gw_host * gw_host_get(server *srv, connection *con, gw_extension *extension, int balance, int debug) { 777 gw_host *host; 778 unsigned long last_max = ULONG_MAX; 779 int max_usage = INT_MAX; 780 int ndx = -1; 781 size_t k; 782 783 if (extension->used <= 1) { 784 if (1 == extension->used && extension->hosts[0]->active_procs > 0) { 785 ndx = 0; 786 } 787 } else switch(balance) { 788 case GW_BALANCE_HASH: 789 /* hash balancing */ 790 791 if (debug) { 792 log_error_write(srv, __FILE__, __LINE__, "sd", 793 "proxy - used hash balancing, hosts:", 794 extension->used); 795 } 796 797 for (k = 0, ndx = -1, last_max = ULONG_MAX; k < extension->used; ++k) { 798 unsigned long cur_max; 799 host = extension->hosts[k]; 800 if (0 == host->active_procs) continue; 801 802 cur_max = generate_crc32c(CONST_BUF_LEN(con->uri.path)) 803 + generate_crc32c(CONST_BUF_LEN(host->host)) /* cachable */ 804 + generate_crc32c(CONST_BUF_LEN(con->uri.authority)); 805 806 if (debug) { 807 log_error_write(srv, __FILE__, __LINE__, "sbbbd", 808 "proxy - election:", con->uri.path, 809 host->host, con->uri.authority, cur_max); 810 } 811 812 if (last_max < cur_max || last_max == ULONG_MAX) { 813 last_max = cur_max; 814 ndx = k; 815 } 816 } 817 818 break; 819 case GW_BALANCE_LEAST_CONNECTION: 820 /* fair balancing */ 821 if (debug) { 822 log_error_write(srv, __FILE__, __LINE__, "s", 823 "proxy - used least connection"); 824 } 825 826 for (k = 0, ndx = -1, max_usage = INT_MAX; k < extension->used; ++k) { 827 host = extension->hosts[k]; 828 if (0 == host->active_procs) continue; 829 830 if (host->load < max_usage) { 831 max_usage = host->load; 832 ndx = k; 833 } 834 } 835 836 break; 837 case GW_BALANCE_RR: 838 /* round robin */ 839 if (debug) { 840 log_error_write(srv, __FILE__, __LINE__, "s", 841 "proxy - used round-robin balancing"); 842 } 843 844 /* just to be sure */ 845 force_assert(extension->used < INT_MAX); 846 847 host = extension->hosts[0]; 848 849 /* Use last_used_ndx from first host in list */ 850 k = extension->last_used_ndx; 851 ndx = k + 1; /* use next host after the last one */ 852 if (ndx < 0) ndx = 0; 853 854 /* Search first active host after last_used_ndx */ 855 while (ndx < (int) extension->used 856 && 0 == (host = extension->hosts[ndx])->active_procs) ++ndx; 857 858 if (ndx >= (int) extension->used) { 859 /* didn't find a higher id, wrap to the start */ 860 for (ndx = 0; ndx <= (int) k; ++ndx) { 861 host = extension->hosts[ndx]; 862 if (0 != host->active_procs) break; 863 } 864 865 /* No active host found */ 866 if (0 == host->active_procs) ndx = -1; 867 } 868 869 /* Save new index for next round */ 870 extension->last_used_ndx = ndx; 871 872 break; 873 case GW_BALANCE_STICKY: 874 /* source sticky balancing */ 875 876 if (debug) { 877 log_error_write(srv, __FILE__, __LINE__, "sd", 878 "proxy - used sticky balancing, hosts:", 879 extension->used); 880 } 881 882 for (k = 0, ndx = -1, last_max = ULONG_MAX; k < extension->used; ++k) { 883 unsigned long cur_max; 884 host = extension->hosts[k]; 885 886 if (0 == host->active_procs) continue; 887 888 cur_max = generate_crc32c(CONST_BUF_LEN(con->dst_addr_buf)) 889 + generate_crc32c(CONST_BUF_LEN(host->host)) 890 + host->port; 891 892 if (debug) { 893 log_error_write(srv, __FILE__, __LINE__, "sbbdd", 894 "proxy - election:", con->dst_addr_buf, 895 host->host, host->port, cur_max); 896 } 897 898 if (last_max < cur_max || last_max == ULONG_MAX) { 899 last_max = cur_max; 900 ndx = k; 901 } 902 } 903 904 break; 905 default: 906 break; 907 } 908 909 if (-1 != ndx) { 910 /* found a server */ 911 host = extension->hosts[ndx]; 912 913 if (debug) { 914 log_error_write(srv, __FILE__, __LINE__, "sbd", 915 "gw - found a host", host->host, host->port); 916 } 917 918 return host; 919 } else if (0 == srv->srvconf.max_worker) { 920 /* special-case adaptive spawning and 0 == host->min_procs */ 921 for (k = 0; k < extension->used; ++k) { 922 host = extension->hosts[k]; 923 if (0 == host->min_procs && 0 == host->num_procs 924 && !buffer_string_is_empty(host->bin_path)) { 925 gw_proc_spawn(srv, host, debug); 926 if (host->num_procs) return host; 927 } 928 } 929 } 930 931 /* all hosts are down */ 932 /* sorry, we don't have a server alive for this ext */ 933 con->http_status = 503; /* Service Unavailable */ 934 con->mode = DIRECT; 935 936 /* only send the 'no handler' once */ 937 if (!extension->note_is_sent) { 938 extension->note_is_sent = 1; 939 log_error_write(srv, __FILE__, __LINE__, "sBSbsbs", 940 "all handlers for", con->uri.path, "?", 941 con->uri.query, "on", extension->key, "are down."); 942 } 943 944 return NULL; 945 } 946 947 static int gw_establish_connection(server *srv, gw_host *host, gw_proc *proc, pid_t pid, int gw_fd, int debug) { 948 if (-1 == connect(gw_fd, proc->saddr, proc->saddrlen)) { 949 if (errno == EINPROGRESS || 950 errno == EALREADY || 951 errno == EINTR) { 952 if (debug > 2) { 953 log_error_write(srv, __FILE__, __LINE__, "sb", 954 "connect delayed; will continue later:", 955 proc->connection_name); 956 } 957 958 return 1; 959 } else { 960 gw_proc_connect_error(srv, host, proc, pid, errno, debug); 961 return -1; 962 } 963 } 964 965 if (debug > 1) { 966 log_error_write(srv, __FILE__, __LINE__, "sd", 967 "connect succeeded: ", gw_fd); 968 } 969 970 return 0; 971 } 972 973 static void gw_restart_dead_procs(server *srv, gw_host *host, int debug, int trigger) { 974 for (gw_proc *proc = host->first; proc; proc = proc->next) { 975 if (debug > 2) { 976 log_error_write(srv, __FILE__, __LINE__, "sbdddd", 977 "proc:", proc->connection_name, proc->state, 978 proc->is_local, proc->load, proc->pid); 979 } 980 981 switch (proc->state) { 982 case PROC_STATE_RUNNING: 983 break; 984 case PROC_STATE_OVERLOADED: 985 gw_proc_check_enable(srv, host, proc); 986 break; 987 case PROC_STATE_KILLED: 988 if (trigger && ++proc->disabled_until > 4) { 989 int sig = (proc->disabled_until <= 8) 990 ? host->kill_signal 991 : proc->disabled_until <= 16 ? SIGTERM : SIGKILL; 992 kill(proc->pid, sig); 993 } 994 break; 995 case PROC_STATE_DIED_WAIT_FOR_PID: 996 /*(state should not happen in workers if server.max-worker > 0)*/ 997 /*(if PROC_STATE_DIED_WAIT_FOR_PID is used in future, might want 998 * to save proc->disabled_until before gw_proc_waitpid() since 999 * gw_proc_waitpid will set proc->disabled_until to srv->cur_ts, 1000 * and so process will not be restarted below until one sec later)*/ 1001 if (0 == gw_proc_waitpid(srv, host, proc)) { 1002 gw_proc_check_enable(srv, host, proc); 1003 } 1004 1005 if (proc->state != PROC_STATE_DIED) break; 1006 /* fall through *//*(we have a dead proc now)*/ 1007 1008 case PROC_STATE_DIED: 1009 /* local procs get restarted by us, 1010 * remote ones hopefully by the admin */ 1011 1012 if (!buffer_string_is_empty(host->bin_path)) { 1013 /* we still have connections bound to this proc, 1014 * let them terminate first */ 1015 if (proc->load != 0) break; 1016 1017 /* avoid spinning if child exits too quickly */ 1018 if (proc->disabled_until >= srv->cur_ts) break; 1019 1020 /* restart the child */ 1021 1022 if (debug) { 1023 log_error_write(srv, __FILE__, __LINE__, "ssbsdsd", 1024 "--- gw spawning", 1025 "\n\tsocket", proc->connection_name, 1026 "\n\tcurrent:", 1, "/", host->max_procs); 1027 } 1028 1029 if (gw_spawn_connection(srv, host, proc, debug)) { 1030 log_error_write(srv, __FILE__, __LINE__, "s", 1031 "ERROR: spawning gw failed."); 1032 } 1033 } else { 1034 gw_proc_check_enable(srv, host, proc); 1035 } 1036 break; 1037 } 1038 } 1039 } 1040 1041 1042 1043 1044 #include "base.h" 1045 #include "connections.h" 1046 #include "joblist.h" 1047 #include "response.h" 1048 1049 1050 /* ok, we need a prototype */ 1051 static handler_t gw_handle_fdevent(server *srv, void *ctx, int revents); 1052 1053 1054 static gw_handler_ctx * handler_ctx_init(size_t sz) { 1055 gw_handler_ctx *hctx = calloc(1, 0 == sz ? sizeof(*hctx) : sz); 1056 force_assert(hctx); 1057 1058 /*hctx->response = chunk_buffer_acquire();*//*(allocated when needed)*/ 1059 1060 hctx->request_id = 0; 1061 hctx->gw_mode = GW_RESPONDER; 1062 hctx->state = GW_STATE_INIT; 1063 hctx->proc = NULL; 1064 1065 hctx->fd = -1; 1066 1067 hctx->reconnects = 0; 1068 hctx->send_content_body = 1; 1069 1070 /*hctx->rb = chunkqueue_init();*//*(allocated when needed)*/ 1071 hctx->wb = chunkqueue_init(); 1072 hctx->wb_reqlen = 0; 1073 1074 return hctx; 1075 } 1076 1077 static void handler_ctx_free(gw_handler_ctx *hctx) { 1078 /* caller MUST have called gw_backend_close(srv, hctx) if necessary */ 1079 if (hctx->handler_ctx_free) hctx->handler_ctx_free(hctx); 1080 chunk_buffer_release(hctx->response); 1081 1082 chunkqueue_free(hctx->rb); 1083 chunkqueue_free(hctx->wb); 1084 1085 free(hctx); 1086 } 1087 1088 static void handler_ctx_clear(gw_handler_ctx *hctx) { 1089 /* caller MUST have called gw_backend_close(srv, hctx) if necessary */ 1090 1091 hctx->proc = NULL; 1092 hctx->host = NULL; 1093 hctx->ext = NULL; 1094 /*hctx->ext_auth is intentionally preserved to flag prior authorizer*/ 1095 1096 hctx->gw_mode = GW_RESPONDER; 1097 hctx->state = GW_STATE_INIT; 1098 /*hctx->state_timestamp = 0;*//*(unused; left as-is)*/ 1099 1100 if (hctx->rb) chunkqueue_reset(hctx->rb); 1101 if (hctx->wb) chunkqueue_reset(hctx->wb); 1102 hctx->wb_reqlen = 0; 1103 1104 if (hctx->response) buffer_clear(hctx->response); 1105 1106 hctx->fd = -1; 1107 hctx->reconnects = 0; 1108 hctx->request_id = 0; 1109 hctx->send_content_body = 1; 1110 1111 /*plugin_config conf;*//*(no need to reset for same request)*/ 1112 1113 /*hctx->remote_conn = NULL;*//*(no need to reset for same request)*/ 1114 /*hctx->plugin_data = NULL;*//*(no need to reset for same request)*/ 1115 } 1116 1117 1118 void * gw_init(void) { 1119 return calloc(1, sizeof(gw_plugin_data)); 1120 } 1121 1122 1123 void gw_plugin_config_free(gw_plugin_config *s) { 1124 gw_exts *exts = s->exts; 1125 if (exts) { 1126 for (size_t j = 0; j < exts->used; ++j) { 1127 gw_extension *ex = exts->exts[j]; 1128 for (size_t n = 0; n < ex->used; ++n) { 1129 gw_proc *proc; 1130 gw_host *host = ex->hosts[n]; 1131 1132 for (proc = host->first; proc; proc = proc->next) { 1133 if (proc->pid > 0) { 1134 kill(proc->pid, host->kill_signal); 1135 } 1136 1137 if (proc->is_local && 1138 !buffer_string_is_empty(proc->unixsocket)) { 1139 unlink(proc->unixsocket->ptr); 1140 } 1141 } 1142 1143 for (proc = host->unused_procs; proc; proc = proc->next) { 1144 if (proc->pid > 0) { 1145 kill(proc->pid, host->kill_signal); 1146 } 1147 if (proc->is_local && 1148 !buffer_string_is_empty(proc->unixsocket)) { 1149 unlink(proc->unixsocket->ptr); 1150 } 1151 } 1152 } 1153 } 1154 1155 gw_extensions_free(s->exts); 1156 gw_extensions_free(s->exts_auth); 1157 gw_extensions_free(s->exts_resp); 1158 } 1159 array_free(s->ext_mapping); 1160 free(s); 1161 } 1162 1163 handler_t gw_free(server *srv, void *p_d) { 1164 gw_plugin_data *p = p_d; 1165 if (p->config_storage) { 1166 for (size_t i = 0; i < srv->config_context->used; ++i) { 1167 gw_plugin_config *s = p->config_storage[i]; 1168 if (NULL == s) continue; 1169 gw_plugin_config_free(s); 1170 } 1171 free(p->config_storage); 1172 } 1173 free(p); 1174 return HANDLER_GO_ON; 1175 } 1176 1177 int gw_set_defaults_backend(server *srv, gw_plugin_data *p, data_unset *du, size_t i, int sh_exec) { 1178 /* per-module plugin_config MUST have common "base class" gw_plugin_config*/ 1179 /* per-module plugin_data MUST have pointer-compatible common "base class" 1180 * with gw_plugin_data (stemming from gw_plugin_config compatibility) */ 1181 1182 data_array *da = (data_array *)du; 1183 gw_plugin_config *s = p->config_storage[i]; 1184 buffer *gw_mode; 1185 gw_host *host = NULL; 1186 1187 if (NULL == da) return 1; 1188 1189 if (da->type != TYPE_ARRAY || !array_is_kvarray(da->value)) { 1190 log_error_write(srv, __FILE__, __LINE__, "s", 1191 "unexpected value for xxxxx.server; expected " 1192 "( \"ext\" => ( \"backend-label\" => ( \"key\" => \"value\" )))"); 1193 return 0; 1194 } 1195 1196 p->srv_pid = srv->pid; 1197 1198 gw_mode = buffer_init(); 1199 1200 s->exts = gw_extensions_init(); 1201 s->exts_auth = gw_extensions_init(); 1202 s->exts_resp = gw_extensions_init(); 1203 /*s->balance = GW_BALANCE_LEAST_CONNECTION;*//*(default)*/ 1204 1205 /* 1206 * gw.server = ( "<ext>" => ( ... ), 1207 * "<ext>" => ( ... ) ) 1208 */ 1209 1210 for (size_t j = 0; j < da->value->used; ++j) { 1211 data_array *da_ext = (data_array *)da->value->data[j]; 1212 1213 /* 1214 * da_ext->key == name of the extension 1215 */ 1216 1217 /* 1218 * gw.server = ( "<ext>" => 1219 * ( "<host>" => ( ... ), 1220 * "<host>" => ( ... ) 1221 * ), 1222 * "<ext>" => ... ) 1223 */ 1224 1225 for (size_t n = 0; n < da_ext->value->used; ++n) { 1226 data_array *da_host = (data_array *)da_ext->value->data[n]; 1227 1228 config_values_t fcv[] = { 1229 { "host", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ 1230 { "docroot", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ 1231 { "mode", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 2 */ 1232 { "socket", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 3 */ 1233 { "bin-path", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 4 */ 1234 1235 { "check-local", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 5 */ 1236 { "port", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 6 */ 1237 { "min-procs", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 7 */ 1238 { "max-procs", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 8 */ 1239 { "max-load-per-proc", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 9 */ 1240 { "idle-timeout", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 10 */ 1241 { "disable-time", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 11 */ 1242 1243 { "bin-environment", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 12 */ 1244 { "bin-copy-environment", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 13 */ 1245 1246 { "broken-scriptfilename", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 14 */ 1247 { "allow-x-send-file", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 15 */ 1248 { "strip-request-uri", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 16 */ 1249 { "kill-signal", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 17 */ 1250 { "fix-root-scriptname", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 18 */ 1251 { "listen-backlog", NULL, T_CONFIG_INT, T_CONFIG_SCOPE_CONNECTION }, /* 19 */ 1252 { "x-sendfile", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 20 */ 1253 { "x-sendfile-docroot",NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 21 */ 1254 { "tcp-fin-propagate", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 22 */ 1255 1256 { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } 1257 }; 1258 unsigned short host_mode = GW_RESPONDER; 1259 1260 if (da_host->type != TYPE_ARRAY || !array_is_kvany(da_host->value)){ 1261 log_error_write(srv, __FILE__, __LINE__, "SBS", 1262 "unexpected value for gw.server near [", 1263 da_host->key, "](string); expected ( \"ext\" => ( \"backend-label\" => ( \"key\" => \"value\" )))"); 1264 1265 goto error; 1266 } 1267 1268 host = gw_host_init(); 1269 buffer_clear(gw_mode); 1270 1271 buffer_copy_buffer(host->id, da_host->key); 1272 1273 host->check_local = 1; 1274 host->min_procs = 4; 1275 host->max_procs = 4; 1276 host->max_load_per_proc = 1; 1277 host->idle_timeout = 60; 1278 host->disable_time = 1; 1279 host->break_scriptfilename_for_php = 0; 1280 host->kill_signal = SIGTERM; 1281 host->fix_root_path_name = 0; 1282 host->listen_backlog = 1024; 1283 host->xsendfile_allow = 0; 1284 host->refcount = 0; 1285 1286 fcv[0].destination = host->host; 1287 fcv[1].destination = host->docroot; 1288 fcv[2].destination = gw_mode; 1289 fcv[3].destination = host->unixsocket; 1290 fcv[4].destination = host->bin_path; 1291 1292 fcv[5].destination = &(host->check_local); 1293 fcv[6].destination = &(host->port); 1294 fcv[7].destination = &(host->min_procs); 1295 fcv[8].destination = &(host->max_procs); 1296 fcv[9].destination = &(host->max_load_per_proc); 1297 fcv[10].destination = &(host->idle_timeout); 1298 fcv[11].destination = &(host->disable_time); 1299 1300 fcv[12].destination = host->bin_env; 1301 fcv[13].destination = host->bin_env_copy; 1302 fcv[14].destination = &(host->break_scriptfilename_for_php); 1303 fcv[15].destination = &(host->xsendfile_allow); 1304 fcv[16].destination = host->strip_request_uri; 1305 fcv[17].destination = &(host->kill_signal); 1306 fcv[18].destination = &(host->fix_root_path_name); 1307 fcv[19].destination = &(host->listen_backlog); 1308 fcv[20].destination = &(host->xsendfile_allow); 1309 fcv[21].destination = host->xsendfile_docroot; 1310 fcv[22].destination = &(host->tcp_fin_propagate); 1311 1312 if (0 != config_insert_values_internal(srv, da_host->value, fcv, T_CONFIG_SCOPE_CONNECTION)) { 1313 goto error; 1314 } 1315 1316 for (size_t m = 0; m < da_host->value->used; ++m) { 1317 if (NULL != strchr(da_host->value->data[m]->key->ptr, '_')) { 1318 log_error_write(srv, __FILE__, __LINE__, "sb", 1319 "incorrect directive contains underscore ('_') instead of dash ('-'):", 1320 da_host->value->data[m]->key); 1321 } 1322 } 1323 1324 if ((!buffer_string_is_empty(host->host) || host->port) 1325 && !buffer_string_is_empty(host->unixsocket)) { 1326 log_error_write(srv, __FILE__, __LINE__, "sbsbsbs", 1327 "either host/port or socket have to be set in:", 1328 da->key, "= (", 1329 da_ext->key, " => (", 1330 da_host->key, " ( ..."); 1331 1332 goto error; 1333 } 1334 1335 if (!buffer_string_is_empty(host->host) && *host->host->ptr == '/' 1336 && buffer_string_is_empty(host->unixsocket)) { 1337 buffer_copy_buffer(host->unixsocket, host->host); 1338 } 1339 1340 if (!buffer_string_is_empty(host->unixsocket)) { 1341 /* unix domain socket */ 1342 struct sockaddr_un un; 1343 1344 if (buffer_string_length(host->unixsocket) + 1 > sizeof(un.sun_path) - 2) { 1345 log_error_write(srv, __FILE__, __LINE__, "sbsbsbs", 1346 "unixsocket is too long in:", 1347 da->key, "= (", 1348 da_ext->key, " => (", 1349 da_host->key, " ( ..."); 1350 1351 goto error; 1352 } 1353 1354 if (!buffer_string_is_empty(host->bin_path)) { 1355 gw_host *duplicate = unixsocket_is_dup(p, i+1, host->unixsocket); 1356 if (NULL != duplicate) { 1357 if (!buffer_is_equal(host->bin_path, duplicate->bin_path)) { 1358 log_error_write(srv, __FILE__, __LINE__, "sb", 1359 "duplicate unixsocket path:", 1360 host->unixsocket); 1361 goto error; 1362 } 1363 gw_host_free(host); 1364 host = duplicate; 1365 ++host->refcount; 1366 } 1367 } 1368 1369 host->family = AF_UNIX; 1370 } else { 1371 /* tcp/ip */ 1372 1373 if (buffer_string_is_empty(host->host) && 1374 buffer_string_is_empty(host->bin_path)) { 1375 log_error_write(srv, __FILE__, __LINE__, "sbsbsbs", 1376 "host or bin-path have to be set in:", 1377 da->key, "= (", 1378 da_ext->key, " => (", 1379 da_host->key, " ( ..."); 1380 1381 goto error; 1382 } else if (0 == host->port) { 1383 host->port = 80; 1384 } 1385 1386 if (buffer_string_is_empty(host->host)) { 1387 buffer_copy_string_len(host->host, 1388 CONST_STR_LEN("127.0.0.1")); 1389 } 1390 1391 host->family = (NULL != strchr(host->host->ptr, ':')) 1392 ? AF_INET6 1393 : AF_INET; 1394 } 1395 1396 if (host->refcount) { 1397 /* already init'd; skip spawning */ 1398 } else if (!buffer_string_is_empty(host->bin_path)) { 1399 /* a local socket + self spawning */ 1400 struct stat st; 1401 parse_binpath(&host->args, host->bin_path); 1402 if (0 != stat(host->args.ptr[0], &st) || !S_ISREG(st.st_mode) 1403 || !(st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) { 1404 log_error_write(srv, __FILE__, __LINE__, "SSs", 1405 "invalid \"bin-path\" => \"", host->bin_path->ptr, 1406 "\" (check that file exists, is regular file, " 1407 "and is executable by lighttpd)"); 1408 } 1409 1410 if (sh_exec) { 1411 /*(preserve prior behavior for SCGI exec of command)*/ 1412 /*(admin should really prefer to put 1413 * any complex command into a script)*/ 1414 for (size_t m = 0; m < host->args.used; ++m) 1415 free(host->args.ptr[m]); 1416 free(host->args.ptr); 1417 1418 host->args.ptr = calloc(4, sizeof(char *)); 1419 force_assert(host->args.ptr); 1420 host->args.used = 3; 1421 host->args.size = 4; 1422 host->args.ptr[0] = malloc(sizeof("/bin/sh")); 1423 force_assert(host->args.ptr[0]); 1424 memcpy(host->args.ptr[0], "/bin/sh", sizeof("/bin/sh")); 1425 host->args.ptr[1] = malloc(sizeof("-c")); 1426 force_assert(host->args.ptr[1]); 1427 memcpy(host->args.ptr[1], "-c", sizeof("-c")); 1428 host->args.ptr[2] = 1429 malloc(sizeof("exec ")-1 1430 + buffer_string_length(host->bin_path) + 1); 1431 force_assert(host->args.ptr[2]); 1432 memcpy(host->args.ptr[2], "exec ", sizeof("exec ")-1); 1433 memcpy(host->args.ptr[2]+sizeof("exec ")-1, 1434 host->bin_path->ptr, 1435 buffer_string_length(host->bin_path)+1); 1436 host->args.ptr[3] = NULL; 1437 } 1438 1439 if (host->min_procs > host->max_procs) 1440 host->min_procs = host->max_procs; 1441 if (host->min_procs!= host->max_procs 1442 && 0 != srv->srvconf.max_worker) { 1443 host->min_procs = host->max_procs; 1444 log_error_write(srv, __FILE__, __LINE__, "s", 1445 "adaptive backend spawning disabled " 1446 "(server.max_worker is non-zero)"); 1447 } 1448 if (host->max_load_per_proc < 1) 1449 host->max_load_per_proc = 0; 1450 1451 if (s->debug) { 1452 log_error_write(srv, __FILE__, __LINE__, "ssbsdsbsdsd", 1453 "--- gw spawning local", 1454 "\n\tproc:", host->bin_path, 1455 "\n\tport:", host->port, 1456 "\n\tsocket", host->unixsocket, 1457 "\n\tmin-procs:", host->min_procs, 1458 "\n\tmax-procs:", host->max_procs); 1459 } 1460 1461 for (size_t pno = 0; pno < host->min_procs; ++pno) { 1462 gw_proc *proc = gw_proc_init(); 1463 proc->id = host->num_procs++; 1464 host->max_id++; 1465 1466 if (buffer_string_is_empty(host->unixsocket)) { 1467 proc->port = host->port + pno; 1468 } else { 1469 buffer_copy_buffer(proc->unixsocket, host->unixsocket); 1470 buffer_append_string_len(proc->unixsocket, 1471 CONST_STR_LEN("-")); 1472 buffer_append_int(proc->unixsocket, pno); 1473 } 1474 1475 if (s->debug) { 1476 log_error_write(srv, __FILE__, __LINE__, "ssdsbsdsd", 1477 "--- gw spawning", 1478 "\n\tport:", host->port, 1479 "\n\tsocket", host->unixsocket, 1480 "\n\tcurrent:", pno, "/", host->max_procs); 1481 } 1482 1483 if (0 != gw_proc_sockaddr_init(srv, host, proc)) { 1484 gw_proc_free(proc); 1485 goto error; 1486 } 1487 1488 if (!srv->srvconf.preflight_check 1489 && gw_spawn_connection(srv, host, proc, s->debug)) { 1490 log_error_write(srv, __FILE__, __LINE__, "s", 1491 "[ERROR]: spawning gw failed."); 1492 gw_proc_free(proc); 1493 goto error; 1494 } 1495 1496 gw_status_init(srv, host, proc); 1497 1498 proc->next = host->first; 1499 if (host->first) host->first->prev = proc; 1500 1501 host->first = proc; 1502 } 1503 } else { 1504 gw_proc *proc; 1505 1506 proc = gw_proc_init(); 1507 proc->id = host->num_procs++; 1508 host->max_id++; 1509 gw_proc_set_state(host, proc, PROC_STATE_RUNNING); 1510 1511 if (buffer_string_is_empty(host->unixsocket)) { 1512 proc->port = host->port; 1513 } else { 1514 buffer_copy_buffer(proc->unixsocket, host->unixsocket); 1515 } 1516 1517 gw_status_init(srv, host, proc); 1518 1519 host->first = proc; 1520 1521 host->min_procs = 1; 1522 host->max_procs = 1; 1523 1524 if (0 != gw_proc_sockaddr_init(srv, host, proc)) goto error; 1525 } 1526 1527 if (!buffer_string_is_empty(gw_mode)) { 1528 if (strcmp(gw_mode->ptr, "responder") == 0) { 1529 host_mode = GW_RESPONDER; 1530 } else if (strcmp(gw_mode->ptr, "authorizer") == 0) { 1531 host_mode = GW_AUTHORIZER; 1532 } else { 1533 log_error_write(srv, __FILE__, __LINE__, "sbs", 1534 "WARNING: unknown gw mode:", 1535 gw_mode,"(ignored, mode set to responder)"); 1536 } 1537 } 1538 1539 if (host->xsendfile_docroot->used) { 1540 size_t k; 1541 for (k = 0; k < host->xsendfile_docroot->used; ++k) { 1542 data_string *ds = (data_string *)host->xsendfile_docroot->data[k]; 1543 if (ds->type != TYPE_STRING) { 1544 log_error_write(srv, __FILE__, __LINE__, "s", 1545 "unexpected type for x-sendfile-docroot; expected: \"x-sendfile-docroot\" => ( \"/allowed/path\", ... )"); 1546 goto error; 1547 } 1548 if (ds->value->ptr[0] != '/') { 1549 log_error_write(srv, __FILE__, __LINE__, "SBs", 1550 "x-sendfile-docroot paths must begin with '/'; invalid: \"", ds->value, "\""); 1551 goto error; 1552 } 1553 buffer_path_simplify(ds->value, ds->value); 1554 buffer_append_slash(ds->value); 1555 } 1556 } 1557 1558 /* s->exts is list of exts -> hosts 1559 * s->exts now used as combined list 1560 * of authorizer and responder hosts (for backend maintenance) 1561 * s->exts_auth is list of exts -> authorizer hosts 1562 * s->exts_resp is list of exts -> responder hosts 1563 * For each path/extension: 1564 * there may be an independent GW_AUTHORIZER and GW_RESPONDER 1565 * (The GW_AUTHORIZER and GW_RESPONDER could be handled by the same 1566 * host, and an admin might want to do that for large uploads, 1567 * since GW_AUTHORIZER runs prior to receiving (potentially large) 1568 * request body from client and can authorizer or deny request 1569 * prior to receiving the full upload) 1570 */ 1571 gw_extension_insert(s->exts, da_ext->key, host); 1572 1573 if (host_mode == GW_AUTHORIZER) { 1574 ++host->refcount; 1575 gw_extension_insert(s->exts_auth, da_ext->key, host); 1576 } else if (host_mode == GW_RESPONDER) { 1577 ++host->refcount; 1578 gw_extension_insert(s->exts_resp, da_ext->key, host); 1579 } /*(else should have been rejected above)*/ 1580 1581 host = NULL; 1582 } 1583 } 1584 1585 buffer_free(gw_mode); 1586 return 1; 1587 1588 error: 1589 if (NULL != host) gw_host_free(host); 1590 buffer_free(gw_mode); 1591 return 0; 1592 } 1593 1594 int gw_set_defaults_balance(server *srv, gw_plugin_config *s, data_unset *du) { 1595 buffer *b; 1596 if (NULL == du) { 1597 b = NULL; 1598 } else if (du->type == TYPE_STRING) { 1599 b = ((data_string *)du)->value; 1600 } else { 1601 log_error_write(srv, __FILE__, __LINE__, "s", 1602 "unexpected type for xxxxx.balance; expected string"); 1603 return 0; 1604 } 1605 if (buffer_string_is_empty(b)) { 1606 s->balance = GW_BALANCE_LEAST_CONNECTION; 1607 } else if (buffer_is_equal_string(b, CONST_STR_LEN("fair"))) { 1608 s->balance = GW_BALANCE_LEAST_CONNECTION; 1609 } else if (buffer_is_equal_string(b, CONST_STR_LEN("least-connection"))) { 1610 s->balance = GW_BALANCE_LEAST_CONNECTION; 1611 } else if (buffer_is_equal_string(b, CONST_STR_LEN("round-robin"))) { 1612 s->balance = GW_BALANCE_RR; 1613 } else if (buffer_is_equal_string(b, CONST_STR_LEN("hash"))) { 1614 s->balance = GW_BALANCE_HASH; 1615 } else if (buffer_is_equal_string(b, CONST_STR_LEN("sticky"))) { 1616 s->balance = GW_BALANCE_STICKY; 1617 } else { 1618 log_error_write(srv, __FILE__, __LINE__, "sb", 1619 "xxxxx.balance has to be one of: " 1620 "least-connection, round-robin, hash, sticky, but not:", 1621 b); 1622 return 0; 1623 } 1624 return 1; 1625 } 1626 1627 static void gw_set_state(server *srv, gw_handler_ctx *hctx, gw_connection_state_t state) { 1628 hctx->state = state; 1629 hctx->state_timestamp = srv->cur_ts; 1630 } 1631 1632 1633 void gw_set_transparent(server *srv, gw_handler_ctx *hctx) { 1634 if (AF_UNIX != hctx->host->family) { 1635 if (-1 == fdevent_set_tcp_nodelay(hctx->fd, 1)) { 1636 /*(error, but not critical)*/ 1637 } 1638 } 1639 hctx->wb_reqlen = -1; 1640 gw_set_state(srv, hctx, GW_STATE_WRITE); 1641 } 1642 1643 1644 static void gw_backend_close(server *srv, gw_handler_ctx *hctx) { 1645 if (hctx->fd >= 0) { 1646 fdevent_fdnode_event_del(srv->ev, hctx->fdn); 1647 /*fdevent_unregister(srv->ev, hctx->fd);*//*(handled below)*/ 1648 fdevent_sched_close(srv->ev, hctx->fd, 1); 1649 hctx->fdn = NULL; 1650 hctx->fd = -1; 1651 } 1652 1653 if (hctx->host) { 1654 if (hctx->proc) { 1655 gw_proc_release(srv, hctx->host, hctx->proc, hctx->conf.debug); 1656 hctx->proc = NULL; 1657 } 1658 1659 gw_host_reset(srv, hctx->host); 1660 hctx->host = NULL; 1661 } 1662 } 1663 1664 static void gw_connection_close(server *srv, gw_handler_ctx *hctx) { 1665 gw_plugin_data *p = hctx->plugin_data; 1666 connection *con = hctx->remote_conn; 1667 1668 gw_backend_close(srv, hctx); 1669 handler_ctx_free(hctx); 1670 con->plugin_ctx[p->id] = NULL; 1671 1672 if (con->mode == p->id) { 1673 http_response_backend_done(srv, con); 1674 } 1675 } 1676 1677 static handler_t gw_reconnect(server *srv, gw_handler_ctx *hctx) { 1678 gw_backend_close(srv, hctx); 1679 1680 hctx->host = gw_host_get(srv, hctx->remote_conn, hctx->ext, 1681 hctx->conf.balance, hctx->conf.debug); 1682 if (NULL == hctx->host) return HANDLER_FINISHED; 1683 1684 gw_host_assign(srv, hctx->host); 1685 hctx->request_id = 0; 1686 hctx->opts.xsendfile_allow = hctx->host->xsendfile_allow; 1687 hctx->opts.xsendfile_docroot = hctx->host->xsendfile_docroot; 1688 gw_set_state(srv, hctx, GW_STATE_INIT); 1689 return HANDLER_COMEBACK; 1690 } 1691 1692 1693 handler_t gw_connection_reset(server *srv, connection *con, void *p_d) { 1694 gw_plugin_data *p = p_d; 1695 gw_handler_ctx *hctx = con->plugin_ctx[p->id]; 1696 if (hctx) gw_connection_close(srv, hctx); 1697 1698 return HANDLER_GO_ON; 1699 } 1700 1701 1702 static void gw_conditional_tcp_fin(server *srv, gw_handler_ctx *hctx) { 1703 connection *con = hctx->remote_conn; 1704 /*assert(con->conf.stream_request_body & FDEVENT_STREAM_REQUEST_TCP_FIN);*/ 1705 if (!chunkqueue_is_empty(hctx->wb)) return; 1706 if (!hctx->host->tcp_fin_propagate) return; 1707 if (hctx->gw_mode == GW_AUTHORIZER) return; 1708 if (con->conf.stream_request_body & FDEVENT_STREAM_REQUEST_BACKEND_SHUT_WR) 1709 return; 1710 1711 /* propagate shutdown SHUT_WR to backend if TCP half-close on con->fd */ 1712 con->conf.stream_request_body |= FDEVENT_STREAM_REQUEST_BACKEND_SHUT_WR; 1713 con->conf.stream_request_body &= ~FDEVENT_STREAM_REQUEST_POLLIN; 1714 con->is_readable = 0; 1715 shutdown(hctx->fd, SHUT_WR); 1716 fdevent_fdnode_event_clr(srv->ev, hctx->fdn, FDEVENT_OUT); 1717 } 1718 1719 static handler_t gw_write_request(server *srv, gw_handler_ctx *hctx) { 1720 switch(hctx->state) { 1721 case GW_STATE_INIT: 1722 /* do we have a running process for this host (max-procs) ? */ 1723 hctx->proc = NULL; 1724 1725 for (gw_proc *proc = hctx->host->first; proc; proc = proc->next) { 1726 if (proc->state == PROC_STATE_RUNNING) { 1727 hctx->proc = proc; 1728 break; 1729 } 1730 } 1731 1732 /* all children are dead */ 1733 if (hctx->proc == NULL) { 1734 return HANDLER_ERROR; 1735 } 1736 1737 /* check the other procs if they have a lower load */ 1738 for (gw_proc *proc = hctx->proc->next; proc; proc = proc->next) { 1739 if (proc->state != PROC_STATE_RUNNING) continue; 1740 if (proc->load < hctx->proc->load) hctx->proc = proc; 1741 } 1742 1743 gw_proc_load_inc(srv, hctx->host, hctx->proc); 1744 1745 hctx->fd = fdevent_socket_nb_cloexec(hctx->host->family,SOCK_STREAM,0); 1746 if (-1 == hctx->fd) { 1747 if (errno == EMFILE || errno == EINTR) { 1748 log_error_write(srv, __FILE__, __LINE__, "sd", 1749 "wait for fd at connection:", 1750 hctx->remote_conn->fd); 1751 return HANDLER_WAIT_FOR_FD; 1752 } 1753 1754 log_error_write(srv, __FILE__, __LINE__, "ssdd", 1755 "socket failed:", strerror(errno), 1756 srv->cur_fds, srv->max_fds); 1757 return HANDLER_ERROR; 1758 } 1759 1760 srv->cur_fds++; 1761 1762 hctx->fdn = fdevent_register(srv->ev,hctx->fd,gw_handle_fdevent,hctx); 1763 1764 if (hctx->proc->is_local) { 1765 hctx->pid = hctx->proc->pid; 1766 } 1767 1768 switch (gw_establish_connection(srv, hctx->host, hctx->proc, hctx->pid, 1769 hctx->fd, hctx->conf.debug)) { 1770 case 1: /* connection is in progress */ 1771 fdevent_fdnode_event_set(srv->ev, hctx->fdn, FDEVENT_OUT); 1772 gw_set_state(srv, hctx, GW_STATE_CONNECT_DELAYED); 1773 return HANDLER_WAIT_FOR_EVENT; 1774 case -1:/* connection error */ 1775 return HANDLER_ERROR; 1776 case 0: /* everything is ok, go on */ 1777 hctx->reconnects = 0; 1778 break; 1779 } 1780 /* fall through */ 1781 case GW_STATE_CONNECT_DELAYED: 1782 if (hctx->state == GW_STATE_CONNECT_DELAYED) { /*(not GW_STATE_INIT)*/ 1783 int socket_error = fdevent_connect_status(hctx->fd); 1784 if (socket_error != 0) { 1785 gw_proc_connect_error(srv, hctx->host, hctx->proc, hctx->pid, 1786 socket_error, hctx->conf.debug); 1787 return HANDLER_ERROR; 1788 } 1789 /* go on with preparing the request */ 1790 } 1791 1792 gw_proc_connect_success(srv, hctx->host, hctx->proc, hctx->conf.debug); 1793 1794 gw_set_state(srv, hctx, GW_STATE_PREPARE_WRITE); 1795 /* fall through */ 1796 case GW_STATE_PREPARE_WRITE: 1797 /* ok, we have the connection */ 1798 1799 { 1800 handler_t rc = hctx->create_env(srv, hctx); 1801 if (HANDLER_GO_ON != rc) { 1802 if (HANDLER_FINISHED != rc && HANDLER_ERROR != rc) 1803 fdevent_fdnode_event_clr(srv->ev, hctx->fdn, FDEVENT_OUT); 1804 return rc; 1805 } 1806 } 1807 1808 /*(disable Nagle algorithm if streaming and content-length unknown)*/ 1809 if (AF_UNIX != hctx->host->family) { 1810 connection *con = hctx->remote_conn; 1811 if (con->request.content_length < 0) { 1812 if (-1 == fdevent_set_tcp_nodelay(hctx->fd, 1)) { 1813 /*(error, but not critical)*/ 1814 } 1815 } 1816 } 1817 1818 fdevent_fdnode_event_add(srv->ev, hctx->fdn, FDEVENT_IN|FDEVENT_RDHUP); 1819 gw_set_state(srv, hctx, GW_STATE_WRITE); 1820 /* fall through */ 1821 case GW_STATE_WRITE: 1822 if (!chunkqueue_is_empty(hctx->wb)) { 1823 int ret; 1824 #if 0 1825 if (hctx->conf.debug > 1) { 1826 log_error_write(srv, __FILE__, __LINE__, "sdsx", 1827 "send data to backend ( fd =", hctx->fd, 1828 "), size =", chunkqueue_length(hctx->wb)); 1829 } 1830 #endif 1831 ret = srv->network_backend_write(srv, hctx->fd, hctx->wb, 1832 MAX_WRITE_LIMIT); 1833 1834 chunkqueue_remove_finished_chunks(hctx->wb); 1835 1836 if (ret < 0) { 1837 switch(errno) { 1838 case EPIPE: 1839 case ENOTCONN: 1840 case ECONNRESET: 1841 /* the connection got dropped after accept() 1842 * we don't care about that -- 1843 * if you accept() it, you have to handle it. 1844 */ 1845 log_error_write(srv, __FILE__, __LINE__, "ssosb", 1846 "connection was dropped after accept() " 1847 "(perhaps the gw process died),", 1848 "write-offset:", hctx->wb->bytes_out, 1849 "socket:", hctx->proc->connection_name); 1850 return HANDLER_ERROR; 1851 default: 1852 log_error_write(srv, __FILE__, __LINE__, "ssd", 1853 "write failed:", strerror(errno), errno); 1854 return HANDLER_ERROR; 1855 } 1856 } 1857 } 1858 1859 if (hctx->wb->bytes_out == hctx->wb_reqlen) { 1860 fdevent_fdnode_event_clr(srv->ev, hctx->fdn, FDEVENT_OUT); 1861 gw_set_state(srv, hctx, GW_STATE_READ); 1862 } else { 1863 off_t wblen = hctx->wb->bytes_in - hctx->wb->bytes_out; 1864 if ((hctx->wb->bytes_in < hctx->wb_reqlen || hctx->wb_reqlen < 0) 1865 && wblen < 65536 - 16384) { 1866 connection *con = hctx->remote_conn; 1867 /*(con->conf.stream_request_body & FDEVENT_STREAM_REQUEST)*/ 1868 if (!(con->conf.stream_request_body 1869 & FDEVENT_STREAM_REQUEST_POLLIN)) { 1870 con->conf.stream_request_body |= 1871 FDEVENT_STREAM_REQUEST_POLLIN; 1872 con->is_readable = 1;/*trigger optimistic read from client*/ 1873 } 1874 } 1875 if (0 == wblen) { 1876 fdevent_fdnode_event_clr(srv->ev, hctx->fdn, FDEVENT_OUT); 1877 } else { 1878 fdevent_fdnode_event_add(srv->ev, hctx->fdn, FDEVENT_OUT); 1879 } 1880 } 1881 1882 if (hctx->remote_conn->conf.stream_request_body 1883 & FDEVENT_STREAM_REQUEST_TCP_FIN) 1884 gw_conditional_tcp_fin(srv, hctx); 1885 1886 return HANDLER_WAIT_FOR_EVENT; 1887 case GW_STATE_READ: 1888 /* waiting for a response */ 1889 return HANDLER_WAIT_FOR_EVENT; 1890 default: 1891 log_error_write(srv, __FILE__, __LINE__, "s", "(debug) unknown state"); 1892 return HANDLER_ERROR; 1893 } 1894 } 1895 1896 static handler_t gw_write_error(server *srv, gw_handler_ctx *hctx) { 1897 connection *con = hctx->remote_conn; 1898 int status = con->http_status; 1899 1900 if (hctx->state == GW_STATE_INIT || 1901 hctx->state == GW_STATE_CONNECT_DELAYED) { 1902 1903 /* (optimization to detect backend process exit while processing a 1904 * large number of ready events; (this block could be removed)) */ 1905 if (0 == srv->srvconf.max_worker) 1906 gw_restart_dead_procs(srv, hctx->host, hctx->conf.debug, 0); 1907 1908 /* cleanup this request and let request handler start request again */ 1909 if (hctx->reconnects++ < 5) return gw_reconnect(srv, hctx); 1910 } 1911 1912 if (hctx->backend_error) hctx->backend_error(hctx); 1913 gw_connection_close(srv, hctx); 1914 con->http_status = (status == 400) ? 400 : 503; 1915 return HANDLER_FINISHED; 1916 } 1917 1918 static handler_t gw_send_request(server *srv, gw_handler_ctx *hctx) { 1919 handler_t rc = gw_write_request(srv, hctx); 1920 return (HANDLER_ERROR != rc) ? rc : gw_write_error(srv, hctx); 1921 } 1922 1923 1924 static handler_t gw_recv_response(server *srv, gw_handler_ctx *hctx); 1925 1926 1927 handler_t gw_handle_subrequest(server *srv, connection *con, void *p_d) { 1928 gw_plugin_data *p = p_d; 1929 gw_handler_ctx *hctx = con->plugin_ctx[p->id]; 1930 if (NULL == hctx) return HANDLER_GO_ON; 1931 if (con->mode != p->id) return HANDLER_GO_ON; /* not my job */ 1932 1933 if ((con->conf.stream_response_body & FDEVENT_STREAM_RESPONSE_BUFMIN) 1934 && con->file_started) { 1935 if (chunkqueue_length(con->write_queue) > 65536 - 4096) { 1936 fdevent_fdnode_event_clr(srv->ev, hctx->fdn, FDEVENT_IN); 1937 } 1938 else if (!(fdevent_fdnode_interest(hctx->fdn) & FDEVENT_IN)) { 1939 /* optimistic read from backend */ 1940 handler_t rc; 1941 rc = gw_recv_response(srv, hctx); /*(might invalidate hctx)*/ 1942 if (rc != HANDLER_GO_ON) return rc; /*(unless HANDLER_GO_ON)*/ 1943 fdevent_fdnode_event_add(srv->ev, hctx->fdn, FDEVENT_IN); 1944 } 1945 } 1946 1947 /* (do not receive request body before GW_AUTHORIZER has run or else 1948 * the request body is discarded with handler_ctx_clear() after running 1949 * the FastCGI Authorizer) */ 1950 1951 if (hctx->gw_mode != GW_AUTHORIZER 1952 && (0 == hctx->wb->bytes_in 1953 ? (con->state == CON_STATE_READ_POST || -1 == hctx->wb_reqlen) 1954 : (hctx->wb->bytes_in < hctx->wb_reqlen || hctx->wb_reqlen < 0))) { 1955 /* leave excess data in con->request_content_queue, which is 1956 * buffered to disk if too large and backend can not keep up */ 1957 /*(64k - 4k to attempt to avoid temporary files 1958 * in conjunction with FDEVENT_STREAM_REQUEST_BUFMIN)*/ 1959 if (hctx->wb->bytes_in - hctx->wb->bytes_out > 65536 - 4096) { 1960 if (con->conf.stream_request_body & FDEVENT_STREAM_REQUEST_BUFMIN) { 1961 con->conf.stream_request_body &= ~FDEVENT_STREAM_REQUEST_POLLIN; 1962 } 1963 if (0 != hctx->wb->bytes_in) return HANDLER_WAIT_FOR_EVENT; 1964 } 1965 else { 1966 handler_t r = connection_handle_read_post_state(srv, con); 1967 chunkqueue *req_cq = con->request_content_queue; 1968 #if 0 /*(not reached since we send 411 Length Required below)*/ 1969 if (hctx->wb_reqlen < -1 && con->request.content_length >= 0) { 1970 /* (completed receiving Transfer-Encoding: chunked) */ 1971 hctx->wb_reqlen= -hctx->wb_reqlen + con->request.content_length; 1972 if (hctx->stdin_append) { 1973 handler_t rc = hctx->stdin_append(srv, hctx); 1974 if (HANDLER_GO_ON != rc) return rc; 1975 } 1976 } 1977 #endif 1978 if ((0 != hctx->wb->bytes_in || -1 == hctx->wb_reqlen) 1979 && !chunkqueue_is_empty(req_cq)) { 1980 if (hctx->stdin_append) { 1981 handler_t rc = hctx->stdin_append(srv, hctx); 1982 if (HANDLER_GO_ON != rc) return rc; 1983 } 1984 else 1985 chunkqueue_append_chunkqueue(hctx->wb, req_cq); 1986 if (fdevent_fdnode_interest(hctx->fdn) & FDEVENT_OUT) { 1987 return (r == HANDLER_GO_ON) ? HANDLER_WAIT_FOR_EVENT : r; 1988 } 1989 } 1990 if (r != HANDLER_GO_ON) return r; 1991 1992 1993 /* XXX: create configurable flag */ 1994 /* CGI environment requires that Content-Length be set. 1995 * Send 411 Length Required if Content-Length missing. 1996 * (occurs here if client sends Transfer-Encoding: chunked 1997 * and module is flagged to stream request body to backend) */ 1998 /* proxy currently sends HTTP/1.0 request and ideally should send 1999 * Content-Length with request if request body is present, so 2000 * send 411 Length Required if Content-Length missing. */ 2001 if (-1 == con->request.content_length) { 2002 return connection_handle_read_post_error(srv, con, 411); 2003 } 2004 } 2005 } 2006 2007 { 2008 handler_t rc =((0==hctx->wb->bytes_in || !chunkqueue_is_empty(hctx->wb)) 2009 && hctx->state != GW_STATE_CONNECT_DELAYED) 2010 ? gw_send_request(srv, hctx) 2011 : HANDLER_WAIT_FOR_EVENT; 2012 if (HANDLER_WAIT_FOR_EVENT != rc) return rc; 2013 } 2014 2015 if (con->conf.stream_request_body & FDEVENT_STREAM_REQUEST_TCP_FIN) 2016 gw_conditional_tcp_fin(srv, hctx); 2017 2018 return HANDLER_WAIT_FOR_EVENT; 2019 } 2020 2021 2022 static handler_t gw_recv_response(server *srv, gw_handler_ctx *hctx) { 2023 connection *con = hctx->remote_conn; 2024 gw_proc *proc = hctx->proc; 2025 gw_host *host = hctx->host; 2026 /*(XXX: make this a configurable flag for other protocols)*/ 2027 buffer *b = hctx->opts.backend == BACKEND_FASTCGI 2028 ? chunk_buffer_acquire() 2029 : hctx->response; 2030 2031 handler_t rc = 2032 http_response_read(srv, hctx->remote_conn, &hctx->opts, b, hctx->fdn); 2033 2034 if (b != hctx->response) chunk_buffer_release(b); 2035 2036 switch (rc) { 2037 default: 2038 return HANDLER_GO_ON; 2039 case HANDLER_FINISHED: 2040 if (hctx->gw_mode == GW_AUTHORIZER 2041 && (200 == con->http_status || 0 == con->http_status)) { 2042 /* 2043 * If we are here in AUTHORIZER mode then a request for authorizer 2044 * was processed already, and status 200 has been returned. We need 2045 * now to handle authorized request. 2046 */ 2047 buffer *physpath = NULL; 2048 2049 if (!buffer_string_is_empty(host->docroot)) { 2050 buffer_copy_buffer(con->physical.doc_root, host->docroot); 2051 buffer_copy_buffer(con->physical.basedir, host->docroot); 2052 2053 buffer_copy_buffer(con->physical.path, host->docroot); 2054 buffer_append_string_buffer(con->physical.path, con->uri.path); 2055 physpath = con->physical.path; 2056 } 2057 2058 proc->last_used = srv->cur_ts; 2059 gw_backend_close(srv, hctx); 2060 handler_ctx_clear(hctx); 2061 2062 /* don't do more than 6 loops here; normally shouldn't happen */ 2063 if (++con->loops_per_request > 5) { 2064 log_error_write(srv, __FILE__, __LINE__, "sb", 2065 "too many loops while processing request:", 2066 con->request.orig_uri); 2067 con->http_status = 500; /* Internal Server Error */ 2068 con->mode = DIRECT; 2069 return HANDLER_FINISHED; 2070 } 2071 2072 /* restart the request so other handlers can process it */ 2073 2074 if (physpath) con->physical.path = NULL; 2075 connection_response_reset(srv,con);/*(includes con->http_status=0)*/ 2076 /* preserve con->physical.path with modified docroot */ 2077 if (physpath) con->physical.path = physpath; 2078 2079 /*(FYI: if multiple FastCGI authorizers were to be supported, 2080 * next one could be started here instead of restarting request)*/ 2081 2082 con->mode = DIRECT; 2083 return HANDLER_COMEBACK; 2084 } else { 2085 /* we are done */ 2086 gw_connection_close(srv, hctx); 2087 } 2088 2089 return HANDLER_FINISHED; 2090 case HANDLER_COMEBACK: /*(not expected; treat as error)*/ 2091 case HANDLER_ERROR: 2092 /* (optimization to detect backend process exit while processing a 2093 * large number of ready events; (this block could be removed)) */ 2094 if (proc->is_local && 1 == proc->load && proc->pid == hctx->pid 2095 && proc->state != PROC_STATE_DIED && 0 == srv->srvconf.max_worker) { 2096 /* intentionally check proc->disabed_until before gw_proc_waitpid */ 2097 if (proc->disabled_until < srv->cur_ts 2098 && 0 != gw_proc_waitpid(srv, host, proc)) { 2099 if (hctx->conf.debug) { 2100 log_error_write(srv, __FILE__, __LINE__, "ssbsdsd", 2101 "--- gw spawning", 2102 "\n\tsocket", proc->connection_name, 2103 "\n\tcurrent:", 1, "/", host->num_procs); 2104 } 2105 2106 if (gw_spawn_connection(srv, host, proc, hctx->conf.debug)) { 2107 log_error_write(srv, __FILE__, __LINE__, "s", 2108 "respawning failed, will retry later"); 2109 } 2110 } 2111 } 2112 2113 if (con->file_started == 0) { 2114 /* nothing has been sent out yet, try to use another child */ 2115 2116 if (hctx->wb->bytes_out == 0 && 2117 hctx->reconnects++ < 5) { 2118 2119 log_error_write(srv, __FILE__, __LINE__, "ssbsBSBs", 2120 "response not received, request not sent", 2121 "on socket:", proc->connection_name, 2122 "for", con->uri.path, "?", con->uri.query, ", reconnecting"); 2123 2124 return gw_reconnect(srv, hctx); 2125 } 2126 2127 log_error_write(srv, __FILE__, __LINE__, "sosbsBSBs", 2128 "response not received, request sent:", hctx->wb->bytes_out, 2129 "on socket:", proc->connection_name, "for", 2130 con->uri.path, "?", con->uri.query, ", closing connection"); 2131 } else { 2132 log_error_write(srv, __FILE__, __LINE__, "ssbsBSBs", 2133 "response already sent out, but backend returned error", 2134 "on socket:", proc->connection_name, "for", 2135 con->uri.path, "?", con->uri.query, ", terminating connection"); 2136 } 2137 2138 if (hctx->backend_error) hctx->backend_error(hctx); 2139 http_response_backend_error(srv, con); 2140 gw_connection_close(srv, hctx); 2141 return HANDLER_FINISHED; 2142 } 2143 } 2144 2145 2146 static handler_t gw_handle_fdevent(server *srv, void *ctx, int revents) { 2147 gw_handler_ctx *hctx = ctx; 2148 connection *con = hctx->remote_conn; 2149 2150 joblist_append(srv, con); 2151 2152 if (revents & FDEVENT_IN) { 2153 handler_t rc = gw_recv_response(srv, hctx); /*(might invalidate hctx)*/ 2154 if (rc != HANDLER_GO_ON) return rc; /*(unless HANDLER_GO_ON)*/ 2155 } 2156 2157 if (revents & FDEVENT_OUT) { 2158 return gw_send_request(srv, hctx); /*(might invalidate hctx)*/ 2159 } 2160 2161 /* perhaps this issue is already handled */ 2162 if (revents & (FDEVENT_HUP|FDEVENT_RDHUP)) { 2163 if (hctx->state == GW_STATE_CONNECT_DELAYED) { 2164 /* getoptsock will catch this one (right ?) 2165 * 2166 * if we are in connect we might get an EINPROGRESS 2167 * in the first call and an FDEVENT_HUP in the 2168 * second round 2169 * 2170 * FIXME: as it is a bit ugly. 2171 * 2172 */ 2173 gw_send_request(srv, hctx); 2174 } else if (con->file_started) { 2175 /* drain any remaining data from kernel pipe buffers 2176 * even if (con->conf.stream_response_body 2177 * & FDEVENT_STREAM_RESPONSE_BUFMIN) 2178 * since event loop will spin on fd FDEVENT_HUP event 2179 * until unregistered. */ 2180 handler_t rc; 2181 const unsigned short flags = con->conf.stream_response_body; 2182 con->conf.stream_response_body &= ~FDEVENT_STREAM_RESPONSE_BUFMIN; 2183 con->conf.stream_response_body |= FDEVENT_STREAM_RESPONSE_POLLRDHUP; 2184 do { 2185 rc = gw_recv_response(srv,hctx); /*(might invalidate hctx)*/ 2186 } while (rc == HANDLER_GO_ON); /*(unless HANDLER_GO_ON)*/ 2187 con->conf.stream_response_body = flags; 2188 return rc; /* HANDLER_FINISHED or HANDLER_ERROR */ 2189 } else { 2190 gw_proc *proc = hctx->proc; 2191 log_error_write(srv, __FILE__, __LINE__, "sBSbsbsd", 2192 "error: unexpected close of gw connection for", 2193 con->uri.path, "?", con->uri.query, 2194 "(no gw process on socket:", proc->connection_name, "?)", 2195 hctx->state); 2196 2197 gw_connection_close(srv, hctx); 2198 } 2199 } else if (revents & FDEVENT_ERR) { 2200 log_error_write(srv, __FILE__, __LINE__, "s", 2201 "gw: got a FDEVENT_ERR. Don't know why."); 2202 2203 if (hctx->backend_error) hctx->backend_error(hctx); 2204 http_response_backend_error(srv, con); 2205 gw_connection_close(srv, hctx); 2206 } 2207 2208 return HANDLER_FINISHED; 2209 } 2210 2211 handler_t gw_check_extension(server *srv, connection *con, gw_plugin_data *p, int uri_path_handler, size_t hctx_sz) { 2212 #if 0 /*(caller must handle)*/ 2213 if (con->mode != DIRECT) return HANDLER_GO_ON; 2214 gw_patch_connection(srv, con, p); 2215 if (NULL == p->conf.exts) return HANDLER_GO_ON; 2216 #endif 2217 2218 buffer *fn = uri_path_handler ? con->uri.path : con->physical.path; 2219 size_t s_len = buffer_string_length(fn); 2220 gw_extension *extension = NULL; 2221 gw_host *host = NULL; 2222 gw_handler_ctx *hctx; 2223 unsigned short gw_mode; 2224 2225 if (0 == s_len) return HANDLER_GO_ON; /*(not expected)*/ 2226 2227 /* check p->conf.exts_auth list and then p->conf.ext_resp list 2228 * (skip p->conf.exts_auth if array is empty 2229 * or if GW_AUTHORIZER already ran in this request) */ 2230 hctx = con->plugin_ctx[p->id]; 2231 /*(hctx not NULL if GW_AUTHORIZER ran; hctx->ext_auth check is redundant)*/ 2232 gw_mode = (NULL == hctx || NULL == hctx->ext_auth) 2233 ? 0 /*GW_AUTHORIZER p->conf.exts_auth will be searched next*/ 2234 : GW_AUTHORIZER; /*GW_RESPONDER p->conf.exts_resp will be searched next*/ 2235 2236 do { 2237 2238 gw_exts *exts; 2239 if (0 == gw_mode) { 2240 gw_mode = GW_AUTHORIZER; 2241 exts = p->conf.exts_auth; 2242 } else { 2243 gw_mode = GW_RESPONDER; 2244 exts = p->conf.exts_resp; 2245 } 2246 2247 if (0 == exts->used) continue; 2248 2249 /* gw.map-extensions maps extensions to existing gw.server entries 2250 * 2251 * gw.map-extensions = ( ".php3" => ".php" ) 2252 * 2253 * gw.server = ( ".php" => ... ) 2254 * 2255 * */ 2256 2257 /* check if extension-mapping matches */ 2258 if (p->conf.ext_mapping) { 2259 data_string *ds = 2260 (data_string *)array_match_key_suffix(p->conf.ext_mapping, fn); 2261 if (NULL != ds) { 2262 /* found a mapping */ 2263 /* check if we know the extension */ 2264 size_t k; 2265 for (k = 0; k < exts->used; ++k) { 2266 extension = exts->exts[k]; 2267 2268 if (buffer_is_equal(ds->value, extension->key)) { 2269 break; 2270 } 2271 } 2272 2273 if (k == exts->used) { 2274 /* found nothing */ 2275 extension = NULL; 2276 } 2277 } 2278 } 2279 2280 if (extension == NULL) { 2281 size_t uri_path_len = buffer_string_length(con->uri.path); 2282 2283 /* check if extension matches */ 2284 for (size_t k = 0; k < exts->used; ++k) { 2285 gw_extension *ext = exts->exts[k]; 2286 size_t ct_len = buffer_string_length(ext->key); 2287 2288 /* check _url_ in the form "/gw_pattern" */ 2289 if (ext->key->ptr[0] == '/') { 2290 if (ct_len <= uri_path_len 2291 && 0==memcmp(con->uri.path->ptr,ext->key->ptr,ct_len)){ 2292 extension = ext; 2293 break; 2294 } 2295 } else if (ct_len <= s_len 2296 && 0 == memcmp(fn->ptr + s_len - ct_len, 2297 ext->key->ptr, ct_len)) { 2298 /* check extension in the form ".fcg" */ 2299 extension = ext; 2300 break; 2301 } 2302 } 2303 } 2304 2305 } while (NULL == extension && gw_mode != GW_RESPONDER); 2306 2307 /* extension doesn't match */ 2308 if (NULL == extension) { 2309 return HANDLER_GO_ON; 2310 } 2311 2312 /* check if we have at least one server for this extension up and running */ 2313 host = gw_host_get(srv, con, extension, p->conf.balance, p->conf.debug); 2314 if (NULL == host) { 2315 return HANDLER_FINISHED; 2316 } 2317 2318 /* a note about no handler is not sent yet */ 2319 extension->note_is_sent = 0; 2320 2321 /* 2322 * if check-local is disabled, use the uri.path handler 2323 * 2324 */ 2325 2326 /* init handler-context */ 2327 if (uri_path_handler) { 2328 if (host->check_local != 0) { 2329 return HANDLER_GO_ON; 2330 } else { 2331 /* do not split path info for authorizer */ 2332 if (gw_mode != GW_AUTHORIZER) { 2333 /* the prefix is the SCRIPT_NAME, 2334 * everything from start to the next slash 2335 * this is important for check-local = "disable" 2336 * 2337 * if prefix = /admin.gw 2338 * 2339 * /admin.gw/foo/bar 2340 * 2341 * SCRIPT_NAME = /admin.gw 2342 * PATH_INFO = /foo/bar 2343 * 2344 * if prefix = /cgi-bin/ 2345 * 2346 * /cgi-bin/foo/bar 2347 * 2348 * SCRIPT_NAME = /cgi-bin/foo 2349 * PATH_INFO = /bar 2350 * 2351 * if prefix = /, and fix-root-path-name is enable 2352 * 2353 * /cgi-bin/foo/bar 2354 * 2355 * SCRIPT_NAME = /cgi-bin/foo 2356 * PATH_INFO = /bar 2357 * 2358 */ 2359 char *pathinfo; 2360 2361 /* the rewrite is only done for /prefix/? matches */ 2362 if (host->fix_root_path_name && extension->key->ptr[0] == '/' 2363 && extension->key->ptr[1] == '\0'){ 2364 buffer_copy_buffer(con->request.pathinfo, con->uri.path); 2365 buffer_clear(con->uri.path); 2366 } else if (extension->key->ptr[0] == '/' 2367 && buffer_string_length(con->uri.path) 2368 > buffer_string_length(extension->key) 2369 && (pathinfo = 2370 strchr(con->uri.path->ptr 2371 + buffer_string_length(extension->key), 2372 '/')) != NULL) { 2373 /* rewrite uri.path and pathinfo */ 2374 2375 buffer_copy_string(con->request.pathinfo, pathinfo); 2376 buffer_string_set_length( 2377 con->uri.path, 2378 buffer_string_length(con->uri.path) 2379 - buffer_string_length(con->request.pathinfo)); 2380 } 2381 } 2382 } 2383 } 2384 2385 if (!hctx) hctx = handler_ctx_init(hctx_sz); 2386 2387 hctx->remote_conn = con; 2388 hctx->plugin_data = p; 2389 hctx->host = host; 2390 hctx->proc = NULL; 2391 hctx->ext = extension; 2392 gw_host_assign(srv, host); 2393 2394 hctx->gw_mode = gw_mode; 2395 if (gw_mode == GW_AUTHORIZER) { 2396 hctx->ext_auth = hctx->ext; 2397 } 2398 2399 /*hctx->conf.exts = p->conf.exts;*/ 2400 /*hctx->conf.exts_auth = p->conf.exts_auth;*/ 2401 /*hctx->conf.exts_resp = p->conf.exts_resp;*/ 2402 /*hctx->conf.ext_mapping = p->conf.ext_mapping;*/ 2403 hctx->conf.balance = p->conf.balance; 2404 hctx->conf.proto = p->conf.proto; 2405 hctx->conf.debug = p->conf.debug; 2406 2407 hctx->opts.fdfmt = S_IFSOCK; 2408 hctx->opts.authorizer = (gw_mode == GW_AUTHORIZER); 2409 hctx->opts.local_redir = 0; 2410 hctx->opts.xsendfile_allow = host->xsendfile_allow; 2411 hctx->opts.xsendfile_docroot = host->xsendfile_docroot; 2412 2413 con->plugin_ctx[p->id] = hctx; 2414 2415 con->mode = p->id; 2416 2417 if (con->conf.log_request_handling) { 2418 log_error_write(srv, __FILE__, __LINE__, "s", "handling it in mod_gw"); 2419 } 2420 2421 return HANDLER_GO_ON; 2422 } 2423 2424 static void gw_handle_trigger_host(server *srv, gw_host *host, int debug) { 2425 /* 2426 * TODO: 2427 * 2428 * - add timeout for a connect to a non-gw process 2429 * (use state_timestamp + state) 2430 * 2431 * perhaps we should kill a connect attempt after 10-15 seconds 2432 * 2433 * currently we wait for the TCP timeout which is 180 seconds on Linux 2434 */ 2435 2436 /* check each child proc to detect if proc exited */ 2437 2438 gw_proc *proc; 2439 time_t idle_timestamp; 2440 int overload = 1; 2441 2442 for (proc = host->first; proc; proc = proc->next) { 2443 gw_proc_waitpid(srv, host, proc); 2444 } 2445 2446 gw_restart_dead_procs(srv, host, debug, 1); 2447 2448 /* check if adaptive spawning enabled */ 2449 if (host->min_procs == host->max_procs) return; 2450 if (buffer_string_is_empty(host->bin_path)) return; 2451 2452 for (proc = host->first; proc; proc = proc->next) { 2453 if (proc->load <= host->max_load_per_proc) { 2454 overload = 0; 2455 break; 2456 } 2457 } 2458 2459 if (overload && host->num_procs && host->num_procs < host->max_procs) { 2460 /* overload, spawn new child */ 2461 if (debug) { 2462 log_error_write(srv, __FILE__, __LINE__, "s", 2463 "overload detected, spawning a new child"); 2464 } 2465 2466 gw_proc_spawn(srv, host, debug); 2467 } 2468 2469 idle_timestamp = srv->cur_ts - host->idle_timeout; 2470 for (proc = host->first; proc; proc = proc->next) { 2471 if (host->num_procs <= host->min_procs) break; 2472 if (0 != proc->load) continue; 2473 if (proc->pid <= 0) continue; 2474 if (proc->last_used >= idle_timestamp) continue; 2475 2476 /* terminate proc that has been idling for a long time */ 2477 if (debug) { 2478 log_error_write(srv, __FILE__, __LINE__, "ssbsd", 2479 "idle-timeout reached, terminating child:", 2480 "socket:", proc->unixsocket, "pid", proc->pid); 2481 } 2482 2483 gw_proc_kill(srv, host, proc); 2484 2485 /* proc is now in unused, let next second handle next process */ 2486 break; 2487 } 2488 2489 for (proc = host->unused_procs; proc; proc = proc->next) { 2490 gw_proc_waitpid(srv, host, proc); 2491 } 2492 } 2493 2494 static void gw_handle_trigger_exts(server *srv, gw_exts *exts, int debug) { 2495 for (size_t j = 0; j < exts->used; ++j) { 2496 gw_extension *ex = exts->exts[j]; 2497 for (size_t n = 0; n < ex->used; ++n) { 2498 gw_handle_trigger_host(srv, ex->hosts[n], debug); 2499 } 2500 } 2501 } 2502 2503 static void gw_handle_trigger_exts_wkr(server *srv, gw_exts *exts) { 2504 for (size_t j = 0; j < exts->used; ++j) { 2505 gw_extension * const ex = exts->exts[j]; 2506 for (size_t n = 0; n < ex->used; ++n) { 2507 gw_host * const host = ex->hosts[n]; 2508 for (gw_proc *proc = host->first; proc; proc = proc->next) { 2509 if (proc->state == PROC_STATE_OVERLOADED) 2510 gw_proc_check_enable(srv, host, proc); 2511 } 2512 } 2513 } 2514 } 2515 2516 handler_t gw_handle_trigger(server *srv, void *p_d) { 2517 gw_plugin_data *p = p_d; 2518 int wkr = (0 != srv->srvconf.max_worker && p->srv_pid != srv->pid); 2519 2520 for (size_t i = 0; i < srv->config_context->used; i++) { 2521 gw_plugin_config *conf = p->config_storage[i]; 2522 gw_exts *exts = conf->exts; 2523 int debug = conf->debug ? conf->debug : p->config_storage[0]->debug; 2524 if (NULL == exts) continue; 2525 wkr 2526 ? gw_handle_trigger_exts_wkr(srv, exts) 2527 : gw_handle_trigger_exts(srv, exts, debug); 2528 } 2529 2530 return HANDLER_GO_ON; 2531 } 2532 2533 handler_t gw_handle_waitpid_cb(server *srv, void *p_d, pid_t pid, int status) { 2534 gw_plugin_data *p = p_d; 2535 if (0 != srv->srvconf.max_worker && p->srv_pid != srv->pid) 2536 return HANDLER_GO_ON; 2537 2538 for (size_t i = 0; i < srv->config_context->used; ++i) { 2539 gw_plugin_config *conf = p->config_storage[i]; 2540 gw_exts *exts = conf->exts; 2541 int debug = conf->debug ? conf->debug : p->config_storage[0]->debug; 2542 if (NULL == exts) continue; 2543 for (size_t j = 0; j < exts->used; ++j) { 2544 gw_extension *ex = exts->exts[j]; 2545 for (size_t n = 0; n < ex->used; ++n) { 2546 gw_host *host = ex->hosts[n]; 2547 gw_proc *proc; 2548 for (proc = host->first; proc; proc = proc->next) { 2549 if (!proc->is_local || proc->pid != pid) continue; 2550 2551 gw_proc_waitpid_log(srv, host, proc, status); 2552 gw_proc_set_state(host, proc, PROC_STATE_DIED); 2553 proc->pid = 0; 2554 2555 /* restart, but avoid spinning if child exits too quickly */ 2556 if (proc->disabled_until < srv->cur_ts) { 2557 if (proc->state != PROC_STATE_KILLED) 2558 proc->disabled_until = srv->cur_ts; 2559 if (gw_spawn_connection(srv, host, proc, debug)) { 2560 log_error_write(srv, __FILE__, __LINE__, "s", 2561 "ERROR: spawning gw failed."); 2562 } 2563 } 2564 2565 return HANDLER_FINISHED; 2566 } 2567 for (proc = host->unused_procs; proc; proc = proc->next) { 2568 if (!proc->is_local || proc->pid != pid) continue; 2569 2570 gw_proc_waitpid_log(srv, host, proc, status); 2571 if (proc->state != PROC_STATE_KILLED) 2572 proc->disabled_until = srv->cur_ts; 2573 gw_proc_set_state(host, proc, PROC_STATE_DIED); 2574 proc->pid = 0; 2575 return HANDLER_FINISHED; 2576 } 2577 } 2578 } 2579 } 2580 2581 return HANDLER_GO_ON; 2582 } 2583