/*
 *
 * 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
 *	RBD
 *
 *	$Id: psend.c,v 6.4 1999/06/12 18:04:35 kmeyer1 Exp $
 *
 *	Function:	- physical layer send
 *			- send messages on virtual circuits
 *			- OTB version (UNIX, STREAM sockets)
 *
 *			- psendopen(): create VC to send messages
 *			- psend(): send message on VC
 *			- psendclose(): destroy sender's VC
 */

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

#include <errno.h>
#include <signal.h>
#include <string.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <netdb.h>				/* Solaris MAXHOSTNAMELEN */
#include <unistd.h>

#include <net.h>
#include <portable.h>
#include <t_types.h>
#include <terror.h>
#include <typical.h>
#include <vctable.h>

/*
 * external functions
 */
extern void		_ltot_usrnmsg();
extern void		_ltot_sysnmsg();
extern void		_ttol_usrnmsg();
extern void		_ttol_sysnmsg();
extern int4		getrtype();
extern int4		mwrite();
extern int		gethostname();
extern int		getinetaddr();
extern int		sfh_sock_open_srv_inet_stm();
extern int		sfh_sock_accept_tmout();
extern int		_vcdelete();
extern int		_vcinsert();
extern struct vcdesc	*_vcfind();

/*
 *	psendopen
 *
 *	Function:	- create a virtual circuit for the sender
 *			- works only between different OTB nodes
 *	Accepts:	- ptr network message descriptor
 *	Returns:	- 0 or ERROR
 */
int
psendopen(nhead)

struct nmsg		*nhead;

{
	int		sock;			/* main socket */
	int		vcsock;			/* VC socket */
	int		port;			/* port # */
	int		flag;			/* socket option flag */
	int4		nodetype;		/* destination node type */
	char		host[MAXHOSTNAMELEN];	/* host name */
	unsigned char	hostaddr[4];		/* host address */
	struct nmsg	protocol;		/* protocol message */
	sigset_t	newset;			/* new signal set */
	sigset_t	oldset;			/* old signal set */
/*
 * The destination node must be a different OTB node.
 */
	if ((nhead->nh_node == LOCAL) || (nhead->nh_node == getnodeid())) {
		errno = EBADNODE;
		return(ERROR);
	}

	nodetype = getrtype(nhead->nh_node);
	if ((nodetype == NOTNODETYPE) || tisitb(nodetype)) {
		errno = EBADNODE;
		return(ERROR);
	}
/*
 * Get the host network address.
 */
	if (gethostname(host, MAXHOSTNAMELEN)) {
		return(ERROR);
	}
	if (getinetaddr(host, hostaddr)) {
		return(ERROR);
	}
/*
 * Create a server side socket.
 */
	port = 0;
	if ((sock = sfh_sock_open_srv_inet_stm(&port)) < 0) {
		return(ERROR);
	}
/*
 * Send the VC protocol message.
 * nh_data[0]: host network address in network byte order
 * nh_data[1]: host port number in Trollius byte order
 * nh_data[2]: host nodeid in Trollius byte order
 * nh_data[3]: host negative PID in Trollius byte order
 */
	protocol.nh_node = nhead->nh_node;
	protocol.nh_event = nhead->nh_event;
	protocol.nh_type = nhead->nh_type;
	protocol.nh_flags = DRAWDATA;
	protocol.nh_length = 0;
	protocol.nh_msg = 0;

	memcpy((char *) protocol.nh_data, (char *) hostaddr, 4);
	protocol.nh_data[1] = port;
	protocol.nh_data[2] = getnodeid();
	protocol.nh_data[3] = -getpid();
	ltoti4(&protocol.nh_data[1], &protocol.nh_data[1]);
	ltoti4(&protocol.nh_data[2], &protocol.nh_data[2]);
	ltoti4(&protocol.nh_data[3], &protocol.nh_data[3]);

	if (nsend(&protocol)) {
		close(sock);
		return(ERROR);
	}
/*
 * Wait for the receiver to be ready then accept the connection.
 */
	protocol.nh_event = -getpid();
	protocol.nh_type = 0;
	protocol.nh_flags = 0;

	if (nrecv(&protocol)) {
		close(sock);
		return(ERROR);
	}
/*
 * Send an OK-to-connect to the receiver.
 * Block the SIGUSR1 signal.
 */
	protocol.nh_node = nhead->nh_node;
	protocol.nh_event = protocol.nh_data[0];

	sigemptyset(&newset);
	sigaddset(&newset, LAM_SIGUSR);
	sigprocmask(SIG_BLOCK, &newset, &oldset);

	if (nsend(&protocol)) {
		sigprocmask(SIG_SETMASK, &oldset, (sigset_t *) 0);
		close(sock);
		return(ERROR);
	}
/*
 * Block accepting the connection. 
 */
	if ((vcsock = sfh_sock_accept_tmout(sock, -1)) < 0) {
		sigprocmask(SIG_SETMASK, &oldset, (sigset_t *) 0);
		close(sock);
		return(ERROR);
	}

	sigprocmask(SIG_SETMASK, &oldset, (sigset_t *) 0);
	close(sock);
/*
 * Don't delay sending on the socket.
 */
	flag = 1;
	if (setsockopt(vcsock,
			IPPROTO_TCP, TCP_NODELAY,
			(char *) &flag, sizeof(flag))) {
		close(vcsock);
		return(ERROR);
	}
/*
 * Insert the virtual circuit in the VC table.
 */
	if (_vcinsert(nhead->nh_node,
			nhead->nh_event, nhead->nh_type, vcsock)) {
		close(vcsock);
		return(ERROR);
	}

	return(0);
}

/*
 *	psend
 *
 *	Function:	- send a network message on a virtual circuit
 *	Accepts:	- ptr network message descriptor
 *	Returns:	- 0 or ERROR
 */
int
psend(nhead)

struct nmsg		*nhead;

{
	struct vcdesc	*p;			/* favourite pointer */
	char		*msg;			/* ptr to message */
	int4		msglen;			/* message length */
	int4		nbytes;			/* # of bytes sent */
/*
 * Locate the virtual circuit in the VC table.
 */
	p = _vcfind(nhead->nh_node, nhead->nh_event, nhead->nh_type);
	if (p == 0) {
		return(ERROR);
	}
/*
 * Convert the message to Trollius byte order.
 */
	msg = nhead->nh_msg;
	msglen = nhead->nh_length;

	_ltot_usrnmsg(nhead);
	_ltot_sysnmsg(nhead);
/*
 * Send the message on the virtual circuit.
 */
	nbytes = mwrite(p->vc_path, (char *) nhead, sizeof(struct nmsg));
	if (nbytes < sizeof(struct nmsg)) {
		_ttol_sysnmsg(nhead);
		_ttol_usrnmsg(nhead);
		errno = (nbytes < 0) ? errno : EIO;
		return(ERROR);
	}

	if (msglen > 0) {
		nbytes = mwrite(p->vc_path, msg, msglen);
		if (nbytes < msglen) {
			_ttol_sysnmsg(nhead);
			_ttol_usrnmsg(nhead);
			errno = (nbytes < 0) ? errno : EIO;
			return(ERROR);
		}
	}
/*
 * Convert the message back to local byte order.
 */
	_ttol_sysnmsg(nhead);
	_ttol_usrnmsg(nhead);

	return(0);
}

/*
 *	psendclose
 *
 *	Function:	- close a virtual circuit for the sender
 *	Accepts:	- ptr network message descriptor
 *	Returns:	- 0 or ERROR
 */
int
psendclose(nhead)

struct nmsg		*nhead;

{
	struct vcdesc	*p;			/* favourite pointer */
/*
 * Locate the virtual circuit entry in the VC table.
 */
	p = _vcfind(nhead->nh_node, nhead->nh_event, nhead->nh_type);
	if (p == 0) {
		return(ERROR);
	}
/*
 * Close the VC socket and delete the table entry.
 */
	close(p->vc_path);

	return(_vcdelete(nhead->nh_node, nhead->nh_event, nhead->nh_type));
}
