This fixes the main issues in virt-rescue and is usable. There are some enhancements which could be made (in follow up work): - An escape sequence and escape commands that could be handled by virt-rescue, eg. to shut down the appliance, mount or unmount filesystems. - `virt-rescue -i' could be implemented cleanly by performing the right API calls before handing control to the rescue shell. Rich.
Richard W.M. Jones
2017-Mar-03 12:27 UTC
[Libguestfs] [PATCH 1/5] 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 12:27 UTC
[Libguestfs] [PATCH 2/5] 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 4e1f781..43f5e67 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 @@ -396,6 +396,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) { @@ -417,6 +430,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 12:27 UTC
[Libguestfs] [PATCH 3/5] 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 12:27 UTC
[Libguestfs] [PATCH 4/5] 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 12:27 UTC
[Libguestfs] [PATCH 5/5] 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 | 253 ++++++++++++++++++++++++++++++++++++++++++++--------- 3 files changed, 263 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..8c8bced 100644 --- a/rescue/rescue.c +++ b/rescue/rescue.c @@ -26,18 +26,24 @@ #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 +60,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 +144,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 +307,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 +318,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 +334,200 @@ 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); + } + /* 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