aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/signal.cpp
diff options
context:
space:
mode:
authorGravatar David Adam <zanchey@ucc.gu.uwa.edu.au>2015-07-26 10:20:13 +0800
committerGravatar David Adam <zanchey@ucc.gu.uwa.edu.au>2015-07-26 10:20:13 +0800
commit3929e9de0e69666b37df87347d5ce15663e81347 (patch)
treeb2701c439c0260840ce1c68beaebf7de1178cc53 /src/signal.cpp
parent793e1afa084982dac92c4fe19e50c25e326a79c2 (diff)
parentf4d1657c22c81a7720a91026f915b80d2d6aa6e8 (diff)
Merge branch 'master' into iwyu
Diffstat (limited to 'src/signal.cpp')
-rw-r--r--src/signal.cpp695
1 files changed, 695 insertions, 0 deletions
diff --git a/src/signal.cpp b/src/signal.cpp
new file mode 100644
index 00000000..b13f0c01
--- /dev/null
+++ b/src/signal.cpp
@@ -0,0 +1,695 @@
+/** \file signal.c
+
+The library for various signal related issues
+
+*/
+
+#include "config.h" // IWYU pragma: keep
+
+#include <wchar.h>
+#include <stdio.h>
+#include <signal.h>
+#include <errno.h>
+
+#ifdef HAVE_SIGINFO_H
+#include <siginfo.h>
+#endif
+
+#include "common.h"
+#include "fallback.h" // IWYU pragma: keep
+#include "wutil.h"
+#include "signal.h"
+#include "event.h"
+#include "reader.h"
+#include "proc.h"
+
+
+/**
+ Struct describing an entry for the lookup table used to convert
+ between signal names and signal ids, etc.
+*/
+struct lookup_entry
+{
+ /**
+ Signal id
+ */
+ int signal;
+ /**
+ Signal name
+ */
+ const wchar_t *name;
+ /**
+ Signal description
+ */
+ const wchar_t *desc;
+};
+
+/**
+ The number of signal blocks in place. Increased by signal_block, decreased by signal_unblock.
+*/
+static int block_count=0;
+
+
+/**
+ Lookup table used to convert between signal names and signal ids,
+ etc.
+*/
+static const struct lookup_entry lookup[] =
+{
+#ifdef SIGHUP
+ {
+ SIGHUP,
+ L"SIGHUP",
+ N_(L"Terminal hung up")
+ }
+ ,
+#endif
+#ifdef SIGINT
+ {
+ SIGINT,
+ L"SIGINT",
+ N_(L"Quit request from job control (^C)")
+ }
+ ,
+#endif
+#ifdef SIGQUIT
+ {
+ SIGQUIT,
+ L"SIGQUIT",
+ N_(L"Quit request from job control with core dump (^\\)")
+ }
+ ,
+#endif
+#ifdef SIGILL
+ {
+ SIGILL,
+ L"SIGILL",
+ N_(L"Illegal instruction")
+ }
+ ,
+#endif
+#ifdef SIGTRAP
+ {
+ SIGTRAP,
+ L"SIGTRAP",
+ N_(L"Trace or breakpoint trap")
+ }
+ ,
+#endif
+#ifdef SIGABRT
+ {
+ SIGABRT,
+ L"SIGABRT",
+ N_(L"Abort")
+ }
+ ,
+#endif
+#ifdef SIGBUS
+ {
+ SIGBUS,
+ L"SIGBUS",
+ N_(L"Misaligned address error")
+ }
+ ,
+#endif
+#ifdef SIGFPE
+ {
+ SIGFPE,
+ L"SIGFPE",
+ N_(L"Floating point exception")
+ }
+ ,
+#endif
+#ifdef SIGKILL
+ {
+ SIGKILL,
+ L"SIGKILL",
+ N_(L"Forced quit")
+ }
+ ,
+#endif
+#ifdef SIGUSR1
+ {
+ SIGUSR1,
+ L"SIGUSR1",
+ N_(L"User defined signal 1")
+ }
+ ,
+#endif
+#ifdef SIGUSR2
+ {
+ SIGUSR2, L"SIGUSR2",
+ N_(L"User defined signal 2")
+ }
+ ,
+#endif
+#ifdef SIGSEGV
+ {
+ SIGSEGV,
+ L"SIGSEGV",
+ N_(L"Address boundary error")
+ }
+ ,
+#endif
+#ifdef SIGPIPE
+ {
+ SIGPIPE,
+ L"SIGPIPE",
+ N_(L"Broken pipe")
+ }
+ ,
+#endif
+#ifdef SIGALRM
+ {
+ SIGALRM,
+ L"SIGALRM",
+ N_(L"Timer expired")
+ }
+ ,
+#endif
+#ifdef SIGTERM
+ {
+ SIGTERM,
+ L"SIGTERM",
+ N_(L"Polite quit request")
+ }
+ ,
+#endif
+#ifdef SIGCHLD
+ {
+ SIGCHLD,
+ L"SIGCHLD",
+ N_(L"Child process status changed")
+ }
+ ,
+#endif
+#ifdef SIGCONT
+ {
+ SIGCONT,
+ L"SIGCONT",
+ N_(L"Continue previously stopped process")
+ }
+ ,
+#endif
+#ifdef SIGSTOP
+ {
+ SIGSTOP,
+ L"SIGSTOP",
+ N_(L"Forced stop")
+ }
+ ,
+#endif
+#ifdef SIGTSTP
+ {
+ SIGTSTP,
+ L"SIGTSTP",
+ N_(L"Stop request from job control (^Z)")
+ }
+ ,
+#endif
+#ifdef SIGTTIN
+ {
+ SIGTTIN,
+ L"SIGTTIN",
+ N_(L"Stop from terminal input")
+ }
+ ,
+#endif
+#ifdef SIGTTOU
+ {
+ SIGTTOU,
+ L"SIGTTOU",
+ N_(L"Stop from terminal output")
+ }
+ ,
+#endif
+#ifdef SIGURG
+ {
+ SIGURG,
+ L"SIGURG",
+ N_(L"Urgent socket condition")
+ }
+ ,
+#endif
+#ifdef SIGXCPU
+ {
+ SIGXCPU,
+ L"SIGXCPU",
+ N_(L"CPU time limit exceeded")
+ }
+ ,
+#endif
+#ifdef SIGXFSZ
+ {
+ SIGXFSZ,
+ L"SIGXFSZ",
+ N_(L"File size limit exceeded")
+ }
+ ,
+#endif
+#ifdef SIGVTALRM
+ {
+ SIGVTALRM,
+ L"SIGVTALRM",
+ N_(L"Virtual timer expired")
+ }
+ ,
+#endif
+#ifdef SIGPROF
+ {
+ SIGPROF,
+ L"SIGPROF",
+ N_(L"Profiling timer expired")
+ }
+ ,
+#endif
+#ifdef SIGWINCH
+ {
+ SIGWINCH,
+ L"SIGWINCH",
+ N_(L"Window size change")
+ }
+ ,
+#endif
+#ifdef SIGWIND
+ {
+ SIGWIND,
+ L"SIGWIND",
+ N_(L"Window size change")
+ }
+ ,
+#endif
+#ifdef SIGIO
+ {
+ SIGIO,
+ L"SIGIO",
+ N_(L"I/O on asynchronous file descriptor is possible")
+ }
+ ,
+#endif
+#ifdef SIGPWR
+ {
+ SIGPWR,
+ L"SIGPWR",
+ N_(L"Power failure")
+ }
+ ,
+#endif
+#ifdef SIGSYS
+ {
+ SIGSYS,
+ L"SIGSYS",
+ N_(L"Bad system call")
+ }
+ ,
+#endif
+#ifdef SIGINFO
+ {
+ SIGINFO,
+ L"SIGINFO",
+ N_(L"Information request")
+ }
+ ,
+#endif
+#ifdef SIGSTKFLT
+ {
+ SIGSTKFLT,
+ L"SISTKFLT",
+ N_(L"Stack fault")
+ }
+ ,
+#endif
+#ifdef SIGEMT
+ {
+ SIGEMT,
+ L"SIGEMT",
+ N_(L"Emulator trap")
+ }
+ ,
+#endif
+#ifdef SIGIOT
+ {
+ SIGIOT,
+ L"SIGIOT",
+ N_(L"Abort (Alias for SIGABRT)")
+ }
+ ,
+#endif
+#ifdef SIGUNUSED
+ {
+ SIGUNUSED,
+ L"SIGUNUSED",
+ N_(L"Unused signal")
+ }
+ ,
+#endif
+ {
+ 0,
+ 0,
+ 0
+ }
+}
+;
+
+
+/**
+ Test if \c name is a string describing the signal named \c canonical.
+*/
+static int match_signal_name(const wchar_t *canonical,
+ const wchar_t *name)
+{
+ if (wcsncasecmp(name, L"sig", 3)==0)
+ name +=3;
+
+ return wcscasecmp(canonical+3,name) == 0;
+}
+
+
+int wcs2sig(const wchar_t *str)
+{
+ int i;
+ wchar_t *end=0;
+
+ for (i=0; lookup[i].desc ; i++)
+ {
+ if (match_signal_name(lookup[i].name, str))
+ {
+ return lookup[i].signal;
+ }
+ }
+ errno=0;
+ int res = fish_wcstoi(str, &end, 10);
+ if (!errno && res>=0 && !*end)
+ return res;
+
+ return -1;
+}
+
+
+const wchar_t *sig2wcs(int sig)
+{
+ int i;
+
+ for (i=0; lookup[i].desc ; i++)
+ {
+ if (lookup[i].signal == sig)
+ {
+ return lookup[i].name;
+ }
+ }
+
+ return _(L"Unknown");
+}
+
+const wchar_t *signal_get_desc(int sig)
+{
+ int i;
+
+ for (i=0; lookup[i].desc ; i++)
+ {
+ if (lookup[i].signal == sig)
+ {
+ return _(lookup[i].desc);
+ }
+ }
+
+ return _(L"Unknown");
+}
+
+/**
+ Standard signal handler
+*/
+static void default_handler(int signal, siginfo_t *info, void *context)
+{
+ if (event_is_signal_observed(signal))
+ {
+ event_fire_signal(signal);
+ }
+}
+
+/**
+ Respond to a winch signal by checking the terminal size
+*/
+static void handle_winch(int sig, siginfo_t *info, void *context)
+{
+ common_handle_winch(sig);
+ default_handler(sig, 0, 0);
+}
+
+/**
+ Respond to a hup signal by exiting, unless it is caught by a
+ shellscript function, in which case we do nothing.
+*/
+static void handle_hup(int sig, siginfo_t *info, void *context)
+{
+ if (event_is_signal_observed(SIGHUP))
+ {
+ default_handler(sig, 0, 0);
+ }
+ else
+ {
+ reader_exit(1, 1);
+ }
+}
+
+/** Handle sigterm. The only thing we do is restore the front process ID, then die. */
+static void handle_term(int sig, siginfo_t *info, void *context)
+{
+ restore_term_foreground_process_group();
+ signal(SIGTERM, SIG_DFL);
+ raise(SIGTERM);
+}
+
+/**
+ Interactive mode ^C handler. Respond to int signal by setting
+ interrupted-flag and stopping all loops and conditionals.
+*/
+static void handle_int(int sig, siginfo_t *info, void *context)
+{
+ reader_handle_int(sig);
+ default_handler(sig, info, context);
+}
+
+/**
+ sigchld handler. Does notification and calls the handler in proc.c
+*/
+static void handle_chld(int sig, siginfo_t *info, void *context)
+{
+ job_handle_signal(sig, info, context);
+ default_handler(sig, info, context);
+}
+
+void signal_reset_handlers()
+{
+ int i;
+
+ struct sigaction act;
+ sigemptyset(& act.sa_mask);
+ act.sa_flags=0;
+ act.sa_handler=SIG_DFL;
+
+ for (i=0; lookup[i].desc ; i++)
+ {
+ sigaction(lookup[i].signal, &act, 0);
+ }
+}
+
+
+/**
+ Sets appropriate signal handlers.
+*/
+void signal_set_handlers()
+{
+ struct sigaction act;
+
+ if (get_is_interactive() == -1)
+ return;
+
+ sigemptyset(& act.sa_mask);
+ act.sa_flags=SA_SIGINFO;
+ act.sa_sigaction = &default_handler;
+
+ /*
+ First reset everything to a use default_handler, a function
+ whose sole action is to fire of an event
+ */
+ sigaction(SIGINT, &act, 0);
+ sigaction(SIGQUIT, &act, 0);
+ sigaction(SIGTSTP, &act, 0);
+ sigaction(SIGTTIN, &act, 0);
+ sigaction(SIGTTOU, &act, 0);
+ sigaction(SIGCHLD, &act, 0);
+
+ /*
+ Ignore sigpipe, it is generated if fishd dies, but we can
+ recover.
+ */
+ sigaction(SIGPIPE, &act, 0);
+
+ if (get_is_interactive())
+ {
+ /*
+ Interactive mode. Ignore interactive signals. We are a
+ shell, we know whats best for the user. ;-)
+ */
+
+ act.sa_handler=SIG_IGN;
+
+ sigaction(SIGINT, &act, 0);
+ sigaction(SIGQUIT, &act, 0);
+ sigaction(SIGTSTP, &act, 0);
+ sigaction(SIGTTIN, &act, 0);
+ sigaction(SIGTTOU, &act, 0);
+
+ act.sa_sigaction = &handle_int;
+ act.sa_flags = SA_SIGINFO;
+ if (sigaction(SIGINT, &act, 0))
+ {
+ wperror(L"sigaction");
+ FATAL_EXIT();
+ }
+
+ act.sa_sigaction = &handle_chld;
+ act.sa_flags = SA_SIGINFO;
+ if (sigaction(SIGCHLD, &act, 0))
+ {
+ wperror(L"sigaction");
+ FATAL_EXIT();
+ }
+
+#ifdef SIGWINCH
+ act.sa_flags = SA_SIGINFO;
+ act.sa_sigaction= &handle_winch;
+ if (sigaction(SIGWINCH, &act, 0))
+ {
+ wperror(L"sigaction");
+ FATAL_EXIT();
+ }
+#endif
+
+ act.sa_flags = SA_SIGINFO;
+ act.sa_sigaction= &handle_hup;
+ if (sigaction(SIGHUP, &act, 0))
+ {
+ wperror(L"sigaction");
+ FATAL_EXIT();
+ }
+
+ // SIGTERM restores the terminal controlling process before dying
+ act.sa_flags = SA_SIGINFO;
+ act.sa_sigaction= &handle_term;
+ if (sigaction(SIGTERM, &act, 0))
+ {
+ wperror(L"sigaction");
+ FATAL_EXIT();
+ }
+
+ }
+ else
+ {
+ /*
+ Non-interactive. Ignore interrupt, check exit status of
+ processes to determine result instead.
+ */
+ act.sa_handler=SIG_IGN;
+
+ sigaction(SIGINT, &act, 0);
+ sigaction(SIGQUIT, &act, 0);
+
+ act.sa_handler=SIG_DFL;
+
+ act.sa_sigaction = &handle_chld;
+ act.sa_flags = SA_SIGINFO;
+ if (sigaction(SIGCHLD, &act, 0))
+ {
+ wperror(L"sigaction");
+ exit_without_destructors(1);
+ }
+ }
+
+}
+
+void signal_handle(int sig, int do_handle)
+{
+ struct sigaction act;
+
+ /*
+ These should always be handled
+ */
+ if ((sig == SIGINT) ||
+ (sig == SIGQUIT) ||
+ (sig == SIGTSTP) ||
+ (sig == SIGTTIN) ||
+ (sig == SIGTTOU) ||
+ (sig == SIGCHLD))
+ return;
+
+ sigemptyset(&act.sa_mask);
+ if (do_handle)
+ {
+ act.sa_flags = SA_SIGINFO;
+ act.sa_sigaction = &default_handler;
+ }
+ else
+ {
+ act.sa_flags = 0;
+ act.sa_handler = SIG_DFL;
+ }
+
+ sigaction(sig, &act, 0);
+}
+
+void get_signals_with_handlers(sigset_t *set)
+{
+ sigemptyset(set);
+ for (int i=0; lookup[i].desc ; i++)
+ {
+ struct sigaction act = {};
+ sigaction(lookup[i].signal, NULL, &act);
+ if (act.sa_handler != SIG_DFL)
+ sigaddset(set, lookup[i].signal);
+ }
+}
+
+void signal_block()
+{
+ ASSERT_IS_MAIN_THREAD();
+ sigset_t chldset;
+
+ if (!block_count)
+ {
+ sigfillset(&chldset);
+ VOMIT_ON_FAILURE(pthread_sigmask(SIG_BLOCK, &chldset, NULL));
+ }
+
+ block_count++;
+// debug( 0, L"signal block level increased to %d", block_count );
+}
+
+void signal_unblock()
+{
+ ASSERT_IS_MAIN_THREAD();
+ sigset_t chldset;
+
+ block_count--;
+
+ if (block_count < 0)
+ {
+ debug(0, _(L"Signal block mismatch"));
+ bugreport();
+ FATAL_EXIT();
+ }
+
+ if (!block_count)
+ {
+ sigfillset(&chldset);
+ VOMIT_ON_FAILURE(pthread_sigmask(SIG_UNBLOCK, &chldset, 0));
+ }
+// debug( 0, L"signal block level decreased to %d", block_count );
+}
+
+bool signal_is_blocked()
+{
+ return !!block_count;
+}
+