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