/***********************************************************************
*                                                                      *
*               This software is part of the ast package               *
*           Copyright (c) 1990-2006 AT&T Knowledge Ventures            *
*                      and is licensed under the                       *
*                  Common Public License, Version 1.0                  *
*                      by AT&T Knowledge Ventures                      *
*                                                                      *
*                A copy of the License is available at                 *
*            http://www.opensource.org/licenses/cpl1.0.txt             *
*         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
*                                                                      *
*              Information and Software Systems Research               *
*                            AT&T Research                             *
*                           Florham Park NJ                            *
*                                                                      *
*                 Glenn Fowler <gsf@research.att.com>                  *
*                                                                      *
***********************************************************************/
#pragma prototyped
/*
 * Glenn Fowler
 * AT&T Research
 *
 * send n fds to stream fd
 */

#include "cslib.h"

int
cssend(register Cs_t* state, int fd, int* fds, int n)
{

#if CS_LIB_STREAM || CS_LIB_V10

	register int	i;
	struct csfdhdr	hdr;

	if (n > 0)
	{
#if CS_LIB_STREAM
		struct strbuf	dat;
#endif
		csprotect(&cs);
		hdr.count = n;
		hdr.pid = getpid();
#if CS_LIB_STREAM
		dat.buf = "";
		dat.len = 0;
		if (putmsg(fd, NiL, &dat, 0) < 0 || write(fd, &hdr, sizeof(hdr)) != sizeof(hdr))
#else
		if (write(fd, &hdr, sizeof(hdr)) != sizeof(hdr))
#endif
		{
			messagef((state->id, NiL, -1, "send: %d: hdr write error", fd));
			return -1;
		}
		for (i = 0; i < n; i++)
			if (ioctl(fd, I_SENDFD, FDARG(fds[i])))
			{
				messagef((state->id, NiL, -1, "send: %d: ioctl I_SENDFD error", fd));
				return -1;
			}
	}
	return 0;

#else

#if CS_LIB_SOCKET_RIGHTS

	register char*	s;
	register int	i;
	struct iovec	iov;
	Csid_t		id;
	struct msghdr	msg;
	struct
	{
#if _mem_msg_control_msghdr
	struct cmsghdr	hdr;
#else
#define msg_control	msg_accrights
#define msg_controllen	msg_accrightslen
#endif
	int		fds[OPEN_MAX + 1];
	}		ctl;
	char		tmp[PATH_MAX + 1];

	csprotect(&cs);
	if (n >= elementsof(ctl.fds))
	{
		errno = EINVAL;
		return -1;
	}

	/*
	 * the first fd is to verify uid,gid in csrecv()
	 */

	s = csvar(state, CS_VAR_LOCAL, 0);
	if (eaccess(s, X_OK) && (mkdir(s, S_IRWXU|S_IRWXG|S_IRWXO) || chmod(s, S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO)))
	{
		messagef((state->id, NiL, -1, "send: %d: %s: invalid authentication directory ", fd, s));
		return -1;
	}
	if (!(s = pathtemp(tmp, sizeof(tmp), s, "cs", 0)))
	{
		messagef((state->id, NiL, -1, "send: %d: authentication tmp file error", fd));
		return -1;
	}
	if ((i = open(s, O_WRONLY|O_CREAT|O_TRUNC, CS_AUTH_MODE)) < 0 || chmod(s, CS_AUTH_MODE) < 0)
	{
		messagef((state->id, NiL, -1, "send: %d: %s: authentication file creat error", fd, s));
		return -1;
	}
	if (remove(s))
	{
		messagef((state->id, NiL, -1, "send: %d: %s: authentication file remove error", fd, s));
		close(i);
		errno = EPERM;
		return -1;
	}
	ctl.fds[0] = i;
	for (i = 0; i < n; i++)
		ctl.fds[i + 1] = fds[i];
	id.hid = 0;
	id.pid = getpid();
	id.uid = geteuid();
	id.gid = getegid();
	msg.msg_name = 0;
	msg.msg_namelen = 0;
	msg.msg_iov = &iov;
	msg.msg_iovlen = 1;
	iov.iov_base = (caddr_t)&id;
	iov.iov_len = sizeof(id);
	msg.msg_control = (caddr_t)&ctl;
	msg.msg_controllen = (char*)&ctl.fds[n + 1] - (char*)&ctl;
#if _mem_msg_control_msghdr
	ctl.hdr.cmsg_len = msg.msg_controllen;
	ctl.hdr.cmsg_level = SOL_SOCKET;
	ctl.hdr.cmsg_type = SCM_RIGHTS;
#endif
#ifdef EMSGSIZE
	n = 0;
	while ((i = sendmsg(fd, &msg, 0) < 0 ? -1 : 0) < 0 && errno == EMSGSIZE && n++ < 5)
		sleep(1);
#else
	i = sendmsg(fd, &msg, 0) < 0 ? -1 : 0;
#endif
	if (i) messagef((state->id, NiL, -1, "send: %d: sendmsg error", fd));
	close(ctl.fds[0]);
	return i;

#else

	messagef((state->id, NiL, -8, "send(%d,%d) call", fd, n));
	if (!access(CS_PROC_FD_TST, F_OK))
	{
		register int	i;
		register int	j;
		register char*	s;

		static struct
		{
			int*	fds;
			int	num;
			int	max;
		}		hold;

		/*
		 * the sent fd's must be kept open until
		 * the other side receives them, so we dup
		 * and hold them open until the next cssend()
		 */

		if (hold.fds)
			for (i = 0; i < hold.num; i++)
				if (hold.fds[i] >= 0)
				{
					close(hold.fds[i]);
					hold.fds[i] = 0;
				}
		if (n > hold.max)
		{
			hold.max = roundof(n, 8);
			hold.fds = newof(hold.fds, int, hold.max, 0);
		}
		s = state->temp;
		s += sfsprintf(s, sizeof(state->temp), "%lu %d", getpid(), n);
		if (hold.fds)
		{
			hold.num = n;
			for (i = 0; i < n; i++)
			{
				if ((j = hold.fds[i] = dup(fds[i])) < 0)
					j = fds[i];
				else fcntl(j, F_SETFD, FD_CLOEXEC);
				s += sfsprintf(s, sizeof(state->temp) - (s - state->temp), " %d", j);
			}
		}
		else for (i = 0; i < n; i++)
			s += sfsprintf(s, sizeof(state->temp) - (s - state->temp), " %d", fds[i]);
		s += sfsprintf(s, sizeof(state->temp) - (s - state->temp), "\n");
		n = s - state->temp;
		messagef((state->id, NiL, -8, "send(%d, %-*.*s) call", fd, n - 1, n - 1, state->temp));
		i = send(fd, state->temp, n, 0) == n ? 0 : -1;
		if (i) messagef((state->id, NiL, -1, "send: %d: write error", fd));
		return i;
	}
	errno = EINVAL;
	messagef((state->id, NiL, -1, "send: %d: not supported", fd));
	return -1;

#endif

#endif

}

int
_cs_send(int fd, int* fds, int n)
{
	return cssend(&cs, fd, fds, n);
}
