]> git.proxmox.com Git - mirror_frr.git/blob - lib/sigevent.c
Merge pull request #12798 from donaldsharp/rib_match_multicast
[mirror_frr.git] / lib / sigevent.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /* Quagga signal handling functions.
3 * Copyright (C) 2004 Paul Jakma,
4 */
5
6 #include <zebra.h>
7 #include <sigevent.h>
8 #include <log.h>
9 #include <memory.h>
10 #include <lib_errors.h>
11
12 #ifdef HAVE_UCONTEXT_H
13 #ifdef GNU_LINUX
14 /* get REG_EIP from ucontext.h */
15 #ifndef __USE_GNU
16 #define __USE_GNU
17 #endif /* __USE_GNU */
18 #endif /* GNU_LINUX */
19 #include <ucontext.h>
20 #endif /* HAVE_UCONTEXT_H */
21
22
23 /* master signals descriptor struct */
24 static struct frr_sigevent_master_t {
25 struct thread *t;
26
27 struct frr_signal_t *signals;
28 int sigc;
29
30 volatile sig_atomic_t caught;
31 } sigmaster;
32
33 /* Generic signal handler
34 * Schedules signal event thread
35 */
36 static void frr_signal_handler(int signo)
37 {
38 int i;
39 struct frr_signal_t *sig;
40
41 for (i = 0; i < sigmaster.sigc; i++) {
42 sig = &(sigmaster.signals[i]);
43
44 if (sig->signal == signo)
45 sig->caught = 1;
46 }
47
48 sigmaster.caught = 1;
49 }
50
51 /*
52 * Check whether any signals have been received and are pending. This is done
53 * with the application's key signals blocked. The complete set of signals
54 * is returned in 'setp', so the caller can restore them when appropriate.
55 * If there are pending signals, returns 'true', 'false' otherwise.
56 */
57 bool frr_sigevent_check(sigset_t *setp)
58 {
59 sigset_t blocked;
60 int i;
61 bool ret;
62
63 sigemptyset(setp);
64 sigemptyset(&blocked);
65
66 /* Set up mask of application's signals */
67 for (i = 0; i < sigmaster.sigc; i++)
68 sigaddset(&blocked, sigmaster.signals[i].signal);
69
70 pthread_sigmask(SIG_BLOCK, &blocked, setp);
71
72 /* Now that the application's signals are blocked, test. */
73 ret = (sigmaster.caught != 0);
74
75 return ret;
76 }
77
78 /* check if signals have been caught and run appropriate handlers */
79 int frr_sigevent_process(void)
80 {
81 struct frr_signal_t *sig;
82 int i;
83 #ifdef SIGEVENT_BLOCK_SIGNALS
84 /* shouldn't need to block signals, but potentially may be needed */
85 sigset_t newmask, oldmask;
86
87 /*
88 * Block most signals, but be careful not to defer SIGTRAP because
89 * doing so breaks gdb, at least on NetBSD 2.0. Avoid asking to
90 * block SIGKILL, just because we shouldn't be able to do so.
91 */
92 sigfillset(&newmask);
93 sigdelset(&newmask, SIGTRAP);
94 sigdelset(&newmask, SIGKILL);
95
96 if ((sigprocmask(SIG_BLOCK, &newmask, &oldmask)) < 0) {
97 flog_err_sys(EC_LIB_SYSTEM_CALL,
98 "frr_signal_timer: couldnt block signals!");
99 return -1;
100 }
101 #endif /* SIGEVENT_BLOCK_SIGNALS */
102
103 if (sigmaster.caught > 0) {
104 sigmaster.caught = 0;
105 /* must not read or set sigmaster.caught after here,
106 * race condition with per-sig caught flags if one does
107 */
108
109 for (i = 0; i < sigmaster.sigc; i++) {
110 sig = &(sigmaster.signals[i]);
111
112 if (sig->caught > 0) {
113 sig->caught = 0;
114 if (sig->handler)
115 sig->handler();
116 }
117 }
118 }
119
120 #ifdef SIGEVENT_BLOCK_SIGNALS
121 if (sigprocmask(SIG_UNBLOCK, &oldmask, NULL) < 0)
122 return -1;
123 #endif /* SIGEVENT_BLOCK_SIGNALS */
124
125 return 0;
126 }
127
128 #ifdef SIGEVENT_SCHEDULE_THREAD
129 /* timer thread to check signals. shouldn't be needed */
130 void frr_signal_timer(struct thread *t)
131 {
132 struct frr_sigevent_master_t *sigm;
133
134 sigm = THREAD_ARG(t);
135 sigm->t = NULL;
136 thread_add_timer(sigm->t->master, frr_signal_timer, &sigmaster,
137 FRR_SIGNAL_TIMER_INTERVAL, &sigm->t);
138 frr_sigevent_process();
139 }
140 #endif /* SIGEVENT_SCHEDULE_THREAD */
141
142 /* Initialization of signal handles. */
143 /* Signal wrapper. */
144 static int signal_set(int signo)
145 {
146 int ret;
147 struct sigaction sig;
148 struct sigaction osig;
149
150 sig.sa_handler = &frr_signal_handler;
151 sigfillset(&sig.sa_mask);
152 sig.sa_flags = 0;
153 if (signo == SIGALRM) {
154 #ifdef SA_INTERRUPT
155 sig.sa_flags |= SA_INTERRUPT; /* SunOS */
156 #endif
157 } else {
158 #ifdef SA_RESTART
159 sig.sa_flags |= SA_RESTART;
160 #endif /* SA_RESTART */
161 }
162
163 ret = sigaction(signo, &sig, &osig);
164 if (ret < 0)
165 return ret;
166 else
167 return 0;
168 }
169
170 /* XXX This function should be enhanced to support more platforms
171 (it currently works only on Linux/x86). */
172 static void *program_counter(void *context)
173 {
174 #ifdef HAVE_UCONTEXT_H
175 #ifdef GNU_LINUX
176 /* these are from GNU libc, rather than Linux, strictly speaking */
177 #if defined(REG_EIP)
178 # define REG_INDEX REG_EIP
179 #elif defined(REG_RIP)
180 # define REG_INDEX REG_RIP
181 #elif defined(__powerpc__)
182 # define REG_INDEX 32
183 #endif
184 #endif /* GNU_LINUX */
185
186 #ifdef REG_INDEX
187 #ifdef HAVE_UCONTEXT_T_UC_MCONTEXT_GREGS
188 # define REGS gregs[REG_INDEX]
189 #elif defined(HAVE_UCONTEXT_T_UC_MCONTEXT_UC_REGS)
190 # define REGS uc_regs->gregs[REG_INDEX]
191 #endif /* HAVE_UCONTEXT_T_UC_MCONTEXT_GREGS */
192 #endif /* REG_INDEX */
193
194 #ifdef REGS
195 if (context)
196 return (void *)(((ucontext_t *)context)->uc_mcontext.REGS);
197 #elif defined(HAVE_UCONTEXT_T_UC_MCONTEXT_REGS__NIP)
198 /* older Linux / struct pt_regs ? */
199 if (context)
200 return (void *)(((ucontext_t *)context)->uc_mcontext.regs->nip);
201 #endif /* REGS */
202
203 #endif /* HAVE_UCONTEXT_H */
204 return NULL;
205 }
206
207 static void __attribute__((noreturn))
208 exit_handler(int signo, siginfo_t *siginfo, void *context)
209 {
210 void *pc = program_counter(context);
211
212 zlog_signal(signo, "exiting...", siginfo, pc);
213 _exit(128 + signo);
214 }
215
216 static void __attribute__((noreturn))
217 core_handler(int signo, siginfo_t *siginfo, void *context)
218 {
219 void *pc = program_counter(context);
220
221 /* make sure we don't hang in here. default for SIGALRM is terminate.
222 * - if we're in backtrace for more than a second, abort. */
223 struct sigaction sa_default = {.sa_handler = SIG_DFL};
224
225 sigaction(SIGALRM, &sa_default, NULL);
226 sigaction(signo, &sa_default, NULL);
227
228 sigset_t sigset;
229
230 sigemptyset(&sigset);
231 sigaddset(&sigset, SIGALRM);
232 sigprocmask(SIG_UNBLOCK, &sigset, NULL);
233
234 alarm(1);
235
236 zlog_signal(signo, "aborting...", siginfo, pc);
237
238 /* dump memory stats on core */
239 log_memstats(stderr, "core_handler");
240
241 zlog_tls_buffer_fini();
242
243 /* give the kernel a chance to generate a coredump */
244 sigaddset(&sigset, signo);
245 sigprocmask(SIG_UNBLOCK, &sigset, NULL);
246 raise(signo);
247
248 /* only chance to end up here is if the default action for signo is
249 * something other than kill or coredump the process
250 */
251 _exit(128 + signo);
252 }
253
254 static void trap_default_signals(void)
255 {
256 static const int core_signals[] = {
257 SIGQUIT, SIGILL, SIGABRT,
258 #ifdef SIGEMT
259 SIGEMT,
260 #endif
261 SIGFPE, SIGBUS, SIGSEGV,
262 #ifdef SIGSYS
263 SIGSYS,
264 #endif
265 #ifdef SIGXCPU
266 SIGXCPU,
267 #endif
268 #ifdef SIGXFSZ
269 SIGXFSZ,
270 #endif
271 };
272 static const int exit_signals[] = {
273 SIGHUP, SIGINT, SIGALRM, SIGTERM, SIGUSR1, SIGUSR2,
274 #ifdef SIGPOLL
275 SIGPOLL,
276 #endif
277 #ifdef SIGVTALRM
278 SIGVTALRM,
279 #endif
280 #ifdef SIGSTKFLT
281 SIGSTKFLT,
282 #endif
283 };
284 static const int ignore_signals[] = {
285 SIGPIPE,
286 };
287 static const struct {
288 const int *sigs;
289 unsigned int nsigs;
290 void (*handler)(int signo, siginfo_t *info, void *context);
291 } sigmap[] = {
292 {core_signals, array_size(core_signals), core_handler},
293 {exit_signals, array_size(exit_signals), exit_handler},
294 {ignore_signals, array_size(ignore_signals), NULL},
295 };
296 unsigned int i;
297
298 for (i = 0; i < array_size(sigmap); i++) {
299 unsigned int j;
300
301 for (j = 0; j < sigmap[i].nsigs; j++) {
302 struct sigaction oact;
303 if ((sigaction(sigmap[i].sigs[j], NULL, &oact) == 0)
304 && (oact.sa_handler == SIG_DFL)) {
305 struct sigaction act;
306 sigfillset(&act.sa_mask);
307 if (sigmap[i].handler == NULL) {
308 act.sa_handler = SIG_IGN;
309 act.sa_flags = 0;
310 } else {
311 /* Request extra arguments to signal
312 * handler. */
313 act.sa_sigaction = sigmap[i].handler;
314 act.sa_flags = SA_SIGINFO;
315 #ifdef SA_RESETHAND
316 /* don't try to print backtraces
317 * recursively */
318 if (sigmap[i].handler == core_handler)
319 act.sa_flags |= SA_RESETHAND;
320 #endif
321 }
322 if (sigaction(sigmap[i].sigs[j], &act, NULL)
323 < 0)
324 flog_err(
325 EC_LIB_SYSTEM_CALL,
326 "Unable to set signal handler for signal %d: %s",
327 sigmap[i].sigs[j],
328 safe_strerror(errno));
329 }
330 }
331 }
332 }
333
334 void signal_init(struct thread_master *m, int sigc,
335 struct frr_signal_t signals[])
336 {
337
338 int i = 0;
339 struct frr_signal_t *sig;
340
341 /* First establish some default handlers that can be overridden by
342 the application. */
343 trap_default_signals();
344
345 while (i < sigc) {
346 sig = &signals[i];
347 if (signal_set(sig->signal) < 0)
348 exit(-1);
349 i++;
350 }
351
352 sigmaster.sigc = sigc;
353 sigmaster.signals = signals;
354
355 #ifdef SIGEVENT_SCHEDULE_THREAD
356 sigmaster.t = NULL;
357 thread_add_timer(m, frr_signal_timer, &sigmaster,
358 FRR_SIGNAL_TIMER_INTERVAL, &sigmaster.t);
359 #endif /* SIGEVENT_SCHEDULE_THREAD */
360 }