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