]> granicus.if.org Git - apache/blob - modules/ldap/util_ldap_cache_mgr.c
Final stage in this ldap commitathon. This fixes some problems
[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     }
130     else {
131         /* Cache shm is not used */
132         return strdup(s);
133     }
134 #else
135     return strdup(s);
136 #endif
137 }
138
139 /*
140  * Duplicate a subgroupList from one compare entry to another.
141  * Returns: ptr to a new copy of the subgroupList or NULL if allocation failed.
142  */
143 util_compare_subgroup_t *util_ald_sgl_dup(util_ald_cache_t *cache, util_compare_subgroup_t *sgl_in)
144 {
145     int i = 0;
146     util_compare_subgroup_t *sgl_out = NULL;
147
148     if (!sgl_in) {
149         return NULL;
150     }
151
152     sgl_out = (util_compare_subgroup_t *) util_ald_alloc(cache, sizeof(util_compare_subgroup_t));
153     if (sgl_out) {
154         sgl_out->subgroupDNs = util_ald_alloc(cache, sizeof(char *) * sgl_in->len);
155         if (sgl_out->subgroupDNs) {
156             for (i = 0; i < sgl_in->len; i++) {
157                 sgl_out->subgroupDNs[i] = util_ald_strdup(cache, sgl_in->subgroupDNs[i]);
158                 if (!sgl_out->subgroupDNs[i]) {
159                     /* We ran out of SHM, delete the strings we allocated for the SGL */
160                     for (i = (i - 1); i >= 0; i--) {
161                             util_ald_free(cache, sgl_out->subgroupDNs[i]);
162                     }
163                     util_ald_free(cache, sgl_out->subgroupDNs);
164                     util_ald_free(cache, sgl_out);
165                     sgl_out =  NULL;
166                     break;
167                 }
168             }
169             /* We were able to allocate new strings for all the subgroups */
170             if (sgl_out != NULL) {
171                 sgl_out->len = sgl_in->len;
172             }
173         }
174     }
175
176     return sgl_out;
177 }
178
179 /*
180  * Delete an entire subgroupList.
181  */
182 void util_ald_sgl_free(util_ald_cache_t *cache, util_compare_subgroup_t **sgl)
183 {
184     int i = 0;
185     if (sgl == NULL || *sgl == NULL) {
186         return;
187     }
188
189     for (i = 0; i < (*sgl)->len; i++) {
190         util_ald_free(cache, (*sgl)->subgroupDNs[i]);
191     }
192     util_ald_free(cache, *sgl);
193 }
194
195 /*
196  * Computes the hash on a set of strings. The first argument is the number
197  * of strings to hash, the rest of the args are strings.
198  * Algorithm taken from glibc.
199  */
200 unsigned long util_ald_hash_string(int nstr, ...)
201 {
202     int i;
203     va_list args;
204     unsigned long h=0, g;
205     char *str, *p;
206
207     va_start(args, nstr);
208     for (i=0; i < nstr; ++i) {
209         str = va_arg(args, char *);
210         for (p = str; *p; ++p) {
211             h = ( h << 4 ) + *p;
212             if ( ( g = h & 0xf0000000 ) ) {
213                 h = h ^ (g >> 24);
214                 h = h ^ g;
215             }
216         }
217     }
218     va_end(args);
219
220     return h;
221 }
222
223
224 /*
225   Purges a cache that has gotten full. We keep track of the time that we
226   added the entry that made the cache 3/4 full, then delete all entries
227   that were added before that time. It's pretty simplistic, but time to
228   purge is only O(n), which is more important.
229 */
230 void util_ald_cache_purge(util_ald_cache_t *cache)
231 {
232     unsigned long i;
233     util_cache_node_t *p, *q, **pp;
234     apr_time_t t;
235
236     if (!cache)
237         return;
238
239     cache->last_purge = apr_time_now();
240     cache->npurged = 0;
241     cache->numpurges++;
242
243     for (i=0; i < cache->size; ++i) {
244         pp = cache->nodes + i;
245         p = *pp;
246         while (p != NULL) {
247             if (p->add_time < cache->marktime) {
248                 q = p->next;
249                 (*cache->free)(cache, p->payload);
250                 util_ald_free(cache, p);
251                 cache->numentries--;
252                 cache->npurged++;
253                 p = *pp = q;
254             }
255             else {
256                 pp = &(p->next);
257                 p = *pp;
258             }
259         }
260     }
261
262     t = apr_time_now();
263     cache->avg_purgetime =
264          ((t - cache->last_purge) + (cache->avg_purgetime * (cache->numpurges-1))) /
265          cache->numpurges;
266 }
267
268
269 /*
270  * create caches
271  */
272 util_url_node_t *util_ald_create_caches(util_ldap_state_t *st, const char *url)
273 {
274     util_url_node_t curl, *newcurl = NULL;
275     util_ald_cache_t *search_cache;
276     util_ald_cache_t *compare_cache;
277     util_ald_cache_t *dn_compare_cache;
278
279     /* create the three caches */
280     search_cache = util_ald_create_cache(st,
281                       st->search_cache_size,
282                       util_ldap_search_node_hash,
283                       util_ldap_search_node_compare,
284                       util_ldap_search_node_copy,
285                       util_ldap_search_node_free,
286                       util_ldap_search_node_display);
287     compare_cache = util_ald_create_cache(st,
288                       st->compare_cache_size,
289                       util_ldap_compare_node_hash,
290                       util_ldap_compare_node_compare,
291                       util_ldap_compare_node_copy,
292                       util_ldap_compare_node_free,
293                       util_ldap_compare_node_display);
294     dn_compare_cache = util_ald_create_cache(st,
295                       st->compare_cache_size,
296                       util_ldap_dn_compare_node_hash,
297                       util_ldap_dn_compare_node_compare,
298                       util_ldap_dn_compare_node_copy,
299                       util_ldap_dn_compare_node_free,
300                       util_ldap_dn_compare_node_display);
301
302     /* check that all the caches initialised successfully */
303     if (search_cache && compare_cache && dn_compare_cache) {
304
305         /* The contents of this structure will be duplicated in shared
306            memory during the insert.  So use stack memory rather than
307            pool memory to avoid a memory leak. */
308         memset (&curl, 0, sizeof(util_url_node_t));
309         curl.url = url;
310         curl.search_cache = search_cache;
311         curl.compare_cache = compare_cache;
312         curl.dn_compare_cache = dn_compare_cache;
313
314         newcurl = util_ald_cache_insert(st->util_ldap_cache, &curl);
315
316     }
317
318     return newcurl;
319 }
320
321
322 util_ald_cache_t *util_ald_create_cache(util_ldap_state_t *st,
323                                 long cache_size,
324                                 unsigned long (*hashfunc)(void *),
325                                 int (*comparefunc)(void *, void *),
326                                 void * (*copyfunc)(util_ald_cache_t *cache, void *),
327                                 void (*freefunc)(util_ald_cache_t *cache, void *),
328                                 void (*displayfunc)(request_rec *r, util_ald_cache_t *cache, void *))
329 {
330     util_ald_cache_t *cache;
331     unsigned long i;
332
333     if (cache_size <= 0)
334         return NULL;
335
336 #if APR_HAS_SHARED_MEMORY
337     if (!st->cache_rmm) {
338         return NULL;
339     }
340     else {
341         apr_rmm_off_t block = apr_rmm_calloc(st->cache_rmm, sizeof(util_ald_cache_t));
342         cache = block ? (util_ald_cache_t *)apr_rmm_addr_get(st->cache_rmm, block) : NULL;
343     }
344 #else
345     cache = (util_ald_cache_t *)calloc(sizeof(util_ald_cache_t), 1);
346 #endif
347     if (!cache)
348         return NULL;
349
350 #if APR_HAS_SHARED_MEMORY
351     cache->rmm_addr = st->cache_rmm;
352     cache->shm_addr = st->cache_shm;
353 #endif
354     cache->maxentries = cache_size;
355     cache->numentries = 0;
356     cache->size = cache_size / 3;
357     if (cache->size < 64) cache->size = 64;
358         for (i = 0; primes[i] && primes[i] < cache->size; ++i) ;
359             cache->size = primes[i]? primes[i] : primes[i-1];
360
361     cache->nodes = (util_cache_node_t **)util_ald_alloc(cache, cache->size * sizeof(util_cache_node_t *));
362     if (!cache->nodes) {
363         util_ald_free(cache, cache);
364         return NULL;
365     }
366
367     for (i=0; i < cache->size; ++i)
368         cache->nodes[i] = NULL;
369
370     cache->hash = hashfunc;
371     cache->compare = comparefunc;
372     cache->copy = copyfunc;
373     cache->free = freefunc;
374     cache->display = displayfunc;
375
376     cache->fullmark = cache->maxentries / 4 * 3;
377     cache->marktime = 0;
378     cache->avg_purgetime = 0.0;
379     cache->numpurges = 0;
380     cache->last_purge = 0;
381     cache->npurged = 0;
382
383     cache->fetches = 0;
384     cache->hits = 0;
385     cache->inserts = 0;
386     cache->removes = 0;
387
388     return cache;
389 }
390
391 void util_ald_destroy_cache(util_ald_cache_t *cache)
392 {
393     unsigned long i;
394     util_cache_node_t *p, *q;
395
396     if (cache == NULL)
397         return;
398
399     for (i = 0; i < cache->size; ++i) {
400         p = cache->nodes[i];
401         q = NULL;
402         while (p != NULL) {
403             q = p->next;
404            (*cache->free)(cache, p->payload);
405            util_ald_free(cache, p);
406            p = q;
407         }
408     }
409     util_ald_free(cache, cache->nodes);
410     util_ald_free(cache, cache);
411 }
412
413 void *util_ald_cache_fetch(util_ald_cache_t *cache, void *payload)
414 {
415     unsigned long hashval;
416     util_cache_node_t *p;
417
418     if (cache == NULL)
419         return NULL;
420
421     cache->fetches++;
422
423     hashval = (*cache->hash)(payload) % cache->size;
424
425     for (p = cache->nodes[hashval];
426          p && !(*cache->compare)(p->payload, payload);
427          p = p->next) ;
428
429     if (p != NULL) {
430         cache->hits++;
431         return p->payload;
432     }
433     else {
434         return NULL;
435     }
436 }
437
438 /*
439  * Insert an item into the cache.
440  * *** Does not catch duplicates!!! ***
441  */
442 void *util_ald_cache_insert(util_ald_cache_t *cache, void *payload)
443 {
444     unsigned long hashval;
445     util_cache_node_t *node;
446
447     /* sanity check */
448     if (cache == NULL || payload == NULL) {
449         return NULL;
450     }
451
452     /* check if we are full - if so, try purge */
453     if (cache->numentries >= cache->maxentries) {
454         util_ald_cache_purge(cache);
455         if (cache->numentries >= cache->maxentries) {
456             /* if the purge was not effective, we leave now to avoid an overflow */
457             return NULL;
458         }
459     }
460
461     /* should be safe to add an entry */
462     if ((node = (util_cache_node_t *)util_ald_alloc(cache, sizeof(util_cache_node_t))) == NULL) {
463         return NULL;
464     }
465
466     /* Take a copy of the payload before proceeeding. */
467     payload = (*cache->copy)(cache, payload);
468     if (!payload) {
469         util_ald_free(cache, node);
470         return NULL;
471     }
472
473     /* populate the entry */
474     cache->inserts++;
475     hashval = (*cache->hash)(payload) % cache->size;
476     node->add_time = apr_time_now();
477     node->payload = payload;
478     node->next = cache->nodes[hashval];
479     cache->nodes[hashval] = node;
480
481     /* if we reach the full mark, note the time we did so
482      * for the benefit of the purge function
483      */
484     if (++cache->numentries == cache->fullmark) {
485         cache->marktime=apr_time_now();
486     }
487
488     return node->payload;
489 }
490
491 void util_ald_cache_remove(util_ald_cache_t *cache, void *payload)
492 {
493     unsigned long hashval;
494     util_cache_node_t *p, *q;
495
496     if (cache == NULL)
497         return;
498
499     cache->removes++;
500     hashval = (*cache->hash)(payload) % cache->size;
501     for (p = cache->nodes[hashval], q=NULL;
502          p && !(*cache->compare)(p->payload, payload);
503          p = p->next) {
504          q = p;
505     }
506
507     /* If p is null, it means that we couldn't find the node, so just return */
508     if (p == NULL)
509         return;
510
511     if (q == NULL) {
512         /* We found the node, and it's the first in the list */
513         cache->nodes[hashval] = p->next;
514     }
515     else {
516         /* We found the node and it's not the first in the list */
517         q->next = p->next;
518     }
519     (*cache->free)(cache, p->payload);
520     util_ald_free(cache, p);
521     cache->numentries--;
522 }
523
524 char *util_ald_cache_display_stats(request_rec *r, util_ald_cache_t *cache, char *name, char *id)
525 {
526     unsigned long i;
527     int totchainlen = 0;
528     int nchains = 0;
529     double chainlen;
530     util_cache_node_t *n;
531     char *buf, *buf2;
532     apr_pool_t *p = r->pool;
533
534     if (cache == NULL) {
535         return "";
536     }
537
538     for (i=0; i < cache->size; ++i) {
539         if (cache->nodes[i] != NULL) {
540             nchains++;
541             for (n = cache->nodes[i];
542                  n != NULL && n != n->next;
543                  n = n->next) {
544                 totchainlen++;
545             }
546         }
547     }
548     chainlen = nchains? (double)totchainlen / (double)nchains : 0;
549
550     if (id) {
551         buf2 = apr_psprintf(p,
552                  "<a href=\"%s?%s\">%s</a>",
553              r->uri,
554              id,
555              name);
556     }
557     else {
558         buf2 = name;
559     }
560
561     buf = apr_psprintf(p,
562              "<tr valign='top'>"
563              "<td nowrap>%s</td>"
564              "<td align='right' nowrap>%lu (%.0f%% full)</td>"
565              "<td align='right'>%.1f</td>"
566              "<td align='right'>%lu/%lu</td>"
567              "<td align='right'>%.0f%%</td>"
568              "<td align='right'>%lu/%lu</td>",
569          buf2,
570          cache->numentries,
571          (double)cache->numentries / (double)cache->maxentries * 100.0,
572          chainlen,
573          cache->hits,
574          cache->fetches,
575          (cache->fetches > 0 ? (double)(cache->hits) / (double)(cache->fetches) * 100.0 : 100.0),
576          cache->inserts,
577          cache->removes);
578
579     if (cache->numpurges) {
580         char str_ctime[APR_CTIME_LEN];
581
582         apr_ctime(str_ctime, cache->last_purge);
583         buf = apr_psprintf(p,
584                  "%s"
585                  "<td align='right'>%lu</td>\n"
586                  "<td align='right' nowrap>%s</td>\n",
587              buf,
588              cache->numpurges,
589              str_ctime);
590     }
591     else {
592         buf = apr_psprintf(p,
593                  "%s<td colspan='2' align='center'>(none)</td>\n",
594              buf);
595     }
596
597     buf = apr_psprintf(p, "%s<td align='right'>%.2gms</td>\n</tr>", buf, cache->avg_purgetime);
598
599     return buf;
600 }
601
602 char *util_ald_cache_display(request_rec *r, util_ldap_state_t *st)
603 {
604     unsigned long i,j;
605     char *buf, *t1, *t2, *t3;
606     char *id1, *id2, *id3;
607     char *argfmt = "cache=%s&id=%d&off=%d";
608     char *scanfmt = "cache=%4s&id=%u&off=%u%1s";
609     apr_pool_t *pool = r->pool;
610     util_cache_node_t *p = NULL;
611     util_url_node_t *n = NULL;
612
613     util_ald_cache_t *util_ldap_cache = st->util_ldap_cache;
614
615
616     if (!util_ldap_cache) {
617         return "<tr valign='top'><td nowrap colspan=7>Cache has not been enabled/initialised.</td></tr>";
618     }
619
620     if (r->args && strlen(r->args)) {
621         char cachetype[5], lint[2];
622         unsigned int id, off;
623         char date_str[APR_CTIME_LEN];
624
625         if ((3 == sscanf(r->args, scanfmt, cachetype, &id, &off, lint)) &&
626             (id < util_ldap_cache->size)) {
627
628             if ((p = util_ldap_cache->nodes[id]) != NULL) {
629                 n = (util_url_node_t *)p->payload;
630                 buf = (char*)n->url;
631             }
632             else {
633                 buf = "";
634             }
635
636             ap_rprintf(r,
637                        "<p>\n"
638                        "<table border='0'>\n"
639                        "<tr>\n"
640                        "<td bgcolor='#000000'><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Cache Name:</b></font></td>"
641                        "<td bgcolor='#ffffff'><font size='-1' face='Arial,Helvetica' color='#000000'><b>%s (%s)</b></font></td>"
642                        "</tr>\n"
643                        "</table>\n</p>\n",
644                        buf,
645                        cachetype[0] == 'm'? "Main" :
646                        (cachetype[0] == 's' ? "Search" :
647                         (cachetype[0] == 'c' ? "Compares" : "DNCompares")));
648
649             switch (cachetype[0]) {
650                 case 'm':
651                     if (util_ldap_cache->marktime) {
652                         apr_ctime(date_str, util_ldap_cache->marktime);
653                     }
654                     else
655                         date_str[0] = 0;
656
657                     ap_rprintf(r,
658                                "<p>\n"
659                                "<table border='0'>\n"
660                                "<tr>\n"
661                                "<td bgcolor='#000000'><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Size:</b></font></td>"
662                                "<td bgcolor='#ffffff'><font size='-1' face='Arial,Helvetica' color='#000000'><b>%ld</b></font></td>"
663                                "</tr>\n"
664                                "<tr>\n"
665                                "<td bgcolor='#000000'><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Max Entries:</b></font></td>"
666                                "<td bgcolor='#ffffff'><font size='-1' face='Arial,Helvetica' color='#000000'><b>%ld</b></font></td>"
667                                "</tr>\n"
668                                "<tr>\n"
669                                "<td bgcolor='#000000'><font size='-1' face='Arial,Helvetica' color='#ffffff'><b># Entries:</b></font></td>"
670                                "<td bgcolor='#ffffff'><font size='-1' face='Arial,Helvetica' color='#000000'><b>%ld</b></font></td>"
671                                "</tr>\n"
672                                "<tr>\n"
673                                "<td bgcolor='#000000'><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Full Mark:</b></font></td>"
674                                "<td bgcolor='#ffffff'><font size='-1' face='Arial,Helvetica' color='#000000'><b>%ld</b></font></td>"
675                                "</tr>\n"
676                                "<tr>\n"
677                                "<td bgcolor='#000000'><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Full Mark Time:</b></font></td>"
678                                "<td bgcolor='#ffffff'><font size='-1' face='Arial,Helvetica' color='#000000'><b>%s</b></font></td>"
679                                "</tr>\n"
680                                "</table>\n</p>\n",
681                                util_ldap_cache->size,
682                                util_ldap_cache->maxentries,
683                                util_ldap_cache->numentries,
684                                util_ldap_cache->fullmark,
685                                date_str);
686
687                     ap_rputs("<p>\n"
688                              "<table border='0'>\n"
689                              "<tr bgcolor='#000000'>\n"
690                              "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>LDAP URL</b></font></td>"
691                              "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Size</b></font></td>"
692                              "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Max Entries</b></font></td>"
693                              "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b># Entries</b></font></td>"
694                              "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Full Mark</b></font></td>"
695                              "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Full Mark Time</b></font></td>"
696                              "</tr>\n", r
697                             );
698                     for (i=0; i < util_ldap_cache->size; ++i) {
699                         for (p = util_ldap_cache->nodes[i]; p != NULL; p = p->next) {
700
701                             (*util_ldap_cache->display)(r, util_ldap_cache, p->payload);
702                         }
703                     }
704                     ap_rputs("</table>\n</p>\n", r);
705
706
707                     break;
708                 case 's':
709                     ap_rputs("<p>\n"
710                              "<table border='0'>\n"
711                              "<tr bgcolor='#000000'>\n"
712                              "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>LDAP Filter</b></font></td>"
713                              "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>User Name</b></font></td>"
714                              "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Last Bind</b></font></td>"
715                              "</tr>\n", r
716                             );
717                     if (n) {
718                         for (i=0; i < n->search_cache->size; ++i) {
719                             for (p = n->search_cache->nodes[i]; p != NULL; p = p->next) {
720
721                                 (*n->search_cache->display)(r, n->search_cache, p->payload);
722                             }
723                         }
724                     }
725                     ap_rputs("</table>\n</p>\n", r);
726                     break;
727                 case 'c':
728                     ap_rputs("<p>\n"
729                              "<table border='0'>\n"
730                              "<tr bgcolor='#000000'>\n"
731                              "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>DN</b></font></td>"
732                              "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Attribute</b></font></td>"
733                              "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Value</b></font></td>"
734                              "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Last Compare</b></font></td>"
735                              "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Result</b></font></td>"
736                              "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Sub-groups?</b></font></td>"
737                              "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>S-G Checked?</b></font></td>"
738                              "</tr>\n", r
739                             );
740                     if (n) {
741                         for (i=0; i < n->compare_cache->size; ++i) {
742                             for (p = n->compare_cache->nodes[i]; p != NULL; p = p->next) {
743
744                                 (*n->compare_cache->display)(r, n->compare_cache, p->payload);
745                             }
746                         }
747                     }
748                     ap_rputs("</table>\n</p>\n", r);
749                     break;
750                 case 'd':
751                     ap_rputs("<p>\n"
752                              "<table border='0'>\n"
753                              "<tr bgcolor='#000000'>\n"
754                              "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Require DN</b></font></td>"
755                              "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Actual DN</b></font></td>"
756                              "</tr>\n", r
757                             );
758                     if (n) {
759                         for (i=0; i < n->dn_compare_cache->size; ++i) {
760                             for (p = n->dn_compare_cache->nodes[i]; p != NULL; p = p->next) {
761
762                                 (*n->dn_compare_cache->display)(r, n->dn_compare_cache, p->payload);
763                             }
764                         }
765                     }
766                     ap_rputs("</table>\n</p>\n", r);
767                     break;
768                 default:
769                     break;
770             }
771
772         }
773         else {
774             buf = "";
775         }
776     }
777     else {
778         ap_rputs("<p>\n"
779                  "<table border='0'>\n"
780                  "<tr bgcolor='#000000'>\n"
781                  "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Cache Name</b></font></td>"
782                  "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Entries</b></font></td>"
783                  "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Avg. Chain Len.</b></font></td>"
784                  "<td colspan='2'><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Hits</b></font></td>"
785                  "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Ins/Rem</b></font></td>"
786                  "<td colspan='2'><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Purges</b></font></td>"
787                  "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Avg Purge Time</b></font></td>"
788                  "</tr>\n", r
789                 );
790
791
792         id1 = apr_psprintf(pool, argfmt, "main", 0, 0);
793         buf = util_ald_cache_display_stats(r, st->util_ldap_cache, "LDAP URL Cache", id1);
794
795         for (i=0; i < util_ldap_cache->size; ++i) {
796             for (p = util_ldap_cache->nodes[i],j=0; p != NULL; p = p->next,j++) {
797
798                 n = (util_url_node_t *)p->payload;
799
800                 t1 = apr_psprintf(pool, "%s (Searches)", n->url);
801                 t2 = apr_psprintf(pool, "%s (Compares)", n->url);
802                 t3 = apr_psprintf(pool, "%s (DNCompares)", n->url);
803                 id1 = apr_psprintf(pool, argfmt, "srch", i, j);
804                 id2 = apr_psprintf(pool, argfmt, "cmpr", i, j);
805                 id3 = apr_psprintf(pool, argfmt, "dncp", i, j);
806
807                 buf = apr_psprintf(pool, "%s\n\n"
808                                          "%s\n\n"
809                                          "%s\n\n"
810                                          "%s\n\n",
811                                          buf,
812                                          util_ald_cache_display_stats(r, n->search_cache, t1, id1),
813                                          util_ald_cache_display_stats(r, n->compare_cache, t2, id2),
814                                          util_ald_cache_display_stats(r, n->dn_compare_cache, t3, id3)
815                                   );
816             }
817         }
818         ap_rputs(buf, r);
819         ap_rputs("</table>\n</p>\n", r);
820     }
821
822     return buf;
823 }
824
825 #endif /* APR_HAS_LDAP */