/* $Id: maxsouschefs_cps.C,v 1.2 2005/07/28 19:53:42 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 a 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/@192.168.1.103%8888,ugrx7buwzevr8bicstvr47ag5h5zttrn/keyfile.kr > /dev/null

*/

/*
 *
 * Copyright (C) 2005 Kevin Fu (fubob@mit.edu)
 * 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 "maxsouschefs_cps.h"
#include "bench.h"
#include <sys/time.h>

bool done = false;
uint32 active_traces;
uint32 conncnt = 0;
uint32 successful_rpcs = 0;
char *hostname;
u_int64_t start_tm, end_tm;

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

u_int32_t bigdeal =0;


void
done_handler ()
{
  end_tm = get_time ();
  warn << "Conn/sec   Duration (usec)    Succ GETDATA RPCs  #conns #attmptconns\n";
  warn << "results "
       << conncnt*1000000/(end_tm - start_tm) << " "
       << end_tm - start_tm << " "
       << successful_rpcs   << " "
       << conncnt << " "
       << bigdeal;
  exit (0);
}

void
srvcon::fail (int err)
{
  warn << "connection failed" << strerror (errno) << "\n";
  active_traces--;
  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 {
    sfsroc = aclnt::alloc (s, sfsro_program_2);
    chefs_key keyarg;
    keyarg.gk_id = (u_int32_t)1;
    sfsroc->call (SFSROPROC2_GETKEY, &keyarg, &keyres,
		  wrap (this, &srvcon::getkeyres));
  }
}

void
srvcon::getkeyres (clnt_stat err)
{
  if (err)
    fail (EIO);
  else {
    ref<sfsro_getdataargs> gdargs = New refcounted<sfsro_getdataargs> ();
    gdargs->sname = carg.ci5->sname;
    memcpy (gdargs->fh.base (), trace[0], 20);
    
    sfsroc->call (SFSROPROC2_GETDATA, gdargs, &sfsrores,
		  wrap (this, &srvcon::getdata, gdargs, 1));
  }
}

void
srvcon::getdata (ref<sfsro_getdataargs> gdargs, uint32 num, clnt_stat err)
{

  if (err) {
    fail (EIO);
    return;
  }

  successful_rpcs++;
  if (num >= trace_size) {
    conncnt++;
    if (!done) {
      warn << "Next trace starting\n";
      vNew srvcon ();
    }
    else {
      active_traces--;
      warn << "Active traces = " << active_traces << "\n";
      if (active_traces == 0)
	done_handler ();
    }
    delete this;
  } else {
    memcpy (gdargs->fh.base (), trace[num], 20);
    sfsroc->call (SFSROPROC2_GETDATA, gdargs, &sfsrores,
		  wrap (this, &srvcon::getdata, gdargs, num + 1));
  }
}



void
handler ()
{
  warn << "Timer goes ding!\n";
  done = true;  
}


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 sec> [<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 = atoi (argv[5]);

  warn << "trace_size " << trace_size << "\n";
  
  if (duration_tm>= 1000000) {
    warn << "Duration too long.  Go away.\n";
    exit (-1);
  }

  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;

  /*
sfsrosd: ASRV_TRACE: serve sfs_program_1:SFSPROC_CONNECT x=98c6902
sfs_connectinfo ARGS = {
  u_int32_t civers = 0x5;
  sfs_connectinfo_5 ci5 = {
    u_int32_t release = 0x8;
    sfs_service service = SFS_SFS;
    string sname<> = "@192.168.1.103%8888,ugrx7buwzevr8bicstvr47ag5h5zttrn";
    string extensions<><> = [0] {};
  };
};
  */

  carg.set_civers (5); 
  carg.ci5->release = SFS_RELEASE;
  carg.ci5->service = SFS_SFS;
  carg.ci5->sname = strbuf () << "@" << hostname << "%" << sfsro_port
			      << "," << hostid;
  

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

  struct itimerval itv;
  timerclear (&itv.it_interval);
  timerclear (&itv.it_value);
  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++) {
    active_traces++;
    vNew srvcon ();
  }
  amain ();

}


