1
0
Files
irix-657m-src/eoe/cmd/getty/getty.c
2022-09-29 17:59:04 +03:00

1880 lines
45 KiB
C

/* (uu)getty - sets up speed, various terminal flags, line discipline,
* and waits for new prospective user to enter name, before
* calling "login".
*/
#ident "$Revision: 1.80 $"
#ifndef UUGETTY
#define USAGE "usage: getty [-hN] [-t time] [-s oline] line [speed [terminal [ldisc]]]\n"
#endif
/* -h says don't hangup by dropping carrier during the
* initialization phase. Normally carrier is dropped to
* make the dataswitch release the line.
* -N honors /etc/nologin
* -t says timeout after the number of seconds in "time" have
* elapsed even if nothing is typed. This is useful
* for making sure dialup lines release if someone calls
* in and then doesn't actually login in.
* -L de overwrites the environment variable LANG
* "line" is the device in "/dev".
* "speed" is a pointer into the "/etc/getty_defs" where the
* definition for the speeds and other associated flags are
* to be found.
* "term" is the name of the terminal type.
* "ldisc" is the name of the line discipline.
*/
#ifdef UUGETTY
#define USAGE "usage: uugetty [-hNDr] [-t time] [-i dial1[,dial2] [-d secs] line [speed [terminal [ldisc]]]\n"
#endif
/* -D requests debugging
* -r requires that a character other than EOT be received before
* continuing. This cannot be used if two chat scripts
* are used with -i.
* -i dialer says to initialize the line with the labeled entry in
* /etc/uucp/Dialers
* -i dialer1,dialer2 says to initialize with the first Dialers entry
* and to use the second to wait for "CONNECT".
* -d delays a number of seconds after the first character before
* believing anything.
*/
/* Usage: getty -c gettydefs_like_file
*
* The "-c" flag is used to have "getty" check a gettydefs file.
* "getty" parses the entire file and prints out its findings so
* that the user can make sure that the file contains the proper
* information.
*/
#ifndef UUGETTY
#define uugetty_ck(line)
#define delock(line)
#define UUCPUID 0
int Debug;
#ifdef sgi
#include <limits.h>
#define AUX_LANG /* LANG handling on */
#endif /* sgi */
#define TRUE 1
#define FALSE 0
#define SUCCESS 0
#endif /* !UUGETTY */
#define FAILURE (-1)
#define ID 1
#define IFLAGS 2
#define FFLAGS 3
#define MESSAGE 4
#define NEXTID 5
#define ACTIVE 1
#define FINISHED 0
#include <unistd.h>
#include <stdlib.h>
#include <getopt.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <termio.h>
#include <signal.h>
#include <fcntl.h>
#include <utmp.h>
#include <pwd.h>
#include <ctype.h>
#include <string.h>
#include <bstring.h>
#include <paths.h>
#include <syslog.h>
#include <errno.h>
#include <stdio.h>
#include <sys/capability.h>
#ifdef UUGETTY
#include "uucp.h"
#undef DEBUG
static void uugetty_ck(char *);
#endif
struct Gdef {
char *g_id; /* identification for modes & speeds */
struct termio g_iflags; /* initial terminal flags */
struct termio g_fflags; /* final terminal flags */
char *g_message; /* login message */
char *g_nextid; /* next id if this speed is wrong */
};
static struct Gdef *find_def(char *id);
static void parse(char *, char **, int);
static void badcall(char*, char*, int, int);
static char *getvar(char*, int *);
#define GOODNAME 1
#define NONAME 0
#define BADSPEED (-1)
#define MAXIDLENGTH 15 /* Maximum length the "g_id" and "g_nextid"
* strings can take. Longer ones will be
* truncated.
*/
#define OMAXMESS 79 /* old max message length */
#define MAXMESSAGE 512 /* Maximum length the "g_message" string
* can be. Longer ones are truncated.
*/
/* Maximum length of line in /etc/gettydefs file and the maximum */
/* length of the user response to the "login" message. */
#define OMAXLINE 255 /* original MAXLINE */
#define MAXLINE OMAXLINE-OMAXMESS+MAXMESSAGE /* 688 */
#define MAXARGS 64 /* Maximum number of arguments that can be
* passed to "login"
*/
struct Symbols {
char *s_symbol; /* Name of symbol */
unsigned s_value ; /* Value of symbol */
};
/* The following four symbols define the "SANE" state. */
#define ISANE (BRKINT|IGNPAR|ISTRIP|ICRNL|IXON)
#define OSANE (OPOST|ONLCR)
#define CSANE (CS8|CREAD)
#ifdef IEXTEN
#define LSANE (ISIG|ICANON|ECHO|ECHOE|ECHOK|IEXTEN)
#else
#define LSANE (ISIG|ICANON|ECHO|ECHOE|ECHOK)
#endif
/* Modes set with the TCSETAW ioctl command. */
static struct Symbols imodes[] = {
"IGNBRK", IGNBRK,
"BRKINT", BRKINT,
"IGNPAR", IGNPAR,
"PARMRK", PARMRK,
"INPCK", INPCK,
"ISTRIP", ISTRIP,
"INLCR", INLCR,
"IGNCR", IGNCR,
"ICRNL", ICRNL,
"IUCLC", IUCLC,
"IXON", IXON,
"IXANY", IXANY,
"IXOFF", IXOFF,
NULL, 0
};
static struct Symbols omodes[] = {
"OPOST", OPOST,
"OLCUC", OLCUC,
"ONLCR", ONLCR,
"OCRNL", OCRNL,
"ONOCR", ONOCR,
"ONLRET", ONLRET,
"OFILL", OFILL,
"OFDEL", OFDEL,
"NLDLY", NLDLY,
"NL0", NL0,
"NL1", NL1,
"CRDLY", CRDLY,
"CR0", CR0,
"CR1", CR1,
"CR2", CR2,
"CR3", CR3,
"TABDLY", TABDLY,
"TAB0", TAB0,
"TAB1", TAB1,
"TAB2", TAB2,
"TAB3", TAB3,
"BSDLY", BSDLY,
"BS0", BS0,
"BS1", BS1,
"VTDLY", VTDLY,
"VT0", VT0,
"VT1", VT1,
"FFDLY", FFDLY,
"FF0", FF0,
"FF1", FF1,
NULL, 0
};
static struct Symbols cmodes[] = {
"CS5", CS5,
"CS6", CS6,
"CS7", CS7,
"CS8", CS8,
"CSTOPB", CSTOPB,
"CREAD", CREAD,
"PARENB", PARENB,
"PARODD", PARODD,
"HUPCL", HUPCL,
"CLOCAL", CLOCAL,
NULL, 0
};
/*
* Speed keywords. Words of the form B[0-9]+ (B9600, etc.) are compared
* in the code. We don't list them here because there's approximately
* 2^32 possible values.
*/
static struct Symbols smodes[] = {
"EXTA", EXTA,
"EXTB", EXTB,
NULL, 0
};
static struct Symbols lmodes[] = {
"ISIG", ISIG,
"ICANON", ICANON,
"XCASE", XCASE,
"ECHO", ECHO,
"ECHOE", ECHOE,
"ECHOK", ECHOK,
"ECHONL", ECHONL,
"NOFLSH", NOFLSH,
NULL, 0
};
/* Terminal types set with the LDSETT ioctl command. */
static struct Symbols terminals[] = {
"none", TERM_NONE,
#ifdef TERM_V10
"vt100", TERM_V10,
#endif
#ifdef TERM_H45
"hp45", TERM_H45,
#endif
#ifdef TERM_C10
"c100", TERM_C100,
#endif
#ifdef TERM_TEX
"tektronix", TERM_TEX,
"tek", TERM_TEX,
#endif
#ifdef TERM_D40
"ds40-1", TERM_D40,
#endif
#ifdef TERM_V61
"vt61", TERM_V61,
#endif
#ifdef TERM_TEC
"tec", TERM_TEC,
#endif
NULL, 0
};
static struct Symbols linedisc[] = {
"LDISC0", LDISC0,
"LDISC1", LDISC1,
NULL, 0
};
/* If the /etc/gettydefs file can't be opened, the following */
/* default is used. */
static struct Gdef default_gdef = {
"default",
{ ICRNL, 0, CREAD+HUPCL,
0, B9600, B9600, LDISC1},
{ ICRNL, OPOST+ONLCR+NLDLY+TAB3, CS7+CREAD+HUPCL,
ISIG+ICANON+ECHO+ECHOE+ECHOK, B9600, B9600, LDISC1},
"LOGIN: ",
"default",
};
#define ISSUE_FILE _PATH_ISSUE
#define GETTY_DEFS "/etc/gettydefs"
static int check;
static char *checkgdfile; /* Name of gettydefs file during check mode. */
static int nologin; /* wait for /etc/nologin to disappear */
#ifdef AUX_LANG
static char envlang[NL_LANGMAX]; /* LANG environment variable */
#endif /* AUX_LANG */
int wait_read; /* wait for read before output "login" */
char *chat1_str = 0; /* chat-up the modem before starting */
static char *chat2_str = 0; /* jabber after "RING" until "CONNECT" */
#define CHAT_MAX 50
char *chat_args[CHAT_MAX+1];
static int dead_time = 0; /* delay this many seconds after 1st char */
static char *line_locked = 0; /* locked device */
/* "quoted" takes a quoted character, starting at the quote */
/* character, and returns a single character plus the size of */
/* the quote string. "quoted" recognizes the following as */
/* special, \n,\r,\v,\t,\b,\f as well as the \nnn notation. */
static char
quoted(char *ptr,
int *qsize)
{
register char c,*rptr;
register int i;
rptr = ptr;
switch(*++rptr) {
case 'n':
c = '\n';
break;
case 'r':
c = '\r';
break;
case 'v':
c = '\013';
break;
case 'b':
c = '\b';
break;
case 't':
c = '\t';
break;
case 'f':
c = '\f';
break;
default:
/* If this is a numeric string, take up to three characters of */
/* it as the value of the quoted character. */
if (*rptr >= '0' && *rptr <= '7') {
for (i=0,c=0; i < 3;i++) {
c = c*8 + (*rptr - '0');
if (*++rptr < '0' || *rptr > '7') break;
}
rptr--;
/* If the character following the '\\' is a NULL, back up the */
/* ptr so that the NULL won't be missed. The sequence */
/* backslash null is essentually illegal. */
} else if (*rptr == '\0') {
c = '\0';
rptr--;
/* In all other cases the quoting does nothing. */
} else c = *rptr;
break;
}
/* Compute the size of the quoted character. */
(*qsize) = rptr - ptr + 1;
return(c);
}
static char *
getword(register char *ptr,
int *size)
{
register char *optr,c;
static char word[MAXIDLENGTH+1];
int qsize;
/* Skip over all white spaces including quoted spaces and tabs. */
for (*size=0; isspace(*ptr) || *ptr == '\\';) {
if (*ptr == '\\') {
c = quoted(ptr,&qsize);
(*size) += qsize;
ptr += qsize+1;
/* If this quoted character is not a space or a tab or a newline */
/* then break. */
if (isspace(c) == 0) break;
} else {
(*size)++;
ptr++;
}
}
/* Put all characters from here to next white space or '#' or '\0' */
/* into the word, up to the size of the word. */
for (optr= word,*optr='\0'; isspace(*ptr) == 0 &&
*ptr != '\0' && *ptr != '#'; ptr++,(*size)++) {
/* If the character is quoted, analyze it. */
if (*ptr == '\\') {
c = quoted(ptr,&qsize);
(*size) += qsize;
ptr += qsize;
} else c = *ptr;
/* If there is room, add this character to the word. */
if (optr < &word[MAXIDLENGTH+1] ) *optr++ = c;
}
/* Make sure the line is null terminated. */
*optr++ = '\0';
return(word);
}
/* "search" scans through a table of Symbols trying to find a */
/* match for the supplied string. If it does, it returns the */
/* pointer to the Symbols structure, otherwise it returns NULL. */
static struct Symbols *
search(register char *target,
register struct Symbols *symbols)
{
/* Each symbol array terminates with a null pointer for an */
/* "s_symbol". Scan until a match is found, or the null pointer */
/* is reached. */
for (;symbols->s_symbol != NULL; symbols++)
if (strcmp(target,symbols->s_symbol) == 0) return(symbols);
return(NULL);
}
/* "fields" picks up the words in the next field and converts all */
/* recognized words into the proper mask and puts it in the target */
/* field. */
static char *
fields(register char *ptr,
struct termio *termio)
{
extern struct Symbols imodes[],omodes[],cmodes[],lmodes[];
register struct Symbols *symbol;
char *word;
int size;
extern int check;
termio->c_iflag = 0;
termio->c_oflag = 0;
termio->c_cflag = 0;
termio->c_ospeed = 0;
termio->c_lflag = 0;
while (*ptr != '#' && *ptr != '\0') {
/* Pick up the next word in the sequence. */
word = getword(ptr,&size);
/* If there is a word, scan the two mode tables for it. */
if (*word != '\0') {
/* If the word is the special word "SANE", put in all the flags */
/* that are needed for SANE tty behavior. */
if (strcmp(word,"SANE") == 0) {
termio->c_iflag |= ISANE;
termio->c_oflag |= OSANE;
termio->c_cflag |= CSANE;
termio->c_lflag |= LSANE;
} else if ((symbol = search(word,imodes)) != NULL)
termio->c_iflag |= symbol->s_value;
else if ((symbol = search(word,omodes)) != NULL)
termio->c_oflag |= symbol->s_value;
else if ((symbol = search(word,cmodes)) != NULL)
termio->c_cflag |= symbol->s_value;
else if ((symbol = search(word,lmodes)) != NULL)
termio->c_lflag |= symbol->s_value;
else if ((symbol = search(word,smodes)) != NULL)
termio->c_ospeed = symbol->s_value;
else if ((word[0] == 'B')
&& (strspn(word+1,"0123456789")
== strlen(word+1)))
termio->c_ospeed = (speed_t)strtoul(word+1,0,0);
else if (check)
fprintf(stdout,"Undefined: %s\n",word);
}
/* Advance pointer to after the word. */
ptr += size;
}
/* If we didn't end on a '#', return NULL, otherwise return the */
/* updated pointer. */
return(*ptr != '#' ? NULL : ptr);
}
/* pause to reflect after SIGHUP */
static void
huphand(void)
{
if (Debug)
syslog(LOG_DEBUG, "quiting after SIGHUP");
(void)close(0);
(void)close(1);
(void)close(2);
sginap(5*HZ);
exit(1);
}
static void
setupline(register struct Gdef *speedef,
int termtype,
int lined)
{
struct termio termio;
struct termcb termcb;
/* Set the terminal type to "none", which will clear all old */
/* special flags, if a terminal type was set from before. */
termcb.st_flgs = 0;
termcb.st_termt = TERM_NONE;
termcb.st_vrow = 0;
(void)ioctl(0,LDSETT,&termcb);
termcb.st_termt = termtype;
(void)ioctl(0,LDSETT,&termcb);
/* Get the current state of the modes and such for the terminal. */
if (0 > ioctl(0,TCGETA,&termio))
badcall("ioctl(TCGETA%s)%d: %m", "",__LINE__, 0);
if (termtype != TERM_NONE) {
/* If there is a terminal type, take away settings so that */
/* terminal is "raw" and "no echo". Also take away the orginal */
/* speed setting. */
termio.c_iflag = 0;
termio.c_cflag &= ~(CSIZE|PARENB);
termio.c_cflag |= CS8|CREAD|HUPCL;
termio.c_lflag &= ~(ISIG|ICANON|ECHO|ECHOE|ECHOK);
/* Add in the speed. */
termio.c_ospeed = (speedef->g_iflags.c_ospeed);
} else {
termio.c_iflag = speedef->g_iflags.c_iflag;
termio.c_oflag = speedef->g_iflags.c_oflag;
#ifdef CNEW_RTSCTS
/* preserve CNEW_RTSCTS so flow control will still work
* if line is a ttyf port
*/
termio.c_cflag = (termio.c_cflag & CNEW_RTSCTS) |
speedef->g_iflags.c_cflag;
#else
termio.c_cflag = speedef->g_iflags.c_cflag;
#endif
termio.c_ospeed = speedef->g_iflags.c_ospeed;
termio.c_lflag = speedef->g_iflags.c_lflag;
}
/* Make sure that raw reads are 1 character at a time with no */
/* timeout. */
termio.c_cc[VMIN] = 1;
termio.c_cc[VTIME] = 0;
/* Add the line discipline. */
termio.c_line = lined;
if (0 > ioctl(0,TCSETAF,&termio))
badcall("ioctl(TCSETAF%s)%d: %m", "",__LINE__, 0);
/* get rid of any cruft on both input and output */
sginap(HZ/2);
if (0 > ioctl(0,TCFLSH,2))
badcall("ioctl(TCFLSH%s)%d: %m", "",__LINE__, 0);
}
static void
openline(register char *line,
register struct Gdef *speedef,
int termtype,
int lined,
int hangup)
{
#ifdef UUGETTY
int i;
#endif
struct stat statb;
register FILE *f;
register int lfd;
struct termio lterm;
cap_t ocap;
cap_value_t cap_fowner = CAP_FOWNER;
cap_value_t cap_device_mgt = CAP_DEVICE_MGT;
/* we want the line to be our controlling tty */
lfd = open("/dev/tty",0);
if (lfd >= 0) {
(void)ioctl(lfd, TIOCNOTTY, 0);
(void)close(lfd);
}
setpgrp(); /* just in case, for debugging, ... */
(void)signal(SIGHUP,SIG_IGN); /* do not worry about carrier yet */
uugetty_ck(line); /* quit if UUCP is using line */
errno = 0;
lfd = open(line,O_RDWR|O_NDELAY);
if (lfd > 0) {
if (Debug != 0)
badcall("failed to open \"%s\" as FD 0: fd=%d",
line,lfd, 0);
(void)close(lfd);
closelog(); /* in case FD 0,1, or 2 */
errno = 0;
lfd = open(line,O_RDWR|O_NDELAY);
}
if (lfd != 0)
badcall("cannot open \"%s\" as FD 0: %m, fd=%d", line,lfd, 1);
/* Change the ownership of the terminal line to uucp or root and set */
/* the protections to only allow him to read the line. */
stat(line,&statb);
ocap = cap_acquire(1, &cap_fowner);
chown(line,UUCPUID,statb.st_gid);
chmod(line,0622);
cap_surrender(ocap);
if (hangup) {
if (speedef->g_fflags.c_cflag & HUPCL) {
if (0 > ioctl(0,TCGETA,&lterm))
badcall("ioctl(TCGETA,\"%s\"): %m", line,0, 0);
lterm.c_ospeed = B0;
lterm.c_lflag &= ~(ECHO|ECHOE|ECHOK);
if (0 > ioctl(0,TCSETAF,&lterm)
&& errno != ENXIO)
badcall("ioctl(TCSETAF,\"%s\"): %m", line,0, 0);
sginap(HZ*2); /* let the modem catch up */
}
ocap = cap_acquire(1, &cap_device_mgt);
if (0 > vhangup() && Debug != 0) {
cap_surrender(ocap);
badcall("vhangup(%s): %m", line,0, 1);
}
cap_surrender(ocap);
if (0 > close(0))
badcall("close(\"%s\"): %m", line,0, 0);
#ifdef UUGETTY
lfd = open(line,O_RDWR|O_NDELAY);
#else
lfd = open(line,O_RDWR);
#endif /* UUGETTY */
}
f = fdopen(lfd, "r+");
if (lfd != 0 || f != stdin)
badcall("cannot fdopen \"%s\" as FD 0: %m fd=%d", line,lfd,1);
if (0 > fcntl(0, F_SETFL,
fcntl(0, F_GETFL, -1) & ~O_NDELAY)) {
badcall("fcntl(\"%s\"): %m",line,0, 1);
}
(void)signal(SIGHUP,huphand); /* now quit if line does */
/* do not echo anything until we really have the line and it is
* healthy.
*/
setupline(speedef,termtype,lined);
if (0 > ioctl(0,TCGETA,&lterm))
badcall("ioctl(TCGETA2,\"%s\"): %m", line,0, 1);
lterm.c_lflag &= ~(ECHO|ECHOE|ECHOK);
if (0 > ioctl(0,TCSETAF,&lterm))
badcall("ioctl(TCSETAF2, \"%s\"): %m", line,0, 1);
#ifdef UUGETTY
/* chat with the modem if necessary */
if (chat1_str) {
if (Debug)
syslog(LOG_DEBUG, "1st chat with modem on %s", line);
setservice("uugetty");
i = gdial(chat1_str,chat_args,CHAT_MAX);
if (0 == i)
badcall("unknown Dialer script \"%s\"",chat1_str,0, 1);
if (i > 2) {
if (0 != chat(i-2, chat_args+2, 0, 0,0))
badcall("modem initialization failed on %s",
line,0, 1);
/* forget any stray modem cruft */
sginap(HZ);
if (0 > ioctl(0,TCFLSH,2))
badcall("ioctl(TCFLSH,\"%s\"): %m", line,0, 1);
}
}
delock(line_locked);
line_locked = 0;
/* wait until there is some input, and then try to relock the line */
i = 0;
for (;;) {
fd_set readfds;
FD_ZERO(&readfds);
FD_SET(0,&readfds);
/* quit if the line becomes sick */
if (0 > select(0+1,&readfds,0,0,0)) {
if (errno == EAGAIN || errno == EINTR)
continue;
badcall((i == 0 || Debug) ? "select(\"%s\"): %m" : 0,
line,0, 1);
}
if (nologin && !access(_PATH_NOLOGIN, F_OK)) {
(void)close(0);
syslog(LOG_DEBUG,
"\"%s\" existed upon awakening for %s",
_PATH_NOLOGIN, line);
do {
sginap(HZ);
} while (!access(_PATH_NOLOGIN, F_OK));
exit(1);
}
if (FD_ISSET(0, &readfds)) {
uugetty_ck(line); /* lock the line */
break;
}
}
/* restore modes in case UUCP changed them while we waited
* flush any modem cruft unless we will chat the modem
*/
if (0 > ioctl(0, chat2_str ? TCSETA : TCSETAF, &lterm))
badcall("ioctl(TCSETAF3, \"%s\"): %m", line,0, 1);
if (dead_time != 0) { /* wait for modem cruft */
/* be sure modem cruft is not flow controlled. */
if (0 > ioctl(0,TCFLSH,2))
badcall("ioctl(TCFLSH, \"%s\"): %m", line,0, 1);
sginap(dead_time*HZ);
if (0 > ioctl(0,TCFLSH,2))
badcall("ioctl(TCFLSH, \"%s\"): %m", line,0, 1);
}
/* wait to read the first character */
if (wait_read) {
char buffer;
/*
* Check for read failure or EOT.
* (EOT may come from a cu on the other side.)
* This code is to prevent the situation of
* "login" program getting started here while
* a uugetty is running on the other end of the line.
* NOTE: Cu on a direct line when ~. is encountered will
* send EOTs to the other side. EOT=\004
*/
i = read(0, &buffer, 1);
if (i != 1 || buffer == '\004') {
char *msg = 0;
if (i < 0) {
msg = "-r read: %m";
} else if (Debug) {
if (i == 0)
msg = "-r read produced %d";
else
msg = "-r read receive EOT";
}
badcall(msg, 0,0, 1);
}
}
/* look for CONNECT. If we dont get it, then quit quietly. */
if (chat2_str) {
if (Debug)
syslog(LOG_DEBUG, "2nd chat with modem on %s", line);
setservice("uugetty");
i = gdial(chat2_str,chat_args,CHAT_MAX);
if (0 == i)
badcall("unknown Dialer script \"%s\"",chat2_str,0, 1);
if (i > 2) {
if (0 != chat(i-2, chat_args+2, 0, 0,0)) {
if (Debug != 0)
badcall("2nd chat failed on \"%s\"",
line,0, 0);
exit(1);
}
}
/* forget any stray modem cruft */
sginap(HZ/2);
if (0 > ioctl(0,TCFLSH,2))
badcall("ioctl(TCFLSH, \"%s\"): %m", line,0, 1);
}
#endif /* UUGETTY */
/* check for /etc/nologin in case it was created while we were
* waiting for a carrier.
*/
if (nologin && !access(_PATH_NOLOGIN, F_OK)) {
(void)close(0);
syslog(LOG_DEBUG, "\"%s\" existed upon awakening for %s",
_PATH_NOLOGIN, line);
do {
sginap(HZ);
} while (!access(_PATH_NOLOGIN, F_OK));
exit(1);
}
ocap = cap_acquire(1, &cap_fowner);
chown(line,0,statb.st_gid); /* we now own the line */
chmod(line,0622);
cap_surrender(ocap);
closelog(); /* in case FD 0,1, or 2 */
fclose(stdout);
fclose(stderr);
if (0 > dup2(0,1)
|| 0 == fdopen(1, "r+"))
badcall("failed to open \"%s\" as STDOUT: %m", line,0, 1);
if (0 > dup2(0,2)
|| 0 == fdopen(2, "r+"))
badcall("failed to open \"%s\" as STDERR: %m", line,0, 1);
setbuf(stdin,NULL);
setbuf(stdout,NULL);
setbuf(stderr,NULL);
/* Set the terminal type and line discipline, after we know we own it. */
setupline(speedef,termtype,lined);
}
static void
account(char *line)
{
register int ownpid;
register int ppid;
register struct utmp *u;
register int fd;
/* Look in "utmp" file for our own entry and change it to LOGIN. */
ownpid = getpid();
ppid = getppid();
while ((u = getutent()) != NULL) {
/* Is this our own entry? */
if (u->ut_type == INIT_PROCESS &&
(u->ut_pid == ownpid || u->ut_pid == ppid)) {
strncpy(u->ut_line,line,sizeof(u->ut_line));
strncpy(u->ut_user,"LOGIN",sizeof(u->ut_user));
u->ut_type = LOGIN_PROCESS;
u->ut_pid = ownpid;
/* Write out the updated entry. */
pututline(u);
break;
}
}
/* If we were successful in finding an entry for ourself in the */
/* utmp file, then attempt to append to the end of the wtmp file. */
if (u != NULL && (fd = open(WTMP_FILE, O_WRONLY|O_APPEND)) >= 0) {
cap_value_t cap_dac_write = CAP_DAC_WRITE;
cap_t ocap = cap_acquire(1, &cap_dac_write);
write(fd, (char *)u, sizeof(*u));
cap_surrender(ocap);
close(fd);
}
/* Close the utmp file. */
endutent();
}
/*
* Look for /etc/autologin.TTYLINE and use its contents as the login name if
* it exists.
*/
static int
check_autologin(char *line, char *name)
{
char *autopath;
register FILE *autol;
int len;
len = sizeof ("/etc/autologin") + strlen (line)+1 + sizeof (".on");
if ((autopath = malloc (len)) == NULL) {
return (NONAME);
}
if (sprintf (autopath, "%s.%s.on", "/etc/autologin", line) < 0) {
free (autopath);
return (NONAME);
}
if (access (autopath, 0) == 0) {
unlink (autopath);
autopath[strlen(autopath)-3] = NULL;
if ((autol = fopen(autopath, "r")) != NULL) {
if (fscanf (autol, "%s", name) == EOF) {
free (autopath);
return (NONAME);
}
putc('\n',stdout);
free (autopath);
return(GOODNAME);
}
}
free (autopath);
return (NONAME);
}
/* Pick up the user's name from the standard input or
* /etc/autologin.TTYLINE if it exists.
* If it sees a line terminated with a <linefeed>, it sets ICRNL.
*/
static int
getname(char *user,
char *line,
struct termio *termio)
{
register char *ptr,c;
register int rawc;
/* Get the previous modes, erase, and kill characters and speeds. */
if (0 > ioctl(0,TCGETA,termio))
badcall("ioctl(TCGETA%s)%d: %m", "",__LINE__, 0);
termio->c_iflag &= ICRNL;
termio->c_oflag = 0;
#ifdef CNEW_RTSCTS
termio->c_cflag &= CNEW_RTSCTS;
#else
termio->c_cflag = 0;
#endif
termio->c_lflag &= ECHO;
termio->c_cc[VEOF] = CEOF; /* fix damage from RAW mode, but */
termio->c_cc[VEOL] = 0; /* assume no other changes to c_cc[] */
if (check_autologin (line, user) == GOODNAME) {
return (GOODNAME);
}
ptr = user;
do {
/* If it isn't possible to read line, exit. */
if ((rawc = getc(stdin)) == EOF)
huphand();
/* If a null character was typed, try another speed. */
if ((c = (rawc & 0177)) == '\0')
return(BADSPEED);
if (c == '\b' || c == '#') {
/* If there is anything to erase, erase a character. */
if (ptr > user) {
--ptr;
if (*ptr >= ' ' && *ptr < 127)
printf("\b \b");
}
/* If the character is a kill line, start the line over */
} else if (c == '@' || c == CDEL || c == CKILL
|| c == CTRL('X')) {
while (ptr > user) {
ptr--;
if (*ptr >= ' ' && *ptr < 127)
printf("\b \b");
}
} else {
if ((termio->c_lflag&ECHO) == 0)
putc(rawc,stdout);
*ptr++ = c;
}
/* Continue the above loop until a line terminator is found or */
/* until user name array is full. */
} while (c != '\n' && c != '\r' && ptr < (user + MAXLINE));
/* Remove the last character from name. */
*--ptr = '\0';
if (ptr == user) return(NONAME);
/* If the line terminated with a <lf>, put ICRNL and ONLCR into */
/* into the modes. */
if (c == '\r') {
putc('\n',stdout);
termio->c_iflag |= ICRNL;
termio->c_oflag |= ONLCR;
/* When line ends with a <lf>, then add the <cr>. */
} else putc('\r',stdout);
return(GOODNAME);
}
main(argc,argv)
int argc;
char **argv;
{
int i;
char *errmsg;
char *line;
#ifdef AUX_LANG
char *lang = 0;
#endif /* AUX_LANG */
register struct Gdef *speedef;
char oldspeed[MAXIDLENGTH+1],newspeed[MAXIDLENGTH+1];
int termtype,lined;
int hangup,timeout;
extern struct Symbols terminals[],linedisc[];
extern void timedout();
register struct Symbols *answer;
char user[MAXLINE],*largs[MAXARGS],*ptr,buffer[MAXLINE];
FILE *fp;
struct termio termio;
#define ESC 27
static char clrscreen[] = {ESC,'H',ESC,'J'};
signal(SIGINT,SIG_IGN);
signal(SIGQUIT,SIG_IGN); /* no reason to drop a core */
#ifdef UUGETTY
openlog("uugetty", LOG_PID|LOG_CONS|LOG_ODELAY, LOG_AUTH);
#else
openlog("getty", LOG_PID|LOG_CONS|LOG_ODELAY, LOG_AUTH);
#endif
hangup = TRUE;
timeout = 0;
errmsg = USAGE;
opterr = 0;
while ((i = getopt(argc, argv, "hDNri:d:t:c:L:")) != -1) {
switch (i) {
case 'h':
hangup = FALSE;
break;
/* Turn on some debugging */
case 'D':
Debug = 9;
break;
/* wait for /etc/nologin to go away */
case 'N':
nologin =1;
break;
/* wait to receive something from the line before proceeding */
case 'r':
wait_read = TRUE;
break;
/* remember to chat with the modem before starting */
case 'i':
chat1_str = optarg;
chat2_str = strchr(optarg,',');
if (0 != chat2_str)
*chat2_str++ = '\0';
break;
/* delay after initial character */
case 'd':
dead_time = strtol(optarg,&ptr,0);
if (dead_time <= 0
|| dead_time > 100
|| *ptr != '\0') {
syslog(LOG_ERR,"invalid delay \"%s\"",optarg);
errmsg = 0;;
}
break;
case 't':
timeout = strtol(optarg,&ptr,0);
if (timeout <= 0
|| timeout > 100
|| *ptr != '\0') {
syslog(LOG_ERR,"invalid timeout \"%s\"",optarg);
errmsg = 0;;
}
break;
/* Check a "gettydefs" file mode. */
case 'c':
check = TRUE;
checkgdfile = optarg;
signal(SIGINT,SIG_DFL);
fp = fopen(checkgdfile,"r");
if ((fp = fopen(checkgdfile,"r")) == NULL) {
fprintf(stderr,"Cannot open %s\n",checkgdfile);
exit(1);
}
fclose(fp);
/* Call "find_def" to check the check file.
* With the "check" flag set, it will parse the
* entire file, printing out the results.
*/
find_def(NULL);
exit(0);
#ifdef AUX_LANG
case 'L' :
lang = optarg;
break;
#endif /* AUX_LANG */
default:
syslog(LOG_ERR,errmsg);
errmsg = 0;
break;
}
}
/* There must be at least one operand. If there isn't, complain. */
argc -= optind;
argv += optind;
if (argc <= 0 || **argv == '-' || errmsg == 0)
badcall(errmsg,0,0, 1);
line = *argv;
/* point the UUCP chat code at the system log */
(void)fclose(stdin);
(void)close(0);
(void)fflush(stdout);
(void)close(1);
(void)fflush(stderr);
(void)close(2);
#if !defined(UUGETTY) && defined(AUX_LANG)
if(lang) {
(void)strcpy(envlang, "LANG=");
(void)strcat(envlang, lang);
(void)putenv(envlang);
}
#endif /* !defined(UUGETTY) && defined(AUX_LANG) */
#ifdef UUGETTY
if (Debug) {
static char pidstr0[] = "uugetty[%d]";
static char pidstr[sizeof(pidstr0)+8];
int fildes[2];
pid_t pid;
int serror;
char *es;
if (0 > pipe(fildes)) {
pid = -1;
es = "pipe() failed for log";
} else {
pid = fork();
es = "fork() failed for log";
}
if (pid == 0) {
sprintf(pidstr,pidstr0,getppid());
(void)alarm(0);
(void)signal(SIGHUP,SIG_IGN);
(void)signal(SIGINT,SIG_IGN);
(void)signal(SIGUSR1,SIG_IGN);
(void)signal(SIGUSR2,SIG_IGN);
(void)signal(SIGTERM,SIG_IGN);
if (0 <= dup2(fildes[0],0)) {
for (i = getdtablehi(); --i > 0; )
(void)close(i);
(void)execlp("logger", "logger",
"-t", pidstr,
"-p", "daemon.debug", 0);
}
syslog(LOG_ERR, "failed to start logger: %m");
exit(-1);
} else {
(void)close(fildes[0]);
es = "unable to dup2() log";
if (0 > dup2(fildes[1],1)
|| 0 > dup2(fildes[1],2))
pid = -1;
if (fildes[1] != 1 && fildes[1] != 2)
(void)close(fildes[1]);
}
if (pid < 0) {
serror = errno;
(void)freopen("/dev/log", "a", stderr);
errno = serror;
badcall(es, 0,0, 1);
}
setlinebuf(stdout);
setlinebuf(stderr);
}
#endif /* UUGETTY */
/* If a "speed" was provided, search for it in the */
/* "getty_defs" file. If none was provided, take the first entry */
/* of the "getty_defs" file as the initial settings. */
if (--argc > 0 ) {
if ((speedef = find_def(*++argv)) == NULL) {
syslog(LOG_ERR,"unable to find \"%s\" in %s for %s",
*argv,GETTY_DEFS,line);
/* Use the default value instead. */
speedef = find_def(NULL);
}
} else speedef = find_def(NULL);
/* If a terminal type was supplied, try to find it in list. */
termtype = TERM_NONE;
if (--argc > 0) {
answer = search(*++argv,terminals);
if (0 != answer)
termtype = answer->s_value;
else
syslog(LOG_ERR,"%s is an undefined terminal type.",
*argv);
}
/* If a line discipline was supplied, try to find it in list. */
lined = LDISC1;
if (--argc > 0) {
answer = search(*++argv,linedisc);
if (answer != 0)
lined = answer->s_value;
else
syslog(LOG_ERR,"%s is an undefined line discipline.",
*argv);
}
chdir("/dev");
account(line); /* Perform "utmp" accounting. */
/* wait until the system is on */
if (nologin && !access(_PATH_NOLOGIN, F_OK)) {
syslog(LOG_DEBUG, "\"%s\" existed when starting %s",
_PATH_NOLOGIN, line);
do {
sginap(HZ);
} while (!access(_PATH_NOLOGIN, F_OK));
exit(0); /* quit since syslog() stole FD 0 */
}
/* Attempt to open standard input, output, and error on specified line. */
openline(line,speedef,termtype,lined,hangup);
/* Loop until user is successful in requesting login. */
for (;;) {
/* If there is no terminal type, just advance a line. */
if (termtype == TERM_NONE) {
/* A bug in the stdio package requires that the first output on */
/* the newly reopened stderr stream be a putc rather than an */
/* fprintf. */
putc('\r',stderr);
putc('\n',stderr);
/* If there is a terminal type, clear the screen with the common */
/* crt language. Note that the characters have to be written in */
/* one write, and hence can't go through standard io, which is */
/* currently unbuffered. */
} else write(fileno(stderr),clrscreen,sizeof(clrscreen));
/* If getty is supposed to die if no one logs in after a */
/* predetermined amount of time, set the timer. */
if(timeout) {
signal(SIGALRM,timedout);
alarm(timeout);
}
/* Print out the issue file. */
if ((fp = fopen(ISSUE_FILE,"r")) != NULL) {
while (fgets(buffer,sizeof(buffer),fp) != NULL) {
fputs(buffer,stderr);
putc('\r',stderr);
}
fclose(fp);
}
/* Print the login message. */
fprintf(stderr,"%s",speedef->g_message);
/* Get the user's typed response and respond appropriately. */
switch(getname(user,line,&termio)) {
case GOODNAME:
if (timeout) alarm(0);
/* If a terminal type was specified, keep only those parts of */
/* the gettydef final settings which were not explicitely turned */
/* on when the terminal type was set. */
if (termtype != TERM_NONE) {
termio.c_iflag |= (ISTRIP|ICRNL|IXON|IXANY)
| (speedef->g_fflags.c_iflag
& ~(ISTRIP|ICRNL|IXON|IXANY));
termio.c_oflag |= (OPOST|ONLCR)
| (speedef->g_fflags.c_oflag
& ~(OPOST|ONLCR));
#ifdef CNEW_RTSCTS
/* preserve CNEW_RTSCTS so flow control
* will still work if line is a ttyf port
*/
termio.c_cflag =
(termio.c_cflag & CNEW_RTSCTS) |
speedef->g_fflags.c_cflag;
#else
termio.c_cflag = speedef->g_fflags.c_cflag;
#endif
termio.c_lflag = (ISIG|ICANON|ECHO|ECHOE|ECHOK)
| (speedef->g_fflags.c_lflag
& ~(ISIG|ICANON|ECHO|ECHOE|ECHOK));
} else {
termio.c_iflag |= speedef->g_fflags.c_iflag;
termio.c_oflag |= speedef->g_fflags.c_oflag;
termio.c_cflag |= speedef->g_fflags.c_cflag;
termio.c_lflag |= speedef->g_fflags.c_lflag;
}
termio.c_ospeed = speedef->g_fflags.c_ospeed;
termio.c_line = lined;
if (0 > ioctl(0,TCSETAW,&termio))
badcall("ioctl(TCSETAW,\"%s\")%d: %m",
line,__LINE__, 0);
/* Parse the input line from the user, breaking it at white */
/* spaces. */
largs[0] = "login";
parse(user,&largs[1],MAXARGS-1);
/* silently destroy attempts to pass tricky args to login. */
if (largs[1] != 0 && largs[1][0] == '-')
largs[1] = 0;
closelog(); /* avoid propagating stray FDs */
execv(_PATH_SCHEME,largs);
syslog(LOG_ERR,
"failed to exec %s: %m", _PATH_SCHEME);
closelog();
execv("/bin/login",largs);
syslog(LOG_ERR, "failed to exec /bin/login: %m");
closelog();
execv("/etc/login",largs);
syslog(LOG_ERR, "failed to exec /etc/login: %m");
exit(1);
/* If the speed supplied was bad, try the next speed in the list. */
case BADSPEED:
/* Save the name of the old speed definition incase new one is */
/* bad. Copy the new speed out of the static so that "find_def" */
/* won't overwrite it in the process of looking for new entry. */
strcpy(oldspeed,speedef->g_id);
strcpy(newspeed,speedef->g_nextid);
if ((speedef = find_def(newspeed)) == NULL) {
syslog(LOG_ERR,"pointer to next speed in entry %s is bad.",
oldspeed);
/* In case of error, go back to the original entry. */
if ((speedef = find_def(oldspeed)) == NULL) {
/* If the old entry has disappeared, then quit and let next "getty" try. */
badcall("unable to find %s again",
oldspeed,0, 1);
}
}
/* Setup the terminal for the new information. */
setupline(speedef,termtype,lined);
break;
/* If no name was supplied, not nothing, but try again. */
case NONAME:
break;
}
}
}
/* complain about the line and quit */
static void
badcall(char *fmt, char* sarg, int iarg, int quit)
{
int i;
if (quit)
signal(SIGINT,SIG_DFL);
if (0 != fmt)
syslog(LOG_ERR,fmt,sarg,iarg);
if (quit) {
if (line_locked) {
delock(line_locked);
line_locked = 0;
}
for (i = getdtablehi(); --i > 0; )
(void)close(i);
sginap(30*HZ);
exit(1);
}
}
#ifdef UUGETTY
/* share a line with uucico/cu/ct
* wait for open,
* When success, check to see if LCK..line exists
* If it does, that means that uucico/cu/ct is using the line,
* so wait and exit when the LCK..line file goes away
*/
/* dummies for using uucp .o routines
* This seems like a royal kludge, but it is what the original uugetty
* did.
*/
void
cleanup(int code)
{
if (Debug)
syslog(LOG_DEBUG, "exiting with %d", code);
exit(code);
}
/* dummies for uucp routines
*/
void
assert(s1, s2, i1, file, line)
char *s1; char *s2; int i1; char *file; int line;
{
syslog(LOG_ERR, "%s %s (%d) [FILE: %s, LINE: %d]",
s1, s2, i1, file, line);
}
void
logent(text, status)
char *text; char *status;
{
if (!Debug)
return;
syslog(LOG_DEBUG, "%s (%s)", status, text);
}
jmp_buf Sjbuf;
/* see if the line is available for uugetty
* Do not return if it is not
*/
static void
uugetty_ck(line)
char *line;
{
char lckname[MAXNAMLEN]; /* lock file name LCK..line */
if (mlock(line)) { /* There is a lock file already */
(void)signal(SIGHUP,SIG_IGN); /* stop worrying about carrier */
if (Debug)
syslog(LOG_DEBUG, "\"%s\" is busy", line);
(void)close(0); /* release the device */
(void)sprintf(lckname, "%s.%s", LOCKPRE, line);
for (;;) { /* wait for LCK..line to go away */
if (checkLock(lckname) == 0)
break;
sginap(5*HZ);
}
sginap(1*HZ);
if (Debug)
syslog(LOG_DEBUG, "finished waiting for lock to go away");
exit(0);
}
line_locked = line;
}
#endif /* UUGETTY */
void
timedout()
{
if (Debug)
syslog(LOG_DEBUG, "after timeout");
exit(1);
}
/* "find_def" scans "/etc/gettydefs" for a string with the */
/* requested "id". If the "id" is NULL, then the first entry is */
/* taken, hence the first entry must be the default entry. */
/* If a match for the "id" is found, then the line is parsed and */
/* the Gdef structure filled. Errors in parsing generate error */
/* messages on the system console. */
static struct Gdef *
find_def(char *id)
{
register struct Gdef *gptr;
register char *ptr,c;
FILE *fp;
int i,input,state,size,rawc,field;
char oldc,*optr,*gdfile;
char line[MAXLINE+1];
static struct Gdef def;
static char d_id[MAXIDLENGTH+1],d_nextid[MAXIDLENGTH+1];
static char d_message[MAXMESSAGE+1];
extern int check;
static char *states[] = {
"","id","initial flags","final flags","message","next id"
};
/* Decide whether to read the real /etc/gettydefs or the supplied */
/* check file. */
if (check) gdfile = checkgdfile;
else gdfile = GETTY_DEFS;
/* Open the "/etc/gettydefs" file. Be persistent. */
for (i=0; i < 3; i++) {
if ((fp = fopen(gdfile,"r")) != NULL)
break;
sginap(3*HZ); /* Wait a little and then try again. */
}
/* If unable to open, complain and then use the built in default. */
if (fp == NULL) {
if (check) {
fprintf(stderr, "can't open \"%s\"\n", gdfile);
} else {
syslog(LOG_ERR, "can't open \"%s\"", gdfile);
}
return(&default_gdef);
}
/* Start searching for the line with the proper "id". */
input = ACTIVE;
do {
for(ptr= line,oldc='\0'; ptr < &line[sizeof(line)] &&
(rawc = getc(fp)) != EOF; ptr++,oldc = c) {
c = *ptr = rawc;
/* Search for two \n's in a row. */
if (c == '\n' && oldc == '\n') break;
}
/* If we didn't end with a '\n' or EOF, then the line is too long. */
/* Skip over the remainder of the stuff in the line so that we */
/* start correctly on next line. */
if (rawc != EOF && c != '\n') {
for (oldc='\0'; (rawc = getc(fp)) != EOF;oldc=c) {
c = rawc;
if (c == '\n' && oldc != '\n') break;
}
if (check)
fprintf(stdout,"Entry too long.\n");
}
/* If we ended at the end of the file, then if there is no */
/* input, break out immediately otherwise set the "input" */
/* flag to FINISHED so that the "do" loop will terminate. */
if (rawc == EOF) {
if (ptr == line) break;
else input = FINISHED;
}
/* If the last character stored was an EOF or '\n', replace it */
/* with a '\0'. */
if (*ptr == (EOF & 0377) || *ptr == '\n') *ptr = '\0';
/* If the buffer is full, then make sure there is a null after the */
/* last character stored. */
else *++ptr == '\0';
if (check)
fprintf(stdout,"\n**** Next Entry ****\n%s\n",line);
/* If line starts with #, treat as comment */
if(line[0] == '#') continue;
/* Initialize "def" and "gptr". */
gptr = &def;
gptr->g_id = (char*)NULL;
gptr->g_iflags.c_iflag = 0;
gptr->g_iflags.c_oflag = 0;
gptr->g_iflags.c_cflag = 0;
gptr->g_iflags.c_ospeed = 0;
gptr->g_iflags.c_lflag = 0;
gptr->g_fflags.c_iflag = 0;
gptr->g_fflags.c_oflag = 0;
gptr->g_fflags.c_cflag = 0;
gptr->g_fflags.c_lflag = 0;
gptr->g_fflags.c_ospeed = 0;
gptr->g_message = (char*)NULL;
gptr->g_nextid = (char*)NULL;
/* Now that we have the complete line, scan if for the various */
/* fields. Advance to new field at each unquoted '#'. */
for (state=ID,ptr= line; state != FAILURE && state != SUCCESS;) {
switch(state) {
case ID:
/* Find word in ID field and move it to "d_id" array. */
strncpy(d_id,getword(ptr,&size),MAXIDLENGTH);
gptr->g_id = d_id;
/* Move to the next field. If there is anything but white space */
/* following the id up until the '#', then set state to FAILURE. */
ptr += size;
while (isspace(*ptr)) ptr++;
if (*ptr != '#') {
field = state;
state = FAILURE;
} else {
ptr++; /* Skip the '#' */
state = IFLAGS;
}
break;
/* Extract the "g_iflags" */
case IFLAGS:
if ((ptr = fields(ptr,&gptr->g_iflags)) == NULL) {
field = state;
state = FAILURE;
} else {
gptr->g_iflags.c_iflag &= (ICRNL|IUCLC);
if((gptr->g_iflags.c_cflag & CSIZE) == 0)
gptr->g_iflags.c_cflag |= CS8;
gptr->g_iflags.c_cflag |= CREAD|HUPCL;
gptr->g_iflags.c_lflag &= ~(ISIG|ICANON
|XCASE|ECHOE|ECHOK);
ptr++;
state = FFLAGS;
}
break;
/* Extract the "g_fflags". */
case FFLAGS:
if ((ptr = fields(ptr,&gptr->g_fflags)) == NULL) {
field = state;
state = FAILURE;
} else {
/* Force the CREAD mode in regardless of what the user specified. */
gptr->g_fflags.c_cflag |= CREAD;
ptr++;
state = MESSAGE;
}
break;
/* Take the entire next field as the "login" message. */
/* Follow usual quoting procedures for control characters. */
case MESSAGE:
for (optr= d_message; (c = *ptr) != '\0'
&& c != '#';ptr++) {
/* If the next character is a backslash, then get the quoted */
/* character as one item. */
if (c == '\\') {
c = quoted(ptr,&size);
/* -1 accounts for ++ that takes place later. */
ptr += size - 1;
}
/* If the next character is a dollar sign, get the variable name */
/* following it and interpolate its value into d_message */
if (c == '$') {
char *value;
register int length;
value = getvar(ptr, &size);
ptr += size;
length = strlen(value);
if (optr + length
< &d_message[MAXMESSAGE]) {
bcopy(value, optr,
length);
optr += length;
}
} else
/* If there is room, store the next character in d_message. */
if (optr < &d_message[MAXMESSAGE]) {
*optr++ = c;
}
}
/* If we ended on a '#', then all is okay. Move state to NEXTID. */
/* If we didn't, then set state to FAILURE. */
if (c == '#') {
gptr->g_message = d_message;
state = NEXTID;
/* Make sure message is null terminated. */
*optr++ = '\0';
ptr++;
} else {
field = state;
state = FAILURE;
}
break;
/* Finally get the "g_nextid" field. If this is successful, then */
/* the line parsed okay. */
case NEXTID:
/* Find the first word in the field and save it as the next id. */
strncpy(d_nextid,getword(ptr,&size),MAXIDLENGTH);
gptr->g_nextid = d_nextid;
/* There should be nothing else on the line. Starting after the */
/* word found, scan to end of line. If anything beside white */
/* space, set state to FAILURE. */
ptr += size;
while (isspace(*ptr)) ptr++;
if (*ptr != '\0') {
field = state;
state = FAILURE;
} else state = SUCCESS;
break;
}
}
/* If a line was successfully picked up and parsed, compare the */
/* "g_id" field with the "id" we are looking for. */
if (state == SUCCESS) {
/* If there is an "id", compare them. */
if (id != NULL) {
if (strcmp(id,gptr->g_id) == 0) {
fclose(fp);
return(gptr);
}
/* If there is no "id", then return this first successfully */
/* parsed line outright. */
} else if (check == FALSE) {
fclose(fp);
return(gptr);
/* In check mode print out the results of the parsing. */
} else {
fprintf(stdout,"id: %s\n",gptr->g_id);
fprintf(stdout,"initial flags:\niflag- %o oflag- %o cflag- %o lflag- %o ospeed- %d\n",
gptr->g_iflags.c_iflag,
gptr->g_iflags.c_oflag,
gptr->g_iflags.c_cflag,
gptr->g_iflags.c_lflag,
gptr->g_iflags.c_ospeed);
fprintf(stdout,"final flags:\niflag- %o oflag- %o cflag- %o lflag- %o ospeed- %d\n",
gptr->g_fflags.c_iflag,
gptr->g_fflags.c_oflag,
gptr->g_fflags.c_cflag,
gptr->g_fflags.c_lflag,
gptr->g_iflags.c_ospeed);
fprintf(stdout,"message: %s\n",gptr->g_message);
fprintf(stdout,"next id: %s\n",gptr->g_nextid);
}
/* If parsing failed in check mode, complain, otherwise ignore */
/* the bad line. */
} else if (check) {
*++ptr = '\0';
fprintf(stdout,"Parsing failure in the \"%s\" field\n\
%s<--error detected here\n",
states[field],line);
}
} while (input == ACTIVE);
/* If no match was found, then return NULL. */
fclose(fp);
return(NULL);
}
/*
* Get the variable name which follows the '$' pointed at by cp.
* Return a pointer to its value directly. Return the variable name
* length indirectly through sizep.
*/
static char *
getvar(char *cp, int *sizep)
{
register char *vp;
static char hostname[MAXHOSTNAMELEN];
vp = getword(cp + 1, sizep);
if (strcmp(vp, "HOSTNAME") == 0) {
if (hostname[0] == '\0') {
gethostname(hostname, MAXHOSTNAMELEN);
}
return hostname;
} else {
if (check)
fprintf(stderr, "getty: undefined variable %.*s\n",
*sizep, cp + 1);
else
syslog(LOG_ERR,"undefined variable %.*s",
*sizep, cp + 1);
return "?";
}
}
/* "parse" breaks up the user's response into seperate arguments */
/* and fills the supplied array with those arguments. Quoting */
/* with the backspace is allowed. */
static void
parse(char *string, char **args, int cnt)
{
register char *ptrin,*ptrout;
register int i;
int qsize;
for (i=0; i < cnt; i++) args[i] = (char *)NULL;
for (ptrin = ptrout = string,i=0; *ptrin != '\0' && i < cnt; i++) {
/* Skip excess white spaces between arguments. */
while(*ptrin == ' ' || *ptrin == '\t') {
ptrin++;
ptrout++;
}
/* Save the address of the argument if there is something there. */
if (*ptrin == '\0') break;
else args[i] = ptrout;
/* Span the argument itself. The '\' character causes quoting */
/* of the next character to take place (except for '\0'). */
while (*ptrin != '\0') {
/* Is this the quote character? */
if (*ptrin == '\\') {
*ptrout++ = quoted(ptrin,&qsize);
ptrin += qsize;
/* Is this the end of the argument? If so quit loop. */
} else if (*ptrin == ' ' || *ptrin == '\t') {
ptrin++;
break;
/* If this is a normal letter of the argument, save it, advancing */
/* the pointers at the same time. */
} else *ptrout++ = *ptrin++;
}
/* Null terminate the string. */
*ptrout++ = '\0';
}
}