/* rewritten so that it uses no library routines */
#if	SFS_COMPAT
#if defined(__NeXT__)
#include <syscall.h>
#else
#include <sys/syscall.h>
#endif
#endif
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
/* #include <sys/uio.h> */
/* #include <sgtty.h> */
#include <signal.h>
#if	1
#if defined(_IBMR2)
#include <sys/select.h>
#endif
#else
#if defined(HAVE_SYS_SELECT_H)
#include <sys/select.h>
#endif
#endif

#include "glimpse.h"
#include "defs.h"

int
mystrlen(str, max)
	char	*str;
	int	max;
{
	int	i=0;

	while ((i<max) && (str[i] != '\0')) i++;
	return i;
}

int
readn(fd, ptr, nbytes)
int	fd;
char	*ptr;
int	nbytes;
{
	int	nleft, nread;

	nleft = nbytes;
	while (nleft > 0) {
#if	SFS_COMPAT
		nread = syscall(SYS_read, fd, ptr, nleft);
#else
		nread = read(fd, ptr, nleft);
#endif
		if (nread < 0) return(nread);
		else if (nread == 0) break;	/* EOF */
		nleft -= nread;
		ptr += nread;
	}
	return (nbytes - nleft);
}

int
writen(fd, ptr, nbytes)
int	fd;
char	*ptr;
int	nbytes;
{
	int	nleft, nwritten;

	nleft = nbytes;
	while (nleft > 0) {
#if	SFS_COMPAT
		nwritten = syscall(SYS_write, fd, ptr, nleft);
#else
		nwritten = write(fd, ptr, nleft);
#endif
		if (nwritten <= 0) return nwritten;
		nleft -= nwritten;
		ptr += nwritten;
	}
	return (nbytes - nleft);
}

int
readline(sockfd, ptr, maxlen)
int	sockfd;
char	*ptr;
int	maxlen;
{
	int	n, rc;
	char	c;

	for (n=1; n<maxlen; n++) {
		if ((rc = readn(sockfd, &c, 1)) == 1) {
			*ptr++ = c;
			if (c == '\n') break;
		} else if (rc == 0) {
			if (n==1) return (0);	/* EOF */
			else break;
		} else return (-1);
	}
	*ptr = 0;
	return n;
}

#if	USE_MSGHDR
/*
 * This piece of code was causing compilation problems.
 * It was not being used anyway. So it has been deleted.
 * -bg, Jan 4th 95
 */

int
sendfile(sockfd, fds, num)
int	sockfd, fds[], num;
{
	struct iovec	iov[1];
	struct msghdr	msg;
	int		ret;

	iov[0].iov_base = (char *) NULL;
	iov[0].iov_len = 0;
	msg.msg_iov = iov;
	msg.msg_iovlen = 1;
	msg.msg_name = (caddr_t) NULL;
	msg.msg_namelen = 0;
	msg.msg_accrights = (caddr_t) fds;
	msg.msg_accrightslen = num * sizeof(int);

	errno = 0;
#if	SFS_COMPAT
	if ((ret = syscall(SYS_sendmsg, sockfd, &msg, 0)) < 0) {
#else
	if ((ret = sendmsg(sockfd, &msg, 0)) < 0) {
#endif
#if	0
	printf("sendmsg ret = %x, errno = %d\n", ret, errno);
#endif
		return (-1);
	}
#if	0
	printf("sent fds %x %x %x, ret = %x, errno = %d\n", fds[0], fds[1], fds[2], ret, errno);
#endif
	return (0);
}

int
send_clfds(sockfd, clstdin, clstdout, clstderr)
int	sockfd, clstdin, clstdout, clstderr;
{
	int	fds[3];

	fds[0] = clstdin;
	fds[1] = clstdout;
	fds[2] = clstderr;
	if (sendfile(sockfd, fds, 3) < 0) return -1;
	return 0;
}

int
getfile(sockfd, fds, num)
int	sockfd, fds[], num;
{
	struct iovec	iov[1];
	struct msghdr	msg;
	int		ret;

	iov[0].iov_base = (char *) NULL;
	iov[0].iov_len = 0;
	msg.msg_iov = iov;
	msg.msg_iovlen = 1;
	msg.msg_name = (caddr_t) NULL;
	msg.msg_namelen = 0;
	msg.msg_accrights = (caddr_t)fds;
	msg.msg_accrightslen = num*sizeof(int);

	errno = 0;
#if	SFS_COMPAT
	if ((ret = syscall(SYS_recvmsg, sockfd, &msg, 0)) < 0) {
#else
	if ((ret = recvmsg(sockfd, &msg, 0)) < 0) {
#endif
#if	0
		printf("bad recvmsg: ret = %x, errno = %d\n", ret, errno);
#endif
		return -1;
	}
#if	0
	printf("got fds %x %x %x, ret = %x, errno = %d\n", fds[0], fds[1], fds[2], ret, errno);
#endif
	return 0;
}

int
get_clfds(sockfd, pclstdin, pclstdout, pclstderr)
int	sockfd, *pclstdin, *pclstdout, *pclstderr;
{
	int	fds[3];

	if (getfile(sockfd, fds, 3) < 0) return -1;
	if (((*pclstdin = fds[0]) < 0) || (*pclstdin >= 20)) return -1;
	if (((*pclstdout = fds[1]) < 0) || (*pclstdout >= 20)) return -1;
	if (((*pclstderr = fds[2]) < 0) || (*pclstderr >= 20)) return -1;
	return 0;
}
#endif	/*USE_MSGHDR*/

int
linearize(sockfd, reqbuf, reqlen, argc, argv, pid)
int	sockfd;
int	reqlen, argc;
char	*reqbuf, *argv[];
int	pid;
{
	int	i;
	unsigned char	array[4];
	int	ptr = 0;
	int	len;

	array[0] = (pid & 0xff000000) >> 24;
	array[1] = (pid & 0xff0000) >> 16;
	array[2] = (pid & 0xff00) >> 8;
	array[3] = (pid & 0xff);
	if (sockfd >= 0) {
		if (writen(sockfd, array, 4) < 4) return -1;
	}
	if (reqbuf != NULL) {
		if (ptr + 4 >= reqlen) return -1;
		memcpy(reqbuf+ptr, array, 4);
		ptr += 4;
	}

	array[0] = (argc & 0xff000000) >> 24;
	array[1] = (argc & 0xff0000) >> 16;
	array[2] = (argc & 0xff00) >> 8;
	array[3] = (argc & 0xff);
	if (sockfd >= 0) {
		if (writen(sockfd, array, 4) < 4) return -1;
	}
	if (reqbuf != NULL) {
		if (ptr + 4 >= reqlen) return -1;
		memcpy(reqbuf+ptr, array, 4);
		ptr += 4;
	}

	for (i=0; i<argc; i++) {
		len = strlen(argv[i]);
		if (sockfd >= 0) {
			if (writen(sockfd, argv[i], len + 1) < len + 1) return -1;
			if (writen(sockfd, "\n", 1) < 1) return -1;	/* so that we can do gets */
		}
		if (reqbuf != NULL) {
			if (ptr + len + 2 >= reqlen) return -1;
			strcpy(reqbuf+ptr, argv[i]);
			ptr += len+1;
			reqbuf[ptr++] = '\0';	/* so that we can do strcpy */
		}
#if	0
		printf("sending %s\n", argv[i]);
#endif
	}
	return ptr;
}

int
delinearize(sockfd, reqbuf, reqlen, pargc, pargv, ppid)
int	sockfd;
int	reqlen, *pargc;
char	*reqbuf, **pargv[];
int	*ppid;
{
	int	i;
	char	line[MAXLINE];
	int	len;
	int	ptr = 0;
	unsigned char	array[4];

	*ppid = 0;
	*pargc = 0;
	*pargv = NULL;
	memset(array, '\0', 4);

	if (sockfd >= 0) if (readn(sockfd, array, 4) != 4) return -1;
	if (reqbuf != NULL) {
		if (ptr+4 >= reqlen) return -1;
		memcpy(array, reqbuf+ptr, 4);
		ptr += 4;
	}
	*ppid = (array[0] << 24) + (array[1] << 16) + (array[2] << 8) + array[3];

	memset(array, '\0', 4);
	if (sockfd >= 0) if (readn(sockfd, array, 4) != 4) return -1;
	if (reqbuf != NULL) {
		if (ptr+4 >= reqlen) return -1;
		memcpy(array, reqbuf+ptr, 4);
		ptr += 4;
	}
	*pargc = (array[0] << 24) + (array[1] << 16) + (array[2] << 8) + array[3];
#if	0
	printf("clargc=%x\n", *pargc);
#endif
	/* VERY important, set hard-coded limit to MAX_ARGS*MAX_NAME_LEN; otherwise can cause the server to allocate TONS of memory */
	if (*pargc <= 0 || *pargc >= (MAX_ARGS*MAX_NAME_LEN)) { *pargc = 0; return -1; }

	if ((*pargv = (char **)malloc(sizeof(char *) * *pargc)) == NULL) {
		/* no memory, so discard */
		*pargc = 0;
		return - 1;
	}
	memset(*pargv, '\0', sizeof(char *) * *pargc);
	for (i=0; i<*pargc; i++) {
		if (sockfd >= 0) {
			if (readline(sockfd, line, MAXLINE) <= 0) return -1;
			if ((len = mystrlen(line, MAXLINE)) <= 0) {
				i--;
				continue;
			}
			if (((*pargv)[i] = (char *)malloc(len + 2)) == NULL) return -1;
			line[len] = '\0';	/* overwrite the '\n' */
			strcpy((*pargv)[i], line);
		}
		if (reqbuf != NULL) {
			if ( ((len = mystrlen(reqbuf+ptr, reqlen-ptr)) <= 0) || (len >= MAXLINE) ) return -1;
			if (((*pargv)[i] = (char *)malloc(len + 2)) == NULL) return -1;
			strcpy((*pargv)[i], reqbuf+ptr);
			ptr += len + 2;
		}
#if	0
		printf("clargv[%x]=%s\n", i, (*pargv)[i]);
#endif
	}
	return ptr;
}

int
sendreq(sockfd, reqbuf, clstdin, clstdout, clstderr, clargc, clargv, clpid)
int	sockfd, clstdin, clstdout, clstderr, clargc, clpid;
char	reqbuf[MAX_ARGS*MAX_NAME_LEN], *clargv[];
{
#if	USE_MSGHDR
	struct iovec	iov[1];
	struct msghdr	msg;
	int		ret;
	int		fds[3];
#endif	/*USE_MSGHDR*/

#if	USE_MSGHDR
	if ((ret = linearize(-1, reqbuf, MAX_ARGS*MAX_NAME_LEN, clargc, clargv, clpid)) < 0) return -1;

	fds[2] = clstdin;
	fds[1] = clstdout;
	fds[0] = clstderr;

	iov[0].iov_base = (char *) reqbuf;
	iov[0].iov_len = ret;
	msg.msg_iov = iov;
	msg.msg_iovlen = 1;
	msg.msg_name = (caddr_t) NULL;
	msg.msg_namelen = 0;
	msg.msg_accrights = (caddr_t) fds;
	msg.msg_accrightslen = 2 * sizeof(int);	/* don't send clstdin */

	errno = 0;
#if	SFS_COMPAT
	if ((ret = syscall(SYS_sendmsg, sockfd, &msg, 0)) < 0) {
#else
	if ((ret = sendmsg(sockfd, &msg, 0)) < 0) {
#endif
#if	0
	printf("sendmsg ret = %x, errno = %d\n", ret, errno);
#endif
		return (-1);
	}
#if	0
	printf("sendreq %x %x %x, ret = %x, errno = %d\n", fds[0], fds[1], fds[2], ret, errno);
#endif
#else	/*USE_MSGHDR*/
	if (linearize(sockfd, (char *)NULL, MAX_ARGS*MAX_NAME_LEN, clargc, clargv, clpid) < 0) return -1;
#endif	/*USE_MSGHDR*/
	return (0);
}

int
getreq(sockfd, reqbuf, pclstdin, pclstdout, pclstderr, pclargc, pclargv, pclpid)
int	sockfd, *pclstdin, *pclstdout, *pclstderr, *pclargc, *pclpid;
char	reqbuf[MAX_ARGS*MAX_NAME_LEN], **pclargv[];
{
#if	USE_MSGHDR
	struct iovec	iov[1];
	struct msghdr	msg;
	int		ret;
	int		fds[3];
#endif	/*USE_MSGHDR*/

#if	USE_MSGHDR
	iov[0].iov_base = (char *) reqbuf;
	iov[0].iov_len = MAX_ARGS * MAX_NAME_LEN;
	msg.msg_iov = iov;
	msg.msg_iovlen = 1;
	msg.msg_name = (caddr_t) NULL;
	msg.msg_namelen = 0;
	msg.msg_accrights = (caddr_t)fds;
	msg.msg_accrightslen = 2*sizeof(int);

	errno = 0;
#if	SFS_COMPAT
	if ((ret = syscall(SYS_recvmsg, sockfd, &msg, 0)) < 0) {
#else
	if ((ret = recvmsg(sockfd, &msg, 0)) < 0) {
#endif
#if	0
		printf("bad recvmsg: ret = %x, errno = %d\n", ret, errno);
#endif
		return -1;
	}

	*pclstdin = fds[2];
	*pclstdout = fds[1];
	*pclstderr = fds[0];

	if ((ret == delinearize(-1, reqbuf, MAX_ARGS * MAX_NAME_LEN, pclargc, pclargv, pclpid)) < 0) return -1;
#if	0
	printf("getreq %x %x %x, ret = %x, errno = %d\n", fds[0], fds[1], fds[2], ret, errno);
#endif
#else	/*USE_MSGHDR*/
	if (delinearize(sockfd, (char *)NULL, MAX_ARGS * MAX_NAME_LEN, pclargc, pclargv, pclpid) < 0) return -1;
	*pclstdin = -1;
	*pclstdout = sockfd;
	*pclstderr = sockfd;
#endif	/*USE_MSGHDR*/
	return (0);
}
