
/*
 *           PVM 3.2:  Parallel Virtual Machine System 3.2
 *               University of Tennessee, Knoxville TN.
 *           Oak Ridge National Laboratory, Oak Ridge TN.
 *                   Emory University, Atlanta GA.
 *      Authors:  A. L. Beguelin, J. J. Dongarra, G. A. Geist,
 *    W. C. Jiang, R. J. Manchek, B. K. Moore, and V. S. Sunderam
 *                   (C) 1992 All Rights Reserved
 *
 *                              NOTICE
 *
 * Permission to use, copy, modify, and distribute this software and
 * its documentation for any purpose and without fee is hereby granted
 * provided that the above copyright notice appear in all copies and
 * that both the copyright notice and this permission notice appear in
 * supporting documentation.
 *
 * Neither the Institutions (Emory University, Oak Ridge National
 * Laboratory, and University of Tennessee) nor the Authors make any
 * representations about the suitability of this software for any
 * purpose.  This software is provided ``as is'' without express or
 * implied warranty.
 *
 * PVM 3.2 was funded in part by the U.S. Department of Energy, the
 * National Science Foundation and the State of Tennessee.
 */

/*
 *	host.c
 *
 *	Host table functions.
 *
$Log: host.c,v $
 * Revision 1.2  1993/09/15  15:03:31  manchek
 * defined max() if not already there
 *
 * Revision 1.1  1993/08/30  23:26:47  manchek
 * Initial revision
 *
 */

#include <sys/param.h>
#ifndef IMA_TITN
#include <sys/types.h>
#include <sys/ioctl.h>
#else
#include <bsd/sys/types.h>
#include <sys/43ioctl.h>
#endif
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#ifdef	IMA_E88K
#include <sys/net/if.h>
#else
#include <net/if.h>
#endif
#include <netdb.h>
#include <stdio.h>
#ifdef	SYSVSTR
#include <string.h>
#else
#include <strings.h>
#endif
#include <ctype.h>
#include "protoglarp.h"
#include "global.h"
#include "pvmalloc.h"
#include "host.h"
#include "mesg.h"
#include "pkt.h"
#include "ddpro.h"
#include "bfunc.h"

#ifndef	max
#define	max(a,b)	((a)>(b)?(a):(b))
#endif

#ifdef SOCKADHASLEN
#define SIZ(p) max(sizeof(p), (p).sa_len)
#else
#define SIZ(p) sizeof(p)
#endif


char *inadport_decimal();


/***************
 **  Globals  **
 **           **
 ***************/

extern int debugmask;				/* from pvmd.c */
extern struct htab *hosts;			/* from pvmd.c */
extern int tidhmask;				/* from pvmd.c */


/***************
 **  Private  **
 **           **
 ***************/

static char rcsid[] = "$Id: host.c,v 1.2 1993/09/15 15:03:31 manchek Exp $";
static char pvmtxt[1024];			/* scratch space for error messages */


/*****************
 **  Interface  **
 **             **
 *****************/

/*	hd_new()
*
*	Make a new host descriptor.
*/

struct hostd *
hd_new(hh)
	int hh;		/* index in host table */
{
	struct hostd *hp;

	if (hp = TALLOC(1, struct hostd, "hd")) {
		BZERO((char*)hp, sizeof(struct hostd));
		hp->hd_ref = 1;
		hp->hd_hostpart = hh << (ffs(tidhmask) - 1);
		hp->hd_sad.sin_family = AF_INET;
		hp->hd_txq = pk_new(0);
		hp->hd_opq = pk_new(0);
		hp->hd_rxq = pk_new(0);
		hp->hd_txseq = 1;
		hp->hd_rxseq = 1;
		hp->hd_speed = 1000;
		hp->hd_rtt.tv_sec = 1;	/* XXX const */
		hp->hd_mcas = mca_new();
	}
	return hp;
}


/*	hd_free()
*
*	Free a host descriptor and all associated storage.
*	Probably want to use hd_unref() instead.
*/

void
hd_free(hp)
	struct hostd *hp;
{
	struct mca *mcap;

	if (hp->hd_name)
		PVM_FREE(hp->hd_name);
	if (hp->hd_arch)
		PVM_FREE(hp->hd_arch);
	if (hp->hd_login)
		PVM_FREE(hp->hd_login);
	if (hp->hd_dpath)
		PVM_FREE(hp->hd_dpath);
	if (hp->hd_epath)
		PVM_FREE(hp->hd_epath);
	if (hp->hd_bpath)
		PVM_FREE(hp->hd_bpath);
	if (hp->hd_txq)
		pk_free(hp->hd_txq);
	if (hp->hd_opq)
		pk_free(hp->hd_opq);
	if (hp->hd_rxq)
		pk_free(hp->hd_rxq);
	if (hp->hd_rxm)
		mesg_free(hp->hd_rxm);
	if (mcap = hp->hd_mcas) {
		while (mcap->mc_link != mcap)
			mca_free(mcap->mc_link);
		mca_free(mcap);
	}
	PVM_FREE(hp);
}


/*	hd_unref()
*
*	Decrement the refcount of a host descriptor.  Free it if count
*	reaches zero.
*/

void
hd_unref(hp)
	struct hostd *hp;
{
	if (--hp->hd_ref < 1)
		hd_free(hp);
}


hd_dump(hp)
	struct hostd *hp;
{
	int n;
	struct pkt *pp, *pp2;

	sprintf(pvmtxt, " hd_dump() ref %d t%x n \"%s\" ar \"%s\" lo \"%s\"\n",
			hp->hd_ref,
			hp->hd_hostpart,
			(hp->hd_name ? hp->hd_name : ""),
			(hp->hd_arch ? hp->hd_arch : ""),
			(hp->hd_login ? hp->hd_login : ""));
	pvmlogerror(pvmtxt);

	n = 0;
	if (pp2 = hp->hd_txq)
		for (pp = pp2->pk_link; pp != pp2; pp = pp->pk_link)
			n++;
	sprintf(pvmtxt, "           sa %s mtu %d f 0x%x e %d txq %d\n",
			inadport_decimal(&hp->hd_sad),
			hp->hd_mtu,
			hp->hd_flag,
			hp->hd_err,
			n);
	pvmlogerror(pvmtxt);

	sprintf(pvmtxt,
			"           tx %d rx %d rtt %d.%06d\n",
			hp->hd_txseq, hp->hd_rxseq,
			hp->hd_rtt.tv_sec, hp->hd_rtt.tv_usec);
	pvmlogerror(pvmtxt);
}


/*	nametohost()
*
*	Get a host descriptor by hostname.
*/

struct hostd *
nametohost(htp, name)
	struct htab *htp;
	char *name;
{
	int hh;
	struct hostd *hp;

	if (!strcmp(name, "."))
		return htp->ht_hosts[htp->ht_local];

	for (hh = htp->ht_last; hh > 0; hh--)
		if ((hp = htp->ht_hosts[hh]) && !strcmp(name, hp->hd_name))
			return hp;
	return (struct hostd*)0;
}


/*	indtohost()
*
*	Get a host descriptor by its host table index.
*/

struct hostd *
indtohost(htp, n)
	struct htab *htp;
	int n;
{
	return (n >= 1 && n <= htp->ht_last) ? htp->ht_hosts[n] : 0;
}


/*	tidtohost()
*
*	Get a host descriptor by its tid.
*/

struct hostd *
tidtohost(htp, tid)
	struct htab *htp;
	int tid;
{
	return indtohost(htp, (tid & tidhmask) >> (ffs(tidhmask) - 1));
}


/*	ht_free()
*
*	Free a host table and unref all hosts in it.
*/

void
ht_free(htp)
	struct htab *htp;
{
	int i;

	for (i = 1; i <= htp->ht_last; i++)
		if (htp->ht_hosts[i])
			hd_unref(htp->ht_hosts[i]);
	PVM_FREE(htp->ht_hosts);
	PVM_FREE(htp);
}


/*	ht_new()
*
*	Make a new (empty) host table.  Length is advisory as ht_hosts
*	is extended as needed.
*/

struct htab *
ht_new(siz)
	int siz;		/* initial length of ht_hosts[] */
{
	struct htab *htp;

	if (siz < 1)
		siz = 1;
	htp = TALLOC(1, struct htab, "ht1");
	BZERO((char*)htp, sizeof(struct htab));
	htp->ht_last = siz;
	htp->ht_hosts = TALLOC(siz + 1, struct hostd*, "ht2");
	BZERO((char*)htp->ht_hosts, (siz + 1) * sizeof(struct hostd*));
	return htp;
}


/*	ht_insert()
*
*	Add a host to a host table.  Extend ht_hosts if necessary.
*	Update ht_narch.
*/

ht_insert(htp, hp)
	struct htab *htp;
	struct hostd *hp;
{
	int hh;
	int i;
	unsigned long mask = 0, tmpmask;

	hh = (hp->hd_hostpart & tidhmask) >> (ffs(tidhmask) - 1);

	/* extend ht_hosts[] if no room */

	if (hh > htp->ht_last) {
		int n = htp->ht_last;

		htp->ht_last = (hh * 3) / 2;
		htp->ht_hosts = TREALLOC(htp->ht_hosts, htp->ht_last + 1, struct hostd*);
		while (++n <= htp->ht_last)
			htp->ht_hosts[n] = 0;
	}

	/* if already have an entry, take this as an update XXX kind of a hack */

	if (htp->ht_hosts[hh]) {	/* already have an entry */

		struct hostd *hp2 = htp->ht_hosts[hh];

		if (hp->hd_name) {
			if (hp2->hd_name)
				PVM_FREE(hp2->hd_name);
			hp2->hd_name = STRALLOC(hp->hd_name);
		}
		if (hp->hd_arch) {
			if (hp2->hd_arch)
				PVM_FREE(hp2->hd_arch);
			hp2->hd_arch = STRALLOC(hp->hd_arch);
		}
		hp2->hd_mtu = hp->hd_mtu;
		hp2->hd_sad = hp->hd_sad;
		hp2->hd_speed = hp->hd_speed;

	} else {					/* add new entry */

		htp->ht_hosts[hh] = hp;
		if (hh)
			htp->ht_cnt++;
		hp->hd_ref++;
	}

	/* update number of arches */

	htp->ht_narch = 0;
	for (hh = htp->ht_last; hh > 0; hh--) {
		hp = htp->ht_hosts[hh];
		if (hp && hp->hd_arch) {
			i = getarchcode(htp->ht_hosts[hh]->hd_arch);
			tmpmask = (1 << i);
			if (tmpmask & ~mask)
				htp->ht_narch++;
			mask |= tmpmask;
		}
	}
}


/*	ht_delete()
*
*	Remove a host from a host table and unreference it.
*/

ht_delete(htp, hp)
	struct htab *htp;
	struct hostd *hp;
{
	int hh;
	int i;
	unsigned long mask = 0, tmpmask;

	hh = (hp->hd_hostpart & tidhmask) >> (ffs(tidhmask) - 1);
	if (hh < 0 || hh > htp->ht_last || htp->ht_hosts[hh] != hp) {
		pvmlogerror("ht_delete() host not in table\n");
		return;
	}
	htp->ht_hosts[hh] = 0;
	if (hh)
		htp->ht_cnt--;

	hd_unref(hp);

	/* update number of arches */

	htp->ht_narch = 0;
	for (hh = htp->ht_last; hh > 0; hh--) {
		if (!(hp = htp->ht_hosts[hh]))
			continue;
		if (hp->hd_arch) {
			i = getarchcode(htp->ht_hosts[hh]->hd_arch);
			tmpmask = (1 << i);
			if (tmpmask & ~mask)
				htp->ht_narch++;
			mask |= tmpmask;
		}
	}
}


ht_dump(htp)
	struct htab *htp;
{
	int hh;

	sprintf(pvmtxt,
"ht_dump() ser %d last %d cnt %d master %d cons %d local %d narch %d\n",
			htp->ht_serial, htp->ht_last, htp->ht_cnt, htp->ht_master,
			htp->ht_cons, htp->ht_local, htp->ht_narch);
	pvmlogerror(pvmtxt);
	for (hh = 0; hh <= htp->ht_last; hh++)
		if (htp->ht_hosts[hh])
			hd_dump(htp->ht_hosts[hh]);
}


/*	ht_merge()
*
*	Add entries in src host table to dst host table.
*/

int
ht_merge(dst, src)
	struct htab *dst, *src;
{
	int hh;
	struct hostd *hp;

	for (hh = src->ht_last; hh > 0; hh--)
		if (hp = src->ht_hosts[hh])
			ht_insert(dst, hp);
	return 0;
}


/*	readhostfile()
*
*	Read a host file and return host table with options and ipaddrs
*	filled in.
*/

struct htab *
readhostfile(fn)
	char *fn;
{
	struct htab *htp;
	struct hostd *hp;
	FILE *ff = 0;
	char buf[512];		/* line buffer */
	int lnum = 0;		/* line counter */
	char *av[10];		/* parsed words of hostfile line */
	int ac;
	int err = 0;		/* error count */
	struct hostent *he;
	char *p;
	struct in_addr *my_in_addrs;
	int num_addrs;
	int i;
	struct hostd *defaults = 0;
	int maxhostid = tidhmask >> (ffs(tidhmask) - 1);

	htp = ht_new(1);

	/*
	* get list of this host's interfaces so we can detect ourself in file
	*/

	if (iflist(&my_in_addrs, &num_addrs) == -1 || num_addrs < 1) {
		sprintf(pvmtxt, "readhostfile() iflist failed\n");
		goto bail;
	}

	if (!(ff = fopen(fn, "r"))) {
		sprintf(pvmtxt, "readhostfile() %s: can't read\n", fn);
		pvmlogerror(pvmtxt);
		goto bail;
	}

	/* parse each line of host file */

	while (fgets(buf, sizeof(buf), ff)) {
		lnum++;
		for (p = buf; *p && isspace(*p); p++);

		if (!*p || *p == '#')	/* leading '#' is comment */
			continue;

		if (lnum > maxhostid) {
			pvmlogerror("readhostfile() %s: too many lines\n", fn);
			err++;
			break;
		}
		hp = hd_new(lnum);

		if (*p == '&') {		/* leading '&' is no-start */
			hp->hd_flag |= HF_NOSTART;
			p++;
		}

		ac = sizeof(av)/sizeof(av[0]);
		if (acav(p, &ac, av)) {
			sprintf(pvmtxt, "readhostfile() %s %d: line too long\n", fn, lnum);
			pvmlogerror(pvmtxt);
			err++;
			goto badline;
		}
		if (!ac)
			goto badline;

	/* add options to host descriptor */

		while (--ac > 0) {
			if (!strcmp(av[ac], "pw")) {
#ifdef NOREXEC
				sprintf(pvmtxt, "readhostfile() %s %d: can't do \"%s\"\n",
						fn, lnum, av[ac]);
				pvmlogerror(pvmtxt);
				err++;
#else
				hp->hd_flag |= HF_ASKPW;
#endif
				continue;
			}
			if (!strcmp(av[ac], "ms")) {
				hp->hd_flag |= HF_MANUAL;
				continue;
			}
			if (!strncmp(av[ac], "lo=", 3)) {
				if (hp->hd_login)
					PVM_FREE(hp->hd_login);
				hp->hd_login = STRALLOC(av[ac] + 3);
				continue;
			}
			if (!strncmp(av[ac], "dx=", 3)) {
				if (hp->hd_dpath)
					PVM_FREE(hp->hd_dpath);
				hp->hd_dpath = STRALLOC(av[ac] + 3);
				continue;
			}
			if (!strncmp(av[ac], "ep=", 3)) {
				if (hp->hd_epath)
					PVM_FREE(hp->hd_epath);
				hp->hd_epath = STRALLOC(av[ac] + 3);
				continue;
			}
			if (!strncmp(av[ac], "sp=", 3)) {
				hp->hd_speed = atoi(av[ac] + 3);
				continue;
			}
			if (!strncmp(av[ac], "bx=", 3)) {
				if (hp->hd_bpath)
					PVM_FREE(hp->hd_bpath);
				hp->hd_bpath = STRALLOC(av[ac] + 3);
				continue;
			}
			sprintf(pvmtxt, "readhostfile() %s %d: unknown option \"%s\"\n",
					fn, lnum, av[ac]);
			pvmlogerror(pvmtxt);
			err++;
		}

	/*
	* if host == "*", change the default options
	* instead of adding a new host to the list
	*/

		if (!strcmp(av[0], "*")) {
			if (defaults)
				hd_unref(defaults);
			defaults = hp;
			continue;
		}

	/* Set unspecified fields of hp to defaults */

		if (defaults) {
			hp->hd_flag |= defaults->hd_flag;

			if (!hp->hd_login && defaults->hd_login)
				hp->hd_login = STRALLOC(defaults->hd_login);

			if (!hp->hd_dpath && defaults->hd_dpath)
				hp->hd_dpath = STRALLOC(defaults->hd_dpath);

			if (!hp->hd_epath && defaults->hd_epath)
				hp->hd_epath = STRALLOC(defaults->hd_epath);

			if (!hp->hd_bpath && defaults->hd_bpath)
				hp->hd_bpath = STRALLOC(defaults->hd_bpath);
		}

		hp->hd_name = STRALLOC(av[0]);
/*
		hp->hd_arch = STRALLOC("");
*/

	/* look up ip addr */

		if (!(he = gethostbyname(hp->hd_name))) {
			sprintf(pvmtxt, "readhostfile() %s %d: %s: can't gethostbyname\n",
				fn, lnum, hp->hd_name);
			pvmlogerror(pvmtxt);
			err++;
			goto badline;
		}
		BCOPY(he->h_addr_list[0], (char*)&hp->hd_sad.sin_addr,
			sizeof(struct in_addr));

	/* mark master host to not start */

		for (i = num_addrs; i-- > 0; ) {
			if (BCMP((char*)&my_in_addrs[i], (char*)&hp->hd_sad.sin_addr,
					sizeof(struct in_addr)) == 0)
				break;
		}
		if (i >= 0)
			hp->hd_flag |= HF_NOSTART;

		ht_insert(htp, hp);
		hd_unref(hp);
		continue;

badline:
		hd_unref(hp);
	}
	if (err) {
		sprintf(pvmtxt, "readhostfile() %s: %d errors in hostfile\n", fn, err);
		pvmlogerror(pvmtxt);
	}

	if (defaults)
		hd_unref(defaults);
	fclose(ff);
	return htp;

bail:
	if (defaults)
		hd_unref(defaults);
	if (ff)
		fclose(ff);
	ht_free(htp);
	return (struct htab*)0;
}


/*	iflist()
*
*	Return list of addresses for active network interfaces.
*/

#ifdef	SIOCGIFCONF

int
iflist(alp, np)
	struct in_addr **alp;	/* return list of addresses */
	int *np;				/* return len of alp */
{
	int soc = -1;						/* socket */
	static struct in_addr *iplist = 0;	/* list of interface addrs found */
	int nip = 0;						/* length of iplist */
	char buf[4096];						/* return space for SIOCGIOCONF */
	struct ifconf sif;
	struct ifreq *reqp;
	struct ifreq req;
	char *cp;

	if (iplist)
		PVM_FREE(iplist);
	iplist = TALLOC(10, struct in_addr, "ifl");

	if ((soc = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
		perror("socket");
		goto bail;
	}

	sif.ifc_ifcu.ifcu_buf = buf;
	sif.ifc_len = sizeof(buf);
	if (ioctl(soc, SIOCGIFCONF, &sif) == -1) {
		perror("ioctl");
		goto bail;
	}

	for (cp = sif.ifc_ifcu.ifcu_buf;
			cp - sif.ifc_ifcu.ifcu_buf < sif.ifc_len;
			cp += sizeof(*reqp) - sizeof(struct sockaddr) + SIZ(reqp->ifr_addr))
	{
		reqp = (struct ifreq*)cp;
		if (reqp->ifr_addr.sa_family != AF_INET)
			continue;
		BCOPY(reqp->ifr_name, req.ifr_name, sizeof(req.ifr_name));
		if (ioctl(soc, SIOCGIFFLAGS, &req) == -1) {
			perror("ioctl");
			goto bail;
		}
		if (IFF_UP & req.ifr_ifru.ifru_flags) {
			if (nip > 0 && !(nip % 10))
				iplist = TREALLOC(iplist, (nip + 10), struct in_addr);
			iplist[nip++] =
			((struct sockaddr_in*)(&reqp->ifr_ifru.ifru_addr))->sin_addr;
			if (debugmask & PDMNET) {
				long a;

				a = ((struct sockaddr_in*)(&reqp->ifr_ifru.ifru_addr))->sin_addr.s_addr;
				a = ntohl(a);
				sprintf(pvmtxt, "iflist() %s %d.%d.%d.%d\n",
						reqp->ifr_name,
						0xff & (a >> 24),
						0xff & (a >> 16),
						0xff & (a >> 8),
						0xff & a);
				pvmlogerror(pvmtxt);
			}
		}
	}

	*alp = iplist;
	*np = nip;
	return 0;

bail:
	(void)close(soc);
	return -1;
}

#else	/*SIOCGIFCONF*/

int
iflist(alp, np)
	struct in_addr **alp;	/* return list of addresses */
	int *np;				/* return len of alp */
{
	static struct in_addr *iplist = 0;	/* list of interface addrs found */
	int nip = 0;						/* length of iplist */
	char hn[MAXHOSTNAMELEN];
	struct hostent *he;
	char **p;

	if (iplist)
		free(iplist);
	iplist = TALLOC(10, struct in_addr, "ifl");

	if (gethostname(hn, sizeof(hn))) {
		perror("gethostname");
		goto bail;
	}
	if (!(he = gethostbyname(hn))) {
		fprintf(stderr, "can't gethostbyname\n");
		goto bail;
	}
	for (; he->h_addr_list[nip]; nip++) {
		if (nip > 0 && !(nip % 10))
			iplist = TREALLOC(iplist, (nip + 10), struct in_addr);
		iplist[nip].s_addr = ((struct in_addr*)(he->h_addr_list[nip]))->s_addr;
	}

	*alp = iplist;
	*np = nip;
	return 0;

bail:
	return -1;
}

#endif	/*SIOCGIFCONF*/


/*	acav()
*
*	Parse a string into words separated by whitespace.
*	Max number of words is original value of *acp.
*
*	Trashes out the original string.
*	Returns 0 with av[0]..av[*acp - 1] pointing to the words.
*	Returns 1 if too many words.
*/

int
acav(s, acp, av)
	char *s;		/* the string to parse */
	int *acp;		/* max words in, ac out */
	char **av;		/* pointers to args */
{
	register int ac;
	register char *p = s;
	register n = *acp;

	/* separate out words of command */

	ac = 0;
	while (*p) {
		while (isspace(*p)) p++;
		if (*p) {
			if (ac >= n) {
	/* command too long */
				*acp = ac;
				return 1;
			}
			av[ac++] = p;
			while (*p && !isspace(*p)) p++;
			if (*p) *p++ = 0;
		}
	}
	*acp = ac;
	return 0;
}


