aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrian Somers <brian@FreeBSD.org>1997-12-27 07:22:12 +0000
committerBrian Somers <brian@FreeBSD.org>1997-12-27 07:22:12 +0000
commit313572f3e3691aea94eceea0ea10a27167c14796 (patch)
tree2482dc16f5188a314f23ad5beec3850d948629ba
parent6293b3997d029bcbf85ff14c6a97257b52ae1b51 (diff)
downloadsrc-313572f3e3691aea94eceea0ea10a27167c14796.tar.gz
src-313572f3e3691aea94eceea0ea10a27167c14796.zip
Allow (and document) execution of commands from within
our chat script. You can now even run chat(8) - see ppp.conf.sample.
Notes
Notes: svn path=/head/; revision=32017
-rw-r--r--etc/ppp/ppp.conf.sample6
-rw-r--r--usr.sbin/ppp/chat.c209
-rw-r--r--usr.sbin/ppp/command.c28
-rw-r--r--usr.sbin/ppp/ppp.873
-rw-r--r--usr.sbin/ppp/ppp.8.m473
5 files changed, 286 insertions, 103 deletions
diff --git a/etc/ppp/ppp.conf.sample b/etc/ppp/ppp.conf.sample
index 379a85f4c7e2..acdf52e717aa 100644
--- a/etc/ppp/ppp.conf.sample
+++ b/etc/ppp/ppp.conf.sample
@@ -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:
#
diff --git a/usr.sbin/ppp/chat.c b/usr.sbin/ppp/chat.c
index b3e3e06d2890..96030729bb18 100644
--- a/usr.sbin/ppp/chat.c
+++ b/usr.sbin/ppp/chat.c
@@ -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;
@@ -413,74 +517,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)
{
char *cp;
@@ -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);
diff --git a/usr.sbin/ppp/command.c b/usr.sbin/ppp/command.c
index 43092473a45b..76c686b079e5 100644
--- a/usr.sbin/ppp/command.c
+++ b/usr.sbin/ppp/command.c
@@ -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);
}
diff --git a/usr.sbin/ppp/ppp.8 b/usr.sbin/ppp/ppp.8
index aeb4ec9536f5..967c2eae0a83 100644
--- a/usr.sbin/ppp/ppp.8
+++ b/usr.sbin/ppp/ppp.8
@@ -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
diff --git a/usr.sbin/ppp/ppp.8.m4 b/usr.sbin/ppp/ppp.8.m4
index aeb4ec9536f5..967c2eae0a83 100644
--- a/usr.sbin/ppp/ppp.8.m4
+++ b/usr.sbin/ppp/ppp.8.m4
@@ -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