/*
 *
 * Copyright 1998-1999, University of Notre Dame.
 * Authors: Jeffrey M. Squyres, Kinis L. Meyer with M. D. McNally 
 *          and Andrew Lumsdaine
 *
 * This file is part of the Notre Dame LAM implementation of MPI.
 *
 * You should have received a copy of the License Agreement for the
 * Notre Dame LAM implementation of MPI along with the software; see
 * the file LICENSE.  If not, contact Office of Research, University
 * of Notre Dame, Notre Dame, IN 46556.
 *
 * Permission to modify the code and to distribute modified code is
 * granted, provided the text of this NOTICE is retained, a notice that
 * the code was modified is included with the above COPYRIGHT NOTICE and
 * with the COPYRIGHT NOTICE in the LICENSE file, and that the LICENSE
 * file is distributed with the modified code.
 *
 * LICENSOR MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED.
 * By way of example, but not limitation, Licensor MAKES NO
 * REPRESENTATIONS OR WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY
 * PARTICULAR PURPOSE OR THAT THE USE OF THE LICENSED SOFTWARE COMPONENTS
 * OR DOCUMENTATION WILL NOT INFRINGE ANY PATENTS, COPYRIGHTS, TRADEMARKS
 * OR OTHER RIGHTS.  
 *
 * Additional copyrights may follow.
 *
 *	Ohio Trollius
 *	Copyright 1996 The Ohio State University
 *	RBD/GDB
 *
 *	$Id: lamnet.c,v 6.4 1999/07/28 00:31:52 jsquyres Exp $
 * 
 *	Function:	- LAM network functions
 */

#include <lam_config.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#ifndef SIOCGIFCONF
#include <sys/sockio.h>
#endif
#include <sys/time.h>
#include <net/if.h>
#include <netinet/in.h>
#include <errno.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <unistd.h>

#include <laminternal.h>
#include <lamnet.h>
#include <portable.h>
#include <terror.h>
#include <typical.h>

#define LAMIFMAX	32

/*
 * global functions
 */
int			lamnet_findhosts();
int			lamnet_findorig();
int			lamnet_dups();

/*
 * local functions
 */
static int		getifaddr();

/*
 * local variables
 */
static char		origname[MAXHOSTNAMELEN + 1];
static unsigned long	ifbuf[256];

/*
 *	lamnet_findhosts
 *
 *	Function:	- gets the network address of each host
 *			- "localhost" is changed to the local hostname
 *	Accepts:	- node description array
 *			- array size
 *			- bad host index (out)
 */
int
lamnet_findhosts(lamnet, nlamnet, badhost)

struct lamnode		*lamnet;
int			nlamnet;
int			*badhost;

{
	int		i;

	if (badhost) *badhost = -1;
/*
 * Get the hostname of the machine the tool is running on.
 */
	if (gethostname(origname, MAXHOSTNAMELEN)) return(LAMERROR);
/*
 * Find INET addresses for each node.
 */
	for (i = 0; i < nlamnet; ++i) {
/*
 * Change "localhost" to the real host name.
 */
		if (! strcmp(lamnet[i].lnd_hname, "localhost")) {
			free(lamnet[i].lnd_hname);
			lamnet[i].lnd_hname = malloc((unsigned)
					strlen(origname) + 1);
			if (lamnet[i].lnd_hname == 0) return(LAMERROR);
			strcpy(lamnet[i].lnd_hname, origname);
		}
/*
 * Get the host network address.
 */
		if (getinetaddr(lamnet[i].lnd_hname,
			(unsigned char *) &lamnet[i].lnd_addr.sin_addr)) {

		    if (badhost) *badhost = i;
		    return(LAMERROR);
		}

		lamnet[i].lnd_addr.sin_family = AF_INET;
	}

	return(0);
}

/*
 *	lamnet_findorig
 *
 *	Function:	- finds the origin host node
 *			- origin is the machine this code is running on
 *			- calls lamnet_findhosts() if necessary
 *			- do matching on the network addresses
 *			- make sure each host is unique (no duplicates)
 *	Accepts:	- network description array
 *			- array size
 *	Returns:	- index of origin or LAMERROR
 */
int
lamnet_findorig(lamnet, nlamnet)

struct lamnode		*lamnet;
int			nlamnet;

{
	int		i, j;
	int		nifaddr;	/* # of interface addresses */
	uint4		ifaddr[LAMIFMAX];

	nifaddr = getifaddr(ifaddr, LAMIFMAX);
	if (nifaddr < 0) return(LAMERROR);
/*
 * Loop over the host entries, comparing their addresses to
 * our local host interface addresses.
 */
	for (i = 0; i < nlamnet; ++i) {

		for (j = 0; j < nifaddr; ++j) {

			if (ifaddr[j] == lamnet[i].lnd_addr.sin_addr.s_addr) {
				return(i);
			}
		}
	}

	errno = EINVAL;
	return(LAMERROR);
}

/*
 *	lamnet_dups
 *
 *	Function:	- checks for duplicate host entries
 *			- O(n^2) algorithm used for now (n is small)
 *	Accepts:	- network description array
 *			- array size
 *			- index of duplicated entry
 *	Returns:	- TRUE if duplicates, else FALSE
 */
int
lamnet_dups(lamnet, nlamnet, pdup)

struct lamnode		*lamnet;
int			nlamnet;
int			*pdup;

{
	int		i, j;
/*
 * Loop over all nodes.
 */
	for (i = 0; i < nlamnet; ++i) {
/*
 * Loop over the subsequent nodes.
 */
	    for (j = i + 1; j < nlamnet; ++j) {

		if ((lamnet[i].lnd_addr.sin_addr.s_addr ==
			lamnet[j].lnd_addr.sin_addr.s_addr) &&
			((lamnet[i].lnd_uname == lamnet[j].lnd_uname) ||
			((lamnet[i].lnd_uname != 0) &&
			(lamnet[j].lnd_uname != 0) &&
			!strcmp(lamnet[i].lnd_uname, lamnet[j].lnd_uname)))) {
		    *pdup = i;
		    return(TRUE);
		}
	    }
	}

	return(FALSE);
}

/*
 *	getifaddr
 *
 *	Function:	- obtains all up interface addresses for local host
 *	Accepts:	- INET address array (returned addresses)
 *			- max array size
 *	Returns:	- number of addresses or LAMERROR
 */
static int
getifaddr(ifaddr, nifaddr)

uint4			*ifaddr;
int			nifaddr;

{
	int		sock;
	int		length;
	int		num;
	int		rem;
	char		*p;
	struct ifconf	config;
	struct ifreq	req, *preq;
	struct sockaddr_in
			*psin;
/*
 * Create an Internet socket.
 */
	sock = socket(AF_INET, SOCK_DGRAM, 0);
	if (sock < 0) return(LAMERROR);
/*
 * Get network interface configuration.
 */
	config.ifc_len = sizeof(ifbuf);
	config.ifc_req = (struct ifreq *) ifbuf;

	if (ioctl(sock, SIOCGIFCONF, &config)) {
		close(sock);
		return(LAMERROR);
	}
/*
 * Loop through all interfaces.
 */
	p = (char *) ifbuf;
	rem = config.ifc_len;
	num = 0;

	while ((rem > 0) && (nifaddr > 0)) {
		preq = (struct ifreq *) p;
#if LAM_HAVE_SA_LEN
		length = sizeof(struct sockaddr);

		if (preq->ifr_addr.sa_len > length) {
			length = preq->ifr_addr.sa_len;
		}

		length += sizeof(preq->ifr_name);
#else
		length = sizeof(struct ifreq);
#endif

		rem -= length;
		p += length;

		if (preq->ifr_addr.sa_family != AF_INET) continue;

		memcpy(req.ifr_name, preq->ifr_name, sizeof(req.ifr_name));

		if (ioctl(sock, SIOCGIFFLAGS, &req)) {
			close(sock);
			return(LAMERROR);
		}

		if (req.ifr_flags & IFF_UP) {

			psin = (struct sockaddr_in *) &(preq->ifr_addr);
			*ifaddr = psin->sin_addr.s_addr;
			num++;
			ifaddr++;
			nifaddr--;
		}
	}

	close(sock);
	return(num);
}
