1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /* Quagga signal handling functions.
3 * Copyright (C) 2004 Paul Jakma,
10 #include <lib_errors.h>
12 #ifdef HAVE_UCONTEXT_H
14 /* get REG_EIP from ucontext.h */
17 #endif /* __USE_GNU */
18 #endif /* GNU_LINUX */
20 #endif /* HAVE_UCONTEXT_H */
23 /* master signals descriptor struct */
24 static struct frr_sigevent_master_t
{
27 struct frr_signal_t
*signals
;
30 volatile sig_atomic_t caught
;
33 /* Generic signal handler
34 * Schedules signal event thread
36 static void frr_signal_handler(int signo
)
39 struct frr_signal_t
*sig
;
41 for (i
= 0; i
< sigmaster
.sigc
; i
++) {
42 sig
= &(sigmaster
.signals
[i
]);
44 if (sig
->signal
== signo
)
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.
57 bool frr_sigevent_check(sigset_t
*setp
)
64 sigemptyset(&blocked
);
66 /* Set up mask of application's signals */
67 for (i
= 0; i
< sigmaster
.sigc
; i
++)
68 sigaddset(&blocked
, sigmaster
.signals
[i
].signal
);
70 pthread_sigmask(SIG_BLOCK
, &blocked
, setp
);
72 /* Now that the application's signals are blocked, test. */
73 ret
= (sigmaster
.caught
!= 0);
78 /* check if signals have been caught and run appropriate handlers */
79 int frr_sigevent_process(void)
81 struct frr_signal_t
*sig
;
83 #ifdef SIGEVENT_BLOCK_SIGNALS
84 /* shouldn't need to block signals, but potentially may be needed */
85 sigset_t newmask
, oldmask
;
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.
93 sigdelset(&newmask
, SIGTRAP
);
94 sigdelset(&newmask
, SIGKILL
);
96 if ((sigprocmask(SIG_BLOCK
, &newmask
, &oldmask
)) < 0) {
97 flog_err_sys(EC_LIB_SYSTEM_CALL
,
98 "frr_signal_timer: couldnt block signals!");
101 #endif /* SIGEVENT_BLOCK_SIGNALS */
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
109 for (i
= 0; i
< sigmaster
.sigc
; i
++) {
110 sig
= &(sigmaster
.signals
[i
]);
112 if (sig
->caught
> 0) {
120 #ifdef SIGEVENT_BLOCK_SIGNALS
121 if (sigprocmask(SIG_UNBLOCK
, &oldmask
, NULL
) < 0)
123 #endif /* SIGEVENT_BLOCK_SIGNALS */
128 #ifdef SIGEVENT_SCHEDULE_THREAD
129 /* timer thread to check signals. shouldn't be needed */
130 void frr_signal_timer(struct thread
*t
)
132 struct frr_sigevent_master_t
*sigm
;
134 sigm
= THREAD_ARG(t
);
136 thread_add_timer(sigm
->t
->master
, frr_signal_timer
, &sigmaster
,
137 FRR_SIGNAL_TIMER_INTERVAL
, &sigm
->t
);
138 frr_sigevent_process();
140 #endif /* SIGEVENT_SCHEDULE_THREAD */
142 /* Initialization of signal handles. */
143 /* Signal wrapper. */
144 static int signal_set(int signo
)
147 struct sigaction sig
;
148 struct sigaction osig
;
150 sig
.sa_handler
= &frr_signal_handler
;
151 sigfillset(&sig
.sa_mask
);
153 if (signo
== SIGALRM
) {
155 sig
.sa_flags
|= SA_INTERRUPT
; /* SunOS */
159 sig
.sa_flags
|= SA_RESTART
;
160 #endif /* SA_RESTART */
163 ret
= sigaction(signo
, &sig
, &osig
);
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
)
174 #ifdef HAVE_UCONTEXT_H
176 /* these are from GNU libc, rather than Linux, strictly speaking */
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
184 #endif /* GNU_LINUX */
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 */
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 ? */
200 return (void *)(((ucontext_t
*)context
)->uc_mcontext
.regs
->nip
);
203 #endif /* HAVE_UCONTEXT_H */
207 static void __attribute__((noreturn
))
208 exit_handler(int signo
, siginfo_t
*siginfo
, void *context
)
210 void *pc
= program_counter(context
);
212 zlog_signal(signo
, "exiting...", siginfo
, pc
);
216 static void __attribute__((noreturn
))
217 core_handler(int signo
, siginfo_t
*siginfo
, void *context
)
219 void *pc
= program_counter(context
);
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
};
225 sigaction(SIGALRM
, &sa_default
, NULL
);
226 sigaction(signo
, &sa_default
, NULL
);
230 sigemptyset(&sigset
);
231 sigaddset(&sigset
, SIGALRM
);
232 sigprocmask(SIG_UNBLOCK
, &sigset
, NULL
);
236 zlog_signal(signo
, "aborting...", siginfo
, pc
);
238 /* dump memory stats on core */
239 log_memstats(stderr
, "core_handler");
241 zlog_tls_buffer_fini();
243 /* give the kernel a chance to generate a coredump */
244 sigaddset(&sigset
, signo
);
245 sigprocmask(SIG_UNBLOCK
, &sigset
, NULL
);
248 /* only chance to end up here is if the default action for signo is
249 * something other than kill or coredump the process
254 static void trap_default_signals(void)
256 static const int core_signals
[] = {
257 SIGQUIT
, SIGILL
, SIGABRT
,
261 SIGFPE
, SIGBUS
, SIGSEGV
,
272 static const int exit_signals
[] = {
273 SIGHUP
, SIGINT
, SIGALRM
, SIGTERM
, SIGUSR1
, SIGUSR2
,
284 static const int ignore_signals
[] = {
287 static const struct {
290 void (*handler
)(int signo
, siginfo_t
*info
, void *context
);
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
},
298 for (i
= 0; i
< array_size(sigmap
); i
++) {
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
;
311 /* Request extra arguments to signal
313 act
.sa_sigaction
= sigmap
[i
].handler
;
314 act
.sa_flags
= SA_SIGINFO
;
316 /* don't try to print backtraces
318 if (sigmap
[i
].handler
== core_handler
)
319 act
.sa_flags
|= SA_RESETHAND
;
322 if (sigaction(sigmap
[i
].sigs
[j
], &act
, NULL
)
326 "Unable to set signal handler for signal %d: %s",
328 safe_strerror(errno
));
334 void signal_init(struct thread_master
*m
, int sigc
,
335 struct frr_signal_t signals
[])
339 struct frr_signal_t
*sig
;
341 /* First establish some default handlers that can be overridden by
343 trap_default_signals();
347 if (signal_set(sig
->signal
) < 0)
352 sigmaster
.sigc
= sigc
;
353 sigmaster
.signals
= signals
;
355 #ifdef SIGEVENT_SCHEDULE_THREAD
357 thread_add_timer(m
, frr_signal_timer
, &sigmaster
,
358 FRR_SIGNAL_TIMER_INTERVAL
, &sigmaster
.t
);
359 #endif /* SIGEVENT_SCHEDULE_THREAD */