/*
- * Copyright (C) 2017 "IoT.bzh"
+ * Copyright (C) 2017-2019 "IoT.bzh"
* Author José Bollo <jose.bollo@iot.bzh>
*
* Licensed under the Apache License, Version 2.0 (the "License");
#define _GNU_SOURCE
+/*******************************************************************************
+* sig-monitor is under the control of several compilation flags
+*******************************************************************************/
+
+/* controls whether to dump stack or not */
+#if !defined(WITH_SIG_MONITOR_DUMPSTACK)
+# define WITH_SIG_MONITOR_DUMPSTACK 1
+#endif
+
+/* control whether to monitor signals */
+#if !defined(WITH_SIG_MONITOR_SIGNALS)
+# define WITH_SIG_MONITOR_SIGNALS 1
+#endif
+
+/* controls whether to monitor calls */
+#if !defined(WITH_SIG_MONITOR_FOR_CALL)
+# define WITH_SIG_MONITOR_FOR_CALL 1
+#endif
+
+/* control whether to monitor timers */
+#if !defined(WITH_SIG_MONITOR_TIMERS)
+# define WITH_SIG_MONITOR_TIMERS 1
+#endif
+
+#if !WITH_SIG_MONITOR_SIGNALS
+# undef WITH_SIG_MONITOR_FOR_CALL
+# define WITH_SIG_MONITOR_FOR_CALL 0
+#endif
+
+#if !WITH_SIG_MONITOR_FOR_CALL
+# undef WITH_SIG_MONITOR_TIMERS
+# define WITH_SIG_MONITOR_TIMERS 0
+#endif
+
+/******************************************************************************/
+
#include <stdlib.h>
#include <stdio.h>
-#include <signal.h>
#include <string.h>
-#include <setjmp.h>
-#include <time.h>
#include <unistd.h>
-#include <sys/syscall.h>
-#include <execinfo.h>
#include "sig-monitor.h"
+
#include "verbose.h"
-#define SIG_FOR_TIMER SIGVTALRM
+/******************************************************************************/
+#if !WITH_SIG_MONITOR_DUMPSTACK
-/* local handler */
-static _Thread_local sigjmp_buf *error_handler;
-static _Thread_local int in_safe_dumpstack;
+static inline void dumpstack(int crop, int signum) {}
-/* local timers */
-static _Thread_local int thread_timer_set;
-static _Thread_local timer_t thread_timerid;
+#else
+
+#include <execinfo.h>
/*
* Dumps the current stack
}
}
-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]);
-}
+#endif
+/******************************************************************************/
+#if !WITH_SIG_MONITOR_TIMERS
-static void safe_dumpstack(int crop, int signum)
-{
- int args[2] = { crop, signum };
+static inline int timeout_create() { return 0; }
+static inline int timeout_arm(int timeout) { return 0; }
+static inline void timeout_disarm() {}
+static inline void timeout_delete() {}
- in_safe_dumpstack = 1;
- sig_monitor(0, safe_dumpstack_cb, args);
- in_safe_dumpstack = 0;
-}
+#define SIG_FOR_TIMER 0
+
+#else
+
+#include <time.h>
+#include <sys/syscall.h>
+#include <signal.h>
+
+#define SIG_FOR_TIMER SIGVTALRM
+
+/* local per thread timers */
+static _Thread_local int thread_timer_set;
+static _Thread_local timer_t thread_timerid;
/*
* Creates a timer for the current thread
rc = timer_create(CLOCK_THREAD_CPUTIME_ID, &sevp, &thread_timerid);
thread_timer_set = !rc;
}
- return 0;
+ return rc;
}
/*
thread_timer_set = 0;
}
}
+#endif
+/******************************************************************************/
+#if !WITH_SIG_MONITOR_FOR_CALL
+static inline void monitor_raise(int signum) {}
-/* Handles signals that terminate the process */
-static void on_signal_terminate (int signum)
+#else
+
+#include <setjmp.h>
+
+/* local handler */
+static _Thread_local sigjmp_buf *error_handler;
+
+static void monitor(int timeout, void (*function)(int sig, void*), void *arg)
{
- if (!in_safe_dumpstack) {
- ERROR("Terminating signal %d received: %s", signum, strsignal(signum));
- if (signum == SIGABRT)
- safe_dumpstack(3, signum);
+ volatile int signum, signum2;
+ sigjmp_buf jmpbuf, *older;
+
+ older = error_handler;
+ signum = setjmp(jmpbuf);
+ if (signum == 0) {
+ error_handler = &jmpbuf;
+ if (timeout) {
+ timeout_create();
+ timeout_arm(timeout);
+ }
+ function(0, arg);
+ } else {
+ signum2 = setjmp(jmpbuf);
+ if (signum2 == 0)
+ function(signum, arg);
}
- exit(1);
+ if (timeout)
+ timeout_disarm();
+ error_handler = older;
}
-/* Handles monitored signals that can be continued */
-static void on_signal_error(int signum)
+static inline void monitor_raise(int signum)
{
- if (in_safe_dumpstack)
+ if (error_handler != NULL)
longjmp(*error_handler, signum);
+}
+#endif
+/******************************************************************************/
+#if !WITH_SIG_MONITOR_SIGNALS
- ERROR("ALERT! signal %d received: %s", signum, strsignal(signum));
- if (error_handler == NULL && signum == SIG_FOR_TIMER)
- return;
+static inline int enable_signal_handling() { return 0; }
- safe_dumpstack(3, signum);
+#else
- // unlock signal to allow a new signal to come
- if (error_handler != NULL)
- longjmp(*error_handler, signum);
+#include <signal.h>
- ERROR("Unmonitored signal %d received: %s", signum, strsignal(signum));
- exit(2);
-}
+/* internal signal lists */
+static int sigerr[] = { SIGSEGV, SIGFPE, SIGILL, SIGBUS, SIG_FOR_TIMER, 0 };
+static int sigterm[] = { SIGINT, SIGABRT, SIGTERM, 0 };
+
+static int exiting = 0;
+static int enabled = 0;
/* install the handlers */
-static int install(void (*handler)(int), int *signals)
+static int set_signals_handler(void (*handler)(int), int *signals)
{
int result = 1;
struct sigaction sa;
return result;
}
-int sig_monitor_init()
+/*
+ * rescue exit
+ */
+static void on_rescue_exit(int signum)
{
- static int sigerr[] = { SIG_FOR_TIMER, SIGSEGV, SIGFPE, SIGILL, SIGBUS, 0 };
- static int sigterm[] = { SIGINT, SIGABRT, SIGTERM, 0 };
+ ERROR("Rescue exit for signal %d: %s", signum, strsignal(signum));
+ _exit(exiting);
+}
- return (install(on_signal_error, sigerr) & install(on_signal_terminate, sigterm)) - 1;
+/*
+ * Do a direct safe exit
+ */
+static void direct_safe_exit(int code)
+{
+ set_signals_handler(on_rescue_exit, sigerr);
+ set_signals_handler(on_rescue_exit, sigterm);
+ exiting = code;
+ exit(code);
+}
+
+/*
+ * Do a safe exit
+ */
+#if WITH_SIG_MONITOR_NO_DEFERRED_EXIT
+# define safe_exit(x) direct_safe_exit(x)
+#else
+#include "jobs.h"
+static void exit_job(int signum, void* arg)
+{
+ exiting = (int)(intptr_t)arg;
+ if (signum)
+ on_rescue_exit(signum);
+ exit(exiting);
+}
+
+static void safe_exit(int code)
+{
+ if (jobs_queue(safe_exit, 0, exit_job, (void*)(intptr_t)code))
+ direct_safe_exit(code);
+}
+#endif
+
+#if !WITH_SIG_MONITOR_DUMPSTACK
+
+static inline void safe_dumpstack(int crop, int signum) {}
+#define in_safe_dumpstack (0)
+
+#else
+
+static _Thread_local int in_safe_dumpstack;
+
+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 + 3, signum };
+
+ in_safe_dumpstack = 1;
+ sig_monitor(0, safe_dumpstack_cb, args);
+ in_safe_dumpstack = 0;
+}
+#endif
+
+/* Handles signals that terminate the process */
+static void on_signal_terminate (int signum)
+{
+ if (!in_safe_dumpstack) {
+ ERROR("Terminating signal %d received: %s", signum, strsignal(signum));
+ if (signum == SIGABRT)
+ safe_dumpstack(3, signum);
+ }
+ safe_exit(1);
+}
+
+/* Handles monitored signals that can be continued */
+static void on_signal_error(int signum)
+{
+ if (!in_safe_dumpstack) {
+ ERROR("ALERT! signal %d received: %s", signum, strsignal(signum));
+
+ safe_dumpstack(3, signum);
+ }
+ monitor_raise(signum);
+
+ if (signum != SIG_FOR_TIMER) {
+ ERROR("Unmonitored signal %d received: %s", signum, strsignal(signum));
+ safe_exit(2);
+ }
+}
+
+/*
+static void disable_signal_handling()
+{
+ set_signals_handler(SIG_DFL, sigerr);
+ set_signals_handler(SIG_DFL, sigterm);
+ enabled = 0;
+}
+*/
+
+static int enable_signal_handling()
+{
+ if (!set_signals_handler(on_signal_error, sigerr)
+ || !set_signals_handler(on_signal_terminate, sigterm)) {
+ return -1;
+ }
+ enabled = 1;
+ return 0;
+}
+#endif
+/******************************************************************************/
+
+int sig_monitor_init(int enable)
+{
+ return enable ? enable_signal_handling() : 0;
}
int sig_monitor_init_timeouts()
void sig_monitor(int timeout, void (*function)(int sig, void*), void *arg)
{
- volatile int signum, signum2;
- sigjmp_buf jmpbuf, *older;
-
- older = error_handler;
- signum = setjmp(jmpbuf);
- if (signum == 0) {
- error_handler = &jmpbuf;
- if (timeout)
- timeout_arm(timeout);
+#if WITH_SIG_MONITOR_SIGNALS && WITH_SIG_MONITOR_FOR_CALL
+ if (enabled)
+ monitor(timeout, function, arg);
+ else
+#endif
function(0, arg);
- } else {
- signum2 = setjmp(jmpbuf);
- if (signum2 == 0)
- function(signum, arg);
- }
- error_handler = older;
- if (timeout)
- timeout_disarm();
}
-
+void sig_monitor_dumpstack()
+{
+ return dumpstack(1, 0);
+}