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