fish: Add -c/--connect and -d/--domain options.
[libguestfs.git] / fish / fish.c
index 557a6ac..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;
@@ -304,14 +335,18 @@ main (int argc, char *argv[])
       file = optarg;
       break;
 
-    case 'h':
+    case 'h': {
+      int r = 0;
+
       if (optarg)
-        display_command (optarg);
+        r = display_command (optarg);
       else if (argv[optind] && argv[optind][0] != '-')
-        display_command (argv[optind++]);
+        r = display_command (argv[optind++]);
       else
         list_commands ();
-      exit (EXIT_SUCCESS);
+
+      exit (r == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
+    }
 
     case 'i':
       inspector = 1;
@@ -466,12 +501,23 @@ 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.
    */
   if (next_prepared_drive > 1 || mps != NULL) {
+    /* RHBZ#612178: If --listen flag is given, then we will fork into
+     * the background in rc_listen().  However you can't do this while
+     * holding a libguestfs handle open because the recovery process
+     * will think the main program has died and kill qemu.  Therefore
+     * don't use the recovery process for this case.  (A better
+     * solution would be to call launch () etc after the fork, but
+     * that greatly complicates the code here).
+     */
+    if (remote_control_listen)
+      guestfs_set_recovery_proc (g, 0);
+
     if (launch () == -1) exit (EXIT_FAILURE);
     prepare_drives (drvs);
     mount_mps (mps);
@@ -563,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
@@ -585,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);
   }
 }
 
@@ -957,11 +1042,11 @@ issue_command (const char *cmd, char *argv[], const char *pipecmd)
 
   /* Otherwise execute it locally. */
   else if (STRCASEEQ (cmd, "help")) {
-    if (argc == 0)
+    if (argc == 0) {
       list_commands ();
-    else
-      display_command (argv[0]);
-    r = 0;
+      r = 0;
+    } else
+      r = display_command (argv[0]);
   }
   else if (STRCASEEQ (cmd, "quit") ||
            STRCASEEQ (cmd, "exit") ||
@@ -1058,13 +1143,13 @@ list_builtin_commands (void)
   /* actions are printed after this (see list_commands) */
 }
 
-void
+int
 display_builtin_command (const char *cmd)
 {
   /* help for actions is auto-generated, see display_command */
 
   if (STRCASEEQ (cmd, "alloc") ||
-      STRCASEEQ (cmd, "allocate"))
+      STRCASEEQ (cmd, "allocate")) {
     printf (_("alloc - allocate an image\n"
               "     alloc <filename> <size>\n"
               "\n"
@@ -1075,14 +1160,18 @@ display_builtin_command (const char *cmd)
               "\n"
               "    Size can be specified using standard suffixes, eg. '1M'.\n"
               ));
-  else if (STRCASEEQ (cmd, "echo"))
+    return 0;
+  }
+  else if (STRCASEEQ (cmd, "echo")) {
     printf (_("echo - display a line of text\n"
               "     echo [<params> ...]\n"
               "\n"
               "    This echos the parameters to the terminal.\n"));
+    return 0;
+  }
   else if (STRCASEEQ (cmd, "edit") ||
            STRCASEEQ (cmd, "vi") ||
-           STRCASEEQ (cmd, "emacs"))
+           STRCASEEQ (cmd, "emacs")) {
     printf (_("edit - edit a file in the image\n"
               "     edit <filename>\n"
               "\n"
@@ -1096,32 +1185,42 @@ display_builtin_command (const char *cmd)
               "\n"
               "    NOTE: This will not work reliably for large files\n"
               "    (> 2 MB) or binary files containing \\0 bytes.\n"));
-  else if (STRCASEEQ (cmd, "lcd"))
+    return 0;
+  }
+  else if (STRCASEEQ (cmd, "lcd")) {
     printf (_("lcd - local change directory\n"
               "    lcd <directory>\n"
               "\n"
               "    Change guestfish's current directory. This command is\n"
               "    useful if you want to download files to a particular\n"
               "    place.\n"));
-  else if (STRCASEEQ (cmd, "glob"))
+    return 0;
+  }
+  else if (STRCASEEQ (cmd, "glob")) {
     printf (_("glob - expand wildcards in command\n"
               "    glob <command> [<args> ...]\n"
               "\n"
               "    Glob runs <command> with wildcards expanded in any\n"
               "    command args.  Note that the command is run repeatedly\n"
               "    once for each expanded argument.\n"));
+    return 0;
+  }
   else if (STRCASEEQ (cmd, "man") ||
-           STRCASEEQ (cmd, "manual"))
+           STRCASEEQ (cmd, "manual")) {
     printf (_("man - read the manual\n"
               "    man\n"
               "\n"
               "    Opens the manual page for guestfish.\n"));
-  else if (STRCASEEQ (cmd, "help"))
+    return 0;
+  }
+  else if (STRCASEEQ (cmd, "help")) {
     printf (_("help - display a list of commands or help on a command\n"
               "     help cmd\n"
               "     help\n"));
+    return 0;
+  }
   else if (STRCASEEQ (cmd, "more") ||
-           STRCASEEQ (cmd, "less"))
+           STRCASEEQ (cmd, "less")) {
     printf (_("more - view a file in the pager\n"
               "     more <filename>\n"
               "\n"
@@ -1135,19 +1234,25 @@ display_builtin_command (const char *cmd)
               "\n"
               "    NOTE: This will not work reliably for large files\n"
               "    (> 2 MB) or binary files containing \\0 bytes.\n"));
+    return 0;
+  }
   else if (STRCASEEQ (cmd, "quit") ||
            STRCASEEQ (cmd, "exit") ||
-           STRCASEEQ (cmd, "q"))
+           STRCASEEQ (cmd, "q")) {
     printf (_("quit - quit guestfish\n"
               "     quit\n"));
-  else if (STRCASEEQ (cmd, "reopen"))
+    return 0;
+  }
+  else if (STRCASEEQ (cmd, "reopen")) {
     printf (_("reopen - close and reopen the libguestfs handle\n"
               "     reopen\n"
               "\n"
               "Close and reopen the libguestfs handle.  It is not necessary to use\n"
               "this normally, because the handle is closed properly when guestfish\n"
               "exits.  However this is occasionally useful for testing.\n"));
-  else if (STRCASEEQ (cmd, "sparse"))
+    return 0;
+  }
+  else if (STRCASEEQ (cmd, "sparse")) {
     printf (_("sparse - allocate a sparse image file\n"
               "     sparse <filename> <size>\n"
               "\n"
@@ -1166,7 +1271,9 @@ display_builtin_command (const char *cmd)
               "\n"
               "    Size can be specified using standard suffixes, eg. '1M'.\n"
               ));
-  else if (STRCASEEQ (cmd, "supported"))
+    return 0;
+  }
+  else if (STRCASEEQ (cmd, "supported")) {
     printf (_("supported - list supported groups of commands\n"
               "     supported\n"
               "\n"
@@ -1176,15 +1283,21 @@ display_builtin_command (const char *cmd)
               "\n"
               "    See also guestfs(3) section AVAILABILITY.\n"
               ));
-  else if (STRCASEEQ (cmd, "time"))
+    return 0;
+  }
+  else if (STRCASEEQ (cmd, "time")) {
     printf (_("time - measure time taken to run command\n"
               "    time <command> [<args> ...]\n"
               "\n"
               "    This runs <command> as usual, and prints the elapsed\n"
               "    time afterwards.\n"));
-  else
+    return 0;
+  }
+  else {
     fprintf (stderr, _("%s: command not known, use -h to list all commands\n"),
              cmd);
+    return -1;
+  }
 }
 
 /* This is printed when the user types in an unknown command for the
@@ -1667,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)
 {