/*     
 * $RCSfile: bid.c,v $
 *
 * x-kernel v3.3
 *
 * Copyright (c) 1993,1991,1990,1996  Arizona Board of Regents
 *
 * $Log: bid.c,v $
 * Revision 1.3  1996/01/29 22:02:01  slm
 * Updated copyright and version.
 *
 * Revision 1.2  1995/08/03  19:10:24  davidm
 * 64-bit cleanup and x3.2->x3.3 conversion.
 *
 * Revision 1.1  1995/08/01  17:50:21  davidm
 * Initial revision
 *
 * Revision 1.22  1993/12/13  21:06:46  menze
 * Modifications from UMass:
 *
 *   [ 93/09/21          nahum ]
 *   Removed char * cast in mapResolve call
 */

/*
 * Protocol initialization, Session creation/destruction,
 * header load/store routines, control (registration/deregistration)
 */

#include "xkernel.h"
#include "bidctl.h"
#include "bid_i.h"
#include "bid.h"

#ifdef __STDC__

static void	activateSessn( Sessn );
static XkReturn	bidClose( Sessn );
static int	bidControlProtl( Protl, int, char *, int );
static int	bidControlSessn( Sessn, int, char *, int );
static Sessn	bidCreateSessn( Protl, Protl, Protl, ActiveKey * );
static XkReturn	bidDemux( Protl, Sessn, Msg * );
static void	bidHdrDump( BidHdr *, char * );
static long	bidHdrLoad( VOID *, char *, long, VOID * );
static void	bidHdrStore( VOID *, char *, long, VOID * );
static Sessn	bidNewSessn( Protl, Protl, Protl, ActiveKey * );
static Sessn	bidOpen( Protl, Protl, Protl, Part * );
static XkReturn	bidOpenDisable( Protl, Protl, Protl, Part * );
static XkReturn	bidOpenDisableAll( Protl, Protl );
static XkReturn	bidOpenEnable( Protl, Protl, Protl, Part * );
static XkReturn	bidPop( Sessn, Sessn, Msg *, VOID * );
static XkHandle	bidPush( Sessn, Msg * );
static int	bootidChanged( VOID *, VOID *, VOID * );
static void	getProtlFuncs( Protl );
static void	getSessnFuncs( Sessn );

#else

static void	activateSessn();
static int	bidControlProtl();
static int	bidControlSessn();
static Sessn	bidCreateSessn();
static XkReturn	bidDemux();
static void	bidHdrDump();
static long	bidHdrLoad();
static void	bidHdrStore();
static Sessn	bidNewSessn();
static Sessn	bidOpen();
static XkReturn	bidOpenDisable();
static XkReturn	bidOpenEnable();
static XkReturn	bidPop();
static XkHandle	bidPush();
static int	bootidChanged();
static void	getProtlFuncs();
static void	getSessnFuncs();

#endif /* __STDC__ */


int	tracebidp;


void
bid_init( self )
    Protl	self;
{
    Part	p;
    PState	*ps = (PState *)self->state;

    xTrace0(bidp, TR_GROSS_EVENTS, "bid Init");
    ps = X_NEW(PState);
    self->state = (VOID *)ps;
    if (!xIsProtl(xGetProtlDown(self, BID_XPORT_I))) {
	xTrace0(bidp, TR_ERRORS,
		"bid could not get transport protocol -- aborting init");
	return;
    }
    if (!xIsProtl(xGetProtlDown(self, BID_CTL_I))) {
	xTrace0(bidp, TR_ERRORS,
		"bid could not get control protocol -- aborting init");
	return;
    }
    if ( xControlProtl(xGetProtlDown(self, BID_CTL_I), BIDCTL_GET_LOCAL_BID,
		  (char *)&ps->myBid, sizeof(BootId)) < (int)sizeof(BootId) ) {
	xTrace0(bidp, TR_ERRORS, "bid could not get my bid -- aborting init");
	return;
    }
    xTrace1(bidp, TR_GROSS_EVENTS, "bid using id == %x", ps->myBid);
    ps->activeMap = mapCreate(BID_ACTIVE_MAP_SIZE, sizeof(ActiveKey));
    ps->passiveMap = mapCreate(BID_PASSIVE_MAP_SIZE, sizeof(PassiveKey));
    semInit(&ps->sessnCreationSem, 1);
    getProtlFuncs(self);
    partInit(&p, 1);
    partPush(p, ANY_HOST, 0);
    if ( xOpenEnable(self, self, xGetProtlDown(self, BID_XPORT_I), &p)
							== XK_FAILURE ) {
	xTrace0(bidp, TR_ERRORS, "openEnable failed in bid init");
    }
}


static Sessn
bidOpen( self, hlp, hlpType, p )
    Protl	self, hlp, hlpType;
    Part	*p;
{
    Sessn	s;
    PState	*ps = (PState *)self->state;
    ActiveKey	key;

    xTrace0(bidp, TR_MAJOR_EVENTS, "bidOpen");
    bzero((char*)&key, sizeof(key));
    if ( (key.lls = xOpen(self, self, xGetProtlDown(self, 0), p)) == ERR_SESSN ) {
	xTrace0(bidp, TR_MAJOR_EVENTS, "bid could not open lls");
	return ERR_SESSN;
    }
    if ( (key.hlpNum = relProtNum(hlpType, self)) == -1 ) {
	xTrace0(bidp, TR_ERRORS, "bid open couldn't get hlpNum");
	xClose(key.lls);
	return ERR_SESSN;
    }
    if (mapResolve(ps->activeMap, &key, (void**)&s) == XK_SUCCESS) {
	xTrace0(bidp, TR_MAJOR_EVENTS, "bid open found existing sessn");
	xClose(key.lls);
	return s;
    }
    s = bidCreateSessn(self, hlp, hlpType, &key);
    if ( s == ERR_SESSN ) {
	xClose(key.lls);
	return ERR_SESSN;
    }
    xTrace1(bidp, TR_DETAILED, "bidOpen returning %x", s);
    return s;
}


static long
bidHdrLoad( hdr, src, len, arg )
    VOID	*hdr, *arg;
    char 	*src;
    long	len;
{
    xAssert( len == sizeof(BidHdr) );
    bcopy(src, hdr, len);
    ((BidHdr *)hdr)->hlpNum = ntohl(((BidHdr *)hdr)->hlpNum);
    return len;
}


static void
bidHdrStore( hdr, dst, len, arg )
    VOID	*hdr, *arg;
    char 	*dst;
    long	len;
{
    BidHdr	h;

    xAssert( len == sizeof(BidHdr) );
    h.hlpNum = htonl(((BidHdr *)hdr)->hlpNum);
    h.srcBid = ((BidHdr *)hdr)->srcBid;
    h.dstBid = ((BidHdr *)hdr)->dstBid;
    bcopy((char *)&h, dst, len);
}


static Sessn
bidCreateSessn( self, hlp, hlpType, key )
    Protl	self, hlp, hlpType;
    ActiveKey	*key;
{
    PState	*ps = (PState *)self->state;
    Sessn	s;

    /* 
     * We might block (but only briefly), so we'll only allow one
     * thread to create a session at a time to avoid map binding
     * conflicts. 
     */
    semWait(&ps->sessnCreationSem);
    /* 
     * Check again now that we have the lock
     */
    if (mapResolve(ps->activeMap, key, (void**)&s) == XK_FAILURE)
	s = bidNewSessn(self, hlp, hlpType, key);
    semSignal(&ps->sessnCreationSem);
    return s;
}


static Sessn
bidNewSessn( self, hlp, hlpType, key )
    Protl	self, hlp, hlpType;
    ActiveKey	*key;
{
    Sessn	s;
    Protl       llpCtl;
    PState	*ps = (PState *)self->state;
    SState	*ss;
    Part	part;
    IPhost	peer;
    
    if ( xControlSessn(key->lls, GETPEERHOST, (char *)&peer, sizeof(IPhost))
		< (int)sizeof(IPhost) ) {
	return ERR_SESSN;
    }
    s = xCreateSessn(getSessnFuncs, hlp, hlpType, self, 1, &key->lls);
    if ( s == ERR_SESSN ) {
	return ERR_SESSN;
    }
    llpCtl = xGetProtlDown(self, BID_CTL_I);
    partInit(&part, 1);
    partPush(part, &peer, sizeof(peer));
    ss = X_NEW(SState);
    ss->hdr.hlpNum = key->hlpNum;
    ss->hdr.srcBid = ps->myBid;
    ss->peer = peer;
    s->state = (VOID *)ss;
    /* 
     * Register this protocol's interest in this host with the control protocol
     */
    if ( xOpenEnable(self, self, llpCtl, &part) == XK_SUCCESS ) {
	BidctlBootMsg	msg;
	
	msg.h = peer;
	msg.id = 0;
	if ( xControlProtl(llpCtl, BIDCTL_GET_PEER_BID, (char *)&msg,
		      sizeof(msg)) == (int)sizeof(msg) ) {
	    ss->hdr.dstBid = msg.id;
	    s->binding = mapBind(ps->activeMap, key, s);
	    if ( msg.id != 0 ) {
		activateSessn(s);
	    }
	    xAssert( s->binding != ERR_BIND );
	    return s;
	} else {
	    xTrace1(bidp, TR_ERRORS,
		    "bid CreateSessn: couldn't get peer BID for %s",
		    ipHostStr(&peer));
	}
	partInit(&part, 1);
	partPush(part, &peer, sizeof(peer));
	xOpenDisable(self, self, llpCtl, &part);
    } else {
	xTrace0(bidp, TR_ERRORS, "bidCreateSessn couldn't openEnable BIDCTL");
    }
    xFree((char *)ss);
    xDestroySessn(s);
    return ERR_SESSN;
}


static XkReturn
bidClose( self )
    Sessn	self;
{
    Protl	myProtl = xMyProtl(self);
    PState	*ps = (PState *)myProtl->state;
    SState	*ss = (SState *)self->state;
    Sessn	lls;
    Part	part;

    xTrace0(bidp, TR_MAJOR_EVENTS, "bid Close");
    lls = xGetSessnDown(self, 0);

    if ( mapRemoveBinding(ps->activeMap, self->binding) == XK_FAILURE ) {
	xAssert(0);
	return XK_FAILURE;
    }
    partInit(&part, 1);
    partPush(part, &ss->peer, sizeof(ss->peer));
    if ( xOpenDisable(myProtl, myProtl, xGetProtlDown(myProtl, BID_CTL_I), &part)
				== XK_FAILURE ) {
	xTrace0(bidp, TR_ERRORS, "bidClose couldn't openDisable cntrl protl");
    }
    xAssert(xIsSessn(lls));
    xClose(lls);
    xDestroySessn(self);
    return XK_SUCCESS;
}


static XkReturn
bidDemux( self, lls, m )
    Protl	self;
    Sessn	lls;
    Msg		*m;
{
    PState	*ps = (PState *)self->state;
    BidHdr	hdr;
    Sessn	s;
    Enable	*e;
    ActiveKey	key;
    PassiveKey	passive_key;
    void	*buf;

    xTrace0(bidp, TR_EVENTS, "bid demux");
    buf = msgPop(m, sizeof(hdr));
    if (!buf) {
	xError("bidDemux: msgPop failed");
	return XK_FAILURE;
    }
    bidHdrLoad(&hdr, buf, sizeof(hdr), 0);
    xIfTrace(bidp, TR_DETAILED) {
	bidHdrDump(&hdr, "INCOMING");
    }
    bzero((char*)&key, sizeof(key));
    key.lls = lls;
    key.hlpNum = hdr.hlpNum;
    if (mapResolve(ps->activeMap, &key, (void**)&s) == XK_FAILURE) {
	passive_key = hdr.hlpNum;
	if (mapResolve(ps->passiveMap, &passive_key, (void**)&e) == XK_FAILURE){
	    xTrace1(bidp, TR_SOFT_ERRORS,
		    "bid demux -- no protl for hlpNum %d ", hdr.hlpNum);
	    return XK_FAILURE;
	} else {
	    xTrace1(bidp, TR_DETAILED,
		    "bid demux -- found enable for hlpNum %d ", hdr.hlpNum);
	}
	s = bidCreateSessn(self, e->hlp, e->hlpType, &key);
	if ( s == ERR_SESSN ) {
	    return XK_FAILURE;
	}
	xDuplicate(lls);
	xOpenDone(e->hlp, self, s);
    } else {
	xAssert(s->rcnt > 0);
    }
    return xPop(s, lls, m, &hdr);
}


static XkReturn
bidOpenEnable( self, hlp, hlpType, p )
    Protl	self, hlp, hlpType;
    Part	*p;
{
    PState	*ps = (PState *)self->state;
    long	hlpNum;

    hlpNum = relProtNum(hlpType, self);
    xTrace1(bidp, TR_MAJOR_EVENTS, "bid openEnable binding key %d",
	    hlpNum);
    return defaultOpenEnable(ps->passiveMap, hlp, hlpType, &hlpNum);
}


static XkReturn
bidOpenDisable( self, hlp, hlpType, p )
    Protl	self, hlp, hlpType;
    Part	*p;
{
    PState	*ps = (PState *)self->state;
    long	hlpNum;

    xTrace0(bidp, TR_MAJOR_EVENTS, "bid openDisable");
    hlpNum = relProtNum(hlpType, self);
    return defaultOpenDisable(ps->passiveMap, hlp, hlpType, &hlpNum);
}


static XkReturn
bidOpenDisableAll( self, hlp )
    Protl	self, hlp;
{
    PState	*ps = (PState *)self->state;

    xTrace0(bidp, TR_MAJOR_EVENTS, "bid openDisableAll");
    return defaultOpenDisableAll(ps->passiveMap, hlp, 0);
}

static XkReturn
bidPop( self, lls, m, inHdr )
    Sessn	self, lls;
    Msg		*m;
    VOID	*inHdr;
{
    SState	*ss = (SState *)self->state;
    BidHdr	*hdr = (BidHdr *)inHdr;
    
    xAssert(hdr);
    if ( hdr->dstBid != ss->hdr.srcBid ) {
	Msg	nullMsg;

	xTrace2(bidp, TR_MAJOR_EVENTS,
		"bidPop: mismatch of my bid (%x (hdr) vs. %x (state))",
		hdr->dstBid, ss->hdr.srcBid);
	/* 
	 * Send a null message to my peer so it can ask it's control
	 * protocol to straighten it out.
	 */
	msgConstructEmpty(&nullMsg);
	bidPush(self, &nullMsg);
	msgDestroy(&nullMsg);
	return XK_FAILURE;
    }
    if ( hdr->srcBid != ss->hdr.dstBid ) {
	Protl		llpCtl = xGetProtlDown(xMyProtl(self), BID_CTL_I);
	BidctlBootMsg	msg;

	xTrace2(bidp, TR_MAJOR_EVENTS,
		"bidPop: mismatch of peer bid (%x (hdr) vs. %x (state))",
		hdr->srcBid, ss->hdr.srcBid);
	msg.h = ss->peer;
	msg.id = hdr->srcBid;
	xControlProtl(llpCtl, BIDCTL_GET_PEER_BID, (char *)&msg, sizeof(msg));
	return XK_FAILURE;
    }
    return xDemux(xGetUp(self), self, m);
}

static int
bidControlProtl( self, op, buf, len )
    Protl	self;
    int		op, len;
    char 	*buf;
{
    PState	*ps = (PState *)self->state;

    switch ( op ) {

      case BIDCTL_FIRST_CONTACT:
      case BIDCTL_PEER_REBOOTED:
	mapForEach(ps->activeMap, bootidChanged, (BidctlBootMsg *)buf);
	return 0;

      case GETMAXPACKET:
      case GETOPTPACKET:
	if ( xControlProtl(xGetProtlDown(self, 0), op, buf, len) < (int)sizeof(int) ) {
	    return -1;
	}
	*(int *)buf -= sizeof(BidHdr);
	return (sizeof(int));

      default:
	return xControlProtl(xGetProtlDown(self, 0), op, buf, len);
    }
}


static int
bootidChanged( key, val, arg )
    VOID	*key, *val, *arg;
{
    Sessn		s = (Sessn)val;
    BidctlBootMsg	*m = (BidctlBootMsg *)arg;
    SState		*ss;

    xAssert(xIsSessn(s));
    xAssert(arg);
    ss = (SState *)s->state;
    if ( ! IP_EQUAL(ss->peer, m->h) ) {
	xTrace3(bidp, TR_EVENTS,
		"bid sessn %x (host %s) ignoring reboot message for %s",
		s, ipHostStr(&ss->peer), ipHostStr(&m->h));
	return 1;
    }
    activateSessn(s);
    xTrace3(bidp, TR_MAJOR_EVENTS, "bid session %x resetting BID of %s to %x",
	    s, ipHostStr(&m->h), m->id);
    ss->hdr.dstBid = m->id;
    return MFE_CONTINUE;
}


static int
bidControlSessn( self, op, buf, len )
    Sessn	self;
    int		op, len;
    char 	*buf;
{
    switch ( op ) {

      case GETMAXPACKET:
      case GETOPTPACKET:
	if ( xControlSessn(xGetSessnDown(self, 0), op, buf, len) < (int)sizeof(int) ) {
	    return -1;
	}
	*(int *)buf -= sizeof(BidHdr);
	return (sizeof(int));

      default:
	return xControlSessn(xGetSessnDown(self, 0), op, buf, len);
    }
}


static XkHandle
bidPush( self, m )
    Sessn	self;
    Msg		*m;
{
    SState	*ss = (SState *)self->state;
    void	*buf;

    xTrace0(bidp, TR_EVENTS, "bidPush");
    xIfTrace(bidp, TR_DETAILED) {
	bidHdrDump(&ss->hdr, "OUTGOING");
    }
    buf = msgPush(m, sizeof(BidHdr));
    xAssert(buf);
    bidHdrStore(&ss->hdr, buf, sizeof(BidHdr), 0);
    return xPush(xGetSessnDown(self, 0), m);
}


static void
bidHdrDump( h, str )
    BidHdr	*h;
    char	*str;
{
    xTrace1(bidp, TR_ALWAYS, "Bid %s header:", str);
    xTrace3(bidp, TR_ALWAYS,
	    "srcBid: %.8x  dstBid: %.8x  hlpNum: %d",
	    h->srcBid, h->dstBid, h->hlpNum);
}


static void
getProtlFuncs( p )
    Protl	p;
{
    p->open = bidOpen;
    p->controlprotl = bidControlProtl;
    p->demux = bidDemux;
    p->openenable = bidOpenEnable;
    p->opendisable = bidOpenDisable;
    p->opendisableall = bidOpenDisableAll;
}


static void
activateSessn( s )
    Sessn	s;
{
    s->push = bidPush;
    s->pop = bidPop;
}


static void
getSessnFuncs( s )
    Sessn	s;
{
    s->controlsessn = bidControlSessn;
    s->close = bidClose;
}


