2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4 * Copyright 1999-2006 The OpenLDAP Foundation.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted only as authorized by the OpenLDAP
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>.
16 * This work was initially developed by Kurt Spanier for inclusion
17 * in OpenLDAP Software.
24 #include <ac/stdlib.h>
27 #include <ac/dirent.h>
29 #include <ac/socket.h>
30 #include <ac/string.h>
31 #include <ac/unistd.h>
35 #include "ldap_defaults.h"
41 #include "slapd-common.h"
43 #define SEARCHCMD "slapd-search"
44 #define READCMD "slapd-read"
45 #define ADDCMD "slapd-addel"
46 #define MODRDNCMD "slapd-modrdn"
47 #define MODIFYCMD "slapd-modify"
48 #define BINDCMD "slapd-bind"
52 #define OUTERLOOPS "1"
55 #define TSEARCHFILE "do_search.0"
56 #define TREADFILE "do_read.0"
57 #define TADDFILE "do_add."
58 #define TMODRDNFILE "do_modrdn.0"
59 #define TMODIFYFILE "do_modify.0"
60 #define TBINDFILE "do_bind.0"
62 static char *get_file_name( char *dirname, char *filename );
63 static int get_search_filters( char *filename, char *filters[], char *attrs[], char *bases[] );
64 static int get_read_entries( char *filename, char *entries[], char *filters[] );
65 static void fork_child( char *prog, char **args );
66 static void wait4kids( int nkidval );
68 static int maxkids = 20;
72 static HANDLE *children;
73 static char argbuf[BUFSIZ];
74 #define ArgDup(x) strdup(strcat(strcat(strcpy(argbuf,"\""),x),"\""))
76 #define ArgDup(x) strdup(x)
80 usage( char *name, char opt )
83 fprintf( stderr, "%s: unable to handle option \'%c\'\n\n",
89 "-H <uri> | ([-h <host>] -p <port>) "
95 "[-l {<loops>|<type>=<loops>[,...]}] "
104 exit( EXIT_FAILURE );
108 main( int argc, char **argv )
112 char *host = "localhost";
114 char *manager = NULL;
116 char *dirname = NULL;
117 char *progdir = NULL;
119 char *outerloops = OUTERLOOPS;
120 char *retries = RETRIES;
132 char *sreqs[MAXREQS];
133 char *sattrs[MAXREQS];
134 char *sbase[MAXREQS];
136 char *sargs[MAXARGS];
138 char scmd[MAXPATHLEN];
139 char sloops[] = "18446744073709551615UL";
142 char *rreqs[MAXREQS];
144 char *rargs[MAXARGS];
145 char *rflts[MAXREQS];
147 char rcmd[MAXPATHLEN];
148 char rloops[] = "18446744073709551615UL";
150 char *afiles[MAXREQS];
152 char *aargs[MAXARGS];
154 char acmd[MAXPATHLEN];
155 char aloops[] = "18446744073709551615UL";
158 char *nreqs[MAXREQS];
160 char *nargs[MAXARGS];
162 char ncmd[MAXPATHLEN];
163 char nloops[] = "18446744073709551615UL";
166 char *mreqs[MAXREQS];
169 char *margs[MAXARGS];
171 char mcmd[MAXPATHLEN];
172 char mloops[] = "18446744073709551615UL";
175 char *breqs[MAXREQS];
176 char *bcreds[MAXREQS];
177 char *battrs[MAXREQS];
179 char *bargs[MAXARGS];
181 char bcmd[MAXPATHLEN];
182 char bloops[] = "18446744073709551615UL";
183 char **bargs_extra = NULL;
185 char *friendlyOpt = NULL;
187 char *pw_file = NULL;
189 /* extra action to do after bind... */
190 typedef struct extra_t {
192 struct extra_t *next;
195 extra_t *extra = NULL;
198 tester_init( "slapd-tester", TESTER_TESTER );
207 while ( (i = getopt( argc, argv, "AB:CD:d:FH:h:Ii:j:l:L:NP:p:r:t:Ww:y:" )) != EOF ) {
216 **b = ldap_str2charray( optarg, "," );
219 for ( epp = &extra; *epp; epp = &(*epp)->next )
222 for ( p = b; p[0]; p++ ) {
223 *epp = calloc( 1, sizeof( extra_t ) );
224 (*epp)->action = p[0];
236 case 'D': /* slapd manager */
237 manager = ArgDup( optarg );
240 case 'd': /* data directory */
241 dirname = strdup( optarg );
248 case 'H': /* slapd uri */
249 uri = strdup( optarg );
252 case 'h': /* slapd host */
253 host = strdup( optarg );
264 case 'j': /* the number of parallel clients */
265 if ( lutil_atoi( &maxkids, optarg ) != 0 ) {
266 usage( argv[0], 'j' );
270 case 'l': /* the number of loops per client */
271 if ( !isdigit( optarg[0] ) ) {
273 **l = ldap_str2charray( optarg, "," );
275 for ( p = l; p[0]; p++) {
280 { BER_BVC( "add=" ), aloops },
281 { BER_BVC( "bind=" ), bloops },
282 { BER_BVC( "modify=" ), mloops },
283 { BER_BVC( "modrdn=" ), nloops },
284 { BER_BVC( "read=" ), rloops },
285 { BER_BVC( "search=" ), sloops },
290 for ( c = 0; types[c].type.bv_val; c++ ) {
291 if ( strncasecmp( p[0], types[c].type.bv_val, types[c].type.bv_len ) == 0 ) {
296 if ( types[c].type.bv_val == NULL ) {
297 usage( argv[0], 'l' );
300 if ( lutil_atoi( &n, &p[0][types[c].type.bv_len] ) != 0 ) {
301 usage( argv[0], 'l' );
304 snprintf( types[c].buf, sizeof( aloops ), "%d", n );
307 ldap_charray_free( l );
309 } else if ( lutil_atoi( &loops, optarg ) != 0 ) {
310 usage( argv[0], 'l' );
314 case 'L': /* the number of outerloops per client */
315 outerloops = strdup( optarg );
322 case 'P': /* prog directory */
323 progdir = strdup( optarg );
326 case 'p': /* the servers port number */
327 port = strdup( optarg );
330 case 'r': /* the number of retries in case of error */
331 retries = strdup( optarg );
334 case 't': /* the delay in seconds between each retry */
335 delay = strdup( optarg );
338 case 'w': /* the managers passwd */
339 passwd = ArgDup( optarg );
340 memset( optarg, '*', strlen( optarg ) );
352 usage( argv[0], '\0' );
357 if (( dirname == NULL ) || ( port == NULL && uri == NULL ) ||
358 ( manager == NULL ) || ( passwd == NULL ) || ( progdir == NULL ))
359 usage( argv[0], '\0' );
362 children = malloc( maxkids * sizeof(HANDLE) );
364 /* get the file list */
365 if ( ( datadir = opendir( dirname )) == NULL ) {
366 fprintf( stderr, "%s: couldn't open data directory \"%s\".\n",
368 exit( EXIT_FAILURE );
371 /* look for search, read, modrdn, and add/delete files */
372 for ( file = readdir( datadir ); file; file = readdir( datadir )) {
374 if ( !strcasecmp( file->d_name, TSEARCHFILE )) {
375 sfile = get_file_name( dirname, file->d_name );
377 } else if ( !strcasecmp( file->d_name, TREADFILE )) {
378 rfile = get_file_name( dirname, file->d_name );
380 } else if ( !strcasecmp( file->d_name, TMODRDNFILE )) {
381 nfile = get_file_name( dirname, file->d_name );
383 } else if ( !strcasecmp( file->d_name, TMODIFYFILE )) {
384 mfile = get_file_name( dirname, file->d_name );
386 } else if ( !strncasecmp( file->d_name, TADDFILE, strlen( TADDFILE ))
387 && ( anum < MAXREQS )) {
388 afiles[anum++] = get_file_name( dirname, file->d_name );
390 } else if ( !strcasecmp( file->d_name, TBINDFILE )) {
391 bfile = get_file_name( dirname, file->d_name );
399 passwd = getpassphrase( _("Enter LDAP Password: ") );
401 } else if ( pw_file ) {
404 if ( lutil_get_filed_password( pw_file, &pw ) ) {
405 exit( EXIT_FAILURE );
411 /* look for search requests */
413 snum = get_search_filters( sfile, sreqs, sattrs, sbase );
416 /* look for read requests */
418 rnum = get_read_entries( rfile, rreqs, rflts );
421 /* look for modrdn requests */
423 nnum = get_read_entries( nfile, nreqs, NULL );
426 /* look for modify requests */
428 mnum = get_search_filters( mfile, mreqs, NULL, mdn );
431 /* look for bind requests */
433 bnum = get_search_filters( bfile, bcreds, battrs, breqs );
436 /* setup friendly option */
438 switch ( friendly ) {
447 /* NOTE: right now we don't need it more than twice */
453 if ( sloops[0] == '\0' ) snprintf( sloops, sizeof( sloops ), "%d", 10 * loops );
454 if ( rloops[0] == '\0' ) snprintf( rloops, sizeof( rloops ), "%d", 20 * loops );
455 if ( aloops[0] == '\0' ) snprintf( aloops, sizeof( aloops ), "%d", loops );
456 if ( nloops[0] == '\0' ) snprintf( nloops, sizeof( nloops ), "%d", loops );
457 if ( mloops[0] == '\0' ) snprintf( mloops, sizeof( mloops ), "%d", loops );
458 if ( bloops[0] == '\0' ) snprintf( bloops, sizeof( bloops ), "%d", 20 * loops );
461 * generate the search clients
465 snprintf( scmd, sizeof scmd, "%s" LDAP_DIRSEP SEARCHCMD,
467 sargs[sanum++] = scmd;
469 sargs[sanum++] = "-H";
470 sargs[sanum++] = uri;
472 sargs[sanum++] = "-h";
473 sargs[sanum++] = host;
474 sargs[sanum++] = "-p";
475 sargs[sanum++] = port;
477 sargs[sanum++] = "-D";
478 sargs[sanum++] = manager;
479 sargs[sanum++] = "-w";
480 sargs[sanum++] = passwd;
481 sargs[sanum++] = "-l";
482 sargs[sanum++] = sloops;
483 sargs[sanum++] = "-L";
484 sargs[sanum++] = outerloops;
485 sargs[sanum++] = "-r";
486 sargs[sanum++] = retries;
487 sargs[sanum++] = "-t";
488 sargs[sanum++] = delay;
490 sargs[sanum++] = friendlyOpt;
493 sargs[sanum++] = "-C";
496 sargs[sanum++] = "-A";
499 sargs[sanum++] = "-N";
502 sargs[sanum++] = "-i";
503 sargs[sanum++] = ignore;
505 sargs[sanum++] = "-b";
506 sargs[sanum++] = NULL; /* will hold the search base */
507 sargs[sanum++] = "-f";
508 sargs[sanum++] = NULL; /* will hold the search request */
510 sargs[sanum++] = NULL;
511 sargs[sanum] = NULL; /* might hold the "attr" request */
513 sargs[sanum + 1] = NULL;
516 * generate the read clients
520 snprintf( rcmd, sizeof rcmd, "%s" LDAP_DIRSEP READCMD,
522 rargs[ranum++] = rcmd;
524 rargs[ranum++] = "-H";
525 rargs[ranum++] = uri;
527 rargs[ranum++] = "-h";
528 rargs[ranum++] = host;
529 rargs[ranum++] = "-p";
530 rargs[ranum++] = port;
532 rargs[ranum++] = "-D";
533 rargs[ranum++] = manager;
534 rargs[ranum++] = "-w";
535 rargs[ranum++] = passwd;
536 rargs[ranum++] = "-l";
537 rargs[ranum++] = rloops;
538 rargs[ranum++] = "-L";
539 rargs[ranum++] = outerloops;
540 rargs[ranum++] = "-r";
541 rargs[ranum++] = retries;
542 rargs[ranum++] = "-t";
543 rargs[ranum++] = delay;
545 rargs[ranum++] = friendlyOpt;
548 rargs[ranum++] = "-C";
551 rargs[ranum++] = "-A";
554 rargs[ranum++] = "-i";
555 rargs[ranum++] = ignore;
557 rargs[ranum++] = "-e";
558 rargs[ranum++] = NULL; /* will hold the read entry */
560 rargs[ranum++] = NULL;
561 rargs[ranum] = NULL; /* might hold the filter arg */
563 rargs[ranum + 1] = NULL;
566 * generate the modrdn clients
570 snprintf( ncmd, sizeof ncmd, "%s" LDAP_DIRSEP MODRDNCMD,
572 nargs[nanum++] = ncmd;
574 nargs[nanum++] = "-H";
575 nargs[nanum++] = uri;
577 nargs[nanum++] = "-h";
578 nargs[nanum++] = host;
579 nargs[nanum++] = "-p";
580 nargs[nanum++] = port;
582 nargs[nanum++] = "-D";
583 nargs[nanum++] = manager;
584 nargs[nanum++] = "-w";
585 nargs[nanum++] = passwd;
586 nargs[nanum++] = "-l";
587 nargs[nanum++] = nloops;
588 nargs[nanum++] = "-L";
589 nargs[nanum++] = outerloops;
590 nargs[nanum++] = "-r";
591 nargs[nanum++] = retries;
592 nargs[nanum++] = "-t";
593 nargs[nanum++] = delay;
595 nargs[nanum++] = friendlyOpt;
598 nargs[nanum++] = "-C";
601 nargs[nanum++] = "-i";
602 nargs[nanum++] = ignore;
604 nargs[nanum++] = "-e";
605 nargs[nanum++] = NULL; /* will hold the modrdn entry */
606 nargs[nanum++] = NULL;
609 * generate the modify clients
613 snprintf( mcmd, sizeof mcmd, "%s" LDAP_DIRSEP MODIFYCMD,
615 margs[manum++] = mcmd;
617 margs[manum++] = "-H";
618 margs[manum++] = uri;
620 margs[manum++] = "-h";
621 margs[manum++] = host;
622 margs[manum++] = "-p";
623 margs[manum++] = port;
625 margs[manum++] = "-D";
626 margs[manum++] = manager;
627 margs[manum++] = "-w";
628 margs[manum++] = passwd;
629 margs[manum++] = "-l";
630 margs[manum++] = mloops;
631 margs[manum++] = "-L";
632 margs[manum++] = outerloops;
633 margs[manum++] = "-r";
634 margs[manum++] = retries;
635 margs[manum++] = "-t";
636 margs[manum++] = delay;
638 margs[manum++] = friendlyOpt;
641 margs[manum++] = "-C";
644 margs[manum++] = "-i";
645 margs[manum++] = ignore;
647 margs[manum++] = "-e";
648 margs[manum++] = NULL; /* will hold the modify entry */
649 margs[manum++] = "-a";;
650 margs[manum++] = NULL; /* will hold the ava */
651 margs[manum++] = NULL;
654 * generate the add/delete clients
658 snprintf( acmd, sizeof acmd, "%s" LDAP_DIRSEP ADDCMD,
660 aargs[aanum++] = acmd;
662 aargs[aanum++] = "-H";
663 aargs[aanum++] = uri;
665 aargs[aanum++] = "-h";
666 aargs[aanum++] = host;
667 aargs[aanum++] = "-p";
668 aargs[aanum++] = port;
670 aargs[aanum++] = "-D";
671 aargs[aanum++] = manager;
672 aargs[aanum++] = "-w";
673 aargs[aanum++] = passwd;
674 aargs[aanum++] = "-l";
675 aargs[aanum++] = aloops;
676 aargs[aanum++] = "-L";
677 aargs[aanum++] = outerloops;
678 aargs[aanum++] = "-r";
679 aargs[aanum++] = retries;
680 aargs[aanum++] = "-t";
681 aargs[aanum++] = delay;
683 aargs[aanum++] = friendlyOpt;
686 aargs[aanum++] = "-C";
689 aargs[aanum++] = "-i";
690 aargs[aanum++] = ignore;
692 aargs[aanum++] = "-f";
693 aargs[aanum++] = NULL; /* will hold the add data file */
694 aargs[aanum++] = NULL;
697 * generate the bind clients
701 snprintf( bcmd, sizeof bcmd, "%s" LDAP_DIRSEP BINDCMD,
703 bargs[banum++] = bcmd;
705 bargs[banum++] = "-I"; /* init on each bind */
708 bargs[banum++] = "-H";
709 bargs[banum++] = uri;
711 bargs[banum++] = "-h";
712 bargs[banum++] = host;
713 bargs[banum++] = "-p";
714 bargs[banum++] = port;
716 bargs[banum++] = "-l";
717 bargs[banum++] = bloops;
718 bargs[banum++] = "-L";
719 bargs[banum++] = outerloops;
721 bargs[banum++] = "-r";
722 bargs[banum++] = retries;
723 bargs[banum++] = "-t";
724 bargs[banum++] = delay;
727 bargs[banum++] = friendlyOpt;
730 bargs[banum++] = "-C";
733 bargs[banum++] = "-i";
734 bargs[banum++] = ignore;
737 bargs[banum++] = "-B";
738 bargs_extra = &bargs[banum++];
740 bargs[banum++] = "-D";
741 bargs[banum++] = NULL;
742 bargs[banum++] = "-w";
743 bargs[banum++] = NULL;
744 bargs[banum++] = NULL;
746 #define DOREQ(n,j) ((n) && ((maxkids > (n)) ? ((j) < maxkids ) : ((j) < (n))))
748 for ( j = 0; j < MAXREQS; j++ ) {
749 if ( DOREQ( snum, j ) ) {
752 sargs[sanum - 2] = sreqs[jj];
753 sargs[sanum - 4] = sbase[jj];
754 if ( sattrs[jj] != NULL ) {
755 sargs[sanum - 1] = "-a";
756 sargs[sanum] = sattrs[jj];
759 sargs[sanum - 1] = NULL;
761 fork_child( scmd, sargs );
764 if ( DOREQ( rnum, j ) ) {
767 rargs[ranum - 2] = rreqs[jj];
768 if ( rflts[jj] != NULL ) {
769 rargs[ranum - 1] = "-f";
770 rargs[ranum] = rflts[jj];
773 rargs[ranum - 1] = NULL;
775 fork_child( rcmd, rargs );
779 nargs[nanum - 2] = nreqs[j];
780 fork_child( ncmd, nargs );
784 margs[manum - 4] = mdn[j];
785 margs[manum - 2] = mreqs[j];
786 fork_child( mcmd, margs );
790 aargs[aanum - 2] = afiles[j];
791 fork_child( acmd, aargs );
794 if ( DOREQ( bnum, j ) ) {
798 int n = ((double)nextra)*rand()/(RAND_MAX + 1.0);
801 for ( e = extra; n-- > 0; e = e->next )
803 *bargs_extra = e->action;
806 if ( battrs[jj] != NULL ) {
807 bargs[banum - 4] = manager ? manager : "";
808 bargs[banum - 2] = passwd ? passwd : "";
810 bargs[banum - 1] = "-b";
811 bargs[banum] = breqs[jj];
812 bargs[banum + 1] = "-f";
813 bargs[banum + 2] = bcreds[jj];
814 bargs[banum + 3] = "-a";
815 bargs[banum + 4] = battrs[jj];
817 bargs[banum - 4] = breqs[jj];
818 bargs[banum - 2] = bcreds[jj];
819 bargs[banum - 1] = NULL;
822 fork_child( bcmd, bargs );
823 bargs[banum - 1] = NULL;
829 exit( EXIT_SUCCESS );
833 get_file_name( char *dirname, char *filename )
835 char buf[MAXPATHLEN];
837 snprintf( buf, sizeof buf, "%s" LDAP_DIRSEP "%s",
839 return( strdup( buf ));
844 get_search_filters( char *filename, char *filters[], char *attrs[], char *bases[] )
849 if ( (fp = fopen( filename, "r" )) != NULL ) {
852 while (( filter < MAXREQS ) && ( fgets( line, BUFSIZ, fp ))) {
855 if (( nl = strchr( line, '\r' )) || ( nl = strchr( line, '\n' )))
857 bases[filter] = ArgDup( line );
858 fgets( line, BUFSIZ, fp );
859 if (( nl = strchr( line, '\r' )) || ( nl = strchr( line, '\n' )))
862 filters[filter] = ArgDup( line );
864 if ( filters[filter][0] == '+') {
865 char *sep = strchr( filters[filter], ':' );
868 attrs[ filter ] = &filters[ filter ][ 1 ];
870 /* NOTE: don't free this! */
871 filters[ filter ] = &sep[ 1 ];
875 attrs[ filter] = NULL;
889 get_read_entries( char *filename, char *entries[], char *filters[] )
894 if ( (fp = fopen( filename, "r" )) != NULL ) {
897 while (( entry < MAXREQS ) && ( fgets( line, BUFSIZ, fp ))) {
900 if (( nl = strchr( line, '\r' )) || ( nl = strchr( line, '\n' )))
902 if ( filters != NULL && line[0] == '+' ) {
905 if ( ldap_url_parse( &line[1], &lud ) != LDAP_URL_SUCCESS ) {
910 if ( lud->lud_dn == NULL || lud->lud_dn[ 0 ] == '\0' ) {
911 ldap_free_urldesc( lud );
916 entries[entry] = ArgDup( lud->lud_dn );
918 if ( lud->lud_filter ) {
919 filters[entry] = ArgDup( lud->lud_filter );
922 filters[entry] = ArgDup( "(objectClass=*)" );
924 ldap_free_urldesc( lud );
927 entries[entry] = ArgDup( line );
941 fork_child( char *prog, char **args )
943 /* note: obscures global pid var; intended */
946 wait4kids( maxkids );
948 switch ( pid = fork() ) {
951 /* The __LIBASCII execvp only handles ASCII "prog",
952 * we still need to translate the arg vec ourselves.
954 { char *arg2[MAXREQS];
957 for (i=0; args[i]; i++) {
958 arg2[i] = ArgDup(args[i]);
964 execvp( prog, args );
965 tester_perror( "execvp", NULL );
966 exit( EXIT_FAILURE );
969 case -1: /* trouble */
970 tester_perror( "fork", NULL );
973 default: /* parent */
980 wait4kids( int nkidval )
984 while ( nkids >= nkidval ) {
987 if ( WIFSTOPPED(status) ) {
989 "stopping: child stopped with signal %d\n",
990 (int) WSTOPSIG(status) );
992 } else if ( WIFSIGNALED(status) ) {
994 "stopping: child terminated with signal %d%s\n",
995 (int) WTERMSIG(status),
997 WCOREDUMP(status) ? ", core dumped" : ""
1002 exit( WEXITSTATUS(status) );
1004 } else if ( WEXITSTATUS(status) != 0 ) {
1006 "stopping: child exited with status %d\n",
1007 (int) WEXITSTATUS(status) );
1008 exit( WEXITSTATUS(status) );
1018 wait4kids( int nkidval )
1022 while ( nkids >= nkidval ) {
1023 rc = WaitForMultipleObjects( nkids, children, FALSE, INFINITE );
1024 for ( i=rc - WAIT_OBJECT_0; i<nkids-1; i++)
1025 children[i] = children[i+1];
1031 fork_child( char *prog, char **args )
1035 wait4kids( maxkids );
1037 rc = _spawnvp( _P_NOWAIT, prog, args );
1040 tester_perror( "_spawnvp", NULL );
1042 children[nkids++] = (HANDLE)rc;