705928120730935060df61b5cd9a61bff02afce9
[src/app-framework-binder.git] / src / sig-monitor.c
1 /*
2  * Copyright (C) 2017-2019 "IoT.bzh"
3  * Author José Bollo <jose.bollo@iot.bzh>
4  *
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
8  *
9  *   http://www.apache.org/licenses/LICENSE-2.0
10  *
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.
16  */
17
18 #define _GNU_SOURCE
19
20 /*******************************************************************************
21 *  sig-monitor is under the control of several compilation flags
22 *******************************************************************************/
23
24 /* controls whether to dump stack or not */
25 #if !defined(WITH_SIG_MONITOR_DUMPSTACK)
26 #  define WITH_SIG_MONITOR_DUMPSTACK 1
27 #endif
28
29 /* control whether to monitor signals */
30 #if !defined(WITH_SIG_MONITOR_SIGNALS)
31 #  define WITH_SIG_MONITOR_SIGNALS 1
32 #endif
33
34 /* controls whether to monitor calls */
35 #if !defined(WITH_SIG_MONITOR_FOR_CALL)
36 #  define WITH_SIG_MONITOR_FOR_CALL 1
37 #endif
38
39 /* control whether to monitor timers */
40 #if !defined(WITH_SIG_MONITOR_TIMERS)
41 #  define WITH_SIG_MONITOR_TIMERS 1
42 #endif
43
44 #if !WITH_SIG_MONITOR_SIGNALS
45 #  undef WITH_SIG_MONITOR_FOR_CALL
46 #  define WITH_SIG_MONITOR_FOR_CALL 0
47 #endif
48
49 #if !WITH_SIG_MONITOR_FOR_CALL
50 #  undef WITH_SIG_MONITOR_TIMERS
51 #  define WITH_SIG_MONITOR_TIMERS 0
52 #endif
53
54 /******************************************************************************/
55
56 #include <stdlib.h>
57 #include <stdio.h>
58 #include <string.h>
59 #include <unistd.h>
60
61 #include "sig-monitor.h"
62
63 #include "verbose.h"
64
65 /******************************************************************************/
66 #if !WITH_SIG_MONITOR_DUMPSTACK
67
68 static inline void dumpstack(int crop, int signum) {}
69
70 #else
71
72 #include <execinfo.h>
73
74 /*
75  * Dumps the current stack
76  */
77 static void dumpstack(int crop, int signum)
78 {
79         int idx, count, rc;
80         void *addresses[100];
81         char **locations;
82         char buffer[8000];
83         size_t pos, length;
84
85         count = backtrace(addresses, sizeof addresses / sizeof *addresses);
86         if (count <= crop)
87                 crop = 0;
88         count -= crop;
89         locations = backtrace_symbols(&addresses[crop], count);
90         if (locations == NULL)
91                 ERROR("can't get the backtrace (returned %d addresses)", count);
92         else {
93                 length = sizeof buffer - 1;
94                 pos = 0;
95                 idx = 0;
96                 while (pos < length && idx < count) {
97                         rc = snprintf(&buffer[pos], length - pos, " [%d/%d] %s\n", idx + 1, count, locations[idx]);
98                         pos += rc >= 0 ? rc : 0;
99                         idx++;
100                 }
101                 buffer[length] = 0;
102                 if (signum)
103                         ERROR("BACKTRACE due to signal %s/%d:\n%s", strsignal(signum), signum, buffer);
104                 else
105                         ERROR("BACKTRACE:\n%s", buffer);
106                 free(locations);
107         }
108 }
109
110 #endif
111 /******************************************************************************/
112 #if !WITH_SIG_MONITOR_TIMERS
113
114 static inline int timeout_create() { return 0; }
115 static inline int timeout_arm(int timeout) { return 0; }
116 static inline void timeout_disarm() {}
117 static inline void timeout_delete() {}
118
119 #define SIG_FOR_TIMER   0
120
121 #else
122
123 #include <time.h>
124 #include <sys/syscall.h>
125 #include <signal.h>
126
127 #define SIG_FOR_TIMER   SIGVTALRM
128
129 /* local per thread timers */
130 static _Thread_local int thread_timer_set;
131 static _Thread_local timer_t thread_timerid;
132
133 /*
134  * Creates a timer for the current thread
135  *
136  * Returns 0 in case of success
137  */
138 static inline int timeout_create()
139 {
140         int rc;
141         struct sigevent sevp;
142
143         if (thread_timer_set)
144                 rc = 0;
145         else {
146                 sevp.sigev_notify = SIGEV_THREAD_ID;
147                 sevp.sigev_signo = SIG_FOR_TIMER;
148                 sevp.sigev_value.sival_ptr = NULL;
149 #if defined(sigev_notify_thread_id)
150                 sevp.sigev_notify_thread_id = (pid_t)syscall(SYS_gettid);
151 #else
152                 sevp._sigev_un._tid = (pid_t)syscall(SYS_gettid);
153 #endif
154                 rc = timer_create(CLOCK_THREAD_CPUTIME_ID, &sevp, &thread_timerid);
155                 thread_timer_set = !rc;
156         }
157         return rc;
158 }
159
160 /*
161  * Arms the alarm in timeout seconds for the current thread
162  */
163 static inline int timeout_arm(int timeout)
164 {
165         int rc;
166         struct itimerspec its;
167
168         rc = timeout_create();
169         if (rc == 0) {
170                 its.it_interval.tv_sec = 0;
171                 its.it_interval.tv_nsec = 0;
172                 its.it_value.tv_sec = timeout;
173                 its.it_value.tv_nsec = 0;
174                 rc = timer_settime(thread_timerid, 0, &its, NULL);
175         }
176
177         return rc;
178 }
179
180 /*
181  * Disarms the current alarm
182  */
183 static inline void timeout_disarm()
184 {
185         if (thread_timer_set)
186                 timeout_arm(0);
187 }
188
189 /*
190  * Destroy any alarm resource for the current thread
191  */
192 static inline void timeout_delete()
193 {
194         if (thread_timer_set) {
195                 timer_delete(thread_timerid);
196                 thread_timer_set = 0;
197         }
198 }
199 #endif
200 /******************************************************************************/
201 #if !WITH_SIG_MONITOR_FOR_CALL
202
203 static inline void monitor_raise(int signum) {}
204
205 #else
206
207 #include <setjmp.h>
208
209 /* local handler */
210 static _Thread_local sigjmp_buf *error_handler;
211
212 static void monitor(int timeout, void (*function)(int sig, void*), void *arg)
213 {
214         volatile int signum, signum2;
215         sigjmp_buf jmpbuf, *older;
216
217         older = error_handler;
218         signum = setjmp(jmpbuf);
219         if (signum == 0) {
220                 error_handler = &jmpbuf;
221                 if (timeout) {
222                         timeout_create();
223                         timeout_arm(timeout);
224                 }
225                 function(0, arg);
226         } else {
227                 signum2 = setjmp(jmpbuf);
228                 if (signum2 == 0)
229                         function(signum, arg);
230         }
231         if (timeout)
232                 timeout_disarm();
233         error_handler = older;
234 }
235
236 static inline void monitor_raise(int signum)
237 {
238         if (error_handler != NULL)
239                 longjmp(*error_handler, signum);
240 }
241 #endif
242 /******************************************************************************/
243 #if !WITH_SIG_MONITOR_SIGNALS
244
245 static inline int enable_signal_handling() { return 0; }
246
247 #else
248
249 #include <signal.h>
250
251 /* internal signal lists */
252 static int sigerr[] = { SIGSEGV, SIGFPE, SIGILL, SIGBUS, SIG_FOR_TIMER, 0 };
253 static int sigterm[] = { SIGINT, SIGABRT, SIGTERM, 0 };
254
255 static int exiting = 0;
256 static int enabled = 0;
257
258 /* install the handlers */
259 static int set_signals_handler(void (*handler)(int), int *signals)
260 {
261         int result = 1;
262         struct sigaction sa;
263
264         sa.sa_handler = handler;
265         sigemptyset(&sa.sa_mask);
266         sa.sa_flags = SA_NODEFER;
267         while(*signals > 0) {
268                 if (sigaction(*signals, &sa, NULL) < 0) {
269                         ERROR("failed to install signal handler for signal %s: %m", strsignal(*signals));
270                         result = 0;
271                 }
272                 signals++;
273         }
274         return result;
275 }
276
277 /*
278  * rescue exit
279  */
280 static void on_rescue_exit(int signum)
281 {
282         ERROR("Rescue exit for signal %d: %s", signum, strsignal(signum));
283         _exit(exiting);
284 }
285
286 /*
287  * Do a safe exit
288  */
289 static void safe_exit(int code)
290 {
291         set_signals_handler(on_rescue_exit, sigerr);
292         set_signals_handler(on_rescue_exit, sigterm);
293         exiting = code;
294         exit(code);
295 }
296
297 #if !WITH_SIG_MONITOR_DUMPSTACK
298
299 static inline void safe_dumpstack(int crop, int signum) {}
300 #define in_safe_dumpstack (0)
301
302 #else
303
304 static _Thread_local int in_safe_dumpstack;
305
306 static void safe_dumpstack_cb(int signum, void *closure)
307 {
308         int *args = closure;
309         if (signum)
310                 ERROR("Can't provide backtrace: raised signal %s", strsignal(signum));
311         else
312                 dumpstack(args[0], args[1]);
313 }
314
315 static void safe_dumpstack(int crop, int signum)
316 {
317         int args[2] = { crop + 3, signum };
318
319         in_safe_dumpstack = 1;
320         sig_monitor(0, safe_dumpstack_cb, args);
321         in_safe_dumpstack = 0;
322 }
323 #endif
324
325 /* Handles signals that terminate the process */
326 static void on_signal_terminate (int signum)
327 {
328         if (!in_safe_dumpstack) {
329                 ERROR("Terminating signal %d received: %s", signum, strsignal(signum));
330                 if (signum == SIGABRT)
331                         safe_dumpstack(3, signum);
332         }
333         safe_exit(1);
334 }
335
336 /* Handles monitored signals that can be continued */
337 static void on_signal_error(int signum)
338 {
339         if (!in_safe_dumpstack) {
340                 ERROR("ALERT! signal %d received: %s", signum, strsignal(signum));
341
342                 safe_dumpstack(3, signum);
343         }
344         monitor_raise(signum);
345
346         if (signum != SIG_FOR_TIMER) {
347                 ERROR("Unmonitored signal %d received: %s", signum, strsignal(signum));
348                 safe_exit(2);
349         }
350 }
351
352 /*
353 static void disable_signal_handling()
354 {
355         set_signals_handler(SIG_DFL, sigerr);
356         set_signals_handler(SIG_DFL, sigterm);
357         enabled = 0;
358 }
359 */
360
361 static int enable_signal_handling()
362 {
363         if (!set_signals_handler(on_signal_error, sigerr)
364              || !set_signals_handler(on_signal_terminate, sigterm)) {
365                 return -1;
366         }
367         enabled = 1;
368         return 0;
369 }
370 #endif
371 /******************************************************************************/
372
373 int sig_monitor_init(int enable)
374 {
375         return enable ? enable_signal_handling() : 0;
376 }
377
378 int sig_monitor_init_timeouts()
379 {
380         return timeout_create();
381 }
382
383 void sig_monitor_clean_timeouts()
384 {
385         timeout_delete();
386 }
387
388 void sig_monitor(int timeout, void (*function)(int sig, void*), void *arg)
389 {
390 #if WITH_SIG_MONITOR_SIGNALS && WITH_SIG_MONITOR_FOR_CALL
391         if (enabled)
392                 monitor(timeout, function, arg);
393         else
394 #endif
395                 function(0, arg);
396 }
397
398 void sig_monitor_dumpstack()
399 {
400         return dumpstack(1, 0);
401 }