1/*	$NetBSD$	*/
2
3/* OpenLDAP: pkg/ldap/tests/progs/slapd-tester.c,v 1.46.2.10 2010/04/13 20:23:59 kurt Exp */
4/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 *
6 * Copyright 1999-2010 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#include "portable.h"
23
24#include <stdio.h>
25
26#include "ac/stdlib.h"
27
28#include "ac/ctype.h"
29#include "ac/dirent.h"
30#include "ac/param.h"
31#include "ac/socket.h"
32#include "ac/string.h"
33#include "ac/unistd.h"
34#include "ac/wait.h"
35
36
37#include "ldap_defaults.h"
38#include "lutil.h"
39
40#include "ldap.h"
41#include "ldap_pvt.h"
42#include "lber_pvt.h"
43#include "slapd-common.h"
44
45#define SEARCHCMD		"slapd-search"
46#define READCMD			"slapd-read"
47#define ADDCMD			"slapd-addel"
48#define MODRDNCMD		"slapd-modrdn"
49#define MODIFYCMD		"slapd-modify"
50#define BINDCMD			"slapd-bind"
51#define MAXARGS      		100
52#define MAXREQS			5000
53#define LOOPS			100
54#define OUTERLOOPS		"1"
55#define RETRIES			"0"
56
57#define TSEARCHFILE		"do_search.0"
58#define TREADFILE		"do_read.0"
59#define TADDFILE		"do_add."
60#define TMODRDNFILE		"do_modrdn.0"
61#define TMODIFYFILE		"do_modify.0"
62#define TBINDFILE		"do_bind.0"
63
64static char *get_file_name( char *dirname, char *filename );
65static int  get_search_filters( char *filename, char *filters[], char *attrs[], char *bases[], LDAPURLDesc *luds[] );
66static int  get_read_entries( char *filename, char *entries[], char *filters[] );
67static void fork_child( char *prog, char **args );
68static void	wait4kids( int nkidval );
69
70static int      maxkids = 20;
71static int      nkids;
72
73#ifdef HAVE_WINSOCK
74static HANDLE	*children;
75static char argbuf[BUFSIZ];
76#define	ArgDup(x) strdup(strcat(strcat(strcpy(argbuf,"\""),x),"\""))
77#else
78#define	ArgDup(x) strdup(x)
79#endif
80
81static void
82usage( char *name, char opt )
83{
84	if ( opt ) {
85		fprintf( stderr, "%s: unable to handle option \'%c\'\n\n",
86			name, opt );
87	}
88
89	fprintf( stderr,
90		"usage: %s "
91		"-H <uri> | ([-h <host>] -p <port>) "
92		"-D <manager> "
93		"-w <passwd> "
94		"-d <datadir> "
95		"[-i <ignore>] "
96		"[-j <maxchild>] "
97		"[-l {<loops>|<type>=<loops>[,...]}] "
98		"[-L <outerloops>] "
99		"-P <progdir> "
100		"[-r <maxretries>] "
101		"[-t <delay>] "
102		"[-C] "
103		"[-F] "
104		"[-I] "
105		"[-N]\n",
106		name );
107	exit( EXIT_FAILURE );
108}
109
110int
111main( int argc, char **argv )
112{
113	int		i, j;
114	char		*uri = NULL;
115	char		*host = "localhost";
116	char		*port = NULL;
117	char		*manager = NULL;
118	char		*passwd = NULL;
119	char		*dirname = NULL;
120	char		*progdir = NULL;
121	int		loops = LOOPS;
122	char		*outerloops = OUTERLOOPS;
123	char		*retries = RETRIES;
124	char		*delay = "0";
125	DIR		*datadir;
126	struct dirent	*file;
127	int		friendly = 0;
128	int		chaserefs = 0;
129	int		noattrs = 0;
130	int		nobind = 0;
131	int		noinit = 1;
132	char		*ignore = NULL;
133	/* search */
134	char		*sfile = NULL;
135	char		*sreqs[MAXREQS];
136	char		*sattrs[MAXREQS];
137	char		*sbase[MAXREQS];
138	LDAPURLDesc	*slud[MAXREQS];
139	int		snum = 0;
140	char		*sargs[MAXARGS];
141	int		sanum;
142	int		sextra_args = 0;
143	char		scmd[MAXPATHLEN];
144	/* static so that its address can be used in initializer below. */
145	static char	sloops[LDAP_PVT_INTTYPE_CHARS(unsigned long)];
146	/* read */
147	char		*rfile = NULL;
148	char		*rreqs[MAXREQS];
149	int		rnum = 0;
150	char		*rargs[MAXARGS];
151	char		*rflts[MAXREQS];
152	int		ranum;
153	int		rextra_args = 0;
154	char		rcmd[MAXPATHLEN];
155	static char	rloops[LDAP_PVT_INTTYPE_CHARS(unsigned long)];
156	/* addel */
157	char		*afiles[MAXREQS];
158	int		anum = 0;
159	char		*aargs[MAXARGS];
160	int		aanum;
161	char		acmd[MAXPATHLEN];
162	static char	aloops[LDAP_PVT_INTTYPE_CHARS(unsigned long)];
163	/* modrdn */
164	char		*nfile = NULL;
165	char		*nreqs[MAXREQS];
166	int		nnum = 0;
167	char		*nargs[MAXARGS];
168	int		nanum;
169	char		ncmd[MAXPATHLEN];
170	static char	nloops[LDAP_PVT_INTTYPE_CHARS(unsigned long)];
171	/* modify */
172	char		*mfile = NULL;
173	char		*mreqs[MAXREQS];
174	char		*mdn[MAXREQS];
175	int		mnum = 0;
176	char		*margs[MAXARGS];
177	int		manum;
178	char		mcmd[MAXPATHLEN];
179	static char	mloops[LDAP_PVT_INTTYPE_CHARS(unsigned long)];
180	/* bind */
181	char		*bfile = NULL;
182	char		*breqs[MAXREQS];
183	char		*bcreds[MAXREQS];
184	char		*battrs[MAXREQS];
185	int		bnum = 0;
186	char		*bargs[MAXARGS];
187	int		banum;
188	char		bcmd[MAXPATHLEN];
189	static char	bloops[LDAP_PVT_INTTYPE_CHARS(unsigned long)];
190	char		**bargs_extra = NULL;
191
192	char		*friendlyOpt = NULL;
193	int		pw_ask = 0;
194	char		*pw_file = NULL;
195
196	/* extra action to do after bind... */
197	typedef struct extra_t {
198		char		*action;
199		struct extra_t	*next;
200	}		extra_t;
201
202	extra_t		*extra = NULL;
203	int		nextra = 0;
204
205	tester_init( "slapd-tester", TESTER_TESTER );
206
207	sloops[0] = '\0';
208	rloops[0] = '\0';
209	aloops[0] = '\0';
210	nloops[0] = '\0';
211	mloops[0] = '\0';
212	bloops[0] = '\0';
213
214	while ( ( i = getopt( argc, argv, "AB:CD:d:FH:h:Ii:j:L:l:NP:p:r:t:Ww:y:" ) ) != EOF )
215	{
216		switch ( i ) {
217		case 'A':
218			noattrs++;
219			break;
220
221		case 'B': {
222			char	**p,
223				**b = ldap_str2charray( optarg, "," );
224			extra_t	**epp;
225
226			for ( epp = &extra; *epp; epp = &(*epp)->next )
227				;
228
229			for ( p = b; p[0]; p++ ) {
230				*epp = calloc( 1, sizeof( extra_t ) );
231				(*epp)->action = p[0];
232				epp = &(*epp)->next;
233				nextra++;
234			}
235
236			ldap_memfree( b );
237			} break;
238
239		case 'C':
240			chaserefs++;
241			break;
242
243		case 'D':		/* slapd manager */
244			manager = ArgDup( optarg );
245			break;
246
247		case 'd':		/* data directory */
248			dirname = strdup( optarg );
249			break;
250
251		case 'F':
252			friendly++;
253			break;
254
255		case 'H':		/* slapd uri */
256			uri = strdup( optarg );
257			break;
258
259		case 'h':		/* slapd host */
260			host = strdup( optarg );
261			break;
262
263		case 'I':
264			noinit = 0;
265			break;
266
267		case 'i':
268			ignore = optarg;
269			break;
270
271		case 'j':		/* the number of parallel clients */
272			if ( lutil_atoi( &maxkids, optarg ) != 0 ) {
273				usage( argv[0], 'j' );
274			}
275			break;
276
277		case 'l':		/* the number of loops per client */
278			if ( !isdigit( (unsigned char) optarg[0] ) ) {
279				char	**p,
280					**l = ldap_str2charray( optarg, "," );
281
282				for ( p = l; p[0]; p++) {
283					struct {
284						struct berval	type;
285						char		*buf;
286					} types[] = {
287						{ BER_BVC( "add=" ),	aloops },
288						{ BER_BVC( "bind=" ),	bloops },
289						{ BER_BVC( "modify=" ),	mloops },
290						{ BER_BVC( "modrdn=" ),	nloops },
291						{ BER_BVC( "read=" ),	rloops },
292						{ BER_BVC( "search=" ),	sloops },
293						{ BER_BVNULL,		NULL }
294					};
295					int	c, n;
296
297					for ( c = 0; types[c].type.bv_val; c++ ) {
298						if ( strncasecmp( p[0], types[c].type.bv_val, types[c].type.bv_len ) == 0 ) {
299							break;
300						}
301					}
302
303					if ( types[c].type.bv_val == NULL ) {
304						usage( argv[0], 'l' );
305					}
306
307					if ( lutil_atoi( &n, &p[0][types[c].type.bv_len] ) != 0 ) {
308						usage( argv[0], 'l' );
309					}
310
311					snprintf( types[c].buf, sizeof( aloops ), "%d", n );
312				}
313
314				ldap_charray_free( l );
315
316			} else if ( lutil_atoi( &loops, optarg ) != 0 ) {
317				usage( argv[0], 'l' );
318			}
319			break;
320
321		case 'L':		/* the number of outerloops per client */
322			outerloops = strdup( optarg );
323			break;
324
325		case 'N':
326			nobind++;
327			break;
328
329		case 'P':		/* prog directory */
330			progdir = strdup( optarg );
331			break;
332
333		case 'p':		/* the servers port number */
334			port = strdup( optarg );
335			break;
336
337		case 'r':		/* the number of retries in case of error */
338			retries = strdup( optarg );
339			break;
340
341		case 't':		/* the delay in seconds between each retry */
342			delay = strdup( optarg );
343			break;
344
345		case 'w':		/* the managers passwd */
346			passwd = ArgDup( optarg );
347			memset( optarg, '*', strlen( optarg ) );
348			break;
349
350		case 'W':
351			pw_ask++;
352			break;
353
354		case 'y':
355			pw_file = optarg;
356			break;
357
358		default:
359			usage( argv[0], '\0' );
360			break;
361		}
362	}
363
364	if (( dirname == NULL ) || ( port == NULL && uri == NULL ) ||
365			( manager == NULL ) || ( passwd == NULL ) || ( progdir == NULL ))
366	{
367		usage( argv[0], '\0' );
368	}
369
370#ifdef HAVE_WINSOCK
371	children = malloc( maxkids * sizeof(HANDLE) );
372#endif
373	/* get the file list */
374	if ( ( datadir = opendir( dirname )) == NULL ) {
375		fprintf( stderr, "%s: couldn't open data directory \"%s\".\n",
376					argv[0], dirname );
377		exit( EXIT_FAILURE );
378	}
379
380	/*  look for search, read, modrdn, and add/delete files */
381	for ( file = readdir( datadir ); file; file = readdir( datadir )) {
382
383		if ( !strcasecmp( file->d_name, TSEARCHFILE )) {
384			sfile = get_file_name( dirname, file->d_name );
385			continue;
386		} else if ( !strcasecmp( file->d_name, TREADFILE )) {
387			rfile = get_file_name( dirname, file->d_name );
388			continue;
389		} else if ( !strcasecmp( file->d_name, TMODRDNFILE )) {
390			nfile = get_file_name( dirname, file->d_name );
391			continue;
392		} else if ( !strcasecmp( file->d_name, TMODIFYFILE )) {
393			mfile = get_file_name( dirname, file->d_name );
394			continue;
395		} else if ( !strncasecmp( file->d_name, TADDFILE, strlen( TADDFILE ))
396			&& ( anum < MAXREQS )) {
397			afiles[anum++] = get_file_name( dirname, file->d_name );
398			continue;
399		} else if ( !strcasecmp( file->d_name, TBINDFILE )) {
400			bfile = get_file_name( dirname, file->d_name );
401			continue;
402		}
403	}
404
405	closedir( datadir );
406
407	if ( pw_ask ) {
408		passwd = getpassphrase( _("Enter LDAP Password: ") );
409
410	} else if ( pw_file ) {
411		struct berval	pw;
412
413		if ( lutil_get_filed_password( pw_file, &pw ) ) {
414			exit( EXIT_FAILURE );
415		}
416
417		passwd = pw.bv_val;
418	}
419
420	if ( !sfile && !rfile && !nfile && !mfile && !bfile && !anum ) {
421		fprintf( stderr, "no data files found.\n" );
422		exit( EXIT_FAILURE );
423	}
424
425	/* look for search requests */
426	if ( sfile ) {
427		snum = get_search_filters( sfile, sreqs, sattrs, sbase, slud );
428		if ( snum < 0 ) {
429			fprintf( stderr,
430				"unable to parse file \"%s\" line %d\n",
431				sfile, -2*(snum + 1));
432			exit( EXIT_FAILURE );
433		}
434	}
435
436	/* look for read requests */
437	if ( rfile ) {
438		rnum = get_read_entries( rfile, rreqs, rflts );
439		if ( rnum < 0 ) {
440			fprintf( stderr,
441				"unable to parse file \"%s\" line %d\n",
442				rfile, -2*(rnum + 1) );
443			exit( EXIT_FAILURE );
444		}
445	}
446
447	/* look for modrdn requests */
448	if ( nfile ) {
449		nnum = get_read_entries( nfile, nreqs, NULL );
450		if ( nnum < 0 ) {
451			fprintf( stderr,
452				"unable to parse file \"%s\" line %d\n",
453				nfile, -2*(nnum + 1) );
454			exit( EXIT_FAILURE );
455		}
456	}
457
458	/* look for modify requests */
459	if ( mfile ) {
460		mnum = get_search_filters( mfile, mreqs, NULL, mdn, NULL );
461		if ( mnum < 0 ) {
462			fprintf( stderr,
463				"unable to parse file \"%s\" line %d\n",
464				mfile, -2*(mnum + 1) );
465			exit( EXIT_FAILURE );
466		}
467	}
468
469	/* look for bind requests */
470	if ( bfile ) {
471		bnum = get_search_filters( bfile, bcreds, battrs, breqs, NULL );
472		if ( bnum < 0 ) {
473			fprintf( stderr,
474				"unable to parse file \"%s\" line %d\n",
475				bfile, -2*(bnum + 1) );
476			exit( EXIT_FAILURE );
477		}
478	}
479
480	/* setup friendly option */
481
482	switch ( friendly ) {
483	case 0:
484		break;
485
486	case 1:
487		friendlyOpt = "-F";
488		break;
489
490	default:
491		/* NOTE: right now we don't need it more than twice */
492	case 2:
493		friendlyOpt = "-FF";
494		break;
495	}
496
497	if ( sloops[0] == '\0' ) snprintf( sloops, sizeof( sloops ), "%d", 10 * loops );
498	if ( rloops[0] == '\0' ) snprintf( rloops, sizeof( rloops ), "%d", 20 * loops );
499	if ( aloops[0] == '\0' ) snprintf( aloops, sizeof( aloops ), "%d", loops );
500	if ( nloops[0] == '\0' ) snprintf( nloops, sizeof( nloops ), "%d", loops );
501	if ( mloops[0] == '\0' ) snprintf( mloops, sizeof( mloops ), "%d", loops );
502	if ( bloops[0] == '\0' ) snprintf( bloops, sizeof( bloops ), "%d", 20 * loops );
503
504	/*
505	 * generate the search clients
506	 */
507
508	sanum = 0;
509	snprintf( scmd, sizeof scmd, "%s" LDAP_DIRSEP SEARCHCMD,
510		progdir );
511	sargs[sanum++] = scmd;
512	if ( uri ) {
513		sargs[sanum++] = "-H";
514		sargs[sanum++] = uri;
515	} else {
516		sargs[sanum++] = "-h";
517		sargs[sanum++] = host;
518		sargs[sanum++] = "-p";
519		sargs[sanum++] = port;
520	}
521	sargs[sanum++] = "-D";
522	sargs[sanum++] = manager;
523	sargs[sanum++] = "-w";
524	sargs[sanum++] = passwd;
525	sargs[sanum++] = "-l";
526	sargs[sanum++] = sloops;
527	sargs[sanum++] = "-L";
528	sargs[sanum++] = outerloops;
529	sargs[sanum++] = "-r";
530	sargs[sanum++] = retries;
531	sargs[sanum++] = "-t";
532	sargs[sanum++] = delay;
533	if ( friendly ) {
534		sargs[sanum++] = friendlyOpt;
535	}
536	if ( chaserefs ) {
537		sargs[sanum++] = "-C";
538	}
539	if ( noattrs ) {
540		sargs[sanum++] = "-A";
541	}
542	if ( nobind ) {
543		sargs[sanum++] = "-N";
544	}
545	if ( ignore ) {
546		sargs[sanum++] = "-i";
547		sargs[sanum++] = ignore;
548	}
549	sargs[sanum++] = "-b";
550	sargs[sanum++] = NULL;		/* will hold the search base */
551	sargs[sanum++] = "-s";
552	sargs[sanum++] = NULL;		/* will hold the search scope */
553	sargs[sanum++] = "-f";
554	sargs[sanum++] = NULL;		/* will hold the search request */
555
556	sargs[sanum++] = NULL;
557	sargs[sanum++] = NULL;		/* might hold the "attr" request */
558	sextra_args += 2;
559
560	sargs[sanum] = NULL;
561
562	/*
563	 * generate the read clients
564	 */
565
566	ranum = 0;
567	snprintf( rcmd, sizeof rcmd, "%s" LDAP_DIRSEP READCMD,
568		progdir );
569	rargs[ranum++] = rcmd;
570	if ( uri ) {
571		rargs[ranum++] = "-H";
572		rargs[ranum++] = uri;
573	} else {
574		rargs[ranum++] = "-h";
575		rargs[ranum++] = host;
576		rargs[ranum++] = "-p";
577		rargs[ranum++] = port;
578	}
579	rargs[ranum++] = "-D";
580	rargs[ranum++] = manager;
581	rargs[ranum++] = "-w";
582	rargs[ranum++] = passwd;
583	rargs[ranum++] = "-l";
584	rargs[ranum++] = rloops;
585	rargs[ranum++] = "-L";
586	rargs[ranum++] = outerloops;
587	rargs[ranum++] = "-r";
588	rargs[ranum++] = retries;
589	rargs[ranum++] = "-t";
590	rargs[ranum++] = delay;
591	if ( friendly ) {
592		rargs[ranum++] = friendlyOpt;
593	}
594	if ( chaserefs ) {
595		rargs[ranum++] = "-C";
596	}
597	if ( noattrs ) {
598		rargs[ranum++] = "-A";
599	}
600	if ( ignore ) {
601		rargs[ranum++] = "-i";
602		rargs[ranum++] = ignore;
603	}
604	rargs[ranum++] = "-e";
605	rargs[ranum++] = NULL;		/* will hold the read entry */
606
607	rargs[ranum++] = NULL;
608	rargs[ranum++] = NULL;		/* might hold the filter arg */
609	rextra_args += 2;
610
611	rargs[ranum] = NULL;
612
613	/*
614	 * generate the modrdn clients
615	 */
616
617	nanum = 0;
618	snprintf( ncmd, sizeof ncmd, "%s" LDAP_DIRSEP MODRDNCMD,
619		progdir );
620	nargs[nanum++] = ncmd;
621	if ( uri ) {
622		nargs[nanum++] = "-H";
623		nargs[nanum++] = uri;
624	} else {
625		nargs[nanum++] = "-h";
626		nargs[nanum++] = host;
627		nargs[nanum++] = "-p";
628		nargs[nanum++] = port;
629	}
630	nargs[nanum++] = "-D";
631	nargs[nanum++] = manager;
632	nargs[nanum++] = "-w";
633	nargs[nanum++] = passwd;
634	nargs[nanum++] = "-l";
635	nargs[nanum++] = nloops;
636	nargs[nanum++] = "-L";
637	nargs[nanum++] = outerloops;
638	nargs[nanum++] = "-r";
639	nargs[nanum++] = retries;
640	nargs[nanum++] = "-t";
641	nargs[nanum++] = delay;
642	if ( friendly ) {
643		nargs[nanum++] = friendlyOpt;
644	}
645	if ( chaserefs ) {
646		nargs[nanum++] = "-C";
647	}
648	if ( ignore ) {
649		nargs[nanum++] = "-i";
650		nargs[nanum++] = ignore;
651	}
652	nargs[nanum++] = "-e";
653	nargs[nanum++] = NULL;		/* will hold the modrdn entry */
654	nargs[nanum] = NULL;
655
656	/*
657	 * generate the modify clients
658	 */
659
660	manum = 0;
661	snprintf( mcmd, sizeof mcmd, "%s" LDAP_DIRSEP MODIFYCMD,
662		progdir );
663	margs[manum++] = mcmd;
664	if ( uri ) {
665		margs[manum++] = "-H";
666		margs[manum++] = uri;
667	} else {
668		margs[manum++] = "-h";
669		margs[manum++] = host;
670		margs[manum++] = "-p";
671		margs[manum++] = port;
672	}
673	margs[manum++] = "-D";
674	margs[manum++] = manager;
675	margs[manum++] = "-w";
676	margs[manum++] = passwd;
677	margs[manum++] = "-l";
678	margs[manum++] = mloops;
679	margs[manum++] = "-L";
680	margs[manum++] = outerloops;
681	margs[manum++] = "-r";
682	margs[manum++] = retries;
683	margs[manum++] = "-t";
684	margs[manum++] = delay;
685	if ( friendly ) {
686		margs[manum++] = friendlyOpt;
687	}
688	if ( chaserefs ) {
689		margs[manum++] = "-C";
690	}
691	if ( ignore ) {
692		margs[manum++] = "-i";
693		margs[manum++] = ignore;
694	}
695	margs[manum++] = "-e";
696	margs[manum++] = NULL;		/* will hold the modify entry */
697	margs[manum++] = "-a";;
698	margs[manum++] = NULL;		/* will hold the ava */
699	margs[manum] = NULL;
700
701	/*
702	 * generate the add/delete clients
703	 */
704
705	aanum = 0;
706	snprintf( acmd, sizeof acmd, "%s" LDAP_DIRSEP ADDCMD,
707		progdir );
708	aargs[aanum++] = acmd;
709	if ( uri ) {
710		aargs[aanum++] = "-H";
711		aargs[aanum++] = uri;
712	} else {
713		aargs[aanum++] = "-h";
714		aargs[aanum++] = host;
715		aargs[aanum++] = "-p";
716		aargs[aanum++] = port;
717	}
718	aargs[aanum++] = "-D";
719	aargs[aanum++] = manager;
720	aargs[aanum++] = "-w";
721	aargs[aanum++] = passwd;
722	aargs[aanum++] = "-l";
723	aargs[aanum++] = aloops;
724	aargs[aanum++] = "-L";
725	aargs[aanum++] = outerloops;
726	aargs[aanum++] = "-r";
727	aargs[aanum++] = retries;
728	aargs[aanum++] = "-t";
729	aargs[aanum++] = delay;
730	if ( friendly ) {
731		aargs[aanum++] = friendlyOpt;
732	}
733	if ( chaserefs ) {
734		aargs[aanum++] = "-C";
735	}
736	if ( ignore ) {
737		aargs[aanum++] = "-i";
738		aargs[aanum++] = ignore;
739	}
740	aargs[aanum++] = "-f";
741	aargs[aanum++] = NULL;		/* will hold the add data file */
742	aargs[aanum] = NULL;
743
744	/*
745	 * generate the bind clients
746	 */
747
748	banum = 0;
749	snprintf( bcmd, sizeof bcmd, "%s" LDAP_DIRSEP BINDCMD,
750		progdir );
751	bargs[banum++] = bcmd;
752	if ( !noinit ) {
753		bargs[banum++] = "-I";	/* init on each bind */
754	}
755	if ( uri ) {
756		bargs[banum++] = "-H";
757		bargs[banum++] = uri;
758	} else {
759		bargs[banum++] = "-h";
760		bargs[banum++] = host;
761		bargs[banum++] = "-p";
762		bargs[banum++] = port;
763	}
764	bargs[banum++] = "-l";
765	bargs[banum++] = bloops;
766	bargs[banum++] = "-L";
767	bargs[banum++] = outerloops;
768#if 0
769	bargs[banum++] = "-r";
770	bargs[banum++] = retries;
771	bargs[banum++] = "-t";
772	bargs[banum++] = delay;
773#endif
774	if ( friendly ) {
775		bargs[banum++] = friendlyOpt;
776	}
777	if ( chaserefs ) {
778		bargs[banum++] = "-C";
779	}
780	if ( ignore ) {
781		bargs[banum++] = "-i";
782		bargs[banum++] = ignore;
783	}
784	if ( nextra ) {
785		bargs[banum++] = "-B";
786		bargs_extra = &bargs[banum++];
787	}
788	bargs[banum++] = "-D";
789	bargs[banum++] = NULL;
790	bargs[banum++] = "-w";
791	bargs[banum++] = NULL;
792	bargs[banum] = NULL;
793
794#define	DOREQ(n,j) ((n) && ((maxkids > (n)) ? ((j) < maxkids ) : ((j) < (n))))
795
796	for ( j = 0; j < MAXREQS; j++ ) {
797		/* search */
798		if ( DOREQ( snum, j ) ) {
799			int	jj = j % snum;
800			int	x = sanum - sextra_args;
801
802			/* base */
803			if ( sbase[jj] != NULL ) {
804				sargs[sanum - 7] = sbase[jj];
805
806			} else {
807				sargs[sanum - 7] = slud[jj]->lud_dn;
808			}
809
810			/* scope */
811			if ( slud[jj] != NULL ) {
812				sargs[sanum - 5] = (char *)ldap_pvt_scope2str( slud[jj]->lud_scope );
813
814			} else {
815				sargs[sanum - 5] = "sub";
816			}
817
818			/* filter */
819			if ( sreqs[jj] != NULL ) {
820				sargs[sanum - 3] = sreqs[jj];
821
822			} else if ( slud[jj]->lud_filter != NULL ) {
823				sargs[sanum - 3] = slud[jj]->lud_filter;
824
825			} else {
826				sargs[sanum - 3] = "(objectClass=*)";
827			}
828
829			/* extras */
830			sargs[x] = NULL;
831
832			/* attr */
833			if ( sattrs[jj] != NULL ) {
834				sargs[x++] = "-a";
835				sargs[x++] = sattrs[jj];
836			}
837
838			/* attrs */
839			if ( slud[jj] != NULL && slud[jj]->lud_attrs != NULL ) {
840				int	i;
841
842				for ( i = 0; slud[jj]->lud_attrs[ i ] != NULL && x + i < MAXARGS - 1; i++ ) {
843					sargs[x + i] = slud[jj]->lud_attrs[ i ];
844				}
845				sargs[x + i] = NULL;
846			}
847
848			fork_child( scmd, sargs );
849		}
850
851		/* read */
852		if ( DOREQ( rnum, j ) ) {
853			int	jj = j % rnum;
854			int	x = ranum - rextra_args;
855
856			rargs[ranum - 3] = rreqs[jj];
857			if ( rflts[jj] != NULL ) {
858				rargs[x++] = "-f";
859				rargs[x++] = rflts[jj];
860			}
861			rargs[x] = NULL;
862			fork_child( rcmd, rargs );
863		}
864
865		/* rename */
866		if ( j < nnum ) {
867			nargs[nanum - 1] = nreqs[j];
868			fork_child( ncmd, nargs );
869		}
870
871		/* modify */
872		if ( j < mnum ) {
873			margs[manum - 3] = mdn[j];
874			margs[manum - 1] = mreqs[j];
875			fork_child( mcmd, margs );
876		}
877
878		/* add/delete */
879		if ( j < anum ) {
880			aargs[aanum - 1] = afiles[j];
881			fork_child( acmd, aargs );
882		}
883
884		/* bind */
885		if ( DOREQ( bnum, j ) ) {
886			int	jj = j % bnum;
887
888			if ( nextra ) {
889				int	n = ((double)nextra)*rand()/(RAND_MAX + 1.0);
890				extra_t	*e;
891
892				for ( e = extra; n-- > 0; e = e->next )
893					;
894				*bargs_extra = e->action;
895			}
896
897			if ( battrs[jj] != NULL ) {
898				bargs[banum - 3] = manager ? manager : "";
899				bargs[banum - 1] = passwd ? passwd : "";
900
901				bargs[banum + 0] = "-b";
902				bargs[banum + 1] = breqs[jj];
903				bargs[banum + 2] = "-f";
904				bargs[banum + 3] = bcreds[jj];
905				bargs[banum + 4] = "-a";
906				bargs[banum + 5] = battrs[jj];
907				bargs[banum + 6] = NULL;
908
909			} else {
910				bargs[banum - 3] = breqs[jj];
911				bargs[banum - 1] = bcreds[jj];
912				bargs[banum] = NULL;
913			}
914
915			fork_child( bcmd, bargs );
916			bargs[banum] = NULL;
917		}
918	}
919
920	wait4kids( -1 );
921
922	exit( EXIT_SUCCESS );
923}
924
925static char *
926get_file_name( char *dirname, char *filename )
927{
928	char buf[MAXPATHLEN];
929
930	snprintf( buf, sizeof buf, "%s" LDAP_DIRSEP "%s",
931		dirname, filename );
932	return( strdup( buf ));
933}
934
935
936static int
937get_search_filters( char *filename, char *filters[], char *attrs[], char *bases[], LDAPURLDesc *luds[] )
938{
939	FILE    *fp;
940	int     filter = 0;
941
942	if ( (fp = fopen( filename, "r" )) != NULL ) {
943		char  line[BUFSIZ];
944
945		while (( filter < MAXREQS ) && ( fgets( line, BUFSIZ, fp ))) {
946			char	*nl;
947			int	got_URL = 0;
948
949			if (( nl = strchr( line, '\r' )) || ( nl = strchr( line, '\n' )))
950				*nl = '\0';
951
952			if ( luds ) luds[filter] = NULL;
953
954			if ( luds && strncmp( line, "ldap:///", STRLENOF( "ldap:///" ) ) == 0 ) {
955				LDAPURLDesc	*lud;
956
957				got_URL = 1;
958				bases[filter] = NULL;
959				if ( ldap_url_parse( line, &lud ) != LDAP_URL_SUCCESS ) {
960					filter = -filter - 1;
961					break;
962				}
963
964				if ( lud->lud_dn == NULL || lud->lud_exts != NULL ) {
965					filter = -filter - 1;
966					ldap_free_urldesc( lud );
967					break;
968				}
969
970				luds[filter] = lud;
971
972			} else {
973				bases[filter] = ArgDup( line );
974			}
975			fgets( line, BUFSIZ, fp );
976			if (( nl = strchr( line, '\r' )) || ( nl = strchr( line, '\n' )))
977				*nl = '\0';
978
979			filters[filter] = ArgDup( line );
980			if ( attrs ) {
981				if ( filters[filter][0] == '+') {
982					char	*sep = strchr( filters[filter], ':' );
983
984					attrs[ filter ] = &filters[ filter ][ 1 ];
985					if ( sep != NULL ) {
986						sep[ 0 ] = '\0';
987						/* NOTE: don't free this! */
988						filters[ filter ] = &sep[ 1 ];
989					}
990
991				} else {
992					attrs[ filter ] = NULL;
993				}
994			}
995			filter++;
996
997		}
998		fclose( fp );
999	}
1000
1001	return filter;
1002}
1003
1004
1005static int
1006get_read_entries( char *filename, char *entries[], char *filters[] )
1007{
1008	FILE    *fp;
1009	int     entry = 0;
1010
1011	if ( (fp = fopen( filename, "r" )) != NULL ) {
1012		char  line[BUFSIZ];
1013
1014		while (( entry < MAXREQS ) && ( fgets( line, BUFSIZ, fp ))) {
1015			char *nl;
1016
1017			if (( nl = strchr( line, '\r' )) || ( nl = strchr( line, '\n' )))
1018				*nl = '\0';
1019			if ( filters != NULL && line[0] == '+' ) {
1020				LDAPURLDesc	*lud;
1021
1022				if ( ldap_url_parse( &line[1], &lud ) != LDAP_URL_SUCCESS ) {
1023					entry = -entry - 1;
1024					break;
1025				}
1026
1027				if ( lud->lud_dn == NULL || lud->lud_dn[ 0 ] == '\0' ) {
1028					ldap_free_urldesc( lud );
1029					entry = -entry - 1;
1030					break;
1031				}
1032
1033				entries[entry] = ArgDup( lud->lud_dn );
1034
1035				if ( lud->lud_filter ) {
1036					filters[entry] = ArgDup( lud->lud_filter );
1037
1038				} else {
1039					filters[entry] = ArgDup( "(objectClass=*)" );
1040				}
1041				ldap_free_urldesc( lud );
1042
1043			} else {
1044				entries[entry] = ArgDup( line );
1045			}
1046
1047			entry++;
1048
1049		}
1050		fclose( fp );
1051	}
1052
1053	return( entry );
1054}
1055
1056#ifndef HAVE_WINSOCK
1057static void
1058fork_child( char *prog, char **args )
1059{
1060	/* note: obscures global pid var; intended */
1061	pid_t	pid;
1062
1063	wait4kids( maxkids );
1064
1065	switch ( pid = fork() ) {
1066	case 0:		/* child */
1067#ifdef HAVE_EBCDIC
1068		/* The __LIBASCII execvp only handles ASCII "prog",
1069		 * we still need to translate the arg vec ourselves.
1070		 */
1071		{ char *arg2[MAXREQS];
1072		int i;
1073
1074		for (i=0; args[i]; i++) {
1075			arg2[i] = ArgDup(args[i]);
1076			__atoe(arg2[i]);
1077		}
1078		arg2[i] = NULL;
1079		args = arg2; }
1080#endif
1081		execvp( prog, args );
1082		tester_perror( "execvp", NULL );
1083		{ int i;
1084			for (i=0; args[i]; i++);
1085			fprintf(stderr,"%d args\n", i);
1086			for (i=0; args[i]; i++)
1087				fprintf(stderr,"%d %s\n", i, args[i]);
1088		}
1089
1090		exit( EXIT_FAILURE );
1091		break;
1092
1093	case -1:	/* trouble */
1094		tester_perror( "fork", NULL );
1095		break;
1096
1097	default:	/* parent */
1098		nkids++;
1099		break;
1100	}
1101}
1102
1103static void
1104wait4kids( int nkidval )
1105{
1106	int		status;
1107
1108	while ( nkids >= nkidval ) {
1109		wait( &status );
1110
1111		if ( WIFSTOPPED(status) ) {
1112			fprintf( stderr,
1113			    "stopping: child stopped with signal %d\n",
1114			    (int) WSTOPSIG(status) );
1115
1116		} else if ( WIFSIGNALED(status) ) {
1117			fprintf( stderr,
1118			    "stopping: child terminated with signal %d%s\n",
1119			    (int) WTERMSIG(status),
1120#ifdef WCOREDUMP
1121				WCOREDUMP(status) ? ", core dumped" : ""
1122#else
1123				""
1124#endif
1125				);
1126			exit( WEXITSTATUS(status)  );
1127
1128		} else if ( WEXITSTATUS(status) != 0 ) {
1129			fprintf( stderr,
1130			    "stopping: child exited with status %d\n",
1131			    (int) WEXITSTATUS(status) );
1132			exit( WEXITSTATUS(status) );
1133
1134		} else {
1135			nkids--;
1136		}
1137	}
1138}
1139#else
1140
1141static void
1142wait4kids( int nkidval )
1143{
1144	int rc, i;
1145
1146	while ( nkids >= nkidval ) {
1147		rc = WaitForMultipleObjects( nkids, children, FALSE, INFINITE );
1148		for ( i=rc - WAIT_OBJECT_0; i<nkids-1; i++)
1149			children[i] = children[i+1];
1150		nkids--;
1151	}
1152}
1153
1154static void
1155fork_child( char *prog, char **args )
1156{
1157	int rc;
1158
1159	wait4kids( maxkids );
1160
1161	rc = _spawnvp( _P_NOWAIT, prog, args );
1162
1163	if ( rc == -1 ) {
1164		tester_perror( "_spawnvp", NULL );
1165	} else {
1166		children[nkids++] = (HANDLE)rc;
1167	}
1168}
1169#endif
1170