This set of patches fixes virt-rescue rather cleanly. In particular the problems with handling ^C are completely fixed. Work still to be done before this can go upstream: - Shutdown doesn't work properly if you exit the shell. At the moment to exit you must do 'reboot -f'. Future improvements: - 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 handling control to the rescue shell. I'm not intending the do the future stuff just yet. Rich.
Richard W.M. Jones
2017-Mar-03 10:23 UTC
[Libguestfs] [PATCH WIP 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 10:23 UTC
[Libguestfs] [PATCH WIP 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 10:23 UTC
[Libguestfs] [PATCH WIP 3/5] appliance: Fix job control in virt-rescue.
See comment and link to busybox FAQ for explanation. --- appliance/init | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/appliance/init b/appliance/init index 8be27a2..b3b8a45 100755 --- a/appliance/init +++ b/appliance/init @@ -194,7 +194,15 @@ else echo "You have to mount the guest's partitions under /sysroot" echo "before you can examine them." echo - bash -i + # 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 + # + # Get name of the serial port, from console= passed by libguestfs. + export serial=`grep -Eo 'console=[^[:space:]]+' /proc/cmdline | + sed s/console=//` + setsid bash -c 'exec bash </dev/$serial >/dev/$serial 2>&1' echo echo "virt-rescue: Syncing the disk now before exiting ..." echo -- 2.9.3
Richard W.M. Jones
2017-Mar-03 10:23 UTC
[Libguestfs] [PATCH WIP 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 10:23 UTC
[Libguestfs] [PATCH WIP 5/5] rescue: Modify virt-rescue so it doesn't use direct mode.
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. This is all a bit ugly, but largely hidden inside virt-rescue. --- appliance/init | 75 +++++++++------- rescue/Makefile.am | 1 + rescue/rescue.c | 253 ++++++++++++++++++++++++++++++++++++++++++++--------- 3 files changed, 252 insertions(+), 77 deletions(-) diff --git a/appliance/init b/appliance/init index b3b8a45..7ae5014 100755 --- a/appliance/init +++ b/appliance/init @@ -159,41 +159,44 @@ 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. - - # 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 - - 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 virt-rescue shell. + + # We need a daemon, even in virt-rescue. + $cmd & pid=$! + + # 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 + + 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 # 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: @@ -203,9 +206,13 @@ else export serial=`grep -Eo 'console=[^[:space:]]+' /proc/cmdline | sed s/console=//` setsid bash -c 'exec bash </dev/$serial >/dev/$serial 2>&1' - echo - echo "virt-rescue: Syncing the disk now before exiting ..." - echo + echo + echo "virt-rescue: Syncing the disk now before exiting ..." + echo + sync + + # Wait for the daemon to shutdown gracefully. + wait $pid 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