BSD 4_4 development
[unix-history] / usr / src / contrib / usr.x25 / nimd / nim.c
/*
* 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';
}