#ifndef lint
static char sccsid[] = "@(#)rpcsrv.c	1.16 (UKC) 24/9/86";
#endif  lint
/*
 * Yet another RPC server. The top level of the rpc server.
 * It is execced by a process with an open transport connection ;
 * its first argument is the file descriptor open to it.
 * It expects the listening/connecting/accepting to have been done.
 *
 * Validation (if any) must be done before this is called - it does as it is
 * told without question. (I hope)
 * 
 * All system calls mentioned here are the real ones on this machine.
 *
 * Calls implemented:
 *
 *	open	creat	mkdir	read	write	writev	close	_exit
 *	stat	lstat	fstat	ioctl	lseek	readlink
 *	getuid	getgid	geteuid	getegid	getpid	getppid
 *	chmod	unlink	rmdir	chdir	dup	gettimeofday
 */

#include <stdio.h>
#include <sys/types.h>	/* for the next two include files */
#include <sys/stat.h>	/* for stat, lstat, fstat */
#include <sys/uio.h>	/* for writev */
#include <sys/ioctl.h>	/* for IOC_{IN,OUT} */
#include <sys/time.h>	/* for gettimeofday */
#include <sys/tserrno.h>
#include "cmds.h"	/* for rpc cmd tokens */
#include "trans.h"	/* for function type declarations */

char *malloc();
char *realloc();

extern int errno;

static int tsfd;		/* fd open to transport service */
char *buf;		/* General purpose buffer */
long bufsiz = 0;	/* Current size thereof */

main(argc,argv)
char **argv;
{
	register int i;
	struct stat stbuf;
	/* Get transport service file descriptor from argument */
	if (argc != 2) {
		fputs("rpcsrv: Arg count\n", stderr);
		exit(1);
	}
	tsfd = atoi(argv[1]);

	/*
	 * Get highest free file descriptor (usually 19) for tsfd so that
	 * its presence does not affect the file descriptor returned from open()
	 * Some programs rely on this behaviour.
	 */

	for (i = _NFILE-1; i != tsfd; --i) {
		if (fstat(i, &stbuf) == -1) {
			/* this fd is closed */
			(void) dup2(tsfd, i);
			(void) close(tsfd);
			tsfd = i;
			break;
		}
	}
	
	/* this main loop ends when a TS_DISCONNECT is encountered by low level
	 * subroutines in trans.c which exit from there.
	 * Or when we we die horribly from some bug.
	 */

	for(;;) {
		register int retval;
		cmd_t cmd;

		cmd = recvcmd(tsfd);
		switch (cmd) {

		case SYS_open:
			{
				char *filename;
				register long flags, mode;
				(void) recvstring(tsfd,&filename, -1);
				flags = recvint(tsfd);
				mode = recvint(tsfd);
#ifdef DEBUG
fprintf(stderr, "rpcsrv: SYS_open(\"%s\")\n", filename);
#endif

				retval = open(filename,(int)flags,(int)mode);
#ifdef DEBUG
fprintf(stderr, "rpcsrv: retval = %d\n", retval);
#endif

				sendint(tsfd, retval);
				if (retval < 0)
					sendint(tsfd, errno);
				free(filename);
				break;
			}
		case SYS_creat:
			{
				char *filename;
				register long mode;
				(void) recvstring(tsfd,&filename, -1);
				mode = recvint(tsfd);
#ifdef DEBUG
fprintf(stderr, "rpcsrv: SYS_creat(\"%s\")\n", filename);
#endif

				retval = creat(filename,(int)mode);

				sendint(tsfd, retval);
				if (retval < 0)
					sendint(tsfd, errno);
				free(filename);
				break;
			}
		case SYS_mkdir:
			{
				char *filename;
				register long mode;
				(void) recvstring(tsfd,&filename, -1);
				mode = recvint(tsfd);
#ifdef DEBUG
fprintf(stderr, "rpcsrv: SYS_mkdir(\"%s\")\n", filename);
#endif

				retval = mkdir(filename,(int)mode);

				sendint(tsfd, retval);
				if (retval != 0)
					sendint(tsfd, errno);
				free(filename);
				break;
			}
		case SYS_chmod:
			{
				char *filename;
				register long mode;
				(void) recvstring(tsfd,&filename, -1);
				mode = recvint(tsfd);
#ifdef DEBUG
fprintf(stderr, "rpcsrv: SYS_chmod(\"%s\")\n", filename);
#endif

				retval = chmod(filename,(int)mode);

				sendint(tsfd, retval);
				if (retval != 0)
					sendint(tsfd, errno);
				free(filename);
				break;
			}
		case SYS_unlink:
			{
				char *filename;
				(void) recvstring(tsfd,&filename, -1);
#ifdef DEBUG
fprintf(stderr, "rpcsrv: SYS_unlink(\"%s\")\n", filename);
#endif

				retval = unlink(filename);

				sendint(tsfd, retval);
				if (retval != 0)
					sendint(tsfd, errno);
				free(filename);
				break;
			}
		case SYS_chdir:
			{
				char *filename;
				(void) recvstring(tsfd,&filename, -1);
#ifdef DEBUG
fprintf(stderr, "rpcsrv: SYS_chdir(\"%s\")\n", filename);
#endif

				retval = chdir(filename);

				sendint(tsfd, retval);
				if (retval != 0)
					sendint(tsfd, errno);
				free(filename);
				break;
			}
		case SYS_rmdir:
			{
				char *filename;
				(void) recvstring(tsfd,&filename, -1);
#ifdef DEBUG
fprintf(stderr, "rpcsrv: SYS_rmdir(\"%s\")\n", filename);
#endif

				retval = rmdir(filename);

				sendint(tsfd, retval);
				if (retval != 0)
					sendint(tsfd, errno);
				free(filename);
				break;
			}
		case SYS_read:
			{
				register long fd;
				register long nbytes;

#ifdef DEBUG
fputs("rpcsrv: SYS_read\n", stderr);
#endif
				fd = recvint(tsfd);
				nbytes = recvint(tsfd);
				expandbuf(nbytes);

				retval = read ((int)fd, buf, (int)nbytes);

				sendint(tsfd, retval);
				if (retval > 0)
					sendbuf(tsfd,buf,retval);
				else if (retval < 0)
					sendint(tsfd, errno);
				/* (retval == 0) => EOF => no further info required */
				break;
			}
		case SYS_write:
			{
				register long fd;
				register long nbytes;

#ifdef DEBUG
fputs("rpcsrv: SYS_write\n", stderr);
#endif
				fd = recvint(tsfd);
				nbytes = recvint(tsfd);
				expandbuf(nbytes);
				(void) recvbuf(tsfd,&buf,(int) nbytes);

				retval = write((int)fd,buf,(int)nbytes);

				sendint(tsfd, retval);
				if (retval == -1)
					sendint(tsfd, errno);
				break;
			}
		case SYS_writev:
			{
				register long fd;
				register long ioveclen;
				register struct iovec *iov;
				register int i;

#ifdef DEBUG
fputs("rpcsrv: SYS_writev\n", stderr);
#endif
				fd = recvint(tsfd);
				ioveclen = recvint(tsfd);
				iov = (struct iovec *) malloc((unsigned) ioveclen * sizeof(struct iovec));
				for (i=0; i<ioveclen; i++) {
					iov[i].iov_len = recvbuf(tsfd,&(iov[i].iov_base), -1);
				}

				retval = writev((int) fd, iov, (int) ioveclen);

				sendint(tsfd, retval);
				if (retval = -1)
					sendint(tsfd, errno);
				for (i=0; i<ioveclen; i++) free(iov[i].iov_base);
				free((char *) iov);
				break;
			}
		case SYS_close:
			{
				register long fd;

#ifdef DEBUG
fputs("rpcsrv: SYS_close\n", stderr);
#endif
				fd = recvint(tsfd);

				retval = close((int)fd);

				sendint(tsfd, retval);
				if (retval != 0)
					sendint(tsfd, errno);
				break;
			}
		case SYS_dup:
			{
				register long fd;

#ifdef DEBUG
fputs("rpcsrv: SYS_dup\n", stderr);
#endif
				fd = recvint(tsfd);

				retval = dup((int)fd);

				sendint(tsfd, retval);
				if (retval == -1)
					sendint(tsfd, errno);
				break;
			}
		case SYS_exit:
			{
#ifdef DEBUG
fputs("rpcsrv: SYS_exit\n", stderr);
#endif
				retval = recvint(tsfd);
				rpcexit(tsfd);
				_exit(retval);
			}
		case SYS_stat:
			{
				char *path;
				struct stat stbuf;

#ifdef DEBUG
fputs("rpcsrv: SYS_stat\n", stderr);
#endif
				(void) recvstring(tsfd,&path, -1);

				retval = stat(path, &stbuf);

				sendint(tsfd, retval);
				if (retval == 0)
					sendstat(tsfd,&stbuf);
				else
					sendint(tsfd, errno);
				free(path);
				break;
			}
		case SYS_lstat:
			{
				char *path;
				struct stat stbuf;

#ifdef DEBUG
fputs("rpcsrv: SYS_lstat\n", stderr);
#endif
				(void) recvstring(tsfd,&path, -1);

				retval = lstat(path, &stbuf);

				sendint(tsfd, retval);
				if (retval == 0)
					sendstat(tsfd,&stbuf);
				else
					sendint(tsfd, errno);
				free(path);
				break;
			}
		case SYS_fstat:
			{
				int fd;
				struct stat stbuf;

#ifdef DEBUG
fputs("rpcsrv: SYS_fstat\n", stderr);
#endif
				fd = recvint(tsfd);

				retval = fstat(fd,&stbuf);

				sendint(tsfd, retval);
				if (retval == 0)
					sendstat(tsfd,&stbuf);
				else
					sendint(tsfd, errno);
				break;
			}
		case SYS_ioctl:
			{
				register long fd;
				register long request;
				char *argp;

#ifdef DEBUG
fputs("rpcsrv: SYS_ioctl\n", stderr);
#endif
				fd = recvint(tsfd);
				request = recvint(tsfd);
				argp = malloc((unsigned) IOC_SIZE(request));
				if (request & IOC_IN)
					(void) recvbuf(tsfd,&argp, (int) IOC_SIZE(request));
				
				retval = ioctl((int) fd, (int) request, argp);

				sendint(tsfd, retval);
				if (retval == -1)
					sendint(tsfd, errno);
				if (request & IOC_OUT)
					sendbuf(tsfd,argp, (int) IOC_SIZE(request));
				free(argp);
				break;
			}
		case SYS_lseek:
			{
				register long fd;
				register long offset;
				register long whence;
				register long retval;

#ifdef DEBUG
fputs("rpcsrv: SYS_lseek\n", stderr);
#endif
				fd = recvint(tsfd);
				offset = recvint(tsfd);
				whence = recvint(tsfd);

				retval = lseek((int) fd, offset, (int) whence);

				sendint(tsfd,retval);
				if (retval < 0)
					sendint(tsfd, errno);
				break;
			}
		case SYS_readlink:
			{
				char *name;
				long bufsiz;

#ifdef DEBUG
fputs("rpcsrv: SYS_readlink\n", stderr);
#endif
				(void) recvstring(tsfd,&name,-1);
				bufsiz = recvint(tsfd);
				expandbuf(bufsiz);

				retval = readlink(name, buf, (int) bufsiz);

				sendint(tsfd, retval);
				if (retval == -1)
					sendint(tsfd, errno);
				else
					sendstring(tsfd,buf,retval);
				free(name);
				break;
			}
		case SYS_gettimeofday:
			{
				struct timeval t;
				struct timezone tz;
				struct timezone *tzp;
#ifdef DEBUG
fputs("rpcsrv: SYS_gettimeofday\n", stderr);
#endif
				tzp = (struct timezone *) recvint(tsfd);
				if (tzp != NULL) tzp = &tz;

				retval = gettimeofday(&t, tzp);

				sendint(tsfd, retval);
				if (retval != 0)
					sendint(tsfd, errno);
				else {
					sendlong(tsfd, (long) t.tv_sec);
					sendlong(tsfd, t.tv_usec);
					if (tzp != NULL) {
						sendint(tsfd, tz.tz_minuteswest);
						sendint(tsfd, tz.tz_dsttime);
					}
				}
				break;
			}
		/*
		 *	Functions NOT wanted by RFS
		 */

		case SYS_getuid:
			{
				sendint(tsfd, getuid());
				break;
			}

		case SYS_getgid:
			{
				sendint(tsfd, getgid());
				break;
			}

		case SYS_geteuid:
			{
				sendint(tsfd, geteuid());
				break;
			}

		case SYS_getegid:
			{
				sendint(tsfd, getegid());
				break;
			}

		case SYS_getpid:
			{
				sendint(tsfd, getpid());
				break;
			}

		case SYS_getppid:
			{
				sendint(tsfd, getppid());
				break;
			}
		default:
			{
				char mesg[64];
				rpcfail(tsfd,sprintf(mesg, "Unrecognised rpc command token %d.\n", cmd));
			}
		}
	}
}

/*
 *	ensure that global buffer "buf" is at least nbytes long
 */

static
expandbuf(nbytes)
long nbytes;
{
	if (nbytes <= bufsiz) return;

	if ((buf = realloc(buf,(unsigned) nbytes)) == NULL){
		rpcfail(tsfd, "rpcsrv: realloc fails.\n");
	} else {
		bufsiz = nbytes;
	}
}
