/* $Id: maxrw_cps.C,v 1.3 2000/08/30 07:53:53 fubob Exp $ */

/* Use this program to simulate a herd of SFSRW clients.  You
   can then test the maximum number of sustainable connections per
   second to an SFSRW server
   
*/

/*
 *
 * 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 "maxrw_cps.h"
#include "parseopt.h"

unsigned long count = 0;
unsigned long attempted = 0;
u_int64_t bytecount = 0;
u_int64_t starttime;
char *opt_name = NULL;
bool opt_verbose = false;
int opt_test = 0;
in_addr addr;

ptr<rabin_priv> client_key;

char *hostid;
int sfsrw_port;

sfs_connectarg carg;
diropargs3 lookuparg;
nfs_fh3 readlinkarg; 

inline u_int64_t
gettime ()
{
  timeval tv;
  gettimeofday (&tv, NULL);
  return tv.tv_sec * INT64(1000000) + tv.tv_usec;
}


void
init ()
{
  kc = client_key;
  
  srvcon::connect ();
}

void
connect () {
  tcpconnect (*ia, sfsrw_port,
	      wrap (getsockres));
}

void
getsockres (int fd)
{
  if (fd < 0)
    timecb (timenow + 1, wrap (connect));
  else {
    s = axprt_crypt::alloc (fd);
    sfsc = aclnt::alloc (s, sfs_program_1);

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


void
srvcon::sfsconres (clnt_stat err)
{
  if (err)
    fail (EIO);
  else if (conres.status)
    fail (conres.status);
  else {
    sfs_client_crypt (sfsc, kc, carg, *conres.reply,
		      wrap (cryptres));
  }
}

void
srvcon::cryptres (const sfs_hash *sessid)
{
  if (sessid) {
    //    s->authinfo.sessid = *sessid;

    sfsc->call (SFSPROC_GETFSINFO, NULL, &fsires,
		wrap (getfsinfores));

  }
  else
    fail (EIO);
}


void
srvcon::getfsinfores (clnt_stat err)
{
  if (err)
    fail (EIO);
  else if (fsires.prog != ex_NFS_PROGRAM || fsires.nfs->vers != ex_NFS_V3)
    fail (EPROTONOSUPPORT);
  else {

    sfsrwc = aclnt::alloc (s, ex_nfs_program_3);
    
    sfsrwc->call (ex_NFSPROC3_LOOKUP, &lookuparg, &lres,
		  wrap (lookupres));
    
  }
}


void
srvcon::lookupres (clnt_stat err) 
{
  if (err)
    fail (EIO);
  else {

    /*
      
      sfsrwcd: ACLNT_TRACE: call ex_nfs_program_3:ex_NFSPROC3_GETATTR x=3
      nfs_fh3 ARGS = {
      opaque data<64> = [32] {
      e1, 86, 8e, 80, dc, 20, fa, 38,
      93, c5, 09, bc, 44, a6, 01, e0,
      6b, 36, c8, 26, 55, 0b, 22, 97,
      66, 3e, 23, cd, 18, 6e, 85, 69
      };
      };
    */

    
    sfsrwc->call (ex_NFSPROC3_READLINK, &readlinkarg, &rres,
		wrap (this, &srvcon::readlinkres));

  }
}

void
readlinkres (clnt_stat err)
{
  // Handle readlink of the symlink "foo"


  /*
    sfsrwcd: ACLNT_TRACE: call ex_nfs_program_3:ex_NFSPROC3_LOOKUP x=5
    diropargs3 ARGS = {
    nfs_fh3 dir = {
    opaque data<64> = [32] {
    e1, 86, 8e, 80, dc, 20, fa, 38,
    93, c5, 09, bc, 44, a6, 01, e0,
    6b, 36, c8, 26, 55, 0b, 22, 97,
    66, 3e, 23, cd, 18, 6e, 85, 69
    };
    };
    string name<> = "ny";
  */


}

void
accressres (clnt_stat err)
{
/*
  sfsrwcd: ACLNT_TRACE: call ex_nfs_program_3:ex_NFSPROC3_ACCESS x=6
  access3args ARGS = {
  nfs_fh3 object = {
  opaque data<64> = [32] {
  e1, 86, 8e, 80, dc, 20, fa, 38,
  93, c5, 09, bc, 44, a6, 01, e0,
  6b, 36, c8, 26, 55, 0b, 22, 97,
  66, 3e, 23, cd, 18, 6e, 85, 69
  };
  };
  u_int32_t access = 0x3f;
  };
*/
}

/*
sfsrwcd: ACLNT_TRACE: call ex_nfs_program_3:ex_NFSPROC3_FSINFO x=7
nfs_fh3 ARGS = {
  opaque data<64> = [32] {
    e1, 86, 8e, 80, dc, 20, fa, 38,
    93, c5, 09, bc, 44, a6, 01, e0,
    aa, 02, 6d, 88, 6b, 9b, 24, 2b,
    1f, 60, 4c, 7c, 7d, c3, b3, 50
  };
};
*/

/*
sfsrwcd: ACLNT_TRACE: call ex_nfs_program_3:ex_NFSPROC3_READLINK x=8
nfs_fh3 ARGS = {
  opaque data<64> = [32] {
    e1, 86, 8e, 80, dc, 20, fa, 38,
    93, c5, 09, bc, 44, a6, 01, e0,
    aa, 02, 6d, 88, 6b, 9b, 24, 2b,
    1f, 60, 4c, 7c, 7d, c3, b3, 50
  };
};
*/

void
usage ()
{

  warnx << "Trace numbers are:\n\n";

  fatal << "\nusage: " << progname << " [-p trigger-port] [-n num-clients]\n"
	<< "            [-t seconds] [-l label] [-v] -r trace-num server\n\n"
	<< "Remember to set SFS_PORT\n";
}

int
main (int argc, char **argv)
{
  setprogname (argv[0]);
  sfsconst_init ();

  //  warn ("pid %d\n", getpid ());

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

  int numconn = atoi (argv[1]);
  sfsrw_port = atoi (argv[2]);
  hostname = argv[3];
  hostid = argv[4];
  u_int64_t duration_tm = atoi(argv[5]);
  
  if (duration_tm>= 1000000) {
    warn << "Duration too long.  Go away.\n";
    exit (-1);
  }
  
  const int pubkeybits = 768; // sfs_pubkeysize;
  client_key =
    New refcounted<rabin_priv> (rabin_keygen (pubkeybits));
  
  warn << "Making " << duration_tm
       << " seconds 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);
  }
  ia = (struct in_addr *) h->h_addr;
  
  lookuparg.dir.data.setsize (32);
  memcpy (lookuparg.dir.data.base (), 
	  "\xe1\x86\x8e\x80\xdc\x20\xfa\x38\x93\xc5\x09\xbc\x44\xa6\x01\xe0\x71\x48\xc6\x50\x75\xd2\xdd\xda\x7f\xd7\xc8\x5e\xc1\xe1\x14\x06",
	  32);
	  
  lookuparg.name = "bar";
  
  readlinkarg.data.setsize (32);
  memcpy (readlinkarg.data.base (),
	  "\x7a\x8a\x25\xbb\x89\xaf\x6e\xda\xe9\x8d\x38\x05\x4b\x98\x80\x25\x80\xec\x50\xfc\xc4\x68\xd1\x81\xac\xf8\x25\x6d\x5b\xb2\x15\x77\xae\x29\xb5\xcf\xba\xa3\x4e\x2b",
	  32);

  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  = duration_tm;
  itv.it_value.tv_usec = 0;
  
  // 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 = get_time();
  for (int i=0; i< numconn; i++) 
    vNew srvcon ();
  amain ();
}


