inspect: Refuse to parse /etc/fstab if it is huge.
[libguestfs.git] / fish / fish.c
index 4379845..5d7aac6 100644 (file)
@@ -30,7 +30,6 @@
 #include <sys/wait.h>
 #include <locale.h>
 #include <langinfo.h>
-#include <termios.h>
 
 #ifdef HAVE_LIBREADLINE
 #include <readline/readline.h>
 #include <guestfs.h>
 
 #include "fish.h"
+#include "options.h"
+
 #include "c-ctype.h"
 #include "closeout.h"
 #include "progname.h"
 
-/* List of drives added via -a, -d or -N options. */
-struct drv {
-  struct drv *next;
-  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 {
-  struct mp *next;
-  char *device;
-  char *mountpoint;
-};
-
 static void set_up_terminal (void);
-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 void free_drives (struct drv *drv);
-static void free_mps (struct mp *mp);
 static int launch (void);
 static void interactive (void);
 static void shell_script (void);
@@ -95,6 +67,7 @@ int read_only = 0;
 int quit = 0;
 int verbose = 0;
 int remote_control_listen = 0;
+int remote_control_csh = 0;
 int remote_control = 0;
 int exit_on_error = 1;
 int command_num = 0;
@@ -133,10 +106,12 @@ usage (int status)
              "  -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"
+             "  --csh                Make --listen csh-compatible\n"
              "  -d|--domain guest    Add disks from libvirt guest\n"
              "  -D|--no-dest-paths   Don't tab-complete paths from guest fs\n"
              "  --echo-keys          Don't turn off echo for passphrases\n"
              "  -f|--file file       Read commands from file\n"
+             "  --format[=raw|..]    Force disk format for -a option\n"
              "  -i|--inspector       Automatically mount filesystems\n"
              "  --keys-from-stdin    Read passphrases from stdin\n"
              "  --listen             Listen for remote commands\n"
@@ -149,8 +124,8 @@ usage (int status)
              "  -r|--ro              Mount read-only\n"
              "  --selinux            Enable SELinux support\n"
              "  -v|--verbose         Verbose messages\n"
-             "  -x                   Echo each command before executing it\n"
              "  -V|--version         Display version and exit\n"
+             "  -x                   Echo each command before executing it\n"
              "For more information, see the manpage %s(1).\n"),
              program_name, program_name, program_name,
              program_name, program_name, program_name,
@@ -175,14 +150,16 @@ main (int argc, char *argv[])
 
   enum { HELP_OPTION = CHAR_MAX + 1 };
 
-  static const char *options = "a:c:d:Df:h::im:nN:rv?Vx";
+  static const char *options = "a:c:d:Df:h::im:nN:rv?Vwx";
   static const struct option long_options[] = {
     { "add", 1, 0, 'a' },
     { "cmd-help", 2, 0, 'h' },
     { "connect", 1, 0, 'c' },
+    { "csh", 0, 0, 0 },
     { "domain", 1, 0, 'd' },
     { "echo-keys", 0, 0, 0 },
     { "file", 1, 0, 'f' },
+    { "format", 2, 0, 0 },
     { "help", 0, 0, HELP_OPTION },
     { "inspector", 0, 0, 'i' },
     { "keys-from-stdin", 0, 0, 0 },
@@ -195,6 +172,7 @@ main (int argc, char *argv[])
     { "no-progress-bars", 0, 0, 0 },
     { "remote", 2, 0, 0 },
     { "ro", 0, 0, 'r' },
+    { "rw", 0, 0, 'w' },
     { "selinux", 0, 0, 0 },
     { "verbose", 0, 0, 'v' },
     { "version", 0, 0, 'V' },
@@ -205,6 +183,7 @@ main (int argc, char *argv[])
   struct mp *mps = NULL;
   struct mp *mp;
   char *p, *file = NULL;
+  const char *format = NULL;
   int c;
   int option_index;
   struct sigaction sa;
@@ -226,8 +205,6 @@ main (int argc, char *argv[])
     exit (EXIT_FAILURE);
   }
 
-  guestfs_set_autosync (g, 1);
-
   /* If developing, add ./appliance to the path.  Note that libtools
    * interferes with this because uninstalled guestfish is a shell
    * script that runs the real program with an absolute path.  Detect
@@ -284,6 +261,13 @@ main (int argc, char *argv[])
         override_progress_bars = 0;
       } else if (STREQ (long_options[option_index].name, "echo-keys")) {
         echo_keys = 1;
+      } else if (STREQ (long_options[option_index].name, "format")) {
+        if (!optarg || STREQ (optarg, ""))
+          format = NULL;
+        else
+          format = optarg;
+      } else if (STREQ (long_options[option_index].name, "csh")) {
+        remote_control_csh = 1;
       } else {
         fprintf (stderr, _("%s: unknown long option: %s (%d)\n"),
                  program_name, long_options[option_index].name, option_index);
@@ -292,60 +276,15 @@ main (int argc, char *argv[])
       break;
 
     case 'a':
-      if (access (optarg, R_OK) != 0) {
-        perror (optarg);
-        exit (EXIT_FAILURE);
-      }
-      drv = malloc (sizeof (struct drv));
-      if (!drv) {
-        perror ("malloc");
-        exit (EXIT_FAILURE);
-      }
-      drv->type = drv_a;
-      drv->a.filename = optarg;
-      drv->next = drvs;
-      drvs = drv;
+      OPTION_a;
       break;
 
     case 'c':
-      libvirt_uri = optarg;
+      OPTION_c;
       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;
-      break;
-
-    case 'N':
-      if (STRCASEEQ (optarg, "list") ||
-          STRCASEEQ (optarg, "help") ||
-          STRCASEEQ (optarg, "h") ||
-          STRCASEEQ (optarg, "?")) {
-        list_prepared_drives ();
-        exit (EXIT_SUCCESS);
-      }
-      drv = malloc (sizeof (struct drv));
-      if (!drv) {
-        perror ("malloc");
-        exit (EXIT_FAILURE);
-      }
-      drv->type = drv_N;
-      if (asprintf (&drv->N.filename, "test%d.img",
-                    next_prepared_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;
+      OPTION_d;
       break;
 
     case 'D':
@@ -375,48 +314,61 @@ main (int argc, char *argv[])
     }
 
     case 'i':
-      inspector = 1;
+      OPTION_i;
       break;
 
     case 'm':
-      mp = malloc (sizeof (struct mp));
-      if (!mp) {
-        perror ("malloc");
-        exit (EXIT_FAILURE);
-      }
-      p = strchr (optarg, ':');
-      if (p) {
-        *p = '\0';
-        mp->mountpoint = p+1;
-      } else
-        mp->mountpoint = bad_cast ("/");
-      mp->device = optarg;
-      mp->next = mps;
-      mps = mp;
+      OPTION_m;
       break;
 
     case 'n':
-      guestfs_set_autosync (g, 0);
+      OPTION_n;
+      break;
+
+    case 'N':
+      if (STRCASEEQ (optarg, "list") ||
+          STRCASEEQ (optarg, "help") ||
+          STRCASEEQ (optarg, "h") ||
+          STRCASEEQ (optarg, "?")) {
+        list_prepared_drives ();
+        exit (EXIT_SUCCESS);
+      }
+      drv = malloc (sizeof (struct drv));
+      if (!drv) {
+        perror ("malloc");
+        exit (EXIT_FAILURE);
+      }
+      drv->type = drv_N;
+      if (asprintf (&drv->N.filename, "test%d.img",
+                    next_prepared_drive++) == -1) {
+        perror ("asprintf");
+        exit (EXIT_FAILURE);
+      }
+      drv->N.data = create_prepared_file (optarg, drv->N.filename);
+      drv->N.data_free = free_prep_data;
+      drv->N.device = NULL;     /* filled in by add_drives */
+      drv->next = drvs;
+      drvs = drv;
       break;
 
     case 'r':
-      read_only = 1;
+      OPTION_r;
       break;
 
     case 'v':
-      verbose++;
-      guestfs_set_verbose (g, verbose);
+      OPTION_v;
       break;
 
-    case 'V': {
-      struct guestfs_version *v = guestfs_version (g);
-      printf ("%s %"PRIi64".%"PRIi64".%"PRIi64"%s\n", program_name,
-              v->major, v->minor, v->release, v->extra);
-      exit (EXIT_SUCCESS);
-    }
+    case 'V':
+      OPTION_V;
+      break;
+
+    case 'w':
+      OPTION_w;
+      break;
 
     case 'x':
-      guestfs_set_trace (g, 1);
+      OPTION_x;
       break;
 
     case HELP_OPTION:
@@ -442,6 +394,7 @@ main (int argc, char *argv[])
         }
         drv->type = drv_a;
         drv->a.filename = argv[optind];
+        drv->a.format = NULL;
         drv->next = drvs;
         drvs = drv;
       } else {                  /* simulate -d option */
@@ -595,95 +548,6 @@ pod2text (const char *name, const char *shortdesc, const char *str)
   pclose (fp);
 }
 
-/* List is built in reverse order, so mount them in reverse order. */
-static void
-mount_mps (struct mp *mp)
-{
-  int r;
-
-  if (mp) {
-    mount_mps (mp->next);
-
-    /* Don't use guestfs_mount here because that will default to mount
-     * options -o sync,noatime.  For more information, see guestfs(3)
-     * section "LIBGUESTFS GOTCHAS".
-     */
-    const char *options = read_only ? "ro" : "";
-    r = guestfs_mount_options (g, options, mp->device, mp->mountpoint);
-    if (r == -1) {
-      /* Display possible mountpoints before exiting. */
-      char **fses = guestfs_list_filesystems (g);
-      if (fses == NULL || fses[0] == NULL)
-        goto out;
-      fprintf (stderr,
-               _("guestfish: '%s' could not be mounted.  Did you mean one of these?\n"),
-               mp->device);
-      size_t i;
-      for (i = 0; fses[i] != NULL; i += 2)
-        fprintf (stderr, "\t%s (%s)\n", fses[i], fses[i+1]);
-
-    out:
-      exit (EXIT_FAILURE);
-    }
-  }
-}
-
-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) {
-    next_drive = add_drives (drv->next, next_drive);
-
-    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
 prepare_drives (struct drv *drv)
 {
@@ -694,38 +558,6 @@ prepare_drives (struct drv *drv)
   }
 }
 
-static void
-free_drives (struct drv *drv)
-{
-  if (!drv) return;
-  free_drives (drv->next);
-
-  switch (drv->type) {
-  case drv_a: /* a.filename is optarg, don't free it */ break;
-  case drv_d: /* d.filename is optarg, don't free it */ break;
-  case drv_N:
-    free (drv->N.filename);
-    free (drv->N.device);
-    free_prep_data (drv->N.data);
-    break;
-  default: ;                    /* keep GCC happy */
-  }
-  free (drv);
-}
-
-static void
-free_mps (struct mp *mp)
-{
-  if (!mp) return;
-  free_mps (mp->next);
-
-  /* The drive and mountpoint fields are not allocated
-   * from the heap, so we should not free them here.
-   */
-
-  free (mp);
-}
-
 static int
 launch (void)
 {
@@ -803,7 +635,7 @@ script (int prompt)
               "Welcome to guestfish, the libguestfs filesystem interactive shell for\n"
               "editing virtual machine filesystems.\n"
               "\n"
-              "Type: 'help' for a list of commands\n"
+              "Type: 'help' for help on commands\n"
               "      'man' to read the manual\n"
               "      'quit' to quit the shell\n"
               "\n"));
@@ -1102,7 +934,7 @@ issue_command (const char *cmd, char *argv[], const char *pipecmd)
   /* Otherwise execute it locally. */
   else if (STRCASEEQ (cmd, "help")) {
     if (argc == 0) {
-      list_commands ();
+      display_help ();
       r = 0;
     } else
       r = display_command (argv[0]);
@@ -1561,7 +1393,7 @@ file_in (const char *arg)
 static char *
 file_in_heredoc (const char *endmarker)
 {
-  static const char template[] = "/tmp/heredocXXXXXX";
+  TMP_TEMPLATE_ON_STACK (template);
   file_in_tmpfile = strdup (template);
   if (file_in_tmpfile == NULL) {
     perror ("strdup");
@@ -1657,66 +1489,3 @@ 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 (!echo_keys) {
-      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;
-}