fish: Add -c/--connect and -d/--domain options.
[libguestfs.git] / fish / fish.c
index 4276ae1..bc7d96c 100644 (file)
@@ -29,6 +29,7 @@
 #include <sys/types.h>
 #include <sys/wait.h>
 #include <locale.h>
+#include <termios.h>
 
 #ifdef HAVE_LIBREADLINE
 #include <readline/readline.h>
 #include "closeout.h"
 #include "progname.h"
 
+/* List of drives added via -a, -d or -N options. */
 struct drv {
   struct drv *next;
-  char *filename;               /* disk filename (for -a or -N options) */
-  prep_data *data;              /* prepared type (for -N option only) */
-  char *device;                 /* device inside the appliance */
+  enum { drv_a, drv_d, drv_N } type;
+  union {
+    struct {
+      char *filename;       /* disk filename */
+    } a;
+    struct {
+      char *guest;          /* guest name */
+    } d;
+    struct {
+      char *filename;       /* disk filename (testX.img) */
+      prep_data *data;      /* prepared type */
+      char *device;         /* device inside the appliance */
+    } N;
+  };
 };
 
 struct mp {
@@ -55,7 +68,7 @@ struct mp {
   char *mountpoint;
 };
 
-static void add_drives (struct drv *drv);
+static char add_drives (struct drv *drv, char next_drive);
 static void prepare_drives (struct drv *drv);
 static void mount_mps (struct mp *mp);
 static int launch (void);
@@ -80,6 +93,8 @@ int remote_control_listen = 0;
 int remote_control = 0;
 int exit_on_error = 1;
 int command_num = 0;
+int keys_from_stdin = 0;
+const char *libvirt_uri = NULL;
 
 static void __attribute__((noreturn))
 usage (int status)
@@ -107,9 +122,12 @@ usage (int status)
              "  -h|--cmd-help        List available commands\n"
              "  -h|--cmd-help cmd    Display detailed help on 'cmd'\n"
              "  -a|--add image       Add image\n"
+             "  -c|--connect uri     Specify libvirt URI for -d option\n"
+             "  -d|--domain guest    Add disks from libvirt guest\n"
              "  -D|--no-dest-paths   Don't tab-complete paths from guest fs\n"
              "  -f|--file file       Read commands from file\n"
              "  -i|--inspector       Run virt-inspector to get disk mountpoints\n"
+             "  --keys-from-stdin    Read passphrases from stdin\n"
              "  --listen             Listen for remote commands\n"
              "  -m|--mount dev[:mnt] Mount dev on mnt (if omitted, /)\n"
              "  -n|--no-sync         Don't autosync\n"
@@ -142,13 +160,16 @@ main (int argc, char *argv[])
 
   enum { HELP_OPTION = CHAR_MAX + 1 };
 
-  static const char *options = "a:Df:h::im:nN:rv?Vx";
+  static const char *options = "a:c:d:Df:h::im:nN:rv?Vx";
   static const struct option long_options[] = {
     { "add", 1, 0, 'a' },
     { "cmd-help", 2, 0, 'h' },
+    { "connect", 1, 0, 'c' },
+    { "domain", 1, 0, 'd' },
     { "file", 1, 0, 'f' },
     { "help", 0, 0, HELP_OPTION },
     { "inspector", 0, 0, 'i' },
+    { "keys-from-stdin", 0, 0, 0 },
     { "listen", 0, 0, 0 },
     { "mount", 1, 0, 'm' },
     { "new", 1, 0, 'N' },
@@ -170,7 +191,6 @@ main (int argc, char *argv[])
   int inspector = 0;
   int option_index;
   struct sigaction sa;
-  char next_drive = 'a';
   int next_prepared_drive = 1;
 
   initialize_readline ();
@@ -239,6 +259,8 @@ main (int argc, char *argv[])
         }
       } else if (STREQ (long_options[option_index].name, "selinux")) {
         guestfs_set_selinux (g, 1);
+      } else if (STREQ (long_options[option_index].name, "keys-from-stdin")) {
+        keys_from_stdin = 1;
       } else {
         fprintf (stderr, _("%s: unknown long option: %s (%d)\n"),
                  program_name, long_options[option_index].name, option_index);
@@ -256,15 +278,26 @@ main (int argc, char *argv[])
         perror ("malloc");
         exit (EXIT_FAILURE);
       }
-      drv->filename = optarg;
-      drv->data = NULL;
-      /* We could fill the device field in, but in fact we
-       * only use it for the -N option at present.
-       */
-      drv->device = NULL;
+      drv->type = drv_a;
+      drv->a.filename = optarg;
+      drv->next = drvs;
+      drvs = drv;
+      break;
+
+    case 'c':
+      libvirt_uri = optarg;
+      break;
+
+    case 'd':
+      drv = malloc (sizeof (struct drv));
+      if (!drv) {
+        perror ("malloc");
+        exit (EXIT_FAILURE);
+      }
+      drv->type = drv_d;
+      drv->d.guest = optarg;
       drv->next = drvs;
       drvs = drv;
-      next_drive++;
       break;
 
     case 'N':
@@ -277,16 +310,14 @@ main (int argc, char *argv[])
         perror ("malloc");
         exit (EXIT_FAILURE);
       }
-      if (asprintf (&drv->filename, "test%d.img",
+      drv->type = drv_N;
+      if (asprintf (&drv->N.filename, "test%d.img",
                     next_prepared_drive++) == -1) {
         perror ("asprintf");
         exit (EXIT_FAILURE);
       }
-      drv->data = create_prepared_file (optarg, drv->filename);
-      if (asprintf (&drv->device, "/dev/sd%c", next_drive++) == -1) {
-        perror ("asprintf");
-        exit (EXIT_FAILURE);
-      }
+      drv->N.data = create_prepared_file (optarg, drv->N.filename);
+      drv->N.device = NULL;     /* filled in by add_drives */
       drv->next = drvs;
       drvs = drv;
       break;
@@ -470,7 +501,7 @@ main (int argc, char *argv[])
   }
 
   /* If we've got drives to add, add them now. */
-  add_drives (drvs);
+  add_drives (drvs, 'a');
 
   /* If we've got mountpoints or prepared drives, we must launch the
    * guest and mount them.
@@ -578,21 +609,60 @@ mount_mps (struct mp *mp)
   }
 }
 
-static void
-add_drives (struct drv *drv)
+static char
+add_drives (struct drv *drv, char next_drive)
 {
   int r;
 
+  if (next_drive > 'z') {
+    fprintf (stderr,
+             _("guestfish: too many drives added on the command line\n"));
+    exit (EXIT_FAILURE);
+  }
+
   if (drv) {
-    add_drives (drv->next);
+    next_drive = add_drives (drv->next, next_drive);
 
-    if (drv->data /* -N option is not affected by --ro */ || !read_only)
-      r = guestfs_add_drive (g, drv->filename);
-    else
-      r = guestfs_add_drive_ro (g, drv->filename);
-    if (r == -1)
-      exit (EXIT_FAILURE);
+    switch (drv->type) {
+    case drv_a:
+      if (!read_only)
+        r = guestfs_add_drive (g, drv->a.filename);
+      else
+        r = guestfs_add_drive_ro (g, drv->a.filename);
+      if (r == -1)
+        exit (EXIT_FAILURE);
+
+      next_drive++;
+      break;
+
+    case drv_d:
+      r = add_libvirt_drives (drv->d.guest);
+      if (r == -1)
+        exit (EXIT_FAILURE);
+
+      next_drive += r;
+      break;
+
+    case drv_N:
+      /* -N option is not affected by --ro */
+      r = guestfs_add_drive (g, drv->N.filename);
+      if (r == -1)
+        exit (EXIT_FAILURE);
+
+      if (asprintf (&drv->N.device, "/dev/sd%c", next_drive) == -1) {
+        perror ("asprintf");
+        exit (EXIT_FAILURE);
+      }
+
+      next_drive++;
+      break;
+
+    default: /* keep GCC happy */
+      abort ();
+    }
   }
+
+  return next_drive;
 }
 
 static void
@@ -600,8 +670,8 @@ prepare_drives (struct drv *drv)
 {
   if (drv) {
     prepare_drives (drv->next);
-    if (drv->data)
-      prepare_drive (drv->filename, drv->data, drv->device);
+    if (drv->type == drv_N)
+      prepare_drive (drv->N.filename, drv->N.data, drv->N.device);
   }
 }
 
@@ -1710,6 +1780,67 @@ file_out (const char *arg)
   return ret;
 }
 
+/* Read a passphrase ('Key') from /dev/tty with echo off.
+ * The caller (cmds.c) will call free on the string afterwards.
+ * Based on the code in cryptsetup file lib/utils.c.
+ */
+char *
+read_key (const char *param)
+{
+  FILE *infp, *outfp;
+  struct termios orig, temp;
+  char *ret = NULL;
+
+  /* Read and write to /dev/tty if available. */
+  if (keys_from_stdin ||
+      (infp = outfp = fopen ("/dev/tty", "w+")) == NULL) {
+    infp = stdin;
+    outfp = stdout;
+  }
+
+  /* Print the prompt and set no echo. */
+  int tty = isatty (fileno (infp));
+  int tcset = 0;
+  if (tty) {
+    fprintf (outfp, _("Enter key or passphrase (\"%s\"): "), param);
+
+    if (tcgetattr (fileno (infp), &orig) == -1) {
+      perror ("tcgetattr");
+      goto error;
+    }
+    memcpy (&temp, &orig, sizeof temp);
+    temp.c_lflag &= ~ECHO;
+
+    tcsetattr (fileno (infp), TCSAFLUSH, &temp);
+    tcset = 1;
+  }
+
+  size_t n = 0;
+  ssize_t len;
+  len = getline (&ret, &n, infp);
+  if (len == -1) {
+    perror ("getline");
+    ret = NULL;
+    goto error;
+  }
+
+  /* Remove the terminating \n if there is one. */
+  if (len > 0 && ret[len-1] == '\n')
+    ret[len-1] = '\0';
+
+ error:
+  /* Restore echo, close file descriptor. */
+  if (tty && tcset) {
+    printf ("\n");
+    tcsetattr (fileno (infp), TCSAFLUSH, &orig);
+  }
+
+  if (infp != stdin)
+    fclose (infp); /* outfp == infp, so this is closed also */
+
+  return ret;
+}
+
 static void
 print_shell_quote (FILE *stream, const char *str)
 {