diff --git a/0001-generator-Deprecate-direct-mode-guestfs_set_direct-g.patch b/0001-generator-Deprecate-direct-mode-guestfs_set_direct-g.patch new file mode 100644 index 0000000..2849dc8 --- /dev/null +++ b/0001-generator-Deprecate-direct-mode-guestfs_set_direct-g.patch @@ -0,0 +1,126 @@ +From 29ae313bc407d60449ba84808f98ec846eb3dc39 Mon Sep 17 00:00:00 2001 +From: "Richard W.M. Jones" +Date: Thu, 2 Mar 2017 12:42:46 +0000 +Subject: [PATCH 01/10] generator: Deprecate direct mode (guestfs_set_direct, + guestfs_get_direct). + +(cherry picked from commit 26948d5cb17391a32856b18b8a5d6ae58a179507) +--- + 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)." }; + 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, +-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 and L." }; + ++ { 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, ++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 + diff --git a/0002-New-API-internal-get-console-socket-to-support-virt-.patch b/0002-New-API-internal-get-console-socket-to-support-virt-.patch new file mode 100644 index 0000000..f1ddf45 --- /dev/null +++ b/0002-New-API-internal-get-console-socket-to-support-virt-.patch @@ -0,0 +1,185 @@ +From 7e7ba9a06c729bed1e782641488e64a2cb805cec Mon Sep 17 00:00:00 2001 +From: "Richard W.M. Jones" +Date: Thu, 2 Mar 2017 11:06:27 +0000 +Subject: [PATCH 02/10] 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. + +(cherry picked from commit 84c9f98c2e09a459ced3c7e85191f2e47d149a52) +--- + 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." }; + ++ { 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 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 and L." }; + { 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 ++ ++#include ++#include ++#include ++#include ++ ++#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 + diff --git a/0003-lib-Return-EPIPE-for-appliance-closed-the-connection.patch b/0003-lib-Return-EPIPE-for-appliance-closed-the-connection.patch new file mode 100644 index 0000000..1fc86de --- /dev/null +++ b/0003-lib-Return-EPIPE-for-appliance-closed-the-connection.patch @@ -0,0 +1,39 @@ +From 4ddc1bbeb283e145055deefdd2d8b9a0cb06cd89 Mon Sep 17 00:00:00 2001 +From: "Richard W.M. Jones" +Date: Fri, 3 Mar 2017 10:14:20 +0000 +Subject: [PATCH 03/10] lib: Return EPIPE for "appliance closed the connection + unexpectedly". + +(cherry picked from commit 8af9acd4e31e9880e14735b2242c496ee017c0d9) +--- + 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 + diff --git a/0004-rescue-Modify-virt-rescue-so-it-doesn-t-use-direct-m.patch b/0004-rescue-Modify-virt-rescue-so-it-doesn-t-use-direct-m.patch new file mode 100644 index 0000000..ac28d30 --- /dev/null +++ b/0004-rescue-Modify-virt-rescue-so-it-doesn-t-use-direct-m.patch @@ -0,0 +1,533 @@ +From c3f60523655194eddc31c0e56765bd8f0a04d463 Mon Sep 17 00:00:00 2001 +From: "Richard W.M. Jones" +Date: Thu, 2 Mar 2017 13:46:48 +0000 +Subject: [PATCH 04/10] 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. + +(cherry picked from commit 32d60801443647b3523b9374c431fefdbf054e3c) +--- + appliance/init | 91 ++++++++-------- + rescue/Makefile.am | 1 + + rescue/rescue.c | 306 +++++++++++++++++++++++++++++++++++++++++++++-------- + 3 files changed, 312 insertions(+), 86 deletions(-) + +diff --git a/appliance/init b/appliance/init +index 8a26e1a..810f84d 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. ++ # 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=//) ++ # We need a daemon, even in virt-rescue. ++ $cmd & + +- # Remove LD_PRELOAD=libSegFault set above. +- unset LD_PRELOAD ++ # Get name of the serial port, from console= passed by libguestfs. ++ guestfs_serial=$(grep -Eo 'console=[^[:space:]]+' /proc/cmdline | ++ sed s/console=//) + +- :> $HOME/.bashrc +- grep -Eo 'TERM=[^[:space:]]+' /proc/cmdline >> $HOME/.bashrc +- echo "PS1='> '" >> $HOME/.bashrc +- echo "export TERM PS1" >> $HOME/.bashrc ++ # Remove LD_PRELOAD=libSegFault set above. ++ unset LD_PRELOAD + +- # 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 2>&1" +- } ++ :> $HOME/.bashrc ++ grep -Eo 'TERM=[^[:space:]]+' /proc/cmdline >> $HOME/.bashrc ++ echo "PS1='> '" >> $HOME/.bashrc ++ echo "export TERM PS1" >> $HOME/.bashrc + +- 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 ++ # 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 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..43f41f9 100644 +--- a/rescue/rescue.c ++++ b/rescue/rescue.c +@@ -23,21 +23,32 @@ + #include + #include + #include ++#include + #include + #include + #include ++#include ++#include ++#include + #include + #include + #include + ++#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,8 @@ main (int argc, char *argv[]) + int memsize = 0; + int smp = 0; + int suggest = 0; ++ char *append_full; ++ int sock; + + g = guestfs_create (); + if (g == NULL) +@@ -295,30 +311,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 +322,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,22 +338,253 @@ 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"); ++ ++ /* 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); ++ } ++ ++ /* Put stdin in raw mode so that we can receive ^C and other ++ * special keys. ++ */ ++ 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 + */ +- ignore_value (guestfs_launch (g)); ++ signal (SIGTSTP, tstp_handler); ++ signal (SIGCONT, cont_handler); ++ ++ do_rescue (sock); ++ ++ restore_tty (); ++ ++ /* 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); + } + ++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); + + static int +-- +2.9.3 + diff --git a/0005-rescue-Implement-m-and-i-options.patch b/0005-rescue-Implement-m-and-i-options.patch new file mode 100644 index 0000000..220f903 --- /dev/null +++ b/0005-rescue-Implement-m-and-i-options.patch @@ -0,0 +1,400 @@ +From 8825bc7b3d43dd00ca805516fdc4da59be2a39dd Mon Sep 17 00:00:00 2001 +From: "Richard W.M. Jones" +Date: Fri, 3 Mar 2017 13:52:31 +0000 +Subject: [PATCH 05/10] 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. + +(cherry picked from commit 33d2ae796119ae5dd38e2afcbf1ba4216bd99846) +--- + 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 810f84d..968429c 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 43f41f9..9f5c8b8 100644 +--- a/rescue/rescue.c ++++ b/rescue/rescue.c +@@ -23,7 +23,6 @@ + #include + #include + #include +-#include + #include + #include + #include +@@ -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; +@@ -196,6 +204,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)"), +@@ -214,10 +226,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 as a synonym for --memsize. ++ */ ++ if (sscanf (optarg, "%d", &m) == 1) ++ memsize = m; ++ else { ++ OPTION_m; ++ } + break; + + case 'r': +@@ -288,7 +309,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); +@@ -332,12 +352,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 +@@ -347,21 +361,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)); + + /* Save the initial state of the tty so we always have the original + * state to go back to. +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 use the --ro option. ++For live VMs you I 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 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 > 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 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) and + partitions (with L) 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 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 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 (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 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 ++(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 or C. This is rarely needed, but can be useful if ++multiple drivers are valid for a filesystem (eg: C and C), ++or if libguestfs misidentifies a filesystem. ++ + =item B<--network> + + Enable QEMU user networking in the guest. See L. +@@ -217,9 +234,10 @@ Enable N E 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. 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. +-- +2.9.3 + diff --git a/0006-rescue-Implement-escape-sequences.patch b/0006-rescue-Implement-escape-sequences.patch new file mode 100644 index 0000000..20e5d8f --- /dev/null +++ b/0006-rescue-Implement-escape-sequences.patch @@ -0,0 +1,615 @@ +From 43fce2182988513cb74a122431b74af15b856bb5 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 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..f7f7d84 +--- /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 9f5c8b8..44f7e61 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 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 ++ ++#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 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.9.3 + diff --git a/0007-rescue-Move-suggest-code-to-separate-file.patch b/0007-rescue-Move-suggest-code-to-separate-file.patch new file mode 100644 index 0000000..0b4a739 --- /dev/null +++ b/0007-rescue-Move-suggest-code-to-separate-file.patch @@ -0,0 +1,397 @@ +From 521550ef7febe442f534cfc86c4a373126f3e98e Mon Sep 17 00:00:00 2001 +From: "Richard W.M. Jones" +Date: Sat, 4 Mar 2017 15:35:09 +0000 +Subject: [PATCH 07/10] rescue: Move --suggest code to separate file. + +Just code motion. + +(cherry picked from commit 5ea17e97e4413c3db4449ded72b9677cce09444f) +--- + rescue/Makefile.am | 3 +- + rescue/rescue.c | 144 ------------------------------------------- + rescue/rescue.h | 5 ++ + rescue/suggest.c | 175 +++++++++++++++++++++++++++++++++++++++++++++++++++++ + 4 files changed, 182 insertions(+), 145 deletions(-) + create mode 100644 rescue/suggest.c + +diff --git a/rescue/Makefile.am b/rescue/Makefile.am +index eb60baf..d478c8e 100644 +--- a/rescue/Makefile.am ++++ b/rescue/Makefile.am +@@ -28,7 +28,8 @@ bin_PROGRAMS = virt-rescue + virt_rescue_SOURCES = \ + escape.c \ + rescue.c \ +- rescue.h ++ rescue.h \ ++ suggest.c + + virt_rescue_CPPFLAGS = \ + -DGUESTFS_WARN_DEPRECATED=1 \ +diff --git a/rescue/rescue.c b/rescue/rescue.c +index 44f7e61..6ef4607 100644 +--- a/rescue/rescue.c ++++ b/rescue/rescue.c +@@ -55,7 +55,6 @@ 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); + + /* Currently open libguestfs handle. */ + guestfs_h *g; +@@ -654,149 +653,6 @@ cont_handler (int sig) + raw_tty (); + } + +-static void suggest_filesystems (void); +- +-static int +-compare_keys_len (const void *p1, const void *p2) +-{ +- const char *key1 = * (char * const *) p1; +- const char *key2 = * (char * const *) p2; +- return strlen (key1) - strlen (key2); +-} +- +-/* virt-rescue --suggest flag does a kind of inspection on the +- * drives and suggests mount commands that you should use. +- */ +-static void +-do_suggestion (struct drv *drvs) +-{ +- CLEANUP_FREE_STRING_LIST char **roots = NULL; +- size_t i; +- +- /* For inspection, force add_drives to add the drives read-only. */ +- read_only = 1; +- +- /* Add drives. */ +- add_drives (drvs, 'a'); +- +- /* Free up data structures, no longer needed after this point. */ +- free_drives (drvs); +- +- printf (_("Inspecting the virtual machine or disk image ...\n\n")); +- fflush (stdout); +- +- if (guestfs_launch (g) == -1) +- exit (EXIT_FAILURE); +- +- /* Don't use inspect_mount, since for virt-rescue we should allow +- * arbitrary disks and disks with more than one OS on them. Let's +- * do this using the basic API instead. +- */ +- roots = guestfs_inspect_os (g); +- if (roots == NULL) +- exit (EXIT_FAILURE); +- +- if (roots[0] == NULL) { +- suggest_filesystems (); +- return; +- } +- +- printf (_("This disk contains one or more operating systems. You can use these mount\n" +- "commands in virt-rescue (at the > prompt) to mount the filesystems.\n\n")); +- +- for (i = 0; roots[i] != NULL; ++i) { +- CLEANUP_FREE_STRING_LIST char **mps = NULL; +- CLEANUP_FREE char *type = NULL, *distro = NULL, *product_name = NULL; +- int major, minor; +- size_t j; +- +- type = guestfs_inspect_get_type (g, roots[i]); +- distro = guestfs_inspect_get_distro (g, roots[i]); +- product_name = guestfs_inspect_get_product_name (g, roots[i]); +- major = guestfs_inspect_get_major_version (g, roots[i]); +- minor = guestfs_inspect_get_minor_version (g, roots[i]); +- +- printf (_("# %s is the root of a %s operating system\n" +- "# type: %s, distro: %s, version: %d.%d\n" +- "# %s\n\n"), +- roots[i], type ? : "unknown", +- type ? : "unknown", distro ? : "unknown", major, minor, +- product_name ? : ""); +- +- mps = guestfs_inspect_get_mountpoints (g, roots[i]); +- if (mps == NULL) +- exit (EXIT_FAILURE); +- +- /* Sort by key length, shortest key first, so that we end up +- * mounting the filesystems in the correct order. +- */ +- qsort (mps, guestfs_int_count_strings (mps) / 2, 2 * sizeof (char *), +- compare_keys_len); +- +- for (j = 0; mps[j] != NULL; j += 2) +- printf ("mount %s /sysroot%s\n", mps[j+1], mps[j]); +- +- /* If it's Linux, print the bind-mounts and a chroot command. */ +- if (type && STREQ (type, "linux")) { +- printf ("mount --rbind /dev /sysroot/dev\n"); +- printf ("mount --rbind /proc /sysroot/proc\n"); +- printf ("mount --rbind /sys /sysroot/sys\n"); +- printf ("\n"); +- printf ("cd /sysroot\n"); +- printf ("chroot /sysroot\n"); +- } +- +- printf ("\n"); +- } +-} +- +-/* Inspection failed, so it doesn't contain any OS that we recognise. +- * However there might still be filesystems so print some suggestions +- * for those. +- */ +-static void +-suggest_filesystems (void) +-{ +- size_t i, count; +- +- CLEANUP_FREE_STRING_LIST char **fses = guestfs_list_filesystems (g); +- if (fses == NULL) +- exit (EXIT_FAILURE); +- +- /* Count how many are not swap or unknown. Possibly we should try +- * mounting to see which are mountable, but that has a high +- * probability of breaking. +- */ +-#define TEST_MOUNTABLE(fs) STRNEQ ((fs), "swap") && STRNEQ ((fs), "unknown") +- count = 0; +- for (i = 0; fses[i] != NULL; i += 2) { +- if (TEST_MOUNTABLE (fses[i+1])) +- count++; +- } +- +- if (count == 0) { +- printf (_("This disk contains no mountable filesystems that we recognize.\n\n" +- "However you can still use virt-rescue on the disk image, to try to mount\n" +- "filesystems that are not recognized by libguestfs, or to create partitions,\n" +- "logical volumes and filesystems on a blank disk.\n")); +- return; +- } +- +- printf (_("This disk contains one or more filesystems, but we don't recognize any\n" +- "operating system. You can use these mount commands in virt-rescue (at the\n" +- "> prompt) to mount these filesystems.\n\n")); +- +- for (i = 0; fses[i] != NULL; i += 2) { +- printf (_("# %s has type '%s'\n"), fses[i], fses[i+1]); +- +- if (TEST_MOUNTABLE (fses[i+1])) +- printf ("mount %s /sysroot\n", fses[i]); +- +- printf ("\n"); +- } +-#undef TEST_MOUNTABLE +-} +- + static void add_scratch_disk (struct drv **drvs); + + static void +diff --git a/rescue/rescue.h b/rescue/rescue.h +index ccffb5e..4f5a04a 100644 +--- a/rescue/rescue.h ++++ b/rescue/rescue.h +@@ -23,6 +23,8 @@ + + #include "guestfs.h" + ++#include "options.h" ++ + extern guestfs_h *g; + extern int read_only; + extern int live; +@@ -44,4 +46,7 @@ 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); + ++/* suggest.c */ ++extern void do_suggestion (struct drv *drvs); ++ + #endif /* RESCUE_H */ +diff --git a/rescue/suggest.c b/rescue/suggest.c +new file mode 100644 +index 0000000..07c9f47 +--- /dev/null ++++ b/rescue/suggest.c +@@ -0,0 +1,175 @@ ++/* 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 "guestfs.h" ++#include "guestfs-internal-frontend.h" ++ ++#include "options.h" ++ ++#include "rescue.h" ++ ++static void suggest_filesystems (void); ++ ++static int ++compare_keys_len (const void *p1, const void *p2) ++{ ++ const char *key1 = * (char * const *) p1; ++ const char *key2 = * (char * const *) p2; ++ return strlen (key1) - strlen (key2); ++} ++ ++/* virt-rescue --suggest flag does a kind of inspection on the ++ * drives and suggests mount commands that you should use. ++ */ ++void ++do_suggestion (struct drv *drvs) ++{ ++ CLEANUP_FREE_STRING_LIST char **roots = NULL; ++ size_t i; ++ ++ /* For inspection, force add_drives to add the drives read-only. */ ++ read_only = 1; ++ ++ /* Add drives. */ ++ add_drives (drvs, 'a'); ++ ++ /* Free up data structures, no longer needed after this point. */ ++ free_drives (drvs); ++ ++ printf (_("Inspecting the virtual machine or disk image ...\n\n")); ++ fflush (stdout); ++ ++ if (guestfs_launch (g) == -1) ++ exit (EXIT_FAILURE); ++ ++ /* Don't use inspect_mount, since for virt-rescue we should allow ++ * arbitrary disks and disks with more than one OS on them. Let's ++ * do this using the basic API instead. ++ */ ++ roots = guestfs_inspect_os (g); ++ if (roots == NULL) ++ exit (EXIT_FAILURE); ++ ++ if (roots[0] == NULL) { ++ suggest_filesystems (); ++ return; ++ } ++ ++ printf (_("This disk contains one or more operating systems. You can use these mount\n" ++ "commands in virt-rescue (at the > prompt) to mount the filesystems.\n\n")); ++ ++ for (i = 0; roots[i] != NULL; ++i) { ++ CLEANUP_FREE_STRING_LIST char **mps = NULL; ++ CLEANUP_FREE char *type = NULL, *distro = NULL, *product_name = NULL; ++ int major, minor; ++ size_t j; ++ ++ type = guestfs_inspect_get_type (g, roots[i]); ++ distro = guestfs_inspect_get_distro (g, roots[i]); ++ product_name = guestfs_inspect_get_product_name (g, roots[i]); ++ major = guestfs_inspect_get_major_version (g, roots[i]); ++ minor = guestfs_inspect_get_minor_version (g, roots[i]); ++ ++ printf (_("# %s is the root of a %s operating system\n" ++ "# type: %s, distro: %s, version: %d.%d\n" ++ "# %s\n\n"), ++ roots[i], type ? : "unknown", ++ type ? : "unknown", distro ? : "unknown", major, minor, ++ product_name ? : ""); ++ ++ mps = guestfs_inspect_get_mountpoints (g, roots[i]); ++ if (mps == NULL) ++ exit (EXIT_FAILURE); ++ ++ /* Sort by key length, shortest key first, so that we end up ++ * mounting the filesystems in the correct order. ++ */ ++ qsort (mps, guestfs_int_count_strings (mps) / 2, 2 * sizeof (char *), ++ compare_keys_len); ++ ++ for (j = 0; mps[j] != NULL; j += 2) ++ printf ("mount %s /sysroot%s\n", mps[j+1], mps[j]); ++ ++ /* If it's Linux, print the bind-mounts and a chroot command. */ ++ if (type && STREQ (type, "linux")) { ++ printf ("mount --rbind /dev /sysroot/dev\n"); ++ printf ("mount --rbind /proc /sysroot/proc\n"); ++ printf ("mount --rbind /sys /sysroot/sys\n"); ++ printf ("\n"); ++ printf ("cd /sysroot\n"); ++ printf ("chroot /sysroot\n"); ++ } ++ ++ printf ("\n"); ++ } ++} ++ ++/* Inspection failed, so it doesn't contain any OS that we recognise. ++ * However there might still be filesystems so print some suggestions ++ * for those. ++ */ ++static void ++suggest_filesystems (void) ++{ ++ size_t i, count; ++ ++ CLEANUP_FREE_STRING_LIST char **fses = guestfs_list_filesystems (g); ++ if (fses == NULL) ++ exit (EXIT_FAILURE); ++ ++ /* Count how many are not swap or unknown. Possibly we should try ++ * mounting to see which are mountable, but that has a high ++ * probability of breaking. ++ */ ++#define TEST_MOUNTABLE(fs) STRNEQ ((fs), "swap") && STRNEQ ((fs), "unknown") ++ count = 0; ++ for (i = 0; fses[i] != NULL; i += 2) { ++ if (TEST_MOUNTABLE (fses[i+1])) ++ count++; ++ } ++ ++ if (count == 0) { ++ printf (_("This disk contains no mountable filesystems that we recognize.\n\n" ++ "However you can still use virt-rescue on the disk image, to try to mount\n" ++ "filesystems that are not recognized by libguestfs, or to create partitions,\n" ++ "logical volumes and filesystems on a blank disk.\n")); ++ return; ++ } ++ ++ printf (_("This disk contains one or more filesystems, but we don't recognize any\n" ++ "operating system. You can use these mount commands in virt-rescue (at the\n" ++ "> prompt) to mount these filesystems.\n\n")); ++ ++ for (i = 0; fses[i] != NULL; i += 2) { ++ printf (_("# %s has type '%s'\n"), fses[i], fses[i+1]); ++ ++ if (TEST_MOUNTABLE (fses[i+1])) ++ printf ("mount %s /sysroot\n", fses[i]); ++ ++ printf ("\n"); ++ } ++#undef TEST_MOUNTABLE ++} +-- +2.9.3 + diff --git a/0008-rescue-docs-It-is-no-longer-necessary-to-mount-files.patch b/0008-rescue-docs-It-is-no-longer-necessary-to-mount-files.patch new file mode 100644 index 0000000..d6b55a7 --- /dev/null +++ b/0008-rescue-docs-It-is-no-longer-necessary-to-mount-files.patch @@ -0,0 +1,35 @@ +From 0862b461c299297689936c432ee62b8bc2f500a9 Mon Sep 17 00:00:00 2001 +From: "Richard W.M. Jones" +Date: Fri, 24 Mar 2017 22:51:06 +0000 +Subject: [PATCH 08/10] rescue: docs: It is no longer necessary to mount + filesystems by hand. + +Fix the manual page to reflect the new -i option. + +Fixes commit 33d2ae796119ae5dd38e2afcbf1ba4216bd99846. + +(cherry picked from commit c38b48409e067ba973d02bb10273350aa31b558e) +--- + rescue/virt-rescue.pod | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/rescue/virt-rescue.pod b/rescue/virt-rescue.pod +index bd6f954..dfa74e2 100644 +--- a/rescue/virt-rescue.pod ++++ b/rescue/virt-rescue.pod +@@ -35,9 +35,9 @@ For live VMs you I 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 + Linux commands. What you see in F (F, F etc) is the +-rescue appliance. You must mount the virtual machine's filesystems by +-hand. There is an empty directory called F where you can +-mount filesystems. ++rescue appliance. You must mount the virtual machine's filesystems. ++There is an empty directory called F where you can mount ++filesystems. + + To automatically mount the virtual machine's filesystems under + F use the I<-i> option. This uses libguestfs inspection to +-- +2.9.3 + diff --git a/0009-rescue-docs-Note-that-you-can-run-virt-rescue-on-dis.patch b/0009-rescue-docs-Note-that-you-can-run-virt-rescue-on-dis.patch new file mode 100644 index 0000000..8254666 --- /dev/null +++ b/0009-rescue-docs-Note-that-you-can-run-virt-rescue-on-dis.patch @@ -0,0 +1,28 @@ +From 5f60f8dad427af5a07c54613a26cbd9c02ee9b51 Mon Sep 17 00:00:00 2001 +From: "Richard W.M. Jones" +Date: Fri, 24 Mar 2017 22:52:02 +0000 +Subject: [PATCH 09/10] rescue: docs: Note that you can run virt-rescue on + disks too. + +(cherry picked from commit 267569f7ad22ad14d9ef4fa27b038469ae115c45) +--- + rescue/virt-rescue.pod | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/rescue/virt-rescue.pod b/rescue/virt-rescue.pod +index dfa74e2..5cfbd6e 100644 +--- a/rescue/virt-rescue.pod ++++ b/rescue/virt-rescue.pod +@@ -69,7 +69,8 @@ Virt-rescue can be used on I disk image file or device, not just + a virtual machine. For example you can use it on a blank file if you + want to partition that file (although we would recommend using + L instead as it is more suitable for this purpose). You +-can even use virt-rescue on things like SD cards. ++can even use virt-rescue on things like USB drives, SD cards and hard ++disks. + + You can get virt-rescue to give you scratch disk(s) to play with. + This is useful for testing out Linux utilities (see I<--scratch>). +-- +2.9.3 + diff --git a/0010-rescue-Don-t-document-suggest-option-in-help-output.patch b/0010-rescue-Don-t-document-suggest-option-in-help-output.patch new file mode 100644 index 0000000..1b6f5e2 --- /dev/null +++ b/0010-rescue-Don-t-document-suggest-option-in-help-output.patch @@ -0,0 +1,42 @@ +From 1727f1d181ed66d5563f564911c9b68e631de10a Mon Sep 17 00:00:00 2001 +From: "Richard W.M. Jones" +Date: Sat, 25 Mar 2017 23:34:09 +0000 +Subject: [PATCH 10/10] rescue: Don't document --suggest option in --help + output. + +Also fix the docs test to ignore it. + +Updates/fixes commit 33d2ae796119ae5dd38e2afcbf1ba4216bd99846. + +(cherry picked from commit aa7b7e26c3aa434f2b299055df63c52fb3a2f93f) +--- + rescue/rescue.c | 1 - + rescue/test-virt-rescue-docs.sh | 3 ++- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/rescue/rescue.c b/rescue/rescue.c +index 6ef4607..c88e9f3 100644 +--- a/rescue/rescue.c ++++ b/rescue/rescue.c +@@ -101,7 +101,6 @@ usage (int status) + " --scratch[=N] Add scratch disk(s)\n" + " --selinux For backwards compat only, does nothing\n" + " --smp N Enable SMP with N >= 2 virtual CPUs\n" +- " --suggest Suggest mount commands for this guest\n" + " -v|--verbose Verbose messages\n" + " -V|--version Display version and exit\n" + " -w|--rw Mount read-write\n" +diff --git a/rescue/test-virt-rescue-docs.sh b/rescue/test-virt-rescue-docs.sh +index 25f8f60..e5fbf26 100755 +--- a/rescue/test-virt-rescue-docs.sh ++++ b/rescue/test-virt-rescue-docs.sh +@@ -21,4 +21,5 @@ set -e + $TEST_FUNCTIONS + skip_if_skipped + +-$top_srcdir/podcheck.pl virt-rescue.pod virt-rescue ++$top_srcdir/podcheck.pl virt-rescue.pod virt-rescue \ ++ --ignore=--suggest +-- +2.9.3 + diff --git a/libguestfs.spec b/libguestfs.spec index cb06e99..bcf770e 100644 --- a/libguestfs.spec +++ b/libguestfs.spec @@ -34,7 +34,7 @@ Summary: Access and modify virtual machine disk images Name: libguestfs Epoch: 1 Version: 1.36.3 -Release: 1%{?dist} +Release: 2%{?dist} License: LGPLv2+ # Source and patches. @@ -44,6 +44,20 @@ Source0: http://libguestfs.org/download/1.36-stable/%{name}-%{version}.tar Source1: http://libguestfs.org/download/1.36-stable/%{name}-%{version}.tar.gz.sig %endif +# Include rewritten and greatly improved virt-rescue from upstream. +# Patches can be found on the 'fedora-26' branch upstream: +# https://github.com/libguestfs/libguestfs/tree/fedora-26 +Patch0001: 0001-generator-Deprecate-direct-mode-guestfs_set_direct-g.patch +Patch0002: 0002-New-API-internal-get-console-socket-to-support-virt-.patch +Patch0003: 0003-lib-Return-EPIPE-for-appliance-closed-the-connection.patch +Patch0004: 0004-rescue-Modify-virt-rescue-so-it-doesn-t-use-direct-m.patch +Patch0005: 0005-rescue-Implement-m-and-i-options.patch +Patch0006: 0006-rescue-Implement-escape-sequences.patch +Patch0007: 0007-rescue-Move-suggest-code-to-separate-file.patch +Patch0008: 0008-rescue-docs-It-is-no-longer-necessary-to-mount-files.patch +Patch0009: 0009-rescue-docs-Note-that-you-can-run-virt-rescue-on-dis.patch +Patch0010: 0010-rescue-Don-t-document-suggest-option-in-help-output.patch + # Replacement README file for Fedora users. Source4: README-replacement.in @@ -1392,6 +1406,9 @@ install -m 0644 utils/boot-benchmark/boot-benchmark.1 $RPM_BUILD_ROOT%{_mandir}/ %changelog +* Sat Apr 1 2017 Richard W.M. Jones - 1:1.36.3-2 +- Include rewritten and greatly improved virt-rescue from upstream. + * Fri Mar 24 2017 Richard W.M. Jones - 1:1.36.3-1 - New upstream version 1.36.3.