From 323350c82b49ac4d3519fe7ef9959c79dd9a83db Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" Date: Sat, 4 Mar 2017 11:47:59 +0000 Subject: [PATCH 06/10] rescue: Implement escape sequences. This implements a few useful escape sequences: > ^]? virt-rescue escape sequences: ^] ? - print this message ^] h - print this message ^] q - quit virt-rescue ^] s - sync the filesystems ^] u - unmount filesystems ^] x - quit virt-rescue ^] z - suspend virt-rescue to pass the escape key through to the rescue shell, type it twice ^]i root device: /dev/sda3 product name: Fedora 25 (Twenty Five) type: linux distro: fedora ^]z [3]+ Stopped ./run virt-rescue --scratch $ fg > ^]u unmounting filesystems ... [ 21.158558] XFS (sda3): Unmounting Filesystem (cherry picked from commit 3637c42f4e521eb647d7dfae7f48eb1689d0af54) --- rescue/Makefile.am | 4 +- rescue/escape.c | 302 +++++++++++++++++++++++++++++++++++++++++++++++++ rescue/rescue.c | 30 ++++- rescue/rescue.h | 47 ++++++++ rescue/virt-rescue.pod | 74 ++++++++++++ 5 files changed, 454 insertions(+), 3 deletions(-) create mode 100644 rescue/escape.c create mode 100644 rescue/rescue.h diff --git a/rescue/Makefile.am b/rescue/Makefile.am index c83c43458..eb60bafa4 100644 --- a/rescue/Makefile.am +++ b/rescue/Makefile.am @@ -26,7 +26,9 @@ EXTRA_DIST = \ bin_PROGRAMS = virt-rescue virt_rescue_SOURCES = \ - rescue.c + escape.c \ + rescue.c \ + rescue.h virt_rescue_CPPFLAGS = \ -DGUESTFS_WARN_DEPRECATED=1 \ diff --git a/rescue/escape.c b/rescue/escape.c new file mode 100644 index 000000000..f7f7d84c4 --- /dev/null +++ b/rescue/escape.c @@ -0,0 +1,302 @@ +/* virt-rescue + * Copyright (C) 2010-2017 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "c-ctype.h" + +#include "guestfs.h" +#include "guestfs-internal-frontend.h" + +#include "rescue.h" + +static void print_help (void); +static void print_inspector (void); +static void crlf (void); +static void print_escape_key (void); + +/* Parse the -e parameter from the command line. */ +int +parse_escape_key (const char *arg) +{ + size_t len; + + if (STREQ (arg, "none")) + return 0; + + len = strlen (arg); + if (arg == 0) + return -1; + + switch (arg[0]) { + case '^': + if (len == 2 && + ((arg[1] >= 'a' && arg[1] <= 'z') || + (arg[1] >= 'A' && arg[1] <= '_'))) { + return c_toupper (arg[1]) - '@'; + } + else + return -1; + break; + } + + return -1; +} + +/* Print one-line end user description of the escape key. + * + * This is printed when virt-rescue starts. + */ +void +print_escape_key_help (void) +{ + crlf (); + /* Difficult to translate this string. XXX */ + printf ("The virt-rescue escape key is ‘"); + print_escape_key (); + printf ("’. Type ‘"); + print_escape_key (); + printf (" h’ for help."); + crlf (); +} + +void +init_escape_state (struct escape_state *state) +{ + state->in_escape = false; +} + +/* Process escapes in the tty input buffer. + * + * This function has a state parameter 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. + */ +bool +process_escapes (struct escape_state *state, char *buf, size_t *len) +{ + size_t i; + + for (i = 0; i < *len; ++i) { +#define DROP_CURRENT_CHAR() \ + memmove (&buf[i], &buf[i+1], --(*len)) +#define PRINT_ESC() \ + do { print_escape_key (); putchar (buf[i]); crlf (); } while (0) + + if (!state->in_escape) { + if (buf[i] == escape_key) { + /* Drop the escape key from the buffer and go to escape mode. */ + DROP_CURRENT_CHAR (); + state->in_escape = true; + } + } + else /* in escape sequence */ { + if (buf[i] == escape_key) /* ^] ^] means send ^] to rescue shell */ + state->in_escape = false; + else { + switch (buf[i]) { + case '?': case 'h': + PRINT_ESC (); + print_help (); + break; + + case 'i': + PRINT_ESC (); + print_inspector (); + break; + + case 'q': case 'x': + PRINT_ESC (); + return true /* exit virt-rescue at once */; + + case 's': + PRINT_ESC (); + printf (_("attempting to sync filesystems ...")); + crlf (); + guestfs_sync (g); + break; + + case 'u': + PRINT_ESC (); + printf (_("unmounting filesystems ...")); + crlf (); + guestfs_umount_all (g); + break; + + case 'z': + PRINT_ESC (); + raise (SIGTSTP); + break; + + default: + /* Any unrecognized escape sequence will be dropped. We + * could be obnoxious and ring the bell, but I hate it when + * programs do that. + */ + break; + } + + /* Drop the escape key and return to non-escape mode. */ + DROP_CURRENT_CHAR (); + state->in_escape = false; + + /* The output is line buffered, this is just to make sure + * everything gets written to stdout before we continue + * writing to STDOUT_FILENO. + */ + fflush (stdout); + } + } /* in escape sequence */ + } /* for */ + + return false /* don't exit */; +} + +/* This is called when the user types ^] h */ +static void +print_help (void) +{ + printf (_("virt-rescue escape sequences:")); + crlf (); + + putchar (' '); + print_escape_key (); + printf (_(" ? - print this message")); + crlf (); + + putchar (' '); + print_escape_key (); + printf (_(" h - print this message")); + crlf (); + + if (inspector) { + putchar (' '); + print_escape_key (); + printf (_(" i - print inspection data")); + crlf (); + } + + putchar (' '); + print_escape_key (); + printf (_(" q - quit virt-rescue")); + crlf (); + + putchar (' '); + print_escape_key (); + printf (_(" s - sync the filesystems")); + crlf (); + + putchar (' '); + print_escape_key (); + printf (_(" u - unmount filesystems")); + crlf (); + + putchar (' '); + print_escape_key (); + printf (_(" x - quit virt-rescue")); + crlf (); + + putchar (' '); + print_escape_key (); + printf (_(" z - suspend virt-rescue")); + crlf (); + + printf (_("to pass the escape key through to the rescue shell, type it twice")); + crlf (); +} + +/* This is called when the user types ^] i */ +static void +print_inspector (void) +{ + CLEANUP_FREE_STRING_LIST char **roots; + size_t i; + const char *root; + char *str; + + if (inspector) { + roots = guestfs_inspect_get_roots (g); + if (roots) { + crlf (); + for (i = 0; roots[i] != NULL; ++i) { + root = roots[i]; + printf (_("root device: %s"), root); + crlf (); + + str = guestfs_inspect_get_product_name (g, root); + if (str) { + printf (_(" product name: %s"), str); + crlf (); + } + free (str); + + str = guestfs_inspect_get_type (g, root); + if (str) { + printf (_(" type: %s"), str); + crlf (); + } + free (str); + + str = guestfs_inspect_get_distro (g, root); + if (str) { + printf (_(" distro: %s"), str); + crlf (); + } + free (str); + } + } + } +} + +/* Because the terminal is in raw mode, we have to send CR LF instead + * of printing just \n. + */ +static void +crlf (void) +{ + putchar ('\r'); + putchar ('\n'); +} + +static void +print_escape_key (void) +{ + switch (escape_key) { + case 0: + printf ("none"); + break; + case '\x1'...'\x1f': + putchar ('^'); + putchar (escape_key + '@'); + break; + default: + abort (); + } +} diff --git a/rescue/rescue.c b/rescue/rescue.c index 2b461378d..5281b1161 100644 --- a/rescue/rescue.c +++ b/rescue/rescue.c @@ -1,5 +1,5 @@ /* virt-rescue - * Copyright (C) 2010-2012 Red Hat Inc. + * Copyright (C) 2010-2017 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 @@ -40,10 +40,14 @@ #include "xvasprintf.h" #include "guestfs.h" +#include "guestfs-internal-frontend.h" + #include "windows.h" #include "options.h" #include "display-options.h" +#include "rescue.h" + static void log_message_callback (guestfs_h *g, void *opaque, uint64_t event, int event_handle, int flags, const char *buf, size_t buf_len, const uint64_t *array, size_t array_len); static void do_rescue (int sock); static void raw_tty (void); @@ -65,6 +69,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; @@ -86,6 +91,7 @@ usage (int status) " --append kernelopts Append kernel options\n" " -c|--connect uri Specify libvirt URI for -d option\n" " -d|--domain guest Add disks from libvirt guest\n" + " -e ^x|none Set or disable escape key (default ^])\n" " --format[=raw|..] Force disk format for -a option\n" " --help Display brief help\n" " -i|--inspector Automatically mount filesystems\n" @@ -119,7 +125,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 }, @@ -226,6 +232,12 @@ main (int argc, char *argv[]) OPTION_d; break; + case 'e': + escape_key = parse_escape_key (optarg); + if (escape_key == -1) + error (EXIT_FAILURE, 0, _("unrecognized escape key: %s"), optarg); + break; + case 'i': OPTION_i; break; @@ -428,6 +440,10 @@ main (int argc, char *argv[]) signal (SIGTSTP, tstp_handler); signal (SIGCONT, cont_handler); + /* Print the escape key if set. */ + if (escape_key > 0) + print_escape_key_help (); + do_rescue (sock); restore_tty (); @@ -478,6 +494,9 @@ do_rescue (int sock) { size_t rlen = 0; size_t wlen = 0; + struct escape_state escape_state; + + init_escape_state (&escape_state); while (sock >= 0 || rlen > 0) { struct pollfd fds[3]; @@ -534,6 +553,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 (&escape_state, wbuf, &wlen)) + return; } /* Log message from appliance. */ diff --git a/rescue/rescue.h b/rescue/rescue.h new file mode 100644 index 000000000..ccffb5eb3 --- /dev/null +++ b/rescue/rescue.h @@ -0,0 +1,47 @@ +/* virt-rescue + * Copyright (C) 2010-2017 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef RESCUE_H +#define RESCUE_H + +#include + +#include "guestfs.h" + +extern guestfs_h *g; +extern int read_only; +extern int live; +extern int verbose; +extern int keys_from_stdin; +extern int echo_keys; +extern const char *libvirt_uri; +extern int inspector; +extern int in_guestfish; +extern int in_virt_rescue; +extern int escape_key; + +/* escape.c */ +struct escape_state { + bool in_escape; +}; +extern void init_escape_state (struct escape_state *state); +extern bool process_escapes (struct escape_state *state, char *buf, size_t *len); +extern int parse_escape_key (const char *); +extern void print_escape_key_help (void); + +#endif /* RESCUE_H */ diff --git a/rescue/virt-rescue.pod b/rescue/virt-rescue.pod index b651f84e7..bd6f954e9 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 key. + +=item C + +I<-e none> means there is no escape key, escapes are disabled. + +=back + +See L below for further information. + =item B<--format=raw|qcow2|..> =item B<--format> @@ -321,6 +344,57 @@ See L 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<^] s> + +Synchronize the filesystems (sync). + +=item C<^] u> + +Unmounts all the filesystems, except for the root (appliance) +filesystems. + +=item C<^] z> + +Suspend virt-rescue (like pressing C<^Z> except that it affects +virt-rescue rather than the program inside the rescue shell). + +=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 -- 2.13.2