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;
131 char *sreqs[MAXREQS];
132 char *sattrs[MAXREQS];
133 char *sbase[MAXREQS];
135 char *sargs[MAXARGS];
137 char scmd[MAXPATHLEN];
138 char sloops[] = "18446744073709551615UL";
141 char *rreqs[MAXREQS];
143 char *rargs[MAXARGS];
144 char *rflts[MAXREQS];
146 char rcmd[MAXPATHLEN];
147 char rloops[] = "18446744073709551615UL";
149 char *afiles[MAXREQS];
151 char *aargs[MAXARGS];
153 char acmd[MAXPATHLEN];
154 char aloops[] = "18446744073709551615UL";
157 char *nreqs[MAXREQS];
159 char *nargs[MAXARGS];
161 char ncmd[MAXPATHLEN];
162 char nloops[] = "18446744073709551615UL";
165 char *mreqs[MAXREQS];
168 char *margs[MAXARGS];
170 char mcmd[MAXPATHLEN];
171 char mloops[] = "18446744073709551615UL";
174 char *breqs[MAXREQS];
175 char *bcreds[MAXREQS];
176 char *battrs[MAXREQS];
178 char *bargs[MAXARGS];
180 char bcmd[MAXPATHLEN];
181 char bloops[] = "18446744073709551615UL";
182 char **bargs_extra = NULL;
184 char *friendlyOpt = NULL;
186 char *pw_file = NULL;
188 /* extra action to do after bind... */
189 typedef struct extra_t {
191 struct extra_t *next;
194 extra_t *extra = NULL;
197 tester_init( "slapd-tester", TESTER_TESTER );
206 while ( (i = getopt( argc, argv, "AB:CD:d:FH:h:i:j:l:L:NP:p:r:t:w:Wy:" )) != EOF ) {
215 **b = ldap_str2charray( optarg, "," );
218 for ( epp = &extra; *epp; epp = &(*epp)->next )
221 for ( p = b; p[0]; p++ ) {
222 *epp = calloc( 1, sizeof( extra_t ) );
223 (*epp)->action = p[0];
235 case 'D': /* slapd manager */
236 manager = ArgDup( optarg );
239 case 'd': /* data directory */
240 dirname = strdup( optarg );
247 case 'H': /* slapd uri */
248 uri = strdup( optarg );
251 case 'h': /* slapd host */
252 host = strdup( optarg );
259 case 'j': /* the number of parallel clients */
260 if ( lutil_atoi( &maxkids, optarg ) != 0 ) {
261 usage( argv[0], 'j' );
265 case 'l': /* the number of loops per client */
266 if ( !isdigit( optarg[0] ) ) {
268 **l = ldap_str2charray( optarg, "," );
270 for ( p = l; p[0]; p++) {
275 { BER_BVC( "add=" ), aloops },
276 { BER_BVC( "bind=" ), bloops },
277 { BER_BVC( "modify=" ), mloops },
278 { BER_BVC( "modrdn=" ), nloops },
279 { BER_BVC( "read=" ), rloops },
280 { BER_BVC( "search=" ), sloops },
285 for ( c = 0; types[c].type.bv_val; c++ ) {
286 if ( strncasecmp( p[0], types[c].type.bv_val, types[c].type.bv_len ) == 0 ) {
291 if ( types[c].type.bv_val == NULL ) {
292 usage( argv[0], 'l' );
295 if ( lutil_atoi( &n, &p[0][types[c].type.bv_len] ) != 0 ) {
296 usage( argv[0], 'l' );
299 snprintf( types[c].buf, sizeof( aloops ), "%d", n );
302 ldap_charray_free( l );
304 } else if ( lutil_atoi( &loops, optarg ) != 0 ) {
305 usage( argv[0], 'l' );
309 case 'L': /* the number of outerloops per client */
310 outerloops = strdup( optarg );
317 case 'P': /* prog directory */
318 progdir = strdup( optarg );
321 case 'p': /* the servers port number */
322 port = strdup( optarg );
325 case 'r': /* the number of retries in case of error */
326 retries = strdup( optarg );
329 case 't': /* the delay in seconds between each retry */
330 delay = strdup( optarg );
333 case 'w': /* the managers passwd */
334 passwd = ArgDup( optarg );
335 memset( optarg, '*', strlen( optarg ) );
347 usage( argv[0], '\0' );
352 if (( dirname == NULL ) || ( port == NULL && uri == NULL ) ||
353 ( manager == NULL ) || ( passwd == NULL ) || ( progdir == NULL ))
354 usage( argv[0], '\0' );
357 children = malloc( maxkids * sizeof(HANDLE) );
359 /* get the file list */
360 if ( ( datadir = opendir( dirname )) == NULL ) {
361 fprintf( stderr, "%s: couldn't open data directory \"%s\".\n",
363 exit( EXIT_FAILURE );
366 /* look for search, read, modrdn, and add/delete files */
367 for ( file = readdir( datadir ); file; file = readdir( datadir )) {
369 if ( !strcasecmp( file->d_name, TSEARCHFILE )) {
370 sfile = get_file_name( dirname, file->d_name );
372 } else if ( !strcasecmp( file->d_name, TREADFILE )) {
373 rfile = get_file_name( dirname, file->d_name );
375 } else if ( !strcasecmp( file->d_name, TMODRDNFILE )) {
376 nfile = get_file_name( dirname, file->d_name );
378 } else if ( !strcasecmp( file->d_name, TMODIFYFILE )) {
379 mfile = get_file_name( dirname, file->d_name );
381 } else if ( !strncasecmp( file->d_name, TADDFILE, strlen( TADDFILE ))
382 && ( anum < MAXREQS )) {
383 afiles[anum++] = get_file_name( dirname, file->d_name );
385 } else if ( !strcasecmp( file->d_name, TBINDFILE )) {
386 bfile = get_file_name( dirname, file->d_name );
394 passwd = getpassphrase( _("Enter LDAP Password: ") );
396 } else if ( pw_file ) {
399 if ( lutil_get_filed_password( pw_file, &pw ) ) {
400 exit( EXIT_FAILURE );
406 /* look for search requests */
408 snum = get_search_filters( sfile, sreqs, sattrs, sbase );
411 /* look for read requests */
413 rnum = get_read_entries( rfile, rreqs, rflts );
416 /* look for modrdn requests */
418 nnum = get_read_entries( nfile, nreqs, NULL );
421 /* look for modify requests */
423 mnum = get_search_filters( mfile, mreqs, NULL, mdn );
426 /* look for bind requests */
428 bnum = get_search_filters( bfile, bcreds, battrs, breqs );
431 /* setup friendly option */
433 switch ( friendly ) {
442 /* NOTE: right now we don't need it more than twice */
448 if ( sloops[0] == '\0' ) snprintf( sloops, sizeof( sloops ), "%d", 10 * loops );
449 if ( rloops[0] == '\0' ) snprintf( rloops, sizeof( rloops ), "%d", 20 * loops );
450 if ( aloops[0] == '\0' ) snprintf( aloops, sizeof( aloops ), "%d", loops );
451 if ( nloops[0] == '\0' ) snprintf( nloops, sizeof( nloops ), "%d", loops );
452 if ( mloops[0] == '\0' ) snprintf( mloops, sizeof( mloops ), "%d", loops );
453 if ( bloops[0] == '\0' ) snprintf( bloops, sizeof( bloops ), "%d", 20 * loops );
456 * generate the search clients
460 snprintf( scmd, sizeof scmd, "%s" LDAP_DIRSEP SEARCHCMD,
462 sargs[sanum++] = scmd;
464 sargs[sanum++] = "-H";
465 sargs[sanum++] = uri;
467 sargs[sanum++] = "-h";
468 sargs[sanum++] = host;
469 sargs[sanum++] = "-p";
470 sargs[sanum++] = port;
472 sargs[sanum++] = "-D";
473 sargs[sanum++] = manager;
474 sargs[sanum++] = "-w";
475 sargs[sanum++] = passwd;
476 sargs[sanum++] = "-l";
477 sargs[sanum++] = sloops;
478 sargs[sanum++] = "-L";
479 sargs[sanum++] = outerloops;
480 sargs[sanum++] = "-r";
481 sargs[sanum++] = retries;
482 sargs[sanum++] = "-t";
483 sargs[sanum++] = delay;
485 sargs[sanum++] = friendlyOpt;
488 sargs[sanum++] = "-C";
491 sargs[sanum++] = "-A";
494 sargs[sanum++] = "-N";
497 sargs[sanum++] = "-i";
498 sargs[sanum++] = ignore;
500 sargs[sanum++] = "-b";
501 sargs[sanum++] = NULL; /* will hold the search base */
502 sargs[sanum++] = "-f";
503 sargs[sanum++] = NULL; /* will hold the search request */
505 sargs[sanum++] = NULL;
506 sargs[sanum] = NULL; /* might hold the "attr" request */
508 sargs[sanum + 1] = NULL;
511 * generate the read clients
515 snprintf( rcmd, sizeof rcmd, "%s" LDAP_DIRSEP READCMD,
517 rargs[ranum++] = rcmd;
519 rargs[ranum++] = "-H";
520 rargs[ranum++] = uri;
522 rargs[ranum++] = "-h";
523 rargs[ranum++] = host;
524 rargs[ranum++] = "-p";
525 rargs[ranum++] = port;
527 rargs[ranum++] = "-D";
528 rargs[ranum++] = manager;
529 rargs[ranum++] = "-w";
530 rargs[ranum++] = passwd;
531 rargs[ranum++] = "-l";
532 rargs[ranum++] = rloops;
533 rargs[ranum++] = "-L";
534 rargs[ranum++] = outerloops;
535 rargs[ranum++] = "-r";
536 rargs[ranum++] = retries;
537 rargs[ranum++] = "-t";
538 rargs[ranum++] = delay;
540 rargs[ranum++] = friendlyOpt;
543 rargs[ranum++] = "-C";
546 rargs[ranum++] = "-A";
549 rargs[ranum++] = "-i";
550 rargs[ranum++] = ignore;
552 rargs[ranum++] = "-e";
553 rargs[ranum++] = NULL; /* will hold the read entry */
555 rargs[ranum++] = NULL;
556 rargs[ranum] = NULL; /* might hold the filter arg */
558 rargs[ranum + 1] = NULL;
561 * generate the modrdn clients
565 snprintf( ncmd, sizeof ncmd, "%s" LDAP_DIRSEP MODRDNCMD,
567 nargs[nanum++] = ncmd;
569 nargs[nanum++] = "-H";
570 nargs[nanum++] = uri;
572 nargs[nanum++] = "-h";
573 nargs[nanum++] = host;
574 nargs[nanum++] = "-p";
575 nargs[nanum++] = port;
577 nargs[nanum++] = "-D";
578 nargs[nanum++] = manager;
579 nargs[nanum++] = "-w";
580 nargs[nanum++] = passwd;
581 nargs[nanum++] = "-l";
582 nargs[nanum++] = nloops;
583 nargs[nanum++] = "-L";
584 nargs[nanum++] = outerloops;
585 nargs[nanum++] = "-r";
586 nargs[nanum++] = retries;
587 nargs[nanum++] = "-t";
588 nargs[nanum++] = delay;
590 nargs[nanum++] = friendlyOpt;
593 nargs[nanum++] = "-C";
596 nargs[nanum++] = "-i";
597 nargs[nanum++] = ignore;
599 nargs[nanum++] = "-e";
600 nargs[nanum++] = NULL; /* will hold the modrdn entry */
601 nargs[nanum++] = NULL;
604 * generate the modify clients
608 snprintf( mcmd, sizeof mcmd, "%s" LDAP_DIRSEP MODIFYCMD,
610 margs[manum++] = mcmd;
612 margs[manum++] = "-H";
613 margs[manum++] = uri;
615 margs[manum++] = "-h";
616 margs[manum++] = host;
617 margs[manum++] = "-p";
618 margs[manum++] = port;
620 margs[manum++] = "-D";
621 margs[manum++] = manager;
622 margs[manum++] = "-w";
623 margs[manum++] = passwd;
624 margs[manum++] = "-l";
625 margs[manum++] = mloops;
626 margs[manum++] = "-L";
627 margs[manum++] = outerloops;
628 margs[manum++] = "-r";
629 margs[manum++] = retries;
630 margs[manum++] = "-t";
631 margs[manum++] = delay;
633 margs[manum++] = friendlyOpt;
636 margs[manum++] = "-C";
639 margs[manum++] = "-i";
640 margs[manum++] = ignore;
642 margs[manum++] = "-e";
643 margs[manum++] = NULL; /* will hold the modify entry */
644 margs[manum++] = "-a";;
645 margs[manum++] = NULL; /* will hold the ava */
646 margs[manum++] = NULL;
649 * generate the add/delete clients
653 snprintf( acmd, sizeof acmd, "%s" LDAP_DIRSEP ADDCMD,
655 aargs[aanum++] = acmd;
657 aargs[aanum++] = "-H";
658 aargs[aanum++] = uri;
660 aargs[aanum++] = "-h";
661 aargs[aanum++] = host;
662 aargs[aanum++] = "-p";
663 aargs[aanum++] = port;
665 aargs[aanum++] = "-D";
666 aargs[aanum++] = manager;
667 aargs[aanum++] = "-w";
668 aargs[aanum++] = passwd;
669 aargs[aanum++] = "-l";
670 aargs[aanum++] = aloops;
671 aargs[aanum++] = "-L";
672 aargs[aanum++] = outerloops;
673 aargs[aanum++] = "-r";
674 aargs[aanum++] = retries;
675 aargs[aanum++] = "-t";
676 aargs[aanum++] = delay;
678 aargs[aanum++] = friendlyOpt;
681 aargs[aanum++] = "-C";
684 aargs[aanum++] = "-i";
685 aargs[aanum++] = ignore;
687 aargs[aanum++] = "-f";
688 aargs[aanum++] = NULL; /* will hold the add data file */
689 aargs[aanum++] = NULL;
692 * generate the bind clients
696 snprintf( bcmd, sizeof bcmd, "%s" LDAP_DIRSEP BINDCMD,
698 bargs[banum++] = bcmd;
699 bargs[banum++] = "-I"; /* don't init on each bind */
701 bargs[banum++] = "-H";
702 bargs[banum++] = uri;
704 bargs[banum++] = "-h";
705 bargs[banum++] = host;
706 bargs[banum++] = "-p";
707 bargs[banum++] = port;
709 bargs[banum++] = "-l";
710 bargs[banum++] = bloops;
711 bargs[banum++] = "-L";
712 bargs[banum++] = outerloops;
714 bargs[banum++] = "-r";
715 bargs[banum++] = retries;
716 bargs[banum++] = "-t";
717 bargs[banum++] = delay;
720 bargs[banum++] = friendlyOpt;
723 bargs[banum++] = "-C";
726 bargs[banum++] = "-i";
727 bargs[banum++] = ignore;
730 bargs[banum++] = "-B";
731 bargs_extra = &bargs[banum++];
733 bargs[banum++] = "-D";
734 bargs[banum++] = NULL;
735 bargs[banum++] = "-w";
736 bargs[banum++] = NULL;
737 bargs[banum++] = NULL;
739 #define DOREQ(n,j) ((n) && ((maxkids > (n)) ? ((j) < maxkids ) : ((j) < (n))))
741 for ( j = 0; j < MAXREQS; j++ ) {
742 if ( DOREQ( snum, j ) ) {
745 sargs[sanum - 2] = sreqs[jj];
746 sargs[sanum - 4] = sbase[jj];
747 if ( sattrs[jj] != NULL ) {
748 sargs[sanum - 1] = "-a";
749 sargs[sanum] = sattrs[jj];
752 sargs[sanum - 1] = NULL;
754 fork_child( scmd, sargs );
757 if ( DOREQ( rnum, j ) ) {
760 rargs[ranum - 2] = rreqs[jj];
761 if ( rflts[jj] != NULL ) {
762 rargs[ranum - 1] = "-f";
763 rargs[ranum] = rflts[jj];
766 rargs[ranum - 1] = NULL;
768 fork_child( rcmd, rargs );
772 nargs[nanum - 2] = nreqs[j];
773 fork_child( ncmd, nargs );
777 margs[manum - 4] = mdn[j];
778 margs[manum - 2] = mreqs[j];
779 fork_child( mcmd, margs );
783 aargs[aanum - 2] = afiles[j];
784 fork_child( acmd, aargs );
787 if ( DOREQ( bnum, j ) ) {
791 int n = ((double)nextra)*rand()/(RAND_MAX + 1.0);
794 for ( e = extra; n-- > 0; e = e->next )
796 *bargs_extra = e->action;
799 if ( battrs[jj] != NULL ) {
800 bargs[banum - 4] = manager ? manager : "";
801 bargs[banum - 2] = passwd ? passwd : "";
803 bargs[banum - 1] = "-b";
804 bargs[banum] = breqs[jj];
805 bargs[banum + 1] = "-f";
806 bargs[banum + 2] = bcreds[jj];
807 bargs[banum + 3] = "-a";
808 bargs[banum + 4] = battrs[jj];
810 bargs[banum - 4] = breqs[jj];
811 bargs[banum - 2] = bcreds[jj];
812 bargs[banum - 1] = NULL;
815 fork_child( bcmd, bargs );
816 bargs[banum - 1] = NULL;
822 exit( EXIT_SUCCESS );
826 get_file_name( char *dirname, char *filename )
828 char buf[MAXPATHLEN];
830 snprintf( buf, sizeof buf, "%s" LDAP_DIRSEP "%s",
832 return( strdup( buf ));
837 get_search_filters( char *filename, char *filters[], char *attrs[], char *bases[] )
842 if ( (fp = fopen( filename, "r" )) != NULL ) {
845 while (( filter < MAXREQS ) && ( fgets( line, BUFSIZ, fp ))) {
848 if (( nl = strchr( line, '\r' )) || ( nl = strchr( line, '\n' )))
850 bases[filter] = ArgDup( line );
851 fgets( line, BUFSIZ, fp );
852 if (( nl = strchr( line, '\r' )) || ( nl = strchr( line, '\n' )))
855 filters[filter] = ArgDup( line );
857 if ( filters[filter][0] == '+') {
858 char *sep = strchr( filters[filter], ':' );
861 attrs[ filter ] = &filters[ filter ][ 1 ];
863 /* NOTE: don't free this! */
864 filters[ filter ] = &sep[ 1 ];
868 attrs[ filter] = NULL;
882 get_read_entries( char *filename, char *entries[], char *filters[] )
887 if ( (fp = fopen( filename, "r" )) != NULL ) {
890 while (( entry < MAXREQS ) && ( fgets( line, BUFSIZ, fp ))) {
893 if (( nl = strchr( line, '\r' )) || ( nl = strchr( line, '\n' )))
895 if ( filters != NULL && line[0] == '+' ) {
898 if ( ldap_url_parse( &line[1], &lud ) != LDAP_URL_SUCCESS ) {
903 if ( lud->lud_dn == NULL || lud->lud_dn[ 0 ] == '\0' ) {
904 ldap_free_urldesc( lud );
909 entries[entry] = ArgDup( lud->lud_dn );
911 if ( lud->lud_filter ) {
912 filters[entry] = ArgDup( lud->lud_filter );
915 filters[entry] = ArgDup( "(objectClass=*)" );
917 ldap_free_urldesc( lud );
920 entries[entry] = ArgDup( line );
934 fork_child( char *prog, char **args )
936 /* note: obscures global pid var; intended */
939 wait4kids( maxkids );
941 switch ( pid = fork() ) {
944 /* The __LIBASCII execvp only handles ASCII "prog",
945 * we still need to translate the arg vec ourselves.
947 { char *arg2[MAXREQS];
950 for (i=0; args[i]; i++) {
951 arg2[i] = ArgDup(args[i]);
957 execvp( prog, args );
958 tester_perror( "execvp", NULL );
959 exit( EXIT_FAILURE );
962 case -1: /* trouble */
963 tester_perror( "fork", NULL );
966 default: /* parent */
973 wait4kids( int nkidval )
977 while ( nkids >= nkidval ) {
980 if ( WIFSTOPPED(status) ) {
982 "stopping: child stopped with signal %d\n",
983 (int) WSTOPSIG(status) );
985 } else if ( WIFSIGNALED(status) ) {
987 "stopping: child terminated with signal %d%s\n",
988 (int) WTERMSIG(status),
990 WCOREDUMP(status) ? ", core dumped" : ""
995 exit( WEXITSTATUS(status) );
997 } else if ( WEXITSTATUS(status) != 0 ) {
999 "stopping: child exited with status %d\n",
1000 (int) WEXITSTATUS(status) );
1001 exit( WEXITSTATUS(status) );
1011 wait4kids( int nkidval )
1015 while ( nkids >= nkidval ) {
1016 rc = WaitForMultipleObjects( nkids, children, FALSE, INFINITE );
1017 for ( i=rc - WAIT_OBJECT_0; i<nkids-1; i++)
1018 children[i] = children[i+1];
1024 fork_child( char *prog, char **args )
1028 wait4kids( maxkids );
1030 rc = _spawnvp( _P_NOWAIT, prog, args );
1033 tester_perror( "_spawnvp", NULL );
1035 children[nkids++] = (HANDLE)rc;