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