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