2 * Copyright (C) 2017 "IoT.bzh"
3 * Author José Bollo <jose.bollo@iot.bzh>
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
26 #include <sys/syscall.h>
29 #include "sig-monitor.h"
32 #define SIG_FOR_TIMER SIGVTALRM
35 static _Thread_local sigjmp_buf *error_handler;
36 static _Thread_local int in_safe_dumpstack;
39 static _Thread_local int thread_timer_set;
40 static _Thread_local timer_t thread_timerid;
43 * Dumps the current stack
45 static void dumpstack(int crop, int signum)
48 void *addresses[1000];
51 count = backtrace(addresses, sizeof addresses / sizeof *addresses);
52 locations = backtrace_symbols(addresses, count);
53 if (locations == NULL)
54 ERROR("can't get the backtrace (returned %d addresses)", count);
56 for (idx = crop; idx < count; idx++)
57 ERROR("[BACKTRACE %d/%d] %s", idx - crop + 1, count - crop, locations[idx]);
62 static void safe_dumpstack_cb(int signum, void *closure)
66 ERROR("Can't provide backtrace: raised signal %s", strsignal(signum));
68 dumpstack(args[0], args[1]);
71 static void safe_dumpstack(int crop, int signum)
73 int args[2] = { crop, signum };
75 in_safe_dumpstack = 1;
76 sig_monitor(0, safe_dumpstack_cb, args);
77 in_safe_dumpstack = 0;
81 * Creates a timer for the current thread
83 * Returns 0 in case of success
85 static inline int timeout_create()
93 sevp.sigev_notify = SIGEV_THREAD_ID;
94 sevp.sigev_signo = SIG_FOR_TIMER;
95 sevp.sigev_value.sival_ptr = NULL;
96 #if defined(sigev_notify_thread_id)
97 sevp.sigev_notify_thread_id = (pid_t)syscall(SYS_gettid);
99 sevp._sigev_un._tid = (pid_t)syscall(SYS_gettid);
101 rc = timer_create(CLOCK_THREAD_CPUTIME_ID, &sevp, &thread_timerid);
102 thread_timer_set = !rc;
108 * Arms the alarm in timeout seconds for the current thread
110 static inline int timeout_arm(int timeout)
113 struct itimerspec its;
115 rc = timeout_create();
117 its.it_interval.tv_sec = 0;
118 its.it_interval.tv_nsec = 0;
119 its.it_value.tv_sec = timeout;
120 its.it_value.tv_nsec = 0;
121 rc = timer_settime(thread_timerid, 0, &its, NULL);
128 * Disarms the current alarm
130 static inline void timeout_disarm()
132 if (thread_timer_set)
137 * Destroy any alarm resource for the current thread
139 static inline void timeout_delete()
141 if (thread_timer_set) {
142 timer_delete(thread_timerid);
143 thread_timer_set = 0;
148 /* Handles signals that terminate the process */
149 static void on_signal_terminate (int signum)
151 if (!in_safe_dumpstack) {
152 ERROR("Terminating signal %d received: %s", signum, strsignal(signum));
153 if (signum == SIGABRT)
154 safe_dumpstack(3, signum);
159 /* Handles monitored signals that can be continued */
160 static void on_signal_error(int signum)
164 if (in_safe_dumpstack)
165 longjmp(*error_handler, signum);
167 ERROR("ALERT! signal %d received: %s", signum, strsignal(signum));
168 if (error_handler == NULL && signum == SIG_FOR_TIMER)
171 safe_dumpstack(3, signum);
173 // unlock signal to allow a new signal to come
174 if (error_handler != NULL)
175 longjmp(*error_handler, signum);
177 ERROR("Unmonitored signal %d received: %s", signum, strsignal(signum));
181 /* install the handlers */
182 static int install(void (*handler)(int), int *signals)
187 sa.sa_handler = handler;
188 sigemptyset(&sa.sa_mask);
189 sa.sa_flags = SA_NODEFER;
190 while(*signals > 0) {
191 if (sigaction(*signals, &sa, NULL) < 0) {
192 ERROR("failed to install signal handler for signal %s: %m", strsignal(*signals));
200 int sig_monitor_init()
202 static int sigerr[] = { SIG_FOR_TIMER, SIGSEGV, SIGFPE, SIGILL, SIGBUS, 0 };
203 static int sigterm[] = { SIGINT, SIGABRT, SIGTERM, 0 };
205 return (install(on_signal_error, sigerr) & install(on_signal_terminate, sigterm)) - 1;
208 int sig_monitor_init_timeouts()
210 return timeout_create();
213 void sig_monitor_clean_timeouts()
218 void sig_monitor(int timeout, void (*function)(int sig, void*), void *arg)
220 volatile int signum, signum2;
221 sigjmp_buf jmpbuf, *older;
223 older = error_handler;
224 signum = setjmp(jmpbuf);
226 error_handler = &jmpbuf;
228 timeout_arm(timeout);
231 signum2 = setjmp(jmpbuf);
233 function(signum, arg);
235 error_handler = older;