diff options
author | brian <brian@cvs.openbsd.org> | 1997-12-27 07:22:18 +0000 |
---|---|---|
committer | brian <brian@cvs.openbsd.org> | 1997-12-27 07:22:18 +0000 |
commit | a5e5f14966a9131af8a9ac18945ee9cb33d7deea (patch) | |
tree | 9ba21043c04adaa68280ad5b2e4bf99384d438e2 /usr.sbin | |
parent | ab0c2c5bb2c3745bd5ae5141ed818d8b834d3366 (diff) |
Allow (and document) execution of commands from within
our chat script.
You can now even run chat(8) - see ppp.conf.sample.
Diffstat (limited to 'usr.sbin')
-rw-r--r-- | usr.sbin/ppp/chat.c | 209 | ||||
-rw-r--r-- | usr.sbin/ppp/command.c | 28 | ||||
-rw-r--r-- | usr.sbin/ppp/ppp.8 | 73 |
3 files changed, 209 insertions, 101 deletions
diff --git a/usr.sbin/ppp/chat.c b/usr.sbin/ppp/chat.c index aa202db904b..4c3085480ee 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.4 1997/12/24 09:30:26 brian Exp $ + * $Id: chat.c,v 1.5 1997/12/27 07:22:16 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 53a5d0226e9..c18822bf169 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.13 1997/12/24 09:30:27 brian Exp $ + * $Id: command.c,v 1.14 1997/12/27 07:22:16 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 1f4c473052f..af5f19c2777 100644 --- a/usr.sbin/ppp/ppp.8 +++ b/usr.sbin/ppp/ppp.8 @@ -1,4 +1,4 @@ -.\" $Id: ppp.8,v 1.10 1997/12/21 03:15:55 brian Exp $ +.\" $Id: ppp.8,v 1.11 1997/12/27 07:22:17 brian Exp $ .Dd 20 September 1995 .Os OpenBSD .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 |