1
/** BEGIN COPYRIGHT BLOCK
2
* This Program is free software; you can redistribute it and/or modify it under
3
* the terms of the GNU General Public License as published by the Free Software
4
* Foundation; version 2 of the License.
6
* This Program is distributed in the hope that it will be useful, but WITHOUT
7
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
8
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
10
* You should have received a copy of the GNU General Public License along with
11
* this Program; if not, write to the Free Software Foundation, Inc., 59 Temple
12
* Place, Suite 330, Boston, MA 02111-1307 USA.
14
* In addition, as a special exception, Red Hat, Inc. gives You the additional
15
* right to link the code of this Program with code not covered under the GNU
16
* General Public License ("Non-GPL Code") and to distribute linked combinations
17
* including the two, subject to the limitations in this paragraph. Non-GPL Code
18
* permitted under this exception must only link to the code of this Program
19
* through those well defined interfaces identified in the file named EXCEPTION
20
* found in the source code files (the "Approved Interfaces"). The files of
21
* Non-GPL Code may instantiate templates or use macros or inline functions from
22
* the Approved Interfaces without causing the resulting work to be covered by
23
* the GNU General Public License. Only Red Hat, Inc. may make changes or
24
* additions to the list of Approved Interfaces. You must obey the GNU General
25
* Public License in all respects for all of the Program code and other code used
26
* in conjunction with the Program except the Non-GPL Code covered by this
27
* exception. If you modify this file, you may extend this exception to your
28
* version of the file, but you are not obligated to do so. If you do not wish to
29
* provide this exception without modification, you must delete this exception
30
* statement from your version and license this file solely under the GPL without
34
* Copyright (C) 2001 Sun Microsystems, Inc. Used by permission.
35
* Copyright (C) 2005 Red Hat, Inc.
36
* All rights reserved.
37
* END COPYRIGHT BLOCK **/
43
/* resourcelimit.c - binder-based resource limits implementation */
47
* Implementation notes:
49
* At present this code only provides support for integer-based
52
* When a successful bind occurs (i.e., when bind_credentials_set() is
53
* called), reslimit_update_from_dn() or reslimit_update_from_entry()
54
* must be called. These functions look in the binder entry and pull
55
* out attribute values that correspond to resource limits. Typically
56
* operational attributes are used, e.g., nsSizeLimit to hold a
57
* binder-specific search size limit. The attributes should be single
58
* valued; if not, this code ignores all but the first value it finds.
59
* The virtual attribute interface is used to retrieve the binder entry
60
* values, so they can be based on COS, etc.
62
* Any resource limits found in the binder entry are cached in the
63
* connection structure. A connection object extension is used for this
64
* purpose. This means that if the attributes that correspond to binder
65
* entry are changed the resource limit won't be affected until the next
66
* bind occurs as that entry. The data in the connection extension is
67
* protected using a single writer/multiple reader locking scheme.
69
* A plugin or server subsystem that wants to use the resource limit
70
* subsystem should call slapi_reslimit_register() once for each limit it
71
* wants tracked. Note that slapi_reslimit_register() should be called
72
* early, i.e., before any client connections are accepted.
73
* slapi_reslimit_register() gives back an integer handle that is used
74
* later to refer to the limit in question. Here's a sample call:
76
#if SLAPI_RESLIMIT_SAMPLE_CODE
77
static int sizelimit_reslimit_handle = -1;
79
if ( slapi_reslimit_register( SLAPI_RESLIMIT_TYPE_INT, "nsSizeLimit",
80
&sizelimit_reslimit_handle ) != SLAPI_RESLIMIT_STATUS_SUCCESS ) {
81
/* limit could not be registered -- fatal error? */
87
* A successful call to slapi_reslimit_register() results in a new
88
* entry in the reslimit_map, which is private to this source file.
89
* The map data structure is protected using a single writer/multiple
90
* reader locking scheme.
92
* To retrieve a binder-based limit, simple call
93
* slapi_reslimit_get_integer_limit(). If a value was present in the
94
* binder entry, it will be given back to the caller and
95
* SLAPI_RESLIMIT_STATUS_SUCCESS will be returned. If no value was
96
* present or the connection is NULL, SLAPI_RESLIMIT_STATUS_NOVALUE is
97
* returned. Other errors may be returned also. Here's a sample call:
99
#if SLAPI_RESLIMIT_SAMPLE_CODE
102
rc = slapi_reslimit_get_integer_limit( conn, sizelimit_reslimit_handle,
106
case SLAPI_RESLIMIT_STATUS_SUCCESS: /* got a value */
108
case SLAPI_RESLIMIT_STATUS_NOVALUE: /* no limit value available */
109
sizelimit = 500; /* use a default value */
111
default: /* some other error occurred */
112
sizelimit = 500; /* use a default value */
117
* The function reslimit_cleanup() is called from main() to dispose of
118
* memory, locks, etc. so tools like Purify() don't report leaks at exit.
120
/* End of implementation notes */
128
#define SLAPI_RESLIMIT_MODULE "binder-based resource limits"
129
/* #define SLAPI_RESLIMIT_DEBUG */ /* define this to enable extra logging */
130
/* also forces trace log messages to */
131
/* always be logged */
133
#ifdef SLAPI_RESLIMIT_DEBUG
134
#define SLAPI_RESLIMIT_TRACELEVEL LDAP_DEBUG_ANY
135
#else /* SLAPI_RESLIMIT_DEBUG */
136
#define SLAPI_RESLIMIT_TRACELEVEL LDAP_DEBUG_TRACE
137
#endif /* SLAPI_RESLIMIT_DEBUG */
141
* Structures and types.
143
/* Per-connection resource limits data */
144
typedef struct slapi_reslimit_conndata {
145
Slapi_RWLock *rlcd_rwlock; /* to serialize access to the rest */
146
int rlcd_integer_count; /* size of rlcd_integer_limit array */
147
PRBool *rlcd_integer_available; /* array that says whether each */
148
/* value is available */
149
int *rlcd_integer_value; /* array that holds limit values */
150
} SLAPIResLimitConnData;
152
/* Mapping between attribute and limit */
153
typedef struct slapi_reslimit_map {
154
int rlmap_type; /* always SLAPI_RESLIMIT_TYPE_INT for now */
155
char *rlmap_at; /* attribute type name */
160
* Static variables (module globals).
162
static int reslimit_inited = 0;
163
static int reslimit_connext_objtype = 0;
164
static int reslimit_connext_handle = 0;
165
static struct slapi_reslimit_map *reslimit_map = NULL;
166
static int reslimit_map_count = 0;
167
static struct slapi_componentid *reslimit_componentid=NULL;
170
* reslimit_map_rwlock is used to serialize access to
171
* reslimit_map and reslimit_map_count
173
static Slapi_RWLock *reslimit_map_rwlock = NULL;
178
static int reslimit_init( void );
179
static void *reslimit_connext_constructor( void *object, void *parent );
180
static void reslimit_connext_destructor( void *extension, void *object,
182
static int reslimit_get_ext( Slapi_Connection *conn, const char *logname,
183
SLAPIResLimitConnData **rlcdpp );
184
static char ** reslimit_get_registered_attributes();
188
* reslimit_init() must be called before any resource related work
189
* is done. It is safe to call this more than once, but reslimit_inited
190
* can be tested to avoid a call.
192
* Returns zero if all goes well and non-zero if not.
195
reslimit_init( void )
197
if ( reslimit_inited == 0 ) {
198
if ( slapi_register_object_extension( SLAPI_RESLIMIT_MODULE,
199
SLAPI_EXT_CONNECTION, reslimit_connext_constructor,
200
reslimit_connext_destructor,
201
&reslimit_connext_objtype, &reslimit_connext_handle )
203
slapi_log_error( SLAPI_LOG_FATAL, SLAPI_RESLIMIT_MODULE,
204
"reslimit_init: slapi_register_object_extension()"
209
if (( reslimit_map_rwlock = slapi_new_rwlock()) == NULL ) {
210
slapi_log_error( SLAPI_LOG_FATAL, SLAPI_RESLIMIT_MODULE,
211
"reslimit_init: slapi_new_rwlock() failed\n" );
218
reslimit_componentid=generate_componentid(NULL,COMPONENT_RESLIMIT);
227
* Dispose of any allocated memory, locks, other resources. Called when
228
* server is shutting down.
231
reslimit_cleanup( void )
235
if ( reslimit_map != NULL ) {
236
for ( i = 0; i < reslimit_map_count; ++i ) {
237
if ( reslimit_map[ i ].rlmap_at != NULL ) {
238
slapi_ch_free( (void **)&reslimit_map[ i ].rlmap_at );
241
slapi_ch_free( (void **)&reslimit_map );
244
if ( reslimit_map_rwlock != NULL ) {
245
slapi_destroy_rwlock( reslimit_map_rwlock );
248
if ( reslimit_componentid != NULL ) {
249
release_componentid( reslimit_componentid );
255
* constructor for the connection object extension.
258
reslimit_connext_constructor( void *object, void *parent )
260
SLAPIResLimitConnData *rlcdp;
261
Slapi_RWLock *rwlock;
263
if (( rwlock = slapi_new_rwlock()) == NULL ) {
264
slapi_log_error( SLAPI_LOG_FATAL, SLAPI_RESLIMIT_MODULE,
265
"reslimit_connext_constructor: slapi_new_rwlock() failed\n" );
269
rlcdp = (SLAPIResLimitConnData *)slapi_ch_calloc( 1,
270
sizeof( SLAPIResLimitConnData ));
271
rlcdp->rlcd_rwlock = rwlock;
278
* destructor for the connection object extension.
281
reslimit_connext_destructor( void *extension, void *object, void *parent )
283
SLAPIResLimitConnData *rlcdp = (SLAPIResLimitConnData *)extension;
285
if ( rlcdp->rlcd_integer_available != NULL ) {
286
slapi_ch_free( (void **)&rlcdp->rlcd_integer_available );
288
if ( rlcdp->rlcd_integer_value != NULL ) {
289
slapi_ch_free( (void **)&rlcdp->rlcd_integer_value );
291
slapi_destroy_rwlock( rlcdp->rlcd_rwlock );
292
slapi_ch_free( (void **)&rlcdp );
297
* utility function to retrieve the connection object extension.
299
* if logname is non-NULL, errors are logged.
302
reslimit_get_ext( Slapi_Connection *conn, const char *logname,
303
SLAPIResLimitConnData **rlcdpp )
305
if ( !reslimit_inited && reslimit_init() != 0 ) {
306
if ( NULL != logname ) {
307
slapi_log_error( SLAPI_LOG_FATAL, SLAPI_RESLIMIT_MODULE,
308
"%s: reslimit_init() failed\n", logname );
310
return( SLAPI_RESLIMIT_STATUS_INIT_FAILURE );
313
if (( *rlcdpp = (SLAPIResLimitConnData *)slapi_get_object_extension(
314
reslimit_connext_objtype, conn,
315
reslimit_connext_handle )) == NULL ) {
316
if ( NULL != logname ) {
317
slapi_log_error( SLAPI_LOG_FATAL, SLAPI_RESLIMIT_MODULE,
318
"%s: slapi_get_object_extension() returned NULL\n", logname );
320
return( SLAPI_RESLIMIT_STATUS_INTERNAL_ERROR );
323
return( SLAPI_RESLIMIT_STATUS_SUCCESS );
327
/**** Semi-public functions start here ***********************************/
329
* These functions are exposed to other parts of the server only, i.e.,
330
* they are NOT part of the official SLAPI API.
334
* Set the resource limits associated with connection `conn' based on the
335
* entry named by `dn'. If `dn' is NULL, limits are returned to their
338
* A SLAPI_RESLIMIT_STATUS_... code is returned.
341
reslimit_update_from_dn( Slapi_Connection *conn, Slapi_DN *dn )
349
char ** attrs = reslimit_get_registered_attributes();
350
(void) slapi_search_internal_get_entry( dn, attrs, &e , reslimit_componentid);
354
rc = reslimit_update_from_entry( conn, e );
357
slapi_entry_free( e );
365
* Set the resource limits associated with connection `conn' based on the
366
* entry `e'. If `e' is NULL, limits are returned to their default state.
367
* If `conn' is NULL, nothing is done.
369
* A SLAPI_RESLIMIT_STATUS_... code is returned.
372
reslimit_update_from_entry( Slapi_Connection *conn, Slapi_Entry *e )
374
char *fnname = "reslimit_update_from_entry()";
375
char *actual_type_name, *get_ext_logname;
376
int i, rc, type_name_disposition, free_flags;
377
SLAPIResLimitConnData *rlcdp;
380
LDAPDebug( SLAPI_RESLIMIT_TRACELEVEL, "=> %s conn=0x%x, entry=0x%x\n",
383
rc = SLAPI_RESLIMIT_STATUS_SUCCESS; /* optimistic */
385
/* if conn is NULL, there is nothing to be done */
386
if ( conn == NULL ) {
392
get_ext_logname = NULL; /* do not log errors if resetting limits */
394
get_ext_logname = fnname;
396
if (( rc = reslimit_get_ext( conn, get_ext_logname, &rlcdp )) !=
397
SLAPI_RESLIMIT_STATUS_SUCCESS ) {
401
/* LOCK FOR READ -- map lock */
402
slapi_rwlock_rdlock( reslimit_map_rwlock );
403
/* LOCK FOR WRITE -- conn. data lock */
404
slapi_rwlock_wrlock( rlcdp->rlcd_rwlock );
406
if ( rlcdp->rlcd_integer_value == NULL ) {
407
rlcdp->rlcd_integer_count = reslimit_map_count;
408
if ( rlcdp->rlcd_integer_count > 0 ) {
409
rlcdp->rlcd_integer_available = (PRBool *)slapi_ch_calloc(
410
rlcdp->rlcd_integer_count, sizeof( PRBool ));
411
rlcdp->rlcd_integer_value = (int *)slapi_ch_calloc(
412
rlcdp->rlcd_integer_count, sizeof( int ));
416
for ( i = 0; i < rlcdp->rlcd_integer_count; ++i ) {
417
if ( reslimit_map[ i ].rlmap_type != SLAPI_RESLIMIT_TYPE_INT ) {
421
LDAPDebug( SLAPI_RESLIMIT_TRACELEVEL,
422
"%s: setting limit for handle %d (based on %s)\n",
423
fnname, i, reslimit_map[ i ].rlmap_at );
425
rlcdp->rlcd_integer_available[ i ] = PR_FALSE;
427
if ( NULL != e && 0 == slapi_vattr_values_get( e,
428
reslimit_map[ i ].rlmap_at, &vs, &type_name_disposition,
429
&actual_type_name, 0, &free_flags )) {
433
if ((( index = slapi_valueset_first_value( vs, &v )) != -1) &&
435
rlcdp->rlcd_integer_value[ i ] = slapi_value_get_int( v );
436
rlcdp->rlcd_integer_available[ i ] = PR_TRUE;
438
LDAPDebug( SLAPI_RESLIMIT_TRACELEVEL,
439
"%s: set limit based on %s to %d\n",
440
fnname, reslimit_map[ i ].rlmap_at,
441
rlcdp->rlcd_integer_value[ i ] );
443
if ( slapi_valueset_next_value( vs, index, &v ) != -1 ) {
444
slapi_log_error( SLAPI_LOG_FATAL, SLAPI_RESLIMIT_MODULE,
445
"%s: ignoring multiple values for %s in entry %s\n",
446
fnname, reslimit_map[ i ].rlmap_at,
447
slapi_entry_get_dn_const( e ));
451
slapi_vattr_values_free( &vs, &actual_type_name, free_flags);
455
slapi_rwlock_unlock( rlcdp->rlcd_rwlock );
456
/* UNLOCKED -- conn. data lock */
457
slapi_rwlock_unlock( reslimit_map_rwlock );
458
/* UNLOCKED -- map lock */
461
LDAPDebug( SLAPI_RESLIMIT_TRACELEVEL, "<= %s returning status %d\n",
467
/* return the list of registered attributes */
469
static char ** reslimit_get_registered_attributes()
475
/* LOCK FOR READ -- map lock */
476
slapi_rwlock_rdlock( reslimit_map_rwlock );
478
for ( i = 0; i < reslimit_map_count; ++i ) {
479
if ( reslimit_map[ i ].rlmap_at != NULL ) {
480
charray_add(&attrs, slapi_ch_strdup(reslimit_map[ i ].rlmap_at));
484
slapi_rwlock_unlock( reslimit_map_rwlock );
490
/**** Public functions can be found below this point *********************/
492
* These functions are exposed to plugins, i.e., they are part of the
493
* official SLAPI API.
497
* Register a new resource to be tracked. `type' must be
498
* SLAPI_RESLIMIT_TYPE_INT and `attrname' is an LDAP attribute type that
499
* is consulted in the bound entry to determine the limit's value.
501
* A SLAPI_RESLIMIT_STATUS_... code is returned. If it is ...SUCCESS, then
502
* `*handlep' is set to an opaque integer value that should be used in
503
* subsequent calls to slapi_reslimit_get_integer_limit().
506
slapi_reslimit_register( int type, const char *attrname, int *handlep )
508
char *fnname = "slapi_reslimit_register()";
511
LDAPDebug( SLAPI_RESLIMIT_TRACELEVEL, "=> %s attrname=%s\n",
512
fnname, attrname, 0 );
514
rc = SLAPI_RESLIMIT_STATUS_SUCCESS; /* optimistic */
516
/* initialize if necessary */
517
if ( !reslimit_inited && reslimit_init() != 0 ) {
518
slapi_log_error( SLAPI_LOG_FATAL, SLAPI_RESLIMIT_MODULE,
519
"%s: reslimit_init() failed\n", fnname );
520
rc = SLAPI_RESLIMIT_STATUS_INIT_FAILURE;
524
/* sanity check parameters */
525
if ( type != SLAPI_RESLIMIT_TYPE_INT || attrname == NULL
526
|| handlep == NULL ) {
527
slapi_log_error( SLAPI_LOG_FATAL, SLAPI_RESLIMIT_MODULE,
528
"%s: parameter error\n", fnname );
529
rc = SLAPI_RESLIMIT_STATUS_PARAM_ERROR;
533
/* LOCK FOR WRITE -- map lock */
534
slapi_rwlock_wrlock( reslimit_map_rwlock );
537
* check that attrname is not already registered
539
for ( i = 0; i < reslimit_map_count; ++i ) {
540
if ( 0 == slapi_attr_type_cmp( reslimit_map[ i ].rlmap_at,
541
attrname, SLAPI_TYPE_CMP_EXACT )) {
542
slapi_log_error( SLAPI_LOG_FATAL, SLAPI_RESLIMIT_MODULE,
543
"%s: parameter error (%s already registered)\n",
545
rc = SLAPI_RESLIMIT_STATUS_PARAM_ERROR;
546
goto unlock_and_return;
551
* expand the map array and add the new element
553
reslimit_map = (SLAPIResLimitMap *)slapi_ch_realloc(
554
(char *)reslimit_map,
555
(1 + reslimit_map_count) * sizeof( SLAPIResLimitMap ));
556
reslimit_map[ reslimit_map_count ].rlmap_type = type;
557
reslimit_map[ reslimit_map_count ].rlmap_at
558
= slapi_ch_strdup( attrname );
559
*handlep = reslimit_map_count;
560
++reslimit_map_count;
563
slapi_rwlock_unlock( reslimit_map_rwlock );
564
/* UNLOCKED -- map lock */
567
LDAPDebug( SLAPI_RESLIMIT_TRACELEVEL,
568
"<= %s returning status=%d, handle=%d\n", fnname, rc,
569
(handlep == NULL) ? -1 : *handlep );
576
* Retrieve the integer limit associated with connection `conn' for
577
* the resource identified by `handle'.
579
* A SLAPI_RESLIMIT_STATUS_... code is returned:
581
* SLAPI_RESLIMIT_STATUS_SUCCESS -- `*limitp' is set to the limit value.
582
* SLAPI_RESLIMIT_STATUS_NOVALUE -- no limit value is available (use default).
583
* Another SLAPI_RESLIMIT_STATUS_... code -- some more fatal error occurred.
585
* If `conn' is NULL, SLAPI_RESLIMIT_STATUS_NOVALUE is returned.
588
slapi_reslimit_get_integer_limit( Slapi_Connection *conn, int handle,
591
char *fnname = "slapi_reslimit_get_integer_limit()";
593
SLAPIResLimitConnData *rlcdp;
595
LDAPDebug( SLAPI_RESLIMIT_TRACELEVEL, "=> %s conn=0x%x, handle=%d\n",
596
fnname, conn, handle );
598
rc = SLAPI_RESLIMIT_STATUS_SUCCESS; /* optimistic */
600
/* sanity check parameters */
601
if ( limitp == NULL ) {
602
slapi_log_error( SLAPI_LOG_FATAL, SLAPI_RESLIMIT_MODULE,
603
"%s: parameter error\n", fnname );
604
rc = SLAPI_RESLIMIT_STATUS_PARAM_ERROR;
608
if ( conn == NULL ) {
609
rc = SLAPI_RESLIMIT_STATUS_NOVALUE;
613
if (( rc = reslimit_get_ext( conn, fnname, &rlcdp )) !=
614
SLAPI_RESLIMIT_STATUS_SUCCESS ) {
617
if(rlcdp->rlcd_integer_count==0) { /* peek at it to avoid lock */
618
rc = SLAPI_RESLIMIT_STATUS_NOVALUE;
620
slapi_rwlock_rdlock( rlcdp->rlcd_rwlock );
621
if(rlcdp->rlcd_integer_count==0) {
622
rc = SLAPI_RESLIMIT_STATUS_NOVALUE;
623
} else if ( handle < 0 || handle >= rlcdp->rlcd_integer_count ) {
624
slapi_log_error( SLAPI_LOG_FATAL, SLAPI_RESLIMIT_MODULE,
625
"%s: unknown handle %d\n", fnname, handle );
626
rc = SLAPI_RESLIMIT_STATUS_UNKNOWN_HANDLE;
627
} else if ( rlcdp->rlcd_integer_available[ handle ] ) {
628
*limitp = rlcdp->rlcd_integer_value[ handle ];
630
rc = SLAPI_RESLIMIT_STATUS_NOVALUE;
632
slapi_rwlock_unlock( rlcdp->rlcd_rwlock );
637
if ( LDAPDebugLevelIsSet( LDAP_DEBUG_TRACE )) {
638
if ( rc == SLAPI_RESLIMIT_STATUS_SUCCESS ) {
639
LDAPDebug( SLAPI_RESLIMIT_TRACELEVEL,
640
"<= %s returning SUCCESS, value=%d\n", fnname, *limitp, 0 );
641
} else if ( rc == SLAPI_RESLIMIT_STATUS_NOVALUE ) {
642
LDAPDebug( SLAPI_RESLIMIT_TRACELEVEL, "<= %s returning NO VALUE\n",
645
LDAPDebug( SLAPI_RESLIMIT_TRACELEVEL, "<= %s returning ERROR %d\n",
652
/**** Public functions END ***********************************************/