2 * Copyright (C) 2017 "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.
26 #include <sys/syscall.h>
28 #include "sig-monitor.h"
32 static _Thread_local sigjmp_buf *error_handler;
35 static _Thread_local int thread_timer_set;
36 static _Thread_local timer_t thread_timerid;
39 * Creates a timer for the current thread
41 * Returns 0 in case of success
43 static inline int timeout_create()
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);
57 sevp._sigev_un._tid = (pid_t)syscall(SYS_gettid);
59 rc = timer_create(CLOCK_THREAD_CPUTIME_ID, &sevp, &thread_timerid);
60 thread_timer_set = !rc;
66 * Arms the alarm in timeout seconds for the current thread
68 static inline int timeout_arm(int timeout)
71 struct itimerspec its;
73 rc = timeout_create();
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);
86 * Disarms the current alarm
88 static inline void timeout_disarm()
95 * Destroy any alarm resource for the current thread
97 static inline void timeout_delete()
99 if (thread_timer_set) {
100 timer_delete(thread_timerid);
101 thread_timer_set = 0;
106 /* Handles signals that terminate the process */
107 static void on_signal_terminate (int signum)
109 ERROR("Terminating signal %d received: %s", signum, strsignal(signum));
113 /* Handles monitored signals that can be continued */
114 static void on_signal_error(int signum)
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);
125 if (signum == SIGALRM)
127 ERROR("Unmonitored signal %d received: %s", signum, strsignal(signum));
131 /* install the handlers */
132 static int install(void (*handler)(int), int *signals)
135 while(*signals > 0) {
136 if (signal(*signals, handler) == SIG_ERR) {
137 ERROR("failed to install signal handler for signal %s", strsignal(*signals));
145 int sig_monitor_init()
147 static int sigerr[] = { SIGALRM, SIGSEGV, SIGFPE, 0 };
148 static int sigterm[] = { SIGINT, SIGABRT, 0 };
150 return (install(on_signal_error, sigerr) & install(on_signal_terminate, sigterm)) - 1;
153 int sig_monitor_init_timeouts()
155 return timeout_create();
158 void sig_monitor_clean_timeouts()
163 void sig_monitor(int timeout, void (*function)(int sig, void*), void *arg)
165 sig_monitor3(timeout, (void (*)(int,void*,void*,void*))function, arg, NULL, NULL);
168 void sig_monitor2(int timeout, void (*function)(int sig, void*, void*), void *arg1, void *arg2)
170 sig_monitor3(timeout, (void (*)(int,void*,void*,void*))function, arg1, arg2, NULL);
173 void sig_monitor3(int timeout, void (*function)(int sig, void*, void*, void*), void *arg1, void *arg2, void *arg3)
175 volatile int signum, signum2;
176 sigjmp_buf jmpbuf, *older;
178 older = error_handler;
179 signum = setjmp(jmpbuf);
181 error_handler = &jmpbuf;
183 timeout_arm(timeout);
184 function(0, arg1, arg2, arg3);
186 signum2 = setjmp(jmpbuf);
188 function(signum, arg1, arg2, arg3);
190 error_handler = older;