|
|
a30de4 |
From 2d013d5d67d5896869dff4b08c0674b2d91e721f Mon Sep 17 00:00:00 2001
|
|
|
a30de4 |
From: "Richard W.M. Jones" <rjones@redhat.com>
|
|
|
a30de4 |
Date: Thu, 2 Mar 2017 13:46:48 +0000
|
|
|
a30de4 |
Subject: [PATCH] rescue: Modify virt-rescue so it doesn't use direct mode
|
|
|
a30de4 |
(RHBZ#1152819, RHBZ#1171654).
|
|
|
a30de4 |
|
|
|
a30de4 |
Instead of using "direct mode" (which was basically a quick hack),
|
|
|
a30de4 |
virt-rescue now launches the appliance with a running daemon.
|
|
|
a30de4 |
|
|
|
a30de4 |
The daemon doesn't do much -- there is still a bash shell which the
|
|
|
a30de4 |
user interacts with. The daemon is there simply to provide the
|
|
|
a30de4 |
initial GUESTFS_LAUNCH_FLAG message and to handle shutdown a bit more
|
|
|
a30de4 |
gracefully.
|
|
|
a30de4 |
|
|
|
a30de4 |
To interact with the shell, and replacing direct mode, virt-rescue now
|
|
|
a30de4 |
prints out log messages (the output of the shell), and sends input
|
|
|
a30de4 |
typed by the user directly to the console socket. This uses the
|
|
|
a30de4 |
guestfs_internal_get_console_socket API added previously. Most of the
|
|
|
a30de4 |
complexity behind this is hidden in virt-rescue.
|
|
|
a30de4 |
|
|
|
a30de4 |
This fully fixes the handling of ^C (RHBZ#1152819). Also there were
|
|
|
a30de4 |
earlier reports that full screen commands like 'vim' didn't work well,
|
|
|
a30de4 |
(RHBZ#1171654), but in this version vim appears to work fine, albeit
|
|
|
a30de4 |
only using 80x24 of the screen because of the serial console.
|
|
|
a30de4 |
|
|
|
a30de4 |
(cherry picked from commit 32d60801443647b3523b9374c431fefdbf054e3c)
|
|
|
a30de4 |
---
|
|
|
a30de4 |
appliance/init | 93 ++++++++--------
|
|
|
a30de4 |
rescue/Makefile.am | 1 +
|
|
|
a30de4 |
rescue/rescue.c | 306 +++++++++++++++++++++++++++++++++++++++++++++--------
|
|
|
a30de4 |
3 files changed, 313 insertions(+), 87 deletions(-)
|
|
|
a30de4 |
|
|
|
a30de4 |
diff --git a/appliance/init b/appliance/init
|
|
|
a30de4 |
index 735ba8946..ea763c2d3 100755
|
|
|
a30de4 |
--- a/appliance/init
|
|
|
a30de4 |
+++ b/appliance/init
|
|
|
a30de4 |
@@ -160,60 +160,63 @@ if test "$guestfs_verbose" = 1 && test "$guestfs_boot_analysis" != 1; then
|
|
|
a30de4 |
echo -n "uptime: "; cat /proc/uptime
|
|
|
a30de4 |
fi
|
|
|
a30de4 |
|
|
|
a30de4 |
-if ! test "$guestfs_rescue" = 1; then
|
|
|
a30de4 |
- # Run the daemon.
|
|
|
a30de4 |
- cmd="guestfsd"
|
|
|
a30de4 |
- eval `grep -Eo 'guestfs_channel=[^[:space:]]+' /proc/cmdline`
|
|
|
a30de4 |
- if test "x$guestfs_channel" != "x"; then
|
|
|
a30de4 |
+# Run the daemon.
|
|
|
a30de4 |
+cmd="guestfsd"
|
|
|
a30de4 |
+eval `grep -Eo 'guestfs_channel=[^[:space:]]+' /proc/cmdline`
|
|
|
a30de4 |
+if test "x$guestfs_channel" != "x"; then
|
|
|
a30de4 |
cmd="$cmd --channel $guestfs_channel"
|
|
|
a30de4 |
- fi
|
|
|
a30de4 |
- if test "$guestfs_verbose" = 1; then
|
|
|
a30de4 |
+fi
|
|
|
a30de4 |
+if test "$guestfs_verbose" = 1; then
|
|
|
a30de4 |
cmd="$cmd --verbose"
|
|
|
a30de4 |
- fi
|
|
|
a30de4 |
- if test "$guestfs_network" = 1; then
|
|
|
a30de4 |
+fi
|
|
|
a30de4 |
+if test "$guestfs_network" = 1; then
|
|
|
a30de4 |
cmd="$cmd --network"
|
|
|
a30de4 |
- fi
|
|
|
a30de4 |
- echo $cmd
|
|
|
a30de4 |
- $cmd
|
|
|
a30de4 |
+fi
|
|
|
a30de4 |
+if ! test "$guestfs_rescue" = 1; then
|
|
|
a30de4 |
+ echo $cmd
|
|
|
a30de4 |
+ $cmd
|
|
|
a30de4 |
else
|
|
|
a30de4 |
- # Run virt-rescue shell.
|
|
|
a30de4 |
+ # Run virt-rescue shell.
|
|
|
a30de4 |
|
|
|
a30de4 |
- # Get name of the serial port, from console= passed by libguestfs.
|
|
|
a30de4 |
- # XXX Consider using /proc/consoles
|
|
|
a30de4 |
- guestfs_serial=$(grep -Eo 'console=[^[:space:]]+' /proc/cmdline |
|
|
|
a30de4 |
- sed s/console=//)
|
|
|
a30de4 |
+ # We need a daemon, even in virt-rescue.
|
|
|
a30de4 |
+ $cmd &
|
|
|
a30de4 |
|
|
|
a30de4 |
- # Remove LD_PRELOAD=libSegFault set above.
|
|
|
a30de4 |
- unset LD_PRELOAD
|
|
|
a30de4 |
+ # Get name of the serial port, from console= passed by libguestfs.
|
|
|
a30de4 |
+ # XXX Consider using /proc/consoles
|
|
|
a30de4 |
+ guestfs_serial=$(grep -Eo 'console=[^[:space:]]+' /proc/cmdline |
|
|
|
a30de4 |
+ sed s/console=//)
|
|
|
a30de4 |
|
|
|
a30de4 |
- :> $HOME/.bashrc
|
|
|
a30de4 |
- grep -Eo 'TERM=[^[:space:]]+' /proc/cmdline >> $HOME/.bashrc
|
|
|
a30de4 |
- echo "PS1='><rescue> '" >> $HOME/.bashrc
|
|
|
a30de4 |
- echo "export TERM PS1" >> $HOME/.bashrc
|
|
|
a30de4 |
+ # Remove LD_PRELOAD=libSegFault set above.
|
|
|
a30de4 |
+ unset LD_PRELOAD
|
|
|
a30de4 |
|
|
|
a30de4 |
- # The shell is opened by default on /dev/console, which (on Linux)
|
|
|
a30de4 |
- # is not a controlling terminal, causing job control to fail. For
|
|
|
a30de4 |
- # how we work around this, see:
|
|
|
a30de4 |
- # https://busybox.net/FAQ.html#job_control
|
|
|
a30de4 |
- run_bash_with_ctty ()
|
|
|
a30de4 |
- {
|
|
|
a30de4 |
- setsid bash -c \
|
|
|
a30de4 |
- "exec bash </dev/$guestfs_serial >/dev/$guestfs_serial 2>&1"
|
|
|
a30de4 |
- }
|
|
|
a30de4 |
+ :> $HOME/.bashrc
|
|
|
a30de4 |
+ grep -Eo 'TERM=[^[:space:]]+' /proc/cmdline >> $HOME/.bashrc
|
|
|
a30de4 |
+ echo "PS1='><rescue> '" >> $HOME/.bashrc
|
|
|
a30de4 |
+ echo "export TERM PS1" >> $HOME/.bashrc
|
|
|
a30de4 |
|
|
|
a30de4 |
- echo
|
|
|
a30de4 |
- echo "------------------------------------------------------------"
|
|
|
a30de4 |
- echo
|
|
|
a30de4 |
- echo "Welcome to virt-rescue, the libguestfs rescue shell."
|
|
|
a30de4 |
- echo
|
|
|
a30de4 |
- echo "Note: The contents of / are the rescue appliance."
|
|
|
a30de4 |
- echo "You have to mount the guest's partitions under /sysroot"
|
|
|
a30de4 |
- echo "before you can examine them."
|
|
|
a30de4 |
- echo
|
|
|
a30de4 |
- run_bash_with_ctty
|
|
|
a30de4 |
- echo
|
|
|
a30de4 |
- echo "virt-rescue: Syncing the disk now before exiting ..."
|
|
|
a30de4 |
- echo
|
|
|
a30de4 |
+ # The shell is opened by default on /dev/console, which (on Linux)
|
|
|
a30de4 |
+ # is not a controlling terminal, causing job control to fail. For
|
|
|
a30de4 |
+ # how we work around this, see:
|
|
|
a30de4 |
+ # https://busybox.net/FAQ.html#job_control
|
|
|
a30de4 |
+ run_bash_with_ctty ()
|
|
|
a30de4 |
+ {
|
|
|
a30de4 |
+ setsid bash -c \
|
|
|
a30de4 |
+ "exec bash </dev/$guestfs_serial >/dev/$guestfs_serial 2>&1"
|
|
|
a30de4 |
+ }
|
|
|
a30de4 |
+
|
|
|
a30de4 |
+ echo
|
|
|
a30de4 |
+ echo "------------------------------------------------------------"
|
|
|
a30de4 |
+ echo
|
|
|
a30de4 |
+ echo "Welcome to virt-rescue, the libguestfs rescue shell."
|
|
|
a30de4 |
+ echo
|
|
|
a30de4 |
+ echo "Note: The contents of / (root) are the rescue appliance."
|
|
|
a30de4 |
+ echo "You have to mount the guest's partitions under /sysroot"
|
|
|
a30de4 |
+ echo "before you can examine them."
|
|
|
a30de4 |
+ echo
|
|
|
a30de4 |
+ run_bash_with_ctty
|
|
|
a30de4 |
+ echo
|
|
|
a30de4 |
+ echo "virt-rescue: Syncing the disk now before exiting ..."
|
|
|
a30de4 |
+ echo
|
|
|
a30de4 |
fi
|
|
|
a30de4 |
|
|
|
a30de4 |
sync
|
|
|
a30de4 |
diff --git a/rescue/Makefile.am b/rescue/Makefile.am
|
|
|
a30de4 |
index 7919aafd5..99d4b79ae 100644
|
|
|
a30de4 |
--- a/rescue/Makefile.am
|
|
|
a30de4 |
+++ b/rescue/Makefile.am
|
|
|
a30de4 |
@@ -30,6 +30,7 @@ virt_rescue_SOURCES = \
|
|
|
a30de4 |
|
|
|
a30de4 |
virt_rescue_CPPFLAGS = \
|
|
|
a30de4 |
-DGUESTFS_WARN_DEPRECATED=1 \
|
|
|
a30de4 |
+ -DGUESTFS_PRIVATE=1 \
|
|
|
a30de4 |
-DLOCALEBASEDIR=\""$(datadir)/locale"\" \
|
|
|
a30de4 |
-I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \
|
|
|
a30de4 |
-I$(top_srcdir)/lib -I$(top_builddir)/lib \
|
|
|
a30de4 |
diff --git a/rescue/rescue.c b/rescue/rescue.c
|
|
|
a30de4 |
index b692e5a07..b145dcd40 100644
|
|
|
a30de4 |
--- a/rescue/rescue.c
|
|
|
a30de4 |
+++ b/rescue/rescue.c
|
|
|
a30de4 |
@@ -23,21 +23,32 @@
|
|
|
a30de4 |
#include <string.h>
|
|
|
a30de4 |
#include <inttypes.h>
|
|
|
a30de4 |
#include <unistd.h>
|
|
|
a30de4 |
+#include <fcntl.h>
|
|
|
a30de4 |
#include <getopt.h>
|
|
|
a30de4 |
#include <errno.h>
|
|
|
a30de4 |
#include <error.h>
|
|
|
a30de4 |
+#include <signal.h>
|
|
|
a30de4 |
+#include <termios.h>
|
|
|
a30de4 |
+#include <poll.h>
|
|
|
a30de4 |
#include <locale.h>
|
|
|
a30de4 |
#include <assert.h>
|
|
|
a30de4 |
#include <libintl.h>
|
|
|
a30de4 |
|
|
|
a30de4 |
+#include "full-write.h"
|
|
|
a30de4 |
+#include "getprogname.h"
|
|
|
a30de4 |
#include "ignore-value.h"
|
|
|
a30de4 |
#include "xvasprintf.h"
|
|
|
a30de4 |
-#include "getprogname.h"
|
|
|
a30de4 |
|
|
|
a30de4 |
#include "guestfs.h"
|
|
|
a30de4 |
#include "options.h"
|
|
|
a30de4 |
#include "display-options.h"
|
|
|
a30de4 |
|
|
|
a30de4 |
+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);
|
|
|
a30de4 |
+static void do_rescue (int sock);
|
|
|
a30de4 |
+static void raw_tty (void);
|
|
|
a30de4 |
+static void restore_tty (void);
|
|
|
a30de4 |
+static void tstp_handler (int sig);
|
|
|
a30de4 |
+static void cont_handler (int sig);
|
|
|
a30de4 |
static void add_scratch_disks (int n, struct drv **drvs);
|
|
|
a30de4 |
static void do_suggestion (struct drv *drvs);
|
|
|
a30de4 |
|
|
|
a30de4 |
@@ -54,6 +65,9 @@ int inspector = 0;
|
|
|
a30de4 |
int in_guestfish = 0;
|
|
|
a30de4 |
int in_virt_rescue = 1;
|
|
|
a30de4 |
|
|
|
a30de4 |
+/* Old terminal settings. */
|
|
|
a30de4 |
+static struct termios old_termios;
|
|
|
a30de4 |
+
|
|
|
a30de4 |
static void __attribute__((noreturn))
|
|
|
a30de4 |
usage (int status)
|
|
|
a30de4 |
{
|
|
|
a30de4 |
@@ -135,6 +149,8 @@ main (int argc, char *argv[])
|
|
|
a30de4 |
int memsize = 0;
|
|
|
a30de4 |
int smp = 0;
|
|
|
a30de4 |
int suggest = 0;
|
|
|
a30de4 |
+ char *append_full;
|
|
|
a30de4 |
+ int sock;
|
|
|
a30de4 |
|
|
|
a30de4 |
g = guestfs_create ();
|
|
|
a30de4 |
if (g == NULL)
|
|
|
a30de4 |
@@ -295,30 +311,6 @@ main (int argc, char *argv[])
|
|
|
a30de4 |
usage (EXIT_FAILURE);
|
|
|
a30de4 |
}
|
|
|
a30de4 |
|
|
|
a30de4 |
-#pragma GCC diagnostic push
|
|
|
a30de4 |
-#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
|
|
a30de4 |
- /* Setting "direct mode" is required for the rescue appliance. */
|
|
|
a30de4 |
- if (guestfs_set_direct (g, 1) == -1)
|
|
|
a30de4 |
- exit (EXIT_FAILURE);
|
|
|
a30de4 |
-#pragma GCC diagnostic pop
|
|
|
a30de4 |
-
|
|
|
a30de4 |
- {
|
|
|
a30de4 |
- /* The libvirt backend doesn't support direct mode. As a temporary
|
|
|
a30de4 |
- * workaround, force the appliance backend, but warn about it.
|
|
|
a30de4 |
- */
|
|
|
a30de4 |
- CLEANUP_FREE char *backend = guestfs_get_backend (g);
|
|
|
a30de4 |
- if (backend) {
|
|
|
a30de4 |
- if (STREQ (backend, "libvirt") ||
|
|
|
a30de4 |
- STRPREFIX (backend, "libvirt:")) {
|
|
|
a30de4 |
- fprintf (stderr, _("%s: warning: virt-rescue doesn't work with the libvirt backend\n"
|
|
|
a30de4 |
- "at the moment. As a workaround, forcing backend = 'direct'.\n"),
|
|
|
a30de4 |
- getprogname ());
|
|
|
a30de4 |
- if (guestfs_set_backend (g, "direct") == -1)
|
|
|
a30de4 |
- exit (EXIT_FAILURE);
|
|
|
a30de4 |
- }
|
|
|
a30de4 |
- }
|
|
|
a30de4 |
- }
|
|
|
a30de4 |
-
|
|
|
a30de4 |
/* Set other features. */
|
|
|
a30de4 |
if (memsize > 0)
|
|
|
a30de4 |
if (guestfs_set_memsize (g, memsize) == -1)
|
|
|
a30de4 |
@@ -330,16 +322,15 @@ main (int argc, char *argv[])
|
|
|
a30de4 |
if (guestfs_set_smp (g, smp) == -1)
|
|
|
a30de4 |
exit (EXIT_FAILURE);
|
|
|
a30de4 |
|
|
|
a30de4 |
- {
|
|
|
a30de4 |
- /* Kernel command line must include guestfs_rescue=1 (see
|
|
|
a30de4 |
- * appliance/init) as well as other options.
|
|
|
a30de4 |
- */
|
|
|
a30de4 |
- CLEANUP_FREE char *append_full = xasprintf ("guestfs_rescue=1%s%s",
|
|
|
a30de4 |
- append ? " " : "",
|
|
|
a30de4 |
- append ? append : "");
|
|
|
a30de4 |
- if (guestfs_set_append (g, append_full) == -1)
|
|
|
a30de4 |
- exit (EXIT_FAILURE);
|
|
|
a30de4 |
- }
|
|
|
a30de4 |
+ /* Kernel command line must include guestfs_rescue=1 (see
|
|
|
a30de4 |
+ * appliance/init) as well as other options.
|
|
|
a30de4 |
+ */
|
|
|
a30de4 |
+ append_full = xasprintf ("guestfs_rescue=1%s%s",
|
|
|
a30de4 |
+ append ? " " : "",
|
|
|
a30de4 |
+ append ? append : "");
|
|
|
a30de4 |
+ if (guestfs_set_append (g, append_full) == -1)
|
|
|
a30de4 |
+ exit (EXIT_FAILURE);
|
|
|
a30de4 |
+ free (append_full);
|
|
|
a30de4 |
|
|
|
a30de4 |
/* Add drives. */
|
|
|
a30de4 |
add_drives (drvs);
|
|
|
a30de4 |
@@ -347,22 +338,253 @@ main (int argc, char *argv[])
|
|
|
a30de4 |
/* Free up data structures, no longer needed after this point. */
|
|
|
a30de4 |
free_drives (drvs);
|
|
|
a30de4 |
|
|
|
a30de4 |
- /* Run the appliance. This won't return until the user quits the
|
|
|
a30de4 |
- * appliance.
|
|
|
a30de4 |
+ /* Add an event handler to print "log messages". These will be the
|
|
|
a30de4 |
+ * output of the appliance console during launch and shutdown.
|
|
|
a30de4 |
+ * After launch, we will read the console messages directly from the
|
|
|
a30de4 |
+ * socket and they won't be passed through the event callback.
|
|
|
a30de4 |
*/
|
|
|
a30de4 |
- if (!verbose)
|
|
|
a30de4 |
- guestfs_set_error_handler (g, NULL, NULL);
|
|
|
a30de4 |
+ if (guestfs_set_event_callback (g, log_message_callback,
|
|
|
a30de4 |
+ GUESTFS_EVENT_APPLIANCE, 0, NULL) == -1)
|
|
|
a30de4 |
+ exit (EXIT_FAILURE);
|
|
|
a30de4 |
|
|
|
a30de4 |
- /* We expect launch to fail, so ignore the return value, and don't
|
|
|
a30de4 |
- * bother with explicit guestfs_shutdown either.
|
|
|
a30de4 |
+ /* Run the appliance. */
|
|
|
a30de4 |
+ if (guestfs_launch (g) == -1)
|
|
|
a30de4 |
+ exit (EXIT_FAILURE);
|
|
|
a30de4 |
+
|
|
|
a30de4 |
+ sock = guestfs_internal_get_console_socket (g);
|
|
|
a30de4 |
+ if (sock == -1)
|
|
|
a30de4 |
+ exit (EXIT_FAILURE);
|
|
|
a30de4 |
+
|
|
|
a30de4 |
+ /* Try to set all sockets to non-blocking. */
|
|
|
a30de4 |
+ if (fcntl (STDIN_FILENO, F_SETFL, O_NONBLOCK) == -1)
|
|
|
a30de4 |
+ perror ("could not set stdin to non-blocking");
|
|
|
a30de4 |
+ if (fcntl (STDOUT_FILENO, F_SETFL, O_NONBLOCK) == -1)
|
|
|
a30de4 |
+ perror ("could not set stdout to non-blocking");
|
|
|
a30de4 |
+ if (fcntl (sock, F_SETFL, O_NONBLOCK) == -1)
|
|
|
a30de4 |
+ perror ("could not set console socket to non-blocking");
|
|
|
a30de4 |
+
|
|
|
a30de4 |
+ /* Save the initial state of the tty so we always have the original
|
|
|
a30de4 |
+ * state to go back to.
|
|
|
a30de4 |
+ */
|
|
|
a30de4 |
+ if (tcgetattr (STDIN_FILENO, &old_termios) == -1) {
|
|
|
a30de4 |
+ perror ("tcgetattr: stdin");
|
|
|
a30de4 |
+ exit (EXIT_FAILURE);
|
|
|
a30de4 |
+ }
|
|
|
a30de4 |
+
|
|
|
a30de4 |
+ /* Put stdin in raw mode so that we can receive ^C and other
|
|
|
a30de4 |
+ * special keys.
|
|
|
a30de4 |
+ */
|
|
|
a30de4 |
+ raw_tty ();
|
|
|
a30de4 |
+
|
|
|
a30de4 |
+ /* Restore the tty settings when the process exits. */
|
|
|
a30de4 |
+ atexit (restore_tty);
|
|
|
a30de4 |
+
|
|
|
a30de4 |
+ /* Catch tty stop and cont signals so we can cleanup.
|
|
|
a30de4 |
+ * See https://www.gnu.org/software/libc/manual/html_node/Signaling-Yourself.html
|
|
|
a30de4 |
*/
|
|
|
a30de4 |
- ignore_value (guestfs_launch (g));
|
|
|
a30de4 |
+ signal (SIGTSTP, tstp_handler);
|
|
|
a30de4 |
+ signal (SIGCONT, cont_handler);
|
|
|
a30de4 |
+
|
|
|
a30de4 |
+ do_rescue (sock);
|
|
|
a30de4 |
+
|
|
|
a30de4 |
+ restore_tty ();
|
|
|
a30de4 |
+
|
|
|
a30de4 |
+ /* Shut down the appliance. */
|
|
|
a30de4 |
+ guestfs_push_error_handler (g, NULL, NULL);
|
|
|
a30de4 |
+ if (guestfs_shutdown (g) == -1) {
|
|
|
a30de4 |
+ const char *err;
|
|
|
a30de4 |
+
|
|
|
a30de4 |
+ /* Ignore "appliance closed the connection unexpectedly" since
|
|
|
a30de4 |
+ * this can happen if the user reboots the appliance.
|
|
|
a30de4 |
+ */
|
|
|
a30de4 |
+ if (guestfs_last_errno (g) == EPIPE)
|
|
|
a30de4 |
+ goto next;
|
|
|
a30de4 |
|
|
|
a30de4 |
+ /* Otherwise it's a real error. */
|
|
|
a30de4 |
+ err = guestfs_last_error (g);
|
|
|
a30de4 |
+ fprintf (stderr, "libguestfs: error: %s\n", err);
|
|
|
a30de4 |
+ exit (EXIT_FAILURE);
|
|
|
a30de4 |
+ }
|
|
|
a30de4 |
+ next:
|
|
|
a30de4 |
+ guestfs_pop_error_handler (g);
|
|
|
a30de4 |
guestfs_close (g);
|
|
|
a30de4 |
|
|
|
a30de4 |
exit (EXIT_SUCCESS);
|
|
|
a30de4 |
}
|
|
|
a30de4 |
|
|
|
a30de4 |
+static void
|
|
|
a30de4 |
+log_message_callback (guestfs_h *g, void *opaque, uint64_t event,
|
|
|
a30de4 |
+ int event_handle, int flags,
|
|
|
a30de4 |
+ const char *buf, size_t buf_len,
|
|
|
a30de4 |
+ const uint64_t *array, size_t array_len)
|
|
|
a30de4 |
+{
|
|
|
a30de4 |
+ if (buf_len > 0) {
|
|
|
a30de4 |
+ ignore_value (full_write (STDOUT_FILENO, buf, buf_len));
|
|
|
a30de4 |
+ }
|
|
|
a30de4 |
+}
|
|
|
a30de4 |
+
|
|
|
a30de4 |
+/* This is the main loop for virt-rescue. We read and write
|
|
|
a30de4 |
+ * directly to the console socket.
|
|
|
a30de4 |
+ */
|
|
|
a30de4 |
+#define BUFSIZE 4096
|
|
|
a30de4 |
+static char rbuf[BUFSIZE]; /* appliance -> local tty */
|
|
|
a30de4 |
+static char wbuf[BUFSIZE]; /* local tty -> appliance */
|
|
|
a30de4 |
+
|
|
|
a30de4 |
+static void
|
|
|
a30de4 |
+do_rescue (int sock)
|
|
|
a30de4 |
+{
|
|
|
a30de4 |
+ size_t rlen = 0;
|
|
|
a30de4 |
+ size_t wlen = 0;
|
|
|
a30de4 |
+
|
|
|
a30de4 |
+ while (sock >= 0 || rlen > 0) {
|
|
|
a30de4 |
+ struct pollfd fds[3];
|
|
|
a30de4 |
+ nfds_t nfds = 2;
|
|
|
a30de4 |
+ int r;
|
|
|
a30de4 |
+ ssize_t n;
|
|
|
a30de4 |
+
|
|
|
a30de4 |
+ fds[0].fd = STDIN_FILENO;
|
|
|
a30de4 |
+ fds[0].events = 0;
|
|
|
a30de4 |
+ if (BUFSIZE-wlen > 0)
|
|
|
a30de4 |
+ fds[0].events = POLLIN;
|
|
|
a30de4 |
+ fds[0].revents = 0;
|
|
|
a30de4 |
+
|
|
|
a30de4 |
+ fds[1].fd = STDOUT_FILENO;
|
|
|
a30de4 |
+ fds[1].events = 0;
|
|
|
a30de4 |
+ if (rlen > 0)
|
|
|
a30de4 |
+ fds[1].events |= POLLOUT;
|
|
|
a30de4 |
+ fds[1].revents = 0;
|
|
|
a30de4 |
+
|
|
|
a30de4 |
+ if (sock >= 0) {
|
|
|
a30de4 |
+ fds[2].fd = sock;
|
|
|
a30de4 |
+ fds[2].events = 0;
|
|
|
a30de4 |
+ if (BUFSIZE-rlen > 0)
|
|
|
a30de4 |
+ fds[2].events |= POLLIN;
|
|
|
a30de4 |
+ if (wlen > 0)
|
|
|
a30de4 |
+ fds[2].events |= POLLOUT;
|
|
|
a30de4 |
+ fds[2].revents = 0;
|
|
|
a30de4 |
+ nfds++;
|
|
|
a30de4 |
+ }
|
|
|
a30de4 |
+
|
|
|
a30de4 |
+ r = poll (fds, nfds, -1);
|
|
|
a30de4 |
+ if (r == -1) {
|
|
|
a30de4 |
+ if (errno == EINTR || errno == EAGAIN)
|
|
|
a30de4 |
+ continue;
|
|
|
a30de4 |
+ perror ("poll");
|
|
|
a30de4 |
+ return;
|
|
|
a30de4 |
+ }
|
|
|
a30de4 |
+
|
|
|
a30de4 |
+ /* Input from local tty. */
|
|
|
a30de4 |
+ if ((fds[0].revents & POLLIN) != 0) {
|
|
|
a30de4 |
+ assert (BUFSIZE-wlen > 0);
|
|
|
a30de4 |
+ n = read (STDIN_FILENO, wbuf+wlen, BUFSIZE-wlen);
|
|
|
a30de4 |
+ if (n == -1) {
|
|
|
a30de4 |
+ if (errno == EINTR || errno == EAGAIN)
|
|
|
a30de4 |
+ continue;
|
|
|
a30de4 |
+ perror ("read");
|
|
|
a30de4 |
+ return;
|
|
|
a30de4 |
+ }
|
|
|
a30de4 |
+ if (n == 0) {
|
|
|
a30de4 |
+ /* We don't expect this to happen. Maybe the whole tty went away?
|
|
|
a30de4 |
+ * Anyway, we should exit as soon as possible.
|
|
|
a30de4 |
+ */
|
|
|
a30de4 |
+ return;
|
|
|
a30de4 |
+ }
|
|
|
a30de4 |
+ if (n > 0)
|
|
|
a30de4 |
+ wlen += n;
|
|
|
a30de4 |
+ }
|
|
|
a30de4 |
+
|
|
|
a30de4 |
+ /* Log message from appliance. */
|
|
|
a30de4 |
+ if (nfds > 2 && (fds[2].revents & POLLIN) != 0) {
|
|
|
a30de4 |
+ assert (BUFSIZE-rlen > 0);
|
|
|
a30de4 |
+ n = read (sock, rbuf+rlen, BUFSIZE-rlen);
|
|
|
a30de4 |
+ if (n == -1) {
|
|
|
a30de4 |
+ if (errno == EINTR || errno == EAGAIN)
|
|
|
a30de4 |
+ continue;
|
|
|
a30de4 |
+ if (errno == ECONNRESET)
|
|
|
a30de4 |
+ goto appliance_closed;
|
|
|
a30de4 |
+ perror ("read");
|
|
|
a30de4 |
+ return;
|
|
|
a30de4 |
+ }
|
|
|
a30de4 |
+ if (n == 0) {
|
|
|
a30de4 |
+ appliance_closed:
|
|
|
a30de4 |
+ sock = -1;
|
|
|
a30de4 |
+ /* Don't actually close the socket, because it's owned by
|
|
|
a30de4 |
+ * the guestfs handle.
|
|
|
a30de4 |
+ */
|
|
|
a30de4 |
+ continue;
|
|
|
a30de4 |
+ }
|
|
|
a30de4 |
+ if (n > 0)
|
|
|
a30de4 |
+ rlen += n;
|
|
|
a30de4 |
+ }
|
|
|
a30de4 |
+
|
|
|
a30de4 |
+ /* Write log messages to local tty. */
|
|
|
a30de4 |
+ if ((fds[1].revents & POLLOUT) != 0) {
|
|
|
a30de4 |
+ assert (rlen > 0);
|
|
|
a30de4 |
+ n = write (STDOUT_FILENO, rbuf, rlen);
|
|
|
a30de4 |
+ if (n == -1) {
|
|
|
a30de4 |
+ perror ("write");
|
|
|
a30de4 |
+ continue;
|
|
|
a30de4 |
+ }
|
|
|
a30de4 |
+ rlen -= n;
|
|
|
a30de4 |
+ memmove (rbuf, rbuf+n, rlen);
|
|
|
a30de4 |
+ }
|
|
|
a30de4 |
+
|
|
|
a30de4 |
+ /* Write commands to the appliance. */
|
|
|
a30de4 |
+ if (nfds > 2 && (fds[2].revents & POLLOUT) != 0) {
|
|
|
a30de4 |
+ assert (wlen > 0);
|
|
|
a30de4 |
+ n = write (sock, wbuf, wlen);
|
|
|
a30de4 |
+ if (n == -1) {
|
|
|
a30de4 |
+ perror ("write");
|
|
|
a30de4 |
+ continue;
|
|
|
a30de4 |
+ }
|
|
|
a30de4 |
+ wlen -= n;
|
|
|
a30de4 |
+ memmove (wbuf, wbuf+n, wlen);
|
|
|
a30de4 |
+ }
|
|
|
a30de4 |
+ }
|
|
|
a30de4 |
+}
|
|
|
a30de4 |
+
|
|
|
a30de4 |
+/* Put the tty in raw mode. */
|
|
|
a30de4 |
+static void
|
|
|
a30de4 |
+raw_tty (void)
|
|
|
a30de4 |
+{
|
|
|
a30de4 |
+ struct termios termios;
|
|
|
a30de4 |
+
|
|
|
a30de4 |
+ if (tcgetattr (STDIN_FILENO, &termios) == -1) {
|
|
|
a30de4 |
+ perror ("tcgetattr: stdin");
|
|
|
a30de4 |
+ exit (EXIT_FAILURE);
|
|
|
a30de4 |
+ }
|
|
|
a30de4 |
+ cfmakeraw (&termios);
|
|
|
a30de4 |
+ if (tcsetattr (STDIN_FILENO, TCSANOW, &termios) == -1) {
|
|
|
a30de4 |
+ perror ("tcsetattr: stdin");
|
|
|
a30de4 |
+ exit (EXIT_FAILURE);
|
|
|
a30de4 |
+ }
|
|
|
a30de4 |
+}
|
|
|
a30de4 |
+
|
|
|
a30de4 |
+/* Restore the tty to (presumably) cooked mode as it was when
|
|
|
a30de4 |
+ * the program was started.
|
|
|
a30de4 |
+ */
|
|
|
a30de4 |
+static void
|
|
|
a30de4 |
+restore_tty (void)
|
|
|
a30de4 |
+{
|
|
|
a30de4 |
+ tcsetattr (STDIN_FILENO, TCSANOW, &old_termios);
|
|
|
a30de4 |
+}
|
|
|
a30de4 |
+
|
|
|
a30de4 |
+/* When we get SIGTSTP, switch back to cooked mode. */
|
|
|
a30de4 |
+static void
|
|
|
a30de4 |
+tstp_handler (int sig)
|
|
|
a30de4 |
+{
|
|
|
a30de4 |
+ restore_tty ();
|
|
|
a30de4 |
+ signal (SIGTSTP, SIG_DFL);
|
|
|
a30de4 |
+ raise (SIGTSTP);
|
|
|
a30de4 |
+}
|
|
|
a30de4 |
+
|
|
|
a30de4 |
+/* When we get SIGCONF, switch to raw mode. */
|
|
|
a30de4 |
+static void
|
|
|
a30de4 |
+cont_handler (int sig)
|
|
|
a30de4 |
+{
|
|
|
a30de4 |
+ raw_tty ();
|
|
|
a30de4 |
+}
|
|
|
a30de4 |
+
|
|
|
a30de4 |
static void suggest_filesystems (void);
|
|
|
a30de4 |
|
|
|
a30de4 |
static int
|
|
|
a30de4 |
--
|
|
|
a30de4 |
2.14.3
|
|
|
a30de4 |
|