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