/*
 *
 * 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: precv.c,v 6.3 1999/06/12 18:04:35 kmeyer1 Exp $
 *
 *	Function:	- physical layer receive
 *			- receive messages on virtual circuits
 *			- OTB version (UNIX, STREAM sockets)
 *
 *			- precvopen(): create VC to receive messages
 *			- precv(): receive message on VC
 *			- precvclose(): destroy receiver's VC
 */

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

#include <errno.h>
#include <string.h>
#include <unistd.h>

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

#ifndef TO_PRECVO_CONN
#define TO_PRECVO_CONN	100
#endif

/*
 * external variables
 */
extern int		errno;

/*
 * external functions
 */
extern void		_ltot_usrnmsg();
extern void		_ltot_sysnmsg();
extern void		_ttol_usrnmsg();
extern void		_ttol_sysnmsg();
extern int4		mread();
extern int		sfh_sock_open_clt_inet_stm();
extern int		_vcdelete();
extern int		_vcinsert();
extern struct vcdesc	*_vcfind();

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

struct nmsg		*nhead;

{
	int		vcsock;		/* VC socket */
	int		sndport;	/* sender port # */
	int		i;		/* favourite counter */
	unsigned char	sndaddr[4];	/* sender address */
	struct nmsg	protocol;	/* protocol message */
/*
 * Receive the virtual circuit protocol message.
 */
	protocol.nh_event = nhead->nh_event;
	protocol.nh_type = nhead->nh_type;
	protocol.nh_flags = 0;
	protocol.nh_length = 0;
	protocol.nh_msg = 0;

	if (nrecv(&protocol)) {
		return(ERROR);
	}
/*
 * Modify the receiver's nh_type field to match the synchronization.
 */
	nhead->nh_type = protocol.nh_type;
/*
 * Let the sender know the receiver is ready.
 */
	memcpy((char *) sndaddr, (char *) protocol.nh_data, 4);
	ttoli4(&protocol.nh_data[1], &sndport);

	ttoli4(&protocol.nh_data[2], &protocol.nh_node);
	ttoli4(&protocol.nh_data[3], &protocol.nh_event);
	protocol.nh_type = 0;

	protocol.nh_data[0] = -getpid();

	if (nsend(&protocol)) {
		return(ERROR);
	}
/*
 * Receive the OK-to-connect message from the sender.
 */
	protocol.nh_event = -getpid();

	if (nrecv(&protocol)) {
		return(ERROR);
	}
/*
 * Connect the virtual circuit.
 * Keep trying till success or timeout.
 */
	for (i = 0; i < TO_PRECVO_CONN; ++i) {
		if ((vcsock =
			sfh_sock_open_clt_inet_stm(sndaddr, sndport)) >= 0) {

			break;
		}
	}

	if (vcsock < 0) {
		return(ERROR);
	}
/*
 * Insert the virtual circuit in the VC table.
 */
	if (_vcinsert(LOCAL, nhead->nh_event, nhead->nh_type, vcsock)) {
		close(vcsock);
		return(ERROR);
	}

	return(0);
}

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

struct nmsg		*nhead;

{
	struct nmsg	incoming;		/* incoming header */
	struct vcdesc	*p;			/* favourite pointer */
	int4		extra_len;		/* extra msg length */
	int4		nbytes;			/* # bytes read */
	int4		save_flags;		/* saved nh_flags */
	char		bitbucket;		/* to trash extra msg */
/*
 * Locate the virtual circuit in the VC table.
 */
	p = _vcfind(LOCAL, nhead->nh_event, nhead->nh_type);
	if (p == 0) {
		return(ERROR);
	}
/*
 * Receive the incoming message header.
 */
	nbytes = mread(p->vc_path, (char *) &incoming, sizeof(struct nmsg));
	if (nbytes < sizeof(struct nmsg)) {
		errno = (nbytes < 0) ? errno : EIO;
		return(ERROR);
	}
/*
 * Convert the system part of the header to local byte order.
 */
	_ttol_sysnmsg(&incoming);
/*
 * Set the message length.
 */
	if (nhead->nh_length > incoming.nh_length) {
		nhead->nh_length = incoming.nh_length;
	}
/*
 * Copy the received data pouch as is (i.e. in Trollius byte order).
 */
	memcpy((char *) nhead->nh_data,
		(char *) incoming.nh_data, NHDSIZE * sizeof(int4));
/*
 * Receive the user's message.
 */
	if (nhead->nh_length > 0) {
		nbytes = mread(p->vc_path, nhead->nh_msg, nhead->nh_length);
		if (nbytes < nhead->nh_length) {
			errno = (nbytes < 0) ? errno : EIO;
			return(ERROR);
		}
	}
/*
 * Use the incoming nh_flags to convert the message to local byte order.
 * Restore the original nh_flags as given by the caller.
 */
	save_flags = nhead->nh_flags;
	nhead->nh_flags = incoming.nh_flags;
	_ttol_usrnmsg(nhead);
	nhead->nh_flags = save_flags;
/*
 * Receive and trash any unused portion of the message sent.
 */
	extra_len = incoming.nh_length - nhead->nh_length;

	for ( ; extra_len > 0; --extra_len) {
		nbytes = mread(p->vc_path, &bitbucket, (int4) 1);
		if (nbytes < 1) {
			errno = (nbytes < 0) ? errno : EIO;
			return(ERROR);
		}
	}

	return(0);
}

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

struct nmsg		*nhead;

{
	struct vcdesc	*p;			/* favourite pointer */
/*
 * Locate the virtual circuit entry in the VC table.
 */
	p = _vcfind(LOCAL, 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(LOCAL, nhead->nh_event, nhead->nh_type));
}
