summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/sig-monitor.c50
1 files changed, 38 insertions, 12 deletions
diff --git a/src/sig-monitor.c b/src/sig-monitor.c
index fbc5b1fb..a291cd9f 100644
--- a/src/sig-monitor.c
+++ b/src/sig-monitor.c
@@ -33,6 +33,7 @@
/* local handler */
static _Thread_local sigjmp_buf *error_handler;
+static _Thread_local int in_safe_dumpstack;
/* local timers */
static _Thread_local int thread_timer_set;
@@ -41,7 +42,7 @@ static _Thread_local timer_t thread_timerid;
/*
* Dumps the current stack
*/
-static void dumpstack(int crop)
+static void dumpstack(int crop, int signum)
{
int idx, count;
void *addresses[1000];
@@ -58,6 +59,24 @@ static void dumpstack(int crop)
}
}
+static void safe_dumpstack_cb(int signum, void *closure)
+{
+ int *args = closure;
+ if (signum)
+ ERROR("Can't provide backtrace: raised signal %s", strsignal(signum));
+ else
+ dumpstack(args[0], args[1]);
+}
+
+static void safe_dumpstack(int crop, int signum)
+{
+ int args[2] = { crop, signum };
+
+ in_safe_dumpstack = 1;
+ sig_monitor(0, safe_dumpstack_cb, args);
+ in_safe_dumpstack = 0;
+}
+
/*
* Creates a timer for the current thread
*
@@ -129,9 +148,11 @@ static inline void timeout_delete()
/* Handles signals that terminate the process */
static void on_signal_terminate (int signum)
{
- ERROR("Terminating signal %d received: %s", signum, strsignal(signum));
- if (signum == SIGABRT)
- dumpstack(3);
+ if (!in_safe_dumpstack) {
+ ERROR("Terminating signal %d received: %s", signum, strsignal(signum));
+ if (signum == SIGABRT)
+ safe_dumpstack(3, signum);
+ }
exit(1);
}
@@ -140,19 +161,19 @@ static void on_signal_error(int signum)
{
sigset_t sigset;
+ if (in_safe_dumpstack)
+ longjmp(*error_handler, signum);
+
ERROR("ALERT! signal %d received: %s", signum, strsignal(signum));
if (error_handler == NULL && signum == SIG_FOR_TIMER)
return;
- dumpstack(3);
+ safe_dumpstack(3, signum);
// unlock signal to allow a new signal to come
- if (error_handler != NULL) {
- sigemptyset(&sigset);
- sigaddset(&sigset, signum);
- sigprocmask(SIG_UNBLOCK, &sigset, 0);
+ if (error_handler != NULL)
longjmp(*error_handler, signum);
- }
+
ERROR("Unmonitored signal %d received: %s", signum, strsignal(signum));
exit(2);
}
@@ -161,9 +182,14 @@ static void on_signal_error(int signum)
static int install(void (*handler)(int), int *signals)
{
int result = 1;
+ struct sigaction sa;
+
+ sa.sa_handler = handler;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = SA_NODEFER;
while(*signals > 0) {
- if (signal(*signals, handler) == SIG_ERR) {
- ERROR("failed to install signal handler for signal %s", strsignal(*signals));
+ if (sigaction(*signals, &sa, NULL) < 0) {
+ ERROR("failed to install signal handler for signal %s: %m", strsignal(*signals));
result = 0;
}
signals++;