[comp.sources.misc] v09i065: agetty, alternative getty for System-V

allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc) (12/15/89)

Posting-number: Volume 9, Issue 65
Submitted-by: wietse@wzv.win.tue.nl (Wietse Z. Venema)
Archive-name: agetty

This is a SYSV getty replacement that adapts itself to parity bits and
to erase, kill and end-of-line characters when a user enters her login
name.  It has an optional facility do detect the baud rate of incoming
calls from the status messages produced by some multi-speed Hayes(tm)
modem clones.

#! /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 Wed Dec 13 00:22:33 1989
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'\" \(490 characters\)
sed "s/^X//" >'README' <<'END_OF_FILE'
X@(#) README 1.1 11/26/89 22:04:59
X
XThis is a SYSV getty replacement that adapts itself to parity bits, and
Xto erase, kill and end-of-line characters found in its input.  It also
Xhas an optional facility do detect the baud rate of incoming calls from
Xthe status messages produced by some multi-speed Hayes modem clones.
XThis program is an adapted version of a getty I once wrote for a V7 UNIX
Ximplementation; it does not use the /etc/gettydefs file.
X
X		Wietse Venema (wietse@wzv.win.tue.nl)
END_OF_FILE
if test 490 -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'\" \(20261 characters\)
sed "s/^X//" >'agetty.c' <<'END_OF_FILE'
X/*++
X/* NAME
X/*	agetty 8
X/* SUMMARY
X/*	alternative System-V getty
X/* SYNOPSIS
X/*	agetty [-a alternate_rates] [-h] [-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 not present in the System
X/*	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/*	backspace (erase); carriage-return and linefeed (end of line).
X/* .IP o
X/*	Optionally recognizes the baud rate of incoming
X/*	calls from the status messages produced by some multi-speed Hayes (tm)
X/*	modem clones.
X/* .PP
X/*	This program does not use the \fI/etc/gettydefs\fR file. Except for 
X/*	differences described here the program appears to operate similar
X/*	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/* -m
X/*	Try to extract the baud rate of incoming calls from the status message
X/*	produced by some Hayes (tm) multi-speed modem clones. 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 this feature will
X/*	work only on lightly-loaded systems, you will probably want to use this
X/*	feature 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 Hayes clone:
X/* .ti +5
X/*		/etc/agetty -t60 -m -a1200,300 ttyM1 2400
X/* FILES
X/*	/etc/utmp, the system log file.
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 login prompt is always output with space parity.
X/* DIAGNOSTICS
X/*	All diagnostics are written to the console.
X/*
X/*	In particular, \fIagetty\fR will detect if it is asked to open
X/*	someting that is not a terminal.
X/* AUTHOR(S)
X/*	W.Z. Venema 
X/*	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/*	89/12/11 23:02:55
X/* VERSION/RELEASE
X/*	1.20
X/*--*/
X
X#ifndef lint
Xstatic char sccsid[] = "@(#) agetty.c 1.20 12/11/89 23:02:55";
X#endif
X
X#include <stdio.h>
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. HANGUP should be defined only if your tty
X  * driver is not capable of hanging up the modem (by briefly dropping DTR).
X  * If HANGUP is defined you probably cannot use the auto-baud and time-out
X  * features.
X  */
X
X#define LOGIN "\r\nlogin: "		/* login prompt */
X
X/* #define HANGUP			/* enable hangup code */
X
X/* Some shorthands for control characters */
X
X#define CTL(x)		(x ^ 0100)	/* controllify */
X#define	CR		CTL('M')	/* carriage return */
X#define	NL		CTL('J')	/* linefeed */
X#define	BS		CTL('H')	/* backspace */
X#define	DEL		CTL('?')	/* delete */
X
X/* Default values of special characters; you may want to change this */
X
X#define DEF_INTR	CTL('C')	/* default interrupt character */
X#define DEF_QUIT	CTL('\\')	/* default quit character */
X#define DEF_KILL	CTL('U')	/* default kill character */
X#define DEF_EOF		CTL('D')	/* default EOF character */
X#define DEF_SWITCH	CTL('^')	/* default switch character */
X#define DEF_ERASE	BS		/* default erase character */
X#define DEF_EOL		0
X
X/* Storage for command-line options */
X
X#define	MAXSPEED	10
X
Xstruct options {
X    int     autobaud;			/* process modem status messages */
X    int     timeout;			/* timeout period */
X    int     hangup;			/* hang up tty */
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/* 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	0,				/* no modem status message processing */
X	0,				/* no timeout */
X	1,				/* do hangup */
X	1,				/* no baud-rate cycling */
X	0,				/* use baud-rate argument as speed */
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.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.autobaud)
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)) == 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 should be properly output */
X
X    (void) write(1, "\n", 1);
X
X    /* Let /bin/login take care of password validation */
X
X    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 ((c = getopt(argc, argv, "a:hmt:")) != EOF) {
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->hangup = 0;
X	    break;
X	case 'm':				/* parse modem status message */
X	    op->autobaud = 1;
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
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/* 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/* 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)
Xstruct options *op;
Xstruct chardata *cp;
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    ioctl(0, TCFLSH, (struct termio *) 0);
X
X    /* Read a login name */
X
X    for (*logname = 0; *logname == 0; /* void */ ) {
X
X	/* Write the prompt, with "parity" bit == 0 */
X
X	(void) write(1, LOGIN, sizeof(LOGIN) - 1);
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;		/* send 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
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    error("usage: %s [-a alternate_rates] [-h] [-m] [-t timeout] line baud_rate",
X	  progname);
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}
X
END_OF_FILE
if test 20261 -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'\" \(3388 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
X.SH SYNOPSIS
X.na
X.nf
Xagetty [-a alternate_rates] [-h] [-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 not present in the System
XV 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
Xbackspace (erase); carriage-return and linefeed (end of line).
X.IP o
XOptionally recognizes the baud rate of incoming
Xcalls from the status messages produced by some multi-speed Hayes (tm)
Xmodem clones.
X.PP
XThis program does not use the \fI/etc/gettydefs\fR file. Except for
Xdifferences described here the program appears to operate similar
Xto 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-m
XTry to extract the baud rate of incoming calls from the status message
Xproduced by some Hayes (tm) multi-speed modem clones. 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 this feature will
Xwork only on lightly-loaded systems, you will probably want to use this
Xfeature 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 Hayes clone:
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.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 login prompt is always output with space parity.
X.SH DIAGNOSTICS
X.ad
X.fi
XAll diagnostics are written to the console.
X
XIn particular, \fIagetty\fR will detect if it is asked to open
Xsometing that is not a terminal.
X.SH AUTHOR(S)
X.na
X.nf
XW.Z. Venema
Xwietse@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
X89/12/11 23:02:55
X.SH VERSION/RELEASE
X.na
X.nf
X1.20
END_OF_FILE
if test 3388 -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