Version 3: - Tidies up the code further. - Implements correct handling of SIGTSTP and SIGCONT. - Adds: ^] s - sync filesystems - Adds: ^] z - suspend virt-rescue Rich.
Richard W.M. Jones
2017-Mar-04 15:11 UTC
[Libguestfs] [PATCH v3 1/7] generator: Deprecate direct mode (guestfs_set_direct, guestfs_get_direct).
--- generator/actions_properties.ml | 28 ---------------------------- generator/actions_properties_deprecated.ml | 30 ++++++++++++++++++++++++++++++ rescue/rescue.c | 3 +++ test-tool/test-tool.c | 1 - 4 files changed, 33 insertions(+), 29 deletions(-) diff --git a/generator/actions_properties.ml b/generator/actions_properties.ml index 8f6455b..87144b1 100644 --- a/generator/actions_properties.ml +++ b/generator/actions_properties.ml @@ -260,34 +260,6 @@ C<guestfs_set_event_callback>)." }; Return the command trace flag." }; { defaults with - name = "set_direct"; added = (1, 0, 72); - style = RErr, [Bool "direct"], []; - fish_alias = ["direct"]; config_only = true; - blocking = false; - shortdesc = "enable or disable direct appliance mode"; - longdesc = "\ -If the direct appliance mode flag is enabled, then stdin and -stdout are passed directly through to the appliance once it -is launched. - -One consequence of this is that log messages aren't caught -by the library and handled by C<guestfs_set_log_message_callback>, -but go straight to stdout. - -You probably don't want to use this unless you know what you -are doing. - -The default is disabled." }; - - { defaults with - name = "get_direct"; added = (1, 0, 72); - style = RBool "direct", [], []; - blocking = false; - shortdesc = "get direct appliance mode flag"; - longdesc = "\ -Return the direct appliance mode flag." }; - - { defaults with name = "set_recovery_proc"; added = (1, 0, 77); style = RErr, [Bool "recoveryproc"], []; fish_alias = ["recovery-proc"]; config_only = true; diff --git a/generator/actions_properties_deprecated.ml b/generator/actions_properties_deprecated.ml index def17b9..5327782 100644 --- a/generator/actions_properties_deprecated.ml +++ b/generator/actions_properties_deprecated.ml @@ -125,6 +125,36 @@ Return the current backend. See C<guestfs_set_backend> and L<guestfs(3)/BACKEND>." }; + { defaults with + name = "set_direct"; added = (1, 0, 72); + style = RErr, [Bool "direct"], []; + deprecated_by = Deprecated_no_replacement; + fish_alias = ["direct"]; config_only = true; + blocking = false; + shortdesc = "enable or disable direct appliance mode"; + longdesc = "\ +If the direct appliance mode flag is enabled, then stdin and +stdout are passed directly through to the appliance once it +is launched. + +One consequence of this is that log messages aren't caught +by the library and handled by C<guestfs_set_log_message_callback>, +but go straight to stdout. + +You probably don't want to use this unless you know what you +are doing. + +The default is disabled." }; + + { defaults with + name = "get_direct"; added = (1, 0, 72); + style = RBool "direct", [], []; + deprecated_by = Deprecated_no_replacement; + blocking = false; + shortdesc = "get direct appliance mode flag"; + longdesc = "\ +Return the direct appliance mode flag." }; + ] let daemon_functions = [ diff --git a/rescue/rescue.c b/rescue/rescue.c index ed40ba6..572a844 100644 --- a/rescue/rescue.c +++ b/rescue/rescue.c @@ -295,9 +295,12 @@ main (int argc, char *argv[]) usage (EXIT_FAILURE); } +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" /* Setting "direct mode" is required for the rescue appliance. */ if (guestfs_set_direct (g, 1) == -1) exit (EXIT_FAILURE); +#pragma GCC diagnostic pop { /* The libvirt backend doesn't support direct mode. As a temporary diff --git a/test-tool/test-tool.c b/test-tool/test-tool.c index 20e2a32..2ae266d 100644 --- a/test-tool/test-tool.c +++ b/test-tool/test-tool.c @@ -224,7 +224,6 @@ main (int argc, char *argv[]) p = guestfs_get_cachedir (g); printf ("guestfs_get_cachedir: %s\n", p ? : "(null)"); free (p); - printf ("guestfs_get_direct: %d\n", guestfs_get_direct (g)); p = guestfs_get_hv (g); printf ("guestfs_get_hv: %s\n", p); free (p); -- 2.9.3
Richard W.M. Jones
2017-Mar-04 15:11 UTC
[Libguestfs] [PATCH v3 2/7] New API: internal-get-console-socket to support virt-rescue.
This API intended for use by virt-rescue only gets the file descriptor of the console socket. --- generator/actions_core.ml | 11 +++++++ generator/actions_properties_deprecated.ml | 4 +-- lib/Makefile.am | 1 + lib/conn-socket.c | 16 +++++++++- lib/guestfs-internal.h | 3 ++ lib/rescue.c | 47 ++++++++++++++++++++++++++++++ 6 files changed, 79 insertions(+), 3 deletions(-) create mode 100644 lib/rescue.c diff --git a/generator/actions_core.ml b/generator/actions_core.ml index ed89f74..765a7fe 100644 --- a/generator/actions_core.ml +++ b/generator/actions_core.ml @@ -1722,6 +1722,17 @@ call it returns a simple true/false boolean result, instead of throwing an exception if a feature is not found. For other documentation see C<guestfs_available>." }; + { defaults with + name = "internal_get_console_socket"; added = (1, 37, 1); + style = RInt "fd", [], []; + visibility = VInternal; + test_excuse = "writing to the socket may block"; + shortdesc = "get the appliance console socket"; + longdesc = "\ +This call is used by L<virt-rescue(1)> to write directly to +appliance console (for passing through keystrokes). It should +not normally be used by other libguestfs users." }; + ] let daemon_functions = [ diff --git a/generator/actions_properties_deprecated.ml b/generator/actions_properties_deprecated.ml index 5327782..f36509e 100644 --- a/generator/actions_properties_deprecated.ml +++ b/generator/actions_properties_deprecated.ml @@ -128,7 +128,7 @@ See C<guestfs_set_backend> and L<guestfs(3)/BACKEND>." }; { defaults with name = "set_direct"; added = (1, 0, 72); style = RErr, [Bool "direct"], []; - deprecated_by = Deprecated_no_replacement; + deprecated_by = Replaced_by "internal_get_console_socket"; fish_alias = ["direct"]; config_only = true; blocking = false; shortdesc = "enable or disable direct appliance mode"; @@ -149,7 +149,7 @@ The default is disabled." }; { defaults with name = "get_direct"; added = (1, 0, 72); style = RBool "direct", [], []; - deprecated_by = Deprecated_no_replacement; + deprecated_by = Replaced_by "internal_get_console_socket"; blocking = false; shortdesc = "get direct appliance mode flag"; longdesc = "\ diff --git a/lib/Makefile.am b/lib/Makefile.am index e1ab1bf..774274b 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -116,6 +116,7 @@ libguestfs_la_SOURCES = \ private-data.c \ proto.c \ qemu.c \ + rescue.c \ stringsbuf.c \ structs-compare.c \ structs-copy.c \ diff --git a/lib/conn-socket.c b/lib/conn-socket.c index 2cd261a..8ecfed8 100644 --- a/lib/conn-socket.c +++ b/lib/conn-socket.c @@ -1,5 +1,5 @@ /* libguestfs - * Copyright (C) 2013 Red Hat Inc. + * Copyright (C) 2013-2017 Red Hat Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -397,6 +397,19 @@ handle_log_message (guestfs_h *g, return 1; } +static int +get_console_sock (guestfs_h *g, struct connection *connv) +{ + struct connection_socket *conn = (struct connection_socket *) connv; + + if (conn->console_sock == -1) { + error (g, _("console socket not connected")); + return -1; + } + + return conn->console_sock; +} + static void free_conn_socket (guestfs_h *g, struct connection *connv) { @@ -418,6 +431,7 @@ static struct connection_ops ops = { .read_data = read_data, .write_data = write_data, .can_read_data = can_read_data, + .get_console_sock = get_console_sock, }; /** diff --git a/lib/guestfs-internal.h b/lib/guestfs-internal.h index 7126b88..5e9d97c 100644 --- a/lib/guestfs-internal.h +++ b/lib/guestfs-internal.h @@ -373,6 +373,9 @@ struct connection_ops { * Returns: 1 = yes, 0 = no, -1 = error */ int (*can_read_data) (guestfs_h *g, struct connection *); + + /* Get the console socket (to support virt-rescue). */ + int (*get_console_sock) (guestfs_h *g, struct connection *); }; /** diff --git a/lib/rescue.c b/lib/rescue.c new file mode 100644 index 0000000..ae7811a --- /dev/null +++ b/lib/rescue.c @@ -0,0 +1,47 @@ +/* libguestfs + * Copyright (C) 2017 Red Hat Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * Support for virt-rescue(1). + */ + +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <libintl.h> + +#include "guestfs.h" +#include "guestfs-internal.h" +#include "guestfs-internal-actions.h" + +int +guestfs_impl_internal_get_console_socket (guestfs_h *g) +{ + if (!g->conn) { + error (g, _("no console socket, the handle must be launched")); + return -1; + } + + if (!g->conn->ops->get_console_sock) + NOT_SUPPORTED (g, -1, + _("connection class does not support getting the console socket")); + + return g->conn->ops->get_console_sock (g, g->conn); +} -- 2.9.3
Richard W.M. Jones
2017-Mar-04 15:11 UTC
[Libguestfs] [PATCH v3 3/7] appliance: Fix job control in virt-rescue.
See comment and link to busybox FAQ for explanation. --- appliance/init | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/appliance/init b/appliance/init index 8be27a2..bb8b709 100755 --- a/appliance/init +++ b/appliance/init @@ -177,6 +177,10 @@ if ! test "$guestfs_rescue" = 1; then else # Run virt-rescue shell. + # Get name of the serial port, from console= passed by libguestfs. + guestfs_serial=$(grep -Eo 'console=[^[:space:]]+' /proc/cmdline | + sed s/console=//) + # Remove LD_PRELOAD=libSegFault set above. unset LD_PRELOAD @@ -185,6 +189,16 @@ else echo "PS1='><rescue> '" >> $HOME/.bashrc echo "export TERM PS1" >> $HOME/.bashrc + # The shell is opened by default on /dev/console, which (on Linux) + # is not a controlling terminal, causing job control to fail. For + # how we work around this, see: + # https://busybox.net/FAQ.html#job_control + run_bash_with_ctty () + { + setsid bash -c \ + "exec bash </dev/$guestfs_serial >/dev/$guestfs_serial 2>&1" + } + echo echo "------------------------------------------------------------" echo @@ -194,7 +208,7 @@ else echo "You have to mount the guest's partitions under /sysroot" echo "before you can examine them." echo - bash -i + run_bash_with_ctty echo echo "virt-rescue: Syncing the disk now before exiting ..." echo -- 2.9.3
Richard W.M. Jones
2017-Mar-04 15:11 UTC
[Libguestfs] [PATCH v3 4/7] lib: Return EPIPE for "appliance closed the connection unexpectedly".
--- lib/errors.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/lib/errors.c b/lib/errors.c index c2af611..ace6a89 100644 --- a/lib/errors.c +++ b/lib/errors.c @@ -358,12 +358,15 @@ void guestfs_int_unexpected_close_error (guestfs_h *g) { if (g->verbose) - error (g, _("appliance closed the connection unexpectedly, see earlier error messages")); + guestfs_int_error_errno (g, EPIPE, + _("appliance closed the connection unexpectedly, " + "see earlier error messages")); else - error (g, _( - "appliance closed the connection unexpectedly.\n" - "This usually means the libguestfs appliance crashed.\n" - DEBUG_ADVICE)); + guestfs_int_error_errno (g, EPIPE, + _("appliance closed the connection unexpectedly.\n" + "This usually means the libguestfs appliance " + "crashed.\n" + DEBUG_ADVICE)); } /** -- 2.9.3
Richard W.M. Jones
2017-Mar-04 15:11 UTC
[Libguestfs] [PATCH v3 5/7] rescue: Modify virt-rescue so it doesn't use direct mode (RHBZ#1152819, RHBZ#1171654).
Instead of using "direct mode" (which was basically a quick hack), virt-rescue now launches the appliance with a running daemon. The daemon doesn't do much -- there is still a bash shell which the user interacts with. The daemon is there simply to provide the initial GUESTFS_LAUNCH_FLAG message and to handle shutdown a bit more gracefully. To interact with the shell, and replacing direct mode, virt-rescue now prints out log messages (the output of the shell), and sends input typed by the user directly to the console socket. This uses the guestfs_internal_get_console_socket API added previously. Most of the complexity behind this is hidden in virt-rescue. This fully fixes the handling of ^C (RHBZ#1152819). Also there were earlier reports that full screen commands like 'vim' didn't work well, (RHBZ#1171654), but in this version vim appears to work fine, albeit only using 80x24 of the screen because of the serial console. --- appliance/init | 101 +++++++++--------- rescue/Makefile.am | 1 + rescue/rescue.c | 306 +++++++++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 316 insertions(+), 92 deletions(-) diff --git a/appliance/init b/appliance/init index bb8b709..fa42c2b 100755 --- a/appliance/init +++ b/appliance/init @@ -159,59 +159,62 @@ if test "$guestfs_verbose" = 1 && test "$guestfs_boot_analysis" != 1; then echo -n "uptime: "; cat /proc/uptime fi -if ! test "$guestfs_rescue" = 1; then - # Run the daemon. - cmd="guestfsd" - eval `grep -Eo 'guestfs_channel=[^[:space:]]+' /proc/cmdline` - if test "x$guestfs_channel" != "x"; then +# Run the daemon. +cmd="guestfsd" +eval `grep -Eo 'guestfs_channel=[^[:space:]]+' /proc/cmdline` +if test "x$guestfs_channel" != "x"; then cmd="$cmd --channel $guestfs_channel" - fi - if test "$guestfs_verbose" = 1; then +fi +if test "$guestfs_verbose" = 1; then cmd="$cmd --verbose" - fi - if test "$guestfs_network" = 1; then +fi +if test "$guestfs_network" = 1; then cmd="$cmd --network" - fi - echo $cmd - $cmd +fi +if ! test "$guestfs_rescue" = 1; then + echo $cmd + $cmd else - # Run virt-rescue shell. - - # Get name of the serial port, from console= passed by libguestfs. - guestfs_serial=$(grep -Eo 'console=[^[:space:]]+' /proc/cmdline | - sed s/console=//) - - # Remove LD_PRELOAD=libSegFault set above. - unset LD_PRELOAD - - :> $HOME/.bashrc - grep -Eo 'TERM=[^[:space:]]+' /proc/cmdline >> $HOME/.bashrc - echo "PS1='><rescue> '" >> $HOME/.bashrc - echo "export TERM PS1" >> $HOME/.bashrc - - # The shell is opened by default on /dev/console, which (on Linux) - # is not a controlling terminal, causing job control to fail. For - # how we work around this, see: - # https://busybox.net/FAQ.html#job_control - run_bash_with_ctty () - { - setsid bash -c \ - "exec bash </dev/$guestfs_serial >/dev/$guestfs_serial 2>&1" - } - - echo - echo "------------------------------------------------------------" - echo - echo "Welcome to virt-rescue, the libguestfs rescue shell." - echo - echo "Note: The contents of / are the rescue appliance." - echo "You have to mount the guest's partitions under /sysroot" - echo "before you can examine them." - echo - run_bash_with_ctty - echo - echo "virt-rescue: Syncing the disk now before exiting ..." - echo + # Run virt-rescue shell. + + # We need a daemon, even in virt-rescue. + $cmd & + + # Get name of the serial port, from console= passed by libguestfs. + guestfs_serial=$(grep -Eo 'console=[^[:space:]]+' /proc/cmdline | + sed s/console=//) + + # Remove LD_PRELOAD=libSegFault set above. + unset LD_PRELOAD + + :> $HOME/.bashrc + grep -Eo 'TERM=[^[:space:]]+' /proc/cmdline >> $HOME/.bashrc + echo "PS1='><rescue> '" >> $HOME/.bashrc + echo "export TERM PS1" >> $HOME/.bashrc + + # The shell is opened by default on /dev/console, which (on Linux) + # is not a controlling terminal, causing job control to fail. For + # how we work around this, see: + # https://busybox.net/FAQ.html#job_control + run_bash_with_ctty () + { + setsid bash -c \ + "exec bash </dev/$guestfs_serial >/dev/$guestfs_serial 2>&1" + } + + echo + echo "------------------------------------------------------------" + echo + echo "Welcome to virt-rescue, the libguestfs rescue shell." + echo + echo "Note: The contents of / (root) are the rescue appliance." + echo "You have to mount the guest's partitions under /sysroot" + echo "before you can examine them." + echo + run_bash_with_ctty + echo + echo "virt-rescue: Syncing the disk now before exiting ..." + echo fi sync diff --git a/rescue/Makefile.am b/rescue/Makefile.am index 7919aaf..99d4b79 100644 --- a/rescue/Makefile.am +++ b/rescue/Makefile.am @@ -30,6 +30,7 @@ virt_rescue_SOURCES = \ virt_rescue_CPPFLAGS = \ -DGUESTFS_WARN_DEPRECATED=1 \ + -DGUESTFS_PRIVATE=1 \ -DLOCALEBASEDIR=\""$(datadir)/locale"\" \ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ -I$(top_srcdir)/lib -I$(top_builddir)/lib \ diff --git a/rescue/rescue.c b/rescue/rescue.c index 572a844..28a62ce 100644 --- a/rescue/rescue.c +++ b/rescue/rescue.c @@ -23,21 +23,32 @@ #include <string.h> #include <inttypes.h> #include <unistd.h> +#include <fcntl.h> #include <getopt.h> #include <errno.h> #include <error.h> +#include <signal.h> +#include <termios.h> +#include <poll.h> #include <locale.h> #include <assert.h> #include <libintl.h> +#include "full-write.h" +#include "getprogname.h" #include "ignore-value.h" #include "xvasprintf.h" -#include "getprogname.h" #include "guestfs.h" #include "options.h" #include "display-options.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); +static void restore_tty (void); +static void tstp_handler (int sig); +static void cont_handler (int sig); static void add_scratch_disks (int n, struct drv **drvs); static void do_suggestion (struct drv *drvs); @@ -54,6 +65,9 @@ int inspector = 0; int in_guestfish = 0; int in_virt_rescue = 1; +/* Old terminal settings. */ +static struct termios old_termios; + static void __attribute__((noreturn)) usage (int status) { @@ -135,6 +149,16 @@ main (int argc, char *argv[]) int memsize = 0; int smp = 0; int suggest = 0; + char *append_full; + int sock; + + /* Save the initial state of the tty so we always have the original + * state to go back to. + */ + if (tcgetattr (STDIN_FILENO, &old_termios) == -1) { + perror ("tcgetattr: stdin"); + exit (EXIT_FAILURE); + } g = guestfs_create (); if (g == NULL) @@ -295,30 +319,6 @@ main (int argc, char *argv[]) usage (EXIT_FAILURE); } -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" - /* Setting "direct mode" is required for the rescue appliance. */ - if (guestfs_set_direct (g, 1) == -1) - exit (EXIT_FAILURE); -#pragma GCC diagnostic pop - - { - /* The libvirt backend doesn't support direct mode. As a temporary - * workaround, force the appliance backend, but warn about it. - */ - CLEANUP_FREE char *backend = guestfs_get_backend (g); - if (backend) { - if (STREQ (backend, "libvirt") || - STRPREFIX (backend, "libvirt:")) { - fprintf (stderr, _("%s: warning: virt-rescue doesn't work with the libvirt backend\n" - "at the moment. As a workaround, forcing backend = 'direct'.\n"), - getprogname ()); - if (guestfs_set_backend (g, "direct") == -1) - exit (EXIT_FAILURE); - } - } - } - /* Set other features. */ if (memsize > 0) if (guestfs_set_memsize (g, memsize) == -1) @@ -330,16 +330,15 @@ main (int argc, char *argv[]) if (guestfs_set_smp (g, smp) == -1) exit (EXIT_FAILURE); - { - /* Kernel command line must include guestfs_rescue=1 (see - * appliance/init) as well as other options. - */ - CLEANUP_FREE char *append_full = xasprintf ("guestfs_rescue=1%s%s", - append ? " " : "", - append ? append : ""); - if (guestfs_set_append (g, append_full) == -1) - exit (EXIT_FAILURE); - } + /* Kernel command line must include guestfs_rescue=1 (see + * appliance/init) as well as other options. + */ + append_full = xasprintf ("guestfs_rescue=1%s%s", + append ? " " : "", + append ? append : ""); + if (guestfs_set_append (g, append_full) == -1) + exit (EXIT_FAILURE); + free (append_full); /* Add drives. */ add_drives (drvs, 'a'); @@ -347,20 +346,241 @@ main (int argc, char *argv[]) /* Free up data structures, no longer needed after this point. */ free_drives (drvs); - /* Run the appliance. This won't return until the user quits the - * appliance. + /* Add an event handler to print "log messages". These will be the + * output of the appliance console during launch and shutdown. + * After launch, we will read the console messages directly from the + * socket and they won't be passed through the event callback. */ - if (!verbose) - guestfs_set_error_handler (g, NULL, NULL); + if (guestfs_set_event_callback (g, log_message_callback, + GUESTFS_EVENT_APPLIANCE, 0, NULL) == -1) + exit (EXIT_FAILURE); - /* We expect launch to fail, so ignore the return value, and don't - * bother with explicit guestfs_shutdown either. + /* Run the appliance. */ + if (guestfs_launch (g) == -1) + exit (EXIT_FAILURE); + + sock = guestfs_internal_get_console_socket (g); + if (sock == -1) + exit (EXIT_FAILURE); + + /* Try to set all sockets to non-blocking. */ + if (fcntl (STDIN_FILENO, F_SETFL, O_NONBLOCK) == -1) + perror ("could not set stdin to non-blocking"); + if (fcntl (STDOUT_FILENO, F_SETFL, O_NONBLOCK) == -1) + perror ("could not set stdout to non-blocking"); + if (fcntl (sock, F_SETFL, O_NONBLOCK) == -1) + perror ("could not set console socket to non-blocking"); + + /* Put stdin in raw mode so that we can receive ^C and other + * special keys. */ - ignore_value (guestfs_launch (g)); + raw_tty (); + + /* Restore the tty settings when the process exits. */ + atexit (restore_tty); + + /* Catch tty stop and cont signals so we can cleanup. + * See https://www.gnu.org/software/libc/manual/html_node/Signaling-Yourself.html + */ + signal (SIGTSTP, tstp_handler); + signal (SIGCONT, cont_handler); + + do_rescue (sock); + + /* Shut down the appliance. */ + guestfs_push_error_handler (g, NULL, NULL); + if (guestfs_shutdown (g) == -1) { + const char *err; + + /* Ignore "appliance closed the connection unexpectedly" since + * this can happen if the user reboots the appliance. + */ + if (guestfs_last_errno (g) == EPIPE) + goto next; + /* Otherwise it's a real error. */ + err = guestfs_last_error (g); + fprintf (stderr, "libguestfs: error: %s\n", err); + exit (EXIT_FAILURE); + } + next: + guestfs_pop_error_handler (g); guestfs_close (g); - exit (EXIT_SUCCESS); + exit (EXIT_SUCCESS); /* implicitly calls restore_tty */ +} + +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) +{ + if (buf_len > 0) { + ignore_value (full_write (STDOUT_FILENO, buf, buf_len)); + } +} + +/* This is the main loop for virt-rescue. We read and write + * directly to the console socket. + */ +#define BUFSIZE 4096 +static char rbuf[BUFSIZE]; /* appliance -> local tty */ +static char wbuf[BUFSIZE]; /* local tty -> appliance */ + +static void +do_rescue (int sock) +{ + size_t rlen = 0; + size_t wlen = 0; + + while (sock >= 0 || rlen > 0) { + struct pollfd fds[3]; + nfds_t nfds = 2; + int r; + ssize_t n; + + fds[0].fd = STDIN_FILENO; + fds[0].events = 0; + if (BUFSIZE-wlen > 0) + fds[0].events = POLLIN; + fds[0].revents = 0; + + fds[1].fd = STDOUT_FILENO; + fds[1].events = 0; + if (rlen > 0) + fds[1].events |= POLLOUT; + fds[1].revents = 0; + + if (sock >= 0) { + fds[2].fd = sock; + fds[2].events = 0; + if (BUFSIZE-rlen > 0) + fds[2].events |= POLLIN; + if (wlen > 0) + fds[2].events |= POLLOUT; + fds[2].revents = 0; + nfds++; + } + + r = poll (fds, nfds, -1); + if (r == -1) { + if (errno == EINTR || errno == EAGAIN) + continue; + perror ("poll"); + return; + } + + /* Input from local tty. */ + if ((fds[0].revents & POLLIN) != 0) { + assert (BUFSIZE-wlen > 0); + n = read (STDIN_FILENO, wbuf+wlen, BUFSIZE-wlen); + if (n == -1) { + if (errno == EINTR || errno == EAGAIN) + continue; + perror ("read"); + return; + } + if (n == 0) { + /* We don't expect this to happen. Maybe the whole tty went away? + * Anyway, we should exit as soon as possible. + */ + return; + } + if (n > 0) + wlen += n; + } + + /* Log message from appliance. */ + if (nfds > 2 && (fds[2].revents & POLLIN) != 0) { + assert (BUFSIZE-rlen > 0); + n = read (sock, rbuf+rlen, BUFSIZE-rlen); + if (n == -1) { + if (errno == EINTR || errno == EAGAIN) + continue; + if (errno == ECONNRESET) + goto appliance_closed; + perror ("read"); + return; + } + if (n == 0) { + appliance_closed: + sock = -1; + /* Don't actually close the socket, because it's owned by + * the guestfs handle. + */ + continue; + } + if (n > 0) + rlen += n; + } + + /* Write log messages to local tty. */ + if ((fds[1].revents & POLLOUT) != 0) { + assert (rlen > 0); + n = write (STDOUT_FILENO, rbuf, rlen); + if (n == -1) { + perror ("write"); + continue; + } + rlen -= n; + memmove (rbuf, rbuf+n, rlen); + } + + /* Write commands to the appliance. */ + if (nfds > 2 && (fds[2].revents & POLLOUT) != 0) { + assert (wlen > 0); + n = write (sock, wbuf, wlen); + if (n == -1) { + perror ("write"); + continue; + } + wlen -= n; + memmove (wbuf, wbuf+n, wlen); + } + } +} + +/* Put the tty in raw mode. */ +static void +raw_tty (void) +{ + struct termios termios; + + if (tcgetattr (STDIN_FILENO, &termios) == -1) { + perror ("tcgetattr: stdin"); + exit (EXIT_FAILURE); + } + cfmakeraw (&termios); + if (tcsetattr (STDIN_FILENO, TCSANOW, &termios) == -1) { + perror ("tcsetattr: stdin"); + exit (EXIT_FAILURE); + } +} + +/* Restore the tty to (presumably) cooked mode as it was when + * the program was started. + */ +static void +restore_tty (void) +{ + tcsetattr (STDIN_FILENO, TCSANOW, &old_termios); +} + +/* When we get SIGTSTP, switch back to cooked mode. */ +static void +tstp_handler (int sig) +{ + restore_tty (); + signal (SIGTSTP, SIG_DFL); + raise (SIGTSTP); +} + +/* When we get SIGCONF, switch to raw mode. */ +static void +cont_handler (int sig) +{ + raw_tty (); } static void suggest_filesystems (void); -- 2.9.3
Richard W.M. Jones
2017-Mar-04 15:11 UTC
[Libguestfs] [PATCH v3 6/7] rescue: Implement -m and -i options.
`virt-rescue -a disk -i' does the right thing. `-m' was previously an alternate form of `--memsize'. By sniffing the parameter we can make `-m MB' continue to work, while also allowing `-m' to be used as a short form for the `--mount' option. This also removes most of the description of `--suggest' from the man page, since it is no longer needed. --- appliance/init | 12 +++++-- rescue/Makefile.am | 3 +- rescue/rescue.c | 87 +++++++++++++++++++++++++++++++++++++------------- rescue/virt-rescue.pod | 80 ++++++++++++++++++++++++++++------------------ 4 files changed, 126 insertions(+), 56 deletions(-) diff --git a/appliance/init b/appliance/init index fa42c2b..b951857 100755 --- a/appliance/init +++ b/appliance/init @@ -180,6 +180,10 @@ else # We need a daemon, even in virt-rescue. $cmd & + # XXX This gives a bit of time for virt-rescue to connect to the + # daemon and mount any filesystems. + sleep 2 + # Get name of the serial port, from console= passed by libguestfs. guestfs_serial=$(grep -Eo 'console=[^[:space:]]+' /proc/cmdline | sed s/console=//) @@ -208,8 +212,12 @@ else echo "Welcome to virt-rescue, the libguestfs rescue shell." echo echo "Note: The contents of / (root) are the rescue appliance." - echo "You have to mount the guest's partitions under /sysroot" - echo "before you can examine them." + if ! test -d "/sysroot/dev"; then + echo "You have to mount the guest's partitions under /sysroot" + echo "before you can examine them." + else + echo "Use 'cd /sysroot' or 'chroot /sysroot' to see guest filesystems." + fi echo run_bash_with_ctty echo diff --git a/rescue/Makefile.am b/rescue/Makefile.am index 99d4b79..c83c434 100644 --- a/rescue/Makefile.am +++ b/rescue/Makefile.am @@ -35,7 +35,7 @@ virt_rescue_CPPFLAGS = \ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ -I$(top_srcdir)/lib -I$(top_builddir)/lib \ -I$(top_srcdir)/common/options -I$(top_builddir)/common/options \ - -I$(top_srcdir)/fish \ + -I$(top_srcdir)/common/windows -I$(top_builddir)/common/windows \ -I$(srcdir)/../gnulib/lib -I../gnulib/lib virt_rescue_CFLAGS = \ @@ -43,6 +43,7 @@ virt_rescue_CFLAGS = \ $(LIBXML2_CFLAGS) virt_rescue_LDADD = \ + $(top_builddir)/common/windows/libwindows.la \ $(top_builddir)/common/options/liboptions.la \ $(top_builddir)/common/utils/libutils.la \ $(top_builddir)/lib/libguestfs.la \ diff --git a/rescue/rescue.c b/rescue/rescue.c index 28a62ce..7548607 100644 --- a/rescue/rescue.c +++ b/rescue/rescue.c @@ -23,7 +23,6 @@ #include <string.h> #include <inttypes.h> #include <unistd.h> -#include <fcntl.h> #include <getopt.h> #include <errno.h> #include <error.h> @@ -37,9 +36,11 @@ #include "full-write.h" #include "getprogname.h" #include "ignore-value.h" +#include "nonblocking.h" #include "xvasprintf.h" #include "guestfs.h" +#include "windows.h" #include "options.h" #include "display-options.h" @@ -87,7 +88,9 @@ usage (int status) " -d|--domain guest Add disks from libvirt guest\n" " --format[=raw|..] Force disk format for -a option\n" " --help Display brief help\n" - " -m|--memsize MB Set memory size in megabytes\n" + " -i|--inspector Automatically mount filesystems\n" + " -m|--mount dev[:mnt[:opts[:fstype]] Mount dev on mnt (if omitted, /)\n" + " --memsize MB Set memory size in megabytes\n" " --network Enable network\n" " -r|--ro Access read-only\n" " --scratch[=N] Add scratch disk(s)\n" @@ -116,7 +119,7 @@ main (int argc, char *argv[]) enum { HELP_OPTION = CHAR_MAX + 1 }; - static const char options[] = "a:c:d:m:rvVwx"; + static const char options[] = "a:c:d:im:rvVwx"; static const struct option long_options[] = { { "add", 1, 0, 'a' }, { "append", 1, 0, 0 }, @@ -124,8 +127,10 @@ main (int argc, char *argv[]) { "domain", 1, 0, 'd' }, { "format", 2, 0, 0 }, { "help", 0, 0, HELP_OPTION }, + { "inspector", 0, 0, 'i' }, { "long-options", 0, 0, 0 }, - { "memsize", 1, 0, 'm' }, + { "mount", 1, 0, 'm' }, + { "memsize", 1, 0, 0 }, { "network", 0, 0, 0 }, { "ro", 0, 0, 'r' }, { "rw", 0, 0, 'w' }, @@ -140,13 +145,16 @@ main (int argc, char *argv[]) }; struct drv *drvs = NULL; struct drv *drv; + struct mp *mps = NULL; + struct mp *mp; + char *p; const char *format = NULL; bool format_consumed = true; int c; int option_index; int network = 0; const char *append = NULL; - int memsize = 0; + int memsize = 0, m; int smp = 0; int suggest = 0; char *append_full; @@ -204,6 +212,10 @@ main (int argc, char *argv[]) _("--scratch parameter '%s' should be >= 1"), optarg); add_scratch_disks (n, &drvs); } + } else if (STREQ (long_options[option_index].name, "memsize")) { + if (sscanf (optarg, "%d", &memsize) != 1) + error (EXIT_FAILURE, 0, + _("could not parse memory size '%s'"), optarg); } else error (EXIT_FAILURE, 0, _("unknown long option: %s (%d)"), @@ -222,10 +234,19 @@ main (int argc, char *argv[]) OPTION_d; break; + case 'i': + OPTION_i; + break; + case 'm': - if (sscanf (optarg, "%d", &memsize) != 1) - error (EXIT_FAILURE, 0, - _("could not parse memory size '%s'"), optarg); + /* For backwards compatibility with virt-rescue <= 1.36, we + * must handle -m <number> as a synonym for --memsize. + */ + if (sscanf (optarg, "%d", &m) == 1) + memsize = m; + else { + OPTION_m; + } break; case 'r': @@ -296,7 +317,6 @@ main (int argc, char *argv[]) * options parsing code. Assert here that they have known-good * values. */ - assert (inspector == 0); assert (keys_from_stdin == 0); assert (echo_keys == 0); assert (live == 0); @@ -340,12 +360,6 @@ main (int argc, char *argv[]) exit (EXIT_FAILURE); free (append_full); - /* Add drives. */ - add_drives (drvs, 'a'); - - /* Free up data structures, no longer needed after this point. */ - free_drives (drvs); - /* Add an event handler to print "log messages". These will be the * output of the appliance console during launch and shutdown. * After launch, we will read the console messages directly from the @@ -355,21 +369,50 @@ main (int argc, char *argv[]) GUESTFS_EVENT_APPLIANCE, 0, NULL) == -1) exit (EXIT_FAILURE); - /* Run the appliance. */ + /* Do the guest drives and mountpoints. */ + add_drives (drvs, 'a'); if (guestfs_launch (g) == -1) exit (EXIT_FAILURE); + if (inspector) + inspect_mount (); + mount_mps (mps); + + free_drives (drvs); + free_mps (mps); + + /* Also bind-mount /dev etc under /sysroot, if -i was given. */ + if (inspector) { + CLEANUP_FREE_STRING_LIST char **roots; + int windows; + + roots = guestfs_inspect_get_roots (g); + windows = roots && roots[0] && is_windows (g, roots[0]); + if (!windows) { + const char *cmd[5] = { "mount", "--rbind", NULL, NULL, NULL }; + char *r; + + cmd[2] = "/dev"; cmd[3] = "/sysroot/dev"; + r = guestfs_debug (g, "sh", (char **) cmd); + free (r); + + cmd[2] = "/proc"; cmd[3] = "/sysroot/proc"; + r = guestfs_debug (g, "sh", (char **) cmd); + free (r); + + cmd[2] = "/sys"; cmd[3] = "/sysroot/sys"; + r = guestfs_debug (g, "sh", (char **) cmd); + free (r); + } + } sock = guestfs_internal_get_console_socket (g); if (sock == -1) exit (EXIT_FAILURE); /* Try to set all sockets to non-blocking. */ - if (fcntl (STDIN_FILENO, F_SETFL, O_NONBLOCK) == -1) - perror ("could not set stdin to non-blocking"); - if (fcntl (STDOUT_FILENO, F_SETFL, O_NONBLOCK) == -1) - perror ("could not set stdout to non-blocking"); - if (fcntl (sock, F_SETFL, O_NONBLOCK) == -1) - perror ("could not set console socket to non-blocking"); + ignore_value (set_nonblocking_flag (STDIN_FILENO, 1)); + ignore_value (set_nonblocking_flag (STDOUT_FILENO, 1)); + ignore_value (set_nonblocking_flag (sock, 1)); /* Put stdin in raw mode so that we can receive ^C and other * special keys. diff --git a/rescue/virt-rescue.pod b/rescue/virt-rescue.pod index b8aa326..b651f84 100644 --- a/rescue/virt-rescue.pod +++ b/rescue/virt-rescue.pod @@ -6,9 +6,7 @@ virt-rescue - Run a rescue shell on a virtual machine virt-rescue [--options] -d domname - virt-rescue [--options] -a disk.img [-a disk.img ...] - - virt-rescue --suggest (-d domname | -a disk.img ...) + virt-rescue [--options] -a disk.img [-a disk.img ...] [-i] Old style: @@ -26,13 +24,13 @@ machine or disk image. You can run virt-rescue on any virtual machine known to libvirt, or directly on disk image(s): - virt-rescue -d GuestName + virt-rescue -d GuestName -i - virt-rescue --ro -a /path/to/disk.img + virt-rescue --ro -a /path/to/disk.img -i virt-rescue -a /dev/sdc -For live VMs you I<must> use the --ro option. +For live VMs you I<must> use the I<--ro> option. When you run virt-rescue on a virtual machine or disk image, you are placed in an interactive bash shell where you can use many ordinary @@ -41,26 +39,10 @@ rescue appliance. You must mount the virtual machine's filesystems by hand. There is an empty directory called F</sysroot> where you can mount filesystems. -You can get virt-rescue to suggest mount commands for you by using the -I<--suggest> option (in another terminal): - - $ virt-rescue --suggest -d Fedora15 - Inspecting the virtual machine or disk image ... - - This disk contains one or more operating systems. You can use these - mount commands in virt-rescue (at the ><rescue> prompt) to mount the - filesystems. - - # /dev/vg_f15x32/lv_root is the root of a linux operating system - # type: linux, distro: fedora, version: 15.0 - # Fedora release 15 (Lovelock) - - mount /dev/vg_f15x32/lv_root /sysroot/ - mount /dev/vda1 /sysroot/boot - mount --bind /dev /sysroot/dev - mount --bind /dev/pts /sysroot/dev/pts - mount --bind /proc /sysroot/proc - mount --bind /sys /sysroot/sys +To automatically mount the virtual machine's filesystems under +F</sysroot> use the I<-i> option. This uses libguestfs inspection to +find the filesystems and mount them in the right place. You can also +mount filesystems individually using the I<-m> option. Another way is to list the logical volumes (with L<lvs(8)>) and partitions (with L<parted(8)>) and mount them by hand: @@ -170,7 +152,15 @@ If you have untrusted raw-format guest disk images, you should use this option to specify the disk format. This avoids a possible security problem with malicious guests (CVE-2010-3851). -=item B<-m> MB +=item B<-i> + +=item B<--inspector> + +Using L<virt-inspector(1)> code, inspect the disks looking for +an operating system and mount filesystems as they would be +mounted on the real virtual machine. + +The filesystems are mounted on F</sysroot> in the rescue environment. =item B<--memsize> MB @@ -179,6 +169,33 @@ default is set by libguestfs and is small but adequate for running system tools. The occasional program might need more memory. The parameter is specified in megabytes. +=item B<-m> dev[:mountpoint[:options[:fstype]]] + +=item B<--mount> dev[:mountpoint[:options[:fstype]]] + +Mount the named partition or logical volume on the given mountpoint +B<in the guest> (this has nothing to do with mountpoints in the host). + +If the mountpoint is omitted, it defaults to F</>. You have to mount +something on F</>. + +The filesystems are mounted under F</sysroot> in the rescue environment. + +The third (and rarely used) part of the mount parameter is the list of +mount options used to mount the underlying filesystem. If this is not +given, then the mount options are either the empty string or C<ro> +(the latter if the I<--ro> flag is used). By specifying the mount +options, you override this default choice. Probably the only time you +would use this is to enable ACLs and/or extended attributes if the +filesystem can support them: + + -m /dev/sda1:/:acl,user_xattr + +The fourth part of the parameter is the filesystem driver to use, such +as C<ext3> or C<ntfs>. This is rarely needed, but can be useful if +multiple drivers are valid for a filesystem (eg: C<ext2> and C<ext3>), +or if libguestfs misidentifies a filesystem. + =item B<--network> Enable QEMU user networking in the guest. See L</NETWORK>. @@ -217,9 +234,10 @@ Enable N E<ge> 2 virtual CPUs in the rescue appliance. =item B<--suggest> -Inspect the disk image and suggest what mount commands should be used -to mount the disks. You should use the I<--suggest> option in a -second terminal, then paste the commands into another virt-rescue. +This option was used in older versions of virt-rescue to suggest what +commands you could use to mount filesystems under F</sysroot>. For +the current version of virt-rescue, it is easier to use the I<-i> +option instead. This option implies I<--ro> and is safe to use even if the guest is up or if another virt-rescue is running. @@ -240,7 +258,7 @@ Display version number and exit. =item B<--rw> -This changes the I<-a> and I<-d> options so that disks are +This changes the I<-a>, I<-d> and I<-m> options so that disks are added and mounts are done read-write. See L<guestfish(1)/OPENING DISKS FOR READ AND WRITE>. -- 2.9.3
Richard W.M. Jones
2017-Mar-04 15:11 UTC
[Libguestfs] [PATCH v3 7/7] 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 ^]s - sync the filesystems ^]u - unmount filesystems ^]x - quit virt-rescue ^]z - suspend virt-rescue to send 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 ^]u unmounting filesystems ... [ 21.158558] XFS (sda3): Unmounting Filesystem --- rescue/Makefile.am | 4 +- rescue/escape.c | 278 +++++++++++++++++++++++++++++++++++++++++++++++++ rescue/rescue.c | 29 +++++- rescue/rescue.h | 47 +++++++++ rescue/virt-rescue.pod | 74 +++++++++++++ 5 files changed, 429 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 c83c434..eb60baf 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 0000000..00e7d5a --- /dev/null +++ b/rescue/escape.c @@ -0,0 +1,278 @@ +/* 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 <config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <stdbool.h> +#include <string.h> +#include <unistd.h> +#include <signal.h> +#include <locale.h> +#include <libintl.h> + +#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)) + + 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_help (); + break; + + case 'i': + print_inspector (); + break; + + case 'q': case 'x': + return true /* exit virt-rescue at once */; + + case 's': + crlf (); + printf (_("attempting to sync filesystems ...")); + crlf (); + guestfs_sync (g); + break; + + case 'u': + crlf (); + printf (_("unmounting filesystems ...")); + crlf (); + guestfs_umount_all (g); + break; + + case 'z': + 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) +{ + crlf (); + printf (_("virt-rescue escape sequences:")); crlf (); + print_escape_key (); + printf (_("? - print this message")); + crlf (); + print_escape_key (); + printf (_("h - print this message")); + crlf (); + if (inspector) { + print_escape_key (); + printf (_("i - print inspection data")); + crlf (); + } + print_escape_key (); + printf (_("q - quit virt-rescue")); + crlf (); + print_escape_key (); + printf (_("s - sync the filesystems")); + crlf (); + print_escape_key (); + printf (_("u - unmount filesystems")); + crlf (); + print_escape_key (); + printf (_("x - quit virt-rescue")); + crlf (); + print_escape_key (); + printf (_("z - suspend virt-rescue")); + crlf (); + printf (_("to send 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 7548607..a0a7de2 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; @@ -119,7 +124,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 }, @@ -234,6 +239,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 +439,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); /* Shut down the appliance. */ @@ -476,6 +491,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]; @@ -532,6 +550,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 0000000..ccffb5e --- /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 <stdbool.h> + +#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 b651f84..bd6f954 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,57 @@ 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<^] 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<not> -- 2.9.3