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;
130
/* Cache shm is not used */
140
* Computes the hash on a set of strings. The first argument is the number
141
* of strings to hash, the rest of the args are strings.
142
* Algorithm taken from glibc.
144
unsigned long util_ald_hash_string(int nstr, ...)
148
unsigned long h=0, g;
151
va_start(args, nstr);
152
for (i=0; i < nstr; ++i) {
153
str = va_arg(args, char *);
154
for (p = str; *p; ++p) {
156
if ( ( g = h & 0xf0000000 ) ) {
169
Purges a cache that has gotten full. We keep track of the time that we
170
added the entry that made the cache 3/4 full, then delete all entries
171
that were added before that time. It's pretty simplistic, but time to
172
purge is only O(n), which is more important.
174
void util_ald_cache_purge(util_ald_cache_t *cache)
177
util_cache_node_t *p, *q, **pp;
183
cache->last_purge = apr_time_now();
187
for (i=0; i < cache->size; ++i) {
188
pp = cache->nodes + i;
191
if (p->add_time < cache->marktime) {
193
(*cache->free)(cache, p->payload);
194
util_ald_free(cache, p);
207
cache->avg_purgetime =
208
((t - cache->last_purge) + (cache->avg_purgetime * (cache->numpurges-1))) /
216
util_url_node_t *util_ald_create_caches(util_ldap_state_t *st, const char *url)
218
util_url_node_t curl, *newcurl = NULL;
219
util_ald_cache_t *search_cache;
220
util_ald_cache_t *compare_cache;
221
util_ald_cache_t *dn_compare_cache;
223
/* create the three caches */
224
search_cache = util_ald_create_cache(st,
225
st->search_cache_size,
226
util_ldap_search_node_hash,
227
util_ldap_search_node_compare,
228
util_ldap_search_node_copy,
229
util_ldap_search_node_free,
230
util_ldap_search_node_display);
231
compare_cache = util_ald_create_cache(st,
232
st->compare_cache_size,
233
util_ldap_compare_node_hash,
234
util_ldap_compare_node_compare,
235
util_ldap_compare_node_copy,
236
util_ldap_compare_node_free,
237
util_ldap_compare_node_display);
238
dn_compare_cache = util_ald_create_cache(st,
239
st->compare_cache_size,
240
util_ldap_dn_compare_node_hash,
241
util_ldap_dn_compare_node_compare,
242
util_ldap_dn_compare_node_copy,
243
util_ldap_dn_compare_node_free,
244
util_ldap_dn_compare_node_display);
246
/* check that all the caches initialised successfully */
247
if (search_cache && compare_cache && dn_compare_cache) {
249
/* The contents of this structure will be duplicated in shared
250
memory during the insert. So use stack memory rather than
251
pool memory to avoid a memory leak. */
252
memset (&curl, 0, sizeof(util_url_node_t));
254
curl.search_cache = search_cache;
255
curl.compare_cache = compare_cache;
256
curl.dn_compare_cache = dn_compare_cache;
258
newcurl = util_ald_cache_insert(st->util_ldap_cache, &curl);
266
util_ald_cache_t *util_ald_create_cache(util_ldap_state_t *st,
268
unsigned long (*hashfunc)(void *),
269
int (*comparefunc)(void *, void *),
270
void * (*copyfunc)(util_ald_cache_t *cache, void *),
271
void (*freefunc)(util_ald_cache_t *cache, void *),
272
void (*displayfunc)(request_rec *r, util_ald_cache_t *cache, void *))
274
util_ald_cache_t *cache;
280
#if APR_HAS_SHARED_MEMORY
281
if (!st->cache_rmm) {
285
apr_rmm_off_t block = apr_rmm_calloc(st->cache_rmm, sizeof(util_ald_cache_t));
286
cache = block ? (util_ald_cache_t *)apr_rmm_addr_get(st->cache_rmm, block) : NULL;
289
cache = (util_ald_cache_t *)calloc(sizeof(util_ald_cache_t), 1);
294
#if APR_HAS_SHARED_MEMORY
295
cache->rmm_addr = st->cache_rmm;
296
cache->shm_addr = st->cache_shm;
298
cache->maxentries = cache_size;
299
cache->numentries = 0;
300
cache->size = cache_size / 3;
301
if (cache->size < 64) cache->size = 64;
302
for (i = 0; primes[i] && primes[i] < cache->size; ++i) ;
303
cache->size = primes[i]? primes[i] : primes[i-1];
305
cache->nodes = (util_cache_node_t **)util_ald_alloc(cache, cache->size * sizeof(util_cache_node_t *));
307
util_ald_free(cache, cache);
311
for (i=0; i < cache->size; ++i)
312
cache->nodes[i] = NULL;
314
cache->hash = hashfunc;
315
cache->compare = comparefunc;
316
cache->copy = copyfunc;
317
cache->free = freefunc;
318
cache->display = displayfunc;
320
cache->fullmark = cache->maxentries / 4 * 3;
322
cache->avg_purgetime = 0.0;
323
cache->numpurges = 0;
324
cache->last_purge = 0;
335
void util_ald_destroy_cache(util_ald_cache_t *cache)
338
util_cache_node_t *p, *q;
343
for (i = 0; i < cache->size; ++i) {
348
(*cache->free)(cache, p->payload);
349
util_ald_free(cache, p);
353
util_ald_free(cache, cache->nodes);
354
util_ald_free(cache, cache);
357
void *util_ald_cache_fetch(util_ald_cache_t *cache, void *payload)
359
unsigned long hashval;
360
util_cache_node_t *p;
367
hashval = (*cache->hash)(payload) % cache->size;
368
for (p = cache->nodes[hashval];
369
p && !(*cache->compare)(p->payload, payload);
382
* Insert an item into the cache.
383
* *** Does not catch duplicates!!! ***
385
void *util_ald_cache_insert(util_ald_cache_t *cache, void *payload)
387
unsigned long hashval;
388
util_cache_node_t *node;
391
if (cache == NULL || payload == NULL) {
395
/* check if we are full - if so, try purge */
396
if (cache->numentries >= cache->maxentries) {
397
util_ald_cache_purge(cache);
398
if (cache->numentries >= cache->maxentries) {
399
/* if the purge was not effective, we leave now to avoid an overflow */
404
/* should be safe to add an entry */
405
if ((node = (util_cache_node_t *)util_ald_alloc(cache, sizeof(util_cache_node_t))) == NULL) {
409
/* Take a copy of the payload before proceeeding. */
410
payload = (*cache->copy)(cache, payload);
412
util_ald_free(cache, node);
416
/* populate the entry */
418
hashval = (*cache->hash)(payload) % cache->size;
419
node->add_time = apr_time_now();
420
node->payload = payload;
421
node->next = cache->nodes[hashval];
422
cache->nodes[hashval] = node;
424
/* if we reach the full mark, note the time we did so
425
* for the benefit of the purge function
427
if (++cache->numentries == cache->fullmark) {
428
cache->marktime=apr_time_now();
431
return node->payload;
434
void util_ald_cache_remove(util_ald_cache_t *cache, void *payload)
436
unsigned long hashval;
437
util_cache_node_t *p, *q;
443
hashval = (*cache->hash)(payload) % cache->size;
444
for (p = cache->nodes[hashval], q=NULL;
445
p && !(*cache->compare)(p->payload, payload);
450
/* If p is null, it means that we couldn't find the node, so just return */
455
/* We found the node, and it's the first in the list */
456
cache->nodes[hashval] = p->next;
459
/* We found the node and it's not the first in the list */
462
(*cache->free)(cache, p->payload);
463
util_ald_free(cache, p);
467
char *util_ald_cache_display_stats(request_rec *r, util_ald_cache_t *cache, char *name, char *id)
473
util_cache_node_t *n;
475
apr_pool_t *p = r->pool;
481
for (i=0; i < cache->size; ++i) {
482
if (cache->nodes[i] != NULL) {
484
for (n = cache->nodes[i];
485
n != NULL && n != n->next;
491
chainlen = nchains? (double)totchainlen / (double)nchains : 0;
494
buf2 = apr_psprintf(p,
495
"<a href=\"%s?%s\">%s</a>",
504
buf = apr_psprintf(p,
507
"<td align='right' nowrap>%lu (%.0f%% full)</td>"
508
"<td align='right'>%.1f</td>"
509
"<td align='right'>%lu/%lu</td>"
510
"<td align='right'>%.0f%%</td>"
511
"<td align='right'>%lu/%lu</td>",
514
(double)cache->numentries / (double)cache->maxentries * 100.0,
518
(cache->fetches > 0 ? (double)(cache->hits) / (double)(cache->fetches) * 100.0 : 100.0),
522
if (cache->numpurges) {
523
char str_ctime[APR_CTIME_LEN];
525
apr_ctime(str_ctime, cache->last_purge);
526
buf = apr_psprintf(p,
528
"<td align='right'>%lu</td>\n"
529
"<td align='right' nowrap>%s</td>\n",
535
buf = apr_psprintf(p,
536
"%s<td colspan='2' align='center'>(none)</td>\n",
540
buf = apr_psprintf(p, "%s<td align='right'>%.2gms</td>\n</tr>", buf, cache->avg_purgetime);
545
char *util_ald_cache_display(request_rec *r, util_ldap_state_t *st)
548
char *buf, *t1, *t2, *t3;
549
char *id1, *id2, *id3;
550
char *argfmt = "cache=%s&id=%d&off=%d";
551
char *scanfmt = "cache=%4s&id=%u&off=%u%1s";
552
apr_pool_t *pool = r->pool;
553
util_cache_node_t *p = NULL;
554
util_url_node_t *n = NULL;
556
util_ald_cache_t *util_ldap_cache = st->util_ldap_cache;
559
if (!util_ldap_cache) {
560
return "<tr valign='top'><td nowrap colspan=7>Cache has not been enabled/initialised.</td></tr>";
563
if (r->args && strlen(r->args)) {
564
char cachetype[5], lint[2];
565
unsigned int id, off;
566
char date_str[APR_CTIME_LEN+1];
568
if ((3 == sscanf(r->args, scanfmt, cachetype, &id, &off, lint)) &&
569
(id < util_ldap_cache->size)) {
571
if ((p = util_ldap_cache->nodes[id]) != NULL) {
572
n = (util_url_node_t *)p->payload;
581
"<table border='0'>\n"
583
"<td bgcolor='#000000'><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Cache Name:</b></font></td>"
584
"<td bgcolor='#ffffff'><font size='-1' face='Arial,Helvetica' color='#000000'><b>%s (%s)</b></font></td>"
588
cachetype[0] == 'm'? "Main" :
589
(cachetype[0] == 's' ? "Search" :
590
(cachetype[0] == 'c' ? "Compares" : "DNCompares")));
592
switch (cachetype[0]) {
594
if (util_ldap_cache->marktime) {
595
apr_ctime(date_str, util_ldap_cache->marktime);
602
"<table border='0'>\n"
604
"<td bgcolor='#000000'><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Size:</b></font></td>"
605
"<td bgcolor='#ffffff'><font size='-1' face='Arial,Helvetica' color='#000000'><b>%ld</b></font></td>"
608
"<td bgcolor='#000000'><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Max Entries:</b></font></td>"
609
"<td bgcolor='#ffffff'><font size='-1' face='Arial,Helvetica' color='#000000'><b>%ld</b></font></td>"
612
"<td bgcolor='#000000'><font size='-1' face='Arial,Helvetica' color='#ffffff'><b># Entries:</b></font></td>"
613
"<td bgcolor='#ffffff'><font size='-1' face='Arial,Helvetica' color='#000000'><b>%ld</b></font></td>"
616
"<td bgcolor='#000000'><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Full Mark:</b></font></td>"
617
"<td bgcolor='#ffffff'><font size='-1' face='Arial,Helvetica' color='#000000'><b>%ld</b></font></td>"
620
"<td bgcolor='#000000'><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Full Mark Time:</b></font></td>"
621
"<td bgcolor='#ffffff'><font size='-1' face='Arial,Helvetica' color='#000000'><b>%s</b></font></td>"
624
util_ldap_cache->size,
625
util_ldap_cache->maxentries,
626
util_ldap_cache->numentries,
627
util_ldap_cache->fullmark,
631
"<table border='0'>\n"
632
"<tr bgcolor='#000000'>\n"
633
"<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>LDAP URL</b></font></td>"
634
"<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Size</b></font></td>"
635
"<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Max Entries</b></font></td>"
636
"<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b># Entries</b></font></td>"
637
"<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Full Mark</b></font></td>"
638
"<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Full Mark Time</b></font></td>"
641
for (i=0; i < util_ldap_cache->size; ++i) {
642
for (p = util_ldap_cache->nodes[i]; p != NULL; p = p->next) {
644
(*util_ldap_cache->display)(r, util_ldap_cache, p->payload);
647
ap_rputs("</table>\n</p>\n", r);
653
"<table border='0'>\n"
654
"<tr bgcolor='#000000'>\n"
655
"<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>LDAP Filter</b></font></td>"
656
"<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>User Name</b></font></td>"
657
"<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Last Bind</b></font></td>"
661
for (i=0; i < n->search_cache->size; ++i) {
662
for (p = n->search_cache->nodes[i]; p != NULL; p = p->next) {
664
(*n->search_cache->display)(r, n->search_cache, p->payload);
668
ap_rputs("</table>\n</p>\n", r);
672
"<table border='0'>\n"
673
"<tr bgcolor='#000000'>\n"
674
"<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>DN</b></font></td>"
675
"<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Attribute</b></font></td>"
676
"<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Value</b></font></td>"
677
"<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Last Compare</b></font></td>"
678
"<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Result</b></font></td>"
682
for (i=0; i < n->compare_cache->size; ++i) {
683
for (p = n->compare_cache->nodes[i]; p != NULL; p = p->next) {
685
(*n->compare_cache->display)(r, n->compare_cache, p->payload);
689
ap_rputs("</table>\n</p>\n", r);
693
"<table border='0'>\n"
694
"<tr bgcolor='#000000'>\n"
695
"<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Require DN</b></font></td>"
696
"<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Actual DN</b></font></td>"
700
for (i=0; i < n->dn_compare_cache->size; ++i) {
701
for (p = n->dn_compare_cache->nodes[i]; p != NULL; p = p->next) {
703
(*n->dn_compare_cache->display)(r, n->dn_compare_cache, p->payload);
707
ap_rputs("</table>\n</p>\n", r);
720
"<table border='0'>\n"
721
"<tr bgcolor='#000000'>\n"
722
"<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Cache Name</b></font></td>"
723
"<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Entries</b></font></td>"
724
"<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Avg. Chain Len.</b></font></td>"
725
"<td colspan='2'><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Hits</b></font></td>"
726
"<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Ins/Rem</b></font></td>"
727
"<td colspan='2'><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Purges</b></font></td>"
728
"<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Avg Purge Time</b></font></td>"
733
id1 = apr_psprintf(pool, argfmt, "main", 0, 0);
734
buf = util_ald_cache_display_stats(r, st->util_ldap_cache, "LDAP URL Cache", id1);
736
for (i=0; i < util_ldap_cache->size; ++i) {
737
for (p = util_ldap_cache->nodes[i],j=0; p != NULL; p = p->next,j++) {
739
n = (util_url_node_t *)p->payload;
741
t1 = apr_psprintf(pool, "%s (Searches)", n->url);
742
t2 = apr_psprintf(pool, "%s (Compares)", n->url);
743
t3 = apr_psprintf(pool, "%s (DNCompares)", n->url);
744
id1 = apr_psprintf(pool, argfmt, "srch", i, j);
745
id2 = apr_psprintf(pool, argfmt, "cmpr", i, j);
746
id3 = apr_psprintf(pool, argfmt, "dncp", i, j);
748
buf = apr_psprintf(pool, "%s\n\n"
753
util_ald_cache_display_stats(r, n->search_cache, t1, id1),
754
util_ald_cache_display_stats(r, n->compare_cache, t2, id2),
755
util_ald_cache_display_stats(r, n->dn_compare_cache, t3, id3)
760
ap_rputs("</table>\n</p>\n", r);
766
#endif /* APR_HAS_LDAP */