Debugging can now be enabled at runtime.
---
example-sshpass.c | 46 +++++++++++++++++++++++++-------
miniexpect.c | 78 ++++++++++++++++++++++++++++++++++---------------------
miniexpect.h | 6 +++++
miniexpect.pod | 21 +++++++++++++++
4 files changed, 113 insertions(+), 38 deletions(-)
diff --git a/example-sshpass.c b/example-sshpass.c
index 3449a20..11df02e 100644
--- a/example-sshpass.c
+++ b/example-sshpass.c
@@ -24,8 +24,10 @@
* The remaining arguments are passed to the ssh subprocess.
*
* For example:
- * sshpass 123456 ssh
remote.example.com
- * sshpass 123456 ssh -l root
remote.example.com
+ * sshpass [-d] 123456 ssh
remote.example.com
+ * sshpass [-d] 123456 ssh -l root
remote.example.com
+ *
+ * Use the -d flag to enable debugging to stderr.
*/
#include <config.h>
@@ -42,9 +44,18 @@
static pcre *compile_re (const char *rex);
+static void
+usage (void)
+{
+ fprintf (stderr, "usage: sshpass PASSWORD ssh [SSH-ARGS...] HOST\n");
+ exit (EXIT_FAILURE);
+}
+
int
main (int argc, char *argv[])
{
+ int opt;
+ int debug = 0;
mexp_h *h;
const char *password;
int status;
@@ -52,20 +63,31 @@ main (int argc, char *argv[])
const int ovecsize = 12;
int ovector[ovecsize];
- if (argc <= 3) {
- fprintf (stderr, "usage: sshpass PASSWORD ssh [SSH-ARGS...] HOST\n");
- exit (EXIT_FAILURE);
+ while ((opt = getopt (argc, argv, "d")) != -1) {
+ switch (opt) {
+ case 'd':
+ debug = 1;
+ break;
+ default:
+ usage ();
+ }
}
- password = argv[1];
+ if (argc-optind <= 2)
+ usage ();
+
+ password = argv[optind];
+ optind++;
printf ("starting ssh command ...\n");
- h = mexp_spawnv (argv[2], &argv[2]);
+ h = mexp_spawnv (argv[optind], &argv[optind]);
if (h == NULL) {
perror ("mexp_spawnv: ssh");
exit (EXIT_FAILURE);
}
+ if (debug)
+ mexp_set_debug_file (h, stderr);
/* Wait for the password prompt. */
password_re = compile_re ("assword");
@@ -91,10 +113,16 @@ main (int argc, char *argv[])
goto error;
}
- /* Got the password prompt, so send a password. */
+ /* Got the password prompt, so send a password.
+ *
+ * Note use of mexp_printf_password here which is identical to
+ * mexp_printf except that it hides the password in debugging
+ * output.
+ */
printf ("sending the password ...\n");
- if (mexp_printf (h, "%s\n", password) == -1) {
+ if (mexp_printf_password (h, "%s", password) == -1 ||
+ mexp_printf (h, "\n") == -1) {
perror ("mexp_printf");
goto error;
}
diff --git a/miniexpect.c b/miniexpect.c
index d2ceb69..1ac373b 100644
--- a/miniexpect.c
+++ b/miniexpect.c
@@ -46,11 +46,7 @@
#include "miniexpect.h"
-#define DEBUG 0
-
-#if DEBUG
static void debug_buffer (FILE *, char *);
-#endif
static mexp_h *
create_handle (void)
@@ -68,6 +64,7 @@ create_handle (void)
h->buffer = NULL;
h->len = h->alloc = 0;
h->next_match = -1;
+ h->debug_fp = NULL;
h->user1 = h->user2 = h->user3 = NULL;
return h;
@@ -296,9 +293,8 @@ mexp_expect (mexp_h *h, const mexp_regexp *regexps, int *ovector, int
ovecsize)
pfds[0].events = POLLIN;
pfds[0].revents = 0;
r = poll (pfds, 1, timeout);
-#if DEBUG
- fprintf (stderr, "DEBUG: poll returned %d\n", r);
-#endif
+ if (h->debug_fp)
+ fprintf (h->debug_fp, "DEBUG: poll returned %d\n", r);
if (r == -1)
return MEXP_ERROR;
@@ -318,9 +314,8 @@ mexp_expect (mexp_h *h, const mexp_regexp *regexps, int *ovector, int
ovecsize)
h->alloc += h->read_size;
}
rs = read (h->fd, h->buffer + h->len, h->read_size);
-#if DEBUG
- fprintf (stderr, "DEBUG: read returned %zd\n", rs);
-#endif
+ if (h->debug_fp)
+ fprintf (h->debug_fp, "DEBUG: read returned %zd\n", rs);
if (rs == -1) {
/* Annoyingly on Linux (I'm fairly sure this is a bug) if the
* writer closes the connection, the entire pty is destroyed,
@@ -336,12 +331,12 @@ mexp_expect (mexp_h *h, const mexp_regexp *regexps, int *ovector,
int ovecsize)
/* We read something. */
h->len += rs;
h->buffer[h->len] = '\0';
-#if DEBUG
- fprintf (stderr, "DEBUG: read %zd bytes from pty\n", rs);
- fprintf (stderr, "DEBUG: buffer content: ");
- debug_buffer (stderr, h->buffer);
- fprintf (stderr, "\n");
-#endif
+ if (h->debug_fp) {
+ fprintf (h->debug_fp, "DEBUG: read %zd bytes from pty\n", rs);
+ fprintf (h->debug_fp, "DEBUG: buffer content: ");
+ debug_buffer (h->debug_fp, h->buffer);
+ fprintf (h->debug_fp, "\n");
+ }
try_match:
/* See if there is a full or partial match against any regexp. */
@@ -395,28 +390,32 @@ mexp_expect (mexp_h *h, const mexp_regexp *regexps, int *ovector,
int ovecsize)
}
}
-int
-mexp_printf (mexp_h *h, const char *fs, ...)
+static int mexp_vprintf (mexp_h *h, int password, const char *fs, va_list args)
+ __attribute__((format(printf,3,0)));
+
+static int
+mexp_vprintf (mexp_h *h, int password, const char *fs, va_list args)
{
- va_list args;
char *msg;
int len;
size_t n;
ssize_t r;
char *p;
- va_start (args, fs);
len = vasprintf (&msg, fs, args);
- va_end (args);
if (len < 0)
return -1;
-#if DEBUG
- fprintf (stderr, "DEBUG: writing: ");
- debug_buffer (stderr, msg);
- fprintf (stderr, "\n");
-#endif
+ if (h->debug_fp) {
+ if (!password) {
+ fprintf (h->debug_fp, "DEBUG: writing: ");
+ debug_buffer (h->debug_fp, msg);
+ fprintf (h->debug_fp, "\n");
+ }
+ else
+ fprintf (h->debug_fp, "DEBUG: writing the password\n");
+ }
n = len;
p = msg;
@@ -435,12 +434,35 @@ mexp_printf (mexp_h *h, const char *fs, ...)
}
int
+mexp_printf (mexp_h *h, const char *fs, ...)
+{
+ int r;
+ va_list args;
+
+ va_start (args, fs);
+ r = mexp_vprintf (h, 0, fs, args);
+ va_end (args);
+ return r;
+}
+
+int
+mexp_printf_password (mexp_h *h, const char *fs, ...)
+{
+ int r;
+ va_list args;
+
+ va_start (args, fs);
+ r = mexp_vprintf (h, 1, fs, args);
+ va_end (args);
+ return r;
+}
+
+int
mexp_send_interrupt (mexp_h *h)
{
return write (h->fd, "\003", 1);
}
-#if DEBUG
/* Print escaped buffer to fp. */
static void
debug_buffer (FILE *fp, char *buf)
@@ -465,5 +487,3 @@ debug_buffer (FILE *fp, char *buf)
buf++;
}
}
-#endif
-
diff --git a/miniexpect.h b/miniexpect.h
index e4d6010..14d8236 100644
--- a/miniexpect.h
+++ b/miniexpect.h
@@ -29,6 +29,7 @@
#ifndef MINIEXPECT_H_
#define MINIEXPECT_H_
+#include <stdio.h>
#include <unistd.h>
#include <pcre.h>
@@ -44,6 +45,7 @@ struct mexp_h {
ssize_t next_match;
size_t read_size;
int pcre_error;
+ FILE *debug_fp;
void *user1;
void *user2;
void *user3;
@@ -62,6 +64,8 @@ typedef struct mexp_h mexp_h;
#define mexp_get_read_size(h) ((h)->read_size)
#define mexp_set_read_size(h, size) ((h)->read_size = (size))
#define mexp_get_pcre_error(h) ((h)->pcre_error)
+#define mexp_set_debug_file(h, fp) ((h)->debug_fp = (fp))
+#define mexp_get_debug_file(h) ((h)->debug_fp)
/* Spawn a subprocess. */
extern mexp_h *mexp_spawnvf (unsigned flags, const char *file, char **argv);
@@ -99,6 +103,8 @@ extern int mexp_expect (mexp_h *h, const mexp_regexp *regexps,
/* Sending commands, keypresses. */
extern int mexp_printf (mexp_h *h, const char *fs, ...)
__attribute__((format(printf,2,3)));
+extern int mexp_printf_password (mexp_h *h, const char *fs, ...)
+ __attribute__((format(printf,2,3)));
extern int mexp_send_interrupt (mexp_h *h);
#endif /* MINIEXPECT_H_ */
diff --git a/miniexpect.pod b/miniexpect.pod
index cd008fc..fe76979 100644
--- a/miniexpect.pod
+++ b/miniexpect.pod
@@ -177,6 +177,18 @@ returned by L<pcre_exec(3)>, and so you can call it like this:
=back
+B<void mexp_set_debug_file (mexp *h, FILE *fp);>
+
+B<FILE *mexp_get_debug_file (mexp *h);>
+
+Set or get the debug file of the handle. To enable debugging, pass a
+non-C<NULL> file handle, eg. C<stderr>. To disable debugging, pass
+C<NULL>. Debugging messages are printed on the file handle.
+
+Note that all output and input gets printed, including passwords. To
+prevent passwords from being printed, modify your code to call
+C<mexp_printf_password> instead of C<mexp_printf>.
+
The following fields in the handle do not have methods, but can be
accessed directly instead:
@@ -415,6 +427,8 @@ However we also provide a convenience function:
B<int mexp_printf (mexp_h *h, const char *fs, ...);>
+B<int mexp_printf_password (mexp_h *h, const char *fs, ...);>
+
This returns the number of bytes, if the whole message was written OK.
If there was an error, -1 is returned and the error is available in
C<errno>.
@@ -435,6 +449,13 @@ send a command followed by a newline you have to do something like:
mexp_printf (h, "exit\n");
+=item *
+
+C<mexp_printf_password> works identically to C<mexp_printf> except
+that the output is I<not> sent to the debugging file if debugging is
+enabled. As the name suggests, use this for passwords so that they
+don't appear in debugging output.
+
=back
B<int mexp_send_interrupt (mexp_h *h);>
--
2.13.2