]>
Commit | Line | Data |
---|---|---|
c49b3069 | 1 | /* Quagga signal handling functions. |
2 | * Copyright (C) 2004 Paul Jakma, | |
3 | * | |
4 | * This file is part of Quagga. | |
5 | * | |
6 | * Quagga is free software; you can redistribute it and/or modify it | |
7 | * under the terms of the GNU General Public License as published by the | |
8 | * Free Software Foundation; either version 2, or (at your option) any | |
9 | * later version. | |
10 | * | |
11 | * Quagga is distributed in the hope that it will be useful, but | |
12 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | * General Public License for more details. | |
15 | * | |
896014f4 DL |
16 | * You should have received a copy of the GNU General Public License along |
17 | * with this program; see the file COPYING; if not, write to the Free Software | |
18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | |
c49b3069 | 19 | */ |
20 | ||
21 | #include <zebra.h> | |
22 | #include <sigevent.h> | |
23 | #include <log.h> | |
837d16cc | 24 | #include <memory.h> |
481bc15f | 25 | #include <lib_errors.h> |
c49b3069 | 26 | |
31364274 | 27 | #ifdef SA_SIGINFO |
40abf239 | 28 | #ifdef HAVE_UCONTEXT_H |
29 | #ifdef GNU_LINUX | |
30 | /* get REG_EIP from ucontext.h */ | |
67bf16c0 | 31 | #ifndef __USE_GNU |
40abf239 | 32 | #define __USE_GNU |
67bf16c0 | 33 | #endif /* __USE_GNU */ |
40abf239 | 34 | #endif /* GNU_LINUX */ |
35 | #include <ucontext.h> | |
36 | #endif /* HAVE_UCONTEXT_H */ | |
31364274 | 37 | #endif /* SA_SIGINFO */ |
40abf239 | 38 | |
39 | ||
05c447dd | 40 | /* master signals descriptor struct */ |
d62a17ae | 41 | struct quagga_sigevent_master_t { |
42 | struct thread *t; | |
43 | ||
44 | struct quagga_signal_t *signals; | |
45 | int sigc; | |
c49b3069 | 46 | |
d62a17ae | 47 | volatile sig_atomic_t caught; |
05c447dd | 48 | } sigmaster; |
c49b3069 | 49 | |
d62a17ae | 50 | /* Generic signal handler |
c49b3069 | 51 | * Schedules signal event thread |
52 | */ | |
d62a17ae | 53 | static void quagga_signal_handler(int signo) |
c49b3069 | 54 | { |
d62a17ae | 55 | int i; |
56 | struct quagga_signal_t *sig; | |
57 | ||
58 | for (i = 0; i < sigmaster.sigc; i++) { | |
59 | sig = &(sigmaster.signals[i]); | |
60 | ||
61 | if (sig->signal == signo) | |
62 | sig->caught = 1; | |
63 | } | |
64 | ||
65 | sigmaster.caught = 1; | |
66 | } | |
c49b3069 | 67 | |
05c447dd | 68 | /* check if signals have been caught and run appropriate handlers */ |
d62a17ae | 69 | int quagga_sigevent_process(void) |
c49b3069 | 70 | { |
d62a17ae | 71 | struct quagga_signal_t *sig; |
72 | int i; | |
05c447dd | 73 | #ifdef SIGEVENT_BLOCK_SIGNALS |
d62a17ae | 74 | /* shouldnt need to block signals, but potentially may be needed */ |
75 | sigset_t newmask, oldmask; | |
76 | ||
77 | /* | |
78 | * Block most signals, but be careful not to defer SIGTRAP because | |
79 | * doing so breaks gdb, at least on NetBSD 2.0. Avoid asking to | |
80 | * block SIGKILL, just because we shouldn't be able to do so. | |
81 | */ | |
82 | sigfillset(&newmask); | |
83 | sigdelset(&newmask, SIGTRAP); | |
84 | sigdelset(&newmask, SIGKILL); | |
85 | ||
86 | if ((sigprocmask(SIG_BLOCK, &newmask, &oldmask)) < 0) { | |
09c866e3 QY |
87 | flog_err_sys(LIB_ERR_SYSTEM_CALL, |
88 | "quagga_signal_timer: couldnt block signals!"); | |
d62a17ae | 89 | return -1; |
90 | } | |
05c447dd | 91 | #endif /* SIGEVENT_BLOCK_SIGNALS */ |
92 | ||
d62a17ae | 93 | if (sigmaster.caught > 0) { |
94 | sigmaster.caught = 0; | |
95 | /* must not read or set sigmaster.caught after here, | |
96 | * race condition with per-sig caught flags if one does | |
97 | */ | |
98 | ||
99 | for (i = 0; i < sigmaster.sigc; i++) { | |
100 | sig = &(sigmaster.signals[i]); | |
101 | ||
102 | if (sig->caught > 0) { | |
103 | sig->caught = 0; | |
104 | if (sig->handler) | |
105 | sig->handler(); | |
106 | } | |
107 | } | |
108 | } | |
c49b3069 | 109 | |
05c447dd | 110 | #ifdef SIGEVENT_BLOCK_SIGNALS |
d62a17ae | 111 | if (sigprocmask(SIG_UNBLOCK, &oldmask, NULL) < 0) |
112 | ; | |
113 | return -1; | |
05c447dd | 114 | #endif /* SIGEVENT_BLOCK_SIGNALS */ |
115 | ||
d62a17ae | 116 | return 0; |
c49b3069 | 117 | } |
118 | ||
05c447dd | 119 | #ifdef SIGEVENT_SCHEDULE_THREAD |
120 | /* timer thread to check signals. Shouldnt be needed */ | |
d62a17ae | 121 | int quagga_signal_timer(struct thread *t) |
05c447dd | 122 | { |
d62a17ae | 123 | struct quagga_sigevent_master_t *sigm; |
d62a17ae | 124 | |
125 | sigm = THREAD_ARG(t); | |
126 | sigm->t = NULL; | |
127 | thread_add_timer(sigm->t->master, quagga_signal_timer, &sigmaster, | |
128 | QUAGGA_SIGNAL_TIMER_INTERVAL, &sigm->t); | |
129 | return quagga_sigevent_process(); | |
05c447dd | 130 | } |
131 | #endif /* SIGEVENT_SCHEDULE_THREAD */ | |
132 | ||
c49b3069 | 133 | /* Initialization of signal handles. */ |
40abf239 | 134 | /* Signal wrapper. */ |
d62a17ae | 135 | static int signal_set(int signo) |
c49b3069 | 136 | { |
d62a17ae | 137 | int ret; |
138 | struct sigaction sig; | |
139 | struct sigaction osig; | |
140 | ||
141 | sig.sa_handler = &quagga_signal_handler; | |
142 | sigfillset(&sig.sa_mask); | |
143 | sig.sa_flags = 0; | |
144 | if (signo == SIGALRM) { | |
c49b3069 | 145 | #ifdef SA_INTERRUPT |
d62a17ae | 146 | sig.sa_flags |= SA_INTERRUPT; /* SunOS */ |
c49b3069 | 147 | #endif |
d62a17ae | 148 | } else { |
c49b3069 | 149 | #ifdef SA_RESTART |
d62a17ae | 150 | sig.sa_flags |= SA_RESTART; |
c49b3069 | 151 | #endif /* SA_RESTART */ |
d62a17ae | 152 | } |
c49b3069 | 153 | |
d62a17ae | 154 | ret = sigaction(signo, &sig, &osig); |
155 | if (ret < 0) | |
156 | return ret; | |
157 | else | |
158 | return 0; | |
c49b3069 | 159 | } |
160 | ||
31364274 | 161 | #ifdef SA_SIGINFO |
162 | ||
40abf239 | 163 | /* XXX This function should be enhanced to support more platforms |
164 | (it currently works only on Linux/x86). */ | |
d62a17ae | 165 | static void *program_counter(void *context) |
40abf239 | 166 | { |
167 | #ifdef HAVE_UCONTEXT_H | |
168 | #ifdef GNU_LINUX | |
d62a17ae | 169 | /* these are from GNU libc, rather than Linux, strictly speaking */ |
170 | #if defined(REG_EIP) | |
bccbd141 | 171 | # define REG_INDEX REG_EIP |
d62a17ae | 172 | #elif defined(REG_RIP) |
bccbd141 | 173 | # define REG_INDEX REG_RIP |
d62a17ae | 174 | #elif defined(__powerpc__) |
bccbd141 | 175 | # define REG_INDEX 32 |
d62a17ae | 176 | #endif |
bccbd141 JT |
177 | #elif defined(SUNOS_5) /* !GNU_LINUX */ |
178 | # define REG_INDEX REG_PC | |
d62a17ae | 179 | #endif /* GNU_LINUX */ |
bccbd141 JT |
180 | |
181 | #ifdef REG_INDEX | |
d62a17ae | 182 | #ifdef HAVE_UCONTEXT_T_UC_MCONTEXT_GREGS |
bccbd141 | 183 | # define REGS gregs[REG_INDEX] |
d62a17ae | 184 | #elif defined(HAVE_UCONTEXT_T_UC_MCONTEXT_UC_REGS) |
bccbd141 | 185 | # define REGS uc_regs->gregs[REG_INDEX] |
d62a17ae | 186 | #endif /* HAVE_UCONTEXT_T_UC_MCONTEXT_GREGS */ |
bccbd141 JT |
187 | #endif /* REG_INDEX */ |
188 | ||
189 | #ifdef REGS | |
d62a17ae | 190 | if (context) |
191 | return (void *)(((ucontext_t *)context)->uc_mcontext.REGS); | |
192 | #elif defined(HAVE_UCONTEXT_T_UC_MCONTEXT_REGS__NIP) | |
193 | /* older Linux / struct pt_regs ? */ | |
194 | if (context) | |
195 | return (void *)(((ucontext_t *)context)->uc_mcontext.regs->nip); | |
bccbd141 JT |
196 | #endif /* REGS */ |
197 | ||
40abf239 | 198 | #endif /* HAVE_UCONTEXT_H */ |
d62a17ae | 199 | return NULL; |
40abf239 | 200 | } |
201 | ||
31364274 | 202 | #endif /* SA_SIGINFO */ |
203 | ||
d62a17ae | 204 | static void __attribute__((noreturn)) |
31364274 | 205 | exit_handler(int signo |
206 | #ifdef SA_SIGINFO | |
d62a17ae | 207 | , |
208 | siginfo_t *siginfo, void *context | |
31364274 | 209 | #endif |
9d303b37 | 210 | ) |
59a06a91 | 211 | { |
d62a17ae | 212 | zlog_signal(signo, "exiting..." |
31364274 | 213 | #ifdef SA_SIGINFO |
d62a17ae | 214 | , |
215 | siginfo, program_counter(context) | |
31364274 | 216 | #endif |
9d303b37 | 217 | ); |
d62a17ae | 218 | _exit(128 + signo); |
59a06a91 | 219 | } |
220 | ||
d62a17ae | 221 | static void __attribute__((noreturn)) |
31364274 | 222 | core_handler(int signo |
223 | #ifdef SA_SIGINFO | |
d62a17ae | 224 | , |
225 | siginfo_t *siginfo, void *context | |
31364274 | 226 | #endif |
9d303b37 | 227 | ) |
59a06a91 | 228 | { |
d62a17ae | 229 | /* make sure we don't hang in here. default for SIGALRM is terminate. |
230 | * - if we're in backtrace for more than a second, abort. */ | |
231 | struct sigaction sa_default = {.sa_handler = SIG_DFL}; | |
232 | sigaction(SIGALRM, &sa_default, NULL); | |
3f11a103 | 233 | |
d62a17ae | 234 | sigset_t sigset; |
235 | sigemptyset(&sigset); | |
236 | sigaddset(&sigset, SIGALRM); | |
237 | sigprocmask(SIG_UNBLOCK, &sigset, NULL); | |
3f11a103 | 238 | |
d62a17ae | 239 | alarm(1); |
3f11a103 | 240 | |
d62a17ae | 241 | zlog_signal(signo, "aborting..." |
31364274 | 242 | #ifdef SA_SIGINFO |
d62a17ae | 243 | , |
244 | siginfo, program_counter(context) | |
31364274 | 245 | #endif |
9d303b37 | 246 | ); |
d62a17ae | 247 | /* dump memory stats on core */ |
9eed278b | 248 | log_memstats(stderr, "core_handler"); |
d62a17ae | 249 | abort(); |
59a06a91 | 250 | } |
251 | ||
d62a17ae | 252 | static void trap_default_signals(void) |
59a06a91 | 253 | { |
d62a17ae | 254 | static const int core_signals[] = { |
255 | SIGQUIT, SIGILL, | |
59a06a91 | 256 | #ifdef SIGEMT |
d62a17ae | 257 | SIGEMT, |
59a06a91 | 258 | #endif |
d62a17ae | 259 | SIGFPE, SIGBUS, SIGSEGV, |
59a06a91 | 260 | #ifdef SIGSYS |
d62a17ae | 261 | SIGSYS, |
59a06a91 | 262 | #endif |
263 | #ifdef SIGXCPU | |
d62a17ae | 264 | SIGXCPU, |
59a06a91 | 265 | #endif |
266 | #ifdef SIGXFSZ | |
d62a17ae | 267 | SIGXFSZ, |
59a06a91 | 268 | #endif |
d62a17ae | 269 | }; |
270 | static const int exit_signals[] = { | |
271 | SIGHUP, SIGINT, SIGALRM, SIGTERM, SIGUSR1, SIGUSR2, | |
59a06a91 | 272 | #ifdef SIGPOLL |
d62a17ae | 273 | SIGPOLL, |
59a06a91 | 274 | #endif |
275 | #ifdef SIGVTALRM | |
d62a17ae | 276 | SIGVTALRM, |
59a06a91 | 277 | #endif |
278 | #ifdef SIGSTKFLT | |
d62a17ae | 279 | SIGSTKFLT, |
59a06a91 | 280 | #endif |
d62a17ae | 281 | }; |
282 | static const int ignore_signals[] = { | |
283 | SIGPIPE, | |
284 | }; | |
285 | static const struct { | |
286 | const int *sigs; | |
d7c0a89a | 287 | unsigned int nsigs; |
d62a17ae | 288 | void (*handler)(int signo |
31364274 | 289 | #ifdef SA_SIGINFO |
d62a17ae | 290 | , |
291 | siginfo_t *info, void *context | |
31364274 | 292 | #endif |
9d303b37 | 293 | ); |
d62a17ae | 294 | } sigmap[] = { |
295 | {core_signals, array_size(core_signals), core_handler}, | |
296 | {exit_signals, array_size(exit_signals), exit_handler}, | |
297 | {ignore_signals, array_size(ignore_signals), NULL}, | |
298 | }; | |
d7c0a89a | 299 | unsigned int i; |
d62a17ae | 300 | |
301 | for (i = 0; i < array_size(sigmap); i++) { | |
d7c0a89a | 302 | unsigned int j; |
d62a17ae | 303 | |
304 | for (j = 0; j < sigmap[i].nsigs; j++) { | |
305 | struct sigaction oact; | |
306 | if ((sigaction(sigmap[i].sigs[j], NULL, &oact) == 0) | |
307 | && (oact.sa_handler == SIG_DFL)) { | |
308 | struct sigaction act; | |
309 | sigfillset(&act.sa_mask); | |
310 | if (sigmap[i].handler == NULL) { | |
311 | act.sa_handler = SIG_IGN; | |
312 | act.sa_flags = 0; | |
313 | } else { | |
31364274 | 314 | #ifdef SA_SIGINFO |
d62a17ae | 315 | /* Request extra arguments to signal |
316 | * handler. */ | |
317 | act.sa_sigaction = sigmap[i].handler; | |
318 | act.sa_flags = SA_SIGINFO; | |
31364274 | 319 | #else |
d62a17ae | 320 | act.sa_handler = sigmap[i].handler; |
321 | act.sa_flags = 0; | |
3f11a103 DL |
322 | #endif |
323 | #ifdef SA_RESETHAND | |
d62a17ae | 324 | /* don't try to print backtraces |
325 | * recursively */ | |
326 | if (sigmap[i].handler == core_handler) | |
327 | act.sa_flags |= SA_RESETHAND; | |
31364274 | 328 | #endif |
d62a17ae | 329 | } |
330 | if (sigaction(sigmap[i].sigs[j], &act, NULL) | |
331 | < 0) | |
332 | zlog_warn( | |
333 | "Unable to set signal handler for signal %d: %s", | |
334 | sigmap[i].sigs[j], | |
335 | safe_strerror(errno)); | |
336 | } | |
337 | } | |
338 | } | |
59a06a91 | 339 | } |
340 | ||
d62a17ae | 341 | void signal_init(struct thread_master *m, int sigc, |
342 | struct quagga_signal_t signals[]) | |
c49b3069 | 343 | { |
344 | ||
d62a17ae | 345 | int i = 0; |
346 | struct quagga_signal_t *sig; | |
347 | ||
348 | /* First establish some default handlers that can be overridden by | |
349 | the application. */ | |
350 | trap_default_signals(); | |
351 | ||
352 | while (i < sigc) { | |
353 | sig = &signals[i]; | |
354 | if (signal_set(sig->signal) < 0) | |
355 | exit(-1); | |
356 | i++; | |
357 | } | |
358 | ||
359 | sigmaster.sigc = sigc; | |
360 | sigmaster.signals = signals; | |
361 | ||
362 | #ifdef SIGEVENT_SCHEDULE_THREAD | |
363 | sigmaster.t = NULL; | |
364 | thread_add_timer(m, quagga_signal_timer, &sigmaster, | |
365 | QUAGGA_SIGNAL_TIMER_INTERVAL, &sigmaster.t); | |
05c447dd | 366 | #endif /* SIGEVENT_SCHEDULE_THREAD */ |
c49b3069 | 367 | } |