v08i073: Bidirectional getty/login for SystemV, Part02/02

sources-request at mirror.UUCP sources-request at mirror.UUCP
Fri Feb 20 08:49:32 AEST 1987


Submitted by: mit-eddie!cdx39!jc (John Chambers)
Mod.sources: Volume 8, Issue 73
Archive-name: uutty/Part02

[  Sorry for the mixed-mode address on the previous article...  --r$  ]

: This is a shar archive. Extract with sh, not csh
echo file: lastfield.c
cat > lastfield.c << '\!Funky\!Stuff\!'
/*
** Find the last field of a string.
*/
char *
lastfield(p,c)
	char *p;	/* Null-terminated string to scan */
	int   c;	/* Separator char, usually '/' */
{	char *r;

	r = p;
	while (*p)			/* Find the last field of the name */
		if (*p++ == c)
			r = p;
	return r;
}
\!Funky\!Stuff\!
echo file: lockname.c
cat > lockname.c << '\!Funky\!Stuff\!'
#include "uutty.h"
/*
** Build name for a lockfile.
*/
lockname()
{	char *dp, *lp, *rp;

	D5("lockname(\"%s\")",device);
	devfld = dp = lastfield(device,'/');
	D4("devfld=\"%s\" device=\"%s\"",devfld,device);
	lp = lockfile;		/* Place to build lockfile name */
	rp = lockroot;		/* Place to build lockfile name */
	while (*rp) *lp++ = *rp++;	/* Copy root to lockname */
	while (*dp) *lp++ = *dp++;	/* Append the device name */
	*lp = 0;
	D4("lockname:lockfile=\"%s\"",lockfile);
}
\!Funky\!Stuff\!
echo file: lockup.c
cat > lockup.c << '\!Funky\!Stuff\!'
#include "uutty.h"
/* 
** The lockfile exists; hang until it disappears.
*/
lockup()
{	int n;

	n = 0;
	do {
		if ((n==0 && debug>=2) || debug>=3)
			P("%s Lockfile \"%s\" exists.",getime(),lockfile);
		if (lsleep > 0)
			sleep(lsleep);
		++n;
		}
			while (stat(lockfile,&status) >= 0);
	D3("locked: Lockfile \"%s\" freed.",lockfile);
	if (debug>=2)
		P("%s Lockfile \"%s\" gone.",getime(),lockfile);
	return n;
}
\!Funky\!Stuff\!
echo file: lockwait.c
cat > lockwait.c << '\!Funky\!Stuff\!'
#include "uutty.h"
/*
** Wait on a lockfile.
*/
lockwait()
{	unsigned n;

	D5("lockwait()");
	if (lockfile[0] == 0) lockname();
	D4("lockwait:lockfile=\"%s\"",lockfile);
	n = 0;
	if (stat(lockfile,&status) >= 0) {
		D4("%s Lockfile \"%s\" exists.",getime(),lockfile);
		n = lockup();
		D4("%s Lockfile \"%s\" gone.",getime(),lockfile);
	}
	if (n) {				/* Port may be screwed up */
		opendev();			/* Close and reopen it */
		restdev();			/* Get it into proper state */
	}
	D4("lockwait:Returned after %d waits.",n);
	return n;
}
\!Funky\!Stuff\!
echo file: main.c
cat > main.c << '\!Funky\!Stuff\!'
#include "uutty.h"
#include <signal.h>
static char id[] = "@(#)RELEASE: 1.0  Sep 20 1986 ~jc/uutty";
/*
**	uutty device [option]...
**
** This is John Chambers' own personal serial-port daemon, 
** to watch a port, determine what sort of critter is at the 
** other end of the line, and engage it in a semi-intelligent 
** dialog.  The primary goal of this version, uutty, is to
** make it possible to use direct links (null modems) freely in
** both directions, without the problems caused by the getty(1)
** program.  In particular, it allows the use of the uucp package
** (including uux and cu) in both directions across a line.
**
** Another thing uutty is good for is using a modem in either
** without needing any special actions.
**
** Uutty is a thinly-disguised "state machine" whose basic 
** behavior is determined by a global "state variable" ss
** and a string of input characters.  The state variable is
** simply an integer that encodes the last action performed.  
** An input routine is called, and when it returns, the input
** is examined to determine the program's next action.  Normally, 
** this action is to produce an output string, change the state 
** variable, and wait for the next input.
**
** The major intent of this program is to do a login interview,
** but to ignore anyone at the other end of the line that acts
** like it's trying to log us in.  If getty or this program is
** at the other end, the result will be silence until someone
** sends a CR or LF (which elicits "login:") or something that
** is acceptable as a login id.  Note in particular that input
** containing colons and spaces will be ignored.
**
** The other major intent is to cooperate with any software that
** sets the uucp lockfiles ("/usr/spool/uucp/LCK..<device>"), by
** checking for such lockfiles frequently, and sleeping any time
** they are found.  This program never creates a lockfile; it just
** sleeps when someone else creates one.  Thus, this program may
** be left running on a port used by uucp, cu, or other such serial-
** port users.  There is a slight possibility that this program
** may eat up part of the first response from the port, but that
** is rarely much of a problem.  One or two CRs will usually suffice
** to elicit a "gin:" prompt from the other end.
**
** A third, minor intent is to recognize overly-intelligent modems,
** and refuse to become engaged in a conversations with them.  This
** is done primarily by rejecting input that contains whitespace or
** special characters, or which contains only upper-case letters.
** This will handle the error messages produced by most commercial
** modems.  However, users may wish to add to the code that examines
** login ids and passwords, and add more checks for specific modems.
**
** You may notice that this program is full of Dx(...) debug calls.
** These are defined in dbg.h, and may be suppressed fairly easily.
** Most C compilers will react to, for instance:
** 	#define D9 if(0) dmsg
** by generating no code at all.  You might think this would make
** your program smaller and faster, and you'd be partly right.  It
** will definitely be smaller, but probably not much faster.  Anyhow,
** the speed of this program isn't much of an issue; if it gets into
** a feedback loop with a modem, you don't much care how efficiently
** it brings your system to its knees!  It's probably a wiser idea
** to leave most of the diagnostic junk in.  You'll be surprised at
** how useful it can be to have that stuff in an audit trail.
**
** Some of the options recognized are:
**
** -b<n>    use a baud rate of <n>.  Final "00" may be omitted.
**
** -c<n>    slow down by counting to <n> between output chunks.
**
** -d<n>    debug level <n>; default is -d1.
**
** -e<s>    exit string <s> to send when terminating.
**
** -f       fork a subprocess for starting shells.
**
** -i<s>    initialization string <s> is sent whenever it is
**          decided that the other end is insane or jabbering.
**
** -l       create uucp lockfiles before starting a shell; delete 
**          them afterwards.  This option implies the -f option.
**
** -n<s>    nudge string <s> is sent when this program wants a response.
**
** -p<f>    port to use, where <f> is usually /dev/tty??.  This is
**			the same as using <f> without any prefix.
**
** -s<n>    sleep <n> seconds between output chunks.
**
** -x<n>    debug level <n>.
**
** To be responsible, I should repeat the warning stated elsewhere:
** with debug level -d2 or higher, uutty writes everything it sees,
** including unencrypted login ids and passwords, to its diagnostic
** output stream (audit trail).  This makes it a Trojan Horse of the
** first kind.  If you are security-conscious, you might take steps
** to hide the audit trail, and purge it frequently.
**
** In any case, the audit trail grows without bounds, so you will want
** to take steps to keep it within bounds.  The easiest way is with an
** entry in crontab that starts up a daily cleanup script (/etc/cleanup
** is a real good name for it).  This script can move the uutty audit 
** trails to a different name, to produce one level of backup.
*/
main(ac,av)
	char**av;
{	int i, r;

	time(&currtime);		/* Note the start time */
	progname = av[0];		/* For debug output */
	prgnam = lastfield(progname,'/');
	D4("prgnam=\"%s\" progname=\"%s\"",prgnam,progname);
	euid = geteuid();
	egid = getegid();
	ruid = getuid();
	rgid = getgid();
	D3("euid=%d egid=%d ruid=%d rgid=%d",euid,egid,ruid,rgid);
	args(ac,av);			/* Process command-line args */
	if (debug) {			/* Generate first entries in audit trail */
		P("+--------------------------------------------------------------------+");
		P("%s %s started.",getime(),progname);
	}
	timeout = 255;			/* Use max timeout on reads */
	sig();				/* Intercept all the signals */
	slowfl = (count > 0) || (slow > 0);
	opendev();			/* Open the port which we are to use */
	if (dev != 0) {close(0); i = dup(dev); D3("File %d=\"%s\"",i,device);}
	if (dev != 1) {close(1); i = dup(dev); D3("File %d=\"%s\"",i,device);}
	lockwait();			/* First check to see if it's busy */
/*
** The code to handle raw, 8-bit communication get a bit weird from
** system to system.  Here, we try to trap the initial state of the
** port, so that when we die, we can first restore it.
*/
#ifdef SYS5
	if (raw && isatty(dev)) {	/* We will want to restore its initial state */
		D5("main:before ioctl(%d,%d,%06lX)",dev,TCGETA,&trminit);
		i = ioctl(dev,TCGETA,&trminit);  /* Note initial state of terminal */
		D5("main: after ioctl(%d,%d,%06lX)=%d",dev,TCGETA,&trminit,i);
		D7("main: %d:\tcflag=%06o",dev,trminit.c_cflag);
		D7("main: %d:\tiflag=%06o",dev,trminit.c_iflag);
		D7("main: %d:\tlflag=%06o",dev,trminit.c_lflag);
		D7("main: %d:\toflag=%06o",dev,trminit.c_oflag);
		termfl = 1;
/*	crlf = "\n\r";			** Not needed on Unix systems */
	}
#endif
	restdev();			/* Get port to desired (raw, very public) state */
#ifdef SYS5
/*
** The following functions suffice to find, update, and write
** the appropriate entry in the /etc/utmp file, which on SYS/V
** is the record of logged-in users.  This may well have to be
** changed for other systems.  The hope here is that this is a
** reasonable way to modularize the job, though the names may
** not be ideal...
*/
	D8("main:before findutmp()");
	up = findutmp();		/* Try to locate our /etc/utmp entry */
	D7("main: after findutmp()=%06lX",up);
	D8("main:before fillutmp(\"%s\",%X,\"%s\",%d)",prgnam,(char*)0,devfld,LOGIN_PROCESS);
	fillutmp(prgnam,(char*)0,devfld,LOGIN_PROCESS);
	D7("main: after fillutmp()");
	D8("pswd:before pututline(%06lX)",up);
	pututline(up);			/* Put modified line into /etc/utmp */
	D7("pswd: after pututline(%06lX)",up);
#endif
	r = talk();			/* Attempt a conversation */
	die(r);			/* If we should ever decide to stop */
	return 0;			/* Paranoia! */
}
\!Funky\!Stuff\!
echo file: makeraw.c
cat > makeraw.c << '\!Funky\!Stuff\!'
/* Tell the device driver to do raw I/O on the device.  
 * Unfortunately, this is done in different ways on different
 * brands of Unix.  Make sure that your system is included
 * somewhere in the following list.
 */
#include <dbg.h>
#ifdef BERK
#	include <sgtty.h>
	int ldisc = NETLDISC;
	struct sgttyb sgttyb;
#endif
#ifdef PCIX
#	include <termio.h>
	struct termio termio;
#endif
#ifdef SYS5
#	include <termio.h>
	struct termio termio;
#endif
extern int baudmask;	/* CBAUD mask, if baud rate specified */
extern int errno;
int  flowcontrol = 1;
uint timeout = 100;	/* Timeout in 0.1 second quanta */

makeraw(fn)
	int fn;		/* File number */
{	int i;

	D5("makeraw(%d)",fn);
	errno = 0;
#ifdef SYS5
	i = ioctl(fn,TCGETA,&termio);
	D4("makeraw: %d:\tcflag=%06o [old]",fn,termio.c_cflag);
	D4("makeraw: %d:\tiflag=%06o [old]",fn,termio.c_iflag);
	D4("makeraw: %d:\tlflag=%06o [old]",fn,termio.c_lflag);
	D4("makeraw: %d:\toflag=%06o [old]",fn,termio.c_oflag);
	D5("makeraw: ioctl(fn=%d,TCGETA=%d,&termio=%08X)=%d",fn,TCGETA,&termio,i);
	if (baudmask)			/* Wipe out all of cflag but speed */
	     termio.c_cflag  = baudmask;
	else termio.c_cflag &= CBAUD;
	termio.c_cflag |= CS8 | CREAD | HUPCL;
	termio.c_iflag  = flowcontrol? (IXON | IXANY | IXOFF): 0;
	termio.c_lflag  = 0;
	termio.c_oflag  = 0;
	termio.c_cc[4]  = 0;		/* Number of bytes to buffer up */
	termio.c_cc[5]  = timeout;	/* Timeout in 0.1 sec units */
	D4("makeraw: %d:\tcflag=%06o [new]",fn,termio.c_cflag);
	D4("makeraw: %d:\tiflag=%06o [new]",fn,termio.c_iflag);
	D4("makeraw: %d:\tlflag=%06o [new]",fn,termio.c_lflag);
	D4("makeraw: %d:\toflag=%06o [new]",fn,termio.c_oflag);
	i = ioctl(fn,TCSETA,&termio);
#endif
#ifdef SYS3
	i = ioctl(fn,TCGETA,&termio);
	termio.c_iflag |=  IGNCR | IXON | IXANY | IXOFF;
	termio.c_lflag  =  0;
	D5("makeraw: \tcflag=%06o",termio.c_cflag);
	D5("makeraw: \tiflag=%06o",termio.c_iflag);
	D5("makeraw: \tlflag=%06o",termio.c_lflag);
	D5("makeraw: \toflag=%06o",termio.c_oflag);
	i = ioctl(fn,TCSETA,&termio);
#endif
#ifdef BERK		/* Berkeley Unix has its own conventions */
	i = ioctl(fn,TIOCGETP,&sgttyb);
	sgttyb.sg_flags  =  RAW | TANDEM;
	ioctl(fn,TIOCSETP,&sgttyb);
	if (errno) {
		E("makeraw: Can't do raw i/o on \"%s\"",fnnam);
		exit(-1);
	}
#endif
	D5("makeraw: %d is now raw",fn);
}
\!Funky\!Stuff\!
echo file: makesane.c
cat > makesane.c << '\!Funky\!Stuff\!'
/* Tell the device driver to do normal I/O on the device.  
 * Unfortunately, this is done in different ways on different
 * brands of Unix.  Make sure that your system is included
 * somewhere in the following list.
 */
#include <dbg.h>
#ifdef BERK
#	include <sgtty.h>
	int ldisc = NETLDISC;
	struct sgttyb sgttyb;
#endif
#ifdef PCIX
#	include <termio.h>
	struct termio termio;
#endif
#ifdef SYS5
#	include <termio.h>
static	struct termio trmstat;
#endif
extern int errno;
extern int baudmask;	/* CBAUD mask if baud rate specified */

makesane(fn)
	int fn;		/* File number */
{	int i;

	D5("makesane(%d)",fn);
	errno = 0;
#ifdef SYS5
	i = ioctl(fn,TCGETA,&trmstat);
	if (debug >= 4) {
	  P("makesane: %d:\tcflag=%06o [old]",fn,trmstat.c_cflag);
	  P("makesane: %d:\tiflag=%06o [old]",fn,trmstat.c_iflag);
	  P("makesane: %d:\tlflag=%06o [old]",fn,trmstat.c_lflag);
	  P("makesane: %d:\toflag=%06o [old]",fn,trmstat.c_oflag);
	}
	D5("makesane: ioctl(fn=%d,TCGETA=%d,&trmstat=%08X)=%d",fn,TCGETA,&trmstat,i);
/* 
** The following was given by "stty -a" on one SYS/V machine:
**	speed 9600 baud; line = 2; intr = ^c; quit = ^\; erase = ^H; kill = ^x; eof = ^d; eol = ^@;susp = ^z;dsus = ^y
**	-parenb -parodd cs8 -cstopb -hupcl cread -clocal -tostop 
**	-ignbrk brkint ignpar -parmrk -inpck istrip -inlcr -igncr icrnl -iuclc 
**	ixon -ixany -ixoff 
**	isig icanon -xcase echo echoe echok -echonl -noflsh 
**	opost -olcuc onlcr -ocrnl -onocr -onlret -ofill -ofdel tab3 
speed 9600 baud; line = 2; intr = ^c; quit = ^\; erase = DEL; kill = ^x; eof = ^d; eol = ^@;susp = ^z;dsus = ^y
-parenb -parodd cs8 -cstopb -hupcl cread -clocal -tostop 
-ignbrk brkint ignpar -parmrk -inpck istrip -inlcr -igncr icrnl -iuclc 
ixon -ixany -ixoff 
isig icanon -xcase echo echoe echok -echonl -noflsh 
opost -olcuc onlcr -ocrnl -onocr -onlret -ofill -ofdel tab3 
*/
	trmstat.c_cflag &= CBAUD;	/* Save the speed */
	if (baudmask) {
	  D4("makesane: baudmask=0%o",baudmask);
	  trmstat.c_cflag = baudmask;	/* Set a different speed */
	}
	trmstat.c_cflag |= HUPCL  | CREAD  | CS8;
	trmstat.c_iflag  = BRKINT | IGNPAR | ISTRIP | ICRNL | IXON;
	trmstat.c_lflag  = ISIG   | ICANON | ECHO   | ECHOE | ECHOK;
	trmstat.c_oflag  = OPOST  | ONLCR  | TAB3;
	trmstat.c_cc[0] = 0x03;	/* INTR = ^C */
	trmstat.c_cc[1] = 0x1C;	/* QUIT = ^\ */
	trmstat.c_cc[2] = 0x10;	/* ERASE= ^H [BS] */
	trmstat.c_cc[3] = 0x18;	/* KILL = ^X */
	trmstat.c_cc[4] = 0x04;	/* EOF  = ^D */
	trmstat.c_cc[5] = 0;	/* EOL  char */
	trmstat.c_cc[6] = 0;	/* EOL2 char */
/*	trmstat.c_cc[7] = __;	** Reserved */
	trmstat.c_cc[8] = 0x1A;	/* SUSP = ^Z */
	trmstat.c_cc[9] = 0x19;	/* SUSP = ^Y */
	if (debug >= 5)  {
	  P("makesane: %d:\tcflag=%06o [new]",fn,trmstat.c_cflag);
	  P("makesane: %d:\tiflag=%06o [new]",fn,trmstat.c_iflag);
	  P("makesane: %d:\tlflag=%06o [new]",fn,trmstat.c_lflag);
	  P("makesane: %d:\toflag=%06o [new]",fn,trmstat.c_oflag);
	  Hexdnm(&trmstat.c_cc[0],NCC,"c_cc:");
	}
	i = ioctl(fn,TCSETA,&trmstat);
#endif
#ifdef SYS3		/* Note: Tested only on PC/IX */
	i = ioctl(fn,TCGETA,&trmstat);
	trmstat.c_cflag |= HUPCL  | PARENB | CREAD  | CS7;
	trmstat.c_iflag  = BRKINT | IGNPAR | ISTRIP | ICRNL | IXON;
	trmstat.c_lflag  = ISIG   | ICANON | ECHO   | ECHOE | ECHOK;
	trmstat.c_oflag  = OPOST  | ONLCR  | TAB3;
	D5("makesane: \tcflag=%06o",trmstat.c_cflag);
	D5("makesane: \tiflag=%06o",trmstat.c_iflag);
	D5("makesane: \tlflag=%06o",trmstat.c_lflag);
	D5("makesane: \toflag=%06o",trmstat.c_oflag);
	i = ioctl(fn,TCSETA,&trmstat);
#endif
#ifdef BERK		/* Note: Tested only on 4.2 */
	i = ioctl(fn,TIOCGETP,&sgttyb);
	sgttyb.sg_flags  =  SANE | TANDEM;
	ioctl(fn,TIOCSETP,&sgttyb);
	if (errno) {
		E("makesane: Can't do sane i/o on \"%s\"",fnnam);
		exit(-1);
	}
#endif
	D5("makesane: %d is now sane.",fn);
}
\!Funky\!Stuff\!
echo file: nextbyte.c
cat > nextbyte.c << '\!Funky\!Stuff\!'
#include "uutty.h"
/*
** Extract one byte from the port's input buffer, triggering
** a read if necessary.  If no data is available within the
** timeout limit, -1 is returned.  All other return values
** should be small positive integers, in [1,255].
*/
nextbyte()
{	int i;

	D9("nextbyte()");
loop:
	if (ibfa >= ibfz) {
		errno = 0;
		lockwait();
		D9("nextbyte:before read(%d,%06lX,%d)",dev,ibuf,IBUF);
		if ((i = read(dev,ibuf,IBUF)) <= 0) {
			D9("nextbyte: read(%d,%06lX,%d)=%d\t[errno=%d]"
				,dev,ibuf,IBUF,i,errno);
			Fail;
		}
		D8("nextbyte: after read(%d,%06lX,%d)=%d\t[errno=%d]"
			,dev,ibuf,IBUF,i,errno);
		if (debug >= 4) {
			dbgtimep = getime();
			Hexdnm(ibuf,i,"Read:");
		}
		if (echoing) {
			write(dev,ibuf,i);
		}
		ibfa = ibuf;
		ibfz = ibuf + i;
		*ibfz = '\0';
	}
	i = ASCII(*ibfa++);
	D9("nextbyte()=%02X='%c'",i,dsp(i));
	if (i == 0) Loop;		/* Don't return nulls */
	return i;
fail:
	D8("nextbyte()=-1 [FAILURE]");
	return -1;
}
\!Funky\!Stuff\!
echo file: opendev.c
cat > opendev.c << '\!Funky\!Stuff\!'
#include "uutty.h"
/*
** Close and re-open the port.
*/
opendev()
{
	D6("opendev()");
	if (dev >= 0) close(dev);
	if (device) {
		if ((dev = open(device,2)) < 0) {
			E("FATAL ERROR\n%s Can't open \"%s\"\t[errno=%d]",getime(),device,errno);
			die(1);
		}
		if (debug) P("%s Opened dev=%d=\"%s\".",getime(),dev,device);
	}
	return dev;
}
\!Funky\!Stuff\!
echo file: option.c
cat > option.c << '\!Funky\!Stuff\!'
#include "uutty.h"
/* 
** Process an option string.  Note that we get
** a pointer to the initial char, usually '-'.
*/
extern int baudmask;	/* CBAUD mask, if baud rate specified */
extern int baudrate;	/* Actual baud rate */

option(op)
	char*op;
{	int  n;

	D4("option(\"%s\")",op);
	switch (op[1]) {	/* Which option? */
	default:
		sprintf(stderr,"Unknown option \"%s\" ignored.\n",op);
		break;
	case 'B':		/* Set baud rate */
	case 'b':
		n = sscanf(op+2,"%u",&baudrate);
		if (n < 1) baudrate = 1;
		D3("baudrate=%u\n",baudrate);
		switch (baudrate) {
		case 12: case 1200: baudmask = B1200; break;
		case 24: case 2400: baudmask = B2400; break;
		case 48: case 4800: baudmask = B4800; break;
		case 96: case 9600: baudmask = B9600; break;
		default: E("Can't handle baud rate of %d",baudrate);
			baudrate = baudmask = 0;
		}
		break;
	case 'C':	/* Count between I/O operations */
	case 'c':
		n = sscanf(op+2,"%u",&count);
		if (n < 1) count = 1;
		D3("count=%u\n",count);
		slowfl = count || slow;
		break;
	case 'D':	/* Set debug level */
	case 'd':
	case 'X':	/* Set debug level */
	case 'x':
		n = sscanf(op+2,"%u",&debug);
		if (n < 1) debug = 1;
		D3("debug=%d\n",debug);
		break;
	case 'E':
	case 'e':		/* Exit message */
		if (op[2]) {	/* -e"msg" is exit message */
			m_exit = op + 2;
			D3("m_exit=\"%s\"",m_exit);
		} else {		/* -e turns on echoing */
			echoing = echofl = 1;
			D3("echofl=%d",echofl);
		}
		break;
	case 'F':
	case 'f':       /* Fork subprocesses for a shells */
		forkfl++;
		D3("forkfl=%d",forkfl);
		break;
	case 'H':
	case 'h':	/* Display "help" messages */
		help();
		break;
	case 'I':
	case 'i':       /* Initialization message */
		m_init = op + 2;
		D3("m_init=\"%s\"",m_init);
		break;
	case 'L':
	case 'l':       /* Create lockfile on login */
		lockfl++;
		forkfl++;
		D3("forkfl=%d lockfl=%d",forkfl,lockfl);
		break;
	case 'N':
	case 'n':       /* Nudge message */
		m_nudge = op + 2;
		D3("m_nudge=\"%s\"",m_nudge);
		break;
	case 'P':
	case 'p':       /* Port name */
		device = op + 2;
		D3("device=\"%s\"",device);
		break;
	case 'R':
	case 'r':       /* Raw I/O [default=TRUE] */
		n = sscanf(op+2,"%d",&raw);
		if (n < 1) raw = 1;
		D3("raw=%d\n",raw);
		break;
	case 'S':
	case 's':       /* Slow output, sleep(slow) between buffers */
		n = sscanf(op+2,"%d",&slow);
		if (n < 1) slow = 1;
		D3("slow=%d\n",slow);
		break;
	}
	return 0;
}
\!Funky\!Stuff\!
echo file: pread.c
cat > pread.c << '\!Funky\!Stuff\!'
#include "uutty.h"
/*
** Read a response from the debugger into a buffer.  Note the three
** stop conditions: finding an EOL char; filling the buffer; timeout.
*/
pread(stopch,bp,bs)
	int  stopch;
	char*bp;
	int  bs;
{	int  c, n;
	uint reads;

	D4("pread(stopch=%02X,bp=%06lX,bs=%d)",stopch,bp,bs);
	n = reads = 0;
	while (n < bs) {
		c = nextbyte();		/* Next char from port */
		if (c <= 0) {		/* No data returned? */
			if (debug >= 4)
				if (reads) P("pread: %d reads",reads);
			if (++reads > l_reads) {
				errno = ETIMEOUT;	/* Timeout if too many */
				D3("Input timeout...");
				Fail;			/* Timeout; return what we have */
			}
			continue;
		}
		D9("pread: c=%02X='%c' bp=%06lX bs=%d",c,dsp(c),bp,bs);
		c &= iomask;		/* Trim it to 7 bits */
		if (c == 0) continue;	/* Ignore nulls */
		*bp++ = c;			/* Note the EOL char is returned */
		++n;			/* Count the input chars */
		--bs;			/* Decr count of bytes wanted */
		if (c == stopch) Done;	/* Assorted EOL chars */
		switch (c) {
			case 0x03:		/* ^C = ETX */
				if (debug) P("%s: ^C in input, quitting [id]",getime());
				die(0);
			case '>' :
			case '\r':
			case '\n':
			case ':' :
			case '%' :
			case 0x04:		/* ^D = EOT  */
			case 0x06:		/* ^D = ACK  */
			case 0x15:		/* ^U = NAK */
				Done;
		}
		reads = 0;			/* Got data; reset timeout counter */
	}
done:
	*bp = 0;			/* Final null for debugging */
fail:
	return n;
}
\!Funky\!Stuff\!
echo file: pswd.c
cat > pswd.c << '\!Funky\!Stuff\!'
#include "uutty.h"
/* 
** We have received something that is believed to be a password.
** It is this routine's job to combine it with the current userid,
** and determine whether the combination is acceptable.  This
** routine should work on most Unix systems, but who knows?
*/
pswd(rp)
	char *rp;
{	char *p, *q;
	int   i;
	struct passwd *pp;

	D4("pswd: r=\"%s\" ss=%d",rp,ss);
	for (p=rp; *p; ++p) {	/* Examine the chars for acceptability */
		switch(*p) {
		case ':':		/* Colons aren't legal */
			D3("Invalid char '%c' in password",*p);
			Fail;
		case '\r':
		case '\n':
			*p = 0;
			goto gotit;
		case '!':		/* Special goodie for killing daemon */
			if (p[1] == 'Q') {
				if (debug) P("%s: !Q in input, quitting [id]",getime());
				die(0);
			}
		default:
			continue;
		}
	}
gotit:			/* Make a copy of the supposed id */
	p = rp;
	q = passwd;
	while (*p && q<passwd+PASSWD)
		*q++ = *p++;
	*q = 0;
	if (debug >= 3) P("%s PASSWD=\"%s\"",getime(),passwd);
	D3("userid:\"%s\"",userid);
	D3("pswd:\"%s\"",passwd);
	pp = getpwnam(userid);
	if (pp == 0) {
		D1("Login \"%s\" incorrect.",userid);
		D3("userid \"%s\" not found.",userid);
		Fail;
	}
	if (debug >= 6) Hexdnm(pp,sizeof(*pp),"Passwd:");
	D4("pw_name  =\"%s\"",pp->pw_name);
	D4("pw_passwd=\"%s\"",pp->pw_passwd);
	D4("pw_dir   =\"%s\"",pp->pw_dir);
	D4("pw_shell =\"%s\"",pp->pw_shell);
	D5("pswd:before crypt(\"%s\",\"%s\")",passwd,pp->pw_passwd);
	p = crypt(passwd,pp->pw_passwd);
	D5("pswd: after crypt(\"%s\",\"%s\")=\"%s\"",passwd,pp->pw_passwd,p);
	if (strcmp(p,pp->pw_passwd)) {
		Pwrite("Login incorrect.\r\n");
		D3("pswd \"%s\" not correct.",passwd);
		Fail;
	}
	D3("Login: uid=%d=\"%s\" group=%d accepted.",pp->pw_uid,pp->pw_name,pp->pw_gid);
/*
** To do the next few changes, we probably need to be a super-user:
*/
#ifdef SYS5
/*
** Attempt to build a utmp structure.
*/
	errno = 0;
	up = 0;
	D5("before ttyslot()");
	i = ttyslot();	/* Identify our /etc/utmp line */
	D4("ttyslot()=%d\t[errno=%d]",i,errno);
	findutmp();
	p = 0;
	fillutmp(pp->pw_name,p,devfld,USER_PROCESS);
	D4("pswd:before pututline(%06lX)",up);
	pututline(up);
#endif
	errno = 0;
	i = chmod(device,0644);			/* Restrict terminal access to owner */
	D4("chmod(\"%s\",0%o)=%d\t[errno=%d]",device,0644,i,errno);
	D3("Change \"%s\" to user %d=%s, group %d, permissions 644.",device,pp->pw_uid,pp->pw_name,pp->pw_gid);
	i = chown(device,pp->pw_uid,pp->pw_gid);	/* Change terminal's group */
	D4("chown(\"%s\",%d,%d)=%d",device,pp->pw_uid,pp->pw_gid,i);
	D3("New group %d.",pp->pw_gid);
	i = setgid(pp->pw_gid);			/* Change terminal's owner */
	D4("setgid(%d)=%d",pp->pw_gid,i);
	if (i < 0) Fail; 
	D3("New user  %d.",pp->pw_uid);
	i = setuid(pp->pw_uid);			/* Change to login id */
	D4("setuid(%d)=%d",pp->pw_uid,i);
	if (i < 0) Fail;
	D3("New directory \"%s\"",pp->pw_dir);
	i = chdir(pp->pw_dir);			/* Move to login directory */
	D4("chdir(\"%s\")=%d",pp->pw_dir,i);
	if (i < 0) Fail;
/*
** Invoke the login shell.
*/
	D5("pswd:before exec(1,\"%s\",%lX)",pp->pw_shell,pp);
	exec(1,pp->pw_shell,pp);		/* Start up a shell */
	D5("pswd: after exec(1,\"%s\",%lX)",pp->pw_shell,pp);
	target = "?";				/* We shouldn't get here */
fail:
	D4("pswd(\"%s\") FAILED.",rp);
	if (echofl ) Awrite("\r\nLogin incorrect.");
	if (m_login) {
		Awrite(m_login);
		ss = S_LOGIN;				/* Note login prompt sent */
		D4("State %d=%s",ss,gestate());
	}
	sleep(1);
	return 0;
}
\!Funky\!Stuff\!
echo file: pwrite.c
cat > pwrite.c << '\!Funky\!Stuff\!'
#include "uutty.h"
/* 
** Write a character string to the port, stopping at the first null.
** This will be done slowly iff slowfl is turned on.
*/
pwrite(msg)
	char*msg;
{	int  i, n;
	char c, *p;

	D5("pwrite(%08lX)",msg);
	n = strlen(msg);
	if (debug) {
		dbgtimep = getime();
		if (debug >= 2) Ascdnm(msg,n,"Send:");
		if (debug >= 4) Hexdnm(msg,n,"Send:");
	}
	if (slowfl) {
		D8("port_wr:slow=%d",slow);
		p = msg;
		while (c = *p++) {
			Slowly;
			D9("port_wr:before write(%d,%06lX,%d)",dev,&c,1);
			i = write(dev,&c,1);
			D9("port_wr: after write(%d,%06lX,%d)=%d",dev,&c,1,i);
			if (i <= 0) {
				if (debug) P("%s: write failed, quitting.",getime());
				die(2);
			}
		}
	} else {
		D9("port_wr:before write(%d,\"%s\",%d)",dev,msg,n);
		i = write(dev,msg,n);
		D9("port_wr: after write()=%d\t[errno=%d]",i,errno);
	}
}
\!Funky\!Stuff\!
echo file: restdev.c
cat > restdev.c << '\!Funky\!Stuff\!'
#include "uutty.h"
/*
** Set the device to our desired (raw) state.  This may only 
** work if we are the super-user.  This routine, such as it is, 
** should work on just about any Unix system.  See makeraw.c
** for the real system-dependent stuff.
*/
restdev()
{	int i;

	D6("restdev()");
	makeraw(dev);			/* We want to do raw I/O */
	errno = 0;
	D2("Change \"%s\" to user %d, group %d, permissions 666.",device,euid,egid);
	i = chown(device,euid,egid);	/* Try to get ownership */
	D4("restdev: chown(\"%s\",%d,%d)=%d",device,euid,egid,i);
	errno = 0;
	i = chmod(device,0666);		/* Make it publicly accessible */
	D4("restdev: chmod(\"%s\",0%o)=%d\t[errno=%d]",device,0666,i,errno);
	return i;
}
\!Funky\!Stuff\!
echo file: resync.c
cat > resync.c << '\!Funky\!Stuff\!'
#include "uutty.h"
/* 
** Discard buffered input, and try to get us to a start where
** the next input will be a response to the most recent (or next)
** output.
*/
resync()
{
	if (ibfa < ibfz) {
		dbgtimep = getime();
		if (debug >= 2) Ascdnm(ibfa,ibfz-ibfa,"Drop:");
		if (debug >= 4) Hexdnm(ibfa,ibfz-ibfa,"Drop:");
		restdev();		/* Make sure the device is OK */
		sleep(5);		/* Try not to respond to garbage */
	}
	ibfa = ibfz+1;
}
\!Funky\!Stuff\!
echo file: sendbrk.c
cat > sendbrk.c << '\!Funky\!Stuff\!'
#include "uutty.h"
/* 
** If fd is a terminal-type device, and ioctl(-,TCSBRK,0) works,
** this will send a break signal down the line, which may (or may
** not) get the attention of whatever is at the other end.
*/
sendbrk(fd)
{	int i;

	D4("BREAK");
	errno = 0;
	D5("sendbrk:before ioctl(fd=%d,TCSBRK=%d,0)",fd,TCSBRK);
	i = ioctl(fd,TCSBRK,0);
	D5("sendbrk: after ioctl(fd=%d,TCSBRK=%d,0)=%d	[errno=%d]",fd,TCSBRK,i,errno);
	if (i<0 || errno)
		D2("ioctl(%d,TCSBRK=%d,0)=%d\t[errno=%d]",fd,TCSBRK,i,errno);
	return i;
}
\!Funky\!Stuff\!
echo file: shprompt.c
cat > shprompt.c << '\!Funky\!Stuff\!'
#include "uutty.h"
/* 
** We seem to have gotten a shell prompt, but it's kinda hard to be sure.
** This routine is somewhat of a relic in uutty, but has been left here
** because you might want to deal with this case.  What we try to do
** basically is to get the shell to logout.
*/
shprompt(rp) char *rp;
{
	D5("shprompt(\"%s\")",rp);
	target = "shell";
	switch(ss) {
	case S_INIT:
	case S_PASSWD:
	default:
		E("****Can't handle shell prompt \"%s\" in state %d=%s.",rp,ss,gestate());
		Awrite("exit\r");		/* This terminates most shells */
		Awrite("logout\r");		/* This terminates other shells */
		Awrite("\4\3");			/* This works with still others */
		if (m_exit) Awrite(m_exit);	/* Other optional exit message */
		if (m_init) Awrite(m_init);	/* Try to tell the modem to quit */
		ss = S_IDLE;
		D4("State %d=%s",ss,gestate());
	}
}
\!Funky\!Stuff\!
echo file: sig.c
cat > sig.c << '\!Funky\!Stuff\!'
#include "uutty.h"
#include <signal.h>

sig_1() {P("Signal  1 [SIGHUP]" ); fflush(stdout); die( 1);}
sig_2() {P("Signal  2 [SIGINT]" ); fflush(stdout); die( 2);}
sig_3() {P("Signal  3 [SIGQUIT]"); fflush(stdout); die( 3);}
sig_4() {P("Signal  4 [SIGILL]" ); fflush(stdout); die( 4);}
sig_5() {P("Signal  5 [SIGTRAP]"); fflush(stdout); die( 5);}
sig_6() {P("Signal  6 [SIGIOT]" ); fflush(stdout); die( 6);}
sig_7() {P("Signal  7 [SIGEMT]" ); fflush(stdout); die( 7);}
sig_8() {P("Signal  8 [SIGFPE]" ); fflush(stdout); die( 8);}
sig_9() {P("Signal  9 [SIGKILL]"); fflush(stdout); die( 9);}
sig10() {P("Signal 10 [SIGBUS]" ); fflush(stdout); die(10);}
sig11() {P("Signal 11 [SIGSEGV]"); fflush(stdout); die(11);}
sig12() {P("Signal 12 [SIGSYS]" ); fflush(stdout); die(12);}
sig13() {P("Signal 13 [SIGPIPE]"); fflush(stdout); die(13);}
sig14() {P("Signal 14 [SIGALRM]"); fflush(stdout); die(14);}
sig15() {P("Signal 15 [SIGTERM]"); fflush(stdout); die(15);}
sig16() {P("Signal 16 [SIGADDR]"); fflush(stdout); die(16);}
sig17() {P("Signal 17 [SIGZERO]"); fflush(stdout); die(17);}
sig18() {P("Signal 18 [SIGCHK]" ); fflush(stdout); die(18);}
sig19() {P("Signal 19 [SIGOVER]"); fflush(stdout); die(19);}
sig20() {P("Signal 20 [SIGPRIV]"); fflush(stdout); die(20);}
sig21() {P("Signal 21 [SIGUSR1]"); fflush(stdout); die(21);}
sig22() {P("Signal 22 [SIGUSR2]"); fflush(stdout); die(22);}

/* Catch all the signals we can:
*/
sig() 
{
  signal( 1,sig_1);
  signal( 2,sig_2);
  signal( 3,sig_3);
  signal( 4,sig_4);
  signal( 5,sig_5);
  signal( 6,sig_6);
  signal( 7,sig_7);
  signal( 8,sig_8);
  signal( 9,sig_9);
  signal(10,sig10);
  signal(11,sig11);
  signal(11,sig11);
  signal(12,sig12);
  signal(13,sig13);
  signal(14,sig14);
  signal(15,sig15);
  signal(16,sig16);
  signal(17,sig17);
  signal(18,sig18);
  signal(19,sig19);
  signal(20,sig20);
  signal(21,sig21);
  signal(22,sig22);
}
\!Funky\!Stuff\!
echo file: slowly.c
cat > slowly.c << '\!Funky\!Stuff\!'
#include "uutty.h"
/* 
** This routine handles requests to do things slowly.
** Note the two ways of delaying:  sleeping for "slow"
** seconds, or counting down from "count" to 1.
*/
slowly()
{	uint u;

	D8("slowly() slow=%d count=%d",slow,count);
	if (slow > 0) {
		D8("slowly: slow=%d",slow);
		sleep(slow);
	}
	for (u=count; u; u--) ;
	D8("slowly() done");
}
\!Funky\!Stuff\!
echo file: st.c
cat > st.c << '\!Funky\!Stuff\!'
#include "dbg.h"
/* 
** Assorted string-manipulation subroutines.
*/
/* Match a null-terminated string against the initial portion
** of another string.  If the match succeeds, return its length;
** else return 0.
*/
int st_init(x,y)
	char *x;
	char *y;
{	char *s;
	char *t;

	s = x;
	t = y;
	while (*s)
		if (*s++ != *t++) goto fail;
	D9("st_init(\"%s\",\"%s\")=%d",x,y,s-x);
	return(s - x);
fail:
	D9("st_init(\"%s\",\"%s\")=%d",x,y,0);
	return 0;
}
/* Return the int value of the trailing digits of a null-terminated string.
** Note that non-numeric chars reset the value, so only digits that follow
** all non-numerics are used.  Also, only the last '-' is effective.  Thus
** "15-x-37" gives the value 37.
*/	
int st_ival(s)
	char *s;
{	int   c, val, sign;

	sign = val = 0;
	while (c = *s++)
		if ('0'<=c && c<='9')
			val = (val * 10) + (c - '0');
		else if (c == '-')
			sign = c;
		else 
			sign = val = 0;
	return(sign ? -val : val);
}
/* Convert a long to an ASCII string.
** Return pointer to byte just after the value.
*/
char * st_ltoa(l,a)
	long  l;
	char *a;	
{
	if (l < 0) {*a++ = '-'; l = -l;}
	if (l > 9) a = st_ltoa((l/10),a);
	*a++ = '0' + (l % 10);
	return a;
}
/* Return the long value of the trailing digits of a null-terminated string.
** Note that non-numeric chars reset the value, so only digits that follow
** all non-numerics are used.  Also, only the last '-' is effective.  Thus
** "15-x-37" gives the value 37.
*/	
long st_lval(s)
	char *s;
{	int   c, sign;
	long  val;

	val = sign = 0;
	D4("st_lval: sign=%d val=%ld",sign,val);
	while (c = *s++) {
		if ('0'<=c && c<='9')
			val = (val * 10) + (c - '0');
		else if (c == '-')
			sign = c;
		else 
			val = sign = 0;
		D4("st_lval: sign=%d val=%ld c=%02x='%c'",sign,val,c,dsp(c));
	}
	return(sign ? -val : val);
}	
\!Funky\!Stuff\!
echo file: talk.c
cat > talk.c << '\!Funky\!Stuff\!'
#include "uutty.h"
/* 
** This routine assumes that it is talking to a port connected
** to a VMEbus device with an on-board debugger.  It exchanges
** some pleasantries to verify this fact, then procedes to ask
** the debugger to show it the requested chunk of its memory.
*/
talk()
{	int  c, i, n, r;
	int  baddies, lowers, uppers;
	int  messages;

	D4("talk()");
	r = 0;
	ss = S_INIT;
	D4("State %d=%s",ss,gestate());
	lockwait();		/* Try to avoid collisions */
	if (device) target = device;
/*
** We have a long list of initialization strings here.
** This has come in handy in a few cases, but usually
** they are mostly null.
*/
	if (m_init )  Awrite(m_init);
	if (m_init1) {Awrite(m_init1); sleep(SLEEP1);}
	if (m_init2) {Awrite(m_init2); sleep(SLEEP2);}
	if (m_init3) {Awrite(m_init3); sleep(SLEEP3);}
	ss = S_INIT;
	D4("State %d=%s",ss,gestate());
	if (m_login)  Awrite(m_login);
	ss = S_LOGIN;
	D4("State %d=%s",ss,gestate());
	Response;
nudge:			/* Try to elicit a response */
	D3("Nudge.");
	if (++nudges > Nudges) {
		E("Too many nudges.");
		r = 1;
		Dead;
	}
	lockwait();		/* Try to avoid interference */
	Resync;
	if (m_nudge)		/* Is a nudge message defined? */
		Awrite(m_nudge);	/* If so, send it */
idle:					/* We will now accept anything */
	D3("Idle.");
	ss = S_IDLE;		/* Note we're awaiting a response */
	D4("State %d=%s",ss,gestate());
	messages = 0;		/* Counter to trigger nudges */
response:				/* Read a response */
	D4("Response: state=%d=%s",ss,gestate());
	if (debug > 0 && target != oldtarg) {
		if (debug >= 3) P("%s Talking to %s.",getime(),target);
		oldtarg = target;
	}
	/*
	** Check for any of a list of known debugger prompts.
	** If we don't get one of then, try to nudge the debugger,
	** and try again, giving up after some number of tries.
	*/
	errno = 0;
	D4("talk:l_tries=%d",l_tries);
	sleep(1);		/* Sheer laziness */
	lockwait();
	if (ss != S_PASSWD)	/* If not expecting password, do echoing if requested */
		echoing = echofl;
	while ((n = Pread(eol0,rsp,rspmax)) <= 0) {
		D3("No response.");
		/*
		** Note this program loops forever waiting for something to come down the line.
		** You might want to do something else here if there's no input.
		*/
	}
	D4("talk:Pread()=%d",n);
	rsp[n] = 0;
	++messages;
	if (debug) {
		dbgtimep = getime();
		if (debug >= 2) Ascdnm(rsp,n,"Got:");
		if (debug >= 4) Hexdnm(rsp,n,"Got:");
	}
	/*
	** Next, we do some preliminary checking for sanity of the input.
	** Since this program's duty is to do login interviews, the only
	** input we really want is something that might be a login id or
	** a password.  Another likely input is a nudge from a counterpart
	** on the other end, to which we respond with a prompt.
	*/
	if (n < 3) {		/* Just "\r", "\n", or "\r\n" is a nudge */
		D4("Short response, %d chars.",n);
		switch (c = rsp[0]) {
		case 0x03:		/* ^C = ETX (usually means "die") */
			if (debug) P("%s: ^C in input, quitting.",getime());
			die(c);
		case '\n':		/* ^J = LF */
		case '\r':		/* ^M = CR */
		case 0x04:		/* ^D = EOT */
		case 0x02:		/* ^B = STX */
		case 0x01:		/* ^A = SOH */
			ss = S_IDLE;		/* Flush garbage from buffer */
			D4("State %d=%s",ss,gestate());
			lockwait();
			if (ibfa < ibfz) {	/* Is there input waiting? */
				D3("Nudge plus garbage received, ignored.");
				Resync;			/* Flush garbage from buffer */
			} else {			/* No input waiting */
				D3("Nudged; send login prompt \"%s\"",m_login);
				Awrite(m_login);	/* Send them a login prompt */
				ss = S_LOGIN;		/* Note that we did it */
				D4("State %d=%s",ss,gestate());
				messages = 0;
			}
			Response;			/* See if there's a response */
		case 0x10:			/* This is a uucp Start-of-message */
			D3("We seem to be talking to a UUCP demon.");
			makeraw(dev);		/* Paranoia! */
			Awrite("OOOOOOOO\r");	/* Try to get it to stop */
			Resync;			/* Discard the rest of the message */
			if (m_init) Awrite(m_init);
			ss = S_INIT;		/* Try to get back to idle state */
			D4("State %d=%s",ss,gestate());
			Response;
		}
	}
	baddies = lowers = uppers = 0;/* These are for counting letters */
	D4("Scan id for char classes...");
	for (i=0; i<n; i++) {		/* Examine the response for nasty stuff */
		c = rsp[i];			/* One byte at a time */
		if (islower(c)) {
			++lowers;			/* Lower-case letters are desirable */
			D6("c=%02X-'%c' lowers=%d",c,dsp(c),lowers);
		} else
		if (isupper(c)) {
			++uppers;			/* Upper-case letters are acceptable */
			D6("c=%02X-'%c' uppers=%d",c,dsp(c),uppers);
		} else			/* Everything else is dubious */
		switch (c) {
		case 0x03:			/* Special goody to let others kill us */
		case 0x02:
		case 0x01:
			if (debug) P("%s: %02X in input, quitting [id]",getime(),c);
			die(c);
		case '#':			/* These are likely shell prompts */
		case '$':			/* Default Bourne shell prompt */
		case '%':			/* Default C- shell prompt */
		case '>':			/* Popular prompt in some circles */
			if (i >= n-2) {		/* Likely only if at end of input */
				shprompt(rsp);		/* Can we handle it? */
				Response;
			}				/* If earlier in string, reject it */
		case 0x00:
		case 0x04: case 0x05: case 0x06: case 0x07:
		case 0x08: case 0x09:            case 0x0B:
		case 0x0C:            case 0x0E: case 0x0F:
		case 0x10: case 0x11: case 0x12: case 0x13:
		case 0x14: case 0x15: case 0x16: case 0x17:
		case 0x18: case 0x19: case 0x1A: case 0x1B:
		case 0x1C: case 0x1D: case 0x1E: case 0x1F:
		case ' ' : case ':' :	/* None of these are acceptable in ids or passwords */
			D3("Invalid char %02x='%c' in input.",c,dsp(c));
			++baddies;
			break;
		}
	}
	if (baddies || ((lowers+uppers) == 0)) {	/* Is it acceptable? */
		D4("Unacceptable; baddies=%d lowers=%d uppers=%d.",baddies,lowers,uppers);
		Resync;			/* No; drop buffered input */
		lockwait();
		if (baddies && messages > THRESH) {
			makeraw(dev);		/* Others may have munged the driver */
			if (m_init) {
				Awrite(m_init);
				ss = S_INIT;
				D4("State %d=%s",ss,gestate());
			} else {
				Awrite(m_login);
				ss = S_LOGIN;
				D4("State %d=%s",ss,gestate());
			}
			messages = 0;
		}
		ss = S_IDLE;
		D4("State %d=%s",ss,gestate());
		Response;
	}
	switch (ss) {		/* Special actions depending on state */
	case S_EXIT:		/* We are trying to exit */
		if (m_exit) {	/* Is there an exit command? */
			Awrite(m_exit);	/* If so, send it */
			ss = S_EXIT;	/* Note again that we're trying to quit */
			D4("State %d=%s",ss,gestate());
		}
		Response;
	case S_IDLE:		/* We're waiting for the other end to act */
		D4("Got id while idle.");
		c = rsp[0];		/* First char of response is sometimes special */
		goto maybeid;
	case S_LOGIN:		/* We just sent a login prompt */
		D4("Got response to login prompt.");
	maybeid:		/* We may have a login id */
		D4("Got possible login id.");
		if (lowers == 0) {	/* No lower-case letters; it's not an id */
			lockwait();	/* Make sure we're not interfering */
			makeraw(dev);	/* Paranoia! */
			if (m_init) {	/* Do we have a special init string? */
				Awrite(m_init);
				ss = S_INIT;
				D4("State %d=%s",ss,gestate());
			}
			Response;		/* Go wait for next input */
		}
		i = checkid(rsp);	/* Put it through final tests */
		if (i <= 0) {	/* If it fails... */
			if (debug) {
				dbgtimep = getime();
				if (debug >= 2) Ascdnm(rsp,n,"Bad id:");
				if (debug >= 4) Hexdnm(rsp,n,"Bad id:");
			}
			Resync;		/* Get port into known state */
			sleep(5);		/* Extra delay for safety's sake */
			ss = S_IDLE;
			D4("State %d=%s",ss,gestate());
			Response;		/* Go wait for next input */
		}
		/* It sure looks like an id */
		D3("Send password prompt \"%s\"",m_passwd);
		echoing = 0;
		Awrite(m_passwd);
		ss = S_PASSWD;
		D4("State %d=%s",ss,gestate());
		Response;
	case S_PASSWD:		/* We just sent a password prompt */
		D4("Got response to password prompt.");
		i = pswd(rsp);	/* Check it out to see if it's a good guy */
		if (i <= 0) {	/* Successful call won't return */
			D3("Unacceptable password \"%s\"",rsp);
			Response;
		}
		E("pswd(\"%s\")=%d Shouldn't happen.",rsp,i);
		Response;
	default:
		D4("ss=%d not special",ss);
	}
	if (debug) {
		dbgtimep = getime();
		if (debug >= 2) Ascdnm(rsp,n,"Ignore:");
		if (debug >= 4) Hexdnm(rsp,n,"Ignore:");
	}
	Response;
dead:  P("Giving up; %s seems to be dead.",target);
	return r;
}
\!Funky\!Stuff\!
echo file: unlock.c
cat > unlock.c << '\!Funky\!Stuff\!'
#include "uutty.h"
/*
** Remove the lockfile.
*/
unlock()
{
	D2("%s: Delete lockfile %d=\"%s\".",getime(),lockfn,lockfile);
	unlink(lockfile);
	D4("Close lockfn=%d.",lockfn);
	close(lockfn);
	lockfn = -1;
	locked = 0;
}
\!Funky\!Stuff\!



More information about the Mod.sources mailing list