Merge ppp-2.3.1 changes onto mainline

This commit is contained in:
peter 1997-08-22 15:24:36 +00:00
parent 2e41030def
commit 7072a26c53
2 changed files with 621 additions and 126 deletions

View File

@ -1,6 +1,6 @@
.\" -*- nroff -*-
.\" manual page [] for chat 1.8
.\" $Id$
.\" $Id: chat.8,v 1.8 1997/02/22 19:54:22 peter Exp $
.\" SH section heading
.\" SS subsection heading
.\" LP paragraph
@ -43,6 +43,12 @@ Set the file for output of the report strings. If you use the keyword
option is not used and you still use \fIREPORT\fR keywords, the
\fIstderr\fR file is used for the report strings.
.TP
.B -e
Start with the echo option turned on. Echoing may also be turned on
or off at specific points in the chat script by using the \fIECHO\fR
keyword. When echoing is enabled, all output from the modem is echoed
to \fIstderr\fR.
.TP
.B -v
Request that the \fIchat\fR script be executed in a verbose mode. The
\fIchat\fR program will then log all text received from the modem and
@ -52,6 +58,16 @@ Logging is
done to the \fIlocal2\fR facility at level \fIinfo\fR for verbose tracing
and level \fIerr\fR for some errors.
.TP
.B -V
Request that the \fIchat\fR script be executed in a stderr verbose
mode. The \fIchat\fR program will then log all text received from the
modem and the output strings which it sends to the stderr device. This
device is usually the local console at the station running the chat or
pppd program. This option will not work properly if the stderr is
redirected to the /dev/null location as is the case should pppd be run
in the 'detached' mode. In that case, use the '-v' option to record
the session on the SYSLOG device.
.TP
.B script
If the script is not specified in a file with the \fI-f\fR option then
the script is included as parameters to the \fIchat\fR program.
@ -109,6 +125,20 @@ for the same login: prompt, however, if one was not received, a single
return sequence is sent and then it will look for login: again. Should line
noise obscure the first login prompt then sending the empty line will
usually generate a login prompt again.
.SH COMMENTS
Comments can be embedded in the chat script. A comment is a line which
starts with the \fB#\fR (hash) character in column 1. Such comment
lines are just ignored by the chat program. If a '#' character is to
be expected as the first character of the expect sequence, you should
quote the expect string.
If you want to wait for a prompt that starts with a # (hash)
character, you would have to write something like this:
.IP
# Now wait for the prompt and send logout string
.br
\'# ' logout
.LP
.SH ABORT STRINGS
Many modems will report the status of the call as a string. These
strings may be \fBCONNECTED\fR or \fBNO CARRIER\fR or \fBBUSY\fR. It
@ -132,6 +162,60 @@ character sequence. The script will then fail because it found a match to
the abort string. If it received the string \fINO CARRIER\fR, it will abort
for the same reason. Either string may be received. Either string will
terminate the \fIchat\fR script.
.SH CLR_ABORT STRINGS
This sequence allows for clearing previously set \fBABORT\fR strings.
\fBABORT\fR strings are kept in an array of a pre-determined size (at
compilation time); \fBCLR_ABORT\fR will reclaim the space for cleared
entries so that new strings can use that space.
.SH SAY STRINGS
The \fBSAY\fR directive allows the script to send strings to the user
at the terminal via standard error. If \fBchat\fR is being run by
pppd, and pppd is running as a daemon (detached from its controlling
terminal), standard error will normally be redirected to the file
/etc/ppp/connect-errors.
.LP
\fBSAY\fR strings must be enclosed in single or double quotes. If
carriage return and line feed are needed in the string to be output,
you must explicitely add them to your string.
.LP
The SAY strings could be used to give progress messages in sections of
the script where you want to have 'ECHO OFF' but still let the user
know what is happening. An example is:
.IP
ABORT BUSY
.br
ECHO OFF
.br
SAY "Dialling your ISP...\\n"
.br
\'' ATDT5551212
.br
TIMEOUT 120
.br
SAY "Waiting up to 2 minutes for connection ... "
.br
CONNECT ''
.br
SAY "Connected, now logging in ...\n"
.br
ogin: account
.br
ssword: pass
.br
$ \c
SAY "Logged in OK ...\n"
\fIetc ...\fR
.LP
This sequence will only present the SAY strings to the user and all
the details of the script will remain hidden. For example, if the
above script works, the user will see:
.IP
Dialling your ISP...
.br
Waiting up to 2 minutes for connection ... Connected, now logging in ...
.br
Logged in OK ...
.LP
.SH REPORT STRINGS
A \fBreport\fR string is similar to the ABORT string. The difference
@ -158,6 +242,82 @@ ATDT5551212 to dial the telephone. The expected string is
of the script is executed. In addition the program will write to the
expect-file the string "CONNECT" plus any characters which follow it
such as the connection rate.
.SH CLR_REPORT STRINGS
This sequence allows for clearing previously set \fBREPORT\fR strings.
\fBREPORT\fR strings are kept in an array of a pre-determined size (at
compilation time); \fBCLR_REPORT\fR will reclaim the space for cleared
entries so that new strings can use that space.
.SH ECHO
The echo options controls whether the output from the modem is echoed
to \fIstderr\fR. This option may be set with the \fI-e\fR option, but
it can also be controlled by the \fIECHO\fR keyword. The "expect-send"
pair \fIECHO\fR \fION\fR enables echoing, and \fIECHO\fR \fIOFF\fR
disables it. With this keyword you can select which parts of the
conversation should be visible. For instance, with the following
script:
.IP
ABORT 'BUSY'
.br
ABORT 'NO CARRIER'
.br
'' ATZ
.br
OK\\r\\n ATD1234567
.br
\\r\\n \\c
.br
ECHO ON
.br
CONNECT \\c
.br
ogin: account
.LP
all output resulting from modem configuration and dialing is not visible,
but starting with the \fICONNECT\fR (or \fIBUSY\fR) message, everything
will be echoed.
.SH HANGUP
The HANGUP options control whether a modem hangup should be considered
as an error or not. This option is useful in scripts for dialling
systems which will hang up and call your system back. The HANGUP
options can be \fBON\fR or \fBOFF\fR.
.br
When HANGUP is set OFF and the modem hangs up (e.g., after the first
stage of logging in to a callback system), \fBchat\fR will continue
running the script (e.g., waiting for the incoming call and second
stage login prompt). As soon as the incoming call is connected, you
should use the \fBHANGUP ON\fR directive to reinstall normal hang up
signal behavior. Here is an (simple) example script:
.IP
ABORT 'BUSY'
.br
'' ATZ
.br
OK\\r\\n ATD1234567
.br
\\r\\n \\c
.br
CONNECT \\c
.br
\'Callback login:' call_back_ID
.br
HANGUP OFF
.br
ABORT "Bad Login"
.br
\'Callback Password:' Call_back_password
.br
TIMEOUT 120
.br
CONNECT \\c
.br
HANGUP ON
.br
ABORT "NO CARRIER"
.br
ogin:--BREAK--ogin: real_account
.br
\fIetc ...\fR
.LP
.SH TIMEOUT
The initial timeout value is 45 seconds. This may be changed using the \fB-t\fR
parameter.

View File

@ -13,14 +13,51 @@
*
* This software is in the public domain.
*
* Please send all bug reports, requests for information, etc. to:
* -----------------
*
* Added SAY keyword to send output to stderr.
* This allows to turn ECHO OFF and to output specific, user selected,
* text to give progress messages. This best works when stderr
* exists (i.e.: pppd in nodetach mode).
*
* Added HANGUP directives to allow for us to be called
* back. When HANGUP is set to NO, chat will not hangup at HUP signal.
* We rely on timeouts in that case.
*
* Added CLR_ABORT to clear previously set ABORT string. This has been
* dictated by the HANGUP above as "NO CARRIER" (for example) must be
* an ABORT condition until we know the other host is going to close
* the connection for call back. As soon as we have completed the
* first stage of the call back sequence, "NO CARRIER" is a valid, non
* fatal string. As soon as we got called back (probably get "CONNECT"),
* we should re-arm the ABORT "NO CARRIER". Hence the CLR_ABORT command.
* Note that CLR_ABORT packs the abort_strings[] array so that we do not
* have unused entries not being reclaimed.
*
* In the same vein as above, added CLR_REPORT keyword.
*
* Allow for comments. Line starting with '#' are comments and are
* ignored. If a '#' is to be expected as the first character, the
* expect string must be quoted.
*
*
* Francis Demierre <Francis@SwissMail.Com>
* Thu May 15 17:15:40 MET DST 1997
*
* Al Longyear (longyear@netcom.com)
* (I was the last person to change this code.)
*
* Added -r "report file" switch & REPORT keyword.
* Robert Geer <bgeer@xmission.com>
*
*
* Added -e "echo" switch & ECHO keyword
* Dick Streefland <dicks@tasking.nl>
*
*
* Considerable updates and modifications by
* Al Longyear <longyear@pobox.com>
* Paul Mackerras <paulus@cs.anu.edu.au>
*
*
* The original author is:
*
* Karl Fox <karl@MorningStar.Com>
@ -29,17 +66,22 @@
* Columbus, OH 43221
* (614)451-1883
*
*
*/
static char rcsid[] = "$Id: chat.c,v 1.7 1997/04/02 09:55:26 jmg Exp $";
#ifndef lint
static char rcsid[] = "$Id: chat.c,v 1.8 1997/06/24 06:52:33 charnier Exp $";
#endif
#include <stdio.h>
#include <ctype.h>
#include <time.h>
#include <fcntl.h>
#include <signal.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <syslog.h>
@ -90,7 +132,9 @@ static int _O = 0; /* Internal state */
#define MAX_REPORTS 50
#define DEFAULT_CHAT_TIMEOUT 45
int echo = 0;
int verbose = 0;
int Verbose = 0;
int quiet = 0;
int report = 0;
int exit_code = 0;
@ -117,11 +161,15 @@ struct termios saved_tty_parameters;
char *abort_string[MAX_ABORTS], *fail_reason = (char *)0,
fail_buffer[50];
int n_aborts = 0, abort_next = 0, timeout_next = 0;
int n_aborts = 0, abort_next = 0, timeout_next = 0, echo_next = 0;
int clear_abort_next = 0;
char *report_string[MAX_REPORTS] ;
char report_buffer[50] ;
int n_reports = 0, report_next = 0, report_gathering = 0 ;
int clear_report_next = 0;
int say_next = 0, hup_next = 0;
void *dup_mem __P((void *b, size_t c));
void *copy_of __P((char *s));
@ -137,6 +185,7 @@ SIGTYPE sighup __P((int signo));
void unalarm __P((void));
void init __P((void));
void set_tty_parameters __P((void));
void echo_stderr __P((int));
void break_sequence __P((void));
void terminate __P((int status));
void do_file __P((char *chat_file));
@ -152,6 +201,10 @@ char *clean __P((register char *s, int sending));
void break_sequence __P((void));
void terminate __P((int status));
void die __P((void));
void pack_array __P((char **array, int end));
char *expect_strtok __P((char *, char *));
int main __P((int, char *[]));
void *dup_mem(b, c)
void *b;
@ -188,16 +241,24 @@ char **argv;
tzset();
while (option = OPTION(argc, argv))
while ((option = OPTION(argc, argv)) != 0)
{
switch (option)
{
case 'e':
++echo;
break;
case 'v':
++verbose;
break;
case 'V':
++Verbose;
break;
case 'f':
if (arg = OPTARG(argc, argv))
if ((arg = OPTARG(argc, argv)) != NULL)
{
chat_file = copy_of(arg);
}
@ -208,7 +269,7 @@ char **argv;
break;
case 't':
if (arg = OPTARG(argc, argv))
if ((arg = OPTARG(argc, argv)) != NULL)
{
timeout = atoi(arg);
}
@ -284,11 +345,11 @@ char **argv;
}
else
{
while (arg = ARG(argc, argv))
while ((arg = ARG(argc, argv)) != NULL)
{
chat_expect(arg);
if (arg = ARG(argc, argv))
if ((arg = ARG(argc, argv)) != NULL)
{
chat_send(arg);
}
@ -296,6 +357,7 @@ char **argv;
}
terminate(0);
return 0;
}
/*
@ -330,6 +392,11 @@ char *chat_file;
linect++;
sp = buf;
/* lines starting with '#' are comments. If a real '#'
is to be expected, it should be quoted .... */
if ( *sp == '#' ) continue;
while (*sp != '\0')
{
if (*sp == ' ' || *sp == '\t')
@ -395,7 +462,7 @@ static void
usage()
{
fprintf(stderr, "%s %s\n",
"usage: chat [-v] [-t timeout] [-r report-file]",
"usage: chat [-e] [-v] [-V] [-t timeout] [-r report-file]",
"{-f chat-file | chat-script}");
exit(1);
}
@ -405,16 +472,20 @@ char *p;
void logf (str)
const char *str;
{
p = line + strlen(line);
strcat (p, str);
{
int l = strlen(line);
if (str[strlen(str)-1] == '\n')
{
if (l + strlen(str) >= sizeof(line)) {
syslog(LOG_INFO, "%s", line);
l = 0;
}
strcpy(line + l, str);
if (str[strlen(str)-1] == '\n') {
syslog (LOG_INFO, "%s", line);
line[0] = 0;
}
}
}
void logflush()
{
@ -572,14 +643,34 @@ void break_sequence()
void terminate(status)
int status;
{
echo_stderr(-1);
if (report_file != (char *) 0 && report_fp != (FILE *) NULL)
{
{
/*
* Allow the last of the report string to be gathered before we terminate.
*/
if (report_gathering) {
int c, rep_len;
rep_len = strlen(report_buffer);
while (rep_len + 1 <= sizeof(report_buffer)) {
alarm(1);
c = get_char();
alarm(0);
if (c < 0 || iscntrl(c))
break;
report_buffer[rep_len] = c;
++rep_len;
}
report_buffer[rep_len] = 0;
fprintf (report_fp, "chat: %s\n", report_buffer);
}
if (verbose)
{
fprintf (report_fp, "Closing \"%s\".\n", report_file);
}
fclose (report_fp);
report_fp = (FILE*) NULL;
report_fp = (FILE *) NULL;
}
#if defined(get_term_param)
@ -675,7 +766,7 @@ int sending;
break;
case 'q':
quiet = ! quiet;
quiet = 1;
break;
case 'r':
@ -751,105 +842,174 @@ int sending;
return dup_mem (temp, (size_t) (s1 - temp)); /* may have embedded nuls */
}
/*
* A modified version of 'strtok'. This version skips \ sequences.
*/
char *expect_strtok (s, term)
char *s, *term;
{
static char *str = "";
int escape_flag = 0;
char *result;
/*
* If a string was specified then do initial processing.
*/
if (s)
{
str = s;
}
/*
* If this is the escape flag then reset it and ignore the character.
*/
if (*str)
{
result = str;
}
else
{
result = (char *) 0;
}
while (*str)
{
if (escape_flag)
{
escape_flag = 0;
++str;
continue;
}
if (*str == '\\')
{
++str;
escape_flag = 1;
continue;
}
/*
* If this is not in the termination string, continue.
*/
if (strchr (term, *str) == (char *) 0)
{
++str;
continue;
}
/*
* This is the terminator. Mark the end of the string and stop.
*/
*str++ = '\0';
break;
}
return (result);
}
/*
* Process the expect string
*/
void chat_expect(s)
register char *s;
void chat_expect (s)
char *s;
{
char *expect;
char *reply;
if (strcmp(s, "HANGUP") == 0)
{
++hup_next;
return;
}
if (strcmp(s, "ABORT") == 0)
{
++abort_next;
return;
}
if (strcmp(s, "CLR_ABORT") == 0)
{
++clear_abort_next;
return;
}
if (strcmp(s, "REPORT") == 0)
{
++report_next;
return;
}
if (strcmp(s, "CLR_REPORT") == 0)
{
++clear_report_next;
return;
}
if (strcmp(s, "TIMEOUT") == 0)
{
++timeout_next;
return;
}
while (*s)
if (strcmp(s, "ECHO") == 0)
{
register char *hyphen;
for (hyphen = s; *hyphen; ++hyphen)
{
if (*hyphen == '-')
{
if (hyphen == s || hyphen[-1] != '\\')
{
break;
}
}
}
if (*hyphen == '-')
{
*hyphen = '\0';
if (get_string(s))
{
return;
}
else
{
s = hyphen + 1;
for (hyphen = s; *hyphen; ++hyphen)
{
if (*hyphen == '-')
{
if (hyphen == s || hyphen[-1] != '\\')
{
break;
}
}
}
if (*hyphen == '-')
{
*hyphen = '\0';
chat_send(s);
s = hyphen + 1;
}
else
{
chat_send(s);
return;
}
}
}
else
{
if (get_string(s))
{
return;
}
else
{
if (fail_reason)
{
syslog(LOG_INFO, "Failed (%s)", fail_reason);
}
else
{
syslog(LOG_INFO, "Failed");
}
terminate(exit_code);
}
}
++echo_next;
return;
}
if (strcmp(s, "SAY") == 0)
{
++say_next;
return;
}
/*
* Fetch the expect and reply string.
*/
for (;;)
{
expect = expect_strtok (s, "-");
s = (char *) 0;
if (expect == (char *) 0)
{
return;
}
reply = expect_strtok (s, "-");
/*
* Handle the expect string. If successful then exit.
*/
if (get_string (expect))
{
return;
}
/*
* If there is a sub-reply string then send it. Otherwise any condition
* is terminal.
*/
if (reply == (char *) 0 || exit_code != 3)
{
break;
}
chat_send (reply);
}
/*
* The expectation did not occur. This is terminal.
*/
if (fail_reason)
{
syslog(LOG_INFO, "Failed (%s)", fail_reason);
}
else
{
syslog(LOG_INFO, "Failed");
}
terminate(exit_code);
}
/*
* Translate the input character to the appropriate string for printing
* the data.
*/
char *character(c)
int c;
{
@ -884,6 +1044,29 @@ int c;
void chat_send (s)
register char *s;
{
if (say_next)
{
say_next = 0;
s = clean(s,0);
write(2, s, strlen(s));
free(s);
return;
}
if (hup_next)
{
hup_next = 0;
if (strcmp(s, "OFF") == 0)
signal(SIGHUP, SIG_IGN);
else
signal(SIGHUP, sighup);
return;
}
if (echo_next)
{
echo_next = 0;
echo = (strcmp(s, "ON") == 0);
return;
}
if (abort_next)
{
char *s1;
@ -920,6 +1103,52 @@ register char *s;
return;
}
if (clear_abort_next)
{
char *s1;
char *s2 = s;
int i;
int old_max;
int pack = 0;
clear_abort_next = 0;
s1 = clean(s, 0);
if (strlen(s1) > strlen(s)
|| strlen(s1) + 1 > sizeof(fail_buffer))
{
syslog(LOG_WARNING, "Illegal or too-long CLR_ABORT string ('%s')", s);
die();
}
old_max = n_aborts;
for (i=0; i < n_aborts; i++)
{
if ( strcmp(s1,abort_string[i]) == 0 )
{
free(abort_string[i]);
abort_string[i] = NULL;
pack++;
n_aborts--;
if (verbose)
{
logf("clear abort on (");
for (s2 = s; *s2; ++s2)
{
logf(character(*s2));
}
logf(")\n");
}
}
}
free(s1);
if (pack) pack_array(abort_string,old_max);
return;
}
if (report_next)
{
char *s1;
@ -954,6 +1183,52 @@ register char *s;
return;
}
if (clear_report_next)
{
char *s1;
char *s2 = s;
int i;
int old_max;
int pack = 0;
clear_report_next = 0;
s1 = clean(s, 0);
if (strlen(s1) > strlen(s) || strlen(s1) > sizeof fail_buffer - 1)
{
syslog(LOG_WARNING, "Illegal or too-long REPORT string ('%s')", s);
die();
}
old_max = n_reports;
for (i=0; i < n_reports; i++)
{
if ( strcmp(s1,report_string[i]) == 0 )
{
free(report_string[i]);
report_string[i] = NULL;
pack++;
n_reports--;
if (verbose)
{
logf("clear report (");
for (s2 = s; *s2; ++s2)
{
logf(character(*s2));
}
logf(")\n");
}
}
}
free(s1);
if (pack) pack_array(report_string,old_max);
return;
}
if (timeout_next)
{
timeout_next = 0;
@ -1088,6 +1363,7 @@ int c;
int put_string (s)
register char *s;
{
quiet = 0;
s = clean(s, 1);
if (verbose)
@ -1153,6 +1429,37 @@ register char *s;
return (1);
}
/*
* Echo a character to stderr.
* When called with -1, a '\n' character is generated when
* the cursor is not at the beginning of a line.
*/
void echo_stderr(n)
int n;
{
static int need_lf;
char *s;
switch (n)
{
case '\r': /* ignore '\r' */
break;
case -1:
if (need_lf == 0)
break;
/* fall through */
case '\n':
write(2, "\n", 1);
need_lf = 0;
break;
default:
s = character(n);
write(2, s, strlen(s));
need_lf = 1;
break;
}
}
/*
* 'Wait for' this string to appear on this file descriptor.
*/
@ -1206,6 +1513,10 @@ register char *string;
{
int n, abort_len, report_len;
if (echo)
{
echo_stderr(c);
}
if (verbose)
{
if (c == '\n')
@ -1218,40 +1529,13 @@ register char *string;
}
}
if (Verbose) {
if (c == '\n') fputc( '\n', stderr );
else if (c != '\r') fprintf( stderr, "%s", character(c) );
}
*s++ = c;
if (s - temp >= len &&
c == string[len - 1] &&
strncmp(s - len, string, len) == 0)
{
if (verbose)
{
logf(" -- got it\n");
}
alarm(0);
alarmed = 0;
return (1);
}
for (n = 0; n < n_aborts; ++n)
{
if (s - temp >= (abort_len = strlen(abort_string[n])) &&
strncmp(s - abort_len, abort_string[n], abort_len) == 0)
{
if (verbose)
{
logf(" -- failed\n");
}
alarm(0);
alarmed = 0;
exit_code = n + 4;
strcpy(fail_reason = fail_buffer, abort_string[n]);
return (0);
}
}
if (!report_gathering)
{
for (n = 0; n < n_reports; ++n)
@ -1287,6 +1571,38 @@ register char *string;
}
}
if (s - temp >= len &&
c == string[len - 1] &&
strncmp(s - len, string, len) == 0)
{
if (verbose)
{
logf(" -- got it\n");
}
alarm(0);
alarmed = 0;
return (1);
}
for (n = 0; n < n_aborts; ++n)
{
if (s - temp >= (abort_len = strlen(abort_string[n])) &&
strncmp(s - abort_len, abort_string[n], abort_len) == 0)
{
if (verbose)
{
logf(" -- failed\n");
}
alarm(0);
alarmed = 0;
exit_code = n + 4;
strcpy(fail_reason = fail_buffer, abort_string[n]);
return (0);
}
}
if (s >= end)
{
strncpy (temp, s - minlen, minlen);
@ -1346,3 +1662,22 @@ usleep( usec ) /* returns 0 if ok, else -1 */
return select( 0, (long *)0, (long *)0, (long *)0, &delay );
}
#endif
void
pack_array (array, end)
char **array; /* The address of the array of string pointers */
int end; /* The index of the next free entry before CLR_ */
{
int i, j;
for (i = 0; i < end; i++) {
if (array[i] == NULL) {
for (j = i+1; j < end; ++j)
if (array[j] != NULL)
array[i++] = array[j];
for (; i < end; ++i)
array[i] = NULL;
break;
}
}
}