text: /* $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 <thread.h>
  
  #include <stdio.h>
  
  #ifdef WIN32
  #include <windows.h>
  #include <winsock.h>
  #include <time.h>
  #include <process.h>
  #include <io.h>
  #endif /* WIN32 */
  
  #include <errno.h>
  #include <signal.h>
  #include <stdlib.h>
  #include <string.h>
  
  #ifndef WIN32
  #include <netdb.h>
  #include <unistd.h>
  #endif /* WIN32 */
  
  #include <time.h>
  #include <math.h>
  
  #ifndef WIN32
  #include <sys/param.h>
  #endif /* WIN32 */
  
  #include <sys/types.h>
  
  #ifndef WIN32
  #include <sys/ipc.h>
  #include <sys/shm.h>
  #include <sys/errno.h>
  #include <sys/socket.h>
  #include <sys/time.h>
  #include <sys/wait.h>
  #include <netinet/in.h>
  #include <arpa/inet.h>
  #endif /* WIN32 */
  
  #include <ctype.h>
  
  #include "sysdep.h"
  #include "bench.h"
  
  #define _BSD_SIGNALS
  #define INFINITY 100000000
  #define DEFAULTWWWPORT 80
  #define LOG_FILE "logfile"
  #ifdef WIN32
  #define DEBUG_FILE  "c:/tmp/webstone-debug"
  #else
  #define DEBUG_FILE  "/tmp/webstone-debug"
  #endif /* WIN32 */
  #define NCCARGS 4096
  
  /* global variables */
  
    THREAD FILE *debugfile = stderr;
    page_list_t *load_file_list; /* actually a dynamic array */
  
  int amclient = 0;
  int havewebserver = 0;
  int haveproxyserver = 0;
  int   savefile = 0;
  NETPORT portnum = DEFAULTWWWPORT;
  int   timeexpired = 0;
  int     debug = 0;
  long int  number_of_pages = 0;
  char  webmaster[MAXHOSTNAMELEN];
  char  webserver[MAXHOSTNAMELEN];
  char  proxyserver[MAXHOSTNAMELEN];
  
  
  #ifdef WIN32
  HANDLE  hSemaphore;
  int CounterSemaphore = 0;   /* counter semaphore for children */
  #endif /* WIN32 */
  
  static void ClientThread(void *);
  
  /* used to bypass DNS/YP name resolution for every page */
  struct hostent  webserv_phe, webmast_phe;
  struct protoent webserv_ppe, webmast_ppe;
  unsigned long webserv_addr, webmast_addr;
  short     webserv_type, webmast_type;               /* socket type */
  
  /* End of globals */
  
  
  static void
  usage(const char *progname)
  {
      returnerr("Usage: %s [-d]  [-w webserver]  [-p port_num]\n",
    progname);
      returnerr("\t[-c masterhost:port]  [-t run_time | -l loops]\n");
      returnerr("\t[-n numclients]  [-R]\n");
      returnerr("\t[-f config_file]  [-u uilfile | url ...]\n");
      errexit("\n");
  } /* END usage() */
  
  static void
  alarmhandler(void)
  {
    /* RECEIVED AN ALARM SIGNAL */
    timeexpired = 1;
  } /* END alarmhandler() */
  
  #ifndef WIN32
  static void
  childhandler(void)
  {
    int status;
  
    /* RECEIVED A SIGNAL THAT A CHILD PROCESS HAS DIED */
    D_PRINTF( "A child process has died\n" );
    while (wait3(&status, WNOHANG, (struct rusage *)0) >= 0)
      {
        /* do nothing */
        ;
      }
  } /* END childhandler() */
  #endif /* WIN32 */
  
  
  /* look up the host name and protocol
   * called once by main() since all threads
   * use the same protocol and address
   */
  
  int resolve_addrs(char *host, char *protocol, struct hostent *host_phe, struct protoent *proto_ppe, unsigned long *addr,
    short *type) {
  struct hostent *phe;
  struct protoent *ppe;
  
      /* if IP address given, convert to internal form */
      if (host[0] >= '0' && host[0] <= '9') {
    *addr = inet_addr(host);
    if (*addr == INADDR_NONE)
        return(returnerr("Invalid IP address %s\n", host));
  
      } else {
    /* look up by name */
    phe = gethostbyname(host);
    if (phe == 0)
    {
        D_PRINTF( "Gethostbyname failed: %s", neterrstr() );
        return(returnerr("Can't get %s host entry\n", host));
    }
    memcpy(host_phe, phe, sizeof(struct hostent));
    memcpy((char *)addr, phe->h_addr, sizeof(*addr));
      }
  
      /* Map protocol name to protocol number */
      ppe = getprotobyname(protocol);
  
      if (ppe == 0)
      {
    D_PRINTF( "protobyname returned %d\n",  ppe );
    return(returnerr("Can't get %s protocol entry\n",protocol));
      }
      memcpy(proto_ppe, ppe, sizeof(struct protoent));
  
      D_PRINTF( "Protocol number %d\n", ppe->p_proto );
  
      /* Use protocol to choose a socket type */
      if (strcmp(protocol,"udp") == 0)
      {
    *type = SOCK_DGRAM;
      }
      else
      {
    *type = SOCK_STREAM;
    D_PRINTF( "Choosing SOCK_STREAM %d type %d %s\n",
        SOCK_STREAM, *type, neterrstr() );
      }
  
      return 0;
  }
  
  /* connect to a socket given the hostname and protocol */
  SOCKET
  connectsock(char *host, NETPORT portnum, char *protocol)
   {
    struct sockaddr_in sin;   /* an Internet endpoint address */
    SOCKET  s;              /* socket descriptor */
    int     type;           /* socket type */
    short   proto;
    int     returnval;  /* temporary return value */
  
    D_PRINTF( "Beginning connectsock; host=%s port=%d proto=%s\n", host,
    portnum, protocol );
  
    sin.sin_family = AF_INET;
    memset((char *)&sin, 0, sizeof(sin));
    D_PRINTF( "Zeroed address structure\n" );
  
    sin.sin_port = htons(portnum);
    D_PRINTF( "Set port number %d\n", portnum );
  
    /* get the contact information */
    if (strcmp(host, webserver) == 0) {
        sin.sin_addr.S_ADDR = webserv_addr;
        sin.sin_family = PF_INET;
        proto = webserv_ppe.p_proto;
        type = webserv_type;
    } else if (strcmp(host, webmaster) == 0) {
        sin.sin_addr.S_ADDR = webmast_addr;
        sin.sin_family = PF_INET;
        proto = webmast_ppe.p_proto;
        type = webmast_type;
    } else {
        struct hostent  host_phe;
        struct protoent host_ppe;
        unsigned long host_addr;
        short     host_type;       /* socket type */
  
        if (resolve_addrs(host, "tcp", &host_phe, &host_ppe, &host_addr, &host_type))
      return returnerr("Can't resolve hostname %s in get()\n", host);
        sin.sin_addr.S_ADDR = host_addr;
        sin.sin_family = PF_INET;
        proto = host_ppe.p_proto;
        type = host_type;
    }
  
    /* Allocate a socket */
    s = socket(PF_INET, type, proto);
    D_PRINTF( "Socket %d returned %d, %s\n",
    type, s, neterrstr() );
  
    if (BADSOCKET(s))
    {
      D_PRINTF( "Can't create socket: %s\n",neterrstr() );
      return BADSOCKET_VALUE;
    }
  
    /* Connect the socket */
    D_PRINTF( "Trying to connect %d with size %d, %s\n",
    s, sizeof(sin), neterrstr() );
    D_PRINTF( "Address is family %d, port %d, addr %s\n",
    sin.sin_family, ntohs(sin.sin_port),
    inet_ntoa(sin.sin_addr) );
  
    returnval = connect(s, (struct sockaddr *)&sin, sizeof(sin));
    D_PRINTF( "Connect returned %d, %s\n",
    returnval, neterrstr() );
    if (returnval < 0)
      {
        D_PRINTF( "Can't connect: %s\n", neterrstr() );
        NETCLOSE(s);
        return BADSOCKET_VALUE;
      }
  
    /* all done, returning socket descriptor */
    D_PRINTF( "Returning %d from connectsock call\n", s );
    return(s);
  
  } /* END connectsock() */
  
  SOCKET
  connecttomaster(char *str)
  {
      char    *tempch;
      SOCKET  sock;
      char  msg[100];
      char  ConnectStr[100];  /* Fix to handle multiple threads */
      int   tries;
  
      strcpy(ConnectStr, str);
  
      /*
       * BREAK UP THE connectstr INTO A HOSTNAME/HOST-IP AND A PORT NUMBER.
       */
      if((tempch = strpbrk(ConnectStr,":")) == 0)
      {
          /*
           * INCORRECT FORMAT OF ConnectStr. CORRECT FORMAT IS
           * HOSTNAME:PORT OR HOST-IP:PORT
           */
          D_PRINTF( "Incorrect format %s: use hostname:port or ip_addr:port\n",
      ConnectStr );
    return(returnerr("Incorrect format %s: use host:port or ip_addr:port\n",
      ConnectStr));
      }
  
      /*
       * ZERO OUT THE COLON SO WE HAVE TWO STRINGS, THE HOSTNAME AND THE PORT
       */
      *tempch = '\0';
      tempch++;
  
      /* loop here to connect to webmaster - TCP/IP allows no more than 5
       * connection requests outstanding at once and thus the webmaster may
       * reject a connection if there are a lot of client processes
       */
  #define MAXTRIES 30
  #define TRYDELAY_SEC  1
      for (tries = 0; tries < MAXTRIES; tries++) {
  
          sock = connectsock(ConnectStr,(NETPORT)atoi(tempch),"tcp");
  
    if (!BADSOCKET(sock))
        break;
  
    sleep(TRYDELAY_SEC);
      }
  
      if (BADSOCKET(sock))
      {
    /*  ERROR CONNECTING TO MASTER PROCESS */
    return(returnerr("Could not connect to master process\n"));
      }
  
      /*
       * SIGNAL THE MASTER THAT WE ARE READY TO PROCEED.  WHEN ALL
       * CHILD PROCESSES HAVE CONNECTED AND SENT THIS SIGNAL,
       * THE MASTER WILL ISSUE US A GO SIGNAL.
       */
      if(NETWRITE(sock,READYSTR,READYSTRLEN) != READYSTRLEN)
      {
    return(returnerr("Error sending READY message to master"));
      }
  
      memset(msg,0,GOSTRLEN+1);
      if(NETREAD(sock,msg,GOSTRLEN) != GOSTRLEN)
      {
    D_PRINTF( "Error receiving GO message from master: %s\n", neterrstr()
      );
    return(returnerr("Error receiving GO message from master\n"));
      }
  
      if(strncmp(GOSTR,msg,GOSTRLEN))
      {
         /*
          * WE RECEIVED A MESSAGE OTHER THAN GO. PRINT IT OUT AND RETURN ERROR
          */
    return(returnerr("Received non-GO message %s\n",msg));
      }
  
      return(sock);
  
  } /* END connecttomaster() */
  
  
  static void
  accumstats(rqst_timer_t *rqsttimer, page_stats_t *pagestats, stats_t *timestat)
  {
      rqst_stats_t  rqststats;
  
  #define TFMT  "%10u:%10u"
      /*
       * DUMP THE TIMING INFORMATION HERE, OR COMPUTE WHAT YOU WANT TO
       * PRINT OUT LATER.
       */
  
      D_PRINTF( "Total bytes read: %d \t Body size read: %d\n",
    rqsttimer->totalbytes,
    rqsttimer->bodybytes );
  
      D_PRINTF( "Enter time:     " TFMT " \t Exit Time:     " TFMT "\n",
    rqsttimer->entertime.tv_sec,
    rqsttimer->entertime.tv_usec,
    rqsttimer->exittime.tv_sec,
    rqsttimer->exittime.tv_usec );
      D_PRINTF( "Before connect: " TFMT " \t After connect: " TFMT "\n",
    rqsttimer->beforeconnect.tv_sec,
    rqsttimer->beforeconnect.tv_usec,
    rqsttimer->afterconnect.tv_sec,
    rqsttimer->afterconnect.tv_usec );
      D_PRINTF( "Before header:  " TFMT " \t After header:  " TFMT "\n",
    rqsttimer->beforeheader.tv_sec,
    rqsttimer->beforeheader.tv_usec,
    rqsttimer->afterheader.tv_sec,
    rqsttimer->afterheader.tv_usec );
      D_PRINTF( "After body:     " TFMT "\n",
    rqsttimer->afterbody.tv_sec,
    rqsttimer->afterbody.tv_usec );
  
      rqstat_times(&(rqststats), rqsttimer);
      rqstat_sum(&(timestat->rs), &(rqststats));
      rqstat_sum(&(pagestats->rs), &(rqststats));
  
      if (rqsttimer->page_number != 999)
      {
    timestat->page_numbers[rqsttimer->page_number] += 1;
      }
  
  #undef TFMT
  } /* END accumstats */
  
  
  /*
   * fetch the set of files that constitute a page
   *
   * maxcount = the number of files in the WWW page
   * pageval = the number of the WWW page (offset in load_file_list[])
   *  (if -1, use page # 0 - does this still work?)
   *
   * returns the number of files retrieved
   */
  static int
  makeload(int maxcount, int pageval, THREAD rqst_timer_t *timerarray, THREAD stats_t *timestat, THREAD SOCKET mastersock, THREAD page_stats_t *page_stats)
  {
      int cnt;
      int returnval;
      page_stats_t page_stats_tmp;
      char server[MAXHOSTNAMELEN];
  
      NETPORT loc_portnum;
  
      D_PRINTF( "Starting makeload(maxcount %d, pageval %d)\n",
    maxcount, pageval );
  
      strcpy( server, webserver); /* Put in default value */
  
      page_stats_init(&page_stats_tmp);
      D_PRINTF( "Page stats initialized\n" );
  
      for (cnt = 0; cnt < maxcount; cnt++)
      {
    D_PRINTF( "Loop count %d in makeload()\n", cnt );
    if (pageval == -1)
    {
        pageval = cnt;
    }
    if (timeexpired)
    {
        break;
    }
  
    /* check for a filename */
    if (strlen(load_file_list[pageval].filename[cnt]) < 1)
    {
        D_PRINTF( "Bad filename at pageval %d, count %d\n",
      pageval, cnt );
        return(returnerr("Bad filename at pageval %d, count %d\n",
      pageval, cnt));
    }
  
          /*        if (load_file_list[pageval].port_number[cnt] != 0)
          {
              loc_portnum = load_file_list[pageval].port_number[cnt];
          }
          else
          {
              loc_portnum = portnum;
          } */
          loc_portnum = portnum;
          if ((load_file_list[pageval].servername[cnt] != 0)
          &&
        *load_file_list[pageval].servername[cnt])
    {
        D_PRINTF( "Copying URL server %s to server\n",
      load_file_list[pageval].servername[cnt] );
        strcpy(server, load_file_list[pageval].servername[cnt]);
    }
  
    if (haveproxyserver)
      {
        D_PRINTF( "Copying proxy %s to webserver\n", proxyserver );
        strcpy(server, proxyserver);
      }
  
  
    D_PRINTF( "Calling get(%s, %d, %s, &(timearray[%d]))\n",
        server, loc_portnum, load_file_list[pageval].filename[cnt],
        cnt );
  
    returnval = get(server, loc_portnum,
          load_file_list[pageval].filename[cnt],
          &(timerarray[cnt]));
    if (returnval < 0)
    {
        D_PRINTF( "***GET() RETURNED AN ERROR\n" );
    }
  
    /*
    * DID GET() RETURN A VALID TIME?
    */
    if ((returnval == 0) && (timerarray[cnt].valid == 2))
    {
        timerarray[cnt].page_number = pageval;
  
        accumstats(&timerarray[cnt], &page_stats_tmp, timestat);
    }
    else if (!timeexpired) /* INVALID, INCREMENT THE ERROR COUNTER */
    {
        D_PRINTF( "GET error counter incremented\n" );
        timestat->rs.totalerrs++;
    }
  
    if (amclient) {
        fd_set readfds;
        struct timeval timeout;
        int rv;
  
        timeout.tv_sec = 0;
        timeout.tv_usec = 0;
        FD_ZERO(&readfds);
        FD_SET(mastersock, &readfds);
  
        /* if the webmaster has aborted, quit */
        D_PRINTF("Before select() on webmaster socket\n");
        if (rv = select(FD_SETSIZE, &readfds, 0, 0, &timeout))  {
      D_PRINTF("select() returned %d\n", rv);
      D_PRINTF("Client terminating at request of webmaster\n");
      exit(2);
        }
    }
  
      } /* END for cnt */
  
      /*
       * DO WE HAVE A VALID RETURN FROM GET()?
       * WHY NOT USE returnval HERE?
       */
      if ((returnval == 0) &&
      (cnt == load_file_list[pageval].num_of_files) &&
      (timerarray[cnt-1].valid == 2))
      {
    rqst_stats_t *ps_rs;
    rqst_stats_t *pst_rs;
  
    ps_rs = &(page_stats[pageval].rs);
    pst_rs = &(page_stats_tmp.rs);
  
    rqstat_sum(ps_rs, pst_rs);
  
    page_stats[pageval].totalpages++;
  
    if (page_stats[pageval].page_size == 0)
    {
        page_stats[pageval].page_size = (unsigned)
      page_stats_tmp.rs.totalbody;
    }
      }
  
      D_PRINTF( "\nMakeload output page %d: %d errors, %d pages\n",
        pageval, timestat->rs.totalerrs, page_stats[pageval].totalpages );
      D_PRINTF( "Makeload returning %d\n", cnt );
  
      return(cnt);
  
  } /* END makeload() */
  
  #ifdef WIN32
  /* close socket library at exit() time */
  void sock_cleanup(void) {
  
      WSACleanup();
  }
  #endif /* WIN32 */
  
  /* globalize variables that were in main() */
  long int  numfiles = 0;
  int testtime = 0;
  int numloops = 0;
  int numclients = 0;
  int record_all_transactions = 0;
  int uil_filelist_f = 0; /* filedescriptor of URLs to fetch? */
  int verbose = 0;
  int     total_weight;
  char  uil_filelist[NCCARGS];
  char  filelist[MAXNUMOFFILES][MAXPATHLEN];
  char  configfile[MAXPATHLEN];
  char  connectstr[MAXHOSTNAMELEN+10];
  
  void
  ACE_TMAIN(int argc, ACE_TCHAR *argv[])
  {
      int   file_count=0;
      int   getoptch;
      int   currarg;
      extern char *optarg;
      extern int  optind;
      int   i, j;
      char  *tempch;
      int   err;
  
  #define SLEEP_USEC  100
  #ifdef WIN32
      WSADATA   WSAData;
  #else
  
      struct timeval sleeptime;
  
      /* set the amount of time that we'll pause before sending a "." to the
         webmaster */
  
      sleeptime.tv_sec = SLEEP_USEC/1000000;
      sleeptime.tv_usec = SLEEP_USEC % 1000000;
  #endif /* WIN32 */
  
      debugfile = stderr;
  
  #ifdef WIN32
      MessageBeep(~0U); /* announce our existence */
      MessageBeep(~0U);
      MessageBeep(~0U);
  
      err = WSAStartup(MAKEWORD(1,1), &WSAData);
      if (err != 0) {
    errexit("Error in WSAStartup()\n");
      }
  
      atexit(sock_cleanup);
  
      SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
  
      /* create semaphore in locked state */
      hSemaphore = CreateSemaphore(0, 0, 1, 0);
      if(hSemaphore == 0)
      {
    errexit("Create semaphore failed: %d", GetLastError());
      }
  #endif /* WIN32 */
  
      memset(webserver, 0, sizeof(webserver));
      memset(webmaster, 0, sizeof(webmaster));
      memset(proxyserver, 0, sizeof(proxyserver));
      memset(connectstr, 0, sizeof(connectstr));
  
      /*
       * PARSE THE COMMAND LINE OPTIONS
       */
  
      while((getoptch = getopt(argc,argv,"P:f:t:l:p:u:R:w:c:n:sdv")) != EOF)
      {
          switch(getoptch)
          {
    case 'c':
              sprintf(connectstr, "%s", optarg);
        amclient = 1;
              printf("%s", OKSTR);     /* sent back to webmaster */
              fflush(stdout);
        break;
          case 'd':
              debug = 0; /* sumedh */
              break;
    case 'f':
        sprintf(configfile, "%s", optarg);
        break;
          case 'l':
              numloops = atoi(optarg);
              break;
          case 'n':
              numclients = atoi(optarg);
              break;
    case 'u':
        sprintf(uil_filelist, "%s", optarg);
        uil_filelist_f = 1;
        break;
    case 'p':
        portnum = atoi(optarg);
        break;
    case 's':
        savefile = 1;
        break;
          case 't':
              testtime = 60 * atoi(optarg);
              break;
    case 'v':
        verbose = 1;
        break;
          case 'w':
              havewebserver = 1;
              sprintf(webserver,"%s",optarg);
              break;
    case 'P':
        haveproxyserver = 1;
        sprintf(proxyserver, "%s", optarg);
        break;
    case 'R':
        record_all_transactions = 1;
        break;
          default:
              usage(argv[0]);
          }
      }
  
      returnerr("Client begins...\n");
      D_PRINTF( "Running in debug mode\n\n" );
  
      /* print the command line */
      for (i = 0; i < argc; i++)
    D_PRINTF( "%s ", argv[i] );
      D_PRINTF( "\n\n" );
  
      if(testtime && numloops)
      {
    /*
           * EITHER numloops OR testtime, BUT NOT BOTH.
           */
          usage(argv[0]);
      }
  
      if(havewebserver != 1)
      {
  #ifdef WIN32
          /*
           * THE SERVER'S NAME MUST BE SPECIFIED
           */
          returnerr("No WWW Server specified\n");
          usage(argv[0]);
  #else
    /* IF IT ISN'T, WE ASSUME LOCALHOST */
    sprintf(webserver, "%s", "localhost");
    havewebserver = 1;
  #endif /* WIN32 */
      }
  
      currarg = optind;
      numfiles = 0;
      while(currarg != argc)
      {
         /*
          * GET THE URLS TO RETRIEVE.
          */
         if (numfiles == MAXNUMOFFILES) {
       returnerr("Maximum of %d files on the command line.\n");
       usage(argv[0]);
         }
         sscanf(argv[currarg],"%s",filelist[numfiles]);
         numfiles++;
         currarg++;
      }
  
      if ((numfiles != 0) && uil_filelist_f)
      {
    returnerr("Both a filelist and UIL specified.\n");
    usage(argv[0]);
      }
  
      if((numfiles == 0) && !(uil_filelist_f))
      {
          /*
           * AT LEAST ONE FILE MUST BE SPECIFIED
           */
    returnerr("No UIL resources or filelist specified \n");
          usage(argv[0]);
      }
  
      if((numloops == 0) && (testtime == 0))
      {
          /*
           * NO SPECIFIED NUMBER OF LOOPS, AND NO TEST TIME
           */
         usage(argv[0]);
      }
      if(numclients > MAXPROCSPERNODE || numclients < 1)
      {
    returnerr("Number of Clients must be between 1 and %d\n", MAXPROCSPERNODE);
    exit(1);
      }
  
      /* allow use of IP address */
      if(amclient) {
    if((tempch = strpbrk(connectstr,":")) == 0)
    {
        /*
         * INCORRECT FORMAT OF ConnectStr. CORRECT FORMAT IS
         * HOSTNAME:PORT OR HOST-IP:PORT
         */
        D_PRINTF( "Incorrect format %s: use hostname:port or ip_addr:port\n",
           connectstr );
        returnerr("Incorrect format %s: use host:port or ip_addr:port\n", connectstr);
        exit(1);
    } else {
        strncpy(webmaster, connectstr, tempch-connectstr);
    }
    if(resolve_addrs(webmaster, "tcp", &webmast_phe, &webmast_ppe, &webmast_addr, &webmast_type))
        exit(1);
      }
  
      if (haveproxyserver)
      {
    D_PRINTF( "Copying proxy %s to webserver\n", proxyserver );
    strcpy(webserver, proxyserver);
      }
  
      if (resolve_addrs(webserver, "tcp", &webserv_phe, &webserv_ppe, &webserv_addr, &webserv_type))
    exit(1);
  
      /*
       * INITIALIZE DATA
       */
      /* allocate space for dynamic arrays */
      load_file_list =
        (page_list_t *)mymalloc((MAXNUMOFPAGES)*sizeof(page_list_t));
  
      if (uil_filelist_f)
      {
        /* take a guess at the number of URLs in the file */
        D_PRINTF( "About to parse filelist %s\n", uil_filelist );
        number_of_pages = count_file_list(uil_filelist);
        numfiles = 1;
  
        /* IF WE HAVE A FILELIST, PARSE IT */
        /* allocate memory */
        D_PRINTF( "Allocating page list: %ld by %d\n",
    number_of_pages, numfiles );
        for (i=0; i<number_of_pages; i++)
    {
      for (j=0; j<MAXNUMOFFILES; j++)
        {
          load_file_list[i].servername[j] =
      (char *)mymalloc(URL_SIZE);
          load_file_list[i].filename[j] =
      (char *)mymalloc(URL_SIZE);
        }
    }
  
        D_PRINTF( "Parsing file list: %s\n", uil_filelist );
        parse_file_list(uil_filelist, load_file_list,
            &number_of_pages, &numfiles);
        /* free memory for pages that won't be used? */
        D_PRINTF( "Actual page list: %ld by %d\n",
    number_of_pages, MAXNUMOFFILES );
  
        D_PRINTF( "Setting up weighting for %ld pages\n",
    number_of_pages );
        total_weight = load_percent(load_file_list, number_of_pages);
        /*      total_weight = load_percent(load_file_list, number_of_pages, pages); */
      }
      else
      {
    /* no uil file */
    number_of_pages = numfiles;
      }
  
      if (number_of_pages < 1)
        {
    /* no pages - exit */
    D_PRINTF( "No valid URLs found\n" );
    errexit("No valid URLs found\n");
        }
  
  #ifndef WIN32
      /*
       * IF WE ARE TO FORK ADDITIONAL CLIENTS ON THIS MACHINE,
       * WE MUST DO IT BEFORE WE CONNECT TO THE MASTER.
       *
       * FIRST, SET UP SIGNAL HANDLING
       */
      signal(SIGCHLD, childhandler);
      for(i = 0; i < numclients; i++)
      {
        thr_create (0, 0, ClientThread, 0, THR_BOUND, 0);
  
        /* switch(fork())
           {
               case 0:
                   numclients = 1;
                   ClientThread(0);
                   exit(0);
                   break;
               case -1:
                   errexit("Error forking child processes\n");
                   exit(1);
               default:
                  break;
           } */
        select(0,(fd_set *)0,(fd_set *)0, (fd_set *)0, &sleeptime);
      }
  
      /*
       * Wait for all children to exit.
       */
  
      while (thr_join(0, 0, 0) == 0);
  
      /*     for(;;)
         {
        int pid = wait((int*)0);
    if ((pid == -1) && errno == ECHILD) break;
         } */
  #else
      /* start threads on NT */
      for (i = 0; i < numclients; i++)
      {
    if (_beginthread(ClientThread, 0, 0) == -1)
    {
        errexit("_beginthread failed: %d", GetLastError());
    }
      }
  #endif /* WIN32 */
  
  #ifdef WIN32
      /* wait for children to get to sync point */
      while (CounterSemaphore < numclients)
          sleep(1);
      CounterSemaphore = 0;
  
      /* start all children simultaneously */
      ReleaseSemaphore(hSemaphore, 1, 0);
  
      if (testtime) {
    sleep(testtime);
    alarmhandler(); /* signal end of test to threads */
      }
  
      /*
       * Wait for all threads to exit.
       */
      while (CounterSemaphore < numclients)
          sleep(1);
  
      CloseHandle(hSemaphore);
  #endif /* WIN32 */
  
      return;
  } /* end main() */
  
  void ClientThread(void *dummy)
  {
  
    THREAD FILE *logfile;
  
    THREAD stats_t  timestat;
  
    THREAD rqst_timer_t timerarray[MAXNUMOFFILES];
    THREAD SOCKET mastersock = BADSOCKET_VALUE; /* connection to webmaster */
  
  
    THREAD page_stats_t *page_stats;    /* actually a dynamic array */
  
      int   loopcnt = 0;
      int   filecnt;
      int   loop;
      int   ran_number;
      int   page_index;
      int   page_number;
      int   file_count = 0;
      char  file_name[50];
      struct timeval  runningtime;
      time_t    junk;
      int   i;
      int   returnval;
  
      /*
       * INITIALIZE DATA
       */
  
      page_stats =
        (page_stats_t *)mymalloc((number_of_pages)*sizeof(page_stats_t));
  
      for (i=0; i < number_of_pages; i++) {
          page_stats_init(&(page_stats[i]));
      }
  
      if (debug)
        {
    /*
     *  OPEN A DEBUG FILE
     */
    fflush(stderr);
    sprintf(file_name, "%s.%d", DEBUG_FILE, (int)getpid());
    debugfile = fopen(file_name, "w+");
    if (debugfile == 0)
        errexit("Can't open debug file\n");
    D_PRINTF( "Running in debug mode, %d\n",amclient );
      }
  
      if (record_all_transactions)
      {
    /*
     *  OPEN A LOG FILE.
     */
    sprintf(file_name, "%s%d", LOG_FILE, (int)getpid());
    returnerr("Log file is %s\n", file_name);
    logfile = fopen(file_name, "w+");
      }
  
      /* Initialize random number generator */
      junk = getpid ();
      rand_r(&junk);
      D_PRINTF( "Random seed: %d\n", junk );
  
      for (i=0; i < MAXNUMOFFILES; i++)
      {
    rqtimer_init(&(timerarray[i]));
      }
      stats_init(&timestat);
  
      D_PRINTF( "Number of files %d\n", numfiles );
  
      timestat.total_num_of_files = numfiles;
  
      if (amclient)
      {
          /*
           * WE ARE A CLIENT PROCESS. (i.e. WE ARE NOT RUN BY A USER, BUT BY
           * THE MASTER WWWSTONE PROCESS. WE NEED TO CONNECT TO THE
           * MASTER WHO WILL SYNCHRONIZE ALL THE CLIENTS.
           */
    D_PRINTF( "Trying to connect with %s\n",connectstr );
  
    mastersock = connecttomaster(connectstr);
  
    D_PRINTF( "connecttomaster returns %d, %s\n",
            mastersock, neterrstr() );
  
    if(BADSOCKET(mastersock))
          {
              /*
               * ERROR CONNECTING TO THE MASTER. ABORT.
               */
              errexit("Error connecting to the master: %s\n", neterrstr());
          }
      } /* END IF CLIENT */
  
  #ifdef WIN32
      /* Tell parent we're ready */
      InterlockedIncrement(&CounterSemaphore);
  
      /* Wait for main() thread to release us */
      WaitForSingleObject(hSemaphore, INFINITE);
      ReleaseSemaphore(hSemaphore, 1, 0);
  #endif /* WIN32 */
      if (testtime != 0)
      {
         /*
          * IF RUNNING A TIMED TEST, WE WILL LOOP
    * UNTIL THE ALARM GOES OFF.
          * WE'LL ALSO NEED TO SET THE SIGNAL HANDLER
          */
  #ifndef WIN32
        /*signal(SIGALRM, alarmhandler);*/
         /*
          * SEND SIGALRM IN testtime SECONDS
          */
        /*alarm(testtime);*/
  #endif /* WIN32 */
      }
  
      /*
       * AND THEY'RE OFF...
       */
  
      if (testtime)
    numloops = INFINITY;
      GETTIMEOFDAY(&(timestat.starttime), &(timestat.starttimezone));
  
      /* LOOP UNTIL WE HIT numloops, OR WE RUN OUT OF TIME */
      for(loopcnt = 0;  (loopcnt < numloops) && !timeexpired;  loopcnt++)
      {
    /*
     * THIS IS WHERE LOAD TESTING IS DONE.
     * GET A RANDOM NUMBER, THEN INDEX INTO THE
     * PAGE, AND THEN REQUEST THAT SET OF FILES.
     */
    if (uil_filelist_f) /* HAVE FILELIST */
    {
        D_PRINTF( "Have filelist\n" );
        /* if (testtime != 0) /* RUNNING IN TIMED MODE */
              if (1)
        {
      D_PRINTF( "Running in timed mode\n" );
      /* random number between 0 and totalweight-1 */
                  junk = getpid ();
      ran_number = (rand_r(&junk) % total_weight);
      D_PRINTF( "random %ld\n", ran_number );
  
      /* loop through pages, find correct one
       * while ran_number is positive, decrement it
       * by the load_num of the current page
       * example: ran_number is 5, pages have weights of 10 and 10
       *          first iteration page_index = 0, ran_number = -5
       *          iteration halted, page_index = 0
       */
      page_index = -1;
      while (ran_number >= 0)
        {
          page_index++;
          D_PRINTF( "Current page index %d: %ld - %d\n",
            page_index, ran_number,
            load_file_list[page_index].load_num
            );
          ran_number -= load_file_list[page_index].load_num;
        }
  
      if (page_index >= number_of_pages) { page_index--; }
  
      D_PRINTF( "Final page index %d\n", page_index );
      filecnt = makeload(load_file_list[page_index].num_of_files,
                 page_index, timerarray, &timestat, mastersock, page_stats);
                  testtime = 1;
        }
        else /* NOT RUNNING IN TIMED MODE */
        {
      for (page_number = 0;
           page_number < number_of_pages;
           page_number++)
      {
          filecnt = makeload(load_file_list[page_number].num_of_files,
        page_number, timerarray, &timestat, mastersock, page_stats);
  
      } /* END for page_number */
        } /* END if/else TIMED MODE */
    }
    else /* NO FILELIST */
    {
        D_PRINTF( "No filelist\n" );
       /*
        * LOOP THROUGH UNTIL numfiles TIMES OR UNTIL TIMER EXPIRES
        * AND ALARM SETS filecnt TO INFINITY.
        */
  
        /* does this still work?? */
        /* filecnt = makeload(numfiles, -1, timerarray); */
    } /* END if HAVE FILELIST */
  
    if (filecnt > 0)
        file_count += filecnt;
  
      } /* END while loopcnt */
  
      GETTIMEOFDAY(&(timestat.endtime), &(timestat.endtimezone));
      D_PRINTF( "Test run complete\n" );
      signal(SIGALRM, 0);
  
      if (testtime == 0)
      {
    numfiles = loopcnt;
  
    if (uil_filelist_f)
    {
        numfiles = file_count;
    }
      }
  
      /* This option ( "-R" ) looks broken (e.g. l > 50) -- JEF 2/15/96 */
      if (record_all_transactions)
      {
    /*
     * DUMP THE LOG FILE INFORMATION.
     */
    for (loop=0; loop < (loopcnt * file_count); loop++)
    {
        fprintf(logfile, " entertime \t%d.%d\n"
             " beforeconnect \t%d.%d\n"
             " afterconnect \t%d.%d\n"
             " beforeheader \t%d.%d\n"
             " afterheader \t%d.%d\n"
             " afterbody \t%d.%d\n"
             " exittime \t%d.%d\n"
             " total bytes \t%d\n"
             " body bytes\t%d\n",
      timerarray[loop].entertime.tv_sec,
      timerarray[loop].entertime.tv_usec,
      timerarray[loop].beforeconnect.tv_sec,
      timerarray[loop].beforeconnect.tv_usec,
      timerarray[loop].afterconnect.tv_sec,
      timerarray[loop].afterconnect.tv_usec,
      timerarray[loop].beforeheader.tv_sec,
      timerarray[loop].beforeheader.tv_usec,
      timerarray[loop].afterheader.tv_sec,
      timerarray[loop].afterheader.tv_usec,
      timerarray[loop].afterbody.tv_sec,
      timerarray[loop].afterbody.tv_usec,
      timerarray[loop].exittime.tv_sec,
      timerarray[loop].exittime.tv_usec,
      timerarray[loop].totalbytes,
      timerarray[loop].bodybytes);
    } /* end for loop */
      } /* end if recording all transactions */
  
      D_PRINTF( "total errors: %d\n",timestat.rs.totalerrs );
      /* gethostname(timestat.hostname,MAXHOSTNAMELEN); */
      /* D_PRINTF( "Test for host: %s\n",timestat.hostname ); */
      D_PRINTF( "Server is: %s running at port number: %d\n",
    webserver,portnum );
  
      /* sprintf(timestat.hostname,"%s:%d",timestat.hostname,getpid()); */
      if (amclient) /* CLIENT TO A WEBMASTER */
      {
     char *stats_as_text;
  
          /*
           * SEND THE TIMING DATA TO THE MASTER
           */
    stats_as_text = stats_to_text(&timestat);
    D_PRINTF( "stats_to_text returned %s\n", stats_as_text );
  
    returnval = senddata(mastersock, stats_as_text,
      SIZEOF_STATSTEXTBASE + number_of_pages*SIZEOF_DOUBLETEXT);
    D_PRINTF( "Wrote time stats to master %d\n", returnval );
  
    if (returnval < 1)
      {
        D_PRINTF( "Error while writing time stats: %s\n",
          neterrstr() );
        errexit("Error while writing time stats: %s\n",
          neterrstr());
      }
  
    if (uil_filelist_f)
      /* write pagestats */
      {
        char *page_stats_as_text;
        for (i = 0; i < number_of_pages; i++)
          {
      D_PRINTF( "On page_stats[%d]\n", i );
      page_stats_as_text = page_stats_to_text(&page_stats[i]);
      returnval = strlen(page_stats_as_text);
      D_PRINTF( "page_stats_to_text[%d] returned %d\n",
        i, returnval );
      returnval = senddata(mastersock, page_stats_as_text,
               SIZEOF_PAGESTATSTEXT);
      if (returnval < 1)
        {
          D_PRINTF( "Error while writing page_stats[%d]: %s\n",
            i, neterrstr() );
          errexit("Error while writing page_stats[%d]: %s\n",
            i, neterrstr());
        } /* end if */
      D_PRINTF( "Wrote %d bytes of page_stats[%d] to master\n",
        returnval, i );
          } /* end for */
      } /* end if filelist */
  
    D_PRINTF( "About to close socket\n" );
          if (NETCLOSE(mastersock))
        D_PRINTF( "Close socket error: %s\n", neterrstr() );
      }
      else /* NOT A CLIENT TO A WEBMASTER */
      {
    if (testtime)
      {
        printf("Test ran for: %d minutes\n",(testtime/60));
    }
    else
    {
        printf("Test ran for: %d iterations.\n",numloops);
    }
    compdifftime(&(timestat.endtime), &(timestat.starttime),
      &(runningtime));
    printf("Total time of test (sec) %d.%d\n", runningtime.tv_sec,
      runningtime.tv_usec);
    printf("Files retrieved per iteration: %d\n",numfiles);  /* 'per iteration' */
    printf("----------------------------------\n");
    printf("Totals:\n\n");
    rqstat_print(&(timestat.rs));
  
    if (timestat.rs.totalconnects == 0)
        goto end;
    printf("Thruput = %5.2lf Kbytes/sec\n",
           thruputpersec(timestat.rs.totalbytes, &runningtime) / 1000);
  
    if (uil_filelist_f && numloops && verbose)
    {
        for (loop = 0; loop < number_of_pages; loop++)
        {
      if (timestat.page_numbers[loop] != 0)
      {
          printf ("===============================================================================\n");
          printf ("Page # %d\n\n", loop);
          printf ("Total number of times page was hit %d\n",
            page_stats[loop].totalpages);
          rqstat_print(&(page_stats[loop].rs));
          printf ("Page size %d \n", page_stats[loop].page_size);
          printf ("===============================================================================\n\n");
      } /* END if timestat */
        } /* END for loop */
    } /* END if filelist */
      } /* END if client */
  
  end:
      if(record_all_transactions)
        fclose(logfile);
      if(debug)
      {
    D_PRINTF( "Client exiting.\n" );
    fclose(debugfile);
      }
  
  #ifdef WIN32
      /* tell parent we're done */
      InterlockedIncrement(&CounterSemaphore);
  #endif /* WIN32 */
  
  } /* END ClientThread() */
  