1 /*
2 * Portions Copyright 1998 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
4 */
5 #pragma ident "%Z%%M% %I% %E% SMI"
6 /*
7 * Copyright (c) 1993 The Regents of the University of Michigan.
8 * All rights reserved.
9 *
10 * cache.c - local caching support for LDAP
11 */
12
13 #ifndef NO_CACHE
14
15 #ifndef lint
16 static char copyright[] = "@(#) Copyright (c) 1993 The Regents of the University of Michigan.\nAll rights reserved.\n";
17 #endif
18
19 #include <stdio.h>
20 #include <string.h>
21 #ifdef MACOS
22 #include <stdlib.h>
23 #include <time.h>
24 #include "macos.h"
25 #else /* MACOS */
26 #if defined( DOS ) || defined( _WIN32 )
27 #include <malloc.h>
28 #include "msdos.h"
29 #ifdef NCSA
30 #include "externs.h"
31 #endif /* NCSA */
32 #ifdef WINSOCK
33 #include <time.h>
34 #endif /* WINSOCK */
35 #else /* DOS */
36 #include <sys/types.h>
37 #include <sys/socket.h>
38 #endif /* DOS */
39 #endif /* MACOS */
40 #include "lber.h"
41 #include "ldap.h"
42 #include "ldap-private.h"
43 #include "ldap-int.h"
44
45 #ifdef NEEDPROTOS
46 static int cache_hash( BerElement *ber );
47 static LDAPMessage *msg_dup( LDAPMessage *msg );
48 static int request_cmp( BerElement *req1, BerElement *req2 );
49 static int chain_contains_dn( LDAPMessage *msg, char *dn );
50 static ssize_t msg_size( LDAPMessage *msg );
51 static void check_cache_memused( LDAPCache *lc );
52 static void uncache_entry_or_req( LDAP *ld, char *dn, int msgid );
53 #else /* NEEDPROTOS */
54 static int cache_hash();
55 static LDAPMessage *msg_dup();
56 static int request_cmp();
57 static int chain_contains_dn();
58 static ssize_t msg_size();
59 static void check_cache_memused();
60 static void uncache_entry_or_req();
61 #endif /* NEEDPROTOS */
62
63
64 int
ldap_enable_cache(LDAP * ld,time_t timeout,ssize_t maxmem)65 ldap_enable_cache( LDAP *ld, time_t timeout, ssize_t maxmem )
66 {
67 #if defined( SUN ) && defined( _REENTRANT )
68 LOCK_LDAP(ld);
69 #endif
70 if ( ld->ld_cache == NULLLDCACHE ) {
71 if (( ld->ld_cache = (LDAPCache *)malloc( sizeof( LDAPCache )))
72 == NULLLDCACHE ) {
73 ld->ld_errno = LDAP_NO_MEMORY;
74 #if defined( SUN ) && defined( _REENTRANT )
75 UNLOCK_LDAP(ld);
76 #endif
77 return( -1 );
78 }
79 (void) memset( ld->ld_cache, 0, sizeof( LDAPCache ));
80 ld->ld_cache->lc_memused = sizeof( LDAPCache );
81 }
82
83 ld->ld_cache->lc_timeout = timeout;
84 ld->ld_cache->lc_maxmem = maxmem;
85 check_cache_memused( ld->ld_cache );
86 ld->ld_cache->lc_enabled = 1;
87 #if defined( SUN ) && defined( _REENTRANT )
88 UNLOCK_LDAP(ld);
89 #endif
90 return( 0 );
91 }
92
93
94 void
ldap_disable_cache(LDAP * ld)95 ldap_disable_cache( LDAP *ld )
96 {
97 #if defined( SUN ) && defined( _REENTRANT )
98 LOCK_LDAP(ld);
99 #endif
100 if ( ld->ld_cache != NULLLDCACHE ) {
101 ld->ld_cache->lc_enabled = 0;
102 }
103 #if defined( SUN ) && defined( _REENTRANT )
104 UNLOCK_LDAP(ld);
105 #endif
106 }
107
108
109
110 void
ldap_set_cache_options(LDAP * ld,unsigned int opts)111 ldap_set_cache_options( LDAP *ld, unsigned int opts )
112 {
113 #if defined( SUN ) && defined( _REENTRANT )
114 LOCK_LDAP(ld);
115 #endif
116 if ( ld->ld_cache != NULLLDCACHE ) {
117 ld->ld_cache->lc_options = opts;
118 }
119 #if defined( SUN ) && defined( _REENTRANT )
120 UNLOCK_LDAP(ld);
121 #endif
122 }
123
124
125 void
ldap_destroy_cache(LDAP * ld)126 ldap_destroy_cache( LDAP *ld )
127 {
128 #if defined( SUN ) && defined( _REENTRANT )
129 LOCK_LDAP(ld);
130 #endif
131 if ( ld->ld_cache != NULLLDCACHE ) {
132 ldap_flush_cache( ld );
133 free( (char *)ld->ld_cache );
134 ld->ld_cache = NULLLDCACHE;
135 }
136 #if defined( SUN ) && defined( _REENTRANT )
137 UNLOCK_LDAP(ld);
138 #endif
139 }
140
141
142 void
ldap_flush_cache(LDAP * ld)143 ldap_flush_cache( LDAP *ld )
144 {
145 int i;
146 LDAPMessage *m, *next;
147
148 #if defined( SUN ) && defined( _REENTRANT )
149 LOCK_LDAP(ld);
150 #endif
151 Debug( LDAP_DEBUG_TRACE, catgets(slapdcat, 1, 90, "ldap_flush_cache\n"), 0, 0, 0 );
152
153 if ( ld->ld_cache != NULLLDCACHE ) {
154 /* delete all requests in the queue */
155 for ( m = ld->ld_cache->lc_requests; m != NULLMSG; m = next ) {
156 next = m->lm_next;
157 ldap_msgfree( m );
158 }
159 ld->ld_cache->lc_requests = NULLMSG;
160
161 /* delete all messages in the cache */
162 for ( i = 0; i < LDAP_CACHE_BUCKETS; ++i ) {
163 for ( m = ld->ld_cache->lc_buckets[ i ];
164 m != NULLMSG; m = next ) {
165 next = m->lm_next;
166 ldap_msgfree( m );
167 }
168 ld->ld_cache->lc_buckets[ i ] = NULLMSG;
169 }
170 ld->ld_cache->lc_memused = sizeof( LDAPCache );
171 }
172 #if defined( SUN ) && defined( _REENTRANT )
173 UNLOCK_LDAP(ld);
174 #endif
175 }
176
177
178 void
ldap_uncache_request(LDAP * ld,int msgid)179 ldap_uncache_request( LDAP *ld, int msgid )
180 {
181 #if defined( SUN ) && defined( _REENTRANT )
182 LOCK_LDAP(ld);
183 #endif
184 Debug( LDAP_DEBUG_TRACE, catgets(slapdcat, 1, 91, "ldap_uncache_request %1$d ld_cache %2$x\n"),
185 msgid, ld->ld_cache, 0 );
186
187 uncache_entry_or_req( ld, NULL, msgid );
188 #if defined( SUN ) && defined( _REENTRANT )
189 UNLOCK_LDAP(ld);
190 #endif
191 }
192
193
194 void
ldap_uncache_entry(LDAP * ld,char * dn)195 ldap_uncache_entry( LDAP *ld, char *dn )
196 {
197 #if defined( SUN ) && defined( _REENTRANT )
198 LOCK_LDAP(ld);
199 #endif
200 Debug( LDAP_DEBUG_TRACE, catgets(slapdcat, 1, 92, "ldap_uncache_entry %1$s ld_cache %2$x\n"),
201 dn, ld->ld_cache, 0 );
202
203 uncache_entry_or_req( ld, dn, 0 );
204 #if defined( SUN ) && defined( _REENTRANT )
205 UNLOCK_LDAP(ld);
206 #endif
207 }
208
209
210 static void
uncache_entry_or_req(LDAP * ld,char * dn,int msgid)211 uncache_entry_or_req( LDAP *ld,
212 char *dn, /* if non-NULL, uncache entry */
213 int msgid ) /* request to uncache (if dn == NULL) */
214 {
215 int i;
216 LDAPMessage *m, *prev, *next;
217
218 Debug( LDAP_DEBUG_TRACE,
219 catgets(slapdcat, 1, 93, "ldap_uncache_entry_or_req dn %1$s msgid %2$d ld_cache %3$x\n"),
220 dn, msgid, ld->ld_cache );
221
222 if ( ld->ld_cache == NULLLDCACHE ) {
223 return;
224 }
225
226 /* first check the request queue */
227 prev = NULLMSG;
228 for ( m = ld->ld_cache->lc_requests; m != NULLMSG; m = next ) {
229 next = m->lm_next;
230 if (( dn != NULL && chain_contains_dn( m, dn )) ||
231 ( dn == NULL && m->lm_msgid == msgid )) {
232 if ( prev == NULLMSG ) {
233 ld->ld_cache->lc_requests = next;
234 } else {
235 prev->lm_next = next;
236 }
237 ld->ld_cache->lc_memused -= msg_size( m );
238 ldap_msgfree( m );
239 } else {
240 prev = m;
241 }
242 }
243
244 /* now check the rest of the cache */
245 for ( i = 0; i < LDAP_CACHE_BUCKETS; ++i ) {
246 prev = NULLMSG;
247 for ( m = ld->ld_cache->lc_buckets[ i ]; m != NULLMSG;
248 m = next ) {
249 next = m->lm_next;
250 if (( dn != NULL && chain_contains_dn( m, dn )) ||
251 ( dn == NULL && m->lm_msgid == msgid )) {
252 if ( prev == NULLMSG ) {
253 ld->ld_cache->lc_buckets[ i ] = next;
254 } else {
255 prev->lm_next = next;
256 }
257 ld->ld_cache->lc_memused -= msg_size( m );
258 ldap_msgfree( m );
259 } else {
260 prev = m;
261 }
262 }
263 }
264 }
265
266
267 void
add_request_to_cache(LDAP * ld,unsigned int msgtype,BerElement * request)268 add_request_to_cache( LDAP *ld, unsigned int msgtype, BerElement *request )
269 {
270 LDAPMessage *new;
271 size_t len;
272
273 Debug( LDAP_DEBUG_TRACE, catgets(slapdcat, 1, 94, "add_request_to_cache\n"), 0, 0, 0 );
274
275 ld->ld_errno = LDAP_SUCCESS;
276 if ( ld->ld_cache == NULLLDCACHE ||
277 ( ld->ld_cache->lc_enabled == 0 )) {
278 return;
279 }
280
281 if (( new = (LDAPMessage *) calloc( 1, sizeof(LDAPMessage) ))
282 != NULL ) {
283 if (( new->lm_ber = alloc_ber_with_options( ld )) == NULLBER ) {
284 free( (char *)new );
285 return;
286 }
287 len = request->ber_ptr - request->ber_buf;
288 if (( new->lm_ber->ber_buf = (char *) malloc( len ))
289 == NULL ) {
290 ber_free( new->lm_ber, 0 );
291 free( (char *)new );
292 ld->ld_errno = LDAP_NO_MEMORY;
293 return;
294 }
295 SAFEMEMCPY( new->lm_ber->ber_buf, request->ber_buf, len );
296 new->lm_ber->ber_ptr = new->lm_ber->ber_buf;
297 new->lm_ber->ber_end = new->lm_ber->ber_buf + len;
298 new->lm_msgid = ld->ld_msgid;
299 new->lm_msgtype = (int) msgtype;;
300 new->lm_next = ld->ld_cache->lc_requests;
301 ld->ld_cache->lc_requests = new;
302 } else {
303 ld->ld_errno = LDAP_NO_MEMORY;
304 }
305 }
306
307
308 void
add_result_to_cache(LDAP * ld,LDAPMessage * result)309 add_result_to_cache( LDAP *ld, LDAPMessage *result )
310 {
311 LDAPMessage *m, **mp, *req, *new, *prev;
312 int err, keep;
313
314 Debug( LDAP_DEBUG_TRACE, catgets(slapdcat, 1, 95, "add_result_to_cache: id %1$d, type %2$d\n"),
315 result->lm_msgid, result->lm_msgtype, 0 );
316
317 if ( ld->ld_cache == NULLLDCACHE ||
318 ( ld->ld_cache->lc_enabled == 0 )) {
319 Debug( LDAP_DEBUG_TRACE, catgets(slapdcat, 1, 96, "artc: cache disabled\n"), 0, 0, 0 );
320 return;
321 }
322
323 if ( result->lm_msgtype != LDAP_RES_SEARCH_ENTRY &&
324 result->lm_msgtype != LDAP_RES_SEARCH_RESULT &&
325 result->lm_msgtype != LDAP_RES_SEARCH_REFERENCE &&
326 result->lm_msgtype != LDAP_RES_COMPARE ) {
327 /*
328 * only cache search and compare operations
329 */
330 Debug( LDAP_DEBUG_TRACE,
331 catgets(slapdcat, 1, 97, "artc: only caching search & compare operations\n"), 0, 0, 0 );
332 return;
333 }
334
335 /*
336 * if corresponding request is in the lc_requests list, add this
337 * result to it. if this result completes the results for the
338 * request, add the request/result chain to the cache proper.
339 */
340 prev = NULLMSG;
341 for ( m = ld->ld_cache->lc_requests; m != NULL; m = m->lm_next ) {
342 if ( m->lm_msgid == result->lm_msgid ) {
343 break;
344 }
345 prev = m;
346 }
347
348 if ( m != NULLMSG ) { /* found request; add to end of chain */
349 req = m;
350 for ( ; m->lm_chain != NULLMSG; m = m->lm_chain )
351 ;
352 if (( new = msg_dup( result )) != NULLMSG ) {
353 new->lm_chain = NULLMSG;
354 m->lm_chain = new;
355 Debug( LDAP_DEBUG_TRACE,
356 catgets(slapdcat, 1, 98, "artc: result added to cache request chain\n"),
357 0, 0, 0 );
358 }
359 if ( result->lm_msgtype == LDAP_RES_SEARCH_RESULT ||
360 result->lm_msgtype == LDAP_RES_COMPARE ) {
361 /*
362 * this result completes the chain of results
363 * add to cache proper if appropriate
364 */
365 keep = 0; /* pessimistic */
366 err = ldap_result2error( ld, result, 0 );
367 if ( err == LDAP_SUCCESS ||
368 ( result->lm_msgtype == LDAP_RES_COMPARE &&
369 ( err == LDAP_COMPARE_FALSE ||
370 err == LDAP_COMPARE_TRUE ||
371 err == LDAP_NO_SUCH_ATTRIBUTE ))) {
372 keep = 1;
373 }
374
375 if ( ld->ld_cache->lc_options == 0 ) {
376 if ( err == LDAP_SIZELIMIT_EXCEEDED ) {
377 keep = 1;
378 }
379 } else if (( ld->ld_cache->lc_options &
380 LDAP_CACHE_OPT_CACHEALLERRS ) != 0 ) {
381 keep = 1;
382 }
383
384 if ( prev == NULLMSG ) {
385 ld->ld_cache->lc_requests = req->lm_next;
386 } else {
387 prev->lm_next = req->lm_next;
388 }
389
390 if ( !keep ) {
391 Debug( LDAP_DEBUG_TRACE,
392 catgets(slapdcat, 1, 99, "artc: not caching result with error %d\n"),
393 err, 0, 0 );
394 ldap_msgfree( req );
395 } else {
396 mp = &ld->ld_cache->lc_buckets[
397 cache_hash( req->lm_ber ) ];
398 req->lm_next = *mp;
399 *mp = req;
400 req->lm_time = time( NULL );
401 ld->ld_cache->lc_memused += msg_size( req );
402 check_cache_memused( ld->ld_cache );
403 Debug( LDAP_DEBUG_TRACE,
404 catgets(slapdcat, 1, 100, "artc: cached result with error %d\n"),
405 err, 0, 0 );
406 }
407 }
408 } else {
409 Debug( LDAP_DEBUG_TRACE, catgets(slapdcat, 1, 101, "artc: msgid not in request list\n"),
410 0, 0, 0 );
411 }
412 }
413
414
415 /*
416 * look in the cache for this request
417 * return 0 if found, -1 if not
418 * if found, the corresponding result messages are added to the incoming
419 * queue with the correct (new) msgid so that subsequent ldap_result calls
420 * will find them.
421 */
422 int
check_cache(LDAP * ld,unsigned int msgtype,BerElement * request)423 check_cache( LDAP *ld, unsigned int msgtype, BerElement *request )
424 {
425 LDAPMessage *m, *new, *prev, *next;
426 BerElement reqber;
427 int first, hash;
428 unsigned long validtime;
429
430 Debug( LDAP_DEBUG_TRACE, catgets(slapdcat, 1, 102, "check_cache\n"), 0, 0, 0 );
431
432 if ( ld->ld_cache == NULLLDCACHE ||
433 ( ld->ld_cache->lc_enabled == 0 )) {
434 return( -1 );
435 }
436
437 reqber.ber_buf = reqber.ber_ptr = request->ber_buf;
438 reqber.ber_end = request->ber_ptr;
439
440 validtime = time( NULL ) - ld->ld_cache->lc_timeout;
441
442 prev = NULLMSG;
443 hash = cache_hash( &reqber );
444 for ( m = ld->ld_cache->lc_buckets[ hash ]; m != NULLMSG; m = next ) {
445 Debug( LDAP_DEBUG_TRACE,catgets(slapdcat, 1, 103, "cc: examining id %1$d,type %2$d\n"),
446 m->lm_msgid, m->lm_msgtype, 0 );
447 if ( m->lm_time < validtime ) {
448 /* delete expired message */
449 next = m->lm_next;
450 if ( prev == NULL ) {
451 ld->ld_cache->lc_buckets[ hash ] = next;
452 } else {
453 prev->lm_next = next;
454 }
455 Debug( LDAP_DEBUG_TRACE, catgets(slapdcat, 1, 104, "cc: expired id %d\n"),
456 m->lm_msgid, 0, 0 );
457 ld->ld_cache->lc_memused -= msg_size( m );
458 ldap_msgfree( m );
459 } else {
460 if ( m->lm_msgtype == msgtype &&
461 request_cmp( m->lm_ber, &reqber ) == 0 ) {
462 break;
463 }
464 next = m->lm_next;
465 prev = m;
466 }
467 }
468
469 if ( m == NULLMSG ) {
470 return( -1 );
471 }
472
473 /*
474 * add duplicates of responses to incoming queue
475 */
476 first = 1;
477 #if defined( SUN ) && defined( _REENTRANT )
478 LOCK_RESPONSE(ld);
479 #endif
480 for ( m = m->lm_chain; m != NULLMSG; m = m->lm_chain ) {
481 if (( new = msg_dup( m )) == NULLMSG ) {
482 #if defined( SUN ) && defined( _REENTRANT )
483 UNLOCK_RESPONSE(ld);
484 #endif
485 return( -1 );
486 }
487
488 new->lm_msgid = ld->ld_msgid;
489 new->lm_chain = NULLMSG;
490 if ( first ) {
491 new->lm_next = ld->ld_responses;
492 ld->ld_responses = new;
493 first = 0;
494 } else {
495 prev->lm_chain = new;
496 }
497 prev = new;
498 Debug( LDAP_DEBUG_TRACE, catgets(slapdcat, 1, 105, "cc: added type %d\n"),
499 new->lm_msgtype, 0, 0 );
500 }
501
502 Debug( LDAP_DEBUG_TRACE, catgets(slapdcat, 1, 106, "cc: result returned from cache\n"), 0, 0, 0 );
503 #if defined( SUN ) && defined( _REENTRANT )
504 UNLOCK_RESPONSE(ld);
505 #endif
506 return( 0 );
507 }
508
509
510 static int
cache_hash(BerElement * ber)511 cache_hash( BerElement *ber )
512 {
513 BerElement bercpy;
514 unsigned int len;
515
516 /*
517 * just take the length of the packet and mod with # of buckets
518 */
519 bercpy = *ber;
520 if ( ber_skip_tag( &bercpy, &len ) == LBER_ERROR
521 || ber_scanf( &bercpy, "x" ) == LBER_ERROR ) {
522 len = 0; /* punt: just return zero */
523 } else {
524 len = (int) ( bercpy.ber_end - bercpy.ber_ptr );
525 }
526
527 Debug( LDAP_DEBUG_TRACE, catgets(slapdcat, 1, 107, "cache_hash: len is %1$ld, returning %2$ld\n"),
528 len, len % LDAP_CACHE_BUCKETS, 0 );
529 return ( len % LDAP_CACHE_BUCKETS );
530 }
531
532
533 static LDAPMessage *
msg_dup(LDAPMessage * msg)534 msg_dup( LDAPMessage *msg )
535 {
536 LDAPMessage *new;
537 size_t len;
538
539 if (( new = (LDAPMessage *)malloc( sizeof(LDAPMessage))) != NULL ) {
540 *new = *msg; /* struct copy */
541 if (( new->lm_ber = ber_dup( msg->lm_ber )) == NULLBER ) {
542 free( (char *)new );
543 return( NULLMSG );
544 }
545 len = msg->lm_ber->ber_end - msg->lm_ber->ber_buf;
546 if (( new->lm_ber->ber_buf = (char *) malloc( len )) == NULL ) {
547 ber_free( new->lm_ber, 0 );
548 free( (char *)new );
549 return( NULLMSG );
550 }
551 SAFEMEMCPY( new->lm_ber->ber_buf, msg->lm_ber->ber_buf, len );
552
553 new->lm_ber->ber_ptr = new->lm_ber->ber_buf +
554 ( msg->lm_ber->ber_ptr - msg->lm_ber->ber_buf );
555 new->lm_ber->ber_end = new->lm_ber->ber_buf + len;
556 }
557
558 return( new );
559 }
560
561
562 static int
request_cmp(BerElement * req1,BerElement * req2)563 request_cmp( BerElement *req1, BerElement *req2 )
564 {
565 unsigned int len;
566 size_t slen;
567 BerElement r1, r2;
568
569 r1 = *req1; /* struct copies */
570 r2 = *req2;
571
572 /*
573 * skip the enclosing tags (sequence markers) and the msg ids
574 */
575 if ( ber_skip_tag( &r1, &len ) == LBER_ERROR || ber_scanf( &r1, "x" )
576 == LBER_ERROR ) {
577 return( -1 );
578 }
579 if ( ber_skip_tag( &r2, &len ) == LBER_ERROR || ber_scanf( &r2, "x" )
580 == LBER_ERROR ) {
581 return( -1 );
582 }
583
584 /*
585 * check remaining length and bytes if necessary
586 */
587 if (( slen = r1.ber_end - r1.ber_ptr ) != r2.ber_end - r2.ber_ptr ) {
588 return( -1 ); /* different lengths */
589 }
590 return( memcmp( r1.ber_ptr, r2.ber_ptr, slen ));
591 }
592
593
594 static int
chain_contains_dn(LDAPMessage * msg,char * dn)595 chain_contains_dn( LDAPMessage *msg, char *dn )
596 {
597 LDAPMessage *m;
598 BerElement ber;
599 int msgid;
600 char *s;
601 int rc;
602
603
604 /*
605 * first check the base or dn of the request
606 */
607 ber = *msg->lm_ber; /* struct copy */
608 if ( ber_scanf( &ber, "{i{a", &msgid, &s ) != LBER_ERROR ) {
609 rc = ( strcasecmp( dn, s ) == 0 ) ? 1 : 0;
610 free( s );
611 if ( rc != 0 ) {
612 return( rc );
613 }
614 }
615
616 if ( msg->lm_msgtype == LDAP_REQ_COMPARE ) {
617 return( 0 );
618 }
619
620 /*
621 * now check the dn of each search result
622 */
623 rc = 0;
624 for ( m = msg->lm_chain; m != NULLMSG && rc == 0 ; m = m->lm_chain ) {
625 if ( m->lm_msgtype != LDAP_RES_SEARCH_ENTRY ) {
626 continue;
627 }
628 ber = *m->lm_ber; /* struct copy */
629 if ( ber_scanf( &ber, "{a", &s ) != LBER_ERROR ) {
630 rc = ( strcasecmp( dn, s ) == 0 ) ? 1 : 0;
631 free( s );
632 }
633 }
634
635 return( rc );
636 }
637
638
639 static ssize_t
msg_size(LDAPMessage * msg)640 msg_size( LDAPMessage *msg )
641 {
642 LDAPMessage *m;
643 ssize_t size;
644
645 size = 0;
646 for ( m = msg; m != NULLMSG; m = m->lm_chain ) {
647 size += sizeof( LDAPMessage ) + m->lm_ber->ber_end -
648 m->lm_ber->ber_buf;
649 }
650
651 return( size );
652 }
653
654
655 #define THRESHOLD_FACTOR 3 / 4
656 #define SIZE_FACTOR 2 / 3
657
658 static void
check_cache_memused(LDAPCache * lc)659 check_cache_memused( LDAPCache *lc )
660 {
661 /*
662 * this routine is called to check if the cache is too big (lc_maxmem >
663 * minimum cache size and lc_memused > lc_maxmem). If too big, it reduces
664 * the cache size to < SIZE_FACTOR * lc_maxmem. The algorithm is as follows:
665 * remove_threshold = lc_timeout seconds;
666 * do {
667 * remove everything older than remove_threshold seconds;
668 * remove_threshold = remove_threshold * THRESHOLD_FACTOR;
669 * } while ( cache size is > SIZE_FACTOR * lc_maxmem )
670 */
671 int i;
672 unsigned long remove_threshold, validtime;
673 LDAPMessage *m, *prev, *next;
674
675 Debug( LDAP_DEBUG_TRACE, catgets(slapdcat, 1, 108, "check_cache_memused: %1$ld bytes in use (%2$ld max)\n"),
676 lc->lc_memused, lc->lc_maxmem, 0 );
677
678 if ( lc->lc_maxmem <= sizeof( LDAPCache )
679 || lc->lc_memused <= lc->lc_maxmem * SIZE_FACTOR ) {
680 return;
681 }
682
683 remove_threshold = lc->lc_timeout;
684 while ( lc->lc_memused > lc->lc_maxmem * SIZE_FACTOR ) {
685 validtime = time( NULL ) - remove_threshold;
686 for ( i = 0; i < LDAP_CACHE_BUCKETS; ++i ) {
687 prev = NULLMSG;
688 for ( m = lc->lc_buckets[ i ]; m != NULLMSG;
689 m = next ) {
690 next = m->lm_next;
691 if ( m->lm_time < validtime ) {
692 if ( prev == NULLMSG ) {
693 lc->lc_buckets[ i ] = next;
694 } else {
695 prev->lm_next = next;
696 }
697 lc->lc_memused -= msg_size( m );
698 Debug( LDAP_DEBUG_TRACE,
699 catgets(slapdcat, 1, 109, "ccm: removed %d\n"),
700 m->lm_msgid, 0, 0 );
701 ldap_msgfree( m );
702 } else {
703 prev = m;
704 }
705 }
706 }
707 remove_threshold *= THRESHOLD_FACTOR;
708 }
709
710 Debug( LDAP_DEBUG_TRACE, catgets(slapdcat, 1, 110, "ccm: reduced usage to %ld bytes\n"),
711 lc->lc_memused, 0, 0 );
712 }
713
714 #endif /* !NO_CACHE */
715