/*
 * This file contains the routines to implement the Berkeley Remote-Who
 * Protocol.
 */

/*
 * based on 4.2bsd, modified by martin levy, bellcore 85.
 */

#include	"../../lib/defs.h"
#include	"../../mos/mos.h"
#include	"../../aux/mosu.h"
#include	"../src/const.h"
#include	"../src/param.h"
#include	"../src/defs.h"
#include	"../src/macs.h"
#include	"../src/net.h"
#include	"../src/ext.h"

#include	"../in/in.h"
#include	"../in/inext.h"
#include	"../in/inrte.h"
#include	"../in/inudp.h"
#include	"../in/inparam.h"

#include	"rwhod.h"

#define	RWHO_WAIT	2	/* the opcode for the timer */

static char rwhobad[] = "RWHO bad %s 0%o, %d.%d.%d.%d\r\n";

long rwhonow = 0;			/* boot time is 0 */

/* The initialization and the main timer loop of the protocol. */
RWHO()
{
	sig	timer_signal;

	/* enter timer loop. */
	for (;;) {
		rwho_timer();
		rwhonow += TIMER_RATE;
		stime(RWHO_WAIT, 0, TIMER_RATE * NTICKS);
		waits(RWHO_WAIT, &timer_signal);
	}
}

/* This routine gets called each time the timer fires.  It broadcasts the
 * current rwho packet.
 */
rwho_timer()
{
	reg	int	i;
	net	*netp;
	reg	iorb	*iob;
	reg	struct	whod *pkt;
	ext	char	name[];
	ext	iorb	*get_rwho_pkt();
	ext	long	swabl();

	/* Broadcast rwho packet to all connected internet nets */

	for (i=0,netp=nets;i<nnets;netp++,i++) {
		if ((iniatlst[i] == NULL) ||
		    (!(netp->n_cap & C_BRD)) ||
#ifdef MIT
		    (mkina(&iniatlst[i]->inia_addr)->i_saddr.i_snet != myanet))
#else
		    (mkina(&iniatlst[i]->inia_addr)->i_caddr.i_cneth != myanet))
#endif
			continue;
		if ((iob = get_rwho_pkt()) == NULL)
			return;
		pkt = (struct whod *)(iob->i_addr +
			sizeof(inpkt) + sizeof(udppkt));
		pkt->wd_vers = WHODVERSION;
		pkt->wd_type = WHODTYPE_STATUS;
		strcpy(pkt->wd_hostname, name);
		pkt->wd_loadav[0] = pkt->wd_loadav[1] = pkt->wd_loadav[2] = 0;
		pkt->wd_boottime = 0;
		pkt->wd_sendtime = swabl(rwhonow);
		rwho_send(iob, ANYHOST, netp, sizeof(struct whod));
	}
}

/* Fills in the internet and udp headers of an rwho packet and then sends
 * the packet off.
 */
rwho_send(iob, dst, netp, len)
reg	iorb	*iob;
reg	inaddr	*dst;
net	*netp;
int	len;
{
	void	rwho_gw_xmt();
	reg	udppkt	*upkt;
	inaddr	adr;
	inaddr	*idst;

	upkt = iob->i_addr + sizeof(inpkt);
	upkt->u_src = swab(RWHODUDPSOCK);
	upkt->u_dst = swab(RWHODUDPSOCK);
	upkt->u_len = swab(len + sizeof(udppkt));
	upkt->u_chk = 0;	/* it might be a good idea to set this */

	/* This is specific to class A networks.  It sets the destination
	 * to the internet broadcast address of the subnet. */
	if (dst == ANYHOST) {
		/* Maybe we can someday implement the real internet broadcast
		 * standard? */
#ifdef MIT
		adr.i_word.i_wordh =
		 ((inaddr *)&iniatlst[netp->n_net]->inia_addr)->i_word.i_wordh;
		adr.i_word.i_wordl = INLNBRD;
#else
		/* class C */
		adr.i_caddr.i_cneth = ((inaddr *)&iniatlst[netp->n_net]->inia_addr)->i_caddr.i_cneth;
		adr.i_caddr.i_cnetl = ((inaddr *)&iniatlst[netp->n_net]->inia_addr)->i_caddr.i_cnetl;
		adr.i_caddr.i_chst = INLNBRD;
#endif
		idst = &adr;
	}
	else	idst = dst;

	mkinpkt(iob, &(iniatlst[netp->n_net]->inia_addr), idst, INUDP, len + sizeof(udppkt));
	iob->i_usr2 = (word)netp;
	iob->i_usr3 = dst;
	addtsk(gwhnd, INPPRI, rwho_gw_xmt, iob);
	if (*gwmsgq == NULL)
		signal(gwpid, gwopc, 0);
}

/* Runs in the Gateway process to send the packets. */
rwho_gw_xmt(iob)
iorb	*iob;
{
	int	ret_code;
	net	*netp;
	word	*dst;

	netp = iob->i_usr2;
	dst = iob->i_usr3;
	if ((ret_code = (*netp->n_send)(iob, netp, P_IN, dst)) != D_OK) {
		iflog(L_ERRU)
			dolog("RWHO:error 0%o sending pkt to net 0%o\r\n",
				ret_code, netp->n_net);
		freebuf(bufsize, iob);
	}
}

/* Allocate a packet, setting i_addr correctly. */
iorb	*
get_rwho_pkt()
{
	reg	iorb	*iob;

	if ((iob = getbuf(bufsize)) == NULL) {
		iflog(L_ERRU)
			dolog("RWHO: couldn't alloc pkt\r\n");
		return iob;
	}
	iob->i_addr = ((byte *)iob) + pktoff;
	return iob;
}

rwho_in(iob)
iorb	*iob;
{
	freebuf(bufsiz, iob);
}
