1 /****************************************************************************
2  *                                                                          *
3  * The contents of this file are subject to the WebStone Public License     *
4  * Version 1.0 (the "License"); you may not use this file except in         *
5  * compliance with the License. You may obtain a copy of the License        *
6  * at http://www.mindcraft.com/webstone/license10.html                      *
7  *                                                                          *
8  * Software distributed under the License is distributed on an "AS IS"      *
9  * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See      *
10  * the License for the specific language governing rights and limitations   *
11  * under the License.                                                       *
12  *                                                                          *
13  * The Original Code is WebStone 2.5.                                       *
14  *                                                                          *
15  * The Initial Developer of the Original Code is Silicon Graphics, Inc.     *
16  * and Mindcraft, Inc.. Portions created by Silicon Graphics. and           *
17  * Mindcraft. are Copyright (C) 1995-1998 Silicon Graphics, Inc. and        *
18  * Mindcraft, Inc. All Rights Reserved.                                     *
19  *                                                                          *
20  * Contributor(s): ______________________________________.                  *
21  *                                                                          *
22  * @(#) bench.c 2.4@(#)                                                     *
23  ***************************************************************************/
24 
25 
26 #include <stdio.h>
27 #include <errno.h>
28 #include <signal.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <time.h>
32 #include <ctype.h>
33 
34 #ifndef WIN32
35 #include <unistd.h>
36 #include <sys/param.h>
37 #include <sys/types.h>
38 #include <sys/errno.h>
39 #include <sys/socket.h>
40 #include <sys/time.h>
41 #include <netinet/in.h>
42 #include <netdb.h>
43 #include <arpa/inet.h>
44 #else /* WIN32 */
45 #define	FD_SETSIZE  1024 /* max size for select() - keep before <winsock.h>
46 			  * and same size as MAXCLIENTS */
47 #include <windows.h>
48 #include <winsock.h>
49 #include <io.h>
50 #include <process.h>
51 #endif /* WIN32 */
52 
53 #include "sysdep.h"
54 #include "bench.h"
55 
56 /* command line options/data */
57 int	    savefile = 0;
58 int	    debug = DEBUG_OFF;
59 int	    norexec = 0;
60 int	    haveproxyserver = 0;
61 char	    proxyserver[MAXHOSTNAMELEN];
62 char	    network_mask_str[30] = "255.255.255.0";
63 unsigned    network_mask = 0;
64 int	    servaddrin_config = 0;
65 int	    dumpall = 0;
66 int	    testtime = 0;
67 int	    havewebserver = 0;
68 int	    numloops = 0;
69 NETPORT	    portnum = 0;
70 int	    record_all_transactions = 0;
71 int		use_fixed_random_seed = 0;
72 int		verbose = 0;
73 char	webserver[MAXHOSTNAMELEN];
74 char	configfile[MAXPATHLEN];
75 char	webclient_path[MAXPATHLEN];
76 char	debug_filename[MAXPATHLEN];
77 int		uil_filelist_f = 0;
78 char	uil_filelist[MAXPATHLEN];
79 char	child_uil_filelist[MAXPATHLEN];
80 char	filelist[256][MAXPATHLEN];
81 int		randomize = 0;
82 fd_set	zerofdset;
83 
84 /* other key data */
85 long int    number_of_pages = 0;
86 long int    num_input_lines = 0;
87 int	    totalnumclients = 0;
88 int	    num_rexecs = 0;
89 SOCKET	    socknum[MAXCLIENTS];
90 SOCKET	    sockIO[MAXTOTALPROCS];
91 SOCKET	    sockErr[MAXTOTALPROCS];
92 THREAD FILE *debugfile;
93 
94 struct hostent  *master_phe;   /* IP addresses for webmaster */
95 
96 static void
usage(const char * progname)97 usage(const char *progname)
98 {
99 	fprintf(stderr, "Usage: %s [-a] [-d] -f config_file -C webclient_path\n",
100 		progname);
101 	fprintf(stderr, "          [-t run_time | -l num_loops] [-p port_num]\n");
102 	fprintf(stderr, "          [-r] [-s] [-v] [-w webserver_URL] \n");
103 	fprintf(stderr, "          [-u masterfilelist] [-U clientfilelist] [-W]\n");
104 	fprintf(stderr, "          [-P proxy_server] [-R] [-S]\n");
105 	fprintf(stderr, "\n");
106 	fprintf(stderr, "-w webserver URL [URL ...]\n\n");
107 	fprintf(stderr, "-a print timing information for all clients\n");
108 	fprintf(stderr, "-d turn on debug statements\n");
109 	fprintf(stderr, "-f config_file\tfile specifying clients\n");
110 	fprintf(stderr, "-l number of iterations to retrieve uils\n");
111 	fprintf(stderr, "-p port number of web server if not 80\n");
112 	fprintf(stderr, "-s save client gets to /tmp/webstone.data.*\n");
113 	fprintf(stderr, "-t run_time\tduration of test in minutes\n");
114 	fprintf(stderr, "-u URL file\tfilelist of URLs for webmaster\n");
115 	fprintf(stderr, "-U URL file\tfilelist of URLs for webclient\n");
116 	fprintf(stderr, "-v verbose mode\n");
117 	fprintf(stderr, "-D debugfile\tFile to receive debug data on client\n");
118 	fprintf(stderr, "-P servername\tuse proxy server for transactions\n");
119 	fprintf(stderr, "-W webserver addresses are in the config file\n");
120 	fprintf(stderr, "-R record all transactions\n");
121 	fprintf(stderr, "-S used fixed seed for random number generator\n");
122 	fprintf(stderr, "-T turn on trace statements\n");
123 	errexit("\n");
124 }
125 
126 static SOCKET
passivesock(const NETPORT portnum,const char * protocol,const int qlen)127 passivesock(const NETPORT portnum, const char *protocol, const int qlen)
128 {
129 	struct protoent    *ppe; /* pointer to protocol info entry */
130 	struct sockaddr_in sin;  /* Internet endpoint address */
131 	SOCKET    s;             /* socket descriptor */
132 
133 	D_PRINTF("Entering passivesock(): portnum=%d, protocol='%s', qlen=%d\n",
134 				portnum, protocol, qlen);
135 
136 	memset((char *)&sin, 0, sizeof(sin));
137 
138 	sin.sin_family = AF_INET;
139 	sin.sin_addr.s_addr = INADDR_ANY;
140 
141 	errno = 0;
142 	sin.sin_port = htons(portnum);
143 
144 	/* Map protocol name to number */
145 	CLEAR_SOCK_ERR;
146 	if ((ppe = getprotobyname(protocol)) == NULL)
147 		errexit("Can't get \"%s\" protocol entry\n", protocol);
148 
149 	/* allocate a socket */
150 	CLEAR_SOCK_ERR;
151 	s = socket(PF_INET, SOCK_STREAM, ppe->p_proto);
152 	if (BADSOCKET(s))
153 	{
154 		D_PRINTF("socket() returned %d. p_proto=%d, errno=%d (%s)",
155 				s, ppe->p_proto, s, GET_NET_ERR, neterrstr());
156 		errexit("Can't create socket: %s\n", neterrstr());
157 	}
158 
159 	/* Bind the socket */
160 	CLEAR_SOCK_ERR;
161 	if (SOCK_ERR(bind(s, (struct sockaddr *)&sin, sizeof(sin))))
162 		errexit("Can't bind to port %d: %s\n", portnum, neterrstr());
163 
164 	/*
165 	 * If it's a stream, listen for connections.
166 	 * On NT and many UNIX systems, the backlog parameter is silently
167 	 * limited to 5 connections.
168 	 */
169 	CLEAR_SOCK_ERR;
170 	if (SOCK_ERR(listen(s, qlen)))
171 		errexit("Can't listen on port %s: %s\n", portnum, neterrstr());
172 
173 	return s;
174 }
175 
176 
177 /*
178  * abort_clients()
179  *	Called by SIGINT handler as well as other error handlers.
180  *	This just does an exit() which will close all socket connections.
181  *	This doesn't explicitly kill the clients but they will get an error
182  *	and die when they try to send results to the webmaster.  If any
183  *	children are stuck then they'll have to be killed by hand.
184  */
185 static void
abort_clients(void)186 abort_clients(void)
187 {
188 	exit(2);
189 }
190 
191 
192 /* signal handler for SIGINT */
193 static void
sig_int(int sig)194 sig_int(int sig)
195 {
196 	/* its not safe to call fprintf() from a signal handler. */
197 	abort_clients();
198 }
199 
200 
201 #ifdef WIN32
202 /* echo stdout/stderr from clients */
203 void
echo_client(void * arg)204 echo_client(void *arg)
205 {
206 	SOCKET	*sockarr;
207 	char 	buf[BUFSIZ];
208 	int	i, len, rv;
209 	fd_set	readfds;
210 	struct timeval timeout;
211 	int	is_err = (int) arg;
212 	int	fdout;
213 	int	anyleft;
214 
215 	timeout.tv_sec = 5L;
216 	timeout.tv_usec = 0L;
217 
218 	if (is_err)
219 	{
220 		sockarr = sockErr;
221 		fdout = _fileno(stderr);
222 	}
223 	else
224 	{
225 		sockarr = sockIO;
226 		fdout = _fileno(stdout);
227 	}
228 
229 	while (1)
230 	{
231 		FD_ZERO(&readfds);
232 		for (i = anyleft = 0; i < num_rexecs; i++)
233 		{
234 			if (sockarr[i] != BADSOCKET_VALUE)
235 			{
236 				FD_SET(sockarr[i], &readfds);
237 				anyleft++;
238 			}
239 		}
240 		if (!anyleft)
241 			return;
242 		WSASetLastError(0);
243 		rv = select(num_rexecs, &readfds, NULL, NULL, &timeout);
244 		if (rv == 0)
245 			continue;
246 		if (rv < 0)
247 		{
248 			if (WSAGetLastError() == WSANOTINITIALISED)
249 				return;
250 			errexit("echo_client(): select() returns %d: %s\n",
251 					rv, neterrstr());
252 		}
253 
254 		/* loop over the sockets that are ready with data */
255 		for (i = 0; i < num_rexecs; i++)
256 		{
257 			if (sockarr[i] != BADSOCKET_VALUE && FD_ISSET(sockarr[i], &readfds))
258 			{
259 				len = NETREAD(sockarr[i], buf, sizeof(buf));
260 				if (len <= 0) /* mark connection closed */
261 					sockarr[i] = BADSOCKET_VALUE;
262 				if (len < 0)
263 				{
264 					if (WSAGetLastError() == WSANOTINITIALISED)
265 						return;
266 					fprintf(stderr,
267 							"Error in echo_client() after NETREAD(): %s\n",
268 							neterrstr());
269 					continue;
270 				}
271 				write(fdout, buf, len);	/* copy to stdout or stderr */
272 			}
273 		}
274 	}
275 }
276 
277 #else
278 static int
echo_client(const int fd,int is_err)279 echo_client(const int fd, int is_err)
280 {
281 	/*
282 	 * WRITE TEXT FROM FILE DESCRIPTOR INTO STDOUT
283 	 */
284 	char buf[BUFSIZ];
285 	int  cc;
286 
287 	D_PRINTF("entering echo_client() for %s\n", (is_err ? "stderr" : "stdout"));
288 
289 	while (getppid() != 1)
290 	{
291 		cc = NETREAD(fd, buf, sizeof(buf));
292 
293 		/*
294 		 * If this is the error stream (stderr of the clients) then only
295 		 * print it to our stdout if we are in debugging mode.
296 		 */
297 		if ((cc > 0) && is_err && DEBUGGING_ON)
298 				write(STDOUT_FILENO, buf, cc);
299 	}
300 	D_PRINTF("Exiting echo_client() for %s\n", (is_err ? "stderr" : "stdout"));
301 	NETCLOSE(fd);
302 }
303 #endif /* WIN32 */
304 
305 
306 /* Picks the appropriate webmaster IP address based on the address of the
307  * client.  This is significant only for hosts with multiple interfaces.
308  * Return value is a string with the IP address or hostname (or NULL)
309  */
310 char *
pick_webmaster_IP_address(char * client_hostname,struct hostent * master_phe,unsigned netmask)311 pick_webmaster_IP_address(char *client_hostname, struct hostent *master_phe,
312 							unsigned netmask)
313 {
314 	static char buf[20];
315 	unsigned char addr[4];
316 	int client_addr;
317 	int i;
318 
319 	/* Uncomment the following line to let clients do their own
320 	 * hostname lookup to find the webmaster system.
321 	 */
322 	/* return master_phe->h_name; */
323 
324     if (isdigit(client_hostname[0]))
325 	{
326 		/* we have an IP address */
327 		client_addr = inet_addr(client_hostname);
328 		if (client_addr == INADDR_NONE)
329 			return NULL;
330     }
331 	else
332 	{
333 		/* we have a hostname, use the webmaster hostname */
334 		return master_phe->h_name;
335     }
336 
337     for (i = 0; master_phe->h_addr_list[i] != NULL; i++)
338 	{
339 		if ((*(int *)(master_phe->h_addr_list[i]) & netmask) ==
340 													(client_addr & netmask))
341 		{
342 			memcpy((char *)addr, master_phe->h_addr_list[i], sizeof(addr));
343 			sprintf(buf, "%d.%d.%d.%d", addr[0], addr[1], addr[2], addr[3]);
344 			return buf;
345 		}
346     }
347 	/* No common net - can't give a verified hostname to webclient
348 	 */
349     return NULL;
350 }
351 
352 
353 /*
354  * Command line parsing
355  */
356 static void
ParseCmdLine(int argc,char ** argv)357 ParseCmdLine(int argc, char **argv )
358 {
359 	int		getoptch;
360 	int		currarg;
361 	extern char	*optarg;
362 	extern int	optind;
363 	FILE	*filelist_fp;
364 
365     /*
366      * PARSE THE COMMAND LINE OPTIONS
367      */
368 	while((getoptch = getopt(argc,argv,"aC:dD:f:l:M:n:p:P:R:sSt:Tu:U:vw:WX"))
369 		!= EOF)
370     {
371 		switch(getoptch)
372 		{
373 		case 'C':
374 			strcpy(webclient_path, optarg);
375 			break;
376 		case 'D':
377 			strcpy(debug_filename, optarg);
378 			break;
379 		case 'M':
380 			strcpy(network_mask_str, optarg);
381 			break;
382 		case 'P':
383 			haveproxyserver = 1;
384 			strcpy(proxyserver, optarg);
385 			break;
386 		case 'R':
387 			record_all_transactions = 1;
388 			break;
389 		case 'S':
390 			use_fixed_random_seed = 1;
391 			break;
392 		case 'T':
393 			debug |= DEBUG_TRACE;
394 			break;
395 		case 'U':
396 			strcpy(child_uil_filelist, optarg);
397 			break;
398 		case 'X':
399 			norexec = 1;
400 			break;
401 		case 'W':
402 			servaddrin_config = 1;
403 			break;
404 		case 'a':
405 			dumpall = 1;
406 			break;
407 		case 'd':
408 			debug = DEBUG_ALL;
409 			break;
410 		case 'f':
411 			strcpy(configfile, optarg);
412 			break;
413 		case 'l':
414 			numloops = atoi(optarg);
415 			break;
416 		case 'p':
417 			portnum = atoi(optarg);
418 			break;
419 		case 's':
420 			savefile = 1;
421 			break;
422 		case 't':
423 			testtime = atoi(optarg);
424 			break;
425 		case 'u':
426 			uil_filelist_f = 1;
427 			strcpy(uil_filelist, optarg);
428 			break;
429 		case 'v':
430 			verbose = 1;
431 			break;
432 		case 'w':
433 			havewebserver = 1;
434 			strcpy(webserver, optarg);
435 			break;
436 		default:
437 			fprintf(stderr,"-%c: Flag not recognized\n", (char)getoptch);
438 			usage(argv[0]);
439 		} /* end switch */
440 	} /* end while */
441 
442 	if (numloops && testtime)
443 		errexit("Can't have both -l and -t\n");
444 
445 	if (!havewebserver && !servaddrin_config)
446 	{
447 		/*
448 		 * THE SERVERS NAME MUST BE SPECIFIED
449 		 */
450 		fprintf(stderr,"No WWW Server specified\n");
451 		usage(argv[0]);
452 	}
453 
454 	if (havewebserver && servaddrin_config)
455 	{
456 		/*
457 		 * CAN'T HAVE BOTH -w and -W
458 		 */
459 		fprintf(stderr, "Can't have both -w and -W options\n");
460 		usage(argv[0]);
461 	}
462 
463 	network_mask = inet_addr(network_mask_str);
464 	if (network_mask == INADDR_NONE)
465 	{
466 		fprintf(stderr, "Invalid network mask (-M %s)\n", network_mask_str);
467 		usage(argv[0]);
468 	}
469 
470 	if (strlen(webclient_path) == 0)
471 	{
472 		fprintf(stderr,"No path specified for the webclient program\n");
473 		usage(argv[0]);
474 	}
475 
476 	if (strlen(configfile) == 0)
477 	{
478 		/*
479 		 * THE MASTER MUST HAVE A CONFIGURATION FILE TO READ.
480 		 */
481 		fprintf(stderr,"No Configuration file specified\n");
482 		usage(argv[0]);
483 	}
484 
485 	/*
486 	 * Get the list of UILs to retrieve from the server.  The
487 	 * page count here may include comment lines.  We'll get the
488 	 * actual count from the webclient program later.
489 	 */
490 	if (uil_filelist_f == 1)
491 	{
492 		D_PRINTF( "webmaster(): About to read filelist %s\n",
493 			uil_filelist );
494 
495 		filelist_fp = fopen(uil_filelist, "r");
496 		if (filelist_fp == NULL)
497 		{
498 			D_PRINTF( "Error %d opening filelist %s: %s\n",
499 					errno, uil_filelist, strerror(errno) );;
500 			errexit("Error %d opening filelist %s: %s\n",
501 					errno, uil_filelist, strerror(errno));
502 		}
503 		while (fgets(filelist[num_input_lines], MAXPATHLEN, filelist_fp)
504 			!= (char *)NULL)
505 				num_input_lines++;
506 		fclose(filelist_fp);
507 	}
508 	else if (optind < argc)  /* UILs are on the command  line */
509 	{
510 		currarg = optind;
511 		while(currarg != argc)
512 		{
513 
514 			/*
515 			 * GET THE UILS TO RETRIEVE.
516 			 */
517 			sscanf(argv[currarg],"%s",filelist[num_input_lines]);
518 			num_input_lines++;
519 			currarg++;
520 		}
521 	}
522 	else if (*child_uil_filelist)
523 	{
524 		num_input_lines=1;	/* Bogus value; trust that webclient has a file */
525 	}
526 	else	/* Get UILs from stdin */
527 	{
528 		while (fgets(filelist[num_input_lines], MAXPATHLEN, stdin)
529 			!= (char *)NULL)
530 				num_input_lines++;
531 	}
532 
533 
534 	if (num_input_lines == 0)
535 	{
536 		/*
537 		 * AT LEAST ONE FILE MUST BE SPECIFIED
538 		 */
539 		fprintf(stderr,"No URL resources specified\n");
540 		usage(argv[0]);
541 	}
542 }
543 
544 /*
545  * This function sets up the socket we will use to synchronize with the
546  * clients.
547  * Returns the socket number if successful, doesn't return if it fails
548  */
549 SOCKET
SetupSyncSocket(struct sockaddr_in * serveraddr)550 SetupSyncSocket(struct sockaddr_in *serveraddr)
551 {
552 	int sock, len;
553 
554 	D_PRINTF("entering SetupSyncSocket()\n");
555 
556 	/*
557 	 * passivesock() will exit if there is any error so the returned
558 	 * socket is always valid.
559 	 */
560     sock = passivesock(0, "tcp", MAXCLIENTS);
561 
562     len = sizeof(struct sockaddr);
563 	CLEAR_SOCK_ERR;
564     if (SOCK_ERR(getsockname(sock, (struct sockaddr *)serveraddr, &len)))
565 		errexit("Could not get socket informaton\n");
566 
567 	return sock;
568 }
569 
570 /* Added by Rajesh Shah 5/18/96 */
571 int
HostEntCpy(struct hostent * dest,struct hostent * src)572 HostEntCpy(struct hostent *dest, struct hostent *src)
573 {
574 	size_t count;
575 	char **sptr, **dptr;
576 	struct in_addr *iptr;
577 
578 	dest->h_name = (char *)malloc(strlen(src->h_name)+1);
579 	strcpy(dest->h_name, src->h_name);
580 	printf("WebMaster name = %s\n", dest->h_name);
581 	dest->h_aliases = src->h_aliases;
582 	dest->h_addrtype = src->h_addrtype;
583 	dest->h_length = src->h_length;
584 
585 	/*
586 	 * ADDED: by Greg Burrell of Mindcraft Inc. 10/22/96
587 	 * PROBLEM: we can't just do the assignment:
588 	 *
589 	 *              dest->h_addr_list = src->h_addr_list
590 	 *
591 	 *     because those are just pointers and the memory pointed to
592 	 *     may get overwritten during the next gethostbyname() call.
593 	 *
594 	 * FIX: Make a copy of the h_addr_list of a hostent structure.
595 	 */
596 	for(count = 0, sptr = src->h_addr_list; *sptr != NULL; sptr++, count++);
597 	if ((dest->h_addr_list = malloc(count + 1)) == NULL)
598 		return 0;
599 	if ((iptr = malloc(count * sizeof(struct in_addr))) == NULL)
600 		return 0;
601 	for (sptr = src->h_addr_list, dptr = dest->h_addr_list;
602 			*sptr != NULL; sptr++, dptr++, iptr++)
603 	{
604 		*iptr = *((struct in_addr *) *sptr);
605 		*dptr = (char *) iptr;
606 	}
607 	*dptr = NULL;
608 	return 1;
609 }
610 
611 
612 /*
613  * Function which generates a commandline for the webclients
614  */
615 void
MakeCmdLine(char * commandline)616 MakeCmdLine(char *commandline)
617 {
618     char tmpcommandline[NCCARGS];
619     char hostname[MAXHOSTNAMELEN];
620     struct hostent  *master_phe_tmp;
621 
622     /*
623      * BUILD THE PORTIONS OF THE cmdline FOR EACH CLIENT THAT WE CAN BUILD NOW.
624      * WE WILL FILL IN THE NUMBER OF CLIENTS LATER WITH AN sprintf.
625      */
626     D_PRINTF( "Calling gethostname\n" );
627 
628 	CLEAR_SOCK_ERR;
629     if(SOCK_ERR(gethostname(hostname,MAXHOSTNAMELEN)))
630 		errexit("Could not retrieve local host name");
631 
632 	/* Convert hostname to address, to avoid DNS problems for webclients. */
633 	/* Copy the output of gethostbyname() to our own storage,
634 	 * so it isn't overwritten by a later call to gethostbyname(). */
635 
636 	master_phe_tmp = gethostbyname(hostname);
637 	if (master_phe_tmp == NULL)
638 		errexit("Unable to resolve webmaster hostname '%s'\n", hostname);
639 	master_phe = (struct hostent *)malloc(sizeof(struct hostent));
640 	HostEntCpy(master_phe, master_phe_tmp);
641 
642     sprintf(commandline,"%s", webclient_path);
643 
644     if(haveproxyserver)
645     {
646         sprintf(tmpcommandline, " -P %s", proxyserver);
647         strcat(commandline, tmpcommandline);
648     }
649 
650     if (DEBUGGING_ON)
651         strcat(commandline, " -d");
652 	else if (TRACING_ON)
653         strcat(commandline, " -T");
654 
655     if (*debug_filename)
656     {
657 		strcat(commandline," -D ");
658 		strcat(commandline,debug_filename);
659     }
660 
661     if (numloops != 0)
662     {
663         sprintf(tmpcommandline," -l %d", numloops);
664         strcat(commandline,tmpcommandline);
665     }
666 
667     if (portnum)
668     {
669         sprintf(tmpcommandline," -p %d", portnum);
670         strcat(commandline,tmpcommandline);
671     }
672 
673     if (savefile)
674         strcat(commandline," -s");
675 
676     if (record_all_transactions)
677 		strcat(commandline, " -R");
678 
679     if (testtime != 0)
680     {
681         sprintf(tmpcommandline," -t %d", testtime);
682         strcat(commandline,tmpcommandline);
683     }
684 
685     if (*child_uil_filelist)
686     {
687         sprintf(tmpcommandline," -u %s", child_uil_filelist);
688         strcat(commandline,tmpcommandline);
689     }
690 
691     /*
692      * SET UP A SPACE FOR THE NUMBER OF CLIENTS ON THE commandline.
693      */
694 	sprintf(tmpcommandline,
695 		(use_fixed_random_seed ? "%s -n %%d -w %%s -c %%s:%%d -S %%d" :
696 								 "%s -n %%d -w %%s -c %%s:%%d"),
697 		commandline);
698     strcpy(commandline,tmpcommandline);
699 }
700 
701 
702 /*
703  * rexec to the client hosts and start the webclients
704  */
705 int
RexecClients(char * commandline,char clienthostname[MAXCLIENTS][MAXHOSTNAMELEN],struct sockaddr_in * serveraddr)706 RexecClients(char *commandline,
707 			char clienthostname[MAXCLIENTS][MAXHOSTNAMELEN],
708 			struct sockaddr_in *serveraddr)
709 {
710 	int		tmpfd;
711 	int		numclients = 0;
712 	char	tmpcommandline[NCCARGS];
713 	struct servent *inetport;
714 	int		cnt;
715 	char	buffer[NCCARGS];
716 	char	login[MAXUSERNAME];
717 	char	password[MAXPASSWD];
718 	FILE	*fp;
719 	int		returnval;
720 	char	*tmphostname;
721 	int		client_random_seed_base = 0;
722 	int		i;
723 
724 	D_PRINTF( "Entering RexecClients(\"%s\", \"%s\", %lu)\n",
725 		commandline, clienthostname[0], (long)serveraddr);
726 	fflush(debugfile);
727 
728 	/*
729 	 * OPEN UP THE CONFIG FILE. FOR EACH LINE IN THE CONFIG FILE, CHECK
730 	 * ITS VALIDITY AND THEN rexec A COMMAND ON THE CLIENT.
731 	 */
732 	errno = 0;
733 	D_PRINTF("Opening configfile: %s\n", configfile);
734 	if ((fp = fopen(configfile,"r")) == NULL)
735 		errexit("Could not open config file %s\n", configfile);
736 
737     CLEAR_SOCK_ERR;
738 	if ((inetport = getservbyname("exec","tcp")) == NULL)
739 		errexit("Could not get service name for exec/tcp\n");
740 
741 	D_PRINTF( "getservbyname returned %d\n", ntohs(inetport->s_port) );
742 
743 	cnt = 0;
744 
745 	while(1)
746 	{
747 		char	webserver2[MAXHOSTNAMELEN];
748 		char	linebuf[150];
749 		int		num;
750 		char	*primename;
751 
752 		if (NULL == fgets(linebuf, sizeof(linebuf), fp))
753 			break;
754 		num = sscanf(linebuf,"%s %s %s %d %s", clienthostname[cnt], login,
755 						password, &numclients, webserver2);
756 		D_PRINTF("	%s %s %s %d %s\n", clienthostname[cnt], login,
757 						password, numclients, webserver2);
758 		if (num < 4)
759 			break;
760 		if (servaddrin_config)
761 		{
762 			if (num == 4)
763 				errexit("No webserver specified in config file for %s\n",
764 						clienthostname[cnt]);
765 			strcpy(webserver, webserver2);
766 		}
767 
768 		if (numclients == 0)
769 			continue;			/* no work for this client system */
770 		if (numclients < 0)
771 			errexit("Number of clients must be >= 0\n");
772 		if (numclients > MAXPROCSPERNODE)
773 			errexit("Number of clients per node can't exceed %d\n",
774 					MAXPROCSPERNODE);
775 		totalnumclients += numclients;
776 
777 		primename = pick_webmaster_IP_address(clienthostname[cnt], master_phe,
778 												network_mask);
779 		if (primename == NULL)
780 			errexit("Bad client address %s for Client %d\n",
781 					clienthostname[cnt], cnt);
782 
783 		fprintf(stdout, "Client %d: %s \t# Processes: %d\n    Webserver: %s\tWebmaster: %s:%d\n", cnt, clienthostname[cnt], numclients, webserver, primename,
784 				ntohs(serveraddr->sin_port));
785 		fflush(stdout);
786 
787 		if (use_fixed_random_seed) {
788 			sprintf(tmpcommandline, commandline, numclients, webserver,
789 					primename, ntohs(serveraddr->sin_port),
790 					client_random_seed_base);
791 			client_random_seed_base += numclients;
792 		}
793 		else
794 			sprintf(tmpcommandline, commandline, numclients, webserver,
795 					primename, ntohs(serveraddr->sin_port));
796 
797 		D_PRINTF( "%s rexec %s\n",clienthostname[cnt],tmpcommandline );
798 		if (norexec)
799 			sleep(30);	/* gives some time to start clients for debugging */
800 		else
801 		{
802 			tmphostname = &(clienthostname[cnt][0]);
803 			D_PRINTF("rexec( %s, %d, %s, %s, \"%s,\" %d)\n",
804 				tmphostname, inetport->s_port, login, password,
805 				tmpcommandline, sockErr[cnt]);
806 			tmpfd = rexec(&tmphostname, inetport->s_port, login, password,
807 							 tmpcommandline, &sockErr[cnt]);
808 			if(BADSOCKET(sockIO[cnt] = tmpfd))
809 			{
810 				errexit("Could not rexec: rexec to client %s, cmdline %s failed\n", clienthostname[cnt],tmpcommandline);
811 			}
812 
813 			/*
814 			 * Send the list of UILs to the webclient process, before it
815 			 * forks
816 			 */
817 			if (!(*child_uil_filelist))
818 			{
819 				for (i = 0; i < num_input_lines; i++)
820 				{
821 					D_PRINTF("webmaster: sending <%s> to client, socket %d.\n",
822 						filelist[i], sockIO[cnt]);
823 					NETWRITE(tmpfd, filelist[i], strlen(filelist[i]));
824 				}
825 			}
826 			D_PRINTF("webmaster: sending <%s> to client, socket %d.\n",
827 					ENDTOKEN, tmpfd);
828 			/*
829 		 	 * write an extra newline in case the last line of the filelist
830 			 * didn't end in a newline.
831 			 */
832 			NETWRITE(tmpfd, "\n", 1);
833 			NETWRITE(tmpfd, ENDTOKEN, ENDTOKENSTRLEN);
834 
835 			memset(buffer, 0, sizeof(buffer));
836 
837 			if (NETREAD(tmpfd, buffer, 10) <= 0)
838 			{
839 				D_PRINTF( "Error reading number of pages: %s\n", neterrstr());
840 				fprintf(stderr,
841 					"Error reading number of pages: %s\nSocket number %d\n",
842 								neterrstr(),tmpfd);
843 				abort_clients();
844 				errexit("");
845 			}
846 			sscanf(buffer, "%d", &number_of_pages);
847 
848 			memset(buffer, 0, sizeof(buffer));
849 
850 			if (NETREAD(tmpfd, buffer, 10) <= 0)
851 			{
852 				D_PRINTF( "Error reading randomize flag: %s\n", neterrstr());
853 				fprintf(stderr,
854 					"Error reading randomize flag: %s\nSocket number %d\n",
855 								neterrstr(),tmpfd);
856 				abort_clients();
857 				errexit("");
858 			}
859 			sscanf(buffer, "%d", &randomize);
860 			D_PRINTF( "randomize set to %d.\n", randomize);
861 
862 			memset(buffer, 0, sizeof(buffer));
863 
864 			returnval = NETREAD(tmpfd, buffer, OKSTRLEN);
865 			if (returnval <= 0 || memcmp(buffer, OKSTR, OKSTRLEN) != 0)
866 			{
867 				errexit("rexec to client %s, cmdline %s received error %s\n",
868 						clienthostname[cnt],tmpcommandline, buffer);
869 			}
870 			D_PRINTF("Received OK from %s.\n", clienthostname[cnt] );
871 		}
872 
873 #ifndef WIN32
874 		/*
875 	     * Set up processes to read the stdout and stderr of the
876 		 * rexec().
877 		 */
878 		D_PRINTF( "Forking webclient stdout process\n" );
879 		switch (fork())
880 		{
881 			case -1:   /* ERROR */
882 					errexit("fork: %s\n", strerror(errno));
883 			case 0:    /* CHILD */
884 					exit(echo_client(sockIO[cnt], 0));
885 			default:   /* PARENT */
886 					break;
887 		}
888 		D_PRINTF( "Forking webclient stderr process\n" );
889 		switch (fork())
890 		{
891 			case -1:   /* ERROR */
892 					errexit("fork: %s\n", strerror(errno));
893 			case 0:    /* CHILD */
894 					exit(echo_client(sockErr[cnt], 1));
895 			default:   /* PARENT */
896 					break;
897 		}
898 #endif /* ! WIN32 */
899 
900 		cnt++;
901 		if (cnt > MAXCLIENTS || cnt > FD_SETSIZE)
902 			errexit("Number of Clients can't exceed %d\n", MAXCLIENTS);
903 	}
904 
905 	num_rexecs = cnt;
906 	if (totalnumclients > MAXTOTALPROCS)
907 		errexit("Total number of processes can't exceed  %d\n", MAXTOTALPROCS);
908 
909 #ifdef WIN32
910 	/* start threads to echo stdout/stderr from clients */
911 	_beginthread(echo_client, 0, (void *)0);
912 	_beginthread(echo_client, 0, (void *)1);
913 #endif /* WIN32 */
914 
915 	fprintf(stdout,"\n");
916 	fprintf(stdout,"\n");
917 	fclose(fp);
918 
919 	return totalnumclients;
920 }
921 
922 
923 /*
924  * GetReady()
925  *		When each client thread or process starts up it makes a connection
926  *		back to the web master.
927  */
928 void
GetReady(fd_set * fdset,int totalnumclients,int sock)929 GetReady(fd_set *fdset, int totalnumclients, int sock)
930 {
931 	int		cnt, len;
932 	char	buffer[NCCARGS];
933 
934 	D_PRINTF( "Beginning accept loop\n" );
935 	for (cnt = 0; cnt < totalnumclients; cnt++)
936 	{
937 		fd_set readfds;
938 		struct timeval timeout;
939 		int rv;
940 
941 		timeout.tv_sec = MAX_ACCEPT_SECS;
942 		timeout.tv_usec = 0;
943 		FD_ZERO(&readfds);
944 		FD_SET(sock, &readfds);
945 
946 		/* Time out if any of the clients are unable to connect to us. */
947 		if (!(rv = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout)))
948 		{
949 			fprintf(stdout,
950 				"Listen timeout after %d seconds (%d clients so far)\n",
951 						MAX_ACCEPT_SECS, cnt);
952 			D_PRINTF("select() timed out after %d seconds\n", MAX_ACCEPT_SECS);
953 			errexit("Webmaster terminating\n");
954 		}
955 
956 		CLEAR_SOCK_ERR;
957 		if (BADSOCKET(socknum[cnt] = accept(sock, NULL, 0)))
958 		{
959 			abort_clients();
960 			errexit("accept() error: %s", neterrstr());
961 		}
962 
963 		FD_SET(socknum[cnt], fdset);
964 		D_PRINTF( "GetReady(): cnt=%d, socket=%d\n", cnt, socknum[cnt] );
965 	}
966 
967 	/*
968 	 * WAIT FOR A READY.
969 	 */
970 	fprintf(stdout,"Waiting for READY from %d clients\n",totalnumclients);
971 	fflush(stdout);
972 
973 	for (cnt = 0; cnt < totalnumclients; cnt++)
974 	{
975 		len = NETREAD(socknum[cnt], buffer, READYSTRLEN);
976 		if (len != READYSTRLEN)
977 		{
978 			abort_clients();
979 			errexit("Error reading from client #%d\n", cnt);
980 		}
981 		if (memcmp(buffer, READYSTR, READYSTRLEN))
982 		{
983 			abort_clients();
984 			errexit("Received bad READY string: len %d, value %s\n",
985 					len,buffer);
986 		}
987 		D_PRINTF("Got ready from client #%d\n", cnt);
988 	}
989 	fprintf(stdout,"All READYs received\n");
990 	fflush(stdout);
991 }
992 
993 /*
994  * Start all the clients by sending them a GO message.
995  * totalnumclients is the total number of clients
996  * socknum is an array with the socket descriptors for all the client
997  * connections.  We know they are all valid descriptors because we wouldn't
998  * have gotten this far if they weren't.
999  */
1000 void
SendGo(int totalnumclients,int * socknum)1001 SendGo(int totalnumclients, int *socknum)
1002 {
1003 	int cnt;
1004 
1005 	fprintf(stdout, "Sending GO to all clients\n");
1006 	for(cnt = 0; cnt < totalnumclients; cnt++)
1007 	{
1008 		if (NETWRITE(socknum[cnt], GOSTR, GOSTRLEN) != GOSTRLEN)
1009 		{
1010 			abort_clients();
1011 			errexit("Error sending GO to client %d: %s\n", cnt,
1012 					neterrstr());
1013 		}
1014 	}
1015 }
1016 
1017 
1018 /*
1019  * This function gathers statistics from all the clients
1020  */
1021 void
GetResults(fd_set * fdset,page_stats_t ** page_stats,time_t * endtime,char * timestr,int totalnumclients,stats_t statarray[])1022 GetResults(fd_set *fdset, page_stats_t **page_stats, time_t *endtime,
1023 			char *timestr, int totalnumclients, stats_t statarray[])
1024 {
1025 	fd_set	leftfdset;
1026 	char	*stats_as_text;
1027 	char	*page_stats_as_text;
1028 	int		returnval;
1029 	int		cnt,i;
1030 
1031 
1032 	/* DOESN'T ACTUALLY PRINT UNTIL THE FIRST CLIENT REPORTS */
1033 	fprintf(stdout,"Reading results ");
1034 
1035 	/*
1036 	 * COPY THE FILE DESCRIPTORS TO A TMP LIST,
1037 	 * ALLOCATE MEMORY FOR STATS, PAGESTATS IN TEXT FORM
1038 	 */
1039 	leftfdset = *fdset;
1040 	stats_as_text = (char *)mymalloc(SIZEOF_STATSTEXT+1);
1041 	page_stats_as_text = (char *)mymalloc(SIZEOF_PAGESTATSTEXT+1);
1042 
1043 
1044 	/*
1045 	 * LOOP UNTIL ALL CLIENTS HAVE REPORTED
1046 	 */
1047 	for(cnt = 0; cnt < totalnumclients; cnt++)
1048 	{
1049 		/*
1050 		 * Send "GO" to each socket and receive its data.
1051 		 */
1052 		if(!BADSOCKET(socknum[cnt]) && (FD_ISSET(socknum[cnt],&leftfdset)))
1053 		{
1054 			int len;
1055 
1056 			D_PRINTF( "Sending GO to receive data from client %d\n", cnt );
1057 			if (NETWRITE(socknum[cnt], GOSTR, GOSTRLEN) != GOSTRLEN)
1058 			{
1059 				abort_clients();
1060 				errexit("Error sending GO to client %d: %s\n", cnt,
1061 						neterrstr());
1062 			}
1063 
1064 			len = SIZEOF_STATSTEXTBASE + number_of_pages*SIZEOF_DOUBLETEXT;
1065 			returnval = recvdata(socknum[cnt], stats_as_text, len);
1066 			if (returnval < 0)
1067 			{
1068 				D_PRINTF( "Error reading timing stats: %s\n", neterrstr() );
1069 				fprintf(stderr,
1070 					"Error reading timing stats: %s\nSocket number %d\n",
1071 								neterrstr(),socknum[cnt]);
1072 				fprintf(stderr, "recvdata() returned %d\n", returnval);
1073 				abort_clients();
1074 				errexit("");
1075 			}
1076 
1077 			/* convert text to stats */
1078 			stats_as_text[len] = 0;	/* add an end marker */
1079 			statarray[cnt] = *text_to_stats(stats_as_text);
1080 
1081 			fputc('.', stdout); /* PROGRESS MARKER */
1082 			fflush(stdout);
1083 
1084 			if (randomize)
1085 			{
1086 				for (i = 0; i < number_of_pages; i++)
1087 				{
1088 					D_PRINTF( "On page_stats[%d][%d]\n", cnt, i );
1089 					returnval = recvdata(socknum[cnt], page_stats_as_text,
1090 										SIZEOF_PAGESTATSTEXT);
1091 					if (returnval < 0)
1092 					{
1093 						D_PRINTF( "Error reading page_stats[%d][%d]: %s\n",
1094 									cnt, i, neterrstr() );
1095 						fprintf(stderr,
1096 								"Error reading page_stats[%d][%d]: %s\n",
1097 										cnt, i, neterrstr());
1098 						fprintf(stderr, "recvdata() returned %d\n", returnval);
1099 						abort_clients();
1100 						errexit("");
1101 					}
1102 					page_stats_as_text[returnval] = 0; /* add end marker */
1103 					page_stats[cnt][i] =
1104 								*text_to_page_stats(page_stats_as_text);
1105 				} /* end for */
1106 			}
1107 
1108 			FD_CLR(socknum[cnt],&leftfdset);
1109 			NETCLOSE(socknum[cnt]);
1110 			D_PRINTF("Finished with socket %d\n", socknum[cnt]);
1111 			socknum[cnt] = BADSOCKET_VALUE;
1112 		} /* end if socknum */
1113 	} /* end for cnt */
1114 
1115 	/*
1116  	 * DONE READING RESULTS FROM CLIENTS
1117 	 */
1118 	*endtime = time(NULL);
1119 	timestr = asctime(localtime(endtime));
1120 	fprintf(stdout,"\nAll clients ended at %s\n",timestr);
1121 	fflush(stdout);
1122 
1123 	/* FREE MEMORY ALLOCATED FOR CLIENT STATS, PAGESTATS AS TEXT */
1124 	free(stats_as_text);
1125 	free(page_stats_as_text);
1126 }
1127 
1128 /*
1129  * Prints out all the results
1130  */
1131 void
PrintResults(page_stats_t ** page_stats,time_t endtime,char * timestr,int totalnumclients,stats_t statarray[],page_stats_t * page_stats_total)1132 PrintResults(page_stats_t **page_stats, time_t endtime, char *timestr,
1133 				int totalnumclients, stats_t statarray[],
1134 				page_stats_t *page_stats_total)
1135 {
1136 	stats_t	masterstat;
1137 	int		cnt, i, j;
1138 	double	thruput;
1139 	struct timeval	dtime;
1140 
1141     /*
1142      * Print everything out.
1143 	 */
1144 	stats_init(&masterstat);
1145 	masterstat.rs.totalconnects = 0;
1146 	for(cnt = 0; cnt < totalnumclients; cnt++)
1147 	{
1148 
1149 		if(statarray[cnt].rs.totalconnects == 0)
1150 		{
1151 			fprintf(stdout,
1152 			"No connects from client %d. Is the Web server running?\n", cnt);
1153 		}
1154 		else if(statarray[cnt].rs.totalconnects > 0)
1155 		{
1156 			if(dumpall)
1157 			{
1158 				fprintf(stdout,"----------------------------------\n");
1159 				fprintf(stdout,
1160 						"Total number of pages retrieved from server: %u\n",
1161 						statarray[cnt].totalpages);
1162 
1163 				rqstat_fprint(stdout, &(statarray[cnt].rs));
1164 
1165 				thruput = thruputpersec((double)(statarray[cnt].rs.totalbytes),
1166 										&(statarray[cnt].rs.totalresponsetime));
1167 
1168 				fprintf(stdout,
1169 						"Thruput average per connection: %.0lf bytes/sec\n",
1170 						thruput);
1171 			}
1172 
1173 			D_PRINTF( "Summing stats for %d, with %ld total connections\n",
1174 						cnt, statarray[cnt].rs.totalconnects );
1175 			rqstat_sum(&masterstat.rs, &(statarray[cnt].rs));
1176 		}
1177 		else
1178 			masterstat.rs.totalerrs += statarray[cnt].rs.totalerrs;
1179 		fflush(stdout);
1180 	}
1181 
1182 	if(masterstat.rs.totalconnects == 0)
1183 	{
1184 		fprintf(stdout, "No connects at all. Is the Web server running?\n");
1185 		return;
1186 	}
1187 
1188 	for (i=0; i < totalnumclients; i++)
1189 	{
1190 		for (j=0; j < number_of_pages; j++)
1191 		{
1192 			D_PRINTF( "Summing page stats for %d, page %d, with %d connects\n",
1193 						i, j, statarray[i].page_numbers[j] );
1194 
1195 			if (statarray[i].page_numbers[j] != 0)
1196 			{
1197 				rqst_stats_t *pst_rs;
1198 				rqst_stats_t *ps_rs;
1199 
1200 				pst_rs = &(page_stats_total[j].rs);
1201 				ps_rs  = &(page_stats[i][j].rs);
1202 
1203 				rqstat_sum(pst_rs, ps_rs);
1204 
1205 				page_stats_total[j].totalpages += page_stats[i][j].totalpages;
1206 				masterstat.totalpages += page_stats[i][j].totalpages;
1207 
1208 				/* yes, this is assignment, not sum */
1209 				page_stats_total[j].page_size = page_stats[i][j].page_size;
1210 				page_stats_total[j].page_valid = 1;
1211 			}
1212 		}
1213 	}
1214 
1215 	/*
1216 	 * If the -v (verbose) option was used then print even more.  Note that
1217 	 * using the -v option can cause a LOT of data to be output and may fill
1218 	 * up disks if you're saving all stdout and stderr to a log file.  It may
1219 	 * also greatly increase the run time while all the data is dumped to
1220 	 * screen.
1221      */
1222 	if (verbose)
1223 	{
1224 		for (i = 0; i < number_of_pages; i++)
1225 		{
1226 			if (page_stats_total[i].page_valid == 1)
1227 			{
1228 				page_stats_t *pst;
1229 
1230 				pst = &(page_stats_total[i]);
1231 
1232 				printf ("===============================================================================\n");
1233 				printf ("Page # %lu\n\n", i);
1234 				printf ("Total number of times page was hit %u\n",
1235 						pst->totalpages);
1236 
1237 				rqstat_print(&(pst->rs));
1238 
1239 				printf ("Page size %u \n", pst->page_size);
1240 				printf ("===============================================================================\n\n");
1241 			}
1242 		}
1243 	}
1244 
1245 	fprintf(stdout,"===============================================================================\n");
1246 
1247 	/*
1248 	 * Validate run.
1249 	 */
1250 	masterstat.total_num_of_files = statarray[0].total_num_of_files;
1251 	for (i=1; i < totalnumclients; i++)
1252 	{
1253 		if ((statarray[i].rs.totalconnects > 0) &&
1254 			(statarray[i].total_num_of_files != masterstat.total_num_of_files))
1255 		{
1256 			fprintf(stdout,"**********************************************************************\n");
1257 			fprintf(stdout,"**** ERROR: number of files in each test configuration is not the same\n");
1258 			fprintf(stdout,"**** ERROR: Check configuration file %s on each client\n", configfile);
1259 			fprintf(stdout,"**********************************************************************\n");
1260 			break;
1261 		}
1262 	}
1263 
1264 	/*
1265 	 * Print summary statistics
1266 	 */
1267 	fprintf(stdout, "WEBSTONE %s results:\n", VERSION);
1268 
1269 	fprintf(stdout,
1270 			"Total number of clients:         %5d\n", totalnumclients);
1271 	if (testtime)
1272 	{
1273 		fprintf(stdout,
1274 			"Test time:                       %5d minutes\n", testtime);
1275 
1276 		fprintf(stdout,
1277 			"Server connection rate:        %10.2lf connections/sec\n",
1278 				(double)(masterstat.rs.totalconnects)/(60*testtime));
1279 
1280 		fprintf(stdout,
1281 			"Server error rate:             %10.2lf err/sec\n",
1282 				(double)(masterstat.rs.totalerrs)/(60*testtime));
1283 
1284 		fprintf(stdout,
1285 			"Server thruput:                %10.2lf Mbit/sec\n",
1286 				(double)(8*masterstat.rs.totalbytes)/(60*testtime*1000000));
1287 
1288 		fprintf(stdout,
1289 			"Little's Load Factor:          %10.2lf \n",
1290 				(double)(masterstat.rs.totalresponsetime.tv_sec)/
1291 					(1000*60*testtime));
1292 	}
1293 
1294 	avgtime(&masterstat.rs.totalresponsetime, masterstat.rs.totalconnects,
1295 			&dtime);
1296 
1297 	fprintf(stdout,
1298 			"Average response time:         %11.3lf sec\n",
1299 			timevaldouble(&dtime) / 1000);
1300 
1301 	if (masterstat.rs.totalconnects)
1302 	{
1303 		fprintf(stdout,
1304 			"Error Level:                   %10.2lf %%\n",
1305 		(double)(100 * masterstat.rs.totalerrs)/(masterstat.rs.totalconnects));
1306 	}
1307 	else
1308 	{
1309 		fprintf(stdout,
1310 			"Error Level:                   UNDEFINED\n");
1311 		fprintf(stderr,
1312 			"No connections during run; is the Web server running?\n");
1313 	}
1314 
1315 	/* so much for the key metrics */
1316 
1317 	thruput = 8000 * thruputpersec((double)(masterstat.rs.totalbytes),
1318 								&(masterstat.rs.totalresponsetime));
1319 
1320 	fprintf(stdout,
1321 			"Average client thruput:        %10.2lf Mbit/sec\n",
1322 				thruput/1000000);
1323 
1324 	fprintf(stdout,
1325 			"Sum of client response times:  %10.2lf sec\n",
1326 			timevaldouble(&(masterstat.rs.totalresponsetime)) / 1000);
1327 
1328 	fprintf(stdout,
1329 			"Total number of pages read:      %5ld\n\n",
1330 			masterstat.totalpages);
1331 
1332 	/* Remaining stats are the same as usual */
1333 	rqstat_fprint(stdout, &(masterstat.rs));
1334 	fflush(stdout);
1335 }
1336 
1337 
1338 #ifdef WIN32
1339 /* close socket library */
1340 void
sock_cleanup(void)1341 sock_cleanup(void)
1342 {
1343 	WSACleanup();
1344 }
1345 #endif /* WIN32 */
1346 
1347 int
main(const int argc,char * argv[])1348 main(const int argc, char *argv[])
1349 {
1350 
1351 	/*
1352 	 * sync_sock is the socket that clients will use to let us know that
1353 	 * they've started up and are ready to go.
1354 	 */
1355     int		sync_sock;
1356 
1357     int		i, j;
1358     char	buffer[NCCARGS];
1359     char 	commandline[NCCARGS];
1360     char 	*timestr;
1361     time_t 	starttime;
1362     time_t	endtime;
1363     fd_set	fdset;
1364 
1365     /* make the big arrays static to avoid stack overflow */
1366     static char		clienthostname[MAXCLIENTS][MAXHOSTNAMELEN];
1367     static stats_t	statarray[MAXCLIENTS];
1368     page_stats_t **page_stats;
1369     page_stats_t *page_stats_total;
1370     struct sockaddr_in 	serveraddr;
1371 
1372 #ifdef WIN32
1373     WSADATA	WSAData;
1374     COORD	dwSize;
1375 
1376     if ((WSAStartup(MAKEWORD(1,1), &WSAData)) != 0)
1377 		errexit("Error in WSAStartup()\n");
1378     atexit(sock_cleanup);
1379 
1380     /* increase size of output window */
1381     dwSize.X = 80;
1382     dwSize.Y = 500;
1383     SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), dwSize);
1384 #endif /* WIN32 */
1385 
1386 
1387     /* Initalization of variables. */
1388     /* debugfile = stdout; */
1389     debugfile = stderr;
1390     memset(buffer, 0, NCCARGS);
1391     memset(webserver, 0, MAXHOSTNAMELEN);
1392     memset(configfile, 0, MAXPATHLEN);
1393     FD_ZERO(&zerofdset);
1394     FD_ZERO(&fdset);
1395 
1396     for(i = 0; i < MAXCLIENTS; i++)
1397     {
1398         socknum[i] = BADSOCKET_VALUE;
1399         statarray[i].rs.totalconnects = 0;
1400     }
1401 
1402     signal(SIGINT, sig_int);
1403 
1404     ParseCmdLine(argc, argv);
1405     sync_sock = SetupSyncSocket(&serveraddr);
1406     MakeCmdLine(commandline);
1407     totalnumclients = RexecClients(commandline, clienthostname, &serveraddr);
1408 	if(totalnumclients == 0)
1409 	{
1410 		errexit("No work to do!\n");
1411 	}
1412 
1413     /* Initalization of variables. */
1414     page_stats = (page_stats_t **)
1415 					mymalloc(totalnumclients*sizeof(page_stats_t *));
1416     for (i=0; i < totalnumclients; i++)
1417 	{
1418 		page_stats[i] = (page_stats_t *)
1419 						mymalloc(number_of_pages*sizeof(page_stats_t));
1420 	}
1421 	page_stats_total = (page_stats_t *)
1422 						mymalloc(number_of_pages*sizeof(page_stats_t));
1423 
1424 	for (i=0; i < totalnumclients; i++)
1425 		stats_init(&(statarray[i]));
1426 
1427 	for (i=0; i < totalnumclients; i++)
1428 	{
1429 		for (j=0; j < number_of_pages; j++)
1430 			page_stats_init(&(page_stats[i][j]));
1431 	}
1432 	for (i=0; i < number_of_pages; i++)
1433 		page_stats_init(&(page_stats_total[i]));
1434 
1435 	GetReady(&fdset, totalnumclients, sync_sock );
1436 	NETCLOSE(sync_sock);
1437 
1438 	/*
1439 	 * Send a "GO" message to all webclients so they will start their tests.
1440 	 */
1441 	SendGo(totalnumclients, socknum);
1442 
1443 	/*
1444 	 * Wait for all of the webclients to complete.  We have a socket
1445 	 * connection to each webclient process or thread so we should get
1446  	 * a reply from each one.  The reply will be the timing information.
1447 	 */
1448 	starttime = time(NULL);
1449 	timestr = asctime(localtime(&starttime));
1450 	fprintf(stdout, "All clients started at %s\n", timestr);
1451 	fprintf(stdout, "Waiting for clients completion...\n");
1452 	fflush(stdout);
1453 
1454 	/*
1455 	 * If this is a timed test, we might as well snooze.  The clients have
1456 	 * their own timers and will contact us when they are finished with
1457 	 * their tests and ready to report their data.
1458 	 */
1459 	if (testtime)
1460 		sleep(testtime * 60);
1461 
1462 
1463 	GetResults(&fdset, page_stats, &endtime, timestr, totalnumclients,
1464 				statarray);
1465 
1466 	PrintResults(page_stats, endtime, timestr, totalnumclients, statarray,
1467 				page_stats_total);
1468 
1469 	exit(0);
1470 }
1471 
1472