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