New API: fallocate64 (replaces fallocate).
[libguestfs.git] / fish / fish.c
index 4a7e70b..557a6ac 100644 (file)
@@ -1,5 +1,5 @@
 /* guestfish - the filesystem interactive shell
 /* guestfish - the filesystem interactive shell
- * Copyright (C) 2009 Red Hat Inc.
+ * Copyright (C) 2009-2010 Red Hat Inc.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -58,13 +58,17 @@ struct mp {
 static void add_drives (struct drv *drv);
 static void prepare_drives (struct drv *drv);
 static void mount_mps (struct mp *mp);
 static void add_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 void script (int prompt);
 static void cmdline (char *argv[], int optind, int argc);
 static void initialize_readline (void);
 static void cleanup_readline (void);
 static void interactive (void);
 static void shell_script (void);
 static void script (int prompt);
 static void cmdline (char *argv[], int optind, int argc);
 static void initialize_readline (void);
 static void cleanup_readline (void);
+#ifdef HAVE_LIBREADLINE
 static void add_history_line (const char *);
 static void add_history_line (const char *);
+#endif
+static void print_shell_quote (FILE *stream, const char *str);
 
 /* Currently open libguestfs handle. */
 guestfs_h *g;
 
 /* Currently open libguestfs handle. */
 guestfs_h *g;
@@ -72,24 +76,11 @@ guestfs_h *g;
 int read_only = 0;
 int quit = 0;
 int verbose = 0;
 int read_only = 0;
 int quit = 0;
 int verbose = 0;
-int echo_commands = 0;
 int remote_control_listen = 0;
 int remote_control = 0;
 int exit_on_error = 1;
 int command_num = 0;
 
 int remote_control_listen = 0;
 int remote_control = 0;
 int exit_on_error = 1;
 int command_num = 0;
 
-int
-launch (guestfs_h *_g)
-{
-  assert (_g == g);
-
-  if (guestfs_is_config (g)) {
-    if (guestfs_launch (g) == -1)
-      return -1;
-  }
-  return 0;
-}
-
 static void __attribute__((noreturn))
 usage (int status)
 {
 static void __attribute__((noreturn))
 usage (int status)
 {
@@ -361,7 +352,7 @@ main (int argc, char *argv[])
       exit (EXIT_SUCCESS);
 
     case 'x':
       exit (EXIT_SUCCESS);
 
     case 'x':
-      echo_commands = 1;
+      guestfs_set_trace (g, 1);
       break;
 
     case HELP_OPTION:
       break;
 
     case HELP_OPTION:
@@ -374,9 +365,6 @@ main (int argc, char *argv[])
 
   /* Inspector mode invalidates most of the other arguments. */
   if (inspector) {
 
   /* Inspector mode invalidates most of the other arguments. */
   if (inspector) {
-    char cmd[1024];
-    int r;
-
     if (drvs || mps || remote_control_listen || remote_control ||
         guestfs_get_selinux (g)) {
       fprintf (stderr, _("%s: cannot use -i option with -a, -m, -N, "
     if (drvs || mps || remote_control_listen || remote_control ||
         guestfs_get_selinux (g)) {
       fprintf (stderr, _("%s: cannot use -i option with -a, -m, -N, "
@@ -391,42 +379,89 @@ main (int argc, char *argv[])
       exit (EXIT_FAILURE);
     }
 
       exit (EXIT_FAILURE);
     }
 
-    strcpy (cmd, "a=`virt-inspector");
+    char *cmd;
+    size_t cmdlen;
+    FILE *fp = open_memstream (&cmd, &cmdlen);
+    if (fp == NULL) {
+      perror ("open_memstream");
+      exit (EXIT_FAILURE);
+    }
+
+    fprintf (fp, "virt-inspector");
     while (optind < argc) {
     while (optind < argc) {
-      if (strlen (cmd) + strlen (argv[optind]) + strlen (real_argv0) + 60
-          >= sizeof cmd) {
-        fprintf (stderr,
-                 _("%s: virt-inspector command too long for fixed-size buffer\n"),
-                 program_name);
-        exit (EXIT_FAILURE);
-      }
-      strcat (cmd, " '");
-      strcat (cmd, argv[optind]);
-      strcat (cmd, "'");
+      fputc (' ', fp);
+      print_shell_quote (fp, argv[optind]);
       optind++;
     }
 
     if (read_only)
       optind++;
     }
 
     if (read_only)
-      strcat (cmd, " --ro-fish");
+      fprintf (fp, " --ro-fish");
     else
     else
-      strcat (cmd, " --fish");
+      fprintf (fp, " --fish");
 
 
-    sprintf (&cmd[strlen(cmd)], "` && %s $a", real_argv0);
+    if (fclose (fp) == -1) {
+      perror ("fclose");
+      exit (EXIT_FAILURE);
+    }
+
+    if (verbose)
+      fprintf (stderr,
+               "%s -i: running: %s\n", program_name, cmd);
+
+    FILE *pp = popen (cmd, "r");
+    if (pp == NULL) {
+      perror (cmd);
+      exit (EXIT_FAILURE);
+    }
+
+    char *cmd2;
+    fp = open_memstream (&cmd2, &cmdlen);
+    if (fp == NULL) {
+      perror ("open_memstream");
+      exit (EXIT_FAILURE);
+    }
+
+    fprintf (fp, "%s", real_argv0);
 
     if (guestfs_get_verbose (g))
 
     if (guestfs_get_verbose (g))
-      strcat (cmd, " -v");
+      fprintf (fp, " -v");
     if (!guestfs_get_autosync (g))
     if (!guestfs_get_autosync (g))
-      strcat (cmd, " -n");
+      fprintf (fp, " -n");
+    if (guestfs_get_trace (g))
+      fprintf (fp, " -x");
+
+    char *insp = NULL;
+    size_t insplen;
+    if (getline (&insp, &insplen, pp) == -1) {
+      perror (cmd);
+      exit (EXIT_FAILURE);
+    }
+    fprintf (fp, " %s", insp);
+
+    if (pclose (pp) == -1) {
+      perror (cmd);
+      exit (EXIT_FAILURE);
+    }
+
+    if (fclose (fp) == -1) {
+      perror ("fclose");
+      exit (EXIT_FAILURE);
+    }
 
     if (verbose)
       fprintf (stderr,
 
     if (verbose)
       fprintf (stderr,
-               "%s -i: running virt-inspector command:\n%s\n", program_name, cmd);
+               "%s -i: running: %s\n", program_name, cmd2);
 
 
-    r = system (cmd);
+    int r = system (cmd2);
     if (r == -1) {
     if (r == -1) {
-      perror ("system");
+      perror (cmd2);
       exit (EXIT_FAILURE);
     }
       exit (EXIT_FAILURE);
     }
+
+    free (cmd);
+    free (cmd2);
+    free (insp);
+
     exit (WEXITSTATUS (r));
   }
 
     exit (WEXITSTATUS (r));
   }
 
@@ -437,7 +472,7 @@ main (int argc, char *argv[])
    * guest and mount them.
    */
   if (next_prepared_drive > 1 || mps != NULL) {
    * guest and mount them.
    */
   if (next_prepared_drive > 1 || mps != NULL) {
-    if (launch (g) == -1) exit (EXIT_FAILURE);
+    if (launch () == -1) exit (EXIT_FAILURE);
     prepare_drives (drvs);
     mount_mps (mps);
   }
     prepare_drives (drvs);
     mount_mps (mps);
   }
@@ -550,10 +585,21 @@ prepare_drives (struct drv *drv)
 {
   if (drv) {
     prepare_drives (drv->next);
 {
   if (drv) {
     prepare_drives (drv->next);
-    prepare_drive (drv->filename, drv->data, drv->device);
+    if (drv->data)
+      prepare_drive (drv->filename, drv->data, drv->device);
   }
 }
 
   }
 }
 
+static int
+launch (void)
+{
+  if (guestfs_is_config (g)) {
+    if (guestfs_launch (g) == -1)
+      return -1;
+  }
+  return 0;
+}
+
 static void
 interactive (void)
 {
 static void
 interactive (void)
 {
@@ -621,7 +667,8 @@ 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 help with commands\n"
+              "Type: 'help' for a list of commands\n"
+              "      'man' to read the manual\n"
               "      'quit' to quit the shell\n"
               "\n"));
 
               "      'quit' to quit the shell\n"
               "\n"));
 
@@ -855,13 +902,6 @@ issue_command (const char *cmd, char *argv[], const char *pipecmd)
   /* This counts the commands issued, starting at 1. */
   command_num++;
 
   /* This counts the commands issued, starting at 1. */
   command_num++;
 
-  if (echo_commands) {
-    printf ("%s", cmd);
-    for (i = 0; argv[i] != NULL; ++i)
-      printf (" %s", argv[i]);
-    printf ("\n");
-  }
-
   /* For | ... commands.  Annoyingly we can't use popen(3) here. */
   if (pipecmd) {
     int fd[2];
   /* For | ... commands.  Annoyingly we can't use popen(3) here. */
   if (pipecmd) {
     int fd[2];
@@ -942,6 +982,9 @@ issue_command (const char *cmd, char *argv[], const char *pipecmd)
     r = do_lcd (cmd, argc, argv);
   else if (STRCASEEQ (cmd, "glob"))
     r = do_glob (cmd, argc, argv);
     r = do_lcd (cmd, argc, argv);
   else if (STRCASEEQ (cmd, "glob"))
     r = do_glob (cmd, argc, argv);
+  else if (STRCASEEQ (cmd, "man") ||
+           STRCASEEQ (cmd, "manual"))
+    r = do_man (cmd, argc, argv);
   else if (STRCASEEQ (cmd, "more") ||
            STRCASEEQ (cmd, "less"))
     r = do_more (cmd, argc, argv);
   else if (STRCASEEQ (cmd, "more") ||
            STRCASEEQ (cmd, "less"))
     r = do_more (cmd, argc, argv);
@@ -949,6 +992,8 @@ issue_command (const char *cmd, char *argv[], const char *pipecmd)
     r = do_reopen (cmd, argc, argv);
   else if (STRCASEEQ (cmd, "sparse"))
     r = do_sparse (cmd, argc, argv);
     r = do_reopen (cmd, argc, argv);
   else if (STRCASEEQ (cmd, "sparse"))
     r = do_sparse (cmd, argc, argv);
+  else if (STRCASEEQ (cmd, "supported"))
+    r = do_supported (cmd, argc, argv);
   else if (STRCASEEQ (cmd, "time"))
     r = do_time (cmd, argc, argv);
   else
   else if (STRCASEEQ (cmd, "time"))
     r = do_time (cmd, argc, argv);
   else
@@ -981,10 +1026,12 @@ issue_command (const char *cmd, char *argv[], const char *pipecmd)
 void
 list_builtin_commands (void)
 {
 void
 list_builtin_commands (void)
 {
-  /* help and quit should appear at the top */
+  /* help, man and quit should appear at the top */
   printf ("%-20s %s\n",
           "help", _("display a list of commands or help on a command"));
   printf ("%-20s %s\n",
   printf ("%-20s %s\n",
           "help", _("display a list of commands or help on a command"));
   printf ("%-20s %s\n",
+          "man", _("read the manual"));
+  printf ("%-20s %s\n",
           "quit", _("quit guestfish"));
 
   printf ("%-20s %s\n",
           "quit", _("quit guestfish"));
 
   printf ("%-20s %s\n",
@@ -1004,6 +1051,8 @@ list_builtin_commands (void)
   printf ("%-20s %s\n",
           "sparse", _("allocate a sparse image file"));
   printf ("%-20s %s\n",
   printf ("%-20s %s\n",
           "sparse", _("allocate a sparse image file"));
   printf ("%-20s %s\n",
+          "supported", _("list supported groups of commands"));
+  printf ("%-20s %s\n",
           "time", _("measure time taken to run command"));
 
   /* actions are printed after this (see list_commands) */
           "time", _("measure time taken to run command"));
 
   /* actions are printed after this (see list_commands) */
@@ -1024,16 +1073,8 @@ display_builtin_command (const char *cmd)
               "\n"
               "    For more advanced image creation, see qemu-img utility.\n"
               "\n"
               "\n"
               "    For more advanced image creation, see qemu-img utility.\n"
               "\n"
-              "    Size can be specified (where <nn> means a number):\n"
-              "    <nn>             number of kilobytes\n"
-              "      eg: 1440       standard 3.5\" floppy\n"
-              "    <nn>K or <nn>KB  number of kilobytes\n"
-              "    <nn>M or <nn>MB  number of megabytes\n"
-              "    <nn>G or <nn>GB  number of gigabytes\n"
-              "    <nn>T or <nn>TB  number of terabytes\n"
-              "    <nn>P or <nn>PB  number of petabytes\n"
-              "    <nn>E or <nn>EB  number of exabytes\n"
-              "    <nn>sects        number of 512 byte sectors\n"));
+              "    Size can be specified using standard suffixes, eg. '1M'.\n"
+              ));
   else if (STRCASEEQ (cmd, "echo"))
     printf (_("echo - display a line of text\n"
               "     echo [<params> ...]\n"
   else if (STRCASEEQ (cmd, "echo"))
     printf (_("echo - display a line of text\n"
               "     echo [<params> ...]\n"
@@ -1048,7 +1089,7 @@ display_builtin_command (const char *cmd)
               "    This is used to edit a file.\n"
               "\n"
               "    It is the equivalent of (and is implemented by)\n"
               "    This is used to edit a file.\n"
               "\n"
               "    It is the equivalent of (and is implemented by)\n"
-              "    running \"cat\", editing locally, and then \"write-file\".\n"
+              "    running \"cat\", editing locally, and then \"write\".\n"
               "\n"
               "    Normally it uses $EDITOR, but if you use the aliases\n"
               "    \"vi\" or \"emacs\" you will get those editors.\n"
               "\n"
               "    Normally it uses $EDITOR, but if you use the aliases\n"
               "    \"vi\" or \"emacs\" you will get those editors.\n"
@@ -1069,6 +1110,12 @@ display_builtin_command (const char *cmd)
               "    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"));
               "    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"));
+  else if (STRCASEEQ (cmd, "man") ||
+           STRCASEEQ (cmd, "manual"))
+    printf (_("man - read the manual\n"
+              "    man\n"
+              "\n"
+              "    Opens the manual page for guestfish.\n"));
   else if (STRCASEEQ (cmd, "help"))
     printf (_("help - display a list of commands or help on a command\n"
               "     help cmd\n"
   else if (STRCASEEQ (cmd, "help"))
     printf (_("help - display a list of commands or help on a command\n"
               "     help cmd\n"
@@ -1117,16 +1164,18 @@ display_builtin_command (const char *cmd)
               "\n"
               "    For more advanced image creation, see qemu-img utility.\n"
               "\n"
               "\n"
               "    For more advanced image creation, see qemu-img utility.\n"
               "\n"
-              "    Size can be specified (where <nn> means a number):\n"
-              "    <nn>             number of kilobytes\n"
-              "      eg: 1440       standard 3.5\" floppy\n"
-              "    <nn>K or <nn>KB  number of kilobytes\n"
-              "    <nn>M or <nn>MB  number of megabytes\n"
-              "    <nn>G or <nn>GB  number of gigabytes\n"
-              "    <nn>T or <nn>TB  number of terabytes\n"
-              "    <nn>P or <nn>PB  number of petabytes\n"
-              "    <nn>E or <nn>EB  number of exabytes\n"
-              "    <nn>sects        number of 512 byte sectors\n"));
+              "    Size can be specified using standard suffixes, eg. '1M'.\n"
+              ));
+  else if (STRCASEEQ (cmd, "supported"))
+    printf (_("supported - list supported groups of commands\n"
+              "     supported\n"
+              "\n"
+              "    This command returns a list of the optional groups\n"
+              "    known to the daemon, and indicates which ones are\n"
+              "    supported by this build of the libguestfs appliance.\n"
+              "\n"
+              "    See also guestfs(3) section AVAILABILITY.\n"
+              ));
   else if (STRCASEEQ (cmd, "time"))
     printf (_("time - measure time taken to run command\n"
               "    time <command> [<args> ...]\n"
   else if (STRCASEEQ (cmd, "time"))
     printf (_("time - measure time taken to run command\n"
               "    time <command> [<args> ...]\n"
@@ -1368,6 +1417,13 @@ initialize_readline (void)
 
   rl_readline_name = "guestfish";
   rl_attempted_completion_function = do_completion;
 
   rl_readline_name = "guestfish";
   rl_attempted_completion_function = do_completion;
+
+  /* Note that .inputrc (or /etc/inputrc) is not read until the first
+   * call the readline(), which happens later.  Therefore, these
+   * provide default values which can be overridden by the user if
+   * they wish.
+   */
+  (void) rl_variable_bind ("completion-ignore-case", "on");
 #endif
 }
 
 #endif
 }
 
@@ -1394,14 +1450,14 @@ cleanup_readline (void)
 #endif
 }
 
 #endif
 }
 
+#ifdef HAVE_LIBREADLINE
 static void
 add_history_line (const char *line)
 {
 static void
 add_history_line (const char *line)
 {
-#ifdef HAVE_LIBREADLINE
   add_history (line);
   nr_history_lines++;
   add_history (line);
   nr_history_lines++;
-#endif
 }
 }
+#endif
 
 int
 xwrite (int fd, const void *v_buf, size_t len)
 
 int
 xwrite (int fd, const void *v_buf, size_t len)
@@ -1610,3 +1666,17 @@ file_out (const char *arg)
   }
   return ret;
 }
   }
   return ret;
 }
+
+static void
+print_shell_quote (FILE *stream, const char *str)
+{
+#define SAFE(c) (c_isalnum((c)) ||                                     \
+                 (c) == '/' || (c) == '-' || (c) == '_' || (c) == '.')
+  int i;
+
+  for (i = 0; str[i]; ++i) {
+    if (!SAFE(str[i]))
+      putc ('\\', stream);
+    putc (str[i], stream);
+  }
+}