[Libguestfs] [PATCH] rescue: Implement escape sequences.

Richard W.M. Jones rjones at redhat.com
Sat Mar 4 11:50:34 UTC 2017


This implements a few useful escape sequences:

><rescue> ^]?
virt-rescue escape sequences:
^]? - print this message
^]h - print this message
^]i - print inspection data
^]q - quit virt-rescue
^]u - unmount filesystems
^]x - quit virt-rescue
to send the escape key to the rescue shell, type it twice

^]i

root device: /dev/sda3
  product name: Fedora 25 (Twenty Five)
  type: linux
  distro: fedora

^]u

unmounting filesystems ...
[   21.158558] XFS (sda3): Unmounting Filesystem
---
 rescue/rescue.c        | 145 ++++++++++++++++++++++++++++++++++++++++++++++++-
 rescue/virt-rescue.pod |  65 ++++++++++++++++++++++
 2 files changed, 209 insertions(+), 1 deletion(-)

diff --git a/rescue/rescue.c b/rescue/rescue.c
index fb747df..06920c2 100644
--- a/rescue/rescue.c
+++ b/rescue/rescue.c
@@ -20,6 +20,7 @@
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <stdbool.h>
 #include <string.h>
 #include <inttypes.h>
 #include <unistd.h>
@@ -32,6 +33,7 @@
 #include <assert.h>
 #include <libintl.h>
 
+#include "c-ctype.h"
 #include "full-write.h"
 #include "getprogname.h"
 #include "ignore-value.h"
@@ -61,6 +63,7 @@ const char *libvirt_uri = NULL;
 int inspector = 0;
 int in_guestfish = 0;
 int in_virt_rescue = 1;
+int escape_key = '\x1d';        /* ^] */
 
 /* Old terminal settings. */
 static struct termios old_termios;
@@ -115,7 +118,7 @@ main (int argc, char *argv[])
 
   enum { HELP_OPTION = CHAR_MAX + 1 };
 
-  static const char options[] = "a:c:d:im:rvVwx";
+  static const char options[] = "a:c:d:e:im:rvVwx";
   static const struct option long_options[] = {
     { "add", 1, 0, 'a' },
     { "append", 1, 0, 0 },
@@ -223,6 +226,24 @@ main (int argc, char *argv[])
       OPTION_d;
       break;
 
+    case 'e':
+      if (STREQ (optarg, "none"))
+        escape_key = 0;
+      else if (STRPREFIX (optarg, "^")) {
+        if (strlen (optarg) == 2 &&
+            ((optarg[1] >= 'a' && optarg[1] <= 'z') ||
+             (optarg[1] >= 'A' && optarg[1] <= '_'))) {
+          escape_key = c_toupper (optarg[1]) - '@';
+        }
+        else
+          error (EXIT_FAILURE, 0,
+                 _("unrecognized ^-escape in -e option: %s"), optarg);
+      }
+      else
+        error (EXIT_FAILURE, 0,
+               _("unrecognized escape key: %s"), optarg);
+      break;
+
     case 'i':
       OPTION_i;
       break;
@@ -463,6 +484,9 @@ log_message_callback (guestfs_h *g, void *opaque, uint64_t event,
 static char rbuf[BUFSIZE];      /* appliance -> local tty */
 static char wbuf[BUFSIZE];      /* local tty -> appliance */
 
+static bool process_escapes (char *buf, size_t *len);
+static void print_escape_key (void);
+
 static void
 do_rescue (int sock)
 {
@@ -524,6 +548,13 @@ do_rescue (int sock)
       }
       if (n > 0)
         wlen += n;
+
+      /* Process escape sequences in the tty input.  If the function
+       * returns true, then we exit the loop causing virt-rescue to
+       * exit.
+       */
+      if (escape_key > 0 && process_escapes (wbuf, &wlen))
+        return;
     }
 
     /* Log message from appliance. */
@@ -576,6 +607,118 @@ do_rescue (int sock)
   }
 }
 
+/* Process escapes in the tty input buffer.
+ *
+ * This function has internal state so that we can handle an escape
+ * sequence split over the end of the buffer.  Escape sequences are
+ * removed from the buffer.
+ *
+ * Returns true iff virt-rescue should exit.
+ */
+static bool
+process_escapes (char *buf, size_t *len)
+{
+  size_t i;
+  static bool in_escape = false;
+
+  for (i = 0; i < *len; ++i) {
+    if (!in_escape) {
+      if (buf[i] == escape_key) {
+        /* Drop the escape key from the buffer and go to escape mode. */
+        memmove (&buf[i], &buf[i+1], --(*len));
+        in_escape = 1;
+      }
+    }
+    else /* in escape sequence */ {
+      if (buf[i] == escape_key) /* ^] ^] means send ^] to rescue shell */
+        in_escape = 0;
+      else {
+        switch (buf[i]) {
+        case '?': case 'h':
+          printf ("\r\n");
+          printf (_("virt-rescue escape sequences:\r\n"));
+          print_escape_key (); printf (_("? - print this message\r\n"));
+          print_escape_key (); printf (_("h - print this message\r\n"));
+          if (inspector) {
+            print_escape_key (); printf (_("i - print inspection data\r\n"));
+          }
+          print_escape_key (); printf (_("q - quit virt-rescue\r\n"));
+          print_escape_key (); printf (_("u - unmount filesystems\r\n"));
+          print_escape_key (); printf (_("x - quit virt-rescue\r\n"));
+          printf (_("to send the escape key to the rescue shell, type it twice\r\n"));
+          break;
+
+        case 'i':
+          if (inspector) {
+            CLEANUP_FREE_STRING_LIST char **roots;
+            size_t i;
+
+            roots = guestfs_inspect_get_roots (g);
+            if (roots) {
+              printf ("\r\n");
+              for (i = 0; roots[i] != NULL; ++i) {
+                const char *root = roots[i];
+                char *str;
+
+                printf (_("root device: %s\r\n"), root);
+                str = guestfs_inspect_get_product_name (g, root);
+                if (str)
+                  printf (_("  product name: %s\r\n"), str);
+                free (str);
+                str = guestfs_inspect_get_type (g, root);
+                if (str)
+                  printf (_("  type: %s\r\n"), str);
+                free (str);
+                str = guestfs_inspect_get_distro (g, root);
+                if (str)
+                  printf (_("  distro: %s\r\n"), str);
+                free (str);
+              }
+            }
+          }
+          break;
+
+        case 'q': case 'x':
+          return true /* exit virt-rescue at once */;
+
+        case 'u':
+          printf ("\r\n");
+          printf (_("unmounting filesystems ...\r\n"));
+          guestfs_umount_all (g);
+          break;
+
+        default:
+          /* Any unrecognized escape sequence will be dropped.  We could
+           * be obnoxious and ring the bell. XXX
+           */
+          break;
+        }
+        /* Drop the escape key and return to non-escape mode. */
+        memmove (&buf[i], &buf[i+1], --(*len));
+        in_escape = 0;
+      }
+    }
+  }
+
+  return false /* don't exit */;
+}
+
+static void
+print_escape_key (void)
+{
+  switch (escape_key) {
+  case 0:
+    printf ("none");
+    break;
+  case '\x1'...'\x1f':
+    putchar ('^');
+    putchar (escape_key + '@');
+    break;
+  default:
+    abort ();
+  }
+}
+
 static void
 restore_tty (void)
 {
diff --git a/rescue/virt-rescue.pod b/rescue/virt-rescue.pod
index b651f84..6439b98 100644
--- a/rescue/virt-rescue.pod
+++ b/rescue/virt-rescue.pod
@@ -128,6 +128,29 @@ not used at all.
 Add all the disks from the named libvirt guest.  Domain UUIDs can be
 used instead of names.
 
+=item B<-e none>
+
+Disable the escape key.
+
+=item B<-e> KEY
+
+Set the escape key to the given key sequence.  The default is C<^]>.
+To specify the escape key you can use:
+
+=over 4
+
+=item C<^x>
+
+Control key + C<x> key.
+
+=item C<none>
+
+I<-e none> means there is no escape key, escapes are disabled.
+
+=back
+
+See L</ESCAPE KEY> below for further information.
+
 =item B<--format=raw|qcow2|..>
 
 =item B<--format>
@@ -321,6 +344,48 @@ See L<bash(1)> for more details.
 
 =back
 
+=head1 ESCAPE KEY
+
+Virt-rescue supports various keyboard escape sequences which are
+entered by pressing C<^]> (Control key + C<]> key).
+
+You can change the escape key using the I<-e> option on the command
+line (see above), and you can disable escapes completely using
+I<-e none>.  The rest of this section assumes the default escape key.
+
+The following escapes can be used:
+
+=over 4
+
+=item C<^] ?>
+
+=item C<^] h>
+
+Prints a brief help text about escape sequences.
+
+=item C<^] i>
+
+Prints brief libguestfs inspection information for the guest.  This
+only works if you used I<-i> on the virt-rescue command line.
+
+=item C<^] q>
+
+=item C<^] x>
+
+Quits virt-rescue immediately.
+
+=item C<^] u>
+
+Unmounts all the filesystems, except for the root (appliance)
+filesystems.
+
+=item C<^] ^]>
+
+Sends the literal character C<^]> (ASCII 0x1d) through to the rescue
+shell.
+
+=back
+
 =head1 CAPTURING CORE DUMPS
 
 If you are testing a tool inside virt-rescue and the tool (B<not>
-- 
2.9.3




More information about the Libguestfs mailing list