/*
 *
 * 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.
 *
 *	Software for Humanity
 *	NJN
 *
 *	This program is freely distributed in the hope that it will be useful,
 *	but WITHOUT ANY WARRANTY; without even the implied warranty of
 *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 *
 *	$Id: srfd.c,v 6.6 1999/09/10 05:09:19 jsquyres Exp $
 *
 *	Function:	- file descriptor passing
 */

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

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/uio.h>
#include <errno.h>
#include <stddef.h>

#if LAM_HAVE_BSD44_FD_PASSING

#include <sys/un.h>

#define	CONTROLLEN	(sizeof(struct cmsghdr) + sizeof(int))

typedef struct cmsgfd {
	struct cmsghdr	ctl;			/* control message header */
	int		dummy;			/* space for file descriptor */
} cmsgfd_t;

/*
 * The only OS we have come across which supports BSD44 fd passing
 * and does not define CMSG_DATA is Linux.  
 */
#ifndef CMSG_DATA
#define	CMSG_DATA(c)	((void *) ((c)->cmsg_data))
#endif

#elif LAM_HAVE_SVR4_FD_PASSING

#include <stropts.h>

#endif


/*
 *	sfh_send_fd
 *
 *	Function:	- pass a single file descriptor over a stream
 *			- based on code from Stevens, "Advanced
 *			  Programming in the Unix Environment"
 *	Accepts:	- stream file descriptor
 *			- file descriptor to pass
 *	Returns:	- 0 (success) or -1 (error)
 */

#if LAM_HAVE_BSD43_FD_PASSING

int
sfh_send_fd(stream, fd)

int			stream;
int			fd;

{
	struct iovec	iov[1];
	struct msghdr	msg;
	char		buf[1];
/*
 * Pass one char and access rights.
 */
	iov[0].iov_base = buf;
	iov[0].iov_len = 1;
	msg.msg_iov = iov;
	msg.msg_iovlen = 1;
	msg.msg_name = (caddr_t) 0;
	msg.msg_namelen = 0;
	msg.msg_accrights = (caddr_t) &fd;
	msg.msg_accrightslen = sizeof(int);

	if (sendmsg(stream, &msg, 0) != 1) {
		return(-1);
	}

	return(0);
}

#elif LAM_HAVE_BSD44_FD_PASSING

int
sfh_send_fd(stream, fd)

int			stream;
int			fd;

{
	struct iovec	iov[1];
	struct msghdr	msg;
	char		buf[1];
	cmsgfd_t	ctlfd;			/* control plus file desc. */
/*
 * Pass one char and control message.
 */
	iov[0].iov_base = buf;
	iov[0].iov_len = 1;
	msg.msg_iov = iov;
	msg.msg_iovlen = 1;
	msg.msg_name = NULL;
	msg.msg_namelen = 0;
	msg.msg_control = (caddr_t) &ctlfd;
	msg.msg_controllen = CONTROLLEN;

	ctlfd.ctl.cmsg_level = SOL_SOCKET;
	ctlfd.ctl.cmsg_type = SCM_RIGHTS;
	ctlfd.ctl.cmsg_len = CONTROLLEN;
	*(int *)CMSG_DATA(&ctlfd.ctl) = fd;

	if (sendmsg(stream, &msg, 0) != 1) {
		return(-1);
	}

	return(0);
}

#elif LAM_HAVE_SVR4_FD_PASSING

int
sfh_send_fd(stream, fd)

int			stream;
int			fd;

{
	char		buf[1];
/*
 * Send one char and the file descriptor.
 */
	if (write(stream, buf, 1) != 1) {
		return(-1);
	}

	if (ioctl(stream, I_SENDFD, fd) < 0) {
		return(-1);
	}

	return(0);
}

#else

/*
 * Not supported.
 */
int
sfh_send_fd(stream, fd)

int			stream;
int			fd;

{
#if defined(ENOTSUP)
	errno = ENOTSUP;
#else
	errno = ENOSYS;
#endif
	return(-1);
}

#endif


/*
 *	sfh_recv_fd
 *
 *	Function:	- receive a single file descriptor from a stream
 *			- based on code from Stevens, "Advanced
 *			  Programming in the Unix Environment"
 *	Accepts:	- stream file descriptor
 *			- file descriptor to pass
 *	Returns:	- 0 (success) or -1 (error)
 */

#if LAM_HAVE_BSD43_FD_PASSING

int
sfh_recv_fd(stream)

int			stream;

{
	int		newfd = -1;
	struct msghdr	msg;
	struct iovec	iov[1];
	char		buf[1];
/*
 * Receive one char and access rights.
 */
	iov[0].iov_base = buf;
	iov[0].iov_len = 1;
	msg.msg_iov = iov;
	msg.msg_iovlen = 1;
	msg.msg_name = (caddr_t) 0;
	msg.msg_namelen = 0;
	msg.msg_accrights = (caddr_t) &newfd;
	msg.msg_accrightslen = sizeof(int);

	if (recvmsg(stream, &msg, 0) != 1) {
		return(-1);
	}

	if (msg.msg_accrightslen != sizeof(int)) {
		return(-1);
	}

	return(newfd);
}

#elif LAM_HAVE_BSD44_FD_PASSING

int
sfh_recv_fd(stream)

int			stream;

{
	struct msghdr	msg;
	struct iovec	iov[1];
	char		buf[1];
	cmsgfd_t	ctlfd;			/* control plus file desc. */
/*
 * Receive one char and control message.
 */
	iov[0].iov_base = buf;
	iov[0].iov_len = 1;
	msg.msg_iov = iov;
	msg.msg_iovlen = 1;
	msg.msg_name = (caddr_t) 0;
	msg.msg_namelen = 0;
	msg.msg_control = (caddr_t) &ctlfd.ctl;
	msg.msg_controllen = CONTROLLEN;

	if (recvmsg(stream, &msg, 0) != 1) {
		return(-1);
	}

	return(*(int *)CMSG_DATA(&ctlfd.ctl));
}

#elif LAM_HAVE_SVR4_FD_PASSING

int
sfh_recv_fd(stream)

int			stream;

{
	int		flag;
	char		buf[1];
	struct strbuf	dat;
	struct strrecvfd
			recvfd;
/*
 * Receive one char and the file descriptor.
 */
	dat.buf = buf;
	dat.maxlen = 1;
	flag = 0;

	if (getmsg(stream, (void *) 0, &dat, &flag) < 0) {
		return(-1);
	}

	if (ioctl(stream, I_RECVFD, &recvfd) < 0) {
		return(-1);
	}

	return(recvfd.fd);
}

#else

/*
 * Not supported.
 */
int
sfh_recv_fd(stream)

int			stream;

{
#if defined(ENOTSUP)
	errno = ENOTSUP;
#else
	errno = ENOSYS;
#endif
	return(-1);
}

#endif
