Allow (and document) execution of commands from within
our chat script. You can now even run chat(8) - see ppp.conf.sample.
This commit is contained in:
parent
6293b3997d
commit
313572f3e3
@ -4,7 +4,7 @@
|
||||
#
|
||||
# Originally written by Toshiharu OHNO
|
||||
#
|
||||
# $Id: ppp.conf.sample,v 1.24 1997/11/12 00:52:16 brian Exp $
|
||||
# $Id: ppp.conf.sample,v 1.25 1997/11/18 19:21:47 brian Exp $
|
||||
#
|
||||
#################################################################
|
||||
|
||||
@ -125,6 +125,10 @@ examples:
|
||||
#
|
||||
set server 6670
|
||||
#
|
||||
# If you don't like ppp's builtin chat, use an external one:
|
||||
#
|
||||
set login "\"!chat \\\\-f /etc/ppp/ppp.dev.chat\""
|
||||
#
|
||||
# If we have a ``strange'' modem that must be re-initialized when we
|
||||
# hangup:
|
||||
#
|
||||
|
@ -18,7 +18,7 @@
|
||||
* Columbus, OH 43221
|
||||
* (614)451-1883
|
||||
*
|
||||
* $Id: chat.c,v 1.41 1997/12/23 22:38:51 brian Exp $
|
||||
* $Id: chat.c,v 1.42 1997/12/24 09:28:54 brian Exp $
|
||||
*
|
||||
* TODO:
|
||||
* o Support more UUCP compatible control sequences.
|
||||
@ -66,6 +66,7 @@ static int abort_next, timeout_next;
|
||||
static int numaborts;
|
||||
static char *AbortStrings[50];
|
||||
static char inbuff[IBSIZE * 2 + 1];
|
||||
static jmp_buf ChatEnv;
|
||||
|
||||
#define MATCH 1
|
||||
#define NOMATCH 0
|
||||
@ -275,6 +276,92 @@ connect_log(const char *str, int single_p)
|
||||
flush_log();
|
||||
}
|
||||
|
||||
static void
|
||||
ExecStr(char *command, char *out, int olen)
|
||||
{
|
||||
pid_t pid;
|
||||
int fids[2];
|
||||
char *vector[MAXARGS], *startout, *endout;
|
||||
int stat, nb;
|
||||
|
||||
LogPrintf(LogCHAT, "Exec: %s\n", command);
|
||||
MakeArgs(command, vector, VECSIZE(vector));
|
||||
|
||||
if (pipe(fids) < 0) {
|
||||
LogPrintf(LogCHAT, "Unable to create pipe in ExecStr: %s\n",
|
||||
strerror(errno));
|
||||
longjmp(ChatEnv, 2);
|
||||
}
|
||||
if ((pid = fork()) == 0) {
|
||||
TermTimerService();
|
||||
signal(SIGINT, SIG_DFL);
|
||||
signal(SIGQUIT, SIG_DFL);
|
||||
signal(SIGTERM, SIG_DFL);
|
||||
signal(SIGHUP, SIG_DFL);
|
||||
signal(SIGALRM, SIG_DFL);
|
||||
if (modem == 2) {
|
||||
int nmodem;
|
||||
nmodem = dup(modem);
|
||||
close(modem);
|
||||
modem = nmodem;
|
||||
}
|
||||
close(fids[0]);
|
||||
dup2(fids[1], 2);
|
||||
close(fids[1]);
|
||||
dup2(modem, 0);
|
||||
dup2(modem, 1);
|
||||
if ((nb = open("/dev/tty", O_RDWR)) > 3) {
|
||||
dup2(nb, 3);
|
||||
close(nb);
|
||||
}
|
||||
setuid(geteuid());
|
||||
execvp(vector[0], vector);
|
||||
fprintf(stderr, "execvp failed: %s: %s\n", vector[0], strerror(errno));
|
||||
exit(127);
|
||||
} else {
|
||||
char *name = strdup(vector[0]);
|
||||
|
||||
close(fids[1]);
|
||||
endout = out + olen - 1;
|
||||
startout = out;
|
||||
while (out < endout) {
|
||||
nb = read(fids[0], out, 1);
|
||||
if (nb <= 0)
|
||||
break;
|
||||
out++;
|
||||
}
|
||||
*out = '\0';
|
||||
close(fids[0]);
|
||||
close(fids[1]);
|
||||
waitpid(pid, &stat, WNOHANG);
|
||||
if (WIFSIGNALED(stat)) {
|
||||
LogPrintf(LogWARN, "%s: signal %d\n", name, WTERMSIG(stat));
|
||||
free(name);
|
||||
longjmp(ChatEnv, 3);
|
||||
} else if (WIFEXITED(stat)) {
|
||||
switch (WEXITSTATUS(stat)) {
|
||||
case 0:
|
||||
free(name);
|
||||
break;
|
||||
case 127:
|
||||
LogPrintf(LogWARN, "%s: %s\n", name, startout);
|
||||
free(name);
|
||||
longjmp(ChatEnv, 4);
|
||||
break;
|
||||
default:
|
||||
LogPrintf(LogWARN, "%s: exit %d\n", name, WEXITSTATUS(stat));
|
||||
free(name);
|
||||
longjmp(ChatEnv, 5);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
LogPrintf(LogWARN, "%s: Unexpected exit result\n", name);
|
||||
free(name);
|
||||
longjmp(ChatEnv, 6);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
WaitforString(const char *estr)
|
||||
{
|
||||
@ -284,16 +371,33 @@ WaitforString(const char *estr)
|
||||
fd_set rfds;
|
||||
int i, nfds, nb;
|
||||
char buff[IBSIZE];
|
||||
|
||||
|
||||
#ifdef SIGALRM
|
||||
int omask;
|
||||
|
||||
omask = sigblock(sigmask(SIGALRM));
|
||||
#endif
|
||||
clear_log();
|
||||
ExpandString(estr, buff, sizeof buff, 0);
|
||||
LogPrintf(LogCHAT, "Wait for (%d): %s --> %s\n", TimeoutSec, estr, buff);
|
||||
if (*estr == '!') {
|
||||
ExpandString(estr + 1, buff, sizeof buff, 0);
|
||||
ExecStr(buff, buff, sizeof buff);
|
||||
} else {
|
||||
ExpandString(estr, buff, sizeof buff, 0);
|
||||
}
|
||||
if (LogIsKept(LogCHAT)) {
|
||||
s = buff + strlen(buff) - 1;
|
||||
while (s >= buff && *s == '\n')
|
||||
s--;
|
||||
if (!strcmp(estr, buff))
|
||||
LogPrintf(LogCHAT, "Wait for (%d): %.*s\n",
|
||||
TimeoutSec, s - buff + 1, buff);
|
||||
else
|
||||
LogPrintf(LogCHAT, "Wait for (%d): %s --> %.*s\n",
|
||||
TimeoutSec, estr, s - buff + 1, buff);
|
||||
}
|
||||
|
||||
if (buff[0] == '\0')
|
||||
return (MATCH);
|
||||
|
||||
str = buff;
|
||||
inp = inbuff;
|
||||
|
||||
@ -412,74 +516,6 @@ WaitforString(const char *estr)
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
ExecStr(char *command, char *out)
|
||||
{
|
||||
int pid;
|
||||
int fids[2];
|
||||
char *vector[MAXARGS];
|
||||
int stat, nb;
|
||||
char *cp;
|
||||
char tmp[300];
|
||||
|
||||
cp = inbuff + strlen(inbuff) - 1;
|
||||
while (cp > inbuff) {
|
||||
if (*cp < ' ' && *cp != '\t') {
|
||||
cp++;
|
||||
break;
|
||||
}
|
||||
cp--;
|
||||
}
|
||||
if (snprintf(tmp, sizeof tmp, "%s %s", command, cp) >= sizeof tmp) {
|
||||
LogPrintf(LogCHAT, "Too long string to ExecStr: \"%s\"\n", command);
|
||||
return;
|
||||
}
|
||||
MakeArgs(tmp, vector, VECSIZE(vector));
|
||||
|
||||
if (pipe(fids) < 0) {
|
||||
LogPrintf(LogCHAT, "Unable to create pipe in ExecStr: %s\n",
|
||||
strerror(errno));
|
||||
return;
|
||||
}
|
||||
pid = fork();
|
||||
if (pid == 0) {
|
||||
TermTimerService();
|
||||
signal(SIGINT, SIG_DFL);
|
||||
signal(SIGQUIT, SIG_DFL);
|
||||
signal(SIGTERM, SIG_DFL);
|
||||
signal(SIGHUP, SIG_DFL);
|
||||
signal(SIGALRM, SIG_DFL);
|
||||
close(fids[0]);
|
||||
if (dup2(fids[1], 1) < 0) {
|
||||
LogPrintf(LogCHAT, "dup2(fids[1], 1) in ExecStr: %s\n", strerror(errno));
|
||||
return;
|
||||
}
|
||||
close(fids[1]);
|
||||
nb = open("/dev/tty", O_RDWR);
|
||||
if (dup2(nb, 0) < 0) {
|
||||
LogPrintf(LogCHAT, "dup2(nb, 0) in ExecStr: %s\n", strerror(errno));
|
||||
return;
|
||||
}
|
||||
setuid(geteuid());
|
||||
LogPrintf(LogCHAT, "exec: %s\n", command);
|
||||
pid = execvp(command, (char **)vector);
|
||||
LogPrintf(LogCHAT, "execvp failed for (%d/%d): %s\n", pid, errno, command);
|
||||
exit(127);
|
||||
} else {
|
||||
close(fids[1]);
|
||||
for (;;) {
|
||||
nb = read(fids[0], out, 1);
|
||||
if (nb <= 0)
|
||||
break;
|
||||
out++;
|
||||
}
|
||||
*out = '\0';
|
||||
close(fids[0]);
|
||||
close(fids[1]);
|
||||
waitpid(pid, &stat, WNOHANG);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
SendString(const char *str)
|
||||
{
|
||||
@ -499,14 +535,18 @@ SendString(const char *str)
|
||||
} else {
|
||||
if (*str == '!') {
|
||||
ExpandString(str + 1, buff + 2, sizeof buff - 2, 0);
|
||||
ExecStr(buff + 2, buff + 2);
|
||||
ExecStr(buff + 2, buff + 2, sizeof buff - 2);
|
||||
} else {
|
||||
ExpandString(str, buff + 2, sizeof buff - 2, 1);
|
||||
}
|
||||
if (strstr(str, "\\P")) /* Do not log the password itself. */
|
||||
LogPrintf(LogCHAT, "sending: %s\n", str);
|
||||
else
|
||||
LogPrintf(LogCHAT, "sending: %s\n", buff + 2);
|
||||
LogPrintf(LogCHAT, "Sending: %s", str);
|
||||
else {
|
||||
cp = buff + strlen(buff + 2) + 1;
|
||||
while (cp >= buff + 2 && *cp == '\n')
|
||||
cp--;
|
||||
LogPrintf(LogCHAT, "Sending: %.*s\n", cp - buff - 1, buff + 2);
|
||||
}
|
||||
cp = buff;
|
||||
if (DEV_IS_SYNC)
|
||||
memcpy(buff, "\377\003", 2); /* Prepend HDLC header */
|
||||
@ -531,9 +571,8 @@ ExpectString(char *str)
|
||||
++timeout_next;
|
||||
return (MATCH);
|
||||
}
|
||||
LogPrintf(LogCHAT, "Expecting %s\n", str);
|
||||
LogPrintf(LogCHAT, "Expecting: %s\n", str);
|
||||
while (*str) {
|
||||
|
||||
/*
|
||||
* Check whether if string contains sub-send-expect.
|
||||
*/
|
||||
@ -571,7 +610,6 @@ ExpectString(char *str)
|
||||
return (MATCH);
|
||||
}
|
||||
} else {
|
||||
|
||||
/*
|
||||
* Simple case. Wait for string.
|
||||
*/
|
||||
@ -581,7 +619,6 @@ ExpectString(char *str)
|
||||
return (MATCH);
|
||||
}
|
||||
|
||||
static jmp_buf ChatEnv;
|
||||
static void (*oint) (int);
|
||||
|
||||
static void
|
||||
@ -596,15 +633,17 @@ DoChat(char *script)
|
||||
{
|
||||
char *vector[MAXARGS];
|
||||
char *const *argv;
|
||||
int argc, n, state;
|
||||
int argc, n, state, err;
|
||||
|
||||
if (!script || !*script)
|
||||
return MATCH;
|
||||
|
||||
/* While we're chatting, we want an INT to fail us */
|
||||
if (setjmp(ChatEnv)) {
|
||||
if ((err = setjmp(ChatEnv))) {
|
||||
signal(SIGINT, oint);
|
||||
return (-1);
|
||||
if (err == 1)
|
||||
/* Caught a SIGINT during chat */
|
||||
return (-1);
|
||||
return (NOMATCH);
|
||||
}
|
||||
oint = signal(SIGINT, StopDial);
|
||||
|
||||
|
@ -17,7 +17,7 @@
|
||||
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* $Id: command.c,v 1.117 1997/12/23 22:38:52 brian Exp $
|
||||
* $Id: command.c,v 1.118 1997/12/24 09:28:54 brian Exp $
|
||||
*
|
||||
*/
|
||||
#include <sys/param.h>
|
||||
@ -216,7 +216,6 @@ ShellCommand(struct cmdargs const *arg, int bg)
|
||||
{
|
||||
const char *shell;
|
||||
pid_t shpid;
|
||||
FILE *oVarTerm;
|
||||
int argc;
|
||||
char *argv[MAXARGS];
|
||||
|
||||
@ -259,6 +258,13 @@ ShellCommand(struct cmdargs const *arg, int bg)
|
||||
if ((shpid = fork()) == 0) {
|
||||
int dtablesize, i, fd;
|
||||
|
||||
TermTimerService();
|
||||
signal(SIGINT, SIG_DFL);
|
||||
signal(SIGQUIT, SIG_DFL);
|
||||
signal(SIGTERM, SIG_DFL);
|
||||
signal(SIGHUP, SIG_DFL);
|
||||
signal(SIGALRM, SIG_DFL);
|
||||
|
||||
if (VarTerm)
|
||||
fd = fileno(VarTerm);
|
||||
else if ((fd = open("/dev/null", O_RDWR)) == -1) {
|
||||
@ -268,15 +274,6 @@ ShellCommand(struct cmdargs const *arg, int bg)
|
||||
for (i = 0; i < 3; i++)
|
||||
dup2(fd, i);
|
||||
|
||||
if (fd > 2)
|
||||
if (VarTerm) {
|
||||
oVarTerm = VarTerm;
|
||||
VarTerm = 0;
|
||||
if (oVarTerm && oVarTerm != stdout)
|
||||
fclose(oVarTerm);
|
||||
} else
|
||||
close(fd);
|
||||
|
||||
for (dtablesize = getdtablesize(), i = 3; i < dtablesize; i++)
|
||||
close(i);
|
||||
|
||||
@ -305,15 +302,16 @@ ShellCommand(struct cmdargs const *arg, int bg)
|
||||
exit(1);
|
||||
}
|
||||
} else if (VarTerm)
|
||||
fprintf(VarTerm, "ppp: Pausing until %s finishes\n", arg->argv[0]);
|
||||
printf("ppp: Pausing until %s finishes\n", arg->argv[0]);
|
||||
execvp(argv[0], argv);
|
||||
} else {
|
||||
if (VarTerm)
|
||||
fprintf(VarTerm, "ppp: Pausing until %s finishes\n", shell);
|
||||
printf("ppp: Pausing until %s finishes\n", shell);
|
||||
execl(shell, shell, NULL);
|
||||
}
|
||||
|
||||
LogPrintf(LogWARN, "exec() of %s failed\n", arg->argc > 0 ? arg->argv[0] : shell);
|
||||
LogPrintf(LogWARN, "exec() of %s failed\n",
|
||||
arg->argc > 0 ? arg->argv[0] : shell);
|
||||
exit(255);
|
||||
}
|
||||
if (shpid == (pid_t) - 1) {
|
||||
@ -324,7 +322,7 @@ ShellCommand(struct cmdargs const *arg, int bg)
|
||||
waitpid(shpid, &status, 0);
|
||||
}
|
||||
|
||||
TtyCommandMode(1);
|
||||
TtyCommandMode(0);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
.\" $Id: ppp.8,v 1.89 1997/12/21 02:34:27 brian Exp $
|
||||
.\" $Id: ppp.8,v 1.90 1997/12/21 03:16:14 brian Exp $
|
||||
.Dd 20 September 1995
|
||||
.Os FreeBSD
|
||||
.Dt PPP 8
|
||||
@ -1931,6 +1931,77 @@ This means that in practice you should use two escapes, for example:
|
||||
set dial "... ATDT\\\\T CONNECT"
|
||||
.Ed
|
||||
.Pp
|
||||
It is also possible to execute external commands from the chat script.
|
||||
To do this, the first character of the expect or send string is an
|
||||
exclaimation mark
|
||||
.Pq Dq \&! .
|
||||
When the command is executed, standard input and standard output are
|
||||
directed to the modem device (see the
|
||||
.Dq set device
|
||||
command), and standard error is read by
|
||||
.Nm
|
||||
and substituted as the expect or send string. If
|
||||
.Nm
|
||||
is running in interactive mode, file descriptor 4 is attached to
|
||||
.Pa /dev/tty .
|
||||
.Pp
|
||||
For example (wrapped for readability);
|
||||
.Bd -literal -offset indent
|
||||
set login "TIMEOUT 5 \\"\\" \\"\\" login:--login: ppp \e
|
||||
word: ppp \\"!sh \\\\\\\\-c \\\\\\"echo \\\\\\\\-n label: >&2\\\\\\"\\" \e
|
||||
\\"!/bin/echo in\\" HELLO"
|
||||
.Ed
|
||||
.Pp
|
||||
would result in the following chat sequence (output using the
|
||||
.Sq set log local chat
|
||||
command before dialing):
|
||||
.Bd -literal -offset indent
|
||||
Dial attempt 1 of 1
|
||||
dial OK!
|
||||
Chat: Expecting:
|
||||
Chat: Sending:
|
||||
Chat: Expecting: login:--login:
|
||||
Chat: Wait for (5): login:
|
||||
Chat: Sending: ppp
|
||||
Chat: Expecting: word:
|
||||
Chat: Wait for (5): word:
|
||||
Chat: Sending: ppp
|
||||
Chat: Expecting: !sh \\-c "echo \\-n label: >&2"
|
||||
Chat: Exec: sh -c "echo -n label: >&2"
|
||||
Chat: Wait for (5): !sh \\-c "echo \\-n label: >&2" --> label:
|
||||
Chat: Exec: /bin/echo in
|
||||
Chat: Sending:
|
||||
Chat: Expecting: HELLO
|
||||
Chat: Wait for (5): HELLO
|
||||
login OK!
|
||||
.Ed
|
||||
.Pp
|
||||
Note (again) the use of the escape character, allowing many levels of
|
||||
nesting. Here, there are four parsers at work. The first parses the
|
||||
original line, reading it as three arguments. The second parses the
|
||||
third argument, reading it as 11 arguments. At this point, it is
|
||||
important that the
|
||||
.Dq \&-
|
||||
signs are escaped, otherwise this parser will see them as constituting
|
||||
an expect-send-expect sequence. When the
|
||||
.Dq \&!
|
||||
character is seen, the execution parser reads the first command as three
|
||||
arguments, and then
|
||||
.Xr sh 1
|
||||
itself expands the argument after the
|
||||
.Fl c .
|
||||
As we wish to send the output back to the modem, in the first example
|
||||
we redirect our output to file descriptor 2 (stderr) so that
|
||||
.Nm
|
||||
itself sends and logs it, and in the second example, we just output to stdout,
|
||||
which is attached directly to the modem.
|
||||
.Pp
|
||||
This, of course means that it is possible to execute an entirely external
|
||||
.Dq chat
|
||||
command rather than using the internal one. See
|
||||
.Xr chat 8
|
||||
for a good alternative.
|
||||
.Pp
|
||||
.It set hangup chat-script
|
||||
This specifies the chat script that will be used to reset the modem
|
||||
before it is closed. It should not normally be necessary, but can
|
||||
|
@ -1,4 +1,4 @@
|
||||
.\" $Id: ppp.8,v 1.89 1997/12/21 02:34:27 brian Exp $
|
||||
.\" $Id: ppp.8,v 1.90 1997/12/21 03:16:14 brian Exp $
|
||||
.Dd 20 September 1995
|
||||
.Os FreeBSD
|
||||
.Dt PPP 8
|
||||
@ -1931,6 +1931,77 @@ This means that in practice you should use two escapes, for example:
|
||||
set dial "... ATDT\\\\T CONNECT"
|
||||
.Ed
|
||||
.Pp
|
||||
It is also possible to execute external commands from the chat script.
|
||||
To do this, the first character of the expect or send string is an
|
||||
exclaimation mark
|
||||
.Pq Dq \&! .
|
||||
When the command is executed, standard input and standard output are
|
||||
directed to the modem device (see the
|
||||
.Dq set device
|
||||
command), and standard error is read by
|
||||
.Nm
|
||||
and substituted as the expect or send string. If
|
||||
.Nm
|
||||
is running in interactive mode, file descriptor 4 is attached to
|
||||
.Pa /dev/tty .
|
||||
.Pp
|
||||
For example (wrapped for readability);
|
||||
.Bd -literal -offset indent
|
||||
set login "TIMEOUT 5 \\"\\" \\"\\" login:--login: ppp \e
|
||||
word: ppp \\"!sh \\\\\\\\-c \\\\\\"echo \\\\\\\\-n label: >&2\\\\\\"\\" \e
|
||||
\\"!/bin/echo in\\" HELLO"
|
||||
.Ed
|
||||
.Pp
|
||||
would result in the following chat sequence (output using the
|
||||
.Sq set log local chat
|
||||
command before dialing):
|
||||
.Bd -literal -offset indent
|
||||
Dial attempt 1 of 1
|
||||
dial OK!
|
||||
Chat: Expecting:
|
||||
Chat: Sending:
|
||||
Chat: Expecting: login:--login:
|
||||
Chat: Wait for (5): login:
|
||||
Chat: Sending: ppp
|
||||
Chat: Expecting: word:
|
||||
Chat: Wait for (5): word:
|
||||
Chat: Sending: ppp
|
||||
Chat: Expecting: !sh \\-c "echo \\-n label: >&2"
|
||||
Chat: Exec: sh -c "echo -n label: >&2"
|
||||
Chat: Wait for (5): !sh \\-c "echo \\-n label: >&2" --> label:
|
||||
Chat: Exec: /bin/echo in
|
||||
Chat: Sending:
|
||||
Chat: Expecting: HELLO
|
||||
Chat: Wait for (5): HELLO
|
||||
login OK!
|
||||
.Ed
|
||||
.Pp
|
||||
Note (again) the use of the escape character, allowing many levels of
|
||||
nesting. Here, there are four parsers at work. The first parses the
|
||||
original line, reading it as three arguments. The second parses the
|
||||
third argument, reading it as 11 arguments. At this point, it is
|
||||
important that the
|
||||
.Dq \&-
|
||||
signs are escaped, otherwise this parser will see them as constituting
|
||||
an expect-send-expect sequence. When the
|
||||
.Dq \&!
|
||||
character is seen, the execution parser reads the first command as three
|
||||
arguments, and then
|
||||
.Xr sh 1
|
||||
itself expands the argument after the
|
||||
.Fl c .
|
||||
As we wish to send the output back to the modem, in the first example
|
||||
we redirect our output to file descriptor 2 (stderr) so that
|
||||
.Nm
|
||||
itself sends and logs it, and in the second example, we just output to stdout,
|
||||
which is attached directly to the modem.
|
||||
.Pp
|
||||
This, of course means that it is possible to execute an entirely external
|
||||
.Dq chat
|
||||
command rather than using the internal one. See
|
||||
.Xr chat 8
|
||||
for a good alternative.
|
||||
.Pp
|
||||
.It set hangup chat-script
|
||||
This specifies the chat script that will be used to reset the modem
|
||||
before it is closed. It should not normally be necessary, but can
|
||||
|
Loading…
x
Reference in New Issue
Block a user