/*
 * control.c
 *
 * x-kernel v3.3
 *
 * Copyright (c) 1991,1996  Arizona Board of Regents
 *
 * $Revision: 1.2 $
 * $Date: 1996/02/01 15:36:43 $
 */


#define PROT_STRING "meg"

#include "xkernel.h"
#include "ip.h"
/* #include "site.h" */
#include "xsim.h"


#include "xkernel.h"
#ifndef XKMACHKERNEL
#include "x_stdio.h"
#else
#include "assert.h"
#endif ! XKMACHKERNEL

#define TIMES 1
#define LENGTH 1000*1024
#define BUFFER_SIZE 50 * 1024
#define DROP_INTERVAL 0 
#define DIS_INTERVAL 0 

static  IPhost 	ServerAddr = {0,0,0,0};
static  IPhost  ClientAddr = {0,0,0,0};

typedef struct tcpVars {
  IPhost	myHost;
  Protl		myProtl;
  Sessn		clientDownSes;
  XTime 	starttime;
  int 		serverParam;
  int 		clientParam;
  char 		*serverString; 
  IPhost 	serverAddr;
  IPhost 	clientAddr;
  Semaphore 	s;
  u_short 	space;
  int 		length;
  int 		maxPacket;
  int 		vdrop_flag;
  int 		vdisorder_flag;
  int 		mytrace;
  int		delay;
  long    	serverPort;
  long    	clientPort;
  int		foundMyAddr;
  char		*hostStr;
  char		*dbName;
  char		*llpName;
  int		serverRcvDup;
  float		bw;
  char		*dbStr;
  char		*dbStr2;
  char		*dbStr3;
  int		trips;
  int		rlen, plen, rtime;
  int		cnt;
  char		timeArray[30];
  Msg		m, savedMsg;
} tcpVars;

tcpVars *VarsArray[30];
int	VarsArrayN = 0;
int     tracecontrolp;
char	*TcpProtls[] = {"void", "ttcp", "rtcp", "vtcp", "void"};
XTime   total;

#ifdef __STDC__

static	void 	client( Event, void * );
static 	void 	server( Event, void * );
static 	void	subtime( XTime *, XTime *, XTime *);
static  void 	processOptions(tcpVars *);
static  int	isServer(tcpVars *);
static  int	isClient(tcpVars *);
static  void	clientSetPart(tcpVars *, Part *);
static  void	serverSetPart(tcpVars *, Part *);
static	XkReturn closeDone(Sessn);
static	XkReturn saveServerSessn(Protl, Protl, Sessn, Protl);
static  int 	runTest(tcpVars *);
static  XkReturn serverDemux(Protl,Sessn,Msg *);
static  XkReturn clientDemux(Protl,Sessn,Msg *);
int 	control_init(Protl);


#else

static	void 	client();
static 	void 	server();
static 	void	subtime();
static  void 	processOptions();
static  int	isServer();
static  int	isClient();
static  void	clientSetPart();
static  void	serverSetPart();
static	XkReturn closeDone();
static	XkReturn saveServerSessn();
static  int 	runTest();
static  XkReturn serverDemux();
static  XkReturn clientDemux();
int 	control_init();


#endif __STDC__

#ifdef XNETSIM
extern	int	xsimBegTest();
extern 	int	xsimEndTest();
#endif

extern  int	Yield();

#ifdef XKMACHKERNEL
static int
sscanf1(str, format, a1)
char *str, format;
int *a1;
{
  int n;

  *a1=0;
  while (*str >= '0' && *str <= '9')
    *a1 = 10*(*a1) + (*str++ - '0');
  return(1);
}
#else
#define sscanf1 sscanf
#endif XKMACHKERNEL


#ifdef __STDC__
#define DOUBLEQUOTEWRAP(x) #x
#define STRINGIFY(z) DOUBLEQUOTEWRAP(z)
#endif

#ifdef XNETSIM
extern int	xsimArgc;
extern char	**xsimArgv;
#endif

static void
processOptions(v)
  tcpVars *v;

{
  int		i, k=-1, foundMyAddr=0;
  char 	*arg, *hostStr, str[100];
/*   IPhost	host; */
    
#define argPrefix(str) (! strncmp(arg, str, strlen(str)))
#define argEq(str) (! strcmp(arg, str) )

  sprintf(str, "%s/%s", v->myProtl->name, v->myProtl->instName);

#ifndef XNETSIM
  foundMyAddr = 1;
#else
  v->hostStr = hostStr = v->myProtl->instName;
  for (i=0; i < xsimArgc; i++) {
    arg = xsimArgv[i];
    
    if ( argEq("-s") ) {
      v->serverParam = 1;
    } else if ( argPrefix("-c") ) {
      v->clientParam = 1;
      v->serverString = arg + 2;
    } else if (argPrefix("-len=")) {
      sscanf(arg+5, "%d", &k);
      if (k > 0) v->length = k*1024;
    } else if ( argPrefix("-trips=") ) {
      sscanf1(arg + strlen("-trips="), "%d", &v->trips);
    } else if ( argPrefix("-buf=") ) {
      sscanf(arg+5, "%d", &k);
      if (k > 0) v->space = k*1024;
    } else if (argPrefix("-delay=")) {
      sscanf(arg+7, "%d", &k);
      if (k > 0) v->delay = k;
    } else if (argPrefix("-dbStr=")) {
      v->dbStr = arg + 7;
    } else if (argPrefix("-dbStr2=")) {
      v->dbStr2 = arg + 8;
    } else if (argPrefix("-dbStr3=")) {
      v->dbStr3 = arg + 8;
    } else if (argPrefix("-db=")) {
      v->dbName = arg + 4;
    }
  }
  
#endif XNETSIM

  for (i=1; i < globalArgc; i++) {
    arg = globalArgv[i];
    
    if (argEq("-N")) {
      if (foundMyAddr)
	break; 
      arg = globalArgv[++i];
      if (strcmp(str, arg)) {
	i++;
	continue;
      }     
/*       arg = globalArgv[++i]; */
/*       str2ipHost(&host, arg); */
/*       v->myHost = host; */
      foundMyAddr = 1;
/*       v->hostStr = hostStr = arg; */
/*       xsimDbg(TMG_FLAG, */
/* 	      printf("[%s] %s found my address\n", hostStr, str)); */
      continue; 
    } else if (!foundMyAddr) {
      continue;
    } else if ( argEq("-s") ) {
      v->serverParam = 1;
    } else if ( argPrefix("-c") ) {
      v->clientParam = 1;
      v->serverString = arg + 2;
    } else if (argPrefix("-len=")) {
      sscanf(arg+5, "%d", &k);
      if (k > 0) v->length = k*1024;
    } else if ( argPrefix("-trips=") ) {
      sscanf1(arg + strlen("-trips="), "%d", &v->trips);
    } else if ( argPrefix("-buf=") ) {
      sscanf(arg+5, "%d", &k);
      if (k > 0) v->space = k*1024;
    } else if (argPrefix("-delay=")) {
      sscanf(arg+7, "%d", &k);
      if (k > 0) v->delay = k;
    } else if (argPrefix("-dbStr=")) {
      v->dbStr = arg + 7;
    } else if (argPrefix("-dbStr2=")) {
      v->dbStr2 = arg + 8;
    } else if (argPrefix("-dbStr3=")) {
      v->dbStr3 = arg + 8;
    } else if (argPrefix("-db=")) {
      v->dbName = arg + 4;
    }
  }
  
#undef argPrefix
#undef argEq
}

int
control_init( self )
    Protl self;
{
    Protl	llp;
    tcpVars	*v;
    int		begTime;

    v = (tcpVars *)xMalloc(sizeof(tcpVars));
    VarsArray[VarsArrayN++] = v;
#ifndef XNETSIM    
    time(&begTime);
    strcpy(v->timeArray, ctime(&begTime));
#endif
    v->serverParam = 0;
    v->clientParam = 0;
    v->serverAddr = ServerAddr;
    v->clientAddr = ClientAddr;
    v->space = BUFFER_SIZE;
    v->length = LENGTH;
    v->mytrace = 0;
    v->delay = 0;
    v->serverPort = 2001;
    v->cnt = 0;
    v->clientPort = ANY_PORT;
    v->foundMyAddr = 0;
    v->hostStr = "?????";
    v->dbName = NULL;
    v->dbStr = NULL;
    v->dbStr2 = NULL;
    v->dbStr3 = NULL;
    v->trips = 1;
    v->myProtl = self;
    v->rlen = v->plen = v->rtime = 0;

    processOptions(v);

    llp = xGetProtlDown(self, 0);
    if (!xIsProtl(llp)) {
	Kabort("Test protocol has no lower protocol");
    }
    v->llpName = llp->name;
    xControlProtl(xGetProtlDown(self, 0), GETMYHOST, (char *)&v->myHost, sizeof(IPhost));
    xsimDbg(TMG_FLAG,
	    printf("[%s] %s found my address\n", v->hostStr, self->name, 
		   self->instName));

    printf("[%s] %s control test\n", v->hostStr, PROT_STRING);
    /* 
     * Call the per-test initialization function which gives the test
     * the opportunity to override the default functions
     */
    if (isServer(v)) {
#ifdef XNETSIM
      xsimBegTest();
#endif
      evDetach( evSchedule(server, (void *)v, 0) );
    } else if (isClient(v)) {
#ifdef XNETSIM
      xsimBegTest();
#endif
      evDetach( evSchedule(client, (void *)v, v->delay*1000) );
    } else {
      printf("[%s] %stest: I am neither server nor client\n", 
	     v->hostStr, PROT_STRING);
    }
    return 0;
}


static int
isServer(v)
  tcpVars *v;

{
    if ( v->serverParam ) {
	return TRUE;
    }
    return ! bcmp((char *)&v->myHost, (char *)&v->serverAddr, sizeof(IPhost));
}


static int
isClient(v)
  tcpVars *v;
{
    
    if ( v->clientParam ) {
	str2ipHost(&v->serverAddr, v->serverString);
	v->clientAddr = v->myHost;
	return TRUE;
    }
    return ! bcmp((char *)&v->myHost, (char *)&v->clientAddr, sizeof(IPhost));
}



static void
clientSetPart( v, p )
  tcpVars 	*v;
  Part 		*p;
{
  partInit(p, 1);
  partPush(*p, &v->serverAddr, sizeof(IPhost));
/*   partPush(p[0], &v->serverPort, sizeof(long)); */
  printf("[%s] Client sending to: %s\n", v->hostStr,
	 ipHostStr(&v->serverAddr));

}

static void
serverSetPart( v, p )
  tcpVars 	*v;
  Part 		*p;
{
  partInit(p, 1);
  partPush(*p, ANY_HOST, 0);
/*   partPush(*p, &v->serverPort, sizeof(long)); */
  printf("[%s] Server responding to: %s\n", v->hostStr, "ANY_HOST");
}

tcpVars *getMyVars( self )
    Protl self;
{
  char str[100];
  int  k;

  for (k=0; k<VarsArrayN; k++) {
    if (self == VarsArray[k]->myProtl)
      return VarsArray[k];
  } 
    
  sprintf(str, "getMyVars fails for Protl: %x\n", self);
  Kabort(str);
  return NULL;  
}           

static XkReturn
closeDone( lls )
  Sessn	lls;
{
    xTrace2(prottest, TR_MAJOR_EVENTS, "%s test -- closedone (%x) called",
	    "meg", lls);
    return XK_SUCCESS;
}

#  ifdef __STDC__
static XkReturn   saveServerSessn( Protl, Protl, Sessn, Protl );
#  endif

static XkReturn
saveServerSessn( self, llp, s, hlpType )
    Protl self, llp, hlpType;
    Sessn s;
{
  tcpVars *v;

  v = getMyVars(self);
  xTrace1(prottest, TR_MAJOR_EVENTS,
	  "%stest program duplicates lower server session",
	  PROT_STRING);

  xDuplicate(s);

/*
  if (xControlSessn(s, TCP_SETRCVBUFSIZE, (char*)&v->space, sizeof(v->space)) < 0) {
    xError("saveServerSessn: TCP_SETRCVBUFSIZE failed");
  } 

  if (xControlSessn(s, TCP_SETSNDBUFSIZE, (char*)&v->space, sizeof(v->space)) < 0) {
    xError("saveServerSessn: TCP_SETSNDBUFSIZE failed");
  } 

  if (xControlSessn(s, TCP_SETRCVBUFSPACE, (char*)&v->space, sizeof(v->space)) < 0){
    xError("saveServerSessn: TCP_SETRCVBUFSPACE failed");
  } 
*/
  return XK_SUCCESS;
}


static void
server( ev, foo )
  Event	ev;
  VOID 	*foo;
{
  Part 	p;
  tcpVars 	*v=(tcpVars *)foo;
  
  printf("\n*** I am the server\n");/* Don't change (LSB) */
  printf("[%s] buffer size: %d\n", v->hostStr, v->space);
  v->myProtl->demux = serverDemux;
  v->myProtl->opendone = saveServerSessn;
  v->myProtl->closedone = closeDone;
  serverSetPart(v, &p);
  if ( xOpenEnable(v->myProtl, v->myProtl, xGetProtlDown(v->myProtl, 0), &p)
      == XK_FAILURE ) {
    printf("[%s] %stest server can't openenable lower protocol\n",
	   v->hostStr, PROT_STRING);
  } else {
    printf("[%s] %stest server done with xopenenable\n", 
	   v->hostStr, PROT_STRING);
    }
  return;
}


static void
client( ev, foo )
    Event	ev;
    VOID 	*foo;
{
  char  str[100], buf[4000], *cp;
  Part	p[2];
  int 	vals[5], id=0;
  FILE  *fout, *fin;
  tcpVars *v=(tcpVars *)foo;
  
  printf("\n*** I am the client\n");
  
  v->myProtl->demux = clientDemux;
  semInit(&v->s,0);
  
  clientSetPart(v, p);
  if ( v->clientDownSes == 0 ) {
    v->clientDownSes = xOpen(v->myProtl, v->myProtl, 
			     xGetProtlDown(v->myProtl, 0), p);
    if ( v->clientDownSes == ERR_SESSN ) {
      printf("[%s] control: open failed!\n", v->hostStr);
      Kabort("End of test");
      return;
    }
  }
  
  runTest(v);

  printf("\n[%s] Protocol:             control\n", v->hostStr);
  printf("[%s] No. packets got thru: %d\n", v->hostStr, v->cnt);
  if (v->dbName) {
    if ((fin = fopen("idFile", "r")) != NULL) {
      fscanf(fin, "%d", &id);
      fclose(fin);
    }
    if ((fout = fopen(v->dbName, "a")) == NULL) 
      printf("Could not open db file: `%s'\n", v->dbName);
    else {
      fprintf(fout, "%%\n+++++\n%%\nProtocol: %s\n", "control");
      fprintf(fout, "id: %d\n", id);

#ifdef XNETSIM      
      fprintf(fout, "Host: %s\n", v->myProtl->instName);
      fprintf(fout, "Path: xsim\n");
#else
      cp = v->timeArray;
      fprintf(fout, "Date: %c%c%c %c%c %c%c\n", cp[4], cp[5], cp[6],
          cp[8], cp[9], cp[22], cp[23]);
      fprintf(fout, "Dow: %c%c%c\n", cp[0], cp[1], cp[2]);
      fprintf(fout, "Time: %c%c%c%c%c\n", cp[11], cp[12], cp[13], cp[14], 
	      cp[15]);
#endif
      fprintf(fout, "got thru:  %d\n", v->cnt);
      fprintf(fout, "con state: %.1f\n", (v->cnt*100.0)/200);
      if (v->dbStr != NULL)
	fprintf(fout, "%s\n", v->dbStr);
      if (v->dbStr2 != NULL)
	fprintf(fout, "%s\n", v->dbStr2);
      if (v->dbStr3 != NULL)
	fprintf(fout, "%s\n", v->dbStr3);
      fclose(fout);
    }
  }

  xClose(v->clientDownSes);
  sprintf(str, "[%s] ***** End of test (client)  *****", v->hostStr);

#ifndef XNETSIM
  Kabort(str);
#else
  printf("%s\n", str);
  xsimEndTest();
#endif

    
}

static int
runTest( v )
    tcpVars *v;
{
  XTime 	now;
  Msg		savedMsg, m;
  int 		test, len=v->length, times=v->trips, atimes[500], i, j=2, n=0;
  int		packets=200, interval=10000, k;
  char		*buf, str[100];
  int 		totalBytes;
    
  printf("[%s] runTest: entered %d packets at %dms intervals\n", v->hostStr,
	 packets, interval/1000);
  totalBytes = 0;
  xGetTime(&v->starttime);

  buf = msgConstructAllocate(&savedMsg, 1000);
  *buf = 0;
  msgConstructEmpty(&m);
  for (test = 0; test < packets; test++) {
    msgAssign(&m, &savedMsg);
    if (xPush(v->clientDownSes, &m) == XMSG_ERR_HANDLE) {
      printf("[%s] runTest: xpush failed! ********\n", v->hostStr);
    } 
    xGetTime(&now);
    i = (now.sec - v->starttime.sec)*1000 +(now.usec - v->starttime.usec)/1000;
    atimes[test] = i;
    if (test%3 == 0) 
      usleep(interval);
/*       if (++j > 5) */
/* 	j = 2, n = 1; */
/*     } */
  }

  msgDestroy(&savedMsg); 
  buf = msgConstructAllocate(&savedMsg, 4);
  *buf = 'e';
  for (test = 0; test < 10; test++) {
    msgAssign(&m, &savedMsg);
    if (xPush(v->clientDownSes, &m) == XMSG_ERR_HANDLE) {
      printf("[%s] runTest: xpush failed! ********\n", v->hostStr);
    } 
    usleep(50000);
  }
  semWait(&v->s);

  for (k=0; k<packets; k++) {
    printf("%4d ", atimes[k]);
    if ((k+1)%16 == 0)
      printf("\n");
  }
  printf("\n");
  
  msgDestroy(&savedMsg); 
  msgDestroy(&m); 

  return 0;
}

static long             
msgLoad( void *ptr, char *netData, long len, void *arg )
{
    xAssert(len == sizeof(int));
    bcopy(netData, (char *)ptr, sizeof(int));
    return sizeof(int); 
}           

static XkReturn
serverDemux( self, lls, dg )
    Protl self;
    Sessn lls;
    Msg *dg;
{
  char    *buf;
  int	  vals[5], i;
  tcpVars *v;
  void    *buffer;

  v = getMyVars(self);

  buffer = msgPop(dg, sizeof(int));
  if (! buffer)
    printf("[%s] clientDemux: Could not pop in server\n" v->hostStr);
  else
    msgLoad((void *)&i, buffer, sizeof(int), 0);

  if (i != 0) {
    buf = msgConstructAllocate(&v->savedMsg, 4);
    msgConstructEmpty(&v->m);
    *((int *)buf) = v->cnt;
    printf("[%s] packets rcvd: %d\n", v->hostStr, v->cnt);
    for (i=0; i<10; i++) {
      msgAssign(&v->m, &v->savedMsg);
      if (xPush(lls, &v->m) == XMSG_ERR_HANDLE) {
	printf("[%s] serverDemux: xpush failed!\n", v->hostStr);
      }
      usleep(50000);
    }
    Kabort("***** End of test (server)  *****");
  }
  v->cnt++;
  return XK_SUCCESS;
}

static XkReturn
clientDemux( self, lls, dg )
    protl self;
    Sessn lls;
    Msg *dg;
{
  tcpVars *v;
  void *buf;

  v = getMyVars(self);

  buf = msgPop(dg, sizeof(int));
  if (! buf)
    printf("clientDemux: Could not pop v->cnt\n");
  else
    msgLoad((void *)&v->cnt, buf, sizeof(int), 0);

/*   printf("[%s] packets rcvd os: %d\n", v->hostStr, v->cnt); */
  semSignal(&v->s);
  return XK_SUCCESS;
}

static void
subtime(t1, t2, t3)
    XTime *t1, *t2, *t3;
{
    t3->sec = t2->sec - t1->sec;
    t3->usec = t2->usec - t1->usec;
    if (t3->usec < 0) {
	t3->usec += 1000000;
	t3->sec -= 1;
    }
}

