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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 * util_ldap_cache_mgr.c: LDAP cache manager things
20 * Original code from auth_ldap module for Apache v1.3:
21 * Copyright 1998, 1999 Enbridge Pipelines Inc.
22 * Copyright 1999-2001 Dave Carrigan
26 #include "util_ldap.h"
27 #include "util_ldap_cache.h"
28 #include <apr_strings.h>
30 APLOG_USE_MODULE(ldap);
34 /* only here until strdup is gone */
37 /* here till malloc is gone */
40 static const unsigned long primes[] =
79 void util_ald_free(util_ald_cache_t *cache, const void *ptr)
81 #if APR_HAS_SHARED_MEMORY
82 if (cache->rmm_addr) {
84 /* Free in shared memory */
85 apr_rmm_free(cache->rmm_addr, apr_rmm_offset_get(cache->rmm_addr, (void *)ptr));
89 /* Cache shm is not used */
98 void *util_ald_alloc(util_ald_cache_t *cache, unsigned long size)
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;
109 /* Cache shm is not used */
110 return (void *)calloc(sizeof(char), size);
113 return (void *)calloc(sizeof(char), size);
117 const char *util_ald_strdup(util_ald_cache_t *cache, const char *s)
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;
133 /* Cache shm is not used */
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.
145 util_compare_subgroup_t *util_ald_sgl_dup(util_ald_cache_t *cache, util_compare_subgroup_t *sgl_in)
148 util_compare_subgroup_t *sgl_out = NULL;
154 sgl_out = (util_compare_subgroup_t *) util_ald_alloc(cache, sizeof(util_compare_subgroup_t));
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]);
165 util_ald_free(cache, sgl_out->subgroupDNs);
166 util_ald_free(cache, sgl_out);
171 /* We were able to allocate new strings for all the subgroups */
172 if (sgl_out != NULL) {
173 sgl_out->len = sgl_in->len;
182 * Delete an entire subgroupList.
184 void util_ald_sgl_free(util_ald_cache_t *cache, util_compare_subgroup_t **sgl)
187 if (sgl == NULL || *sgl == NULL) {
191 for (i = 0; i < (*sgl)->len; i++) {
192 util_ald_free(cache, (*sgl)->subgroupDNs[i]);
194 util_ald_free(cache, *sgl);
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.
202 unsigned long util_ald_hash_string(int nstr, ...)
206 unsigned long h=0, g;
209 va_start(args, nstr);
210 for (i=0; i < nstr; ++i) {
211 str = va_arg(args, char *);
212 for (p = str; *p; ++p) {
214 if ( ( g = h & 0xf0000000 ) ) {
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.
232 void util_ald_cache_purge(util_ald_cache_t *cache)
235 util_cache_node_t *p, *q, **pp;
241 cache->last_purge = apr_time_now();
245 for (i=0; i < cache->size; ++i) {
246 pp = cache->nodes + i;
249 if (p->add_time < cache->marktime) {
251 (*cache->free)(cache, p->payload);
252 util_ald_free(cache, p);
265 cache->avg_purgetime =
266 ((t - cache->last_purge) + (cache->avg_purgetime * (cache->numpurges-1))) /
274 util_url_node_t *util_ald_create_caches(util_ldap_state_t *st, const char *url)
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;
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);
304 /* check that all the caches initialised successfully */
305 if (search_cache && compare_cache && dn_compare_cache) {
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));
312 curl.search_cache = search_cache;
313 curl.compare_cache = compare_cache;
314 curl.dn_compare_cache = dn_compare_cache;
316 newcurl = util_ald_cache_insert(st->util_ldap_cache, &curl);
324 util_ald_cache_t *util_ald_create_cache(util_ldap_state_t *st,
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 *))
332 util_ald_cache_t *cache;
334 #if APR_HAS_SHARED_MEMORY
341 #if APR_HAS_SHARED_MEMORY
342 if (!st->cache_rmm) {
343 cache = (util_ald_cache_t *)calloc(sizeof(util_ald_cache_t), 1);
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;
350 cache = (util_ald_cache_t *)calloc(sizeof(util_ald_cache_t), 1);
355 #if APR_HAS_SHARED_MEMORY
356 cache->rmm_addr = st->cache_rmm;
357 cache->shm_addr = st->cache_shm;
359 cache->maxentries = cache_size;
360 cache->numentries = 0;
361 cache->size = cache_size / 3;
362 if (cache->size < 64)
364 for (i = 0; primes[i] && primes[i] < cache->size; ++i)
366 cache->size = primes[i] ? primes[i] : primes[i-1];
368 cache->nodes = (util_cache_node_t **)util_ald_alloc(cache, cache->size * sizeof(util_cache_node_t *));
370 /* This frees cache in the right way even if !APR_HAS_SHARED_MEMORY or !st->cache_rmm */
371 util_ald_free(cache, cache);
375 for (i=0; i < cache->size; ++i)
376 cache->nodes[i] = NULL;
378 cache->hash = hashfunc;
379 cache->compare = comparefunc;
380 cache->copy = copyfunc;
381 cache->free = freefunc;
382 cache->display = displayfunc;
384 cache->fullmark = cache->maxentries / 4 * 3;
386 cache->avg_purgetime = 0.0;
387 cache->numpurges = 0;
388 cache->last_purge = 0;
399 void util_ald_destroy_cache(util_ald_cache_t *cache)
402 util_cache_node_t *p, *q;
407 for (i = 0; i < cache->size; ++i) {
412 (*cache->free)(cache, p->payload);
413 util_ald_free(cache, p);
417 util_ald_free(cache, cache->nodes);
418 util_ald_free(cache, cache);
421 void *util_ald_cache_fetch(util_ald_cache_t *cache, void *payload)
423 unsigned long hashval;
424 util_cache_node_t *p;
431 hashval = (*cache->hash)(payload) % cache->size;
433 for (p = cache->nodes[hashval];
434 p && !(*cache->compare)(p->payload, payload);
447 * Insert an item into the cache.
448 * *** Does not catch duplicates!!! ***
450 void *util_ald_cache_insert(util_ald_cache_t *cache, void *payload)
452 unsigned long hashval;
454 util_cache_node_t *node;
457 if (cache == NULL || payload == NULL) {
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");
472 node = (util_cache_node_t *)util_ald_alloc(cache,
473 sizeof(util_cache_node_t));
476 * XXX: The cache management should be rewritten to work
477 * properly when LDAPSharedCacheSize is too small.
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) {
484 * We have not even reached fullmark, trigger a complete purge.
485 * This is still better than not being able to add new entries
488 cache->marktime = apr_time_now();
490 util_ald_cache_purge(cache);
491 node = (util_cache_node_t *)util_ald_alloc(cache,
492 sizeof(util_cache_node_t));
494 ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, APLOGNO(01325)
495 "Could not allocate memory for LDAP cache entry");
500 /* Take a copy of the payload before proceeding. */
501 tmp_payload = (*cache->copy)(cache, payload);
502 if (tmp_payload == NULL) {
504 * XXX: The cache management should be rewritten to work
505 * properly when LDAPSharedCacheSize is too small.
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) {
512 * We have not even reached fullmark, trigger a complete purge.
513 * This is still better than not being able to add new entries
516 cache->marktime = apr_time_now();
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);
527 payload = tmp_payload;
529 /* populate the entry */
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;
537 /* if we reach the full mark, note the time we did so
538 * for the benefit of the purge function
540 if (++cache->numentries == cache->fullmark) {
541 cache->marktime=apr_time_now();
544 return node->payload;
547 void util_ald_cache_remove(util_ald_cache_t *cache, void *payload)
549 unsigned long hashval;
550 util_cache_node_t *p, *q;
556 hashval = (*cache->hash)(payload) % cache->size;
557 for (p = cache->nodes[hashval], q=NULL;
558 p && !(*cache->compare)(p->payload, payload);
563 /* If p is null, it means that we couldn't find the node, so just return */
568 /* We found the node, and it's the first in the list */
569 cache->nodes[hashval] = p->next;
572 /* We found the node and it's not the first in the list */
575 (*cache->free)(cache, p->payload);
576 util_ald_free(cache, p);
580 char *util_ald_cache_display_stats(request_rec *r, util_ald_cache_t *cache, char *name, char *id)
586 util_cache_node_t *n;
588 apr_pool_t *p = r->pool;
594 for (i=0; i < cache->size; ++i) {
595 if (cache->nodes[i] != NULL) {
597 for (n = cache->nodes[i];
598 n != NULL && n != n->next;
604 chainlen = nchains? (double)totchainlen / (double)nchains : 0;
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)),
617 buf = apr_psprintf(p,
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>",
627 (double)cache->numentries / (double)cache->maxentries * 100.0,
631 (cache->fetches > 0 ? (double)(cache->hits) / (double)(cache->fetches) * 100.0 : 100.0),
635 if (cache->numpurges) {
636 char str_ctime[APR_CTIME_LEN];
638 apr_ctime(str_ctime, cache->last_purge);
639 buf = apr_psprintf(p,
641 "<td align='right'>%lu</td>\n"
642 "<td align='right' nowrap>%s</td>\n",
648 buf = apr_psprintf(p,
649 "%s<td colspan='2' align='center'>(none)</td>\n",
653 buf = apr_psprintf(p, "%s<td align='right'>%.2gms</td>\n</tr>", buf, cache->avg_purgetime);
658 char *util_ald_cache_display(request_rec *r, util_ldap_state_t *st)
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;
669 util_ald_cache_t *util_ldap_cache = st->util_ldap_cache;
672 if (!util_ldap_cache) {
673 ap_rputs("<tr valign='top'><td nowrap colspan=7>Cache has not been enabled/initialised.</td></tr>", r);
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];
682 if ((3 == sscanf(r->args, scanfmt, cachetype, &id, &off, lint)) &&
683 (id < util_ldap_cache->size)) {
685 if ((p = util_ldap_cache->nodes[id]) != NULL) {
686 n = (util_url_node_t *)p->payload;
695 "<table border='0'>\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>"
702 cachetype[0] == 'm'? "Main" :
703 (cachetype[0] == 's' ? "Search" :
704 (cachetype[0] == 'c' ? "Compares" : "DNCompares")));
706 switch (cachetype[0]) {
708 if (util_ldap_cache->marktime) {
709 apr_ctime(date_str, util_ldap_cache->marktime);
716 "<table border='0'>\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>"
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>"
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>"
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>"
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>"
738 util_ldap_cache->size,
739 util_ldap_cache->maxentries,
740 util_ldap_cache->numentries,
741 util_ldap_cache->fullmark,
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>"
755 for (i=0; i < util_ldap_cache->size; ++i) {
756 for (p = util_ldap_cache->nodes[i]; p != NULL; p = p->next) {
758 (*util_ldap_cache->display)(r, util_ldap_cache, p->payload);
761 ap_rputs("</table>\n</p>\n", r);
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>"
775 for (i=0; i < n->search_cache->size; ++i) {
776 for (p = n->search_cache->nodes[i]; p != NULL; p = p->next) {
778 (*n->search_cache->display)(r, n->search_cache, p->payload);
782 ap_rputs("</table>\n</p>\n", r);
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>"
798 for (i=0; i < n->compare_cache->size; ++i) {
799 for (p = n->compare_cache->nodes[i]; p != NULL; p = p->next) {
801 (*n->compare_cache->display)(r, n->compare_cache, p->payload);
805 ap_rputs("</table>\n</p>\n", r);
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>"
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) {
819 (*n->dn_compare_cache->display)(r, n->dn_compare_cache, p->payload);
823 ap_rputs("</table>\n</p>\n", r);
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>"
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);
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++) {
855 n = (util_url_node_t *)p->payload;
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);
864 buf = apr_psprintf(pool, "%s\n\n"
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)
876 ap_rputs("</table>\n</p>\n", r);
882 #endif /* APR_HAS_LDAP */