/* $Id: maxssl_cps.C,v 1.5 2000/08/29 07:38:49 fubob Exp $ */

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

/*
 *
 * Copyright (C) 2000 Kevin Fu (fubob@mit.edu)
 * Copyright (C) 2000 Michael Kaminsky (kaminsky@lcs.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 "maxssl_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;
int opt_port = 8443;
in_addr addr;
int fd_null;
str sslpath;


static char *av1[] = {
  "sslgetpage",
  "18.26.4.31:8443:u4umis9hrqw5p6p26ts6vjtcm77p392h/symlink.txt",
  NULL
};


static char *av2[] = {
  "sslgetpage",
  "18.26.4.31:8443:u4umis9hrqw5p6p26ts6vjtcm77p392h/one/two/three/symlink.txt",
  NULL
};


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

void init ();

void
ssldone (int status)
{
  if (status) {
    warn << "sslgetpage failed with status: " << status << "\n";
  }

  count++;

  init();

}

void
init ()
{
  attempted++;
  pid_t pid = aspawn (sslpath, ((opt_test == 1)?av1:av2), 0, fd_null);
  
  if (pid < 0) {
    fatal ("fork: %m\n");
  }

  chldcb (pid, wrap (ssldone));
}



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

  } else {

    printf ("%5s %10lu %10qd %10f %6lu %6lu %8.2f %8.2f\n",
	    opt_name?opt_name:"Local",
	    0L,
	    elapsed_usec,
	    0.0,
	    count,
	    attempted,
	    count / (elapsed_usec / 1e6F),
	    attempted / (elapsed_usec/ 1e6F));
  }

  exit (0);
}

void
usage ()
{
  warnx << " Test 1 == Single component\n"
	<< " Test 2 == Multi component\n\n";

  fatal << "\nusage: " << progname << " [-p trigger-port] [-n num-clients]\n"
	<< "         [-t seconds] [-l label] [-v] -r test-num server port\n\n";

}


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

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

  int ch;
  while ((ch = getopt (argc, argv, "p:n:t:r:l: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 'v':
      opt_verbose = true;
      break;
    case 'l':
      opt_name = optarg;
      break;
    case 'r':
      if (!convertint (optarg, &opt_test))
	usage ();
      break;
    default:
      usage ();
      break;
    }

  if ( (optind + 2 != argc)
       || ((opt_test != 1) && (opt_test != 2)))
    usage ();

  if (!convertint (argv[optind+1], &opt_port))
    usage ();

  hostent *hp = gethostbyname (argv[optind]);
  if (!hp)
    fatal << argv[1] << ": no such host\n";
  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));


  fd_null = open ("/dev/null", O_WRONLY, 0);

  /*  char mycwd[100];
  if (getcwd (mycwd, 100) == NULL) {
    warn << "Can't get CWD\n";
    exit (-1);
  }

  sslpath = mycwd; 
  sslpath = sslpath << "/sslgetpage";
  */

  sslpath =  "/tmp/sslgetpage";

  if (opt_verbose) {
    warn << "sslgetpath path: " << sslpath << "\n";
    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 ();

  for (int i=0; i< opt_nclients; i++) 
    init ();
  amain ();
}
