Added:
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:
parent
5e164803b6
commit
c6f2c73939
@ -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
526
libexec/getty/chat.c
Normal 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;
|
||||
}
|
@ -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));
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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 }
|
||||
};
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user