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

/*
 *
 * Copyright (C) 2000 David Mazieres (dm@uun.org)
 * 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 "arpc.h"
#include "sfsmisc.h"
#include "sfsro_prot.h"
#include "parseopt.h"
#include "crypt.h"


ptr<rabin_priv> client_key;

sfs_connectarg carg;

nfs_fh3 getattrarg;
diropargs3 lookuparg;
access3args accessarg1;
nfs_fh3 fsinfoarg;
nfs_fh3 readlinkarg;


struct player {
  static unsigned long playcount;
  static unsigned long playcount_attempted;

  const in_addr srvaddr;

  ptr<aclnt> cs;
  ptr<aclnt> cr;
  ptr<aclnt> sfsc;
  ptr<aclnt> sfsrwc;

  sfs_connectres cres;
  sfs_fsinfo fsires;
  ex_lookup3res lres;
  ex_readlink3res rres;

  player (in_addr a)
    : srvaddr (a)
  { start (); }
  void start ();
  void getfd (int fd);
  void conres (clnt_stat stat);
  void cryptres (const sfs_hash *sessid);
  void fsinfores (clnt_stat stat);
  void lookupres (clnt_stat stat);
  void readlinkres (clnt_stat stat);
};

unsigned long player::playcount;
unsigned long player::playcount_attempted;

bool opt_verbose = false;
int opt_trace = -1;

void
player::start ()
{
  cr = cs = NULL;

  playcount_attempted++;

  tcpconnect (srvaddr, sfs_port, wrap (this, &player::getfd));
}

void
player::getfd (int fd)
{
  if (fd < 0) {
    warn ("tcpconnect: %m\n");	// XXX
    timecb (timenow + 1, wrap (this, &player::start));
    return;
  }

 
  ref<axprt_crypt> x (axprt_crypt::alloc (fd));
  cs = aclnt::alloc (x, sfs_program_1);
  cr = aclnt::alloc (x, ex_nfs_program_3);

  sfs_connectarg carg;
  carg.release = sfs_release;
  carg.service = SFS_SFS;
  carg.name = "";
  cs->call (SFSPROC_CONNECT, &carg, &cres, wrap (this, &player::conres));
}
 
void
player::conres (clnt_stat stat)
{
  if (stat) {
    warn << stat;
    start ();
    return;
  }
  
  sfs_client_crypt (cs, client_key, carg, *cres.reply,
		    wrap (this, &player::cryptres));
}

void
player::cryptres (const sfs_hash *sessid)
{
  if (!sessid) {
    warn << sessid;
    start ();
    return;
  }

  cs->call (SFSPROC_GETFSINFO, NULL, &fsires, 
	    wrap (this, &player::fsinfores));

}

void
player::fsinfores (clnt_stat stat)
{
  if (stat) {
    warn << stat;
    start ();
    return;
  }
  if (fsires.status)
    warn ("ENOENT\n");

  cr->call (ex_NFSPROC3_GETATTR, &getattrarg, NULL,
	    wrap (this, &player::getattrres));
    
  
}


void
player::fsinfores (clnt_stat stat)
{
  if (stat) {
    warn << stat;
    start ();
    return;
  }
  if (fsires.status)
    warn ("ENOENT\n");

  cr->call (ex_NFSPROC3_LOOKUP, &lookuparg, &lres,
		wrap (this, &player::lookupres));
    
  
}




void
player::lookupres (clnt_stat stat)
{
  if (stat) {
    warn << stat << "\n";
    start ();
    return;
  }
  if (lres.status)
    warn ("ENOENT\n");
  cr->call (ex_NFSPROC3_READLINK, &readlinkarg, &rres,
	    wrap (this, &player::readlinkres));

}

void
player::readlinkres (clnt_stat stat)
{
  if (stat) {
    warn << stat << "\n";
    start ();
    return;
  }

  playcount++;
  start ();
}

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

u_int64_t starttime;

void
finish ()
{
  u_int64_t elapsed_usec = gettime () - starttime;
  if (opt_verbose) {
    printf (" usec: %qd\n", elapsed_usec);
    printf ("Successful plays: %lu\n", player::playcount);
    printf ("Attempted plays: %lu\n", player::playcount_attempted);
    printf ("Successful plays/s: %f\n", 
	    player::playcount / (elapsed_usec / 1e6F));
    printf ("Attempted plays/s: %f\n\n", 
	    player::playcount_attempted / (elapsed_usec/ 1e6F));

  } else {

    printf ("%21qd %17lu %6lu %14f %14f\n",
	    elapsed_usec,
	    player::playcount,
	    player::playcount_attempted,
	    player::playcount / (elapsed_usec / 1e6F),
	    player::playcount_attempted / (elapsed_usec / 1e6F));
  }

  exit (0);
}

void
usage ()
{

  warnx << "Trace numbers are:\n\n"
	<< "0: Readlink\n"
	<< "1: cat of multi-component dir\n";
 

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


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



  int opt_triggerport = 0;
  int opt_nclients = 20;
  double opt_nseconds = 5.0;

  int ch;
  while ((ch = getopt (argc, argv, "p:n:t:r:v")) != -1)
    switch (ch) {
    case 'p':
      if (!convertint (optarg, &opt_triggerport))
	usage ();
      break;
    case 'n':
      if (!convertint (optarg, &opt_nclients))
	usage ();
      break;
    case 't':
      opt_nseconds = atof (optarg);
      if (opt_nseconds <= 0)
	usage ();
      break;
    case 'r':
      if (!convertint (optarg, &opt_trace))
	usage ();
      break;
    case 'v':
      opt_verbose = true;
      break;
    default:
      usage ();
      break;
    }

  if ((optind + 2 != argc) 
      || (opt_trace < 0) 
      || (opt_trace >= (int)tracevec_size))
    usage ();

  hostent *hp = gethostbyname (argv[optind]);
  if (!hp)
    fatal << argv[1] << ": no such host\n";
  in_addr addr = *(in_addr *) hp->h_addr;



  itimerval itv;
  bzero (&itv, sizeof (itv));
  itv.it_value.tv_sec = long (opt_nseconds);
  itv.it_value.tv_usec = long (1000000 * (opt_nseconds - itv.it_value.tv_sec));
  sigcb (SIGALRM, wrap (finish));


  if (opt_verbose) {
    printf ("Clients     Est. Duration (usec)    Bytes   Act. Duration (usec)   MB/s   Succ. Plays   Attem. Plays  Succ. Plays/s   Attem. Plays/s\n");
 
    printf ("[ %d clients / %f seconds ]\n", opt_nclients, opt_nseconds);
  }

  if (opt_triggerport) {
    int fd = inetsocket (SOCK_DGRAM, opt_triggerport);
    if (fd < 0)
      fatal ("socket: %m\n");
    char c;
    socklen_t l = 0;
    recvfrom (fd, &c, 1, 0, NULL, &l);
  }

  if (setitimer (ITIMER_REAL, &itv, NULL) < 0)
    fatal ("setitimer: %m\n");

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


  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);

  for (int i = 0; i < opt_nclients; i++)
    vNew player (addr);

  amain ();

}
