]> granicus.if.org Git - apache/blob - modules/ldap/util_ldap_cache_mgr.c
Implement a BASEDIR build environment variable to allow the NetWare build to relocate...
[apache] / modules / ldap / util_ldap_cache_mgr.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 /*
18  * util_ldap_cache_mgr.c: LDAP cache manager things
19  *
20  * Original code from auth_ldap module for Apache v1.3:
21  * Copyright 1998, 1999 Enbridge Pipelines Inc.
22  * Copyright 1999-2001 Dave Carrigan
23  */
24
25 #include "httpd.h"
26 #include "util_ldap.h"
27 #include "util_ldap_cache.h"
28 #include <apr_strings.h>
29
30 #if APR_HAS_LDAP
31
32 /* only here until strdup is gone */
33 #include <string.h>
34
35 /* here till malloc is gone */
36 #include <stdlib.h>
37
38 static const unsigned long primes[] =
39 {
40   11,
41   19,
42   37,
43   73,
44   109,
45   163,
46   251,
47   367,
48   557,
49   823,
50   1237,
51   1861,
52   2777,
53   4177,
54   6247,
55   9371,
56   14057,
57   21089,
58   31627,
59   47431,
60   71143,
61   106721,
62   160073,
63   240101,
64   360163,
65   540217,
66   810343,
67   1215497,
68   1823231,
69   2734867,
70   4102283,
71   6153409,
72   9230113,
73   13845163,
74   0
75 };
76
77 void util_ald_free(util_ald_cache_t *cache, const void *ptr)
78 {
79 #if APR_HAS_SHARED_MEMORY
80     if (cache->rmm_addr) {
81         if (ptr)
82             /* Free in shared memory */
83             apr_rmm_free(cache->rmm_addr, apr_rmm_offset_get(cache->rmm_addr, (void *)ptr));
84     }
85     else {
86         if (ptr)
87             /* Cache shm is not used */
88             free((void *)ptr);
89     }
90 #else
91     if (ptr)
92         free((void *)ptr);
93 #endif
94 }
95
96 void *util_ald_alloc(util_ald_cache_t *cache, unsigned long size)
97 {
98     if (0 == size)
99         return NULL;
100 #if APR_HAS_SHARED_MEMORY
101     if (cache->rmm_addr) {
102         /* allocate from shared memory */
103         apr_rmm_off_t block = apr_rmm_calloc(cache->rmm_addr, size);
104         return block ? (void *)apr_rmm_addr_get(cache->rmm_addr, block) : NULL;
105     }
106     else {
107         /* Cache shm is not used */
108         return (void *)calloc(sizeof(char), size);
109     }
110 #else
111     return (void *)calloc(sizeof(char), size);
112 #endif
113 }
114
115 const char *util_ald_strdup(util_ald_cache_t *cache, const char *s)
116 {
117 #if APR_HAS_SHARED_MEMORY
118     if (cache->rmm_addr) {
119         /* allocate from shared memory */
120         apr_rmm_off_t block = apr_rmm_calloc(cache->rmm_addr, strlen(s)+1);
121         char *buf = block ? (char *)apr_rmm_addr_get(cache->rmm_addr, block) : NULL;
122         if (buf) {
123             strcpy(buf, s);
124             return buf;
125         }
126         else {
127             return NULL;
128         }
129     } else {
130         /* Cache shm is not used */
131         return strdup(s);
132     }
133 #else
134     return strdup(s);
135 #endif
136 }
137
138
139 /*
140  * Computes the hash on a set of strings. The first argument is the number
141  * of strings to hash, the rest of the args are strings.
142  * Algorithm taken from glibc.
143  */
144 unsigned long util_ald_hash_string(int nstr, ...)
145 {
146     int i;
147     va_list args;
148     unsigned long h=0, g;
149     char *str, *p;
150
151     va_start(args, nstr);
152     for (i=0; i < nstr; ++i) {
153         str = va_arg(args, char *);
154         for (p = str; *p; ++p) {
155             h = ( h << 4 ) + *p;
156             if ( ( g = h & 0xf0000000 ) ) {
157                 h = h ^ (g >> 24);
158                 h = h ^ g;
159             }
160         }
161     }
162     va_end(args);
163
164     return h;
165 }
166
167
168 /*
169   Purges a cache that has gotten full. We keep track of the time that we
170   added the entry that made the cache 3/4 full, then delete all entries
171   that were added before that time. It's pretty simplistic, but time to
172   purge is only O(n), which is more important.
173 */
174 void util_ald_cache_purge(util_ald_cache_t *cache)
175 {
176     unsigned long i;
177     util_cache_node_t *p, *q, **pp;
178     apr_time_t t;
179
180     if (!cache)
181         return;
182
183     cache->last_purge = apr_time_now();
184     cache->npurged = 0;
185     cache->numpurges++;
186
187     for (i=0; i < cache->size; ++i) {
188         pp = cache->nodes + i;
189         p = *pp;
190         while (p != NULL) {
191             if (p->add_time < cache->marktime) {
192                 q = p->next;
193                 (*cache->free)(cache, p->payload);
194                 util_ald_free(cache, p);
195                 cache->numentries--;
196                 cache->npurged++;
197                 p = *pp = q;
198             }
199             else {
200                 pp = &(p->next);
201                 p = *pp;
202             }
203         }
204     }
205
206     t = apr_time_now();
207     cache->avg_purgetime =
208          ((t - cache->last_purge) + (cache->avg_purgetime * (cache->numpurges-1))) /
209          cache->numpurges;
210 }
211
212
213 /*
214  * create caches
215  */
216 util_url_node_t *util_ald_create_caches(util_ldap_state_t *st, const char *url)
217 {
218     util_url_node_t curl, *newcurl = NULL;
219     util_ald_cache_t *search_cache;
220     util_ald_cache_t *compare_cache;
221     util_ald_cache_t *dn_compare_cache;
222
223     /* create the three caches */
224     search_cache = util_ald_create_cache(st,
225                       st->search_cache_size,
226                       util_ldap_search_node_hash,
227                       util_ldap_search_node_compare,
228                       util_ldap_search_node_copy,
229                       util_ldap_search_node_free,
230                       util_ldap_search_node_display);
231     compare_cache = util_ald_create_cache(st,
232                       st->compare_cache_size,
233                       util_ldap_compare_node_hash,
234                       util_ldap_compare_node_compare,
235                       util_ldap_compare_node_copy,
236                       util_ldap_compare_node_free,
237                       util_ldap_compare_node_display);
238     dn_compare_cache = util_ald_create_cache(st,
239                       st->compare_cache_size,
240                       util_ldap_dn_compare_node_hash,
241                       util_ldap_dn_compare_node_compare,
242                       util_ldap_dn_compare_node_copy,
243                       util_ldap_dn_compare_node_free,
244                       util_ldap_dn_compare_node_display);
245
246     /* check that all the caches initialised successfully */
247     if (search_cache && compare_cache && dn_compare_cache) {
248
249         /* The contents of this structure will be duplicated in shared
250            memory during the insert.  So use stack memory rather than
251            pool memory to avoid a memory leak. */
252         memset (&curl, 0, sizeof(util_url_node_t));
253         curl.url = url;
254         curl.search_cache = search_cache;
255         curl.compare_cache = compare_cache;
256         curl.dn_compare_cache = dn_compare_cache;
257
258         newcurl = util_ald_cache_insert(st->util_ldap_cache, &curl);
259
260     }
261
262     return newcurl;
263 }
264
265
266 util_ald_cache_t *util_ald_create_cache(util_ldap_state_t *st,
267                                 long cache_size,
268                                 unsigned long (*hashfunc)(void *),
269                                 int (*comparefunc)(void *, void *),
270                                 void * (*copyfunc)(util_ald_cache_t *cache, void *),
271                                 void (*freefunc)(util_ald_cache_t *cache, void *),
272                                 void (*displayfunc)(request_rec *r, util_ald_cache_t *cache, void *))
273 {
274     util_ald_cache_t *cache;
275     unsigned long i;
276
277     if (cache_size <= 0)
278         return NULL;
279
280 #if APR_HAS_SHARED_MEMORY
281     if (!st->cache_rmm) {
282         return NULL;
283     }
284     else {
285         apr_rmm_off_t block = apr_rmm_calloc(st->cache_rmm, sizeof(util_ald_cache_t));
286         cache = block ? (util_ald_cache_t *)apr_rmm_addr_get(st->cache_rmm, block) : NULL;
287     }
288 #else
289     cache = (util_ald_cache_t *)calloc(sizeof(util_ald_cache_t), 1);
290 #endif
291     if (!cache)
292         return NULL;
293
294 #if APR_HAS_SHARED_MEMORY
295     cache->rmm_addr = st->cache_rmm;
296     cache->shm_addr = st->cache_shm;
297 #endif
298     cache->maxentries = cache_size;
299     cache->numentries = 0;
300     cache->size = cache_size / 3;
301     if (cache->size < 64) cache->size = 64;
302         for (i = 0; primes[i] && primes[i] < cache->size; ++i) ;
303             cache->size = primes[i]? primes[i] : primes[i-1];
304
305     cache->nodes = (util_cache_node_t **)util_ald_alloc(cache, cache->size * sizeof(util_cache_node_t *));
306     if (!cache->nodes) {
307         util_ald_free(cache, cache);
308         return NULL;
309     }
310
311     for (i=0; i < cache->size; ++i)
312         cache->nodes[i] = NULL;
313
314     cache->hash = hashfunc;
315     cache->compare = comparefunc;
316     cache->copy = copyfunc;
317     cache->free = freefunc;
318     cache->display = displayfunc;
319
320     cache->fullmark = cache->maxentries / 4 * 3;
321     cache->marktime = 0;
322     cache->avg_purgetime = 0.0;
323     cache->numpurges = 0;
324     cache->last_purge = 0;
325     cache->npurged = 0;
326
327     cache->fetches = 0;
328     cache->hits = 0;
329     cache->inserts = 0;
330     cache->removes = 0;
331
332     return cache;
333 }
334
335 void util_ald_destroy_cache(util_ald_cache_t *cache)
336 {
337     unsigned long i;
338     util_cache_node_t *p, *q;
339
340     if (cache == NULL)
341         return;
342
343     for (i = 0; i < cache->size; ++i) {
344         p = cache->nodes[i];
345         q = NULL;
346         while (p != NULL) {
347             q = p->next;
348            (*cache->free)(cache, p->payload);
349            util_ald_free(cache, p);
350            p = q;
351         }
352     }
353     util_ald_free(cache, cache->nodes);
354     util_ald_free(cache, cache);
355 }
356
357 void *util_ald_cache_fetch(util_ald_cache_t *cache, void *payload)
358 {
359     unsigned long hashval;
360     util_cache_node_t *p;
361
362     if (cache == NULL)
363         return NULL;
364
365     cache->fetches++;
366
367     hashval = (*cache->hash)(payload) % cache->size;
368     for (p = cache->nodes[hashval];
369          p && !(*cache->compare)(p->payload, payload);
370     p = p->next) ;
371
372     if (p != NULL) {
373         cache->hits++;
374         return p->payload;
375     }
376     else {
377         return NULL;
378     }
379 }
380
381 /*
382  * Insert an item into the cache.
383  * *** Does not catch duplicates!!! ***
384  */
385 void *util_ald_cache_insert(util_ald_cache_t *cache, void *payload)
386 {
387     unsigned long hashval;
388     util_cache_node_t *node;
389
390     /* sanity check */
391     if (cache == NULL || payload == NULL) {
392         return NULL;
393     }
394
395     /* check if we are full - if so, try purge */
396     if (cache->numentries >= cache->maxentries) {
397         util_ald_cache_purge(cache);
398         if (cache->numentries >= cache->maxentries) {
399             /* if the purge was not effective, we leave now to avoid an overflow */
400             return NULL;
401         }
402     }
403
404     /* should be safe to add an entry */
405     if ((node = (util_cache_node_t *)util_ald_alloc(cache, sizeof(util_cache_node_t))) == NULL) {
406         return NULL;
407     }
408
409     /* Take a copy of the payload before proceeeding. */
410     payload = (*cache->copy)(cache, payload);
411     if (!payload) {
412         util_ald_free(cache, node);
413         return NULL;
414     }
415
416     /* populate the entry */
417     cache->inserts++;
418     hashval = (*cache->hash)(payload) % cache->size;
419     node->add_time = apr_time_now();
420     node->payload = payload;
421     node->next = cache->nodes[hashval];
422     cache->nodes[hashval] = node;
423
424     /* if we reach the full mark, note the time we did so
425      * for the benefit of the purge function
426      */
427     if (++cache->numentries == cache->fullmark) {
428         cache->marktime=apr_time_now();
429     }
430
431     return node->payload;
432 }
433
434 void util_ald_cache_remove(util_ald_cache_t *cache, void *payload)
435 {
436     unsigned long hashval;
437     util_cache_node_t *p, *q;
438
439     if (cache == NULL)
440         return;
441
442     cache->removes++;
443     hashval = (*cache->hash)(payload) % cache->size;
444     for (p = cache->nodes[hashval], q=NULL;
445          p && !(*cache->compare)(p->payload, payload);
446          p = p->next) {
447          q = p;
448     }
449
450     /* If p is null, it means that we couldn't find the node, so just return */
451     if (p == NULL)
452         return;
453
454     if (q == NULL) {
455         /* We found the node, and it's the first in the list */
456         cache->nodes[hashval] = p->next;
457     }
458     else {
459         /* We found the node and it's not the first in the list */
460         q->next = p->next;
461     }
462     (*cache->free)(cache, p->payload);
463     util_ald_free(cache, p);
464     cache->numentries--;
465 }
466
467 char *util_ald_cache_display_stats(request_rec *r, util_ald_cache_t *cache, char *name, char *id)
468 {
469     unsigned long i;
470     int totchainlen = 0;
471     int nchains = 0;
472     double chainlen;
473     util_cache_node_t *n;
474     char *buf, *buf2;
475     apr_pool_t *p = r->pool;
476
477     if (cache == NULL) {
478         return "";
479     }
480
481     for (i=0; i < cache->size; ++i) {
482         if (cache->nodes[i] != NULL) {
483             nchains++;
484             for (n = cache->nodes[i];
485                  n != NULL && n != n->next;
486                  n = n->next) {
487                 totchainlen++;
488             }
489         }
490     }
491     chainlen = nchains? (double)totchainlen / (double)nchains : 0;
492
493     if (id) {
494         buf2 = apr_psprintf(p,
495                  "<a href=\"%s?%s\">%s</a>",
496              r->uri,
497              id,
498              name);
499     }
500     else {
501         buf2 = name;
502     }
503
504     buf = apr_psprintf(p,
505              "<tr valign='top'>"
506              "<td nowrap>%s</td>"
507              "<td align='right' nowrap>%lu (%.0f%% full)</td>"
508              "<td align='right'>%.1f</td>"
509              "<td align='right'>%lu/%lu</td>"
510              "<td align='right'>%.0f%%</td>"
511              "<td align='right'>%lu/%lu</td>",
512          buf2,
513          cache->numentries,
514          (double)cache->numentries / (double)cache->maxentries * 100.0,
515          chainlen,
516          cache->hits,
517          cache->fetches,
518          (cache->fetches > 0 ? (double)(cache->hits) / (double)(cache->fetches) * 100.0 : 100.0),
519          cache->inserts,
520          cache->removes);
521
522     if (cache->numpurges) {
523         char str_ctime[APR_CTIME_LEN];
524
525         apr_ctime(str_ctime, cache->last_purge);
526         buf = apr_psprintf(p,
527                  "%s"
528                  "<td align='right'>%lu</td>\n"
529                  "<td align='right' nowrap>%s</td>\n",
530              buf,
531              cache->numpurges,
532              str_ctime);
533     }
534     else {
535         buf = apr_psprintf(p,
536                  "%s<td colspan='2' align='center'>(none)</td>\n",
537              buf);
538     }
539
540     buf = apr_psprintf(p, "%s<td align='right'>%.2gms</td>\n</tr>", buf, cache->avg_purgetime);
541
542     return buf;
543 }
544
545 char *util_ald_cache_display(request_rec *r, util_ldap_state_t *st)
546 {
547     unsigned long i,j;
548     char *buf, *t1, *t2, *t3;
549     char *id1, *id2, *id3;
550     char *argfmt = "cache=%s&id=%d&off=%d";
551     char *scanfmt = "cache=%4s&id=%u&off=%u%1s";
552     apr_pool_t *pool = r->pool;
553     util_cache_node_t *p = NULL;
554     util_url_node_t *n = NULL;
555
556     util_ald_cache_t *util_ldap_cache = st->util_ldap_cache;
557
558
559     if (!util_ldap_cache) {
560         return "<tr valign='top'><td nowrap colspan=7>Cache has not been enabled/initialised.</td></tr>";
561     }
562
563     if (r->args && strlen(r->args)) {
564         char cachetype[5], lint[2];
565         unsigned int id, off;
566         char date_str[APR_CTIME_LEN+1];
567
568         if ((3 == sscanf(r->args, scanfmt, cachetype, &id, &off, lint)) &&
569             (id < util_ldap_cache->size)) {
570
571             if ((p = util_ldap_cache->nodes[id]) != NULL) {
572                 n = (util_url_node_t *)p->payload;
573                 buf = (char*)n->url;
574             }
575             else {
576                 buf = "";
577             }
578
579             ap_rprintf(r,
580                        "<p>\n"
581                        "<table border='0'>\n"
582                        "<tr>\n"
583                        "<td bgcolor='#000000'><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Cache Name:</b></font></td>"
584                        "<td bgcolor='#ffffff'><font size='-1' face='Arial,Helvetica' color='#000000'><b>%s (%s)</b></font></td>"
585                        "</tr>\n"
586                        "</table>\n</p>\n",
587                        buf,
588                        cachetype[0] == 'm'? "Main" :
589                        (cachetype[0] == 's' ? "Search" :
590                         (cachetype[0] == 'c' ? "Compares" : "DNCompares")));
591
592             switch (cachetype[0]) {
593                 case 'm':
594                     if (util_ldap_cache->marktime) {
595                         apr_ctime(date_str, util_ldap_cache->marktime);
596                     }
597                     else
598                         date_str[0] = 0;
599
600                     ap_rprintf(r,
601                                "<p>\n"
602                                "<table border='0'>\n"
603                                "<tr>\n"
604                                "<td bgcolor='#000000'><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Size:</b></font></td>"
605                                "<td bgcolor='#ffffff'><font size='-1' face='Arial,Helvetica' color='#000000'><b>%ld</b></font></td>"
606                                "</tr>\n"
607                                "<tr>\n"
608                                "<td bgcolor='#000000'><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Max Entries:</b></font></td>"
609                                "<td bgcolor='#ffffff'><font size='-1' face='Arial,Helvetica' color='#000000'><b>%ld</b></font></td>"
610                                "</tr>\n"
611                                "<tr>\n"
612                                "<td bgcolor='#000000'><font size='-1' face='Arial,Helvetica' color='#ffffff'><b># Entries:</b></font></td>"
613                                "<td bgcolor='#ffffff'><font size='-1' face='Arial,Helvetica' color='#000000'><b>%ld</b></font></td>"
614                                "</tr>\n"
615                                "<tr>\n"
616                                "<td bgcolor='#000000'><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Full Mark:</b></font></td>"
617                                "<td bgcolor='#ffffff'><font size='-1' face='Arial,Helvetica' color='#000000'><b>%ld</b></font></td>"
618                                "</tr>\n"
619                                "<tr>\n"
620                                "<td bgcolor='#000000'><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Full Mark Time:</b></font></td>"
621                                "<td bgcolor='#ffffff'><font size='-1' face='Arial,Helvetica' color='#000000'><b>%s</b></font></td>"
622                                "</tr>\n"
623                                "</table>\n</p>\n",
624                                util_ldap_cache->size,
625                                util_ldap_cache->maxentries,
626                                util_ldap_cache->numentries,
627                                util_ldap_cache->fullmark,
628                                date_str);
629
630                     ap_rputs("<p>\n"
631                              "<table border='0'>\n"
632                              "<tr bgcolor='#000000'>\n"
633                              "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>LDAP URL</b></font></td>"
634                              "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Size</b></font></td>"
635                              "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Max Entries</b></font></td>"
636                              "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b># Entries</b></font></td>"
637                              "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Full Mark</b></font></td>"
638                              "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Full Mark Time</b></font></td>"
639                              "</tr>\n", r
640                             );
641                     for (i=0; i < util_ldap_cache->size; ++i) {
642                         for (p = util_ldap_cache->nodes[i]; p != NULL; p = p->next) {
643
644                             (*util_ldap_cache->display)(r, util_ldap_cache, p->payload);
645                         }
646                     }
647                     ap_rputs("</table>\n</p>\n", r);
648
649
650                     break;
651                 case 's':
652                     ap_rputs("<p>\n"
653                              "<table border='0'>\n"
654                              "<tr bgcolor='#000000'>\n"
655                              "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>LDAP Filter</b></font></td>"
656                              "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>User Name</b></font></td>"
657                              "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Last Bind</b></font></td>"
658                              "</tr>\n", r
659                             );
660                     if (n) {
661                         for (i=0; i < n->search_cache->size; ++i) {
662                             for (p = n->search_cache->nodes[i]; p != NULL; p = p->next) {
663
664                                 (*n->search_cache->display)(r, n->search_cache, p->payload);
665                             }
666                         }
667                     }
668                     ap_rputs("</table>\n</p>\n", r);
669                     break;
670                 case 'c':
671                     ap_rputs("<p>\n"
672                              "<table border='0'>\n"
673                              "<tr bgcolor='#000000'>\n"
674                              "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>DN</b></font></td>"
675                              "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Attribute</b></font></td>"
676                              "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Value</b></font></td>"
677                              "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Last Compare</b></font></td>"
678                              "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Result</b></font></td>"
679                              "</tr>\n", r
680                             );
681                     if (n) {
682                         for (i=0; i < n->compare_cache->size; ++i) {
683                             for (p = n->compare_cache->nodes[i]; p != NULL; p = p->next) {
684
685                                 (*n->compare_cache->display)(r, n->compare_cache, p->payload);
686                             }
687                         }
688                     }
689                     ap_rputs("</table>\n</p>\n", r);
690                     break;
691                 case 'd':
692                     ap_rputs("<p>\n"
693                              "<table border='0'>\n"
694                              "<tr bgcolor='#000000'>\n"
695                              "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Require DN</b></font></td>"
696                              "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Actual DN</b></font></td>"
697                              "</tr>\n", r
698                             );
699                     if (n) {
700                         for (i=0; i < n->dn_compare_cache->size; ++i) {
701                             for (p = n->dn_compare_cache->nodes[i]; p != NULL; p = p->next) {
702
703                                 (*n->dn_compare_cache->display)(r, n->dn_compare_cache, p->payload);
704                             }
705                         }
706                     }
707                     ap_rputs("</table>\n</p>\n", r);
708                     break;
709                 default:
710                     break;
711             }
712
713         }
714         else {
715             buf = "";
716         }
717     }
718     else {
719         ap_rputs("<p>\n"
720                  "<table border='0'>\n"
721                  "<tr bgcolor='#000000'>\n"
722                  "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Cache Name</b></font></td>"
723                  "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Entries</b></font></td>"
724                  "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Avg. Chain Len.</b></font></td>"
725                  "<td colspan='2'><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Hits</b></font></td>"
726                  "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Ins/Rem</b></font></td>"
727                  "<td colspan='2'><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Purges</b></font></td>"
728                  "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Avg Purge Time</b></font></td>"
729                  "</tr>\n", r
730                 );
731
732
733         id1 = apr_psprintf(pool, argfmt, "main", 0, 0);
734         buf = util_ald_cache_display_stats(r, st->util_ldap_cache, "LDAP URL Cache", id1);
735
736         for (i=0; i < util_ldap_cache->size; ++i) {
737             for (p = util_ldap_cache->nodes[i],j=0; p != NULL; p = p->next,j++) {
738
739                 n = (util_url_node_t *)p->payload;
740
741                 t1 = apr_psprintf(pool, "%s (Searches)", n->url);
742                 t2 = apr_psprintf(pool, "%s (Compares)", n->url);
743                 t3 = apr_psprintf(pool, "%s (DNCompares)", n->url);
744                 id1 = apr_psprintf(pool, argfmt, "srch", i, j);
745                 id2 = apr_psprintf(pool, argfmt, "cmpr", i, j);
746                 id3 = apr_psprintf(pool, argfmt, "dncp", i, j);
747
748                 buf = apr_psprintf(pool, "%s\n\n"
749                                          "%s\n\n"
750                                          "%s\n\n"
751                                          "%s\n\n",
752                                          buf,
753                                          util_ald_cache_display_stats(r, n->search_cache, t1, id1),
754                                          util_ald_cache_display_stats(r, n->compare_cache, t2, id2),
755                                          util_ald_cache_display_stats(r, n->dn_compare_cache, t3, id3)
756                                   );
757             }
758         }
759         ap_rputs(buf, r);
760         ap_rputs("</table>\n</p>\n", r);
761     }
762
763     return buf;
764 }
765
766 #endif /* APR_HAS_LDAP */