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>
32 /* only here until strdup is gone */
35 /* here till malloc is gone */
38 static const unsigned long primes[] =
77 void util_ald_free(util_ald_cache_t *cache, const void *ptr)
79 #if APR_HAS_SHARED_MEMORY
80 if (cache->rmm_addr) {
82 /* Free in shared memory */
83 apr_rmm_free(cache->rmm_addr, apr_rmm_offset_get(cache->rmm_addr, (void *)ptr));
87 /* Cache shm is not used */
96 void *util_ald_alloc(util_ald_cache_t *cache, unsigned long size)
100 #if APR_HAS_SHARED_MEMORY
101 if (cache->rmm_addr) {
102 /* allocate from shared memory */
103 apr_rmm_off_t block = apr_rmm_calloc(cache->rmm_addr, size);
104 return block ? (void *)apr_rmm_addr_get(cache->rmm_addr, block) : NULL;
107 /* Cache shm is not used */
108 return (void *)calloc(sizeof(char), size);
111 return (void *)calloc(sizeof(char), size);
115 const char *util_ald_strdup(util_ald_cache_t *cache, const char *s)
117 #if APR_HAS_SHARED_MEMORY
118 if (cache->rmm_addr) {
119 /* allocate from shared memory */
120 apr_rmm_off_t block = apr_rmm_calloc(cache->rmm_addr, strlen(s)+1);
121 char *buf = block ? (char *)apr_rmm_addr_get(cache->rmm_addr, block) : NULL;
131 /* Cache shm is not used */
140 * Duplicate a subgroupList from one compare entry to another.
141 * Returns: ptr to a new copy of the subgroupList or NULL if allocation failed.
143 util_compare_subgroup_t *util_ald_sgl_dup(util_ald_cache_t *cache, util_compare_subgroup_t *sgl_in)
146 util_compare_subgroup_t *sgl_out = NULL;
148 if (!sgl_in) return NULL;
150 sgl_out = (util_compare_subgroup_t *) util_ald_alloc(cache, sizeof(util_compare_subgroup_t));
151 sgl_out->subgroupDNs = util_ald_alloc(cache, sizeof(char *) * sgl_in->len);
152 sgl_out->len = sgl_in->len;
154 for (i = 0; i < sgl_in->len; i++) {
155 sgl_out->subgroupDNs[i] = util_ald_strdup(cache, sgl_in->subgroupDNs[i]);
162 * Delete an entire subgroupList.
164 void util_ald_sgl_free(util_ald_cache_t *cache, util_compare_subgroup_t **sgl)
167 if (sgl == NULL || *sgl == NULL) {
171 for (i = 0; i < (*sgl)->len; i++) {
172 util_ald_free(cache, (*sgl)->subgroupDNs[i]);
174 util_ald_free(cache, *sgl);
178 * Computes the hash on a set of strings. The first argument is the number
179 * of strings to hash, the rest of the args are strings.
180 * Algorithm taken from glibc.
182 unsigned long util_ald_hash_string(int nstr, ...)
186 unsigned long h=0, g;
189 va_start(args, nstr);
190 for (i=0; i < nstr; ++i) {
191 str = va_arg(args, char *);
192 for (p = str; *p; ++p) {
194 if ( ( g = h & 0xf0000000 ) ) {
207 Purges a cache that has gotten full. We keep track of the time that we
208 added the entry that made the cache 3/4 full, then delete all entries
209 that were added before that time. It's pretty simplistic, but time to
210 purge is only O(n), which is more important.
212 void util_ald_cache_purge(util_ald_cache_t *cache)
215 util_cache_node_t *p, *q, **pp;
221 cache->last_purge = apr_time_now();
225 for (i=0; i < cache->size; ++i) {
226 pp = cache->nodes + i;
229 if (p->add_time < cache->marktime) {
231 (*cache->free)(cache, p->payload);
232 util_ald_free(cache, p);
245 cache->avg_purgetime =
246 ((t - cache->last_purge) + (cache->avg_purgetime * (cache->numpurges-1))) /
254 util_url_node_t *util_ald_create_caches(util_ldap_state_t *st, const char *url)
256 util_url_node_t curl, *newcurl = NULL;
257 util_ald_cache_t *search_cache;
258 util_ald_cache_t *compare_cache;
259 util_ald_cache_t *dn_compare_cache;
261 /* create the three caches */
262 search_cache = util_ald_create_cache(st,
263 st->search_cache_size,
264 util_ldap_search_node_hash,
265 util_ldap_search_node_compare,
266 util_ldap_search_node_copy,
267 util_ldap_search_node_free,
268 util_ldap_search_node_display);
269 compare_cache = util_ald_create_cache(st,
270 st->compare_cache_size,
271 util_ldap_compare_node_hash,
272 util_ldap_compare_node_compare,
273 util_ldap_compare_node_copy,
274 util_ldap_compare_node_free,
275 util_ldap_compare_node_display);
276 dn_compare_cache = util_ald_create_cache(st,
277 st->compare_cache_size,
278 util_ldap_dn_compare_node_hash,
279 util_ldap_dn_compare_node_compare,
280 util_ldap_dn_compare_node_copy,
281 util_ldap_dn_compare_node_free,
282 util_ldap_dn_compare_node_display);
284 /* check that all the caches initialised successfully */
285 if (search_cache && compare_cache && dn_compare_cache) {
287 /* The contents of this structure will be duplicated in shared
288 memory during the insert. So use stack memory rather than
289 pool memory to avoid a memory leak. */
290 memset (&curl, 0, sizeof(util_url_node_t));
292 curl.search_cache = search_cache;
293 curl.compare_cache = compare_cache;
294 curl.dn_compare_cache = dn_compare_cache;
296 newcurl = util_ald_cache_insert(st->util_ldap_cache, &curl);
304 util_ald_cache_t *util_ald_create_cache(util_ldap_state_t *st,
306 unsigned long (*hashfunc)(void *),
307 int (*comparefunc)(void *, void *),
308 void * (*copyfunc)(util_ald_cache_t *cache, void *),
309 void (*freefunc)(util_ald_cache_t *cache, void *),
310 void (*displayfunc)(request_rec *r, util_ald_cache_t *cache, void *))
312 util_ald_cache_t *cache;
318 #if APR_HAS_SHARED_MEMORY
319 if (!st->cache_rmm) {
323 apr_rmm_off_t block = apr_rmm_calloc(st->cache_rmm, sizeof(util_ald_cache_t));
324 cache = block ? (util_ald_cache_t *)apr_rmm_addr_get(st->cache_rmm, block) : NULL;
327 cache = (util_ald_cache_t *)calloc(sizeof(util_ald_cache_t), 1);
332 #if APR_HAS_SHARED_MEMORY
333 cache->rmm_addr = st->cache_rmm;
334 cache->shm_addr = st->cache_shm;
336 cache->maxentries = cache_size;
337 cache->numentries = 0;
338 cache->size = cache_size / 3;
339 if (cache->size < 64) cache->size = 64;
340 for (i = 0; primes[i] && primes[i] < cache->size; ++i) ;
341 cache->size = primes[i]? primes[i] : primes[i-1];
343 cache->nodes = (util_cache_node_t **)util_ald_alloc(cache, cache->size * sizeof(util_cache_node_t *));
345 util_ald_free(cache, cache);
349 for (i=0; i < cache->size; ++i)
350 cache->nodes[i] = NULL;
352 cache->hash = hashfunc;
353 cache->compare = comparefunc;
354 cache->copy = copyfunc;
355 cache->free = freefunc;
356 cache->display = displayfunc;
358 cache->fullmark = cache->maxentries / 4 * 3;
360 cache->avg_purgetime = 0.0;
361 cache->numpurges = 0;
362 cache->last_purge = 0;
373 void util_ald_destroy_cache(util_ald_cache_t *cache)
376 util_cache_node_t *p, *q;
381 for (i = 0; i < cache->size; ++i) {
386 (*cache->free)(cache, p->payload);
387 util_ald_free(cache, p);
391 util_ald_free(cache, cache->nodes);
392 util_ald_free(cache, cache);
395 void *util_ald_cache_fetch(util_ald_cache_t *cache, void *payload)
397 unsigned long hashval;
398 util_cache_node_t *p;
405 hashval = (*cache->hash)(payload) % cache->size;
407 for (p = cache->nodes[hashval];
408 p && !(*cache->compare)(p->payload, payload);
421 * Insert an item into the cache.
422 * *** Does not catch duplicates!!! ***
424 void *util_ald_cache_insert(util_ald_cache_t *cache, void *payload)
426 unsigned long hashval;
427 util_cache_node_t *node;
430 if (cache == NULL || payload == NULL) {
434 /* check if we are full - if so, try purge */
435 if (cache->numentries >= cache->maxentries) {
436 util_ald_cache_purge(cache);
437 if (cache->numentries >= cache->maxentries) {
438 /* if the purge was not effective, we leave now to avoid an overflow */
443 /* should be safe to add an entry */
444 if ((node = (util_cache_node_t *)util_ald_alloc(cache, sizeof(util_cache_node_t))) == NULL) {
448 /* Take a copy of the payload before proceeeding. */
449 payload = (*cache->copy)(cache, payload);
451 util_ald_free(cache, node);
455 /* populate the entry */
457 hashval = (*cache->hash)(payload) % cache->size;
458 node->add_time = apr_time_now();
459 node->payload = payload;
460 node->next = cache->nodes[hashval];
461 cache->nodes[hashval] = node;
463 /* if we reach the full mark, note the time we did so
464 * for the benefit of the purge function
466 if (++cache->numentries == cache->fullmark) {
467 cache->marktime=apr_time_now();
470 return node->payload;
473 void util_ald_cache_remove(util_ald_cache_t *cache, void *payload)
475 unsigned long hashval;
476 util_cache_node_t *p, *q;
482 hashval = (*cache->hash)(payload) % cache->size;
483 for (p = cache->nodes[hashval], q=NULL;
484 p && !(*cache->compare)(p->payload, payload);
489 /* If p is null, it means that we couldn't find the node, so just return */
494 /* We found the node, and it's the first in the list */
495 cache->nodes[hashval] = p->next;
498 /* We found the node and it's not the first in the list */
501 (*cache->free)(cache, p->payload);
502 util_ald_free(cache, p);
506 char *util_ald_cache_display_stats(request_rec *r, util_ald_cache_t *cache, char *name, char *id)
512 util_cache_node_t *n;
514 apr_pool_t *p = r->pool;
520 for (i=0; i < cache->size; ++i) {
521 if (cache->nodes[i] != NULL) {
523 for (n = cache->nodes[i];
524 n != NULL && n != n->next;
530 chainlen = nchains? (double)totchainlen / (double)nchains : 0;
533 buf2 = apr_psprintf(p,
534 "<a href=\"%s?%s\">%s</a>",
543 buf = apr_psprintf(p,
546 "<td align='right' nowrap>%lu (%.0f%% full)</td>"
547 "<td align='right'>%.1f</td>"
548 "<td align='right'>%lu/%lu</td>"
549 "<td align='right'>%.0f%%</td>"
550 "<td align='right'>%lu/%lu</td>",
553 (double)cache->numentries / (double)cache->maxentries * 100.0,
557 (cache->fetches > 0 ? (double)(cache->hits) / (double)(cache->fetches) * 100.0 : 100.0),
561 if (cache->numpurges) {
562 char str_ctime[APR_CTIME_LEN];
564 apr_ctime(str_ctime, cache->last_purge);
565 buf = apr_psprintf(p,
567 "<td align='right'>%lu</td>\n"
568 "<td align='right' nowrap>%s</td>\n",
574 buf = apr_psprintf(p,
575 "%s<td colspan='2' align='center'>(none)</td>\n",
579 buf = apr_psprintf(p, "%s<td align='right'>%.2gms</td>\n</tr>", buf, cache->avg_purgetime);
584 char *util_ald_cache_display(request_rec *r, util_ldap_state_t *st)
587 char *buf, *t1, *t2, *t3;
588 char *id1, *id2, *id3;
589 char *argfmt = "cache=%s&id=%d&off=%d";
590 char *scanfmt = "cache=%4s&id=%u&off=%u%1s";
591 apr_pool_t *pool = r->pool;
592 util_cache_node_t *p = NULL;
593 util_url_node_t *n = NULL;
595 util_ald_cache_t *util_ldap_cache = st->util_ldap_cache;
598 if (!util_ldap_cache) {
599 return "<tr valign='top'><td nowrap colspan=7>Cache has not been enabled/initialised.</td></tr>";
602 if (r->args && strlen(r->args)) {
603 char cachetype[5], lint[2];
604 unsigned int id, off;
605 char date_str[APR_CTIME_LEN];
607 if ((3 == sscanf(r->args, scanfmt, cachetype, &id, &off, lint)) &&
608 (id < util_ldap_cache->size)) {
610 if ((p = util_ldap_cache->nodes[id]) != NULL) {
611 n = (util_url_node_t *)p->payload;
620 "<table border='0'>\n"
622 "<td bgcolor='#000000'><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Cache Name:</b></font></td>"
623 "<td bgcolor='#ffffff'><font size='-1' face='Arial,Helvetica' color='#000000'><b>%s (%s)</b></font></td>"
627 cachetype[0] == 'm'? "Main" :
628 (cachetype[0] == 's' ? "Search" :
629 (cachetype[0] == 'c' ? "Compares" : "DNCompares")));
631 switch (cachetype[0]) {
633 if (util_ldap_cache->marktime) {
634 apr_ctime(date_str, util_ldap_cache->marktime);
641 "<table border='0'>\n"
643 "<td bgcolor='#000000'><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Size:</b></font></td>"
644 "<td bgcolor='#ffffff'><font size='-1' face='Arial,Helvetica' color='#000000'><b>%ld</b></font></td>"
647 "<td bgcolor='#000000'><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Max Entries:</b></font></td>"
648 "<td bgcolor='#ffffff'><font size='-1' face='Arial,Helvetica' color='#000000'><b>%ld</b></font></td>"
651 "<td bgcolor='#000000'><font size='-1' face='Arial,Helvetica' color='#ffffff'><b># Entries:</b></font></td>"
652 "<td bgcolor='#ffffff'><font size='-1' face='Arial,Helvetica' color='#000000'><b>%ld</b></font></td>"
655 "<td bgcolor='#000000'><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Full Mark:</b></font></td>"
656 "<td bgcolor='#ffffff'><font size='-1' face='Arial,Helvetica' color='#000000'><b>%ld</b></font></td>"
659 "<td bgcolor='#000000'><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Full Mark Time:</b></font></td>"
660 "<td bgcolor='#ffffff'><font size='-1' face='Arial,Helvetica' color='#000000'><b>%s</b></font></td>"
663 util_ldap_cache->size,
664 util_ldap_cache->maxentries,
665 util_ldap_cache->numentries,
666 util_ldap_cache->fullmark,
670 "<table border='0'>\n"
671 "<tr bgcolor='#000000'>\n"
672 "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>LDAP URL</b></font></td>"
673 "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Size</b></font></td>"
674 "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Max Entries</b></font></td>"
675 "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b># Entries</b></font></td>"
676 "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Full Mark</b></font></td>"
677 "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Full Mark Time</b></font></td>"
680 for (i=0; i < util_ldap_cache->size; ++i) {
681 for (p = util_ldap_cache->nodes[i]; p != NULL; p = p->next) {
683 (*util_ldap_cache->display)(r, util_ldap_cache, p->payload);
686 ap_rputs("</table>\n</p>\n", r);
692 "<table border='0'>\n"
693 "<tr bgcolor='#000000'>\n"
694 "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>LDAP Filter</b></font></td>"
695 "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>User Name</b></font></td>"
696 "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Last Bind</b></font></td>"
700 for (i=0; i < n->search_cache->size; ++i) {
701 for (p = n->search_cache->nodes[i]; p != NULL; p = p->next) {
703 (*n->search_cache->display)(r, n->search_cache, p->payload);
707 ap_rputs("</table>\n</p>\n", r);
711 "<table border='0'>\n"
712 "<tr bgcolor='#000000'>\n"
713 "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>DN</b></font></td>"
714 "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Attribute</b></font></td>"
715 "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Value</b></font></td>"
716 "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Last Compare</b></font></td>"
717 "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Result</b></font></td>"
718 "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Sub-groups?</b></font></td>"
719 "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>S-G Checked?</b></font></td>"
723 for (i=0; i < n->compare_cache->size; ++i) {
724 for (p = n->compare_cache->nodes[i]; p != NULL; p = p->next) {
726 (*n->compare_cache->display)(r, n->compare_cache, p->payload);
730 ap_rputs("</table>\n</p>\n", r);
734 "<table border='0'>\n"
735 "<tr bgcolor='#000000'>\n"
736 "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Require DN</b></font></td>"
737 "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Actual DN</b></font></td>"
741 for (i=0; i < n->dn_compare_cache->size; ++i) {
742 for (p = n->dn_compare_cache->nodes[i]; p != NULL; p = p->next) {
744 (*n->dn_compare_cache->display)(r, n->dn_compare_cache, p->payload);
748 ap_rputs("</table>\n</p>\n", r);
761 "<table border='0'>\n"
762 "<tr bgcolor='#000000'>\n"
763 "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Cache Name</b></font></td>"
764 "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Entries</b></font></td>"
765 "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Avg. Chain Len.</b></font></td>"
766 "<td colspan='2'><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Hits</b></font></td>"
767 "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Ins/Rem</b></font></td>"
768 "<td colspan='2'><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Purges</b></font></td>"
769 "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Avg Purge Time</b></font></td>"
774 id1 = apr_psprintf(pool, argfmt, "main", 0, 0);
775 buf = util_ald_cache_display_stats(r, st->util_ldap_cache, "LDAP URL Cache", id1);
777 for (i=0; i < util_ldap_cache->size; ++i) {
778 for (p = util_ldap_cache->nodes[i],j=0; p != NULL; p = p->next,j++) {
780 n = (util_url_node_t *)p->payload;
782 t1 = apr_psprintf(pool, "%s (Searches)", n->url);
783 t2 = apr_psprintf(pool, "%s (Compares)", n->url);
784 t3 = apr_psprintf(pool, "%s (DNCompares)", n->url);
785 id1 = apr_psprintf(pool, argfmt, "srch", i, j);
786 id2 = apr_psprintf(pool, argfmt, "cmpr", i, j);
787 id3 = apr_psprintf(pool, argfmt, "dncp", i, j);
789 buf = apr_psprintf(pool, "%s\n\n"
794 util_ald_cache_display_stats(r, n->search_cache, t1, id1),
795 util_ald_cache_display_stats(r, n->compare_cache, t2, id2),
796 util_ald_cache_display_stats(r, n->dn_compare_cache, t3, id3)
801 ap_rputs("</table>\n</p>\n", r);
807 #endif /* APR_HAS_LDAP */