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