fish: Add --listen --csh to for csh, tcsh compatibility.
[libguestfs.git] / fish / fish.c
index 9ca57be..9f20bba 100644 (file)
@@ -30,7 +30,6 @@
 #include <sys/wait.h>
 #include <locale.h>
 #include <langinfo.h>
 #include <sys/wait.h>
 #include <locale.h>
 #include <langinfo.h>
-#include <termios.h>
 
 #ifdef HAVE_LIBREADLINE
 #include <readline/readline.h>
 
 #ifdef HAVE_LIBREADLINE
 #include <readline/readline.h>
 #include <guestfs.h>
 
 #include "fish.h"
 #include <guestfs.h>
 
 #include "fish.h"
+#include "options.h"
+
 #include "c-ctype.h"
 #include "closeout.h"
 #include "progname.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 void set_up_terminal (void);
-static char add_drives (struct drv *drv, char next_drive);
 static void prepare_drives (struct drv *drv);
 static void prepare_drives (struct drv *drv);
-static void mount_mps (struct mp *mp);
 static int launch (void);
 static void interactive (void);
 static void shell_script (void);
 static int launch (void);
 static void interactive (void);
 static void shell_script (void);
@@ -93,6 +67,7 @@ int read_only = 0;
 int quit = 0;
 int verbose = 0;
 int remote_control_listen = 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;
 int remote_control = 0;
 int exit_on_error = 1;
 int command_num = 0;
@@ -131,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"
              "  -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"
              "  -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"
              "  -i|--inspector       Automatically mount filesystems\n"
              "  --keys-from-stdin    Read passphrases from stdin\n"
              "  --listen             Listen for remote commands\n"
@@ -147,8 +124,8 @@ usage (int status)
              "  -r|--ro              Mount read-only\n"
              "  --selinux            Enable SELinux support\n"
              "  -v|--verbose         Verbose messages\n"
              "  -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"
              "  -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,
              "For more information, see the manpage %s(1).\n"),
              program_name, program_name, program_name,
              program_name, program_name, program_name,
@@ -178,9 +155,11 @@ main (int argc, char *argv[])
     { "add", 1, 0, 'a' },
     { "cmd-help", 2, 0, 'h' },
     { "connect", 1, 0, 'c' },
     { "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' },
     { "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 },
     { "help", 0, 0, HELP_OPTION },
     { "inspector", 0, 0, 'i' },
     { "keys-from-stdin", 0, 0, 0 },
@@ -203,6 +182,7 @@ main (int argc, char *argv[])
   struct mp *mps = NULL;
   struct mp *mp;
   char *p, *file = NULL;
   struct mp *mps = NULL;
   struct mp *mp;
   char *p, *file = NULL;
+  const char *format = NULL;
   int c;
   int option_index;
   struct sigaction sa;
   int c;
   int option_index;
   struct sigaction sa;
@@ -224,8 +204,6 @@ main (int argc, char *argv[])
     exit (EXIT_FAILURE);
   }
 
     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
   /* 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
@@ -282,6 +260,13 @@ main (int argc, char *argv[])
         override_progress_bars = 0;
       } else if (STREQ (long_options[option_index].name, "echo-keys")) {
         echo_keys = 1;
         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);
       } else {
         fprintf (stderr, _("%s: unknown long option: %s (%d)\n"),
                  program_name, long_options[option_index].name, option_index);
@@ -290,60 +275,15 @@ main (int argc, char *argv[])
       break;
 
     case 'a':
       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':
       break;
 
     case 'c':
-      libvirt_uri = optarg;
+      OPTION_c;
       break;
 
     case 'd':
       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':
       break;
 
     case 'D':
@@ -373,48 +313,57 @@ main (int argc, char *argv[])
     }
 
     case 'i':
     }
 
     case 'i':
-      inspector = 1;
+      OPTION_i;
       break;
 
     case 'm':
       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':
       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':
       break;
 
     case 'r':
-      read_only = 1;
+      OPTION_r;
       break;
 
     case 'v':
       break;
 
     case 'v':
-      verbose++;
-      guestfs_set_verbose (g, verbose);
+      OPTION_v;
       break;
 
       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 'x':
 
     case 'x':
-      guestfs_set_trace (g, 1);
+      OPTION_x;
       break;
 
     case HELP_OPTION:
       break;
 
     case HELP_OPTION:
@@ -440,6 +389,7 @@ main (int argc, char *argv[])
         }
         drv->type = drv_a;
         drv->a.filename = argv[optind];
         }
         drv->type = drv_a;
         drv->a.filename = argv[optind];
+        drv->a.format = NULL;
         drv->next = drvs;
         drvs = drv;
       } else {                  /* simulate -d option */
         drv->next = drvs;
         drvs = drv;
       } else {                  /* simulate -d option */
@@ -485,6 +435,10 @@ main (int argc, char *argv[])
     mount_mps (mps);
   }
 
     mount_mps (mps);
   }
 
+  /* Free up data structures, no longer needed after this point. */
+  free_drives (drvs);
+  free_mps (mps);
+
   /* Remote control? */
   if (remote_control_listen && remote_control) {
     fprintf (stderr,
   /* Remote control? */
   if (remote_control_listen && remote_control) {
     fprintf (stderr,
@@ -589,95 +543,6 @@ pod2text (const char *name, const char *shortdesc, const char *str)
   pclose (fp);
 }
 
   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)
 {
 static void
 prepare_drives (struct drv *drv)
 {
@@ -765,7 +630,7 @@ script (int prompt)
               "Welcome to guestfish, the libguestfs filesystem interactive shell for\n"
               "editing virtual machine filesystems.\n"
               "\n"
               "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"));
               "      'man' to read the manual\n"
               "      'quit' to quit the shell\n"
               "\n"));
@@ -1064,7 +929,7 @@ issue_command (const char *cmd, char *argv[], const char *pipecmd)
   /* Otherwise execute it locally. */
   else if (STRCASEEQ (cmd, "help")) {
     if (argc == 0) {
   /* Otherwise execute it locally. */
   else if (STRCASEEQ (cmd, "help")) {
     if (argc == 0) {
-      list_commands ();
+      display_help ();
       r = 0;
     } else
       r = display_command (argv[0]);
       r = 0;
     } else
       r = display_command (argv[0]);
@@ -1398,6 +1263,7 @@ cleanup_readline (void)
 #else
     (void) write_history (histfile);
 #endif
 #else
     (void) write_history (histfile);
 #endif
+    clear_history ();
   }
 #endif
 }
   }
 #endif
 }
@@ -1522,7 +1388,7 @@ file_in (const char *arg)
 static char *
 file_in_heredoc (const char *endmarker)
 {
 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");
   file_in_tmpfile = strdup (template);
   if (file_in_tmpfile == NULL) {
     perror ("strdup");
@@ -1618,66 +1484,3 @@ file_out (const char *arg)
   }
   return ret;
 }
   }
   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;
-}