[Libguestfs] [PATCH] fish: reset the console on ^Z RHBZ#1213844

Maros Zatko mzatko at redhat.com
Tue Mar 8 19:49:01 UTC 2016


Patch registers SIGTSTP hook where it sends reset terminal color
control sequence using write and fsync.
Handler is installed only if signal is not being ignored.

Patch uses rl_free_line_state and rl_cleanup_after_signal to unhook
readline from terminal, then it calls original TSTP handler using
approach in URL below and again hooks readline using
rl_reset_after_signal.

Handling is based on code from:
http://man7.org/tlpi/code/online/dist/pgsjc/handling_SIGTSTP.c.html

This approach seems to mostly work. User is sometimes able to get
readline into state when it doesn't correctly respond to newline.
^Z and subsequent fg helps there as reset command helps broken
terminal.
---
 fish/fish.c | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 76 insertions(+)

diff --git a/fish/fish.c b/fish/fish.c
index d26f8b3..ec855f4 100644
--- a/fish/fish.c
+++ b/fish/fish.c
@@ -73,6 +73,7 @@ static void cleanup_readline (void);
 static char *decode_ps1 (const char *);
 static void add_history_line (const char *);
 #endif
+static void tstp_handler (int sig);
 
 static int override_progress_bars = -1;
 static struct progress_bar *bar = NULL;
@@ -159,6 +160,65 @@ usage (int status)
   exit (status);
 }
 
+/* Handler for SIGTSTP */
+static void
+tstp_handler (int sig)
+{
+  /* Source: http://man7.org/tlpi/code/online/dist/pgsjc/handling_SIGTSTP.c.html */
+  sigset_t tstp_mask, prev_mask;
+  int saved_errno;
+  struct sigaction sa;
+
+  /* In case we change 'errno' here */
+  saved_errno = errno;
+
+#ifdef HAVE_LIBREADLINE
+  /* Cleanup readline, unhook from terminal */
+  rl_free_line_state ();
+  rl_cleanup_after_signal ();
+#endif
+
+  /* Reset terminal color */
+#define RESETCOLOR "\033[0m"
+  if (write (1, RESETCOLOR, sizeof RESETCOLOR) != sizeof RESETCOLOR)
+    perror ("write");
+  fsync (1);
+
+  /* Set handling to default */
+  if (signal (SIGTSTP, SIG_DFL) == SIG_ERR)
+    perror ("signal");
+
+  /* Generate a further SIGTSTP */
+  raise (SIGTSTP);
+
+  /* Unblock SIGTSTP; the pending SIGTSTP immediately suspends the program */
+
+  sigemptyset (&tstp_mask);
+  sigaddset (&tstp_mask, SIGTSTP);
+  if (sigprocmask (SIG_UNBLOCK, &tstp_mask, &prev_mask) == -1)
+    perror ("sigprocmask");
+
+  /* Execution resumes here after SIGCONT */
+
+  /* Reblock SIGTSTP */
+  if (sigprocmask (SIG_SETMASK, &prev_mask, NULL) == -1)
+    perror ("sigprocmask");
+
+  /* Reestablish handler */
+  sigemptyset (&sa.sa_mask);
+  sa.sa_flags = SA_RESTART;
+  sa.sa_handler = tstp_handler;
+  if (sigaction (SIGTSTP, &sa, NULL) == -1)
+    perror ("sigaction");
+
+#ifdef HAVE_LIBREADLINE
+  /* Restore readline state */
+  rl_reset_after_signal ();
+#endif
+
+  errno = saved_errno;
+}
+
 int
 main (int argc, char *argv[])
 {
@@ -439,6 +499,22 @@ main (int argc, char *argv[])
       exit (EXIT_FAILURE);
   }
 
+  /* Register ^Z handler. We need it to reset terminal colors
+   */
+  if (is_interactive) {
+    /* Only establish handler for SIGTSTP if it is not being ignored */
+    if (sigaction(SIGTSTP, NULL, &sa) == -1)
+      perror ("sigaction");
+
+    if (sa.sa_handler != SIG_IGN) {
+      sigemptyset(&sa.sa_mask);
+      sa.sa_flags = SA_RESTART;
+      sa.sa_handler = tstp_handler;
+      if (sigaction(SIGTSTP, &sa, NULL) == -1)
+        perror ("sigaction");
+    }
+  }
+
   /* Old-style -i syntax?  Since -a/-d/-N and -i was disallowed
    * previously, if we have -i without any drives but with something
    * on the command line, it must be old-style syntax.
-- 
2.5.0




More information about the Libguestfs mailing list