V10/630/src/mux.c

Compare this file to the similar file:
Show the results in this format:

#if	TRACING | PSTATISTICS
#include <stdio.h>
#endif

#include <sys/param.h>
#include <sys/types.h>
#include <sys/stream.h>
#include <sys/ttyio.h>
#include <sys/filio.h>
#include <signal.h>
#include <errno.h>
#include <jioctl.h>
#include <tty.h>

#include "msgs.h"
#include "pconfig.h"
#include "proto.h"
#include "packets.h"
#include "pstats.h"

/*
 *	One layer structure per file-descriptor
 */
struct layer {
	char		chan;		/* jerq protocol channel */
	char		busy;
	char		dx, dy;		/* Window size in characters */
	short		bitsx, bitsy;	/* Window size in bits */
	int		more;
	struct ttychars	ttychars;
	char		bchan;		/* holding area for blocked data */
	char		bcount;
	char		bbuf[MAXPKTDSIZE];
};

#define	NLAYERS	16		/* Same as in jerq itself */
#define	NSELFD	20		/* Maximum file descriptors for 'select' */
#define	SELTIMO	(1000*3)	/* 'select' timeout in millisecs */
#define	CDSIZE	(sizeof(struct sgttyb)-1)

char		*jerqprog;
char		*jerqstart;
char		*progname;
char		*shell;
int		quitflag;
fd_set		rdfd;
int		enabled=1;
int		blocked=0;
struct layer	layer[NSELFD];
char		buf[MAXPKTDSIZE+MSGHLEN];
struct sgttyb	sttymodes;
struct sgttyb	sttysave;
struct ttydevb	devmodes;
struct ttydevb	devsave;
struct tchars	tcharssave;
struct ttychars	ttychars;
struct ttychars zerochars;
short		booted;
extern int	receive();
extern int	creceive();
void		dosig();
int		twrite();
void		wrmesgb();
extern char	*sys_errlist[];
extern char	*getenv();
extern char	*itoa();
extern char	*strcpy();
extern char	*strcat();
extern int	tty_ld;
extern int	mesg_ld;
extern int	errno;
extern int	sys_nerr;
extern int	strlen();
extern int	write();

struct{
	short	speed;
	short	bytes;
} speeds[] = {
	EXTA, 1920,
	B9600, 960,
	B4800, 480,
	B1200, 120,
	B300, 30,
	0, 960,		/* default */
};
#define	NSPEEDS	((sizeof speeds)/(sizeof speeds[0]))
#define	max(A,B)	(((A)>(B))?(A):(B))

struct Pchannel	pconvs[NLAYERS];
struct Pconfig	pconfig	= {
#	ifndef	TRACING
	write,
#	else
	twrite,
#	endif
	receive,
	(void(*)())creceive,
};

#ifdef	TRACING
int	twrite();
FILE	*tracefd;
void	trace();
void	tread();
#define	ifdeftracing(a)		a
#else
#define trace(a, b)
#define	tread(a, b)
#define	ifdeftracing(a)
#endif

#if	TRACING==1 || PDEBUG==1
char 	tracefile[]="traces";
#define	_exit	exit
#endif

main(argc, argv)
	char *argv[];
{
	register int n;
	char cmdline[64];
	progname=argv[0];
	for(n=1; n<argc; n++) {
		if(strcmp(argv[n], "-L")==0)
			jerqstart=argv[++n];
		else if(strcmp(argv[n], "-l")==0){
			register fd; char jfd[32];
			fd = creat(mktemp(strcpy(jfd, "/tmp/.muxXXXXXX")), 0744);
			write(fd, "#!/bin/sh\n", 10);
			++n, write(fd, argv[n], strlen(argv[n]));
			close(fd);
			strcpy(cmdline, jfd);
			strcat(strcat(strcat(cmdline, "; rm "), jfd), "; exit");
			jerqstart = cmdline;
		}else{
			n=argc+1; break;
		}
	}
	if(n!=argc)
		return service(argc-1, argv+1);
	if((jerqprog=getenv("MUXTERM")) == 0)
		jerqprog="/usr/jerq/lib/muxterm";
	if((shell=getenv("SHELL")) == 0)
		shell="sh";
	ioctl(0, TIOCGETP, &sttymodes);
	sttysave=sttymodes;
	if(ioctl(0, TIOCGDEV, &devmodes)>=0)	/* band-aid for old systems */
		devsave=devmodes;
	else {
		devsave.ispeed=devmodes.ispeed=sttymodes.sg_ispeed;
		devsave.ospeed=devmodes.ospeed=sttymodes.sg_ospeed;
	}
	ioctl(0, TIOCGETC, &tcharssave);
	setmodes(&ttychars, &sttymodes);
	settchars(&ttychars, &tcharssave);
	sttymodes.sg_flags|=RAW;
	sttymodes.sg_flags&=~ECHO;
	ioctl(0, TIOCSETP, &sttymodes);
	devmodes.flags|=F8BIT;
	ioctl(0, TIOCSDEV, &devmodes);
	signal(SIGPIPE, (int (*)())1);
#ifdef	TRACING
	tracefd=fopen(tracefile, "w");
#ifdef	PDEBUG
	ptracefd = tracefd;
#endif
#endif
#if	TRACING!=1 && PDEBUG==1
	ptracefd=fopen(tracefile, "w");
#endif
	if(boot(jerqprog))
		quit("can't boot terminal program");
	booted++;
	ioctl(0, TIOCEXCL, 0);
	trace(0, 0);
	trace("start\n", 0);
	for(n=0; n<NSPEEDS; n++)
		if(speeds[n].speed<=devmodes.ospeed)
			break;
	n=speeds[n].bytes;
	Pxtimeout=max((((NLAYERS-2)*sizeof(struct Packet)*NPCBUFS+n-1)/n), 3);
	Prtimeout=max(((sizeof(struct Packet)+n-1)/n), 2);
	Pscanrate=1;
	trace("speed = %d", n);
	trace(" xtimo = %d", Pxtimeout);
	trace(" rtimo = %d\n", Prtimeout);
	Pxfdesc=1;
	if(pinit(NLAYERS)==-1)
		quit("bad protocol initialization");
	buf[0]=JTIMO;
	buf[1]=Prtimeout;
	buf[2]=Pxtimeout;
	if (jerqstart && (n = strlen(jerqstart)) <= MAXPKTDSIZE-3) {
		strncpy(&buf[3], jerqstart, n);
		n += 3;
	} else
		n = 3;
	(void)psend(0, buf, n);
	while(scan()!=-1){
		if(quitflag)
			quit(NULL);
		ifdeftracing(fflush(tracefd));
	}
	trace("errno = %d\n", errno);
	quit("select");
}
scan()
{
	register fd, bit, n, ret=0;

	trace(0, 0);
	trace("enabled %o\n", enabled);
	if(blocked){		/* try to clear blocked channels */
		for(fd=0, bit=1; fd<NSELFD; fd++, bit<<=1)
			if((bit&blocked) &&
			   psend(layer[fd].bchan, layer[fd].bbuf, layer[fd].bcount)!=-1){
				blocked&=~bit;
				return 0;
			}
	}
	rdfd.fds_bits[0]=enabled&~blocked;
	while(select(NSELFD, &rdfd, (fd_set *)0, SELTIMO)==-1){
		if(errno!=EINTR)
			return -1;
		ret++;
	}
	trace(0, 0);
	trace("selected %o\n", rdfd.fds_bits[0]);
	if(rdfd.fds_bits[0]==0) {
		if(Ptflag)
			ptimeout(SIGALRM);
	}else for(fd=0, bit=1; fd<NSELFD; fd++, bit<<=1)
		if(bit&rdfd.fds_bits[0]){
			while((n=read(fd, buf, sizeof buf))==-1)
				if(errno!=EINTR)
					return -1;
				else{
					trace("read error, errno=%d\n", errno);
					return 0;
				}
			ifdeftracing(if(n==0) trace("0 byte read", 0));
			if(fd==0){
				if(n==0)
					quit("EOF on jerq");
				tread(buf, n);
				precv(buf, n);
			} else if(unpack(fd, buf, n))
				enabled&=~bit;
		}
	return ret;	/* used in quit */
}
psend_hold(chan, bufp, count, fd)
	int chan;
	register char *bufp;
	int count;
	register fd;
{
	int ret;
	register i;
	if((ret=psend(chan, bufp, count))==-1){
		trace("psend hold on fd %d\n", fd);
		layer[fd].bchan=chan;
		layer[fd].bcount=count;
		for(i=0; i<count; i++)
			layer[fd].bbuf[i]=bufp[i];
		blocked|=1<<fd;
	}
	return ret;
}
quit(s)
	register char *s;
{
	register l, i;

	ifdeftracing(trace("\nmux: %s\n", s); trace(0, 0); fflush(tracefd));
	if(booted){
		for(i=0; i<NSELFD; i++)
			if(layer[i].busy)
				(void)close(i);
		layer[0].chan=0;
		sendioctl(0, JTERM);	/* kill demux ==> boot terminal */
		for(i=Pxtimeout+1; Ptflag && i>0;){
			enabled=1;
			if((l=scan())==-1)
				break;
			i-=l;
		}
		alarm(0);
	}
	ioctl(0, TIOCSDEV, &devsave);
	ioctl(0, TIOCSETP, &sttysave);
	ioctl(0, TIOCNXCL, 0);
	sleep(2);
	if (s) {
		write(2, progname, strlen(progname));
		write(2, ": ", 2);
		write(2, s, strlen(s));
		write(2, "\n", 1);
	}
#ifdef	PSTATISTICS
	for(i=0, l=0; i<PS_NSTATS; i++)
		if (pstats[i].count) {
			if(l++==0)
				fprintf(stderr, "\nPacket protocol statistics:\n");
			fprintf(stderr, "%6ld %s\n"
				,pstats[i].count
#ifdef	PSTATSDESC
				,pstats[i].descp
#else
				,""
#endif
				);
			trace("%6ld ", pstats[i].count);
			trace("%s\n", pstats[i].descp);
		}
	fflush(stderr);
#endif
#if	TRACING == 1 || PDEBUG == 1
	fprintf(stderr, "\nThere are traces in '%s'\n", tracefile);
#endif
	ifdeftracing(fflush(tracefd); abort());
#ifdef	MONITOR
	monitor(0);
#endif
	_exit(0);
}
/*
 *	Unpack a message buffer bp of length n.
 */
unpack(fd, bp, n)
	register int fd;
	char *bp;
	int n;
{
	register struct mesg *mp;
	struct ttychars tempchars;
	static char cdbuf[256];
	char *s;
	register int size;
	mp = (struct mesg *)bp;
	size = mp->losize + (mp->hisize<<8);
	trace("unpack fd %d", fd);
	trace(" size %d", n);
	trace(" count %d\n", size);
	if(n<=0)
		mp->type=M_HANGUP;
	else if(layer[fd].more>0){
		layer[fd].more-=n;
		return sendchars(fd, bp, n);
	}
	switch (mp->type) {
	case M_HANGUP:
		trace("shell died\n", 0);
		wait((int *)0);
		if(layer[fd].busy){
			sendioctl(fd, JDELETE);
			/*(void)psend(layer[fd].chan, "Shell died.\n", 12);*/
		}
		layer[fd].busy = 0;
		close(fd);
		enabled &= ~(1<<fd);
		return 1;
	case M_DELAY:
	default:
		trace("ignore type 0%o\n", mp->type);
		return 0;
	case M_DELIM:
	case M_DATA:
		if(size==0){
			trace("size 0 %s ignored\n", mp->type==M_DELIM? "delim" : "data");
			return 0;
		}
		break;
	case M_IOCTL:
		mp->type = M_IOCACK;
		switch (*(int *)(bp+MSGHLEN)) {
		case TIOCSETP:
		case TIOCSETN:
			tempchars=layer[fd].ttychars;
			setmodes(&tempchars, (struct sgttyb *)(bp+MSGHLEN+sizeof(int)));
			ttyset(fd, &tempchars);
			size = 0;
			break;
		case TIOCGETP:
			tempchars=layer[fd].ttychars;
			getmodes(&tempchars, (struct sgttyb *)(bp+MSGHLEN+sizeof(int)));
			size=sizeof(struct sgttyb)+sizeof(int);
			ttyset(fd, &tempchars);
			break;
		case TIOCSETC:
			tempchars=layer[fd].ttychars;
			settchars(&tempchars, (struct tchars *)(bp+MSGHLEN+sizeof(int)));
			ttyset(fd, &tempchars);
			size=0;
			break;
		case TIOCGETC:
			gettchars(&layer[fd].ttychars, (struct tchars *)(bp+MSGHLEN+sizeof(int)));
			size=sizeof (struct tchars) + sizeof (int);
			break;
		case TIOCSDEV:
			size=0;
			break;
		case TIOCGDEV:
			size=sizeof(devsave)+sizeof(int);
			*(struct ttydevb *)(bp+MSGHLEN+sizeof(int))=devsave;
			break;
		case JMUX:
			size=0;
			break;
		case JWINSIZE:
			*((int *)(bp+MSGHLEN))=JWINSIZE;	/* answering JWINSIZE ioctl */
#define	BP	((struct winsize *)(bp+MSGHLEN+sizeof(int)))
			BP->bytesx=layer[fd].dx;
			BP->bytesy=layer[fd].dy;
			BP->bitsx=layer[fd].bitsx;
			BP->bitsy=layer[fd].bitsy;
			size=sizeof(struct winsize)+sizeof(int);
			break;
		case JTERM:
		case JBOOT:
		case JZOMBOOT:
		case JEXIT:
			sendioctl(fd, *(int *)(bp+MSGHLEN));
			size=0;
			break;
		case JCHDIR:
			s=bp+MSGHLEN+sizeof(int);
			if(*s==0){
				if(chdir(cdbuf)!=0) 
					mp->type = M_IOCNAK;
				cdbuf[0]=0;
			}else
				strcat(cdbuf, s);
			size = 0;
			break;
		default:
			mp->type = M_IOCNAK;
			size = 0;
		}
		mp->magic = MSGMAGIC;		/* safety net */
		mp->losize = size;
		mp->hisize = size>>8;
		write(fd, bp, MSGHLEN+size);
		trace("unpack ioctl type '%c'", *(int *)(bp+MSGHLEN)>>8);
		trace(" %d\n", *(int *)(bp+MSGHLEN)&0xff);
		return 0;
	}
	if(size>MAXPKTDSIZE){
		layer[fd].more=size-MAXPKTDSIZE;
		size=MAXPKTDSIZE;
	}
	return sendchars(fd, bp+MSGHLEN, size);
}
getmodes(tp, bp)
	register struct ttychars *tp;
	register struct sgttyb *bp;
{
	bp->sg_ispeed=sttysave.sg_ispeed;
	bp->sg_ospeed=sttysave.sg_ospeed;
	bp->sg_flags=(tp->flags1<<8)|(tp->flags0&0xFF);
	bp->sg_erase=tp->erase;
	bp->sg_kill=tp->kill;
}
setmodes(tp, bp)
	register struct ttychars *tp;
	register struct sgttyb *bp;
{
	tp->flags0=bp->sg_flags;
	tp->flags1=bp->sg_flags>>8;
	tp->erase=bp->sg_erase;
	tp->kill=bp->sg_kill;
}
gettchars(tp, bp)
	register struct ttychars *tp;
	register struct tchars *bp;
{
	bp->t_intrc=tp->intrc;
	bp->t_quitc=tp->quitc;
	bp->t_startc=tp->startc;
	bp->t_stopc=tp->stopc;
	bp->t_eofc=tp->eofc;
	bp->t_brkc=tp->brkc;
}
settchars(tp, bp)
	register struct ttychars *tp;
	register struct tchars *bp;
{
	tp->intrc=bp->t_intrc;
	tp->quitc=bp->t_quitc;
	tp->startc=bp->t_startc;
	tp->stopc=bp->t_stopc;
	tp->eofc=bp->t_eofc;
	tp->brkc=bp->t_brkc;
}
ttyset(fd, tp)
	struct ttychars *tp;
{
	register char *p, *q;
	register i;
	static struct ttycmesg m={JTTYC};
	for(i=0, p=(char *)tp, q=(char *)&layer[fd].ttychars; *p++==*q++; i++)
		if(i>=sizeof(struct ttychars))
			return;	/* no need to send; they're identical */
	m.chan=layer[fd].chan;
	layer[fd].ttychars=*tp;
	m.ttychars=*tp;
	(void)psend_hold(0, (char *)&m, sizeof m, fd);
}
sendioctl(fd, cmd)
{
	char ioctlvec[2];
	ioctlvec[0]=cmd;
	ioctlvec[1]=layer[fd].chan;
	if(psend_hold(0, ioctlvec, sizeof ioctlvec, fd)!=-1)
		unblock(fd);
}
int
sendchars(fd, s, cc)
	char *s;
	int cc;
{
	register int	l=layer[fd].chan;
	register int	n;

#	ifdef TRACING
	char buf[256];
	trace("write %d chars ", cc);
	trace("to layer %d\n", l);
	strncpy(buf, s, cc);
	buf[cc]=0;
	trace("<%s>\n", buf);
#	endif
	if(fd!=0 && layer[fd].busy==0)
		return 0;		/* layer was deleted, but there's still data */
	if(cc>0)
		do{
			if((n=cc)>MAXPKTDSIZE)
				n=MAXPKTDSIZE;
			if(psend(l, s, n)==-1){
				trace("layer %d blocked\n", l);
				return fd;	/* BUG */
			}
		}while(s+=n, (cc-=n)>0);
	unblock(fd);
	return 0;
}
unblock(fd)
	int fd;
{
	register Pch_p	pcp=&pconvs[layer[fd].chan];

	trace("unblock for layer %d", layer[fd].chan);
	trace(" freepkts=%d\n", pcp->freepkts);
	if(fd==0)
		return;
	if(pcp->freepkts>=1)
		enabled|=1<<fd;
	else
		enabled&=~(1<<fd);
}
void
lerror(l, s, t)
	int l;
	char *s, t;
{
	char	ebuf[128];
	int	busy;
	strcpy(ebuf, s);
	if(errno){
		strcat(ebuf, ": ");
		if(errno < sys_nerr)
			strcat(ebuf, sys_errlist[errno]);
		else{
			strcat(ebuf, "error ");
			strcat(ebuf, itoa(errno));
		}
		errno=0;
	}
	strcat(ebuf, "\n");
	trace("lerror type %d", t);
	trace(" for layer %d", l);
	trace(" %s\n", ebuf);
	layer[0].chan=l;
	sendchars(0, ebuf, strlen(ebuf));
}
int
creceive(l, s, n)
	char *s;
{
	if(s[0]!=C_UNBLK || n!=1)
		quit("bad control type");
	(void)receive(l, s, n);
}
int
receive(l, s, cc)
	int l;
	register char *s;
	register int cc;
{
	register int i;
	struct mesg hupmsg;
	if((i=ltofd(l))==-1)
		switch(*s){
		case C_NEW:
		case C_EXIT:
			break;
		default:
			errno = 0;
			lerror(l, "inactive layer", *s);
		case C_UNBLK:
			return 0;
		}
	while(cc--){
		trace("receive C type %d", *s);
		trace(" for layer %d", l);
		trace(" fd %d\n", i);
		switch(*s++){
		case C_SENDCHAR:	/* send layer char */
			wrmesgb(i, s++, 1);
			delim(i);
			cc--;
			break;
		case C_DELIM:		/* send delimiter */
			delim(i);
			break;
		case C_NEW:		/* make layer */
			if((i=doshell())==-1){
				lerror(l, "new layer", C_NEW);
				trace("can't start\n", i);
				cc-=6;
				break;
			}
			layer[i].busy=1;
			layer[i].chan=l;
			ttyset(i, &ttychars);
			trace("new fd %d ", i);
			trace("layer %d ", l);
			enabled |= (1<<i);
		case C_RESHAPE:
			layer[i].dx=*s++;
			trace("x wid %d ", layer[i].dx);
			layer[i].dy=*s++;
			trace("y wid %d\n", layer[i].dy);
			layer[i].bitsx=(unsigned char)*s++;
			layer[i].bitsx|=(*s++)<<8;
			layer[i].bitsy=(unsigned char)*s++;
			layer[i].bitsy|=(*s++)<<8;
			cc-=6;
			break;
		case C_UNBLK:		/* unblock layer */
			unblock(i);
			break;
		case C_PUSHLD:		/* obsolescent */
		case C_POPLD:
			break;
		case C_DELETE:		/* delete layer */
			hupmsg.losize=0;
			hupmsg.hisize=0;
			hupmsg.magic=MSGMAGIC;
			hupmsg.type=M_HANGUP;
			write(i, (char *)&hupmsg, MSGHLEN);
			layer[i].busy=0;
			pconvs[layer[i].chan].freepkts=1;	/* hack */
			unblock(i);
			layer[i].ttychars=zerochars;
			break;
		case C_EXIT:		/* exit */
			quitflag++;
			return 0;
		case C_SENDNCHARS:	/* send cc characters */
			wrmesgb(i, s, cc);
			return 0;
		case C_SENDNCHARD:	/* send cc characters with delimeter */
			wrmesgb(i, s, cc);
			delim(i);
			return 0;
		case C_KILL:	/* send layer signal */
			dosig(i, *s++);
			cc--;
			break;
		default:
			quit("unknown state incase 0");
		}
		ifdeftracing(if(cc<0) quit("bad count in receive"));
	}
	return 0;
}
int
ltofd(l)
{
	register i;
	if(l==0)
		return 0;
	for(i=1; i<NSELFD; i++)
		if(layer[i].busy && layer[i].chan==l)
			return i;
	trace("unknown layer %d\n", l);
	return -1;
}
void
dosig(fd, sig)		/* Interrupt shell */
{
	char sigbuf[MSGHLEN+1];
	register struct mesg *mp;

	mp = (struct mesg *)sigbuf;
	mp->type=M_SIGNAL;
	mp->magic=MSGMAGIC;
	mp->losize=sizeof(char);
	mp->hisize=0;
	sigbuf[MSGHLEN]=sig;
	write(fd, sigbuf, MSGHLEN+1);
	mp->type=M_FLUSH;
	mp->magic=MSGMAGIC;
	mp->losize=0;
	mp->hisize=0;
	write(fd, sigbuf, MSGHLEN);
}
void
wrmesgb(fd, cp, n)
	register char *cp;
	int n;
{
	char wrbuf[MAXPKTDSIZE+MSGHLEN];
	register char *bp;
	register struct mesg *mp;
	register int i;

	ifdeftracing(fprintf(tracefd, "mesg to fd %d: <%.*s>\n", fd, n, cp));
	mp = (struct mesg *)wrbuf;
	mp->type=M_DATA;
	mp->magic=MSGMAGIC;
	mp->losize=n;
	mp->hisize=n>>8;
	bp=wrbuf+MSGHLEN;
	i=n;
	while(i--)
		*bp++=*cp++;
	write(fd, wrbuf, MSGHLEN+n);
}
delim(fd)
{
	struct mesg delbuf;

	ifdeftracing(fprintf(tracefd, "delim fd %d\n", fd));
	delbuf.type=M_DELIM;
	delbuf.magic=MSGMAGIC;
	delbuf.losize=0;
	delbuf.hisize=0;
	write(fd, (char *)&delbuf, MSGHLEN);
}
int
doshell()
{
	register fd, slave;
	int fp[2];
	trace("do shell\n", 0);
	if(pipe(fp)<0){
		trace("can't pipe, errno=%d\n", errno);
		return -1;
	}
	slave=fp[0];
	fd=fp[1];
	trace("pipe %d\n", fd);
	if(ioctl(fd, FIOPUSHLD, &mesg_ld) == -1){
		trace("FIOPUSHLD fails, errno=%d\n", errno);
		close(fd);
		return -1;
	}
	setdelim(fd);	/* hack */
	switch(fork()){
	case 0:
		/* close every file descriptor in sight, and then some */
		for(fd=0; fd<5+NLAYERS; fd++)
			if(fd!=slave)
				close(fd);
		dup(slave); dup(slave); dup(slave); dup(slave);
		close(slave);
		ioctl(0, TIOCSPGRP, 0);
		signal(SIGPIPE, (int (*)())0);
		execlp(shell, shell, 0);
		perror(shell);
		exit(1);
		break;
	case -1:
		close(fd);
		close(slave);
		return -1;
	}
	trace("doshell succeeds\n", 0);
	close(slave);
	return fd;
}
setdelim(fd)
{
	struct mesg m;

	m.type = M_YDEL;
	m.magic = MSGMAGIC;
	m.losize = m.hisize = 0;
	write(fd, (char *)&m, MSGHLEN);
}
int
boot(s)
	char *s;
{
	if(system("/usr/jerq/bin/32ld", "32ld", s))
		return 1;
	sleep(2);
	return 0;
}
int
system(s, t, u)
char *s, *t, *u;
{
	int status, pid, l;

	if ((pid=fork())==0){
		execl(s, t, u, 0);
		_exit(127);
	}
	while ((l = wait(&status)) != pid && l != -1)
		;
	if (l == -1)
		status = -1;
	return(status);
}
char *
itoa(i)
	register int i;
{
	static char str[11];
	register char *	sp = &str[sizeof str];

	*--sp = '\0';
	if(i>0){
		do
			*--sp=i%10+'0';
		while((i/=10)>0);
	}else
		*--sp='0';
	return sp;
}
service(argc, argv)
	char *argv[];
{
	if(strcmp(argv[0], "cd")==0){
		char *where=argv[1];
		char buf[CDSIZE+1];
		buf[CDSIZE]=0;
		if(where==0 && (where=getenv("HOME"))==0){
			write(2, "cd: no HOME set\n", 16);
			return 1;
		}
		while(*where){
			strncpy(buf, where, CDSIZE);
			ioctl(0, JCHDIR, buf);
			where+=strlen(buf);
		}
		if(ioctl(0, JCHDIR, where)!=0){
			write(2, "cd: bad directory\n", 18);
			return 1;
		}
		return 0;
	}
	if(strcmp(argv[0], "exit")==0)
		return ioctl(0, JEXIT, 0);
	write(2, "mux: no such command ", 21);
	write(2, argv[0], strlen(argv[0]));
	write(2, "\n", 1);
	return 1;
}
#ifdef	TRACING
/*VARARGS1*/
void
trace(s, a)
	char *s, *a;
{
	long t;
	extern long time();
	extern char *ctime();

	if(s)
		fprintf(tracefd, s, a);
	else{
		(void)time(&t);
		fprintf(tracefd, "%.9s", ctime(&t)+11);
	}
}
int
twrite(fd, s, n)
	unsigned char *	s;
{
	register i;
	fprintf(tracefd, "to jerq: ");
	for(i=0; i<n; i++)
		fprintf(tracefd, "<%o>", s[i]);
	fprintf(tracefd, "\n");
	return write(fd, s, n);
}
void
tread(s, n)
	unsigned char *	s;
{
	register i;
	fprintf(tracefd, "from jerq: ");
	for(i=0; i<n; i++)
		fprintf(tracefd, "<%o>", s[i]);
	fprintf(tracefd, "\n");
}
#endif