|
|
researchv10 Norman
/* /sccs/src/cmd/uucp/s.uugetty.c
uugetty.c 1.3 8/30/84 17:38:06
*/
#include "uucp.h"
VERSION(@(#)uugetty.c 1.3);
/* @(#)getty.c 1.5 */
/* This program, uugetty, is the standard getty modified */
/* to allow a tty line to be used by uucp/cu or a dial in. */
/* For it to work with 801/212 dialers, */
/* put an entry into inittab like */
/* 30:2:respawn:/usr/lib/uucp/uugetty -t 60 cul04 1200 */
/* For direct lines, or intelligent modems use */
/* 30:2:respawn:/usr/lib/uucp/uugetty -r -t 60 cul04 1200 */
/* When this line is used, the Systems file that is */
/* used to call into it must have the following script */
/* "" \r\d\r\d\r\d\r in:--in: ... */
/* This is because the uugetty expects to read a character */
/* before the login message is output. */
/* It will only work on USG systems at least 5.0 or systems */
/* that permit kill(0, pid). */
/* This scheme was proposed by Larry Wehr and works as follows: */
/* The fopen of the line hangs until carrier is detected. */
/* At this point, uugetty attempts to create a LCK..line file */
/* If if fails, that means that uucico or cu are using the line. */
/* In this case, just do a busy wait, periodically checking */
/* for the LCK..line file. When it goes away, then exit and */
/* a new uugetty will be spawned. */
/* If it succeeds, then this is someone logging in--do normal */
/* getty processing. */
/* NOTE: */
/* When the person hangs up (login case) the LCK..line file */
/* will remain, but this is ok because the ulockf() function */
/* used by uucico and uugetty and cu check the pid in the LCK */
/* file and if it doesn't exist, the LCK file is removed when */
/* it is needed. */
/* Also, uugetty always sets the owner of the line to uucp. */
/* This is so that uucico/cu can get at the line. (The new */
/* cu will run setuid uucp--or this will not work.) */
/* */
/* There is an additional option for direct lines and lines */
/* that have intelligent modems (ones that return on open */
/* immediately. The -r option means wait for one character */
/* before putting out the login message. If the character */
/* comes in, then check the LCK file and proceed as above. */
/* getty - sets up speed, various terminal flags, line discipline, */
/* and waits for new prospective user to enter name, before */
/* calling "login". */
/* */
/* Usage: getty [-r] [-h] [-t time] line speed_label terminal */
/* line_disc */
/* */
/* -h says don't hangup by dropping carrier during the */
/* initialization phase. Normally carrier is dropped to */
/* make the dataswitch release the line. */
/* -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. */
/* -r says wait for a character before putting out login message */
/* (This is for uugetty.c with intelligent modems */
/* "line" is the device in "/dev". */
/* "speed_label" is a pointer into the "/etc/getty_defs" */
/* where the definition for the speeds and */
/* other associated flags are to be found. */
/* "terminal" is the name of the terminal type. */
/* "line_disc" is the name of the line discipline. */
/* */
/* 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. */
/* */
/* dummies for using uucp .o routines */
void assert(){}
cleanup(){}
void logent(){} /* so we can load ulockf() */
/* undefine DEBUG - it is defined in uucp.h */
#undef DEBUG
#include <utmp.h>
#include <sys/crtctl.h>
#define TRUE 1
#define FALSE 0
#define FAILURE -1
#define SUCCESS 0
#define ID 1
#define IFLAGS 2
#define FFLAGS 3
#define MESSAGE 4
#define NEXTID 5
#define ACTIVE 1
#define FINISHED 0
#define ABORT 0177 /* Delete */
#define QUIT ('\\'&037) /* ^\ */
#define ERASE '#'
#define BACKSPACE '\b'
#define KILL '@'
#ifdef OSS
/* The following three characters are the standard OSS erase, */
/* kill, and abort characters. */
#define STDERASE '_'
#define STDKILL '$'
#define STDABORT '&'
#endif
#define control(x) (x&037)
#define GOODNAME 1
#define NONAME 0
#define BADSPEED -1
#ifndef fioctl
#define fioctl(x,y,z) ioctl(fileno(x),y,z)
#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 */
};
#define MAXIDLENGTH 15 /* Maximum length the "g_id" and "g_nextid" \
* strings can take. Longer ones will be \
* truncated. \
*/
#define MAXMESSAGE 79 /* 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 MAXLINE 255
#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 (CS7|PARENB|CREAD)
#define LSANE (ISIG|ICANON|ECHO|ECHOK)
/* Modes set with the TCSETAW ioctl command. */
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
};
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
};
struct Symbols cmodes[] = {
"B0", B0,
"B50", B50,
"B75", B75,
"B110", B110,
"B134", B134,
"B150", B150,
"B200", B200,
"B300", B300,
"B600", B600,
"B1200", B1200,
"B1800", B1800,
"B2400", B2400,
"B4800", B4800,
"B9600", B9600,
#ifdef EXTA
"EXTA", EXTA,
#endif
#ifdef EXTB
"EXTB", EXTB,
#endif
#ifdef B19200
"B19200", B19200,
#endif
#ifdef B38400
"B38400", B38400,
#endif
"CS5", CS5,
"CS6", CS6,
"CS7", CS7,
"CS8", CS8,
"CSTOPB", CSTOPB,
"CREAD", CREAD,
"PARENB", PARENB,
"PARODD", PARODD,
"HUPCL", HUPCL,
"CLOCAL", CLOCAL,
NULL, 0
};
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. */
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
};
/* Line disciplines set by the TIOCSETD ioctl command. */
#ifndef LDISC0
#define LDISC0 0
#endif
struct Symbols linedisc[] = {
"LDISC0", LDISC0,
NULL, 0
};
/* If the /etc/gettydefs file can't be opened, the following */
/* default is used. */
struct Gdef DEFAULT = {
"default",
ICRNL,0,B300+CREAD+HUPCL,0,
LDISC0,ABORT,QUIT,ERASE,KILL,'\0','\0','\0','\0',
ICRNL,OPOST+ONLCR+NLDLY+TAB3,B300+CS7+CREAD+HUPCL,
ISIG+ICANON+ECHO+ECHOE+ECHOK,
LDISC0,ABORT,QUIT,ERASE,KILL,'\0','\0','\0','\0',
"LOGIN: ",
"default"
};
#ifndef DEBUG
char *CTTY = "/dev/syscon";
#else
char *CTTY = "/dev/sysconx";
#endif
char *ISSUE_FILE = "/etc/issue";
char *GETTY_DEFS = "/etc/gettydefs";
int check = {
FALSE
};
char *checkgdfile; /* Name of gettydefs file during
* check mode.
*/
main(argc,argv)
int argc;
char **argv;
{
char *line;
register struct Gdef *speedef;
char oldspeed[MAXIDLENGTH+1],newspeed[MAXIDLENGTH+1];
extern struct Gdef *find_def();
int termtype,lined;
extern char *ISSUE_FILE,*GETTY_DEFS;
extern int check;
int hangup,timeout;
extern char *checkgdfile;
extern struct Symbols *search(),terminals[],linedisc[];
extern int timedout();
register struct Symbols *answer;
char user[MAXLINE],*largs[MAXARGS],*ptr,buffer[MAXLINE];
int wait_read = FALSE; /* wait for read before output "login" */
FILE *fp;
FILE *fdup();
struct utsname utsname;
struct termcb termcb;
struct termio termio;
static char clrscreen[2] = {
ESC,CS
};
struct stat statb;
char lckname[MAXNAMESIZE]; /* lock file name LCK..line */
signal(SIGINT,SIG_IGN);
signal(SIGQUIT,SIG_DFL);
hangup = TRUE;
timeout = 0;
while(--argc && **++argv == '-') {
for(ptr = *argv + 1; *ptr;ptr++) switch(*ptr) {
case 'h':
hangup = FALSE;
break;
case 'r':
wait_read = TRUE;
break;
case 't':
if(isdigit(*++ptr)) {
sscanf(ptr,"%d",&timeout);
/* Advance "ptr" so that it is pointing to the last digit of the */
/* timeout argument. */
while(isdigit(*++ptr));
ptr--;
} else if(--argc) {
if(isdigit(*(ptr = *++argv)))
sscanf(ptr,"%d",&timeout);
else error("getty: timeout argument invalid. \"%s\"\n", *argv);
}
break;
/* Check a "gettydefs" file mode. */
case 'c':
signal(SIGINT,SIG_DFL);
if(--argc == 0) {
fprintf(stderr,
"Check Mode Usage: getty -c gettydefs-like-file\n");
exit(1);
}
check = TRUE;
checkgdfile = *++argv;
/* Attempt to open the check gettydefs file. */
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);
default:
break;
}
}
/* There must be at least one argument. If there isn't, complain */
/* and then die after 20 seconds. The 20 second sleep is to keep */
/* "init" from working too hard. */
if(argc < 1) {
error("getty: no terminal line specified.\n");
sleep(20);
exit(1);
} else line = *argv;
/* If a "speed_label" 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) {
error("getty: unable to find %s in \"%s\".\n",
*argv,GETTY_DEFS);
/* 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. */
if(--argc > 0) {
if((answer = search(*++argv,terminals)) == NULL) {
error("getty: %s is an undefined terminal type.\n",
*argv);
termtype = TERM_NONE;
} else termtype = answer->s_value;
} else termtype = TERM_NONE;
/* If a line discipline was supplied, try to find it in list. */
if(--argc > 0) {
if((answer = search(*++argv,linedisc)) == NULL) {
error("getty: %s is an undefined line discipline.\n",
*argv);
lined = LDISC0;
} else lined = answer->s_value;
} else lined = LDISC0;
/* Perform "utmp" accounting. */
account(line);
/* Attempt to open standard input, output, and error on specified */
/* line. */
chdir("/dev");
/* Code added for shared line - input/output */
/* For use by uucico/cu/ct */
/* Method: -- 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 busy wait and exit when */
/* the LCK..line file goes away */
/* If no LCK..line file, normal processing-- */
/* close the line and use openline() as usual */
/* If the -r option, wait for first character also. */
/* Change the ownership of the terminal line to uucp and set */
/* the protections to only allow uucp to read the line. */
stat(line,&statb);
chown(line,UUCPUID,statb.st_gid); /* for uugetty use uucp uid */
chmod(line,0622);
(void) close(0);
(void)fclose(stdin);
(void) close(1);
(void) close(2);
(void) fclose(stdout);
(void) fclose(stderr);
if ( (fopen(line, "r+")) == NULL) { /* this opens stdin */
error("getty: cannot open \"%s\". errno: %d\n",line,errno);
sleep(20);
exit(1);
}
if (wait_read) { /* wait to read the first character */
/* Set the terminal type and line discipline. */
setupline(speedef,termtype,lined);
/*
* Check for read failure or and EOT sent
* (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
*/
if ( read(0, buffer, 1) < 0
|| *buffer == '\004') {
(void) fclose(stdin);
sleep(10);
exit(0);
}
}
if (mlock(line)) { /* There is a lock file already */
/* some process is using the line for output */
(void) fclose(stdin);
(void) sprintf(lckname, "%s.%s", LOCKPRE, line);
for (;;) { /* busy wait for LCK..line to go away */
sleep(60);
if (checkLock(lckname) == 0) /* LCK..line gone */
break;
}
exit(0);
}
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);
}
#ifdef SYS_NAME
/* Generate a message with the system identification in it. */
if (uname(&utsname) != FAILURE) {
sprintf(buffer,"%.9s\r\n", utsname.nodename);
#ifdef UPPERCASE_ONLY
/* Make all the alphabetics upper case. */
for (ptr= buffer; *ptr;ptr++) *ptr = tolower(*ptr);
#endif
fputs(buffer,stderr);
}
/* Print out the issue file. */
if ((fp = fopen(ISSUE_FILE,"r")) != NULL) {
while ((ptr = fgets(buffer,sizeof(buffer),fp)) != NULL) {
fputs(ptr,stderr);
/* In "raw" mode, a carriage return must be supplied at the end of */
/* each line. */
putc('\r',stderr);
}
fclose(fp);
}
#endif
/* Print the login message. */
fprintf(stderr,"%s",speedef->g_message);
/* Get the user's typed response and respond appropriately. */
switch(getname(user,&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));
termio.c_cflag = speedef->g_fflags.c_cflag;
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_line = lined;
fioctl(stdin,TCSETAW,&termio);
/* Parse the input line from the user, breaking it at white */
/* spaces. */
largs[0] = "login";
parse(user,&largs[1],MAXARGS-1);
/* Exec "login". */
#ifndef DEBUG
execv("/bin/login",largs);
exit(1);
#else
exit(0);
#endif
/* 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) {
error("getty: pointer to next speed in entry %s is bad.\n",
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. */
error("getty: unable to find %s again.\n",
oldspeed);
exit(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;
}
}
}
account(line)
char *line;
{
register int ownpid;
register struct utmp *u;
extern struct utmp *getutent(), *pututline();
register FILE *fp;
/* Look in "utmp" file for our own entry and change it to LOGIN. */
ownpid = getpid();
while ((u = getutent()) != NULL) {
/* Is this our own entry? */
if (u->ut_type == INIT_PROCESS && u->ut_pid == ownpid) {
strncpy(u->ut_line,line,sizeof(u->ut_line));
strncpy(u->ut_user,"LOGIN",sizeof(u->ut_user));
u->ut_type = LOGIN_PROCESS;
/* 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 && (fp = fopen(WTMP_FILE,"r+")) != NULL) {
fseek(fp,0L,2); /* Seek to end of file */
fwrite(u,sizeof(*u),1,fp);
fclose(fp);
}
/* Close the utmp file. */
endutent();
}
/* "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. */
struct Symbols *search(target,symbols)
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);
}
error(format,arg1,arg2,arg3,arg4)
char *format;
int arg1,arg2,arg3,arg4;
{
register FILE *fp;
if ((fp = fopen(CTTY,"w")) == NULL) return;
else {
fprintf(fp,format,arg1,arg2,arg3,arg4);
fclose(fp);
}
}
openline(line,speedef,termtype,lined,hangup)
register char *line;
register struct Gdef *speedef;
int termtype,lined,hangup;
{
register FILE *fpin,*fp;
extern int errno;
close(1);
close(2);
fclose(stdout);
fclose(stderr);
fdup(stdin);
fdup(stdin);
setbuf(stdin,NULL);
setbuf(stdout,NULL);
setbuf(stderr,NULL);
/* Unless getty is being invoked by ct, make sure that DTR has been */
/* dropped and reasserted */
if (hangup) hang_up_line();
/* Set the terminal type and line discipline. */
setupline(speedef,termtype,lined);
}
#ifdef HANGUP
hang_up_line()
{
struct termio termio;
fioctl(stdin,TCGETA,&termio);
termio.c_cflag &= ~CBAUD;
termio.c_cflag |= B0;
fioctl(stdin,TCSETAF,&termio);
sleep(1);
}
#else
hang_up_line()
{
}
#endif
timedout()
{
exit(1);
}
setupline(speedef,termtype,lined)
register struct Gdef *speedef;
int termtype,lined;
{
struct termio termio;
struct termcb termcb;
unsigned short timer;
/* 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;
fioctl(stdin,LDSETT,&termcb);
termcb.st_termt = termtype;
fioctl(stdin,LDSETT,&termcb);
/* Get the current state of the modes and such for the terminal. */
fioctl(stdin,TCGETA,&termio);
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|CBAUD);
termio.c_cflag |= CS8|CREAD|HUPCL;
termio.c_lflag &= ~(ISIG|ICANON|ECHO|ECHOE|ECHOK);
/* Add in the speed. */
termio.c_cflag |= (speedef->g_iflags.c_cflag & CBAUD);
} else {
termio.c_iflag = speedef->g_iflags.c_iflag;
termio.c_oflag = speedef->g_iflags.c_oflag;
termio.c_cflag = speedef->g_iflags.c_cflag;
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;
fioctl(stdin,TCSETAF,&termio);
/* Pause briefly while terminal settles. */
for(timer=0; ++timer != 0;);
}
/* "getname" picks up the user's name from the standard input. */
/* It makes certain */
/* determinations about the modes that should be set up for the */
/* terminal depending upon what it sees. If it sees all UPPER */
/* case characters, it sets the IUCLC & OLCUC flags. If it sees */
/* a line terminated with a <linefeed>, it sets ICRNL. If it sees */
/* the user using the "standard" OSS erase, kill, abort, or line */
/* termination characters ( '_','$','&','/','!' respectively) */
/* it resets the erase, kill, and end of line characters. */
int getname(user,termio)
char *user;
struct termio *termio;
{
register char *ptr,c;
register int rawc;
int upper,lower;
/* Get the previous modes, erase, and kill characters and speeds. */
fioctl(stdin,TCGETA,termio);
/* Set the flags to 0 and the erase and kill to the standard */
/* characters. */
termio->c_iflag &= ICRNL;
termio->c_oflag = 0;
termio->c_cflag = 0;
termio->c_lflag &= ECHO;
for (ptr= (char*)termio->c_cc; ptr < (char*)&termio->c_cc[NCC];)
*ptr++ = NULL;
termio->c_cc[VINTR] = ABORT;
termio->c_cc[VQUIT] = QUIT;
termio->c_cc[VERASE] = ERASE;
termio->c_cc[VKILL] = KILL;
termio->c_cc[VEOF] = ('D'&037);
ptr = user;
upper = 0;
lower = 0;
do {
/* If it isn't possible to read line, exit. */
if ((rawc = getc(stdin)) == EOF) exit(0);
/* If a null character was typed, return 0. */
if ((c = (rawc & 0177)) == '\0') return(BADSPEED);
/* Echo the character if ECHO is off. */
if( (termio->c_lflag&ECHO) == 0 )
putc(rawc,stdout);
#ifdef OSS
if (c == ERASE || c == BACKSPACE) {
/* Store this character as the "erase" character. */
termio->c_cc[VERASE] = c;
#else
if (c == ERASE) {
#endif
/* If there is anything to erase, erase a character. */
if (ptr > user) --ptr;
}
#ifdef OSS
else if (c == STDERASE) {
if (ptr > user) --ptr;
/* Set up the "standard OSS" erase, kill, etc. characters. */
termio->c_cc[VINTR] = STDABORT;
termio->c_cc[VERASE] = STDERASE;
termio->c_cc[VKILL] = STDKILL;
termio->c_cc[VEOL] = '/';
termio->c_cc[VEOL2] = '!';
}
#endif
/* If the character is a kill line or abort character, reset the */
/* line. */
else if (c == KILL || c == ABORT || c == control('U')) {
ptr = user;
fputs("\r\n",stdout);
/* Make sure the erase, kill, etc. are set to the UNIX standard. */
termio->c_cc[VINTR] = ABORT;
termio->c_cc[VERASE] = ERASE;
termio->c_cc[VKILL] = KILL;
termio->c_cc[VEOL] = '\0';
termio->c_cc[VEOL2] = '\0';
}
#ifdef OSS
else if (c == STDKILL || c == STDABORT) {
ptr = user;
/* Set up the "standard OSS" erase, kill, etc. characters. */
termio->c_cc[VINTR] = STDABORT;
termio->c_cc[VERASE] = STDERASE;
termio->c_cc[VKILL] = STDKILL;
termio->c_cc[VEOL] = '/';
termio->c_cc[VEOL2] = '!';
}
#endif
/* If the character is lower case, increment the flag for lower case. */
else if (islower(c)) {
lower++;
*ptr++ = c;
}
/* If the character is upper case, increment the flag. */
else if (isupper(c)) {
upper++;
*ptr++ = c;
}
/* Just store all other characters. */
else *ptr++ = c;
}
/* Continue the above loop until a line terminator is found or */
/* until user name array is full. */
#ifdef OSS
while (c != '\n' && c != '\r' && c != '/' && c != '!' &&
ptr < (user + MAXLINE));
#else
while (c != '\n' && c != '\r'
&& ptr < (user + MAXLINE));
#endif
/* Remove the last character from name. */
*--ptr = '\0';
if (ptr == user) return(NONAME);
#ifdef OSS
/* If the line was terminated with one of the printing OSS line */
/* termination characters or is a <cr>, add a <newline>. */
if (c == '/' || c == '!') {
putc('\n',stdout);
/* Set up the "standard OSS" erase, kill, etc. characters. */
termio->c_cc[VINTR] = STDABORT;
termio->c_cc[VERASE] = STDERASE;
termio->c_cc[VKILL] = STDKILL;
termio->c_cc[VEOL] = '/';
termio->c_cc[VEOL2] = '!';
} else
#endif
if (c == '\r') putc('\n',stdout);
/* If the line terminated with a <lf>, put ICRNL and ONLCR into */
/* into the modes. */
if (c == '\r') {
termio->c_iflag |= ICRNL;
termio->c_oflag |= ONLCR;
/* When line ends with a <lf>, then add the <cr>. */
} else putc('\r',stdout);
/* Set the upper-lower case conversion switchs if only upper */
/* case characters were seen in the login and no lower case. */
/* Also convert all the upper case characters to lower case. */
if (upper > 0 && lower == 0) {
termio->c_iflag |= IUCLC;
termio->c_oflag |= OLCUC;
termio->c_lflag |= XCASE;
for (ptr=user; *ptr; ptr++)
if (*ptr >= 'A' && *ptr <= 'Z' ) *ptr += ('a' - 'A');
}
return(GOODNAME);
}
/* "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. */
struct Gdef *find_def(id)
char *id;
{
register struct Gdef *gptr;
register char *ptr,c;
FILE *fp;
int i,input,state,size,rawc,field;
char oldc,*optr,quoted(),*gdfile;
char line[MAXLINE+1];
static struct Gdef def;
extern struct Gdef DEFAULT;
static char d_id[MAXIDLENGTH+1],d_nextid[MAXIDLENGTH+1];
static char d_message[MAXMESSAGE+1];
extern char *GETTY_DEFS;
extern char *getword(),*fields(),*speed();
extern int check;
extern char *checkgdfile;
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;
else sleep(3); /* Wait a little and then try again. */
}
/* If unable to open, complain and then use the built in default. */
if (fp == NULL) {
error("getty: can't open \"%s\".\n",gdfile);
return(&DEFAULT);
}
/* 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_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_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;
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 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\n",
gptr->g_iflags.c_iflag,
gptr->g_iflags.c_oflag,
gptr->g_iflags.c_cflag,
gptr->g_iflags.c_lflag);
fprintf(stdout,"final flags:\niflag- %o oflag- %o cflag- %o lflag- %o\n",
gptr->g_fflags.c_iflag,
gptr->g_fflags.c_oflag,
gptr->g_fflags.c_cflag,
gptr->g_fflags.c_lflag);
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);
}
char *getword(ptr,size)
register char *ptr;
int *size;
{
register char *optr,c;
char quoted();
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);
}
/* "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. */
char quoted(ptr,qsize)
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);
}
/* "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. */
char *fields(ptr,termio)
register char *ptr;
struct termio *termio;
{
extern struct Symbols imodes[],omodes[],cmodes[],lmodes[];
extern struct Symbols *search();
register struct Symbols *symbol;
char *word,*getword();
int size;
extern int check;
termio->c_iflag = 0;
termio->c_oflag = 0;
termio->c_cflag = 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 (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);
}
/* "parse" breaks up the user's response into seperate arguments */
/* and fills the supplied array with those arguments. Quoting */
/* with the backspace is allowed. */
parse(string,args,cnt)
char *string,**args;
int cnt;
{
register char *ptrin,*ptrout;
register int i;
extern char quoted();
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';
}
}
FILE *fdup(fp)
register FILE *fp;
{
register int newfd;
register char *mode;
/* Dup the file descriptor for the specified stream and then */
/* convert it to a stream pointer with the modes of the original */
/* stream pointer. */
if ((newfd = dup(fileno(fp))) != FAILURE) {
/* Determine the proper mode. If the old file was _IORW, then */
/* use the "r+" option, if _IOREAD, the "r" option, or if _IOWRT */
/* the "w" option. Note that since none of these force an lseek */
/* by "fdopen", the dupped file pointer will be at the same spot */
/* as the original. */
if (fp->_flag & _IORW) mode = "r+";
else if (fp->_flag & _IOREAD) mode = "r";
else if (fp->_flag & _IOWRT) mode = "w";
/* Something is wrong, close dupped descriptor and return NULL. */
else {
close(newfd);
return(NULL);
}
/* Now have fdopen finish the job of establishing a new file pointer. */
return(fdopen(newfd,mode));
} else return(NULL);
}
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.