Subject: v08i072: Bidirectional getty/login for SystemV, Part01/02 Newsgroups: mod.sources Approved: mirror!rs Submitted by: cdx39!jc@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 ] [ .B \-d ] [ .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..", where 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 , 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 option, various debugging messages are produced, depending on the value of the digit . 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.. . 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 #include #include #include #include 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 #include /* ** 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 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= 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 /* ** 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 and ; you ** should hunt down references to either of these and put ** the line: ** #include ** 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 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= string */ char home[30]; /* For HOME= string */ char shll[30]; /* For SHELL= 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 ** 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; iut_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\tSet baud rate to ."); P("\t-c\tCount to before reads."); P("\t-e\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\tMessage to send for initalizing port."); P("\t-l\tCreate uucp lockfiles for successful logins."); P("\t-n\tMessage to send to nudge the port."); P("\t-p\tPort name is ."); P("\t-r\tRaw I/O [default=1=true]."); P("\t-s\tSleep seconds before reads."); P("\t-x\tDebug level of ."); P("Note that error output [file 2] is used for debugging."); } \!Funky\!Stuff\!