/*
 * Copyright (c) 2001-2003 The Trustees of Indiana University.  
 *                         All rights reserved.
 * Copyright (c) 1998-2001 University of Notre Dame. 
 *                         All rights reserved.
 * Copyright (c) 1994-1998 The Ohio State University.  
 *                         All rights reserved.
 * 
 * This file is part of the LAM/MPI software package.  For license
 * information, see the LICENSE file in the top level directory of the
 * LAM/MPI source distribution.
 * 
 * $HEADER$
 *
 * $Id: psend.c,v 1.6 2003/02/08 17:48:36 jsquyres 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 <etc_misc.h>
#include <net.h>
#include <portable.h>
#include <t_types.h>
#include <terror.h>
#include <typical.h>
#include <vctable.h>
#include <laminternal.h>
#include <lam_network.h>
#include <etc_misc.h>
#include <rreq.h>
#include <sfh.h>


/*
 * external functions
 */
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(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 (lam_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, -1)) < 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] = -lam_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 = -lam_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(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 < (int)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(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));
}


syntax highlighted by Code2HTML, v. 0.9.1