static char rcsid[] = "$Header:unixtraps.c 12.0$";
static char sccsid[] = "@(#)unixtraps.c	4.2 83/07/31";

/*
 * Function to execute version 6 and version 7 UNIX system calls from
 * compatability mode on UNIX-32V.
 *	Art Wetzel	August 1979
 */

#include <stdio.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#ifdef V6UNIX
#ifdef TRACE
#define	RTSNAME	"/../../../../usr/local/v6trc"
#else
#define	RTSNAME	"/../../../../usr/local/v6run"
#endif
#include "unix6sys.h"
#ifdef TRACE
#include "unix6sysn.h"
#endif
#endif
#ifdef V7UNIX
#ifdef TRACE
#define	RTSNAME	"/../../../../usr/local/v7trc"
#else
#define	RTSNAME	"/../../../../usr/local/v7run"
#endif
#include "unix7sys.h"
#ifdef TRACE
#include "unix7sysn.h"
#endif
#endif
#include "defs.h"
#define	CARRY	1
#define	MAXSARGS	25
#ifdef V6UNIX
#define	ARGVLEN	512
#define	ENVLEN	0
#endif
#ifdef V7UNIX
#define	ARGVLEN	5120
#define	ENVLEN	1000
#endif
char	argvs[ARGVLEN+ENVLEN];
int	args[MAXSARGS];

/* 32v type stat structure */
extern struct	stat	stat32v;

/* place for times data so we can reverse the longs */
struct timebuf {
	long	t1;
	long	t2;
	long	t3;
	long	t4;
} timebuf;

/* place for pipe file descriptors */
int	pipes[2];

/* wait status */
int	wstatus;

#ifdef	V6UNIX
/* version 6 style stat structure */
struct v6nod {
	dev_t	majmin;
	ino_t	inumber;
	unsigned short	flags;
	unsigned char	nlinks;
	unsigned char	uid;
	unsigned char	gid;
	unsigned char	size0;
	unsigned short	size1;
	unsigned short	addr[8];
	long	actime;
	long	modtime;
} *v6stat;
#endif

#ifdef	V7UNIX
/* version 7 style stat structure */
struct	v7stat {
	dev_t	v7st_dev;
	u_short	v7st_ino;
	u_short v7st_mode;
	short  	v7st_nlink;
	short  	v7st_uid;
	short  	v7st_gid;
	dev_t	v7st_rdev;
	int	v7st_size;
	int	v7st_atime;
	int	v7st_mtime;
	int	v7st_ctime;
} statv7;

struct timeb {
	time_t	time;
	u_short	millitm;
	short	timezone;
	short	dstflag;
} timeb;
#endif

/* do the trap stuff for the trap with code */
dotrap(code)
	int code;
{
	register unsigned short *argp, *savp, *savep;
	register int i, j, indirflg;
	register char *avp, *oavp;
	extern sigcatch();
	extern errno;

	/* clear out condition codes of psl */
	psl &= ~017;
	/* special case of indirect sys call */
	if (code == 0) {
		/* remember this was indirect */
		indirflg = 1;
		/* point to args */
		argp = (unsigned short *)*(pc++);
		/* code for indirect sys call */
		code = *argp++;
		/* is it legit */
		if (code>>8 != TRAPS) {
			fprintf(stderr,"Bad indirect sys call at 0x%x\n",pc-2);
			pc++;
			/* set carry flag */
			psl |= CARRY;
			regs[0] = -1;
			return(-1);
		}
		code &= 0377;
	} else {
		/* remember this was not indirect */
		indirflg = 0;
		/* point to args */
		argp = pc;
	}
	/* check if code too high or bad sys code */
	if (code >= NSYSTRAPS || sysargs[code][0] == ILLSYS) {
		fprintf(stderr,"Unimplimented trap %d at 0x%x\n",code,argp);
		/* set carry bit */
		psl |= CARRY;
		regs[0] = -1;
		return(-1);
	}
	/* copy args to known locations */
	i=0;
	for (j=0; j<sysargs[code][0]; j++)
		args[i++] = regs[j];
	for (j=0; j<(sysargs[code][1]); j++)
		args[i++] = *argp++;
#ifdef TRACE
	fprintf(stderr,"pid %d ",getpid());
	if (indirflg)
		fprintf(stderr,"indirect ");
	fprintf(stderr, "%s (%d) from 0%o with %d args",
	    sysnames[code], code, pc-1, i);
	for (j=0; j<i; j++)
		fprintf(stderr," 0%o",args[j]);
	if (code==OPEN || code==STAT || code==CREAT || code==EXEC || 
	    code==UNLNK || code==LINK || code==CHDIR || code==MKNOD)
		fprintf(stderr," (%s)",args[0]);
#ifdef V7UNIX
	if (code==EXECE)
		fprintf(stderr," (%s)",args[0]);
#endif
	if (code==LINK)
		fprintf(stderr," (%s)",args[1]);
#endif
	/* go do whatever sys call it is */
	switch (code) {
	case FORK:
		/* indirect forks return pids on both sides - must do here */
		/* this is possibly a bug in 32V */
		i = fork();
		break;

	case WAIT:
		i = wait(&wstatus);
		args[0] = i;
		args[1] = wstatus;
		break;

	case EXEC:
#ifdef V7UNIX
	case EXECE:
#endif
		/*
		 *  have to do a lot of junk here to fix up an argv
		 *  for execute since (1) the pdp-11 argv consists of 16
		 *  bit pointers and (2) the argv itself is in the
		 *  pdp-11 program space where it would get clobbered
		 *  when a new program is read in and before its
		 *  argv is set up.
		 */
		avp = &argvs[0];
		savp = (unsigned short *)args[1];
#ifdef	V6UNIX
		for (i=1; args[i] = *savp++; i++)
			if (args[i] == 0177777)
				break;
#ifdef	TRACE
			else
				fprintf(stderr,"argv[%d]%s ",i-1,args[i]);
#endif
#endif
#ifdef	V7UNIX
		savep = (unsigned short *)args[2];
		for (i=1; args[i] = *savp++; i++)
#ifdef	TRACE
			fprintf(stderr,"argv[%d]%s ",i-1,args[i]);
#else
			;
#endif
#endif
		if (stat(args[0], &stat32v)) {
			/* return error here if file does not exist */
#ifdef	TRACE
			fprintf(stderr," does not exist\n");
#endif
			i = -1;
			break;
		}
		/* must have execute permission */
		if (stat32v.st_mode & (S_IEXEC>>6))
			goto experm;
		if (stat32v.st_mode & (S_IEXEC>>3)) {
			if (stat32v.st_gid == getegid())
				goto experm;
			if (geteuid() == 0)
				goto experm;
		}
		if (stat32v.st_mode & S_IEXEC) {
			if (stat32v.st_uid == geteuid())
				goto experm;
			if (geteuid() == 0)
				goto experm;
		}
		/* return failure if no exec permision allowed */
		i = -1;
experm:
		/* can't exec a directory */
		if ((stat32v.st_mode&S_IFMT) == S_IFDIR)
			i = -1;
		if (i == -1)
			break;
		args[i] = 0;
		for (j=0; j<i; j++) {
			oavp = (char *)args[j];
			args[j] = (int)avp;
			while (*avp++ = *oavp++)
				;
		}
#ifdef V7UNIX
		if (code == EXECE) {
			for (j = ++i; args[j] = *savep++; j++)
				;
			for ( ; j > i; j--) {
				oavp = (char *)args[j];
				args[j] = (int)avp;
				while (*avp++ = *oavp++)
					;
			}
		}
#endif
		/* SETUID and SETGID files must be started with a fresh RTS */
		if (stat32v.st_mode & S_ISGID || stat32v.st_mode & S_ISUID) {
			/* should add a check here for good magic # in header */
			args[1] = args[0];
			args[0] = (int)RTSNAME;
#ifdef TRACE
			fprintf(stderr," SETUID-GID");
#endif
			if (args[i])
				i = execve(args[0], &args[0], &args[i]);
			else
				i = execv(args[0], &args[0]);
			fprintf(stderr,"can't exec %s\n",RTSNAME);
			break;
		}
		i = execute(args[0], &args[1], &args[i]);
		/* shouldn't get here if exec works */
		break;

	case SEEK:
#ifdef	V6UNIX
		/* fix up negative offsets */
		if (args[2] != 0 && args[2] != 3)
			if (args[1] >= 32768)
				args[1] -= 65536;
		if (args[2] <= 2)
			i = lseek(args[0], args[1], args[2]);
		else
			i = lseek(args[0], args[1]*512, args[2]-3);
		if (i != -1)
			i = 0;
#endif
#ifdef	V7UNIX
		i = lseek(args[0], (args[1]<<16)|(args[2]&0177777), args[3]);
#endif
		break;

#ifdef	V6UNIX
	case MKNOD:
		/* version 6 uses allocated bit which means regular file here */
		if (args[1] & S_IFBLK)
			args[1] &= ~S_IFREG;
		i = mknod(args[0], args[1], args[2]);
		break;
#endif	

	case PIPE:
		i = pipe(pipes);
		args[0] = pipes[0];
		args[1] = pipes[1];
		break;

#ifdef	V6UNIX
	case TELL:
		i = lseek(args[0], 0L, 1);
		break;

	case STTY:
		i = stty(args[0], args[1]);
		break;

	case GTTY:
		i = gtty(args[0], args[1]);
		break;
#endif

	case STAT:
		i = stat(args[0], &stat32v);
		goto allstat;

	case FSTAT:
		/* do the syscall to a local stat buffer */
		i = fstat(args[0], &stat32v);

	allstat:
		/* reverse the longs */
		stat32v.st_size = longrev(stat32v.st_size);
		stat32v.st_atime = longrev(stat32v.st_atime);
		stat32v.st_mtime = longrev(stat32v.st_mtime);
		stat32v.st_ctime = longrev(stat32v.st_ctime);
#ifdef V7UNIX
		statv7.v7st_dev = stat32v.st_dev;
		statv7.v7st_ino = stat32v.st_ino;
		statv7.v7st_mode = stat32v.st_mode;
		statv7.v7st_nlink = stat32v.st_nlink;
		statv7.v7st_uid = stat32v.st_uid;
		statv7.v7st_gid = stat32v.st_gid;
		statv7.v7st_rdev = stat32v.st_rdev;
		statv7.v7st_size = stat32v.st_size;
		statv7.v7st_atime = stat32v.st_atime;
		statv7.v7st_mtime = stat32v.st_mtime;
		statv7.v7st_ctime = stat32v.st_ctime;
		/* copy out otherwise unchanged stat buffer */
		/* in two pieces with st_size as the breaking point */
		/* note that st_rdev is a short but due to alingnmemt */
		/* problems the rest of the structure is out of sync */
		j = (int)((char *)(&statv7.v7st_size) -
		    (char *)(&statv7.v7st_dev));
		bcopy(&statv7, args[1], j);
		bcopy(&statv7.v7st_size, args[1]+j-2, sizeof(struct v7stat)-j);
#endif
#ifdef	V6UNIX
		/* point to user area as v6stat structure */
		v6stat = (struct v6nod *)args[1];
		/* copy out piece by piece */
		v6stat->majmin = stat32v.st_dev;
		v6stat->inumber = stat32v.st_ino;
		v6stat->flags = stat32v.st_mode;
		v6stat->nlinks = (unsigned char)stat32v.st_nlink;
		v6stat->uid = (unsigned char)stat32v.st_uid;
		v6stat->gid = (unsigned char)stat32v.st_gid;
		/* note size already reversed */
		v6stat->size0 = (unsigned char)(stat32v.st_size & 0377);
		v6stat->size1 = (unsigned short)(stat32v.st_size>>16);
		v6stat->actime = stat32v.st_atime;
		v6stat->modtime = stat32v.st_mtime;
		/* patch up flags */
		/* for now just set 100000 bit if not a plain file */
		if (v6stat->flags & 060000)
			v6stat->flags |= 0100000;
#endif
		break;

	case TIMES:
		i = times(&timebuf);
		timebuf.t2 = longrev(timebuf.t2) + timebuf.t1;
		timebuf.t3 = longrev(timebuf.t3);
		timebuf.t4 = longrev(timebuf.t4);
		bcopy(&timebuf.t2,args[0],sizeof(struct timebuf)-sizeof(long));
		break;

#ifdef	V6UNIX
	case SLEEP:
		/* do a sleep function - what about pwb which has alarm? */
		sleep(args[0]);
		break;
#endif

	case GETUID:
		args[0] = getuid();
		args[1] = geteuid();
#ifdef V6UNIX
		i = args[1]<<8 | (args[0] & 0377);
#endif
		break;

	case GETGID:
		args[0] = getgid();
		args[1] = getegid();
#ifdef V6UNIX
		i = args[1]<<8 | (args[0] & 0377);
#endif
		break;

		/* uids and gids are 8 bits in version 6 */
	case SETUID:
	case SETGID:
#ifdef V6UNIX
		args[0] &= 0377;
#endif
		if (code == SETUID)
			i = setuid(args[0]);
		else
			i = setgid(args[0]);
		break;

	case SIG:
		/* if it is a good signal code */
		if (args[0] <= NSIG) {
			/* get the current signal value */
			i = sigvals[args[0]];
			/* reset the signal to the new value */
			sigvals[args[0]] = args[1];
			/* actually do signal except don't reset SIGILL */
			if (args[0] != SIGILL) {
				if (args[1] == (int)SIG_DFL ||
				    args[1] & (int)SIG_IGN) {
					if ((int)signal(args[0],args[1]) == -1)
						i = -1;
				} else {
					if ((int)signal(args[0],sigcatch) == -1)
						i = -1;
				}
			}
		} else
			i = -1;
		break;

	case BRK:
		/* brk is successful unless we run over the stack */
		/* NB: this assumes register usage which need not be used */
		i = 0;
		if (args[0] >= regs[6])
			i = -1;
		break;

	/*
	 * the next bunch are to cope with sys calls removed from 4.2
	 */
	case TIME:
		i = time(0);
		break;

	case STIME: {
		struct timeval tv;

		tv.tv_usec = 0;
		tv.tv_sec = (args[0] & 0xffff) | ((args[1] & 0xffff) << 16);
		i = settimeofday(&tv);
		break;
	}

	case NICE:
		i = nice(args[0]);
		break;

#ifdef V7UNIX
	case ALARM:
		i = alarm(args[0]);
		break;

	case PAUSE:
		i = pause();
		break;

	case UTIME:
		i = utime(args[0], args[1]);
		break;

	case FTIME:
		i = ftime(&timeb);
		timeb.time = longrev(timeb.time);
		bcopy(&timeb, args[0], sizeof timeb - 2);
		break;

	case IOCTL:
		args[1] = mapioctl(args[1]);
		if (args[1] == 0)
			i = -1;
		else
			i = ioctl(args[0], args[1], args[2]);
		break;
#endif

#ifdef	V6UNIX
	case PWBSYS:
		/* ignore pwbsys for now */
		switch (args[2]) {
		case UNAME:
#ifdef	TRACE
			fprintf(stderr,"UNAME with %d %d\n",args[0],args[1]);
#endif
			strcpy(args[0],"pwbname");
			i = 0;
			break;

		case UDATA:
#ifdef	TRACE
			fprintf(stderr,"UDATA with %d %d\n",args[0],args[1]);
#endif
			i = 0;
			break;

		case USTAT:
fprintf(stderr,"USTAT with %d %d\n",args[0],args[1]);
			i = 0;
			break;

		case UTIME:
fprintf(stderr,"UTIME with %d %d\n",args[0],args[1]);
			i = 0;
			break;
		default:
fprintf(stderr,"bad PWBSYS %d\n",args[3]);
			i = -1;
			break;
		}
		break;
#endif

	default:
		/*
		 *	Many sys calls are easily done here since most
		 *	system call codes are the same on version 6 and 7 UNIX
		 *	as they are here.
		 */
		i = syscall(code,args[0],args[1],args[2],args[3],args[4]);
#ifdef V6UNIX
		/* allow read write access to created files for (IDIS v6 mod) */
		if (code==CREAT) {
			/* get actual file mode after create */
			fstat(i, &stat32v);
			close(i);
			/* ensure read/write access to owner */
			chmod(args[0], 0644);
			i = open(args[0], 2);
			/* change mode back the way it was */
			chmod(args[0], stat32v.st_mode);
		}
#endif
		break;
	}
#ifdef TRACE
	fprintf(stderr," sys val -> 0%o\n",i);
#endif
	/* set carry bit if sys error */
	if (i == -1)
		psl |= CARRY;
	/* if not an indirect sys call, adjust the pc */
	if (indirflg == 0)
		pc = argp;
	/* do alternate return on one side of fork */
	if (code == FORK && i != 0)
		pc++;
	/* do the various return value formats */
	switch (sysargs[code][2]) {
	case NORMRET:
		/* normal case only one return value in r0 */
		regs[0] = i;
		break;
	case LONGRET:
		/* return a long in r0 - r1 as in time */
		regs[1] = i;
		regs[0] = i >> 16;
		break;
	case TWORET:
		/* return two ints in r0 - r1 as in pipe */
		if (i == -1)
			regs[0] = i;
		else {
			regs[1] = args[1];
			regs[0] = args[0];
		}
		break;
	}
	if (i== -1)
		regs[0] = errno;
}

long
longrev(l)
	long l;
{
	/* function to reverse the halves of a long */
	union {
		long	lng;
		short	s[2];
	} u;
	register short t;
	u.lng = l;
	t = u.s[0];
	u.s[0] = u.s[1];
	u.s[1] = t;
	return(u.lng);
}

/*
 * Note: these tables are sorted by
 * ioctl "code" (in ascending order).
 */
int fctls[] = { FIOCLEX, FIONCLEX, FIOASYNC, FIONBIO, FIONREAD, 0 };
int tctls[] = {
	TIOCGETD, TIOCSETD, TIOCHPCL, TIOCMODG, TIOCMODS,
	TIOCGETP, TIOCSETP, TIOCSETN, TIOCEXCL, TIOCNXCL,
	TIOCFLUSH,TIOCSETC, TIOCGETC, TIOCREMOTE,TIOCMGET,
	TIOCMBIC, TIOCMBIS, TIOCMSET, TIOCSTART,TIOCSTOP,
	TIOCPKT,  TIOCNOTTY,TIOCSTI,  TIOCOUTQ, TIOCGLTC,
	TIOCSLTC, TIOCSPGRP,TIOCGPGRP,TIOCCDTR, TIOCSDTR,
	TIOCCBRK, TIOCSBRK, TIOCLGET, TIOCLSET, TIOCLBIC,
	TIOCLBIS, 0
};

/*
 * Map an old style ioctl command to new.
 */
mapioctl(cmd)
	int cmd;
{
	register int *map, c;

	switch ((cmd >> 8) & 0xff) {

	case 'f':
		map = fctls;
		break;

	case 't':
		map = tctls;
		break;

	default:
		return (0);
	}
	while ((c = *map) && (c&0xff) < (cmd&0xff))
		map++;
	if (c && (c&0xff) == (cmd&0xff))
		return (c);
	return (0);
}
