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