/*
 *
 * 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 1997 The Ohio State University
 *	GDB/RBD
 *
 *	$Id: di_bootagent.c,v 6.6 1999/08/14 22:57:17 lamteam Exp $
 *
 *	Function:	- obtains link information from boot agent
 *	Accepts:	- link array (value returned)
 *			- link array size (value returned)
 */

#include <lam_config.h>
#include <sfh.h>

#include <netdb.h>			/* Solaris MAXHOSTNAMELEN */
#include <stdio.h>
#include <stdlib.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <unistd.h>

#include <args.h>
#include <dl_inet.h>
#include <laminternal.h>
#include <terror.h>
#include <typical.h>

/*
 * extern functions
 */
extern int		getinetaddr();
extern int		readsockint4();
extern int		readcltnbr();
extern int		writecltcoord();
extern int		writesockint4();

/*
 * local functions
 */
static void		gethaddr();	/* parses inet address */
static void		reply2agent();	/* sends error to boot agent */

/*
 * local variables
 */
static char		my_hname[MAXHOSTNAMELEN + 1];
static unsigned char	agent_haddr[4];	/* boot agent host address */
static int		agent_port;

/*
 *	di_bootagent
 *
 *	Function:	- obtains link information from boot agent
 *	Accepts:	- link array (value returned)
 *			- link array size (value returned)
 *	Returns:	- datalink input socket
 */
int
di_bootagent(pdilinks, pndil)

struct dilink		**pdilinks;
int4			*pndil;

{
	int4		i;
	int4		ngbr_link;	/* neighbour link # */
	int4		ngbr_port;	/* neighbour port # */
	unsigned char	ngbr_haddr[4];	/* neighbour host address */
	unsigned char	my_haddr[4];	/* my host address */
	int		agent_sd;	/* connection to boot agent */
	int		boot_port;	/* my server port for booting */
	int		boot_sd;	/* my server socket for booting */
	int		dli_port;	/* datalink input port */
	int		dli_sd;		/* datalink input socket */

	dli_port = 0;
/*
 * Create the inter-node communication socket.
 */
	if ((dli_sd = sfh_sock_open_srv_inet_dgm(&dli_port)) < 0) {
		lampanic("dli_inet (sfh_sock_open_srv_inet_dgm)");
	}

	if (sfh_sock_set_buf_size(dli_sd, SFH_INET, SO_RCVBUF, 
	    		sizeof(struct dlframe) * DOMAXPENDING)) {
		lampanic("dli_inet (sfh_sock_set_buf_size)");
	}

	gethaddr(getparam('H'), agent_haddr);
/*
 * Get the boot agent's location from the command line.
 */
	if (opt_taken('P')) {
		intparam('P', &agent_port);
	} else {
		errno = EUSAGE;
		lampanic("dli_inet: agent port not defined");
	}
/*
 * Get my host name and network address.
 */
	if (gethostname(my_hname, MAXHOSTNAMELEN + 1)) reply2agent(errno);

	if (getinetaddr(my_hname, my_haddr)) reply2agent(errno);
/*
 * Establish a server socket to receive link information.
 */
	boot_port = 0;
	boot_sd = sfh_sock_open_srv_inet_stm(&boot_port);
	if (boot_sd < 0) reply2agent(errno);
/*
 * Connect to the boot agent (blocking).
 */
	agent_sd = sfh_sock_open_clt_inet_stm(agent_haddr, agent_port);
	if (agent_sd < 0) lampanic("dli_inet (sfh_sock_open_clt_inet_stm)");
/*
 * Send my coordinates to the boot agent.
 */
	if (writecltcoord(agent_sd, (int4) boot_port, (int4) dli_port))
			lampanic("dli_inet (writecltcoord)");

	if (close(agent_sd)) lampanic("dli_inet (writecltcoord)");
/*
 * Accept a connection from the boot agent (blocking).
 */
	agent_sd = sfh_sock_accept_tmout(boot_sd, -1);
	if (agent_sd < 0) lampanic("dli_inet (sfh_sock_accept_tmout)");
/*
 * Read the number of links.
 */
	if (readsockint4(agent_sd, pndil)) lampanic("dli_inet (readsockint4)");
/*
 * Create the links array.
 */
	*pdilinks = (struct dilink *) malloc((unsigned) *pndil *
			sizeof(struct dilink));
	if (*pdilinks == 0) lampanic("dli_inet (malloc)");
/*
 * Init all entries to invalid link.
 */
	for (i = 0; i < *pndil; ++i) {
		(*pdilinks)[i].dil_link = NOTLINKID;
		(*pdilinks)[i].dil_seqrecv = 0;
	}

	for (i = 0; i < *pndil; ++i) {
		if (readcltnbr(agent_sd, &ngbr_link, ngbr_haddr,
				&ngbr_port)) lampanic("dli_inet (readcltnbr)");
		(*pdilinks)[i].dil_link = ngbr_link;
		sfh_sock_fill_inet_addr(ngbr_haddr, (int) ngbr_port,
				&((*pdilinks)[i].dil_addr));
	}

	if (close(agent_sd)) lampanic("dli_inet (close)");
	if (close(boot_sd)) lampanic("dli_inet (close)");

	return(dli_sd);
}

/*
 *	gethaddr
 *
 *	Function:	- parses Internet address from string
 *			- can be a host name or a dotted-decimal number.
 *	Accepts:	- string
 *			- ptr Internet address
 */
static void
gethaddr(token, haddr)

char			*token;
unsigned char		*haddr;

{
	unsigned int	a0, a1, a2, a3;
/*
 * Assume the address is given in dotted-decimal format.
 * If not, try it as an alpha-numeric hostname.
 */
	if (sscanf(token, "%u.%u.%u.%u", &a0, &a1, &a2, &a3) == 4) {
		haddr[0] = a0;
		haddr[1] = a1;
		haddr[2] = a2;
		haddr[3] = a3;
	}
/*
 * Address is in hostname format.
 */
	else {
		if (getinetaddr(token, haddr))
				lampanic("dli_inet (getinetaddr)");
	}
}

/*
 *	reply2agent
 *
 *	Function:	- sends reply error code to boot agent
 *	Accepts:	- error code
 */
static void
reply2agent(errcode)

int			errcode;

{
	int		agent_sd;	/* connection to agent */
/*
 * Connect to the boot agent (blocking).
 */
	agent_sd = sfh_sock_open_clt_inet_stm(agent_haddr, agent_port);
	if (agent_sd < 0) lampanic("dli_inet (sfh_sock_open_clt_inet_stm)");

	if (writesockint4(agent_sd, (int4) errcode))
			lampanic("dli_inet (writesockint4)");

	if (close(agent_sd)) lampanic("dli_inet (writesockint4)");
}
