Simplification of sig-monitor
[src/app-framework-binder.git] / src / sig-monitor.c
1 /*
2  * Copyright (C) 2017 "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 #include <stdlib.h>
21 #include <signal.h>
22 #include <string.h>
23 #include <setjmp.h>
24 #include <time.h>
25 #include <unistd.h>
26 #include <sys/syscall.h>
27
28 #include "sig-monitor.h"
29 #include "verbose.h"
30
31 /* local handler */
32 static _Thread_local sigjmp_buf *error_handler;
33
34 /* local timers */
35 static _Thread_local int thread_timer_set;
36 static _Thread_local timer_t thread_timerid;
37
38 /*
39  * Creates a timer for the current thread
40  *
41  * Returns 0 in case of success
42  */
43 static inline int timeout_create()
44 {
45         int rc;
46         struct sigevent sevp;
47
48         if (thread_timer_set)
49                 rc = 0;
50         else {
51                 sevp.sigev_notify = SIGEV_THREAD_ID;
52                 sevp.sigev_signo = SIGALRM;
53                 sevp.sigev_value.sival_ptr = NULL;
54 #if defined(sigev_notify_thread_id)
55                 sevp.sigev_notify_thread_id = (pid_t)syscall(SYS_gettid);
56 #else
57                 sevp._sigev_un._tid = (pid_t)syscall(SYS_gettid);
58 #endif
59                 rc = timer_create(CLOCK_THREAD_CPUTIME_ID, &sevp, &thread_timerid);
60                 thread_timer_set = !rc;
61         }
62         return 0;
63 }
64
65 /*
66  * Arms the alarm in timeout seconds for the current thread
67  */
68 static inline int timeout_arm(int timeout)
69 {
70         int rc;
71         struct itimerspec its;
72
73         rc = timeout_create();
74         if (rc == 0) {
75                 its.it_interval.tv_sec = 0;
76                 its.it_interval.tv_nsec = 0;
77                 its.it_value.tv_sec = timeout;
78                 its.it_value.tv_nsec = 0;
79                 rc = timer_settime(thread_timerid, 0, &its, NULL);
80         }
81
82         return rc;
83 }
84
85 /*
86  * Disarms the current alarm
87  */
88 static inline void timeout_disarm()
89 {
90         if (thread_timer_set)
91                 timeout_arm(0);
92 }
93
94 /*
95  * Destroy any alarm resource for the current thread
96  */
97 static inline void timeout_delete()
98 {
99         if (thread_timer_set) {
100                 timer_delete(thread_timerid);
101                 thread_timer_set = 0;
102         }
103 }
104
105
106 /* Handles signals that terminate the process */
107 static void on_signal_terminate (int signum)
108 {
109         ERROR("Terminating signal %d received: %s", signum, strsignal(signum));
110         exit(1);
111 }
112
113 /* Handles monitored signals that can be continued */
114 static void on_signal_error(int signum)
115 {
116         sigset_t sigset;
117
118         ERROR("ALERT! signal %d received: %s", signum, strsignal(signum));
119
120         // unlock signal to allow a new signal to come
121         if (error_handler != NULL) {
122                 sigemptyset(&sigset);
123                 sigaddset(&sigset, signum);
124                 sigprocmask(SIG_UNBLOCK, &sigset, 0);
125                 longjmp(*error_handler, signum);
126         }
127         if (signum == SIGALRM)
128                 return;
129         ERROR("Unmonitored signal %d received: %s", signum, strsignal(signum));
130         exit(2);
131 }
132
133 /* install the handlers */
134 static int install(void (*handler)(int), int *signals)
135 {
136         int result = 1;
137         while(*signals > 0) {
138                 if (signal(*signals, handler) == SIG_ERR) {
139                         ERROR("failed to install signal handler for signal %s", strsignal(*signals));
140                         result = 0;
141                 }
142                 signals++;
143         }
144         return result;
145 }
146
147 int sig_monitor_init()
148 {
149         static int sigerr[] = { SIGALRM, SIGSEGV, SIGFPE, 0 };
150         static int sigterm[] = { SIGINT, SIGABRT, 0 };
151
152         return (install(on_signal_error, sigerr) & install(on_signal_terminate, sigterm)) - 1;
153 }
154
155 int sig_monitor_init_timeouts()
156 {
157         return timeout_create();
158 }
159
160 void sig_monitor_clean_timeouts()
161 {
162         timeout_delete();
163 }
164
165 void sig_monitor(int timeout, void (*function)(int sig, void*), void *arg)
166 {
167         volatile int signum, signum2;
168         sigjmp_buf jmpbuf, *older;
169
170         older = error_handler;
171         signum = setjmp(jmpbuf);
172         if (signum == 0) {
173                 error_handler = &jmpbuf;
174                 if (timeout)
175                         timeout_arm(timeout);
176                 function(0, arg);
177         } else {
178                 signum2 = setjmp(jmpbuf);
179                 if (signum2 == 0)
180                         function(signum, arg);
181         }
182         error_handler = older;
183         if (timeout)
184                 timeout_disarm();
185 }
186
187
188
189
190