]> granicus.if.org Git - apache/blob - modules/proxy/mod_proxy_balancer.c
Fix alignment in a <highlight> block.
[apache] / modules / proxy / mod_proxy_balancer.c
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 #include "apr_escape.h"
26 #include "mod_watchdog.h"
27
28 static const char *balancer_mutex_type = "proxy-balancer-shm";
29 ap_slotmem_provider_t *storage = NULL;
30
31 module AP_MODULE_DECLARE_DATA proxy_balancer_module;
32
33 static APR_OPTIONAL_FN_TYPE(set_worker_hc_param) *set_worker_hc_param_f = NULL;
34
35 static int (*ap_proxy_retry_worker_fn)(const char *proxy_function,
36         proxy_worker *worker, server_rec *s) = NULL;
37
38 static APR_OPTIONAL_FN_TYPE(hc_show_exprs) *hc_show_exprs_f = NULL;
39 static APR_OPTIONAL_FN_TYPE(hc_select_exprs) *hc_select_exprs_f = NULL;
40 static APR_OPTIONAL_FN_TYPE(hc_valid_expr) *hc_valid_expr_f = NULL;
41
42
43 /*
44  * Register our mutex type before the config is read so we
45  * can adjust the mutex settings using the Mutex directive.
46  */
47 static int balancer_pre_config(apr_pool_t *pconf, apr_pool_t *plog,
48                                apr_pool_t *ptemp)
49 {
50
51     apr_status_t rv;
52
53     rv = ap_mutex_register(pconf, balancer_mutex_type, NULL,
54                                APR_LOCK_DEFAULT, 0);
55     if (rv != APR_SUCCESS) {
56         return rv;
57     }
58     set_worker_hc_param_f = APR_RETRIEVE_OPTIONAL_FN(set_worker_hc_param);
59     hc_show_exprs_f = APR_RETRIEVE_OPTIONAL_FN(hc_show_exprs);
60     hc_select_exprs_f = APR_RETRIEVE_OPTIONAL_FN(hc_select_exprs);
61     hc_valid_expr_f = APR_RETRIEVE_OPTIONAL_FN(hc_valid_expr);
62     return OK;
63 }
64
65 #if 0
66 extern void proxy_update_members(proxy_balancer **balancer, request_rec *r,
67                                  proxy_server_conf *conf);
68 #endif
69
70 static int proxy_balancer_canon(request_rec *r, char *url)
71 {
72     char *host, *path;
73     char *search = NULL;
74     const char *err;
75     apr_port_t port = 0;
76
77     /* TODO: offset of BALANCER_PREFIX ?? */
78     if (ap_cstr_casecmpn(url, "balancer:", 9) == 0) {
79         url += 9;
80     }
81     else {
82         return DECLINED;
83     }
84
85     ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "canonicalising URL %s", url);
86
87     /* do syntatic check.
88      * We break the URL into host, port, path, search
89      */
90     err = ap_proxy_canon_netloc(r->pool, &url, NULL, NULL, &host, &port);
91     if (err) {
92         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01157)
93                       "error parsing URL %s: %s",
94                       url, err);
95         return HTTP_BAD_REQUEST;
96     }
97     /*
98      * now parse path/search args, according to rfc1738:
99      * process the path. With proxy-noncanon set (by
100      * mod_proxy) we use the raw, unparsed uri
101      */
102     if (apr_table_get(r->notes, "proxy-nocanon")) {
103         path = url;   /* this is the raw path */
104     }
105     else {
106         path = ap_proxy_canonenc(r->pool, url, strlen(url), enc_path, 0,
107                                  r->proxyreq);
108         search = r->args;
109     }
110     if (path == NULL)
111         return HTTP_BAD_REQUEST;
112
113     r->filename = apr_pstrcat(r->pool, "proxy:" BALANCER_PREFIX, host,
114             "/", path, (search) ? "?" : "", (search) ? search : "", NULL);
115
116     r->path_info = apr_pstrcat(r->pool, "/", path, NULL);
117
118     return OK;
119 }
120
121 static void init_balancer_members(apr_pool_t *p, server_rec *s,
122                                  proxy_balancer *balancer)
123 {
124     int i;
125     proxy_worker **workers;
126
127     workers = (proxy_worker **)balancer->workers->elts;
128
129     for (i = 0; i < balancer->workers->nelts; i++) {
130         int worker_is_initialized;
131         proxy_worker *worker = *workers;
132         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01158)
133                      "Looking at %s -> %s initialized?", balancer->s->name,
134                      ap_proxy_worker_name(p, worker));
135         worker_is_initialized = PROXY_WORKER_IS_INITIALIZED(worker);
136         if (!worker_is_initialized) {
137             ap_proxy_initialize_worker(worker, s, p);
138         }
139         ++workers;
140     }
141
142     /* Set default number of attempts to the number of
143      * workers.
144      */
145     if (!balancer->s->max_attempts_set && balancer->workers->nelts > 1) {
146         balancer->s->max_attempts = balancer->workers->nelts - 1;
147         balancer->s->max_attempts_set = 1;
148     }
149 }
150
151 /* Retrieve the parameter with the given name
152  * Something like 'JSESSIONID=12345...N'
153  */
154 static char *get_path_param(apr_pool_t *pool, char *url,
155                             const char *name, int scolon_sep)
156 {
157     char *path = NULL;
158     char *pathdelims = "?&";
159
160     if (scolon_sep) {
161         pathdelims = ";?&";
162     }
163     for (path = strstr(url, name); path; path = strstr(path + 1, name)) {
164         path += strlen(name);
165         if (*path == '=') {
166             /*
167              * Session path was found, get its value
168              */
169             ++path;
170             if (*path) {
171                 char *q;
172                 path = apr_strtok(apr_pstrdup(pool, path), pathdelims, &q);
173                 return path;
174             }
175         }
176     }
177     return NULL;
178 }
179
180 static char *get_cookie_param(request_rec *r, const char *name)
181 {
182     const char *cookies;
183     const char *start_cookie;
184
185     if ((cookies = apr_table_get(r->headers_in, "Cookie"))) {
186         for (start_cookie = ap_strstr_c(cookies, name); start_cookie;
187              start_cookie = ap_strstr_c(start_cookie + 1, name)) {
188             if (start_cookie == cookies ||
189                 start_cookie[-1] == ';' ||
190                 start_cookie[-1] == ',' ||
191                 isspace(start_cookie[-1])) {
192
193                 start_cookie += strlen(name);
194                 while(*start_cookie && isspace(*start_cookie))
195                     ++start_cookie;
196                 if (*start_cookie++ == '=' && *start_cookie) {
197                     /*
198                      * Session cookie was found, get its value
199                      */
200                     char *end_cookie, *cookie;
201                     cookie = apr_pstrdup(r->pool, start_cookie);
202                     if ((end_cookie = strchr(cookie, ';')) != NULL)
203                         *end_cookie = '\0';
204                     if((end_cookie = strchr(cookie, ',')) != NULL)
205                         *end_cookie = '\0';
206                     return cookie;
207                 }
208             }
209         }
210     }
211     return NULL;
212 }
213
214 /* Find the worker that has the 'route' defined
215  */
216 static proxy_worker *find_route_worker(proxy_balancer *balancer,
217                                        const char *route, request_rec *r,
218                                        int recursion)
219 {
220     int i;
221     int checking_standby;
222     int checked_standby;
223
224     proxy_worker **workers;
225
226     checking_standby = checked_standby = 0;
227     while (!checked_standby) {
228         workers = (proxy_worker **)balancer->workers->elts;
229         for (i = 0; i < balancer->workers->nelts; i++, workers++) {
230             proxy_worker *worker = *workers;
231             if ( (checking_standby ? !PROXY_WORKER_IS_STANDBY(worker) : PROXY_WORKER_IS_STANDBY(worker)) )
232                 continue;
233             if (*(worker->s->route) && strcmp(worker->s->route, route) == 0) {
234                 if (PROXY_WORKER_IS_USABLE(worker)) {
235                     return worker;
236                 } else {
237                     /*
238                      * If the worker is in error state run
239                      * retry on that worker. It will be marked as
240                      * operational if the retry timeout is elapsed.
241                      * The worker might still be unusable, but we try
242                      * anyway.
243                      */
244                     ap_proxy_retry_worker_fn("BALANCER", worker, r->server);
245                     if (PROXY_WORKER_IS_USABLE(worker)) {
246                             return worker;
247                     } else {
248                         /*
249                          * We have a worker that is unusable.
250                          * It can be in error or disabled, but in case
251                          * it has a redirection set use that redirection worker.
252                          * This enables to safely remove the member from the
253                          * balancer. Of course you will need some kind of
254                          * session replication between those two remote.
255                          * Also check that we haven't gone thru all the
256                          * balancer members by means of redirects.
257                          * This should avoid redirect cycles.
258                          */
259                         if ((*worker->s->redirect)
260                             && (recursion < balancer->workers->nelts)) {
261                             proxy_worker *rworker = NULL;
262                             rworker = find_route_worker(balancer, worker->s->redirect,
263                                                         r, recursion + 1);
264                             /* Check if the redirect worker is usable */
265                             if (rworker && !PROXY_WORKER_IS_USABLE(rworker)) {
266                                 /*
267                                  * If the worker is in error state run
268                                  * retry on that worker. It will be marked as
269                                  * operational if the retry timeout is elapsed.
270                                  * The worker might still be unusable, but we try
271                                  * anyway.
272                                  */
273                                 ap_proxy_retry_worker_fn("BALANCER", rworker, r->server);
274                             }
275                             if (rworker && PROXY_WORKER_IS_USABLE(rworker))
276                                 return rworker;
277                         }
278                     }
279                 }
280             }
281         }
282         checked_standby = checking_standby++;
283     }
284     return NULL;
285 }
286
287 static proxy_worker *find_session_route(proxy_balancer *balancer,
288                                         request_rec *r,
289                                         char **route,
290                                         const char **sticky_used,
291                                         char **url)
292 {
293     proxy_worker *worker = NULL;
294
295     if (!*balancer->s->sticky)
296         return NULL;
297     /* Try to find the sticky route inside url */
298     *route = get_path_param(r->pool, *url, balancer->s->sticky_path, balancer->s->scolonsep);
299     if (*route) {
300         ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01159)
301                      "Found value %s for stickysession %s",
302                      *route, balancer->s->sticky_path);
303         *sticky_used =  balancer->s->sticky_path;
304     }
305     else {
306         *route = get_cookie_param(r, balancer->s->sticky);
307         if (*route) {
308             *sticky_used =  balancer->s->sticky;
309             ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01160)
310                          "Found value %s for stickysession %s",
311                          *route, balancer->s->sticky);
312         }
313     }
314     /*
315      * If we found a value for stickysession, find the first '.' (or whatever
316      * sticky_separator is set to) within. Everything after '.' (if present)
317      * is our route. 
318      */
319     if ((*route) && (balancer->s->sticky_separator != 0) && ((*route = strchr(*route, balancer->s->sticky_separator)) != NULL ))
320         (*route)++;
321     if ((*route) && (**route)) {
322         ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01161) "Found route %s", *route);
323         /* We have a route in path or in cookie
324          * Find the worker that has this route defined.
325          */
326         worker = find_route_worker(balancer, *route, r, 1);
327         if (worker && strcmp(*route, worker->s->route)) {
328             /*
329              * Notice that the route of the worker chosen is different from
330              * the route supplied by the client.
331              */
332             apr_table_setn(r->subprocess_env, "BALANCER_ROUTE_CHANGED", "1");
333             ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01162)
334                           "Route changed from %s to %s",
335                           *route, worker->s->route);
336         }
337         return worker;
338     }
339     else
340         return NULL;
341 }
342
343 static proxy_worker *find_best_worker(proxy_balancer *balancer,
344                                       request_rec *r)
345 {
346     proxy_worker *candidate = NULL;
347     apr_status_t rv;
348
349 #if APR_HAS_THREADS
350     if ((rv = PROXY_THREAD_LOCK(balancer)) != APR_SUCCESS) {
351         ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01163)
352                       "%s: Lock failed for find_best_worker()",
353                       balancer->s->name);
354         return NULL;
355     }
356 #endif
357
358     candidate = (*balancer->lbmethod->finder)(balancer, r);
359
360     if (candidate)
361         candidate->s->elected++;
362
363 #if APR_HAS_THREADS
364     if ((rv = PROXY_THREAD_UNLOCK(balancer)) != APR_SUCCESS) {
365         ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01164)
366                       "%s: Unlock failed for find_best_worker()",
367                       balancer->s->name);
368     }
369 #endif
370
371     if (candidate == NULL) {
372         /* All the workers are in error state or disabled.
373          * If the balancer has a timeout sleep for a while
374          * and try again to find the worker. The chances are
375          * that some other thread will release a connection.
376          * By default the timeout is not set, and the server
377          * returns SERVER_BUSY.
378          */
379         if (balancer->s->timeout) {
380             /* XXX: This can perhaps be build using some
381              * smarter mechanism, like tread_cond.
382              * But since the statuses can came from
383              * different childs, use the provided algo.
384              */
385             apr_interval_time_t timeout = balancer->s->timeout;
386             apr_interval_time_t step, tval = 0;
387             /* Set the timeout to 0 so that we don't
388              * end in infinite loop
389              */
390             balancer->s->timeout = 0;
391             step = timeout / 100;
392             while (tval < timeout) {
393                 apr_sleep(step);
394                 /* Try again */
395                 if ((candidate = find_best_worker(balancer, r)))
396                     break;
397                 tval += step;
398             }
399             /* restore the timeout */
400             balancer->s->timeout = timeout;
401         }
402     }
403
404     return candidate;
405
406 }
407
408 static int rewrite_url(request_rec *r, proxy_worker *worker,
409                         char **url)
410 {
411     const char *scheme = strstr(*url, "://");
412     const char *path = NULL;
413
414     if (scheme)
415         path = ap_strchr_c(scheme + 3, '/');
416
417     /* we break the URL into host, port, uri */
418     if (!worker) {
419         return ap_proxyerror(r, HTTP_BAD_REQUEST, apr_pstrcat(r->pool,
420                              "missing worker. URI cannot be parsed: ", *url,
421                              NULL));
422     }
423
424     *url = apr_pstrcat(r->pool, worker->s->name, path, NULL);
425
426     return OK;
427 }
428
429 static void force_recovery(proxy_balancer *balancer, server_rec *s)
430 {
431     int i;
432     int ok = 0;
433     proxy_worker **worker;
434
435     worker = (proxy_worker **)balancer->workers->elts;
436     for (i = 0; i < balancer->workers->nelts; i++, worker++) {
437         if (!((*worker)->s->status & PROXY_WORKER_IN_ERROR)) {
438             ok = 1;
439             break;
440         }
441         else {
442             /* Try if we can recover */
443             ap_proxy_retry_worker_fn("BALANCER", *worker, s);
444             if (!((*worker)->s->status & PROXY_WORKER_IN_ERROR)) {
445                 ok = 1;
446                 break;
447             }
448         }
449     }
450     if (!ok && balancer->s->forcerecovery) {
451         /* If all workers are in error state force the recovery.
452          */
453         worker = (proxy_worker **)balancer->workers->elts;
454         for (i = 0; i < balancer->workers->nelts; i++, worker++) {
455             ++(*worker)->s->retries;
456             (*worker)->s->status &= ~PROXY_WORKER_IN_ERROR;
457             ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01165)
458                          "%s: Forcing recovery for worker (%s)",
459                          balancer->s->name, (*worker)->s->hostname_ex);
460         }
461     }
462 }
463
464 static apr_status_t decrement_busy_count(void *worker_)
465 {
466     proxy_worker *worker = worker_;
467     
468     if (worker->s->busy) {
469         worker->s->busy--;
470     }
471
472     return APR_SUCCESS;
473 }
474
475 static int proxy_balancer_pre_request(proxy_worker **worker,
476                                       proxy_balancer **balancer,
477                                       request_rec *r,
478                                       proxy_server_conf *conf, char **url)
479 {
480     int access_status;
481     proxy_worker *runtime;
482     char *route = NULL;
483     const char *sticky = NULL;
484     apr_status_t rv;
485
486     *worker = NULL;
487     /* Step 1: check if the url is for us
488      * The url we can handle starts with 'balancer://'
489      * If balancer is already provided skip the search
490      * for balancer, because this is failover attempt.
491      */
492     if (!*balancer &&
493         !(*balancer = ap_proxy_get_balancer(r->pool, conf, *url, 1)))
494         return DECLINED;
495
496     /* Step 2: Lock the LoadBalancer
497      * XXX: perhaps we need the process lock here
498      */
499 #if APR_HAS_THREADS
500     if ((rv = PROXY_THREAD_LOCK(*balancer)) != APR_SUCCESS) {
501         ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01166)
502                       "%s: Lock failed for pre_request", (*balancer)->s->name);
503         return DECLINED;
504     }
505 #endif
506
507     /* Step 3: force recovery */
508     force_recovery(*balancer, r->server);
509
510     /* Step 3.5: Update member list for the balancer */
511     /* TODO: Implement as provider! */
512     ap_proxy_sync_balancer(*balancer, r->server, conf);
513
514     /* Step 4: find the session route */
515     runtime = find_session_route(*balancer, r, &route, &sticky, url);
516     if (runtime) {
517         if ((*balancer)->lbmethod && (*balancer)->lbmethod->updatelbstatus) {
518             /* Call the LB implementation */
519             (*balancer)->lbmethod->updatelbstatus(*balancer, runtime, r->server);
520         }
521         else { /* Use the default one */
522             int i, total_factor = 0;
523             proxy_worker **workers;
524             /* We have a sticky load balancer
525              * Update the workers status
526              * so that even session routes get
527              * into account.
528              */
529             workers = (proxy_worker **)(*balancer)->workers->elts;
530             for (i = 0; i < (*balancer)->workers->nelts; i++) {
531                 /* Take into calculation only the workers that are
532                  * not in error state or not disabled.
533                  */
534                 if (PROXY_WORKER_IS_USABLE(*workers)) {
535                     (*workers)->s->lbstatus += (*workers)->s->lbfactor;
536                     total_factor += (*workers)->s->lbfactor;
537                 }
538                 workers++;
539             }
540             runtime->s->lbstatus -= total_factor;
541         }
542         runtime->s->elected++;
543
544         *worker = runtime;
545     }
546     else if (route && (*balancer)->s->sticky_force) {
547         int i, member_of = 0;
548         proxy_worker **workers;
549         /*
550          * We have a route provided that doesn't match the
551          * balancer name. See if the provider route is the
552          * member of the same balancer in which case return 503
553          */
554         workers = (proxy_worker **)(*balancer)->workers->elts;
555         for (i = 0; i < (*balancer)->workers->nelts; i++) {
556             if (*((*workers)->s->route) && strcmp((*workers)->s->route, route) == 0) {
557                 member_of = 1;
558                 break;
559             }
560             workers++;
561         }
562         if (member_of) {
563             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01167)
564                           "%s: All workers are in error state for route (%s)",
565                           (*balancer)->s->name, route);
566 #if APR_HAS_THREADS
567             if ((rv = PROXY_THREAD_UNLOCK(*balancer)) != APR_SUCCESS) {
568                 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01168)
569                               "%s: Unlock failed for pre_request",
570                               (*balancer)->s->name);
571             }
572 #endif
573             return HTTP_SERVICE_UNAVAILABLE;
574         }
575     }
576
577 #if APR_HAS_THREADS
578     if ((rv = PROXY_THREAD_UNLOCK(*balancer)) != APR_SUCCESS) {
579         ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01169)
580                       "%s: Unlock failed for pre_request",
581                       (*balancer)->s->name);
582     }
583 #endif
584     if (!*worker) {
585         runtime = find_best_worker(*balancer, r);
586         if (!runtime) {
587             if ((*balancer)->workers->nelts) {
588                 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01170)
589                               "%s: All workers are in error state",
590                               (*balancer)->s->name);
591             } else {
592                 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01171)
593                               "%s: No workers in balancer",
594                               (*balancer)->s->name);
595             }
596
597             return HTTP_SERVICE_UNAVAILABLE;
598         }
599         if (*(*balancer)->s->sticky && runtime) {
600             /*
601              * This balancer has sticky sessions and the client either has not
602              * supplied any routing information or all workers for this route
603              * including possible redirect and hotstandby workers are in error
604              * state, but we have found another working worker for this
605              * balancer where we can send the request. Thus notice that we have
606              * changed the route to the backend.
607              */
608             apr_table_setn(r->subprocess_env, "BALANCER_ROUTE_CHANGED", "1");
609         }
610         *worker = runtime;
611     }
612
613     (*worker)->s->busy++;
614     apr_pool_cleanup_register(r->pool, *worker, decrement_busy_count,
615                               apr_pool_cleanup_null);
616
617     /* Add balancer/worker info to env. */
618     apr_table_setn(r->subprocess_env,
619                    "BALANCER_NAME", (*balancer)->s->name);
620     apr_table_setn(r->subprocess_env,
621                    "BALANCER_WORKER_NAME", (*worker)->s->name);
622     apr_table_setn(r->subprocess_env,
623                    "BALANCER_WORKER_ROUTE", (*worker)->s->route);
624
625     /* Rewrite the url from 'balancer://url'
626      * to the 'worker_scheme://worker_hostname[:worker_port]/url'
627      * This replaces the balancers fictional name with the
628      * real hostname of the elected worker.
629      */
630     access_status = rewrite_url(r, *worker, url);
631     /* Add the session route to request notes if present */
632     if (route) {
633         apr_table_setn(r->notes, "session-sticky", sticky);
634         apr_table_setn(r->notes, "session-route", route);
635
636         /* Add session info to env. */
637         apr_table_setn(r->subprocess_env,
638                        "BALANCER_SESSION_STICKY", sticky);
639         apr_table_setn(r->subprocess_env,
640                        "BALANCER_SESSION_ROUTE", route);
641     }
642     ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01172)
643                   "%s: worker (%s) rewritten to %s",
644                   (*balancer)->s->name, (*worker)->s->name, *url);
645
646     return access_status;
647 }
648
649 static int proxy_balancer_post_request(proxy_worker *worker,
650                                        proxy_balancer *balancer,
651                                        request_rec *r,
652                                        proxy_server_conf *conf)
653 {
654
655     apr_status_t rv;
656
657 #if APR_HAS_THREADS
658     if ((rv = PROXY_THREAD_LOCK(balancer)) != APR_SUCCESS) {
659         ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01173)
660                       "%s: Lock failed for post_request",
661                       balancer->s->name);
662         return HTTP_INTERNAL_SERVER_ERROR;
663     }
664 #endif
665
666     if (!apr_is_empty_array(balancer->errstatuses)
667         && !(worker->s->status & PROXY_WORKER_IGNORE_ERRORS)) {
668         int i;
669         for (i = 0; i < balancer->errstatuses->nelts; i++) {
670             int val = ((int *)balancer->errstatuses->elts)[i];
671             if (r->status == val) {
672                 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01174)
673                               "%s: Forcing worker (%s) into error state "
674                               "due to status code %d matching 'failonstatus' "
675                               "balancer parameter",
676                               balancer->s->name, ap_proxy_worker_name(r->pool, worker),
677                               val);
678                 worker->s->status |= PROXY_WORKER_IN_ERROR;
679                 worker->s->error_time = apr_time_now();
680                 break;
681             }
682         }
683     }
684
685     if (balancer->failontimeout
686         && !(worker->s->status & PROXY_WORKER_IGNORE_ERRORS)
687         && (apr_table_get(r->notes, "proxy_timedout")) != NULL) {
688         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02460)
689                       "%s: Forcing worker (%s) into error state "
690                       "due to timeout and 'failontimeout' parameter being set",
691                        balancer->s->name, ap_proxy_worker_name(r->pool, worker));
692         worker->s->status |= PROXY_WORKER_IN_ERROR;
693         worker->s->error_time = apr_time_now();
694
695     }
696 #if APR_HAS_THREADS
697     if ((rv = PROXY_THREAD_UNLOCK(balancer)) != APR_SUCCESS) {
698         ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01175)
699                       "%s: Unlock failed for post_request", balancer->s->name);
700     }
701 #endif
702     ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01176)
703                   "proxy_balancer_post_request for (%s)", balancer->s->name);
704
705     return OK;
706 }
707
708 static void recalc_factors(proxy_balancer *balancer)
709 {
710     int i;
711     proxy_worker **workers;
712
713
714     /* Recalculate lbfactors */
715     workers = (proxy_worker **)balancer->workers->elts;
716     /* Special case if there is only one worker its
717      * load factor will always be 100
718      */
719     if (balancer->workers->nelts == 1) {
720         (*workers)->s->lbstatus = (*workers)->s->lbfactor = 100;
721         return;
722     }
723     for (i = 0; i < balancer->workers->nelts; i++) {
724         /* Update the status entries */
725         workers[i]->s->lbstatus = workers[i]->s->lbfactor;
726     }
727 }
728
729 static apr_status_t lock_remove(void *data)
730 {
731     int i;
732     proxy_balancer *balancer;
733     server_rec *s = data;
734     void *sconf = s->module_config;
735     proxy_server_conf *conf = (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module);
736
737     balancer = (proxy_balancer *)conf->balancers->elts;
738     for (i = 0; i < conf->balancers->nelts; i++, balancer++) {
739         if (balancer->gmutex) {
740             apr_global_mutex_destroy(balancer->gmutex);
741             balancer->gmutex = NULL;
742         }
743     }
744     return(0);
745 }
746
747 /*
748  * Compute an ID for a vhost based on what makes it selected by requests.
749  * The second and more Host(s)/IP(s):port(s), and the ServerAlias(es) are
750  * optional (see make_servers_ids() below).
751  */
752  static const char *make_server_id(server_rec *s, apr_pool_t *p, int full)
753 {
754     apr_md5_ctx_t md5_ctx;
755     unsigned char md5[APR_MD5_DIGESTSIZE];
756     char host_ip[64]; /* for any IPv[46] string */
757     server_addr_rec *sar;
758     int i;
759
760     apr_md5_init(&md5_ctx);
761     for (sar = s->addrs; sar; sar = sar->next) {
762         host_ip[0] = '\0';
763         apr_sockaddr_ip_getbuf(host_ip, sizeof host_ip, sar->host_addr);
764         apr_md5_update(&md5_ctx, (void *)sar->virthost, strlen(sar->virthost));
765         apr_md5_update(&md5_ctx, (void *)host_ip, strlen(host_ip));
766         apr_md5_update(&md5_ctx, (void *)&sar->host_port,
767                        sizeof(sar->host_port));
768         if (!full) {
769             break;
770         }
771     }
772     if (s->server_hostname) {
773         apr_md5_update(&md5_ctx, (void *)s->server_hostname,
774                        strlen(s->server_hostname));
775     }
776     if (full) {
777         if (s->names) {
778             for (i = 0; i < s->names->nelts; ++i) {
779                 const char *name = APR_ARRAY_IDX(s->names, i, char *);
780                 apr_md5_update(&md5_ctx, (void *)name, strlen(name));
781             }
782         }
783         if (s->wild_names) {
784             for (i = 0; i < s->wild_names->nelts; ++i) {
785                 const char *name = APR_ARRAY_IDX(s->wild_names, i, char *);
786                 apr_md5_update(&md5_ctx, (void *)name, strlen(name));
787             }
788         }
789     }
790     apr_md5_final(md5, &md5_ctx);
791
792     return apr_pescape_hex(p, md5, sizeof md5, 0);
793 }
794
795 /*
796  * First try to compute an unique ID for each vhost with minimal criteria,
797  * that is the first Host/IP:port and ServerName. For most cases this should
798  * be enough and avoids changing the ID unnecessarily accross restart (or
799  * stop/start w.r.t. persisted files) for things that this module does not
800  * care about.
801  *
802  * But if it's not enough (collisions) do a second pass for the full monty,
803  * that is additionally the other Host(s)/IP(s):port(s) and ServerAlias(es).
804  *
805  * Finally, for pathological configs where this is still not enough, let's
806  * append a counter to duplicates, because we really want that ID to be unique
807  * even if the vhost will never be selected to handle requests at run time, at
808  * load time a duplicate may steal the original slotmems (depending on its
809  * balancers' configurations), see how mod_slotmem_shm reuses slots/files based
810  * solely on this ID and resets them if the sizes don't match.
811  */
812 static apr_array_header_t *make_servers_ids(server_rec *main_s, apr_pool_t *p)
813 {
814     server_rec *s = main_s;
815     apr_array_header_t *ids = apr_array_make(p, 10, sizeof(const char *));
816     apr_hash_t *dups = apr_hash_make(p);
817     int idx, *dup, full_monty = 0;
818     const char *id;
819
820     for (idx = 0, s = main_s; s; s = s->next, ++idx) {
821         id = make_server_id(s, p, 0);
822         dup = apr_hash_get(dups, id, APR_HASH_KEY_STRING);
823         apr_hash_set(dups, id, APR_HASH_KEY_STRING,
824                      apr_pmemdup(p, &idx, sizeof(int)));
825         if (dup) {
826             full_monty = 1;
827             APR_ARRAY_IDX(ids, *dup, const char *) = NULL;
828             APR_ARRAY_PUSH(ids, const char *) = NULL;
829         }
830         else {
831             APR_ARRAY_PUSH(ids, const char *) = id;
832         }
833     }
834     if (full_monty) {
835         apr_hash_clear(dups);
836         for (idx = 0, s = main_s; s; s = s->next, ++idx) {
837             id = APR_ARRAY_IDX(ids, idx, const char *);
838             if (id) {
839                 /* Preserve non-duplicates */
840                 continue;
841             }
842             id = make_server_id(s, p, 1);
843             if (apr_hash_get(dups, id, APR_HASH_KEY_STRING)) {
844                 id = apr_psprintf(p, "%s_%x", id, idx);
845             }
846             else {
847                 apr_hash_set(dups, id, APR_HASH_KEY_STRING, (void *)-1);
848             }
849             APR_ARRAY_IDX(ids, idx, const char *) = id;
850         }
851     }
852
853     return ids;
854 }
855
856 /* post_config hook: */
857 static int balancer_post_config(apr_pool_t *pconf, apr_pool_t *plog,
858                          apr_pool_t *ptemp, server_rec *s)
859 {
860     apr_status_t rv;
861     proxy_server_conf *conf;
862     ap_slotmem_instance_t *new = NULL;
863     apr_time_t tstamp;
864     apr_array_header_t *ids;
865     int idx;
866
867     /* balancer_post_config() will be called twice during startup.  So, don't
868      * set up the static data the 1st time through. */
869     if (ap_state_query(AP_SQ_MAIN_STATE) == AP_SQ_MS_CREATE_PRE_CONFIG) {
870         return OK;
871     }
872
873     ap_proxy_retry_worker_fn =
874             APR_RETRIEVE_OPTIONAL_FN(ap_proxy_retry_worker);
875     if (!ap_proxy_retry_worker_fn) {
876         ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02230)
877                      "mod_proxy must be loaded for mod_proxy_balancer");
878         return !OK;
879     }
880
881     /*
882      * Get slotmem setups
883      */
884     storage = ap_lookup_provider(AP_SLOTMEM_PROVIDER_GROUP, "shm",
885                                  AP_SLOTMEM_PROVIDER_VERSION);
886     if (!storage) {
887         ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(01177)
888                      "Failed to lookup provider 'shm' for '%s': is "
889                      "mod_slotmem_shm loaded??",
890                      AP_SLOTMEM_PROVIDER_GROUP);
891         return !OK;
892     }
893
894     ids = make_servers_ids(s, ptemp);
895
896     tstamp = apr_time_now();
897     /*
898      * Go thru each Vhost and create the shared mem slotmem for
899      * each balancer's workers
900      */
901     for (idx = 0; s; ++idx) {
902         int i,j;
903         const char *id;
904         proxy_balancer *balancer;
905         ap_slotmem_type_t type;
906         void *sconf = s->module_config;
907         conf = (proxy_server_conf *)ap_get_module_config(sconf, &proxy_module);
908         /*
909          * During create_proxy_config() we created a dummy id. Now that
910          * we have identifying info, we can create the real id
911          */
912         id = APR_ARRAY_IDX(ids, idx, const char *);
913         conf->id = apr_psprintf(pconf, "p%x",
914                                 ap_proxy_hashfunc(id, PROXY_HASHFUNC_DEFAULT));
915         if (conf->bslot) {
916             /* Shared memory already created for this proxy_server_conf.
917              */
918             s = s->next;
919             continue;
920         }
921         if (conf->bal_persist) {
922             type = AP_SLOTMEM_TYPE_PERSIST | AP_SLOTMEM_TYPE_CLEARINUSE;
923         } else {
924             type = 0;
925         }
926         if (conf->balancers->nelts) {
927             conf->max_balancers = conf->balancers->nelts + conf->bgrowth;
928             ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01178) "Doing balancers create: %d, %d (%d)",
929                          (int)ALIGNED_PROXY_BALANCER_SHARED_SIZE,
930                          (int)conf->balancers->nelts, conf->max_balancers);
931
932             rv = storage->create(&new, conf->id,
933                                  ALIGNED_PROXY_BALANCER_SHARED_SIZE,
934                                  conf->max_balancers, type, pconf);
935             if (rv != APR_SUCCESS) {
936                 ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, APLOGNO(01179) "balancer slotmem_create failed");
937                 return !OK;
938             }
939             conf->bslot = new;
940         }
941         conf->storage = storage;
942
943         /* Initialize shared scoreboard data */
944         balancer = (proxy_balancer *)conf->balancers->elts;
945         for (i = 0; i < conf->balancers->nelts; i++, balancer++) {
946             proxy_worker **workers;
947             proxy_worker *worker;
948             proxy_balancer_shared *bshm;
949             const char *sname;
950             unsigned int index;
951
952             /* now that we have the right id, we need to redo the sname field */
953             ap_pstr2_alnum(pconf, balancer->s->name + sizeof(BALANCER_PREFIX) - 1,
954                            &sname);
955             sname = apr_pstrcat(pconf, conf->id, "_", sname, NULL);
956             PROXY_STRNCPY(balancer->s->sname, sname); /* We know this will succeed */
957
958             balancer->max_workers = balancer->workers->nelts + balancer->growth;
959             /* Create global mutex */
960             rv = ap_global_mutex_create(&(balancer->gmutex), NULL, balancer_mutex_type,
961                                         balancer->s->sname, s, pconf, 0);
962             if (rv != APR_SUCCESS || !balancer->gmutex) {
963                 ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, APLOGNO(01180)
964                              "mutex creation of %s : %s failed", balancer_mutex_type,
965                              balancer->s->sname);
966                 return HTTP_INTERNAL_SERVER_ERROR;
967             }
968             apr_pool_cleanup_register(pconf, (void *)s, lock_remove,
969                                       apr_pool_cleanup_null);
970
971             /* setup shm for balancers */
972             bshm = ap_proxy_find_balancershm(storage, conf->bslot, balancer, &index);
973             if (bshm) {
974                 if ((rv = storage->fgrab(conf->bslot, index)) != APR_SUCCESS) {
975                     ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, APLOGNO(02408) "balancer slotmem_fgrab failed");
976                     return !OK;
977                 }
978             }
979             else {
980                 if ((rv = storage->grab(conf->bslot, &index)) != APR_SUCCESS) {
981                     ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, APLOGNO(01181) "balancer slotmem_grab failed");
982                     return !OK;
983                 }
984                 if ((rv = storage->dptr(conf->bslot, index, (void *)&bshm)) != APR_SUCCESS) {
985                     ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, APLOGNO(01182) "balancer slotmem_dptr failed");
986                     return !OK;
987                 }
988             }
989             if ((rv = ap_proxy_share_balancer(balancer, bshm, index)) != APR_SUCCESS) {
990                 ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, APLOGNO(01183) "Cannot share balancer");
991                 return !OK;
992             }
993
994             /* create slotmem slots for workers */
995             ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01184) "Doing workers create: %s (%s), %d, %d [%u]",
996                          balancer->s->name, balancer->s->sname,
997                          (int)ALIGNED_PROXY_WORKER_SHARED_SIZE,
998                          (int)balancer->max_workers, i);
999
1000             rv = storage->create(&new, balancer->s->sname,
1001                                  ALIGNED_PROXY_WORKER_SHARED_SIZE,
1002                                  balancer->max_workers, type, pconf);
1003             if (rv != APR_SUCCESS) {
1004                 ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, APLOGNO(01185) "worker slotmem_create failed");
1005                 return !OK;
1006             }
1007             balancer->wslot = new;
1008             balancer->storage = storage;
1009
1010             /* sync all timestamps */
1011             balancer->wupdated = balancer->s->wupdated = tstamp;
1012
1013             /* now go thru each worker */
1014             workers = (proxy_worker **)balancer->workers->elts;
1015             for (j = 0; j < balancer->workers->nelts; j++, workers++) {
1016                 proxy_worker_shared *shm;
1017
1018                 worker = *workers;
1019
1020                 shm = ap_proxy_find_workershm(storage, balancer->wslot, worker, &index);
1021                 if (shm) {
1022                     if ((rv = storage->fgrab(balancer->wslot, index)) != APR_SUCCESS) {
1023                         ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, APLOGNO(02409) "worker slotmem_fgrab failed");
1024                         return !OK;
1025                     }
1026                 }
1027                 else {
1028                     if ((rv = storage->grab(balancer->wslot, &index)) != APR_SUCCESS) {
1029                         ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, APLOGNO(01186) "worker slotmem_grab failed");
1030                         return !OK;
1031
1032                     }
1033                     if ((rv = storage->dptr(balancer->wslot, index, (void *)&shm)) != APR_SUCCESS) {
1034                         ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, APLOGNO(01187) "worker slotmem_dptr failed");
1035                         return !OK;
1036                     }
1037                 }
1038                 if ((rv = ap_proxy_share_worker(worker, shm, index)) != APR_SUCCESS) {
1039                     ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, APLOGNO(01188) "Cannot share worker");
1040                     return !OK;
1041                 }
1042                 worker->s->updated = tstamp;
1043             }
1044             if (conf->bal_persist) {
1045                 /* We could have just read-in a persisted config. Force a sync. */
1046                 balancer->wupdated--;
1047                 ap_proxy_sync_balancer(balancer, s, conf);
1048             }
1049         }
1050         s = s->next;
1051     }
1052
1053     return OK;
1054 }
1055
1056 static void create_radio(const char *name, unsigned int flag, request_rec *r)
1057 {
1058     ap_rvputs(r, "<td><label for='", name, "1'>On</label> <input name='", name, "' id='", name, "1' value='1' type=radio", NULL);
1059     if (flag)
1060         ap_rputs(" checked", r);
1061     ap_rvputs(r, "> <br/> <label for='", name, "0'>Off</label> <input name='", name, "' id='", name, "0' value='0' type=radio", NULL);
1062     if (!flag)
1063         ap_rputs(" checked", r);
1064     ap_rputs("></td>\n", r);
1065 }
1066
1067 static void push2table(const char *input, apr_table_t *params,
1068                        const char *allowed[], apr_pool_t *p)
1069 {
1070     char *args;
1071     char *tok, *val;
1072     char *key;
1073
1074     if (input == NULL) {
1075         return;
1076     }
1077     args = apr_pstrdup(p, input);
1078
1079     key = apr_strtok(args, "&", &tok);
1080     while (key) {
1081         val = strchr(key, '=');
1082         if (val) {
1083             *val++ = '\0';
1084         }
1085         else {
1086             val = "";
1087         }
1088         ap_unescape_url(key);
1089         ap_unescape_url(val);
1090         if (allowed == NULL) { /* allow all */
1091             apr_table_set(params, key, val);
1092         }
1093         else {
1094             const char **ok = allowed;
1095             while (*ok) {
1096                 if (strcmp(*ok, key) == 0) {
1097                     apr_table_set(params, key, val);
1098                     break;
1099                 }
1100                 ok++;
1101             }
1102         }
1103         key = apr_strtok(NULL, "&", &tok);
1104     }
1105 }
1106
1107 /*
1108  * Process the paramters and add or update the worker of the balancer
1109  */
1110 static int balancer_process_balancer_worker(request_rec *r, proxy_server_conf *conf,
1111                                             proxy_balancer *bsel,
1112                                             proxy_worker *wsel, int ok2change,
1113                                             apr_table_t *params)
1114 {
1115     apr_status_t rv;
1116     /* First set the params */
1117     if (wsel && ok2change) {
1118         const char *val;
1119         int was_usable = PROXY_WORKER_IS_USABLE(wsel);
1120
1121         ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01192) "settings worker params");
1122
1123         if ((val = apr_table_get(params, "w_lf"))) {
1124             int ival;
1125             double fval = atof(val);
1126             ival = fval * 100.0;
1127             if (ival >= 100 && ival <= 10000) {
1128                 wsel->s->lbfactor = ival;
1129                 if (bsel)
1130                     recalc_factors(bsel);
1131             }
1132         }
1133         if ((val = apr_table_get(params, "w_wr"))) {
1134             if (*val && strlen(val) < sizeof(wsel->s->route))
1135                 strcpy(wsel->s->route, val);
1136             else
1137                 *wsel->s->route = '\0';
1138         }
1139         if ((val = apr_table_get(params, "w_rr"))) {
1140             if (*val && strlen(val) < sizeof(wsel->s->redirect))
1141                 strcpy(wsel->s->redirect, val);
1142             else
1143                 *wsel->s->redirect = '\0';
1144         }
1145         /*
1146          * TODO: Look for all 'w_status_#' keys and then loop thru
1147          * on that # character, since the character == the flag
1148          */
1149         if ((val = apr_table_get(params, "w_status_I"))) {
1150             ap_proxy_set_wstatus(PROXY_WORKER_IGNORE_ERRORS_FLAG, atoi(val), wsel);
1151         }
1152         if ((val = apr_table_get(params, "w_status_N"))) {
1153             ap_proxy_set_wstatus(PROXY_WORKER_DRAIN_FLAG, atoi(val), wsel);
1154         }
1155         if ((val = apr_table_get(params, "w_status_D"))) {
1156             ap_proxy_set_wstatus(PROXY_WORKER_DISABLED_FLAG, atoi(val), wsel);
1157         }
1158         if ((val = apr_table_get(params, "w_status_H"))) {
1159             ap_proxy_set_wstatus(PROXY_WORKER_HOT_STANDBY_FLAG, atoi(val), wsel);
1160         }
1161         if ((val = apr_table_get(params, "w_status_R"))) {
1162             ap_proxy_set_wstatus(PROXY_WORKER_HOT_SPARE_FLAG, atoi(val), wsel);
1163         }
1164         if ((val = apr_table_get(params, "w_status_S"))) {
1165             ap_proxy_set_wstatus(PROXY_WORKER_STOPPED_FLAG, atoi(val), wsel);
1166         }
1167         if ((val = apr_table_get(params, "w_status_C"))) {
1168             ap_proxy_set_wstatus(PROXY_WORKER_HC_FAIL_FLAG, atoi(val), wsel);
1169         }
1170         if ((val = apr_table_get(params, "w_ls"))) {
1171             int ival = atoi(val);
1172             if (ival >= 0 && ival <= 99) {
1173                 wsel->s->lbset = ival;
1174              }
1175         }
1176         if ((val = apr_table_get(params, "w_hi"))) {
1177             apr_interval_time_t hci;
1178             if (ap_timeout_parameter_parse(val, &hci, "ms") == APR_SUCCESS) {
1179                 if (hci >= AP_WD_TM_SLICE) {
1180                     wsel->s->interval = hci;
1181                 }
1182             }
1183         }
1184         if ((val = apr_table_get(params, "w_hp"))) {
1185             int ival = atoi(val);
1186             if (ival >= 1) {
1187                 wsel->s->passes = ival;
1188              }
1189         }
1190         if ((val = apr_table_get(params, "w_hf"))) {
1191             int ival = atoi(val);
1192             if (ival >= 1) {
1193                 wsel->s->fails = ival;
1194              }
1195         }
1196         if ((val = apr_table_get(params, "w_hm"))) {
1197             proxy_hcmethods_t *method = proxy_hcmethods;
1198             for (; method->name; method++) {
1199                 if (!ap_cstr_casecmp(method->name, val) && method->implemented)
1200                     wsel->s->method = method->method;
1201             }
1202         }
1203         if ((val = apr_table_get(params, "w_hu"))) {
1204             if (*val && strlen(val) < sizeof(wsel->s->hcuri))
1205                 strcpy(wsel->s->hcuri, val);
1206             else
1207                 *wsel->s->hcuri = '\0';
1208         }
1209         if (hc_valid_expr_f && (val = apr_table_get(params, "w_he"))) {
1210             if (*val && hc_valid_expr_f(r, val) && strlen(val) < sizeof(wsel->s->hcexpr))
1211                 strcpy(wsel->s->hcexpr, val);
1212             else
1213                 *wsel->s->hcexpr = '\0';
1214         }
1215         /* If the health check method doesn't support an expr, then null it */
1216         if (wsel->s->method == NONE || wsel->s->method == TCP) {
1217             *wsel->s->hcexpr = '\0';
1218         }
1219         /* if enabling, we need to reset all lb params */
1220         if (bsel && !was_usable && PROXY_WORKER_IS_USABLE(wsel)) {
1221             bsel->s->need_reset = 1;
1222         }
1223
1224     }
1225
1226     if (bsel && ok2change) {
1227         const char *val;
1228         int ival;
1229         ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01193)
1230                       "settings balancer params");
1231         if ((val = apr_table_get(params, "b_lbm"))) {
1232             if ((strlen(val) < (sizeof(bsel->s->lbpname)-1)) &&
1233                 strcmp(val, bsel->s->lbpname)) {
1234                 proxy_balancer_method *lbmethod;
1235                 lbmethod = ap_lookup_provider(PROXY_LBMETHOD, val, "0");
1236                 if (lbmethod) {
1237                     PROXY_STRNCPY(bsel->s->lbpname, val);
1238                     bsel->lbmethod = lbmethod;
1239                     bsel->s->wupdated = apr_time_now();
1240                     bsel->s->need_reset = 1;
1241                 }
1242             }
1243         }
1244         if ((val = apr_table_get(params, "b_tmo"))) {
1245             ival = atoi(val);
1246             if (ival >= 0 && ival <= 7200) { /* 2 hrs enuff? */
1247                 bsel->s->timeout = apr_time_from_sec(ival);
1248             }
1249         }
1250         if ((val = apr_table_get(params, "b_max"))) {
1251             ival = atoi(val);
1252             if (ival >= 0 && ival <= 99) {
1253                 bsel->s->max_attempts = ival;
1254             }
1255         }
1256         if ((val = apr_table_get(params, "b_sforce"))) {
1257             ival = atoi(val);
1258             bsel->s->sticky_force = (ival != 0);
1259         }
1260         if ((val = apr_table_get(params, "b_ss")) && *val) {
1261             if (strlen(val) < (sizeof(bsel->s->sticky_path)-1)) {
1262                 if (*val == '-' && *(val+1) == '\0')
1263                     *bsel->s->sticky_path = *bsel->s->sticky = '\0';
1264                 else {
1265                     char *path;
1266                     PROXY_STRNCPY(bsel->s->sticky_path, val);
1267                     PROXY_STRNCPY(bsel->s->sticky, val);
1268
1269                     if ((path = strchr((char *)bsel->s->sticky, '|'))) {
1270                         *path++ = '\0';
1271                         PROXY_STRNCPY(bsel->s->sticky_path, path);
1272                     }
1273                 }
1274             }
1275         }
1276         if ((val = apr_table_get(params, "b_wyes")) &&
1277             (*val == '1' && *(val+1) == '\0') &&
1278             (val = apr_table_get(params, "b_nwrkr"))) {
1279             char *ret;
1280             proxy_worker *nworker;
1281             nworker = ap_proxy_get_worker(r->pool, bsel, conf, val);
1282             if (!nworker && storage->num_free_slots(bsel->wslot)) {
1283 #if APR_HAS_THREADS
1284                 if ((rv = PROXY_GLOBAL_LOCK(bsel)) != APR_SUCCESS) {
1285                     ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01194)
1286                                   "%s: Lock failed for adding worker",
1287                                   bsel->s->name);
1288                 }
1289 #endif
1290                 ret = ap_proxy_define_worker(conf->pool, &nworker, bsel, conf, val, 0);
1291                 if (!ret) {
1292                     unsigned int index;
1293                     proxy_worker_shared *shm;
1294                     PROXY_COPY_CONF_PARAMS(nworker, conf);
1295                     if ((rv = storage->grab(bsel->wslot, &index)) != APR_SUCCESS) {
1296                         ap_log_rerror(APLOG_MARK, APLOG_EMERG, rv, r, APLOGNO(01195)
1297                                       "worker slotmem_grab failed");
1298 #if APR_HAS_THREADS
1299                         if ((rv = PROXY_GLOBAL_UNLOCK(bsel)) != APR_SUCCESS) {
1300                             ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01196)
1301                                           "%s: Unlock failed for adding worker",
1302                                           bsel->s->name);
1303                         }
1304 #endif
1305                         return HTTP_BAD_REQUEST;
1306                     }
1307                     if ((rv = storage->dptr(bsel->wslot, index, (void *)&shm)) != APR_SUCCESS) {
1308                         ap_log_rerror(APLOG_MARK, APLOG_EMERG, rv, r, APLOGNO(01197)
1309                                       "worker slotmem_dptr failed");
1310 #if APR_HAS_THREADS
1311                         if ((rv = PROXY_GLOBAL_UNLOCK(bsel)) != APR_SUCCESS) {
1312                             ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01198)
1313                                           "%s: Unlock failed for adding worker",
1314                                           bsel->s->name);
1315                         }
1316 #endif
1317                         return HTTP_BAD_REQUEST;
1318                     }
1319                     if ((rv = ap_proxy_share_worker(nworker, shm, index)) != APR_SUCCESS) {
1320                         ap_log_rerror(APLOG_MARK, APLOG_EMERG, rv, r, APLOGNO(01199)
1321                                       "Cannot share worker");
1322 #if APR_HAS_THREADS
1323                         if ((rv = PROXY_GLOBAL_UNLOCK(bsel)) != APR_SUCCESS) {
1324                             ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01200)
1325                                           "%s: Unlock failed for adding worker",
1326                                           bsel->s->name);
1327                         }
1328 #endif
1329                         return HTTP_BAD_REQUEST;
1330                     }
1331                     if ((rv = ap_proxy_initialize_worker(nworker, r->server, conf->pool)) != APR_SUCCESS) {
1332                         ap_log_rerror(APLOG_MARK, APLOG_EMERG, rv, r, APLOGNO(01201)
1333                                       "Cannot init worker");
1334 #if APR_HAS_THREADS
1335                         if ((rv = PROXY_GLOBAL_UNLOCK(bsel)) != APR_SUCCESS) {
1336                             ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01202)
1337                                           "%s: Unlock failed for adding worker",
1338                                           bsel->s->name);
1339                         }
1340 #endif
1341                         return HTTP_BAD_REQUEST;
1342                     }
1343                     /* sync all timestamps */
1344                     bsel->wupdated = bsel->s->wupdated = nworker->s->updated = apr_time_now();
1345                     /* by default, all new workers are disabled */
1346                     ap_proxy_set_wstatus(PROXY_WORKER_DISABLED_FLAG, 1, nworker);
1347                 } else {
1348                             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10163)
1349                                   "%s: failed to add worker %s",
1350                                   bsel->s->name, val);
1351 #if APR_HAS_THREADS
1352                     PROXY_GLOBAL_UNLOCK(bsel);
1353 #endif
1354                     return HTTP_BAD_REQUEST;
1355                 }
1356 #if APR_HAS_THREADS
1357                 if ((rv = PROXY_GLOBAL_UNLOCK(bsel)) != APR_SUCCESS) {
1358                     ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01203)
1359                                   "%s: Unlock failed for adding worker",
1360                                   bsel->s->name);
1361                 }
1362 #endif
1363             } else {
1364                 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10164)
1365                                   "%s: failed to add worker %s",
1366                                   bsel->s->name, val);
1367                 return HTTP_BAD_REQUEST;
1368             }
1369
1370         }
1371
1372     }
1373     return APR_SUCCESS;
1374 }
1375
1376 /*
1377  * builds the page and links to configure via HTLM or XML.
1378  */
1379 static void balancer_display_page(request_rec *r, proxy_server_conf *conf,
1380                                   proxy_balancer *bsel,
1381                                   proxy_worker *wsel,
1382                                   int usexml)
1383 {
1384     const char *action;
1385     proxy_balancer *balancer;
1386     proxy_worker *worker;
1387     proxy_worker **workers;
1388     int i, n;
1389     action = ap_construct_url(r->pool, r->uri, r);
1390     ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01204) "genning page");
1391
1392     if (usexml) {
1393         char date[APR_RFC822_DATE_LEN];
1394         ap_set_content_type(r, "text/xml");
1395         ap_rputs("<?xml version='1.0' encoding='UTF-8' ?>\n", r);
1396         ap_rputs("<httpd:manager xmlns:httpd='http://httpd.apache.org'>\n", r);
1397         ap_rputs("  <httpd:balancers>\n", r);
1398         balancer = (proxy_balancer *)conf->balancers->elts;
1399         for (i = 0; i < conf->balancers->nelts; i++) {
1400             ap_rputs("    <httpd:balancer>\n", r);
1401             /* Start proxy_balancer */
1402             ap_rvputs(r, "      <httpd:name>", balancer->s->name, "</httpd:name>\n", NULL);
1403             if (*balancer->s->sticky) {
1404                 ap_rvputs(r, "      <httpd:stickysession>", ap_escape_html(r->pool, balancer->s->sticky),
1405                           "</httpd:stickysession>\n", NULL);
1406                 ap_rprintf(r,
1407                            "      <httpd:nofailover>%s</httpd:nofailover>\n",
1408                            (balancer->s->sticky_force ? "On" : "Off"));
1409             }
1410             ap_rprintf(r,
1411                        "      <httpd:timeout>%" APR_TIME_T_FMT "</httpd:timeout>\n",
1412                        apr_time_sec(balancer->s->timeout));
1413             if (balancer->s->max_attempts_set) {
1414                 ap_rprintf(r,
1415                            "      <httpd:maxattempts>%d</httpd:maxattempts>\n",
1416                            balancer->s->max_attempts);
1417             }
1418             ap_rvputs(r, "      <httpd:lbmethod>", balancer->lbmethod->name,
1419                       "</httpd:lbmethod>\n", NULL);
1420             if (*balancer->s->sticky) {
1421                 ap_rprintf(r,
1422                            "      <httpd:scolonpathdelim>%s</httpd:scolonpathdelim>\n",
1423                            (balancer->s->scolonsep ? "On" : "Off"));
1424             }
1425             /* End proxy_balancer */
1426             ap_rputs("      <httpd:workers>\n", r);
1427             workers = (proxy_worker **)balancer->workers->elts;
1428             for (n = 0; n < balancer->workers->nelts; n++) {
1429                 worker = *workers;
1430                 /* Start proxy_worker */
1431                 ap_rputs("        <httpd:worker>\n", r);
1432                 ap_rvputs(r, "          <httpd:name>", ap_proxy_worker_name(r->pool, worker),
1433                           "</httpd:name>\n", NULL);
1434                 ap_rvputs(r, "          <httpd:scheme>", worker->s->scheme,
1435                           "</httpd:scheme>\n", NULL);
1436                 ap_rvputs(r, "          <httpd:hostname>", worker->s->hostname_ex,
1437                           "</httpd:hostname>\n", NULL);
1438                 ap_rprintf(r, "          <httpd:loadfactor>%.2f</httpd:loadfactor>\n",
1439                           (float)(worker->s->lbfactor)/100.0);
1440                 ap_rprintf(r,
1441                            "          <httpd:port>%d</httpd:port>\n",
1442                            worker->s->port);
1443                 ap_rprintf(r, "          <httpd:min>%d</httpd:min>\n",
1444                            worker->s->min);
1445                 ap_rprintf(r, "          <httpd:smax>%d</httpd:smax>\n",
1446                            worker->s->smax);
1447                 ap_rprintf(r, "          <httpd:max>%d</httpd:max>\n",
1448                            worker->s->hmax);
1449                 ap_rprintf(r,
1450                            "          <httpd:ttl>%" APR_TIME_T_FMT "</httpd:ttl>\n",
1451                            apr_time_sec(worker->s->ttl));
1452                 if (worker->s->timeout_set) {
1453                     ap_rprintf(r,
1454                                "          <httpd:timeout>%" APR_TIME_T_FMT "</httpd:timeout>\n",
1455                                apr_time_sec(worker->s->timeout));
1456                 }
1457                 if (worker->s->acquire_set) {
1458                     ap_rprintf(r,
1459                                "          <httpd:acquire>%" APR_TIME_T_FMT "</httpd:acquire>\n",
1460                                apr_time_msec(worker->s->acquire));
1461                 }
1462                 if (worker->s->recv_buffer_size_set) {
1463                     ap_rprintf(r,
1464                                "          <httpd:recv_buffer_size>%" APR_SIZE_T_FMT "</httpd:recv_buffer_size>\n",
1465                                worker->s->recv_buffer_size);
1466                 }
1467                 if (worker->s->io_buffer_size_set) {
1468                     ap_rprintf(r,
1469                                "          <httpd:io_buffer_size>%" APR_SIZE_T_FMT "</httpd:io_buffer_size>\n",
1470                                worker->s->io_buffer_size);
1471                 }
1472                 if (worker->s->keepalive_set) {
1473                     ap_rprintf(r,
1474                                "          <httpd:keepalive>%s</httpd:keepalive>\n",
1475                                (worker->s->keepalive ? "On" : "Off"));
1476                 }
1477                 /* Begin proxy_worker_stat */
1478                 ap_rputs("          <httpd:status>", r);
1479                 ap_rputs(ap_proxy_parse_wstatus(r->pool, worker), r);
1480                 ap_rputs("</httpd:status>\n", r);
1481                 if ((worker->s->error_time > 0) && apr_rfc822_date(date, worker->s->error_time) == APR_SUCCESS) {
1482                     ap_rvputs(r, "          <httpd:error_time>", date,
1483                               "</httpd:error_time>\n", NULL);
1484                 }
1485                 ap_rprintf(r,
1486                            "          <httpd:retries>%d</httpd:retries>\n",
1487                            worker->s->retries);
1488                 ap_rprintf(r,
1489                            "          <httpd:lbstatus>%d</httpd:lbstatus>\n",
1490                            worker->s->lbstatus);
1491                 ap_rprintf(r,
1492                            "          <httpd:loadfactor>%.2f</httpd:loadfactor>\n",
1493                            (float)(worker->s->lbfactor)/100.0);
1494                 ap_rprintf(r,
1495                            "          <httpd:transferred>%" APR_OFF_T_FMT "</httpd:transferred>\n",
1496                            worker->s->transferred);
1497                 ap_rprintf(r,
1498                            "          <httpd:read>%" APR_OFF_T_FMT "</httpd:read>\n",
1499                            worker->s->read);
1500                 ap_rprintf(r,
1501                            "          <httpd:elected>%" APR_SIZE_T_FMT "</httpd:elected>\n",
1502                            worker->s->elected);
1503                 ap_rvputs(r, "          <httpd:route>",
1504                           ap_escape_html(r->pool, worker->s->route),
1505                           "</httpd:route>\n", NULL);
1506                 ap_rvputs(r, "          <httpd:redirect>",
1507                           ap_escape_html(r->pool, worker->s->redirect),
1508                           "</httpd:redirect>\n", NULL);
1509                 ap_rprintf(r,
1510                            "          <httpd:busy>%" APR_SIZE_T_FMT "</httpd:busy>\n",
1511                            worker->s->busy);
1512                 ap_rprintf(r, "          <httpd:lbset>%d</httpd:lbset>\n",
1513                            worker->s->lbset);
1514                 /* End proxy_worker_stat */
1515                 if (!ap_cstr_casecmp(worker->s->scheme, "ajp")) {
1516                     ap_rputs("          <httpd:flushpackets>", r);
1517                     switch (worker->s->flush_packets) {
1518                         case flush_off:
1519                             ap_rputs("Off", r);
1520                             break;
1521                         case flush_on:
1522                             ap_rputs("On", r);
1523                             break;
1524                         case flush_auto:
1525                             ap_rputs("Auto", r);
1526                             break;
1527                     }
1528                     ap_rputs("</httpd:flushpackets>\n", r);
1529                     if (worker->s->flush_packets == flush_auto) {
1530                         ap_rprintf(r,
1531                                    "          <httpd:flushwait>%d</httpd:flushwait>\n",
1532                                    worker->s->flush_wait);
1533                     }
1534                     if (worker->s->ping_timeout_set) {
1535                         ap_rprintf(r,
1536                                    "          <httpd:ping>%" APR_TIME_T_FMT "</httpd:ping>",
1537                                    apr_time_msec(worker->s->ping_timeout));
1538                     }
1539                 }
1540                 if (worker->s->disablereuse_set) {
1541                     ap_rprintf(r,
1542                                "      <httpd:disablereuse>%s</httpd:disablereuse>\n",
1543                                (worker->s->disablereuse ? "On" : "Off"));
1544                 }
1545                 if (worker->s->conn_timeout_set) {
1546                     ap_rprintf(r,
1547                                "          <httpd:connectiontimeout>%" APR_TIME_T_FMT "</httpd:connectiontimeout>\n",
1548                                apr_time_msec(worker->s->conn_timeout));
1549                 }
1550                 if (worker->s->retry_set) {
1551                     ap_rprintf(r,
1552                                "          <httpd:retry>%" APR_TIME_T_FMT "</httpd:retry>\n",
1553                                apr_time_sec(worker->s->retry));
1554                 }
1555                 ap_rputs("        </httpd:worker>\n", r);
1556                 ++workers;
1557             }
1558             ap_rputs("      </httpd:workers>\n", r);
1559             ap_rputs("    </httpd:balancer>\n", r);
1560             ++balancer;
1561         }
1562         ap_rputs("  </httpd:balancers>\n", r);
1563         ap_rputs("</httpd:manager>", r);
1564     }
1565     else {
1566         ap_set_content_type(r, "text/html; charset=ISO-8859-1");
1567         ap_rputs(DOCTYPE_HTML_3_2
1568                  "<html><head><title>Balancer Manager</title>\n", r);
1569         ap_rputs("<style type='text/css'>\n"
1570                  "table {\n"
1571                  " border-width: 1px;\n"
1572                  " border-spacing: 3px;\n"
1573                  " border-style: solid;\n"
1574                  " border-color: gray;\n"
1575                  " border-collapse: collapse;\n"
1576                  " background-color: white;\n"
1577                  " text-align: center;\n"
1578                  "}\n"
1579                  "th {\n"
1580                  " border-width: 1px;\n"
1581                  " padding: 2px;\n"
1582                  " border-style: dotted;\n"
1583                  " border-color: gray;\n"
1584                  " background-color: lightgray;\n"
1585                  " text-align: center;\n"
1586                  "}\n"
1587                  "td {\n"
1588                  " border-width: 1px;\n"
1589                  " padding: 2px;\n"
1590                  " border-style: dotted;\n"
1591                  " border-color: gray;\n"
1592                  " background-color: white;\n"
1593                  " text-align: center;\n"
1594                  "}\n"
1595                  "</style>\n</head>\n", r);
1596         ap_rputs("<body><h1>Load Balancer Manager for ", r);
1597         ap_rvputs(r, ap_escape_html(r->pool, ap_get_server_name(r)),
1598                   "</h1>\n\n", NULL);
1599         ap_rvputs(r, "<dl><dt>Server Version: ",
1600                   ap_get_server_description(), "</dt>\n", NULL);
1601         ap_rvputs(r, "<dt>Server Built: ",
1602                   ap_get_server_built(), "</dt>\n", NULL);
1603         ap_rvputs(r, "<dt>Balancer changes will ", conf->bal_persist ? "" : "NOT ",
1604                   "be persisted on restart.</dt>", NULL);
1605         ap_rvputs(r, "<dt>Balancers are ", conf->inherit ? "" : "NOT ",
1606                   "inherited from main server.</dt>", NULL);
1607         ap_rvputs(r, "<dt>ProxyPass settings are ", conf->ppinherit ? "" : "NOT ",
1608                   "inherited from main server.</dt>", NULL);
1609         ap_rputs("</dl>\n", r);
1610         balancer = (proxy_balancer *)conf->balancers->elts;
1611         for (i = 0; i < conf->balancers->nelts; i++) {
1612
1613             ap_rputs("<hr />\n<h3>LoadBalancer Status for ", r);
1614             ap_rvputs(r, "<a href=\"", ap_escape_uri(r->pool, r->uri), "?b=",
1615                       balancer->s->name + sizeof(BALANCER_PREFIX) - 1,
1616                       "&amp;nonce=", balancer->s->nonce,
1617                       "\">", NULL);
1618             ap_rvputs(r, balancer->s->name, "</a> [",balancer->s->sname, "]</h3>\n", NULL);
1619             ap_rputs("\n\n<table><tr>"
1620                 "<th>MaxMembers</th><th>StickySession</th><th>DisableFailover</th><th>Timeout</th><th>FailoverAttempts</th><th>Method</th>"
1621                 "<th>Path</th><th>Active</th></tr>\n<tr>", r);
1622             /* the below is a safe cast, since the number of slots total will
1623              * never be more than max_workers, which is restricted to int */
1624             ap_rprintf(r, "<td>%d [%d Used]</td>\n", balancer->max_workers,
1625                        balancer->max_workers - (int)storage->num_free_slots(balancer->wslot));
1626             if (*balancer->s->sticky) {
1627                 if (strcmp(balancer->s->sticky, balancer->s->sticky_path)) {
1628                     ap_rvputs(r, "<td>", ap_escape_html(r->pool, balancer->s->sticky), " | ",
1629                               ap_escape_html(r->pool, balancer->s->sticky_path), NULL);
1630                 }
1631                 else {
1632                     ap_rvputs(r, "<td>", ap_escape_html(r->pool, balancer->s->sticky), NULL);
1633                 }
1634             }
1635             else {
1636                 ap_rputs("<td> (None) ", r);
1637             }
1638             ap_rprintf(r, "</td><td>%s</td>\n",
1639                        balancer->s->sticky_force ? "On" : "Off");
1640             ap_rprintf(r, "<td>%" APR_TIME_T_FMT "</td>",
1641                 apr_time_sec(balancer->s->timeout));
1642             ap_rprintf(r, "<td>%d</td>\n", balancer->s->max_attempts);
1643             ap_rprintf(r, "<td>%s</td>\n",
1644                        balancer->s->lbpname);
1645             ap_rputs("<td>", r);
1646             if (*balancer->s->vhost) {
1647                 ap_rvputs(r, balancer->s->vhost, " -> ", NULL);
1648             }
1649             ap_rvputs(r, balancer->s->vpath, "</td>\n", NULL);
1650             ap_rprintf(r, "<td>%s</td>\n",
1651                        !balancer->s->inactive ? "Yes" : "No");
1652             ap_rputs("</table>\n<br />", r);
1653             ap_rputs("\n\n<table><tr>"
1654                 "<th>Worker URL</th>"
1655                 "<th>Route</th><th>RouteRedir</th>"
1656                 "<th>Factor</th><th>Set</th><th>Status</th>"
1657                 "<th>Elected</th><th>Busy</th><th>Load</th><th>To</th><th>From</th>", r);
1658             if (set_worker_hc_param_f) {
1659                 ap_rputs("<th>HC Method</th><th>HC Interval</th><th>Passes</th><th>Fails</th><th>HC uri</th><th>HC Expr</th>", r);
1660             }
1661             ap_rputs("</tr>\n", r);
1662
1663             workers = (proxy_worker **)balancer->workers->elts;
1664             for (n = 0; n < balancer->workers->nelts; n++) {
1665                 char fbuf[50];
1666                 worker = *workers;
1667                 ap_rvputs(r, "<tr>\n<td><a href=\"",
1668                           ap_escape_uri(r->pool, r->uri), "?b=",
1669                           balancer->s->name + sizeof(BALANCER_PREFIX) - 1, "&amp;w=",
1670                           ap_escape_uri(r->pool, worker->s->name),
1671                           "&amp;nonce=", balancer->s->nonce,
1672                           "\">", NULL);
1673                 ap_rvputs(r, (*worker->s->uds_path ? "<i>" : ""), ap_proxy_worker_name(r->pool, worker),
1674                           (*worker->s->uds_path ? "</i>" : ""), "</a></td>", NULL);
1675                 ap_rvputs(r, "<td>", ap_escape_html(r->pool, worker->s->route),
1676                           NULL);
1677                 ap_rvputs(r, "</td><td>",
1678                           ap_escape_html(r->pool, worker->s->redirect), NULL);
1679                 ap_rprintf(r, "</td><td>%.2f</td>", (float)(worker->s->lbfactor)/100.0);
1680                 ap_rprintf(r, "<td>%d</td><td>", worker->s->lbset);
1681                 ap_rvputs(r, ap_proxy_parse_wstatus(r->pool, worker), NULL);
1682                 ap_rputs("</td>", r);
1683                 ap_rprintf(r, "<td>%" APR_SIZE_T_FMT "</td>", worker->s->elected);
1684                 ap_rprintf(r, "<td>%" APR_SIZE_T_FMT "</td>", worker->s->busy);
1685                 ap_rprintf(r, "<td>%d</td><td>", worker->s->lbstatus);
1686                 ap_rputs(apr_strfsize(worker->s->transferred, fbuf), r);
1687                 ap_rputs("</td><td>", r);
1688                 ap_rputs(apr_strfsize(worker->s->read, fbuf), r);
1689                 if (set_worker_hc_param_f) {
1690                     ap_rprintf(r, "</td><td>%s</td>", ap_proxy_show_hcmethod(worker->s->method));
1691                     ap_rprintf(r, "<td>%" APR_TIME_T_FMT "ms</td>", apr_time_as_msec(worker->s->interval));
1692                     ap_rprintf(r, "<td>%d (%d)</td>", worker->s->passes,worker->s->pcount);
1693                     ap_rprintf(r, "<td>%d (%d)</td>", worker->s->fails, worker->s->fcount);
1694                     ap_rprintf(r, "<td>%s</td>", ap_escape_html(r->pool, worker->s->hcuri));
1695                     ap_rprintf(r, "<td>%s", worker->s->hcexpr);
1696                 }
1697                 ap_rputs("</td></tr>\n", r);
1698
1699                 ++workers;
1700             }
1701             ap_rputs("</table>\n", r);
1702             ++balancer;
1703         }
1704         ap_rputs("<hr />\n", r);
1705         if (hc_show_exprs_f) {
1706             hc_show_exprs_f(r);
1707         }
1708         if (wsel && bsel) {
1709             ap_rputs("<h3>Edit worker settings for ", r);
1710             ap_rvputs(r, (*wsel->s->uds_path?"<i>":""), ap_proxy_worker_name(r->pool, wsel), (*wsel->s->uds_path?"</i>":""), "</h3>\n", NULL);
1711             ap_rputs("<form method='POST' enctype='application/x-www-form-urlencoded' action=\"", r);
1712             ap_rvputs(r, ap_escape_uri(r->pool, action), "\">\n", NULL);
1713             ap_rputs("<table><tr><td>Load factor:</td><td><input name='w_lf' id='w_lf' type=text ", r);
1714             ap_rprintf(r, "value='%.2f'></td></tr>\n", (float)(wsel->s->lbfactor)/100.0);
1715             ap_rputs("<tr><td>LB Set:</td><td><input name='w_ls' id='w_ls' type=text ", r);
1716             ap_rprintf(r, "value='%d'></td></tr>\n", wsel->s->lbset);
1717             ap_rputs("<tr><td>Route:</td><td><input name='w_wr' id='w_wr' type=text ", r);
1718             ap_rvputs(r, "value=\"", ap_escape_html(r->pool, wsel->s->route),
1719                       NULL);
1720             ap_rputs("\"></td></tr>\n", r);
1721             ap_rputs("<tr><td>Route Redirect:</td><td><input name='w_rr' id='w_rr' type=text ", r);
1722             ap_rvputs(r, "value=\"", ap_escape_html(r->pool, wsel->s->redirect),
1723                       NULL);
1724             ap_rputs("\"></td></tr>\n", r);
1725             ap_rputs("<tr><td>Status:</td>", r);
1726             ap_rputs("<td><table><tr>"
1727                      "<th>Ignore Errors</th>"
1728                      "<th>Draining Mode</th>"
1729                      "<th>Disabled</th>"
1730                      "<th>Hot Standby</th>"
1731                      "<th>Hot Spare</th>", r);
1732             if (hc_show_exprs_f) {
1733                 ap_rputs("<th>HC Fail</th>", r);
1734             }
1735             ap_rputs("<th>Stopped</th></tr>\n<tr>", r);
1736             create_radio("w_status_I", (PROXY_WORKER_IS(wsel, PROXY_WORKER_IGNORE_ERRORS)), r);
1737             create_radio("w_status_N", (PROXY_WORKER_IS(wsel, PROXY_WORKER_DRAIN)), r);
1738             create_radio("w_status_D", (PROXY_WORKER_IS(wsel, PROXY_WORKER_DISABLED)), r);
1739             create_radio("w_status_H", (PROXY_WORKER_IS(wsel, PROXY_WORKER_HOT_STANDBY)), r);
1740             create_radio("w_status_R", (PROXY_WORKER_IS(wsel, PROXY_WORKER_HOT_SPARE)), r);
1741             if (hc_show_exprs_f) {
1742                 create_radio("w_status_C", (PROXY_WORKER_IS(wsel, PROXY_WORKER_HC_FAIL)), r);
1743             }
1744             create_radio("w_status_S", (PROXY_WORKER_IS(wsel, PROXY_WORKER_STOPPED)), r);
1745             ap_rputs("</tr></table></td></tr>\n", r);
1746             if (hc_select_exprs_f) {
1747                 proxy_hcmethods_t *method = proxy_hcmethods;
1748                 ap_rputs("<tr><td colspan='2'>\n<table align='center'><tr><th>Health Check param</th><th>Value</th></tr>\n", r);
1749                 ap_rputs("<tr><td>Method</td><td><select name='w_hm'>\n", r);
1750                 for (; method->name; method++) {
1751                     if (method->implemented) {
1752                         ap_rprintf(r, "<option value='%s' %s >%s</option>\n",
1753                                 method->name,
1754                                 (wsel->s->method == method->method) ? "selected" : "",
1755                                 method->name);
1756                     }
1757                 }
1758                 ap_rputs("</select>\n</td></tr>\n", r);
1759                 ap_rputs("<tr><td>Expr</td><td><select name='w_he'>\n", r);
1760                 hc_select_exprs_f(r, wsel->s->hcexpr);
1761                 ap_rputs("</select>\n</td></tr>\n", r);
1762                 ap_rprintf(r, "<tr><td>Interval (ms)</td><td><input name='w_hi' id='w_hi' type='text'"
1763                            "value='%" APR_TIME_T_FMT "'></td></tr>\n", apr_time_as_msec(wsel->s->interval));
1764                 ap_rprintf(r, "<tr><td>Passes trigger</td><td><input name='w_hp' id='w_hp' type='text'"
1765                            "value='%d'></td></tr>\n", wsel->s->passes);
1766                 ap_rprintf(r, "<tr><td>Fails trigger)</td><td><input name='w_hf' id='w_hf' type='text'"
1767                            "value='%d'></td></tr>\n", wsel->s->fails);
1768                 ap_rprintf(r, "<tr><td>HC uri</td><td><input name='w_hu' id='w_hu' type='text'"
1769                            "value=\"%s\"></td></tr>\n", ap_escape_html(r->pool, wsel->s->hcuri));
1770                 ap_rputs("</table>\n</td></tr>\n", r);
1771             }
1772             ap_rputs("<tr><td colspan='2'><input type=submit value='Submit'></td></tr>\n", r);
1773             ap_rvputs(r, "</table>\n<input type=hidden name='w' id='w' ",  NULL);
1774             ap_rvputs(r, "value=\"", ap_escape_uri(r->pool, wsel->s->name), "\">\n", NULL);
1775             ap_rvputs(r, "<input type=hidden name='b' id='b' ", NULL);
1776             ap_rvputs(r, "value=\"", ap_escape_html(r->pool, bsel->s->name + sizeof(BALANCER_PREFIX) - 1),
1777                       "\">\n", NULL);
1778             ap_rvputs(r, "<input type=hidden name='nonce' id='nonce' value='",
1779                       bsel->s->nonce, "'>\n", NULL);
1780             ap_rputs("</form>\n", r);
1781             ap_rputs("<hr />\n", r);
1782         } else if (bsel) {
1783             const apr_array_header_t *provs;
1784             const ap_list_provider_names_t *pname;
1785             int i;
1786             ap_rputs("<h3>Edit balancer settings for ", r);
1787             ap_rvputs(r, ap_escape_html(r->pool, bsel->s->name), "</h3>\n", NULL);
1788             ap_rputs("<form method='POST' enctype='application/x-www-form-urlencoded' action=\"", r);
1789             ap_rvputs(r, ap_escape_uri(r->pool, action), "\">\n", NULL);
1790             ap_rputs("<table>\n", r);
1791             provs = ap_list_provider_names(r->pool, PROXY_LBMETHOD, "0");
1792             if (provs) {
1793                 ap_rputs("<tr><td>LBmethod:</td>", r);
1794                 ap_rputs("<td>\n<select name='b_lbm' id='b_lbm'>", r);
1795                 pname = (ap_list_provider_names_t *)provs->elts;
1796                 for (i = 0; i < provs->nelts; i++, pname++) {
1797                     ap_rvputs(r,"<option value='", pname->provider_name, "'", NULL);
1798                     if (strcmp(pname->provider_name, bsel->s->lbpname) == 0)
1799                         ap_rputs(" selected ", r);
1800                     ap_rvputs(r, ">", pname->provider_name, "\n", NULL);
1801                 }
1802                 ap_rputs("</select>\n</td></tr>\n", r);
1803             }
1804             ap_rputs("<tr><td>Timeout:</td><td><input name='b_tmo' id='b_tmo' type=text ", r);
1805             ap_rprintf(r, "value='%" APR_TIME_T_FMT "'></td></tr>\n", apr_time_sec(bsel->s->timeout));
1806             ap_rputs("<tr><td>Failover Attempts:</td><td><input name='b_max' id='b_max' type=text ", r);
1807             ap_rprintf(r, "value='%d'></td></tr>\n", bsel->s->max_attempts);
1808             ap_rputs("<tr><td>Disable Failover:</td>", r);
1809             create_radio("b_sforce", bsel->s->sticky_force, r);
1810             ap_rputs("</tr>\n", r);
1811             ap_rputs("<tr><td>Sticky Session:</td><td><input name='b_ss' id='b_ss' size=64 type=text ", r);
1812             if (strcmp(bsel->s->sticky, bsel->s->sticky_path)) {
1813                 ap_rvputs(r, "value =\"", ap_escape_html(r->pool, bsel->s->sticky), " | ",
1814                           ap_escape_html(r->pool, bsel->s->sticky_path), NULL);
1815             }
1816             else {
1817                 ap_rvputs(r, "value =\"", ap_escape_html(r->pool, bsel->s->sticky), NULL);
1818             }
1819             ap_rputs("\">&nbsp;&nbsp;&nbsp;&nbsp;(Use '-' to delete)</td></tr>\n", r);
1820             if (storage->num_free_slots(bsel->wslot) != 0) {
1821                 ap_rputs("<tr><td>Add New Worker:</td><td><input name='b_nwrkr' id='b_nwrkr' size=32 type=text>"
1822                          "&nbsp;&nbsp;&nbsp;&nbsp;Are you sure? <input name='b_wyes' id='b_wyes' type=checkbox value='1'>"
1823                          "</td></tr>", r);
1824             }
1825             ap_rputs("<tr><td colspan=2><input type=submit value='Submit'></td></tr>\n", r);
1826             ap_rvputs(r, "</table>\n<input type=hidden name='b' id='b' ", NULL);
1827             ap_rvputs(r, "value=\"", ap_escape_html(r->pool, bsel->s->name + sizeof(BALANCER_PREFIX) - 1),
1828                       "\">\n", NULL);
1829             ap_rvputs(r, "<input type=hidden name='nonce' id='nonce' value='",
1830                       bsel->s->nonce, "'>\n", NULL);
1831             ap_rputs("</form>\n", r);
1832             ap_rputs("<hr />\n", r);
1833         }
1834         ap_rputs(ap_psignature("",r), r);
1835         ap_rputs("</body></html>\n", r);
1836         ap_rflush(r);
1837     }
1838 }
1839
1840 /* Returns non-zero if the Referer: header value passed matches the
1841  * host of the request. */
1842 static int safe_referer(request_rec *r, const char *ref)
1843 {
1844     apr_uri_t uri;
1845
1846     if (apr_uri_parse(r->pool, ref, &uri) || !uri.hostname)
1847         return 0;
1848
1849     return strcasecmp(uri.hostname, ap_get_server_name(r)) == 0;
1850 }
1851
1852 /* Manages the loadfactors and member status
1853  *   The balancer, worker and nonce are obtained from
1854  *   the request args (?b=...&w=...&nonce=....).
1855  *   All other params are pulled from any POST
1856  *   data that exists.
1857  * TODO:
1858  *   /.../<whatever>/balancer/worker/nonce
1859  */
1860 static int balancer_handler(request_rec *r)
1861 {
1862     void *sconf;
1863     proxy_server_conf *conf;
1864     proxy_balancer *balancer, *bsel = NULL;
1865     proxy_worker *wsel = NULL;
1866     apr_table_t *params;
1867     int i;
1868     int ok2change = 1;
1869     const char *name, *ref;
1870     apr_status_t rv;
1871
1872     /* is this for us? */
1873     if (strcmp(r->handler, "balancer-manager")) {
1874         return DECLINED;
1875     }
1876
1877     r->allowed = 0
1878     | (AP_METHOD_BIT << M_GET)
1879     | (AP_METHOD_BIT << M_POST);
1880     if ((r->method_number != M_GET) && (r->method_number != M_POST)) {
1881         return DECLINED;
1882     }
1883
1884     sconf = r->server->module_config;
1885     conf = (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module);
1886     params = apr_table_make(r->pool, 10);
1887
1888     balancer = (proxy_balancer *)conf->balancers->elts;
1889     for (i = 0; i < conf->balancers->nelts; i++, balancer++) {
1890 #if APR_HAS_THREADS
1891         if ((rv = PROXY_THREAD_LOCK(balancer)) != APR_SUCCESS) {
1892             ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01189)
1893                           "%s: Lock failed for balancer_handler",
1894                           balancer->s->name);
1895         }
1896 #endif
1897         ap_proxy_sync_balancer(balancer, r->server, conf);
1898 #if APR_HAS_THREADS
1899         if ((rv = PROXY_THREAD_UNLOCK(balancer)) != APR_SUCCESS) {
1900             ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01190)
1901                           "%s: Unlock failed for balancer_handler",
1902                           balancer->s->name);
1903         }
1904 #endif
1905     }
1906
1907     if (r->args && (r->method_number == M_GET)) {
1908         const char *allowed[] = { "w", "b", "nonce", "xml", NULL };
1909         ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01191) "parsing r->args");
1910
1911         push2table(r->args, params, allowed, r->pool);
1912     }
1913     if (r->method_number == M_POST) {
1914         apr_bucket_brigade *ib;
1915         apr_size_t len = 1024;
1916         char *buf = apr_pcalloc(r->pool, len+1);
1917
1918         ib = apr_brigade_create(r->connection->pool, r->connection->bucket_alloc);
1919         rv = ap_get_brigade(r->input_filters, ib, AP_MODE_READBYTES,
1920                                 APR_BLOCK_READ, len);
1921         if (rv != APR_SUCCESS) {
1922             return ap_map_http_request_error(rv, HTTP_BAD_REQUEST);
1923         }
1924         apr_brigade_flatten(ib, buf, &len);
1925         buf[len] = '\0';
1926         push2table(buf, params, NULL, r->pool);
1927     }
1928
1929     /* Ignore parameters if this looks like XSRF */
1930     ref = apr_table_get(r->headers_in, "Referer");
1931     if (apr_table_elts(params)
1932         && (!ref || !safe_referer(r, ref))) {
1933         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10187)
1934                       "ignoring params in balancer-manager cross-site access");
1935         apr_table_clear(params);
1936     }
1937     
1938     /* Process the parameters */
1939     if ((name = apr_table_get(params, "b")))
1940         bsel = ap_proxy_get_balancer(r->pool, conf,
1941             apr_pstrcat(r->pool, BALANCER_PREFIX, name, NULL), 0);
1942
1943     if ((name = apr_table_get(params, "w"))) {
1944         wsel = ap_proxy_get_worker(r->pool, bsel, conf, name);
1945     }
1946
1947
1948     /* Check that the supplied nonce matches this server's nonce;
1949      * otherwise ignore all parameters, to prevent a CSRF attack. */
1950     if (!bsel ||
1951         (*bsel->s->nonce &&
1952          (
1953           (name = apr_table_get(params, "nonce")) == NULL ||
1954           strcmp(bsel->s->nonce, name) != 0
1955          )
1956         )
1957        ) {
1958         ok2change = 0;
1959     }
1960
1961     /* process the parameters and  add the worker to the balancer */
1962     rv = balancer_process_balancer_worker(r, conf, bsel, wsel, ok2change, params);
1963     if (rv != APR_SUCCESS) {
1964        return HTTP_BAD_REQUEST;
1965     }
1966
1967     /* display the HTML or XML page */
1968     if (apr_table_get(params, "xml")) {
1969         balancer_display_page(r, conf, bsel, wsel, 1);
1970     } else {
1971         balancer_display_page(r, conf, bsel, wsel, 0);
1972     }
1973     return DONE;
1974 }
1975
1976 static void balancer_child_init(apr_pool_t *p, server_rec *s)
1977 {
1978     while (s) {
1979         proxy_balancer *balancer;
1980         int i;
1981         void *sconf = s->module_config;
1982         proxy_server_conf *conf = (proxy_server_conf *)ap_get_module_config(sconf, &proxy_module);
1983         apr_status_t rv;
1984
1985         if (conf->balancers->nelts) {
1986             apr_size_t size;
1987             unsigned int num;
1988             /* In 2.4.x we rely on the provider to return either the same
1989              * in/out &bslot, a valid new one, or NULL for failure/exit().
1990              * TODO? for 2.6+/3.x we possibly could consider returned status
1991              * to be real failures, but e.g. NOTFOUND/ENOSHM* to continue with
1992              * existing conf->bslot (even when the returned one is NULL).
1993              * Hence handle the slotmem reuse it here where we know it's valid
1994              * both for fork()ed post_config()s and MPM winnt-like ones (run in
1995              * child process too). The provider tells what it attached or not, 
1996              * and if not whether the child should stop (fatal) or continue
1997              * with the "inherited" configuration.
1998              */
1999             rv = storage->attach(&conf->bslot, conf->id, &size, &num, p);
2000             if (!conf->bslot) {
2001                 ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, APLOGNO(01205) "slotmem_attach failed");
2002                 exit(1); /* Ugly, but what else? */
2003             }
2004             (void)rv;
2005         }
2006
2007         balancer = (proxy_balancer *)conf->balancers->elts;
2008         for (i = 0; i < conf->balancers->nelts; i++, balancer++) {
2009             rv = ap_proxy_initialize_balancer(balancer, s, p);
2010
2011             if (rv != APR_SUCCESS) {
2012                 ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s, APLOGNO(01206)
2013                              "Failed to init balancer %s in child",
2014                              balancer->s->name);
2015                 exit(1); /* Ugly, but what else? */
2016             }
2017             init_balancer_members(conf->pool, s, balancer);
2018         }
2019         s = s->next;
2020     }
2021
2022 }
2023
2024 static void ap_proxy_balancer_register_hook(apr_pool_t *p)
2025 {
2026     /* Only the mpm_winnt has child init hook handler.
2027      * make sure that we are called after the mpm
2028      * initializes
2029      */
2030     static const char *const aszPred[] = { "mpm_winnt.c", "mod_slotmem_shm.c", NULL};
2031     static const char *const aszPred2[] = { "mod_proxy.c", NULL};
2032      /* manager handler */
2033     ap_hook_post_config(balancer_post_config, aszPred2, NULL, APR_HOOK_MIDDLE);
2034     ap_hook_pre_config(balancer_pre_config, NULL, NULL, APR_HOOK_MIDDLE);
2035     ap_hook_handler(balancer_handler, NULL, NULL, APR_HOOK_FIRST);
2036     ap_hook_child_init(balancer_child_init, aszPred, NULL, APR_HOOK_MIDDLE);
2037     proxy_hook_pre_request(proxy_balancer_pre_request, NULL, NULL, APR_HOOK_FIRST);
2038     proxy_hook_post_request(proxy_balancer_post_request, NULL, NULL, APR_HOOK_FIRST);
2039     proxy_hook_canon_handler(proxy_balancer_canon, NULL, NULL, APR_HOOK_FIRST);
2040 }
2041
2042 AP_DECLARE_MODULE(proxy_balancer) = {
2043     STANDARD20_MODULE_STUFF,
2044     NULL,       /* create per-directory config structure */
2045     NULL,       /* merge per-directory config structures */
2046     NULL,       /* create per-server config structure */
2047     NULL,       /* merge per-server config structures */
2048     NULL,       /* command apr_table_t */
2049     ap_proxy_balancer_register_hook /* register hooks */
2050 };