summaryrefslogtreecommitdiff
path: root/ACE/apps/JAWS/clients/WebSTONE/src/webmaster.c
diff options
context:
space:
mode:
Diffstat (limited to 'ACE/apps/JAWS/clients/WebSTONE/src/webmaster.c')
-rw-r--r--ACE/apps/JAWS/clients/WebSTONE/src/webmaster.c1419
1 files changed, 1419 insertions, 0 deletions
diff --git a/ACE/apps/JAWS/clients/WebSTONE/src/webmaster.c b/ACE/apps/JAWS/clients/WebSTONE/src/webmaster.c
new file mode 100644
index 00000000000..6a3cfae2091
--- /dev/null
+++ b/ACE/apps/JAWS/clients/WebSTONE/src/webmaster.c
@@ -0,0 +1,1419 @@
+/* $Id$ */
+/**************************************************************************
+ * *
+ * Copyright (C) 1995 Silicon Graphics, Inc. *
+ * *
+ * These coded instructions, statements, and computer programs were *
+ * developed by SGI for public use. If any changes are made to this code*
+ * please try to get the changes back to the author. Feel free to make *
+ * modifications and changes to the code and release it. *
+ * *
+ **************************************************************************/
+
+/* FUZZ: disable check_for_math_include */
+/* FUZZ: disable check_for_improper_main_declaration */
+
+#include <stdio.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#ifndef WIN32
+#include <unistd.h>
+#endif /* WIN32 */
+
+#include <math.h>
+
+#ifndef WIN32
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#else
+#define FD_SETSIZE 1024 /* max size for select() - keep before <winsock.h>
+ * and same size as MAXCLIENTS */
+#include <windows.h>
+#include <winsock.h>
+#include <io.h>
+#include <process.h>
+#endif /* WIN32 */
+
+#include "sysdep.h"
+#include "bench.h"
+
+/* command line options/data */
+int savefile = 0;
+int debug = 0;
+int norexec = 0;
+int haveproxyserver = 0;
+char proxyserver[MAXHOSTNAMELEN];
+char network_mask_str[30] = "255.255.255.0";
+unsigned network_mask = 0;
+int servaddrin_config = 0;
+int dumpall = 0;
+int testtime = 0;
+int havewebserver = 0;
+int numloops = 0;
+NETPORT portnum = 0;
+int redirect = 0;
+int record_all_transactions = 0;
+int uil_filelist_f = 0;
+int verbose = 0;
+char webserver[MAXHOSTNAMELEN];
+char configfile[MAXPATHLEN];
+char uil_filelist[NCCARGS];
+
+char filelist[256][MAXPATHLEN];
+fd_set zerofdset;
+
+/* other key data */
+long int number_of_pages = 0;
+int totalnumclients = 0;
+int num_rexecs = 0;
+SOCKET socknum[MAXCLIENTS];
+SOCKET sockIO[MAXTOTALPROCS];
+SOCKET sockErr[MAXTOTALPROCS];
+THREAD FILE *debugfile = stderr;
+struct hostent *master_phe; /* IP addresses for webmaster */
+struct timeval sumedh_start, sumedh_end;
+
+void HostEntCpy(struct hostent *dest, struct hostent *src);
+
+static void
+usage(const char *progname)
+{
+
+ fprintf(stderr, "Usage: %s [-a] [-d] -f config_file [-l numloops]\n",
+ progname);
+ fprintf(stderr, " [-p port_num] [-r] [-s] [-t run_time] \n");
+ fprintf(stderr, "\n");
+ fprintf(stderr, "-w webserver URL [URL ...]\n\n");
+ fprintf(stderr, "-a print timing information for all clients\n");
+ fprintf(stderr, "-d turn on debug statements\n");
+ fprintf(stderr, "-f config_file\tfile specifying clients\n");
+ fprintf(stderr, "-l number of iterations to retrieve uils\n");
+ fprintf(stderr, "-p port number of web server if not 80\n");
+ fprintf(stderr, "-r redirect stdout of clients to /tmp/webstone.xxx\n");
+ fprintf(stderr, "-s save client gets to /tmp/webstone.data.*\n");
+ fprintf(stderr, "-t run_time\tduration of test in minutes\n");
+ fprintf(stderr, "-w webserver\tname of webserver host to contact\n");
+ fprintf(stderr, "-u URL file\tfilelist of URLs\n");
+ fprintf(stderr, "-v verbose mode\n");
+ fprintf(stderr, "-P servername\tuse proxy server for transactions\n");
+ fprintf(stderr, "-W webserver addresses are in the config file\n");
+ fprintf(stderr, "-R record all transactions\n");
+ errexit("\n");
+}
+
+static SOCKET
+passivesock(const NETPORT portnum, const char *protocol, const int qlen)
+{
+ struct protoent *ppe; /* pointer to protocol info entry */
+ struct sockaddr_in sin; /* Internet endpoint address */
+ SOCKET s; /* socket descriptor */
+ int type; /* socket type */
+
+ D_PRINTF( "Beginning passivesock with errno %d\n",errno );
+
+ D_PRINTF( "Zeroing address structure\n" );
+ memset((char *)&sin, 0, sizeof(sin));
+
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = INADDR_ANY;
+
+ /* NOT USED: Map service name to portnumber */
+ D_PRINTF( "Mapping portnum errno %d\n",errno );
+ sin.sin_port = htons(portnum);
+
+ /* Map protocol name to number */
+ D_PRINTF( "Mapping protocol name errno %d\n",errno );
+ if ((ppe = getprotobyname(protocol)) == 0)
+ {
+ errexit("Can't get \"%s\" protocol entry\n", protocol);
+ }
+ errno = 0;
+
+ /* use protocol to choose socket type */
+ D_PRINTF( "Changing socket type, errno %d\n",errno );
+ if (strcmp(protocol, "udp") == 0)
+ {
+ type = SOCK_DGRAM;
+ D_PRINTF( "Choosing SOCK_DGRAM\n" );
+ }
+ else
+ {
+ type = SOCK_STREAM;
+ D_PRINTF( "Choosing SOCK_STREAM, errno %d\n",errno );
+ }
+
+ /* allocate a socket */
+ s = socket(PF_INET, type, ppe->p_proto);
+ if (BADSOCKET(s))
+ {
+ D_PRINTF( "Socket PF_INET %d %d returned %d with %s\n",
+ type, ppe->p_proto, s, neterrstr() );
+ errexit("Can't create socket: %s\n", neterrstr());
+ }
+ D_PRINTF( "Socket %d created with errno %d\n",s,errno );
+
+ /* Bind the socket */
+ if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) < 0)
+ {
+ errexit("Can't bind to port %d: %s\n", portnum, neterrstr());
+ }
+ D_PRINTF( "Bind succeeded\n" );
+
+ /* If it's a stream, listen for connections */
+ /* NOTE: ON NT, the listen() backlog parm is silently limited to 5 conns */
+ if ((type == SOCK_STREAM) && BADSOCKET(listen(s, qlen)))
+ {
+ errexit("Can't listen on port %s: %s\n", portnum, neterrstr());
+ }
+ D_PRINTF( "Listen succeeded\n" );
+
+ /* all done, return socket descriptor */
+ return(s);
+}
+
+
+/* abort clients -- called by SIGINT handler */
+static void abort_clients(void)
+{
+ /* Not supposed to have fprintf in a signal handler, but... */
+ fprintf(stdout, "Webmaster received SIGINT. Terminating.\n");
+ /* exit will close all open connections */
+ exit(2);
+}
+
+/* signal handler for SIGINT */
+static void sig_int(int sig) {
+
+ abort_clients();
+}
+
+#ifdef WIN32
+
+/* echo stdout/stderr from clients */
+void echo_client(void *stream)
+{
+ SOCKET *sockarr;
+ FILE *outfile;
+ int which_stream = (int) stream;
+ char buf[BUFSIZ];
+ int i, len, rv;
+ fd_set readfds;
+
+ /* This code which handles the timeout may need
+ to be ifdef'ed for WIN32 */
+ struct timeval timeout;
+
+ timeout.tv_sec = (long)5;
+ timeout.tv_usec = (long)0;
+
+ if (which_stream) {
+ sockarr = sockIO;
+ outfile = stdout;
+ } else {
+ sockarr = sockErr;
+ outfile = stderr;
+ }
+
+ D_PRINTF( "echo_client running\n" );
+ signal( SIGINT, SIG_DFL); /* restore default behavior
+ for SIGINT */
+
+ while (1) {
+ FD_ZERO(&readfds);
+ for (i = 0; i < num_rexecs; i++)
+ if (sockarr[i] != BADSOCKET_VALUE)
+ FD_SET(sockarr[i], &readfds);
+ rv = select(num_rexecs, &readfds, 0, 0, &timeout);
+ if ( rv == 0)
+ continue;
+ if (rv < 0 && WSAGetLastError() == WSANOTINITIALISED)
+ return;
+ if (rv < 0)
+ errexit("Error in echo_client(): select() returns %d: %s\n", rv, neterrstr());
+
+ /* loop over the sockets that are ready with data */
+ for (i = 0; i < num_rexecs; i++) {
+ if (sockarr[i] != BADSOCKET_VALUE && FD_ISSET(sockarr[i], &readfds)) {
+ len = NETREAD(sockarr[i], buf, sizeof(buf));
+ if (len <= 0) {
+ /* mark connection closed */
+ sockarr[i] = BADSOCKET_VALUE;
+ if (len < 0 && WSAGetLastError() == WSANOTINITIALISED)
+ return;
+ if (len < 0)
+ fprintf(stderr, "Error in echo_client() after NETREAD(): %s\n", neterrstr());
+ continue;
+ }
+
+ /* copy to stdout or stderr */
+ fwrite(buf, sizeof(char), len, outfile);
+ }
+ }
+ }
+ D_PRINTF( "Exiting echo_client\n" );
+}
+
+#else
+static int
+echo_client(char *hostname, const int fd)
+{
+ /*
+ * WRITE TEXT FROM FILE DESCRIPTOR INTO STDOUT
+ */
+ char buf[BUFSIZ];
+ int cc;
+ D_PRINTF( "echo_client running\n" );
+
+ while (getppid() != 1)
+ {
+ cc = NETREAD(fd, buf, sizeof(buf));
+ if (cc > 0)
+ {
+ write(STDOUT_FILENO, buf, cc);
+ }
+ }
+ D_PRINTF( "Exiting echo_client\n" );
+ NETCLOSE(fd);
+}
+#endif /* WIN32 */
+
+/* Picks the appropriate webmaster IP address based on the address of the client.
+ * This is significant only for hosts with multiple interfaces
+ *
+ * return value is a string with the IP address or hostname (or NULL)
+ */
+char *pick_webmaster_IP_address(char *client_hostname, struct hostent *master_phe,
+ unsigned netmask) {
+static char buf[20];
+unsigned char addr[4];
+int client_addr;
+int i;
+
+ if (client_hostname[0] >= '0' && client_hostname[0] <= '9') {
+ /* we have an IP address */
+ client_addr = inet_addr(client_hostname);
+ if (client_addr == INADDR_NONE)
+ return 0;
+ } else {
+ /* we have a hostname, use the webserver hostname */
+ return master_phe->h_name;
+ }
+
+ for (i = 0; master_phe->h_addr_list[i] != 0; i++) {
+ if ((*(int *)(master_phe->h_addr_list[i]) & netmask) ==
+ (client_addr & netmask))
+ goto gotit;
+ }
+ i = 0; /* give up */
+
+gotit:
+ memcpy((char *)addr, master_phe->h_addr_list[i], sizeof(addr)); /* Internet specific */
+ sprintf(buf, "%d.%d.%d.%d", addr[0], addr[1], addr[2], addr[3]);
+ return buf;
+}
+
+/*
+ Command line parsing
+*/
+
+void ParseCmdLine(int argc, char **argv )
+{
+char getoptch;
+int currarg;
+extern char *optarg;
+extern int optind;
+
+ /*
+ * PARSE THE COMMAND LINE OPTIONS
+ */
+ while((getoptch = getopt(argc,argv,"P:f:t:l:p:u:R:w:n:M:adrsvWX")) != (const char)EOF)
+ {
+ switch(getoptch)
+ {
+ case 'M':
+ strcpy(network_mask_str, optarg);
+ break;
+ case 'P':
+ haveproxyserver = 1;
+ strcpy(proxyserver, optarg);
+ break;
+ case 'R':
+ record_all_transactions = 1;
+ break;
+ case 'X':
+ norexec = 1;
+ break;
+ case 'W':
+ servaddrin_config = 1;
+ break;
+ case 'a':
+ dumpall = 1;
+ break;
+ case 'd':
+ debug = 1;
+ break;
+ case 'f':
+ strcpy(configfile, optarg);
+ break;
+ case 'l':
+ numloops = atoi(optarg);
+ break;
+ case 'p':
+ portnum = atoi(optarg);
+ break;
+ case 'r':
+ redirect = 1;
+ break;
+ case 's':
+ savefile = 1;
+ break;
+ case 't':
+ testtime = atoi(optarg);
+ break;
+ case 'u':
+ uil_filelist_f = 1;
+ strcpy(uil_filelist, optarg);
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ case 'w':
+ havewebserver = 1;
+ strcpy(webserver, optarg);
+ break;
+ default:
+ usage(argv[0]);
+ } /* end switch */
+ } /* end while */
+
+ if (numloops && testtime)
+ errexit("Can't have both -l and -t\n");
+
+ if(!havewebserver && !servaddrin_config)
+ {
+ /*
+ * THE SERVERS NAME MUST BE SPECIFIED
+ */
+
+ fprintf(stderr,"No WWW Server specified\n");
+ usage(argv[0]);
+ }
+
+ if (havewebserver && servaddrin_config)
+ {
+ /*
+ * CAN'T HAVE BOTH -w and -W
+ */
+ fprintf(stderr, "Can't have both -w and -W options\n");
+ usage(argv[0]);
+ }
+
+ network_mask = inet_addr(network_mask_str);
+ if (network_mask == INADDR_NONE) {
+ fprintf(stderr, "Invalid network mask (-M %s)\n", network_mask_str);
+ usage(argv[0]);
+ }
+
+ if(strlen(configfile) == 0)
+ {
+ /*
+ * THE MASTER MUST HAVE A CONFIGURATION FILE TO READ.
+ */
+ fprintf(stderr,"No Configuration file specified\n");
+ usage(argv[0]);
+ }
+ /* IF WE DO NOT HAVE A FILE LIST THEN THERE ARE UIL'S AT THE END OF THE
+ * COMMAND LINE SO GRAB THEM.
+ */
+ if (uil_filelist_f == 0)
+ {
+ currarg = optind;
+ number_of_pages = 0;
+ while(currarg != argc)
+ {
+ /*
+ * GET THE UILS TO RETRIEVE.
+ */
+
+ sscanf(argv[currarg],"%s",filelist[number_of_pages]);
+ number_of_pages++;
+ currarg++;
+ }
+ }
+ else
+ {
+ /* have filelist; take a stab at the number of valid URLs */
+ D_PRINTF( "About to parse filelist %s\n", uil_filelist );
+ number_of_pages = count_file_list(uil_filelist);
+ }
+ if (number_of_pages == 0)
+ {
+ /*
+ * AT LEAST ONE FILE MUST BE SPECIFIED
+ */
+ fprintf(stderr,"No URL resources specified\n");
+ usage(argv[0]);
+ }
+}
+
+/*
+ This function sets up the socket we will use to synchronize with the
+ clients.
+ Returns the socket number if successful, doesn't return if it fails
+*/
+
+SOCKET SetupSyncSocket( serveraddr )
+struct sockaddr_in *serveraddr;
+{
+ int sock,len;
+
+ /*
+ * SET UP THE SOCKET WE ARE GOING TO USE TO SYNCHRONIZE WITH THE CLIENTS.
+ */
+ D_PRINTF( "About to call sock %d %d\n", portnum, MAXCLIENTS );
+
+ sock = passivesock(0, "tcp", MAXCLIENTS);
+
+ if (BADSOCKET(sock))
+ {
+ errexit("Couldn't open socket %d: %s\n", sock, neterrstr());
+ }
+ D_PRINTF( "The passivesock call succeeded\n" );
+
+ D_PRINTF( "calling getsockname\n" );
+
+ len = sizeof(struct sockaddr);
+ if(getsockname(sock, (struct sockaddr *)serveraddr, &len) < 0)
+ {
+ errexit("Could not get socket informaton\n");
+ }
+
+ return( sock );
+}
+
+/*
+ Function which generates a commandline for the webclients
+*/
+void MakeCmdLine( commandline)
+char *commandline;
+{
+ char tmpcommandline[NCCARGS];
+ char hostname[MAXHOSTNAMELEN];
+ char *webclient_path, *temp;
+ int cnt;
+ struct hostent *master_phe_tmp; /* temp. variable added by - Rajesh Shah*/
+
+ /*
+ * BUILD THE PORTIONS OF THE cmdline FOR EACH CLIENT THAT WE CAN BUILD NOW.
+ * WE WILL FILL IN THE NUMBER OF CLIENTS LATER WITH AN sprintf.
+ */
+ D_PRINTF( "Calling gethostname\n" );
+
+ if(gethostname(hostname,MAXHOSTNAMELEN) != 0)
+ {
+ errexit("Could not retrieve local host name");
+ } else {
+ /* convert hostname to address (to avoid DNS problems for webclients) */
+ /* The following lines are add to copy the system
+ buffer (output of gethostbyname()) into user area.
+ This is because, there are very good chances that later
+ on system buffer might be overwritten by some calls and
+ still if your pointer is pointing to same addr. nothing
+ but only trouble and trouble! Infact this is what
+ happening when I tried to run webstone benchmark for more
+ then one clients. It used to over write the webmaster name
+ with the first client name and so remaining on client(s)
+ the webclient process(es) were invoked with wrong webmaster
+ name! This behaviour is observed Solaris 2.4 this bug
+ can be hit in any OS. - Rajesh Shah 5/18/96 */
+
+ /* master_phe = gethostbyname(hostname); */
+ master_phe_tmp = gethostbyname(hostname);
+ master_phe = (struct hostent *)malloc(sizeof(struct hostent));
+ HostEntCpy(master_phe, master_phe_tmp);
+ }
+
+ /* set up executable pathname */
+#ifndef WIN32
+ temp = getenv("TMPDIR");
+
+ if ( temp && *temp ) {
+ webclient_path = (char *)mymalloc( strlen(temp) + strlen("/webclient")
+ + 1);
+ strcpy(webclient_path, temp);
+ strcat(webclient_path, "/webclient");
+
+ } else
+#else
+ temp = temp;
+#endif /* WIN32 */
+ webclient_path = PROGPATH;
+
+
+ D_PRINTF( "Path to webclient is: %s\n", webclient_path );
+
+ sprintf(commandline,"%s", webclient_path);
+
+ if(haveproxyserver)
+ {
+ sprintf(tmpcommandline, " -P %s", proxyserver);
+ strcat(commandline, tmpcommandline);
+ }
+ if (debug)
+ {
+ strcat(commandline," -d");
+ }
+ if (numloops != 0)
+ {
+ sprintf(tmpcommandline," -l %d", numloops);
+ strcat(commandline,tmpcommandline);
+ }
+ if (portnum)
+ {
+ sprintf(tmpcommandline," -p %d", portnum);
+ strcat(commandline,tmpcommandline);
+ }
+ if (redirect)
+ {
+ strcat(commandline," -r");
+ }
+ if (savefile)
+ {
+ strcat(commandline," -s");
+ }
+ if (uil_filelist_f)
+ {
+ strcat(commandline," -u ");
+ strcat(commandline,uil_filelist);
+ }
+ if (record_all_transactions)
+ {
+ strcat(commandline," -R");
+ }
+ if (testtime != 0)
+ {
+ sprintf(tmpcommandline," -t %d", testtime);
+ strcat(commandline,tmpcommandline);
+ }
+
+ /*
+ * SET UP A SPACE FOR THE NUMBER OF CLIENTS ON THE commandline.
+ */
+ sprintf(tmpcommandline,"%s -n %%d -w %%s -c %%s:%%d", commandline);
+ strcpy(commandline,tmpcommandline);
+
+ if (uil_filelist_f == 0)
+ {
+ cnt = 0;
+ while(cnt < number_of_pages)
+ {
+ /*
+ * PUT THE FILES AT THE END OF THE LIST.
+ */
+ strcat(commandline," ");
+ strcat(commandline,filelist[cnt]);
+ cnt++;
+ }
+ }
+ puts(commandline);
+}
+
+/*
+ rexec to the client hosts and start the webclients
+*/
+int RexecClients( commandline, clienthostname, serveraddr)
+char *commandline;
+char clienthostname[MAXCLIENTS][MAXHOSTNAMELEN];
+struct sockaddr_in *serveraddr;
+
+{
+ int tmpfd;
+ int numclients = 0;
+ char tmpcommandline[NCCARGS];
+ struct servent *inetport;
+ int cnt;
+ char buffer[NCCARGS];
+ char login[MAXUSERNAME];
+ char password[MAXPASSWD];
+ FILE *fp;
+ int returnval;
+ char *tmphostname;
+
+ memset(buffer, 0, sizeof(buffer));
+
+ /*
+ * OPEN UP THE CONFIG FILE. FOR EACH LINE IN THE CONFIG FILE, CHECK
+ * ITS VALIDITY AND THEN rexec A COMMAND ON THE CLIENT.
+ */
+
+ if ((fp = fopen(configfile,"r")) == 0)
+ {
+ errexit("Could not open config file %s\n", configfile);
+ }
+
+ if ((inetport = getservbyname("exec","tcp")) == 0)
+ {
+ errexit("Could not get service name for exec/tcp\n");
+ }
+ D_PRINTF( "getservbyname returned %d\n", ntohs(inetport->s_port) );
+
+ cnt = 0;
+
+ D_PRINTF( "rexec loop\n" );
+ while(1)
+ {
+ char webserver2[MAXHOSTNAMELEN];
+ char linebuf[150];
+ int num;
+ char *primename;
+
+ if (0 == fgets(linebuf, sizeof(linebuf), fp))
+ break;
+ num = sscanf(linebuf,"%s %s %s %d %s",clienthostname[cnt],login,password,
+ &numclients, webserver2);
+ if (num < 4)
+ break;
+ if (servaddrin_config) {
+ if (num == 4) {
+ errexit("No webserver specified in config file for %s\n", clienthostname[cnt]);
+ }
+ strcpy(webserver, webserver2);
+ }
+
+ if (numclients <= 0)
+ errexit("Number of clients must be >= 0\n");
+ if (numclients > MAXPROCSPERNODE)
+ {
+ errexit("Number of clients per node can't exceed %d\n", MAXPROCSPERNODE);
+ }
+ totalnumclients += numclients;
+
+ primename = pick_webmaster_IP_address(clienthostname[cnt], master_phe, network_mask);
+ if (primename == 0) {
+ errexit("Bad client address %s for Client %d\n", clienthostname[cnt], cnt);
+ }
+
+ fprintf(stdout,"Client %d: %s \t# Processes: %d\n Webserver: %s\tWebmaster: %s:%d\n",
+ cnt, clienthostname[cnt], numclients, webserver, primename,
+ ntohs(serveraddr->sin_port));
+ fflush(stdout);
+ sprintf(tmpcommandline, commandline, numclients, webserver, primename,
+ ntohs(serveraddr->sin_port));
+
+ fprintf(stderr, "tmpcommandline: %s\n", tmpcommandline);
+
+ D_PRINTF( "%s rexec %s\n",&clienthostname[cnt],tmpcommandline );
+ if (norexec) {
+ sleep(30); /* gives some time to start clients for debugging */
+ } else {
+
+ tmphostname = &(clienthostname[cnt][0]);
+ tmpfd = rexec(&tmphostname, inetport->s_port, login, password,
+ tmpcommandline, &sockErr[cnt]);
+ if((sockIO[cnt] = tmpfd) < 0)
+ {
+ errexit("Could not rexec: rexec to client %s, cmdline %s failed\n",
+ clienthostname[cnt],tmpcommandline);
+ }
+ }
+
+
+ returnval = NETREAD(tmpfd, buffer, OKSTRLEN);
+ D_PRINTF( "read returns %d, %s\n", returnval, buffer );
+
+ if (returnval <= 0 || memcmp(buffer, OKSTR, OKSTRLEN) != 0)
+ {
+ errexit("rexec to client %s, cmdline %s received error %s\n",
+ clienthostname[cnt],tmpcommandline, buffer);
+ }
+
+
+ cnt++;
+ if (cnt > MAXCLIENTS || cnt > FD_SETSIZE)
+ {
+ errexit("Number of Clients can't exceed %d\n", MAXCLIENTS);
+ }
+ }
+
+ num_rexecs = cnt;
+ if (totalnumclients > MAXTOTALPROCS)
+ {
+ errexit("Total number of processes can't exceed %d\n",
+ MAXTOTALPROCS);
+ }
+
+#ifndef WIN32
+ /* NOW WE NEED TO HANDLE THE OUTPUT FROM THE REXEC.
+ * TO DO THIS, WE FORK, THEN HAVE ONE PROCESS READ FROM TMPFD.
+ * THE OTHER PROCESS CONTINUES WITH THE PROGRAM
+ */
+ D_PRINTF( "Forking webclient stderr/stdout processes\n" );
+ switch (fork())
+ {
+ case -1: /* ERROR */
+ errexit("fork: %s\n", strerror(errno));
+ case 0: /* CHILD */
+ exit(echo_client(clienthostname[cnt], tmpfd));
+ default: /* PARENT */
+ break;
+ }
+#else
+ /* start threads to echo stdout/stderr from clients */
+ _beginthread(echo_client, 0, (void *)0);
+ _beginthread(echo_client, 0, (void *)1);
+#endif /* WIN32 */
+
+ fprintf(stdout,"\n");
+ fprintf(stdout,"\n");
+ fclose(fp);
+
+ return totalnumclients;
+}
+
+void GetReady( fdset, totalnumclients, sock )
+fd_set *fdset;
+int totalnumclients;
+int sock;
+{
+ int cnt,len;
+ fd_set tmpfdset, leftfdset;
+ char buffer[NCCARGS];
+
+ /*
+ * NOW WE NEED TO ACCEPT ALL THE CONNECTIONS FROM THE CLIENTS,
+ * ACCEPT ALL THE READY MESSAGES
+ */
+
+ D_PRINTF( "Beginning accept loop\n" );
+ for (cnt = 0; cnt < totalnumclients; cnt++)
+ {
+ D_PRINTF( "Client %d:\t", cnt );
+
+ {
+ fd_set readfds;
+ struct timeval timeout;
+ int rv;
+
+ timeout.tv_sec = MAX_ACCEPT_SECS;
+ timeout.tv_usec = 0;
+ FD_ZERO(&readfds);
+ FD_SET(sock, &readfds);
+
+ /* if we're hung, quit */
+ D_PRINTF("Before select() on listen() socket\n");
+ if (!(rv = select(FD_SETSIZE, &readfds, 0, 0, &timeout))) {
+ fprintf(stdout,
+ "Listen timeout after %d seconds (%d clients so far)\n",
+ MAX_ACCEPT_SECS, cnt);
+ D_PRINTF("select() timed out after %d seconds\n", MAX_ACCEPT_SECS);
+ errexit("Webmaster terminating\n");
+ }
+ }
+
+ if(BADSOCKET(socknum[cnt] = accept(sock, 0, 0)))
+ {
+ /*
+ * ERROR accepting FROM THE CLIENTS. WE NEED TO ISSUE AN
+ * ABORT TO ALL.
+ */
+ abort_clients();
+ errexit("Error accepting from one of the clients: %s", neterrstr());
+ } else
+ {
+ /*
+ * SET THE FD IN THE MASK
+ */
+ FD_SET(socknum[cnt],fdset);
+ }
+ D_PRINTF( "on socket %d\n",socknum[cnt] );
+ }
+ D_PRINTF( "\n" );
+
+ /*
+ * WAIT FOR A READY.
+ */
+ sleep(1);
+ fprintf(stdout,"Waiting for READY from %d clients\n",totalnumclients);
+ fflush(stdout);
+ leftfdset = *fdset;
+#ifndef WIN32
+ while(memcmp(&leftfdset,&zerofdset,sizeof(fd_set)))
+ {
+ tmpfdset = leftfdset;
+
+ if(select(FD_SETSIZE,&tmpfdset,NULL,NULL,NULL) < 0)
+ {
+ /*
+ * ERROR SELECTING. ABORT ALL.
+ */
+ abort_clients();
+ errexit("Error accepting from one of the clients: %s\n",
+ neterrstr());
+ break;
+ }
+#else
+ /* I don't see why a select is needed at all--all clients must respond
+ * and there is no synchronization/timing issue.
+ */
+ tmpfdset = leftfdset;
+ {
+#endif /* WIN32 */
+
+ for (cnt = 0; cnt < totalnumclients; cnt++)
+ {
+ /*
+ * SEE WHICH SOCKETS HAVE A INPUT ON THEM PENDING
+ * AND RECEIVE IT.
+ */
+ if(!BADSOCKET(socknum[cnt]) && (FD_ISSET(socknum[cnt],&tmpfdset)))
+ {
+ /*
+ * GET THE READY FROM THIS GUY.
+ * DON'T FORGET TO CLEAR HIS BIT IN THE tmpfdset
+ */
+ len = NETREAD(socknum[cnt],buffer,READYSTRLEN);
+ if(len != READYSTRLEN)
+ {
+ abort_clients();
+ errexit("Error reading from client #%d\n", cnt);
+ }
+ if(memcmp(buffer, READYSTR, READYSTRLEN))
+ {
+ abort_clients();
+ fprintf(stdout,"Received bad READY string: len %d, value %s\n",
+ len,buffer);
+ }
+ FD_CLR(socknum[cnt],&leftfdset);
+ }
+ }
+ }
+ sleep(1);
+ fprintf(stdout,"All READYs received\n");
+ fflush(stdout);
+}
+
+/*
+ Start all the clients by sending them a GO signal
+ totalnumclients is the total number of clients
+ socknum is an int array with the filedescriptors for all the
+ client connections
+*/
+void SendGo( totalnumclients, socknum)
+int totalnumclients;
+int *socknum;
+{
+ int cnt;
+ fprintf(stdout,"Sending GO to all clients\n");
+ for(cnt = 0; cnt < totalnumclients; cnt++)
+ {
+ if(socknum[cnt] > 0)
+ {
+ /*
+ * SEND A GO
+ */
+ if(NETWRITE(socknum[cnt], GOSTR, GOSTRLEN) != GOSTRLEN)
+ {
+ abort_clients();
+ errexit("Error sending GO to client %d: %s\n", cnt, neterrstr());
+ }
+ }
+ }
+}
+
+/*
+ This function gathers statistics from all the clients
+*/
+
+void GetResults(fdset, page_stats, endtime, timestr, totalnumclients,
+ statarray)
+fd_set *fdset;
+page_stats_t **page_stats;
+time_t *endtime;
+char *timestr;
+int totalnumclients;
+stats_t statarray[MAXCLIENTS];
+{
+ fd_set leftfdset,tmpfdset;
+ char *stats_as_text;
+ char *page_stats_as_text;
+ int returnval;
+ int cnt,i;
+
+
+ /* DOESN'T ACTUALLY PRINT UNTIL THE FIRST CLIENT REPORTS */
+ fprintf(stdout,"Reading results ");
+
+ /*
+ * COPY THE FILE DESCRIPTORS TO A TMP LIST,
+ * ALLOCATE MEMORY FOR STATS, PAGESTATS IN TEXT FORM
+ */
+ leftfdset = *fdset;
+ stats_as_text = (char *)mymalloc(SIZEOF_STATSTEXT+1);
+ page_stats_as_text = (char *)mymalloc(SIZEOF_PAGESTATSTEXT+1);
+
+ /*
+ * COPY THE FILE DESCRIPTORS TO A TMP LIST,
+ * PLUS A LIST OF REMAINING FDs
+ */
+ leftfdset = *fdset;
+ /*
+ * LOOP UNTIL ALL CLIENTS HAVE REPORTED
+ * AND tmpfdset IS EMPTY
+ */
+#ifndef WIN32
+ while(memcmp(&leftfdset,&zerofdset,sizeof(fd_set)))
+ {
+ tmpfdset = leftfdset;
+ sleep(1);
+ returnval = select(FD_SETSIZE,&tmpfdset,NULL,NULL,NULL);
+ D_PRINTF( "Call to select returned %d, errno %d\n",
+ returnval, errno );
+ if(returnval < 0)
+ {
+ /*
+ * ERROR SELECTING. ABORT ALL.
+ */
+ D_PRINTF( "select() error %s\n", neterrstr() );
+ abort_clients();
+ errexit("Error selecting from one of the clients\n");
+ }
+#else
+ /* I don't see why a select is needed at all */
+ tmpfdset = leftfdset;
+ {
+#endif /* WIN32 */
+ for(cnt = 0; cnt < totalnumclients; cnt++)
+ {
+ /*
+ * SEE WHICH SOCKETS HAVE A INPUT ON THEM PENDING AND
+ * RECEIVE IT.
+ */
+
+ /* IS THIS A VALID SOCKET? IS IT READY TO READ? */
+ if(!BADSOCKET(socknum[cnt]) && (FD_ISSET(socknum[cnt],&tmpfdset)))
+ {
+ int len;
+
+ /*
+ * GET THE TIMING DATA FROM THIS GUY
+ * THEN REMOVE HIM FROM THE tmpfdset
+ */
+ /*
+ * READ TIME STATS
+ * DOES READ() RETURN THE CORRECT LENGTH?
+ */
+ D_PRINTF( "About to read timestats, count %d, errno %d\n",
+ cnt, errno );
+ len = SIZEOF_STATSTEXTBASE + number_of_pages*SIZEOF_DOUBLETEXT;
+ returnval = recvdata(socknum[cnt], stats_as_text,
+ len);
+ D_PRINTF( "Read time stats %d\n", returnval );
+ if (returnval != len) /* <= 0) */
+ {
+ D_PRINTF( "Error reading timing stats: %s\n",
+ neterrstr() );
+ fprintf(stderr, "Error reading timing stats: %s\nSocket number %d\n",
+ neterrstr(),socknum[cnt]);
+ abort_clients();
+ errexit("");
+ } /* end if */
+
+ /* convert text to stats */
+ stats_as_text[returnval] = 0; /* add an end marker */
+ statarray[cnt] = *text_to_stats(stats_as_text);
+
+ fputc('.', stdout); /* PROGRESS MARKER */
+ fflush(stdout);
+
+ if(uil_filelist_f) /* READ PAGE STATS */
+ {
+ for (i = 0; i < number_of_pages; i++)
+ {
+ D_PRINTF( "On page_stats[%d][%d]\n", cnt, i );
+ returnval = recvdata(socknum[cnt], page_stats_as_text,
+ SIZEOF_PAGESTATSTEXT);
+ D_PRINTF( "Read page stats %d\n", returnval );
+
+ if (returnval != SIZEOF_PAGESTATSTEXT) /* <= 0) */
+ {
+ D_PRINTF( "Error reading page_stats[%d][%d]: %s\n",
+ cnt, i, neterrstr() );
+ fprintf(stderr, "Error reading page_stats[%d][%d]: %s\n",
+ cnt, i, neterrstr());
+ abort_clients();
+ errexit("");
+ }
+ D_PRINTF( "Page stats: read %d bytes\n",
+ returnval );
+
+ page_stats_as_text[returnval] = 0; /* add an end marker */
+ D_PRINTF("strlen(page_stats_as_text) = %d\n",
+ strlen(page_stats_as_text));
+ page_stats[cnt][i] =
+ *text_to_page_stats(page_stats_as_text);
+
+ } /* end for */
+ } /* end if filelist */
+
+ FD_CLR(socknum[cnt],&leftfdset);
+ NETCLOSE(socknum[cnt]);
+ socknum[cnt] = BADSOCKET_VALUE;
+ } /* end if socknum */
+ } /* end for cnt */
+ } /* end while memcmp fd */
+
+ /*
+ * DONE READING RESULTS FROM CLIENTS
+ */
+
+ *endtime = time(0);
+ timestr = asctime(localtime(endtime));
+ fprintf(stdout,"\nAll clients ended at %s\n",timestr);
+ fflush(stdout);
+
+ /* FREE MEMORY ALLOCATED FOR CLIENT STATS, PAGESTATS AS TEXT */
+ free(stats_as_text);
+ free(page_stats_as_text);
+
+}
+
+/*
+ Prints out all the results
+*/
+void PrintResults( page_stats, endtime, timestr, totalnumclients, statarray,
+ page_stats_total)
+page_stats_t **page_stats;
+time_t endtime;
+char *timestr;
+int totalnumclients;
+stats_t statarray[MAXCLIENTS];
+page_stats_t *page_stats_total;
+{
+ stats_t masterstat;
+ int cnt,i,j;
+ double thruput;
+ struct timeval dtime;
+
+ /*
+ * PRINT EVERYTHING OUT
+ */
+ stats_init(&masterstat);
+ for(cnt = 0; cnt < totalnumclients; cnt++)
+ {
+ if((statarray[cnt].rs.totalconnects > 0) && (dumpall))
+ {
+ fprintf(stdout,"----------------------------------\n");
+ /* fprintf(stdout,"Test for host: %s\n",statarray[cnt].hostname); */
+ fprintf(stdout,"Total number of pages retrieved from server: %u\n",
+ statarray[cnt].totalpages);
+
+ rqstat_fprint(stdout, &(statarray[cnt].rs));
+
+ thruput = thruputpersec((double)(statarray[cnt].rs.totalbytes),
+ &(statarray[cnt].rs.totalresponsetime));
+
+ fprintf(stdout, "Thruput average per connection: %.0f bytes/sec\n",
+ thruput);
+ }
+ if(statarray[cnt].rs.totalconnects > 0)
+ {
+ D_PRINTF( "Summing stats for %d, with %ld total connections\n",
+ cnt, statarray[cnt].rs.totalconnects );
+ rqstat_sum(&masterstat.rs, &(statarray[cnt].rs));
+ }
+ else
+ {
+ masterstat.rs.totalerrs += statarray[cnt].rs.totalerrs;
+ }
+ }
+
+ for (i=0; i < totalnumclients; i++)
+ {
+ for (j=0; j < number_of_pages; j++)
+ {
+ D_PRINTF( "Summing page stats for %d, page %d, with %d connects\n",
+ i, j, statarray[i].page_numbers[j] );
+
+ if (statarray[i].page_numbers[j] != 0)
+ {
+ rqst_stats_t *pst_rs;
+ rqst_stats_t *ps_rs;
+
+ pst_rs = &(page_stats_total[j].rs);
+ ps_rs = &(page_stats[i][j].rs);
+
+ rqstat_sum(pst_rs, ps_rs);
+
+ page_stats_total[j].totalpages += page_stats[i][j].totalpages;
+ masterstat.totalpages += page_stats[i][j].totalpages;
+
+ /* yes, this is assignment, not sum */
+ page_stats_total[j].page_size = page_stats[i][j].page_size;
+
+ page_stats_total[j].page_valid = 1;
+ }
+ }
+ }
+
+ /* print page statistics */
+ if (verbose) {
+ for (i = 0; i < number_of_pages; i++)
+ {
+ if (page_stats_total[i].page_valid == 1)
+ {
+ page_stats_t *pst;
+
+ pst = &(page_stats_total[i]);
+
+ printf ("===============================================================================\n");
+ printf ("Page # %d\n\n", i);
+ printf ("Total number of times page was hit %u\n",
+ pst->totalpages);
+
+ rqstat_print(&(pst->rs));
+
+ printf ("Page size %u \n", pst->page_size);
+ printf ("===============================================================================\n\n");
+ }
+ }
+ }
+
+ fprintf(stdout,"===============================================================================\n");
+
+ /*
+ * Validate run.
+ */
+ masterstat.total_num_of_files = statarray[0].total_num_of_files;
+ for (i=1; i < totalnumclients; i++)
+ {
+ if ((statarray[i].rs.totalconnects > 0) &&
+ (statarray[i].total_num_of_files != masterstat.total_num_of_files))
+ {
+ fprintf(stdout,"**********************************************************************\n");
+ fprintf(stdout,"**** ERROR: number of files in each test configuration is not the same\n");
+ fprintf(stdout,"**** ERROR: Check configuration file %s on each client\n", configfile);
+ fprintf(stdout,"**********************************************************************\n");
+ break;
+ }
+ }
+
+
+ /*
+ * Print summary statistics
+ */
+ fprintf(stdout, "WEBSTONE 2.0 results:\n");
+
+ fprintf(stdout, "Total number of clients: \t%d\n", totalnumclients);
+ testtime = sumedh_end.tv_sec - sumedh_start.tv_sec;
+ fprintf(stdout,"Test time: \t\t\t%d seconds\n", testtime);
+
+ fprintf(stdout, "Server connection rate: \t%3.2f connections/sec\n",
+ (double)(masterstat.rs.totalconnects)/(testtime));
+
+ fprintf(stdout, "Server error rate: \t\t%4.4f err/sec\n",
+ (double)(masterstat.rs.totalerrs)/(testtime));
+
+ fprintf(stdout, "Server thruput: \t\t%2.2f Mbit/sec\n",
+ (double)(8*masterstat.rs.totalbytes)/(testtime*1024*1024));
+
+ fprintf(stdout, "Little's Load Factor: \t\t%3.2f \n",
+ (double)(masterstat.rs.totalresponsetime.tv_sec)
+ /(testtime));
+ avgtime(&masterstat.rs.totalresponsetime,
+ masterstat.rs.totalconnects, &dtime);
+
+ fprintf(stdout, "Average response time: \t\t%4.4f millisec\n",
+ (double)1000*(dtime.tv_sec + (double)dtime.tv_usec / 1000000));
+
+ fprintf(stdout, "Error Level:\t\t\t%4.4f %%\n",
+ (double)(100 * masterstat.rs.totalerrs)/(masterstat.rs.totalconnects));
+
+ /* so much for the key metrics */
+
+ thruput = 8 * thruputpersec((double)(masterstat.rs.totalbytes),
+ &(masterstat.rs.totalresponsetime));
+
+ fprintf(stdout, "Average client thruput: \t%4.4f Mbit/sec\n",
+ thruput/(1024*1024));
+
+ fprintf(stdout,"Sum of client response times:\t%u.%u sec\n",
+ masterstat.rs.totalresponsetime.tv_sec,
+ masterstat.rs.totalresponsetime.tv_usec);
+
+ fprintf(stdout,"Total number of pages read:\t%u\n\n",
+ masterstat.totalpages);
+
+ /* Remaining stats are the same as usual */
+
+ rqstat_fprint(stdout, &(masterstat.rs));
+ fflush(stdout);
+
+}
+
+#ifdef WIN32
+/* close socket library */
+void sock_cleanup(void) {
+
+ WSACleanup();
+}
+#endif /* WIN32 */
+
+void
+main(const int argc, char *argv[])
+{
+
+ int sync_sock;
+ int i;
+ int j;
+ char buffer[NCCARGS];
+ char commandline[NCCARGS];
+ char *timestr;
+ time_t starttime;
+ time_t endtime;
+ fd_set fdset;
+ /* make the big arrays static to avoid stack overflow */
+ static char clienthostname[MAXCLIENTS][MAXHOSTNAMELEN];
+ static stats_t statarray[MAXCLIENTS];
+ page_stats_t **page_stats;
+ page_stats_t *page_stats_total;
+ struct sockaddr_in serveraddr;
+
+
+#ifdef WIN32
+ WSADATA WSAData;
+ COORD dwSize;
+
+ if ((WSAStartup(MAKEWORD(1,1), &WSAData)) != 0) {
+ errexit("Error in WSAStartup()\n");
+ }
+ atexit(sock_cleanup);
+
+ /* increase size of output window */
+ dwSize.X = 80;
+ dwSize.Y = 500;
+ SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), dwSize);
+#endif /* WIN32 */
+
+
+ /* Initalization of variables. */
+ debugfile = stdout;
+ memset(buffer, 0, NCCARGS);
+ memset(webserver, 0, MAXHOSTNAMELEN);
+ memset(configfile, 0, MAXPATHLEN);
+ FD_ZERO(&zerofdset);
+ FD_ZERO(&fdset);
+
+ for(i = 0; i < MAXCLIENTS; i++)
+ {
+ socknum[i] = BADSOCKET_VALUE;
+ statarray[i].rs.totalconnects = 0;
+ }
+
+ signal(SIGINT, sig_int);
+
+ ParseCmdLine( argc, argv);
+
+ sync_sock = SetupSyncSocket( &serveraddr );
+
+ MakeCmdLine( commandline);
+
+ totalnumclients = RexecClients( commandline, clienthostname, &serveraddr);
+
+ /* Initalization of variables. */
+ page_stats =
+ (page_stats_t **)
+ mymalloc(totalnumclients*sizeof(page_stats_t *));
+ for (i=0; i < totalnumclients; i++)
+ {
+ page_stats[i] = (page_stats_t *)
+ mymalloc(number_of_pages*sizeof(page_stats_t));
+ }
+ page_stats_total =
+ (page_stats_t *)mymalloc(number_of_pages*sizeof(page_stats_t));
+
+ for (i=0; i < totalnumclients; i++) {
+ stats_init(&(statarray[i]));
+ }
+ for (i=0; i < totalnumclients; i++) {
+ for (j=0; j < number_of_pages; j++) {
+ page_stats_init(&(page_stats[i][j]));
+ }
+ }
+ for (i=0; i < number_of_pages; i++) {
+ page_stats_init(&(page_stats_total[i]));
+ }
+
+ for(i = 0; i < totalnumclients; i++)
+ {
+ socknum[i] = BADSOCKET_VALUE;
+ statarray[i].rs.totalconnects = 0;
+ }
+
+ GetReady( &fdset, totalnumclients, sync_sock );
+ NETCLOSE(sync_sock);
+
+ /*
+ * START ALL OF THE CLIENTS BY SENDING THEM A GO SIGNAL.
+ */
+
+
+ gettimeofday (&sumedh_start, 0);
+ SendGo( totalnumclients, socknum);
+
+ /*
+ * WAIT FOR ALL OF THE CLIENTS TO COMPLETE. WE SHOULD GET A REPLY
+ * FOR EACH SOCKET WE HAVE OPEN. THE REPLY WILL BE THE TIMING
+ * INFORMATION WE USE.
+ */
+
+ starttime = time(0);
+ timestr = asctime(localtime(&starttime));
+ fprintf(stdout,"All clients started at %s\n",timestr);
+ fprintf(stdout,"Waiting for clients completion\n");
+ fflush(stdout);
+
+ /* IF THIS IS A TIMED TEST, WE MIGHT AS WELL SNOOZE */
+ if (testtime) {
+ sleep(testtime * 60);
+ }
+
+ GetResults( &fdset, page_stats, &endtime, timestr, totalnumclients,
+ statarray);
+
+ gettimeofday (&sumedh_end, 0);
+ PrintResults( page_stats, endtime, timestr, totalnumclients, statarray,
+ page_stats_total);
+
+ /* free memory */
+ for (i = 0; i < totalnumclients; i++)
+ {
+ free(page_stats[i]);
+ }
+ free(page_stats);
+ free(page_stats_total);
+
+ exit(0);
+}
+
+/* Added by Rajesh Shah 5/18/96 */
+void
+HostEntCpy(struct hostent *dest, struct hostent *src)
+{
+
+ dest->h_name = (char *)malloc(strlen(src->h_name)+1);
+ strcpy(dest->h_name, src->h_name);
+ printf("WebMaster name = %s\n", dest->h_name);
+ dest->h_aliases = src->h_aliases;
+ dest->h_addrtype = src->h_addrtype;
+ dest->h_length = src->h_length;
+ dest->h_addr_list = src->h_addr_list;
+}