Makefile.am: add "-Wextra -Wshadow"
[miniexpect.git] / example-sshpass.c
index a35c1d4..7130ac8 100644 (file)
@@ -1,5 +1,5 @@
 /* miniexpect example.
- * Copyright (C) 2014 Red Hat Inc.
+ * Copyright (C) 2014-2022 Red Hat Inc.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
  * 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>
 #include <unistd.h>
 #include <assert.h>
 
-#include <pcre.h>
+#define PCRE2_CODE_UNIT_WIDTH 8
+#include <pcre2.h>
 
 #include "miniexpect.h"
 
-static pcre *
-compile_re (const char *rex)
-{
-  const char *errptr;
-  int erroffset;
-  pcre *ret;
+static pcre2_code *compile_re (const char *rex);
 
-  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 void
+usage (void)
+{
+  fprintf (stderr, "usage: sshpass [-d] 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;
-  pcre *password_re, *prompt_re, *hello_re;
-  const int ovecsize = 12;
-  int ovector[ovecsize];
-  int pcre_err;
+  pcre2_code *password_re, *prompt_re, *hello_re;
+  pcre2_match_data *match_data;
 
-  if (argc <= 3) {
-    fprintf (stderr, "usage: sshpass PASSWORD ssh [SSH-ARGS...] HOST\n");
-    exit (EXIT_FAILURE);
+  match_data = pcre2_match_data_create (4, NULL);
+
+  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 ("ssh", &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");
-  switch (mexp_expect (h, password_re, NULL, 0, ovector, ovecsize, &pcre_err)) {
+  switch (mexp_expect (h,
+                       (mexp_regexp[]) {
+                         { 100, .re = password_re },
+                         { 0 }
+                       },
+                       match_data)) {
+  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", mexp_get_pcre_error (h));
     goto error;
-  case MEXP_MATCHED:
-    break;
   }
 
-  /* 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;
   }
@@ -115,31 +135,31 @@ main (int argc, char *argv[])
    * 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 },
+                       },
+                       match_data)) {
+  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);
+    fprintf (stderr, "error: PCRE error: %d\n", mexp_get_pcre_error (h));
     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 */
   }
 
   /* Send a command which will have expected output. */
@@ -152,21 +172,26 @@ main (int argc, char *argv[])
 
   /* 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 },
+                       },
+                       match_data)) {
+  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", mexp_get_pcre_error (h));
     goto error;
-  case MEXP_MATCHED:
-    break;
   }
 
   /* Send exit command and wait for ssh to exit. */
@@ -177,18 +202,17 @@ main (int argc, char *argv[])
     goto error;
   }
 
-  switch (mexp_expect (h, NULL, NULL, 0, NULL, 0, NULL)) {
+  switch (mexp_expect (h, NULL, NULL)) {
   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;
   }
@@ -203,9 +227,33 @@ main (int argc, char *argv[])
 
   printf ("test was successful\n");
 
+  pcre2_match_data_free (match_data);
   exit (EXIT_SUCCESS);
 
  error:
   mexp_close (h);
   exit (EXIT_FAILURE);
 }
+
+/* Helper function to compile a PCRE regexp. */
+static pcre2_code *
+compile_re (const char *rex)
+{
+  int errorcode;
+  PCRE2_SIZE erroroffset;
+  char errormsg[256];
+  pcre2_code *ret;
+
+  ret = pcre2_compile ((PCRE2_SPTR) rex, PCRE2_ZERO_TERMINATED,
+                       0, &errorcode, &erroroffset, NULL);
+  if (ret == NULL) {
+    pcre2_get_error_message (errorcode,
+                             (PCRE2_UCHAR *) errormsg, sizeof errormsg);
+    fprintf (stderr, "error: "
+             "failed to compile regular expression '%s': "
+             "%s at offset %zu\n",
+             rex, errormsg, erroroffset);
+    exit (EXIT_FAILURE);
+  }
+  return ret;
+}