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("'>&nbsp;&nbsp;&nbsp;&nbsp;(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                         "&nbsp;&nbsp;&nbsp;&nbsp;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