From 0f5bfeead29b3c567fc8c3cbb5338dee1b195755 Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Sun, 26 Jun 2016 17:09:29 -0700 Subject: add more ways to exit fish_key_reader A discussion on Gitter proposed allowing the user to signal their desire to exit fish_key_reader by pressing \cC or \cD twice in a row. This implements that. I also decided to refactor how signals are handled. Most notably receiving a signal will no longer print a diagnostic message unless you've enabled debugging with `-d2` (or higher level). --- src/fish_key_reader.cpp | 64 +++++++++++++++++++++++++++++++++++++------------ 1 file changed, 49 insertions(+), 15 deletions(-) diff --git a/src/fish_key_reader.cpp b/src/fish_key_reader.cpp index efac93b9..571e6e54 100644 --- a/src/fish_key_reader.cpp +++ b/src/fish_key_reader.cpp @@ -10,10 +10,10 @@ #include #include +#include #include #include #include -#include #include #include #include @@ -44,7 +44,9 @@ static bool should_exit(unsigned char c) { recent_chars[1] = recent_chars[2]; recent_chars[2] = recent_chars[3]; recent_chars[3] = c; - return memcmp(recent_chars, "exit", 4) == 0 || memcmp(recent_chars, "quit", 4) == 0; + return (memcmp(recent_chars, "exit", 4) == 0 || memcmp(recent_chars, "quit", 4) == 0 || + memcmp(recent_chars + 2, "\x3\x3", 2) == 0 || // ctrl-C, ctrl-C + memcmp(recent_chars + 2, "\x4\x4", 2) == 0); // ctrl-D, ctrl-D } /// Return the key name if the recent sequence of characters matches a known terminfo sequence. @@ -148,11 +150,50 @@ static void process_input(bool continuous_mode) { /// Make sure we cleanup before exiting if we receive a signal that should cause us to exit. /// Otherwise just report receipt of the signal. -static void signal_handler(int signo) { - printf("\nSignal #%d (%ls) received\n\n", signo, sig2wcs(signo)); - if (signo == SIGINT || signo == SIGTERM || signo == SIGABRT || signo == SIGSEGV) { +static struct sigaction old_sigactions[32]; +static void signal_handler(int signo, siginfo_t *siginfo, void *siginfo_arg) { + debug(2, L"signal #%d (%ls) received", signo, sig2wcs(signo)); + // SIGINT isn't included in the following conditional because it is handled specially by fish; + // i.e., it causes \cC to be reinserted into the tty input stream. + if (signo == SIGHUP || signo == SIGTERM || signo == SIGABRT || signo == SIGSEGV) { keep_running = false; } + if (old_sigactions[signo].sa_handler != SIG_IGN && + old_sigactions[signo].sa_handler != SIG_DFL) { + int needs_siginfo = old_sigactions[signo].sa_flags & SA_SIGINFO; + if (needs_siginfo) { + old_sigactions[signo].sa_sigaction(signo, siginfo, siginfo_arg); + } else { + old_sigactions[signo].sa_handler(signo); + } + } +} + +/// Install a handler for every signal. This allows us to restore the tty modes so the terminal is +/// still usable when we die. If the signal already has a handler arrange to invoke it from within +/// our handler. +static void install_our_signal_handlers() { + struct sigaction new_sa, old_sa; + sigemptyset(&new_sa.sa_mask); + new_sa.sa_flags = SA_SIGINFO; + new_sa.sa_sigaction = signal_handler; + + for (int signo = 1; signo < 32; signo++) { + if (sigaction(signo, &new_sa, &old_sa) != -1) { + memcpy(&old_sigactions[signo], &old_sa, sizeof(old_sa)); + if (old_sa.sa_handler == SIG_IGN) { + debug(2, "signal #%d (%ls) was being ignored", signo, sig2wcs(signo)); + } + if (old_sa.sa_flags && ~SA_SIGINFO != 0) { + debug(2, L"signal #%d (%ls) handler had flags 0x%X", signo, sig2wcs(signo), + old_sa.sa_flags); + } + if (old_sa.sa_mask != new_sa.sa_mask) { + debug(2, L"signal #%d (%ls) handler had mask 0x%X", signo, sig2wcs(signo), + old_sa.sa_mask); + } + } + } } /// Setup our environment (e.g., tty modes), process key strokes, then reset the environment. @@ -161,24 +202,17 @@ static void setup_and_process_keys(bool continuous_mode) { setenv("LC_ALL", "POSIX", 1); // ensure we're in a single-byte locale set_main_thread(); setup_fork_guards(); - - // Install a handler for every signal. This allows us to restore the tty modes so the terminal - // is still usable when we die. We do this only to ensure any signal not handled by - // signal_set_handlers() gets handled for a clean exit. - for (int signo = 1; signo < 32; signo++) { - signal(signo, signal_handler); - } - env_init(); reader_init(); input_init(); proc_push_interactive(1); signal_set_handlers(); + install_our_signal_handlers(); if (continuous_mode) { printf("\n"); - printf("To terminate this program type \"exit\" or \"quit\" in this window\n"); - printf("or \"kill %d\" in another window\n", getpid()); + printf("To terminate this program type \"exit\" or \"quit\" in this window,\n"); + printf("or press [ctrl-C] or [ctrl-D] twice in a row.\n"); printf("\n"); } -- cgit v1.2.3