v08i072: Bidirectional getty/login for SystemV, Part01/02

sources-request at mirror.UUCP sources-request at mirror.UUCP
Fri Feb 20 08:44:22 AEST 1987


Submitted by: cdx39!jc at EDDIE.MIT.EDU (John Chambers)
Mod.sources: Volume 8, Issue 72
Archive-name: uutty/Part01

[  I have not tried this.  --r$  ]

Hello.  Enclosed is a program which I've been using for some
time as a replacement for getty; I call it "uutty" as a hint
that it cooperates with uucp/uux/mail/cu/etc.  Several friends
have suggested I broadcast it, so here it is....

Uutty's primary function is to make it easy to use a port in
both directions with little grief.  On a port with an ACU-type
modem, it allows both outgoing and incoming calls without any
need to fiddle with inittab.  On a direct link, it allows the
use of commands like uucp or cu in either direction at any
time. 

Uutty's secondary function is to try to recognize input from 
overly-intelligent modems or other login daemons, and avoid
getting into a cycle-eating conversation with them.

There is also a tertiary function:  optionally producing an
audit (debug) trail of traffic on the port at times when no
program (such as uucico or cu) is using it.  This is mostly
useful when you have a talkative modem or LAN connection.

This version should be classified as a "beta test" version;
it has been tested on only a few varieties of Unix, and it
will probably have to be modified for others.  The two parts
that may not be very portable are the code to put a port into
raw mode (makeraw.c), and the code to log in a user (*.utmp.c).

Another major reason for wanting the source code close at 
hand is that you will likely have to twiddle with the code 
that handles talkative modems, in order to respond correctly
to your modems' own variety of bizarreness.  An especially
common problem is being overly sensitive to speed.  Many
modems won't accept commands at the full line speed (1200
baud or whatever); they assume that commands come from a
person typing at a keyboard, and lose characters when it
comes from a program in a burst.  This program writes the
"init" strings byte-at-a-time, which may be slow enough,
but you may have to slow these writes down even more to
make the modem understand.  
: This is a shar archive. Extract with sh, not csh
echo file: README
cat > README << '\!Funky\!Stuff\!'
	The uutty package is John Chambers' very own person login
	daemon, to replace such things as getty(1) and login(1).
	
	There are several motives for doing this.  You might have
	a modem which you wish to use for both incoming and outgoing
	calls.  You might have a direct ("null modem") link which
	you would like to use from either end.  You might have an
	overly-intelligent modem which is crashing your system by
	engaging getty in a conversation.  All of these can be
	handled by uutty.  
	
	Also, uutty will make an audit trail for you in the file
	of your choice.  Note:  at debug level -d2 and above,
	uutty is a "Trojan horse", since the audit trail will
	contain unencrypted login ids and passwords.  This is
	very useful on occasion; it is also an extreme security
	risk.  Think carefully before you try this on a regular
	basis.
	
	To create the program, first edit the *.h files and the
	Makefile.  There isn't much that needs to be changed,
	but you probably want to modify the -DSYS5 and -DCadmus
	parameters to specify your variety of Unix and your 
	manufacturer's name.  You might also try:
		grep SYS5 *.c
		grep Cadmus *.c
	and check out all these pieces of code to see if they
	look correct for your system.
	
	If you make any interesting changes to port it to your
	system, you might share them with the author:
		...!cdx39!jc (John Chambers)
	This will help towards producing a truly portable version
	of this logger.
	
	When you've convinced yourself that the system dependencies
	are OK, just type "make", and uutty will be compiled.  To
	install it and the manual entry , type "install"; you'll 
	have to be a super-user to do this.
	
	To run it, you need to explain to init about it.  Here 
	are some typical entries from our /etc/inittab file:

t11:23 :respawn:modem /dev/tty11 -B12				# Modem for uucp.
t30:234:respawn:uutty /dev/tty30 -B96 -e 2>>/user/aud/tty30	# Ordinary terminal port.
t40:34:off    : uutty /dev/tty40 -B96 2>>/user/aud/tty40 -l -d2	# Direct link.
t41:34:respawn: uutty /dev/tty41 -B96 2>>/user/aud/tty41 -l -d2	# Direct link.
t42:34:respawn: uutty /dev/tty42 -B96 2>>/user/aud/tty42 -l -d2	# Direct link.
t43:34:respawn: uutty /dev/tty43 -B96 2>>/user/aud/tty43 -l -d2	# Direct link.

	What we are doing here is using uutty on four inter-machine
	direct links (null-modem or cross-over cables), on the
	tty4? ports.  The tty30 port is attached to a terminal
	(well, actually to our LAN); uutty is being run there
	just to show that it works with the -e option.  The tty11 
	port is attached to an overly-intelligent ACU-type modem.  
	The 'modem' program is a shell script, which looks like:
	
>	:
>	MODEM=tty11
>	/usr/lib/uucp/modem_init $1
>	cd /etc
>	PATH=.:$PATH
>	exec uutty /dev/$MODEM -B12 -L -d2 -i\\rqOG0\\rXXXT\\rqOG0 2>>/user/aud/$MODEM 
>	:
>	: We normally won't reach this:
>	sleep 30
>	/usr/lib/uucp/modem_init $1
	
	The purpose of this is to run a special script, modem_init,
	to beat on the modem and try to put it into a quiescent state.
	When this is done, we run uutty at 1200 baud, with locking
	enabled, at debug level 2 (tracing of all input and output),
	and with a bizarre-looking initialization string which turns
	out to be what this particular modem requires to force it to
	hang up and re-initialize itself.  The final sleep and call
	on modem_init are to handle cases where init somehow can't
	find uutty, or it dies instantaneously.  This may look like
	overkill; it turns out to have been useful on occasion.
	
	Note that the audit trail grows without bound.  Once a day,
	we handle it by having cron start up /etc/cleanup, a script
	that moves a select list of files to a backup copy.  This
	generally suffices to keep the audit trail to a reasonable
	size.
	
	When you first run uutty, you should probably try it with
	-d5 or -d8, to have it explain what it's doing.  It will
	likely get into a conversation with your modem; you'll
	have to figure out if there's a possible initialization
	string that will prevent it.  Hint:  Try to tell your
	modem not to echo anything.
	
	A more useful first test is to run uutty on one end of
	a null-modem cable, which could be to another computer,
	or to two ports on the same computer.  Use cu(1) to
	connect to it, and have either getty or uutty running
	on the other end.  After at most two RETURNs, you should
	get a login prompt, and uutty's audit trail should show
	that there is a lockfile.  When you tell cu to disconnect,
	the lockfile will go away, and uutty will wake up in
	half a minute or so.  If you are still logged in at 
	the other end, uutty may discover this, and log you
	out; on the other hand, it may not.  (It's supposed
	to, if you have a Unix prompt ending with '$' or '%'.)
	
	Enjoy!
	
	John M Chambers 
Phone: 617/364-2000x7304
Email: ...{cthulhu,inmet,harvax,mit-eddie,mot[bos],rclex}!cdx39!{jc,news,root,usenet,uucp}
Smail: Codex Corporation; Mailstop C1-30; 20 Cabot Blvd; Mansfield MA 02048-1193
Telex: 922-443 CODEX A MNSF
\!Funky\!Stuff\!
echo file: Makefile
cat > Makefile << '\!Funky\!Stuff\!'
#
# Makefile for uutty login daemon.  To build uutty, just type:
#	make
# To install uutty and the manual page, type:
#	make install
# It's easiest to test if you create an inittab entry like:
#	t43:234:respawn:uutty /dev/tty43 -B96 -l -d8 2>>/user/aud/tty43 
# where /dev/tty43 is a null-modem link to a port on the same or
# another Unix machine.  It should be tested in two directions.
# First, run it with no getty on the other end, and use cu from
# the other end to call up uutty.  After 1 or 2 CR or LF characters,
# it should log you in.  Next, start up getty or uutty on the other
# end, and use cu to connect from this end.  After CR or LF, uutty
# should get out of the way and let the other end log you in.  
#
CFL=	-DSYS5 -DCadmus -I.
CC=	cc $(CFL) -c
D=	uutty.8
H=	*.h 
L=	cc $(CFL) -o
O=	args.o awrite.o checkid.o copy.o data.o dbg.o die.o exec.o \
	fillutmp.o findutmp.o gestate.o getime.o help.o lastfield.o \
	lockname.o lockup.o lockwait.o main.o makeraw.o makesane.o \
	nextbyte.o opendev.o option.o pread.o pswd.o pwrite.o \
	restdev.o resync.o sendbrk.o shprompt.o sig.o slowly.o st.o \
	talk.o unlock.o  
S1=	args.c awrite.c checkid.c copy.c data.c dbg.c die.c \
	exec.c fillutmp.c findutmp.c gestate.c getime.c help.c 
S2=	lastfield.c lockname.c lockup.c lockwait.c \
	main.c makeraw.c makesane.c nextbyte.c opendev.c option.c pread.c \
	pswd.c pwrite.c restdev.c resync.c sendbrk.c shprompt.c \
	sig.c slowly.c st.c talk.c unlock.c  
S=	$(S1) $(S2)
U1=	README Makefile install uutty.8 uutty.h dbg.h $(S1)
U2=	$(S2)
#
# How to compile most of the modules:
# 
.c.o:	$*.c $H;	$(CC) $*.c
#
progs:		uutty
lints:		uutty.L
uutty:		$O;		$L uutty $O
#
# Libraries:
#
install:	/etc/uutty /usr/man/man8/uutty.8
/etc/uutty:	uutty;		Cp uutty /etc/uutty
/usr/man/man8/uutty.8: uutty.8;	Cp uutty.8 /usr/man/man8/uutty.8
#
# Some modules that need special treatment:
#
dbg.o:		dbg.c dbg.h;		$(CC) dbg.c
makeraw.o:	makeraw.c dbg.h;	$(CC) makeraw.c
makesane.o:	makesane.c dbg.h;	$(CC) makesane.c
st.o:		st.c;			cc -c st.c
#
# Run lint on the whole mess:
#
uutty.L:	$H $S;	lint $(CFL) $S >uutty.L
#
# Build the distribution files:
#
uutty.dist:	uutty.dist1 uutty.dist2
uutty.dist1:	uutty.cover1 uutty.shar1;	cat uutty.cover1 uutty.shar1 >uutty.dist1
uutty.dist2:	uutty.cover2 uutty.shar2;	cat uutty.cover2 uutty.shar2 >uutty.dist2
#
# Build the shell archives:
#
uutty.shar1:	$(U1);	shar $(U1) >uutty.shar1
uutty.shar2:	$(U2);	shar $(U2) >uutty.shar2
#
\!Funky\!Stuff\!
echo file: install
cat > install << '\!Funky\!Stuff\!'
: Script to install uutty.
:
echo You must be a super-user to do this...
make install
\!Funky\!Stuff\!
echo file: uutty.8
cat > uutty.8 << '\!Funky\!Stuff\!'
.\" pltroff -manl $1
.TH UUTTY 8
.VE 5
.SH NAME
uutty  \-  two-ended login protocol with intelligent modem handling and uucp lockfiles
.SH SYNOPSIS
.B /etc/uutty
device
[
.B \-b<speed>
] [
.B \-d<level>
] [
.B \-e
] [
.B \-f
] [
.B \-l
] [
.B \-i
initializer
] [
2>>audit_file
]
.br
file
.SH DESCRIPTION
.fi
.B Uutty
is a program that is invoked by
.IR init (8).
It is similar in function to getty(8) and login(1),
but has some extra intelligence so that it may be run
with a single modem used for both incoming and outgoing calls,
or on both ends of a direct port-to-port link.
.PP
With
.B uutty
running on both ends of a line,
terminal emulators like cu(1) may be used
in either direction.
Also, file transfer utilities like uucp(1)
may be initiated from either end.
.PP
An important feature of
.B uutty
is that is recognizes the lockfiles created by uucico(8),
cu(1), uux(1), and other programs.
On most Unix(TM) systems,
these are named "/usr/spool/uucp/LCK..<device>",
where <device> is the last field of the device name.
.B Uutty
checks for the presence of such lockfiles before every read,
and sleeps until they disappear.
.PP
Thus
.B uutty
may be left running on a port while other programs use it.
When a lockfile is created by another program
(such as uucico or cu),
.B uutty
will read at most one more chunk of input
before noticing the lockfile.
It will then "back off" until the other program
signals that it is done with the device by
unlinking the lockfile.
.PP
.B Uutty
is normally run as a background daemon by init(1M).
The entry in /etc/inittab typically looks like:
.nf
.in +5
t42: 34:respawn: uutty /dev/tty42 -B96 -L 2>>somewhere
.in -5
.fi
where
.I somewhere
is an audit file or /dev/null.
.PP
Initially
.B uutty
writes the initialization string, if one is given,
then writes a "login:" prompt to the line.
It then reads the response,
and attempts to determine whether it is an
acceptable login attempt,
or whether it is from another login process
(like itself or getty or login).
In the former case, a password prompt is
written to the device;
in the latter case, the input is ignored.
.PP
An important difference between
.B uutty
and
.B getty
is that
.B uutty
echoes nothing back to the input device.
This avoids uncontrolled feedback
when programs are run on both ends of a line.
However, with the
.B \-e
option, echoing is done.
.PP
.B Uutty
does not invoke the
.IR login (1)
command;
rather it does the entire login interview itself.
.PP
.I Device
is the name of a serial port
(normally in \f3/dev\fP) to which
.B uutty
is to attach itself.
.B Uutty
uses this string as the filename
to open for reading and writing;
it should be the full pathname of the device.
.PP
The optional argument,
.B \-B
.I <speed>,
is a numeric line speed.
Arbitrary speeds may not be recognized;
typically -B300, -B1200, -B2400, -B4800, and -B9600
are the only ones that work correctly.
The default speed is -B9600.
The final
.I 00
may be omitted.
.PP
.B Uutty
sends a login prompt whenever it receives a single CR of LF character as input.
It then expects a login id,
which may be terminated by a new-line or carriage-return character.
Either will cause
.B uutty
to examine the preceding characters for acceptability.
The rules are:
Only printable ASCII characters are accepted,
and of those, most punctuation is rejected.
In particular, any input containing a colon (:)
is assumed to be a prompt from another login process,
and causes
.B uutty
to become very uncommunicative.
White space (blanks and tabs) are unacceptable,
as are any ASCII control characters
(those below hex 20).
.PP
Note that the initial CR or LF must be 'alone',
ining that they must not be followed too soon by
any other characters.
If
.B uutty
finds junk in the input buffer after a CR or LF,
the entire chunk of input is discarded.
This is done to try to avoid getting into
discussions with intelligent modems.
.PP
When acceptable input is received,
a password prompt is sent,
and the response (if any) is subjected to the same
tests as for the login id.
.PP
If the login id and password agree with an entry in the
password file /etc/passwd,
the user's shell is initiated via execv(2),
in the same process in which
.B uutty
was running.
.PP
If a ^C character (numeric 3) is received,
.B uutty
terminates.
Also, this will usually result in the device not being open by any process,
resulting in a "hangup" operation being performed by
the device driver.
See termio(8) for details.
Normally, init(1M) will soon restart the process,
and a new initialization string and login prompt will
be sent to the device.
.PP
The
.B \-i
option is used to supply an initialization string
to be sent when uutty determines that a connection
exists to another system which should be terminated.
The rest of the command-line argument should be the
string, in which several types of notation may be
used to produce non-printing characters.
A complicated example is:
.in +5
.B "-i'\\\\33^T[j%7Fxx\\\\r'"
.in -5
The quotes are to prevent the shell from interpreting anything.
The
.B "\\\\33"
is an octal value for an ASCII ESC character.
The
.B "^T"
is a CTRL-T character, i.e., octal 24 or hex 14, ASCII DC4.
The
.B j
represents itself.
The
.B "%7F"
is a hex value for a DEL character.
The
.B xx
represent themselves.
The final
.B "\\\\r"
is a carriage-return character,
as in the C language conventions.
.PP
A debug option is provided.  When
.B getty
is invoked with the
.B \-d<n>
option, various debugging messages are produced,
depending on the value of the digit <n>.
Such messages are always written to the "standard error" file.
The default is
.B \-d1 ,
which produces output giving the times at which
.B uutty
is started, and when it initiates a shell for some user.
The
.B \-d0
level suppresses all output except fatal error messages.
Higher numbers produce successively more output.
The
.B \-d4
level will generally give sufficient information to
explain just what messages are seen by
.B uutty
and why logins are succeeding or failing.
.PP
If user logins are desired on a port used by uucp(1),
it is a good idea to create lockfiles.
Otherwise, a uucp demon (uucico) may attempt to use the port,
and sheer insanity results.
The
.B \-l
option causes a uucp lockfile to be created for the duration of a login,
preventing uucp from attempting to use the port.
On logout, the lockfile will be removed.
Note:
This implies that the shell is run as a subprocess to uutty,
so ps(1) will show both processes running.
Don't kill the uutty process;
doing so will block further accesses to the port by
uutty or uucp,
until you delete the lockfile by hand.
.PP
If for some reason you wish to start shells in subprocesses,
this may be done via the
.B \-f
option, which stands for "fork".
The
.B \-l
option implies the
.B \-f
option.
.SH FILES
/etc/passwd
/etc/utmp
/usr/spool/uucp/LCK..<device> .
Note: /etc/gettydefs is not referenced by
.I uutty .
.SH "SEE ALSO"
cu(1),
getty(8),
init(8),
inittab(5),
ioctl(2),
login(1),
tty(7),
uucico(8),
uucp(1),
uux(1)
.SH "BUGS"
Programs that don't know how to create uucp(1) lockfiles shouldn't
be run on a port controlled by
.B uutty .
If you must, try surrounding them with a script that creates the
lockfile.
.PP
The
.B \-e
option entails a high risk of feedback saturating either or both
of the computers involved.
.PP
If a
.B CR
or
.B LF
is followed too quickly by further input,
it will be silently discarded.
This is frustrating to people that like to bang
on the RETURN key.
.PP
There are all sorts of truly demented modems on the market;
you may well have to twiddle with the code a bit to persuade
.B uutty
to ingore your modem's noise.
.PP
At debug levels -d2 and above,
.B uutty
is a Trojan Horse,
writing login ids and passwords in the clear to the audit trail.
\!Funky\!Stuff\!
echo file: uutty.h
cat > uutty.h << '\!Funky\!Stuff\!'
#ifndef uutty_h
#include "dbg.h"
/* 
** This is the header file for John Chambers' "Serial Port Daemon".
** Several programs use this header, including "uutty", so there 
** is likely to be stuff here not needed by a particular program.
** A single header is used to make them somewhat consistent.
*/
/*
** The following stuff may be specific to Unix SYS/V:
*/
#ifdef   SYS5
#include <pwd.h>
#include <termio.h>
#include <utmp.h>
#include <sys/errno.h>
#include <sys/stat.h>
  extern struct passwd*getpwnam();
  extern int           ttyslot(); 
  extern struct utmp  *getutent(); 
  extern struct utmp  *getutline(); 
  extern void          pututline(); 
  extern void          setutent(); 
  extern struct stat   status;		/* Child process's status */
  extern struct termio trminit;
  extern int           ttyslot(); 
  extern struct utmp  *up;
  extern struct utmp   utmp;
#endif
/*
** Unix library stuff:
*/
extern char **environ;	/* Environment vector */
extern char  *crypt();	/* Password encryption */
extern char  *ctime();	/* Date/time in ASCII */
/*
** State-type codes, see also the state[] array.
*/
#define S_INIT		0
#define S_IDLE		1
#define S_LOGIN		2
#define S_PASSWD	3
#define S_DL		11
#define S_RX		12
#define S_TC		13
#define S_BV_E		14
#define S_HSUQ		15
#define S_HSU		16
#define S_READS		17
#define S_GO		18
#define S_TRANS		19
#define S_CTRLA		20
#define S_CTRLB		21
#define S_CTRLC		22
#define S_CTRLD		22
#define S_CONNECT	33
#define S_ID		34
#define S_SREC		36
#define S_SR		37
#define S_EXIT		38
#define S_LOAD		39
/*
** Array sizes.
*/
#define BUF    80		/* Size of scratch buffer */
#define IBUF 1000		/* Size of input buffer */
#define OBUF   80		/* Size of output buffer */
#define RSP  1000		/* Size of response buffer */
#define SREC   80		/* Size of S-record buffer */
/*
** Assorted pseudo-functions.
*/
#define ASCII(c) ((c)&0x7F)
#define Awrite(m) awrite(m)
#define CTRL(c)  ((c)&0x1F)
#define Dmp(a,s) dmp((long)(a),(long)(s))
#define Pread(c,p,s) pread((int)(c),(char*)(p),(int)(s))
#define Pwrite(p)    pwrite((char*)(p))
#define Resync       resync()
#define Slowly	     if (slowfl) slowly()
/*
** These might be in some standard libraries:
*/
#define islower(c) ('a'<=(c)&&(c)<='z')
#define isupper(c) ('A'<=(c)&&(c)<='Z')
#define htob(c) ((('0'<=c)&&(c<='9'))?(c-'0'):\
				 (('A'<=c)&&(c<='F'))?(c-'A'+10):\
				 (('a'<=c)&&(c<='f'))?(c-'a'+10):-1)
/*
** Limit type codes:
*/
#define L_READS		10
#define L_ES		2
#define L_TC		3
#define L_DL		4
#define L_LOAD		4
#define L_RX		5
#define L_START		6
#define L_TRIES     	10
/*
** Misc constants:
*/
#define READS      3	/* Read attempts before failure */
#define NUDGES     5	/* Nudge attempts before failure */
#define EGARBAGE 101
#define ETIMEOUT 102
#define THRESH     3	/* Number of failures that elicits prompt */
/*
** Sleep times for various routines.
*/
#define SLEEP1	1
#define SLEEP2	1
#define SLEEP3	1
#define SLEEPR	1
#define SLEEPF	10
#define SLEEPL	10
/*
** Pseudo-commands.
*/
#define Dead		goto dead
#define Response	goto response
#define Nudge		goto nudge
/*
** Pseudo-types.
*/
#define uint unsigned int
/*
** Assorted messages.
*/
extern char m_CTRLA [];
extern char*m_exit;
extern char m_login [];
extern char m_passwd[];
extern char*m_init;
extern char*m_init1;
extern char*m_init2;
extern char*m_init3;
extern char*m_nudge;
/*
** Assorted global function typess.
*/
extern char        * ctime();
extern char        * gestate();
extern char        * getime();
extern char        * lastfield();
extern struct utmp * findutmp();
/*
** Assorted global variables.
*/
extern uint  count;		/* Delay between "typed" chars */
extern char *ctime();		/* Time in ASCII format */
extern long  currtime;		/* Timestamp */
extern char *ctimep;		/* Current ctime() value */
extern char *device;		/* Name of serial port */
extern int   dev;		/* Number of serial port file */
extern char *devfld;		/* Last field of device name */
extern Flag  echofl;		/* True if input is to be echoed */
extern Flag  echoing;		/* True if input is being echoed */
extern int   eol0;		/* Alternate EOL character */
extern int   exitstat;		/* Status to report to exit() */
extern int   files;		/* Number of files processed */
extern Flag  forkfl;
extern char  ibuf[IBUF];	/* Buffer for input from port */
extern char *ibfa;		/* First valid char of ibuf */
extern char *ibfz;		/* Last+1 valid char of ibuf */
extern int   iomask;		/* Constant to convert values to ASCII */
extern int   l_tries;		/* Number of reads between nudges */
extern int   l_reads;		/* Number of reads before timeout */
extern Flag  locked;		/* True if lockfile exists*/
extern Flag  lockfl;		/* Create lockfile on login */
extern int   lockfn;		/* Lockfile number */
extern char  lockfile[50];	/* Place to build lockfile name */
extern char  lockroot[];	/* Pathname for creating lockfiles */
extern int   nudges;		/* Number of times we've nudged */
extern int   Nudges;		/* Limit to nudge attempts */
extern int   lsleep;		/* Sleep time for locks */
extern char *oldtarg;		/* Previous name of target */
extern char *path;		/* Default search path */
extern Flag  pathfl;
extern int   pid;		/* Our process id number */
extern char *prgnam;		/* Last field of program's name */
extern Flag  raw;		/* True if I/O is to be raw */
extern char  rsp[1+RSP];	/* Response buffer */
extern uint  rspmax;		/* Max response we can handle */
extern uint  rspsiz;		/* Current size of response buffer */
extern Flag  slow;		/* True if output should be slow */
extern Flag  slowfl;		/* True if count or slow turned on */
extern int   ss;		/* State: code for last action */
extern char *target;		/* Name of thing on other side of port */
extern Flag  termfl;		/* True if terminal state changed */
extern struct termio trminit;	/* Initial ioctl() setting for device */
extern uint  timeout;		/* Global timeout interval */
/*
** User identification stuff:
*/
#define PASSWD 16		/* Max length of a passwd */
#define USERID 16		/* Max length of a userid */
extern char  passwd[1+PASSWD];	/* Current passwd */
extern char  userid[1+USERID];	/* Current userid */
extern int   euid;		/* Effective user id number */
extern int   ruid;		/* Real user id number */
extern int   egid;		/* Effective group id number */
extern int   rgid;		/* Real group id number */
/*
** Definitions of non-int-valued functions:
*/
struct utmp *findutmp();
struct utmp *findutmp();
       char *getime();
       char *lastfield();
/*
** Some common gotos:
*/
#define Done goto done
#define Fail goto fail
#define Loop goto loop

#ifdef SYS5
#endif

#define uutty_h
#endif
\!Funky\!Stuff\!
echo file: dbg.h
cat > dbg.h << '\!Funky\!Stuff\!'
#ifndef dbg_h
#include <stdio.h>
#include <sys/types.h>
/*
** The global 'debug' variable controls the level of diagnostics
** to be produced.  Some suggested conventions are:
**	debug=0	fatal error messages only.
**	debug=1	diagnostic: serious messages and warnings, default.
**	debug=2	informative: major events and decisions.
**	debug=3	gabby: tracing of likely trouble spots.
**	debug=4	tracing of important events for troubleshooting.
**	debug=5	major function calls and returns.
**	debug=6	details of flow of control.
**	debug=7	major data tracing.
**	debug=8	detailed data tracing.
**	debug=9	everything of conceivable interest to anyone.
** Of course, it is entirely up to you how you use the debug 
** level, and many applications won't have a need for 10 levels. 
** It will help others trying to use your code if they can use
** the debug level easily to find out what's wrong.  At level
** 1, in particular, it is a good idea to give enough info to
** tell the user what went wrong.  For instance, don't just say
** that you can't write to a file; show the pathname that you
** failed to open for writing, and the errno code (even better,
** give the sys_errlist[errno] message).
*/
typedef          char  Flag;
typedef unsigned char  U8;
typedef unsigned short U16;
typedef unsigned long  U32;
/*
** Main debug-message pseudo-functions:
*/
#define D  if(debug> 1)dmsg
#define D1 if(debug>=1)dmsg
#define D2 if(debug>=2)dmsg
#define D3 if(debug>=3)dmsg
#define D4 if(debug>=4)dmsg
#define D5 if(debug>=5)dmsg
#define D6 if(debug>=6)dmsg
#define D7 if(debug>=7)dmsg
#define D8 if(debug>=8)dmsg
#define D9 if(debug>=9)dmsg
#define E emsg
#define P pmsg
#define V if(debug>0)pmsg
#define W wmsg
/*
** These functions produce a 1-line dump, with non-printable
** characters converted to 2-char escape sequences.
*/
#define Ascdump(a,n,o,m) ascdump((char*)(a),(U32)(n),(U32)(o),(char*)(m))
#define Ascdp(a,p) 	Ascdump(a,p-a+1,a,0) 
#define Ascdn(a,n) 	Ascdump(a,n,a,0) 
#define Ascdpm(a,p,m)	Ascdump(a,p-a+1,0,m)
#define Ascdnm(a,n,m)	Ascdump(a,n,0,m)
/*
** These functions produce a 3-line dump, consisting of displayable
** characters, high-order hex digit, and low-order hex digit.  On
** the first line, non-printing characters are shown as '_'.
*/
#define Hexdump(a,n,o,m) hexdump((char*)(a),(U32)(n),(U32)(o),(char*)(m))
#define Hexdp(a,p) 	Hexdump(a,p-a+1,a,0) 
#define Hexdn(a,n) 	Hexdump(a,n,a,0) 
#define Hexdpm(a,p,m)	Hexdump(a,p-a+1,0,m)
#define Hexdnm(a,n,m)	Hexdump(a,n,0L,m)

extern int   ascdump();	/* The actual ASCII-dump routine */
extern char *crlf;	/* Line terminator string, usually "\n" */
extern int   debug;	/* Debug level, 0-9, default=1 */
extern int   dbgsleep;	/* Sleep time after output, default=0 */
extern FILE *dbgout;	/* Diagnostic output stream, default=stderr */
extern int   dsp();	/* Convert an int to a printable char */
extern char *dbgtimep;	/* Time to display in debug messages */
extern int   errno;	/* Unix global error code */
extern int   hexdump();	/* The actual hex-dump routine */
extern char *progname;	/* Name of running program, from argv[0] */

#define dbg_h
#endif
\!Funky\!Stuff\!
echo file: args.c
cat > args.c << '\!Funky\!Stuff\!'
#include "uutty.h"
/*
** Process the command-line arguments.
*/
args(ac,av)
char **av;
{	int  a;

	for (a=1; a<ac; a++) {
		D3("arg %d=\"%s\"",a,av[a]);
		switch (av[a][0]) {
		case '-':
		case '+':
			D6("opt %d=\"%s\"",a,av[a]);
			option(av[a]);
			continue;
		default:
			switch(files++) {
			case 0:		/* Name of serial port */
				device = av[a];
				break;
			default:
				E("Too many args; \"%s\" ignored.",av[a]);
			}
		}
	}
}
\!Funky\!Stuff\!
echo file: awrite.c
cat > awrite.c << '\!Funky\!Stuff\!'
#include "uutty.h"
/* 
** Write a character string to the port, stopping at the first null,
** and processing a variety of escape sequences.  This routine always
** writes one byte at a time.  Doing a system call per byte is a bit
** wasteful of processor time, but it helps assure that we won't flood
** the poor little helpless modem.
*/
#define Gotbyte goto gotbyte 

awrite(msg)
	char*msg;
{	int  i, n;
	char c, d, *p;

	D5("awrite(%08lX)",msg);
	if (debug) {
		dbgtimep = getime();
		if (debug >= 2) P("%s Send:   %s",dbgtimep,msg);
		if (debug >= 4) Hexdnm(msg,1,"Send:");
	}
	n = strlen(msg);
	D8("port_wr:slow=%d",slow);
	p = msg;
	while (c = *p++) {
		switch (c) {
		case '^':		/* CTRL-X notation */
			c = *p++ & 0x3F;
			Gotbyte;
		case '%':		/* %AB is a hex value */
			c = 0;
			n = 2;		/* Accept at most 2 digits */
			while (n-- > 0) {
				switch (d = *p) {
				case '9': case '8': case '7': case '6': case '5': 
				case '4': case '3': case '2': case '1': case '0':
					c = (c << 4) | (c - '0');
					Gotbyte;
				case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
					c = (c << 4) | (c - 'A' + 10);
					Gotbyte;
				case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
					c = (c << 4) | (c - 'a' + 10);
					Gotbyte;
				default:
					goto gotbyte;
				}
				++p;
			}
			Gotbyte;
		case '\\':		/* Escape notation */
			switch (c = *p++) {
			case '7': case '6': case '5': case '4':
			case '3': case '2': case '1': case '0':
				c -= '0';
				if ((d = *p) && ('0'<=d && d<='7')) {
					c = (c << 3) | (d - '0');
					++p;
					if ((d = *p) && ('0'<=d && d<='7')) {
						c = (c << 3) | (d - '0');
						++p;
					}
				}
				Gotbyte;
			case 'B': case 'b': c = '\b'; Gotbyte;
			case 'D': case 'd': sleep(1); continue;
			case 'N': case 'n': c = '\n'; Gotbyte;
			case 'R': case 'r': c = '\r'; Gotbyte;
			case 'T': case 't': c = '\t'; Gotbyte;
			case 'X': case 'x': sendbrk(dev); continue;
			default : c = d; Gotbyte;
			}
		}
gotbyte:	Slowly;
		if (debug >= 3) {
			dbgtimep = getime();
			if (debug >= 3) Ascdnm(&c,1,"Write:");
			if (debug >= 4) Hexdnm(&c,1,"Write:");
		}
		D4("awrite: c=%02X='%c'",c,dsp(c));
		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);
		}
	}
}
\!Funky\!Stuff\!
echo file: checkid.c
cat > checkid.c << '\!Funky\!Stuff\!'
#include "uutty.h"
/* 
** The response string is believed to contain a login id.
** The id is copied to the global variable "userid", and
** its length returned.
*/
checkid(rp)
	char *rp;
{	char *p, *q;

	D4("checkid: r=\"%s\" ss=%d",rp,ss);
	for (p=rp; *p; ++p) {	/* Examine the chars for acceptability */
		switch(*p) {
		case 0x04:		/* EOT triggers a login prompt */
			Pwrite("login:");
			target = "?";
			ss = S_LOGIN;
			D4("State %d=%s",ss,gestate());
			return 1;
		case ' ':		/* Blanks aren't legal */
		case ':':		/* Colons aren't legal */
		case '\b':		/* Backspaces aren't legal */
		case '\t':		/* Tabs aren't legal */
			D3("ID with whitespace ignored.");
			ss = S_IDLE;
			D4("State %d=%s",ss,gestate());
			Fail;
		case '\0':		/* Assorted terminal chars */
		case '\r':
		case '\n':
			*p = 0;
			goto good;
		case '!':		/* Special goodie for killing daemon */
			if (p[1] == 'Q') {
				E("!Q in input; ");
				if (debug) P("%s: !Q in input, quitting [id]",getime());
				die(0);
			}
		default:
			continue;
		}
	}
good:			/* Make a copy of the supposed id */
	p = rp;
	q = userid;
	while (*p && q<userid+USERID)
		*q++ = *p++;
	*q = 0;
	if (debug >= 2) P("%s USERID=\"%s\"",getime(),userid);
	target = "logger";
	return (p-rp);
fail: return 0;
}
\!Funky\!Stuff\!
echo file: copy.c
cat > copy.c << '\!Funky\!Stuff\!'
/* Copy *s to *t, n bytes.
*/
copy(t,s,n)
 char *t, *s;
 int   n;
{
	while (n-- > 0)
		*t++ = *s++;
}
\!Funky\!Stuff\!
echo file: data.c
cat > data.c << '\!Funky\!Stuff\!'
#include "uutty.h"
#include <signal.h>

/*
** Assorted messages.
*/
char*m_exit = 0;			/* May be set to special exit message */
char m_login [] = "\r\nlogin:";		/* Unix login prompt */
char m_passwd[] = "\r\npassword:";	/* Unix password prompt */
char*m_init     = 0;			/* Special init string for device */
char*m_init1    = 0;			/* May be set to special init message */
char*m_init2    = "\r";			/* Normal nudge to get first response */
char*m_init3    = 0;			/* May be set to special init message */
char*m_nudge    = "\r";			/* What to send to trigger a response */
/*
** Assorted global variables.
*/
int   baudmask = 0;	/* CBAUD mask for termio ioctl (B1200, B9600, etc) */
int   baudrate = 0;	/* Numeric value of baud rate (1200, 9600, etc) */
uint  count  = 0;	/* Delay between "typed" chars */
long  currtime;		/* Timestamp */
char *ctimep = "?";	/* ASCII form of currtime */
char *device = 0;	/* Name of serial port */
int   dev    = 0;	/* Number of serial port */
char *devfld= "?";	/* Last field of device name */
Flag  echofl = 0;	/* True if input is to be echoed */
Flag  echoing= 0;	/* True if input is being echoed */
int   eol0   = -1;	/* Extra EOL char to recognize */
int   exitstat = 0;
int   files  = 0;
char  ibuf[IBUF];	/* Buffer for input from port */
char *ibfa   = ibuf;	/* First valid char of ibuf */
char *ibfz   = ibuf;	/* Last+1 valid char of ibuf */
int   iomask = 0x7F;	/* Converts chars to ASCII */
int   l_tries= L_TRIES;	/* Number of reads between nudges */
int   l_reads= L_READS;	/* Number of reads before timeout */
int   lsleep = 30;	/* Sleep time when we hit a lockfile */
Flag  locked = 0;	/* Lockfile created */
Flag  lockfl = 0;	/* Create lockfile on login */
int   lockfn = -1;	/* Lockfile number */
char  lockfile[50] = {0};	/* Place to build lockfile name */
char  lockroot[] = "/usr/spool/uucp/LCK..";
int   nudges = 0;	/* Number of times we've nudged */
int   Nudges = NUDGES;	/* Limit to nudge attempts */
char *oldtarg = "?";	/* Previous name for thing on other side of port */
int   pid    = -1;	/* Our process id number */
char *prgnam = "?";	/* Last field of program's name */
Flag  raw    = 1;	/* Raw I/O on device? */
char  rsp[1+RSP];	/* Response buffer */
uint  rspmax = RSP;	/* Max response we can handle */
Flag  slow   = 0;
Flag  slowfl = 0;	/* True if count or slow turned on */
int   ss     = S_INIT;	/* State: code for last action */
char *target  = "port";	/* Name of thing on other side of port */
Flag  termfl  = 0;	/* True if terminal state changed */
/*
** User identification stuff:
*/
char  passwd[1+PASSWD] = {0};	/* Current passwd */
char  userid[1+USERID] = {0};	/* Current userid */
int   euid    = -1;		/* Effective user id number */
int   ruid    = -1;		/* Real user id number */
int   egid    = -1;		/* Effective group id number */
int   rgid    = -1;		/* Real group id number */

#ifdef SYS5
	struct stat   status;		/* Child process's status */
	struct termio trminit  = {0};	/* For saving terminal (ioctl) status */
	struct utmp  *up = 0;		/* Pointer to /etc/utmp entry */
	struct utmp   utmp = {0};	/* Scratch /etc/utmp entry */
#endif

\!Funky\!Stuff\!
echo file: dbg.c
cat > dbg.c << '\!Funky\!Stuff\!'
/* John Chambers' debugging package for C programs.  This
** package is intended, among other things, to be added to
** other peoples' software after the fact to aid in getting
** it to run.  An attempt has been made to avoid global 
** names that others will use, though there are likely
** to be collisions with other debugging packages.  The
** most common source of conflicts is due to the fact that
** this package includes <stdio.h> and <sys/types.h>; you
** should hunt down references to either of these and put
** the line:
**	#include <dbg.h>
** in place of both of them.
**
** The primary functions defined here are based on conditional 
** calls of printf(), with the program's name inserted at the 
** start, a newline appended, and fflush() called to send it 
** on its way.  There is also a hexdump() routine to produce
** a hex dump of a chunk of memory, and an ascdump() routine
** to display things in ASCII form.  All these are intended 
** to be called via the macros defined in "dbg.h".
**
** The global variable "debug" should be set to a value in [0,9],
** to control the amount of debugging output.
**
** Note that "dbgout" is a FILE*, either stdout or stderr as you
** wish, with stderr the default.
*/
#include "dbg.h"

static char dbgheader[] = "@(#)RELEASE: 1.0  Sep 20 1986 ~jc/dbg";
/* 
** If you normally use terminals with a particular line
** length, you might adjust these.  Note that there is
** an initial 7-byte fieldin all lines, plus a newline,
** so MAXCOLS should be >= MAXCHS+8.
*/
#define MAXCOLS 80		/* Max length of print line */
#define MAXCHRS 64		/* Max chars in one hexdump chunk */

extern int errno;			/* Unix error code */
	char  *crlf   = "\n";		/* Line terminator string, might be "\n\r" */
	FILE  *dbgout = stderr;		/* Can be assigned to switch to stdout */
	int    dbgsleep = 0;		/* Sleep time (secs) after messages */
	int    debug  = 1;		/* Set to desired debug level (0-9) */
	char *dbgtimep  = 0;		/* Set to ASCII date/time if desired in output */
static	int    hex_chrs = MAXCHRS;	/* Number of chars to dump in one line */
static	int    hex_cols = MAXCOLS;	/* Length of print line */
static	int    hex_word = MAXCHRS;	/* Number of bytes to group together */
	char  *progname = "?";		/* Set to argv[0] */
/*
** Debug output routine.  The program's name is
** prepended to the message, a terminator is added,
** and an optional sleep is done.
*/
/*VARARGS1*//*ARGSUSED*/
dmsg(fmt,x0,x1,x2,x3,x4,x5,x6,x7,x8,x9) char *fmt;
{	int n;
	n  = fprintf(dbgout,"%s: ",progname);
	n += fprintf(dbgout,fmt,x0,x1,x2,x3,x4,x5,x6,x7,x8,x9);
	n += fprintf(dbgout,crlf);
	fflush(dbgout);
	if (dbgsleep) sleep(dbgsleep);
	return n;
}
/* Note that emsg() writes to both dbgout and stderr.
*/
/*VARARGS1*//*ARGSUSED*/
emsg(fmt,x0,x1,x2,x3,x4,x5,x6,x7,x8,x9) char *fmt;
{   int n;
	if (dbgout != stderr)
		n = dmsg(fmt,x0,x1,x2,x3,x4,x5,x6,x7,x8,x9);
	n += fprintf(stderr,"%s: ",progname);
	n += fprintf(stderr,fmt,x0,x1,x2,x3,x4,x5,x6,x7,x8,x9);
	n += fprintf(stderr,crlf);
	fflush(stderr);
	return n;
}
/* Print a message to the debug output, adding only
** a terminator, but don't add program identification.
*/
/*VARARGS1*//*ARGSUSED*/
pmsg(fmt,x0,x1,x2,x3,x4,x5,x6,x7,x8,x9) char *fmt;
{   int n;
	n  = fprintf(dbgout,fmt,x0,x1,x2,x3,x4,x5,x6,x7,x8,x9);
	n += fprintf(dbgout,crlf);
	fflush(dbgout);
	if (dbgsleep) sleep(dbgsleep);
	return n;
}
/* Write a message to the debug output, adding nothing.
*/
/*VARARGS1*//*ARGSUSED*/
wmsg(fmt,x0,x1,x2,x3,x4,x5,x6,x7,x8,x9) char *fmt;
{   int n;
	n  = fprintf(dbgout,fmt,x0,x1,x2,x3,x4,x5,x6,x7,x8,x9);
	fflush(dbgout);
	if (dbgsleep) sleep(dbgsleep);
	return n;
}
/* 
** Hexdump routine.  The params are a starting address,
** a byte count, an address to show on the output (which 
** is often the starting address), and an optional message
** for labeling the dump.  If the message is present, the
** address is not displayed, since they both would appear
** at the same place in the output.
**
** Note that the actual output is done by pmsg().
*/
static char  hex_cc[1+MAXCOLS];
static char  hex_hi[1+MAXCOLS];
static char  hex_lo[1+MAXCOLS];
static int   ascfl = 0;			/* True if hex dump not wanted */
/*
** Produce only the ASCII line; omit the two hex lines.
*/
ascdump(a,n,o,m)
	char *a;	/* Address of first byte to dump */
	U32   n;	/* Number of bytes */
	char *o;	/* Offset (address) to report */
	char *m;	/* Initial message, if o==NUL */
{	int   c;

	D4("ascdump(a=%06lX,n=%d,o=%06lX,m=%06lX)",a,n,o,m);
	if (dbgtimep) fprintf(dbgout,"%s ",dbgtimep);
	if (m) sprintf(hex_cc,"%s            ",m);
	  else sprintf(hex_cc,"%7d           ",o);
	fprintf(dbgout,"%7.7s",hex_cc);
	fflush(dbgout);
	while (n-- > 0) {
	  c = *a++ & 0x7F;
	  switch (c) {
	  case '\0': fprintf(dbgout,"\\0"); break;
	  case '^' : fprintf(dbgout,"\\^"); break;
	  case '\\': fprintf(dbgout,"\\\\"); break;
	  case '\b': fprintf(dbgout,"\\b"); break;
	  case '\n': fprintf(dbgout,"\\n"); break;
	  case '\r': fprintf(dbgout,"\\r"); break;
	  case '\t': fprintf(dbgout,"\\t"); break;
	  case 0x7F: fprintf(dbgout,"\\D"); break;
	  default: 
	    if (c < ' ') {
	      fprintf(dbgout,"^%c",c|0x40);
	      break;
	    }
	    fprintf(dbgout,"%c",c);
	    break;
	  }
	}
	fprintf(dbgout,crlf);
	fflush(dbgout);
}
/*
*/
hexdump(a,n,o,m)
	char *a;	/* Address of first byte to dump */
	U32   n;	/* Number of bytes */
	U32   o;	/* Offset (address) to report */
	char *m;	/* Initial message, if o==NUL; -1 for ascii only */
{
	ascfl = 0;
	return hex_dump(a,n,o,m);
}
/*
*/
hex_dump(a,n,o,m)
	char *a;	/* Address of first byte to dump */
	U32   n;	/* Number of bytes */
	U32   o;	/* Offset (address) to report */
	char *m;	/* Initial message, if o==NUL; -1 for ascii only */
{	int   col, i;
	char  c;

	hex_init(m,o);
	col = 0;
	while (n) {	
		c = *a++;
		i = 7 + col + (col / hex_word);
		n--;
		hex_lo[i] = hex_dig(c & 0xF);
		hex_hi[i] = hex_dig((c >> 4) & 0xF);
		hex_cc[i] = dsp(c);
		col++;
		o++;
		if (col >= hex_chrs) {	
			hex_out();
			hex_init(m,o);
			col = 0;
		}
	}
	if (col > 0) hex_out();
	return 0;
}
/* Generate a printable char for x.  If x is an ASCII
** printable char, it is returned.  For others, '_' is
** returned.
*/
dsp(x) 
{	x &= 0x7F;
	return (0x20<=x && x<=0x7E) ? x : '_';
}
/* Given a small integer [0-15], this routine returns
** the corresponding ASCII char for a hex digit.
*/
hex_dig(x) 
{	return  ( 0<=x && x<= 9) ? '0' + x 
	: (10<=x && x<=15) ? 'A' + x - 10 
	: '_';
}
/* Initialize the line images for a given address.
*/
hex_init(m,a)
	char *m;	/* Initial message (<=8 chars) */
	U32   a;	/* Address reported on output */
{	int   i;

	i = hex_chrs + (hex_chrs / hex_word) + 8;	/* Length of print line (including final newline) */
	if (hex_cols < i) {
		hex_cols = i;
		D5("hex_init: cols=%d chrs=%d word=%d",hex_cols,hex_chrs,hex_word);
	}
	for (i=0; i<=hex_cols; i++)
		hex_cc[i] = hex_hi[i] = hex_lo[i] = ' ';
	if (m && a==0) {
		strcpy(hex_cc,m);
	} else {
		sprintf(hex_cc,"%6ld: ",a);
		sprintf(hex_hi,"%6lX: ",a);
	}
	return 0;
}
/* The three lines of hex dump info are complete;
** do a bit of straightening out, and print them.
*/
hex_out()
{	int i;

	for (i=0; i<=hex_cols; i++) {		/* sprintf() may produce nulls */
		if (hex_cc[i] == 0) hex_cc[i] = ' ';
		if (hex_hi[i] == 0) hex_hi[i] = ' ';
		if (hex_lo[i] == 0) hex_lo[i] = ' ';
	}
	for (i=hex_cols; i>0; i--) 			/* Trim lines */
		if (hex_cc[i] == ' ') 
			hex_cc[i] = 0;
		else break;
	for (i=hex_cols; i>0; i--) 
		if (hex_hi[i] == ' ') 
			hex_hi[i] = 0;
		else break;
	for (i=hex_cols; i>0; i--) 
		if (hex_lo[i] == ' ') 
			hex_lo[i] = 0;
		else break;
	if (dbgtimep) fprintf(dbgout,"%s ",dbgtimep);
	pmsg("%s",hex_cc);			/* Symbolic dump */
	if (ascfl == 0) {
	    if (dbgtimep) fprintf(dbgout,"%s ",dbgtimep);
		pmsg("%s",hex_hi);		/* High-order hex digit */
	    if (dbgtimep) fprintf(dbgout,"%s ",dbgtimep);
		pmsg("%s",hex_lo);		/* Low-order hex digit */
	}
	if (dbgsleep) sleep(dbgsleep);
	return 0;
}
\!Funky\!Stuff\!
echo file: die.c
cat > die.c << '\!Funky\!Stuff\!'
#include "uutty.h"
#include <signal.h>

die(retval)
{	int i;

	D4("die(%d)",retval);
	if (locked) unlock();
	l_tries = l_reads = 1;	/* Give up quickly */
	Resync;			/* Gobble up all input */
	if (m_exit) Awrite(m_exit);	/* Special exitial message? */
	if (termfl && isatty(dev)) {	/* Restore terminal status */
		D7("die: %d:\tcflag=%06o",dev,trminit.c_cflag);
		D7("die: %d:\tiflag=%06o",dev,trminit.c_iflag);
		D7("die: %d:\tlflag=%06o",dev,trminit.c_lflag);
		D7("die: %d:\toflag=%06o",dev,trminit.c_oflag);
		D5("die:before ioctl(%d,%d,%06lX)",dev,TCSETA,&trminit);
		i = ioctl(dev,TCSETA,&trminit);
		D5("die: after ioctl(%d,%d,%06lX)=%d",dev,TCSETA,&trminit,i);
		D3("File %d restored to normal.",dev);
	}
	exit(retval);
}
\!Funky\!Stuff\!
echo file: exec.c
cat > exec.c << '\!Funky\!Stuff\!'
#include "uutty.h"
/* 
** Start up a new program in the current process.  This is normally
** to start up a shell for a logged-in user.   If the command can't
** be executed, -1 is returned.	 Note that we can define a new search
** path here which will overwrite the one we were given.
*/
	Flag  forkfl = 0;	/* Should we fork or exec directly? */
	Flag  pathfl = 0;	/* True if PATH defined in environment */
/*
** The 'path' variable should be defined appropriately for your
** system.  If it is null, we will just pass on the path that
** was handed to us, which may be right on many systems.
*/
#ifdef Cadmus
	char *path = "PATH=:.:/bin:/bin.cadmus:/bin.sys5:/bin.sys3:/bin.ver7:/usr/ucb:/usr/bin:/etc:/usr/local:/usr/new:";
#define Path
#endif
#ifdef VME
	char *path = "PATH=:.:/bin:/usr/bin:/etc:";
#define Path
#endif
#ifndef Path
	char *path = 0;		/* No search path */
#endif

exec(cook,cmd,pp)
	int   cook;		/* Should we do cooked or raw I/O? */
	char *cmd;		/* Null-terminated command */
	struct passwd *pp;	/* The user's /etc/passwd entry, unpacked */
{	int   a, c, e, i, n, r;
	int   child;
	Flag  lognfl, shelfl, homefl;
	char *av[10];		/* This should be room enough */
	char *cp, *dp;
	char**ev;
	char  arg0[30];		/* For shell's visible name */
	char  logn[30];		/* For LOGNAME=<id> string */
	char  home[30];		/* For HOME=<dir> string */
	char  shll[30];		/* For SHELL=<prog> string */
	char *shell="/bin/sh";	/* default shell */
	extern long malloc();

	D5("exec(%d,\"%s\",%06lX)",cook,cmd,pp);
	r = -1;
	for (i=0; environ[i]; i++);	/* How big is environ[]? */
	n = (i+5) * sizeof(char**);	/* Space for copy + 5 entries */
	D5("exec: Get %d bytes for %d+5 environment entries.",n,i);
	ev = (char**)malloc(n);		/* Allocate a copy for us */
	if (ev == NULL) {
		E("Can't get %d bytes for environment table [exec].",n);
		Fail;
	}
	for (e=0; environ[e]; e++) {	/* Copy the environment vector */
		ev[e] = environ[e];
		D6("ev[%2d]=\"%s\"",e,ev[e]);
	}
	ev[e] = 0;
	D3("exec: There are %d environment entries.",e);
	pid = getpid();
	D4("exec: This is process %d.",pid);
	a = e = 0;			/* indices for av[] and ev[] */
	for (cp=cmd; *cp; ++cp) {	/* Scan the command for whitespace */
		c = *cp;
		if (c == ' ' || c == '\t') {
			av[a++] = "-sh";
			av[a++] = "-c";
			av[a++] = cmd;
			break;
		} else
		if (c == ':' || c == '\n') break;
	}
	if (a == 0) {				/* the command is just a program name */
		shell = cmd;			/* Note that it's our shell */
		dp = av[a++] = arg0;		/* Name to tell shell */
		*dp++ = '-';			/* Build arg0 with initial '-' */
		cp = lastfield(cmd,'/');	/* Find the last field of the name */
		while (*cp) *dp++ = *cp++;	/* Copy name to arg0 */
		*dp = 0;			/* Gotta have a null terminator */
		D4("exec: arg0=\"%s\"",arg0);
	}
	D3("av[%2d]=\"%s\"",a-1,av[a-1]);

	D7("exec:before sprintf(%06lX,\"%s\",%06lX)",logn,"LOGNAME=%s\0",pp->pw_name);
	sprintf(logn,"LOGNAME=%s\0",pp->pw_name);
	D6("exec: after sprintf() logn=\"%s\"",logn);

	D7("exec:before sprintf(%06lX,\"%s\",%06lX)",home,"HOME=%s\0"   ,pp->pw_dir);
	sprintf(home,"HOME=%s\0"   ,pp->pw_dir);
	D6("exec: after sprintf() home=\"%s\"",home);

	D7("exec:before sprintf(%06lX,\"%s\",%06lX)",shll,"SHELL=%s\0"  ,shell);
	sprintf(shll,"SHELL=%s\0"  ,shell);
	D6("exec: after sprintf() shll=\"%s\"",shll);

	if (debug >= 5) {
		av[a++] = "-VX";		/* This turns on shell debugging */
		D3("av[%2d]=\"%s\"",a-1,av[a-1]);
	}
	av[a] = 0;			/* Paranoia */
/*
** This is supposed to help, but seems not to:
**
	D4("exec: before setpgrp()");
	i = setpgrp();
	D4("exec: setpgrp()=%d",i);
*/
	lognfl = shelfl = homefl = 0;		/* Flags for noticing environment entries */
	for (e=0; ev[e]; e++) {			/* Examine the environment table */
		D3("exec: Examine ev[%2d]=%06lX=\"%s\"",e,ev[e],ev[e]);
		if (st_init("PATH=",ev[e]) > 0) {
			D7("exec: Matched \"PATH=\"; path=%06lX=\"%s\"",path,path);
			if (path) ev[e] = path;	/* Use special path */
			D6("ev[%2d]=\"%s\"",e,ev[e]);
			pathfl++;		/* Note we did it */
		}
		if (st_init("LOGNAME=",ev[e]) > 0) {
			ev[e] = logn;		/* Use special path */
			D6("ev[%2d]=\"%s\"",e,ev[e]);
			lognfl++;		/* Note we did it */
		}
		if (st_init("HOME=",ev[e]) > 0) {
			ev[e] = home;		/* Use special path */
			D6("ev[%2d]=\"%s\"",e,ev[e]);
			homefl++;		/* Note we did it */
		}
		if (st_init("SHELL=",ev[e]) > 0) {
			ev[e] = shll;		/* Use special path */
			D6("ev[%2d]=\"%s\"",e,ev[e]);
			shelfl++;		/* Note we did it */
		}
	}
	if (!homefl) ev[e++] = home;	/* Add the home directory */
	if (!lognfl) ev[e++] = logn;	/* Add the login id */
	if (!shelfl) ev[e++] = shll;	/* Add the shell's name */
	if (!pathfl) ev[e++] = path;	/* Add the search path (may be null) */
	D3("exec: There are %d environment entries.",e);
	ev[e] = 0;			/* Paranoia */
	if (debug >= 3) {		/* Display the shell's parameters */
		P("before execve(\"%s\",%lX,%lX)",shell,av,ev);
		for (i=0; av[i]; i++)
			P("arg[%2d]=\"%s\"",i,av[i]);
		for (i=0; ev[i]; i++)
			P("env[%2d]=\"%s\"",i,ev[i]);
	}
	if (debug) P("%s Start %s for u=%d=%s g=%d.",getime(),shell,pp->pw_uid,pp->pw_name,pp->pw_gid);
	if (cook) makesane(dev);
	if (dev != 0) {close(0); i = dup(dev); D3("exec: File %d=\"%s\"",i,device); }
	if (dev != 1) {close(1); i = dup(dev); D3("exec: File %d=\"%s\"",i,device); }
	child = -1;			/* Default is no child process */
	pid = getpid();			/* Note which process we are now */
	if (forkfl) {			/* Are we forking or execing directly? */
		D4("exec: Forking sub-process for shell.");
		if (lockfl) {		/* Should we create a lockfile? */
			lockfn = creat(lockfile,0);
			if (debug >= 2)
				P("%s: Create lockfile %d=\"%s\".",getime(),lockfn,lockfile);
			if (lockfn < 0) {	/* Either locked or directory isn't writable */
				E("Can't create lockfile \"%s\" [errno=%d]",lockfile,errno);
				die(errno);
			}
			locked = 1;		/* Note that there's a lockfile */
		}
		if ((child = fork()) > 0) {	/* Are we the parent? */
			D3("exec: %s: Shell process %d created.",getime(),child);	
			if (lsleep > 0) sleep(lsleep);
			for (;;) {		/* Loop until shell goes away */
				errno = 0;
				r = wait(0);		/* Sleep until a subprocess dies */
				D3("%s: wait(0)=%d",getime(),r);
				if (r == child)		/* Was it the shell process? */
					Done;		/* If so, all's fine */
				D3("%s: After wait(0)=%d [errno=%d]",getime(),r,errno);
				if (r > 0) {
					E("%s: Wrong child %d died; waiting for %d.",getime(),r,child);
					continue;
				}
				switch(errno) {
				case ECHILD:
					D3("exec: wait(0)=%d [errno=%d=No child]",r,errno);
					Done;
				default:
					E("Unknown errno=%d after wait(0)=%d",errno,r);
					Done;
				}
			}			/* Hang until the shell process dies */
done:			if (debug) sleep(1);
			D3("exec: %s: Child %d gone.",getime(),r);
			if (r > 0) r = 0;
			if (lockfl) unlock();
			if (debug) P("%s: \"%s\" done , die(%d)",getime(),shell,r);
			Fail;
		}
	}
/* 
** If we fall through to here, we are the process to exec a shell.
** We may be the original process or a subprocess.
*/
	if (debug > 1) {
		pid = getpid();
		P("%s: Shell process %d.",getime(),pid);
	}
	if (dev != 2) { close(2); i = dup(dev); }
	/**** Don't produce debug output after this! */
	errno = 0;
	if (dev>2) close(dev);	/* Don't give excess open files to the shell */
	r = execve(shell,av,ev);
	E("Can't exec \"%s\"\t[errno=%d]",shell,errno);
/*
** Note that we always terminate, regardless of how we get here.
** If we are being run from init(1), a new process will be started
** up.  If not, we just go away.  This isn't necessary, but seems
** in general to be a good idea.
*/
fail:				/* Both processes come here to terminate */
	if (lsleep > 0)
		sleep(lsleep);	/* Don't respawn too fast */
	die(r);			/* This will respawn if we're a daemon */
}
\!Funky\!Stuff\!
echo file: fillutmp.c
cat > fillutmp.c << '\!Funky\!Stuff\!'
#include "uutty.h"
/* 
** The following routine may have to be changed for different Unices,
** and probably will have to be totally rewritten for other systems.
**
** Fill in fields of the utmp structure *up, which may be our global
** utmp or may be another one returned by some library routine.
**
** Here that we define our own cute copy macro, which copies <siz> 
** bytes or to the first null, whichever comes first.  Note the test
** to verify that the src address is nonnull.
*/
#define CP(dst,src,siz) {p = dst; if (q = src) for (i=0; i<siz; i++) if (*q) *p++ = *q++; else *p++ = '\0';}

fillutmp(name,idp,line,type)
	char *name;	/* User's login id */
	char *idp;	/* 4-byte line id for utmp entry */
	char *line;	/* Device (port) name */
	int   type;
{	char *p, *q;
	int   i;

	D6("fillutmp(%lX,%lX,%lX,%d)",name,idp,line,type);
	if (name) D5("fillutmp: name=\"%s\"",name);
	if ( idp) D5("fillutmp:  idp=\"%s\"", idp);
	if (line) D5("fillutmp: line=\"%s\"",line);
	if (up == 0) {		/* No ttyslot!!! */
		D5("utmp:before setutent()");
		setutent();			/* Make pututline rescan the file */
		up = &utmp;
	}
#ifdef SYS5
	D5("up=%06lX",up);
	D5("utmp: Copy name \"%s\"",up->ut_user);
	CP(up->ut_user,name,8);			/* Fill in the user field with the login id */
	D5("utmp: Copy   id \"%s\"",up->ut_id);
	CP(up->ut_id,  idp, 8);			/* The "id" field is from the /etc/inittab entry */
	D5("utmp: Copy line \"%s\"",up->ut_line);
	CP(up->ut_line,line,12);		/* Copy the device name to the line field */
	up->ut_pid  = getpid();			/* This is probably already correct, but... */
	up->ut_type = type;
	up->ut_exit.e_termination = 0;		/* Default exit codes are all zero */
	up->ut_exit.e_exit = 0;
	D5("utmp: Note time...");
	time(&currtime);
	up->ut_time = currtime;			/* Tell the OS when (s)he logged in */
	if (debug >= 5) Hexdnm(up,sizeof(struct utmp),"utmp");
#endif
/*
** No return value; the caller trusts us to do it right.
*/
}
\!Funky\!Stuff\!
echo file: findutmp.c
cat > findutmp.c << '\!Funky\!Stuff\!'
#include "uutty.h"
/* 
** Try to find our entry in the /etc/utmp file.
** If we find it, it is copied to our global utmp.
** This works on SYS/V, and probably on SYS/III,
** but likely not on anything else.
*/
struct utmp *
findutmp()
{	int rec;		/* Record number, used in debug output */

	pid = getpid();		/* We are looking for an entry with out process id */
	rec = 0;		/* No records read so far */
	up  = 0;		/* No entry unpacked so far */
#ifdef SYS5
	setutent();		/* Restart at the top of the file */
loop:
	D5("before getutent()");
	errno = 0;
	up = getutent();	/* Read in one /etc/utmp entry */
	++rec;
	D4("getutent()=%06lX rec=%d\t[errno=%d]",up,rec,errno);
	if (up == 0) {
		D3("findutmp() FAILED.");
		return 0;
	}
	if (debug >= 5) Hexdnm(up,sizeof(struct utmp),"utmp");
	if (up->ut_pid != pid) {
		D4("findutmp: rec=%d pid=%d wrong.",rec,pid);
		Loop;
	}
	D4("findutmp: rec=%d pid=%d is me.",rec,pid);
	copy(&utmp,up,sizeof(struct utmp));
	up = &utmp;
	return up;
#endif
}
\!Funky\!Stuff\!
echo file: gestate.c
cat > gestate.c << '\!Funky\!Stuff\!'
#include "uutty.h"
/* 
** Look up the current state and return its symbolic name.
**
** The state[] array is the list of known states.  Note that 
** we assume that states are non-negative, and use -1 as an 
** end-of-array flag.  Note also that zero is a highly-likely 
** state, and should be included as a distinct state (probably 
** S_INIT).  The order is irrelevant, except for speed; a linear 
** lookup is done.
*/
struct state {int ssval; char *ssnam; } state[] = {
	{S_EXIT,	"EXIT"},
	{S_IDLE,	"IDLE"},
	{S_INIT,	"INIT"},
	{S_LOGIN,	"LOGIN"},
	{S_PASSWD,	"PASSWD"},
	{-1,		"UNKNOWN"}
};
char *
gestate()
{	int i;

	for (i=0; state[i].ssval >= 0; i++)
		if (state[i].ssval == ss)
			return state[i].ssnam;
	return state[i].ssnam;			/* Final "unknown" state */
}
\!Funky\!Stuff\!
echo file: getime.c
cat > getime.c << '\!Funky\!Stuff\!'
#include "uutty.h"
/* 
** Get the current local time, return pointer to the ASCII version.
*/
char *getime()
{
		time(&currtime);		/* 32-bit UNIX timestamp */
		ctimep = ctime(&currtime);	/* Convert to ASCII */
		ctimep[24] = '\0';		/* We don't want the newline */
		return ctimep;
}
\!Funky\!Stuff\!
echo file: help.c
cat > help.c << '\!Funky\!Stuff\!'
#include "uutty.h"
/* 
** The -h option triggers this routine.  We should try to
** keep it up to date, so users can find out easily what
** options are currently supported.
*/
help()
{
	P("usage: %s [-options] [device] [2>auditfile]",progname);
	P("options are:");
	P("\t-b<n>\tSet baud rate to <n>.");
	P("\t-c<n>\tCount to <n> before reads.");
	P("\t-e<msg>\tMessage to send to port before exiting.");
	P("\t-f\tFork (create subprocess) to start shells.");
	P("\t-h\tHelp--display this information.");
	P("\t-i<msg>\tMessage to send for initalizing port.");
	P("\t-l\tCreate uucp lockfiles for successful logins.");
	P("\t-n<msg>\tMessage to send to nudge the port.");
	P("\t-p<f>\tPort name is <f>.");
	P("\t-r<n>\tRaw I/O [default=1=true].");
	P("\t-s<n>\tSleep <n> seconds before reads.");
	P("\t-x<n>\tDebug level of <n>.");
	P("Note that error output [file 2] is used for debugging.");
}
\!Funky\!Stuff\!



More information about the Mod.sources mailing list