2 * Copyright (C) 2017, 2018 "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.
27 #include <sys/syscall.h>
30 #include "sig-monitor.h"
33 #define SIG_FOR_TIMER SIGVTALRM
36 static _Thread_local sigjmp_buf *error_handler;
37 static _Thread_local int in_safe_dumpstack;
40 static _Thread_local int thread_timer_set;
41 static _Thread_local timer_t thread_timerid;
43 /* internal signal lists */
44 static int sigerr[] = { SIG_FOR_TIMER, SIGSEGV, SIGFPE, SIGILL, SIGBUS, 0 };
45 static int sigterm[] = { SIGINT, SIGABRT, SIGTERM, 0 };
46 static int exiting = 0;
47 static int enabled = 0;
50 * Dumps the current stack
52 static void dumpstack(int crop, int signum)
60 count = backtrace(addresses, sizeof addresses / sizeof *addresses);
64 locations = backtrace_symbols(&addresses[crop], count);
65 if (locations == NULL)
66 ERROR("can't get the backtrace (returned %d addresses)", count);
68 length = sizeof buffer - 1;
71 while (pos < length && idx < count) {
72 rc = snprintf(&buffer[pos], length - pos, " [%d/%d] %s\n", idx + 1, count, locations[idx]);
73 pos += rc >= 0 ? rc : 0;
78 ERROR("BACKTRACE due to signal %s/%d:\n%s", strsignal(signum), signum, buffer);
80 ERROR("BACKTRACE:\n%s", buffer);
85 static void safe_dumpstack_cb(int signum, void *closure)
89 ERROR("Can't provide backtrace: raised signal %s", strsignal(signum));
91 dumpstack(args[0], args[1]);
94 static void safe_dumpstack(int crop, int signum)
96 int args[2] = { crop + 3, signum };
98 in_safe_dumpstack = 1;
99 sig_monitor(0, safe_dumpstack_cb, args);
100 in_safe_dumpstack = 0;
104 * Creates a timer for the current thread
106 * Returns 0 in case of success
108 static inline int timeout_create()
111 struct sigevent sevp;
113 if (thread_timer_set)
116 sevp.sigev_notify = SIGEV_THREAD_ID;
117 sevp.sigev_signo = SIG_FOR_TIMER;
118 sevp.sigev_value.sival_ptr = NULL;
119 #if defined(sigev_notify_thread_id)
120 sevp.sigev_notify_thread_id = (pid_t)syscall(SYS_gettid);
122 sevp._sigev_un._tid = (pid_t)syscall(SYS_gettid);
124 rc = timer_create(CLOCK_THREAD_CPUTIME_ID, &sevp, &thread_timerid);
125 thread_timer_set = !rc;
131 * Arms the alarm in timeout seconds for the current thread
133 static inline int timeout_arm(int timeout)
136 struct itimerspec its;
138 rc = timeout_create();
140 its.it_interval.tv_sec = 0;
141 its.it_interval.tv_nsec = 0;
142 its.it_value.tv_sec = timeout;
143 its.it_value.tv_nsec = 0;
144 rc = timer_settime(thread_timerid, 0, &its, NULL);
151 * Disarms the current alarm
153 static inline void timeout_disarm()
155 if (thread_timer_set)
160 * Destroy any alarm resource for the current thread
162 static inline void timeout_delete()
164 if (thread_timer_set) {
165 timer_delete(thread_timerid);
166 thread_timer_set = 0;
170 /* install the handlers */
171 static int install(void (*handler)(int), int *signals)
176 sa.sa_handler = handler;
177 sigemptyset(&sa.sa_mask);
178 sa.sa_flags = SA_NODEFER;
179 while(*signals > 0) {
180 if (sigaction(*signals, &sa, NULL) < 0) {
181 ERROR("failed to install signal handler for signal %s: %m", strsignal(*signals));
192 static void on_rescue_exit(int signum)
194 ERROR("Rescue exit for signal %d: %s", signum, strsignal(signum));
201 static void safe_exit(int code)
203 install(on_rescue_exit, sigerr);
204 install(on_rescue_exit, sigterm);
209 /* Handles signals that terminate the process */
210 static void on_signal_terminate (int signum)
212 if (!in_safe_dumpstack) {
213 ERROR("Terminating signal %d received: %s", signum, strsignal(signum));
214 if (signum == SIGABRT)
215 safe_dumpstack(3, signum);
220 /* Handles monitored signals that can be continued */
221 static void on_signal_error(int signum)
223 if (in_safe_dumpstack)
224 longjmp(*error_handler, signum);
226 ERROR("ALERT! signal %d received: %s", signum, strsignal(signum));
227 if (error_handler == NULL && signum == SIG_FOR_TIMER)
230 safe_dumpstack(3, signum);
232 // unlock signal to allow a new signal to come
233 if (error_handler != NULL)
234 longjmp(*error_handler, signum);
236 ERROR("Unmonitored signal %d received: %s", signum, strsignal(signum));
240 void sig_monitor_disable()
243 install(SIG_DFL, sigerr);
244 install(SIG_DFL, sigterm);
247 int sig_monitor_enable()
249 enabled = install(on_signal_error, sigerr) && install(on_signal_terminate, sigterm);
252 sig_monitor_disable();
256 int sig_monitor_init(int enable)
258 return enable ? sig_monitor_enable() : (sig_monitor_disable(), 0);
261 int sig_monitor_init_timeouts()
263 return timeout_create();
266 void sig_monitor_clean_timeouts()
271 static void monitor(int timeout, void (*function)(int sig, void*), void *arg)
273 volatile int signum, signum2;
274 sigjmp_buf jmpbuf, *older;
276 older = error_handler;
277 signum = setjmp(jmpbuf);
279 error_handler = &jmpbuf;
282 timeout_arm(timeout);
286 signum2 = setjmp(jmpbuf);
288 function(signum, arg);
292 error_handler = older;
295 void sig_monitor(int timeout, void (*function)(int sig, void*), void *arg)
298 monitor(timeout, function, arg);