Home | History | Annotate | Line # | Download | only in progs
slapd-mtread.c revision 1.1.1.1
      1 /*	$NetBSD: slapd-mtread.c,v 1.1.1.1 2014/05/28 09:58:54 tron Exp $	*/
      2 
      3 /* $OpenLDAP$ */
      4 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
      5  *
      6  * Copyright 1999-2014 The OpenLDAP Foundation.
      7  * All rights reserved.
      8  *
      9  * Redistribution and use in source and binary forms, with or without
     10  * modification, are permitted only as authorized by the OpenLDAP
     11  * Public License.
     12  *
     13  * A copy of this license is available in file LICENSE in the
     14  * top-level directory of the distribution or, alternatively, at
     15  * <http://www.OpenLDAP.org/license.html>.
     16  */
     17 /* ACKNOWLEDGEMENTS:
     18  * This work was initially developed by Kurt Spanier for inclusion
     19  * in OpenLDAP Software.
     20  */
     21 
     22 /*
     23  * This tool is a MT reader.  It behaves like slapd-read however
     24  * with one or more threads simultaneously using the same connection.
     25  * If -M is enabled, then M threads will also perform write operations.
     26  */
     27 
     28 #include "portable.h"
     29 
     30 #include <stdio.h>
     31 #include "ldap_pvt_thread.h"
     32 
     33 #include "ac/stdlib.h"
     34 
     35 #include "ac/ctype.h"
     36 #include "ac/param.h"
     37 #include "ac/socket.h"
     38 #include "ac/string.h"
     39 #include "ac/unistd.h"
     40 #include "ac/wait.h"
     41 
     42 #include "ldap.h"
     43 #include "lutil.h"
     44 
     45 #include "ldap_pvt.h"
     46 
     47 #include "slapd-common.h"
     48 
     49 #define MAXCONN	512
     50 #define LOOPS	100
     51 #define RETRIES	0
     52 #define DEFAULT_BASE	"ou=people,dc=example,dc=com"
     53 
     54 static void
     55 do_conn( char *uri, char *manager, struct berval *passwd,
     56 	LDAP **ld, int nobind, int maxretries, int conn_num );
     57 
     58 static void
     59 do_read( LDAP *ld, char *entry,
     60 	char **attrs, int noattrs, int nobind, int maxloop,
     61 	int maxretries, int delay, int force, int chaserefs, int idx );
     62 
     63 static void
     64 do_random( LDAP *ld,
     65 	char *sbase, char *filter, char **attrs, int noattrs, int nobind,
     66 	int innerloop, int maxretries, int delay, int force, int chaserefs,
     67 	int idx );
     68 
     69 static void
     70 do_random2( LDAP *ld,
     71 	char *sbase, char *filter, char **attrs, int noattrs, int nobind,
     72 	int innerloop, int maxretries, int delay, int force, int chaserefs,
     73 	int idx );
     74 
     75 static void *
     76 do_onethread( void *arg );
     77 
     78 static void *
     79 do_onerwthread( void *arg );
     80 
     81 #define MAX_THREAD	1024
     82 /* Use same array for readers and writers, offset writers by MAX_THREAD */
     83 int	rt_pass[MAX_THREAD*2];
     84 int	rt_fail[MAX_THREAD*2];
     85 int	*rwt_pass = rt_pass + MAX_THREAD;
     86 int	*rwt_fail = rt_fail + MAX_THREAD;
     87 ldap_pvt_thread_t	rtid[MAX_THREAD*2], *rwtid = rtid + MAX_THREAD;
     88 
     89 /*
     90  * Shared globals (command line args)
     91  */
     92 LDAP		*ld = NULL;
     93 char		*entry = NULL;
     94 char		*filter  = NULL;
     95 int		loops = LOOPS;
     96 int		outerloops = 1;
     97 int		retries = RETRIES;
     98 int		delay = 0;
     99 int		force = 0;
    100 int		chaserefs = 0;
    101 char		*srchattrs[] = { "1.1", NULL };
    102 char		**attrs = srchattrs;
    103 int		noattrs = 0;
    104 int		nobind = 0;
    105 int		threads = 1;
    106 int		rwthreads = 0;
    107 int		verbose = 0;
    108 
    109 int		noconns = 1;
    110 LDAP		**lds = NULL;
    111 
    112 static void
    113 thread_error(int idx, char *string)
    114 {
    115 	char		thrstr[BUFSIZ];
    116 
    117 	snprintf(thrstr, BUFSIZ, "error on tidx: %d: %s", idx, string);
    118 	tester_error( thrstr );
    119 }
    120 
    121 static void
    122 thread_output(int idx, char *string)
    123 {
    124 	char		thrstr[BUFSIZ];
    125 
    126 	snprintf(thrstr, BUFSIZ, "tidx: %d says: %s", idx, string);
    127 	tester_error( thrstr );
    128 }
    129 
    130 static void
    131 thread_verbose(int idx, char *string)
    132 {
    133 	char		thrstr[BUFSIZ];
    134 
    135 	if (!verbose)
    136 		return;
    137 	snprintf(thrstr, BUFSIZ, "tidx: %d says: %s", idx, string);
    138 	tester_error( thrstr );
    139 }
    140 
    141 static void
    142 usage( char *name )
    143 {
    144         fprintf( stderr,
    145 		"usage: %s "
    146 		"-H <uri> | ([-h <host>] -p <port>) "
    147 		"-D <manager> "
    148 		"-w <passwd> "
    149 		"-e <entry> "
    150 		"[-A] "
    151 		"[-C] "
    152 		"[-F] "
    153 		"[-N] "
    154 		"[-v] "
    155 		"[-c connections] "
    156 		"[-f filter] "
    157 		"[-i <ignore>] "
    158 		"[-l <loops>] "
    159 		"[-L <outerloops>] "
    160 		"[-m threads] "
    161 		"[-M threads] "
    162 		"[-r <maxretries>] "
    163 		"[-t <delay>] "
    164 		"[-T <attrs>] "
    165 		"[<attrs>] "
    166 		"\n",
    167 		name );
    168 	exit( EXIT_FAILURE );
    169 }
    170 
    171 int
    172 main( int argc, char **argv )
    173 {
    174 	int		i;
    175 	char		*uri = NULL;
    176 	char		*host = "localhost";
    177 	int		port = -1;
    178 	char		*manager = NULL;
    179 	struct berval	passwd = { 0, NULL };
    180 	char		outstr[BUFSIZ];
    181 	int		ptpass;
    182 	int		testfail = 0;
    183 
    184 
    185 	tester_init( "slapd-mtread", TESTER_READ );
    186 
    187 	/* by default, tolerate referrals and no such object */
    188 	tester_ignore_str2errlist( "REFERRAL,NO_SUCH_OBJECT" );
    189 
    190 	while ( (i = getopt( argc, argv, "ACc:D:e:Ff:H:h:i:L:l:M:m:p:r:t:T:w:v" )) != EOF ) {
    191 		switch ( i ) {
    192 		case 'A':
    193 			noattrs++;
    194 			break;
    195 
    196 		case 'C':
    197 			chaserefs++;
    198 			break;
    199 
    200 		case 'H':		/* the server uri */
    201 			uri = strdup( optarg );
    202 			break;
    203 
    204 		case 'h':		/* the servers host */
    205 			host = strdup( optarg );
    206 
    207 		case 'i':
    208 			tester_ignore_str2errlist( optarg );
    209 			break;
    210 
    211 		case 'N':
    212 			nobind++;
    213 			break;
    214 
    215 		case 'v':
    216 			verbose++;
    217 			break;
    218 
    219 		case 'p':		/* the servers port */
    220 			if ( lutil_atoi( &port, optarg ) != 0 ) {
    221 				usage( argv[0] );
    222 			}
    223 			break;
    224 
    225 		case 'D':		/* the servers manager */
    226 			manager = strdup( optarg );
    227 			break;
    228 
    229 		case 'w':		/* the server managers password */
    230 			passwd.bv_val = strdup( optarg );
    231 			passwd.bv_len = strlen( optarg );
    232 			memset( optarg, '*', passwd.bv_len );
    233 			break;
    234 
    235 		case 'c':		/* the number of connections */
    236 			if ( lutil_atoi( &noconns, optarg ) != 0 ) {
    237 				usage( argv[0] );
    238 			}
    239 			break;
    240 
    241 		case 'e':		/* DN to search for */
    242 			entry = strdup( optarg );
    243 			break;
    244 
    245 		case 'f':		/* the search request */
    246 			filter = strdup( optarg );
    247 			break;
    248 
    249 		case 'F':
    250 			force++;
    251 			break;
    252 
    253 		case 'l':		/* the number of loops */
    254 			if ( lutil_atoi( &loops, optarg ) != 0 ) {
    255 				usage( argv[0] );
    256 			}
    257 			break;
    258 
    259 		case 'L':		/* the number of outerloops */
    260 			if ( lutil_atoi( &outerloops, optarg ) != 0 ) {
    261 				usage( argv[0] );
    262 			}
    263 			break;
    264 
    265 		case 'M':		/* the number of R/W threads */
    266 			if ( lutil_atoi( &rwthreads, optarg ) != 0 ) {
    267 				usage( argv[0] );
    268 			}
    269 			if (rwthreads > MAX_THREAD)
    270 				rwthreads = MAX_THREAD;
    271 			break;
    272 
    273 		case 'm':		/* the number of threads */
    274 			if ( lutil_atoi( &threads, optarg ) != 0 ) {
    275 				usage( argv[0] );
    276 			}
    277 			if (threads > MAX_THREAD)
    278 				threads = MAX_THREAD;
    279 			break;
    280 
    281 		case 'r':		/* the number of retries */
    282 			if ( lutil_atoi( &retries, optarg ) != 0 ) {
    283 				usage( argv[0] );
    284 			}
    285 			break;
    286 
    287 		case 't':		/* delay in seconds */
    288 			if ( lutil_atoi( &delay, optarg ) != 0 ) {
    289 				usage( argv[0] );
    290 			}
    291 			break;
    292 
    293 		case 'T':
    294 			attrs = ldap_str2charray( optarg, "," );
    295 			if ( attrs == NULL ) {
    296 				usage( argv[0] );
    297 			}
    298 			break;
    299 
    300 		default:
    301 			usage( argv[0] );
    302 			break;
    303 		}
    304 	}
    305 
    306 	if (( entry == NULL ) || ( port == -1 && uri == NULL ))
    307 		usage( argv[0] );
    308 
    309 	if ( *entry == '\0' ) {
    310 		fprintf( stderr, "%s: invalid EMPTY entry DN.\n",
    311 				argv[0] );
    312 		exit( EXIT_FAILURE );
    313 	}
    314 
    315 	if ( argv[optind] != NULL ) {
    316 		attrs = &argv[optind];
    317 	}
    318 
    319 	if (noconns < 1)
    320 		noconns = 1;
    321 	if (noconns > MAXCONN)
    322 		noconns = MAXCONN;
    323 	lds = (LDAP **) calloc( sizeof(LDAP *), noconns);
    324 	if (lds == NULL) {
    325 		fprintf( stderr, "%s: Memory error: calloc noconns.\n",
    326 				argv[0] );
    327 		exit( EXIT_FAILURE );
    328 	}
    329 
    330 	uri = tester_uri( uri, host, port );
    331 	/* One connection and one connection only */
    332 	do_conn( uri, manager, &passwd, &ld, nobind, retries, 0 );
    333 	lds[0] = ld;
    334 	for(i = 1; i < noconns; i++) {
    335 		do_conn( uri, manager, &passwd, &lds[i], nobind, retries, i );
    336 	}
    337 
    338 	ldap_pvt_thread_initialize();
    339 
    340 	snprintf(outstr, BUFSIZ, "MT Test Start: conns: %d (%s)", noconns, uri);
    341 	tester_error(outstr);
    342 	snprintf(outstr, BUFSIZ, "Threads: RO: %d RW: %d", threads, rwthreads);
    343 	tester_error(outstr);
    344 
    345 	/* Set up read only threads */
    346 	for ( i = 0; i < threads; i++ ) {
    347 		ldap_pvt_thread_create( &rtid[i], 0, do_onethread, &rtid[i]);
    348 		snprintf(outstr, BUFSIZ, "Created RO thread %d", i);
    349 		thread_verbose(-1, outstr);
    350 	}
    351 	/* Set up read/write threads */
    352 	for ( i = 0; i < rwthreads; i++ ) {
    353 		ldap_pvt_thread_create( &rwtid[i], 0, do_onerwthread, &rwtid[i]);
    354 		snprintf(outstr, BUFSIZ, "Created RW thread %d", i + MAX_THREAD);
    355 		thread_verbose(-1, outstr);
    356 	}
    357 
    358 	ptpass =  outerloops * loops;
    359 
    360 	/* wait for read only threads to complete */
    361 	for ( i = 0; i < threads; i++ )
    362 		ldap_pvt_thread_join(rtid[i], NULL);
    363 	/* wait for read/write threads to complete */
    364 	for ( i = 0; i < rwthreads; i++ )
    365 		ldap_pvt_thread_join(rwtid[i], NULL);
    366 
    367 	for(i = 0; i < noconns; i++) {
    368 		if ( lds[i] != NULL ) {
    369 			ldap_unbind_ext( lds[i], NULL, NULL );
    370 		}
    371 	}
    372 	free( lds );
    373 
    374 	for ( i = 0; i < threads; i++ ) {
    375 		snprintf(outstr, BUFSIZ, "RO thread %d pass=%d fail=%d", i,
    376 			rt_pass[i], rt_fail[i]);
    377 		tester_error(outstr);
    378 		if (rt_fail[i] != 0 || rt_pass[i] != ptpass) {
    379 			snprintf(outstr, BUFSIZ, "FAIL RO thread %d", i);
    380 			tester_error(outstr);
    381 			testfail++;
    382 		}
    383 	}
    384 	for ( i = 0; i < rwthreads; i++ ) {
    385 		snprintf(outstr, BUFSIZ, "RW thread %d pass=%d fail=%d", i + MAX_THREAD,
    386 			rwt_pass[i], rwt_fail[i]);
    387 		tester_error(outstr);
    388 		if (rwt_fail[i] != 0 || rwt_pass[i] != ptpass) {
    389 			snprintf(outstr, BUFSIZ, "FAIL RW thread %d", i);
    390 			tester_error(outstr);
    391 			testfail++;
    392 		}
    393 	}
    394 	snprintf(outstr, BUFSIZ, "MT Test complete" );
    395 	tester_error(outstr);
    396 
    397 	if (testfail)
    398 		exit( EXIT_FAILURE );
    399 	exit( EXIT_SUCCESS );
    400 }
    401 
    402 static void *
    403 do_onethread( void *arg )
    404 {
    405 	int		i, j, thisconn;
    406 	LDAP		**mlds;
    407 	char		thrstr[BUFSIZ];
    408 	int		rc, refcnt = 0;
    409 	int		idx = (ldap_pvt_thread_t *)arg - rtid;
    410 
    411 	mlds = (LDAP **) calloc( sizeof(LDAP *), noconns);
    412 	if (mlds == NULL) {
    413 		thread_error( idx, "Memory error: thread calloc for noconns" );
    414 		exit( EXIT_FAILURE );
    415 	}
    416 
    417 	for ( j = 0; j < outerloops; j++ ) {
    418 		for(i = 0; i < noconns; i++) {
    419 			mlds[i] = ldap_dup(lds[i]);
    420 			if (mlds[i] == NULL) {
    421 				thread_error( idx, "ldap_dup error" );
    422 			}
    423 		}
    424 		rc = ldap_get_option(mlds[0], LDAP_OPT_SESSION_REFCNT, &refcnt);
    425 		snprintf(thrstr, BUFSIZ,
    426 			"RO Thread conns: %d refcnt: %d (rc = %d)",
    427 			noconns, refcnt, rc);
    428 		thread_verbose(idx, thrstr);
    429 
    430 		thisconn = (idx + j) % noconns;
    431 		if (thisconn < 0 || thisconn >= noconns)
    432 			thisconn = 0;
    433 		if (mlds[thisconn] == NULL) {
    434 			thread_error( idx, "(failed to dup)");
    435 			tester_perror( "ldap_dup", "(failed to dup)" );
    436 			exit( EXIT_FAILURE );
    437 		}
    438 		snprintf(thrstr, BUFSIZ, "Using conn %d", thisconn);
    439 		thread_verbose(idx, thrstr);
    440 		if ( filter != NULL ) {
    441 			if (strchr(filter, '['))
    442 				do_random2( mlds[thisconn], entry, filter, attrs,
    443 					noattrs, nobind, loops, retries, delay, force,
    444 					chaserefs, idx );
    445 			else
    446 				do_random( mlds[thisconn], entry, filter, attrs,
    447 					noattrs, nobind, loops, retries, delay, force,
    448 					chaserefs, idx );
    449 
    450 		} else {
    451 			do_read( mlds[thisconn], entry, attrs,
    452 				noattrs, nobind, loops, retries, delay, force,
    453 				chaserefs, idx );
    454 		}
    455 		for(i = 0; i < noconns; i++) {
    456 			(void) ldap_destroy(mlds[i]);
    457 			mlds[i] = NULL;
    458 		}
    459 	}
    460 	free( mlds );
    461 	return( NULL );
    462 }
    463 
    464 static void *
    465 do_onerwthread( void *arg )
    466 {
    467 	int		i, j, thisconn;
    468 	LDAP		**mlds, *ld;
    469 	char		thrstr[BUFSIZ];
    470 	char		dn[256], uids[32], cns[32], *base;
    471 	LDAPMod		*attrp[5], attrs[4];
    472 	char		*oc_vals[] = { "top", "OpenLDAPperson", NULL };
    473 	char		*cn_vals[] = { NULL, NULL };
    474 	char		*sn_vals[] = { NULL, NULL };
    475 	char		*uid_vals[] = { NULL, NULL };
    476 	int		ret;
    477 	int		adds = 0;
    478 	int		dels = 0;
    479 	int		rc, refcnt = 0;
    480 	int		idx = (ldap_pvt_thread_t *)arg - rtid;
    481 
    482 	mlds = (LDAP **) calloc( sizeof(LDAP *), noconns);
    483 	if (mlds == NULL) {
    484 		thread_error( idx, "Memory error: thread calloc for noconns" );
    485 		exit( EXIT_FAILURE );
    486 	}
    487 
    488 	snprintf(uids, sizeof(uids), "rwtest%04d", idx);
    489 	snprintf(cns, sizeof(cns), "rwtest%04d", idx);
    490 	/* add setup */
    491 	for (i = 0; i < 4; i++) {
    492 		attrp[i] = &attrs[i];
    493 		attrs[i].mod_op = 0;
    494 	}
    495 	attrp[4] = NULL;
    496 	attrs[0].mod_type = "objectClass";
    497 	attrs[0].mod_values = oc_vals;
    498 	attrs[1].mod_type = "cn";
    499 	attrs[1].mod_values = cn_vals;
    500 	cn_vals[0] = &cns[0];
    501 	attrs[2].mod_type = "sn";
    502 	attrs[2].mod_values = sn_vals;
    503 	sn_vals[0] = &cns[0];
    504 	attrs[3].mod_type = "uid";
    505 	attrs[3].mod_values = uid_vals;
    506 	uid_vals[0] = &uids[0];
    507 
    508 	for ( j = 0; j < outerloops; j++ ) {
    509 		for(i = 0; i < noconns; i++) {
    510 			mlds[i] = ldap_dup(lds[i]);
    511 			if (mlds[i] == NULL) {
    512 				thread_error( idx, "ldap_dup error" );
    513 			}
    514 		}
    515 		rc = ldap_get_option(mlds[0], LDAP_OPT_SESSION_REFCNT, &refcnt);
    516 		snprintf(thrstr, BUFSIZ,
    517 			"RW Thread conns: %d refcnt: %d (rc = %d)",
    518 			noconns, refcnt, rc);
    519 		thread_verbose(idx, thrstr);
    520 
    521 		thisconn = (idx + j) % noconns;
    522 		if (thisconn < 0 || thisconn >= noconns)
    523 			thisconn = 0;
    524 		if (mlds[thisconn] == NULL) {
    525 			thread_error( idx, "(failed to dup)");
    526 			tester_perror( "ldap_dup", "(failed to dup)" );
    527 			exit( EXIT_FAILURE );
    528 		}
    529 		snprintf(thrstr, BUFSIZ, "START RW Thread using conn %d", thisconn);
    530 		thread_verbose(idx, thrstr);
    531 
    532 		ld = mlds[thisconn];
    533 		if (entry != NULL)
    534 			base = entry;
    535 		else
    536 			base = DEFAULT_BASE;
    537 		snprintf(dn, 256, "cn=%s,%s", cns, base);
    538 
    539 		adds = 0;
    540 		dels = 0;
    541 		for (i = 0; i < loops; i++) {
    542 			ret = ldap_add_ext_s(ld, dn, &attrp[0], NULL, NULL);
    543 			if (ret == LDAP_SUCCESS) {
    544 				adds++;
    545 				ret = ldap_delete_ext_s(ld, dn, NULL, NULL);
    546 				if (ret == LDAP_SUCCESS) {
    547 					dels++;
    548 					rt_pass[idx]++;
    549 				} else {
    550 					thread_output(idx, ldap_err2string(ret));
    551 					rt_fail[idx]++;
    552 				}
    553 			} else {
    554 				thread_output(idx, ldap_err2string(ret));
    555 				rt_fail[idx]++;
    556 			}
    557 		}
    558 
    559 		snprintf(thrstr, BUFSIZ,
    560 			"INNER STOP RW Thread using conn %d (%d/%d)",
    561 			thisconn, adds, dels);
    562 		thread_verbose(idx, thrstr);
    563 
    564 		for(i = 0; i < noconns; i++) {
    565 			(void) ldap_destroy(mlds[i]);
    566 			mlds[i] = NULL;
    567 		}
    568 	}
    569 
    570 	free( mlds );
    571 	return( NULL );
    572 }
    573 
    574 static void
    575 do_conn( char *uri, char *manager, struct berval *passwd,
    576 	LDAP **ldp, int nobind, int maxretries, int conn_num )
    577 {
    578 	LDAP	*ld = NULL;
    579 	int	version = LDAP_VERSION3;
    580 	int  	i = 0, do_retry = maxretries;
    581 	int     rc = LDAP_SUCCESS;
    582 	char	thrstr[BUFSIZ];
    583 
    584 retry:;
    585 	ldap_initialize( &ld, uri );
    586 	if ( ld == NULL ) {
    587 		snprintf( thrstr, BUFSIZ, "connection: %d", conn_num );
    588 		tester_error( thrstr );
    589 		tester_perror( "ldap_initialize", NULL );
    590 		exit( EXIT_FAILURE );
    591 	}
    592 
    593 	(void) ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &version );
    594 	(void) ldap_set_option( ld, LDAP_OPT_REFERRALS,
    595 		chaserefs ? LDAP_OPT_ON : LDAP_OPT_OFF );
    596 
    597 	if ( do_retry == maxretries ) {
    598 		snprintf( thrstr, BUFSIZ, "do_conn #%d\n", conn_num );
    599 		thread_verbose( -1, thrstr );
    600 	}
    601 
    602 	if ( nobind == 0 ) {
    603 		rc = ldap_sasl_bind_s( ld, manager, LDAP_SASL_SIMPLE, passwd, NULL, NULL, NULL );
    604 		if ( rc != LDAP_SUCCESS ) {
    605 			snprintf( thrstr, BUFSIZ, "connection: %d", conn_num );
    606 			tester_error( thrstr );
    607 			tester_ldap_error( ld, "ldap_sasl_bind_s", NULL );
    608 			switch ( rc ) {
    609 			case LDAP_BUSY:
    610 			case LDAP_UNAVAILABLE:
    611 				if ( do_retry > 0 ) {
    612 					ldap_unbind_ext( ld, NULL, NULL );
    613 					ld = NULL;
    614 					do_retry--;
    615 					if ( delay != 0 ) {
    616 					    sleep( delay );
    617 					}
    618 					goto retry;
    619 				}
    620 			/* fallthru */
    621 			default:
    622 				break;
    623 			}
    624 			exit( EXIT_FAILURE );
    625 		}
    626 	}
    627 	*ldp = ld;
    628 }
    629 
    630 static void
    631 do_random( LDAP *ld,
    632 	char *sbase, char *filter, char **srchattrs, int noattrs, int nobind,
    633 	int innerloop, int maxretries, int delay, int force, int chaserefs,
    634 	int idx )
    635 {
    636 	int  	i = 0, do_retry = maxretries;
    637 	char	*attrs[ 2 ];
    638 	int     rc = LDAP_SUCCESS;
    639 	int	nvalues = 0;
    640 	char	**values = NULL;
    641 	LDAPMessage *res = NULL, *e = NULL;
    642 	char	thrstr[BUFSIZ];
    643 
    644 	attrs[ 0 ] = LDAP_NO_ATTRS;
    645 	attrs[ 1 ] = NULL;
    646 
    647 	snprintf( thrstr, BUFSIZ,
    648 			"Read(%d): base=\"%s\", filter=\"%s\".\n",
    649 			innerloop, sbase, filter );
    650 	thread_verbose( idx, thrstr );
    651 
    652 	rc = ldap_search_ext_s( ld, sbase, LDAP_SCOPE_SUBTREE,
    653 		filter, attrs, 0, NULL, NULL, NULL, LDAP_NO_LIMIT, &res );
    654 	switch ( rc ) {
    655 	case LDAP_SIZELIMIT_EXCEEDED:
    656 	case LDAP_TIMELIMIT_EXCEEDED:
    657 	case LDAP_SUCCESS:
    658 		nvalues = ldap_count_entries( ld, res );
    659 		if ( nvalues == 0 ) {
    660 			if ( rc ) {
    661 				tester_ldap_error( ld, "ldap_search_ext_s", NULL );
    662 			}
    663 			break;
    664 		}
    665 
    666 		values = malloc( ( nvalues + 1 ) * sizeof( char * ) );
    667 		for ( i = 0, e = ldap_first_entry( ld, res ); e != NULL; i++, e = ldap_next_entry( ld, e ) )
    668 		{
    669 			values[ i ] = ldap_get_dn( ld, e );
    670 		}
    671 		values[ i ] = NULL;
    672 
    673 		ldap_msgfree( res );
    674 
    675 		if ( do_retry == maxretries ) {
    676 			snprintf( thrstr, BUFSIZ,
    677 				"Read base=\"%s\" filter=\"%s\" got %d values.\n",
    678 				sbase, filter, nvalues );
    679 			thread_verbose( idx, thrstr );
    680 		}
    681 
    682 		for ( i = 0; i < innerloop; i++ ) {
    683 			int	r = ((double)nvalues)*rand()/(RAND_MAX + 1.0);
    684 
    685 			do_read( ld, values[ r ],
    686 				srchattrs, noattrs, nobind, 1, maxretries,
    687 				delay, force, chaserefs, idx );
    688 		}
    689 		for( i = 0; i < nvalues; i++) {
    690 			if (values[i] != NULL)
    691 				ldap_memfree( values[i] );
    692 		}
    693 		free( values );
    694 		break;
    695 
    696 	default:
    697 		tester_ldap_error( ld, "ldap_search_ext_s", NULL );
    698 		break;
    699 	}
    700 
    701 	snprintf( thrstr, BUFSIZ, "Search done (%d).\n", rc );
    702 	thread_verbose( idx, thrstr );
    703 }
    704 
    705 /* substitute a generated int into the filter */
    706 static void
    707 do_random2( LDAP *ld,
    708 	char *sbase, char *filter, char **srchattrs, int noattrs, int nobind,
    709 	int innerloop, int maxretries, int delay, int force, int chaserefs,
    710 	int idx )
    711 {
    712 	int  	i = 0, do_retry = maxretries;
    713 	int     rc = LDAP_SUCCESS;
    714 	int		lo, hi, range;
    715 	int	flen;
    716 	LDAPMessage *res = NULL, *e = NULL;
    717 	char	*ptr, *ftail;
    718 	char	thrstr[BUFSIZ];
    719 	char	fbuf[BUFSIZ];
    720 
    721 	snprintf( thrstr, BUFSIZ,
    722 			"Read(%d): base=\"%s\", filter=\"%s\".\n",
    723 			innerloop, sbase, filter );
    724 	thread_verbose( idx, thrstr );
    725 
    726 	ptr = strchr(filter, '[');
    727 	if (!ptr)
    728 		return;
    729 	ftail = strchr(filter, ']');
    730 	if (!ftail || ftail < ptr)
    731 		return;
    732 
    733 	sscanf(ptr, "[%d-%d]", &lo, &hi);
    734 	range = hi - lo + 1;
    735 
    736 	flen = ptr - filter;
    737 	ftail++;
    738 
    739 	for ( i = 0; i < innerloop; i++ ) {
    740 		int	r = ((double)range)*rand()/(RAND_MAX + 1.0);
    741 		sprintf(fbuf, "%.*s%d%s", flen, filter, r, ftail);
    742 
    743 		rc = ldap_search_ext_s( ld, sbase, LDAP_SCOPE_SUBTREE,
    744 				fbuf, srchattrs, noattrs, NULL, NULL, NULL,
    745 				LDAP_NO_LIMIT, &res );
    746 		if ( res != NULL ) {
    747 			ldap_msgfree( res );
    748 		}
    749 		if ( rc == 0 ) {
    750 			rt_pass[idx]++;
    751 		} else {
    752 			int		first = tester_ignore_err( rc );
    753 			char		buf[ BUFSIZ ];
    754 
    755 			rt_fail[idx]++;
    756 			snprintf( buf, sizeof( buf ), "ldap_search_ext_s(%s)", entry );
    757 
    758 			/* if ignore.. */
    759 			if ( first ) {
    760 				/* only log if first occurrence */
    761 				if ( ( force < 2 && first > 0 ) || abs(first) == 1 ) {
    762 					tester_ldap_error( ld, buf, NULL );
    763 				}
    764 				continue;
    765 			}
    766 
    767 			/* busy needs special handling */
    768 			tester_ldap_error( ld, buf, NULL );
    769 			if ( rc == LDAP_BUSY && do_retry > 0 ) {
    770 				do_retry--;
    771 				continue;
    772 			}
    773 			break;
    774 		}
    775 	}
    776 
    777 	snprintf( thrstr, BUFSIZ, "Search done (%d).\n", rc );
    778 	thread_verbose( idx, thrstr );
    779 }
    780 
    781 static void
    782 do_read( LDAP *ld, char *entry,
    783 	char **attrs, int noattrs, int nobind, int maxloop,
    784 	int maxretries, int delay, int force, int chaserefs, int idx )
    785 {
    786 	int  	i = 0, do_retry = maxretries;
    787 	int     rc = LDAP_SUCCESS;
    788 	char	thrstr[BUFSIZ];
    789 
    790 retry:;
    791 	if ( do_retry == maxretries ) {
    792 		snprintf( thrstr, BUFSIZ, "Read(%d): entry=\"%s\".\n",
    793 			maxloop, entry );
    794 		thread_verbose( idx, thrstr );
    795 	}
    796 
    797 	snprintf(thrstr, BUFSIZ, "LD %p cnt: %d (retried %d) (%s)", \
    798 		 (void *) ld, maxloop, (do_retry - maxretries), entry);
    799 	thread_verbose( idx, thrstr );
    800 
    801 	for ( ; i < maxloop; i++ ) {
    802 		LDAPMessage *res = NULL;
    803 
    804 		rc = ldap_search_ext_s( ld, entry, LDAP_SCOPE_BASE,
    805 				NULL, attrs, noattrs, NULL, NULL, NULL,
    806 				LDAP_NO_LIMIT, &res );
    807 		if ( res != NULL ) {
    808 			ldap_msgfree( res );
    809 		}
    810 
    811 		if ( rc == 0 ) {
    812 			rt_pass[idx]++;
    813 		} else {
    814 			int		first = tester_ignore_err( rc );
    815 			char		buf[ BUFSIZ ];
    816 
    817 			rt_fail[idx]++;
    818 			snprintf( buf, sizeof( buf ), "ldap_search_ext_s(%s)", entry );
    819 
    820 			/* if ignore.. */
    821 			if ( first ) {
    822 				/* only log if first occurrence */
    823 				if ( ( force < 2 && first > 0 ) || abs(first) == 1 ) {
    824 					tester_ldap_error( ld, buf, NULL );
    825 				}
    826 				continue;
    827 			}
    828 
    829 			/* busy needs special handling */
    830 			tester_ldap_error( ld, buf, NULL );
    831 			if ( rc == LDAP_BUSY && do_retry > 0 ) {
    832 				do_retry--;
    833 				goto retry;
    834 			}
    835 			break;
    836 		}
    837 	}
    838 }
    839