]> granicus.if.org Git - apache/blob - modules/ldap/util_ldap_cache_mgr.c
Fix spelling in comments and text files.
[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 #if APR_HAS_SHARED_MEMORY
335     apr_rmm_off_t block;
336 #endif
337
338     if (cache_size <= 0)
339         return NULL;
340
341 #if APR_HAS_SHARED_MEMORY
342     if (!st->cache_rmm) {
343         cache = (util_ald_cache_t *)calloc(sizeof(util_ald_cache_t), 1);
344     }
345     else {
346         block = apr_rmm_calloc(st->cache_rmm, sizeof(util_ald_cache_t));
347         cache = block ? (util_ald_cache_t *)apr_rmm_addr_get(st->cache_rmm, block) : NULL;
348     }
349 #else
350     cache = (util_ald_cache_t *)calloc(sizeof(util_ald_cache_t), 1);
351 #endif
352     if (!cache)
353         return NULL;
354
355 #if APR_HAS_SHARED_MEMORY
356     cache->rmm_addr = st->cache_rmm;
357     cache->shm_addr = st->cache_shm;
358 #endif
359     cache->maxentries = cache_size;
360     cache->numentries = 0;
361     cache->size = cache_size / 3;
362     if (cache->size < 64)
363         cache->size = 64;
364     for (i = 0; primes[i] && primes[i] < cache->size; ++i)
365         ;
366     cache->size = primes[i] ? primes[i] : primes[i-1];
367
368     cache->nodes = (util_cache_node_t **)util_ald_alloc(cache, cache->size * sizeof(util_cache_node_t *));
369     if (!cache->nodes) {
370         /* This frees cache in the right way even if !APR_HAS_SHARED_MEMORY or !st->cache_rmm */
371         util_ald_free(cache, cache);
372         return NULL;
373     }
374
375     for (i=0; i < cache->size; ++i)
376         cache->nodes[i] = NULL;
377
378     cache->hash = hashfunc;
379     cache->compare = comparefunc;
380     cache->copy = copyfunc;
381     cache->free = freefunc;
382     cache->display = displayfunc;
383
384     cache->fullmark = cache->maxentries / 4 * 3;
385     cache->marktime = 0;
386     cache->avg_purgetime = 0.0;
387     cache->numpurges = 0;
388     cache->last_purge = 0;
389     cache->npurged = 0;
390
391     cache->fetches = 0;
392     cache->hits = 0;
393     cache->inserts = 0;
394     cache->removes = 0;
395
396     return cache;
397 }
398
399 void util_ald_destroy_cache(util_ald_cache_t *cache)
400 {
401     unsigned long i;
402     util_cache_node_t *p, *q;
403
404     if (cache == NULL)
405         return;
406
407     for (i = 0; i < cache->size; ++i) {
408         p = cache->nodes[i];
409         q = NULL;
410         while (p != NULL) {
411             q = p->next;
412            (*cache->free)(cache, p->payload);
413            util_ald_free(cache, p);
414            p = q;
415         }
416     }
417     util_ald_free(cache, cache->nodes);
418     util_ald_free(cache, cache);
419 }
420
421 void *util_ald_cache_fetch(util_ald_cache_t *cache, void *payload)
422 {
423     unsigned long hashval;
424     util_cache_node_t *p;
425
426     if (cache == NULL)
427         return NULL;
428
429     cache->fetches++;
430
431     hashval = (*cache->hash)(payload) % cache->size;
432
433     for (p = cache->nodes[hashval];
434          p && !(*cache->compare)(p->payload, payload);
435          p = p->next) ;
436
437     if (p != NULL) {
438         cache->hits++;
439         return p->payload;
440     }
441     else {
442         return NULL;
443     }
444 }
445
446 /*
447  * Insert an item into the cache.
448  * *** Does not catch duplicates!!! ***
449  */
450 void *util_ald_cache_insert(util_ald_cache_t *cache, void *payload)
451 {
452     unsigned long hashval;
453     void *tmp_payload;
454     util_cache_node_t *node;
455
456     /* sanity check */
457     if (cache == NULL || payload == NULL) {
458         return NULL;
459     }
460
461     /* check if we are full - if so, try purge */
462     if (cache->numentries >= cache->maxentries) {
463         util_ald_cache_purge(cache);
464         if (cache->numentries >= cache->maxentries) {
465             /* if the purge was not effective, we leave now to avoid an overflow */
466             ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, APLOGNO(01323)
467                          "Purge of LDAP cache failed");
468             return NULL;
469         }
470     }
471
472     node = (util_cache_node_t *)util_ald_alloc(cache,
473                                                sizeof(util_cache_node_t));
474     if (node == NULL) {
475         /*
476          * XXX: The cache management should be rewritten to work
477          * properly when LDAPSharedCacheSize is too small.
478          */
479         ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL, APLOGNO(01324)
480                      "LDAPSharedCacheSize is too small. Increase it or "
481                      "reduce LDAPCacheEntries/LDAPOpCacheEntries!");
482         if (cache->numentries < cache->fullmark) {
483             /*
484              * We have not even reached fullmark, trigger a complete purge.
485              * This is still better than not being able to add new entries
486              * at all.
487              */
488             cache->marktime = apr_time_now();
489         }
490         util_ald_cache_purge(cache);
491         node = (util_cache_node_t *)util_ald_alloc(cache,
492                                                    sizeof(util_cache_node_t));
493         if (node == NULL) {
494             ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, APLOGNO(01325)
495                          "Could not allocate memory for LDAP cache entry");
496             return NULL;
497         }
498     }
499
500     /* Take a copy of the payload before proceeding. */
501     tmp_payload = (*cache->copy)(cache, payload);
502     if (tmp_payload == NULL) {
503         /*
504          * XXX: The cache management should be rewritten to work
505          * properly when LDAPSharedCacheSize is too small.
506          */
507         ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL, APLOGNO(01326)
508                      "LDAPSharedCacheSize is too small. Increase it or "
509                      "reduce LDAPCacheEntries/LDAPOpCacheEntries!");
510         if (cache->numentries < cache->fullmark) {
511             /*
512              * We have not even reached fullmark, trigger a complete purge.
513              * This is still better than not being able to add new entries
514              * at all.
515              */
516             cache->marktime = apr_time_now();
517         }
518         util_ald_cache_purge(cache);
519         tmp_payload = (*cache->copy)(cache, payload);
520         if (tmp_payload == NULL) {
521             ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, APLOGNO(01327)
522                          "Could not allocate memory for LDAP cache value");
523             util_ald_free(cache, node);
524             return NULL;
525         }
526     }
527     payload = tmp_payload;
528
529     /* populate the entry */
530     cache->inserts++;
531     hashval = (*cache->hash)(payload) % cache->size;
532     node->add_time = apr_time_now();
533     node->payload = payload;
534     node->next = cache->nodes[hashval];
535     cache->nodes[hashval] = node;
536
537     /* if we reach the full mark, note the time we did so
538      * for the benefit of the purge function
539      */
540     if (++cache->numentries == cache->fullmark) {
541         cache->marktime=apr_time_now();
542     }
543
544     return node->payload;
545 }
546
547 void util_ald_cache_remove(util_ald_cache_t *cache, void *payload)
548 {
549     unsigned long hashval;
550     util_cache_node_t *p, *q;
551
552     if (cache == NULL)
553         return;
554
555     cache->removes++;
556     hashval = (*cache->hash)(payload) % cache->size;
557     for (p = cache->nodes[hashval], q=NULL;
558          p && !(*cache->compare)(p->payload, payload);
559          p = p->next) {
560          q = p;
561     }
562
563     /* If p is null, it means that we couldn't find the node, so just return */
564     if (p == NULL)
565         return;
566
567     if (q == NULL) {
568         /* We found the node, and it's the first in the list */
569         cache->nodes[hashval] = p->next;
570     }
571     else {
572         /* We found the node and it's not the first in the list */
573         q->next = p->next;
574     }
575     (*cache->free)(cache, p->payload);
576     util_ald_free(cache, p);
577     cache->numentries--;
578 }
579
580 char *util_ald_cache_display_stats(request_rec *r, util_ald_cache_t *cache, char *name, char *id)
581 {
582     unsigned long i;
583     int totchainlen = 0;
584     int nchains = 0;
585     double chainlen;
586     util_cache_node_t *n;
587     char *buf, *buf2;
588     apr_pool_t *p = r->pool;
589
590     if (cache == NULL) {
591         return "";
592     }
593
594     for (i=0; i < cache->size; ++i) {
595         if (cache->nodes[i] != NULL) {
596             nchains++;
597             for (n = cache->nodes[i];
598                  n != NULL && n != n->next;
599                  n = n->next) {
600                 totchainlen++;
601             }
602         }
603     }
604     chainlen = nchains? (double)totchainlen / (double)nchains : 0;
605
606     if (id) {
607         buf2 = apr_psprintf(p,
608                  "<a href=\"%s?%s\">%s</a>",
609              ap_escape_html(r->pool, ap_escape_uri(r->pool, r->uri)),
610              id,
611              name);
612     }
613     else {
614         buf2 = name;
615     }
616
617     buf = apr_psprintf(p,
618              "<tr valign='top'>"
619              "<td nowrap>%s</td>"
620              "<td align='right' nowrap>%lu (%.0f%% full)</td>"
621              "<td align='right'>%.1f</td>"
622              "<td align='right'>%lu/%lu</td>"
623              "<td align='right'>%.0f%%</td>"
624              "<td align='right'>%lu/%lu</td>",
625          buf2,
626          cache->numentries,
627          (double)cache->numentries / (double)cache->maxentries * 100.0,
628          chainlen,
629          cache->hits,
630          cache->fetches,
631          (cache->fetches > 0 ? (double)(cache->hits) / (double)(cache->fetches) * 100.0 : 100.0),
632          cache->inserts,
633          cache->removes);
634
635     if (cache->numpurges) {
636         char str_ctime[APR_CTIME_LEN];
637
638         apr_ctime(str_ctime, cache->last_purge);
639         buf = apr_psprintf(p,
640                  "%s"
641                  "<td align='right'>%lu</td>\n"
642                  "<td align='right' nowrap>%s</td>\n",
643              buf,
644              cache->numpurges,
645              str_ctime);
646     }
647     else {
648         buf = apr_psprintf(p,
649                  "%s<td colspan='2' align='center'>(none)</td>\n",
650              buf);
651     }
652
653     buf = apr_psprintf(p, "%s<td align='right'>%.2gms</td>\n</tr>", buf, cache->avg_purgetime);
654
655     return buf;
656 }
657
658 char *util_ald_cache_display(request_rec *r, util_ldap_state_t *st)
659 {
660     unsigned long i,j;
661     char *buf, *t1, *t2, *t3;
662     char *id1, *id2, *id3;
663     char *argfmt = "cache=%s&id=%d&off=%d";
664     char *scanfmt = "cache=%4s&id=%u&off=%u%1s";
665     apr_pool_t *pool = r->pool;
666     util_cache_node_t *p = NULL;
667     util_url_node_t *n = NULL;
668
669     util_ald_cache_t *util_ldap_cache = st->util_ldap_cache;
670
671
672     if (!util_ldap_cache) {
673         ap_rputs("<tr valign='top'><td nowrap colspan=7>Cache has not been enabled/initialised.</td></tr>", r);
674         return NULL;
675     }
676
677     if (r->args && strlen(r->args)) {
678         char cachetype[5], lint[2];
679         unsigned int id, off;
680         char date_str[APR_CTIME_LEN];
681
682         if ((3 == sscanf(r->args, scanfmt, cachetype, &id, &off, lint)) &&
683             (id < util_ldap_cache->size)) {
684
685             if ((p = util_ldap_cache->nodes[id]) != NULL) {
686                 n = (util_url_node_t *)p->payload;
687                 buf = (char*)n->url;
688             }
689             else {
690                 buf = "";
691             }
692
693             ap_rprintf(r,
694                        "<p>\n"
695                        "<table border='0'>\n"
696                        "<tr>\n"
697                        "<td bgcolor='#000000'><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Cache Name:</b></font></td>"
698                        "<td bgcolor='#ffffff'><font size='-1' face='Arial,Helvetica' color='#000000'><b>%s (%s)</b></font></td>"
699                        "</tr>\n"
700                        "</table>\n</p>\n",
701                        buf,
702                        cachetype[0] == 'm'? "Main" :
703                        (cachetype[0] == 's' ? "Search" :
704                         (cachetype[0] == 'c' ? "Compares" : "DNCompares")));
705
706             switch (cachetype[0]) {
707                 case 'm':
708                     if (util_ldap_cache->marktime) {
709                         apr_ctime(date_str, util_ldap_cache->marktime);
710                     }
711                     else
712                         date_str[0] = 0;
713
714                     ap_rprintf(r,
715                                "<p>\n"
716                                "<table border='0'>\n"
717                                "<tr>\n"
718                                "<td bgcolor='#000000'><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Size:</b></font></td>"
719                                "<td bgcolor='#ffffff'><font size='-1' face='Arial,Helvetica' color='#000000'><b>%ld</b></font></td>"
720                                "</tr>\n"
721                                "<tr>\n"
722                                "<td bgcolor='#000000'><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Max Entries:</b></font></td>"
723                                "<td bgcolor='#ffffff'><font size='-1' face='Arial,Helvetica' color='#000000'><b>%ld</b></font></td>"
724                                "</tr>\n"
725                                "<tr>\n"
726                                "<td bgcolor='#000000'><font size='-1' face='Arial,Helvetica' color='#ffffff'><b># Entries:</b></font></td>"
727                                "<td bgcolor='#ffffff'><font size='-1' face='Arial,Helvetica' color='#000000'><b>%ld</b></font></td>"
728                                "</tr>\n"
729                                "<tr>\n"
730                                "<td bgcolor='#000000'><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Full Mark:</b></font></td>"
731                                "<td bgcolor='#ffffff'><font size='-1' face='Arial,Helvetica' color='#000000'><b>%ld</b></font></td>"
732                                "</tr>\n"
733                                "<tr>\n"
734                                "<td bgcolor='#000000'><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Full Mark Time:</b></font></td>"
735                                "<td bgcolor='#ffffff'><font size='-1' face='Arial,Helvetica' color='#000000'><b>%s</b></font></td>"
736                                "</tr>\n"
737                                "</table>\n</p>\n",
738                                util_ldap_cache->size,
739                                util_ldap_cache->maxentries,
740                                util_ldap_cache->numentries,
741                                util_ldap_cache->fullmark,
742                                date_str);
743
744                     ap_rputs("<p>\n"
745                              "<table border='0'>\n"
746                              "<tr bgcolor='#000000'>\n"
747                              "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>LDAP URL</b></font></td>"
748                              "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Size</b></font></td>"
749                              "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Max Entries</b></font></td>"
750                              "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b># Entries</b></font></td>"
751                              "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Full Mark</b></font></td>"
752                              "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Full Mark Time</b></font></td>"
753                              "</tr>\n", r
754                             );
755                     for (i=0; i < util_ldap_cache->size; ++i) {
756                         for (p = util_ldap_cache->nodes[i]; p != NULL; p = p->next) {
757
758                             (*util_ldap_cache->display)(r, util_ldap_cache, p->payload);
759                         }
760                     }
761                     ap_rputs("</table>\n</p>\n", r);
762
763
764                     break;
765                 case 's':
766                     ap_rputs("<p>\n"
767                              "<table border='0'>\n"
768                              "<tr bgcolor='#000000'>\n"
769                              "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>LDAP Filter</b></font></td>"
770                              "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>User Name</b></font></td>"
771                              "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Last Bind</b></font></td>"
772                              "</tr>\n", r
773                             );
774                     if (n) {
775                         for (i=0; i < n->search_cache->size; ++i) {
776                             for (p = n->search_cache->nodes[i]; p != NULL; p = p->next) {
777
778                                 (*n->search_cache->display)(r, n->search_cache, p->payload);
779                             }
780                         }
781                     }
782                     ap_rputs("</table>\n</p>\n", r);
783                     break;
784                 case 'c':
785                     ap_rputs("<p>\n"
786                              "<table border='0'>\n"
787                              "<tr bgcolor='#000000'>\n"
788                              "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>DN</b></font></td>"
789                              "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Attribute</b></font></td>"
790                              "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Value</b></font></td>"
791                              "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Last Compare</b></font></td>"
792                              "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Result</b></font></td>"
793                              "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Sub-groups?</b></font></td>"
794                              "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>S-G Checked?</b></font></td>"
795                              "</tr>\n", r
796                             );
797                     if (n) {
798                         for (i=0; i < n->compare_cache->size; ++i) {
799                             for (p = n->compare_cache->nodes[i]; p != NULL; p = p->next) {
800
801                                 (*n->compare_cache->display)(r, n->compare_cache, p->payload);
802                             }
803                         }
804                     }
805                     ap_rputs("</table>\n</p>\n", r);
806                     break;
807                 case 'd':
808                     ap_rputs("<p>\n"
809                              "<table border='0'>\n"
810                              "<tr bgcolor='#000000'>\n"
811                              "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Require DN</b></font></td>"
812                              "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Actual DN</b></font></td>"
813                              "</tr>\n", r
814                             );
815                     if (n) {
816                         for (i=0; i < n->dn_compare_cache->size; ++i) {
817                             for (p = n->dn_compare_cache->nodes[i]; p != NULL; p = p->next) {
818
819                                 (*n->dn_compare_cache->display)(r, n->dn_compare_cache, p->payload);
820                             }
821                         }
822                     }
823                     ap_rputs("</table>\n</p>\n", r);
824                     break;
825                 default:
826                     break;
827             }
828
829         }
830         else {
831             buf = "";
832         }
833     }
834     else {
835         ap_rputs("<p>\n"
836                  "<table border='0'>\n"
837                  "<tr bgcolor='#000000'>\n"
838                  "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Cache Name</b></font></td>"
839                  "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Entries</b></font></td>"
840                  "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Avg. Chain Len.</b></font></td>"
841                  "<td colspan='2'><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Hits</b></font></td>"
842                  "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Ins/Rem</b></font></td>"
843                  "<td colspan='2'><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Purges</b></font></td>"
844                  "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Avg Purge Time</b></font></td>"
845                  "</tr>\n", r
846                 );
847
848
849         id1 = apr_psprintf(pool, argfmt, "main", 0, 0);
850         buf = util_ald_cache_display_stats(r, st->util_ldap_cache, "LDAP URL Cache", id1);
851
852         for (i=0; i < util_ldap_cache->size; ++i) {
853             for (p = util_ldap_cache->nodes[i],j=0; p != NULL; p = p->next,j++) {
854
855                 n = (util_url_node_t *)p->payload;
856
857                 t1 = apr_psprintf(pool, "%s (Searches)", n->url);
858                 t2 = apr_psprintf(pool, "%s (Compares)", n->url);
859                 t3 = apr_psprintf(pool, "%s (DNCompares)", n->url);
860                 id1 = apr_psprintf(pool, argfmt, "srch", i, j);
861                 id2 = apr_psprintf(pool, argfmt, "cmpr", i, j);
862                 id3 = apr_psprintf(pool, argfmt, "dncp", i, j);
863
864                 buf = apr_psprintf(pool, "%s\n\n"
865                                          "%s\n\n"
866                                          "%s\n\n"
867                                          "%s\n\n",
868                                          buf,
869                                          util_ald_cache_display_stats(r, n->search_cache, t1, id1),
870                                          util_ald_cache_display_stats(r, n->compare_cache, t2, id2),
871                                          util_ald_cache_display_stats(r, n->dn_compare_cache, t3, id3)
872                                   );
873             }
874         }
875         ap_rputs(buf, r);
876         ap_rputs("</table>\n</p>\n", r);
877     }
878
879     return buf;
880 }
881
882 #endif /* APR_HAS_LDAP */