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