util_ldap_cache_mgr.c revision 3a11074e78d5961088e8f5520ba3cec163faed9c
a8d1a344e580f29699aed9b88d87fc3c6f5d113bSimo Sorce/* Copyright 2001-2004 The Apache Software Foundation
a8d1a344e580f29699aed9b88d87fc3c6f5d113bSimo Sorce *
a8d1a344e580f29699aed9b88d87fc3c6f5d113bSimo Sorce * Licensed under the Apache License, Version 2.0 (the "License");
a8d1a344e580f29699aed9b88d87fc3c6f5d113bSimo Sorce * you may not use this file except in compliance with the License.
a8d1a344e580f29699aed9b88d87fc3c6f5d113bSimo Sorce * You may obtain a copy of the License at
a8d1a344e580f29699aed9b88d87fc3c6f5d113bSimo Sorce *
a8d1a344e580f29699aed9b88d87fc3c6f5d113bSimo Sorce * http://www.apache.org/licenses/LICENSE-2.0
a8d1a344e580f29699aed9b88d87fc3c6f5d113bSimo Sorce *
a8d1a344e580f29699aed9b88d87fc3c6f5d113bSimo Sorce * Unless required by applicable law or agreed to in writing, software
a8d1a344e580f29699aed9b88d87fc3c6f5d113bSimo Sorce * distributed under the License is distributed on an "AS IS" BASIS,
a8d1a344e580f29699aed9b88d87fc3c6f5d113bSimo Sorce * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
a8d1a344e580f29699aed9b88d87fc3c6f5d113bSimo Sorce * See the License for the specific language governing permissions and
a8d1a344e580f29699aed9b88d87fc3c6f5d113bSimo Sorce * limitations under the License.
a8d1a344e580f29699aed9b88d87fc3c6f5d113bSimo Sorce */
a8d1a344e580f29699aed9b88d87fc3c6f5d113bSimo Sorce
a8d1a344e580f29699aed9b88d87fc3c6f5d113bSimo Sorce/*
a8d1a344e580f29699aed9b88d87fc3c6f5d113bSimo Sorce * util_ldap_cache_mgr.c: LDAP cache manager things
a8d1a344e580f29699aed9b88d87fc3c6f5d113bSimo Sorce *
a8d1a344e580f29699aed9b88d87fc3c6f5d113bSimo Sorce * Original code from auth_ldap module for Apache v1.3:
a8d1a344e580f29699aed9b88d87fc3c6f5d113bSimo Sorce * Copyright 1998, 1999 Enbridge Pipelines Inc.
a8d1a344e580f29699aed9b88d87fc3c6f5d113bSimo Sorce * Copyright 1999-2001 Dave Carrigan
a8d1a344e580f29699aed9b88d87fc3c6f5d113bSimo Sorce */
a8d1a344e580f29699aed9b88d87fc3c6f5d113bSimo Sorce
a8d1a344e580f29699aed9b88d87fc3c6f5d113bSimo Sorce#include <apr_ldap.h>
a8d1a344e580f29699aed9b88d87fc3c6f5d113bSimo Sorce#include "util_ldap.h"
a8d1a344e580f29699aed9b88d87fc3c6f5d113bSimo Sorce#include "util_ldap_cache.h"
a8d1a344e580f29699aed9b88d87fc3c6f5d113bSimo Sorce#include <apr_strings.h>
a8d1a344e580f29699aed9b88d87fc3c6f5d113bSimo Sorce
a8d1a344e580f29699aed9b88d87fc3c6f5d113bSimo Sorce#if APR_HAS_LDAP
a8d1a344e580f29699aed9b88d87fc3c6f5d113bSimo Sorce
a8d1a344e580f29699aed9b88d87fc3c6f5d113bSimo Sorce/* only here until strdup is gone */
a8d1a344e580f29699aed9b88d87fc3c6f5d113bSimo Sorce#include <string.h>
8f2a34cc6964a1f80a1434e05315a7ae0bb5774eSimo Sorce
8f2a34cc6964a1f80a1434e05315a7ae0bb5774eSimo Sorce/* here till malloc is gone */
8f2a34cc6964a1f80a1434e05315a7ae0bb5774eSimo Sorce#include <stdlib.h>
8f2a34cc6964a1f80a1434e05315a7ae0bb5774eSimo Sorce
a8d1a344e580f29699aed9b88d87fc3c6f5d113bSimo Sorcestatic const unsigned long primes[] =
8f2a34cc6964a1f80a1434e05315a7ae0bb5774eSimo Sorce{
a8d1a344e580f29699aed9b88d87fc3c6f5d113bSimo Sorce 11,
a8d1a344e580f29699aed9b88d87fc3c6f5d113bSimo Sorce 19,
8f2a34cc6964a1f80a1434e05315a7ae0bb5774eSimo Sorce 37,
8f2a34cc6964a1f80a1434e05315a7ae0bb5774eSimo Sorce 73,
a8d1a344e580f29699aed9b88d87fc3c6f5d113bSimo Sorce 109,
a8d1a344e580f29699aed9b88d87fc3c6f5d113bSimo Sorce 163,
a8d1a344e580f29699aed9b88d87fc3c6f5d113bSimo Sorce 251,
a8d1a344e580f29699aed9b88d87fc3c6f5d113bSimo Sorce 367,
a8d1a344e580f29699aed9b88d87fc3c6f5d113bSimo Sorce 557,
823,
1237,
1861,
2777,
4177,
6247,
9371,
14057,
21089,
31627,
47431,
71143,
106721,
160073,
240101,
360163,
540217,
810343,
1215497,
1823231,
2734867,
4102283,
6153409,
9230113,
13845163,
0
};
void util_ald_free(util_ald_cache_t *cache, const void *ptr)
{
#if APR_HAS_SHARED_MEMORY
if (cache->rmm_addr) {
if (ptr)
/* Free in shared memory */
apr_rmm_free(cache->rmm_addr, apr_rmm_offset_get(cache->rmm_addr, (void *)ptr));
}
else {
if (ptr)
/* Cache shm is not used */
free((void *)ptr);
}
#else
if (ptr)
free((void *)ptr);
#endif
}
void *util_ald_alloc(util_ald_cache_t *cache, unsigned long size)
{
if (0 == size)
return NULL;
#if APR_HAS_SHARED_MEMORY
if (cache->rmm_addr) {
/* allocate from shared memory */
apr_rmm_off_t block = apr_rmm_calloc(cache->rmm_addr, size);
return block ? (void *)apr_rmm_addr_get(cache->rmm_addr, block) : NULL;
}
else {
/* Cache shm is not used */
return (void *)calloc(sizeof(char), size);
}
#else
return (void *)calloc(sizeof(char), size);
#endif
}
const char *util_ald_strdup(util_ald_cache_t *cache, const char *s)
{
#if APR_HAS_SHARED_MEMORY
if (cache->rmm_addr) {
/* allocate from shared memory */
apr_rmm_off_t block = apr_rmm_calloc(cache->rmm_addr, strlen(s)+1);
char *buf = block ? (char *)apr_rmm_addr_get(cache->rmm_addr, block) : NULL;
if (buf) {
strcpy(buf, s);
return buf;
}
else {
return NULL;
}
} else {
/* Cache shm is not used */
return strdup(s);
}
#else
return strdup(s);
#endif
}
/*
* Computes the hash on a set of strings. The first argument is the number
* of strings to hash, the rest of the args are strings.
* Algorithm taken from glibc.
*/
unsigned long util_ald_hash_string(int nstr, ...)
{
int i;
va_list args;
unsigned long h=0, g;
char *str, *p;
va_start(args, nstr);
for (i=0; i < nstr; ++i) {
str = va_arg(args, char *);
for (p = str; *p; ++p) {
h = ( h << 4 ) + *p;
if ( ( g = h & 0xf0000000 ) ) {
h = h ^ (g >> 24);
h = h ^ g;
}
}
}
va_end(args);
return h;
}
/*
Purges a cache that has gotten full. We keep track of the time that we
added the entry that made the cache 3/4 full, then delete all entries
that were added before that time. It's pretty simplistic, but time to
purge is only O(n), which is more important.
*/
void util_ald_cache_purge(util_ald_cache_t *cache)
{
unsigned long i;
util_cache_node_t *p, *q;
apr_time_t t;
if (!cache)
return;
cache->last_purge = apr_time_now();
cache->npurged = 0;
cache->numpurges++;
for (i=0; i < cache->size; ++i) {
p = cache->nodes[i];
while (p != NULL) {
if (p->add_time < cache->marktime) {
q = p->next;
(*cache->free)(cache, p->payload);
util_ald_free(cache, p);
cache->numentries--;
cache->npurged++;
p = q;
}
else {
p = p->next;
}
}
}
t = apr_time_now();
cache->avg_purgetime =
((t - cache->last_purge) + (cache->avg_purgetime * (cache->numpurges-1))) /
cache->numpurges;
}
/*
* create caches
*/
util_url_node_t *util_ald_create_caches(util_ldap_state_t *st, const char *url)
{
util_url_node_t curl, *newcurl = NULL;
util_ald_cache_t *search_cache;
util_ald_cache_t *compare_cache;
util_ald_cache_t *dn_compare_cache;
/* create the three caches */
search_cache = util_ald_create_cache(st,
util_ldap_search_node_hash,
util_ldap_search_node_compare,
util_ldap_search_node_copy,
util_ldap_search_node_free,
util_ldap_search_node_display);
compare_cache = util_ald_create_cache(st,
util_ldap_compare_node_hash,
util_ldap_compare_node_compare,
util_ldap_compare_node_copy,
util_ldap_compare_node_free,
util_ldap_compare_node_display);
dn_compare_cache = util_ald_create_cache(st,
util_ldap_dn_compare_node_hash,
util_ldap_dn_compare_node_compare,
util_ldap_dn_compare_node_copy,
util_ldap_dn_compare_node_free,
util_ldap_dn_compare_node_display);
/* check that all the caches initialised successfully */
if (search_cache && compare_cache && dn_compare_cache) {
/* The contents of this structure will be duplicated in shared
memory during the insert. So use stack memory rather than
pool memory to avoid a memory leak. */
memset (&curl, 0, sizeof(util_url_node_t));
curl.url = url;
curl.search_cache = search_cache;
curl.compare_cache = compare_cache;
curl.dn_compare_cache = dn_compare_cache;
newcurl = util_ald_cache_insert(st->util_ldap_cache, &curl);
}
return newcurl;
}
util_ald_cache_t *util_ald_create_cache(util_ldap_state_t *st,
unsigned long (*hashfunc)(void *),
int (*comparefunc)(void *, void *),
void * (*copyfunc)(util_ald_cache_t *cache, void *),
void (*freefunc)(util_ald_cache_t *cache, void *),
void (*displayfunc)(request_rec *r, util_ald_cache_t *cache, void *))
{
util_ald_cache_t *cache;
unsigned long i;
if (st->search_cache_size <= 0)
return NULL;
#if APR_HAS_SHARED_MEMORY
if (!st->cache_rmm) {
return NULL;
}
else {
apr_rmm_off_t block = apr_rmm_calloc(st->cache_rmm, sizeof(util_ald_cache_t));
cache = block ? (util_ald_cache_t *)apr_rmm_addr_get(st->cache_rmm, block) : NULL;
}
#else
cache = (util_ald_cache_t *)calloc(sizeof(util_ald_cache_t), 1);
#endif
if (!cache)
return NULL;
#if APR_HAS_SHARED_MEMORY
cache->rmm_addr = st->cache_rmm;
cache->shm_addr = st->cache_shm;
#endif
cache->maxentries = st->search_cache_size;
cache->numentries = 0;
cache->size = st->search_cache_size / 3;
if (cache->size < 64) cache->size = 64;
for (i = 0; primes[i] && primes[i] < cache->size; ++i) ;
cache->size = primes[i]? primes[i] : primes[i-1];
cache->nodes = (util_cache_node_t **)util_ald_alloc(cache, cache->size * sizeof(util_cache_node_t *));
if (!cache->nodes) {
util_ald_free(cache, cache);
return NULL;
}
for (i=0; i < cache->size; ++i)
cache->nodes[i] = NULL;
cache->hash = hashfunc;
cache->compare = comparefunc;
cache->copy = copyfunc;
cache->free = freefunc;
cache->display = displayfunc;
cache->fullmark = cache->maxentries / 4 * 3;
cache->marktime = 0;
cache->avg_purgetime = 0.0;
cache->numpurges = 0;
cache->last_purge = 0;
cache->npurged = 0;
cache->fetches = 0;
cache->hits = 0;
cache->inserts = 0;
cache->removes = 0;
return cache;
}
void util_ald_destroy_cache(util_ald_cache_t *cache)
{
unsigned long i;
util_cache_node_t *p, *q;
if (cache == NULL)
return;
for (i = 0; i < cache->size; ++i) {
p = cache->nodes[i];
q = NULL;
while (p != NULL) {
q = p->next;
(*cache->free)(cache, p->payload);
util_ald_free(cache, p);
p = q;
}
}
util_ald_free(cache, cache->nodes);
util_ald_free(cache, cache);
}
void *util_ald_cache_fetch(util_ald_cache_t *cache, void *payload)
{
int hashval;
util_cache_node_t *p;
if (cache == NULL)
return NULL;
cache->fetches++;
hashval = (*cache->hash)(payload) % cache->size;
for (p = cache->nodes[hashval];
p && !(*cache->compare)(p->payload, payload);
p = p->next) ;
if (p != NULL) {
cache->hits++;
return p->payload;
}
else {
return NULL;
}
}
/*
* Insert an item into the cache.
* *** Does not catch duplicates!!! ***
*/
void *util_ald_cache_insert(util_ald_cache_t *cache, void *payload)
{
int hashval;
util_cache_node_t *node;
/* sanity check */
if (cache == NULL || payload == NULL) {
return NULL;
}
/* check if we are full - if so, try purge */
if (cache->numentries >= cache->maxentries) {
util_ald_cache_purge(cache);
if (cache->numentries >= cache->maxentries) {
/* if the purge was not effective, we leave now to avoid an overflow */
return NULL;
}
}
/* should be safe to add an entry */
if ((node = (util_cache_node_t *)util_ald_alloc(cache, sizeof(util_cache_node_t))) == NULL) {
return NULL;
}
/* populate the entry */
cache->inserts++;
hashval = (*cache->hash)(payload) % cache->size;
node->add_time = apr_time_now();
node->payload = (*cache->copy)(cache, payload);
node->next = cache->nodes[hashval];
cache->nodes[hashval] = node;
/* if we reach the full mark, note the time we did so
* for the benefit of the purge function
*/
if (++cache->numentries == cache->fullmark) {
cache->marktime=apr_time_now();
}
return node->payload;
}
void util_ald_cache_remove(util_ald_cache_t *cache, void *payload)
{
int hashval;
util_cache_node_t *p, *q;
if (cache == NULL)
return;
cache->removes++;
hashval = (*cache->hash)(payload) % cache->size;
for (p = cache->nodes[hashval], q=NULL;
p && !(*cache->compare)(p->payload, payload);
p = p->next) {
q = p;
}
/* If p is null, it means that we couldn't find the node, so just return */
if (p == NULL)
return;
if (q == NULL) {
/* We found the node, and it's the first in the list */
cache->nodes[hashval] = p->next;
}
else {
/* We found the node and it's not the first in the list */
q->next = p->next;
}
(*cache->free)(cache, p->payload);
util_ald_free(cache, p);
cache->numentries--;
}
char *util_ald_cache_display_stats(request_rec *r, util_ald_cache_t *cache, char *name, char *id)
{
unsigned long i;
int totchainlen = 0;
int nchains = 0;
double chainlen;
util_cache_node_t *n;
char *buf, *buf2;
apr_pool_t *p = r->pool;
if (cache == NULL) {
return "";
}
for (i=0; i < cache->size; ++i) {
if (cache->nodes[i] != NULL) {
nchains++;
for (n = cache->nodes[i]; n != NULL; n = n->next)
totchainlen++;
}
}
chainlen = nchains? (double)totchainlen / (double)nchains : 0;
if (id) {
buf2 = apr_psprintf(p,
"<a href=\"%s?%s\">%s</a>",
r->uri,
id,
name);
}
else {
buf2 = name;
}
buf = apr_psprintf(p,
"<tr valign='top'>"
"<td nowrap>%s</td>"
"<td align='right' nowrap>%lu (%.0f%% full)</td>"
"<td align='right'>%.1f</td>"
"<td align='right'>%lu/%lu</td>"
"<td align='right'>%.0f%%</td>"
"<td align='right'>%lu/%lu</td>",
buf2,
cache->numentries,
(double)cache->numentries / (double)cache->maxentries * 100.0,
chainlen,
cache->hits,
cache->fetches,
(cache->fetches > 0 ? (double)(cache->hits) / (double)(cache->fetches) * 100.0 : 100.0),
cache->inserts,
cache->removes);
if (cache->numpurges) {
char str_ctime[APR_CTIME_LEN];
apr_ctime(str_ctime, cache->last_purge);
buf = apr_psprintf(p,
"%s"
"<td align='right'>%lu</td>\n"
"<td align='right' nowrap>%s</td>\n",
buf,
cache->numpurges,
str_ctime);
}
else {
buf = apr_psprintf(p,
"%s<td colspan='2' align='center'>(none)</td>\n",
buf);
}
buf = apr_psprintf(p, "%s<td align='right'>%.2g</td>\n</tr>", buf, cache->avg_purgetime);
return buf;
}
char *util_ald_cache_display(request_rec *r, util_ldap_state_t *st)
{
unsigned long i,j;
char *buf, *t1, *t2, *t3;
char *id1, *id2, *id3;
char *argfmt = "cache=%s&id=%d&off=%d";
char *scanfmt = "cache=%4s&id=%u&off=%u%1s";
apr_pool_t *pool = r->pool;
util_cache_node_t *p = NULL;
util_url_node_t *n = NULL;
util_ald_cache_t *util_ldap_cache = st->util_ldap_cache;
if (!util_ldap_cache) {
return "<tr valign='top'><td nowrap colspan=7>Cache has not been enabled/initialised.</td></tr>";
}
if (r->args && strlen(r->args)) {
char cachetype[5], lint[2];
unsigned int id, off;
char date_str[APR_CTIME_LEN+1];
if ((3 == sscanf(r->args, scanfmt, cachetype, &id, &off, lint)) &&
(id < util_ldap_cache->size)) {
if ((p = util_ldap_cache->nodes[id]) != NULL) {
n = (util_url_node_t *)p->payload;
buf = (char*)n->url;
}
else {
buf = "";
}
ap_rputs(apr_psprintf(r->pool,
"<p>\n"
"<table border='0'>\n"
"<tr>\n"
"<td bgcolor='#000000'><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Cache Name:</b></font></td>"
"<td bgcolor='#ffffff'><font size='-1' face='Arial,Helvetica' color='#000000'><b>%s (%s)</b></font></td>"
"</tr>\n"
"</table>\n</p>\n",
buf,
cachetype[0] == 'm'? "Main" :
(cachetype[0] == 's' ? "Search" :
(cachetype[0] == 'c' ? "Compares" : "DNCompares"))), r);
switch (cachetype[0]) {
case 'm':
if (util_ldap_cache->marktime) {
apr_ctime(date_str, util_ldap_cache->marktime);
}
else
date_str[0] = 0;
ap_rputs(apr_psprintf(r->pool,
"<p>\n"
"<table border='0'>\n"
"<tr>\n"
"<td bgcolor='#000000'><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Size:</b></font></td>"
"<td bgcolor='#ffffff'><font size='-1' face='Arial,Helvetica' color='#000000'><b>%ld</b></font></td>"
"</tr>\n"
"<tr>\n"
"<td bgcolor='#000000'><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Max Entries:</b></font></td>"
"<td bgcolor='#ffffff'><font size='-1' face='Arial,Helvetica' color='#000000'><b>%ld</b></font></td>"
"</tr>\n"
"<tr>\n"
"<td bgcolor='#000000'><font size='-1' face='Arial,Helvetica' color='#ffffff'><b># Entries:</b></font></td>"
"<td bgcolor='#ffffff'><font size='-1' face='Arial,Helvetica' color='#000000'><b>%ld</b></font></td>"
"</tr>\n"
"<tr>\n"
"<td bgcolor='#000000'><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Full Mark:</b></font></td>"
"<td bgcolor='#ffffff'><font size='-1' face='Arial,Helvetica' color='#000000'><b>%ld</b></font></td>"
"</tr>\n"
"<tr>\n"
"<td bgcolor='#000000'><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Full Mark Time:</b></font></td>"
"<td bgcolor='#ffffff'><font size='-1' face='Arial,Helvetica' color='#000000'><b>%s</b></font></td>"
"</tr>\n"
"</table>\n</p>\n",
util_ldap_cache->size,
util_ldap_cache->maxentries,
util_ldap_cache->numentries,
util_ldap_cache->fullmark,
date_str), r);
ap_rputs("<p>\n"
"<table border='0'>\n"
"<tr bgcolor='#000000'>\n"
"<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>LDAP URL</b></font></td>"
"<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Size</b></font></td>"
"<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Max Entries</b></font></td>"
"<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b># Entries</b></font></td>"
"<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Full Mark</b></font></td>"
"<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Full Mark Time</b></font></td>"
"</tr>\n", r
);
for (i=0; i < util_ldap_cache->size; ++i) {
for (p = util_ldap_cache->nodes[i]; p != NULL; p = p->next) {
(*util_ldap_cache->display)(r, util_ldap_cache, p->payload);
}
}
ap_rputs("</table>\n</p>\n", r);
break;
case 's':
ap_rputs("<p>\n"
"<table border='0'>\n"
"<tr bgcolor='#000000'>\n"
"<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>LDAP Filter</b></font></td>"
"<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>User Name</b></font></td>"
"<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Last Bind</b></font></td>"
"</tr>\n", r
);
for (i=0; i < n->search_cache->size; ++i) {
for (p = n->search_cache->nodes[i]; p != NULL; p = p->next) {
(*n->search_cache->display)(r, n->search_cache, p->payload);
}
}
ap_rputs("</table>\n</p>\n", r);
break;
case 'c':
ap_rputs("<p>\n"
"<table border='0'>\n"
"<tr bgcolor='#000000'>\n"
"<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>DN</b></font></td>"
"<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Attribute</b></font></td>"
"<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Value</b></font></td>"
"<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Last Compare</b></font></td>"
"<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Result</b></font></td>"
"</tr>\n", r
);
for (i=0; i < n->compare_cache->size; ++i) {
for (p = n->compare_cache->nodes[i]; p != NULL; p = p->next) {
(*n->compare_cache->display)(r, n->compare_cache, p->payload);
}
}
ap_rputs("</table>\n</p>\n", r);
break;
case 'd':
ap_rputs("<p>\n"
"<table border='0'>\n"
"<tr bgcolor='#000000'>\n"
"<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Require DN</b></font></td>"
"<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Actual DN</b></font></td>"
"</tr>\n", r
);
for (i=0; i < n->dn_compare_cache->size; ++i) {
for (p = n->dn_compare_cache->nodes[i]; p != NULL; p = p->next) {
(*n->dn_compare_cache->display)(r, n->dn_compare_cache, p->payload);
}
}
ap_rputs("</table>\n</p>\n", r);
break;
default:
break;
}
}
}
else {
ap_rputs("<p>\n"
"<table border='0'>\n"
"<tr bgcolor='#000000'>\n"
"<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Cache Name</b></font></td>"
"<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Entries</b></font></td>"
"<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Avg. Chain Len.</b></font></td>"
"<td colspan='2'><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Hits</b></font></td>"
"<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Ins/Rem</b></font></td>"
"<td colspan='2'><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Purges</b></font></td>"
"<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Avg Purge Time</b></font></td>"
"</tr>\n", r
);
id1 = apr_psprintf(pool, argfmt, "main", 0, 0);
buf = util_ald_cache_display_stats(r, st->util_ldap_cache, "LDAP URL Cache", id1);
for (i=0; i < util_ldap_cache->size; ++i) {
for (p = util_ldap_cache->nodes[i],j=0; p != NULL; p = p->next,j++) {
n = (util_url_node_t *)p->payload;
t1 = apr_psprintf(pool, "%s (Searches)", n->url);
t2 = apr_psprintf(pool, "%s (Compares)", n->url);
t3 = apr_psprintf(pool, "%s (DNCompares)", n->url);
id1 = apr_psprintf(pool, argfmt, "srch", i, j);
id2 = apr_psprintf(pool, argfmt, "cmpr", i, j);
id3 = apr_psprintf(pool, argfmt, "dncp", i, j);
buf = apr_psprintf(pool, "%s\n\n"
"%s\n\n"
"%s\n\n"
"%s\n\n",
buf,
util_ald_cache_display_stats(r, n->search_cache, t1, id1),
util_ald_cache_display_stats(r, n->compare_cache, t2, id2),
util_ald_cache_display_stats(r, n->dn_compare_cache, t3, id3)
);
}
}
ap_rputs(buf, r);
ap_rputs("</table>\n</p>\n", r);
}
return buf;
}
#endif /* APR_HAS_LDAP */