Blame SOURCES/0056-rescue-Modify-virt-rescue-so-it-doesn-t-use-direct-m.patch

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