summaryrefslogtreecommitdiffstats
path: root/ldap/servers/slapd/resourcelimit.c
diff options
context:
space:
mode:
authorcvsadm <cvsadm>2005-01-21 00:44:34 +0000
committercvsadm <cvsadm>2005-01-21 00:44:34 +0000
commitb2093e3016027d6b5cf06b3f91f30769bfc099e2 (patch)
treecf58939393a9032182c4fbc4441164a9456e82f8 /ldap/servers/slapd/resourcelimit.c
downloadds-b2093e3016027d6b5cf06b3f91f30769bfc099e2.tar.gz
ds-b2093e3016027d6b5cf06b3f91f30769bfc099e2.tar.xz
ds-b2093e3016027d6b5cf06b3f91f30769bfc099e2.zip
Moving NSCP Directory Server from DirectoryBranch to TRUNK, initial drop. (foxworth)ldapserver7x
Diffstat (limited to 'ldap/servers/slapd/resourcelimit.c')
-rw-r--r--ldap/servers/slapd/resourcelimit.c653
1 files changed, 653 insertions, 0 deletions
diff --git a/ldap/servers/slapd/resourcelimit.c b/ldap/servers/slapd/resourcelimit.c
new file mode 100644
index 00000000..3f0d3840
--- /dev/null
+++ b/ldap/servers/slapd/resourcelimit.c
@@ -0,0 +1,653 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* resourcelimit.c - binder-based resource limits implementation */
+
+
+/*
+ * Implementation notes:
+ *
+ * At present this code only provides support for integer-based
+ * resource limits.
+ *
+ * When a successful bind occurs (i.e., when bind_credentials_set() is
+ * called), reslimit_update_from_dn() or reslimit_update_from_entry()
+ * must be called. These functions look in the binder entry and pull
+ * out attribute values that correspond to resource limits. Typically
+ * operational attributes are used, e.g., nsSizeLimit to hold a
+ * binder-specific search size limit. The attributes should be single
+ * valued; if not, this code ignores all but the first value it finds.
+ * The virtual attribute interface is used to retrieve the binder entry
+ * values, so they can be based on COS, etc.
+ *
+ * Any resource limits found in the binder entry are cached in the
+ * connection structure. A connection object extension is used for this
+ * purpose. This means that if the attributes that correspond to binder
+ * entry are changed the resource limit won't be affected until the next
+ * bind occurs as that entry. The data in the connection extension is
+ * protected using a single writer/multiple reader locking scheme.
+ *
+ * A plugin or server subsystem that wants to use the resource limit
+ * subsystem should call slapi_reslimit_register() once for each limit it
+ * wants tracked. Note that slapi_reslimit_register() should be called
+ * early, i.e., before any client connections are accepted.
+ * slapi_reslimit_register() gives back an integer handle that is used
+ * later to refer to the limit in question. Here's a sample call:
+ */
+#if SLAPI_RESLIMIT_SAMPLE_CODE
+ static int sizelimit_reslimit_handle = -1;
+
+ if ( slapi_reslimit_register( SLAPI_RESLIMIT_TYPE_INT, "nsSizeLimit",
+ &sizelimit_reslimit_handle ) != SLAPI_RESLIMIT_STATUS_SUCCESS ) {
+ /* limit could not be registered -- fatal error? */
+ }
+ ...
+#endif
+
+/*
+ * A successful call to slapi_reslimit_register() results in a new
+ * entry in the reslimit_map, which is private to this source file.
+ * The map data structure is protected using a single writer/multiple
+ * reader locking scheme.
+ *
+ * To retrieve a binder-based limit, simple call
+ * slapi_reslimit_get_integer_limit(). If a value was present in the
+ * binder entry, it will be given back to the caller and
+ * SLAPI_RESLIMIT_STATUS_SUCCESS will be returned. If no value was
+ * present or the connection is NULL, SLAPI_RESLIMIT_STATUS_NOVALUE is
+ * returned. Other errors may be returned also. Here's a sample call:
+ */
+#if SLAPI_RESLIMIT_SAMPLE_CODE
+ int rc, sizelimit;
+
+ rc = slapi_reslimit_get_integer_limit( conn, sizelimit_reslimit_handle,
+ &sizelimit );
+
+ switch( rc ) {
+ case SLAPI_RESLIMIT_STATUS_SUCCESS: /* got a value */
+ break;
+ case SLAPI_RESLIMIT_STATUS_NOVALUE: /* no limit value available */
+ sizelimit = 500; /* use a default value */
+ break;
+ default: /* some other error occurred */
+ sizelimit = 500; /* use a default value */
+ }
+#endif
+
+/*
+ * The function reslimit_cleanup() is called from main() to dispose of
+ * memory, locks, etc. so tools like Purify() don't report leaks at exit.
+ */
+/* End of implementation notes */
+
+#include "slap.h"
+
+
+/*
+ * Macros.
+ */
+#define SLAPI_RESLIMIT_MODULE "binder-based resource limits"
+/* #define SLAPI_RESLIMIT_DEBUG */ /* define this to enable extra logging */
+ /* also forces trace log messages to */
+ /* always be logged */
+
+#ifdef SLAPI_RESLIMIT_DEBUG
+#define SLAPI_RESLIMIT_TRACELEVEL LDAP_DEBUG_ANY
+#else /* SLAPI_RESLIMIT_DEBUG */
+#define SLAPI_RESLIMIT_TRACELEVEL LDAP_DEBUG_TRACE
+#endif /* SLAPI_RESLIMIT_DEBUG */
+
+
+/*
+ * Structures and types.
+ */
+/* Per-connection resource limits data */
+typedef struct slapi_reslimit_conndata {
+ PRRWLock *rlcd_rwlock; /* to serialize access to the rest */
+ int rlcd_integer_count; /* size of rlcd_integer_limit array */
+ PRBool *rlcd_integer_available; /* array that says whether each */
+ /* value is available */
+ int *rlcd_integer_value; /* array that holds limit values */
+} SLAPIResLimitConnData;
+
+/* Mapping between attribute and limit */
+typedef struct slapi_reslimit_map {
+ int rlmap_type; /* always SLAPI_RESLIMIT_TYPE_INT for now */
+ char *rlmap_at; /* attribute type name */
+} SLAPIResLimitMap;
+
+
+/*
+ * Static variables (module globals).
+ */
+static int reslimit_inited = 0;
+static int reslimit_connext_objtype = 0;
+static int reslimit_connext_handle = 0;
+static struct slapi_reslimit_map *reslimit_map = NULL;
+static int reslimit_map_count = 0;
+static struct slapi_componentid *reslimit_componentid=NULL;
+
+/*
+ * reslimit_map_rwlock is used to serialize access to
+ * reslimit_map and reslimit_map_count
+ */
+static PRRWLock *reslimit_map_rwlock = NULL;
+
+/*
+ * Static functions.
+ */
+static int reslimit_init( void );
+static void *reslimit_connext_constructor( void *object, void *parent );
+static void reslimit_connext_destructor( void *extension, void *object,
+ void *parent );
+static int reslimit_get_ext( Slapi_Connection *conn, const char *logname,
+ SLAPIResLimitConnData **rlcdpp );
+static int reslimit_bv2int( const struct berval *bvp );
+static char ** reslimit_get_registered_attributes();
+
+
+/*
+ * reslimit_init() must be called before any resource related work
+ * is done. It is safe to call this more than once, but reslimit_inited
+ * can be tested to avoid a call.
+ *
+ * Returns zero if all goes well and non-zero if not.
+ */
+static int
+reslimit_init( void )
+{
+ if ( reslimit_inited == 0 ) {
+ if ( slapi_register_object_extension( SLAPI_RESLIMIT_MODULE,
+ SLAPI_EXT_CONNECTION, reslimit_connext_constructor,
+ reslimit_connext_destructor,
+ &reslimit_connext_objtype, &reslimit_connext_handle )
+ != 0 ) {
+ slapi_log_error( SLAPI_LOG_FATAL, SLAPI_RESLIMIT_MODULE,
+ "reslimit_init: slapi_register_object_extension()"
+ " failed\n" );
+ return( -1 );
+ }
+
+ if (( reslimit_map_rwlock = PR_NewRWLock( PR_RWLOCK_RANK_NONE,
+ "resourcelimit map rwlock" )) == NULL ) {
+ slapi_log_error( SLAPI_LOG_FATAL, SLAPI_RESLIMIT_MODULE,
+ "reslimit_init: PR_NewRWLock() failed\n" );
+ return( -1 );
+ }
+
+ reslimit_inited = 1;
+ }
+
+ reslimit_componentid=generate_componentid(NULL,COMPONENT_RESLIMIT);
+
+ return( 0 );
+}
+
+
+
+
+/*
+ * Dispose of any allocated memory, locks, other resources. Called when
+ * server is shutting down.
+ */
+void
+reslimit_cleanup( void )
+{
+ int i;
+
+ if ( reslimit_map != NULL ) {
+ for ( i = 0; i < reslimit_map_count; ++i ) {
+ if ( reslimit_map[ i ].rlmap_at != NULL ) {
+ slapi_ch_free( (void **)&reslimit_map[ i ].rlmap_at );
+ }
+ }
+ slapi_ch_free( (void **)&reslimit_map );
+ }
+
+ if ( reslimit_map_rwlock != NULL ) {
+ PR_DestroyRWLock( reslimit_map_rwlock );
+ }
+
+ if ( reslimit_componentid != NULL ) {
+ release_componentid( reslimit_componentid );
+ }
+}
+
+
+/*
+ * constructor for the connection object extension.
+ */
+static void *
+reslimit_connext_constructor( void *object, void *parent )
+{
+ SLAPIResLimitConnData *rlcdp;
+ PRRWLock *rwlock;
+
+ if (( rwlock = PR_NewRWLock( PR_RWLOCK_RANK_NONE,
+ "resource limit connection data rwlock" )) == NULL ) {
+ slapi_log_error( SLAPI_LOG_FATAL, SLAPI_RESLIMIT_MODULE,
+ "reslimit_connext_constructor: PR_NewRWLock() failed\n" );
+ return( NULL );
+ }
+
+ rlcdp = (SLAPIResLimitConnData *)slapi_ch_calloc( 1,
+ sizeof( SLAPIResLimitConnData ));
+ rlcdp->rlcd_rwlock = rwlock;
+
+ return( rlcdp );
+}
+
+
+/*
+ * destructor for the connection object extension.
+ */
+static void
+reslimit_connext_destructor( void *extension, void *object, void *parent )
+{
+ SLAPIResLimitConnData *rlcdp = (SLAPIResLimitConnData *)extension;
+
+ if ( rlcdp->rlcd_integer_available != NULL ) {
+ slapi_ch_free( (void **)&rlcdp->rlcd_integer_available );
+ }
+ if ( rlcdp->rlcd_integer_value != NULL ) {
+ slapi_ch_free( (void **)&rlcdp->rlcd_integer_value );
+ }
+ PR_DestroyRWLock( rlcdp->rlcd_rwlock );
+ slapi_ch_free( (void **)&rlcdp );
+}
+
+
+/*
+ * utility function to retrieve the connection object extension.
+ *
+ * if logname is non-NULL, errors are logged.
+ */
+static int
+reslimit_get_ext( Slapi_Connection *conn, const char *logname,
+ SLAPIResLimitConnData **rlcdpp )
+{
+ if ( !reslimit_inited && reslimit_init() != 0 ) {
+ if ( NULL != logname ) {
+ slapi_log_error( SLAPI_LOG_FATAL, SLAPI_RESLIMIT_MODULE,
+ "%s: reslimit_init() failed\n", logname );
+ }
+ return( SLAPI_RESLIMIT_STATUS_INIT_FAILURE );
+ }
+
+ if (( *rlcdpp = (SLAPIResLimitConnData *)slapi_get_object_extension(
+ reslimit_connext_objtype, conn,
+ reslimit_connext_handle )) == NULL ) {
+ if ( NULL != logname ) {
+ slapi_log_error( SLAPI_LOG_FATAL, SLAPI_RESLIMIT_MODULE,
+ "%s: slapi_get_object_extension() returned NULL\n", logname );
+ }
+ return( SLAPI_RESLIMIT_STATUS_INTERNAL_ERROR );
+ }
+
+ return( SLAPI_RESLIMIT_STATUS_SUCCESS );
+}
+
+
+/*
+ * utility function to convert a string-represented integer to an int.
+ *
+ * XXXmcs: wouldn't need this if slapi_value_get_int() returned a signed int!
+ */
+static int
+reslimit_bv2int( const struct berval *bvp )
+{
+ int rc = 0;
+ char smallbuf[ 25 ], *buf;
+
+ if ( bvp != NULL ) {
+ /* make a copy to ensure it is zero-terminated */
+ if ( bvp->bv_len < sizeof( smallbuf )) {
+ buf = smallbuf;
+ } else {
+ buf = slapi_ch_malloc( bvp->bv_len + 1 );
+ }
+ memcpy( buf, bvp->bv_val, bvp->bv_len);
+ buf[ bvp->bv_len ] = '\0';
+
+ rc = atoi( buf );
+
+ if ( buf != smallbuf ) {
+ slapi_ch_free( (void **)&smallbuf );
+ }
+ }
+
+ return( rc );
+}
+
+
+/**** Semi-public functions start here ***********************************/
+/*
+ * These functions are exposed to other parts of the server only, i.e.,
+ * they are NOT part of the official SLAPI API.
+ */
+
+/*
+ * Set the resource limits associated with connection `conn' based on the
+ * entry named by `dn'. If `dn' is NULL, limits are returned to their
+ * default state.
+ *
+ * A SLAPI_RESLIMIT_STATUS_... code is returned.
+ */
+int
+reslimit_update_from_dn( Slapi_Connection *conn, Slapi_DN *dn )
+{
+ Slapi_Entry *e;
+ int rc;
+
+ e = NULL;
+ if ( dn != NULL ) {
+
+ char ** attrs = reslimit_get_registered_attributes();
+ (void) slapi_search_internal_get_entry( dn, attrs, &e , reslimit_componentid);
+ charray_free(attrs);
+ }
+
+ rc = reslimit_update_from_entry( conn, e );
+
+ if ( NULL != e ) {
+ slapi_entry_free( e );
+ }
+
+ return( rc );
+}
+
+
+/*
+ * Set the resource limits associated with connection `conn' based on the
+ * entry `e'. If `e' is NULL, limits are returned to their default state.
+ * If `conn' is NULL, nothing is done.
+ *
+ * A SLAPI_RESLIMIT_STATUS_... code is returned.
+ */
+int
+reslimit_update_from_entry( Slapi_Connection *conn, Slapi_Entry *e )
+{
+ char *fnname = "reslimit_update_from_entry()";
+ char *actual_type_name, *get_ext_logname;
+ int i, rc, type_name_disposition, free_flags;
+ SLAPIResLimitConnData *rlcdp;
+ Slapi_ValueSet *vs;
+
+ LDAPDebug( SLAPI_RESLIMIT_TRACELEVEL, "=> %s conn=0x%x, entry=0x%x\n",
+ fnname, conn, e );
+
+ rc = SLAPI_RESLIMIT_STATUS_SUCCESS; /* optimistic */
+
+ /* if conn is NULL, there is nothing to be done */
+ if ( conn == NULL ) {
+ goto log_and_return;
+ }
+
+
+ if ( NULL == e ) {
+ get_ext_logname = NULL; /* do not log errors if resetting limits */
+ } else {
+ get_ext_logname = fnname;
+ }
+ if (( rc = reslimit_get_ext( conn, get_ext_logname, &rlcdp )) !=
+ SLAPI_RESLIMIT_STATUS_SUCCESS ) {
+ goto log_and_return;
+ }
+
+ /* LOCK FOR READ -- map lock */
+ PR_RWLock_Rlock( reslimit_map_rwlock );
+ /* LOCK FOR WRITE -- conn. data lock */
+ PR_RWLock_Wlock( rlcdp->rlcd_rwlock );
+
+ if ( rlcdp->rlcd_integer_value == NULL ) {
+ rlcdp->rlcd_integer_count = reslimit_map_count;
+ if ( rlcdp->rlcd_integer_count > 0 ) {
+ rlcdp->rlcd_integer_available = (PRBool *)slapi_ch_calloc(
+ rlcdp->rlcd_integer_count, sizeof( PRBool ));
+ rlcdp->rlcd_integer_value = (int *)slapi_ch_calloc(
+ rlcdp->rlcd_integer_count, sizeof( int ));
+ }
+ }
+
+ for ( i = 0; i < rlcdp->rlcd_integer_count; ++i ) {
+ if ( reslimit_map[ i ].rlmap_type != SLAPI_RESLIMIT_TYPE_INT ) {
+ continue;
+ }
+
+ LDAPDebug( SLAPI_RESLIMIT_TRACELEVEL,
+ "%s: setting limit for handle %d (based on %s)\n",
+ fnname, i, reslimit_map[ i ].rlmap_at );
+
+ rlcdp->rlcd_integer_available[ i ] = PR_FALSE;
+
+ if ( NULL != e && 0 == slapi_vattr_values_get( e,
+ reslimit_map[ i ].rlmap_at, &vs, &type_name_disposition,
+ &actual_type_name, 0, &free_flags )) {
+ Slapi_Value *v;
+ int index;
+ const struct berval *bvp;
+
+ if (( index = slapi_valueset_first_value( vs, &v )) != -1 &&
+ ( bvp = slapi_value_get_berval( v )) != NULL ) {
+ rlcdp->rlcd_integer_value[ i ] = reslimit_bv2int( bvp );
+ rlcdp->rlcd_integer_available[ i ] = PR_TRUE;
+
+ LDAPDebug( SLAPI_RESLIMIT_TRACELEVEL,
+ "%s: set limit based on %s to %d\n",
+ fnname, reslimit_map[ i ].rlmap_at,
+ rlcdp->rlcd_integer_value[ i ] );
+
+ if ( slapi_valueset_next_value( vs, index, &v ) != -1 ) {
+ char ebuf[ BUFSIZ ];
+ slapi_log_error( SLAPI_LOG_FATAL, SLAPI_RESLIMIT_MODULE,
+ "%s: ignoring multiple values for %s in entry \n",
+ fnname, reslimit_map[ i ].rlmap_at,
+ escape_string( slapi_entry_get_dn_const( e ),
+ ebuf ));
+ }
+ }
+
+ slapi_vattr_values_free( &vs, &actual_type_name, free_flags);
+ }
+ }
+
+ PR_RWLock_Unlock( rlcdp->rlcd_rwlock );
+ /* UNLOCKED -- conn. data lock */
+ PR_RWLock_Unlock( reslimit_map_rwlock );
+ /* UNLOCKED -- map lock */
+
+log_and_return:
+ LDAPDebug( SLAPI_RESLIMIT_TRACELEVEL, "<= %s returning status %d\n",
+ fnname, rc, 0 );
+
+ return( rc );
+}
+
+/* return the list of registered attributes */
+
+static char ** reslimit_get_registered_attributes()
+{
+
+ int i;
+ char **attrs=NULL;
+
+ /* LOCK FOR READ -- map lock */
+ PR_RWLock_Rlock( reslimit_map_rwlock );
+
+ for ( i = 0; i < reslimit_map_count; ++i ) {
+ if ( reslimit_map[ i ].rlmap_at != NULL ) {
+ charray_add(&attrs, slapi_ch_strdup(reslimit_map[ i ].rlmap_at));
+ }
+ }
+
+ PR_RWLock_Unlock( reslimit_map_rwlock );
+
+ return attrs;
+}
+
+
+/**** Public functions can be found below this point *********************/
+/*
+ * These functions are exposed to plugins, i.e., they are part of the
+ * official SLAPI API.
+ */
+
+/*
+ * Register a new resource to be tracked. `type' must be
+ * SLAPI_RESLIMIT_TYPE_INT and `attrname' is an LDAP attribute type that
+ * is consulted in the bound entry to determine the limit's value.
+ *
+ * A SLAPI_RESLIMIT_STATUS_... code is returned. If it is ...SUCCESS, then
+ * `*handlep' is set to an opaque integer value that should be used in
+ * subsequent calls to slapi_reslimit_get_integer_limit().
+ */
+int
+slapi_reslimit_register( int type, const char *attrname, int *handlep )
+{
+ char *fnname = "slapi_reslimit_register()";
+ int i, rc;
+
+ LDAPDebug( SLAPI_RESLIMIT_TRACELEVEL, "=> %s attrname=%s\n",
+ fnname, attrname, 0 );
+
+ rc = SLAPI_RESLIMIT_STATUS_SUCCESS; /* optimistic */
+
+ /* initialize if necessary */
+ if ( !reslimit_inited && reslimit_init() != 0 ) {
+ slapi_log_error( SLAPI_LOG_FATAL, SLAPI_RESLIMIT_MODULE,
+ "%s: reslimit_init() failed\n", fnname );
+ rc = SLAPI_RESLIMIT_STATUS_INIT_FAILURE;
+ goto log_and_return;
+ }
+
+ /* sanity check parameters */
+ if ( type != SLAPI_RESLIMIT_TYPE_INT || attrname == NULL
+ || handlep == NULL ) {
+ slapi_log_error( SLAPI_LOG_FATAL, SLAPI_RESLIMIT_MODULE,
+ "%s: parameter error\n", fnname );
+ rc = SLAPI_RESLIMIT_STATUS_PARAM_ERROR;
+ goto log_and_return;
+ }
+
+ /* LOCK FOR WRITE -- map lock */
+ PR_RWLock_Wlock( reslimit_map_rwlock );
+
+ /*
+ * check that attrname is not already registered
+ */
+ for ( i = 0; i < reslimit_map_count; ++i ) {
+ if ( 0 == slapi_attr_type_cmp( reslimit_map[ i ].rlmap_at,
+ attrname, SLAPI_TYPE_CMP_EXACT )) {
+ slapi_log_error( SLAPI_LOG_FATAL, SLAPI_RESLIMIT_MODULE,
+ "%s: parameter error (%s already registered)\n",
+ attrname, fnname );
+ rc = SLAPI_RESLIMIT_STATUS_PARAM_ERROR;
+ goto unlock_and_return;
+ }
+ }
+
+ /*
+ * expand the map array and add the new element
+ */
+ reslimit_map = (SLAPIResLimitMap *)slapi_ch_realloc(
+ (char *)reslimit_map,
+ (1 + reslimit_map_count) * sizeof( SLAPIResLimitMap ));
+ reslimit_map[ reslimit_map_count ].rlmap_type = type;
+ reslimit_map[ reslimit_map_count ].rlmap_at
+ = slapi_ch_strdup( attrname );
+ *handlep = reslimit_map_count;
+ ++reslimit_map_count;
+
+unlock_and_return:
+ PR_RWLock_Unlock( reslimit_map_rwlock );
+ /* UNLOCKED -- map lock */
+
+log_and_return:
+ LDAPDebug( SLAPI_RESLIMIT_TRACELEVEL,
+ "<= %s returning status=%d, handle=%d\n", fnname, rc,
+ (handlep == NULL) ? -1 : *handlep );
+
+ return( rc );
+}
+
+
+/*
+ * Retrieve the integer limit associated with connection `conn' for
+ * the resource identified by `handle'.
+ *
+ * A SLAPI_RESLIMIT_STATUS_... code is returned:
+ *
+ * SLAPI_RESLIMIT_STATUS_SUCCESS -- `*limitp' is set to the limit value.
+ * SLAPI_RESLIMIT_STATUS_NOVALUE -- no limit value is available (use default).
+ * Another SLAPI_RESLIMIT_STATUS_... code -- some more fatal error occurred.
+ *
+ * If `conn' is NULL, SLAPI_RESLIMIT_STATUS_NOVALUE is returned.
+ */
+int
+slapi_reslimit_get_integer_limit( Slapi_Connection *conn, int handle,
+ int *limitp )
+{
+ char *fnname = "slapi_reslimit_get_integer_limit()";
+ int rc;
+ SLAPIResLimitConnData *rlcdp;
+
+ LDAPDebug( SLAPI_RESLIMIT_TRACELEVEL, "=> %s conn=0x%x, handle=%d\n",
+ fnname, conn, handle );
+
+ rc = SLAPI_RESLIMIT_STATUS_SUCCESS; /* optimistic */
+
+ /* sanity check parameters */
+ if ( limitp == NULL ) {
+ slapi_log_error( SLAPI_LOG_FATAL, SLAPI_RESLIMIT_MODULE,
+ "%s: parameter error\n", fnname );
+ rc = SLAPI_RESLIMIT_STATUS_PARAM_ERROR;
+ goto log_and_return;
+ }
+
+ if ( conn == NULL ) {
+ rc = SLAPI_RESLIMIT_STATUS_NOVALUE;
+ goto log_and_return;
+ }
+
+ if (( rc = reslimit_get_ext( conn, fnname, &rlcdp )) !=
+ SLAPI_RESLIMIT_STATUS_SUCCESS ) {
+ goto log_and_return;
+ }
+ if(rlcdp->rlcd_integer_count==0) { /* peek at it to avoid lock */
+ rc = SLAPI_RESLIMIT_STATUS_NOVALUE;
+ } else {
+ PR_RWLock_Rlock( rlcdp->rlcd_rwlock );
+ if(rlcdp->rlcd_integer_count==0) {
+ rc = SLAPI_RESLIMIT_STATUS_NOVALUE;
+ } else if ( handle < 0 || handle >= rlcdp->rlcd_integer_count ) {
+ slapi_log_error( SLAPI_LOG_FATAL, SLAPI_RESLIMIT_MODULE,
+ "%s: unknown handle %d\n", fnname, handle );
+ rc = SLAPI_RESLIMIT_STATUS_UNKNOWN_HANDLE;
+ } else if ( rlcdp->rlcd_integer_available[ handle ] ) {
+ *limitp = rlcdp->rlcd_integer_value[ handle ];
+ } else {
+ rc = SLAPI_RESLIMIT_STATUS_NOVALUE;
+ }
+ PR_RWLock_Unlock( rlcdp->rlcd_rwlock );
+ }
+
+
+log_and_return:
+ if ( LDAPDebugLevelIsSet( LDAP_DEBUG_TRACE )) {
+ if ( rc == SLAPI_RESLIMIT_STATUS_SUCCESS ) {
+ LDAPDebug( SLAPI_RESLIMIT_TRACELEVEL,
+ "<= %s returning SUCCESS, value=%d\n", fnname, *limitp, 0 );
+ } else if ( rc == SLAPI_RESLIMIT_STATUS_NOVALUE ) {
+ LDAPDebug( SLAPI_RESLIMIT_TRACELEVEL, "<= %s returning NO VALUE\n",
+ fnname, 0, 0 );
+ } else {
+ LDAPDebug( SLAPI_RESLIMIT_TRACELEVEL, "<= %s returning ERROR %d\n",
+ fnname, rc, 0 );
+ }
+ }
+
+ return( rc );
+}
+/**** Public functions END ***********************************************/