System V getty clone
T. William Wells
bill at twwells.com
Mon Jan 8 02:26:43 AEST 1990
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 at 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 at 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 at 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 at 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 at 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
More information about the Alt.sources
mailing list