From decwrl!purdue!tut.cis.ohio-state.edu!zaphod.mps.ohio-state.edu!usc!cs.utexas.edu!uunet!allbery Tue Jan 30 09:20:27 PST 1990
Article 1320 of comp.sources.misc:
Path: decwrl!purdue!tut.cis.ohio-state.edu!zaphod.mps.ohio-state.edu!usc!cs.utexas.edu!uunet!allbery
From: wietse@wzv.win.tue.nl (Wietse Z. Venema)
Newsgroups: comp.sources.misc
Subject: v10i050: agetty, SYSV getty for dial-in lines
Keywords: agetty2
Message-ID: <77903@uunet.UU.NET>
Date: 28 Jan 90 18:35:05 GMT
Sender: allbery@uunet.UU.NET
Organization: Eindhoven University of Technology, The Netherlands
Lines: 1042
Approved: allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc)

Posting-number: Volume 10, Issue 50
Submitted-by: wietse@wzv.win.tue.nl (Wietse Z. Venema)
Archive-name: agetty2

This is a SYSV getty replacement with useful features for dial-in lines.

The program adjusts the tty modes to parity bits, and to erase, kill and
end-of-line characters found while reading a login name.  The baud rate
of incoming calls can be established by BREAK character processing or by
parsing status messages produced by multi-speed Hayes-compatible modems.

The first release of this program appeared december 1989. The main new 
feature introduced here is selective processing of the /etc/issue file.

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  README agetty.c agetty.8 Makefile
# Wrapped by wietse@wzv on Sun Jan 28 17:56:42 1990
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'README' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'README'\"
else
echo shar: Extracting \"'README'\" \(440 characters\)
sed "s/^X//" >'README' <<'END_OF_FILE'
X@(#) README 1.4 1/28/90 17:53:02
X
XThis is a SYSV getty replacement with useful features for dial-in lines.
X
XThe program adjusts the tty modes to parity bits, and to erase, kill and
Xend-of-line characters found while reading a login name.  The baud rate
Xof incoming calls can be established by BREAK character processing or by
Xparsing status messages produced by multi-speed Hayes-compatible modems.
X
X		Wietse Venema (wietse@wzv.win.tue.nl)
END_OF_FILE
if test 440 -ne `wc -c <'README'`; then
    echo shar: \"'README'\" unpacked with wrong size!
fi
# end of 'README'
fi
if test -f 'agetty.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'agetty.c'\"
else
echo shar: Extracting \"'agetty.c'\" \(22691 characters\)
sed "s/^X//" >'agetty.c' <<'END_OF_FILE'
X/*++
X/* NAME
X/*	agetty 8
X/* SUMMARY
X/*	alternative System-V getty for dial-up lines
X/* SYNOPSIS
X/*	agetty [-a alternate_rates] [-h] [-i] [-m] [-t timeout] port baud_rate
X/* DESCRIPTION
X/*	\fIagetty\fR opens a tty port, prompts for a login name and invokes the
X/*	/bin/login command. It is normally invoked by \fIinit(8)\fR.
X/*
X/*	\fIagetty\fR has some useful features for dial-up lines that are
X/*	not present in the System V Release 2 getty command:
X/* .IP o
X/*	Adapts the tty settings to parity bits and to
X/*	erase, kill and end-of-line characters found in its input. The
X/*	program understands 7-bit characters with even, odd, none or space
X/*	parity, and 8-bit characters with no parity. The following special
X/*	characters are recognized: @ and Control-U (kill); #, DEL and
X/*	back space (erase); carriage return and line feed (end of line).
X/* .IP o
X/*	Optionally recognizes the baud rate of incoming calls from the 
X/*	status messages produced by some multi-speed Hayes-compatible modems.
X/* .IP o
X/*	Optionally does not display the contents of the \fI/etc/issue\fR file.
X/* .PP
X/*	This program does not use the \fI/etc/gettydefs\fR file. Except for 
X/*	differences described in the documentation, the program appears to 
X/*	operate similar to the System-V Release 2 \fIgetty\fR program.
X/*
X/*	Options:
X/* .TP
X/* -a alternate_rates
X/*	Initially the program will use the \fIbaud_rate\fR as specified.
X/*	Upon receipt of successive BREAK characters the program will step
X/*	through the \fIalternate_rates\fR, which should be specified as a
X/*	comma-separated list (preferably in decreasing order). After all
X/*	\fIalternate_rates\fR have been tried, \fIagetty\fR will try the
X/*	speed specified with the \fIbaud_rate\fR argument and so on.
X/* .TP
X/* -h
X/*	Do not hang up the line. Normally, \fIagetty\fR will lower
X/*	DTR for two seconds to force a modem to hang up (if the hangup
X/*	feature has been compiled into the program).
X/* .TP
X/* -i
X/*	Do not display the contents of \fI/etc/issue\fR before writing the 
X/*	login prompt. Terminals or computer programs may become confused
X/*	when receiving lots of text at the wrong baud rate; dial-up scripts
X/*	may fail if the login prompt is preceded by too much text.
X/* .TP
X/* -m
X/*	Try to extract the baud rate of incoming calls from the status message
X/*	produced by some multi-speed Hayes-compatible modems. These usually
X/*	produce a status message of the form: "<junk><speed><junk>".
X/*	If no \fIspeed\fR is found within one second, the \fIbaud_rate\fR as
X/*	specified on the command line will be used. Since the \fI-m\fR feature 
X/*	will work only on lightly-loaded systems, you will probably want to use
X/*	it in combination with the \fI-a\fR option.
X/* .TP
X/* -t timeout
X/*	Causes the program to terminate if no user name could be read
X/*	within \fItimeout\fR seconds. This is useful only for dial-in lines.
X/* EXAMPLES
X/*	For hard-wired lines:
X/* .ti +5
X/*		/etc/agetty ttyM0 9600
X/*
X/*	For dial-in lines with a 300/1200/2400 baud multi-speed modem:
X/* .ti +5
X/*		/etc/agetty -t60 -m -a1200,300 ttyM1 2400
X/* FILES
X/*	/etc/utmp, the system log file.
X/*	/etc/issue, printed before the login prompt.
X/*	/dev/console, problem reports.
X/* BUGS
X/*	The baud-rate detection code (the \fI-m\fR option) only works if
X/*	\fIagetty\fR is scheduled soon enough after completion of a dial-in
X/*	call (within 30 ms with modems that talk at 2400 baud). For robustness,
X/*	always use the \fI-m\fR option in combination with the \fI-a\fR option.
X/*
X/*	The contents of the /etc/issue file and the login prompt are always
X/*	output with space parity.
X/* DIAGNOSTICS
X/*	All diagnostics are written to the console device. Error messages are
X/*	produced if the \fIport\fR argument does not specify a terminal; if 
X/*	there is no /etc/utmp entry for the current process; and so on.
X/* AUTHOR(S)
X/*	W.Z. Venema <wietse@wzv.win.tue.nl>
X/*	Eindhoven University of Technology
X/*	Department of Mathematics and Computer Science
X/*	Den Dolech 2, P.O. Box 513, 5600 MB Eindhoven, The Netherlands
X/* CREATION DATE
X/*	Sat Nov 25 22:51:05 MET 1989
X/* LAST MODIFICATION
X/*	90/01/28 17:53:06
X/* VERSION/RELEASE
X/*	1.26
X/*--*/
X
X#ifndef lint
Xstatic char sccsid[] = "@(#) agetty.c 1.26 1/28/90 17:53:06";
X#endif
X
X#include <termio.h>
X#include <signal.h>
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <varargs.h>
X#include <ctype.h>
X#include <utmp.h>
X
X /*
X  * Things you may want to modify.
X  * 
X  * HANGUP should be defined only if your tty driver is not able to hang up the
X  * modem (by briefly dropping DTR). If HANGUP is defined you probably cannot
X  * use the auto-baud and time-out features.
X  * 
X  * If ISSUE is not defined, agetty will never display the contents of the
X  * /etc/issue file. You will not want to spit out large "issue" files at the
X  * wrong baud rate.
X  * 
X  * You may disagree with the default line-editing etc. characters defined
X  * below. Note, however, that DEL cannot be used for interrupt generation
X  * and for line editing at the same time.
X  */
X
X#define	ISSUE "/etc/issue"		/* shown before login prompt */
X
X#define LOGIN "login: "			/* login prompt */
X
X/* #define HANGUP			/* enable hangup code */
X
X/* Some shorthands for control characters */
X
X#define CTL(x)		(x ^ 0100)	/* Assumes ASCII dialect */
X#define	CR		CTL('M')	/* carriage return */
X#define	NL		CTL('J')	/* line feed */
X#define	BS		CTL('H')	/* back space */
X#define	DEL		CTL('?')	/* delete */
X
X/* Defaults for line-editing etc. characters; you may want to change this */
X
X#define DEF_INTR	CTL('C')	/* default interrupt character */
X#define DEF_QUIT	CTL('\\')	/* default quit char */
X#define DEF_KILL	CTL('U')	/* default kill char */
X#define DEF_EOF		CTL('D')	/* default EOF char */
X#define DEF_SWITCH	CTL('^')	/* default switch char */
X#define DEF_ERASE	BS		/* default erase char, see below */
X#define DEF_EOL		0
X
X /*
X  * This program does not need the standard-i/o library.  This keeps the
X  * executable small; useful for systems that do not have shared libaries
X  * (Sys-V Rel <3).
X  */
X
X#define	BUFSIZ	1024
X
X/* Storage for command-line options */
X
X#define	MAXSPEED	10
X
Xstruct options {
X    int     flags;			/* toggle switches, see below */
X    int     timeout;			/* time-out period */
X    int     numspeed;			/* number of baud rates to try */
X    int     curspeed;			/* current speed */
X    int     speeds[MAXSPEED];		/* baud rates to be tried */
X    char   *tty;			/* name of tty */
X};
X
X#define	F_PARSE		(1<<0)		/* process modem status messages */
X#define	F_HANGUP	(1<<1)		/* hangup line */
X#define	F_ISSUE		(1<<2)		/* display /etc/issue */
X
X/* Storage for things detected while the login name was read */
X
Xstruct chardata {
X    int     erase;			/* erase character */
X    int     kill;			/* kill character */
X    int     eol;			/* end-of-line character */
X    int     parity;			/* what parity did we see */
X    int     capslock;			/* upper case without lower case */
X};
X
X/* The following is used for understandable diagnostics */
X
Xextern int errno;
Xextern char *sys_errlist[];
Xstatic char *progname;
Xextern char *strcpy();
Xextern char *strcat();
X
X/* ... */
X
Xmain(argc, argv)
Xint     argc;
Xchar  **argv;
X{
X    char   *logname;			/* login name, given to /bin/login */
X    char   *get_logname();
X    struct chardata chardata;		/* set by get_logname() */
X    struct termio termio;		/* terminal mode bits */
X    static struct options options = {
X	F_HANGUP | F_ISSUE,		/* hangup line and show /etc/issue */
X	0,				/* no timeout */
X	1,				/* no alternate baud rates */
X	0,				/* no alternate baud rates */
X    };
X
X    progname = argv[0];
X
X    /* Parse command-line arguments */
X
X    parse_args(argc, argv, &options);
X
X    /* Update the utmp file */
X
X    update_utmp(options.tty);
X
X    /* Open the tty as standard { input, output, error } */
X
X    open_tty(options.tty, &termio);
X
X    /* Optionally hang up the tty */
X
X    if (options.flags & F_HANGUP)
X	hangup_tty(&termio);
X
X    /* Initialize the termio settings (raw mode, eight-bit, blocking i/o) */
X
X    termio_init(&termio, options.speeds[0]);
X
X    /* Optionally detect the baud rate from the modem status message */
X
X    if (options.flags & F_PARSE)
X	auto_baud(&termio);
X
X    /* With dial-in lines, briefly pause to allow modems etc. to settle */
X
X    if (options.timeout)
X	(void) sleep(1);
X
X    /* Optional time-out feature */
X
X    if (options.timeout)
X	(void) alarm((unsigned) options.timeout);
X
X    /* Read the login name */
X
X    while ((logname = get_logname(&options, &chardata, &termio)) == 0)
X	next_speed(&termio, &options);
X
X    /* Disable time-out feature */
X
X    if (options.timeout)
X	(void) alarm(0);
X
X    /* Finalize the termio settings */
X
X    termio_final(&termio, &chardata);
X
X    /* Now the newline character should be properly written */
X
X    (void) write(1, "\n", 1);
X
X    /* Let /bin/login take care of password validation */
X
X    (void) execl("/bin/login", "login", logname, (char *) 0);
X    error("%s: can't exec /bin/login", options.tty);
X    /* NOTREACHED */
X}
X
X/* parse-args - parse command-line arguments */
X
Xparse_args(argc, argv, op)
Xint     argc;
Xchar  **argv;
Xstruct options *op;
X{
X    extern char *optarg;		/* getopt */
X    extern int optind;			/* getopt */
X    int     c;
X
X    while (isascii(c = getopt(argc, argv, "a:himt:"))) {
X	switch (c) {
X	case 'a':				/* enable auto-baud feature */
X	    parse_speeds(op, optarg);
X	    break;
X	case 'h':				/* do not hangup the tty */
X	    op->flags &= ~F_HANGUP;
X	    break;
X	case 'i':				/* do not show /etc/issue */
X	    op->flags &= ~F_ISSUE;
X	    break;
X	case 'm':				/* parse modem status message */
X	    op->flags |= F_PARSE;
X	    break;
X	case 't':				/* time out */
X	    if ((op->timeout = atoi(optarg)) <= 0)
X		error("bad timeout value: %s", optarg);
X	    break;
X	case '?':
X	    usage();
X	}
X    }
X    if (argc != optind + 2)			/* check parameter count */
X	usage();
X    op->tty = argv[optind++];			/* tty name */
X    if ((op->speeds[0] = bcode(argv[optind])) <= 0)	/* baud rate */
X	error("bad speed: %s", argv[optind]);
X}
X
X/* parse_speeds - parse alternate baud rates */
X
Xparse_speeds(op, arg)
Xstruct options *op;
Xchar   *arg;
X{
X    char   *strtok();
X    char   *cp;
X
X    for (cp = strtok(arg, ","); cp != 0; cp = strtok((char *) 0, ",")) {
X	if ((op->speeds[op->numspeed++] = bcode(cp)) <= 0)
X	    error("bad speed: %s", cp);
X	if (op->numspeed > MAXSPEED)
X	    error("too many alternate speeds");
X    }
X}
X
X/* update_utmp - update our utmp entry */
X
Xupdate_utmp(line)
Xchar   *line;
X{
X    struct utmp ut;
X    long    ut_size = sizeof(ut);	/* avoid nonsense */
X    int     ut_fd;
X    int     mypid = getpid();
X    long    time();
X    long    lseek();
X    char   *strncpy();
X
X    /*
X     * The utmp file holds miscellaneous information about things started by
X     * /etc/init and other system-related events. Our purpose is to update
X     * the utmp entry for the current process, in particular the process type
X     * and the tty line we are listening to. Return successfully only if the
X     * utmp file can be opened for update, and if we are able to find our
X     * entry in the utmp file.
X     */
X
X    if ((ut_fd = open(UTMP_FILE, 2)) < 0) {
X	error("%s: open for update", UTMP_FILE);
X    } else {
X	while (read(ut_fd, (char *) &ut, sizeof(ut)) == sizeof(ut)) {
X	    if (ut.ut_type == INIT_PROCESS && ut.ut_pid == mypid) {
X		ut.ut_type = LOGIN_PROCESS;
X		ut.ut_time = time((long *) 0);
X		(void) strncpy(ut.ut_name, "LOGIN", sizeof(ut.ut_name));
X		(void) strncpy(ut.ut_line, line, sizeof(ut.ut_line));
X		(void) lseek(ut_fd, -ut_size, 1);
X		(void) write(ut_fd, (char *) &ut, sizeof(ut));
X		(void) close(ut_fd);
X		return;
X	    }
X	}
X	error("no utmp entry found for process id %u", mypid);
X    }
X}
X
X/* open_tty - open tty as standard { input, output, error } */
X
Xopen_tty(tty, tp)
Xchar   *tty;
Xstruct termio *tp;
X{
X    struct stat st;
X
X    /* Close standard { input, output, error } files, just in case */
X
X    (void) close(0);
X    (void) close(1);
X    (void) close(2);
X    errno = 0;					/* ignore above errors */
X
X    /* Make sure we are given a character device */
X
X    if (chdir("/dev"))
X	error("/dev: chdir() failed");
X    if (stat(tty, &st) < 0)
X	error("/dev/%s: stat() failed", tty);
X    if ((st.st_mode & S_IFMT) != S_IFCHR)
X	error("not a character device: /dev/%s", tty);
X
X    /* Set up new standard input, output and error files */
X
X    if (open(tty, 2) != 0)			/* set up std input */
X	error("/dev/%s: cannot open as standard input", tty);
X    if (dup(0) != 1 || dup(0) != 2)		/* set up std out and std err */
X	error("%s: dup problem", tty);		/* we have a problem */
X    if (ioctl(0, TCGETA, tp) < 0)		/* read tty status bits */
X	error("%s: ioctl failed", tty);		/* this is not a terminal */
X
X    /* It seems to be a terminal; set proper protections and ownership */
X
X    (void) chown(tty, 0, 0);			/* root, sys */
X    (void) chmod(tty, 0622);			/* crw--w--w- */
X    errno = 0;					/* ignore above errors */
X}
X
X#ifdef lint
X#define	HANGUP
X#endif
X
X/* hangup_tty - hang up by forcing DTR down for at least 2 seconds */
X
Xhangup_tty(tp)
Xstruct termio *tp;
X{
X#ifdef	HANGUP
X    (void) signal(SIGHUP, SIG_IGN);
X    tp->c_cflag &= ~CBAUD;
X    tp->c_cflag |= B0;
X    (void) ioctl(0, TCSETA, tp);
X    (void) signal(SIGHUP, SIG_DFL);
X    (void) sleep(2);
X#endif
X}
X
X/* termio_init - initialize termio settings */
X
Xtermio_init(tp, speed)
Xstruct termio *tp;
Xint     speed;
X{
X
X    /*
X     * Initial termio settings: 8-bit characters, raw-mode, blocking i/o.
X     * Special characters are set after we have read the login name; all
X     * reads will be done in raw mode anyway.
X     */
X
X    tp->c_cflag = CS8 | HUPCL | CREAD | speed;
X    tp->c_iflag = tp->c_lflag = tp->c_oflag = tp->c_line = 0;
X    tp->c_cc[VMIN] = 1;
X    tp->c_cc[VTIME] = 0;
X    (void) ioctl(0, TCSETA, tp);
X}
X
X/* auto_baud - extract baud rate from modem status message */
X
Xauto_baud(tp)
Xstruct termio *tp;
X{
X    int     speed;
X    int     vmin;
X    int     iflag;
X    char    buf[BUFSIZ];
X    char   *bp;
X    int     nread;
X
X    /*
X     * This works only if the modem produces its status code AFTER raising
X     * the DCD line, and if the computer is fast enough to set the proper
X     * baud rate before the message has gone by. We expect a message of the
X     * following format:
X     * 
X     * <junk><number><junk>
X     * 
X     * The number is interpreted as the baud rate of the incoming call. If the
X     * modem does not tell us the baud rate within one second we will keep
X     * using the current baud rate. It is advisable to enable baud-rate
X     * cycling (-a option) if the processing of modem status messages is
X     * enabled.
X     */
X
X    /* Use 7-bit characters, don't block if input queue is empty */
X
X    iflag = tp->c_iflag;
X    tp->c_iflag |= ISTRIP;			/* enable 8th-bit stripping */
X    vmin = tp->c_cc[VMIN];
X    tp->c_cc[VMIN] = 0;				/* don't block if queue empty */
X    (void) ioctl(0, TCSETA, tp);
X
X    /*
X     * Wait for a while, then read everything the modem has said so far and
X     * try to extract the speed of the dial-in call.
X     */
X
X    (void) sleep(1);
X    if ((nread = read(0, buf, sizeof(buf) - 1)) > 0) {
X	buf[nread] = '\0';
X	for (bp = buf; bp < buf + nread; bp++) {
X	    if (isascii(*bp) && isdigit(*bp)) {
X		if (speed = bcode(bp)) {
X		    tp->c_cflag &= ~CBAUD;
X		    tp->c_cflag |= speed;
X		}
X		break;
X	    }
X	}
X    }
X    /* Restore settings */
X
X    tp->c_iflag = iflag;
X    tp->c_cc[VMIN] = vmin;
X    (void) ioctl(0, TCSETA, tp);
X}
X
X/* do_prompt - show login prompt, optionally preceded by /etc/issue contents */
X
Xdo_prompt(op, tp)
Xstruct options *op;
Xstruct termio *tp;
X{
X#ifdef	ISSUE
X    int     fd;
X    int     oflag;
X    int     n;
X    char    buf[BUFSIZ];
X#endif
X
X    write(1, "\r\n", 2);			/* Start a new line */
X#ifdef	ISSUE					/* Optional: show /etc/issue */
X    if ((op->flags & F_ISSUE) && (fd = open(ISSUE, 0)) >= 0) {
X	oflag = tp->c_oflag;			/* Save current setting */
X	tp->c_oflag |= (ONLCR | OPOST);		/* Map NL in output to CR-NL */
X	(void) ioctl(0, TCSETAW, tp);
X	while ((n = read(fd, buf, sizeof(buf))) > 0)
X	    (void) write(1, buf, n);
X	tp->c_oflag = oflag;			/* Restore settings */
X	(void) ioctl(0, TCSETAW, tp);		/* Wait till output gone */
X	(void) close(fd);
X    }
X#endif
X    (void) write(1, LOGIN, sizeof(LOGIN) - 1);	/* Always show login prompt */
X}
X
X/* next_speed - select next baud rate */
X
Xnext_speed(tp, op)
Xstruct termio *tp;
Xstruct options *op;
X{
X    op->curspeed = (op->curspeed + 1) % op->numspeed;
X    tp->c_cflag &= ~CBAUD;
X    tp->c_cflag |= op->speeds[op->curspeed];
X    (void) ioctl(0, TCSETA, tp);
X}
X
X/* get_logname - get user name, establish parity, speed, erase, kill, eol */
X
Xchar   *get_logname(op, cp, tp)
Xstruct options *op;
Xstruct chardata *cp;
Xstruct termio *tp;
X{
X    char    logname[BUFSIZ];
X    char   *bp;
X    char    c;				/* input character, full eight bits */
X    char    ascval;			/* low 7 bits of input character */
X    int     bits;			/* # of "1" bits per character */
X    int     mask;			/* mask with 1 bit up */
X    static char *erase[] = {		/* backspace-space-backspace */
X	"\010\040\010",			/* space parity */
X	"\010\040\010",			/* odd parity */
X	"\210\240\210",			/* even parity */
X	"\210\240\210",			/* no parity */
X    };
X
X    /* Initialize kill, erase, parity etcetera (also after switching speeds) */
X
X    cp->kill = DEF_KILL;
X    cp->erase = DEF_ERASE;
X    cp->parity = 0;
X
X    /* Flush any pending input */
X
X    (void) ioctl(0, TCFLSH, (struct termio *) 0);
X
X    /* Read a login name */
X
X    for (*logname = 0; *logname == 0; /* void */ ) {
X
X	/* Write issue file and prompt, with "parity" bit == 0 */
X
X	do_prompt(op, tp);
X
X	/* Read name, watch for break, parity, erase, kill, end-of-line */
X
X	for (bp = logname, cp->eol = 0; cp->eol == 0; /* void */ ) {
X	    if (read(0, &c, 1) < 1)
X		error("%s: read error", op->tty);
X
X	    /* Do BREAK handling elsewhere */
X
X	    if ((c == 0) && op->numspeed > 1)
X		return (0);
X
X	    /* Do parity bit handling */
X
X	    if (c != (ascval = (c & 0177))) {	/* "parity" bit on ? */
X		for (bits = 1, mask = 1; mask & 0177; mask <<= 1)
X		    if (mask & ascval)
X			bits++;			/* count "1" bits */
X		cp->parity |= ((bits & 1) ? 1 : 2);
X	    }
X	    /* Do erase, kill and end-of-line processing */
X
X	    switch (ascval) {
X	    case CR:
X	    case NL:
X		*bp = 0;			/* terminate logname */
X		cp->eol = ascval;		/* set end-of-line char */
X		break;
X	    case BS:
X	    case DEL:
X	    case '#':
X		cp->erase = ascval;		/* set erase character */
X		if (bp > logname) {
X		    (void) write(1, erase[cp->parity], 3);
X		    bp--;
X		}
X		break;
X	    case CTL('U'):
X	    case '@':
X		cp->kill = ascval;		/* set kill character */
X		while (bp > logname) {
X		    (void) write(1, erase[cp->parity], 3);
X		    bp--;
X		}
X		break;
X	    case CTL('D'):
X		exit(0);
X	    default:
X		if (!isascii(ascval) || !isprint(ascval)) {
X		     /* ignore garbage characters */ ;
X		} else if (bp - logname >= sizeof(logname) - 1) {
X		    error("%s: input overrun", op->tty);
X		} else {
X		    (void) write(1, &c, 1);	/* echo the character */
X		    *bp++ = ascval;		/* and store it */
X		}
X		break;
X	    }
X	}
X    }
X    cp->capslock = caps_lock(logname);		/* upper case w/o lower case? */
X    return (logname);
X}
X
X/* termio_final - set the final tty mode bits */
X
Xtermio_final(tp, cp)
Xstruct termio *tp;
Xstruct chardata *cp;
X{
X    /* General terminal-independent stuff */
X
X    tp->c_iflag |= IXON | IXOFF;		/* 2-way flow control */
X    tp->c_lflag |= ICANON | ISIG | ECHO | ECHOE | ECHOK;
X    tp->c_oflag |= OPOST;
X    tp->c_cc[VEOF] = DEF_EOF;
X    tp->c_cc[VEOL] = DEF_EOL;
X    tp->c_cc[VINTR] = DEF_INTR;
X    tp->c_cc[VQUIT] = DEF_QUIT;
X    tp->c_cc[VKILL] = DEF_KILL;
X    tp->c_cc[VERASE] = DEF_ERASE;
X    tp->c_cc[VSWTCH] = DEF_SWITCH;
X
X    /* Account for special characters seen in input */
X
X    if (cp->eol == CR) {
X	tp->c_iflag |= ICRNL;			/* map CR in input to NL */
X	tp->c_oflag |= ONLCR;			/* map NL in output to CR-NL */
X    }
X    tp->c_cc[VERASE] = cp->erase;		/* set erase character */
X    tp->c_cc[VKILL] = cp->kill;			/* set kill character */
X
X    /* Account for the presence or absence of parity bits in input */
X
X    switch (cp->parity) {
X    case 0:					/* space (always 0) parity */
X	break;
X    case 1:					/* odd parity */
X	tp->c_cflag |= PARODD;
X	/* FALLTHROUGH */
X    case 2:					/* even parity */
X	tp->c_cflag |= PARENB;
X	tp->c_iflag |= INPCK | ISTRIP;
X	/* FALLTHROUGH */
X    case (1 | 2):				/* no parity bit */
X	tp->c_cflag &= ~CSIZE;
X	tp->c_cflag |= CS7;
X	break;
X    }
X    /* Account for upper case without lower case */
X
X    if (cp->capslock) {
X	tp->c_iflag |= IUCLC;
X	tp->c_lflag |= XCASE;
X	tp->c_oflag |= OLCUC;
X    }
X    /* Finally, make the new settings effective */
X
X    (void) ioctl(0, TCSETA, tp);
X}
X
X/* caps_lock - string contains upper case without lower case */
X
Xcaps_lock(s)
Xchar   *s;
X{
X    int     hascaps;
X
X    for (hascaps = 0; *s; s++) {
X	if (islower(*s))
X	    return (0);
X	if (hascaps == 0)
X	    hascaps = isupper(*s);
X    }
X    return (hascaps);
X}
X
X/* bcode - convert speed string to speed code; return 0 on failure */
X
Xbcode(s)
Xchar   *s;
X{
X    struct Speedtab {
X	int     speed;
X	int     code;
X    };
X    static struct Speedtab speedtab[] = {
X	50, B50,
X	75, B75,
X	110, B110,
X	134, B134,
X	150, B150,
X	200, B200,
X	300, B300,
X	600, B600,
X	1200, B1200,
X	1800, B1800,
X	2400, B2400,
X	4800, B4800,
X	9600, B9600,
X	19200, EXTA,
X	0, 0,
X    };
X    struct Speedtab *sp;
X    int     speed = atoi(s);
X
X    for (sp = speedtab; sp->speed; sp++)
X	if (sp->speed == speed)
X	    return (sp->code);
X    return (0);
X}
X
X/* usage - explain */
X
Xusage()
X{
X    static char args[] =
X	"[-a alternate_rates] [-h] [-i] [-m] [-t timeout] line baud_rate";
X
X    error("usage: %s %s", progname, args);
X}
X
X/* error - report errors to the console; only understands %s */
X
X#define	str2cpy(b,s1,s2)	strcat(strcpy(b,s1),s2)
X
X/* VARARGS */
X
Xerror(va_alist)
Xva_dcl
X{
X    va_list ap;
X    char   *fmt;
X    int     fd;
X    int     err = errno;
X    char    buf[BUFSIZ];
X    char   *bp;
X
X    if ((fd = open("/dev/console", 1)) >= 0) {
X	(void) str2cpy(buf, progname, ": ");
X	bp = buf + strlen(buf);
X
X	/*
X	 * %s expansion is done by hand. The program would become three times
X	 * as big if we would use the stdio library...
X	 */
X
X	va_start(ap);
X	fmt = va_arg(ap, char *);
X	while (*fmt) {
X	    if (strncmp(fmt, "%s", 2) == 0) {
X		(void) strcat(bp, va_arg(ap, char *));
X		bp += strlen(bp);
X		fmt += 2;
X	    } else {
X		*bp++ = *fmt++;
X	    }
X	}
X	*bp = 0;
X	va_end(ap);
X
X	/* Add system error message if errno was set */
X
X	if (err)
X	    (void) str2cpy(bp, ": ", sys_errlist[errno]);
X
X	/* Terminate with CR-LF since the console mode is unknown */
X
X	(void) strcat(bp, "\r\n");
X	(void) write(fd, buf, strlen(buf));
X	(void) close(fd);
X    }
X    (void) sleep(5);				/* be kind to init */
X    exit(1);
X}
END_OF_FILE
if test 22691 -ne `wc -c <'agetty.c'`; then
    echo shar: \"'agetty.c'\" unpacked with wrong size!
fi
# end of 'agetty.c'
fi
if test -f 'agetty.8' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'agetty.8'\"
else
echo shar: Extracting \"'agetty.8'\" \(3984 characters\)
sed "s/^X//" >'agetty.8' <<'END_OF_FILE'
X.TH AGETTY 8 
X.ad
X.fi
X.SH NAME
Xagetty
X\-
Xalternative System-V getty for dial-up lines
X.SH SYNOPSIS
X.na
X.nf
Xagetty [-a alternate_rates] [-h] [-i] [-m] [-t timeout] port baud_rate
X.SH DESCRIPTION
X.ad
X.fi
X\fIagetty\fR opens a tty port, prompts for a login name and invokes the
X/bin/login command. It is normally invoked by \fIinit(8)\fR.
X
X\fIagetty\fR has some useful features for dial-up lines that are
Xnot present in the System V Release 2 getty command:
X.IP o
XAdapts the tty settings to parity bits and to
Xerase, kill and end-of-line characters found in its input. The
Xprogram understands 7-bit characters with even, odd, none or space
Xparity, and 8-bit characters with no parity. The following special
Xcharacters are recognized: @ and Control-U (kill); #, DEL and
Xback space (erase); carriage return and line feed (end of line).
X.IP o
XOptionally recognizes the baud rate of incoming calls from the
Xstatus messages produced by some multi-speed Hayes-compatible modems.
X.IP o
XOptionally does not display the contents of the \fI/etc/issue\fR file.
X.PP
XThis program does not use the \fI/etc/gettydefs\fR file. Except for
Xdifferences described in the documentation, the program appears to
Xoperate similar to the System-V Release 2 \fIgetty\fR program.
X
XOptions:
X.TP
X-a alternate_rates
XInitially the program will use the \fIbaud_rate\fR as specified.
XUpon receipt of successive BREAK characters the program will step
Xthrough the \fIalternate_rates\fR, which should be specified as a
Xcomma-separated list (preferably in decreasing order). After all
X\fIalternate_rates\fR have been tried, \fIagetty\fR will try the
Xspeed specified with the \fIbaud_rate\fR argument and so on.
X.TP
X-h
XDo not hang up the line. Normally, \fIagetty\fR will lower
XDTR for two seconds to force a modem to hang up (if the hangup
Xfeature has been compiled into the program).
X.TP
X-i
XDo not display the contents of \fI/etc/issue\fR before writing the
Xlogin prompt. Terminals or computer programs may become confused
Xwhen receiving lots of text at the wrong baud rate; dial-up scripts
Xmay fail if the login prompt is preceded by too much text.
X.TP
X-m
XTry to extract the baud rate of incoming calls from the status message
Xproduced by some multi-speed Hayes-compatible modems. These usually
Xproduce a status message of the form: "<junk><speed><junk>".
XIf no \fIspeed\fR is found within one second, the \fIbaud_rate\fR as
Xspecified on the command line will be used. Since the \fI-m\fR feature
Xwill work only on lightly-loaded systems, you will probably want to use
Xit in combination with the \fI-a\fR option.
X.TP
X-t timeout
XCauses the program to terminate if no user name could be read
Xwithin \fItimeout\fR seconds. This is useful only for dial-in lines.
X.SH EXAMPLES
X.na
X.nf
XFor hard-wired lines:
X.ti +5
X/etc/agetty ttyM0 9600
X
XFor dial-in lines with a 300/1200/2400 baud multi-speed modem:
X.ti +5
X/etc/agetty -t60 -m -a1200,300 ttyM1 2400
X.SH FILES
X.na
X.nf
X/etc/utmp, the system log file.
X/etc/issue, printed before the login prompt.
X/dev/console, problem reports.
X.SH BUGS
X.ad
X.fi
XThe baud-rate detection code (the \fI-m\fR option) only works if
X\fIagetty\fR is scheduled soon enough after completion of a dial-in
Xcall (within 30 ms with modems that talk at 2400 baud). For robustness,
Xalways use the \fI-m\fR option in combination with the \fI-a\fR option.
X
XThe contents of the /etc/issue file and the login prompt are always
Xoutput with space parity.
X.SH DIAGNOSTICS
X.ad
X.fi
XAll diagnostics are written to the console device. Error messages are
Xproduced if the \fIport\fR argument does not specify a terminal; if
Xthere is no /etc/utmp entry for the current process; and so on.
X.SH AUTHOR(S)
X.na
X.nf
XW.Z. Venema <wietse@wzv.win.tue.nl>
XEindhoven University of Technology
XDepartment of Mathematics and Computer Science
XDen Dolech 2, P.O. Box 513, 5600 MB Eindhoven, The Netherlands
X.SH CREATION DATE
X.na
X.nf
XSat Nov 25 22:51:05 MET 1989
X.SH LAST MODIFICATION
X.na
X.nf
X90/01/28 17:53:06
X.SH VERSION/RELEASE
X.na
X.nf
X1.26
END_OF_FILE
if test 3984 -ne `wc -c <'agetty.8'`; then
    echo shar: \"'agetty.8'\" unpacked with wrong size!
fi
# end of 'agetty.8'
fi
if test -f 'Makefile' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'Makefile'\"
else
echo shar: Extracting \"'Makefile'\" \(257 characters\)
sed "s/^X//" >'Makefile' <<'END_OF_FILE'
X# @(#) Makefile 1.3 11/26/89 22:20:28 
X
XSHELL	= /bin/sh
XCFLAGS	= -s -O
XFILES	= README agetty.c agetty.8 Makefile
X
Xagetty: agetty.c
X	cc $(CFLAGS) -o $@ $?
X
Xclean:
X	rm -f agetty.o agetty
X
Xshar:	$(FILES)
X	@shar $(FILES)
X
Xagetty.8:
X	srctoman agetty.c >agetty.8
END_OF_FILE
if test 257 -ne `wc -c <'Makefile'`; then
    echo shar: \"'Makefile'\" unpacked with wrong size!
fi
# end of 'Makefile'
fi
echo shar: End of shell archive.
exit 0


