2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4 * Copyright 1999-2015 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"
49 #define SEARCHCMD "slapd-search" EXE
50 #define READCMD "slapd-read" EXE
51 #define ADDCMD "slapd-addel" EXE
52 #define MODRDNCMD "slapd-modrdn" EXE
53 #define MODIFYCMD "slapd-modify" EXE
54 #define BINDCMD "slapd-bind" EXE
58 #define OUTERLOOPS "1"
61 #define TSEARCHFILE "do_search.0"
62 #define TREADFILE "do_read.0"
63 #define TADDFILE "do_add."
64 #define TMODRDNFILE "do_modrdn.0"
65 #define TMODIFYFILE "do_modify.0"
66 #define TBINDFILE "do_bind.0"
68 static char *get_file_name( char *dirname, char *filename );
69 static int get_search_filters( char *filename, char *filters[], char *attrs[], char *bases[], LDAPURLDesc *luds[] );
70 static int get_read_entries( char *filename, char *entries[], char *filters[] );
71 static void fork_child( char *prog, char **args );
72 static void wait4kids( int nkidval );
74 static int maxkids = 20;
78 static HANDLE *children;
79 static char argbuf[BUFSIZ];
80 #define ArgDup(x) strdup(strcat(strcat(strcpy(argbuf,"\""),x),"\""))
82 #define ArgDup(x) strdup(x)
86 usage( char *name, char opt )
89 fprintf( stderr, "%s: unable to handle option \'%c\'\n\n",
95 "-H <uri> | ([-h <host>] -p <port>) "
101 "[-l {<loops>|<type>=<loops>[,...]}] "
111 exit( EXIT_FAILURE );
115 main( int argc, char **argv )
119 char *host = "localhost";
121 char *manager = NULL;
123 char *dirname = NULL;
124 char *progdir = NULL;
126 char *outerloops = OUTERLOOPS;
127 char *retries = RETRIES;
139 char *sreqs[MAXREQS];
140 char *sattrs[MAXREQS];
141 char *sbase[MAXREQS];
142 LDAPURLDesc *slud[MAXREQS];
144 char *sargs[MAXARGS];
147 char scmd[MAXPATHLEN];
149 char swampopt[sizeof("-SSS")];
150 /* static so that its address can be used in initializer below. */
151 static char sloops[LDAP_PVT_INTTYPE_CHARS(unsigned long)];
154 char *rreqs[MAXREQS];
156 char *rargs[MAXARGS];
157 char *rflts[MAXREQS];
160 char rcmd[MAXPATHLEN];
161 static char rloops[LDAP_PVT_INTTYPE_CHARS(unsigned long)];
163 char *afiles[MAXREQS];
165 char *aargs[MAXARGS];
167 char acmd[MAXPATHLEN];
168 static char aloops[LDAP_PVT_INTTYPE_CHARS(unsigned long)];
171 char *nreqs[MAXREQS];
173 char *nargs[MAXARGS];
175 char ncmd[MAXPATHLEN];
176 static char nloops[LDAP_PVT_INTTYPE_CHARS(unsigned long)];
179 char *mreqs[MAXREQS];
182 char *margs[MAXARGS];
184 char mcmd[MAXPATHLEN];
185 static char mloops[LDAP_PVT_INTTYPE_CHARS(unsigned long)];
188 char *breqs[MAXREQS];
189 char *bcreds[MAXREQS];
190 char *battrs[MAXREQS];
192 char *bargs[MAXARGS];
194 char bcmd[MAXPATHLEN];
195 static char bloops[LDAP_PVT_INTTYPE_CHARS(unsigned long)];
196 char **bargs_extra = NULL;
198 char *friendlyOpt = NULL;
200 char *pw_file = NULL;
202 /* extra action to do after bind... */
203 typedef struct extra_t {
205 struct extra_t *next;
208 extra_t *extra = NULL;
211 tester_init( "slapd-tester", TESTER_TESTER );
220 while ( ( i = getopt( argc, argv, "AB:CD:d:FH:h:Ii:j:L:l:NP:p:r:St:Ww:y:" ) ) != EOF )
229 **b = ldap_str2charray( optarg, "," );
232 for ( epp = &extra; *epp; epp = &(*epp)->next )
235 for ( p = b; p[0]; p++ ) {
236 *epp = calloc( 1, sizeof( extra_t ) );
237 (*epp)->action = p[0];
249 case 'D': /* slapd manager */
250 manager = ArgDup( optarg );
253 case 'd': /* data directory */
254 dirname = strdup( optarg );
261 case 'H': /* slapd uri */
262 uri = strdup( optarg );
265 case 'h': /* slapd host */
266 host = strdup( optarg );
277 case 'j': /* the number of parallel clients */
278 if ( lutil_atoi( &maxkids, optarg ) != 0 ) {
279 usage( argv[0], 'j' );
283 case 'l': /* the number of loops per client */
284 if ( !isdigit( (unsigned char) optarg[0] ) ) {
286 **l = ldap_str2charray( optarg, "," );
288 for ( p = l; p[0]; p++) {
293 { BER_BVC( "add=" ), aloops },
294 { BER_BVC( "bind=" ), bloops },
295 { BER_BVC( "modify=" ), mloops },
296 { BER_BVC( "modrdn=" ), nloops },
297 { BER_BVC( "read=" ), rloops },
298 { BER_BVC( "search=" ), sloops },
303 for ( c = 0; types[c].type.bv_val; c++ ) {
304 if ( strncasecmp( p[0], types[c].type.bv_val, types[c].type.bv_len ) == 0 ) {
309 if ( types[c].type.bv_val == NULL ) {
310 usage( argv[0], 'l' );
313 if ( lutil_atoi( &n, &p[0][types[c].type.bv_len] ) != 0 ) {
314 usage( argv[0], 'l' );
317 snprintf( types[c].buf, sizeof( aloops ), "%d", n );
320 ldap_charray_free( l );
322 } else if ( lutil_atoi( &loops, optarg ) != 0 ) {
323 usage( argv[0], 'l' );
327 case 'L': /* the number of outerloops per client */
328 outerloops = strdup( optarg );
335 case 'P': /* prog directory */
336 progdir = strdup( optarg );
339 case 'p': /* the servers port number */
340 port = strdup( optarg );
343 case 'r': /* the number of retries in case of error */
344 retries = strdup( optarg );
351 case 't': /* the delay in seconds between each retry */
352 delay = strdup( optarg );
355 case 'w': /* the managers passwd */
356 passwd = ArgDup( optarg );
357 memset( optarg, '*', strlen( optarg ) );
369 usage( argv[0], '\0' );
374 if (( dirname == NULL ) || ( port == NULL && uri == NULL ) ||
375 ( manager == NULL ) || ( passwd == NULL ) || ( progdir == NULL ))
377 usage( argv[0], '\0' );
381 children = malloc( maxkids * sizeof(HANDLE) );
383 /* get the file list */
384 if ( ( datadir = opendir( dirname )) == NULL ) {
385 fprintf( stderr, "%s: couldn't open data directory \"%s\".\n",
387 exit( EXIT_FAILURE );
390 /* look for search, read, modrdn, and add/delete files */
391 for ( file = readdir( datadir ); file; file = readdir( datadir )) {
393 if ( !strcasecmp( file->d_name, TSEARCHFILE )) {
394 sfile = get_file_name( dirname, file->d_name );
396 } else if ( !strcasecmp( file->d_name, TREADFILE )) {
397 rfile = get_file_name( dirname, file->d_name );
399 } else if ( !strcasecmp( file->d_name, TMODRDNFILE )) {
400 nfile = get_file_name( dirname, file->d_name );
402 } else if ( !strcasecmp( file->d_name, TMODIFYFILE )) {
403 mfile = get_file_name( dirname, file->d_name );
405 } else if ( !strncasecmp( file->d_name, TADDFILE, strlen( TADDFILE ))
406 && ( anum < MAXREQS )) {
407 afiles[anum++] = get_file_name( dirname, file->d_name );
409 } else if ( !strcasecmp( file->d_name, TBINDFILE )) {
410 bfile = get_file_name( dirname, file->d_name );
418 passwd = getpassphrase( _("Enter LDAP Password: ") );
420 } else if ( pw_file ) {
423 if ( lutil_get_filed_password( pw_file, &pw ) ) {
424 exit( EXIT_FAILURE );
430 if ( !sfile && !rfile && !nfile && !mfile && !bfile && !anum ) {
431 fprintf( stderr, "no data files found.\n" );
432 exit( EXIT_FAILURE );
435 /* look for search requests */
437 snum = get_search_filters( sfile, sreqs, sattrs, sbase, slud );
440 "unable to parse file \"%s\" line %d\n",
441 sfile, -2*(snum + 1));
442 exit( EXIT_FAILURE );
446 /* look for read requests */
448 rnum = get_read_entries( rfile, rreqs, rflts );
451 "unable to parse file \"%s\" line %d\n",
452 rfile, -2*(rnum + 1) );
453 exit( EXIT_FAILURE );
457 /* look for modrdn requests */
459 nnum = get_read_entries( nfile, nreqs, NULL );
462 "unable to parse file \"%s\" line %d\n",
463 nfile, -2*(nnum + 1) );
464 exit( EXIT_FAILURE );
468 /* look for modify requests */
470 mnum = get_search_filters( mfile, mreqs, NULL, mdn, NULL );
473 "unable to parse file \"%s\" line %d\n",
474 mfile, -2*(mnum + 1) );
475 exit( EXIT_FAILURE );
479 /* look for bind requests */
481 bnum = get_search_filters( bfile, bcreds, battrs, breqs, NULL );
484 "unable to parse file \"%s\" line %d\n",
485 bfile, -2*(bnum + 1) );
486 exit( EXIT_FAILURE );
490 /* setup friendly option */
491 switch ( friendly ) {
500 /* NOTE: right now we don't need it more than twice */
506 /* setup swamp option */
509 if ( swamp > 3 ) swamp = 3;
510 swampopt[swamp + 1] = '\0';
511 for ( ; swamp-- > 0; ) swampopt[swamp + 1] = 'S';
514 /* setup loop options */
515 if ( sloops[0] == '\0' ) snprintf( sloops, sizeof( sloops ), "%d", 10 * loops );
516 if ( rloops[0] == '\0' ) snprintf( rloops, sizeof( rloops ), "%d", 20 * loops );
517 if ( aloops[0] == '\0' ) snprintf( aloops, sizeof( aloops ), "%d", loops );
518 if ( nloops[0] == '\0' ) snprintf( nloops, sizeof( nloops ), "%d", loops );
519 if ( mloops[0] == '\0' ) snprintf( mloops, sizeof( mloops ), "%d", loops );
520 if ( bloops[0] == '\0' ) snprintf( bloops, sizeof( bloops ), "%d", 20 * loops );
523 * generate the search clients
527 snprintf( scmd, sizeof scmd, "%s" LDAP_DIRSEP SEARCHCMD,
529 sargs[sanum++] = scmd;
531 sargs[sanum++] = "-H";
532 sargs[sanum++] = uri;
534 sargs[sanum++] = "-h";
535 sargs[sanum++] = host;
536 sargs[sanum++] = "-p";
537 sargs[sanum++] = port;
539 sargs[sanum++] = "-D";
540 sargs[sanum++] = manager;
541 sargs[sanum++] = "-w";
542 sargs[sanum++] = passwd;
543 sargs[sanum++] = "-l";
544 sargs[sanum++] = sloops;
545 sargs[sanum++] = "-L";
546 sargs[sanum++] = outerloops;
547 sargs[sanum++] = "-r";
548 sargs[sanum++] = retries;
549 sargs[sanum++] = "-t";
550 sargs[sanum++] = delay;
552 sargs[sanum++] = friendlyOpt;
555 sargs[sanum++] = "-C";
558 sargs[sanum++] = "-A";
561 sargs[sanum++] = "-N";
564 sargs[sanum++] = "-i";
565 sargs[sanum++] = ignore;
568 sargs[sanum++] = swampopt;
570 sargs[sanum++] = "-b";
571 sargs[sanum++] = NULL; /* will hold the search base */
572 sargs[sanum++] = "-s";
573 sargs[sanum++] = NULL; /* will hold the search scope */
574 sargs[sanum++] = "-f";
575 sargs[sanum++] = NULL; /* will hold the search request */
577 sargs[sanum++] = NULL;
578 sargs[sanum++] = NULL; /* might hold the "attr" request */
584 * generate the read clients
588 snprintf( rcmd, sizeof rcmd, "%s" LDAP_DIRSEP READCMD,
590 rargs[ranum++] = rcmd;
592 rargs[ranum++] = "-H";
593 rargs[ranum++] = uri;
595 rargs[ranum++] = "-h";
596 rargs[ranum++] = host;
597 rargs[ranum++] = "-p";
598 rargs[ranum++] = port;
600 rargs[ranum++] = "-D";
601 rargs[ranum++] = manager;
602 rargs[ranum++] = "-w";
603 rargs[ranum++] = passwd;
604 rargs[ranum++] = "-l";
605 rargs[ranum++] = rloops;
606 rargs[ranum++] = "-L";
607 rargs[ranum++] = outerloops;
608 rargs[ranum++] = "-r";
609 rargs[ranum++] = retries;
610 rargs[ranum++] = "-t";
611 rargs[ranum++] = delay;
613 rargs[ranum++] = friendlyOpt;
616 rargs[ranum++] = "-C";
619 rargs[ranum++] = "-A";
622 rargs[ranum++] = "-i";
623 rargs[ranum++] = ignore;
626 rargs[ranum++] = swampopt;
628 rargs[ranum++] = "-e";
629 rargs[ranum++] = NULL; /* will hold the read entry */
631 rargs[ranum++] = NULL;
632 rargs[ranum++] = NULL; /* might hold the filter arg */
638 * generate the modrdn clients
642 snprintf( ncmd, sizeof ncmd, "%s" LDAP_DIRSEP MODRDNCMD,
644 nargs[nanum++] = ncmd;
646 nargs[nanum++] = "-H";
647 nargs[nanum++] = uri;
649 nargs[nanum++] = "-h";
650 nargs[nanum++] = host;
651 nargs[nanum++] = "-p";
652 nargs[nanum++] = port;
654 nargs[nanum++] = "-D";
655 nargs[nanum++] = manager;
656 nargs[nanum++] = "-w";
657 nargs[nanum++] = passwd;
658 nargs[nanum++] = "-l";
659 nargs[nanum++] = nloops;
660 nargs[nanum++] = "-L";
661 nargs[nanum++] = outerloops;
662 nargs[nanum++] = "-r";
663 nargs[nanum++] = retries;
664 nargs[nanum++] = "-t";
665 nargs[nanum++] = delay;
667 nargs[nanum++] = friendlyOpt;
670 nargs[nanum++] = "-C";
673 nargs[nanum++] = "-i";
674 nargs[nanum++] = ignore;
676 nargs[nanum++] = "-e";
677 nargs[nanum++] = NULL; /* will hold the modrdn entry */
681 * generate the modify clients
685 snprintf( mcmd, sizeof mcmd, "%s" LDAP_DIRSEP MODIFYCMD,
687 margs[manum++] = mcmd;
689 margs[manum++] = "-H";
690 margs[manum++] = uri;
692 margs[manum++] = "-h";
693 margs[manum++] = host;
694 margs[manum++] = "-p";
695 margs[manum++] = port;
697 margs[manum++] = "-D";
698 margs[manum++] = manager;
699 margs[manum++] = "-w";
700 margs[manum++] = passwd;
701 margs[manum++] = "-l";
702 margs[manum++] = mloops;
703 margs[manum++] = "-L";
704 margs[manum++] = outerloops;
705 margs[manum++] = "-r";
706 margs[manum++] = retries;
707 margs[manum++] = "-t";
708 margs[manum++] = delay;
710 margs[manum++] = friendlyOpt;
713 margs[manum++] = "-C";
716 margs[manum++] = "-i";
717 margs[manum++] = ignore;
719 margs[manum++] = "-e";
720 margs[manum++] = NULL; /* will hold the modify entry */
721 margs[manum++] = "-a";;
722 margs[manum++] = NULL; /* will hold the ava */
726 * generate the add/delete clients
730 snprintf( acmd, sizeof acmd, "%s" LDAP_DIRSEP ADDCMD,
732 aargs[aanum++] = acmd;
734 aargs[aanum++] = "-H";
735 aargs[aanum++] = uri;
737 aargs[aanum++] = "-h";
738 aargs[aanum++] = host;
739 aargs[aanum++] = "-p";
740 aargs[aanum++] = port;
742 aargs[aanum++] = "-D";
743 aargs[aanum++] = manager;
744 aargs[aanum++] = "-w";
745 aargs[aanum++] = passwd;
746 aargs[aanum++] = "-l";
747 aargs[aanum++] = aloops;
748 aargs[aanum++] = "-L";
749 aargs[aanum++] = outerloops;
750 aargs[aanum++] = "-r";
751 aargs[aanum++] = retries;
752 aargs[aanum++] = "-t";
753 aargs[aanum++] = delay;
755 aargs[aanum++] = friendlyOpt;
758 aargs[aanum++] = "-C";
761 aargs[aanum++] = "-i";
762 aargs[aanum++] = ignore;
764 aargs[aanum++] = "-f";
765 aargs[aanum++] = NULL; /* will hold the add data file */
769 * generate the bind clients
773 snprintf( bcmd, sizeof bcmd, "%s" LDAP_DIRSEP BINDCMD,
775 bargs[banum++] = bcmd;
777 bargs[banum++] = "-I"; /* init on each bind */
780 bargs[banum++] = "-H";
781 bargs[banum++] = uri;
783 bargs[banum++] = "-h";
784 bargs[banum++] = host;
785 bargs[banum++] = "-p";
786 bargs[banum++] = port;
788 bargs[banum++] = "-l";
789 bargs[banum++] = bloops;
790 bargs[banum++] = "-L";
791 bargs[banum++] = outerloops;
793 bargs[banum++] = "-r";
794 bargs[banum++] = retries;
795 bargs[banum++] = "-t";
796 bargs[banum++] = delay;
799 bargs[banum++] = friendlyOpt;
802 bargs[banum++] = "-C";
805 bargs[banum++] = "-i";
806 bargs[banum++] = ignore;
809 bargs[banum++] = "-B";
810 bargs_extra = &bargs[banum++];
812 bargs[banum++] = "-D";
813 bargs[banum++] = NULL;
814 bargs[banum++] = "-w";
815 bargs[banum++] = NULL;
818 #define DOREQ(n,j) ((n) && ((maxkids > (n)) ? ((j) < maxkids ) : ((j) < (n))))
820 for ( j = 0; j < MAXREQS; j++ ) {
822 if ( DOREQ( snum, j ) ) {
824 int x = sanum - sextra_args;
827 if ( sbase[jj] != NULL ) {
828 sargs[sanum - 7] = sbase[jj];
831 sargs[sanum - 7] = slud[jj]->lud_dn;
835 if ( slud[jj] != NULL ) {
836 sargs[sanum - 5] = (char *)ldap_pvt_scope2str( slud[jj]->lud_scope );
839 sargs[sanum - 5] = "sub";
843 if ( sreqs[jj] != NULL ) {
844 sargs[sanum - 3] = sreqs[jj];
846 } else if ( slud[jj]->lud_filter != NULL ) {
847 sargs[sanum - 3] = slud[jj]->lud_filter;
850 sargs[sanum - 3] = "(objectClass=*)";
857 if ( sattrs[jj] != NULL ) {
859 sargs[x++] = sattrs[jj];
863 if ( slud[jj] != NULL && slud[jj]->lud_attrs != NULL ) {
866 for ( i = 0; slud[jj]->lud_attrs[ i ] != NULL && x + i < MAXARGS - 1; i++ ) {
867 sargs[x + i] = slud[jj]->lud_attrs[ i ];
872 fork_child( scmd, sargs );
876 if ( DOREQ( rnum, j ) ) {
878 int x = ranum - rextra_args;
880 rargs[ranum - 3] = rreqs[jj];
881 if ( rflts[jj] != NULL ) {
883 rargs[x++] = rflts[jj];
886 fork_child( rcmd, rargs );
891 nargs[nanum - 1] = nreqs[j];
892 fork_child( ncmd, nargs );
897 margs[manum - 3] = mdn[j];
898 margs[manum - 1] = mreqs[j];
899 fork_child( mcmd, margs );
904 aargs[aanum - 1] = afiles[j];
905 fork_child( acmd, aargs );
909 if ( DOREQ( bnum, j ) ) {
913 int n = ((double)nextra)*rand()/(RAND_MAX + 1.0);
916 for ( e = extra; n-- > 0; e = e->next )
918 *bargs_extra = e->action;
921 if ( battrs[jj] != NULL ) {
922 bargs[banum - 3] = manager ? manager : "";
923 bargs[banum - 1] = passwd ? passwd : "";
925 bargs[banum + 0] = "-b";
926 bargs[banum + 1] = breqs[jj];
927 bargs[banum + 2] = "-f";
928 bargs[banum + 3] = bcreds[jj];
929 bargs[banum + 4] = "-a";
930 bargs[banum + 5] = battrs[jj];
931 bargs[banum + 6] = NULL;
934 bargs[banum - 3] = breqs[jj];
935 bargs[banum - 1] = bcreds[jj];
939 fork_child( bcmd, bargs );
946 exit( EXIT_SUCCESS );
950 get_file_name( char *dirname, char *filename )
952 char buf[MAXPATHLEN];
954 snprintf( buf, sizeof buf, "%s" LDAP_DIRSEP "%s",
956 return( strdup( buf ));
961 get_search_filters( char *filename, char *filters[], char *attrs[], char *bases[], LDAPURLDesc *luds[] )
966 if ( (fp = fopen( filename, "r" )) != NULL ) {
969 while (( filter < MAXREQS ) && ( fgets( line, BUFSIZ, fp ))) {
973 if (( nl = strchr( line, '\r' )) || ( nl = strchr( line, '\n' )))
976 if ( luds ) luds[filter] = NULL;
978 if ( luds && strncmp( line, "ldap:///", STRLENOF( "ldap:///" ) ) == 0 ) {
982 bases[filter] = NULL;
983 if ( ldap_url_parse( line, &lud ) != LDAP_URL_SUCCESS ) {
984 filter = -filter - 1;
988 if ( lud->lud_dn == NULL || lud->lud_exts != NULL ) {
989 filter = -filter - 1;
990 ldap_free_urldesc( lud );
997 bases[filter] = ArgDup( line );
999 if ( fgets( line, BUFSIZ, fp ) == NULL )
1001 if (( nl = strchr( line, '\r' )) || ( nl = strchr( line, '\n' )))
1004 filters[filter] = ArgDup( line );
1006 if ( filters[filter][0] == '+') {
1007 char *sep = strchr( filters[filter], ':' );
1009 attrs[ filter ] = &filters[ filter ][ 1 ];
1010 if ( sep != NULL ) {
1012 /* NOTE: don't free this! */
1013 filters[ filter ] = &sep[ 1 ];
1017 attrs[ filter ] = NULL;
1031 get_read_entries( char *filename, char *entries[], char *filters[] )
1036 if ( (fp = fopen( filename, "r" )) != NULL ) {
1039 while (( entry < MAXREQS ) && ( fgets( line, BUFSIZ, fp ))) {
1042 if (( nl = strchr( line, '\r' )) || ( nl = strchr( line, '\n' )))
1044 if ( filters != NULL && line[0] == '+' ) {
1047 if ( ldap_url_parse( &line[1], &lud ) != LDAP_URL_SUCCESS ) {
1052 if ( lud->lud_dn == NULL || lud->lud_dn[ 0 ] == '\0' ) {
1053 ldap_free_urldesc( lud );
1058 entries[entry] = ArgDup( lud->lud_dn );
1060 if ( lud->lud_filter ) {
1061 filters[entry] = ArgDup( lud->lud_filter );
1064 filters[entry] = ArgDup( "(objectClass=*)" );
1066 ldap_free_urldesc( lud );
1069 if ( filters != NULL )
1070 filters[entry] = NULL;
1072 entries[entry] = ArgDup( line );
1084 #ifndef HAVE_WINSOCK
1086 fork_child( char *prog, char **args )
1088 /* note: obscures global pid var; intended */
1091 wait4kids( maxkids );
1093 switch ( pid = fork() ) {
1096 /* The __LIBASCII execvp only handles ASCII "prog",
1097 * we still need to translate the arg vec ourselves.
1099 { char *arg2[MAXREQS];
1102 for (i=0; args[i]; i++) {
1103 arg2[i] = ArgDup(args[i]);
1109 execvp( prog, args );
1110 tester_perror( "execvp", NULL );
1112 for (i=0; args[i]; i++);
1113 fprintf(stderr,"%d args\n", i);
1114 for (i=0; args[i]; i++)
1115 fprintf(stderr,"%d %s\n", i, args[i]);
1118 exit( EXIT_FAILURE );
1121 case -1: /* trouble */
1122 tester_perror( "fork", NULL );
1125 default: /* parent */
1132 wait4kids( int nkidval )
1136 while ( nkids >= nkidval ) {
1137 pid_t pid = wait( &status );
1139 if ( WIFSTOPPED(status) ) {
1141 "stopping: child PID=%ld stopped with signal %d\n",
1142 (long) pid, (int) WSTOPSIG(status) );
1144 } else if ( WIFSIGNALED(status) ) {
1146 "stopping: child PID=%ld terminated with signal %d%s\n",
1147 (long) pid, (int) WTERMSIG(status),
1149 WCOREDUMP(status) ? ", core dumped" : ""
1154 exit( WEXITSTATUS(status) );
1156 } else if ( WEXITSTATUS(status) != 0 ) {
1158 "stopping: child PID=%ld exited with status %d\n",
1159 (long) pid, (int) WEXITSTATUS(status) );
1160 exit( WEXITSTATUS(status) );
1170 wait4kids( int nkidval )
1174 while ( nkids >= nkidval ) {
1175 rc = WaitForMultipleObjects( nkids, children, FALSE, INFINITE );
1176 for ( i=rc - WAIT_OBJECT_0; i<nkids-1; i++)
1177 children[i] = children[i+1];
1183 fork_child( char *prog, char **args )
1187 wait4kids( maxkids );
1189 rc = _spawnvp( _P_NOWAIT, prog, args );
1192 tester_perror( "_spawnvp", NULL );
1194 children[nkids++] = (HANDLE)rc;