Refactor of threading and signal 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         // unlock signal to allow a new signal to come
119         if (error_handler != NULL) {
120                 sigemptyset(&sigset);
121                 sigaddset(&sigset, signum);
122                 sigprocmask(SIG_UNBLOCK, &sigset, 0);
123                 longjmp(*error_handler, signum);
124         }
125         if (signum == SIGALRM)
126                 return;
127         ERROR("Unmonitored signal %d received: %s", signum, strsignal(signum));
128         exit(2);
129 }
130
131 /* install the handlers */
132 static int install(void (*handler)(int), int *signals)
133 {
134         int result = 1;
135         while(*signals > 0) {
136                 if (signal(*signals, handler) == SIG_ERR) {
137                         ERROR("failed to install signal handler for signal %s", strsignal(*signals));
138                         result = 0;
139                 }
140                 signals++;
141         }
142         return result;
143 }
144
145 int sig_monitor_init()
146 {
147         static int sigerr[] = { SIGALRM, SIGSEGV, SIGFPE, 0 };
148         static int sigterm[] = { SIGINT, SIGABRT, 0 };
149
150         return (install(on_signal_error, sigerr) & install(on_signal_terminate, sigterm)) - 1;
151 }
152
153 int sig_monitor_init_timeouts()
154 {
155         return timeout_create();
156 }
157
158 void sig_monitor_clean_timeouts()
159 {
160         timeout_delete();
161 }
162
163 void sig_monitor(int timeout, void (*function)(int sig, void*), void *arg)
164 {
165         sig_monitor3(timeout, (void (*)(int,void*,void*,void*))function, arg, NULL, NULL);
166 }
167
168 void sig_monitor2(int timeout, void (*function)(int sig, void*, void*), void *arg1, void *arg2)
169 {
170         sig_monitor3(timeout, (void (*)(int,void*,void*,void*))function, arg1, arg2, NULL);
171 }
172
173 void sig_monitor3(int timeout, void (*function)(int sig, void*, void*, void*), void *arg1, void *arg2, void *arg3)
174 {
175         volatile int signum, signum2;
176         sigjmp_buf jmpbuf, *older;
177
178         older = error_handler;
179         signum = setjmp(jmpbuf);
180         if (signum == 0) {
181                 error_handler = &jmpbuf;
182                 if (timeout)
183                         timeout_arm(timeout);
184                 function(0, arg1, arg2, arg3);
185         } else {
186                 signum2 = setjmp(jmpbuf);
187                 if (signum2 == 0)
188                         function(signum, arg1, arg2, arg3);
189         }
190         error_handler = older;
191         if (timeout)
192                 timeout_disarm();
193 }
194
195
196
197
198