]>
Commit | Line | Data |
---|---|---|
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 | 24 | static 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 | 36 | static 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 | */ | |
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 | ||
05c447dd | 78 | /* check if signals have been caught and run appropriate handlers */ |
7cc91e67 | 79 | int 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 | 130 | void frr_signal_timer(struct event *t) |
05c447dd | 131 | { |
7cc91e67 | 132 | struct frr_sigevent_master_t *sigm; |
d62a17ae | 133 | |
e16d030c | 134 | sigm = EVENT_ARG(t); |
d62a17ae | 135 | sigm->t = NULL; |
907a2395 DS |
136 | event_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 | 144 | static 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 | 172 | static 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 | 207 | static void __attribute__((noreturn)) |
5e4f10b1 | 208 | exit_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 | 216 | static void __attribute__((noreturn)) |
5e4f10b1 | 217 | core_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 | 254 | static 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 | ||
cd9d0537 | 334 | void signal_init(struct event_loop *m, int sigc, struct frr_signal_t signals[]) |
c49b3069 | 335 | { |
336 | ||
d62a17ae | 337 | int i = 0; |
7cc91e67 | 338 | struct frr_signal_t *sig; |
d62a17ae | 339 | |
340 | /* First establish some default handlers that can be overridden by | |
341 | the application. */ | |
342 | trap_default_signals(); | |
343 | ||
344 | while (i < sigc) { | |
345 | sig = &signals[i]; | |
346 | if (signal_set(sig->signal) < 0) | |
347 | exit(-1); | |
348 | i++; | |
349 | } | |
350 | ||
351 | sigmaster.sigc = sigc; | |
352 | sigmaster.signals = signals; | |
353 | ||
354 | #ifdef SIGEVENT_SCHEDULE_THREAD | |
355 | sigmaster.t = NULL; | |
907a2395 DS |
356 | event_add_timer(m, frr_signal_timer, &sigmaster, |
357 | FRR_SIGNAL_TIMER_INTERVAL, &sigmaster.t); | |
05c447dd | 358 | #endif /* SIGEVENT_SCHEDULE_THREAD */ |
c49b3069 | 359 | } |