Merge pull request #2309 from mitza-oci/warnings
[ACE_TAO.git] / ACE / apps / JAWS / clients / WebSTONE / src / webmaster.c
blob7176e8bc6792674cb24baf967a547c563c8b9aa2
1 /**************************************************************************
2 * *
3 * Copyright (C) 1995 Silicon Graphics, Inc. *
4 * *
5 * These coded instructions, statements, and computer programs were *
6 * developed by SGI for public use. If any changes are made to this code*
7 * please try to get the changes back to the author. Feel free to make *
8 * modifications and changes to the code and release it. *
9 * *
10 **************************************************************************/
12 /* FUZZ: disable check_for_math_include */
13 /* FUZZ: disable check_for_improper_main_declaration */
15 #include <stdio.h>
16 #include <errno.h>
17 #include <signal.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <time.h>
22 #ifndef WIN32
23 #include <unistd.h>
24 #endif /* WIN32 */
26 #include <math.h>
28 #ifndef WIN32
29 #include <sys/param.h>
30 #include <sys/types.h>
31 #include <sys/errno.h>
32 #include <sys/socket.h>
33 #include <sys/time.h>
34 #include <netinet/in.h>
35 #include <netdb.h>
36 #else
37 #define FD_SETSIZE 1024 /* max size for select() - keep before <winsock.h>
38 * and same size as MAXCLIENTS */
39 #include <windows.h>
40 #include <winsock.h>
41 #include <io.h>
42 #include <process.h>
43 #endif /* WIN32 */
45 #include "sysdep.h"
46 #include "bench.h"
48 /* command line options/data */
49 int savefile = 0;
50 int debug = 0;
51 int norexec = 0;
52 int haveproxyserver = 0;
53 char proxyserver[MAXHOSTNAMELEN];
54 char network_mask_str[30] = "255.255.255.0";
55 unsigned network_mask = 0;
56 int servaddrin_config = 0;
57 int dumpall = 0;
58 int testtime = 0;
59 int havewebserver = 0;
60 int numloops = 0;
61 NETPORT portnum = 0;
62 int redirect = 0;
63 int record_all_transactions = 0;
64 int uil_filelist_f = 0;
65 int verbose = 0;
66 char webserver[MAXHOSTNAMELEN];
67 char configfile[MAXPATHLEN];
68 char uil_filelist[NCCARGS];
70 char filelist[256][MAXPATHLEN];
71 fd_set zerofdset;
73 /* other key data */
74 long int number_of_pages = 0;
75 int totalnumclients = 0;
76 int num_rexecs = 0;
77 SOCKET socknum[MAXCLIENTS];
78 SOCKET sockIO[MAXTOTALPROCS];
79 SOCKET sockErr[MAXTOTALPROCS];
80 THREAD FILE *debugfile = stderr;
81 struct hostent *master_phe; /* IP addresses for webmaster */
82 struct timeval sumedh_start, sumedh_end;
84 void HostEntCpy(struct hostent *dest, struct hostent *src);
86 static void
87 usage(const char *progname)
90 fprintf(stderr, "Usage: %s [-a] [-d] -f config_file [-l numloops]\n",
91 progname);
92 fprintf(stderr, " [-p port_num] [-r] [-s] [-t run_time] \n");
93 fprintf(stderr, "\n");
94 fprintf(stderr, "-w webserver URL [URL ...]\n\n");
95 fprintf(stderr, "-a print timing information for all clients\n");
96 fprintf(stderr, "-d turn on debug statements\n");
97 fprintf(stderr, "-f config_file\tfile specifying clients\n");
98 fprintf(stderr, "-l number of iterations to retrieve uils\n");
99 fprintf(stderr, "-p port number of web server if not 80\n");
100 fprintf(stderr, "-r redirect stdout of clients to /tmp/webstone.xxx\n");
101 fprintf(stderr, "-s save client gets to /tmp/webstone.data.*\n");
102 fprintf(stderr, "-t run_time\tduration of test in minutes\n");
103 fprintf(stderr, "-w webserver\tname of webserver host to contact\n");
104 fprintf(stderr, "-u URL file\tfilelist of URLs\n");
105 fprintf(stderr, "-v verbose mode\n");
106 fprintf(stderr, "-P servername\tuse proxy server for transactions\n");
107 fprintf(stderr, "-W webserver addresses are in the config file\n");
108 fprintf(stderr, "-R record all transactions\n");
109 errexit("\n");
112 static SOCKET
113 passivesock(const NETPORT portnum, const char *protocol, const int qlen)
115 struct protoent *ppe; /* pointer to protocol info entry */
116 struct sockaddr_in sin; /* Internet endpoint address */
117 SOCKET s; /* socket descriptor */
118 int type; /* socket type */
120 D_PRINTF( "Beginning passivesock with errno %d\n",errno );
122 D_PRINTF( "Zeroing address structure\n" );
123 memset((char *)&sin, 0, sizeof(sin));
125 sin.sin_family = AF_INET;
126 sin.sin_addr.s_addr = INADDR_ANY;
128 /* NOT USED: Map service name to portnumber */
129 D_PRINTF( "Mapping portnum errno %d\n",errno );
130 sin.sin_port = htons(portnum);
132 /* Map protocol name to number */
133 D_PRINTF( "Mapping protocol name errno %d\n",errno );
134 if ((ppe = getprotobyname(protocol)) == 0)
136 errexit("Can't get \"%s\" protocol entry\n", protocol);
138 errno = 0;
140 /* use protocol to choose socket type */
141 D_PRINTF( "Changing socket type, errno %d\n",errno );
142 if (strcmp(protocol, "udp") == 0)
144 type = SOCK_DGRAM;
145 D_PRINTF( "Choosing SOCK_DGRAM\n" );
147 else
149 type = SOCK_STREAM;
150 D_PRINTF( "Choosing SOCK_STREAM, errno %d\n",errno );
153 /* allocate a socket */
154 s = socket(PF_INET, type, ppe->p_proto);
155 if (BADSOCKET(s))
157 D_PRINTF( "Socket PF_INET %d %d returned %d with %s\n",
158 type, ppe->p_proto, s, neterrstr() );
159 errexit("Can't create socket: %s\n", neterrstr());
161 D_PRINTF( "Socket %d created with errno %d\n",s,errno );
163 /* Bind the socket */
164 if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) < 0)
166 errexit("Can't bind to port %d: %s\n", portnum, neterrstr());
168 D_PRINTF( "Bind succeeded\n" );
170 /* If it's a stream, listen for connections */
171 /* NOTE: ON NT, the listen() backlog parm is silently limited to 5 conns */
172 if ((type == SOCK_STREAM) && BADSOCKET(listen(s, qlen)))
174 errexit("Can't listen on port %s: %s\n", portnum, neterrstr());
176 D_PRINTF( "Listen succeeded\n" );
178 /* all done, return socket descriptor */
179 return(s);
183 /* abort clients -- called by SIGINT handler */
184 static void abort_clients(void)
186 /* Not supposed to have fprintf in a signal handler, but... */
187 fprintf(stdout, "Webmaster received SIGINT. Terminating.\n");
188 /* exit will close all open connections */
189 exit(2);
192 /* signal handler for SIGINT */
193 static void sig_int(int sig) {
195 abort_clients();
198 #ifdef WIN32
200 /* echo stdout/stderr from clients */
201 void echo_client(void *stream)
203 SOCKET *sockarr;
204 FILE *outfile;
205 int which_stream = (int) stream;
206 char buf[BUFSIZ];
207 int i, len, rv;
208 fd_set readfds;
210 /* This code which handles the timeout may need
211 to be ifdef'ed for WIN32 */
212 struct timeval timeout;
214 timeout.tv_sec = (long)5;
215 timeout.tv_usec = (long)0;
217 if (which_stream) {
218 sockarr = sockIO;
219 outfile = stdout;
220 } else {
221 sockarr = sockErr;
222 outfile = stderr;
225 D_PRINTF( "echo_client running\n" );
226 signal( SIGINT, SIG_DFL); /* restore default behavior
227 for SIGINT */
229 while (1) {
230 FD_ZERO(&readfds);
231 for (i = 0; i < num_rexecs; i++)
232 if (sockarr[i] != BADSOCKET_VALUE)
233 FD_SET(sockarr[i], &readfds);
234 rv = select(num_rexecs, &readfds, 0, 0, &timeout);
235 if ( rv == 0)
236 continue;
237 if (rv < 0 && WSAGetLastError() == WSANOTINITIALISED)
238 return;
239 if (rv < 0)
240 errexit("Error in echo_client(): select() returns %d: %s\n", rv, neterrstr());
242 /* loop over the sockets that are ready with data */
243 for (i = 0; i < num_rexecs; i++) {
244 if (sockarr[i] != BADSOCKET_VALUE && FD_ISSET(sockarr[i], &readfds)) {
245 len = NETREAD(sockarr[i], buf, sizeof(buf));
246 if (len <= 0) {
247 /* mark connection closed */
248 sockarr[i] = BADSOCKET_VALUE;
249 if (len < 0 && WSAGetLastError() == WSANOTINITIALISED)
250 return;
251 if (len < 0)
252 fprintf(stderr, "Error in echo_client() after NETREAD(): %s\n", neterrstr());
253 continue;
256 /* copy to stdout or stderr */
257 fwrite(buf, sizeof(char), len, outfile);
261 D_PRINTF( "Exiting echo_client\n" );
264 #else
265 static int
266 echo_client(char *hostname, const int fd)
269 * WRITE TEXT FROM FILE DESCRIPTOR INTO STDOUT
271 char buf[BUFSIZ];
272 int cc;
273 D_PRINTF( "echo_client running\n" );
275 while (getppid() != 1)
277 cc = NETREAD(fd, buf, sizeof(buf));
278 if (cc > 0)
280 write(STDOUT_FILENO, buf, cc);
283 D_PRINTF( "Exiting echo_client\n" );
284 NETCLOSE(fd);
286 #endif /* WIN32 */
288 /* Picks the appropriate webmaster IP address based on the address of the client.
289 * This is significant only for hosts with multiple interfaces
291 * return value is a string with the IP address or hostname (or NULL)
293 char *pick_webmaster_IP_address(char *client_hostname, struct hostent *master_phe,
294 unsigned netmask) {
295 static char buf[20];
296 unsigned char addr[4];
297 int client_addr;
298 int i;
300 if (client_hostname[0] >= '0' && client_hostname[0] <= '9') {
301 /* we have an IP address */
302 client_addr = inet_addr(client_hostname);
303 if (client_addr == INADDR_NONE)
304 return 0;
305 } else {
306 /* we have a hostname, use the webserver hostname */
307 return master_phe->h_name;
310 for (i = 0; master_phe->h_addr_list[i] != 0; i++) {
311 if ((*(int *)(master_phe->h_addr_list[i]) & netmask) ==
312 (client_addr & netmask))
313 goto gotit;
315 i = 0; /* give up */
317 gotit:
318 memcpy((char *)addr, master_phe->h_addr_list[i], sizeof(addr)); /* Internet specific */
319 sprintf(buf, "%d.%d.%d.%d", addr[0], addr[1], addr[2], addr[3]);
320 return buf;
324 Command line parsing
327 void ParseCmdLine(int argc, char **argv )
329 char getoptch;
330 int currarg;
331 extern char *optarg;
332 extern int optind;
335 * PARSE THE COMMAND LINE OPTIONS
337 while((getoptch = getopt(argc,argv,"P:f:t:l:p:u:R:w:n:M:adrsvWX")) != (const char)EOF)
339 switch(getoptch)
341 case 'M':
342 strcpy(network_mask_str, optarg);
343 break;
344 case 'P':
345 haveproxyserver = 1;
346 strcpy(proxyserver, optarg);
347 break;
348 case 'R':
349 record_all_transactions = 1;
350 break;
351 case 'X':
352 norexec = 1;
353 break;
354 case 'W':
355 servaddrin_config = 1;
356 break;
357 case 'a':
358 dumpall = 1;
359 break;
360 case 'd':
361 debug = 1;
362 break;
363 case 'f':
364 strcpy(configfile, optarg);
365 break;
366 case 'l':
367 numloops = atoi(optarg);
368 break;
369 case 'p':
370 portnum = atoi(optarg);
371 break;
372 case 'r':
373 redirect = 1;
374 break;
375 case 's':
376 savefile = 1;
377 break;
378 case 't':
379 testtime = atoi(optarg);
380 break;
381 case 'u':
382 uil_filelist_f = 1;
383 strcpy(uil_filelist, optarg);
384 break;
385 case 'v':
386 verbose = 1;
387 break;
388 case 'w':
389 havewebserver = 1;
390 strcpy(webserver, optarg);
391 break;
392 default:
393 usage(argv[0]);
394 } /* end switch */
395 } /* end while */
397 if (numloops && testtime)
398 errexit("Can't have both -l and -t\n");
400 if(!havewebserver && !servaddrin_config)
403 * THE SERVERS NAME MUST BE SPECIFIED
406 fprintf(stderr,"No WWW Server specified\n");
407 usage(argv[0]);
410 if (havewebserver && servaddrin_config)
413 * CAN'T HAVE BOTH -w and -W
415 fprintf(stderr, "Can't have both -w and -W options\n");
416 usage(argv[0]);
419 network_mask = inet_addr(network_mask_str);
420 if (network_mask == INADDR_NONE) {
421 fprintf(stderr, "Invalid network mask (-M %s)\n", network_mask_str);
422 usage(argv[0]);
425 if(strlen(configfile) == 0)
428 * THE MASTER MUST HAVE A CONFIGURATION FILE TO READ.
430 fprintf(stderr,"No Configuration file specified\n");
431 usage(argv[0]);
433 /* IF WE DO NOT HAVE A FILE LIST THEN THERE ARE UIL'S AT THE END OF THE
434 * COMMAND LINE SO GRAB THEM.
436 if (uil_filelist_f == 0)
438 currarg = optind;
439 number_of_pages = 0;
440 while(currarg != argc)
443 * GET THE UILS TO RETRIEVE.
446 sscanf(argv[currarg],"%s",filelist[number_of_pages]);
447 number_of_pages++;
448 currarg++;
451 else
453 /* have filelist; take a stab at the number of valid URLs */
454 D_PRINTF( "About to parse filelist %s\n", uil_filelist );
455 number_of_pages = count_file_list(uil_filelist);
457 if (number_of_pages == 0)
460 * AT LEAST ONE FILE MUST BE SPECIFIED
462 fprintf(stderr,"No URL resources specified\n");
463 usage(argv[0]);
468 This function sets up the socket we will use to synchronize with the
469 clients.
470 Returns the socket number if successful, doesn't return if it fails
473 SOCKET SetupSyncSocket( serveraddr )
474 struct sockaddr_in *serveraddr;
476 int sock,len;
479 * SET UP THE SOCKET WE ARE GOING TO USE TO SYNCHRONIZE WITH THE CLIENTS.
481 D_PRINTF( "About to call sock %d %d\n", portnum, MAXCLIENTS );
483 sock = passivesock(0, "tcp", MAXCLIENTS);
485 if (BADSOCKET(sock))
487 errexit("Couldn't open socket %d: %s\n", sock, neterrstr());
489 D_PRINTF( "The passivesock call succeeded\n" );
491 D_PRINTF( "calling getsockname\n" );
493 len = sizeof(struct sockaddr);
494 if(getsockname(sock, (struct sockaddr *)serveraddr, &len) < 0)
496 errexit("Could not get socket informaton\n");
499 return( sock );
503 Function which generates a commandline for the webclients
505 void MakeCmdLine( commandline)
506 char *commandline;
508 char tmpcommandline[NCCARGS];
509 char hostname[MAXHOSTNAMELEN];
510 char *webclient_path, *temp;
511 int cnt;
512 struct hostent *master_phe_tmp; /* temp. variable added by - Rajesh Shah*/
515 * BUILD THE PORTIONS OF THE cmdline FOR EACH CLIENT THAT WE CAN BUILD NOW.
516 * WE WILL FILL IN THE NUMBER OF CLIENTS LATER WITH AN sprintf.
518 D_PRINTF( "Calling gethostname\n" );
520 if(gethostname(hostname,MAXHOSTNAMELEN) != 0)
522 errexit("Could not retrieve local host name");
523 } else {
524 /* convert hostname to address (to avoid DNS problems for webclients) */
525 /* The following lines are add to copy the system
526 buffer (output of gethostbyname()) into user area.
527 This is because, there are very good chances that later
528 on system buffer might be overwritten by some calls and
529 still if your pointer is pointing to same addr. nothing
530 but only trouble and trouble! Infact this is what
531 happening when I tried to run webstone benchmark for more
532 then one clients. It used to over write the webmaster name
533 with the first client name and so remaining on client(s)
534 the webclient process(es) were invoked with wrong webmaster
535 name! This behaviour is observed Solaris 2.4 this bug
536 can be hit in any OS. - Rajesh Shah 5/18/96 */
538 /* master_phe = gethostbyname(hostname); */
539 master_phe_tmp = gethostbyname(hostname);
540 master_phe = (struct hostent *)malloc(sizeof(struct hostent));
541 HostEntCpy(master_phe, master_phe_tmp);
544 /* set up executable pathname */
545 #ifndef WIN32
546 temp = getenv("TMPDIR");
548 if ( temp && *temp ) {
549 webclient_path = (char *)mymalloc( strlen(temp) + strlen("/webclient")
550 + 1);
551 strcpy(webclient_path, temp);
552 strcat(webclient_path, "/webclient");
554 } else
555 #else
556 temp = temp;
557 #endif /* WIN32 */
558 webclient_path = PROGPATH;
561 D_PRINTF( "Path to webclient is: %s\n", webclient_path );
563 sprintf(commandline,"%s", webclient_path);
565 if(haveproxyserver)
567 sprintf(tmpcommandline, " -P %s", proxyserver);
568 strcat(commandline, tmpcommandline);
570 if (debug)
572 strcat(commandline," -d");
574 if (numloops != 0)
576 sprintf(tmpcommandline," -l %d", numloops);
577 strcat(commandline,tmpcommandline);
579 if (portnum)
581 sprintf(tmpcommandline," -p %d", portnum);
582 strcat(commandline,tmpcommandline);
584 if (redirect)
586 strcat(commandline," -r");
588 if (savefile)
590 strcat(commandline," -s");
592 if (uil_filelist_f)
594 strcat(commandline," -u ");
595 strcat(commandline,uil_filelist);
597 if (record_all_transactions)
599 strcat(commandline," -R");
601 if (testtime != 0)
603 sprintf(tmpcommandline," -t %d", testtime);
604 strcat(commandline,tmpcommandline);
608 * SET UP A SPACE FOR THE NUMBER OF CLIENTS ON THE commandline.
610 sprintf(tmpcommandline,"%s -n %%d -w %%s -c %%s:%%d", commandline);
611 strcpy(commandline,tmpcommandline);
613 if (uil_filelist_f == 0)
615 cnt = 0;
616 while(cnt < number_of_pages)
619 * PUT THE FILES AT THE END OF THE LIST.
621 strcat(commandline," ");
622 strcat(commandline,filelist[cnt]);
623 cnt++;
626 puts(commandline);
630 rexec to the client hosts and start the webclients
632 int RexecClients( commandline, clienthostname, serveraddr)
633 char *commandline;
634 char clienthostname[MAXCLIENTS][MAXHOSTNAMELEN];
635 struct sockaddr_in *serveraddr;
638 int tmpfd;
639 int numclients = 0;
640 char tmpcommandline[NCCARGS];
641 struct servent *inetport;
642 int cnt;
643 char buffer[NCCARGS];
644 char login[MAXUSERNAME];
645 char password[MAXPASSWD];
646 FILE *fp;
647 int returnval;
648 char *tmphostname;
650 memset(buffer, 0, sizeof(buffer));
653 * OPEN UP THE CONFIG FILE. FOR EACH LINE IN THE CONFIG FILE, CHECK
654 * ITS VALIDITY AND THEN rexec A COMMAND ON THE CLIENT.
657 if ((fp = fopen(configfile,"r")) == 0)
659 errexit("Could not open config file %s\n", configfile);
662 if ((inetport = getservbyname("exec","tcp")) == 0)
664 errexit("Could not get service name for exec/tcp\n");
666 D_PRINTF( "getservbyname returned %d\n", ntohs(inetport->s_port) );
668 cnt = 0;
670 D_PRINTF( "rexec loop\n" );
671 while(1)
673 char webserver2[MAXHOSTNAMELEN];
674 char linebuf[150];
675 int num;
676 char *primename;
678 if (0 == fgets(linebuf, sizeof(linebuf), fp))
679 break;
680 num = sscanf(linebuf,"%s %s %s %d %s",clienthostname[cnt],login,password,
681 &numclients, webserver2);
682 if (num < 4)
683 break;
684 if (servaddrin_config) {
685 if (num == 4) {
686 errexit("No webserver specified in config file for %s\n", clienthostname[cnt]);
688 strcpy(webserver, webserver2);
691 if (numclients <= 0)
692 errexit("Number of clients must be >= 0\n");
693 if (numclients > MAXPROCSPERNODE)
695 errexit("Number of clients per node can't exceed %d\n", MAXPROCSPERNODE);
697 totalnumclients += numclients;
699 primename = pick_webmaster_IP_address(clienthostname[cnt], master_phe, network_mask);
700 if (primename == 0) {
701 errexit("Bad client address %s for Client %d\n", clienthostname[cnt], cnt);
704 fprintf(stdout,"Client %d: %s \t# Processes: %d\n Webserver: %s\tWebmaster: %s:%d\n",
705 cnt, clienthostname[cnt], numclients, webserver, primename,
706 ntohs(serveraddr->sin_port));
707 fflush(stdout);
708 sprintf(tmpcommandline, commandline, numclients, webserver, primename,
709 ntohs(serveraddr->sin_port));
711 fprintf(stderr, "tmpcommandline: %s\n", tmpcommandline);
713 D_PRINTF( "%s rexec %s\n",&clienthostname[cnt],tmpcommandline );
714 if (norexec) {
715 sleep(30); /* gives some time to start clients for debugging */
716 } else {
718 tmphostname = &(clienthostname[cnt][0]);
719 tmpfd = rexec(&tmphostname, inetport->s_port, login, password,
720 tmpcommandline, &sockErr[cnt]);
721 if((sockIO[cnt] = tmpfd) < 0)
723 errexit("Could not rexec: rexec to client %s, cmdline %s failed\n",
724 clienthostname[cnt],tmpcommandline);
729 returnval = NETREAD(tmpfd, buffer, OKSTRLEN);
730 D_PRINTF( "read returns %d, %s\n", returnval, buffer );
732 if (returnval <= 0 || memcmp(buffer, OKSTR, OKSTRLEN) != 0)
734 errexit("rexec to client %s, cmdline %s received error %s\n",
735 clienthostname[cnt],tmpcommandline, buffer);
739 cnt++;
740 if (cnt > MAXCLIENTS || cnt > FD_SETSIZE)
742 errexit("Number of Clients can't exceed %d\n", MAXCLIENTS);
746 num_rexecs = cnt;
747 if (totalnumclients > MAXTOTALPROCS)
749 errexit("Total number of processes can't exceed %d\n",
750 MAXTOTALPROCS);
753 #ifndef WIN32
754 /* NOW WE NEED TO HANDLE THE OUTPUT FROM THE REXEC.
755 * TO DO THIS, WE FORK, THEN HAVE ONE PROCESS READ FROM TMPFD.
756 * THE OTHER PROCESS CONTINUES WITH THE PROGRAM
758 D_PRINTF( "Forking webclient stderr/stdout processes\n" );
759 switch (fork())
761 case -1: /* ERROR */
762 errexit("fork: %s\n", strerror(errno));
763 case 0: /* CHILD */
764 exit(echo_client(clienthostname[cnt], tmpfd));
765 default: /* PARENT */
766 break;
768 #else
769 /* start threads to echo stdout/stderr from clients */
770 _beginthread(echo_client, 0, (void *)0);
771 _beginthread(echo_client, 0, (void *)1);
772 #endif /* WIN32 */
774 fprintf(stdout,"\n");
775 fprintf(stdout,"\n");
776 fclose(fp);
778 return totalnumclients;
781 void GetReady( fdset, totalnumclients, sock )
782 fd_set *fdset;
783 int totalnumclients;
784 int sock;
786 int cnt,len;
787 fd_set tmpfdset, leftfdset;
788 char buffer[NCCARGS];
791 * NOW WE NEED TO ACCEPT ALL THE CONNECTIONS FROM THE CLIENTS,
792 * ACCEPT ALL THE READY MESSAGES
795 D_PRINTF( "Beginning accept loop\n" );
796 for (cnt = 0; cnt < totalnumclients; cnt++)
798 D_PRINTF( "Client %d:\t", cnt );
801 fd_set readfds;
802 struct timeval timeout;
803 int rv;
805 timeout.tv_sec = MAX_ACCEPT_SECS;
806 timeout.tv_usec = 0;
807 FD_ZERO(&readfds);
808 FD_SET(sock, &readfds);
810 /* if we're hung, quit */
811 D_PRINTF("Before select() on listen() socket\n");
812 if (!(rv = select(FD_SETSIZE, &readfds, 0, 0, &timeout))) {
813 fprintf(stdout,
814 "Listen timeout after %d seconds (%d clients so far)\n",
815 MAX_ACCEPT_SECS, cnt);
816 D_PRINTF("select() timed out after %d seconds\n", MAX_ACCEPT_SECS);
817 errexit("Webmaster terminating\n");
821 if(BADSOCKET(socknum[cnt] = accept(sock, 0, 0)))
824 * ERROR accepting FROM THE CLIENTS. WE NEED TO ISSUE AN
825 * ABORT TO ALL.
827 abort_clients();
828 errexit("Error accepting from one of the clients: %s", neterrstr());
829 } else
832 * SET THE FD IN THE MASK
834 FD_SET(socknum[cnt],fdset);
836 D_PRINTF( "on socket %d\n",socknum[cnt] );
838 D_PRINTF( "\n" );
841 * WAIT FOR A READY.
843 sleep(1);
844 fprintf(stdout,"Waiting for READY from %d clients\n",totalnumclients);
845 fflush(stdout);
846 leftfdset = *fdset;
847 #ifndef WIN32
848 while(memcmp(&leftfdset,&zerofdset,sizeof(fd_set)))
850 tmpfdset = leftfdset;
852 if(select(FD_SETSIZE,&tmpfdset,NULL,NULL,NULL) < 0)
855 * ERROR SELECTING. ABORT ALL.
857 abort_clients();
858 errexit("Error accepting from one of the clients: %s\n",
859 neterrstr());
860 break;
862 #else
863 /* I don't see why a select is needed at all--all clients must respond
864 * and there is no synchronization/timing issue.
866 tmpfdset = leftfdset;
868 #endif /* WIN32 */
870 for (cnt = 0; cnt < totalnumclients; cnt++)
873 * SEE WHICH SOCKETS HAVE A INPUT ON THEM PENDING
874 * AND RECEIVE IT.
876 if(!BADSOCKET(socknum[cnt]) && (FD_ISSET(socknum[cnt],&tmpfdset)))
879 * GET THE READY FROM THIS GUY.
880 * DON'T FORGET TO CLEAR HIS BIT IN THE tmpfdset
882 len = NETREAD(socknum[cnt],buffer,READYSTRLEN);
883 if(len != READYSTRLEN)
885 abort_clients();
886 errexit("Error reading from client #%d\n", cnt);
888 if(memcmp(buffer, READYSTR, READYSTRLEN))
890 abort_clients();
891 fprintf(stdout,"Received bad READY string: len %d, value %s\n",
892 len,buffer);
894 FD_CLR(socknum[cnt],&leftfdset);
898 sleep(1);
899 fprintf(stdout,"All READYs received\n");
900 fflush(stdout);
904 Start all the clients by sending them a GO signal
905 totalnumclients is the total number of clients
906 socknum is an int array with the filedescriptors for all the
907 client connections
909 void SendGo( totalnumclients, socknum)
910 int totalnumclients;
911 int *socknum;
913 int cnt;
914 fprintf(stdout,"Sending GO to all clients\n");
915 for(cnt = 0; cnt < totalnumclients; cnt++)
917 if(socknum[cnt] > 0)
920 * SEND A GO
922 if(NETWRITE(socknum[cnt], GOSTR, GOSTRLEN) != GOSTRLEN)
924 abort_clients();
925 errexit("Error sending GO to client %d: %s\n", cnt, neterrstr());
932 This function gathers statistics from all the clients
935 void GetResults(fdset, page_stats, endtime, timestr, totalnumclients,
936 statarray)
937 fd_set *fdset;
938 page_stats_t **page_stats;
939 time_t *endtime;
940 char *timestr;
941 int totalnumclients;
942 stats_t statarray[MAXCLIENTS];
944 fd_set leftfdset,tmpfdset;
945 char *stats_as_text;
946 char *page_stats_as_text;
947 int returnval;
948 int cnt,i;
951 /* DOESN'T ACTUALLY PRINT UNTIL THE FIRST CLIENT REPORTS */
952 fprintf(stdout,"Reading results ");
955 * COPY THE FILE DESCRIPTORS TO A TMP LIST,
956 * ALLOCATE MEMORY FOR STATS, PAGESTATS IN TEXT FORM
958 leftfdset = *fdset;
959 stats_as_text = (char *)mymalloc(SIZEOF_STATSTEXT+1);
960 page_stats_as_text = (char *)mymalloc(SIZEOF_PAGESTATSTEXT+1);
963 * COPY THE FILE DESCRIPTORS TO A TMP LIST,
964 * PLUS A LIST OF REMAINING FDs
966 leftfdset = *fdset;
968 * LOOP UNTIL ALL CLIENTS HAVE REPORTED
969 * AND tmpfdset IS EMPTY
971 #ifndef WIN32
972 while(memcmp(&leftfdset,&zerofdset,sizeof(fd_set)))
974 tmpfdset = leftfdset;
975 sleep(1);
976 returnval = select(FD_SETSIZE,&tmpfdset,NULL,NULL,NULL);
977 D_PRINTF( "Call to select returned %d, errno %d\n",
978 returnval, errno );
979 if(returnval < 0)
982 * ERROR SELECTING. ABORT ALL.
984 D_PRINTF( "select() error %s\n", neterrstr() );
985 abort_clients();
986 errexit("Error selecting from one of the clients\n");
988 #else
989 /* I don't see why a select is needed at all */
990 tmpfdset = leftfdset;
992 #endif /* WIN32 */
993 for(cnt = 0; cnt < totalnumclients; cnt++)
996 * SEE WHICH SOCKETS HAVE A INPUT ON THEM PENDING AND
997 * RECEIVE IT.
1000 /* IS THIS A VALID SOCKET? IS IT READY TO READ? */
1001 if(!BADSOCKET(socknum[cnt]) && (FD_ISSET(socknum[cnt],&tmpfdset)))
1003 int len;
1006 * GET THE TIMING DATA FROM THIS GUY
1007 * THEN REMOVE HIM FROM THE tmpfdset
1010 * READ TIME STATS
1011 * DOES READ() RETURN THE CORRECT LENGTH?
1013 D_PRINTF( "About to read timestats, count %d, errno %d\n",
1014 cnt, errno );
1015 len = SIZEOF_STATSTEXTBASE + number_of_pages*SIZEOF_DOUBLETEXT;
1016 returnval = recvdata(socknum[cnt], stats_as_text,
1017 len);
1018 D_PRINTF( "Read time stats %d\n", returnval );
1019 if (returnval != len) /* <= 0) */
1021 D_PRINTF( "Error reading timing stats: %s\n",
1022 neterrstr() );
1023 fprintf(stderr, "Error reading timing stats: %s\nSocket number %d\n",
1024 neterrstr(),socknum[cnt]);
1025 abort_clients();
1026 errexit("");
1027 } /* end if */
1029 /* convert text to stats */
1030 stats_as_text[returnval] = 0; /* add an end marker */
1031 statarray[cnt] = *text_to_stats(stats_as_text);
1033 fputc('.', stdout); /* PROGRESS MARKER */
1034 fflush(stdout);
1036 if(uil_filelist_f) /* READ PAGE STATS */
1038 for (i = 0; i < number_of_pages; i++)
1040 D_PRINTF( "On page_stats[%d][%d]\n", cnt, i );
1041 returnval = recvdata(socknum[cnt], page_stats_as_text,
1042 SIZEOF_PAGESTATSTEXT);
1043 D_PRINTF( "Read page stats %d\n", returnval );
1045 if (returnval != SIZEOF_PAGESTATSTEXT) /* <= 0) */
1047 D_PRINTF( "Error reading page_stats[%d][%d]: %s\n",
1048 cnt, i, neterrstr() );
1049 fprintf(stderr, "Error reading page_stats[%d][%d]: %s\n",
1050 cnt, i, neterrstr());
1051 abort_clients();
1052 errexit("");
1054 D_PRINTF( "Page stats: read %d bytes\n",
1055 returnval );
1057 page_stats_as_text[returnval] = 0; /* add an end marker */
1058 D_PRINTF("strlen(page_stats_as_text) = %d\n",
1059 strlen(page_stats_as_text));
1060 page_stats[cnt][i] =
1061 *text_to_page_stats(page_stats_as_text);
1063 } /* end for */
1064 } /* end if filelist */
1066 FD_CLR(socknum[cnt],&leftfdset);
1067 NETCLOSE(socknum[cnt]);
1068 socknum[cnt] = BADSOCKET_VALUE;
1069 } /* end if socknum */
1070 } /* end for cnt */
1071 } /* end while memcmp fd */
1074 * DONE READING RESULTS FROM CLIENTS
1077 *endtime = time(0);
1078 timestr = asctime(localtime(endtime));
1079 fprintf(stdout,"\nAll clients ended at %s\n",timestr);
1080 fflush(stdout);
1082 /* FREE MEMORY ALLOCATED FOR CLIENT STATS, PAGESTATS AS TEXT */
1083 free(stats_as_text);
1084 free(page_stats_as_text);
1089 Prints out all the results
1091 void PrintResults( page_stats, endtime, timestr, totalnumclients, statarray,
1092 page_stats_total)
1093 page_stats_t **page_stats;
1094 time_t endtime;
1095 char *timestr;
1096 int totalnumclients;
1097 stats_t statarray[MAXCLIENTS];
1098 page_stats_t *page_stats_total;
1100 stats_t masterstat;
1101 int cnt,i,j;
1102 double thruput;
1103 struct timeval dtime;
1106 * PRINT EVERYTHING OUT
1108 stats_init(&masterstat);
1109 for(cnt = 0; cnt < totalnumclients; cnt++)
1111 if((statarray[cnt].rs.totalconnects > 0) && (dumpall))
1113 fprintf(stdout,"----------------------------------\n");
1114 /* fprintf(stdout,"Test for host: %s\n",statarray[cnt].hostname); */
1115 fprintf(stdout,"Total number of pages retrieved from server: %u\n",
1116 statarray[cnt].totalpages);
1118 rqstat_fprint(stdout, &(statarray[cnt].rs));
1120 thruput = thruputpersec((double)(statarray[cnt].rs.totalbytes),
1121 &(statarray[cnt].rs.totalresponsetime));
1123 fprintf(stdout, "Thruput average per connection: %.0f bytes/sec\n",
1124 thruput);
1126 if(statarray[cnt].rs.totalconnects > 0)
1128 D_PRINTF( "Summing stats for %d, with %ld total connections\n",
1129 cnt, statarray[cnt].rs.totalconnects );
1130 rqstat_sum(&masterstat.rs, &(statarray[cnt].rs));
1132 else
1134 masterstat.rs.totalerrs += statarray[cnt].rs.totalerrs;
1138 for (i=0; i < totalnumclients; i++)
1140 for (j=0; j < number_of_pages; j++)
1142 D_PRINTF( "Summing page stats for %d, page %d, with %d connects\n",
1143 i, j, statarray[i].page_numbers[j] );
1145 if (statarray[i].page_numbers[j] != 0)
1147 rqst_stats_t *pst_rs;
1148 rqst_stats_t *ps_rs;
1150 pst_rs = &(page_stats_total[j].rs);
1151 ps_rs = &(page_stats[i][j].rs);
1153 rqstat_sum(pst_rs, ps_rs);
1155 page_stats_total[j].totalpages += page_stats[i][j].totalpages;
1156 masterstat.totalpages += page_stats[i][j].totalpages;
1158 /* yes, this is assignment, not sum */
1159 page_stats_total[j].page_size = page_stats[i][j].page_size;
1161 page_stats_total[j].page_valid = 1;
1166 /* print page statistics */
1167 if (verbose) {
1168 for (i = 0; i < number_of_pages; i++)
1170 if (page_stats_total[i].page_valid == 1)
1172 page_stats_t *pst;
1174 pst = &(page_stats_total[i]);
1176 printf ("===============================================================================\n");
1177 printf ("Page # %d\n\n", i);
1178 printf ("Total number of times page was hit %u\n",
1179 pst->totalpages);
1181 rqstat_print(&(pst->rs));
1183 printf ("Page size %u \n", pst->page_size);
1184 printf ("===============================================================================\n\n");
1189 fprintf(stdout,"===============================================================================\n");
1192 * Validate run.
1194 masterstat.total_num_of_files = statarray[0].total_num_of_files;
1195 for (i=1; i < totalnumclients; i++)
1197 if ((statarray[i].rs.totalconnects > 0) &&
1198 (statarray[i].total_num_of_files != masterstat.total_num_of_files))
1200 fprintf(stdout,"**********************************************************************\n");
1201 fprintf(stdout,"**** ERROR: number of files in each test configuration is not the same\n");
1202 fprintf(stdout,"**** ERROR: Check configuration file %s on each client\n", configfile);
1203 fprintf(stdout,"**********************************************************************\n");
1204 break;
1210 * Print summary statistics
1212 fprintf(stdout, "WEBSTONE 2.0 results:\n");
1214 fprintf(stdout, "Total number of clients: \t%d\n", totalnumclients);
1215 testtime = sumedh_end.tv_sec - sumedh_start.tv_sec;
1216 fprintf(stdout,"Test time: \t\t\t%d seconds\n", testtime);
1218 fprintf(stdout, "Server connection rate: \t%3.2f connections/sec\n",
1219 (double)(masterstat.rs.totalconnects)/(testtime));
1221 fprintf(stdout, "Server error rate: \t\t%4.4f err/sec\n",
1222 (double)(masterstat.rs.totalerrs)/(testtime));
1224 fprintf(stdout, "Server thruput: \t\t%2.2f Mbit/sec\n",
1225 (double)(8*masterstat.rs.totalbytes)/(testtime*1024*1024));
1227 fprintf(stdout, "Little's Load Factor: \t\t%3.2f \n",
1228 (double)(masterstat.rs.totalresponsetime.tv_sec)
1229 /(testtime));
1230 avgtime(&masterstat.rs.totalresponsetime,
1231 masterstat.rs.totalconnects, &dtime);
1233 fprintf(stdout, "Average response time: \t\t%4.4f millisec\n",
1234 (double)1000*(dtime.tv_sec + (double)dtime.tv_usec / 1000000));
1236 fprintf(stdout, "Error Level:\t\t\t%4.4f %%\n",
1237 (double)(100 * masterstat.rs.totalerrs)/(masterstat.rs.totalconnects));
1239 /* so much for the key metrics */
1241 thruput = 8 * thruputpersec((double)(masterstat.rs.totalbytes),
1242 &(masterstat.rs.totalresponsetime));
1244 fprintf(stdout, "Average client thruput: \t%4.4f Mbit/sec\n",
1245 thruput/(1024*1024));
1247 fprintf(stdout,"Sum of client response times:\t%u.%u sec\n",
1248 masterstat.rs.totalresponsetime.tv_sec,
1249 masterstat.rs.totalresponsetime.tv_usec);
1251 fprintf(stdout,"Total number of pages read:\t%u\n\n",
1252 masterstat.totalpages);
1254 /* Remaining stats are the same as usual */
1256 rqstat_fprint(stdout, &(masterstat.rs));
1257 fflush(stdout);
1261 #ifdef WIN32
1262 /* close socket library */
1263 void sock_cleanup(void) {
1265 WSACleanup();
1267 #endif /* WIN32 */
1269 void
1270 main(const int argc, char *argv[])
1273 int sync_sock;
1274 int i;
1275 int j;
1276 char buffer[NCCARGS];
1277 char commandline[NCCARGS];
1278 char *timestr;
1279 time_t starttime;
1280 time_t endtime;
1281 fd_set fdset;
1282 /* make the big arrays static to avoid stack overflow */
1283 static char clienthostname[MAXCLIENTS][MAXHOSTNAMELEN];
1284 static stats_t statarray[MAXCLIENTS];
1285 page_stats_t **page_stats;
1286 page_stats_t *page_stats_total;
1287 struct sockaddr_in serveraddr;
1290 #ifdef WIN32
1291 WSADATA WSAData;
1292 COORD dwSize;
1294 if ((WSAStartup(MAKEWORD(1,1), &WSAData)) != 0) {
1295 errexit("Error in WSAStartup()\n");
1297 atexit(sock_cleanup);
1299 /* increase size of output window */
1300 dwSize.X = 80;
1301 dwSize.Y = 500;
1302 SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), dwSize);
1303 #endif /* WIN32 */
1306 /* Initalization of variables. */
1307 debugfile = stdout;
1308 memset(buffer, 0, NCCARGS);
1309 memset(webserver, 0, MAXHOSTNAMELEN);
1310 memset(configfile, 0, MAXPATHLEN);
1311 FD_ZERO(&zerofdset);
1312 FD_ZERO(&fdset);
1314 for(i = 0; i < MAXCLIENTS; i++)
1316 socknum[i] = BADSOCKET_VALUE;
1317 statarray[i].rs.totalconnects = 0;
1320 signal(SIGINT, sig_int);
1322 ParseCmdLine( argc, argv);
1324 sync_sock = SetupSyncSocket( &serveraddr );
1326 MakeCmdLine( commandline);
1328 totalnumclients = RexecClients( commandline, clienthostname, &serveraddr);
1330 /* Initalization of variables. */
1331 page_stats =
1332 (page_stats_t **)
1333 mymalloc(totalnumclients*sizeof(page_stats_t *));
1334 for (i=0; i < totalnumclients; i++)
1336 page_stats[i] = (page_stats_t *)
1337 mymalloc(number_of_pages*sizeof(page_stats_t));
1339 page_stats_total =
1340 (page_stats_t *)mymalloc(number_of_pages*sizeof(page_stats_t));
1342 for (i=0; i < totalnumclients; i++) {
1343 stats_init(&(statarray[i]));
1345 for (i=0; i < totalnumclients; i++) {
1346 for (j=0; j < number_of_pages; j++) {
1347 page_stats_init(&(page_stats[i][j]));
1350 for (i=0; i < number_of_pages; i++) {
1351 page_stats_init(&(page_stats_total[i]));
1354 for(i = 0; i < totalnumclients; i++)
1356 socknum[i] = BADSOCKET_VALUE;
1357 statarray[i].rs.totalconnects = 0;
1360 GetReady( &fdset, totalnumclients, sync_sock );
1361 NETCLOSE(sync_sock);
1364 * START ALL OF THE CLIENTS BY SENDING THEM A GO SIGNAL.
1368 gettimeofday (&sumedh_start, 0);
1369 SendGo( totalnumclients, socknum);
1372 * WAIT FOR ALL OF THE CLIENTS TO COMPLETE. WE SHOULD GET A REPLY
1373 * FOR EACH SOCKET WE HAVE OPEN. THE REPLY WILL BE THE TIMING
1374 * INFORMATION WE USE.
1377 starttime = time(0);
1378 timestr = asctime(localtime(&starttime));
1379 fprintf(stdout,"All clients started at %s\n",timestr);
1380 fprintf(stdout,"Waiting for clients completion\n");
1381 fflush(stdout);
1383 /* IF THIS IS A TIMED TEST, WE MIGHT AS WELL SNOOZE */
1384 if (testtime) {
1385 sleep(testtime * 60);
1388 GetResults( &fdset, page_stats, &endtime, timestr, totalnumclients,
1389 statarray);
1391 gettimeofday (&sumedh_end, 0);
1392 PrintResults( page_stats, endtime, timestr, totalnumclients, statarray,
1393 page_stats_total);
1395 /* free memory */
1396 for (i = 0; i < totalnumclients; i++)
1398 free(page_stats[i]);
1400 free(page_stats);
1401 free(page_stats_total);
1403 exit(0);
1406 /* Added by Rajesh Shah 5/18/96 */
1407 void
1408 HostEntCpy(struct hostent *dest, struct hostent *src)
1411 dest->h_name = (char *)malloc(strlen(src->h_name)+1);
1412 strcpy(dest->h_name, src->h_name);
1413 printf("WebMaster name = %s\n", dest->h_name);
1414 dest->h_aliases = src->h_aliases;
1415 dest->h_addrtype = src->h_addrtype;
1416 dest->h_length = src->h_length;
1417 dest->h_addr_list = src->h_addr_list;