Skip to content

Commit 57b6f56

Browse files
committed
Also restore terminal state on SIGBUS and SIGPIPE
1 parent 6ff2203 commit 57b6f56

3 files changed

Lines changed: 25 additions & 7 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,7 @@ There are a few environment variables that affect the behaviour of all Turbo Vis
306306
* Support for Conpty's [`win32-input-mode`](https://github.com/microsoft/terminal/blob/37b0cfd32ba0aa54e0fe50bb158154d906472a89/doc/specs/%234999%20-%20Improved%20keyboard%20handling%20in%20Conpty.md) (available in WSL).
307307
* Support for [far2l](https://github.com/elfmz/far2l)'s terminal extensions.
308308
* Support for key modifiers (via `TIOCLINUX`) and mouse (via GPM) in the Linux console.
309-
* Custom signal handler that restores the terminal state before the program crashes.
309+
* Custom signal handler that restores the terminal state before the program gets terminated or suspended by these signals: `SIGINT`, `SIGQUIT`, `SIGILL`, `SIGABRT`, `SIGBUS`, `SIGFPE`, `SIGSEGV`, `SIGPIPE`, `SIGTERM`, `SIGTSTP`.
310310
* When `stderr` is a tty, messages written to it are redirected to a buffer to prevent them from messing up the display and are eventually printed to the console when exiting or suspending the application.
311311
* The buffer used for this purpose has a limited size, so writes to `stderr` will fail once the buffer is full. If you wish to preserve all of `stderr`, just redirect it into a file from the command line with `2>`.
312312

include/tvision/internal/sighandl.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ class SignalHandler
2121

2222
enum HandledSignals
2323
{
24-
SigInt, SigQuit, SigIll, SigAbrt, SigFpe, SigSegv, SigTerm, SigTstp,
25-
HandledSignalCount
24+
SigInt, SigQuit, SigIll, SigAbrt, SigBus, SigFpe, SigSegv, SigPipe,
25+
SigTerm, SigTstp, HandledSignalCount
2626
};
2727

2828
public:

source/platform/sighandl.cpp

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ namespace tvision
99

1010
std::atomic<SignalHandlerCallback *> SignalHandler::callback {nullptr};
1111
const int SignalHandler::handledSignals[HandledSignalCount] =
12-
{ SIGINT, SIGQUIT, SIGILL, SIGABRT, SIGFPE, SIGSEGV, SIGTERM, SIGTSTP };
12+
{ SIGINT, SIGQUIT, SIGILL, SIGABRT, SIGBUS, SIGFPE, SIGSEGV, SIGPIPE, SIGTERM, SIGTSTP };
1313

1414
static bool operator==(const struct sigaction &a, const struct sigaction &b) noexcept
1515
{
@@ -59,8 +59,10 @@ SignalHandler::HandlerInfo &SignalHandler::getHandlerInfo(int signo) noexcept
5959
case SIGQUIT: return infos[SigQuit];
6060
case SIGILL: return infos[SigIll];
6161
case SIGABRT: return infos[SigAbrt];
62+
case SIGBUS: return infos[SigBus];
6263
case SIGFPE: return infos[SigFpe];
6364
case SIGSEGV: return infos[SigSegv];
65+
case SIGPIPE: return infos[SigPipe];
6466
case SIGTERM: return infos[SigTerm];
6567
case SIGTSTP: return infos[SigTstp];
6668
default: abort();
@@ -78,22 +80,34 @@ void SignalHandler::handleSignal(int signo, siginfo_t *info, void *context)
7880
if ((callback = SignalHandler::callback) && handlerInfo.running.exchange(true) == false)
7981
{
8082
struct sigaction nextAction = handlerInfo.action;
83+
// Uninstall the current action, just in case this signal gets raised
84+
// again while invoking the callback.
8185
sigaction(signo, nullptr, &currentAction);
86+
// Invoke the callback, which should be signal-safe in theory.
8287
callback(true);
88+
// Install and invoke the action that was in place when we installed
89+
// our handler.
8390
sigaction(signo, &nextAction, nullptr);
8491
if (invokeHandlerOrDefault(signo, nextAction, info, context))
92+
// In some cases it is necessary to exit this handler.
8593
return;
94+
// If the process didn't get killed, get ready to resume normal process
95+
// execution.
8696
callback(false);
97+
// Reinstall the action that was in place when this handler was invoked.
8798
sigaction(signo, &currentAction, nullptr);
8899
handlerInfo.running = false;
89100
}
90101
else
91102
{
92-
// Just invoke the default handler.
103+
// In the unexpected case where our handler was invoked even though
104+
// it is already running or no callback was specified, just invoke the
105+
// default handler.
93106
struct sigaction sa = makeDefaultAction();
94107
sigaction(signo, &sa, &currentAction);
95108
if (invokeDefault(signo, info))
96109
return;
110+
// Reinstall the action that was in place when this handler was invoked.
97111
sigaction(signo, &currentAction, nullptr);
98112
}
99113
}
@@ -121,15 +135,19 @@ bool SignalHandler::invokeHandlerOrDefault( int signo, const struct sigaction &a
121135

122136
bool SignalHandler::invokeDefault(int signo, siginfo_t *info) noexcept
123137
{
124-
// In some cases the signal will be raised again after leaving the handler.
125-
if ((signo == SIGILL || signo == SIGFPE || signo == SIGSEGV) && info->si_code > 0)
138+
// Allow synchronous signals sent by the kernel to be raised again by exiting
139+
// the handler. This will preserve the original stack trace, si_addr, etc.
140+
if ( (signo == SIGILL || signo == SIGBUS || signo == SIGFPE || signo == SIGSEGV)
141+
&& info->si_code > 0 )
126142
return true;
127143
// Otherwise, raise the signal manually.
128144
sigset_t mask, oldMask;
129145
sigemptyset(&mask);
146+
// Unblock this signal.
130147
sigaddset(&mask, signo);
131148
sigprocmask(SIG_UNBLOCK, &mask, &oldMask);
132149
raise(signo);
150+
// If the process didn't get killed, restore the original mask.
133151
sigprocmask(SIG_SETMASK, &oldMask, nullptr);
134152
return false;
135153
}

0 commit comments

Comments
 (0)