ic=expect/send script    modem init script
    ac=expect/send script    modem answer script
    ct#val		     chat script timeout (seconds)
    rt#val		     recycle timeout (seconds) if 'ac' set
    dc#val		     debug bitmask for debugging chat scripts
    hw	(boolean)	     enable crtscts handshaking
    if=path		     'issue' file sent prior login prompt

chat.c is a simplistic expect/send chat module.
This commit is contained in:
davidn 1997-02-02 14:24:57 +00:00
parent 5e164803b6
commit c6f2c73939
8 changed files with 852 additions and 54 deletions

View File

@ -2,7 +2,7 @@
# $FreeBSD$
PROG= getty
SRCS= main.c init.c subr.c
SRCS= main.c init.c subr.c chat.c
DPADD= ${LIBUTIL}
LDADD= -lutil
MAN5= gettytab.5 ttys.5
@ -11,3 +11,4 @@ MAN8= getty.8
#CFLAGS+= -Wall -Wstrict-prototypes -Wno-unused -Wwrite-strings
.include <bsd.prog.mk>

526
libexec/getty/chat.c Normal file
View File

@ -0,0 +1,526 @@
/*-
* Copyright (c) 1997
* David L Nugent <davidn@blaze.net.au>.
* All rights reserved.
*
*
* Redistribution and use in source and binary forms, with or without
* modification, is permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice immediately at the beginning of the file, without modification,
* this list of conditions, and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. This work was done expressly for inclusion into FreeBSD. Other use
* is permitted provided this notation is included.
* 4. Absolutely no warranty of function or purpose is made by the authors.
* 5. Modifications may be freely made to this file providing the above
* conditions are met.
*
* Modem chat module - send/expect style functions for getty
* For semi-intelligent modem handling.
*
* $FreeBSD$
*/
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/resource.h>
#include <sys/ttydefaults.h>
#include <sys/utsname.h>
#include <errno.h>
#include <signal.h>
#include <fcntl.h>
#include <time.h>
#include <ctype.h>
#include <fcntl.h>
#include <libutil.h>
#include <locale.h>
#include <setjmp.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <termios.h>
#include <time.h>
#include <unistd.h>
#include <sys/socket.h>
#include "extern.h"
#define PAUSE_CH (unsigned char)'\xff' /* pause kludge */
#define CHATDEBUG_RECEIVE 0x01
#define CHATDEBUG_SEND 0x02
#define CHATDEBUG_EXPECT 0x04
#define CHATDEBUG_MISC 0x08
#define CHATDEBUG_DEFAULT 0
#define CHAT_DEFAULT_TIMEOUT 10
static int chat_debug = CHATDEBUG_DEFAULT;
static int chat_alarm = CHAT_DEFAULT_TIMEOUT; /* Default */
static volatile int alarmed = 0;
static void chat_alrm __P((int));
static int chat_unalarm __P((void));
static int getdigit __P((unsigned char **, int, int));
static char **read_chat __P((char **));
static char *cleanchr __P((char **, unsigned char));
static char *cleanstr __P((const unsigned char *, int));
static const char *result __P((int));
static int chat_expect __P((const char *));
static int chat_send __P((char const *));
/*
* alarm signal handler
* handle timeouts in read/write
* change stdin to non-blocking mode to prevent
* possible hang in read().
*/
static void
chat_alrm(signo)
int signo;
{
int on = 1;
alarm(1);
alarmed = 1;
signal(SIGALRM, chat_alrm);
ioctl(STDIN_FILENO, FIONBIO, &on);
}
/*
* Turn back on blocking mode reset by chat_alrm()
*/
static int
chat_unalarm()
{
int off = 0;
return ioctl(STDIN_FILENO, FIONBIO, &off);
}
/*
* convert a string of a given base (octal/hex) to binary
*/
static int
getdigit(ptr, base, max)
unsigned char **ptr;
int base, max;
{
int i, val = 0;
char * q;
static const char xdigits[] = "0123456789abcdef";
for (i = 0, q = *ptr; i++ < max; ++q) {
int sval;
const char * s = strchr(xdigits, tolower(*q));
if (s == NULL || (sval = s - xdigits) >= base)
break;
val = (val * base) + sval;
}
*ptr = q;
return val;
}
/*
* read_chat()
* Convert a whitespace delimtied string into an array
* of strings, being expect/send pairs
*/
static char **
read_chat(chatstr)
char **chatstr;
{
char *str = *chatstr;
char **res = NULL;
if (str != NULL) {
char *tmp = NULL;
int l;
if ((l=strlen(str)) > 0 && (tmp=malloc(l + 1)) != NULL &&
(res=malloc((l / 2 + 1) * sizeof(char *))) != NULL) {
static char ws[] = " \t";
char * p;
for (l = 0, p = strtok(strcpy(tmp, str), ws);
p != NULL;
p = strtok(NULL, ws))
{
unsigned char *q, *r;
/* Read escapes */
for (q = r = (unsigned char *)p; *r; ++q)
{
int val;
if (*q == '\\')
{
/* handle special escapes */
switch (*++q)
{
case 'a': /* bell */
*r++ = '\a';
break;
case 'r': /* cr */
*r++ = '\r';
break;
case 'n': /* nl */
*r++ = '\n';
break;
case 'f': /* ff */
*r++ = '\f';
break;
case 'b': /* bs */
*r++ = '\b';
break;
case 'e': /* esc */
*r++ = 27;
break;
case 't': /* tab */
*r++ = '\t';
break;
case 'p': /* pause */
*r++ = PAUSE_CH;
break;
case 's':
case 'S': /* space */
*r++ = ' ';
break;
case 'x': /* hexdigit */
++q;
*r++ = getdigit(&q, 16, 2);
--q;
break;
case '0': /* octal */
++q;
*r++ = getdigit(&q, 8, 3);
--q;
break;
default: /* literal */
*r++ = *q;
break;
case 0: /* not past eos */
--q;
break;
}
} else {
/* copy standard character */
*r++ == *q;
}
}
/* Remove surrounding quotes, if any
*/
if (*p == '"' || *p == '\'') {
q = strrchr(p+1, *p);
if (q != NULL && *q == *p && q[1] == '\0') {
*q = '\0';
strcpy(p, p+1);
}
}
res[l++] = p;
}
res[l] = NULL;
*chatstr = tmp;
return res;
}
free(tmp);
}
return res;
}
/*
* clean a character for display (ctrl/meta character)
*/
static char *
cleanchr(buf, ch)
char **buf;
unsigned char ch;
{
int l;
static char tmpbuf[5];
char * tmp = buf ? *buf : tmpbuf;
if (ch & 0x80) {
strcpy(tmp, "M-");
l = 2;
ch &= 0x7f;
} else
l = 0;
if (ch < 32) {
tmp[l++] = '^';
tmp[l++] = ch + '@';
} else if (ch == 127) {
tmp[l++] = '^';
tmp[l++] = '?';
} else
tmp[l++] = ch;
tmp[l] = '\0';
if (buf)
*buf = tmp + l;
return tmp;
}
/*
* clean a string for display (ctrl/meta characters)
*/
static char *
cleanstr(s, l)
const unsigned char *s;
int l;
{
static unsigned char * tmp = NULL;
static int tmplen = 0;
if (tmplen < l * 4 + 1)
tmp = realloc(tmp, tmplen = l * 4 + 1);
if (tmp == NULL) {
tmplen = 0;
return (char *)"(mem alloc error)";
} else {
int i = 0;
char * p = tmp;
while (i < l)
cleanchr(&p, s[i++]);
*p = '\0';
}
return tmp;
}
/*
* return result as an pseudo-english word
*/
static const char *
result(r)
int r;
{
static const char * results[] = {
"OK", "MEMERROR", "IOERROR", "TIMEOUT"
};
return results[r & 3];
}
/*
* chat_expect()
* scan input for an expected string
*/
static int
chat_expect(str)
const char *str;
{
int len, r = 0;
if (chat_debug & CHATDEBUG_EXPECT)
syslog(LOG_DEBUG, "chat_expect '%s'", cleanstr(str, strlen(str)));
if ((len = strlen(str)) > 0) {
int i = 0;
char * got;
if ((got = malloc(len + 1)) == NULL)
r = 1;
else {
memset(got, 0, len+1);
alarm(chat_alarm);
alarmed = 0;
while (r == 0 && i < len) {
if (alarmed)
r = 3;
else {
unsigned char ch;
if (read(STDIN_FILENO, &ch, 1) == 1) {
if (chat_debug & CHATDEBUG_RECEIVE)
syslog(LOG_DEBUG, "chat_recv '%s' m=%d",
cleanchr(NULL, ch), i);
if (ch == str[i])
got[i++] = ch;
else if (i > 0) {
int j = 1;
/* See if we can resync on a
* partial match in our buffer
*/
while (j < i && memcmp(got + j, str, i - j) != NULL)
j++;
if (j < i)
memcpy(got, got + j, i - j);
i -= j;
}
} else
r = alarmed ? 3 : 2;
}
}
alarm(0);
chat_unalarm();
alarmed = 0;
free(got);
}
}
if (chat_debug & CHATDEBUG_EXPECT)
syslog(LOG_DEBUG, "chat_expect %s", result(r));
return r;
}
/*
* chat_send()
* send a chat string
*/
static int
chat_send(str)
char const *str;
{
int r = 0;
if (chat_debug && CHATDEBUG_SEND)
syslog(LOG_DEBUG, "chat_send '%s'", cleanstr(str, strlen(str)));
if (*str) {
alarm(chat_alarm);
alarmed = 0;
while (r == 0 && *str)
{
unsigned char ch = (unsigned char)*str++;
if (alarmed)
r = 3;
else if (ch == PAUSE_CH)
usleep(500000); /* 1/2 second */
else {
usleep(10000); /* be kind to modem */
if (write(STDOUT_FILENO, &ch, 1) != 1)
r = alarmed ? 3 : 2;
}
}
alarm(0);
chat_unalarm();
alarmed = 0;
}
if (chat_debug & CHATDEBUG_SEND)
syslog(LOG_DEBUG, "chat_send %s", result(r));
return r;
}
/*
* getty_chat()
*
* Termination codes:
* -1 - no script supplied
* 0 - script terminated correctly
* 1 - invalid argument, expect string too large, etc.
* 2 - error on an I/O operation or fatal error condition
* 3 - timeout waiting for a simple string
*
* Parameters:
* char *scrstr - unparsed chat script
* timeout - seconds timeout
* debug - debug value (bitmask)
*/
int
getty_chat(scrstr, timeout, debug)
char *scrstr;
int timeout, debug;
{
int r = -1;
chat_alarm = timeout ? timeout : CHAT_DEFAULT_TIMEOUT;
chat_debug = debug;
if (scrstr != NULL) {
char **script;
if (chat_debug & CHATDEBUG_MISC)
syslog(LOG_DEBUG, "getty_chat script='%s'", scrstr);
if ((script = read_chat(&scrstr)) != NULL) {
struct termios tsave_in, tsave_out;
if (tcgetattr(STDIN_FILENO, &tsave_in) == -1 ||
tcgetattr(STDOUT_FILENO, &tsave_out) == -1) {
syslog(LOG_ERR, "tcgetattr() failed in chat: %m");
r = 2;
} else {
int i = r = 0;
int off = 0;
sig_t old_alarm;
struct termios tneed;
/* We need to be in raw mode for all this
*/
tneed = tsave_in;
cfmakeraw(&tneed);
tcsetattr(STDIN_FILENO, TCSANOW, &tneed);
tcsetattr(STDOUT_FILENO, TCSANOW, &tneed);
old_alarm = signal(SIGALRM, chat_alrm);
chat_unalarm(); /* Force blocking mode at start */
/*
* This is the send/expect loop
*/
while (r == 0 && script[i] != NULL)
if ((r = chat_expect(script[i++])) == 0 && script[i] != NULL)
r = chat_send(script[i++]);
signal(SIGALRM, old_alarm);
free(script);
free(scrstr);
/* Restore flags & previous termios settings
*/
ioctl(STDIN_FILENO, FIONBIO, &off);
tcsetattr(STDIN_FILENO, TCSANOW, &tsave_in);
tcsetattr(STDOUT_FILENO, TCSANOW, &tsave_out);
}
}
if (chat_debug & CHATDEBUG_MISC)
syslog(LOG_DEBUG, "getty_chat %s", result(r));
}
return r;
}

View File

@ -60,3 +60,4 @@ void setchars __P((void));
void setdefaults __P((void));
void setflags __P((int));
int speed __P((int));
int getty_chat __P((char *, int, int));

View File

@ -76,6 +76,7 @@ no entry in the table obtained, nor one in the special
table.
.Bl -column Namexx /usr/bin/login Default
.It Sy Name Type Default Description
.It "ac str unused expect-send chat script for modem answer"
.It "ap bool false terminal uses any parity"
.It "bk str 0377 alternate end of line character (input break)"
.It "c0 num unused tty control flags to write messages"
@ -88,7 +89,9 @@ table.
.It "co bool false console - add"
.Ql \en
after login prompt
.It "de num 0 delay secs before writing first prompt"
.It "ct num 10 chat timeout for ac/ic scripts"
.It "dc num 0 chat debug bitmask"
.It "de num 0 delay secs and flush input before writing first prompt"
.It "ds str" Ta So Li ^Y Sc Ta
.No "delayed suspend character"
.It "dx bool false set"
@ -116,9 +119,12 @@ hangup line on last close
.No "hostname editing string"
.It "hn str hostname hostname"
.It "ht bool false terminal has real tabs"
.It "hw bool false do cts/rts hardware flow control"
.It "i0 num unused tty input flags to write messages"
.It "i1 num unused tty input flags to read login name"
.It "i2 num unused tty input flags to leave terminal as"
.It "ic str unused expect-send chat script for modem initialization"
.It "if str unused display named file before prompt"
.It "ig bool false ignore garbage characters in login name"
.It "im str" Ta Dv NULL Ta
.No "initial (banner) message"
@ -157,6 +163,7 @@ port selector
.No "quit character"
.It "rp str" Ta So Li ^R Sc Ta
.No "line retype character"
.It "rt num unused ring timeout when using ac"
.It "rw bool false do"
.Tn NOT
use raw for input, use cbreak
@ -269,7 +276,7 @@ information about the environment in which
.Xr getty 8
is running.
.Pp
.Bl -tag -width \&%xxx -compact
.Bl -tag -offset indent -width \&%xxxxxxxxxxxxxx
.It \&%d
The current date and time in the locale's representation as of the
.Em \&Lo
@ -364,12 +371,122 @@ If
string is specified and a PPP link bringup sequence is recognized,
getty will invoke the program referenced by the pp option. This
can be used to handle incoming PPP calls.
.Pp
.Nm getty
provides some basic intelligent modem handling by providing a chat
script feature available via two capabilities:
.Pp
.Bl -tag -offset indent -width \&xxxxxxxx -compact
.It ic
Chat script to initialize modem.
.It ac
Chat script to answer a call.
.El
.Pp
A chat script is a set of expect/send string pairs.
When a chat string starts,
.Nm Getty
will wait for the first string, and if it finds it, will send the
second, and so on.
Strings specified are separated by one or more tabs or spaces.
Strings may contain standard ascii characters and special 'escapes',
which consist of a backslash character followed by one or more
characters which are interpreted as follows:
.Pp
.Bl -tag -offset indent -width \&xxxxxxxx -compact
.It \ea
bell character.
.It \eb
backspace.
.It \en
newline.
.It \ee
escape.
.It \ef
formfeed.
.It \ep
half-second pause.
.It \er
carriage return.
.It \eS, \es
space character.
.It \et
tab.
.It \exNN
hexadecimal byte value.
.It \e0NNN
octal byte value.
.El
.Pp
Note that the
.Ql \ep
sequence is only valid for send strings and causes a half-second
pause between sending the previous and next characters.
Hexidecimal values are, at most, 2 hex digits long, and octal
values are a maximum of 3 octal digits.
.Pp
The
.Em \&ic
chat sequence is used to initialize a modem or similar device.
A typical example of an init chat script for a modem with a
hayes compatible command set might look like this:
.Pp
.Dl :ic="" ATE0Q0V1\er OK\er ATS0=0\er OK\er:
.Pp
This script waits for nothing (which always succeeds), sends
a sequence to ensure that the modem is in the correct mode
(suppress command echo, send responses in verbose mode),
and then disables auto-answer.
It waits for an "OK" response before it terminates.
The init sequence is used to check modem responses to ensure that
the modem is functioning correctly.
If the init script fails to complete,
.Nm getty
considers this to be fatal, and results in an error logged via
.Xr syslogd 8 ,
and exiting.
.Pp
Similarly, an answer chat script is used to manually answer the
phone in response to (usually) a "RING".
When run with an answer script,
.Nm getty
opens the port in non-blocking mode, clears any extraneous input
and waits for data on the port.
As soon as any data is available, the answer chat script is
started and scanned for a string, and responds according to
the answer chat script.
With a hayes compatible modem, this would normally look something
like:
.Pp
.Dl :ac=RING\er ATA\er CONNECT:
.Pp
This causes the modem to answer the call via the "ATA" command,
then scans input for a "CONNECT" string.
If this is received before a
.Em \&ct timeout, then a normal login sequence commences.
.Pp
The
.Em \&ct
capability specifies a timeout for all send and expect strings.
This timeout is set individually for each expect wait and send
string and must be at least as long as the time it takes for
a connection to be established between a remote and local
modem (usually around 10 seconds).
.Pp
In most situations, you will want to flush any additional
input after the connection has been detected, and the
.Em \&de
capability may be used to do that, as well as delay for a
short time after the connection has been established during
which all of the connection data has been sent by the modem.
.Pp
.Sh SEE ALSO
.Xr login 1 ,
.Xr gethostname 3 ,
.Xr uname 3 ,
.Xr termcap 5 ,
.Xr getty 8 .
.Xr getty 8 ,
.Xr telnetd 8 .
.Sh BUGS
The special characters (erase, kill, etc.) are reset to system defaults
by

View File

@ -87,6 +87,9 @@ struct gettyflags {
#define LN gettystrs[23].value
#define Lo gettystrs[24].value
#define PP gettystrs[25].value
#define IF gettystrs[26].value
#define IC gettystrs[27].value
#define AC gettystrs[28].value
/*
* Numeric definitions.
@ -132,6 +135,10 @@ struct gettyflags {
#define O2 gettynums[24].value
#define O2set gettynums[24].set
#define DE gettynums[25].value
#define RTset gettynums[26].set
#define RT gettynums[26].value
#define CT gettynums[27].value
#define DC gettynums[28].value
/*
* Boolean values.
@ -162,4 +169,5 @@ struct gettyflags {
#define DX gettyflags[20].value
#define NP gettyflags[21].value
#define MB gettyflags[22].value
#define HW gettyflags[23].value

View File

@ -77,6 +77,9 @@ struct gettystrs gettystrs[] = {
{ "ln", &tmode.c_cc[VLNEXT] }, /* literal next */
{ "Lo" }, /* locale for strftime() */
{ "pp" }, /* ppp login program */
{ "if" }, /* sysv-like 'issue' filename */
{ "ic" }, /* modem init-chat */
{ "ac" }, /* modem answer-chat */
{ 0 }
};
@ -107,6 +110,9 @@ struct gettynums gettynums[] = {
{ "o1" }, /* input o_flags */
{ "o2" }, /* user mode o_flags */
{ "de" }, /* delay before sending 1st prompt */
{ "rt" }, /* reset timeout */
{ "ct" }, /* chat script timeout */
{ "dc" }, /* debug chat script value */
{ 0 }
};
@ -135,5 +141,6 @@ struct gettyflags gettyflags[] = {
{ "dx", 0 }, /* set decctlq */
{ "np", 0 }, /* no parity at all (8bit chars) */
{ "mb", 0 }, /* do MDMBUF flow control */
{ "hw", 0 }, /* do CTSRTS flow control */
{ 0 }
};

View File

@ -128,8 +128,6 @@ char partab[] = {
#define KILL tmode.c_cc[VKILL]
#define EOT tmode.c_cc[VEOF]
jmp_buf timeout;
static void dingdong __P((int));
static int getname __P((void));
static void interrupt __P((int));
@ -140,9 +138,15 @@ static void putf __P((const char *));
static void putpad __P((const char *));
static void puts __P((const char *));
static void timeoverrun __P((int));
static char *getline __P((int));
static void setttymode __P((const char *, int));
static void setdefttymode __P((const char *));
static int opentty __P((const char *, int));
int main __P((int, char **));
jmp_buf timeout;
static void
dingdong(signo)
int signo;
@ -179,7 +183,7 @@ main(argc, argv)
{
extern char **environ;
const char *tname;
int repcnt = 0, failopenlogged = 0;
int repcnt = 0, failopenlogged = 0, first_time = 1;
struct rlimit limit;
int rval;
@ -199,6 +203,12 @@ main(argc, argv)
limit.rlim_cur = GETTY_TIMEOUT;
(void)setrlimit(RLIMIT_CPU, &limit);
gettable("default", defent);
gendefaults();
tname = "default";
if (argc > 1)
tname = argv[1];
/*
* The following is a work around for vhangup interactions
* which cause great problems getting window systems started.
@ -207,7 +217,7 @@ main(argc, argv)
* J. Gettys - MIT Project Athena.
*/
if (argc <= 2 || strcmp(argv[2], "-") == 0)
strcpy(ttyn, ttyname(0));
strcpy(ttyn, ttyname(STDIN_FILENO));
else {
int i;
@ -217,23 +227,54 @@ main(argc, argv)
chown(ttyn, 0, 0);
chmod(ttyn, 0600);
revoke(ttyn);
while ((i = open(ttyn, O_RDWR)) == -1) {
if ((repcnt % 10 == 0) &&
(errno != ENXIO || !failopenlogged)) {
syslog(LOG_ERR, "%s: %m", ttyn);
closelog();
failopenlogged = 1;
gettable(tname, tabent);
/* Init modem sequence has been specified
*/
if (IC) {
if (!opentty(ttyn, O_RDWR|O_NONBLOCK))
exit(1);
setdefttymode(tname);
if (getty_chat(IC, CT, DC) > 0) {
syslog(LOG_ERR, "modem init problem on %s", ttyn);
exit(1);
}
repcnt++;
sleep(60);
}
login_tty(i);
if (AC) {
int i, rfds;
struct timeval timeout;
if (!opentty(ttyn, O_RDWR|O_NONBLOCK))
exit(1);
setdefttymode(tname);
rfds = 1 << 0; /* FD_SET */
timeout.tv_sec = RT;
timeout.tv_usec = 0;
i = select(32, (fd_set*)&rfds, (fd_set*)NULL,
(fd_set*)NULL, RT ? &timeout : NULL);
if (i < 0) {
syslog(LOG_ERR, "select %s: %m", ttyn);
} else if (i == 0) {
syslog(LOG_NOTICE, "recycle tty %s", ttyn);
exit(0); /* recycle for init */
}
i = getty_chat(AC, CT, DC);
if (i > 0) {
syslog(LOG_ERR, "modem answer problem on %s", ttyn);
exit(1);
}
} else { /* blocking open */
if (!opentty(ttyn, O_RDWR))
exit(1);
}
}
}
/* Start with default tty settings */
if (tcgetattr(0, &tmode) < 0) {
syslog(LOG_ERR, "%s: %m", ttyn);
if (tcgetattr(STDIN_FILENO, &tmode) < 0) {
syslog(LOG_ERR, "tcgetattr %s: %m", ttyn);
exit(1);
}
/*
@ -249,37 +290,9 @@ main(argc, argv)
tmode.c_cflag = TTYDEF_CFLAG;
omode = tmode;
gettable("default", defent);
gendefaults();
tname = "default";
if (argc > 1)
tname = argv[1];
for (;;) {
int off = 0;
gettable(tname, tabent);
if (OPset || EPset || APset)
APset++, OPset++, EPset++;
setdefaults();
off = 0;
(void)tcflush(0, TCIOFLUSH); /* clear out the crap */
ioctl(0, FIONBIO, &off); /* turn off non-blocking mode */
ioctl(0, FIOASYNC, &off); /* ditto for async mode */
if (IS)
cfsetispeed(&tmode, speed(IS));
else if (SP)
cfsetispeed(&tmode, speed(SP));
if (OS)
cfsetospeed(&tmode, speed(OS));
else if (SP)
cfsetospeed(&tmode, speed(SP));
setflags(0);
setchars();
if (tcsetattr(0, TCSANOW, &tmode) < 0) {
syslog(LOG_ERR, "%s: %m", ttyn);
exit(1);
}
setttymode(tname, 0);
if (AB) {
tname = autobaud();
continue;
@ -294,15 +307,34 @@ main(argc, argv)
/* if a delay was specified then sleep for that
number of seconds before writing the initial prompt */
if(DE)
if(DE) {
sleep(DE);
/* remove any noise */
(void)tcflush(STDIN_FILENO, TCIOFLUSH);
}
/* if this is the first time through this, and an
issue file has been given, then send it */
if (first_time && IF) {
int fd;
if ((fd = open(IF, O_RDONLY)) != -1) {
char * cp;
while ((cp = getline(fd)) != NULL) {
putf(cp);
}
close(fd);
}
first_time = 0;
}
if (IM && *IM)
putf(IM);
if (setjmp(timeout)) {
cfsetispeed(&tmode, B0);
cfsetospeed(&tmode, B0);
(void)tcsetattr(0, TCSANOW, &tmode);
(void)tcsetattr(STDIN_FILENO, TCSANOW, &tmode);
exit(1);
}
if (TO) {
@ -336,8 +368,8 @@ main(argc, argv)
if (lower || LC)
tmode.sg_flags &= ~LCASE;
#endif
if (tcsetattr(0, TCSANOW, &tmode) < 0) {
syslog(LOG_ERR, "%s: %m", ttyn);
if (tcsetattr(STDIN_FILENO, TCSANOW, &tmode) < 0) {
syslog(LOG_ERR, "tcsetattr %s: %m", ttyn);
exit(1);
}
signal(SIGINT, SIG_DFL);
@ -360,13 +392,88 @@ main(argc, argv)
}
}
static int
opentty(const char *ttyn, int flags)
{
int i, j = 0;
int failopenlogged = 0;
while (j < 10 && (i = open(ttyn, flags)) == -1)
{
if (((j % 10) == 0) && (errno != ENXIO || !failopenlogged)) {
syslog(LOG_ERR, "open %s: %m", ttyn);
failopenlogged = 1;
}
j++;
sleep(60);
}
if (i == -1) {
syslog(LOG_ERR, "open %s: %m", ttyn);
return 0;
}
else {
login_tty(i);
return 1;
}
}
static void
setdefttymode(tname)
const char * tname;
{
if (tcgetattr(STDIN_FILENO, &tmode) < 0) {
syslog(LOG_ERR, "tcgetattr %s: %m", ttyn);
exit(1);
}
tmode.c_iflag = TTYDEF_IFLAG;
tmode.c_oflag = TTYDEF_OFLAG;
tmode.c_lflag = TTYDEF_LFLAG;
tmode.c_cflag = TTYDEF_CFLAG;
omode = tmode;
setttymode(tname, 1);
}
static void
setttymode(tname, raw)
const char * tname;
int raw;
{
int off = 0;
gettable(tname, tabent);
if (OPset || EPset || APset)
APset++, OPset++, EPset++;
setdefaults();
(void)tcflush(STDIN_FILENO, TCIOFLUSH); /* clear out the crap */
ioctl(STDIN_FILENO, FIONBIO, &off); /* turn off non-blocking mode */
ioctl(STDIN_FILENO, FIOASYNC, &off); /* ditto for async mode */
if (IS)
cfsetispeed(&tmode, speed(IS));
else if (SP)
cfsetispeed(&tmode, speed(SP));
if (OS)
cfsetospeed(&tmode, speed(OS));
else if (SP)
cfsetospeed(&tmode, speed(SP));
setflags(0);
setchars();
if (raw)
cfmakeraw(&tmode);
if (tcsetattr(STDIN_FILENO, TCSANOW, &tmode) < 0) {
syslog(LOG_ERR, "tcsetattr %s: %m", ttyn);
exit(1);
}
}
static int
getname()
{
register int c;
register char *np;
unsigned char cs;
int ppp_state;
int ppp_state = 0;
int ppp_connection = 0;
/*
@ -384,7 +491,7 @@ getname()
sleep(PF);
PF = 0;
}
if (tcsetattr(0, TCSANOW, &tmode) < 0) {
if (tcsetattr(STDIN_FILENO, TCSANOW, &tmode) < 0) {
syslog(LOG_ERR, "%s: %m", ttyn);
exit(1);
}
@ -557,6 +664,32 @@ prompt()
putchr('\n');
}
static char *
getline(fd)
int fd;
{
int i = 0;
static char linebuf[512];
/*
* This is certainly slow, but it avoids having to include
* stdio.h unnecessarily. Issue files should be small anyway.
*/
while (i < (sizeof linebuf - 3) && read(fd, linebuf+i, 1)==1) {
if (linebuf[i] == '\n') {
/* Don't rely on newline mode, assume raw */
linebuf[i++] = '\r';
linebuf[i++] = '\n';
linebuf[i] = '\0';
return linebuf;
}
++i;
}
linebuf[i] = '\0';
return i ? linebuf : 0;
}
static void
putf(cp)
register const char *cp;

View File

@ -284,6 +284,11 @@ setflags(n)
else
CLR(cflag, MDMBUF);
if (HW)
SET(cflag, CRTSCTS);
else
CLR(cflag, CRTSCTS);
if (NL) {
SET(iflag, ICRNL);
SET(oflag, ONLCR|OPOST);