summaryrefslogtreecommitdiff
path: root/rts/posix
diff options
context:
space:
mode:
authorSimon Marlow <marlowsd@gmail.com>2011-01-07 12:40:42 +0000
committerSimon Marlow <marlowsd@gmail.com>2011-01-07 12:40:42 +0000
commit8625c675de45bdb8bcfa795572ce7c47687c147c (patch)
tree279efd8a2b66a8486e8e7859ce4415eff06753cc /rts/posix
parent62db6241c578f2b02e266b4dd0b535e0f59950bf (diff)
downloadhaskell-8625c675de45bdb8bcfa795572ce7c47687c147c.tar.gz
catch SIGTSTP and save/restore terminal settings (#4460)
As far as I can tell, it is the responsibility of the program to save and restore its own terminal settings across a suspend/foreground, the shell doesn't do it (which seems odd). So I've added a signal handler for SIGTSTP to the RTS which will save and restore the terminal settings iff we modified them with hSetBuffering or hSetEcho (we already restore them at exit time in these cases).
Diffstat (limited to 'rts/posix')
-rw-r--r--rts/posix/Signals.c77
1 files changed, 77 insertions, 0 deletions
diff --git a/rts/posix/Signals.c b/rts/posix/Signals.c
index e723b8ff0a..1e1bb3f13b 100644
--- a/rts/posix/Signals.c
+++ b/rts/posix/Signals.c
@@ -40,6 +40,10 @@
# include <sys/eventfd.h>
#endif
+#ifdef HAVE_TERMIOS_H
+#include <termios.h>
+#endif
+
#include <stdlib.h>
#include <string.h>
@@ -480,6 +484,75 @@ empty_handler (int sig STG_UNUSED)
}
/* -----------------------------------------------------------------------------
+ SIGTSTP handling
+
+ When a process is suspeended with ^Z and resumed again, the shell
+ makes no attempt to save and restore the terminal settings. So on
+ resume, any terminal setting modificaions we made (e.g. turning off
+ ICANON due to hSetBuffering NoBuffering) may well be lost. Hence,
+ we arrange to save and restore the terminal settings ourselves.
+
+ The trick we use is:
+ - catch SIGTSTP
+ - in the handler, kill(getpid(),SIGTSTP)
+ - when this returns, restore the TTY settings
+ This means we don't have to catch SIGCONT too.
+
+ -------------------------------------------------------------------------- */
+
+static void sigtstp_handler(int sig);
+static void set_sigtstp_action (rtsBool handle);
+
+static void
+sigtstp_handler (int sig)
+{
+ int fd;
+ struct termios ts[3];
+
+ // save the current TTY state for TTYs we modified
+ for (fd = 0; fd <= 2; fd++) {
+ if (__hscore_get_saved_termios(fd) != NULL) {
+ tcgetattr(fd,&ts[fd]);
+ }
+ }
+
+ // de-install the SIGTSTP handler
+ set_sigtstp_action(rtsFalse);
+
+ // really stop the process now
+ {
+ sigset_t mask;
+ sigemptyset(&mask);
+ sigaddset(&mask, sig);
+ sigprocmask(SIG_UNBLOCK, &mask, NULL);
+ kill(getpid(), sig);
+ }
+
+ // on return, restore the TTY state
+ for (fd = 0; fd <= 2; fd++) {
+ if (__hscore_get_saved_termios(fd) != NULL) {
+ tcsetattr(0,TCSANOW,&ts[fd]);
+ }
+ }
+
+ set_sigtstp_action(rtsTrue);
+}
+
+static void
+set_sigtstp_action (rtsBool handle)
+{
+ struct sigaction sa;
+ if (handle) {
+ sa.sa_handler = sigtstp_handler;
+ } else {
+ sa.sa_handler = SIG_DFL;
+ }
+ sa.sa_flags = 0;
+ sigemptyset(&sa.sa_mask);
+ sigaction(SIGTSTP, &sa, NULL);
+}
+
+/* -----------------------------------------------------------------------------
* Install default signal handlers.
*
* The RTS installs a default signal handler for catching
@@ -543,6 +616,8 @@ initDefaultHandlers(void)
if (sigaction(SIGPIPE, &action, &oact) != 0) {
sysErrorBelch("warning: failed to install SIGPIPE handler");
}
+
+ set_sigtstp_action(rtsTrue);
}
void
@@ -562,6 +637,8 @@ resetDefaultHandlers(void)
if (sigaction(SIGPIPE, &action, NULL) != 0) {
sysErrorBelch("warning: failed to uninstall SIGPIPE handler");
}
+
+ set_sigtstp_action(rtsFalse);
}
void