1/* Licensed to the Apache Software Foundation (ASF) under one or more 2 * contributor license agreements. See the NOTICE file distributed with 3 * this work for additional information regarding copyright ownership. 4 * The ASF licenses this file to You under the Apache License, Version 2.0 5 * (the "License"); you may not use this file except in compliance with 6 * the License. You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17/* Load balancer module for Apache proxy */ 18 19#include "mod_proxy.h" 20#include "scoreboard.h" 21#include "ap_mpm.h" 22#include "apr_version.h" 23#include "ap_hooks.h" 24#include "apr_date.h" 25 26static const char *balancer_mutex_type = "proxy-balancer-shm"; 27ap_slotmem_provider_t *storage = NULL; 28 29module AP_MODULE_DECLARE_DATA proxy_balancer_module; 30 31static int (*ap_proxy_retry_worker_fn)(const char *proxy_function, 32 proxy_worker *worker, server_rec *s) = NULL; 33 34/* 35 * Register our mutex type before the config is read so we 36 * can adjust the mutex settings using the Mutex directive. 37 */ 38static int balancer_pre_config(apr_pool_t *pconf, apr_pool_t *plog, 39 apr_pool_t *ptemp) 40{ 41 42 apr_status_t rv; 43 44 rv = ap_mutex_register(pconf, balancer_mutex_type, NULL, 45 APR_LOCK_DEFAULT, 0); 46 if (rv != APR_SUCCESS) { 47 return rv; 48 } 49 50 return OK; 51} 52 53#if 0 54extern void proxy_update_members(proxy_balancer **balancer, request_rec *r, 55 proxy_server_conf *conf); 56#endif 57 58static int proxy_balancer_canon(request_rec *r, char *url) 59{ 60 char *host, *path; 61 char *search = NULL; 62 const char *err; 63 apr_port_t port = 0; 64 65 /* TODO: offset of BALANCER_PREFIX ?? */ 66 if (strncasecmp(url, "balancer:", 9) == 0) { 67 url += 9; 68 } 69 else { 70 return DECLINED; 71 } 72 73 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "canonicalising URL %s", url); 74 75 /* do syntatic check. 76 * We break the URL into host, port, path, search 77 */ 78 err = ap_proxy_canon_netloc(r->pool, &url, NULL, NULL, &host, &port); 79 if (err) { 80 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01157) 81 "error parsing URL %s: %s", 82 url, err); 83 return HTTP_BAD_REQUEST; 84 } 85 /* 86 * now parse path/search args, according to rfc1738: 87 * process the path. With proxy-noncanon set (by 88 * mod_proxy) we use the raw, unparsed uri 89 */ 90 if (apr_table_get(r->notes, "proxy-nocanon")) { 91 path = url; /* this is the raw path */ 92 } 93 else { 94 path = ap_proxy_canonenc(r->pool, url, strlen(url), enc_path, 0, 95 r->proxyreq); 96 search = r->args; 97 } 98 if (path == NULL) 99 return HTTP_BAD_REQUEST; 100 101 r->filename = apr_pstrcat(r->pool, "proxy:", BALANCER_PREFIX, host, 102 "/", path, (search) ? "?" : "", (search) ? search : "", NULL); 103 104 r->path_info = apr_pstrcat(r->pool, "/", path, NULL); 105 106 return OK; 107} 108 109static void init_balancer_members(apr_pool_t *p, server_rec *s, 110 proxy_balancer *balancer) 111{ 112 int i; 113 proxy_worker **workers; 114 115 workers = (proxy_worker **)balancer->workers->elts; 116 117 for (i = 0; i < balancer->workers->nelts; i++) { 118 int worker_is_initialized; 119 proxy_worker *worker = *workers; 120 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01158) 121 "Looking at %s -> %s initialized?", balancer->s->name, 122 ap_proxy_worker_name(p, worker)); 123 worker_is_initialized = PROXY_WORKER_IS_INITIALIZED(worker); 124 if (!worker_is_initialized) { 125 ap_proxy_initialize_worker(worker, s, p); 126 } 127 ++workers; 128 } 129 130 /* Set default number of attempts to the number of 131 * workers. 132 */ 133 if (!balancer->s->max_attempts_set && balancer->workers->nelts > 1) { 134 balancer->s->max_attempts = balancer->workers->nelts - 1; 135 balancer->s->max_attempts_set = 1; 136 } 137} 138 139/* Retrieve the parameter with the given name 140 * Something like 'JSESSIONID=12345...N' 141 */ 142static char *get_path_param(apr_pool_t *pool, char *url, 143 const char *name, int scolon_sep) 144{ 145 char *path = NULL; 146 char *pathdelims = "?&"; 147 148 if (scolon_sep) { 149 pathdelims = ";?&"; 150 } 151 for (path = strstr(url, name); path; path = strstr(path + 1, name)) { 152 path += strlen(name); 153 if (*path == '=') { 154 /* 155 * Session path was found, get its value 156 */ 157 ++path; 158 if (*path) { 159 char *q; 160 path = apr_strtok(apr_pstrdup(pool, path), pathdelims, &q); 161 return path; 162 } 163 } 164 } 165 return NULL; 166} 167 168static char *get_cookie_param(request_rec *r, const char *name) 169{ 170 const char *cookies; 171 const char *start_cookie; 172 173 if ((cookies = apr_table_get(r->headers_in, "Cookie"))) { 174 for (start_cookie = ap_strstr_c(cookies, name); start_cookie; 175 start_cookie = ap_strstr_c(start_cookie + 1, name)) { 176 if (start_cookie == cookies || 177 start_cookie[-1] == ';' || 178 start_cookie[-1] == ',' || 179 isspace(start_cookie[-1])) { 180 181 start_cookie += strlen(name); 182 while(*start_cookie && isspace(*start_cookie)) 183 ++start_cookie; 184 if (*start_cookie++ == '=' && *start_cookie) { 185 /* 186 * Session cookie was found, get its value 187 */ 188 char *end_cookie, *cookie; 189 cookie = apr_pstrdup(r->pool, start_cookie); 190 if ((end_cookie = strchr(cookie, ';')) != NULL) 191 *end_cookie = '\0'; 192 if((end_cookie = strchr(cookie, ',')) != NULL) 193 *end_cookie = '\0'; 194 return cookie; 195 } 196 } 197 } 198 } 199 return NULL; 200} 201 202/* Find the worker that has the 'route' defined 203 */ 204static proxy_worker *find_route_worker(proxy_balancer *balancer, 205 const char *route, request_rec *r) 206{ 207 int i; 208 int checking_standby; 209 int checked_standby; 210 211 proxy_worker **workers; 212 213 checking_standby = checked_standby = 0; 214 while (!checked_standby) { 215 workers = (proxy_worker **)balancer->workers->elts; 216 for (i = 0; i < balancer->workers->nelts; i++, workers++) { 217 proxy_worker *worker = *workers; 218 if ( (checking_standby ? !PROXY_WORKER_IS_STANDBY(worker) : PROXY_WORKER_IS_STANDBY(worker)) ) 219 continue; 220 if (*(worker->s->route) && strcmp(worker->s->route, route) == 0) { 221 if (PROXY_WORKER_IS_USABLE(worker)) { 222 return worker; 223 } else { 224 /* 225 * If the worker is in error state run 226 * retry on that worker. It will be marked as 227 * operational if the retry timeout is elapsed. 228 * The worker might still be unusable, but we try 229 * anyway. 230 */ 231 ap_proxy_retry_worker_fn("BALANCER", worker, r->server); 232 if (PROXY_WORKER_IS_USABLE(worker)) { 233 return worker; 234 } else { 235 /* 236 * We have a worker that is unusable. 237 * It can be in error or disabled, but in case 238 * it has a redirection set use that redirection worker. 239 * This enables to safely remove the member from the 240 * balancer. Of course you will need some kind of 241 * session replication between those two remote. 242 */ 243 if (*worker->s->redirect) { 244 proxy_worker *rworker = NULL; 245 rworker = find_route_worker(balancer, worker->s->redirect, r); 246 /* Check if the redirect worker is usable */ 247 if (rworker && !PROXY_WORKER_IS_USABLE(rworker)) { 248 /* 249 * If the worker is in error state run 250 * retry on that worker. It will be marked as 251 * operational if the retry timeout is elapsed. 252 * The worker might still be unusable, but we try 253 * anyway. 254 */ 255 ap_proxy_retry_worker_fn("BALANCER", rworker, r->server); 256 } 257 if (rworker && PROXY_WORKER_IS_USABLE(rworker)) 258 return rworker; 259 } 260 } 261 } 262 } 263 } 264 checked_standby = checking_standby++; 265 } 266 return NULL; 267} 268 269static proxy_worker *find_session_route(proxy_balancer *balancer, 270 request_rec *r, 271 char **route, 272 const char **sticky_used, 273 char **url) 274{ 275 proxy_worker *worker = NULL; 276 277 if (!*balancer->s->sticky) 278 return NULL; 279 /* Try to find the sticky route inside url */ 280 *route = get_path_param(r->pool, *url, balancer->s->sticky_path, balancer->s->scolonsep); 281 if (*route) { 282 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01159) 283 "Found value %s for stickysession %s", 284 *route, balancer->s->sticky_path); 285 *sticky_used = balancer->s->sticky_path; 286 } 287 else { 288 *route = get_cookie_param(r, balancer->s->sticky); 289 if (*route) { 290 *sticky_used = balancer->s->sticky; 291 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01160) 292 "Found value %s for stickysession %s", 293 *route, balancer->s->sticky); 294 } 295 } 296 /* 297 * If we found a value for stickysession, find the first '.' (or whatever 298 * sticky_separator is set to) within. Everything after '.' (if present) 299 * is our route. 300 */ 301 if ((*route) && (balancer->s->sticky_separator != 0) && ((*route = strchr(*route, balancer->s->sticky_separator)) != NULL )) 302 (*route)++; 303 if ((*route) && (**route)) { 304 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01161) "Found route %s", *route); 305 /* We have a route in path or in cookie 306 * Find the worker that has this route defined. 307 */ 308 worker = find_route_worker(balancer, *route, r); 309 if (worker && strcmp(*route, worker->s->route)) { 310 /* 311 * Notice that the route of the worker chosen is different from 312 * the route supplied by the client. 313 */ 314 apr_table_setn(r->subprocess_env, "BALANCER_ROUTE_CHANGED", "1"); 315 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01162) 316 "Route changed from %s to %s", 317 *route, worker->s->route); 318 } 319 return worker; 320 } 321 else 322 return NULL; 323} 324 325static proxy_worker *find_best_worker(proxy_balancer *balancer, 326 request_rec *r) 327{ 328 proxy_worker *candidate = NULL; 329 apr_status_t rv; 330 331 if ((rv = PROXY_THREAD_LOCK(balancer)) != APR_SUCCESS) { 332 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01163) 333 "%s: Lock failed for find_best_worker()", 334 balancer->s->name); 335 return NULL; 336 } 337 338 candidate = (*balancer->lbmethod->finder)(balancer, r); 339 340 if (candidate) 341 candidate->s->elected++; 342 343 if ((rv = PROXY_THREAD_UNLOCK(balancer)) != APR_SUCCESS) { 344 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01164) 345 "%s: Unlock failed for find_best_worker()", 346 balancer->s->name); 347 } 348 349 if (candidate == NULL) { 350 /* All the workers are in error state or disabled. 351 * If the balancer has a timeout sleep for a while 352 * and try again to find the worker. The chances are 353 * that some other thread will release a connection. 354 * By default the timeout is not set, and the server 355 * returns SERVER_BUSY. 356 */ 357 if (balancer->s->timeout) { 358 /* XXX: This can perhaps be build using some 359 * smarter mechanism, like tread_cond. 360 * But since the statuses can came from 361 * different childs, use the provided algo. 362 */ 363 apr_interval_time_t timeout = balancer->s->timeout; 364 apr_interval_time_t step, tval = 0; 365 /* Set the timeout to 0 so that we don't 366 * end in infinite loop 367 */ 368 balancer->s->timeout = 0; 369 step = timeout / 100; 370 while (tval < timeout) { 371 apr_sleep(step); 372 /* Try again */ 373 if ((candidate = find_best_worker(balancer, r))) 374 break; 375 tval += step; 376 } 377 /* restore the timeout */ 378 balancer->s->timeout = timeout; 379 } 380 } 381 382 return candidate; 383 384} 385 386static int rewrite_url(request_rec *r, proxy_worker *worker, 387 char **url) 388{ 389 const char *scheme = strstr(*url, "://"); 390 const char *path = NULL; 391 392 if (scheme) 393 path = ap_strchr_c(scheme + 3, '/'); 394 395 /* we break the URL into host, port, uri */ 396 if (!worker) { 397 return ap_proxyerror(r, HTTP_BAD_REQUEST, apr_pstrcat(r->pool, 398 "missing worker. URI cannot be parsed: ", *url, 399 NULL)); 400 } 401 402 *url = apr_pstrcat(r->pool, worker->s->name, path, NULL); 403 404 return OK; 405} 406 407static void force_recovery(proxy_balancer *balancer, server_rec *s) 408{ 409 int i; 410 int ok = 0; 411 proxy_worker **worker; 412 413 worker = (proxy_worker **)balancer->workers->elts; 414 for (i = 0; i < balancer->workers->nelts; i++, worker++) { 415 if (!((*worker)->s->status & PROXY_WORKER_IN_ERROR)) { 416 ok = 1; 417 break; 418 } 419 else { 420 /* Try if we can recover */ 421 ap_proxy_retry_worker_fn("BALANCER", *worker, s); 422 if (!((*worker)->s->status & PROXY_WORKER_IN_ERROR)) { 423 ok = 1; 424 break; 425 } 426 } 427 } 428 if (!ok && balancer->s->forcerecovery) { 429 /* If all workers are in error state force the recovery. 430 */ 431 worker = (proxy_worker **)balancer->workers->elts; 432 for (i = 0; i < balancer->workers->nelts; i++, worker++) { 433 ++(*worker)->s->retries; 434 (*worker)->s->status &= ~PROXY_WORKER_IN_ERROR; 435 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01165) 436 "%s: Forcing recovery for worker (%s)", 437 balancer->s->name, (*worker)->s->hostname); 438 } 439 } 440} 441 442static apr_status_t decrement_busy_count(void *worker_) 443{ 444 proxy_worker *worker = worker_; 445 446 if (worker->s->busy) { 447 worker->s->busy--; 448 } 449 450 return APR_SUCCESS; 451} 452 453static int proxy_balancer_pre_request(proxy_worker **worker, 454 proxy_balancer **balancer, 455 request_rec *r, 456 proxy_server_conf *conf, char **url) 457{ 458 int access_status; 459 proxy_worker *runtime; 460 char *route = NULL; 461 const char *sticky = NULL; 462 apr_status_t rv; 463 464 *worker = NULL; 465 /* Step 1: check if the url is for us 466 * The url we can handle starts with 'balancer://' 467 * If balancer is already provided skip the search 468 * for balancer, because this is failover attempt. 469 */ 470 if (!*balancer && 471 !(*balancer = ap_proxy_get_balancer(r->pool, conf, *url, 1))) 472 return DECLINED; 473 474 /* Step 2: Lock the LoadBalancer 475 * XXX: perhaps we need the process lock here 476 */ 477 if ((rv = PROXY_THREAD_LOCK(*balancer)) != APR_SUCCESS) { 478 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01166) 479 "%s: Lock failed for pre_request", (*balancer)->s->name); 480 return DECLINED; 481 } 482 483 /* Step 3: force recovery */ 484 force_recovery(*balancer, r->server); 485 486 /* Step 3.5: Update member list for the balancer */ 487 /* TODO: Implement as provider! */ 488 ap_proxy_sync_balancer(*balancer, r->server, conf); 489 490 /* Step 4: find the session route */ 491 runtime = find_session_route(*balancer, r, &route, &sticky, url); 492 if (runtime) { 493 if ((*balancer)->lbmethod && (*balancer)->lbmethod->updatelbstatus) { 494 /* Call the LB implementation */ 495 (*balancer)->lbmethod->updatelbstatus(*balancer, runtime, r->server); 496 } 497 else { /* Use the default one */ 498 int i, total_factor = 0; 499 proxy_worker **workers; 500 /* We have a sticky load balancer 501 * Update the workers status 502 * so that even session routes get 503 * into account. 504 */ 505 workers = (proxy_worker **)(*balancer)->workers->elts; 506 for (i = 0; i < (*balancer)->workers->nelts; i++) { 507 /* Take into calculation only the workers that are 508 * not in error state or not disabled. 509 */ 510 if (PROXY_WORKER_IS_USABLE(*workers)) { 511 (*workers)->s->lbstatus += (*workers)->s->lbfactor; 512 total_factor += (*workers)->s->lbfactor; 513 } 514 workers++; 515 } 516 runtime->s->lbstatus -= total_factor; 517 } 518 runtime->s->elected++; 519 520 *worker = runtime; 521 } 522 else if (route && (*balancer)->s->sticky_force) { 523 int i, member_of = 0; 524 proxy_worker **workers; 525 /* 526 * We have a route provided that doesn't match the 527 * balancer name. See if the provider route is the 528 * member of the same balancer in which case return 503 529 */ 530 workers = (proxy_worker **)(*balancer)->workers->elts; 531 for (i = 0; i < (*balancer)->workers->nelts; i++) { 532 if (*((*workers)->s->route) && strcmp((*workers)->s->route, route) == 0) { 533 member_of = 1; 534 break; 535 } 536 workers++; 537 } 538 if (member_of) { 539 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01167) 540 "%s: All workers are in error state for route (%s)", 541 (*balancer)->s->name, route); 542 if ((rv = PROXY_THREAD_UNLOCK(*balancer)) != APR_SUCCESS) { 543 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01168) 544 "%s: Unlock failed for pre_request", 545 (*balancer)->s->name); 546 } 547 return HTTP_SERVICE_UNAVAILABLE; 548 } 549 } 550 551 if ((rv = PROXY_THREAD_UNLOCK(*balancer)) != APR_SUCCESS) { 552 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01169) 553 "%s: Unlock failed for pre_request", 554 (*balancer)->s->name); 555 } 556 if (!*worker) { 557 runtime = find_best_worker(*balancer, r); 558 if (!runtime) { 559 if ((*balancer)->workers->nelts) { 560 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01170) 561 "%s: All workers are in error state", 562 (*balancer)->s->name); 563 } else { 564 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01171) 565 "%s: No workers in balancer", 566 (*balancer)->s->name); 567 } 568 569 return HTTP_SERVICE_UNAVAILABLE; 570 } 571 if (*(*balancer)->s->sticky && runtime) { 572 /* 573 * This balancer has sticky sessions and the client either has not 574 * supplied any routing information or all workers for this route 575 * including possible redirect and hotstandby workers are in error 576 * state, but we have found another working worker for this 577 * balancer where we can send the request. Thus notice that we have 578 * changed the route to the backend. 579 */ 580 apr_table_setn(r->subprocess_env, "BALANCER_ROUTE_CHANGED", "1"); 581 } 582 *worker = runtime; 583 } 584 585 (*worker)->s->busy++; 586 apr_pool_cleanup_register(r->pool, *worker, decrement_busy_count, 587 apr_pool_cleanup_null); 588 589 /* Add balancer/worker info to env. */ 590 apr_table_setn(r->subprocess_env, 591 "BALANCER_NAME", (*balancer)->s->name); 592 apr_table_setn(r->subprocess_env, 593 "BALANCER_WORKER_NAME", (*worker)->s->name); 594 apr_table_setn(r->subprocess_env, 595 "BALANCER_WORKER_ROUTE", (*worker)->s->route); 596 597 /* Rewrite the url from 'balancer://url' 598 * to the 'worker_scheme://worker_hostname[:worker_port]/url' 599 * This replaces the balancers fictional name with the 600 * real hostname of the elected worker. 601 */ 602 access_status = rewrite_url(r, *worker, url); 603 /* Add the session route to request notes if present */ 604 if (route) { 605 apr_table_setn(r->notes, "session-sticky", sticky); 606 apr_table_setn(r->notes, "session-route", route); 607 608 /* Add session info to env. */ 609 apr_table_setn(r->subprocess_env, 610 "BALANCER_SESSION_STICKY", sticky); 611 apr_table_setn(r->subprocess_env, 612 "BALANCER_SESSION_ROUTE", route); 613 } 614 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01172) 615 "%s: worker (%s) rewritten to %s", 616 (*balancer)->s->name, (*worker)->s->name, *url); 617 618 return access_status; 619} 620 621static int proxy_balancer_post_request(proxy_worker *worker, 622 proxy_balancer *balancer, 623 request_rec *r, 624 proxy_server_conf *conf) 625{ 626 627 apr_status_t rv; 628 629 if ((rv = PROXY_THREAD_LOCK(balancer)) != APR_SUCCESS) { 630 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01173) 631 "%s: Lock failed for post_request", 632 balancer->s->name); 633 return HTTP_INTERNAL_SERVER_ERROR; 634 } 635 636 if (!apr_is_empty_array(balancer->errstatuses)) { 637 int i; 638 for (i = 0; i < balancer->errstatuses->nelts; i++) { 639 int val = ((int *)balancer->errstatuses->elts)[i]; 640 if (r->status == val) { 641 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01174) 642 "%s: Forcing worker (%s) into error state " 643 "due to status code %d matching 'failonstatus' " 644 "balancer parameter", 645 balancer->s->name, ap_proxy_worker_name(r->pool, worker), 646 val); 647 worker->s->status |= PROXY_WORKER_IN_ERROR; 648 worker->s->error_time = apr_time_now(); 649 break; 650 } 651 } 652 } 653 654 if (balancer->failontimeout 655 && (apr_table_get(r->notes, "proxy_timedout")) != NULL) { 656 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02460) 657 "%s: Forcing worker (%s) into error state " 658 "due to timeout and 'failonstatus' parameter being set", 659 balancer->s->name, ap_proxy_worker_name(r->pool, worker)); 660 worker->s->status |= PROXY_WORKER_IN_ERROR; 661 worker->s->error_time = apr_time_now(); 662 663 } 664 665 if ((rv = PROXY_THREAD_UNLOCK(balancer)) != APR_SUCCESS) { 666 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01175) 667 "%s: Unlock failed for post_request", balancer->s->name); 668 } 669 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01176) 670 "proxy_balancer_post_request for (%s)", balancer->s->name); 671 672 return OK; 673} 674 675static void recalc_factors(proxy_balancer *balancer) 676{ 677 int i; 678 proxy_worker **workers; 679 680 681 /* Recalculate lbfactors */ 682 workers = (proxy_worker **)balancer->workers->elts; 683 /* Special case if there is only one worker its 684 * load factor will always be 1 685 */ 686 if (balancer->workers->nelts == 1) { 687 (*workers)->s->lbstatus = (*workers)->s->lbfactor = 1; 688 return; 689 } 690 for (i = 0; i < balancer->workers->nelts; i++) { 691 /* Update the status entries */ 692 workers[i]->s->lbstatus = workers[i]->s->lbfactor; 693 } 694} 695 696static apr_status_t lock_remove(void *data) 697{ 698 int i; 699 proxy_balancer *balancer; 700 server_rec *s = data; 701 void *sconf = s->module_config; 702 proxy_server_conf *conf = (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module); 703 704 balancer = (proxy_balancer *)conf->balancers->elts; 705 for (i = 0; i < conf->balancers->nelts; i++, balancer++) { 706 if (balancer->gmutex) { 707 apr_global_mutex_destroy(balancer->gmutex); 708 balancer->gmutex = NULL; 709 } 710 } 711 return(0); 712} 713 714/* post_config hook: */ 715static int balancer_post_config(apr_pool_t *pconf, apr_pool_t *plog, 716 apr_pool_t *ptemp, server_rec *s) 717{ 718 apr_status_t rv; 719 proxy_server_conf *conf; 720 ap_slotmem_instance_t *new = NULL; 721 apr_time_t tstamp; 722 723 /* balancer_post_config() will be called twice during startup. So, don't 724 * set up the static data the 1st time through. */ 725 if (ap_state_query(AP_SQ_MAIN_STATE) == AP_SQ_MS_CREATE_PRE_CONFIG) { 726 return OK; 727 } 728 729 if (!ap_proxy_retry_worker_fn) { 730 ap_proxy_retry_worker_fn = 731 APR_RETRIEVE_OPTIONAL_FN(ap_proxy_retry_worker); 732 if (!ap_proxy_retry_worker_fn) { 733 ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02230) 734 "mod_proxy must be loaded for mod_proxy_balancer"); 735 return !OK; 736 } 737 } 738 739 /* 740 * Get slotmem setups 741 */ 742 storage = ap_lookup_provider(AP_SLOTMEM_PROVIDER_GROUP, "shm", 743 AP_SLOTMEM_PROVIDER_VERSION); 744 if (!storage) { 745 ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(01177) 746 "Failed to lookup provider 'shm' for '%s': is " 747 "mod_slotmem_shm loaded??", 748 AP_SLOTMEM_PROVIDER_GROUP); 749 return !OK; 750 } 751 752 tstamp = apr_time_now(); 753 /* 754 * Go thru each Vhost and create the shared mem slotmem for 755 * each balancer's workers 756 */ 757 while (s) { 758 int i,j; 759 char *id; 760 proxy_balancer *balancer; 761 ap_slotmem_type_t type; 762 void *sconf = s->module_config; 763 conf = (proxy_server_conf *)ap_get_module_config(sconf, &proxy_module); 764 /* 765 * During create_proxy_config() we created a dummy id. Now that 766 * we have identifying info, we can create the real id 767 */ 768 id = apr_psprintf(pconf, "%s.%s.%d.%s.%s.%u.%s", 769 (s->server_scheme ? s->server_scheme : "????"), 770 (s->server_hostname ? s->server_hostname : "???"), 771 (int)s->port, 772 (s->server_admin ? s->server_admin : "??"), 773 (s->defn_name ? s->defn_name : "?"), 774 s->defn_line_number, 775 (s->error_fname ? s->error_fname : DEFAULT_ERRORLOG)); 776 conf->id = apr_psprintf(pconf, "p%x-%d", 777 ap_proxy_hashfunc(id, PROXY_HASHFUNC_DEFAULT),(int) tstamp); 778 if (conf->bslot) { 779 /* Shared memory already created for this proxy_server_conf. 780 */ 781 s = s->next; 782 continue; 783 } 784 if (conf->bal_persist) { 785 type = AP_SLOTMEM_TYPE_PERSIST; 786 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01178) "Doing balancers create: bal_persist is , AP_SLOTMEM_TYPE_CLEARINUSE"); 787 } else { 788 type = AP_SLOTMEM_TYPE_CLEARINUSE; 789 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01178) "Doing balancers create: bal_persist is FALSE, AP_SLOTMEM_TYPE_CLEARINUSE"); 790 } 791 if (conf->balancers->nelts) { 792 conf->max_balancers = conf->balancers->nelts + conf->bgrowth; 793 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01178) "Doing balancers create: %d, %d (%d)", 794 (int)ALIGNED_PROXY_BALANCER_SHARED_SIZE, 795 (int)conf->balancers->nelts, conf->max_balancers); 796 797 rv = storage->create(&new, conf->id, 798 ALIGNED_PROXY_BALANCER_SHARED_SIZE, 799 conf->max_balancers, type, pconf); 800 if (rv != APR_SUCCESS) { 801 ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, APLOGNO(01179) "balancer slotmem_create failed for %s", conf->id); 802 return !OK; 803 } 804 ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, s, APLOGNO(01179) "balancer slotmem_create SUCCESS for %s", conf->id); 805 conf->bslot = new; 806 } 807 conf->storage = storage; 808 809 /* Initialize shared scoreboard data */ 810 balancer = (proxy_balancer *)conf->balancers->elts; 811 for (i = 0; i < conf->balancers->nelts; i++, balancer++) { 812 proxy_worker **workers; 813 proxy_worker *worker; 814 proxy_balancer_shared *bshm; 815 const char *sname; 816 unsigned int index; 817 818 /* now that we have the right id, we need to redo the sname field */ 819 ap_pstr2_alnum(pconf, balancer->s->name + sizeof(BALANCER_PREFIX) - 1, 820 &sname); 821 sname = apr_pstrcat(pconf, conf->id, "_", sname, NULL); 822 PROXY_STRNCPY(balancer->s->sname, sname); /* We know this will succeed */ 823 824 balancer->max_workers = balancer->workers->nelts + balancer->growth; 825 826 /* Create global mutex */ 827 rv = ap_global_mutex_create(&(balancer->gmutex), NULL, balancer_mutex_type, 828 balancer->s->sname, s, pconf, 0); 829 if (rv != APR_SUCCESS || !balancer->gmutex) { 830 ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, APLOGNO(01180) 831 "mutex creation of %s : %s failed", balancer_mutex_type, 832 balancer->s->sname); 833 return HTTP_INTERNAL_SERVER_ERROR; 834 } 835 836 apr_pool_cleanup_register(pconf, (void *)s, lock_remove, 837 apr_pool_cleanup_null); 838 839 /* setup shm for balancers */ 840 bshm = ap_proxy_find_balancershm(storage, conf->bslot, balancer, &index); 841 if (bshm) { 842 if ((rv = storage->fgrab(conf->bslot, index)) != APR_SUCCESS) { 843 ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, APLOGNO(02408) "balancer slotmem_fgrab failed"); 844 return !OK; 845 } 846 } 847 else { 848 if ((rv = storage->grab(conf->bslot, &index)) != APR_SUCCESS) { 849 ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, APLOGNO(01181) "balancer slotmem_grab failed"); 850 return !OK; 851 } 852 if ((rv = storage->dptr(conf->bslot, index, (void *)&bshm)) != APR_SUCCESS) { 853 ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, APLOGNO(01182) "balancer slotmem_dptr failed"); 854 return !OK; 855 } 856 } 857 if ((rv = ap_proxy_share_balancer(balancer, bshm, index)) != APR_SUCCESS) { 858 ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, APLOGNO(01183) "Cannot share balancer"); 859 return !OK; 860 } 861 862 /* create slotmem slots for workers */ 863 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01184) "Doing workers create: %s (%s), %d, %d [%u]", 864 balancer->s->name, balancer->s->sname, 865 (int)ALIGNED_PROXY_WORKER_SHARED_SIZE, 866 (int)balancer->max_workers, i); 867 868 rv = storage->create(&new, balancer->s->sname, 869 ALIGNED_PROXY_WORKER_SHARED_SIZE, 870 balancer->max_workers, type, pconf); 871 if (rv != APR_SUCCESS) { 872 ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, APLOGNO(01185) "worker slotmem_create failed"); 873 return !OK; 874 } 875 balancer->wslot = new; 876 balancer->storage = storage; 877 878 /* sync all timestamps */ 879 balancer->wupdated = balancer->s->wupdated = tstamp; 880 881 /* now go thru each worker */ 882 workers = (proxy_worker **)balancer->workers->elts; 883 for (j = 0; j < balancer->workers->nelts; j++, workers++) { 884 proxy_worker_shared *shm; 885 886 worker = *workers; 887 888 shm = ap_proxy_find_workershm(storage, balancer->wslot, worker, &index); 889 if (shm) { 890 if ((rv = storage->fgrab(balancer->wslot, index)) != APR_SUCCESS) { 891 ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, APLOGNO(02409) "worker slotmem_fgrab failed"); 892 return !OK; 893 } 894 } 895 else { 896 if ((rv = storage->grab(balancer->wslot, &index)) != APR_SUCCESS) { 897 ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, APLOGNO(01186) "worker slotmem_grab failed"); 898 return !OK; 899 900 } 901 if ((rv = storage->dptr(balancer->wslot, index, (void *)&shm)) != APR_SUCCESS) { 902 ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, APLOGNO(01187) "worker slotmem_dptr failed"); 903 return !OK; 904 } 905 } 906 if ((rv = ap_proxy_share_worker(worker, shm, index)) != APR_SUCCESS) { 907 ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, APLOGNO(01188) "Cannot share worker"); 908 return !OK; 909 } 910 worker->s->updated = tstamp; 911 } 912 if (conf->bal_persist) { 913 /* We could have just read-in a persisted config. Force a sync. */ 914 balancer->wupdated--; 915 ap_proxy_sync_balancer(balancer, s, conf); 916 } 917 } 918 s = s->next; 919 } 920 921 return OK; 922} 923 924static void create_radio(const char *name, unsigned int flag, request_rec *r) 925{ 926 ap_rvputs(r, "<td>On <input name='", name, "' id='", name, "' value='1' type=radio", NULL); 927 if (flag) 928 ap_rputs(" checked", r); 929 ap_rvputs(r, "> <br/> Off <input name='", name, "' id='", name, "' value='0' type=radio", NULL); 930 if (!flag) 931 ap_rputs(" checked", r); 932 ap_rputs("></td>\n", r); 933} 934 935static void push2table(const char *input, apr_table_t *params, 936 const char *allowed[], apr_pool_t *p) 937{ 938 char *args; 939 char *tok, *val; 940 char *key; 941 942 if (input == NULL) { 943 return; 944 } 945 args = apr_pstrdup(p, input); 946 947 key = apr_strtok(args, "&", &tok); 948 while (key) { 949 val = strchr(key, '='); 950 if (val) { 951 *val++ = '\0'; 952 } 953 else { 954 val = ""; 955 } 956 ap_unescape_url(key); 957 ap_unescape_url(val); 958 if (allowed == NULL) { /* allow all */ 959 apr_table_set(params, key, val); 960 } 961 else { 962 const char **ok = allowed; 963 while (*ok) { 964 if (strcmp(*ok, key) == 0) { 965 apr_table_set(params, key, val); 966 break; 967 } 968 ok++; 969 } 970 } 971 key = apr_strtok(NULL, "&", &tok); 972 } 973} 974 975/* Manages the loadfactors and member status 976 * The balancer, worker and nonce are obtained from 977 * the request args (?b=...&w=...&nonce=....). 978 * All other params are pulled from any POST 979 * data that exists. 980 * TODO: 981 * /.../<whatever>/balancer/worker/nonce 982 */ 983static int balancer_handler(request_rec *r) 984{ 985 void *sconf; 986 proxy_server_conf *conf; 987 proxy_balancer *balancer, *bsel = NULL; 988 proxy_worker *worker, *wsel = NULL; 989 proxy_worker **workers = NULL; 990 apr_table_t *params; 991 int i, n; 992 int ok2change = 1; 993 const char *name; 994 const char *action; 995 apr_status_t rv; 996 997 /* is this for us? */ 998 if (strcmp(r->handler, "balancer-manager")) { 999 return DECLINED; 1000 } 1001 1002 r->allowed = 0 1003 | (AP_METHOD_BIT << M_GET) 1004 | (AP_METHOD_BIT << M_POST); 1005 if ((r->method_number != M_GET) && (r->method_number != M_POST)) { 1006 return DECLINED; 1007 } 1008 1009 sconf = r->server->module_config; 1010 conf = (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module); 1011 params = apr_table_make(r->pool, 10); 1012 1013 balancer = (proxy_balancer *)conf->balancers->elts; 1014 for (i = 0; i < conf->balancers->nelts; i++, balancer++) { 1015 if ((rv = PROXY_THREAD_LOCK(balancer)) != APR_SUCCESS) { 1016 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01189) 1017 "%s: Lock failed for balancer_handler", 1018 balancer->s->name); 1019 } 1020 ap_proxy_sync_balancer(balancer, r->server, conf); 1021 if ((rv = PROXY_THREAD_UNLOCK(balancer)) != APR_SUCCESS) { 1022 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01190) 1023 "%s: Unlock failed for balancer_handler", 1024 balancer->s->name); 1025 } 1026 } 1027 1028 if (r->args && (r->method_number == M_GET)) { 1029 const char *allowed[] = { "w", "b", "nonce", "xml", NULL }; 1030 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01191) "parsing r->args"); 1031 1032 push2table(r->args, params, allowed, r->pool); 1033 } 1034 if (r->method_number == M_POST) { 1035 apr_bucket_brigade *ib; 1036 apr_size_t len = 1024; 1037 char *buf = apr_pcalloc(r->pool, len+1); 1038 1039 ib = apr_brigade_create(r->connection->pool, r->connection->bucket_alloc); 1040 rv = ap_get_brigade(r->input_filters, ib, AP_MODE_READBYTES, 1041 APR_BLOCK_READ, len); 1042 if (rv != APR_SUCCESS) { 1043 return HTTP_INTERNAL_SERVER_ERROR; 1044 } 1045 apr_brigade_flatten(ib, buf, &len); 1046 buf[len] = '\0'; 1047 push2table(buf, params, NULL, r->pool); 1048 } 1049 if ((name = apr_table_get(params, "b"))) 1050 bsel = ap_proxy_get_balancer(r->pool, conf, 1051 apr_pstrcat(r->pool, BALANCER_PREFIX, name, NULL), 0); 1052 1053 if ((name = apr_table_get(params, "w"))) { 1054 wsel = ap_proxy_get_worker(r->pool, bsel, conf, name); 1055 } 1056 1057 1058 /* Check that the supplied nonce matches this server's nonce; 1059 * otherwise ignore all parameters, to prevent a CSRF attack. */ 1060 if (!bsel || 1061 (*bsel->s->nonce && 1062 ( 1063 (name = apr_table_get(params, "nonce")) == NULL || 1064 strcmp(bsel->s->nonce, name) != 0 1065 ) 1066 ) 1067 ) { 1068 apr_table_clear(params); 1069 ok2change = 0; 1070 } 1071 1072 /* First set the params */ 1073 if (wsel && ok2change) { 1074 const char *val; 1075 int was_usable = PROXY_WORKER_IS_USABLE(wsel); 1076 1077 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01192) "settings worker params"); 1078 1079 if ((val = apr_table_get(params, "w_lf"))) { 1080 int ival = atoi(val); 1081 if (ival >= 1 && ival <= 100) { 1082 wsel->s->lbfactor = ival; 1083 if (bsel) 1084 recalc_factors(bsel); 1085 } 1086 } 1087 if ((val = apr_table_get(params, "w_wr"))) { 1088 if (strlen(val) && strlen(val) < sizeof(wsel->s->route)) 1089 strcpy(wsel->s->route, val); 1090 else 1091 *wsel->s->route = '\0'; 1092 } 1093 if ((val = apr_table_get(params, "w_rr"))) { 1094 if (strlen(val) && strlen(val) < sizeof(wsel->s->redirect)) 1095 strcpy(wsel->s->redirect, val); 1096 else 1097 *wsel->s->redirect = '\0'; 1098 } 1099 if ((val = apr_table_get(params, "w_status_I"))) { 1100 ap_proxy_set_wstatus('I', atoi(val), wsel); 1101 } 1102 if ((val = apr_table_get(params, "w_status_N"))) { 1103 ap_proxy_set_wstatus('N', atoi(val), wsel); 1104 } 1105 if ((val = apr_table_get(params, "w_status_D"))) { 1106 ap_proxy_set_wstatus('D', atoi(val), wsel); 1107 } 1108 if ((val = apr_table_get(params, "w_status_H"))) { 1109 ap_proxy_set_wstatus('H', atoi(val), wsel); 1110 } 1111 if ((val = apr_table_get(params, "w_ls"))) { 1112 int ival = atoi(val); 1113 if (ival >= 0 && ival <= 99) { 1114 wsel->s->lbset = ival; 1115 } 1116 } 1117 /* if enabling, we need to reset all lb params */ 1118 if (bsel && !was_usable && PROXY_WORKER_IS_USABLE(wsel)) { 1119 bsel->s->need_reset = 1; 1120 } 1121 1122 } 1123 1124 if (bsel && ok2change) { 1125 const char *val; 1126 int ival; 1127 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01193) 1128 "settings balancer params"); 1129 if ((val = apr_table_get(params, "b_lbm"))) { 1130 if ((strlen(val) < (sizeof(bsel->s->lbpname)-1)) && 1131 strcmp(val, bsel->s->lbpname)) { 1132 proxy_balancer_method *lbmethod; 1133 lbmethod = ap_lookup_provider(PROXY_LBMETHOD, val, "0"); 1134 if (lbmethod) { 1135 PROXY_STRNCPY(bsel->s->lbpname, val); 1136 bsel->lbmethod = lbmethod; 1137 bsel->s->wupdated = apr_time_now(); 1138 bsel->s->need_reset = 1; 1139 } 1140 } 1141 } 1142 if ((val = apr_table_get(params, "b_tmo"))) { 1143 ival = atoi(val); 1144 if (ival >= 0 && ival <= 7200) { /* 2 hrs enuff? */ 1145 bsel->s->timeout = apr_time_from_sec(ival); 1146 } 1147 } 1148 if ((val = apr_table_get(params, "b_max"))) { 1149 ival = atoi(val); 1150 if (ival >= 0 && ival <= 99) { 1151 bsel->s->max_attempts = ival; 1152 } 1153 } 1154 if ((val = apr_table_get(params, "b_sforce"))) { 1155 ival = atoi(val); 1156 bsel->s->sticky_force = (ival != 0); 1157 } 1158 if ((val = apr_table_get(params, "b_ss")) && *val) { 1159 if (strlen(val) < (sizeof(bsel->s->sticky_path)-1)) { 1160 if (*val == '-' && *(val+1) == '\0') 1161 *bsel->s->sticky_path = *bsel->s->sticky = '\0'; 1162 else { 1163 char *path; 1164 PROXY_STRNCPY(bsel->s->sticky_path, val); 1165 PROXY_STRNCPY(bsel->s->sticky, val); 1166 1167 if ((path = strchr((char *)bsel->s->sticky, '|'))) { 1168 *path++ = '\0'; 1169 PROXY_STRNCPY(bsel->s->sticky_path, path); 1170 } 1171 } 1172 } 1173 } 1174 if ((val = apr_table_get(params, "b_wyes")) && 1175 (*val == '1' && *(val+1) == '\0') && 1176 (val = apr_table_get(params, "b_nwrkr"))) { 1177 char *ret; 1178 proxy_worker *nworker; 1179 nworker = ap_proxy_get_worker(r->pool, bsel, conf, val); 1180 if (!nworker && storage->num_free_slots(bsel->wslot)) { 1181 if ((rv = PROXY_GLOBAL_LOCK(bsel)) != APR_SUCCESS) { 1182 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01194) 1183 "%s: Lock failed for adding worker", 1184 bsel->s->name); 1185 } 1186 ret = ap_proxy_define_worker(conf->pool, &nworker, bsel, conf, val, 0); 1187 if (!ret) { 1188 unsigned int index; 1189 proxy_worker_shared *shm; 1190 PROXY_COPY_CONF_PARAMS(nworker, conf); 1191 if ((rv = storage->grab(bsel->wslot, &index)) != APR_SUCCESS) { 1192 ap_log_rerror(APLOG_MARK, APLOG_EMERG, rv, r, APLOGNO(01195) 1193 "worker slotmem_grab failed"); 1194 if ((rv = PROXY_GLOBAL_UNLOCK(bsel)) != APR_SUCCESS) { 1195 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01196) 1196 "%s: Unlock failed for adding worker", 1197 bsel->s->name); 1198 } 1199 return HTTP_BAD_REQUEST; 1200 } 1201 if ((rv = storage->dptr(bsel->wslot, index, (void *)&shm)) != APR_SUCCESS) { 1202 ap_log_rerror(APLOG_MARK, APLOG_EMERG, rv, r, APLOGNO(01197) 1203 "worker slotmem_dptr failed"); 1204 if ((rv = PROXY_GLOBAL_UNLOCK(bsel)) != APR_SUCCESS) { 1205 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01198) 1206 "%s: Unlock failed for adding worker", 1207 bsel->s->name); 1208 } 1209 return HTTP_BAD_REQUEST; 1210 } 1211 if ((rv = ap_proxy_share_worker(nworker, shm, index)) != APR_SUCCESS) { 1212 ap_log_rerror(APLOG_MARK, APLOG_EMERG, rv, r, APLOGNO(01199) 1213 "Cannot share worker"); 1214 if ((rv = PROXY_GLOBAL_UNLOCK(bsel)) != APR_SUCCESS) { 1215 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01200) 1216 "%s: Unlock failed for adding worker", 1217 bsel->s->name); 1218 } 1219 return HTTP_BAD_REQUEST; 1220 } 1221 if ((rv = ap_proxy_initialize_worker(nworker, r->server, conf->pool)) != APR_SUCCESS) { 1222 ap_log_rerror(APLOG_MARK, APLOG_EMERG, rv, r, APLOGNO(01201) 1223 "Cannot init worker"); 1224 if ((rv = PROXY_GLOBAL_UNLOCK(bsel)) != APR_SUCCESS) { 1225 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01202) 1226 "%s: Unlock failed for adding worker", 1227 bsel->s->name); 1228 } 1229 return HTTP_BAD_REQUEST; 1230 } 1231 /* sync all timestamps */ 1232 bsel->wupdated = bsel->s->wupdated = nworker->s->updated = apr_time_now(); 1233 /* by default, all new workers are disabled */ 1234 ap_proxy_set_wstatus('D', 1, nworker); 1235 } 1236 if ((rv = PROXY_GLOBAL_UNLOCK(bsel)) != APR_SUCCESS) { 1237 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01203) 1238 "%s: Unlock failed for adding worker", 1239 bsel->s->name); 1240 } 1241 } 1242 1243 } 1244 1245 } 1246 1247 action = ap_construct_url(r->pool, r->uri, r); 1248 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01204) "genning page"); 1249 1250 if (apr_table_get(params, "xml")) { 1251 char date[APR_RFC822_DATE_LEN]; 1252 ap_set_content_type(r, "text/xml"); 1253 ap_rputs("<?xml version='1.0' encoding='UTF-8' ?>\n", r); 1254 ap_rputs("<httpd:manager xmlns:httpd='http://httpd.apache.org'>\n", r); 1255 ap_rputs(" <httpd:balancers>\n", r); 1256 balancer = (proxy_balancer *)conf->balancers->elts; 1257 for (i = 0; i < conf->balancers->nelts; i++) { 1258 ap_rputs(" <httpd:balancer>\n", r); 1259 /* Start proxy_balancer */ 1260 ap_rvputs(r, " <httpd:name>", balancer->s->name, "</httpd:name>\n", NULL); 1261 if (balancer->s->sticky) { 1262 ap_rvputs(r, " <httpd:stickysession>", balancer->s->sticky, 1263 "</httpd:stickysession>\n", NULL); 1264 ap_rprintf(r, 1265 " <httpd:nofailover>%s</httpd:nofailover>\n", 1266 (balancer->s->sticky_force ? "On" : "Off")); 1267 } 1268 ap_rprintf(r, 1269 " <httpd:timeout>%" APR_TIME_T_FMT "</httpd:timeout>", 1270 apr_time_sec(balancer->s->timeout)); 1271 if (balancer->s->max_attempts_set) { 1272 ap_rprintf(r, 1273 " <httpd:maxattempts>%d</httpd:maxattempts>\n", 1274 balancer->s->max_attempts); 1275 } 1276 ap_rvputs(r, " <httpd:lbmethod>", balancer->lbmethod->name, 1277 "</httpd:lbmethod>\n", NULL); 1278 if (balancer->s->sticky) { 1279 ap_rprintf(r, 1280 " <httpd:scolonpathdelim>%s</httpd:scolonpathdelim>\n", 1281 (balancer->s->scolonsep ? "On" : "Off")); 1282 } 1283 /* End proxy_balancer */ 1284 ap_rputs(" <httpd:workers>\n", r); 1285 workers = (proxy_worker **)balancer->workers->elts; 1286 for (n = 0; n < balancer->workers->nelts; n++) { 1287 worker = *workers; 1288 /* Start proxy_worker */ 1289 ap_rputs(" <httpd:worker>\n", r); 1290 ap_rvputs(r, " <httpd:name>", ap_proxy_worker_name(r->pool, worker), 1291 "</httpd:name>\n", NULL); 1292 ap_rvputs(r, " <httpd:scheme>", worker->s->scheme, 1293 "</httpd:scheme>\n", NULL); 1294 ap_rvputs(r, " <httpd:hostname>", worker->s->hostname, 1295 "</httpd:hostname>\n", NULL); 1296 ap_rprintf(r, " <httpd:loadfactor>%d</httpd:loadfactor>\n", 1297 worker->s->lbfactor); 1298 ap_rprintf(r, 1299 " <httpd:port>%d</httpd:port>\n", 1300 worker->s->port); 1301 ap_rprintf(r, " <httpd:min>%d</httpd:min>\n", 1302 worker->s->min); 1303 ap_rprintf(r, " <httpd:smax>%d</httpd:smax>\n", 1304 worker->s->smax); 1305 ap_rprintf(r, " <httpd:max>%d</httpd:max>\n", 1306 worker->s->hmax); 1307 ap_rprintf(r, 1308 " <httpd:ttl>%" APR_TIME_T_FMT "</httpd:ttl>\n", 1309 apr_time_sec(worker->s->ttl)); 1310 if (worker->s->timeout_set) { 1311 ap_rprintf(r, 1312 " <httpd:timeout>%" APR_TIME_T_FMT "</httpd:timeout>\n", 1313 apr_time_sec(worker->s->timeout)); 1314 } 1315 if (worker->s->acquire_set) { 1316 ap_rprintf(r, 1317 " <httpd:acquire>%" APR_TIME_T_FMT "</httpd:acquire>\n", 1318 apr_time_msec(worker->s->acquire)); 1319 } 1320 if (worker->s->recv_buffer_size_set) { 1321 ap_rprintf(r, 1322 " <httpd:recv_buffer_size>%" APR_SIZE_T_FMT "</httpd:recv_buffer_size>\n", 1323 worker->s->recv_buffer_size); 1324 } 1325 if (worker->s->io_buffer_size_set) { 1326 ap_rprintf(r, 1327 " <httpd:io_buffer_size>%" APR_SIZE_T_FMT "</httpd:io_buffer_size>\n", 1328 worker->s->io_buffer_size); 1329 } 1330 if (worker->s->keepalive_set) { 1331 ap_rprintf(r, 1332 " <httpd:keepalive>%s</httpd:keepalive>\n", 1333 (worker->s->keepalive ? "On" : "Off")); 1334 } 1335 /* Begin proxy_worker_stat */ 1336 ap_rputs(" <httpd:status>", r); 1337 if (worker->s->status & PROXY_WORKER_DISABLED) 1338 ap_rputs("Disabled", r); 1339 else if (worker->s->status & PROXY_WORKER_IN_ERROR) 1340 ap_rputs("Error", r); 1341 else if (worker->s->status & PROXY_WORKER_STOPPED) 1342 ap_rputs("Stopped", r); 1343 else if (worker->s->status & PROXY_WORKER_HOT_STANDBY) 1344 ap_rputs("Standby", r); 1345 else if (PROXY_WORKER_IS_USABLE(worker)) 1346 ap_rputs("OK", r); 1347 else if (!PROXY_WORKER_IS_INITIALIZED(worker)) 1348 ap_rputs("Uninitialized", r); 1349 ap_rputs("</httpd:status>\n", r); 1350 if ((worker->s->error_time > 0) && apr_rfc822_date(date, worker->s->error_time) == APR_SUCCESS) { 1351 ap_rvputs(r, " <httpd:error_time>", date, 1352 "</httpd:error_time>\n", NULL); 1353 } 1354 ap_rprintf(r, 1355 " <httpd:retries>%d</httpd:retries>\n", 1356 worker->s->retries); 1357 ap_rprintf(r, 1358 " <httpd:lbstatus>%d</httpd:lbstatus>\n", 1359 worker->s->lbstatus); 1360 ap_rprintf(r, 1361 " <httpd:loadfactor>%d</httpd:loadfactor>\n", 1362 worker->s->lbfactor); 1363 ap_rprintf(r, 1364 " <httpd:transferred>%" APR_OFF_T_FMT "</httpd:transferred>\n", 1365 worker->s->transferred); 1366 ap_rprintf(r, 1367 " <httpd:read>%" APR_OFF_T_FMT "</httpd:read>\n", 1368 worker->s->read); 1369 ap_rprintf(r, 1370 " <httpd:elected>%" APR_SIZE_T_FMT "</httpd:elected>\n", 1371 worker->s->elected); 1372 ap_rvputs(r, " <httpd:route>", 1373 ap_escape_html(r->pool, worker->s->route), 1374 "</httpd:route>\n", NULL); 1375 ap_rvputs(r, " <httpd:redirect>", 1376 ap_escape_html(r->pool, worker->s->redirect), 1377 "</httpd:redirect>\n", NULL); 1378 ap_rprintf(r, 1379 " <httpd:busy>%" APR_SIZE_T_FMT "</httpd:busy>\n", 1380 worker->s->busy); 1381 ap_rprintf(r, " <httpd:lbset>%d</httpd:lbset>\n", 1382 worker->s->lbset); 1383 /* End proxy_worker_stat */ 1384 if (!strcasecmp(worker->s->scheme, "ajp")) { 1385 ap_rputs(" <httpd:flushpackets>", r); 1386 switch (worker->s->flush_packets) { 1387 case flush_off: 1388 ap_rputs("Off", r); 1389 break; 1390 case flush_on: 1391 ap_rputs("On", r); 1392 break; 1393 case flush_auto: 1394 ap_rputs("Auto", r); 1395 break; 1396 } 1397 ap_rputs("</httpd:flushpackets>\n", r); 1398 if (worker->s->flush_packets == flush_auto) { 1399 ap_rprintf(r, 1400 " <httpd:flushwait>%d</httpd:flushwait>\n", 1401 worker->s->flush_wait); 1402 } 1403 if (worker->s->ping_timeout_set) { 1404 ap_rprintf(r, 1405 " <httpd:ping>%" APR_TIME_T_FMT "</httpd:ping>", 1406 apr_time_msec(worker->s->ping_timeout)); 1407 } 1408 } 1409 if (worker->s->disablereuse_set) { 1410 ap_rprintf(r, 1411 " <httpd:disablereuse>%s</httpd:disablereuse>\n", 1412 (worker->s->disablereuse ? "On" : "Off")); 1413 } 1414 if (worker->s->conn_timeout_set) { 1415 ap_rprintf(r, 1416 " <httpd:connectiontimeout>%" APR_TIME_T_FMT "</httpd:connectiontimeout>\n", 1417 apr_time_msec(worker->s->conn_timeout)); 1418 } 1419 if (worker->s->retry_set) { 1420 ap_rprintf(r, 1421 " <httpd:retry>%" APR_TIME_T_FMT "</httpd:retry>\n", 1422 apr_time_sec(worker->s->retry)); 1423 } 1424 ap_rputs(" </httpd:worker>\n", r); 1425 ++workers; 1426 } 1427 ap_rputs(" </httpd:workers>\n", r); 1428 ap_rputs(" </httpd:balancer>\n", r); 1429 ++balancer; 1430 } 1431 ap_rputs(" </httpd:balancers>\n", r); 1432 ap_rputs("</httpd:manager>", r); 1433 } 1434 else { 1435 ap_set_content_type(r, "text/html; charset=ISO-8859-1"); 1436 ap_rputs(DOCTYPE_HTML_3_2 1437 "<html><head><title>Balancer Manager</title>\n", r); 1438 ap_rputs("<style type='text/css'>\n" 1439 "table {\n" 1440 " border-width: 1px;\n" 1441 " border-spacing: 3px;\n" 1442 " border-style: solid;\n" 1443 " border-color: gray;\n" 1444 " border-collapse: collapse;\n" 1445 " background-color: white;\n" 1446 " text-align: center;\n" 1447 "}\n" 1448 "th {\n" 1449 " border-width: 1px;\n" 1450 " padding: 2px;\n" 1451 " border-style: dotted;\n" 1452 " border-color: gray;\n" 1453 " background-color: white;\n" 1454 " text-align: center;\n" 1455 "}\n" 1456 "td {\n" 1457 " border-width: 1px;\n" 1458 " padding: 2px;\n" 1459 " border-style: dotted;\n" 1460 " border-color: gray;\n" 1461 " background-color: white;\n" 1462 " text-align: center;\n" 1463 "}\n" 1464 "</style>\n</head>\n", r); 1465 ap_rputs("<body><h1>Load Balancer Manager for ", r); 1466 ap_rvputs(r, ap_escape_html(r->pool, ap_get_server_name(r)), 1467 "</h1>\n\n", NULL); 1468 ap_rvputs(r, "<dl><dt>Server Version: ", 1469 ap_get_server_description(), "</dt>\n", NULL); 1470 ap_rvputs(r, "<dt>Server Built: ", 1471 ap_get_server_built(), "</dt>\n", NULL); 1472 ap_rvputs(r, "<dt>Balancer changes will ", conf->bal_persist ? "" : "NOT ", 1473 "be persisted on restart.</dt>", NULL); 1474 ap_rvputs(r, "<dt>Balancers are ", conf->inherit ? "" : "NOT ", 1475 "inherited from main server.</dt>", NULL); 1476 ap_rvputs(r, "<dt>ProxyPass settings are ", conf->ppinherit ? "" : "NOT ", 1477 "inherited from main server.</dt>", NULL); 1478 ap_rputs("</dl>\n", r); 1479 balancer = (proxy_balancer *)conf->balancers->elts; 1480 for (i = 0; i < conf->balancers->nelts; i++) { 1481 1482 ap_rputs("<hr />\n<h3>LoadBalancer Status for ", r); 1483 ap_rvputs(r, "<a href=\"", ap_escape_uri(r->pool, r->uri), "?b=", 1484 balancer->s->name + sizeof(BALANCER_PREFIX) - 1, 1485 "&nonce=", balancer->s->nonce, 1486 "\">", NULL); 1487 ap_rvputs(r, balancer->s->name, "</a> [",balancer->s->sname, "]</h3>\n", NULL); 1488 ap_rputs("\n\n<table><tr>" 1489 "<th>MaxMembers</th><th>StickySession</th><th>DisableFailover</th><th>Timeout</th><th>FailoverAttempts</th><th>Method</th>" 1490 "<th>Path</th><th>Active</th></tr>\n<tr>", r); 1491 /* the below is a safe cast, since the number of slots total will 1492 * never be more than max_workers, which is restricted to int */ 1493 ap_rprintf(r, "<td>%d [%d Used]</td>\n", balancer->max_workers, 1494 balancer->max_workers - (int)storage->num_free_slots(balancer->wslot)); 1495 if (*balancer->s->sticky) { 1496 if (strcmp(balancer->s->sticky, balancer->s->sticky_path)) { 1497 ap_rvputs(r, "<td>", balancer->s->sticky, " | ", 1498 balancer->s->sticky_path, NULL); 1499 } 1500 else { 1501 ap_rvputs(r, "<td>", balancer->s->sticky, NULL); 1502 } 1503 } 1504 else { 1505 ap_rputs("<td> (None) ", r); 1506 } 1507 ap_rprintf(r, "<td>%s</td>\n", 1508 balancer->s->sticky_force ? "On" : "Off"); 1509 ap_rprintf(r, "</td><td>%" APR_TIME_T_FMT "</td>", 1510 apr_time_sec(balancer->s->timeout)); 1511 ap_rprintf(r, "<td>%d</td>\n", balancer->s->max_attempts); 1512 ap_rprintf(r, "<td>%s</td>\n", 1513 balancer->s->lbpname); 1514 ap_rputs("<td>", r); 1515 if (balancer->s->vhost && *(balancer->s->vhost)) { 1516 ap_rvputs(r, balancer->s->vhost, " -> ", NULL); 1517 } 1518 ap_rvputs(r, balancer->s->vpath, "</td>\n", NULL); 1519 ap_rprintf(r, "<td>%s</td>\n", 1520 !balancer->s->inactive ? "Yes" : "No"); 1521 ap_rputs("</table>\n<br />", r); 1522 ap_rputs("\n\n<table><tr>" 1523 "<th>Worker URL</th>" 1524 "<th>Route</th><th>RouteRedir</th>" 1525 "<th>Factor</th><th>Set</th><th>Status</th>" 1526 "<th>Elected</th><th>Busy</th><th>Load</th><th>To</th><th>From</th>" 1527 "</tr>\n", r); 1528 1529 workers = (proxy_worker **)balancer->workers->elts; 1530 for (n = 0; n < balancer->workers->nelts; n++) { 1531 char fbuf[50]; 1532 worker = *workers; 1533 ap_rvputs(r, "<tr>\n<td><a href=\"", 1534 ap_escape_uri(r->pool, r->uri), "?b=", 1535 balancer->s->name + sizeof(BALANCER_PREFIX) - 1, "&w=", 1536 ap_escape_uri(r->pool, worker->s->name), 1537 "&nonce=", balancer->s->nonce, 1538 "\">", NULL); 1539 ap_rvputs(r, (*worker->s->uds_path ? "<i>" : ""), ap_proxy_worker_name(r->pool, worker), 1540 (*worker->s->uds_path ? "</i>" : ""), "</a></td>", NULL); 1541 ap_rvputs(r, "<td>", ap_escape_html(r->pool, worker->s->route), 1542 NULL); 1543 ap_rvputs(r, "</td><td>", 1544 ap_escape_html(r->pool, worker->s->redirect), NULL); 1545 ap_rprintf(r, "</td><td>%d</td>", worker->s->lbfactor); 1546 ap_rprintf(r, "<td>%d</td><td>", worker->s->lbset); 1547 ap_rvputs(r, ap_proxy_parse_wstatus(r->pool, worker), NULL); 1548 ap_rputs("</td>", r); 1549 ap_rprintf(r, "<td>%" APR_SIZE_T_FMT "</td>", worker->s->elected); 1550 ap_rprintf(r, "<td>%" APR_SIZE_T_FMT "</td>", worker->s->busy); 1551 ap_rprintf(r, "<td>%d</td><td>", worker->s->lbstatus); 1552 ap_rputs(apr_strfsize(worker->s->transferred, fbuf), r); 1553 ap_rputs("</td><td>", r); 1554 ap_rputs(apr_strfsize(worker->s->read, fbuf), r); 1555 ap_rputs("</td></tr>\n", r); 1556 1557 ++workers; 1558 } 1559 ap_rputs("</table>\n", r); 1560 ++balancer; 1561 } 1562 ap_rputs("<hr />\n", r); 1563 if (wsel && bsel) { 1564 ap_rputs("<h3>Edit worker settings for ", r); 1565 ap_rvputs(r, (*wsel->s->uds_path?"<i>":""), ap_proxy_worker_name(r->pool, wsel), (*wsel->s->uds_path?"</i>":""), "</h3>\n", NULL); 1566 ap_rputs("<form method=\"POST\" enctype=\"application/x-www-form-urlencoded\" action=\"", r); 1567 ap_rvputs(r, ap_escape_uri(r->pool, action), "\">\n", NULL); 1568 ap_rputs("<dl>\n<table><tr><td>Load factor:</td><td><input name='w_lf' id='w_lf' type=text ", r); 1569 ap_rprintf(r, "value='%d'></td></tr>\n", wsel->s->lbfactor); 1570 ap_rputs("<tr><td>LB Set:</td><td><input name='w_ls' id='w_ls' type=text ", r); 1571 ap_rprintf(r, "value='%d'></td></tr>\n", wsel->s->lbset); 1572 ap_rputs("<tr><td>Route:</td><td><input name='w_wr' id='w_wr' type=text ", r); 1573 ap_rvputs(r, "value=\"", ap_escape_html(r->pool, wsel->s->route), 1574 NULL); 1575 ap_rputs("\"></td></tr>\n", r); 1576 ap_rputs("<tr><td>Route Redirect:</td><td><input name='w_rr' id='w_rr' type=text ", r); 1577 ap_rvputs(r, "value=\"", ap_escape_html(r->pool, wsel->s->redirect), 1578 NULL); 1579 ap_rputs("\"></td></tr>\n", r); 1580 ap_rputs("<tr><td>Status:</td>", r); 1581 ap_rputs("<td><table><tr>" 1582 "<th>Ignore Errors</th>" 1583 "<th>Draining Mode</th>" 1584 "<th>Disabled</th>" 1585 "<th>Hot Standby</th></tr>\n<tr>", r); 1586 create_radio("w_status_I", (PROXY_WORKER_IGNORE_ERRORS & wsel->s->status), r); 1587 create_radio("w_status_N", (PROXY_WORKER_DRAIN & wsel->s->status), r); 1588 create_radio("w_status_D", (PROXY_WORKER_DISABLED & wsel->s->status), r); 1589 create_radio("w_status_H", (PROXY_WORKER_HOT_STANDBY & wsel->s->status), r); 1590 ap_rputs("</tr></table>\n", r); 1591 ap_rputs("<tr><td colspan=2><input type=submit value='Submit'></td></tr>\n", r); 1592 ap_rvputs(r, "</table>\n<input type=hidden name='w' id='w' ", NULL); 1593 ap_rvputs(r, "value='", ap_escape_uri(r->pool, wsel->s->name), "'>\n", NULL); 1594 ap_rvputs(r, "<input type=hidden name='b' id='b' ", NULL); 1595 ap_rvputs(r, "value='", bsel->s->name + sizeof(BALANCER_PREFIX) - 1, 1596 "'>\n", NULL); 1597 ap_rvputs(r, "<input type=hidden name='nonce' id='nonce' value='", 1598 bsel->s->nonce, "'>\n", NULL); 1599 ap_rputs("</form>\n", r); 1600 ap_rputs("<hr />\n", r); 1601 } else if (bsel) { 1602 const apr_array_header_t *provs; 1603 const ap_list_provider_names_t *pname; 1604 int i; 1605 ap_rputs("<h3>Edit balancer settings for ", r); 1606 ap_rvputs(r, bsel->s->name, "</h3>\n", NULL); 1607 ap_rputs("<form method='POST' enctype='application/x-www-form-urlencoded' action='", r); 1608 ap_rvputs(r, action, "'>\n", NULL); 1609 ap_rputs("<dl>\n<table>\n", r); 1610 provs = ap_list_provider_names(r->pool, PROXY_LBMETHOD, "0"); 1611 if (provs) { 1612 ap_rputs("<tr><td>LBmethod:</td>", r); 1613 ap_rputs("<td>\n<select name='b_lbm' id='b_lbm'>", r); 1614 pname = (ap_list_provider_names_t *)provs->elts; 1615 for (i = 0; i < provs->nelts; i++, pname++) { 1616 ap_rvputs(r,"<option value='", pname->provider_name, "'", NULL); 1617 if (strcmp(pname->provider_name, bsel->s->lbpname) == 0) 1618 ap_rputs(" selected ", r); 1619 ap_rvputs(r, ">", pname->provider_name, "\n", NULL); 1620 } 1621 ap_rputs("</select>\n</td></tr>\n", r); 1622 } 1623 ap_rputs("<tr><td>Timeout:</td><td><input name='b_tmo' id='b_tmo' type=text ", r); 1624 ap_rprintf(r, "value='%" APR_TIME_T_FMT "'></td></tr>\n", apr_time_sec(bsel->s->timeout)); 1625 ap_rputs("<tr><td>Failover Attempts:</td><td><input name='b_max' id='b_max' type=text ", r); 1626 ap_rprintf(r, "value='%d'></td></tr>\n", bsel->s->max_attempts); 1627 ap_rputs("<tr><td>Disable Failover:</td>", r); 1628 create_radio("b_sforce", bsel->s->sticky_force, r); 1629 ap_rputs("<tr><td>Sticky Session:</td><td><input name='b_ss' id='b_ss' size=64 type=text ", r); 1630 if (strcmp(bsel->s->sticky, bsel->s->sticky_path)) { 1631 ap_rvputs(r, "value ='", bsel->s->sticky, " | ", 1632 bsel->s->sticky_path, NULL); 1633 } 1634 else { 1635 ap_rvputs(r, "value ='", bsel->s->sticky, NULL); 1636 } 1637 ap_rputs("'> (Use '-' to delete)</td></tr>\n", r); 1638 if (storage->num_free_slots(bsel->wslot) != 0) { 1639 ap_rputs("<tr><td>Add New Worker:</td><td><input name='b_nwrkr' id='b_nwrkr' size=32 type=text>" 1640 " Are you sure? <input name='b_wyes' id='b_wyes' type=checkbox value='1'>" 1641 "</td></tr>", r); 1642 } 1643 ap_rputs("<tr><td colspan=2><input type=submit value='Submit'></td></tr>\n", r); 1644 ap_rvputs(r, "</table>\n<input type=hidden name='b' id='b' ", NULL); 1645 ap_rvputs(r, "value='", bsel->s->name + sizeof(BALANCER_PREFIX) - 1, 1646 "'>\n", NULL); 1647 ap_rvputs(r, "<input type=hidden name='nonce' id='nonce' value='", 1648 bsel->s->nonce, "'>\n", NULL); 1649 ap_rputs("</form>\n", r); 1650 ap_rputs("<hr />\n", r); 1651 } 1652 ap_rputs(ap_psignature("",r), r); 1653 ap_rputs("</body></html>\n", r); 1654 ap_rflush(r); 1655 } 1656 return DONE; 1657} 1658 1659static void balancer_child_init(apr_pool_t *p, server_rec *s) 1660{ 1661 while (s) { 1662 proxy_balancer *balancer; 1663 int i; 1664 void *sconf = s->module_config; 1665 proxy_server_conf *conf = (proxy_server_conf *)ap_get_module_config(sconf, &proxy_module); 1666 apr_status_t rv; 1667 1668 if (conf->balancers->nelts) { 1669 apr_size_t size; 1670 unsigned int num; 1671 storage->attach(&(conf->bslot), conf->id, &size, &num, p); 1672 if (!conf->bslot) { 1673 ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(01205) "slotmem_attach failed"); 1674 exit(1); /* Ugly, but what else? */ 1675 } 1676 } 1677 1678 balancer = (proxy_balancer *)conf->balancers->elts; 1679 for (i = 0; i < conf->balancers->nelts; i++, balancer++) { 1680 rv = ap_proxy_initialize_balancer(balancer, s, p); 1681 1682 if (rv != APR_SUCCESS) { 1683 ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s, APLOGNO(01206) 1684 "Failed to init balancer %s in child", 1685 balancer->s->name); 1686 exit(1); /* Ugly, but what else? */ 1687 } 1688 init_balancer_members(conf->pool, s, balancer); 1689 } 1690 s = s->next; 1691 } 1692 1693} 1694 1695static void ap_proxy_balancer_register_hook(apr_pool_t *p) 1696{ 1697 /* Only the mpm_winnt has child init hook handler. 1698 * make sure that we are called after the mpm 1699 * initializes 1700 */ 1701 static const char *const aszPred[] = { "mpm_winnt.c", "mod_slotmem_shm.c", NULL}; 1702 static const char *const aszPred2[] = { "mod_proxy.c", NULL}; 1703 /* manager handler */ 1704 ap_hook_post_config(balancer_post_config, aszPred2, NULL, APR_HOOK_MIDDLE); 1705 ap_hook_pre_config(balancer_pre_config, NULL, NULL, APR_HOOK_MIDDLE); 1706 ap_hook_handler(balancer_handler, NULL, NULL, APR_HOOK_FIRST); 1707 ap_hook_child_init(balancer_child_init, aszPred, NULL, APR_HOOK_MIDDLE); 1708 proxy_hook_pre_request(proxy_balancer_pre_request, NULL, NULL, APR_HOOK_FIRST); 1709 proxy_hook_post_request(proxy_balancer_post_request, NULL, NULL, APR_HOOK_FIRST); 1710 proxy_hook_canon_handler(proxy_balancer_canon, NULL, NULL, APR_HOOK_FIRST); 1711} 1712 1713AP_DECLARE_MODULE(proxy_balancer) = { 1714 STANDARD20_MODULE_STUFF, 1715 NULL, /* create per-directory config structure */ 1716 NULL, /* merge per-directory config structures */ 1717 NULL, /* create per-server config structure */ 1718 NULL, /* merge per-server config structures */ 1719 NULL, /* command apr_table_t */ 1720 ap_proxy_balancer_register_hook /* register hooks */ 1721}; 1722