This supersedes the two previous patch series: https://www.redhat.com/archives/libguestfs/2017-March/msg00017.html https://www.redhat.com/archives/libguestfs/2017-March/msg00046.html Rich.
Richard W.M. Jones
2017-Mar-03 21:59 UTC
[Libguestfs] [PATCH v2 1/6] 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-03 21:59 UTC
[Libguestfs] [PATCH v2 2/6] 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-03 21:59 UTC
[Libguestfs] [PATCH v2 3/6] 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-03 21:59 UTC
[Libguestfs] [PATCH v2 4/6] 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-03 21:59 UTC
[Libguestfs] [PATCH v2 5/6] 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 | 262 ++++++++++++++++++++++++++++++++++++++++++++---------
3 files changed, 272 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..556c344 100644
--- a/rescue/rescue.c
+++ b/rescue/rescue.c
@@ -23,21 +23,28 @@
#include <string.h>
#include <inttypes.h>
#include <unistd.h>
+#include <fcntl.h>
#include <getopt.h>
#include <errno.h>
#include <error.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 restore_tty (void);
static void add_scratch_disks (int n, struct drv **drvs);
static void do_suggestion (struct drv *drvs);
@@ -54,6 +61,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 +145,9 @@ main (int argc, char *argv[])
int memsize = 0;
int smp = 0;
int suggest = 0;
+ char *append_full;
+ int sock;
+ struct termios termios;
g = guestfs_create ();
if (g == NULL)
@@ -295,30 +308,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 +319,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 +335,208 @@ 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);
+
+ /* Put stdin in raw mode so that we can receive ^C and other
+ * special keys.
*/
- ignore_value (guestfs_launch (g));
+ if (tcgetattr (STDIN_FILENO, &termios) == -1) {
+ perror ("tcgetattr: stdin");
+ exit (EXIT_FAILURE);
+ }
+ old_termios = termios;
+ cfmakeraw (&termios);
+ if (tcsetattr (STDIN_FILENO, TCSANOW, &termios) == -1) {
+ perror ("tcsetattr: stdin");
+ 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");
+
+ /* Restore the tty settings when the process exits. */
+ atexit (restore_tty);
+
+ 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);
+ }
+ }
+}
+
+static void
+restore_tty (void)
+{
+ tcsetattr (STDIN_FILENO, TCSANOW, &old_termios);
}
static void suggest_filesystems (void);
--
2.9.3
Richard W.M. Jones
2017-Mar-03 21:59 UTC
[Libguestfs] [PATCH v2 6/6] 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 556c344..fb747df 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>
@@ -36,9 +35,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"
@@ -83,7 +84,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"
@@ -112,7 +115,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 },
@@ -120,8 +123,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' },
@@ -136,13 +141,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;
@@ -193,6 +201,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)"),
@@ -211,10 +223,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':
@@ -285,7 +306,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);
@@ -329,12 +349,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
@@ -344,9 +358,41 @@ 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)
@@ -367,12 +413,9 @@ main (int argc, char *argv[])
}
/* 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));
/* Restore the tty settings when the process exits. */
atexit (restore_tty);
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