Richard W.M. Jones
2017-Mar-04 11:50 UTC
[Libguestfs] [PATCH] rescue: Implement escape sequences.
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