/* $Id: maxro_cps.C,v 1.3 2000/08/25 22:25:29 fubob Exp $ */

/* Use this program to simulate a herd of SFSRO clients.  You
   can then test the maximum number of sustainable connections per
   second to an SFSRO server.  Warning. this is almost as fast
   as a plain TCP connections.  Running longer than 1/2 second
   will likely use up resources.

   cat /sfstest/soco.lcs.mit.edu:5eeagvu4nq92g5rk9zd258738yudgcgk/manual/mod/mod_ssl/ssl_template.head-chapter.gif > /dev/null

*/

/*
 *
 * Copyright (C) 2000 Kevin Fu (fubob@mit.edu)
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 * USA
 *
 */

#include "maxro_cps.h"

u_int conncnt = 0;
char *hostname;
u_int64_t start_tm, end_tm;
sfs_hash fh[10];

struct in_addr *ia;
char *hostid;
int sfsro_port;
sfs_connectarg carg;

u_int32_t bigdeal =0;

char trace[][20] = {
#include "maxro_trace.h"
};
size_t trace_size = sizeof (trace) / sizeof (trace[0]);


void
srvcon::fail (int err)
{
  warn << "connection failed" << strerror (errno) << "\n";
  delete this;
}


void
srvcon::init ()
{
  bigdeal++;
  tcpconnect (*ia, sfsro_port,
	      wrap (this, &srvcon::getsockres));
}

void
srvcon::getsockres (int fd)
{
  if (fd < 0) {
    timecb (timenow + 1, wrap (this, &srvcon::init));
  } else {
    s = axprt_stream::alloc (fd);
    sfsc = aclnt::alloc (s, sfs_program_1);

    sfsc->call (SFSPROC_CONNECT, &carg, &conres,
		wrap (this, &srvcon::sfsconres));
  }
}


void
srvcon::sfsconres (clnt_stat err)
{
  if (err)
    fail (EIO);
  else if (conres.status)
    fail (conres.status);
  else {
    sfsc->call (SFSPROC_GETFSINFO, NULL, &fsires,
		wrap (this, &srvcon::getfsinfores));
  }
}


void
srvcon::getfsinfores (clnt_stat err)
{

  if (err)
    fail (EIO);
  else if (fsires.prog != SFSRO_PROGRAM || fsires.sfsro->vers != SFSRO_VERSION)
    fail (EPROTONOSUPPORT);
  else {
  
    sfsroc = aclnt::alloc (s, sfsro_program_1);
    sfsroc->call (SFSROPROC_GETDATA, &fsires.sfsro->v1->info.rootfh, &sfsrores,
		  wrap (this, &srvcon::getdata, 0));
  }
}

void
srvcon::getdata (int num, clnt_stat err)
{
  num++;

  if (err)
    fail (EIO);
  else if (num == 10) {

    conncnt++;
    vNew srvcon ();
    delete this;
    
  } else {

    sfsroc->call (SFSROPROC_GETDATA, &fh[num], &sfsrores,
		  wrap (this, &srvcon::getdata, num));
  }
}


void
handler ()
{
  end_tm = wall_time ();
  warn << "Time spent:      " << end_tm - start_tm << " usec\n";
  warn << "Number of conns: " << conncnt << "\n";
  warn << "Number of attempted connections: " << bigdeal << "\n";
  warn << "Connections/sec: " << conncnt*1000000/(end_tm - start_tm) << "\n";

  //  XXXX print in one row.  Wall time, req/sec (req #), err/sec (err#), latency?    CPU time
  
  exit (0);
}

int
main (int argc, char **argv)
{
  setprogname (argv[0]);
  //  warn ("pid %d\n", getpid ());

  if (argc < 6 || argc > 7)
    fatal ("usage: %s <num simult conn> <server port> <server hostname> <HostID> <duration millisec> [<UDP broadcast trigger port>]\n", progname.cstr ());

  int numconn = atoi (argv[1]);
  sfsro_port = atoi (argv[2]);
  hostname = argv[3];
  hostid = argv[4];
  u_int64_t duration_tm = 1000 * atoi (argv[5]);
  
  if (duration_tm>= 1000000) {
    warn << "Duration too long.  Go away.\n";
    exit (-1);
  }

  warn << "Making " << duration_tm
       << " microseconds worth of calls in windows of " << numconn 
       << " connections to " << hostname
       << ":" << hostid << "\n";    

  
  struct hostent *h;
  if ((h = gethostbyname (hostname)) == NULL) {
    warn << "gethostbyname failed\n";
    exit(-1);
  }

  /* LOOKUP (rootfh's inode)  
     67, 87, 99, 42, 47, 38, 6d, 86,
     75, 44, ff, 71, 2c, c5, 33, d1,
     81, 76, aa, 08
  */
  memcpy (fh[0].base (),
	  "\x67\x87\x99\x42\x47\x38\x6d\x86\x75\x44\xff\x71\x2c\xc5\x33\xd1\x81\x76\xaa\x08",
	  20);
  
  /* Get directory entries for / *
     7e, 21, 6a, 57, 67, 4e, 97, 1d,
     00, e5, 3a, 3f, 3a, ae, c0, a3,
     c4, 00, 2f, 60
  */
  memcpy (fh[1].base (),
	  "\x7e\x21\x6a\x57\x67\x4e\x97\x1d\x00\xe5\x3a\x3f\x3a\xae\xc0\xa3\xc4\x00\x2f\x60",
	  20);
  
  /* Get inode for /manual 
     67, 4b, 44, 2d, 6c, ef, 71, 08,
     6a, 21, fb, 79, 0b, c1, 57, 63,
     70, d3, 3e, 3a
  */
  memcpy (fh[2].base (),
	  "\x67\x4b\x44\x2d\x6c\xef\x71\x08\x6a\x21\xfb\x79\x0b\xc1\x57\x63\x70\xd3\x3e\x3a",
	  20);
  
  /* Get directory entries in /manual
     0e, fe, 04, 54, 9c, 30, 7e, 7a,
     e6, f6, 01, 94, 2b, 75, 7c, d4,
     1a, 3c, a0, 52
  */
  memcpy (fh[3].base (),
	  "\x0e\xfe\x04\x54\x9c\x30\x7e\x7a\xe6\xf6\x01\x94\x2b\x75\x7c\xd4\x1a\x3c\xa0\x52",
	  20);
  
  /* Get inode for /manual/mod/
     e3, 30, 80, 79, 0f, c8, 04, 61,
     0b, 73, 15, ee, 1e, bd, 79, e7,
     df, 6b, b3, 96
  */
  memcpy (fh[4].base (),
	  "\xe3\x30\x80\x79\x0f\xc8\x04\x61\x0b\x73\x15\xee\x1e\xbd\x79\xe7\xdf\x6b\xb3\x96",
	  20);
  
  /* Get directory entries for /manual/mod 
     48, 26, 1e, 8a, da, 84, 5b, 75,
     fe, fb, d4, f2, 25, ad, 8e, 37,
     aa, de, 84, e1
  */
  memcpy (fh[5].base (),
	  "\x48\x26\x1e\x8a\xda\x84\x5b\x75\xfe\xfb\xd4\xf2\x25\xad\x8e\x37\xaa\xde\x84\xe1",
	  20);
  
  /* Get inode for /manual/mod/mod_ssl/
     43, 5f, 7c, 36, 05, bc, 9c, a7,
     c0, c0, ff, f4, 99, 42, 1c, 69,
     92, 93, a2, 9e
  */
  memcpy (fh[6].base (),
	  "\x43\x5f\x7c\x36\x05\xbc\x9c\xa7\xc0\xc0\xff\xf4\x99\x42\x1c\x69\x92\x93\xa2\x9e",
	  20);
  
  /* Get directory entries for /manual/mod/mod_ssl/
     cd, 96, d9, 41, fe, d4, 46, 00,
     ea, 37, ef, 18, 93, 5c, e6, 94,
     71, f1, d4, f1
  */
  memcpy (fh[7].base (),
	  "\xcd\x96\xd9\x41\xfe\xd4\x46\x00\xea\x37\xef\x18\x93\x5c\xe6\x94\x71\xf1\xd4\xf1",
	  20);
  
  /* Get inode for /manual/mod/mod_ssl/ssl_template.head-chapter.gif
     32, 33, b6, 26, 36, 55, cb, 50,
     6d, 06, 8e, dd, 48, 3d, ce, 1a,
     b0, 5b, b8, 99
  */
  memcpy (fh[8].base (),
	  "\x32\x33\xb6\x26\x36\x55\xcb\x50\x6d\x06\x8e\xdd\x48\x3d\xce\x1a\xb0\x5b\xb8\x99",
	  20);
  
  /* Get contents of /manual/mod/mod_ssl/ssl_template.head-chapter.gif 
     3c, 8e, cd, bb, 6b, 4c, 2c, f2,
     02, 63, d7, 98, 4f, 89, 18, d2,
     6f, e2, 4c, 2b
  */
  memcpy (fh[9].base (),
	  "\x3c\x8e\xcd\xbb\x6b\x4c\x2c\xf2\x02\x63\xd7\x98\x4f\x89\x18\xd2\x6f\xe2\x4c\x2b",
	  20);

  ia = (struct in_addr *) h->h_addr;

  carg.release = SFS_RELEASE;
  carg.service = SFS_SFS;
  carg.name = hostname;	
  if (!sfs_ascii2hostid (&carg.hostid, hostid)) {
    warn << "Can't decode hostid\n";
    exit (-1);
  }


  sigcb (SIGALRM, wrap (handler));
  sigcb (SIGINT, wrap (handler));
  sigcb (SIGTERM, wrap (handler));

  struct itimerval itv;
  itv.it_value.tv_sec  = 0;
  itv.it_value.tv_usec = duration_tm;

  // Trigger for remote synchronization

  if (argc == 7) {
    int udpsock = inetsocket (SOCK_DGRAM, atoi (argv[6]));
    if (udpsock < 0) {
      fatal ("socket");
    }
    
    int broadcast = 1;
    if (setsockopt (udpsock, SOL_SOCKET, SO_BROADCAST, &broadcast,
		    sizeof (broadcast)) < 0)
      fatal ("setsockopt: %m\n");
    
    char buf[1];
    // Block until triggered
    if (recvfrom (udpsock, buf, 1, 0, NULL, NULL) < 0) {
      fatal ("sendto: %m\n");
    }
  }

  
  int err;
  if ((err = setitimer (ITIMER_REAL, &itv, NULL)) != 0) {
    warn << "setitimer failed " << strerror (errno) << "\n";
    exit (-1);
  }

  start_tm = wall_time();
  for (int i=0; i< numconn; i++) 
    vNew srvcon ();
  amain ();
}


