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 ERROR("ALERT! signal %d received: %s", signum, strsignal(signum));
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);
127 if (signum == SIGALRM)
129 ERROR("Unmonitored signal %d received: %s", signum, strsignal(signum));
133 /* install the handlers */
134 static int install(void (*handler)(int), int *signals)
137 while(*signals > 0) {
138 if (signal(*signals, handler) == SIG_ERR) {
139 ERROR("failed to install signal handler for signal %s", strsignal(*signals));
147 int sig_monitor_init()
149 static int sigerr[] = { SIGALRM, SIGSEGV, SIGFPE, 0 };
150 static int sigterm[] = { SIGINT, SIGABRT, 0 };
152 return (install(on_signal_error, sigerr) & install(on_signal_terminate, sigterm)) - 1;
155 int sig_monitor_init_timeouts()
157 return timeout_create();
160 void sig_monitor_clean_timeouts()
165 void sig_monitor(int timeout, void (*function)(int sig, void*), void *arg)
167 volatile int signum, signum2;
168 sigjmp_buf jmpbuf, *older;
170 older = error_handler;
171 signum = setjmp(jmpbuf);
173 error_handler = &jmpbuf;
175 timeout_arm(timeout);
178 signum2 = setjmp(jmpbuf);
180 function(signum, arg);
182 error_handler = older;