]> git.proxmox.com Git - mirror_frr.git/blame - lib/sigevent.c
*: Rename `struct thread` to `struct event`
[mirror_frr.git] / lib / sigevent.c
CommitLineData
acddc0ed 1// SPDX-License-Identifier: GPL-2.0-or-later
c49b3069 2/* Quagga signal handling functions.
3 * Copyright (C) 2004 Paul Jakma,
c49b3069 4 */
5
6#include <zebra.h>
7#include <sigevent.h>
8#include <log.h>
837d16cc 9#include <memory.h>
481bc15f 10#include <lib_errors.h>
c49b3069 11
40abf239 12#ifdef HAVE_UCONTEXT_H
13#ifdef GNU_LINUX
14/* get REG_EIP from ucontext.h */
67bf16c0 15#ifndef __USE_GNU
40abf239 16#define __USE_GNU
67bf16c0 17#endif /* __USE_GNU */
40abf239 18#endif /* GNU_LINUX */
19#include <ucontext.h>
20#endif /* HAVE_UCONTEXT_H */
21
22
05c447dd 23/* master signals descriptor struct */
7cc91e67 24static struct frr_sigevent_master_t {
e6685141 25 struct event *t;
d62a17ae 26
7cc91e67 27 struct frr_signal_t *signals;
d62a17ae 28 int sigc;
c49b3069 29
d62a17ae 30 volatile sig_atomic_t caught;
05c447dd 31} sigmaster;
c49b3069 32
d62a17ae 33/* Generic signal handler
c49b3069 34 * Schedules signal event thread
35 */
7cc91e67 36static void frr_signal_handler(int signo)
c49b3069 37{
d62a17ae 38 int i;
7cc91e67 39 struct frr_signal_t *sig;
d62a17ae 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}
c49b3069 50
976c5cc1
MS
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 */
57bool 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
05c447dd 78/* check if signals have been caught and run appropriate handlers */
7cc91e67 79int frr_sigevent_process(void)
c49b3069 80{
7cc91e67 81 struct frr_signal_t *sig;
d62a17ae 82 int i;
05c447dd 83#ifdef SIGEVENT_BLOCK_SIGNALS
214d8a60 84 /* shouldn't need to block signals, but potentially may be needed */
d62a17ae 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) {
450971aa 97 flog_err_sys(EC_LIB_SYSTEM_CALL,
7cc91e67 98 "frr_signal_timer: couldnt block signals!");
d62a17ae 99 return -1;
100 }
05c447dd 101#endif /* SIGEVENT_BLOCK_SIGNALS */
102
d62a17ae 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 }
c49b3069 119
05c447dd 120#ifdef SIGEVENT_BLOCK_SIGNALS
d62a17ae 121 if (sigprocmask(SIG_UNBLOCK, &oldmask, NULL) < 0)
51a68f9b 122 return -1;
05c447dd 123#endif /* SIGEVENT_BLOCK_SIGNALS */
124
d62a17ae 125 return 0;
c49b3069 126}
127
05c447dd 128#ifdef SIGEVENT_SCHEDULE_THREAD
214d8a60 129/* timer thread to check signals. shouldn't be needed */
e6685141 130void frr_signal_timer(struct event *t)
05c447dd 131{
7cc91e67 132 struct frr_sigevent_master_t *sigm;
d62a17ae 133
134 sigm = THREAD_ARG(t);
135 sigm->t = NULL;
7cc91e67
DS
136 thread_add_timer(sigm->t->master, frr_signal_timer, &sigmaster,
137 FRR_SIGNAL_TIMER_INTERVAL, &sigm->t);
cc9f21da 138 frr_sigevent_process();
05c447dd 139}
140#endif /* SIGEVENT_SCHEDULE_THREAD */
141
c49b3069 142/* Initialization of signal handles. */
40abf239 143/* Signal wrapper. */
d62a17ae 144static int signal_set(int signo)
c49b3069 145{
d62a17ae 146 int ret;
147 struct sigaction sig;
148 struct sigaction osig;
149
7cc91e67 150 sig.sa_handler = &frr_signal_handler;
d62a17ae 151 sigfillset(&sig.sa_mask);
152 sig.sa_flags = 0;
153 if (signo == SIGALRM) {
c49b3069 154#ifdef SA_INTERRUPT
d62a17ae 155 sig.sa_flags |= SA_INTERRUPT; /* SunOS */
c49b3069 156#endif
d62a17ae 157 } else {
c49b3069 158#ifdef SA_RESTART
d62a17ae 159 sig.sa_flags |= SA_RESTART;
c49b3069 160#endif /* SA_RESTART */
d62a17ae 161 }
c49b3069 162
d62a17ae 163 ret = sigaction(signo, &sig, &osig);
164 if (ret < 0)
165 return ret;
166 else
167 return 0;
c49b3069 168}
169
40abf239 170/* XXX This function should be enhanced to support more platforms
171 (it currently works only on Linux/x86). */
d62a17ae 172static void *program_counter(void *context)
40abf239 173{
174#ifdef HAVE_UCONTEXT_H
175#ifdef GNU_LINUX
d62a17ae 176/* these are from GNU libc, rather than Linux, strictly speaking */
177#if defined(REG_EIP)
bccbd141 178# define REG_INDEX REG_EIP
d62a17ae 179#elif defined(REG_RIP)
bccbd141 180# define REG_INDEX REG_RIP
d62a17ae 181#elif defined(__powerpc__)
bccbd141 182# define REG_INDEX 32
d62a17ae 183#endif
d62a17ae 184#endif /* GNU_LINUX */
bccbd141
JT
185
186#ifdef REG_INDEX
d62a17ae 187#ifdef HAVE_UCONTEXT_T_UC_MCONTEXT_GREGS
bccbd141 188# define REGS gregs[REG_INDEX]
d62a17ae 189#elif defined(HAVE_UCONTEXT_T_UC_MCONTEXT_UC_REGS)
bccbd141 190# define REGS uc_regs->gregs[REG_INDEX]
d62a17ae 191#endif /* HAVE_UCONTEXT_T_UC_MCONTEXT_GREGS */
bccbd141
JT
192#endif /* REG_INDEX */
193
194#ifdef REGS
d62a17ae 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);
bccbd141
JT
201#endif /* REGS */
202
40abf239 203#endif /* HAVE_UCONTEXT_H */
d62a17ae 204 return NULL;
40abf239 205}
206
d62a17ae 207static void __attribute__((noreturn))
5e4f10b1 208exit_handler(int signo, siginfo_t *siginfo, void *context)
59a06a91 209{
c22f6d8c 210 void *pc = program_counter(context);
c22f6d8c
DL
211
212 zlog_signal(signo, "exiting...", siginfo, pc);
d62a17ae 213 _exit(128 + signo);
59a06a91 214}
215
d62a17ae 216static void __attribute__((noreturn))
5e4f10b1 217core_handler(int signo, siginfo_t *siginfo, void *context)
59a06a91 218{
c22f6d8c 219 void *pc = program_counter(context);
c22f6d8c 220
d62a17ae 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};
5be4799d 224
d62a17ae 225 sigaction(SIGALRM, &sa_default, NULL);
5be4799d 226 sigaction(signo, &sa_default, NULL);
3f11a103 227
d62a17ae 228 sigset_t sigset;
5be4799d 229
d62a17ae 230 sigemptyset(&sigset);
231 sigaddset(&sigset, SIGALRM);
232 sigprocmask(SIG_UNBLOCK, &sigset, NULL);
3f11a103 233
d62a17ae 234 alarm(1);
3f11a103 235
c22f6d8c
DL
236 zlog_signal(signo, "aborting...", siginfo, pc);
237
d62a17ae 238 /* dump memory stats on core */
9eed278b 239 log_memstats(stderr, "core_handler");
23961e75
DS
240
241 zlog_tls_buffer_fini();
5be4799d
DL
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);
59a06a91 252}
253
d62a17ae 254static void trap_default_signals(void)
59a06a91 255{
d62a17ae 256 static const int core_signals[] = {
3fb4be22 257 SIGQUIT, SIGILL, SIGABRT,
59a06a91 258#ifdef SIGEMT
d62a17ae 259 SIGEMT,
59a06a91 260#endif
d62a17ae 261 SIGFPE, SIGBUS, SIGSEGV,
59a06a91 262#ifdef SIGSYS
d62a17ae 263 SIGSYS,
59a06a91 264#endif
265#ifdef SIGXCPU
d62a17ae 266 SIGXCPU,
59a06a91 267#endif
268#ifdef SIGXFSZ
d62a17ae 269 SIGXFSZ,
59a06a91 270#endif
d62a17ae 271 };
272 static const int exit_signals[] = {
273 SIGHUP, SIGINT, SIGALRM, SIGTERM, SIGUSR1, SIGUSR2,
59a06a91 274#ifdef SIGPOLL
d62a17ae 275 SIGPOLL,
59a06a91 276#endif
277#ifdef SIGVTALRM
d62a17ae 278 SIGVTALRM,
59a06a91 279#endif
280#ifdef SIGSTKFLT
d62a17ae 281 SIGSTKFLT,
59a06a91 282#endif
d62a17ae 283 };
284 static const int ignore_signals[] = {
285 SIGPIPE,
286 };
287 static const struct {
288 const int *sigs;
d7c0a89a 289 unsigned int nsigs;
5e4f10b1 290 void (*handler)(int signo, siginfo_t *info, void *context);
d62a17ae 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 };
d7c0a89a 296 unsigned int i;
d62a17ae 297
298 for (i = 0; i < array_size(sigmap); i++) {
d7c0a89a 299 unsigned int j;
d62a17ae 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 {
d62a17ae 311 /* Request extra arguments to signal
312 * handler. */
313 act.sa_sigaction = sigmap[i].handler;
314 act.sa_flags = SA_SIGINFO;
3f11a103 315#ifdef SA_RESETHAND
d62a17ae 316 /* don't try to print backtraces
317 * recursively */
318 if (sigmap[i].handler == core_handler)
319 act.sa_flags |= SA_RESETHAND;
31364274 320#endif
d62a17ae 321 }
322 if (sigaction(sigmap[i].sigs[j], &act, NULL)
323 < 0)
ff9d9d5b 324 flog_err(
450971aa 325 EC_LIB_SYSTEM_CALL,
d62a17ae 326 "Unable to set signal handler for signal %d: %s",
327 sigmap[i].sigs[j],
328 safe_strerror(errno));
329 }
330 }
331 }
59a06a91 332}
333
d62a17ae 334void signal_init(struct thread_master *m, int sigc,
7cc91e67 335 struct frr_signal_t signals[])
c49b3069 336{
337
d62a17ae 338 int i = 0;
7cc91e67 339 struct frr_signal_t *sig;
d62a17ae 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;
7cc91e67
DS
357 thread_add_timer(m, frr_signal_timer, &sigmaster,
358 FRR_SIGNAL_TIMER_INTERVAL, &sigmaster.t);
05c447dd 359#endif /* SIGEVENT_SCHEDULE_THREAD */
c49b3069 360}