#include "miniexpect.h"
-static pcre *
-compile_re (const char *rex)
-{
- const char *errptr;
- int erroffset;
- pcre *ret;
-
- ret = pcre_compile (rex, 0, &errptr, &erroffset, NULL);
- if (ret == NULL) {
- fprintf (stderr, "error: failed to compile regular expression '%s': %s at offset %d\n",
- rex, errptr, erroffset);
- exit (EXIT_FAILURE);
- }
- return ret;
-}
+static pcre *compile_re (const char *rex);
int
main (int argc, char *argv[])
pcre *password_re, *prompt_re, *hello_re;
const int ovecsize = 12;
int ovector[ovecsize];
- int pcre_err;
if (argc <= 3) {
fprintf (stderr, "usage: sshpass PASSWORD ssh [SSH-ARGS...] HOST\n");
printf ("starting ssh command ...\n");
- h = mexp_spawnv ("ssh", &argv[2]);
+ h = mexp_spawnv (argv[2], &argv[2]);
if (h == NULL) {
perror ("mexp_spawnv: ssh");
exit (EXIT_FAILURE);
/* Wait for the password prompt. */
password_re = compile_re ("assword");
- switch (mexp_expect (h, password_re, NULL, 0, ovector, ovecsize, &pcre_err)) {
+ switch (mexp_expect (h,
+ (mexp_regexp[]) {
+ { 100, .re = password_re },
+ { 0 }
+ },
+ ovector, ovecsize)) {
+ case 100:
+ break;
case MEXP_EOF:
fprintf (stderr, "error: ssh closed the connection unexpectedly\n");
goto error;
- case MEXP_ERROR:
- perror ("mexp_expect");
- goto error;
case MEXP_TIMEOUT:
fprintf (stderr, "error: timeout before reaching the password prompt\n");
goto error;
+ case MEXP_ERROR:
+ perror ("mexp_expect");
+ goto error;
case MEXP_PCRE_ERROR:
- fprintf (stderr, "error: PCRE error: %d\n", pcre_err);
+ fprintf (stderr, "error: PCRE error: %d\n", h->pcre_error);
goto error;
- case MEXP_MATCHED:
- break;
}
/* Got the password prompt, so send a password. */
* expect checks all these possibilities. Unfortunately since all
* prompts are a little bit different, we have to guess here.
*/
- prompt_re = compile_re ("(assword)|([#$])");
- switch (mexp_expect (h, prompt_re, NULL, 0, ovector, ovecsize, &pcre_err)) {
+ prompt_re = compile_re ("[#$]");
+ switch (mexp_expect (h,
+ (mexp_regexp[]) {
+ { 100, .re = password_re },
+ { 101, .re = prompt_re },
+ { 0 },
+ },
+ ovector, ovecsize)) {
+ case 100: /* Password. */
+ fprintf (stderr, "error: ssh asked for password again, probably the password supplied is wrong\n");
+ goto error;
+ case 101: /* Prompt. */
+ break;
case MEXP_EOF:
fprintf (stderr, "error: ssh closed the connection unexpectedly\n");
goto error;
- case MEXP_ERROR:
- perror ("mexp_expect");
- goto error;
case MEXP_TIMEOUT:
fprintf (stderr, "error: timeout before reaching the prompt\n");
goto error;
+ case MEXP_ERROR:
+ perror ("mexp_expect");
+ goto error;
case MEXP_PCRE_ERROR:
- fprintf (stderr, "error: PCRE error: %d\n", pcre_err);
- goto error;
- case MEXP_MATCHED:
- /* Which part of the regexp matched? */
- if (ovector[2] >= 0) { /* password */
- fprintf (stderr, "error: ssh asked for password again, probably the password supplied is wrong\n");
- goto error;
- }
- else if (ovector[4] >= 0) { /* prompt */
- break;
- }
- else
- abort (); /* shouldn't happen */
+ fprintf (stderr, "error: PCRE error: %d\n", h->pcre_error);
+ goto error;
}
/* Send a command which will have expected output. */
/* Wait for expected output from echo hello command. */
hello_re = compile_re ("hello");
- switch (mexp_expect (h, hello_re, NULL, 0, ovector, ovecsize, &pcre_err)) {
+ switch (mexp_expect (h,
+ (mexp_regexp[]) {
+ { 100, .re = hello_re },
+ { 0 },
+ },
+ ovector, ovecsize)) {
+ case 100:
+ break;
case MEXP_EOF:
fprintf (stderr, "error: ssh closed the connection unexpectedly\n");
goto error;
- case MEXP_ERROR:
- perror ("mexp_expect");
- goto error;
case MEXP_TIMEOUT:
fprintf (stderr, "error: timeout before reading command output\n");
goto error;
+ case MEXP_ERROR:
+ perror ("mexp_expect");
+ goto error;
case MEXP_PCRE_ERROR:
- fprintf (stderr, "error: PCRE error: %d\n", pcre_err);
+ fprintf (stderr, "error: PCRE error: %d\n", h->pcre_error);
goto error;
- case MEXP_MATCHED:
- break;
}
/* Send exit command and wait for ssh to exit. */
goto error;
}
- switch (mexp_expect (h, NULL, NULL, 0, NULL, 0, NULL)) {
+ switch (mexp_expect (h, NULL, NULL, 0)) {
case MEXP_EOF:
/* This is what we're expecting: ssh will close the connection. */
break;
- case MEXP_ERROR:
- perror ("mexp_expect");
- goto error;
case MEXP_TIMEOUT:
fprintf (stderr, "error: timeout before ssh closed the connection\n");
goto error;
+ case MEXP_ERROR:
+ perror ("mexp_expect");
+ goto error;
case MEXP_PCRE_ERROR:
- case MEXP_MATCHED:
fprintf (stderr, "error: unexpected return value from mexp_expect\n");
goto error;
}
mexp_close (h);
exit (EXIT_FAILURE);
}
+
+/* Helper function to compile a PCRE regexp. */
+static pcre *
+compile_re (const char *rex)
+{
+ const char *errptr;
+ int erroffset;
+ pcre *ret;
+
+ ret = pcre_compile (rex, 0, &errptr, &erroffset, NULL);
+ if (ret == NULL) {
+ fprintf (stderr, "error: failed to compile regular expression '%s': %s at offset %d\n",
+ rex, errptr, erroffset);
+ exit (EXIT_FAILURE);
+ }
+ return ret;
+}
h->pid = 0;
h->timeout = 60000;
h->read_size = 1024;
+ h->pcre_error = 0;
h->buffer = NULL;
h->len = h->alloc = 0;
h->user1 = h->user2 = h->user3 = NULL;
}
enum mexp_status
-mexp_expect (mexp_h *h, const pcre *code,
- const pcre_extra *extra,
- int options, int *ovector, int ovecsize,
- int *pcre_ret)
+mexp_expect (mexp_h *h, const mexp_regexp *regexps, int *ovector, int ovecsize)
{
time_t start_t, now_t;
int timeout;
time (&start_t);
- options |= PCRE_PARTIAL_SOFT;
-
/* Clear the read buffer. */
clear_buffer (h);
fprintf (stderr, "DEBUG: buffer content: %s\n", h->buffer);
#endif
- /* See if there is a full or partial match against the regular expression. */
- if (code) {
+ /* See if there is a full or partial match against any regexp. */
+ if (regexps) {
+ size_t i;
+ int can_clear_buffer = 1;
+
assert (h->buffer != NULL);
- r = pcre_exec (code, extra, h->buffer, (int)h->len, 0,
- options, ovector, ovecsize);
- if (pcre_ret)
- *pcre_ret = r;
-
- if (r >= 0) {
- /* A full match. */
- return MEXP_MATCHED;
- }
- else if (r == PCRE_ERROR_NOMATCH) {
- /* No match at all, so we can dump the input buffer. */
- clear_buffer (h);
+ for (i = 0; regexps[i].r > 0; ++i) {
+ int options = regexps[i].options | PCRE_PARTIAL_SOFT;
+
+ r = pcre_exec (regexps[i].re, regexps[i].extra,
+ h->buffer, (int)h->len, 0,
+ options,
+ ovector, ovecsize);
+ h->pcre_error = r;
+
+ if (r >= 0) {
+ /* A full match. */
+ return regexps[i].r;
+ }
+
+ else if (r == PCRE_ERROR_NOMATCH) {
+ /* No match at all. */
+ /* (nothing here) */
+ }
+
+ else if (r == PCRE_ERROR_PARTIAL) {
+ /* Partial match. Keep the buffer and keep reading. */
+ can_clear_buffer = 0;
+ }
+
+ else {
+ /* An actual PCRE error. */
+ return MEXP_PCRE_ERROR;
+ }
}
- else if (r == PCRE_ERROR_PARTIAL) {
- /* Partial match. Keep the buffer and keep reading. */
- /* (nothing here) */
- }
+ /* If none of the regular expressions matched (not partially)
+ * then we can clear the buffer. This is an optimization.
+ */
+ if (can_clear_buffer)
+ clear_buffer (h);
- else {
- /* An actual PCRE error. */
- return MEXP_PCRE_ERROR;
- }
- } /* if (code) */
+ } /* if (regexps) */
}
}
*/
size_t read_size;
+ /* If mexp_expect returns MEXP_PCRE_ERROR, then the actual PCRE
+ * error code is returned here. See pcre_exec(3) for details.
+ */
+ int pcre_error;
+
/* Opaque pointers for use of the caller. The library will not
* touch these.
*/
*/
extern int mexp_close (mexp_h *h);
+/* The list of regular expressions passed to mexp_expect. */
+struct mexp_regexp {
+ int r; /* The returned value from mexp_expect.
+ * Must be > 0.
+ */
+ const pcre *re; /* The compiled regular expression. */
+ const pcre_extra *extra; /* See pcre_exec. */
+ int options; /* See pcre_exec. */
+};
+typedef struct mexp_regexp mexp_regexp;
+
enum mexp_status {
- MEXP_EOF = 0,
- MEXP_ERROR = 1,
- MEXP_TIMEOUT = 2,
- MEXP_MATCHED = 3,
- MEXP_PCRE_ERROR = 4,
+ MEXP_EOF = -1,
+ MEXP_TIMEOUT = -2,
+ MEXP_ERROR = -3,
+ MEXP_PCRE_ERROR = -4,
};
/* Expect some output from the subprocess. Match the output against
- * the PCRE regular expression.
- *
- * 'code', 'extra', 'options', 'ovector' and 'ovecsize' are passed
- * through to the pcre_exec function. See pcreapi(3).
- *
- * If you want to match multiple strings, you have to combine them
- * into a single regexp, eg. "([Pp]assword)|([Ll]ogin)|([Ff]ailed)".
- * Then examine ovector[2], ovector[4], ovector[6] to see if they
- * contain '>= 0' or '-1'. See the pcreapi(3) man page for further
- * information.
- *
- * 'code' may be NULL, which means we don't match against a regular
- * expression. This is useful if you just want to wait for EOF or
- * timeout.
+ * the PCRE regular expression(s) in the list, and return which one
+ * matched.
*
* This can return:
*
- * MEXP_MATCHED:
- * The input matched the regular expression. Use ovector
- * to find out what matched in the buffer (mexp_h->buffer).
* MEXP_TIMEOUT:
* No input matched before the timeout (mexp_h->timeout) was reached.
* MEXP_EOF:
* MEXP_ERROR:
* There was a system call error (eg. from the read call). See errno.
* MEXP_PCRE_ERROR
- * There was a pcre_exec error. *pcre_ret is set to the error code
+ * There was a pcre_exec error. h->pcre_error is set to the error code
* (see pcreapi(3) for a list of PCRE_* error codes and what they mean).
+ *
+ * Notes:
+ *
+ * - 'regexps' may be NULL or an empty list, which means we don't
+ * match against a regular expression. This is useful if you just
+ * want to wait for EOF or timeout.
+ *
+ * - 'regexps[].re', 'regexps[].extra', 'regexps[].options', 'ovector'
+ * and 'ovecsize' are passed through to the pcre_exec function.
*/
-extern enum mexp_status mexp_expect (mexp_h *h, const pcre *code,
- const pcre_extra *extra,
- int options, int *ovector, int ovecsize,
- int *pcre_ret);
+extern int mexp_expect (mexp_h *h, const mexp_regexp *regexps,
+ int *ovector, int ovecsize);
/* This is a convenience function for writing something (eg. a
* password or command) to the subprocess. You could do this by