/*
 * $RCSfile: upi.c,v $
 *
 * x-kernel v3.3
 *
 * Copyright (c) 1993,1991,1990,1996  Arizona Board of Regents
 *
 * $Log: upi.c,v $
 * Revision 1.3  1996/05/15 20:30:02  rrp
 * replaced missing '!' on line 336.
 *
 * Revision 1.2  1996/01/29  19:57:03  slm
 * Changes for x33 release and updated copyright and version.
 *
 * Revision 1.1  1995/07/28  21:41:44  slm
 * Initial revision
 *
 * Revision 1.65.1.2  1994/12/02  17:56:17  hkaram
 * Changed to new mapResolve
 *
 * Revision 1.65.1.1  1994/10/27  20:52:17  hkaram
 * New branch
 *
 * Revision 1.65  1994/08/10  05:35:32  davidm
 * (xGetDown): now an inlinable function defined in upi_inline.h.
 *
 * Revision 1.64  1994/03/13  03:32:58  davidm
 * Pointers are now printed as "%lx" and cast to (u_long).
 *
 * Revision 1.63  1994/02/05  00:08:03  menze
 *   [ 1994/01/30          menze ]
 *   assert.h renamed to xk_assert.h
 *   No longer includes varargs.h directly
 *
 * Revision 1.62  1994/01/25  03:19:29  davidm
 * Removed #include "process.h" (it's included via platform.h already).
 *
 * Revision 1.61  1993/12/13  20:24:19  menze
 * Modifications from UMass:
 *
 *   [ 93/12/13          menze ]
 *   Previous trace deadlock modifications weren't getting reply lengths
 *   right.
 *
 *   [ 93/06/21          nahum ]
 *   Fixed trace deadlock by removing msgLen() calls from within xTrace macros.
 *
 *   [ 93/06/09          nahum ]
 *   Fixed some IRIX build bugs.
 */

#include "upi.h"
#include "xk_debug.h"
#include "xk_assert.h"
#include "prottbl.h"
#include "platform.h"
#include "event.h"

#define UPI_PROTL_MAP_SZ   64
#define UPI_SAFEOBJ_MAP_SZ 512

Protl      xNullProtl;
static Map protlMap;
static Map safeProtlMap;
static Map safeSessnMap;

#ifdef XK_DEBUG
static char *controlops[] = {
    "getmyhost",
    "getmyhostcount",
    "getpeerhost",
    "getpeerhostcount",
    "getbcasthost",
    "getmaxpacket",
    "getoptpacket",
    "getmyproto",
    "getpeerproto",
    "resolve",
    "rresolve",
    "freeresources",
    "getparticipants",
    "setnonblockingio"
};

#define CONTROLMSG(n) \
    ((n) < sizeof controlops / sizeof(char *) ? controlops[n] : "non-standard")
#else
#define CONTROLMSG(n) ""
#endif /* XK_DEBUG */

#ifdef __STDC__

static XkReturn defaultDuplicate(Sessn);
static Sessn    openFailure(void);
static XkReturn returnFailure(void);
static int      controlFailure(void);
static Part     *getParticipantsFailure(void);
static int      noop(void);
static int      findProtl(void *, void *, void *);

#else

static XkReturn defaultDuplicate();
static Sessn    openFailure();
static XkReturn returnFailure();
static int      controlFailure();
static Part     *getParticipantsFailure();
static int      noop();
static int      findProtl();

#endif /* __STDC__ */

/*
 * If inline functions are not being used, the upi_inline functions will be
 * instantiated here.
 */

#define UPI_INLINE_INSTANTIATE
#include "upi_inline.h"

/*****************************************
 * Uniform Protocol Interface Operations *
 *****************************************/

Sessn
xOpen(hlp, hlpType, llp, participants)
Protl hlp, hlpType, llp;
Part  *participants;
{
    Sessn session;

    xAssert(xIsProtl(hlp));
    xAssert(xIsProtl(hlpType));
    xAssert(xIsProtl(llp));
    xTrace3(protocol, TR_MAJOR_EVENTS, "Calling open[%s] by (%s,%s)",
	    llp->fullName, hlp->fullName, hlpType->fullName);
    session = (*llp->open)(llp, hlp, hlpType, participants);
    if (xIsSessn(session))
	session->rcnt++;
    xTrace5(protocol, TR_MAJOR_EVENTS,
	    "Open[%s] by (%s,%s) returns %lx (rcnt == %d)",
	    llp->fullName, hlp->fullName, hlpType->fullName, (u_long)session,
	    xIsSessn(session) ? session->rcnt : 0);
    return session;
}

XkReturn
xOpenEnable(hlp, hlpType, llp, participants)
Protl hlp, hlpType, llp;
Part  *participants;
{
    xAssert(xIsProtl(hlp));
    xAssert(xIsProtl(hlpType));
    xAssert(xIsProtl(llp));
    xTrace3(protocol, TR_MAJOR_EVENTS, "Calling openenable[%s] by (%s,%s)",
	    llp->fullName, hlp->fullName, hlpType->fullName);
    return (*llp->openenable)(llp, hlp, hlpType, participants);
}

XkReturn
xOpenDone(hlp, llp, session)
Protl hlp, llp;
Sessn session;
{
    xAssert(xIsProtl(hlp));
    xAssert(xIsProtl(llp));
    xAssert(xIsSessn(session));
    xIfTrace(protocol, TR_MAJOR_EVENTS) {
	if (session->up != hlp) {
	    xTrace4(protocol, TR_MAJOR_EVENTS,
		    "hlp of session %lx(%s) becomes %lx(%s)",
		    (u_long)session, session->myprotl->fullName, (u_long)hlp,
		    hlp->fullName);
	  }
    }
    session->up = hlp;
    if (!hlp->opendone)
	return XK_SUCCESS;
    xTrace2(protocol, TR_MAJOR_EVENTS, "Calling opendone[%s] by %s",
	    hlp->fullName, llp->fullName);
    xTrace4(protocol, TR_FUNCTIONAL_TRACE,
            "hlp == %lx, lls == %lx, llp == %lx, hlpType == %lx",
	    (u_long)hlp, (u_long)session, (u_long)llp,
	    (u_long)session->hlpType);
    return (*hlp->opendone)(hlp, llp, session, session->hlpType);
}

XkReturn
xCloseDone(session)
Sessn session;
{
    xAssert(xIsSessn(session));
    if (!session->up->closedone)
	return XK_SUCCESS;
    xTrace2(protocol, TR_MAJOR_EVENTS, "Calling closedone[%s] by %s",
	    session->up->fullName, session->myprotl->fullName);
    return (*session->up->closedone)(session);
}

XkReturn
xOpenDisable(hlp, hlpType, llp, participants)
Protl hlp, hlpType, llp;
Part  *participants;
{
    xAssert(xIsProtl(hlp));
    xAssert(xIsProtl(hlpType));
    xAssert(xIsProtl(llp));
    xTrace3(protocol, TR_MAJOR_EVENTS, "Calling opendisable[%s] by (%s,%s)",
	    llp->fullName, hlp->fullName, hlpType->fullName);
    return (*llp->opendisable)(llp, hlp, hlpType, participants);
}

XkReturn
xOpenDisableAll(hlp, llp)
Protl hlp, llp;
{
    xAssert(xIsProtl(hlp));
    xAssert(xIsProtl(llp));
    xTrace2(protocol, TR_MAJOR_EVENTS, "Calling openDisableAll[%s] by (%s)",
	    llp->fullName, hlp->fullName);
    return (*llp->opendisableall)(llp, hlp);
}

XkReturn
xClose(session)
Sessn session;
{
  xTrace0(protocol, TR_EVENTS, "xClose: entered");
  /*
   * DEC_REF_COUNT_UNCOND comes from upi_inline.h
   */
  DEC_REF_COUNT_UNCOND(session, "xClose");
  xTrace0(protocol, TR_FULL_TRACE, "xClose returning");
  return XK_SUCCESS;
}

XkReturn
xDuplicate(session)
Sessn session;
{
  xTrace1(protocol, TR_EVENTS, "calling duplicate[%s]",
	  session->myprotl->fullName);
  return (*session->duplicate)(session);
}

/* default routines */

static XkReturn
defaultDuplicate(session)
Sessn session;
{
    session->rcnt++;
    return XK_SUCCESS;
}

static Sessn
openFailure()
{
    xTrace0(protocol, TR_SOFT_ERRORS, "default UPI operation was invoked");
    return ERR_SESSN;
}

static XkReturn
returnFailure()
{
    xTrace0(protocol, TR_SOFT_ERRORS, "default UPI operation was invoked");
    return XK_FAILURE;
}

static int
controlFailure()
{
    xTrace0(protocol, TR_SOFT_ERRORS, "default UPI operation was invoked");
    return -1;
}

static Part *
getParticipantsFailure()
{
    xTrace0(protocol, TR_SOFT_ERRORS, "default UPI operation was invoked");
    return NULL;
}

static int
noop()
{
    return 0;
}

/*
 * xDemux, xCallDemux, xPush and xCall are defined as macros in upi.h when
 * optimized
 */

#if defined(XK_DEBUG) && ! defined(XK_UPI_MACROS)

XkReturn
xDemux(hlp, lls, message)
Protl hlp;
Sessn lls;
Msg   *message;
{
    int len;

    xAssert(xIsProtl(hlp));
    xAssert(xIsSessn(lls));
    xAssert(xIsProtl(lls->up));
    xIfTrace(protocol, TR_EVENTS) { len = msgLength(message); }
    xTrace4(protocol, TR_EVENTS, "Calling demux[%s(%lx)] by %s, %d bytes",
	    hlp->fullName, (u_long)hlp, lls->myprotl->fullName, len);
    xIfTrace(protocol, TR_FUNCTIONAL_TRACE) {
        xTrace0(protocol, TR_ALWAYS, "       Message:");
        msgShow(message);
    }
    if (!hlp->demux)
        return XK_SUCCESS;
    return (*hlp->demux)(hlp, lls, message);
}

XkReturn
xCallDemux(hlp, lls, request, reply)
Protl hlp;
Sessn lls;
Msg   *request, *reply;
{
    XkReturn retVal;
    int      len;

    xAssert(xIsProtl(hlp));
    xAssert(xIsSessn(lls));
    xAssert(xIsProtl(lls->up));
    xIfTrace(protocol, TR_EVENTS) { len = msgLength(request); }
    xTrace3(protocol, TR_EVENTS, "Calling calldemux[%s] by %s, %d bytes",
            hlp->fullName, lls->myprotl->fullName, len);
    xIfTrace(protocol, TR_FUNCTIONAL_TRACE) {
        xTrace0(protocol, TR_ALWAYS, "       Message:");
        msgShow(request);
    }
    if (!hlp->calldemux)
        return XK_SUCCESS;
    retVal = (*hlp->calldemux)(hlp, lls, request, reply);
    xIfTrace(protocol, TR_EVENTS) { len = msgLength(reply); }
    xTrace2(protocol, TR_EVENTS, "calldemux[%s] returns %d bytes",
	    hlp->fullName, len);
    return retVal;
}

XkHandle
xPush(lls, message)
Sessn lls;
Msg   *message;
{
    int retVal, len;

    EV_CHECK_STACK("xPush");
    xAssert(xIsSessn(lls));
    xIfTrace(protocol, TR_EVENTS) { len = msgLength(message); }
    xTrace3(protocol, TR_EVENTS, "Calling push[%s] by %s, %d bytes",
            lls->myprotl->fullName, lls->up->fullName, len);
    xIfTrace(protocol, TR_FUNCTIONAL_TRACE) {
	xTrace0(protocol, TR_ALWAYS, "       Message:");
	msgShow(message);
    }
    retVal = (*lls->push)(lls, message);
    xTrace3(protocol, TR_EVENTS, "push[%s] by %s returns %d",
            lls->myprotl->fullName, lls->up->fullName, retVal);
    return retVal;
}

XkReturn
xCall(lls, request, reply)
Sessn lls;
Msg   *request, *reply;
{
    XkReturn retVal;
    int len;

    xAssert(xIsSessn(lls));
    xIfTrace(protocol, TR_EVENTS) { len = msgLength(request); }
    xTrace3(protocol, TR_EVENTS, "Calling call[%s] by %s, %d bytes",
            lls->myprotl->fullName, lls->up->fullName, len);
    xIfTrace(protocol, TR_FUNCTIONAL_TRACE) {
	xTrace0(protocol, TR_ALWAYS, "       Message:");
	msgShow(request);
    }
    retVal = (*lls->call)(lls, request, reply);
    xIfTrace(protocol, TR_EVENTS) { len = msgLength(reply); }
    xTrace3(protocol, TR_EVENTS, "call[%s] returns %d bytes in reply to %s",
	    lls->myprotl->fullName, len, lls->up->fullName);
    return retVal;
}

#endif /* defined(XK_DEBUG) && ! defined(XK_UPI_MACROS) */

int
xControlProtl(protocol, opcode, buffer, length)
Protl protocol;
int   opcode;
char  *buffer;
int   length;
{
    int res;

    if (!protocol->controlprotl)
        return 0;
    xTrace3(protocol, TR_EVENTS, "Calling controlprotl[%s] op %s (%d)",
	    protocol->fullName, CONTROLMSG(opcode), opcode);
    res = (protocol->controlprotl)(protocol, opcode, buffer, length);
    xTrace4(protocol, TR_EVENTS, "controlprotl[%s] op %s (%d) returns %d",
	    protocol->fullName, CONTROLMSG(opcode), opcode, res);
    return res;
}

int
xControlSessn(session, opcode, buffer, length)
Sessn session;
int   opcode;
char  *buffer;
int   length;
{
    int res;

    if (!session->controlsessn)
        return 0;
    xTrace3(protocol, TR_EVENTS, "Calling controlsessn[%s] op %s (%d)",
	    session->myprotl->fullName, CONTROLMSG(opcode), opcode);
    res = (session->controlsessn)(session, opcode, buffer, length);
    xTrace4(protocol, TR_EVENTS, "controlsessn[%s] op %s (%d) returns %d",
	    session->myprotl->fullName, CONTROLMSG(opcode), opcode, res);
    return res;
}

Part *
xGetParticipants(session)
Sessn session;
{
    xAssert(xIsSessn(session));
    if (!session->getparticipants)
        return NULL;
    xTrace1(protocol, TR_EVENTS, "Calling getparticipants[%s]",
            session->myprotl->fullName);
    return (*session->getparticipants)(session);
}

/*********************************************
 * Create and Destroy Protocols and Sessions *
 *********************************************/

Protl
xCreateProtl(func, name, instName, traceVar, downc, downv)
ProtlInitFunc func;
char          *name, *instName;
int           *traceVar;
int           downc;
Protl         *downv;
{
    Protl   protl, *dv;
    int     id, i;
    Binding binding;

    xTrace1(protocol, TR_MAJOR_EVENTS, "xCreateProtocol:[%s]", name);
    if ((id = protTblGetId(name)) == -1) {
        xTrace1(protocol, TR_ERRORS,
		"xCreateProtl could not find protocol id for %s", name);
        return ERR_PROTL;
    }

    protl = (Protl)xMalloc(sizeof(struct protl));
    if (!protl) {
        xTrace0(protocol, TR_ERRORS, "xCreateProtl malloc failure(1)");
        return ERR_PROTL;
    }
    bzero((char *)protl, sizeof(struct protl));
    protl->numdown = downc;
    if (downc > STD_DOWN) {
        protl->downlistsz = (((downc - STD_DOWN) / STD_DOWN) + 1) * STD_DOWN;
        protl->downlist   =
	    (Protl *)xMalloc(protl->downlistsz * sizeof(Protl));
        if (!protl->downlist) {
            xFree((char *)protl);
            xTrace0(protocol, TR_ERRORS, "xCreateProtl malloc failure(2)");
            return ERR_PROTL;
        }
    }
    else
        protl->downlistsz = 0;
    dv = protl->down;
    for (i = 0; i < downc; i++, dv++) {
        if (i == STD_DOWN)
            dv = protl->downlist;
        *dv = downv[i];
    }
    protl->name           = xMalloc(strlen(name) + 1);
    strcpy(protl->name, name);
    protl->instName       = xMalloc(strlen(instName) + 1);
    strcpy(protl->instName, instName);
    protl->fullName       = xMalloc(strlen(name) + strlen(instName) + 2);
    sprintf(protl->fullName, "%s%c%s",
	    name, (*instName ? '/' : '\0'), instName);
    protl->id             = id;
    protl->traceVar       = traceVar;
    protl->open           = (XOpenFunc)openFailure;
    protl->openenable     = (XOpenEnableFunc)returnFailure;
    protl->opendisable    = (XOpenDisableFunc)returnFailure;
    protl->opendisableall = (XOpenDisableAllFunc)noop;
    protl->opendone       = (XOpenDoneFunc)noop;
    protl->closedone      = (XCloseDoneFunc)noop;
    protl->demux          = (XDemuxFunc)returnFailure;
    protl->calldemux      = (XCallDemuxFunc)returnFailure;
    protl->controlprotl   = (XControlProtlFunc)controlFailure;
    /* the following SHOULD only be for sessions */
    protl->close          = (XCloseFunc)returnFailure;
    protl->pop            = (XPopFunc)returnFailure;
    protl->callpop        = (XCallPopFunc)returnFailure;
    protl->push           = (XPushFunc)returnFailure;
    protl->call           = (XCallFunc)returnFailure;
    protl->duplicate      = (XDuplicateFunc)defaultDuplicate;
    protl->myprotl        = protl;
    protl->up             = ERR_PROTL;	/* only relevant to sessions */

    binding = mapBind(safeProtlMap, &protl, protl);
    xAssert(binding != ERR_BIND);

    binding = mapBind(protlMap, &protl, protl);
    xAssert(binding != ERR_BIND);

    if (func)
        (*func)(protl);
    return protl;
}

Sessn
xCreateSessn(func, hlp, hlpType, llp, downc, downv)
SessnInitFunc func;
Protl         hlp, hlpType, llp;
int           downc;
Sessn         *downv;
{
    Sessn   sessn, *dv;
    int     i;
    Binding binding;

    xTrace3(protocol, TR_MAJOR_EVENTS, "xCreateSession:[%s] by [%s,%s]",
	    llp->fullName, hlp->fullName, hlpType->fullName);

    sessn = (Sessn)xMalloc(sizeof(struct sessn));
    if (!sessn) {
        xTrace0(protocol, TR_ERRORS, "xCreateSessn malloc failure(1)");
        return ERR_SESSN;
    }
    bzero((char *)sessn, sizeof(struct sessn));
    sessn->numdown = downc;
    if (downc > STD_DOWN) {
        sessn->downlistsz = (((downc - STD_DOWN) / STD_DOWN) + 1) * STD_DOWN;
        sessn->downlist   =
	    (Sessn *)xMalloc(sessn->downlistsz * sizeof(Sessn));
        if (!sessn->downlist) {
            xFree((char *)sessn);
            xTrace0(protocol, TR_ERRORS, "xCreateSessn malloc failure(2)");
            return ERR_SESSN;
        }
    }
    else
        sessn->downlistsz = 0;
    dv = sessn->down;
    for (i = 0; i < downc; i++, dv++) {
        if (i == STD_DOWN)
            dv = sessn->downlist;
        *dv = downv[i];
    }
    sessn->rcnt            = 0;
    sessn->idle            = FALSE;
    sessn->close           = (XCloseFunc)returnFailure;
    sessn->pop             = (XPopFunc)returnFailure;
    sessn->callpop         = (XCallPopFunc)returnFailure;
    sessn->push            = (XPushFunc)returnFailure;
    sessn->call            = (XCallFunc)returnFailure;
    sessn->duplicate       = (XDuplicateFunc)defaultDuplicate;
    sessn->controlsessn    = (XControlSessnFunc)controlFailure;
    sessn->getparticipants = (XGetParticipantsFunc)getParticipantsFailure;
    sessn->myprotl         = llp;
    sessn->up              = hlp;
    sessn->hlpType         = hlpType;
    /* the following SHOULD only be for protocols */
    /* sessn->fullName        = llp->fullName;	/* needed for xTraceP's */
    /* sessn->traceVar        = llp->traceVar;	/* needed for xTraceP's */
    sessn->id              = 0;
    sessn->open            = (XOpenFunc)openFailure;
    sessn->openenable      = (XOpenEnableFunc)returnFailure;
    sessn->opendisable     = (XOpenDisableFunc)returnFailure;
    sessn->opendisableall  = (XOpenDisableAllFunc)returnFailure;
    sessn->opendone        = (XOpenDoneFunc)returnFailure;
    sessn->closedone       = (XCloseDoneFunc)returnFailure;
    sessn->demux           = (XDemuxFunc)returnFailure;
    sessn->calldemux       = (XCallDemuxFunc)returnFailure;

    binding = mapBind(safeSessnMap, &sessn, sessn);
    xAssert(binding != ERR_BIND);

    if (func)
	(*func)(sessn);
    xTrace1(protocol, TR_EVENTS, "xCreateSession returns %lx", (u_long)sessn);
    return sessn;
}

XkReturn
xDestroySessn(session)
Sessn session;
{
    XkReturn xkr;

    xTrace2(protocol, TR_EVENTS, "xDestroySessn[%s(%lx)]",
	    session->myprotl->fullName, (u_long)session);
    xkr = mapRemoveKey(safeSessnMap, &session);
    xAssert(xkr == XK_SUCCESS);
    if (session->state)
	xFree((char *)session->state);
    xFree((char *)session);
    return XK_SUCCESS;
}

/********************
 * Utility Routines *
 ********************/

typedef struct {
    char  *name;
    Protl protl;
} FindProtlArg;

/*
 * xGetProtlByName - return a protocol matching the indicated protocol class
 * (e.g., "tcp", "udp") rather than looking at the instance name at all.
 * If there are multiple instances a protocol will be returned at random.
 */
Protl
xGetProtlByName(name)
char *name;
{
    FindProtlArg arg;

    arg.name  = name;
    arg.protl = ERR_PROTL;
    mapForEach(protlMap, findProtl, &arg);
    return arg.protl;
}

static int
findProtl(key, val, arg)
void *key, *val, *arg;
{
    Protl        protl = (Protl)val;
    FindProtlArg *a    = (FindProtlArg *)arg;

    xAssert(xIsProtl(protl));
    if (!strcmp(protl->name, a->name)) {
	a->protl = protl;
	return 0;
    }
    else
	return MFE_CONTINUE;
}

XkReturn
xSetProtlDown(self, indx, protocol)
Protl self;
int   indx;
Protl protocol;
{
    Protl *newdl;
    int   newsz, n;

    if (indx < STD_DOWN)
	self->down[indx] = protocol;
    else {
	n = indx - STD_DOWN;
	if (n >= self->downlistsz) {
	    /* make newsz the smallest sufficient multiple of STD_DOWN */
	    newsz = ((n / STD_DOWN) + 1) * STD_DOWN;
	    newdl = (Protl *)xMalloc(newsz * sizeof(Protl));
	    if (self->downlist) {
		bcopy((char *)self->downlist, (char *)newdl,
		      self->downlistsz * sizeof(Protl));
		xFree((char *)self->downlist);
	    }
	    self->downlist   = newdl;
	    self->downlistsz = newsz;
	}
	self->downlist[n] = protocol;
    }
    if (indx + 1 > self->numdown)
	self->numdown = indx + 1;
    return XK_SUCCESS;
}

XkReturn
xSetSessnDown(self, indx, session)
Sessn self;
int   indx;
Sessn session;
{
    Sessn *newdl;
    int   newsz, n;

    if (indx < STD_DOWN)
	self->down[indx] = session;
    else {
	n = indx - STD_DOWN;
	if (n >= self->downlistsz) {
	    /* make newsz the smallest sufficient multiple of STD_DOWN */
	    newsz = ((n / STD_DOWN) + 1) * STD_DOWN;
	    newdl = (Sessn *)xMalloc(newsz * sizeof(Sessn));
	    if (self->downlist) {
		bcopy((char *)self->downlist, (char *)newdl,
		      self->downlistsz * sizeof(Sessn));
		xFree((char *)self->downlist);
	    }
	    self->downlist   = newdl;
	    self->downlistsz = newsz;
	}
	self->downlist[n] = session;
    }
    if (indx + 1 > self->numdown)
	self->numdown = indx + 1;
    return XK_SUCCESS;
}

/**************************
 * Miscellaneous Routines *
 **************************/

bool
xIsValidProtl(protocol)
Protl protocol;
{
    return (mapResolve(safeProtlMap, &protocol, NULL) == XK_SUCCESS);
}

bool
xIsValidSessn(session)
Sessn session;
{
    return (mapResolve(safeSessnMap, &session, NULL) == XK_SUCCESS);
}

void
xPrintProtl(protocol)
Protl protocol;
{
    int  i;

    if (!protocol) {
	xTrace0(protocol, TR_ALWAYS, "PROTL NULL");
	return;
    }
    xTrace0(protocol, TR_ALWAYS, "type = Protocol");
    xTrace4(protocol, TR_ALWAYS, "PROTL %lx %s %s %s",
	    (u_long)protocol, protocol->name, protocol->instName,
	    protocol->fullName);
    xTrace1(protocol, TR_ALWAYS, "id = %d", protocol->id);
    xTrace1(protocol, TR_ALWAYS, "numdown = %d ", protocol->numdown);
    for (i = 0; i < protocol->numdown; i++)
	xTrace2(protocol, TR_ALWAYS, "down[%d] = %lx",
		i, (u_long)xGetProtlDown(protocol, i));
}

void
xPrintSessn(session)
Sessn session;
{
    int  i;

    if (!session) {
	xTrace0(protocol, TR_ALWAYS, "SESSN NULL");
	return;
    }
    xTrace0(protocol, TR_ALWAYS, "type = Session");
    xTrace1(protocol, TR_ALWAYS, "SESSN %lx", (u_long)session);
    if (xIsProtl(session->myprotl))
        xTrace4(protocol, TR_ALWAYS, "myprotocol = %lx %s %s %s",
	        (u_long)session->myprotl, session->myprotl->name,
	        session->myprotl->instName, session->myprotl->fullName);
    if (xIsProtl(session->up))
        xTrace2(protocol, TR_ALWAYS, "up = %lx %s",
	        (u_long)session->up, session->up->fullName);
    if (xIsProtl(session->hlpType))
        xTrace2(protocol, TR_ALWAYS, "hlpType = %lx %s",
	        (u_long)session->hlpType, session->hlpType->fullName);
    xTrace1(protocol, TR_ALWAYS, "rcnt = %d", session->rcnt);
    xTrace1(protocol, TR_ALWAYS, "numdown = %d ", session->numdown);
    for (i = 0; i < session->numdown; i++)
	xTrace2(protocol, TR_ALWAYS, "down[%d] = %lx",
		i, (u_long)xGetSessnDown(session, i));
}

void
upiInit()
{
    xTrace0(init, TR_EVENTS, "upi init");
    protlMap = mapCreate(UPI_PROTL_MAP_SZ, sizeof(Protl));
    safeProtlMap = mapCreate(UPI_SAFEOBJ_MAP_SZ, sizeof(Protl));
    safeSessnMap = mapCreate(UPI_SAFEOBJ_MAP_SZ, sizeof(Sessn));
#ifdef XK_DEBUG
    xNullProtl = xCreateProtl((ProtlInitFunc)noop, "null", "", &traceprotocol,
			      0, NULL);
#else
    xNullProtl = xCreateProtl((ProtlInitFunc)noop, "null", "", NULL, 0, NULL);
#endif
}
