4.4BSD/usr/src/contrib/usr.x25/nimd/nim.c

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

/*
 * Network Interface Machine Server
 *
 * Frank Pronk
 * Copyright (c) 1984
 */

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>

#include <signal.h>
#include <errno.h>
#include <setjmp.h>
#include <sgtty.h>
#include <pwd.h>

#include "../h/x29.h"

#include "buf.h"
#include "nim.h"

extern	char chartab[128];

jmp_buf	JmpBuf;			/* non-local goto buffer for interval timer */
short	PtyFd = -1, NetFd = -1;
short	LogFd = -1;
char	*LogDev;
short	TimerOn;		/* interval timer armed */
char	Banner[] = "NIM daemon\r";
extern	int errno;
char	*TraceFile;		/* trace file name */
char	*PtyName;
struct	bufhd ptyoqueue, netoqueue;
struct	buf *packet;
char	user_name[50];

struct	netinfo NetInfo = { CCITT1980, NX29_1980_PARMS, 128 };

struct	PtyPacket {
	char	p_type;
	char	p_data[1];	/* usually more than one byte */
};

char	*x25err();

main(argc, argv)
register char **argv;
{
	register int s;
	register char *p;
	int on = 1, uid;
	struct passwd *pw;

#ifdef waterloo
	/*
	 * If this host doesn't support X.25, give up.
	 */
	s = socket(AF_CCITT, SOCK_STREAM, 0);
	if (s < 0 && errno == EPROTONOSUPPORT) {
		fprint(2, "nimd: X.25 is not supported on this machine\n");
		exit(1);
	}
	close(s);
#endif

	for (argv++; argc > 1; argc--, argv++)
		if (**argv == '-')
			for (p = *argv+1; *p; p++)
			switch (*p) {
			case 'c':
				if (argc > 1) {
					argc--; argv++;
					if (strcmp (*argv, "1978") == 0) {
						NetInfo.n_nparms = NX29_1978_PARMS;
						break;
					} else if (strcmp (*argv, "1980") == 0) {
						NetInfo.n_nparms = NX29_1980_PARMS;
						break;
					}
				}
				fatal ("1978 or 1980 expected after -c");
				break;

			case 't':
				if (argc > 1) {
					argc--; argv++;
					TraceFile = *argv;
				} else
					fatal ("trace file name expected");
				break;

			default:
				fatal("usage: nimd [-c ccitt-date] [-h size] [-n size] [-t trace_file] pty_name");
			}
		else
			PtyName = *argv;

	if (PtyName == 0)
		fatal ("No pseudo-tty specified");

	if (fork())
		exit(0);
	for (s = 0; s < 10; s++)
		(void) close(s);
	(void) open("/", 0);
	(void) dup2(0, 1);
	(void) dup2(0, 2);
	{ int tt = open("/dev/tty", 2);
	  if (tt > 0) {
		ioctl(tt, TIOCNOTTY, (char *)0);
		close(tt);
	  }
	}

	OpenLog ();
	packet = getbuf (MAXPSIZ);
	ResetBufs ();
	signal (SIGPIPE, SIG_IGN);

	for (;;) {
		int bits;

		if ((PtyFd = open (PtyName, 2)) < 0)
			fatal (x25err(PtyName));
		ioctl (PtyFd, FIONBIO, (char *)&on);
		ioctl (PtyFd, TIOCPKT, (char *)&on);
		ioctl (PtyFd, TIOCREMOTE, (char *)&on);
		bits = (1 << PtyFd);
		select (16, (int *)0, &bits, (int *)0, (struct timeval *)0);
		sleep (1);	/* wait for pty to settle down */
#ifdef TIOCGSUID
		/*
		 * Get the slave's uid.
		 */
		uid = -1;
		if (ioctl(PtyFd, TIOCGSUID, &uid) < 0) {
			perror("nimd: ioctl TIOCGSUID");
			close(PtyFd);
			continue;
		}
		if (uid < 0 || (pw = getpwuid(uid)) == 0) {
			message("nimd: uid %d: Who are you?\n", uid);
			close(PtyFd);
			continue;
		}
		strcpy(user_name, pw->pw_name);
		/*
		 * Set effective uid to the slave
		 * so he/she will be charged for X.25 usage.
		 */
		setreuid(0, pw->pw_uid);
#endif
		nim ();
		sleep (1);	/* wait for slave to disconnect */
		setreuid(0, 0);
		CloseLog(); OpenLog();	/* allow log rollover */
	}
	/*NOTREACHED*/
}

fatal (msg)
char *msg;
{
	OpenLog ();
	log ("%s", msg);
	print ("nimd: %s\n", msg);
	exit (1);
}

/*VARARGS*/
message(fmt, a1, a2, a3, a4, a5)
char *fmt;
{
	char buf[128];

	sprint (buf, fmt, a1, a2, a3, a4, a5);
	ToPty (buf, strlen (buf), FROMNIM);
}

error()
{
	register char *errp;

	errp = x25err ((char *)0);
	log (errp);
	message ("nimd: %s\r", errp);
}


/*
 * Main loop.  Select from pty and network, and
 * hand data to nim receiver.
 */

nim()
{
	register int len;
	char buf[MAXPSIZ];
	int on = 1, ibits, obits, first = 1;

	InitProfile (DEFAULT_PROFILE);
	message (Banner);
	State = ST_COMMAND;
	OpenLog ();
	if (user_name[0])
		log("slave connect: %s", user_name);
	else
		log ("slave connect");

	for (;;) {
		(void) setjmp (JmpBuf);
		if (first) {
			obits = (1 << PtyFd);
			ibits = 0;
			first = 0;
			goto do_io;
		}
		ibits = obits = 0;

		/*
		 * Never look for input if there's still
		 * stuff in the corresponding output buffer
		 */

		if (PtyFd >= 0)
			if (!QEMPTY(&ptyoqueue))
				obits |= (1 << PtyFd);
			else
				if (NetFd >= 0 && State == ST_DATA)
					ibits |= (1 << NetFd);

		if (!QEMPTY(&netoqueue) && NetFd >= 0) {
			if (!OutputBlocked)
				obits |= (1 << NetFd);
		} else
			if (PtyFd >= 0)
				ibits |= (1 << PtyFd);

		if (ibits == 0 && obits == 0)	/* nothing to do; go home */
			break;

		if (State & ST_COMMAND) {
			struct timeval TimeOut;

			TimeOut.tv_sec = 60;
			TimeOut.tv_usec = 0;
			if (select (16, &ibits, &obits, (int *)0, &TimeOut) <= 0) {
				log ("slave inactivity timeout");
				break;
			}
		} else {
			if (TimerOn)
				sigsetmask (0);
			(void) select (16, &ibits, &obits, (int *)0, (struct timeval *)0);
			if (TimerOn)
				sigsetmask (1 << (SIGALRM -1 ));
		}

	do_io:
		/*
		 * Something to read from the network...
		 */
		if (ibits & (1 << NetFd))
			if ((len = ReadAndTrace(NetFd, buf, MAXPSIZ, "net rx")) == 0) {
				if (errno != EWOULDBLOCK)
					NetworkError ();
			} else
				FromNet (buf, len);

		/*
		 * Something to read from the pty...
		 */
		if (ibits & (1 << PtyFd))
			if ((len = ReadAndTrace (PtyFd, buf, NetInfo.n_psize+1, "pty rx")) == 0) {
				if (errno != EWOULDBLOCK) {
					close (PtyFd);
					PtyFd = -1;
				}
			} else
				FromPty ((struct PtyPacket *)buf, len);

		if (obits & (1<<NetFd)) {
			if (FlushQueue (NetFd, &netoqueue, "net tx") < 0 && 
				errno != EWOULDBLOCK)
				NetworkError ();
		}

		if (obits & (1 << PtyFd))
			if (FlushQueue (PtyFd, &ptyoqueue, "pty tx") < 0 &&
				errno != EWOULDBLOCK) {
				close (PtyFd);
				PtyFd = -1;
			}
	}
	cleanup ();
}

ReadAndTrace (fd, buf, len, who)
char *buf, *who;
{
	register int bytes;

	bytes = read (fd, buf, len);
	if (bytes <= 0)
		return (0);
	NimTrace (who, buf, bytes);
	return (bytes);
}

NetworkError ()
{

	error ();
	State = ST_COMMAND;
	close (NetFd);
	NetFd = -1;
	message (Banner);
}

cleanup ()
{
	log ("slave disconnect");
	close (NetFd);
	close (PtyFd);
	NetFd = PtyFd = -1;
	if (TimerOn)
		ClearTimer ();
	ResetBufs ();
}

ResetBufs ()
{
	InitQueue (&ptyoqueue);
	InitQueue (&netoqueue);
	RESET (packet);
}

ToPty (str, len, from)
register char *str;
{
	register char *end = str + len, c, c1;
	register struct buf *bp = 0;
	register int lfcode;

	while (str < end) {
		c = *str++;
		c1 = c & 0177;
		if (CurrentX29Parms[X29_AUX_DEV_CONTROL_CODE]) {
			if (c1 == 023) {
				OutputBlocked++;
				continue;
			}
			if (c1 == 021) {
				OutputBlocked = 0;
				continue;
			}
		}
		if (ptyoqueue.b_count > 256)
			continue;
		if (bp == 0)
			bp = getbuf (MAXPSIZ);
		PUTCHAR (c, bp);
		if (SIZE (bp) >= MAXPSIZ-1) {
			enqueue (bp, &ptyoqueue);
			bp = 0;
		}
		if (c1 != '\r' || (lfcode = CurrentX29Parms[X29_LF_AFTER_CR]) <= 0)
			continue;
		if ((lfcode & 01) && from == FROMNET ||
		    (lfcode & 04) && from == FROMPTY ||
		     from == FROMNIM)
			PUTCHAR ('\n', bp);
	}
	if (bp)
		enqueue (bp, &ptyoqueue);
}

FromPty(pp, len)
register struct PtyPacket *pp;
{
	register int echo;
	register struct buf *tp = packet;
	register char *cp, c;
	char c1;

	if (pp->p_type != TIOCPKT_DATA) {	/* fetch control byte */
		PtyControl (pp);
		return;
	}
	if (State & ST_UGLY_50_BAUD_BREAK_IN_PROGRESS)
		return;
	cp = pp->p_data;
	echo = CurrentX29Parms[X29_ECHO_CODE] > 0 && ptyoqueue.b_count < 256;
	while (cp < ((char *)pp)+len) {
		c = (c1 = *cp++) & 0177;
		if (State & ST_ESCSEEN && C_TYPE(c) != C_ESCAPE)
			EnterCommandState ();
		switch (C_TYPE(c)) {
		case C_ERASE:
			if (!ISEMPTY(tp)) {
				tp->b_top--;
				if (echo)
					if (c == '\b')
						ToPty ("\b \b", 3, FROMPTY);
					else
						ToPty (&c1, 1, FROMPTY);
			}
			continue;

		case C_KILL:
			RESET (tp);
			if (echo)
				ToPty ("*poof*\r", 7, FROMPTY);
			continue;

		case C_DISPLAY:
			ToPty (tp->b_bot, SIZE (tp), FROMPTY);
			continue;

		case C_ESCAPE:
			if ((State & (ST_COMMAND|ST_ESCSEEN)) == 0) {
				State |= ST_ESCSEEN;
				continue;
			}
			State &= ~ST_ESCSEEN;
			/* fall through */

		default:
			if (State & ST_COMMAND) {
				if (c == '\r') {
					if (echo)
						ToPty (&c1, 1, FROMPTY);
					PUTCHAR ('\0', tp);
					FlushQueue (PtyFd, &ptyoqueue, "pty tx");
					NimCommand (tp->b_bot);
					RESET(tp);
					continue;
				}
				if (SIZE (tp) < MAXPSIZ-1) {
					PUTCHAR (c, tp);
					if (echo)
						ToPty (&c1, 1, FROMPTY);
				} else
/*					ToPty ("\007", 1, FROMPTY)*/;
			} else {
				PUTCHAR (c1, tp);
				if (echo)
					ToPty (&c1, 1, FROMPTY);
				if (ISFORWARD(c) || SIZE (tp) >= NetInfo.n_psize) {
					ForwardPacket ();
					continue;
				}
			}
		}
	}
	if (!ISEMPTY (tp) && (State & ST_COMMAND) == 0)
		SetTimer ();
}

PtyControl(pp)
register struct PtyPacket *pp;
{
#ifdef notdef
	if ((pp->p_type & (TIOCPKT_FLUSHWRITE|TIOCPKT_FLUSHREAD)) ==
	   (TIOCPKT_FLUSHWRITE|TIOCPKT_FLUSHREAD)) {	/* break indication from pty */
		if (State & ST_COMMAND)
			RESET (packet);
		else
			Break (CurrentX29Parms[X29_BREAK_PROCEDURE_CODE]);
		return;
	   }
#endif
#ifdef TIOCPKT_IOCTL
	if (pp->p_type & TIOCPKT_IOCTL) {	/* some kind of set tty done by slave */
		static short UnixToX29Speed[] = {
			0, 10, 5, 0, 1, 6, 8, 2, 4, 3,	/* B0 thru B1200 */
			12, 13, 14, 15, 16		/* B2400 thru EXTB */
		};
		struct sgttyb sg;

		ioctl(PtyFd, TIOCGETP, (char *)&sg);
		if (sg.sg_ospeed == B50) {
			State |= ST_UGLY_50_BAUD_BREAK_IN_PROGRESS;
			return;
		}
		CurrentX29Parms[X29_TRANSMISSION_SPEED_CODE]
			= UnixToX29Speed[sg.sg_ospeed];
		if (State & ST_UGLY_50_BAUD_BREAK_IN_PROGRESS && sg.sg_ospeed != B50) {
			State &= ~ST_UGLY_50_BAUD_BREAK_IN_PROGRESS;
			if (State & ST_COMMAND)
				RESET (packet);
			else
				Break (CurrentX29Parms[X29_BREAK_PROCEDURE_CODE]);
		}
	}
#endif
}

EnterCommandState ()
{
	State &= ~ST_ESCSEEN;
	State |= ST_COMMAND | ST_ESCCOMM;
	ForwardPacket ();
}

ExitDataState (cause)
char *cause;
{
	ResetBufs ();
	close (NetFd);
	NetFd = -1;
	State = ST_COMMAND;
	OutputBlocked = 0;
	CurrentX29Parms[X29_DISCARD_OUTPUT_CODE] = 0;
	message ("nimd: Call cleared - %s\r", cause);
	log ("Call cleared - %s", cause);
}

ForwardPacket ()
{
	register struct buf *bp, *tp = packet;

	if (!ISEMPTY(tp) && (State & ST_COMMAND) == 0) {
		AddParity (tp->b_bot, SIZE (tp));
		bp = getbuf (SIZE (tp) + 1);
		PUTCHAR(0, bp);
		BufCopy(tp, bp);
		enqueue (bp, &netoqueue);
	}
	RESET (tp);
	if (TimerOn)
		ClearTimer ();
}

ToNet (pp, len)
struct packet *pp;
{
	register struct buf *bp;

	/*
	 * round buffer size up to a multiple of 64 bytes
	 * to reduce accumulation of small and usually
	 * useless buffers in the free list.  This speeds
	 * up malloc().
	 */
	bp = getbuf ((len + 63) & ~63);
	bcopy ((char *)pp, bp->b_bot, len);
	bp->b_top = bp->b_bot + len;
	enqueue (bp, &netoqueue);
}

timeout()
{
	TimerOn = 0;
	ForwardPacket ();
	longjmp (JmpBuf, 0);
}

SetTimer ()
{
	register int t;
	struct itimerval itv;

	if (TimerOn || (t = CurrentX29Parms[X29_IDLE_TIMER_CODE]) <= 0)
		return;
	itv.it_interval.tv_sec = 0;
	itv.it_interval.tv_usec = 0;
	itv.it_value.tv_sec = t / 20;
	itv.it_value.tv_usec = t % 20 * 1000000 / 20;
	signal (SIGALRM, SIG_IGN);	/* cancel possible pending signal */
	signal (SIGALRM, timeout);
	sigsetmask (1 << (SIGALRM - 1));
	TimerOn++;
	setitimer (ITIMER_REAL, &itv, (struct itimerval *)0);
}

ClearTimer ()
{
	struct itimerval itv;

	signal (SIGALRM, SIG_IGN);
	bzero ((char *)&itv, sizeof (itv));
	setitimer (ITIMER_REAL, &itv, (struct itimerval *)0);
	TimerOn = 0;
}

FromNet (bp, len)
char *bp;
{
	if ((*bp & Q_BIT) == 0) {
		register struct x25packet *xp = (struct x25packet *)bp;

		if (CurrentX29Parms[X29_DISCARD_OUTPUT_CODE] == 0) {
			AddParity (xp->p_x25data, len - 1);
			ToPty (xp->p_x25data, len - 1, FROMNET);
		}
		return;
	}
	X29ControlMessage ((struct x29packet *)bp, len);
}

SendX25Interrupt()
{
	char c = 0x77;

	send (NetFd, &c, 1, MSG_OOB);
}

#ifdef fastidious	/* we need stdio */
/*
 * Sorry about this...
 * Defining this dummy procedure prevents the stdio package
 * (about 17K bytes worth) from being loaded.  This program
 * does not require any support from the 4.2bsd stdio library.
 */

#ifdef vax
_cleanup()
{
}
#endif
#endif

NimTrace(who, bp, n)
char *who, *bp;
{
	static int fd;

	if (TraceFile == 0)
		return;
	if(fd <= 0 && (fd = creat(TraceFile, 0640)) < 0)
		return;
	DisplayPacketContents (fd, who, bp, n);
}

OpenLog ()
{

	if (LogFd >= 0)
		return;
	if (LogDev = rindex (PtyName, '/'))
		LogDev++;
	else
		LogDev = PtyName;
	if ((LogFd = open (LOGFILE, 1)) >= 0)
		return;
	LogFd = creat (LOGFILE, 0640);
}

CloseLog()
{
	if (LogFd >= 0) {
		close(LogFd);
		LogFd = -1;
	}
}

/*VARARGS*/
log(fmt, a1, a2, a3, a4, a5)
char *fmt;
{
	register char *DateTime;
	char format[128], *ctime ();
	time_t t;

	if (LogFd < 0)
		return;
	(void) time (&t);
	DateTime = ctime (&t);
	DateTime[19] = '\0';
	sprint (format, "%s %s %s\n", DateTime+4, LogDev, fmt);
	lseek (LogFd, (long)0, 2);
	fprint (LogFd, format, a1, a2, a3, a4, a5);
}

LogPacket (bp, len)
char *bp;
{
	if (LogFd < 0)
		return;
	DisplayPacketContents (LogFd, "net rx", bp, len);
}

DisplayPacketContents (fd, from, pp, len)
char *from;
register char *pp;
register int len;
{
	register int first = 1;
	char buf[128];

	lseek (fd, (long)0, 2);
	do {
		ConvertToOctal (pp, len, buf);
		if (first) {
			fprint (fd, "%s[%d]\t%s\n", from, len, buf);
			first = 0;
		} else
			fprint (fd, "\t\t%s\n", buf);
		ConvertToAscii (pp, len, buf);
		fprint (fd, "\t\t%s\n", buf);
		pp += 16;
		len -= 16;
	} while (len > 0);
}

ConvertToOctal (start, len, bp)
register char *start, *bp;
{
	register char *cp;

	if (len > 16)
		len = 16;
	for (cp = start; cp - start < len; cp++) {
		*bp++ = ((*cp & 0300) >> 6) + '0';
		*bp++ = ((*cp & 070) >> 3) + '0';
		*bp++ = (*cp & 07) + '0';
		*bp++ = ' ';
	}
	bp[-1] = '\0';
}
			
ConvertToAscii (start, len, bp)
register char *start, *bp;
{
	register char *cp;

	if (len > 16)
		len = 16;
	for (cp = start; cp - start < len; cp++) {
		*bp++ = ' ';
		switch (*cp) {
		case '\b':
			*bp++ = '\\';
			*bp++ = 'b';
			break;

		case '\t':
			*bp++ = '\\';
			*bp++ = 't';
			break;

		case '\n':
			*bp++ = '\\';
			*bp++ = 'n';
			break;

		case '\f':
			*bp++ = '\\';
			*bp++ = 'f';
			break;

		case '\r':
			*bp++ = '\\';
			*bp++ = 'r';
			break;

		default:
			*bp++ = ' ';
			if ((*cp&0177) > ' ' && (*cp&0177) < 0177)
				*bp++ = *cp;
			else
				*bp++ = ' ';
		}
		*bp++ = ' ';
	}
	bp[-1] = '\0';
}