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