/* ==================================================================== * The Apache Software License, Version 1.1 * * Copyright (c) 2000-2001 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, * if any, must include the following acknowledgment: * "This product includes software developed by the * Apache Software Foundation (http://www.apache.org/)." * Alternately, this acknowledgment may appear in the software itself, * if and wherever such third-party acknowledgments normally appear. * * 4. The names "Apache" and "Apache Software Foundation" must * not be used to endorse or promote products derived from this * software without prior written permission. For written * permission, please contact apache@apache.org. * * 5. Products derived from this software may not be called "Apache", * nor may "Apache" appear in their name, without prior written * permission of the Apache Software Foundation. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . */ /* * util_ldap_cache_mgr.c: LDAP cache manager things * * Original code from auth_ldap module for Apache v1.3: * Copyright 1998, 1999 Enbridge Pipelines Inc. * Copyright 1999-2001 Dave Carrigan */ #include #include "util_ldap.h" #include "util_ldap_cache.h" #include #ifdef APU_HAS_LDAP /* only here until strdup is gone */ #include /* here till malloc is gone */ #include static const int primes[] = { 11, 19, 37, 73, 109, 163, 251, 367, 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(const void *ptr) { #if APR_HAS_SHARED_MEMORY if (util_ldap_shm) { if (ptr) apr_shm_free(util_ldap_shm, (void *)ptr); } else { if (ptr) free((void *)ptr); } #else if (ptr) free((void *)ptr); #endif } void *util_ald_alloc(unsigned long size) { if (0 == size) return NULL; #if APR_HAS_SHARED_MEMORY if (util_ldap_shm) { return (void *)apr_shm_calloc(util_ldap_shm, size); } else { return (void *)calloc(sizeof(char), size); } #else return (void *)calloc(sizeof(char), size); #endif } const char *util_ald_strdup(const char *s) { #if APR_HAS_SHARED_MEMORY if (util_ldap_shm) { char *buf = apr_shm_malloc(util_ldap_shm, strlen(s)+1); if (buf) { strcpy(buf, s); return buf; } else { return NULL; } } else { 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) { int 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)(p->payload); util_ald_free(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 = 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->search_cache_size, util_ldap_search_node_hash, util_ldap_search_node_compare, util_ldap_search_node_copy, util_ldap_search_node_free); compare_cache = util_ald_create_cache(st->compare_cache_size, util_ldap_compare_node_hash, util_ldap_compare_node_compare, util_ldap_compare_node_copy, util_ldap_compare_node_free); dn_compare_cache = util_ald_create_cache(st->compare_cache_size, util_ldap_dn_compare_node_hash, util_ldap_dn_compare_node_compare, util_ldap_dn_compare_node_copy, util_ldap_dn_compare_node_free); /* check that all the caches initialised successfully */ if (search_cache && compare_cache && dn_compare_cache) { curl = (util_url_node_t *)apr_pcalloc(st->pool, 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; util_ald_cache_insert(util_ldap_cache, curl); } return curl; } util_ald_cache_t *util_ald_create_cache(unsigned long maxentries, unsigned long (*hashfunc)(void *), int (*comparefunc)(void *, void *), void * (*copyfunc)(void *), void (*freefunc)(void *)) { util_ald_cache_t *cache; int i; if (maxentries <= 0) return NULL; cache = (util_ald_cache_t *)util_ald_alloc(sizeof(util_ald_cache_t)); if (!cache) return NULL; cache->maxentries = maxentries; cache->numentries = 0; cache->size = maxentries / 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->size * sizeof(util_cache_node_t *)); if (!cache->nodes) { util_ald_free(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->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) { int 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)(p->payload); util_ald_free(p); p = q; } } util_ald_free(cache->nodes); } 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; if (cache == NULL || payload == NULL) return; cache->inserts++; hashval = (*cache->hash)(payload) % cache->size; node = (util_cache_node_t *)util_ald_alloc(sizeof(util_cache_node_t)); node->add_time = apr_time_now(); node->payload = (*cache->copy)(payload); node->next = cache->nodes[hashval]; cache->nodes[hashval] = node; if (++cache->numentries == cache->fullmark) cache->marktime=apr_time_now(); if (cache->numentries >= cache->maxentries) util_ald_cache_purge(cache); } 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)(p->payload); util_ald_free(p); cache->numentries--; } char *util_ald_cache_display_stats(apr_pool_t *p, util_ald_cache_t *cache, char *name) { int i; int totchainlen = 0; int nchains = 0; double chainlen; util_cache_node_t *n; char *buf; 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; buf = apr_psprintf(p, "" "%s" "%lu (%.0f%% full)" "%.1f" "%lu/%lu" "%.0f%%" "%lu/%lu", name, 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" "%lu\n" "%s\n", buf, cache->numpurges, str_ctime); } else { buf = apr_psprintf(p, "%s(none)\n", buf); } buf = apr_psprintf(p, "%s%.2g\n", buf, cache->avg_purgetime); return buf; } char *util_ald_cache_display(apr_pool_t *pool) { int i; char *buf, *t1, *t2, *t3; if (!util_ldap_cache) { return "Cache has not been enabled/initialised."; } buf = util_ald_cache_display_stats(pool, util_ldap_cache, "LDAP URL Cache"); for (i=0; i < util_ldap_cache->size; ++i) { util_cache_node_t *p; for (p = util_ldap_cache->nodes[i]; p != NULL; p = p->next) { util_url_node_t *n; 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); buf = apr_psprintf(pool, "%s\n\n" "%s\n\n" "%s\n\n" "%s\n\n", buf, util_ald_cache_display_stats(pool, n->search_cache, t1), util_ald_cache_display_stats(pool, n->compare_cache, t2), util_ald_cache_display_stats(pool, n->dn_compare_cache, t3) ); } } return buf; } #endif /* APU_HAS_LDAP */