Minix1.5/commands/getty.c
/* getty - get tty speed Author: Fred van Kempen */
/*
* GETTY - Initialize and serve a login-terminal for INIT.
* Also, select the correct speed. The STTY() code
* was taken from stty(1).c; which was written by
* Andrew S. Tanenbaum.
* If this program is called as 'uugetty', then it
* also performs some tasks that are needed before
* UUCP can use the line.
*
* Usage: getty [-c filename] [-h] [-k] [-t] line [speed]
*
* Version: 3.4 02/17/90
*
* Author: F. van Kempen, MicroWalt Corporation
*/
#include <sys/types.h>
#include <sgtty.h>
#include <unistd.h>
#include <utmp.h>
#include <ctype.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <pwd.h>
#ifdef NULL /* ugly, I know... */
# undef NULL
#endif /* NULL */
#define NULL 0
#ifdef SIG_IGN /* even more ugly... */
# undef SIG_IGN
# define SIG_IGN 1
#endif /* SIG_IGN */
#ifndef BITS8 /* new-style UNIX or MINIX */
# define BITS5 CS5
# define BITS6 CS6
# define BITS7 CS7
# define BITS8 CS8
#endif /* BITS8 */
#define GDEFS "/etc/gettydefs" /* pathname of getty definitions */
#define ISSUE "/etc/issue" /* System-name textfile */
#define LOGIN1 "/bin/login"
#define LOGIN2 "/usr/bin/login"
#define COMMENT '#' /* this char starts a comment-line */
#define SEPA '#' /* this char separates the fields */
#define EOT '\004'
#define EOF (char) 0
#define STARTC 021 /* CTRL-Q */
#define STOPC 023 /* CTRL-S */
#define QUITC 034 /* CTRL-\ */
#define EOFC 004 /* CTRL-D */
#define DELC 0177 /* DEL */
#define ST_IDLE 44 /* the getty is idle */
#define ST_RUNNING 55 /* getty is now RUNNING */
#define ST_SUSPEND 66 /* getty was SUSPENDed for dialout */
typedef struct {
char *label; /* label of this entry */
char *flags1; /* initial flags */
char *flags2; /* final flags */
char *prompt; /* login-prompt */
char *next; /* next label in chain */
} ENTRY;
#define clr1 tty.sg_flags &= ~(BITS5 | BITS6 | BITS7 | BITS8)
#define clr2 tty.sg_flags &= ~(EVENP | ODDP)
static char *Version = "@(#) GETTY 3.4 (02/17/90)";
char *tty_name; /* what is our TTY? */
ENTRY gdefs[16]; /* we have space for 16 definitions */
ENTRY *defs; /* our GETTY definition */
int uugetty = 0; /* were we called as 'uugetty' ??? */
int nohup = 0; /* NO hangup on exit/close */
int timeout = 0; /* use TIMEOUT */
int keyboard = 0; /* are we using the Console? */
int state = ST_IDLE; /* the IDLE/SUSPEND/RUNNING state flag */
struct sgttyb tty; /* the current terminal state */
void do_stty(); /* forward declaration */
void sigcatch(sig)
int sig;
{
/* Catch the signals that want to catch. */
switch(sig) {
case SIGEMT: /* SIGEMT means SUSPEND */
if (state == ST_IDLE) state = ST_SUSPEND;
break;
case SIGIOT: /* SIGIOT means RESTART */
if (state == ST_SUSPEND) state = ST_RUNNING;
break;
case SIGBUS: /* SIGBUS means IGNORE ALL */
signal(SIGEMT, SIG_IGN);
signal(SIGIOT, SIG_IGN);
state = ST_RUNNING;
break;
}
signal(sig, sigcatch);
}
char agetchar(fd)
int fd;
{
/* Read a character, or EOF if zero chars read. */
static char buf[1024];
static char *bufp = buf;
static int nleft = 0;
if (nleft == 0) {
nleft = read(fd, buf, 1024);
bufp = buf;
}
return((--nleft >= 0) ? *bufp++ : EOF);
}
char *readline(fd)
int fd;
{
/* Read one line from the defs-file. */
char linbuff[512];
register int c, len;
register char *bp = linbuff;
/* Read a line of text. */
len = 0;
while (1) {
c = agetchar(fd);
if (c == '\n' || c == EOF) break;
*bp++ = c;
len++;
}
*bp = '\0';
/* Now allocate a buffer for it. */
bp = (char *)malloc(len + 1);
if (bp == (char *) NULL) return((char *) NULL);
/* Copy this line to its buffer. */
strcpy(bp, linbuff);
return((c == EOF) ? (char *) NULL : bp);
}
void decode(text, ep)
char *text;
ENTRY *ep;
{
/* Decode an input line. */
register char *sp;
sp = text;
while (*sp == ' ' || *sp == '\t') sp++;
ep->label = sp; /* first, decode LABEL field */
while (*sp != '\0' && *sp != SEPA) sp++;
*sp++ = '\0';
while (*sp == ' ' || *sp == '\t') sp++;
ep->flags1 = sp; /* next, INITIAL flags field */
while (*sp != '\0' && *sp != SEPA) sp++;
*sp++ = '\0';
while (*sp == ' ' || *sp == '\t') sp++;
ep->flags2 = sp; /* then, FINAL flags field */
while (*sp != '\0' && *sp != SEPA) sp++;
*sp++ = '\0';
while (*sp == ' ' || *sp == '\t') sp++;
ep->prompt = sp; /* and the LOGIN_PROMPT field */
while (*sp != '\0' && *sp != SEPA) sp++;
*sp++ = '\0';
while (*sp == ' ' || *sp == '\t') sp++;
ep->next = sp; /* finally, decode the NEXT field */
while (*sp != '\0' && *sp != SEPA) sp++;
*sp++ = '\0';
}
int readdefs(name, label)
char *name;
char *label;
{
/* Read the defs-file. */
int defsfd; /* definition file */
int defidx; /* definition index */
register char *bp;
if ((defsfd = open(name, O_RDONLY)) < 0) return(defsfd);
defidx = 0;
defs = &gdefs[defidx]; /* set DEFAULT definition */
while (1) {
bp = readline(defsfd);
if (bp == (char *) NULL) break;
switch (*bp) {
case '\0': /* blank line */
continue;
case '#': /* comment line */
continue;
default: /* data! */
decode(bp, &gdefs[defidx]);
if (label != (char *) NULL) {
if (!strcmp(gdefs[defidx].label, label))
defs = &gdefs[defidx];
}
defidx++;
}
}
close(defsfd);
return(0);
}
void select()
{
/* Select next speed from the chain. */
register int idx;
idx = 0;
while (idx < 16) {
if (!(strcmp(gdefs[idx].label, defs->next))) {
defs = &gdefs[idx];
do_stty(defs->flags1);
return;
}
idx++;
}
}
void showfile(name)
char *name;
{
/* Read a textfile and show it on the desired terminal. */
register int fd;
register int len;
char buff[1024];
if ((fd = open(name, O_RDONLY)) < 0) return;
while (1) {
len = read(fd, buff, 80);
if (len > 0)
write(1, buff, len);
else
break;
}
close(fd);
}
/* Perform some UTMP and WTMP accounting.
* This information is needed for programs like
* who(1), last(1) and write(1)...
*/
void wtmp(line)
char *line; /* tty device name */
{
struct utmp entry, oldent;
char *blank = " ";
register char *sp;
int fd, recno;
extern long time();
/* Strip off the /dev part of the TTY name. */
sp = strrchr(line, '/');
if (sp == NULL) sp = line;
else sp++;
/* First, read the current UTMP entry. We need some of its
* parameters! (like PID, ID etc...)
*/
if ((fd = open(UTMP, O_RDONLY)) < 0) return;
recno = 0;
while (read(fd, (char *) &oldent, (unsigned) sizeof(struct utmp))
== sizeof(struct utmp)) {
if (oldent.ut_pid == getpid()) break;
recno++;
}
close(fd);
recno *= sizeof(struct utmp);
/* Clear out the new string fields. */
strncpy(entry.ut_name, blank, sizeof(entry.ut_name));
strncpy(entry.ut_id, blank, sizeof(entry.ut_id));
strncpy(entry.ut_line, blank, sizeof(entry.ut_line));
/* Enter new string fields. */
strcpy(entry.ut_name, "");
strncpy(entry.ut_id, oldent.ut_id, sizeof(entry.ut_id));
strncpy(entry.ut_line, sp, sizeof(entry.ut_line));
/* Copy old numeric fields. */
entry.ut_pid = oldent.ut_pid;
/* Change new numeric fields. */
entry.ut_type = LOGIN_PROCESS;/* we are waiting for a LOGIN! */
time((time_t *) &(entry.ut_time));
/* Write a WTMP record. */
if ((fd = open(WTMP, O_WRONLY)) > 0) {
if (lseek(fd, 0L, SEEK_END) >= 0L) {
write(fd, (char *) &entry, sizeof(struct utmp));
}
close(fd);
}
/* Rewrite the UTMP entry. */
if ((fd = open(UTMP, O_WRONLY)) > 0) {
if (lseek(fd, (long) recno, SEEK_SET) >= 0L) {
write(fd, (char *) &entry, sizeof(struct utmp));
}
close(fd);
}
}
/* Read one character from stdin.
* If it looks bad (highest bit set, or error return)
* try next speed in the list.
*/
int areadch()
{
int ch, st;
char ch1;
while (1) {
st = read(0, &ch1, 1); /* read character from TTY */
if (st < 0) return(-1); /* SIGNAL received! */
ch = ch1 & 0xFF;
if (keyboard == 1) return(ch);
if ((ch == 0) || ((ch & 128) == 128)) {
select();
ioctl(0, TIOCGETP, &tty);
tty.sg_flags |= RAW;
tty.sg_flags &= ~ECHO;
ioctl(0, TIOCSETP, &tty);
ioctl(0, TIOCFLUSH, (struct sgttyb *) NULL);
} else
return(ch);
}
}
/* Handle the process of a GETTY.
*/
void do_getty(name)
char *name;
{
register char *np, *s;
int ch, bs, can, ucmap, crmap;
char ch1;
showfile(ISSUE); /* what is this all about? */
ioctl(0, TIOCGETP, &tty);
tty.sg_flags |= RAW;
tty.sg_flags &= ~ECHO;
ioctl(0, TIOCSETP, &tty);
bs = tty.sg_erase; /* current ERASE (^H) char */
can = tty.sg_kill; /* current CANCEL (^X) char */
ucmap = 0; /* lower->upper case mapping yet */
crmap = 0; /* no CR to LF mapping yet! */
/* Display prompt, and try to match the caller's speed. */
ch = ' ';
*name = '\0';
while (ch != EOF) {
/* Give us a new line */
write(1, "\r\n", 2);
write(1, defs->prompt, (unsigned) strlen(defs->prompt));
ioctl(0, TIOCFLUSH, (struct sgttyb *) NULL);
np = name;
while (ch != EOF) {
ch = areadch(); /* adaptive READ */
switch (ch) {
case -1: /* signalled! */
if (state == ST_SUSPEND) {
while (state != ST_IDLE) {
pause();
if (state == ST_RUNNING)
state = ST_IDLE;
}
}
ch = ' ';
continue;
case '#':
bs = ch;/* old-style ERASE */
ch1 = ch;
write(1, &ch1, 1);
if (np > name) np--;
break;
case '\b': /* new-style ERASE */
bs = ch;
if (np > name) {
np--;
write(1, "\b \b", 3);
} else
write(1, "\007", 1);
break;
case '@': /* old-style KILL */
can = ch;
np = name;
write(1, "@\r\n", 3);
break;
case 030: /* new-style KILL */
can = ch;
np = name;
write(1, "^X\r\n", 3);
break;
case '\r':
crmap = 1; /* ugly keyboard! */
case '\n':
write(1, "\r\n", 2); /* should map this */
*np = '\0';
ch = EOF;
break;
default:
ch1 = ch;
write(1, &ch1, 1);
*np++ = ch;
}
}
if (*name == '\0') ch = ' '; /* blank line typed! */
}
if (crmap == 1) tty.sg_flags |= CRMOD; /* map input CR to LF */
/* Now check if the typed username contains any any lowercase
* characters. If not, tell kernel to map all lowercase output to
* uppercase! */
s = name;
while (*s != '\0') {
if (*s >= 'a' || *s <= 'z') ucmap = 1;
s++;
}
#ifdef UCMODE
if (ucmap == 1) tty.sg_flags |= UCMODE; /* map LOWER to UPPER */
#endif /* UCMODE */
tty.sg_erase = bs;
tty.sg_kill = can;
tty.sg_flags &= ~RAW;
tty.sg_flags |= ECHO;
ioctl(0, TIOCSETP, &tty);
}
/* Execute the login(1) command with the current
* username as its argument. It will reply to the
* calling user by typing "Password: "...
*/
void do_login(name)
char *name;
{
execl(LOGIN1, LOGIN1, name, (char *) NULL);
execl(LOGIN2, LOGIN2, name, (char *) NULL);
}
/* Convert speed to code. */
int getspeed(spd)
char *spd;
{
int code;
if (!strcmp(spd, "B300"))
code = B300;
else if (!strcmp(spd, "B1200"))
code = B1200;
else if (!strcmp(spd, "B2400"))
code = B2400;
else if (!strcmp(spd, "B4800"))
code = B4800;
else if (!strcmp(spd, "B9600"))
code = B9600;
#ifdef B19200
else if (!strcmp(spd, "B19200"))
code = B19200;
#endif
#ifdef B38400
else if (!strcmp(spd, "B38400"))
code = B38400;
#endif
#ifdef B115200
else if (!strcmp(spd, "B115200"))
code = B115200;
#endif
else
code = -1;
return(code);
}
/* Set the terminal to the specified parameters. */
void do_stty(parms)
char *parms;
{
char prms[80]; /* temp. copy of parameters */
struct tchars tch;
register char *bp, *sp;
int code;
/* Get current TTY status and characters. */
ioctl(0, TIOCGETC, (struct sgttyb *) &tch);
tty.sg_flags = 0;
strncpy(prms, parms, 79);
sp = prms;
/* Process all options. */
while (*sp) {
/* Get an option word. */
bp = sp;
while (*sp && *sp != ' ' && *sp != '\t') sp++;
if (*sp) *sp++ = '\0';
if (!strcmp(bp, "-TABS"))
tty.sg_flags &= ~XTABS;
else if (!strcmp(bp, "-ODD"))
tty.sg_flags &= ~ODDP;
else if (!strcmp(bp, "-EVEN"))
tty.sg_flags &= ~EVENP;
else if (!strcmp(bp, "-RAW"))
tty.sg_flags &= ~RAW;
else if (!strcmp(bp, "-CBREAK"))
tty.sg_flags &= ~CBREAK;
else if (!strcmp(bp, "-ECHO"))
tty.sg_flags &= ~ECHO;
else if (!strcmp(bp, "-DCD"))
tty.sg_flags &= ~DCD;
else if (!strcmp(bp, "-NL"))
tty.sg_flags |= CRMOD;
else if (!strcmp(bp, "TABS"))
tty.sg_flags |= XTABS;
else if (!strcmp(bp, "NONE")) {
clr2;
} else if (!strcmp(bp, "EVEN")) {
clr2;
tty.sg_flags |= EVENP;
} else if (!strcmp(bp, "ODD")) {
clr2;
tty.sg_flags |= ODDP;
} else if (!strcmp(bp, "RAW"))
tty.sg_flags |= RAW;
else if (!strcmp(bp, "CBREAK"))
tty.sg_flags |= CBREAK;
else if (!strcmp(bp, "ECHO"))
tty.sg_flags |= ECHO;
else if (!strcmp(bp, "DCD"))
tty.sg_flags |= DCD;
else if (!strcmp(bp, "NL"))
tty.sg_flags &= ~CRMOD;
else if (!strcmp(bp, "BITS5")) {
clr1;
tty.sg_flags |= BITS5;
} else if (!strcmp(bp, "BITS6")) {
clr1;
tty.sg_flags |= BITS6;
} else if (!strcmp(bp, "BITS7")) {
clr1;
tty.sg_flags |= BITS7;
} else if (!strcmp(bp, "BITS8")) {
clr1;
tty.sg_flags |= BITS8;
} else {
code = getspeed(bp);
if (code >= 0) {
tty.sg_ispeed = code;
tty.sg_ospeed = code;
} else {
if (!strcmp(bp, "SANE") || !strcmp(bp, "DEFAULT")) {
tty.sg_flags = (ECHO | CRMOD | XTABS | BITS8);
tty.sg_ispeed = B1200;
tty.sg_ospeed = B1200;
tty.sg_kill = 'X' & 037;
tty.sg_erase = 'H' & 037;
tch.t_intrc = DELC;
tch.t_quitc = QUITC;
tch.t_startc = STARTC;
tch.t_stopc = STOPC;
tch.t_eofc = EOFC;
}
}
}
}
/* Set new parameters. */
ioctl(0, TIOCSETP, &tty);
ioctl(0, TIOCSETC, (struct sgttyb *) &tch);
}
/* The function carrier() must be called when we are ready
* to service the terminal. Basically, it waits for a valid
* connection by monitoring the DCD line from the modem or
* terminal, which goes high in such an event.
*
* On UNIX systems, it is possible to ask the kernel to
* wake us up if a change in DCD status occurs. This
* program then can go to sleep until that happens.
* Otherwise, we will have to
*
* a) constantly monitor the DCD line.
*
* b) assume no check is necessary.
*
* The first method is somewhat expensive in terms of CPU
* time, and the latter is somewhat ugly from the user's
* point of view. Just pick your choice...
*/
#define CHOICE_B
void do_carrier()
{
#ifdef CHOICE_A
struct sgttyb tp;
#endif
if (keyboard == 1) return;
#ifdef CHOICE_A
state = ST_IDLE;
while (1) { /* Check for DCD every second. */
sleep(1);
if (state == ST_SUSPEND) {
while (state != ST_IDLE) {
pause();
if (state == ST_RUNNING) state = ST_IDLE;
}
}
if (state == ST_IDLE) {
ioctl(0, TIOCGETP, &tp);
if (tp.sg_flags & DCD) return;
}
}
#endif /* CHOICE_A */
#ifdef CHOICE_B
return; /* Just assume no check is necessary. */
#endif /* CHOICE_B */
}
void usage()
{
write(2, "Usage: getty [-c filename] [-h] [-k] [-t] line [speed]\n", 55);
}
int main(argc, argv)
int argc;
char *argv[];
{
char *defsfile = GDEFS;
char *label = (char *) NULL;
struct passwd *pw;
register char *s;
char uname[30];
int c;
/* Ignore all signals. */
for (c = 1; c <= _NSIG; c++) signal(c, SIG_IGN);
/* Determine if we were called as 'uugetty'. */
s = strrchr(argv[0], '/');
if (s == (char *) NULL) s = argv[0];
else s++;
if (*s == 'u') uugetty = 1;
c = 1;
tty_name = (char *) NULL;
while (c < argc) {
s = argv[c++];
if (*s == '-') switch (*++s) {
case 'c': /* new GETTYDEFS file */
defsfile = argv[c++];
break;
case 'h': /* NO HANGUP ON CLOSE */
nohup = 1;
break;
case 'k': /* CONSOLE KEYBOARD */
keyboard = 1;
break;
case 't': /* TIMEOUT */
timeout = 1;
break;
default:
usage();
exit(-1);
} else if (tty_name == (char *) NULL)
tty_name = argv[c - 1];
else
label = argv[c - 1];
}
ioctl(0, TIOCGETP, &tty); /* get line parameters */
tty.sg_ispeed = B0;
tty.sg_ospeed = B0;
if (keyboard == 0) ioctl(0, TIOCSETP, &tty); /* hangup the line */
if (tty_name == (char *) NULL) /* new INIT */
tty_name = ttyname(0);
c = readdefs(defsfile, label);/* GETTYDEFS file into core */
if (c != 0) return(c);
if (uugetty) {
pw = getpwnam("uucp"); /* get UUCP user ID and group ID */
if (pw != (struct passwd *) NULL)
chown(tty_name, pw->pw_uid, pw->pw_gid);
else uugetty = 0;
}
if (!uugetty) chown(tty_name, 1, 1); /* set owner of TTY to BIN.SYS */
do_stty(defs->flags1); /* set INITIAL terminal parameters */
wtmp(tty_name); /* perform UTMP/WTMP accounting */
/* Catch some of the available signals. */
signal(SIGEMT, sigcatch);
signal(SIGIOT, sigcatch);
signal(SIGBUS, sigcatch);
do_carrier(); /* wait for a carrier to come... */
do_getty(uname); /* handle getty() */
uname[29] = '\0'; /* make sure the name fits! */
do_stty(defs->flags2); /* set FINAL terminal parameters */
do_login(uname); /* and call login(1) if OK */
return(0); /* never executed */
}