diff options
author | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
---|---|---|
committer | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
commit | 7c478bd95313f5f23a4c958a745db2134aa03244 (patch) | |
tree | c871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/lib/libldap4/common/cache.c | |
download | illumos-joyent-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz |
OpenSolaris Launch
Diffstat (limited to 'usr/src/lib/libldap4/common/cache.c')
-rw-r--r-- | usr/src/lib/libldap4/common/cache.c | 716 |
1 files changed, 716 insertions, 0 deletions
diff --git a/usr/src/lib/libldap4/common/cache.c b/usr/src/lib/libldap4/common/cache.c new file mode 100644 index 0000000000..5fc2b33561 --- /dev/null +++ b/usr/src/lib/libldap4/common/cache.c @@ -0,0 +1,716 @@ +/* + * + * Portions Copyright %G% Sun Microsystems, Inc. + * All Rights Reserved + * + */ +#pragma ident "%Z%%M% %I% %E% SMI" +/* + * Copyright (c) 1993 The Regents of the University of Michigan. + * All rights reserved. + * + * cache.c - local caching support for LDAP + */ + +#ifndef NO_CACHE + +#ifndef lint +static char copyright[] = "@(#) Copyright (c) 1993 The Regents of the University of Michigan.\nAll rights reserved.\n"; +#endif + +#include <stdio.h> +#include <string.h> +#ifdef MACOS +#include <stdlib.h> +#include <time.h> +#include "macos.h" +#else /* MACOS */ +#if defined( DOS ) || defined( _WIN32 ) +#include <malloc.h> +#include "msdos.h" +#ifdef NCSA +#include "externs.h" +#endif /* NCSA */ +#ifdef WINSOCK +#include <time.h> +#endif /* WINSOCK */ +#else /* DOS */ +#include <sys/types.h> +#include <sys/socket.h> +#endif /* DOS */ +#endif /* MACOS */ +#include "lber.h" +#include "ldap.h" +#include "ldap-private.h" +#include "ldap-int.h" + +#ifdef NEEDPROTOS +static int cache_hash( BerElement *ber ); +static LDAPMessage *msg_dup( LDAPMessage *msg ); +static int request_cmp( BerElement *req1, BerElement *req2 ); +static int chain_contains_dn( LDAPMessage *msg, char *dn ); +static ssize_t msg_size( LDAPMessage *msg ); +static void check_cache_memused( LDAPCache *lc ); +static void uncache_entry_or_req( LDAP *ld, char *dn, int msgid ); +#else /* NEEDPROTOS */ +static int cache_hash(); +static LDAPMessage *msg_dup(); +static int request_cmp(); +static int chain_contains_dn(); +static ssize_t msg_size(); +static void check_cache_memused(); +static void uncache_entry_or_req(); +#endif /* NEEDPROTOS */ + + +int +ldap_enable_cache( LDAP *ld, time_t timeout, ssize_t maxmem ) +{ +#if defined( SUN ) && defined( _REENTRANT ) + LOCK_LDAP(ld); +#endif + if ( ld->ld_cache == NULLLDCACHE ) { + if (( ld->ld_cache = (LDAPCache *)malloc( sizeof( LDAPCache ))) + == NULLLDCACHE ) { + ld->ld_errno = LDAP_NO_MEMORY; +#if defined( SUN ) && defined( _REENTRANT ) + UNLOCK_LDAP(ld); +#endif + return( -1 ); + } + (void) memset( ld->ld_cache, 0, sizeof( LDAPCache )); + ld->ld_cache->lc_memused = sizeof( LDAPCache ); + } + + ld->ld_cache->lc_timeout = timeout; + ld->ld_cache->lc_maxmem = maxmem; + check_cache_memused( ld->ld_cache ); + ld->ld_cache->lc_enabled = 1; +#if defined( SUN ) && defined( _REENTRANT ) + UNLOCK_LDAP(ld); +#endif + return( 0 ); +} + + +void +ldap_disable_cache( LDAP *ld ) +{ +#if defined( SUN ) && defined( _REENTRANT ) + LOCK_LDAP(ld); +#endif + if ( ld->ld_cache != NULLLDCACHE ) { + ld->ld_cache->lc_enabled = 0; + } +#if defined( SUN ) && defined( _REENTRANT ) + UNLOCK_LDAP(ld); +#endif +} + + + +void +ldap_set_cache_options( LDAP *ld, unsigned int opts ) +{ +#if defined( SUN ) && defined( _REENTRANT ) + LOCK_LDAP(ld); +#endif + if ( ld->ld_cache != NULLLDCACHE ) { + ld->ld_cache->lc_options = opts; + } +#if defined( SUN ) && defined( _REENTRANT ) + UNLOCK_LDAP(ld); +#endif +} + + +void +ldap_destroy_cache( LDAP *ld ) +{ +#if defined( SUN ) && defined( _REENTRANT ) + LOCK_LDAP(ld); +#endif + if ( ld->ld_cache != NULLLDCACHE ) { + ldap_flush_cache( ld ); + free( (char *)ld->ld_cache ); + ld->ld_cache = NULLLDCACHE; + } +#if defined( SUN ) && defined( _REENTRANT ) + UNLOCK_LDAP(ld); +#endif +} + + +void +ldap_flush_cache( LDAP *ld ) +{ + int i; + LDAPMessage *m, *next; + +#if defined( SUN ) && defined( _REENTRANT ) + LOCK_LDAP(ld); +#endif + Debug( LDAP_DEBUG_TRACE, catgets(slapdcat, 1, 90, "ldap_flush_cache\n"), 0, 0, 0 ); + + if ( ld->ld_cache != NULLLDCACHE ) { + /* delete all requests in the queue */ + for ( m = ld->ld_cache->lc_requests; m != NULLMSG; m = next ) { + next = m->lm_next; + ldap_msgfree( m ); + } + ld->ld_cache->lc_requests = NULLMSG; + + /* delete all messages in the cache */ + for ( i = 0; i < LDAP_CACHE_BUCKETS; ++i ) { + for ( m = ld->ld_cache->lc_buckets[ i ]; + m != NULLMSG; m = next ) { + next = m->lm_next; + ldap_msgfree( m ); + } + ld->ld_cache->lc_buckets[ i ] = NULLMSG; + } + ld->ld_cache->lc_memused = sizeof( LDAPCache ); + } +#if defined( SUN ) && defined( _REENTRANT ) + UNLOCK_LDAP(ld); +#endif +} + + +void +ldap_uncache_request( LDAP *ld, int msgid ) +{ +#if defined( SUN ) && defined( _REENTRANT ) + LOCK_LDAP(ld); +#endif + Debug( LDAP_DEBUG_TRACE, catgets(slapdcat, 1, 91, "ldap_uncache_request %1$d ld_cache %2$x\n"), + msgid, ld->ld_cache, 0 ); + + uncache_entry_or_req( ld, NULL, msgid ); +#if defined( SUN ) && defined( _REENTRANT ) + UNLOCK_LDAP(ld); +#endif +} + + +void +ldap_uncache_entry( LDAP *ld, char *dn ) +{ +#if defined( SUN ) && defined( _REENTRANT ) + LOCK_LDAP(ld); +#endif + Debug( LDAP_DEBUG_TRACE, catgets(slapdcat, 1, 92, "ldap_uncache_entry %1$s ld_cache %2$x\n"), + dn, ld->ld_cache, 0 ); + + uncache_entry_or_req( ld, dn, 0 ); +#if defined( SUN ) && defined( _REENTRANT ) + UNLOCK_LDAP(ld); +#endif +} + + +static void +uncache_entry_or_req( LDAP *ld, + char *dn, /* if non-NULL, uncache entry */ + int msgid ) /* request to uncache (if dn == NULL) */ +{ + int i; + LDAPMessage *m, *prev, *next; + + Debug( LDAP_DEBUG_TRACE, + catgets(slapdcat, 1, 93, "ldap_uncache_entry_or_req dn %1$s msgid %2$d ld_cache %3$x\n"), + dn, msgid, ld->ld_cache ); + + if ( ld->ld_cache == NULLLDCACHE ) { + return; + } + + /* first check the request queue */ + prev = NULLMSG; + for ( m = ld->ld_cache->lc_requests; m != NULLMSG; m = next ) { + next = m->lm_next; + if (( dn != NULL && chain_contains_dn( m, dn )) || + ( dn == NULL && m->lm_msgid == msgid )) { + if ( prev == NULLMSG ) { + ld->ld_cache->lc_requests = next; + } else { + prev->lm_next = next; + } + ld->ld_cache->lc_memused -= msg_size( m ); + ldap_msgfree( m ); + } else { + prev = m; + } + } + + /* now check the rest of the cache */ + for ( i = 0; i < LDAP_CACHE_BUCKETS; ++i ) { + prev = NULLMSG; + for ( m = ld->ld_cache->lc_buckets[ i ]; m != NULLMSG; + m = next ) { + next = m->lm_next; + if (( dn != NULL && chain_contains_dn( m, dn )) || + ( dn == NULL && m->lm_msgid == msgid )) { + if ( prev == NULLMSG ) { + ld->ld_cache->lc_buckets[ i ] = next; + } else { + prev->lm_next = next; + } + ld->ld_cache->lc_memused -= msg_size( m ); + ldap_msgfree( m ); + } else { + prev = m; + } + } + } +} + + +void +add_request_to_cache( LDAP *ld, unsigned int msgtype, BerElement *request ) +{ + LDAPMessage *new; + size_t len; + + Debug( LDAP_DEBUG_TRACE, catgets(slapdcat, 1, 94, "add_request_to_cache\n"), 0, 0, 0 ); + + ld->ld_errno = LDAP_SUCCESS; + if ( ld->ld_cache == NULLLDCACHE || + ( ld->ld_cache->lc_enabled == 0 )) { + return; + } + + if (( new = (LDAPMessage *) calloc( 1, sizeof(LDAPMessage) )) + != NULL ) { + if (( new->lm_ber = alloc_ber_with_options( ld )) == NULLBER ) { + free( (char *)new ); + return; + } + len = request->ber_ptr - request->ber_buf; + if (( new->lm_ber->ber_buf = (char *) malloc( len )) + == NULL ) { + ber_free( new->lm_ber, 0 ); + free( (char *)new ); + ld->ld_errno = LDAP_NO_MEMORY; + return; + } + SAFEMEMCPY( new->lm_ber->ber_buf, request->ber_buf, len ); + new->lm_ber->ber_ptr = new->lm_ber->ber_buf; + new->lm_ber->ber_end = new->lm_ber->ber_buf + len; + new->lm_msgid = ld->ld_msgid; + new->lm_msgtype = (int) msgtype;; + new->lm_next = ld->ld_cache->lc_requests; + ld->ld_cache->lc_requests = new; + } else { + ld->ld_errno = LDAP_NO_MEMORY; + } +} + + +void +add_result_to_cache( LDAP *ld, LDAPMessage *result ) +{ + LDAPMessage *m, **mp, *req, *new, *prev; + int err, keep; + + Debug( LDAP_DEBUG_TRACE, catgets(slapdcat, 1, 95, "add_result_to_cache: id %1$d, type %2$d\n"), + result->lm_msgid, result->lm_msgtype, 0 ); + + if ( ld->ld_cache == NULLLDCACHE || + ( ld->ld_cache->lc_enabled == 0 )) { + Debug( LDAP_DEBUG_TRACE, catgets(slapdcat, 1, 96, "artc: cache disabled\n"), 0, 0, 0 ); + return; + } + + if ( result->lm_msgtype != LDAP_RES_SEARCH_ENTRY && + result->lm_msgtype != LDAP_RES_SEARCH_RESULT && + result->lm_msgtype != LDAP_RES_SEARCH_REFERENCE && + result->lm_msgtype != LDAP_RES_COMPARE ) { + /* + * only cache search and compare operations + */ + Debug( LDAP_DEBUG_TRACE, + catgets(slapdcat, 1, 97, "artc: only caching search & compare operations\n"), 0, 0, 0 ); + return; + } + + /* + * if corresponding request is in the lc_requests list, add this + * result to it. if this result completes the results for the + * request, add the request/result chain to the cache proper. + */ + prev = NULLMSG; + for ( m = ld->ld_cache->lc_requests; m != NULL; m = m->lm_next ) { + if ( m->lm_msgid == result->lm_msgid ) { + break; + } + prev = m; + } + + if ( m != NULLMSG ) { /* found request; add to end of chain */ + req = m; + for ( ; m->lm_chain != NULLMSG; m = m->lm_chain ) + ; + if (( new = msg_dup( result )) != NULLMSG ) { + new->lm_chain = NULLMSG; + m->lm_chain = new; + Debug( LDAP_DEBUG_TRACE, + catgets(slapdcat, 1, 98, "artc: result added to cache request chain\n"), + 0, 0, 0 ); + } + if ( result->lm_msgtype == LDAP_RES_SEARCH_RESULT || + result->lm_msgtype == LDAP_RES_COMPARE ) { + /* + * this result completes the chain of results + * add to cache proper if appropriate + */ + keep = 0; /* pessimistic */ + err = ldap_result2error( ld, result, 0 ); + if ( err == LDAP_SUCCESS || + ( result->lm_msgtype == LDAP_RES_COMPARE && + ( err == LDAP_COMPARE_FALSE || + err == LDAP_COMPARE_TRUE || + err == LDAP_NO_SUCH_ATTRIBUTE ))) { + keep = 1; + } + + if ( ld->ld_cache->lc_options == 0 ) { + if ( err == LDAP_SIZELIMIT_EXCEEDED ) { + keep = 1; + } + } else if (( ld->ld_cache->lc_options & + LDAP_CACHE_OPT_CACHEALLERRS ) != 0 ) { + keep = 1; + } + + if ( prev == NULLMSG ) { + ld->ld_cache->lc_requests = req->lm_next; + } else { + prev->lm_next = req->lm_next; + } + + if ( !keep ) { + Debug( LDAP_DEBUG_TRACE, + catgets(slapdcat, 1, 99, "artc: not caching result with error %d\n"), + err, 0, 0 ); + ldap_msgfree( req ); + } else { + mp = &ld->ld_cache->lc_buckets[ + cache_hash( req->lm_ber ) ]; + req->lm_next = *mp; + *mp = req; + req->lm_time = time( NULL ); + ld->ld_cache->lc_memused += msg_size( req ); + check_cache_memused( ld->ld_cache ); + Debug( LDAP_DEBUG_TRACE, + catgets(slapdcat, 1, 100, "artc: cached result with error %d\n"), + err, 0, 0 ); + } + } + } else { + Debug( LDAP_DEBUG_TRACE, catgets(slapdcat, 1, 101, "artc: msgid not in request list\n"), + 0, 0, 0 ); + } +} + + +/* + * look in the cache for this request + * return 0 if found, -1 if not + * if found, the corresponding result messages are added to the incoming + * queue with the correct (new) msgid so that subsequent ldap_result calls + * will find them. + */ +int +check_cache( LDAP *ld, unsigned int msgtype, BerElement *request ) +{ + LDAPMessage *m, *new, *prev, *next; + BerElement reqber; + int first, hash; + unsigned long validtime; + + Debug( LDAP_DEBUG_TRACE, catgets(slapdcat, 1, 102, "check_cache\n"), 0, 0, 0 ); + + if ( ld->ld_cache == NULLLDCACHE || + ( ld->ld_cache->lc_enabled == 0 )) { + return( -1 ); + } + + reqber.ber_buf = reqber.ber_ptr = request->ber_buf; + reqber.ber_end = request->ber_ptr; + + validtime = time( NULL ) - ld->ld_cache->lc_timeout; + + prev = NULLMSG; + hash = cache_hash( &reqber ); + for ( m = ld->ld_cache->lc_buckets[ hash ]; m != NULLMSG; m = next ) { + Debug( LDAP_DEBUG_TRACE,catgets(slapdcat, 1, 103, "cc: examining id %1$d,type %2$d\n"), + m->lm_msgid, m->lm_msgtype, 0 ); + if ( m->lm_time < validtime ) { + /* delete expired message */ + next = m->lm_next; + if ( prev == NULL ) { + ld->ld_cache->lc_buckets[ hash ] = next; + } else { + prev->lm_next = next; + } + Debug( LDAP_DEBUG_TRACE, catgets(slapdcat, 1, 104, "cc: expired id %d\n"), + m->lm_msgid, 0, 0 ); + ld->ld_cache->lc_memused -= msg_size( m ); + ldap_msgfree( m ); + } else { + if ( m->lm_msgtype == msgtype && + request_cmp( m->lm_ber, &reqber ) == 0 ) { + break; + } + next = m->lm_next; + prev = m; + } + } + + if ( m == NULLMSG ) { + return( -1 ); + } + + /* + * add duplicates of responses to incoming queue + */ + first = 1; +#if defined( SUN ) && defined( _REENTRANT ) + LOCK_RESPONSE(ld); +#endif + for ( m = m->lm_chain; m != NULLMSG; m = m->lm_chain ) { + if (( new = msg_dup( m )) == NULLMSG ) { +#if defined( SUN ) && defined( _REENTRANT ) + UNLOCK_RESPONSE(ld); +#endif + return( -1 ); + } + + new->lm_msgid = ld->ld_msgid; + new->lm_chain = NULLMSG; + if ( first ) { + new->lm_next = ld->ld_responses; + ld->ld_responses = new; + first = 0; + } else { + prev->lm_chain = new; + } + prev = new; + Debug( LDAP_DEBUG_TRACE, catgets(slapdcat, 1, 105, "cc: added type %d\n"), + new->lm_msgtype, 0, 0 ); + } + + Debug( LDAP_DEBUG_TRACE, catgets(slapdcat, 1, 106, "cc: result returned from cache\n"), 0, 0, 0 ); +#if defined( SUN ) && defined( _REENTRANT ) + UNLOCK_RESPONSE(ld); +#endif + return( 0 ); +} + + +static int +cache_hash( BerElement *ber ) +{ + BerElement bercpy; + unsigned int len; + + /* + * just take the length of the packet and mod with # of buckets + */ + bercpy = *ber; + if ( ber_skip_tag( &bercpy, &len ) == LBER_ERROR + || ber_scanf( &bercpy, "x" ) == LBER_ERROR ) { + len = 0; /* punt: just return zero */ + } else { + len = (int) ( bercpy.ber_end - bercpy.ber_ptr ); + } + + Debug( LDAP_DEBUG_TRACE, catgets(slapdcat, 1, 107, "cache_hash: len is %1$ld, returning %2$ld\n"), + len, len % LDAP_CACHE_BUCKETS, 0 ); + return ( len % LDAP_CACHE_BUCKETS ); +} + + +static LDAPMessage * +msg_dup( LDAPMessage *msg ) +{ + LDAPMessage *new; + size_t len; + + if (( new = (LDAPMessage *)malloc( sizeof(LDAPMessage))) != NULL ) { + *new = *msg; /* struct copy */ + if (( new->lm_ber = ber_dup( msg->lm_ber )) == NULLBER ) { + free( (char *)new ); + return( NULLMSG ); + } + len = msg->lm_ber->ber_end - msg->lm_ber->ber_buf; + if (( new->lm_ber->ber_buf = (char *) malloc( len )) == NULL ) { + ber_free( new->lm_ber, 0 ); + free( (char *)new ); + return( NULLMSG ); + } + SAFEMEMCPY( new->lm_ber->ber_buf, msg->lm_ber->ber_buf, len ); + + new->lm_ber->ber_ptr = new->lm_ber->ber_buf + + ( msg->lm_ber->ber_ptr - msg->lm_ber->ber_buf ); + new->lm_ber->ber_end = new->lm_ber->ber_buf + len; + } + + return( new ); +} + + +static int +request_cmp( BerElement *req1, BerElement *req2 ) +{ + unsigned int len; + size_t slen; + BerElement r1, r2; + + r1 = *req1; /* struct copies */ + r2 = *req2; + + /* + * skip the enclosing tags (sequence markers) and the msg ids + */ + if ( ber_skip_tag( &r1, &len ) == LBER_ERROR || ber_scanf( &r1, "x" ) + == LBER_ERROR ) { + return( -1 ); + } + if ( ber_skip_tag( &r2, &len ) == LBER_ERROR || ber_scanf( &r2, "x" ) + == LBER_ERROR ) { + return( -1 ); + } + + /* + * check remaining length and bytes if necessary + */ + if (( slen = r1.ber_end - r1.ber_ptr ) != r2.ber_end - r2.ber_ptr ) { + return( -1 ); /* different lengths */ + } + return( memcmp( r1.ber_ptr, r2.ber_ptr, slen )); +} + + +static int +chain_contains_dn( LDAPMessage *msg, char *dn ) +{ + LDAPMessage *m; + BerElement ber; + int msgid; + char *s; + int rc; + + + /* + * first check the base or dn of the request + */ + ber = *msg->lm_ber; /* struct copy */ + if ( ber_scanf( &ber, "{i{a", &msgid, &s ) != LBER_ERROR ) { + rc = ( strcasecmp( dn, s ) == 0 ) ? 1 : 0; + free( s ); + if ( rc != 0 ) { + return( rc ); + } + } + + if ( msg->lm_msgtype == LDAP_REQ_COMPARE ) { + return( 0 ); + } + + /* + * now check the dn of each search result + */ + rc = 0; + for ( m = msg->lm_chain; m != NULLMSG && rc == 0 ; m = m->lm_chain ) { + if ( m->lm_msgtype != LDAP_RES_SEARCH_ENTRY ) { + continue; + } + ber = *m->lm_ber; /* struct copy */ + if ( ber_scanf( &ber, "{a", &s ) != LBER_ERROR ) { + rc = ( strcasecmp( dn, s ) == 0 ) ? 1 : 0; + free( s ); + } + } + + return( rc ); +} + + +static ssize_t +msg_size( LDAPMessage *msg ) +{ + LDAPMessage *m; + ssize_t size; + + size = 0; + for ( m = msg; m != NULLMSG; m = m->lm_chain ) { + size += sizeof( LDAPMessage ) + m->lm_ber->ber_end - + m->lm_ber->ber_buf; + } + + return( size ); +} + + +#define THRESHOLD_FACTOR 3 / 4 +#define SIZE_FACTOR 2 / 3 + +static void +check_cache_memused( LDAPCache *lc ) +{ +/* + * this routine is called to check if the cache is too big (lc_maxmem > + * minimum cache size and lc_memused > lc_maxmem). If too big, it reduces + * the cache size to < SIZE_FACTOR * lc_maxmem. The algorithm is as follows: + * remove_threshold = lc_timeout seconds; + * do { + * remove everything older than remove_threshold seconds; + * remove_threshold = remove_threshold * THRESHOLD_FACTOR; + * } while ( cache size is > SIZE_FACTOR * lc_maxmem ) + */ + int i; + unsigned long remove_threshold, validtime; + LDAPMessage *m, *prev, *next; + + Debug( LDAP_DEBUG_TRACE, catgets(slapdcat, 1, 108, "check_cache_memused: %1$ld bytes in use (%2$ld max)\n"), + lc->lc_memused, lc->lc_maxmem, 0 ); + + if ( lc->lc_maxmem <= sizeof( LDAPCache ) + || lc->lc_memused <= lc->lc_maxmem * SIZE_FACTOR ) { + return; + } + + remove_threshold = lc->lc_timeout; + while ( lc->lc_memused > lc->lc_maxmem * SIZE_FACTOR ) { + validtime = time( NULL ) - remove_threshold; + for ( i = 0; i < LDAP_CACHE_BUCKETS; ++i ) { + prev = NULLMSG; + for ( m = lc->lc_buckets[ i ]; m != NULLMSG; + m = next ) { + next = m->lm_next; + if ( m->lm_time < validtime ) { + if ( prev == NULLMSG ) { + lc->lc_buckets[ i ] = next; + } else { + prev->lm_next = next; + } + lc->lc_memused -= msg_size( m ); + Debug( LDAP_DEBUG_TRACE, + catgets(slapdcat, 1, 109, "ccm: removed %d\n"), + m->lm_msgid, 0, 0 ); + ldap_msgfree( m ); + } else { + prev = m; + } + } + } + remove_threshold *= THRESHOLD_FACTOR; + } + + Debug( LDAP_DEBUG_TRACE, catgets(slapdcat, 1, 110, "ccm: reduced usage to %ld bytes\n"), + lc->lc_memused, 0, 0 ); +} + +#endif /* !NO_CACHE */ |