From: Simon Marlow Date: Fri, 7 Jan 2011 12:40:42 +0000 (+0000) Subject: catch SIGTSTP and save/restore terminal settings (#4460) X-Git-Url: http://git.megacz.com/?p=ghc-hetmet.git;a=commitdiff_plain;h=8625c675de45bdb8bcfa795572ce7c47687c147c 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). --- diff --git a/rts/posix/Signals.c b/rts/posix/Signals.c index e723b8f..1e1bb3f 100644 --- a/rts/posix/Signals.c +++ b/rts/posix/Signals.c @@ -40,6 +40,10 @@ # include #endif +#ifdef HAVE_TERMIOS_H +#include +#endif + #include #include @@ -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