]>
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> |
c49b3069 | 25 | |
31364274 | 26 | #ifdef SA_SIGINFO |
40abf239 | 27 | #ifdef HAVE_UCONTEXT_H |
28 | #ifdef GNU_LINUX | |
29 | /* get REG_EIP from ucontext.h */ | |
67bf16c0 | 30 | #ifndef __USE_GNU |
40abf239 | 31 | #define __USE_GNU |
67bf16c0 | 32 | #endif /* __USE_GNU */ |
40abf239 | 33 | #endif /* GNU_LINUX */ |
34 | #include <ucontext.h> | |
35 | #endif /* HAVE_UCONTEXT_H */ | |
31364274 | 36 | #endif /* SA_SIGINFO */ |
40abf239 | 37 | |
38 | ||
05c447dd | 39 | /* master signals descriptor struct */ |
c49b3069 | 40 | struct quagga_sigevent_master_t |
41 | { | |
c49b3069 | 42 | struct thread *t; |
43 | ||
05c447dd | 44 | struct quagga_signal_t *signals; |
c49b3069 | 45 | int sigc; |
05c447dd | 46 | |
47 | volatile sig_atomic_t caught; | |
48 | } sigmaster; | |
c49b3069 | 49 | |
50 | /* Generic signal handler | |
51 | * Schedules signal event thread | |
52 | */ | |
59a06a91 | 53 | static void |
c49b3069 | 54 | quagga_signal_handler (int signo) |
55 | { | |
56 | int i; | |
57 | struct quagga_signal_t *sig; | |
58 | ||
59 | for (i = 0; i < sigmaster.sigc; i++) | |
60 | { | |
61 | sig = &(sigmaster.signals[i]); | |
62 | ||
63 | if (sig->signal == signo) | |
05c447dd | 64 | sig->caught = 1; |
c49b3069 | 65 | } |
05c447dd | 66 | |
67 | sigmaster.caught = 1; | |
c49b3069 | 68 | } |
69 | ||
05c447dd | 70 | /* check if signals have been caught and run appropriate handlers */ |
c49b3069 | 71 | int |
05c447dd | 72 | quagga_sigevent_process (void) |
c49b3069 | 73 | { |
c49b3069 | 74 | struct quagga_signal_t *sig; |
75 | int i; | |
05c447dd | 76 | #ifdef SIGEVENT_BLOCK_SIGNALS |
77 | /* shouldnt need to block signals, but potentially may be needed */ | |
78 | sigset_t newmask, oldmask; | |
c49b3069 | 79 | |
b7797131 | 80 | /* |
81 | * Block most signals, but be careful not to defer SIGTRAP because | |
82 | * doing so breaks gdb, at least on NetBSD 2.0. Avoid asking to | |
83 | * block SIGKILL, just because we shouldn't be able to do so. | |
84 | */ | |
c49b3069 | 85 | sigfillset (&newmask); |
b7797131 | 86 | sigdelset (&newmask, SIGTRAP); |
87 | sigdelset (&newmask, SIGKILL); | |
05c447dd | 88 | |
c49b3069 | 89 | if ( (sigprocmask (SIG_BLOCK, &newmask, &oldmask)) < 0) |
90 | { | |
91 | zlog_err ("quagga_signal_timer: couldnt block signals!"); | |
c49b3069 | 92 | return -1; |
93 | } | |
05c447dd | 94 | #endif /* SIGEVENT_BLOCK_SIGNALS */ |
95 | ||
96 | if (sigmaster.caught > 0) | |
c49b3069 | 97 | { |
05c447dd | 98 | sigmaster.caught = 0; |
99 | /* must not read or set sigmaster.caught after here, | |
100 | * race condition with per-sig caught flags if one does | |
101 | */ | |
102 | ||
103 | for (i = 0; i < sigmaster.sigc; i++) | |
c49b3069 | 104 | { |
05c447dd | 105 | sig = &(sigmaster.signals[i]); |
106 | ||
107 | if (sig->caught > 0) | |
108 | { | |
109 | sig->caught = 0; | |
1e7e440f RW |
110 | if (sig->handler) |
111 | sig->handler (); | |
05c447dd | 112 | } |
c49b3069 | 113 | } |
114 | } | |
c49b3069 | 115 | |
05c447dd | 116 | #ifdef SIGEVENT_BLOCK_SIGNALS |
c49b3069 | 117 | if ( sigprocmask (SIG_UNBLOCK, &oldmask, NULL) < 0 ); |
118 | return -1; | |
05c447dd | 119 | #endif /* SIGEVENT_BLOCK_SIGNALS */ |
120 | ||
c49b3069 | 121 | return 0; |
122 | } | |
123 | ||
05c447dd | 124 | #ifdef SIGEVENT_SCHEDULE_THREAD |
125 | /* timer thread to check signals. Shouldnt be needed */ | |
126 | int | |
127 | quagga_signal_timer (struct thread *t) | |
128 | { | |
129 | struct quagga_sigevent_master_t *sigm; | |
130 | struct quagga_signal_t *sig; | |
131 | int i; | |
132 | ||
133 | sigm = THREAD_ARG (t); | |
66e78ae6 QY |
134 | sigm->t = NULL; |
135 | thread_add_timer(sigm->t->master, quagga_signal_timer, &sigmaster, QUAGGA_SIGNAL_TIMER_INTERVAL, | |
136 | &sigm->t); | |
05c447dd | 137 | return quagga_sigevent_process (); |
138 | } | |
139 | #endif /* SIGEVENT_SCHEDULE_THREAD */ | |
140 | ||
c49b3069 | 141 | /* Initialization of signal handles. */ |
40abf239 | 142 | /* Signal wrapper. */ |
59a06a91 | 143 | static int |
c49b3069 | 144 | signal_set (int signo) |
145 | { | |
146 | int ret; | |
147 | struct sigaction sig; | |
148 | struct sigaction osig; | |
149 | ||
150 | sig.sa_handler = &quagga_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 | ||
31364274 | 170 | #ifdef SA_SIGINFO |
171 | ||
40abf239 | 172 | /* XXX This function should be enhanced to support more platforms |
173 | (it currently works only on Linux/x86). */ | |
174 | static void * | |
175 | program_counter(void *context) | |
176 | { | |
177 | #ifdef HAVE_UCONTEXT_H | |
178 | #ifdef GNU_LINUX | |
bccbd141 JT |
179 | /* these are from GNU libc, rather than Linux, strictly speaking */ |
180 | # if defined(REG_EIP) | |
181 | # define REG_INDEX REG_EIP | |
182 | # elif defined(REG_RIP) | |
183 | # define REG_INDEX REG_RIP | |
184 | # elif defined(__powerpc__) | |
185 | # define REG_INDEX 32 | |
186 | # endif | |
187 | #elif defined(SUNOS_5) /* !GNU_LINUX */ | |
188 | # define REG_INDEX REG_PC | |
40abf239 | 189 | #endif /* GNU_LINUX */ |
bccbd141 JT |
190 | |
191 | #ifdef REG_INDEX | |
192 | # ifdef HAVE_UCONTEXT_T_UC_MCONTEXT_GREGS | |
193 | # define REGS gregs[REG_INDEX] | |
194 | # elif defined(HAVE_UCONTEXT_T_UC_MCONTEXT_UC_REGS) | |
195 | # define REGS uc_regs->gregs[REG_INDEX] | |
196 | # endif /* HAVE_UCONTEXT_T_UC_MCONTEXT_GREGS */ | |
197 | #endif /* REG_INDEX */ | |
198 | ||
199 | #ifdef REGS | |
200 | if (context) | |
201 | return (void *)(((ucontext_t *)context)->uc_mcontext.REGS); | |
202 | #elif defined(HAVE_UCONTEXT_T_UC_MCONTEXT_REGS__NIP) | |
203 | /* older Linux / struct pt_regs ? */ | |
204 | if (context) | |
205 | return (void *)(((ucontext_t *)context)->uc_mcontext.regs->nip); | |
206 | #endif /* REGS */ | |
207 | ||
40abf239 | 208 | #endif /* HAVE_UCONTEXT_H */ |
209 | return NULL; | |
210 | } | |
211 | ||
31364274 | 212 | #endif /* SA_SIGINFO */ |
213 | ||
8cc4198f | 214 | static void __attribute__ ((noreturn)) |
31364274 | 215 | exit_handler(int signo |
216 | #ifdef SA_SIGINFO | |
217 | , siginfo_t *siginfo, void *context | |
218 | #endif | |
219 | ) | |
59a06a91 | 220 | { |
31364274 | 221 | zlog_signal(signo, "exiting..." |
222 | #ifdef SA_SIGINFO | |
223 | , siginfo, program_counter(context) | |
224 | #endif | |
225 | ); | |
59a06a91 | 226 | _exit(128+signo); |
227 | } | |
228 | ||
8cc4198f | 229 | static void __attribute__ ((noreturn)) |
31364274 | 230 | core_handler(int signo |
231 | #ifdef SA_SIGINFO | |
232 | , siginfo_t *siginfo, void *context | |
233 | #endif | |
234 | ) | |
59a06a91 | 235 | { |
3f11a103 DL |
236 | /* make sure we don't hang in here. default for SIGALRM is terminate. |
237 | * - if we're in backtrace for more than a second, abort. */ | |
238 | struct sigaction sa_default = { .sa_handler = SIG_DFL }; | |
239 | sigaction (SIGALRM, &sa_default, NULL); | |
240 | ||
241 | sigset_t sigset; | |
242 | sigemptyset (&sigset); | |
243 | sigaddset (&sigset, SIGALRM); | |
244 | sigprocmask (SIG_UNBLOCK, &sigset, NULL); | |
245 | ||
246 | alarm (1); | |
247 | ||
31364274 | 248 | zlog_signal(signo, "aborting..." |
249 | #ifdef SA_SIGINFO | |
250 | , siginfo, program_counter(context) | |
251 | #endif | |
252 | ); | |
b6459124 LB |
253 | /* dump memory stats on core */ |
254 | log_memstats_stderr ("core_handler"); | |
59a06a91 | 255 | abort(); |
256 | } | |
257 | ||
258 | static void | |
259 | trap_default_signals(void) | |
260 | { | |
261 | static const int core_signals[] = { | |
262 | SIGQUIT, | |
263 | SIGILL, | |
264 | #ifdef SIGEMT | |
265 | SIGEMT, | |
266 | #endif | |
267 | SIGFPE, | |
268 | SIGBUS, | |
269 | SIGSEGV, | |
270 | #ifdef SIGSYS | |
271 | SIGSYS, | |
272 | #endif | |
273 | #ifdef SIGXCPU | |
274 | SIGXCPU, | |
275 | #endif | |
276 | #ifdef SIGXFSZ | |
277 | SIGXFSZ, | |
278 | #endif | |
279 | }; | |
280 | static const int exit_signals[] = { | |
281 | SIGHUP, | |
282 | SIGINT, | |
59a06a91 | 283 | SIGALRM, |
284 | SIGTERM, | |
285 | SIGUSR1, | |
286 | SIGUSR2, | |
287 | #ifdef SIGPOLL | |
288 | SIGPOLL, | |
289 | #endif | |
290 | #ifdef SIGVTALRM | |
291 | SIGVTALRM, | |
292 | #endif | |
293 | #ifdef SIGSTKFLT | |
294 | SIGSTKFLT, | |
295 | #endif | |
296 | }; | |
81fc57ca | 297 | static const int ignore_signals[] = { |
298 | SIGPIPE, | |
299 | }; | |
59a06a91 | 300 | static const struct { |
301 | const int *sigs; | |
81fc57ca | 302 | u_int nsigs; |
31364274 | 303 | void (*handler)(int signo |
304 | #ifdef SA_SIGINFO | |
305 | , siginfo_t *info, void *context | |
306 | #endif | |
307 | ); | |
81fc57ca | 308 | } sigmap[] = { |
837d16cc B |
309 | { core_signals, array_size(core_signals), core_handler}, |
310 | { exit_signals, array_size(exit_signals), exit_handler}, | |
311 | { ignore_signals, array_size(ignore_signals), NULL}, | |
59a06a91 | 312 | }; |
81fc57ca | 313 | u_int i; |
59a06a91 | 314 | |
837d16cc | 315 | for (i = 0; i < array_size(sigmap); i++) |
59a06a91 | 316 | { |
81fc57ca | 317 | u_int j; |
59a06a91 | 318 | |
319 | for (j = 0; j < sigmap[i].nsigs; j++) | |
320 | { | |
321 | struct sigaction oact; | |
322 | if ((sigaction(sigmap[i].sigs[j],NULL,&oact) == 0) && | |
323 | (oact.sa_handler == SIG_DFL)) | |
324 | { | |
325 | struct sigaction act; | |
59a06a91 | 326 | sigfillset (&act.sa_mask); |
40abf239 | 327 | if (sigmap[i].handler == NULL) |
328 | { | |
329 | act.sa_handler = SIG_IGN; | |
330 | act.sa_flags = 0; | |
331 | } | |
332 | else | |
333 | { | |
31364274 | 334 | #ifdef SA_SIGINFO |
40abf239 | 335 | /* Request extra arguments to signal handler. */ |
336 | act.sa_sigaction = sigmap[i].handler; | |
337 | act.sa_flags = SA_SIGINFO; | |
31364274 | 338 | #else |
339 | act.sa_handler = sigmap[i].handler; | |
340 | act.sa_flags = 0; | |
3f11a103 DL |
341 | #endif |
342 | #ifdef SA_RESETHAND | |
343 | /* don't try to print backtraces recursively */ | |
344 | if (sigmap[i].handler == core_handler) | |
345 | act.sa_flags |= SA_RESETHAND; | |
31364274 | 346 | #endif |
40abf239 | 347 | } |
59a06a91 | 348 | if (sigaction(sigmap[i].sigs[j],&act,NULL) < 0) |
349 | zlog_warn("Unable to set signal handler for signal %d: %s", | |
350 | sigmap[i].sigs[j],safe_strerror(errno)); | |
351 | ||
352 | } | |
353 | } | |
354 | } | |
355 | } | |
356 | ||
c49b3069 | 357 | void |
05c447dd | 358 | signal_init (struct thread_master *m, int sigc, |
359 | struct quagga_signal_t signals[]) | |
c49b3069 | 360 | { |
361 | ||
362 | int i = 0; | |
363 | struct quagga_signal_t *sig; | |
59a06a91 | 364 | |
365 | /* First establish some default handlers that can be overridden by | |
366 | the application. */ | |
367 | trap_default_signals(); | |
c49b3069 | 368 | |
369 | while (i < sigc) | |
370 | { | |
371 | sig = &signals[i]; | |
372 | if ( signal_set (sig->signal) < 0 ) | |
373 | exit (-1); | |
374 | i++; | |
375 | } | |
376 | ||
377 | sigmaster.sigc = sigc; | |
378 | sigmaster.signals = signals; | |
05c447dd | 379 | |
380 | #ifdef SIGEVENT_SCHEDULE_THREAD | |
66e78ae6 QY |
381 | sigmaster.t = NULL; |
382 | thread_add_timer(m, quagga_signal_timer, &sigmaster, QUAGGA_SIGNAL_TIMER_INTERVAL, | |
383 | &sigmaster.t); | |
05c447dd | 384 | #endif /* SIGEVENT_SCHEDULE_THREAD */ |
c49b3069 | 385 | } |