[alt.sources] System V getty clone

bill@twwells.com (T. William Wells) (01/07/90)

This is basically a drop in replacement for the System V getty
program. There are some minor differences and a number of
enhancements. There is a manual page (not troff, sorry) for this
getty.

I'll be posting this to comp.sources.unix but I know that some
people want it now and c.s.u. appears to be dormant.

echo x - README
sed 's/^X//' >README <<'*-*-END-of-README-*-*'
XThis is getty version 1.0, released January 1, 1990. It is very nearly
Xa drop in replacement for the System V getty, though there are some
Xminor differences. Send bug reports and suggestions to
Xbill@twwells.com. I have made the code as clean as I can; if you don't
Xunderstand something, and the problem is not just that you don't like
Xmy coding style, let me know. I'll either comment or recode as
Xnecessary.
X
XI use this getty on my system, a Microport V/386 3.0e, and it works
Xthere, but I have no practical way to test it on other systems. I
Xunderstand that others have run this with little trouble on other SysV
Xsystems.
X
XThis is pretty much a direct replacement for the standard getty that
Xcame with my Microport V/386 3.0e system. There are two reasons I
Xwrote it: for logging terminal activity and so that the terminal
Xcharacteristics are set up properly for better system security. Some
Xother features: the standard getty has a few irksome bugs and is
Xpoorly documented. These are gone. As for whatever bugs appear in this
Xone, well we now have source. The documentation is, I believe, more
Xuseful than the original getty document. I don't recommend replacing
Xyour getty with this new code till you have made sure that you still
Xhave a way into your system; wouldn't it be embarassing to have to
Xjump through the installation hoops again just because getty won't
Xtalk to you?
X
XRead the copyright notices in the individual files to determine
Xexactly what their restrictions are. Basically, they are free
Xdistribution, no direct commercial use, and keep a modification log in
Xthe code if you distribute modified versions.
X
XThere are several changes I'm thinking about for the next version:
X
X	Permit the specification of the terminal control characters in
X	the gettydefs file. This means that you can set up the
X	terminal to default to ^C as the control character instead of
X	whatever brain damage your system provides.
X
X	Add a new field to the gettydefs entries to permit
X	specification of the program to run; this would let you use
X	different login programs on different lines.
X
X	Change the -c display to interpretable by humans, e.g., show
X	the modes symbolically instead of in octal.
X
X	Add a manual page for /etc/gettydefs.
X
X	Put all the regular configuration stuff into the Makefile.
X
X	Arrange that all static nonconstant variables are initialized by code.
X
X	Eliminate the dependence on the ASCII character set.
X
X	There are still a few line length limits which should go.
*-*-END-of-README-*-*
echo x - getty.8
sed 's/^X//' >getty.8 <<'*-*-END-of-getty.8-*-*'
XNAME
X	getty -- set terminal types, modes, speed, line discipline
X
XSYNOPSIS
X
X	getty [-P] [-T] [-h] [-s]
X	    [-l logfile] [-t timeout] [-i issue_file]
X	    line [speed [type [linedisc]]]
X	getty -c [file]
X
XCOPYRIGHT
X
X	Copyright 1989 By T. William Wells. All Rights Reserved.
X
X	You may distribute this document and its derivatives so long
X	as you do not charge for them; you may charge a fee sufficient
X	to cover distribution costs. You may not include it in a
X	product to be sold. You may not use it as an inducement to
X	buy.
X
X	You may not modify this copyright notice; you may only add to
X	the modification log that follows this copyright notice.
X
X	You may modify this document. If you modify the document and
X	distribute it, you must update the modification log to say who
X	you are and when you made the change.
X
XMODIFICATION LOG
X
X	Send bug reports to T. William Wells, bill@twwells.com.
X	Version 0.1, written by T. William Wells, released March 19, 1989.
X	Version 0.2, modified by T. William Wells, released June 25, 1989.
X	Version 1.0, modified by T. William Wells, released January 1, 1990.
X
XDESCRIPTION
X
X	Getty is one of the programs used to connect a terminal to the
X	system. The normal procedure is for init to fork a getty,
X	getty to exec login, and login to run a shell. Getty can only
X	be executed by the super-user, except when the -c option is
X	used.
X
X	Getty uses its arguments and the contents of /etc/gettydefs to
X	set up an initial connection to the user. It displays a
X	connect message from /etc/issue and prompts the user for a set
X	of arguments. It reads those arguments and passes them along
X	to login. While reading the arguments, getty determines some
X	terminal characteristics which it uses to set various terminal
X	modes just before calling login. If getty reads a NUL
X	character, normally created by the user pressing the break
X	key, getty will change the line speed as specified in the
X	/etc/gettydefs file.
X
X	`Line' is the name of the terminal device to be opened by
X	getty. It is found by looking in /dev. The terminal modes are
X	set to 0660 by getty; the owner and group ids are set from the
X	effective ids of the getty.
X
X	`Speed' is the label for an entry in /etc/gettydefs. Each
X	entry contains the terminal parameters to be established by
X	the getty, including the terminal speed, the login prompt, and
X	the label for the entry to be used next should getty receives
X	a break. There is a default speed entry built into getty which
X	is used if this field is not specified.
X
X	`Type' describes the terminal type attached to the line. The
X	default is "none". This version of getty does not recognize
X	any terminal types other than "none".
X
X	`Linedisc' describes the line discipline to be used when
X	communicating with the terminal. The default is "LDISC0". This
X	version of getty does not recognize any line disciplines other
X	than "LDISC0".
X
X	The program options are:
X
X	-P When you are debugging a new terminal line, you may not
X	   want getty to exit immediately on errors. If there is an
X	   error in the invocation of getty, for example, init will
X	   respawn gettys as fast as they die. Init will eventually
X	   catch on, but why go through the hassle? If you specify the
X	   -P option, getty will pause just before exiting. It will
X	   also change its arguments so that a ps will show it as
X	   "badgetty". When paused, getty will have closed the
X	   terminal line. Once you have fixed the cause of the error,
X	   you can kill the getty which will cause it to exit cleanly.
X
X	-T This version of getty assumes that a succeeding open means
X	   that there is a user on the other end. Accordingly, the
X	   timeout, if specified, starts when the open returns. If the
X	   open succeeds immediately, regardless of the presence of a
X	   connection, you will want the timeout to start when the
X	   first character is recieved. The -T option does that.
X
X	-h Normally, getty hangs up the line immediately after the
X	   open succeeds. On certain lines, notably dial up lines
X	   where the open succeeds only when there is a carrier, this
X	   is a definite mistake. -h will prevent the hang up.
X
X	-s This causes the getty to display the system name right
X	   after it displays the /etc/issue file.
X
X	-l If this is specified, getty writes a record of everything
X	   that happens to the log file. Getty creates the log file
X	   with modes 660, you will want to make sure that it remains
X	   unreadable by anyone other than the super-user, as it may
X	   contain things like passwords entered at the wrong prompt.
X	   Getty tries to write each log message with one system call,
X	   so you should be able to use one log file for all gettys.
X	   Each line in the log file contains the terminal name,
X	   process id of the getty, the date, and a message.
X
X	   You will almost certainly want to truncate this file
X	   periodically.
X
X	-t This specifies the length of time that getty will wait for
X	   a complete login argument. If it doesn't get one by this
X	   time, it just exits. Getty starts its timer when the open
X	   succeeds unless the -T option is specified, in which case
X	   it starts it when the first character is read.
X
X	-i This specifies an alternate to the /etc/issue file, in case
X	   you don't want do use the standard one or there is no
X	   standard one and you want one for a specific line. Note
X	   that specifying a nonexistent file will simply result in no
X	   message being printed out. /dev/null is a good choice for
X	   an empty message.
X
X	Getty -c is used for checking out a definition file. If you
X	don't specify a file name, /etc/gettydefs is used. It is a
X	good idea to run getty -c on a just-edited definition file to
X	see if anything is amiss.
X
X	The initial sequence of operations performed by getty are:
X
X	1) Open the terminal line, set its owner, group, and modes.
X	2) Place accounting entries in /etc/utmp and /etc/wtmp.
X	3) Hang up the line unless -h was specified.
X	4) Start the timeout, unless -T was specified.
X
X	If getty can't get the specified speed entry, it gets the
X	first one from the /etc/gettydefs file. If it can't get to the
X	file, it uses a default that is built into the program. This
X	default is for a terminal at 2400 baud.
X
X	Then, for each speed entry that is tried, it does these steps:
X
X	1) Set terminal modes as specified in the /etc/gettydefs file.
X	   Note that most of these options are ignored. In particular,
X	   the fields in c_iflag, other than ICRNL and IGNPAR are
X	   ignored; not specifying a character size gets you eight
X	   bits; CREAD and HUPCL in c_cflag are always set; and
X	   c_lflag is cleared.
X
X	2) Write the message from /etc/issue and the system name, if
X	   requested.
X
X	3) Get the login arguments. If a break is read, get a new
X	   speed entry and try again. If -T was specified, start the
X	   timeout when the first character is received.
X
X	When the login arguments are read, the terminal modes are set
X	up as specified in the current /etc/gettydefs entry; however,
X	they may be modified by getty. If the input terminator is a
X	return, translation of returns to and from newlines is
X	enabled. If the login arguments contain at least one upper
X	case letter but no lower case letters, case translation is
X	enabled and the login arguments are made all lower case.
X
X	The arguments are then broken up into words and these become
X	the arguments to login. Words are separated by white space. If
X	you want to put white space into an argument, use one of the \
X	escapes described below.
X
X	Getty recognizes a number of special characters when reading
X	the login arguments. They are:
X
X	^H causes a backspace. The character is removed from the input
X	   buffer and a backspace-space-backspace is written to the
X	   terminal to remove the character from the display.
X
X	#  removes one character from the input buffer. There is no
X	   attempt to remove the character from the terminal display.
X
X	^D causes getty to exit.
X
X	Any of ^C, ^U, DEL, ^|, or @ causes the current input to be
X	   discarded. The user is prompted again with the login prompt
X	   but not the /etc/issue message.
X
X	^J and ^M terminate the input.
X
X	\ is an escape character. A NUL still causes a speed change;
X	  however, other characters' interpretations are changed. The
X	  standard C escape sequences do as expected. \E translates to
X	  an escape character. \s is translated to a space; however,
X	  unlike a space, it does not separate login arguments.
X
X	Control characters are not echoed as control characters, but
X	as either ^letter or \octal.
X
X	If the input buffer overflows, getty will exit. This buffer
X	can hold 255 characters.
X
XINSTALLATION
X
X	The following installation instructions are for relatively
X	standard systems; if yours isn't, you'll have to use your own
X	judgement. The main thing left out is what you do if you have
X	put things in an unusual places or want to do unusual things
X	with the getty. It is also assumed that you are replacing an
X	existing getty, and so there are no instructions on how to set
X	up a terminal, etc.
X
X	The steps are:
X
X	1) Edit the getty source to meet your requirements.
X	2) Edit the Makefile.
X	3) Run `make' to compile the getty.
X	4) Run `make install' to put it where it belongs.
X	5) Update /etc/inittab.
X	6) Update /etc/gettydefs.
X
X	There are a number of changes you might want to make in the
X	getty source; these are summarized below:
X
X	BREAK_DELAY     (40 ms)
X
X	Many systems respond poorly when receiving a break; they
X	generate lots of garbage and normal terminal flushing doesn't
X	get rid of it. To deal with that, the getty flushes the buffer
X	itself, by reading characters from the terminal till none
X	appear after a certain delay. As configured, the program waits
X	40ms after the last character received before concluding that
X	the break garbage has gone away. If your system translates a
X	break into exactly one character, you might want to make this
X	10ms; if your system is particularly noisy, you might want to
X	make it longer. This parameter is specified in units of 10
X	milliseconds.
X
X	CONSOLE         (/dev/console)
X
X	Error message go to this device; your console may have a
X	different name.
X
X	DFLT_SPEED      (2400)
X
X	If you want a different default speed in the getty, change
X	this. Along with this change, you might want to change the
X	default gettydefs entry or the definition of SANE; the first
X	is controlled by the Default_def variable, the second by the
X	MODE_[IOCL]SANE variables.
X
X	LOGCMD          (/bin/login)
X
X	This is the file containing the program that getty is to run
X	when it has read the login arguments. Yours may be in
X	/etc/login.
X
X	The one change you might want to make to the Makefile is to
X	specify where the getty should be installed. Note that many
X	systems have an init that does strange things with getty; that
X	means that you can't put the getty in a directory which has
X	`getty' as a component, nor can you use a name other than
X	`getty' for it. The macro to change is GETTYBIN.
X
X	To compile the getty, type `make'. To install it, become root,
X	and type `make install'.
X
X	You may want to modify your /etc/inittab or whatever your init
X	uses to control the programs it runs. What you'd want to
X	change is the command that starts the getty. In particular,
X	you may want to add a -T option, to specify timeout after the
X	first character read; -s, to display the system name; or -l
X	<filename>, to enable getty logging.
X
X	Finally, you may want to edit your existing /etc/gettydefs
X	file, or you may need to create one if your system didn't use
X	one before. If you need to create one, you will have to hunt
X	up the gettydefs manual page. If you want to use your old
X	gettydefs, odds are it will work fine. The one thing that
X	might break is the interpretation of SANE: the one in getty is
X	unlikely to be identical with yours.
X
XFILES
X
X	/bin/login      This is called when the arguments have been read.
X	/dev            The terminal file is found in this directory.
X	/dev/console    Error messages are written here.
X	/etc/gettydefs  The getty parameters are read from here.
X	/etc/issue      This is the initial connection message.
X	/etc/utmp       The entry in this file for the terminal is updated.
X	/etc/wtmp       An accounting record is appended to this file.
X
X	The /etc/gettydefs is interpreted somewhat differently than is
X	described in gettydefs(4). In particular, all the backslash
X	escapes described for input work in it. Also, !<mode> is
X	recognized to turn off modes; this is useful to get SANE minus
X	some particular mode.
X
XSEE ALSO
X
X	gettydefs(4) init(1) ioctl(2) login(1) termio(7) utmp(4)
X
XBUGS
X
X	None known. But if you do find any, let me, T. William Wells,
X	bill@twwells.com, know.
*-*-END-of-getty.8-*-*
echo x - Makefile
sed 's/^X//' >Makefile <<'*-*-END-of-Makefile-*-*'
XGETTYBIN = /etc/bin
XGETTY    = $(GETTYBIN)/getty
XOGETTY   = $(GETTYBIN)/getty.old
XCC       = gcc
XCFLAGS   = -O
XLDCC     = cc
XLFLAGS   =
XLDLIB    = -lc_s
X
X# Builds getty.
X
Xgetty : getty.o
X	$(LDCC) $(LFLAGS) getty.o $(LDLIB)
X	mv a.out $@
X
X# Installs getty. Makes a copy of the old one, if there is no copy already
X# made. You may want to get rid of the copy after all is going well.
X
Xinstall :
X	if [ ! -r $(OGETTY) ]; then mv $(GETTY) $(OGETTY); fi
X	cp getty $(GETTY)
X	strip $(GETTY)
X	chown root $(GETTY)
X	chgrp root $(GETTY)
X	chmod 770 $(GETTY)
X
X# Lints getty.
X
Xlint.out : getty
X	lint getty.c >lint.out 2>&1
X
X# Makes a shar for distribution of the getty.
X
XGETTYSHAR = README getty.8 Makefile getty.c
Xgetty.shar : $(GETTYSHAR)
X	shar $(GETTYSHAR) >shar.tmp
X	mv shar.tmp $@
X
X# Deletes unneeded files in the getty source directory.
X
Xclean :
X	rm -f *.out *.o getty
*-*-END-of-Makefile-*-*
echo x - getty.c
sed 's/^X//' >getty.c <<'*-*-END-of-getty.c-*-*'
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <sys/utsname.h>
X#include <ctype.h>
X#include <errno.h>
X#include <fcntl.h>
X#include <signal.h>
X#include <stdio.h>
X#include <string.h>
X#include <termio.h>
X#include <time.h>
X#include <utmp.h>
X
X/* Copyright 1989 By T. William Wells. All Rights Reserved.
X
X   You may distribute this code and its derivatives so long as you do not
X   charge for them; you may charge a fee sufficient to cover distribution
X   costs. You may not include it in a product to be sold. You may not use it
X   as an inducement to buy.
X
X   You may not modify this copyright notice; you may only add to the
X   modification log that follows this copyright notice.
X
X   You may modify the code. If you modify the code and distribute it, you must
X   update the modification log to say who you are and when you made the
X   change. */
X
X/* Send bug reports to T. William Wells, bill@twwells.com.
X
X   Modification log:
X
X   Version 0.1, written by T. William Wells, released March 19, 1989.
X   Version 0.2, modified by T. William Wells, released June 25, 1989.
X	Problems fixed:
X		Some comments with problems were improved.
X		The echo of ^<letter> was screwed up because another buffer
X			was too short; a null overwrote the '^'.
X		Breaks were not handled correctly all the time, causing the
X			program to rapidly cycle through speeds.
X		When signals were received, sometimes the log messages were
X			not displayed with their time stamps.
X		The log file remained open after the exec.
X		The B19200 and B38400 labels aren't defined for all systems,
X			requiring the references to them to be #ifdef'ed.
X			(Jay Maynard <splut!jay>)
X		The setpgrp that used to be there was wrong; it made getty
X			not work if getty were called from a front end that
X			had already opened the terminals.  (Marc Boucher
X			<marc@clik.qc.ca>)
X
X	Changes:
X		\E can be used to input the escape character; \E is also
X			displayed for the escape.
X		\s can be used to input the space character; \s is also
X			displayed for a space.
X		IGNPAR is no longer ignored as one of the initial modes.
X		Added a -s option to display the system name. (Jay Maynard
X			<splut!jay>)
X		SANE is saner: includes TAB3.
X		Added ! to gettydefs; turns off a mode.
X   Version 1.0, modified by T. William Wells, released January 1, 1990.
X	Changes:
X		Option -i can be used to specify an alternate to the
X		/etc/issue file.
X*/
X
X/* getty - the program to handle initial terminal connections to the system.
X   It sets up the speed, terminal flags, and line discipline. It then waits
X   for some arguments and calls login with them.
X
X   getty [-P] [-T] [-h] [-s] [-l logfile] [-t timeout] [-i issue_file]
X       line [speed [type [linedisc]]]
X   getty -c [file]
X
X   -P When error exiting, set our name to badgetty, close the line, and pause.
X      One makes the getty exit by sending it a signal.
X
X   -T Do the timeout from after the first character read. This is appropriate
X      for lines where the open does not wait for a carrier.
X
X   -h Don't hang up the line when initializing it.
X
X   -l Report getty behavior to the log file.
X
X   -s Display the system name.
X
X   -t If timeout seconds elapse with no input, hang up the line. Timeout is
X      normally from when the open returns, but the -T option changes that.
X
X   -i Specify alternate to /etc/issue.
X
X   Line is the device name as found in /dev.
X
X   Speed is the label of the entry in the definition file which is to be used
X      to initialize the line.
X
X   Type is the terminal type name.
X
X   Linedisc is the line discipline name.
X
X   -c Check the specified file for validity. An interpretation of the file is
X      printed so that it can be checked for correctness. */
X
Xextern unsigned alarm();
Xextern void     endutent();
Xextern void     exit();
Xextern struct utmp *getutent();
Xextern unsigned short getegid();
Xextern unsigned short geteuid();
Xextern char     *optarg;
Xextern int      opterr;
Xextern int      optind;
Xextern struct utmp *pututline();
Xextern char     *sys_errlist[];
Xextern int      sys_nerr;
Xextern time_t   time();
X
X#define BAD_NAME   "badgetty"   /* name shown when getty hangs due to error */
X#define BREAK_DELAY 4           /* VTIME for skipping break garbage */
X#define CONSOLE    "/dev/console" /* msgs go here when parent is init */
X#define DFLT_SPEED B2400        /* speed for default entry */
X#define ESC_CHAR   0x1B         /* character for \E escape */
X#define GETTYDEFS  "/etc/gettydefs" /* default definition file */
X#define INTR_CHAR  (0x1F&'C')   /* other interrupt character */
X#define ISSUE      "/etc/issue" /* file containing signon message */
X#define KILL_CHAR  (0x1F&'U')   /* other line kill character */
X#define LINE_LEN   256          /* length of line buffers, etc. */
X#define LOGIN_ARGV0 "login"     /* command name of login */
X#define LOGIN_CMD  "/bin/login" /* command to execute to do login */
X#define LOG_MODE   0660         /* file modes for log file */
X#define MAX_ARGS   32           /* max args to be passed to login */
X#define TTY_MODE   0660         /* terminal mode */
X
Xtypedef unsigned char UCHAR;
X
X/* These are the standard UNIX fd's; here defined so that they can be found
X   with no difficulty. */
X
X#define TTY_IN  0               /* fd for reading the terminal */
X#define TTY_OUT 1               /* fd for writing the terminal */
X#define TTY_ERR 2               /* fd for error writes to the terminal */
X
X#define LOG_FD  10              /* fd to assign to the log file */
X
X/* A kludge because of the missing LDISC0 in some system includes. */
X
X#ifndef LDISC0
X#define LDISC0  0
X#endif
X
Xtypedef struct {                /* contains one speed entry */
X	char    td_label[16];   /* label for this entry */
X	struct termio td_initial; /* initial terminal flags */
X	struct termio td_final; /* final terminal flags */
X	int     td_msglen;      /* length of msg to prompt with */
X	char    td_logmsg[80];  /* message to prompt with */
X	char    td_nxtlbl[16];  /* next label to try */
X} TTYDEF;
X
Xtypedef struct {                /* used to contain table of word -> value */
X	char    *w_text;        /* input word */
X	unsigned w_value;       /* value for it */
X} WORD;
X
X/* These are the modes that are used when SANE is specified. */
X
X#define MODE_ISANE   (BRKINT | IGNPAR | ISTRIP | ICRNL | IXON)
X#define MODE_OSANE   (OPOST | ONLCR | TAB3)
X#define MODE_CSANE   (CS8 | CREAD | HUPCL)
X#define MODE_LSANE   (ISIG | ICANON | ECHO | ECHOE | ECHOK)
X
X/* This is the default terminal entry; it is used if none is available from
X   the definition file. */
X
XTTYDEF Default_def = {
X	"default",              /* fake speed name */
X
X	/* initial modes */
X
X	ICRNL,          0,              DFLT_SPEED | MODE_CSANE,
X	0,              LDISC0,
X	{ CINTR, CQUIT, CERASE, CKILL, CEOF },
X
X	/* final modes */
X
X	MODE_ISANE,     MODE_OSANE,     DFLT_SPEED | MODE_CSANE,
X	MODE_LSANE,     LDISC0,
X	{ CINTR, CQUIT, CERASE, CKILL, CEOF },
X
X	sizeof("LOGIN: ") - 1, "LOGIN: ", /* prompt */
X	"default",              /* next speed entry */
X};
X
X/* These tables define the input words for setting modes. */
X
XWORD W_imodes[] = {
X    "IGNBRK", IGNBRK, "BRKINT", BRKINT, "IGNPAR", IGNPAR, "PARMRK", PARMRK,
X    "INPCK",  INPCK,  "ISTRIP", ISTRIP, "INLCR",  INLCR,  "IGNCR",  IGNCR,
X    "ICRNL",  ICRNL,  "IUCLC",  IUCLC,  "IXON",   IXON,   "IXANY",  IXANY,
X    "IXOFF",  IXOFF,
X0, 0};
X
XWORD W_omodes[] = {
X    "OPOST",  OPOST,  "OLCUC",  OLCUC,  "ONLCR",  ONLCR,  "OCRNL",  OCRNL,
X    "ONOCR",  ONOCR,  "ONLRET", ONLRET, "OFILL",  OFILL,  "OFDEL",  OFDEL,
X    "NL0",    NL0,    "NL1",    NL1,    "CR0",    CR0,    "CR1",    CR1,
X    "CR2",    CR2,    "CR3",    CR3,    "TAB0",   TAB0,   "TAB1",   TAB1,
X    "TAB2",   TAB2,   "TAB3",   TAB3,   "BS0",    BS0,    "BS1",    BS1,
X    "VT0",    VT0,    "VT1",    VT1,    "FF0",    FF0,    "FF1",    FF1,
X0, 0};
X
XWORD W_cmodes[] = {
X    "B0",     B0,     "B50",    B50,    "B75",    B75,    "B110",   B110,
X    "B134",   B134,   "B150",   B150,   "B200",   B200,   "B300",   B300,
X    "B600",   B600,   "B1200",  B1200,  "B1800",  B1800,  "B2400",  B2400,
X    "B4800",  B4800,  "B9600",  B9600,
X#ifdef B19200
X    "B19200", B19200,
X#endif
X#ifdef B38400
X    "B38400", B38400,
X#endif
X    "EXTA",   EXTA,   "EXTB",   EXTB,   "CS6",    CS6,    "CS7",    CS7,
X    "CS8",    CS8,    "CSTOPB", CSTOPB, "CREAD",  CREAD,  "PARENB", PARENB,
X    "PARODD", PARODD, "HUPCL",  HUPCL,  "CLOCAL", CLOCAL,
X0, 0};
X
XWORD W_lmodes[] = {
X    "ISIG",   ISIG,   "ICANON", ICANON, "XCASE",  XCASE,  "ECHO",   ECHO,
X    "ECHOE",  ECHOE,  "ECHOK",  ECHOK,  "ECHONL", ECHONL, "NOFLSH", NOFLSH,
X0, 0};
X
X/* These are the terminal types recognized by the program. */
X
XWORD W_terminals[] = {
X    "none", TERM_NONE,
X0, 0};
X
X/* These are the line disciplines recognized by the program. */
X
XWORD W_linedisc[] = {
X    "LDISC0", LDISC0,
X0, 0};
X
X/* Global variables. */
X
Xchar    **Argv;                 /* save for argv; used to change name in ps */
Xint     Check_definitions;      /* -c option specified */
Xchar    *Definition_file;       /* definition file to read from */
Xint     Hang_up_on_init;        /* do hang up after terminal opened, ! -h */
Xchar    *Issue_file;            /* file with message to print at connect */
Xint     Line_discipline;        /* line discipline from the command line */
XFILE    *Log_file;              /* file to log to, null if none */
Xint     Our_pid;                /* result of getpid() */
Xint     Parent_is_init;         /* set if getppid() returns 1 */
Xint     Pause_on_exit;          /* do a pause on an error exit */
Xchar    *Program_name;          /* base name of argv[0] */
Xint     Save_signal;            /* used for signal fiddling during logging */
Xint     Show_sysname;           /* -s: display the system name */
Xstruct utsname Sys_name;        /* name of our system */
Xint     Terminal_type;          /* terminal type from command line */
Xunsigned Timeout_length;        /* from command line, zero if no timeout */
Xint     Tmout_after_first;      /* -T: start timeout after first char read */
XTTYDEF  Tty_def;                /* current definition entry */
Xchar    *Tty_line;              /* terminal device name, from the cmd line */
X
X/* Forward referenced functions. */
X
Xvoid    exit_on_signal();
Xint     find_tty_def();
Xvoid    flush_input();
Xvoid    init_at_speed();
Xvoid    init_terminal();
Xvoid    parse_arguments();
Xvoid    read_login_args();
X
Xint
Xmain(argc, argv)
Xint     argc;
Xchar    **argv;
X{
X	char    next_label[LINE_LEN];
X
X	/* Random initialization. */
X
X	Our_pid = getpid();
X	Parent_is_init = getppid() == 1;
X	Argv = argv;
X	uname(&Sys_name);
X
X	/* Parse our arguments; the results go in globals. This does not
X	   return if there is an argument error or if the -c option was
X	   specified. */
X
X	parse_arguments(argc, argv);
X
X	/* Initialize the terminal. Then cycle through the speed entries till
X	   a successful read of the arguments occurs or an error occurs. */
X
X	init_terminal();
X	while (1) {
X		init_at_speed();
X		read_login_args();
X		flush_input();
X		strcpy(next_label, Tty_def.td_nxtlbl);
X		(void)find_tty_def(next_label);
X	}
X}
X
X/* This gets called after logging is done to print deferred a signal log
X   message. */
X
Xvoid
Xlog_signal()
X{
X	int     sig;            /* signal that was deferred */
X
X	sig = Save_signal;
X	Save_signal = 0;
X	if (sig > 0) {
X		exit_on_signal(sig);
X	}
X}
X
X/* Write to the log file. This routine prepends to each line in the log file
X   the terminal name, our pid, and a time stamp. */
X
Xvoid
Xlog_fputs(s)
Xchar    *s;                     /* string to write to the log file */
X{
X	char    *ptr;           /* pointer into the string */
X	time_t  clock;          /* current time */
X	static int begin = 1;   /* set to write the start of a line */
X
X	if (!Log_file) {
X		return;
X	}
X	Save_signal = -1;
X	while (*s) {
X		if (begin) {
X			if (*s == '\n') {
X				++s;
X				continue;
X			}
X			clock = time((long *)0);
X			if (Tty_line) {
X				(void)fprintf(Log_file, "%s", Tty_line);
X			} else {
X				(void)fprintf(Log_file, "<none>");
X			}
X			(void)fprintf(Log_file, " %d %.24s: ",
X			    Our_pid, ctime(&clock));
X			begin = 0;
X		}
X		if (!(ptr = strchr(s, '\n'))) {
X			(void)fputs(s, Log_file);
X			break;
X		}
X		++ptr;
X		(void)fwrite(s, 1, ptr - s, Log_file);
X		(void)fflush(Log_file);
X		begin = 1;
X		s = ptr;
X	}
X	log_signal();
X}
X
X/* This is called when a timeout or other signal occurs. Unfortunately, a
X   signal can occur while logging. To deal with that, we don't really exit
X   when that occurs. Instead, the logger sets a variable which will be
X   interpreted by the signal handler to mean to defer the signal till after
X   the current output is done. There are lots of complex reasons why this must
X   be done; consider, for example, nonreentrancy of stdio calls. Anyway, we
X   don't want a recursive call to log_fputs and Save_signal is used both to
X   prevent this and to record the last signal that occured. Of course, this
X   might cause a signal to be not logged if two of them occured one right
X   after the other, but that can't be solved without major headaches. Note
X   the newline at the start of the exit message: that terminates the log
X   buffer if there was anything in it; otherwise, it does nothing. */
X
Xvoid
Xexit_on_signal(sig)
Xint     sig;                    /* signal number that was caught */
X{
X	char    buf[LINE_LEN];
X
X	if (Save_signal) {
X		Save_signal = sig;
X		return;
X	}
X	sprintf(buf, "\nexiting on signal %d\n", sig);
X	log_fputs(buf);
X	exit(1);
X}
X
X/* Start the terminal timeout alarm. */
X
Xvoid
Xstart_timeout()
X{
X	if (Timeout_length) {
X		(void)signal(SIGALRM, exit_on_signal);
X		(void)alarm(Timeout_length);
X		Timeout_length = 0;
X	}
X}
X
X/* Append text to the console output buffer. When this routine is called with
X   a null pointer, flush that buffer. The hope here is that writing the
X   message with one system call will get it out as one message. The message
X   will also be written to the log file. */
X
Xvoid
Xadd_to_msg(msg)
Xchar    *msg;                   /* string to add to error message */
X{
X	register int    tmp;
X	static int      pos;    /* index into msgbuf */
X	static char     msgbuf[LINE_LEN]; /* message buffer */
X
X	if (!msg) {
X		if (!pos) {
X			return;
X		}
X		msgbuf[pos++] = '\n';
X		msgbuf[pos] = 0;
X		if (Parent_is_init) {
X			if ((tmp = open(CONSOLE, O_WRONLY)) >= 0) {
X				(void)write(tmp, msgbuf, (unsigned)pos);
X				(void)close(tmp);
X			}
X		} else {
X			(void)write(TTY_ERR, msgbuf, (unsigned)pos);
X		}
X		log_fputs(msgbuf);
X		pos = 0;
X		return;
X	}
X	tmp = strlen(msg);
X	if (tmp + pos >= sizeof(msgbuf) - 1) {
X		tmp = sizeof(msgbuf) - 2 - pos;
X	}
X	if (tmp) {
X		strncpy(msgbuf + pos, msg, tmp);
X		pos += tmp;
X	}
X}
X
X/* Print an error message. The error message has two text sections. */
X
Xvoid
Xerror2(m1, m2)
Xchar    *m1;                    /* part one of the message */
Xchar    *m2;                    /* part two of the message */
X{
X	add_to_msg(Program_name);
X	add_to_msg(" on ");
X	add_to_msg(Tty_line ? Tty_line : "<none>");
X	add_to_msg(": ");
X	add_to_msg(m1);
X	add_to_msg(m2);
X	add_to_msg(".");
X	add_to_msg((char *)0);
X}
X
X/* Print an error message. The error message has one text section. */
X
Xvoid
Xerror1(msg)
Xchar    *msg;                   /* text to print */
X{
X	error2(msg, "");
X}
X
X/* Print an error message for a failed system function. This requires that
X   errno be set correctly. The text string provided as input goes in text
X   like: "when (message) got error: (error message)". */
X
Xvoid
Xsys_emsg(msg)
Xchar    *msg;                   /* text to insert into the message */
X{
X	char    buf[LINE_LEN];
X
X	if (errno < sys_nerr) {
X		sprintf(buf, "when %s got error: ", msg);
X		error2(buf, sys_errlist[errno]);
X	} else {
X		sprintf(buf, "when %s got error number %d", msg, errno);
X		error1(buf);
X	}
X}
X
X/* Print a string so that it is unambiguous when read and contains no control
X   characters. All nonprinting characters are displayed as some backslash
X   sequence, similar to the C escapes. Space is also printed this way. */
X
Xvoid
Xqprint(fp, buf, len)
Xregister FILE  *fp;             /* file to write to */
Xregister UCHAR *buf;            /* buffer to write from */
Xint     len;                    /* number of characters in tbe buffer */
X{
X	int     cc;             /* character from the write buffer */
X	UCHAR   *ebuf = buf + len; /* pointer to the end of the write buffer*/
X
X	for ( ; buf < ebuf; ) {
X		switch (cc = *buf++) {
X		case '\\':     (void)fputs("\\\\",fp); break;
X		case '\n':     (void)fputs("\\n", fp); break;
X		case '\t':     (void)fputs("\\t", fp); break;
X		case '\v':     (void)fputs("\\v", fp); break;
X		case '\b':     (void)fputs("\\b", fp); break;
X		case '\r':     (void)fputs("\\r", fp); break;
X		case '\f':     (void)fputs("\\f", fp); break;
X		case ESC_CHAR: (void)fputs("\\E", fp); break;
X		case ' ':      (void)fputs("\\s", fp); break;
X		default:
X			if (isprint(cc)) {
X				(void)putc(cc, fp);
X			} else {
X				(void)fprintf(fp, "\\%03o", cc);
X			}
X			break;
X		}
X	}
X}
X
X/* Print a string to the log file using qprint. This eliminates control
X   characters and makes each space explicit.  */
X
Xvoid
Xlog_qprint(msg, buf, len)
Xchar    *msg;                   /* text to print before the buffer */
XUCHAR   *buf;                   /* buffer to print */
Xint     len;                    /* number of characters in the buffer */
X{
X	if (!Log_file) {
X		return;
X	}
X	log_fputs(msg);
X	Save_signal = -1;
X	qprint(Log_file, buf, len);
X	log_signal();
X	log_fputs("\n");
X}
X
X/* If our parent is init, getty should not ever error exit; it should exec
X   another program or exit in order to have a new getty. However, when an
X   unrecoverable error occurs, something has to be done. What we'll do is set
X   the argument vector so that a ps will show the getty to have failed. Then
X   pause, and if an interrupt is received, exit. */
X
Xvoid
Xdo_exit(n)
Xint     n;                      /* exit code to return to caller */
X{
X	add_to_msg((char *)0);
X	log_fputs("exiting\n");
X	if (Pause_on_exit) {
X		(void)alarm(0);
X		strcpy(Argv[0], BAD_NAME);
X		(void)signal(SIGHUP, SIG_IGN);
X		(void)close(TTY_IN);
X		(void)close(TTY_OUT);
X		(void)close(TTY_ERR);
X		(void)pause();
X	}
X	exit(n);
X}
X
X/* Duplicates an fd. Checks for some errors, as unlikely as they may be. */
X
Xvoid
Xmy_dup2(fd, newfd)
Xint     fd;                     /* fd to duplicate */
Xint     newfd;                  /* fd it is supposed to become */
X{
X	int     ret;
X
X	if (close(newfd) < 0 && errno != EBADF) {
X		sys_emsg("closing an fd");
X		do_exit(1);
X	}
X	if ((ret = fcntl(fd, F_DUPFD, newfd)) != newfd) {
X		if (ret < 0) {
X			sys_emsg("duping an fd");
X		} else {
X			error1("dup didn't return expected fd");
X		}
X		do_exit(1);
X	}
X}
X
X/* An ugly: as init doesn't open any fd's for us, a regular open isn't going
X   to work because it will open one of the fd's we would use for the
X   terminal. All this BS exists so that the fd ends up not being one of the
X   first three. */
X
Xvoid
Xlog_open(name)
Xchar    *name;                  /* name of file to log to. */
X{
X	int     fd;
X
X	if ((fd = open(name, O_WRONLY | O_APPEND | O_CREAT, LOG_MODE)) < 0) {
X		sys_emsg("opening log file");
X		do_exit(1);
X	}
X	if (fd != LOG_FD) {
X		my_dup2(fd, LOG_FD);
X		(void)close(fd);
X		if (!(Log_file = fdopen(LOG_FD, "a"))) {
X			sys_emsg("fdopening log file");
X			do_exit(1);
X		}
X	}
X	if (fcntl(LOG_FD, F_SETFD, 1) == -1) {
X		sys_emsg("setting close on exec");
X		do_exit(1);
X	}
X}
X
X/* Write text to the terminal. This logs the text, writes it to the terminal,
X   and checks for errors. */
X
Xvoid
Xtty_write(buf, len)
Xchar    *buf;                   /* text to write */
Xint     len;                    /* number of characters to write */
X{
X	int     ret;            /* return value from the write */
X	char    errbuf[LINE_LEN]; /* buffer for generating error message */
X
X	if (!len) {
X		return;
X	}
X	if (len < 0) {
X		len = strlen(buf);
X	}
X	log_qprint("write: ", (UCHAR *)buf, len);
X	if ((ret = write(TTY_OUT, buf, (unsigned)len)) < 0) {
X		sys_emsg("writing the terminal");
X		do_exit(1);
X	}
X	if (ret != len) {
X		sprintf(errbuf, "%d bytes weren't written", len - ret);
X		error1(errbuf);
X		do_exit(1);
X	}
X}
X
X/* Do an ioctl to the terminal. Make sure that it works. */
X
Xvoid
Xtty_ioctl(cmd, termio)
Xint     cmd;                    /* command to perform */
Xstruct termio *termio;          /* termio structure to use */
X{
X	if (ioctl(TTY_IN, cmd, termio) < 0) {
X		sys_emsg("doing ioctl for the terminal");
X		do_exit(1);
X	}
X}
X
X/* Scan through a keyword table and return the a pointer to the keyword entry.
X   If there is no matching entry, return null. */
X
XWORD *
Xfind_word(word, table)
Xregister char *word;            /* word to look for */
Xregister WORD *table;           /* table to look into */
X{
X	for ( ; table->w_text; ++table) {
X		if (strcmp(word, table->w_text) == 0) {
X			return (table);
X		}
X	}
X	return (0);
X}
X
X/* Print a usage message and then exit. */
X
Xvoid
Xusage(msg)
Xchar    *msg;                   /* text to insert into the usage message */
X{
X	/* This funny logging is done to prevent the entire usage message from
X	   being written to the log file. */
X
X	log_fputs("usage error\n");
X	log_fputs("exiting\n");
X	Log_file = 0;
X
X	/* Print the usage message and exit. */
X
X	add_to_msg(Program_name);
X	add_to_msg(": ");
X	add_to_msg(msg);
X	add_to_msg("\nusage: ");
X	add_to_msg(Program_name);
X	add_to_msg(" [-P] [-T] [-h] [-l logfile] [-t timeout]\n");
X	add_to_msg((char *)0);
X	add_to_msg("           line [speed [type [linedisc]]]\n");
X	add_to_msg((char *)0);
X	add_to_msg("       ");
X	add_to_msg(Program_name);
X	add_to_msg(" -c [file]");
X	add_to_msg((char *)0);
X	do_exit(1);
X}
X
X/* Parse our arguments. */
X
Xvoid
Xparse_arguments(argc, argv)
Xint     argc;
Xregister char **argv;
X{
X	int     nocheck;        /* flag was set that isn't legal with -c */
X	register WORD *wp;      /* return value from find_word */
X	char    *ptr;           /* pointer into argument */
X
X	/* Record the base part of the path name. */
X
X	if (Program_name = strrchr(argv[0], '/')) {
X		++Program_name;
X	} else {
X		Program_name = argv[0];
X	}
X	/* Interpret the options. */
X
X	nocheck = 0;
X	opterr = 0;
X	Definition_file = GETTYDEFS;
X	Hang_up_on_init = 1;
X	Issue_file = ISSUE;
X	while (1) {
X		switch (getopt(argc, argv, "PTchsi:l:t:")) {
X		default:  usage("illegal option");
X		case -1:  break;
X		case 'P': /* Pause on exit. */
X			Pause_on_exit = 1;
X			nocheck = 1;
X			continue;
X		case 'T': /* Start timeout after first character read. */
X			Tmout_after_first = 1;
X			nocheck = 1;
X			continue;
X		case 'c': /* Check definition file. */
X			Check_definitions = 1;
X			continue;
X		case 'h': /* Don't hang up on init. */
X			Hang_up_on_init = 0;
X			nocheck = 1;
X			continue;
X		case 'l': /* Write messages to log file. */
X			log_open(optarg);
X			nocheck = 1;
X			continue;
X		case 's': /* Show the system name. */
X			Show_sysname = 1;
X			nocheck = 1;
X			continue;
X		case 't': /* Time out after specified interval. */
X			for (ptr = optarg; *ptr; ) {
X				if (!isdigit(*ptr)) {
X					usage("illegal digit");
X				}
X				Timeout_length *= 10;
X				Timeout_length += *ptr++ - '0';
X			}
X			nocheck = 1;
X			continue;
X		case 'i': /* Specify alternate to /etc/issue. */
X			Issue_file = optarg;
X			nocheck = 1;
X			continue;
X		}
X		break;
X	}
X	if (Check_definitions && nocheck) {
X		usage("improper combination of options");
X	}
X	/* If the -c option was specified, call the find_tty_def routine to
X	   parse the entire file and print it out. */
X
X	if (Check_definitions) {
X		if (optind < argc) {
X			Definition_file = argv[optind++];
X		}
X		if (optind != argc) {
X			usage("too many arguments");
X		}
X		if (Parent_is_init) {
X			error1("-c is not legal when called from init");
X			do_exit(1);
X		}
X		(void)find_tty_def((char *)0);
X		do_exit(0);
X	}
X	if (optind == argc) {
X		usage("not enough arguments");
X	}
X	/* Save the terminal line name */
X
X	Tty_line = argv[optind++];
X
X	/* If the speed was provided on the command line, get the entry from
X	   the definition file for that speed. */
X
X	Tty_def = Default_def;
X	if (optind < argc) {
X		if (!find_tty_def(argv[optind])) {
X			(void)find_tty_def((char *)0);
X		}
X		++optind;
X	}
X	/* Get the terminal type, if specified. */
X
X	Terminal_type = TERM_NONE;
X	if (optind < argc) {
X		if (!(wp = find_word(argv[optind], W_terminals))) {
X			error2("can't find terminal type ", argv[optind]);
X		} else {
X			Terminal_type = wp->w_value;
X		}
X		++optind;
X	}
X	/* Get the line discipline. */
X
X	Line_discipline = LDISC0;
X	if (optind < argc) {
X		if (!(wp = find_word(argv[optind], W_linedisc))) {
X			error2("can't find line discipline ", argv[optind]);
X		} else {
X			Line_discipline = wp->w_value;
X		}
X		++optind;
X	}
X	/* One final check: are all arguments used up? */
X
X	if (optind != argc) {
X		usage("too many arguments");
X	}
X}
X
X/* Interpret a quoted character. It returns a pointer to the character right
X   after the quoted character. Note that escaped nuls don't work; they are
X   treated as an end of input. Why? Because it would have been too much
X   trouble to distinguish an escaped nul from a newline at the end of the
X   input buffer. */
X
Xchar *
Xqchar(ptr, rc)
Xregister char *ptr;             /* pointer after the backslash */
Xregister UCHAR *rc;             /* return the character value through here */
X{
X	register int cc;        /* character from the input */
X
X	/* Deal with the alpha escapes and backslash null. */
X
X	switch (cc = *ptr++) {
X	case 'n': *rc = '\n';     return (ptr);
X	case 't': *rc = '\t';     return (ptr);
X	case 'v': *rc = '\v';     return (ptr);
X	case 'b': *rc = '\b';     return (ptr);
X	case 'r': *rc = '\r';     return (ptr);
X	case 'f': *rc = '\f';     return (ptr);
X	case 'E': *rc = ESC_CHAR; return (ptr);
X	case 's': *rc = ' ';      return (ptr);
X	case 0:   *rc = 0;        return (ptr - 1);
X	}
X	/* If nondigit, the quote does nothing. */
X
X	if (cc < '0' || cc > '7') {
X		*rc = cc;
X		return (ptr);
X	}
X	/* Interpret 1-3 digits. */
X
X	*rc = cc - '0';
X	cc = *ptr;
X	if (cc < '0' || cc > '7') {
X		return (ptr);
X	}
X	*rc <<= 3;
X	*rc += cc - '0';
X	cc = *++ptr;
X	if (cc < '0' || cc > '7') {
X		return (ptr);
X	}
X	*rc <<= 3;
X	*rc += cc - '0';
X	return (ptr + 1);
X}
X
X/* Extract a word from some text. It returns a pointer to the next word or the
X   end of the input. A failure to extract the word is signaled by returning
X   null. */
X
Xchar *
Xextract_word(ptr, obuf, size)
Xregister char *ptr;             /* buffer to extract from */
Xchar    *obuf;                  /* output buffer */
Xint     size;                   /* size of output buffer */
X{
X	register int cc;        /* character from the input */
X	UCHAR   qcc;            /* non-register char, for qchar */
X
X	/* Find the start of the word. */
X
X	do {
X		if (!(cc = *ptr++) || cc == '#') {
X			return (0);
X		}
X	} while (isspace(cc));
X
X	/* Move characters to the output buffer. */
X
X	--size;
X	while (cc && !isspace(cc) && cc != '#') {
X		if (cc == '\\') {
X			ptr = qchar(ptr, &qcc);
X			cc = qcc;
X		}
X		if (size) {
X			*obuf++ = cc;
X			--size;
X		}
X		cc = *ptr++;
X	}
X	/* Terminate the buffer and return a pointer to the next word in the
X	   input buffer. */
X
X	*obuf = 0;
X	--ptr;
X	while (isspace(*ptr)) {
X		++ptr;
X	}
X	return (ptr);
X}
X
X/* Extract the termio specifications from a definition line. Return the end
X   of the definition or null on error. */
X
Xchar *
Xget_tty_modes(ptr, termio)
Xchar    *ptr;                   /* definition text */
Xregister struct termio *termio; /* termio structure to initialize */
X{
X	WORD    *wp;            /* return value from find_word */
X	char    word[LINE_LEN]; /* buffer for extracting words */
X
X	/* Clear the termio structure. */
X
X	termio->c_iflag = 0;
X	termio->c_oflag = 0;
X	termio->c_cflag = 0;
X	termio->c_lflag = 0;
X
X	/* Extract the definition words and store the results in the termio
X	   structure. */
X
X	while (isspace(*ptr)) {
X		++ptr;
X	}
X	while (*ptr != '#') {
X		if (!(ptr = extract_word(ptr, word, sizeof(word)))) {
X			return (0);
X		}
X		if (strcmp(word, "SANE") == 0) {
X			termio->c_iflag |= MODE_ISANE;
X			termio->c_oflag |= MODE_OSANE;
X			termio->c_cflag |= MODE_CSANE;
X			termio->c_lflag |= MODE_LSANE;
X		} else if (word[0] != '!') {
X			if (wp = find_word(word, W_imodes)) {
X				termio->c_iflag |= wp->w_value;
X			} else if (wp = find_word(word, W_omodes)) {
X				termio->c_oflag |= wp->w_value;
X			} else if (wp = find_word(word, W_cmodes)) {
X				termio->c_cflag |= wp->w_value;
X			} else if (wp = find_word(word, W_lmodes)) {
X				termio->c_lflag |= wp->w_value;
X			} else if (Check_definitions) {
X				(void)printf("Undefined: %s\n", word);
X			}
X		} else {
X			if (wp = find_word(word + 1, W_imodes)) {
X				termio->c_iflag &= ~wp->w_value;
X			} else if (wp = find_word(word + 1, W_omodes)) {
X				termio->c_oflag &= ~wp->w_value;
X			} else if (wp = find_word(word + 1, W_cmodes)) {
X				termio->c_cflag &= ~wp->w_value;
X			} else if (wp = find_word(word + 1, W_lmodes)) {
X				termio->c_lflag &= ~wp->w_value;
X			} else if (Check_definitions) {
X				(void)printf("Undefined: %s\n", word);
X			}
X		}
X	}
X	return (ptr + 1);
X}
X
X/* Look through the definition file for a definition with the requested label.
X   If the label is null, the first entry will be used. */
X
Xint
Xfind_tty_def(label)
Xchar    *label;                 /* label to look for */
X{
X	register char *ptr;     /* general character pointer */
X	register int cc;        /* current character */
X	register FILE *fp;      /* file pointer for definition file */
X	register int field;     /* <0 means no error, else field of error */
X	register char *nptr;    /* another character pointer */
X	UCHAR   qcc;            /* argument for qchar, can't be register */
X	char    line[BUFSIZ];   /* definition input buffer */
X	static char *fnames[] = { /* names of each input field */
X		"label",   "initial flags", "final flags",
X		"message", "next label",
X	};
X	/* Open the definition file. */
X
X	if (!(fp = fopen(Definition_file, "r"))) {
X		sys_emsg("opening definition file");
X		Tty_def = Default_def;
X		return (0);
X	}
X	while (1) {
X		/* Read one definition from the file; the definitions are
X		   separated by blank lines. */
X
X		ptr = line;
X		*ptr++ = ' ';
X		while (1) {
X			if ((cc = getc(fp)) == EOF) {
X				if (ptr == line + 1) {
X					(void)fclose(fp);
X					Tty_def = Default_def;
X					return (0);
X				}
X				if (ptr[-1] == '\n') {
X					--ptr;
X				}
X				break;
X			} else if (cc == '\n' && ptr[-1] == '\n') {
X				--ptr;
X				break;
X			}
X			if (ptr == &line[sizeof(line)]) {
X				if (Check_definitions) {
X					(void)printf("Entry too long.\n");
X				}
X				while (cc != EOF) {
X					if (cc == '\n') {
X						cc = getc(fp);
X						if (cc == '\n') {
X							break;
X						}
X					} else {
X						cc = getc(fp);
X					}
X				}
X				ptr = line + 1;
X				break;
X			}
X			*ptr++ = cc;
X		}
X		*ptr = 0;
X
X		/* Ignore blank and comment lines. Lines that are too long
X		   also get rejected here. */
X
X		if (!line[1] || line[1] == '#') {
X			continue;
X		}
X		if (Check_definitions) {
X			(void)printf("\n**** Next Entry ****\n%s\n\n",
X			    line + 1);
X		}
X		/* Extract the label of the line. */
X
X		ptr = line + 1;
X		field = -1;
X		if (nptr = extract_word(ptr, Tty_def.td_label,
X		    sizeof(Tty_def.td_label))) {
X			ptr = nptr;
X			if (*ptr != '#') {
X				field = 0;
X			} else {
X				++ptr;
X			}
X		} else {
X			field = 0;
X		}
X		/* Extract the initial flags. */
X
X		if (field < 0) {
X			if (nptr = get_tty_modes(ptr, &Tty_def.td_initial)) {
X				ptr = nptr;
X
X				/* Since not just any modes can be used by
X				   getty, fix up the modes as necessary. */
X
X				Tty_def.td_initial.c_iflag &= ICRNL | IGNPAR;
X				if (!(Tty_def.td_initial.c_cflag & CSIZE)) {
X					Tty_def.td_initial.c_cflag |= CS8;
X				}
X				Tty_def.td_initial.c_cflag |= CREAD | HUPCL;
X				Tty_def.td_initial.c_lflag = 0;
X			} else {
X				field = 2;
X			}
X		}
X		/* Extract the final flags. */
X
X		if (field < 0) {
X			if (nptr = get_tty_modes(ptr, &Tty_def.td_final)) {
X				ptr = nptr;
X			} else {
X				field = 3;
X			}
X		}
X		/* Extract the login message. */
X
X		if (field < 0) {
X			for (nptr = Tty_def.td_logmsg;
X			    nptr < &Tty_def.td_logmsg
X			      [sizeof(Tty_def.td_logmsg)];
X			    *nptr++ = cc) {
X				cc = *ptr++;
X				if (!cc || cc == '#') {
X					break;
X				}
X				if (cc == '\\') {
X					ptr = qchar(ptr, &qcc);
X					cc = qcc;
X				}
X			}
X			Tty_def.td_msglen = nptr - Tty_def.td_logmsg;
X			if (cc != '#') {
X				field = 4;
X			}
X		}
X		/* And finally, extract the next label. */
X
X		if (field < 0) {
X			if (nptr = extract_word(ptr, Tty_def.td_nxtlbl,
X			    sizeof(Tty_def.td_nxtlbl))) {
X				ptr = nptr;
X				if (*ptr) {
X					field = 5;
X				}
X			} else {
X				field = 5;
X			}
X		}
X		/* Display the definition if requested. */
X
X		if (Check_definitions) {
X			(void)printf("label: %s\n", Tty_def.td_label);
X			(void)printf(
X"initial flags: iflag %6o oflag %6o cflag %6o lflag %6o\n",
X			    Tty_def.td_initial.c_iflag,
X			    Tty_def.td_initial.c_oflag,
X			    Tty_def.td_initial.c_cflag,
X			    Tty_def.td_initial.c_lflag);
X			(void)printf(
X"final flags:   iflag %6o oflag %6o cflag %6o lflag %6o\n",
X			    Tty_def.td_final.c_iflag,
X			    Tty_def.td_final.c_oflag,
X			    Tty_def.td_final.c_cflag,
X			    Tty_def.td_final.c_lflag);
X			(void)printf("message: ");
X			qprint(stdout, (UCHAR *)Tty_def.td_logmsg,
X			    Tty_def.td_msglen);
X			(void)printf("\nnext label: %s\n", Tty_def.td_nxtlbl);
X			if (field >= 0) {
X				*++ptr = 0;
X				(void)printf(
X"Error in the \"%s\" field\n%s<--error detected here\n",
X				    fnames[field], line + 1);
X			}
X
X		/* Else check if this is the definition wanted. */
X
X		} else if (field < 0
X		    && (!label || strcmp(label, Tty_def.td_label) == 0)) {
X			break;
X		}
X	}
X	(void)fclose(fp);
X	return (1);
X}
X
X/* This routine is called to set up the terminal for our use. It does all the
X   signal handling, mode changes, file opening, accounting, etc. that is
X   needed before we actually use the terminal. */
X
Xvoid
Xinit_terminal()
X{
X	register struct utmp *uptr; /* used to scan the utmp file */
X	int     fd;             /* fd for the terminal */
X	FILE    *fp;            /* file pointer for wtmp file */
X
X	log_fputs("starting\n");
X
X	/* Ignore keyboard interrupt signals. Sighup and sigterm cause an
X	   exit, however. */
X
X	(void)signal(SIGINT,  SIG_IGN);
X	(void)signal(SIGQUIT, SIG_IGN);
X	(void)signal(SIGHUP,  exit_on_signal);
X	(void)signal(SIGTERM, exit_on_signal);
X
X	/* Change the owner and mode of the terminal line. They are to have
X	   the effective user and group id of the program. Also, the terminal
X	   modes are set to 660 to prevent "other" access to the terminal. */
X
X	if (chdir("/dev") < 0) {
X		sys_emsg("doing chdir to /dev");
X		do_exit(1);
X	}
X	if (chown(Tty_line, (int)geteuid(), (int)getegid()) < 0) {
X		sys_emsg("changing owner of terminal");
X		do_exit(1);
X	}
X	if (chmod(Tty_line, TTY_MODE) < 0) {
X		sys_emsg("changing mode of terminal");
X		do_exit(1);
X	}
X	/* Open up the terminal and make our standard fd's point to the
X	   terminal. From now on, all terminal I/O is done to the opened
X	   terminal. */
X
X	if ((fd = open(Tty_line, O_RDWR)) < 0) {
X		sys_emsg("opening the terminal");
X		do_exit(1);
X	}
X	if (fd != TTY_IN) {
X		my_dup2(fd, TTY_IN);
X	}
X	my_dup2(fd, TTY_OUT);
X	my_dup2(fd, TTY_ERR);
X	if (fd != TTY_IN) {
X		(void)close(fd);
X	}
X	/* Look for an entry in /etc/utmp for us. If one is found, modify the
X	   fields in it appropriately, write it back to the file, and append
X	   the modified entry to /etc/wtmp. */
X
X	while (uptr = getutent()) {
X		if (uptr->ut_type == INIT_PROCESS
X		    && uptr->ut_pid == Our_pid) {
X			strncpy(uptr->ut_line, Tty_line,
X			    sizeof(uptr->ut_line));
X			strncpy(uptr->ut_user, "LOGIN",
X			    sizeof(uptr->ut_user));
X			uptr->ut_type = LOGIN_PROCESS;
X			uptr->ut_time = time((long *)0);
X			(void)pututline(uptr);
X			if (fp = fopen(WTMP_FILE, "a")) {
X				(void)fwrite(uptr, sizeof(*uptr), 1, fp);
X				(void)fclose(fp);
X			}
X			break;
X		}
X	}
X	endutent();
X
X	/* Unless the -h option was specified, hang up the line. */
X
X	if (Hang_up_on_init) {
X		struct termio termio;
X
X		(void)signal(SIGHUP, SIG_IGN);
X		tty_ioctl(TCGETA, &termio);
X		termio.c_cflag &= ~CBAUD;
X		termio.c_cflag |= B0;
X		tty_ioctl(TCSETAF, &termio);
X		(void)signal(SIGHUP, exit_on_signal);
X	}
X	/* If requested, set up a timer. This will cause the getty to exit if
X	   a login hasn't occured. */
X
X	if (!Tmout_after_first) {
X		start_timeout();
X	}
X}
X
X/* Change the modes of the line to correspond to the current initial flags.
X   Then write the connect message to the terminal. */
X
Xvoid
Xinit_at_speed()
X{
X	register char *ptr;     /* pointer into connect message buffer */
X	register int cc;        /* character from the connect message file */
X	FILE    *fp;            /* file pointer for the connect message */
X	struct termio termio;   /* used to set the terminal modes */
X	char    buffer[LINE_LEN]; /* buffer for the connect message */
X
X	/* Log the new speed. */
X
X	log_fputs("new speed label ");
X	log_fputs(Tty_def.td_label);
X	log_fputs("\n");
X
X	/* Set the terminal modes from the initial flags. */
X
X	tty_ioctl(TCGETA, &termio);
X	termio.c_iflag = Tty_def.td_initial.c_iflag;
X	termio.c_oflag = Tty_def.td_initial.c_oflag;
X	termio.c_cflag = Tty_def.td_initial.c_cflag;
X	termio.c_lflag = Tty_def.td_initial.c_lflag;
X	termio.c_line = Line_discipline;
X	termio.c_cc[VMIN] = 1;
X	termio.c_cc[VTIME] = 0;
X	tty_ioctl(TCSETAF, &termio);
X
X	/* Write the connect message. */
X
X	tty_write("\r\n", -1);
X	if (fp = fopen(Issue_file, "r")) {
X		do {
X			ptr = buffer;
X			while (1) {
X				switch (cc = getc(fp)) {
X				case EOF: break;
X				case '\n':
X					*ptr++ = '\r';
X					*ptr++ = '\n';
X					break;
X				default:
X					*ptr++ = cc;
X					if (ptr
X					    == buffer + sizeof(buffer) - 1) {
X						break;
X					}
X					continue;
X				}
X				break;
X			}
X			tty_write(buffer, ptr - buffer);
X		} while (cc != EOF);
X		(void)fclose(fp);
X	}
X	/* If requested, show the system name. */
X
X	if (Show_sysname) {
X		tty_write("\r\nSystem name: ", -1);
X		tty_write(Sys_name.nodename, strlen(Sys_name.nodename));
X		tty_write("\r\n\n", -1);
X	}
X}
X
X/* Getting the input buffer cleared after a break is nontrivial. This routine
X   handles the grunge. The basic problem with flushing the input is that we
X   can't use the ioctl to do it till we know that there isn't anything coming
X   in from the UART. But we don't know that unless we know how long the break
X   is. The compromise is this: after a break, read from the terminal until at
X   least BREAK_DELAY * 10 milliseconds pass without there being anything in
X   the input queue. This means that, immediately after a break, the program
X   may not be willing to accept a character. This should not prove to be a
X   serious problem since the time is very small, in the default 40ms. */
X
Xvoid
Xflush_input()
X{
X	UCHAR   cbuf;           /* character read from the terminal */
X	struct termio termio;   /* used for setting VTIME */
X
X	tty_ioctl(TCGETA, &termio);
X	termio.c_cc[VMIN] = 0;
X	termio.c_cc[VTIME] = BREAK_DELAY;
X	tty_ioctl(TCSETA, &termio);
X	while (1) {
X		switch (read(TTY_IN, (char *)&cbuf, 1)) {
X		case 0: break;
X		case 1: continue;
X		default:
X			sys_emsg("flushing the terminal");
X			do_exit(1);
X		}
X		break;
X	}
X	termio.c_cc[VMIN] = 1;
X	termio.c_cc[VTIME] = 0;
X	tty_ioctl(TCSETA, &termio);
X}
X
X/* Read a line from the user. This routine only returns if the user requests a
X   speed change. */
X
Xvoid
Xread_login_args()
X{
X	register char *wptr;    /* pointer for storing into buffer */
X	register int tmp;
X	register char *rptr;    /* pointer for reading buffer */
X	UCHAR   cbuf;           /* character read from the terminal */
X	int     upper;          /* set if input has an upper case letter */
X	int     lower;          /* set if input has a lower case letter */
X	int     esc;            /* set if previous char was a \ */
X	struct termio termio;   /* used for setting the final modes */
X	char    *arglist[MAX_ARGS + 2]; /* argument list for login */
X	char    buffer[LINE_LEN]; /* input line */
X	static char ctlbuf[2] = "^"; /* buffer for printing ^@ controls */
X	static char backbuf[5] = "\\"; /* buffer for printing \xxx chars */
X
X	/* This loops till a valid login line is returned. */
X
X	wptr = buffer;
X	while (wptr == buffer) {
X
X		/* Print the login prompt. */
X
X		tty_write(Tty_def.td_logmsg, Tty_def.td_msglen);
X
X		/* Read the login argument line. */
X
X		upper = 0;
X		lower = 0;
X		esc = 0;
X		while (1) {
X			/* Read a character. We shouldn't ever get an error,
X			   as all such should have been trapped somewhere. */
X
X			if (read(TTY_IN, (char *)&cbuf, 1) < 0) {
X				sys_emsg("reading the terminal");
X				do_exit(1);
X			}
X			log_qprint("read: ", &cbuf, 1);
X			tmp = cbuf;
X
X			/* If there is to be a timeout and it hasn't already
X			   been started, start it now. This has the effect of
X			   starting the timeout after the first character
X			   read, if it wasn't started after the open. */
X
X			start_timeout();
X
X			/* This means a break from the user. */
X
X			if (!tmp) {
X				return;
X			}
X			/* Echo the character if it is printable. */
X
X			if (isprint(tmp)) {
X				tty_write((char *)&cbuf, 1);
X			}
X			/* If the previous character was a backslash, don't
X			   interpret this character. Note, that to make this
X			   work, we resort to a kludge. Since the following
X			   switch uses tmp, we'll set tmp to space. That
X			   forces the switch to put the character into the
X			   buffer. Of course, the default code must then
X			   reinitialize tmp from cbuf. */
X
X			if (esc) {
X				tmp = ' ';
X				esc = 0;
X			}
X			/* Process the character. */
X
X			switch (tmp) {
X			case '\b': /* backspace */
X				if (wptr > buffer) {
X					--wptr;
X					tty_write("\b \b", -1);
X					if (!isprint(*wptr)) {
X						tty_write("\b \b", -1);
X					}
X				}
X				continue;
X			case CERASE: /* non-control char backspace */
X				if (wptr > buffer) {
X					--wptr;
X				}
X				continue;
X			case CEOF: /* end of file */
X				log_fputs("exiting because of EOF\n");
X				exit(1);
X			case INTR_CHAR: /* control C, for interrupt */
X			case KILL_CHAR: /* control U, line kill */
X			case CINTR: /* interrupt */
X			case CQUIT: /* quit */
X			case CKILL: /* line kill */
X				wptr = buffer;
X				break;
X			case '\n': case '\r': /* end of line characters */
X				*wptr = 0;
X				break;
X			case '\\': /* escape character */
X				esc = 1;
X				/* no break */
X			default:
X				/* See note just above the switch. */
X
X				tmp = cbuf;
X
X				/* If the character is not printing, it hasn't
X				   been echoed. Now we'll print out those, but
X				   we'll do it with printing characters. */
X
X				if (!isprint(tmp)) {
X					if (tmp < 0x20) {
X						ctlbuf[1] = tmp | '@';
X						tty_write(ctlbuf, 2);
X					} else {
X						sprintf(backbuf + 1, "%03o",
X						    tmp);
X						tty_write(backbuf, 4);
X					}
X				}
X				/* If this is the last position in the buffer
X				   the buffer is full. This could be just the
X				   user typing a lot, but is more likely the
X				   line going bonkers. Anyway, we'll exit so
X				   that the line gets hung up. */
X
X				if (wptr == buffer + sizeof(buffer) - 1) {
X					error1("input overflow");
X					do_exit(1);
X				}
X				if (islower(tmp)) {
X					lower = 1;
X				} else if (isupper(tmp)) {
X					upper = 1;
X				}
X				*wptr++ = tmp;
X				continue;
X			}
X			break;
X		}
X		tty_write("\r\n", -1);
X	}
X	log_qprint("args: ", (UCHAR *)buffer, wptr - buffer);
X
X	/* Since we now have a valid input line, turn off the timer. */
X
X	(void)alarm(0);
X
X	/* Change the terminal modes to those specified in the final flags. */
X
X	termio.c_iflag = Tty_def.td_final.c_iflag;
X	termio.c_oflag = Tty_def.td_final.c_oflag;
X	termio.c_cflag = Tty_def.td_final.c_cflag;
X	termio.c_lflag = Tty_def.td_final.c_lflag;
X	termio.c_line = Line_discipline;
X
X	/* Do one kludge: if a return was the line terminator, arrange that
X	   returns are used for I/O instead of line feeds. */
X
X	if (tmp == '\r') {
X		termio.c_iflag |= ICRNL;
X		termio.c_oflag |= ONLCR;
X	}
X	/* Set up the terminal control characters. */
X
X	for (tmp = NCC; --tmp >= 0; ) {
X		termio.c_cc[tmp] = 0;
X	}
X	termio.c_cc[VINTR]  = CINTR;
X	termio.c_cc[VQUIT]  = CQUIT;
X	termio.c_cc[VERASE] = CERASE;
X	termio.c_cc[VKILL]  = CKILL;
X	termio.c_cc[VEOF]   = CEOF;
X
X	/* Do the next kludge: if the input contained an upper case letter but
X	   did not contain a lower case letter, arrange for case translation
X	   on I/O. While we're at it, translate all upper case letters to
X	   lower case. */
X
X	if (upper && !lower) {
X		termio.c_iflag |= IUCLC;
X		termio.c_oflag |= OLCUC;
X		termio.c_lflag |= XCASE;
X		for (wptr = buffer; *wptr; ++wptr) {
X			if (isupper(*wptr)) {
X				*wptr = tolower(*wptr);
X			}
X		}
X	}
X	/* Establish the new terminal modes. */
X
X	tty_ioctl(TCSETAW, &termio);
X
X	/* Split the input line into arguments. */
X
X	arglist[0] = LOGIN_ARGV0;
X	rptr = wptr = buffer;
X	for (tmp = 0; tmp < MAX_ARGS; ++tmp) {
X		while (isspace(*rptr)) {
X			++rptr;
X		}
X		if (!*rptr) {
X			break;
X		}
X		arglist[tmp + 1] = wptr;
X		while (*rptr) {
X			if (isspace(*rptr)) {
X				++rptr;
X				break;
X			}
X			if (*rptr == '\\') {
X				rptr = qchar(rptr + 1, &cbuf);
X				*wptr++ = cbuf;
X			} else {
X				*wptr++ = *rptr++;
X			}
X		}
X		*wptr++ = 0;
X	}
X	arglist[tmp + 1] = 0;
X	log_fputs("execing login\n");
X
X	/* Run the login program. */
X
X	(void)execv(LOGIN_CMD, arglist);
X	sys_emsg("execing login program");
X	do_exit(1);
X}
*-*-END-of-getty.c-*-*
exit