static char rcsid[] = "$Header: conn.c,v 820.1 86/12/04 19:43:19 root Exp $";
static char sccsid[]="%W% %Y% %Q% %G%";

/************************************************************************
*									*
*				Copyright 1984, 1985			*
*			VALID LOGIC SYSTEMS INCORPORATED		*
*									*
*	This listing contains confidential proprietary information	*
*	which is not to be disclosed to unauthorized persons without	*
*	written consent of an officer of Valid Logic Systems 		*
*	Incorporated.							*
*									*
*	The copyright notice appearing above is included to provide	*
*	statutory protection in the event of unauthorized or 		*
*	unintentional public disclosure.				*
*									*
************************************************************************/

/*
 * Connection manager
 *
 *	Bakul Shah
 *
 * bvs 840223 -- original version
 * jam 840228-0301-02-05-0410
 * bvs 840302 -- added support for detecting name collision
 *		 separated listen enable from broadcast one.
 * bvs 840404
 * bvs 840502 -- generation number set in enable, clean table
 *		 on no listen, conn_remove
 * bvs 841012 -- ref. count added to a conn structure.
 *
 *     850101		Clean up state machine and fill in holes in logic.
 *			Remove dependencies on ec driver definitions.
 *
 * jht 850718 -- Initialize myName: conn_enable() was writing hostname @ 0!
 * sas 851002 -- Fixed what I could -- I hate this code !!!
 */

#include "../h/param.h"
#include "../h/socket.h"
#include "../net/if.h"
#include "../h/dir.h"
#include "../h/user.h"
#include "../h/kernel.h"
#include "../h/proc.h"
#include "../h/mbuf.h"
#include "../h/systm.h"
#include "../vnet/vnet.h"
#include "../conn/conn.h"
#include "../conn/conn_packet.h"
#include "../rpc/rpc.h"

typedef int (*fun_t)();

/***********************************************/
/* Connection Manager Socket Address Structure */
/***********************************************/

struct conn_sock_addr
    {
    u_short family;
    node_t  node;
    };

#define CONN_SPL()	(spl5())        /* spl to block out net input */

#define CONN_MAX_NAME		20
#define CONN_MAX_HANDLERS	8

#ifdef	CONN_GROUPS
#define CONN_MAX_GROUP 		32
#else	CONN_GROUPS
#define CONN_MAX_GROUP 		1
#endif	CONN_GROUPS


/****************************************/
/* Connection Table Hashing Definitions */
/****************************************/

#define	CONN_MAX_BINS		32	/* Should be a power of two */

#if	((CONN_MAX_BINS&(CONN_MAX_BINS-1)) == 0)
#define	CONN_HASH(node)		((node)->host.low & (CONN_MAX_BINS-1))
#else
#define	CONN_HASH(node)		((node)->host.low % CONN_MAX_BINS)
#endif

#define	CONN_HASHBIN(node)	(conn_bins[CONN_HASH(node)])
#define CONN_HASHREC(rec)	(&CONN_HASHBIN(&(rec)->node))
#define CONN_HASHCONN(c)	(&CONN_HASHBIN(&(c)->node))


/********************/
/* Static Variables */
/********************/

char	     	myNameBuffer[CONN_MAX_NAME+1] = "unNamed";
char	     *	myName		= &myNameBuffer[0];
connection_t *	conn_freelist = NULL;
connection_t *	conn_bins[CONN_MAX_BINS];
fun_t 	 	conn_handler[CONN_MAX_HANDLERS];
int		conn_enabled = 0;
int		conn_listening = 0;
int		conn_intrqmaxlen = IFQ_MAXLEN;
long		conn_groups;

struct ifnet * ifconn;
long conn_sleeptime;
conn_packet_t * conn_packet = NULL;


/*************************/
/* Packet dispatch table */
/*************************/

int conn_rcvGoingDown();
int conn_rcvImAlive();
int conn_rcvResync();
int conn_rcvResyncAck();

fun_t conn_funTable[] =
    {
    conn_rcvGoingDown,
    conn_rcvImAlive,
    conn_rcvResync,
    conn_rcvResyncAck,
    };


/********************************/
/* Forward Routine Declarations */
/********************************/

connection_t * conn_addr();
connection_t * conn_findByAddress();
connection_t * conn_findConnection();
connection_t * conn_findByName();
conn_packet_t * conn_allocPacket();

/* conn_init - Initialize Connection Manager
 *
 * Synopsis:
 *	result = conn_init();
 *	int result;
 *
 * Parameter Usage:
 *	result	= 0, Initialization failed
 *		= 1, Initialization suceeded
 *
 * Description:
 *
 *	This routine should be called only once.  It is called before
 *	any service is expected of the connection manager.
 *
 *	It is assume that there is only one network interface that the
 *	connection manager needs to deal with.  This interface is also
 *	assumed to already be declared before caling conn_init.
 *
 *	The default connection group defined for this node is initialized.
 *
 *	A single conn packet is allocated to save time and resource
 *	hassles when the connection daemon needs to broadcast its state.
 *	This packet is used only for the broadcast packets and is assumed
 *	to contain the proper information by the daemon.
 */

conn_init()
{
    if (!ifconn)
	return 0;

#ifndef CONN_GROUPS
    conn_groups = 1 << CONN_DEFAULT_GROUP;
#endif CONN_GROUPS

    if (!(conn_packet = conn_allocPacket()))
	return 0;

    return 1;
}

/* conn_attach - Attach exception handler to Connection Manager
 *
 * Synopsis:
 *	result = conn_attach(&handler);
 *	int * handler ();
 *	int result;
 *
 * Parameter Usage:
 *	handler	    Exception routine
 *	result	    = 0, failed to attach handler
 *		    = 1, attached handler successfully
 *
 * Description:
 *
 *	Users of the Connection Manager call this routine to register
 *	an exception routine to be called when any connection changes
 *	status.
 *
 *	If a exception handler is already in the list, it is not
 *	entered a second time.  If list space is exhausted, the
 *	attach fails.
 */

conn_attach(handler)
    int (*handler)();
{
    register int i;

    for (i = 0; i < CONN_MAX_HANDLERS; i++)
	{
	if (!conn_handler[i])
	    {
	    conn_handler[i] = handler;
	    return 1;
	    }

	if (conn_handler[i] == handler)
	    return 1;
	}

    return 0;
}

/* conn_detach - detach an exception handler
 *
 * Synopsis:
 *	result = conn_detach(&handler);
 *	int * handler ();
 *	int result;
 *
 * Parameter Usage:
 *	handler	    Exception handler routine
 *	result	    = 0, handler was not found
 *		    = 1, handler found and detached
 *	
 * Description:
 *
 *	This routine removes exception handlers registered with the
 *	Connection Manager through conn_attach.
 *
 *	NOTE: Exception calls can be initiated at interrupt level.
 *	Until this routine returns the exception handler must field
 *	possible unwanted calls.
 */

conn_detach(handler)
    int (*handler)();
{
    register int i;

    for (i = 0; i < CONN_MAX_HANDLERS; i++)
	if (conn_handler[i] == handler)
	    {
	    conn_handler[i] = NULL;
	    return 1;
	    }

    return 0;
}

/* conn_findByName - Find connection by node name
 *
 * Synopsis:
 *	conn = conn_findByName(&name);
 *	connection_t * conn;
 *	char name [];
 *
 * Parameter Usage:
 *	name	Node name character string (null terminated).
 *	conn	Returned pointer to connection entry.
 *
 * Description:
 *
 *	This routine is called to find a connection entry for a
 *	specific node name.  It is assumed that the most active entry
 *	is desired.  Therefore the search also takes into account
 *	the connection state, returning the most active entry prioritized
 *	in the order "active, active/shutting down, all others".
 *
 *	The reference count of the entry is incremented.  This is to prevent
 *	the connection entry from being deleted until a conn_free call
 *	is performed.
 *
 *	This routine interlocks against interrupt level processing that might
 *	cause the connection to be discarded.
 *
 *	If no connection entry is found, a null pointer is returned.
 *
 *	NOTE: The old form of conn_addr is supplied for compatibility
 *	reasons.
 */

connection_t *
conn_addr(name)
    char * name;
{
    register connection_t * c;

    c = conn_findByName(name);
    if (c)
	conn_free(c);
    return(c);
}

connection_t *
conn_findByName(name)
    char * name;
{
    register connection_t * c;
    register connection_t * best_found = NULL;
    register connection_t * * bin;
    register s = CONN_SPL();

    for (bin = conn_bins; bin != &conn_bins[CONN_MAX_BINS]; ++bin)
	for (c = *bin; c; c = c->next)
	    if (ins_strcmp(name, c->name) == 0)
		{
		best_found = c;
		if (c->state == CONN_ACTIVE)
		    break;
		}

    if (best_found)
	best_found->count++;

    splx(s);
    return best_found;
}

/* conn_findByAddress - Find connection by node address
 *
 * Synopsis:
 *	conn = conn_findByAddress(&node);
 *	connection_t * conn;
 *	node_t node;
 *
 * Parameter Usage:
 *	node	Node address
 *	conn	Returned pointer to connection entry.
 *
 * Description:
 *
 *	This routine is called to find a connection entry for a
 *	specific node address.  It is assumed that the most active entry
 *	is desired.  Therefore the search also takes into account
 *	the connection state, returning the most active entry prioritized
 *	in the order "active, active/shutting down, all others".
 *
 *	It is assumed that the connection entry is in the correct hash
 *	bin.  An exhasutive search is not performed.
 *
 *	The reference count of the entry is incremented.  This is to prevent
 *	the connection entry from being deleted until a conn_free call
 *	is performed.
 *
 *	This routine interlocks against interrupt level processing that might
 *	cause the connection to be discarded.
 *
 *	If no connection entry is found, a null pointer is returned.
 *
 *	NOTE: The old form of conn_findConnection is supplied for compatibility
 *	reasons.
 */

connection_t *
conn_findConnection(node)
    register node_t * node;
{
    register connection_t * c;

    c = conn_findByAddress(node);
    if (c)
	conn_free(c);
    return(c);
}

connection_t *
conn_findByAddress(node)
    register node_t * node;
{
    register connection_t * c;
    register connection_t * best_found = NULL;
    register s = CONN_SPL();

    for (c = CONN_HASHBIN(node); c; c = c->next)
	if (nodecmp(*node, c->node))
	    {
	    best_found = c;
	    if (c->state == CONN_ACTIVE)
		break;
	    }

    if (best_found)
	best_found->count++;

    splx(s);
    return best_found;
}

/* conn_link - Increment connection reference count
 *
 * Synopsis:
 *	conn_link(&conn);
 *	connection_t conn;
 *
 * Parameter Usage:
 *	conn	Connection entry.
 *
 * Description:
 *
 *	NULL ROUTINE
 */

conn_link(lc)
    register connection_t * lc;
{
    register connection_t * c;
}

/* conn_free - Decrement connection reference count
 *
 * Synopsis:
 *	conn_free(&conn);
 *	connection_t conn;
 *
 * Parameter Usage:
 *	conn	Connection record
 *
 * Description:
 *
 *	NULL ROUTINE
 */

conn_free(c)
    register connection_t * c;
{
}

/* conn_new - Create a new connection entry
 *
 * Synopsis:
 *	conn = conn_new(&rec, version);
 *	conn_record_t rec;
 *	int version;
 *	connection_t * conn;
 *
 * Parameter Usage:
 *	rec	Connection message record
 *	version	Version of Connection Manager on remote system
 *	conn	Return pointer to new connection entry
 *
 * Description:
 *	We are only called from conn_input when the address-based 
 *	hash lookup fails to find us.
 */
 
connection_t *
conn_new(rec, version)
	register conn_record_t * rec;
	int version;
{
	register connection_t * c;
	register connection_t * * cp;
	register connection_t * * bin;

	/*
	 * Check for name collision.
	 */

	if (!ins_strcmp(myName,conn_name(rec)) && !nodecmp(myNode, rec->node)) {
		conn_disable();
		printf("conn_new: another node has my name\n");
		return 0;
	}

	/*
	 * See if we can find the connection record in the table
	 * name that corresponds to this one.  If the system has
	 * changed both name and address then as far as we're
	 * concerned, it's a new system.  Note that we checked
	 * our hash bucket in conn_input so we can assume
	 * that there is no other system with the address of this
	 * conn record.
	 */
    	for (bin = conn_bins; bin < &conn_bins[CONN_MAX_BINS]; bin++)
		for (cp = bin; c = *cp; cp = &c->next) {
			if (!ins_strcmp(conn_name(rec), c->name))
				goto gotone;
		}

	/*
	 * No match
	 */
	/*
	 * Rather than getting the connection record and then the name,
	 * we go for the whole ball of wax at once.  This way, if our 
	 * request for memory for the name is denied (freemem == 0), we 
	 * don't have to leave the memory for the connection record 
	 * hanging around.
	 * sas 851023
	 */
	c = (connection_t *)calloc(sizeof(connection_t) + 
		CONN_MAX_NAME+1, C_DONTWAIT);
	if (!c)
		return 0;
	c->name = ((caddr_t)c + sizeof(connection_t));
newrec:

	c->count = 1;
	c->generation = rec->generation;
	c->node = rec->node;
	c->seqno = 0;
	c->flags = 0;
	c->version = version;
	cp = CONN_HASHCONN(c);
	c->next = *cp;
	*cp = c;

	/*
	 * conn_goActive will copy the name and set the timers
	 */
	conn_goActive(c, rec);
	return c;

gotone:
	/*
	 * If this is "ACTIVE", then best guess is that two other nodes
	 * are having a name/address conflict.  Wait until we time one
	 * or the other out before we blow the connection record away.
	 */
	if (c->state == CONN_ACTIVE)
		return 0;

	/*
	 * Otherwise, we're going to overwrite the connection record, move
	 * the record into a new hash bucket (possibly the same one
	 * depending on how the address hashes). Fix the forward pointer from
	 * c and then find ourselves our new hash bucket.  Are we having
	 * fun yet?
	 */
	*cp = c->next;
	cp = (connection_t * *) &CONN_HASHBIN(rec->node);
	goto newrec;

}

/* conn_goActive - Connection transition to active
 *
 * Synopsis:
 *	conn_goActive(&conn, &rec);
 *	connection_t conn;
 *	conn_record_t rec;
 *
 * Parameter Usage:
 *	conn	Connection entry
 *	rec	Packet record that caused "to active" transition.
 *
 * Description:
 *	This routine takes a connection to the active state.  The
 *	appropriate information about uptime and downtime are
 *	set in the connection entry.  An "up" exception is also
 *	issued if the connection was not already active.
 *
 */

conn_goActive(c, rec)
    register connection_t * c;
    register conn_record_t * rec;
{
    c->time         = UPTIME();
    c->timeSinceUp  = rec->timeSinceUp;
    c->timeTillDown = rec->timeTillDown;
    c->flags       &= ~(CONN_INACTIVE|CONN_SHUTTING_DOWN);

    /*
     * While this may seems silly (copying the record's name every time)
     * this is somewhat cheaper than checking to see if it's different
     * everytime then copying if different.
     * BUG  -- What if data is corrupted before/after hardware checksum??
     */
    strcpy(c->name, conn_name(rec));

    if (c->state != CONN_ACTIVE)
	{
	c->state = CONN_ACTIVE;
	conn_raiseException(c, CONN_UP);
	}
}

/* conn_allocPacket - Allocate a single record connection packet
 *
 * Synopsis:
 *	packet = conn_allocPacket();
 *	conn_packet_t * packet;
 *
 * Parameter Usage:
 *	packet	Return pointer to allocation packet buffers.
 *
 * Description:
 *
 *	This routine allocates an mbuf chain that is sufficient to hold
 *	a connection packet containing a single record.  The invariant
 *	portions of the packet are filled in.
 *
 *	If a packet cannot be created, a null (0) pointer is returned.
 */

conn_packet_t *
conn_allocPacket()
{
    register struct mbuf * m;
    register conn_packet_t * packet;
    register conn_record_t * rec;
    register int length;
    
    MGET(m, 0, MT_CONN);
    if (!m)
	return NULL;
    m->m_off = MMINOFF;
    m->m_len = sizeof (conn_packet_t) + sizeof (conn_record_t) + CONN_MAX_NAME;
    packet = mtod(m, conn_packet_t *);
    packet->count = 1;
    packet->version = CONN_VERSION;
    rec = conn_rec(packet);
    rec->node = myNode;
    rec->generation = myGeneration;
    length = strlen(myName) + 2;
    rec->length = (sizeof (conn_record_t) + length) &~ 1;
    rec->groups = conn_groups;
    return packet;
} 

/*
 * The connection daemon runs periodically to broadcast "i'm alive" packet.
 * The packet is sent every 30 seconds.
 * During shutdown it can be used to broadcast
 * "i'm going down in N seconds".
 * This packet is sent every 10 seconds.
 * It also checks whether an alive packet was received within last 90 seconds.
 * If not, an active connection is marked inactive.
 * If a connection is inactive for 90 seconds it will call the RPC routine
 * rpc_exception() so that all communication with the down node can be
 * severed.
 */

conn_daemon(packet)
    register conn_packet_t * packet;
{
    register connection_t * c;
    register connection_t * * bin;
    register conn_record_t * rec = conn_rec(packet);
    register int timeSinceLastMessage;

    if (!conn_listening)
	return;
    /*
     * If we are enabled for broadcast
     * update and send the packet
     */
    if (conn_enabled)
	{
	timeSinceLastMessage  = UPTIME() - rec->timeSinceUp;
	rec->timeSinceUp  += timeSinceLastMessage;
	rec->timeTillDown -= timeSinceLastMessage;
	(*ifconn->if_output)
	    (ifconn, m_copy(dtom(packet), 0, M_COPYALL), &ifconn->if_broadaddr);

	/*
	 * return if we are going down NOW!
	 */

	if (packet->type == CONN_GOING_DOWN && rec->timeTillDown <= 0) 
	    {
	    conn_enabled = 0;
	    return;
	    }
	}

    /*
     * reschedule another call out
     */

    timeout((caddr_t)conn_daemon, packet, conn_sleeptime);

    /*
     * check the connection table
     */

    for (bin = conn_bins; bin != &conn_bins[CONN_MAX_BINS]; ++bin)
	{
	register connection_t * nextc;

	for (c = *bin; c; c = nextc)
	    {
	    register int timeSinceLastCall;

	    nextc = c->next;

	    /*
	     * No need to process down nodes.
	     */

	    if (c->state != CONN_ACTIVE)
		continue;

	    timeSinceLastCall = UPTIME() - c->time;

	    /*
	     * If the conn is shutting down
	     * and the shutdown period has expired,
	     * shut it down now!
	     */

	    if ((c->flags & CONN_SHUTTING_DOWN) &&
		    (c->timeTillDown -= timeSinceLastCall) <= 0)
		{
		c->state = CONN_SHUTDOWN;
		conn_raiseException(c, CONN_DOWN);
		conn_free(c);
		}

	    /*
	     * If we haven't heard from the node for
	     * a very long period of time assume that
	     * it has crashed; mark the connection entry
	     * as such and call the exception handler.
	     *
	     * If we haven't heard from the node for a while,
	     * mark the entry as inactive so that
	     * no new requests to that node will be made.
	     */

	    if (timeSinceLastCall >= CONN_TIME_CRASHED)
		{
		c->state = CONN_CRASHED;
		conn_raiseException(c, CONN_DOWN);
		}
	    else
		if (timeSinceLastCall >= CONN_TIME_INACTIVE)
		    c->flags |= CONN_INACTIVE;
	    }
	}
}

/*
 * This is an interrupt level routine,
 * called directly from the ethernet driver.
 * It updates the connection table.
 * If the connection is known, the generation number is checked.
 * If the number is not same, 
 * all attached exception handlers are called.
 * If the connection is not known, a new one is added.
 */

conn_input()
{
    register struct mbuf *m;
    register connection_t * c;
    register conn_packet_t * packet;
    register long s;
    register conn_record_t * rec;
    register fun_t fun;

    for (;; m_freem(m))
	{
	register int count;
	extern fun_t conn_funTable[];
	short version;

	s = CONN_SPL();
	IF_DEQUEUE(&conn_intrq, m);
	splx(s);
	if (!m)
	    return;
	/*
	 * Drop packets if we are not listening.
	 */
	if (!conn_listening)
	    continue;

	packet = mtod(m, conn_packet_t *);

	/*
	 * Drop packets from a different major version
	 * or if the packet type is illegal.
	 */
	if (major(packet->version) != major(CONN_VERSION) || packet->type < 0 ||
		packet->type >= CONN_MAX_TYPE)
	    continue;

	rec = conn_rec(packet);

	fun = conn_funTable[packet->type];
	version = packet->version;

	/*
	 * Process all records contained in one packet.
	 */
	for (count = packet->count; count--; rec = conn_nextrec(rec))
	    {
	    /*
	     * Drop conn record if not from
	     * one of the groups we belong to.
	     */
	    if (!(rec->groups & conn_groups))
		continue;

	    for (c = *CONN_HASHREC(rec); c; c = c->next)
		{
		if (!nodecmp(c->node, rec->node))
		    continue;

		/*
		 * process the conn record.
		 */
		if ((*fun)(c, rec, version))
		    goto continue2;
		break;
		}
	    /*
	     * No connection was found for the node.
	     * If this is an IM ALIVE message,
	     * create a new connection.
	     */

	    if (packet->type == CONN_IM_ALIVE)
		conn_new(rec, packet->version);
	continue2:;
	    }
	}
}

/* conn_rcvImAlive - Process record from Im Alive packet
 *
 * Synopsis:
 *	result = conn_rcvImAlive(&conn, &rec);
 *	connection_t conn;
 *	conn_record_t rec;
 *	int result;
 *
 * Parameter Usage:
 *	conn	Connection relating to packet record
 *	rec	Connection packet record
 *	result	= 0, Failed processing record
 *		= 1, Succeeded processing record
 *
 * Description:
 *
 */

conn_rcvImAlive(c, rec, version)
	register connection_t * c;
	register conn_record_t * rec;
	int version;
{
	/*
	 * Check if the packet is from a known generation
	 */

	if (c->generation == rec->generation) {
		if (!(c->flags & CONN_CLEANUP))
			conn_goActive(c, rec);
	} else {
#ifdef notdef
		/*
		 * if the conn packet is old, ignore it.
		 */

		if (c->generation > rec->generation)
		    return 1;
#else notdef
		/*
		 * What we really want to do is ignore the packet
		 * only if the connection is still marked as ACTIVE.
		 * If we then receive a packet of the later generation,
		 * we implicitly assume that the earlier packet was a stray.
		 * If we continue to receive packets of the earlier
		 * generation, discarding will eventually result in the
		 * node being marked down which will cause a cleanup.
		 * Finally, the next packet which comes in will cause
		 * us to accept the new (earlier) generation.
		 * sas 860206
		 */
		if (c->generation > rec->generation && c->state == CONN_ACTIVE)
			return 1;
#endif notdef

		/*
		 * If the old connection is still
		 * marked active, declare it crashed.
		 */
		if (c->state == CONN_ACTIVE) {
		    c->state = CONN_SHUTDOWN;
		    conn_raiseException(c, CONN_DOWN);
		    conn_free(c);
		} else {
		/*
		 * THIS IS A "NEW" RECORD.
		 * There is a race here.  If EFS gets a valid connection
		 * record for a dead system which has not yet timed out
		 * and we then get a packet for the system's latest
		 * generation we have a problem.  So, live with it.
		 * This won't happen very often, the other system will
		 * drop our packets.
		 */
			c->count = 1;
			c->generation = rec->generation;
			c->node = rec->node;
			c->seqno = 0;
			c->flags = 0;
			c->version = version;
			conn_goActive(c, rec);
		}
	}

    /* ALWAYS RETURN 1 -- we'll take care of calling conn_goActive */
    return 1;
}

/*
 * if the connection has gone down
 * raise conn exception and mark the
 * connection as shutdown.
 */
/* conn_rcvGoingDown - Process record from Going Down packet
 *
 * Synopsis:
 *	result = conn_rcvGoingDown(&conn, &rec);
 *	connection_t conn;
 *	conn_record_t rec;
 *	int result;
 *
 * Parameter Usage:
 *	conn	Connection relating to packet record
 *	rec	Connection packet record
 *	result	= 0, Failed processing record
 *		= 1, Succeeded processing record
 *
 * Description:
 *
 *	This message indicates that the sending node is going to
 *	shutdown.  If the time till down has expired, cause a down
 *	transition on the connection and clean it up.
 *
 */

conn_rcvGoingDown(c, rec, version)
    register connection_t * c;
    register conn_record_t * rec;
    short version;
{
    /*
     * Ignore going down message from an unknown generation.
     */

    if (c->generation != rec->generation)
	return;

    if (rec->timeTillDown <= 0)
	{
	c->state = CONN_SHUTDOWN;
	conn_raiseException(c, CONN_DOWN);
	}
    else
	c->flags |= CONN_INACTIVE|CONN_SHUTTING_DOWN;

    return 1;
}

/* conn_rcvResync - Process record from Resync packet
 *
 * Synopsis:
 *	result = conn_rcvResync(&conn, &rec);
 *	connection_t conn;
 *	conn_record_t rec;
 *	int result;
 *
 * Parameter Usage:
 *	conn	Connection relating to packet record
 *	rec	Connection packet record
 *	result	= 0, Failed processing record
 *		= 1, Succeeded processing record
 *
 * Description:
 *
 *	This packet indicates that the sender has determined that a
 *	skew exists in the connection state information stored in
 *	the connected nodes.  The correct response to this message is
 *	to crash the connection, send a ResyncAck, and restart with
 *	a clean connection.
 *
 */

conn_rcvResync(c, rec, version)
    register connection_t * c;
    register conn_record_t * rec;
    int version;
{
    return 1;
}

/*
 * The sender recognized our broke conn message,
 * cleaned up and now tells us it is ready to
 * resynchronize with us.
 */
/* conn_rcvResyncAck - Process record from resync acknowledge packet
 *
 * Synopsis:
 *	result = conn_rcvResyncAck(&conn, &rec);
 *	connection_t conn;
 *	conn_record_t rec;
 *	int result;
 *
 * Parameter Usage:
 *	conn	Connection relating to packet record
 *	rec	Connection packet record
 *	result	= 0, Failed processing record
 *		= 1, Succeeded processing record
 *
 * Description:
 *
 *	This is a response to a Resync message.  The sender has discarded
 *	the old connection state information and is ready to continue with
 *	Im_alive messages.
 *
 */

conn_rcvResyncAck(c, rec, version)
    register connection_t * c;
    register conn_record_t * rec;
    int version;
{
    return 1;
}

/*
 * Send a resync connection packet to c->node.
 */

conn_sndResync(c)
    register connection_t * c;
{
}

/*
 * Send a resync ack packet to c->node.
 */

conn_sndResyncAck(c)
    register connection_t * c;
{
}

/* conn_raiseException - Notify attached modules of connection state change
 *
 * Synopsis:
 *	conn_raiseException(&conn, type);
 *	connection_t conn;
 *	int type;
 *
 * Parameters Usage:
 *	conn	Connection going through transition.
 *	type	Either CONN_UP or CONN_DOWN.
 *
 * Description:
 *
 *	This routine calls each routine that registered with the 
 *	Connection Manager through the conn_attach routine.  All
 *	exception handlers are called with the connection entry and
 *	the type of connection transition.
 */

conn_raiseException(c, type)
    register connection_t * c;
{
    register int i;

    for (i = 0; i < CONN_MAX_HANDLERS; i++)
	if (conn_handler[i])
	    (*conn_handler[i])(c, type);
}

/*
 * Start listening to the net but do not
 * broadcast our name on it yet.
 */

conn_listen()
{
    extern conn_daemon();

    if (!conn_packet && !conn_init())
	return;

    if (conn_listening)
	return;

    untimeout((caddr_t)conn_daemon, conn_packet);
    conn_listening = 1;
    conn_sleeptime = conn_enabled ? 
   		     CONN_PERIOD_IM_ALIVE_BRDCAST*hz :
		     CONN_PERIOD_JUST_LISTENING*hz;
    conn_intrq.ifq_maxlen = conn_intrqmaxlen;
    conn_daemon(conn_packet);
}

/*
 * stop listening to the net.
 * also stop broadcasting.
 */

conn_noListen()
{
    register connection_t * * bin;
    register connection_t * c;
    register connection_t * nextc;
 
    conn_listening = 0;
    conn_enabled = 0;
    conn_intrq.ifq_maxlen = 0;

    for (bin = conn_bins; bin != &conn_bins[CONN_MAX_BINS]; ++bin)
	for (c = *bin; c; c = nextc)
	    {
	    nextc = c->next;
	    c->state = CONN_SHUTDOWN;
	    conn_raiseException(c, CONN_DOWN);
	    conn_free(c);
	    }
}

/*
 * Enables the "i'm alive" message broadcast.
 * The arguments node, name and generation are stuffed
 * in the packet.  Return 1 on success and 0 on failure.
 */

conn_enable(name)
    char * name;
{
    register conn_packet_t * packet;
    register connection_t * c;
    register conn_record_t * rec;
    extern conn_daemon();

    if (!conn_packet && !conn_init())
	return 0;

    if (conn_enabled)
	return 1;

    /*
     * Don't come up if another node already has
     * the name we are about to use.
     */

    if ((c = conn_addr(name)) && (c->state == CONN_ACTIVE) &&
	 !nodecmp(c->node, myNode))
	return 0;

    if (conn_listening)
        {
	conn_listening = 0;
	untimeout((caddr_t)conn_daemon, conn_packet);
	}

    /*
     * myGeneration must be a number that
     * increases monotonically with each reboot.
     */

    myGeneration = time.tv_sec;

    /*
     * Stash our hostname,
     * allowing for trailing NUL
     */
    strncpy(myName, name, min(CONN_MAX_NAME,strlen(name)));
    myName[strlen(name)] = 0;
    packet = conn_packet;
    packet->type = CONN_IM_ALIVE;
    rec = conn_rec(packet);
    rec->generation = myGeneration;
    rec->timeSinceUp = UPTIME();
    strcpy(conn_name(rec), myName);
    conn_listening = 1;
    conn_enabled = 1;
    conn_intrq.ifq_maxlen = conn_intrqmaxlen;
    conn_sleeptime = CONN_PERIOD_IM_ALIVE_BRDCAST*hz;
    conn_daemon(conn_packet);
    return 1;
}

/*
 * Disable our connection to the net
 * but continue listening.
 */

conn_disable()
{
    conn_enabled = 0;
    conn_sleeptime = CONN_PERIOD_JUST_LISTENING*hz;
}

/*
 * Shutdown our connection to the net.
 * The shutdown message will be sent every 10 seconds
 * until we are disabled.
 */

conn_shutdown(shutdowntime)
    long shutdowntime;
{
    register conn_packet_t * packet = conn_packet;
    register conn_record_t * rec = conn_rec(packet);

    if (!conn_enabled)
	return;
    untimeout((caddr_t)conn_daemon, conn_packet);
    packet->type      = CONN_GOING_DOWN;
    rec->timeSinceUp  = UPTIME();
    rec->timeTillDown = shutdowntime;
    conn_sleeptime = CONN_PERIOD_GOING_DOWN_BRDCAST*hz;
    conn_daemon(conn_packet);
}

/*
 * Return the Nth connection in the connection
 * list.  This is a temporary way to enumerate
 * the connections we have made.
 */

connection_t *
conn_enumerate(n)
    register int n;
{
    register connection_t * c;
    register connection_t * * bin;

    for (bin = conn_bins; bin != &conn_bins[CONN_MAX_BINS]; ++bin)
	for (c = *bin; c; c = c->next)
	    if (--n < 0)
		goto out;
out:
    return c;
}

/*
 * The user suspects that connection to node ``name''
 * is down.  Break all communication with the node.
 */

conn_suspect(name)
    char * name;
{
    register connection_t * c = conn_addr(name);

    if (!c || c->state != CONN_ACTIVE)
	return 1;
    c->state = CONN_CRASHED;
    conn_raiseException(c, CONN_DOWN);
    conn_sndResync(c);
    return 1;
}

/*
 * Remove an existing entry for a down or crashed node.
 */
conn_remove(name)
    char * name;
{
return  0;
}

#ifdef CONN_GROUPS
/*
 * Add a group to the list of groups for
 * which we will accept connection packets.
 * Return the group set on success and 0 on failure.
 */
conn_addgroup(group)
    register long group;
{
    if (group < 0 || group >= CONN_MAX_GROUP)
	return 0;
    conn_groups |= 1 << group;
    return conn_groups;
}

/*
 * Remove a group from the list of groups.
 * We will no longer recognize connection
 * packets from this group.  Return 1 if the
 * group was removed and 0 if we were not
 * in that group.
 */

conn_delgroup(group)
    register long group;
{
    if (group < 0 || group >= CONN_MAX_GROUP)
	return 0;
    conn_groups &= ~(1 << group);
    return 1;
}

/*
 * Return the mask of groups currently enabled.
 */

conn_getgroup()
{
    return(conn_groups);
}
#endif CONN_GROUPS
