Line data Source code
1 : // SPDX-License-Identifier: GPL-2.0-or-later
2 : /* Quagga signal handling functions.
3 : * Copyright (C) 2004 Paul Jakma,
4 : */
5 :
6 : #include <zebra.h>
7 : #include <sigevent.h>
8 : #include <log.h>
9 : #include <memory.h>
10 : #include <lib_errors.h>
11 :
12 : #ifdef HAVE_UCONTEXT_H
13 : #ifdef GNU_LINUX
14 : /* get REG_EIP from ucontext.h */
15 : #ifndef __USE_GNU
16 : #define __USE_GNU
17 : #endif /* __USE_GNU */
18 : #endif /* GNU_LINUX */
19 : #include <ucontext.h>
20 : #endif /* HAVE_UCONTEXT_H */
21 :
22 :
23 : /* master signals descriptor struct */
24 : static struct frr_sigevent_master_t {
25 : struct event *t;
26 :
27 : struct frr_signal_t *signals;
28 : int sigc;
29 :
30 : volatile sig_atomic_t caught;
31 : } sigmaster;
32 :
33 : /* Generic signal handler
34 : * Schedules signal event thread
35 : */
36 4 : static void frr_signal_handler(int signo)
37 : {
38 4 : int i;
39 4 : struct frr_signal_t *sig;
40 :
41 20 : for (i = 0; i < sigmaster.sigc; i++) {
42 16 : sig = &(sigmaster.signals[i]);
43 :
44 16 : if (sig->signal == signo)
45 4 : sig->caught = 1;
46 : }
47 :
48 4 : sigmaster.caught = 1;
49 4 : }
50 :
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 6529 : bool frr_sigevent_check(sigset_t *setp)
58 : {
59 6529 : sigset_t blocked;
60 6529 : int i;
61 6529 : bool ret;
62 :
63 6529 : sigemptyset(setp);
64 6529 : sigemptyset(&blocked);
65 :
66 : /* Set up mask of application's signals */
67 39174 : for (i = 0; i < sigmaster.sigc; i++)
68 26116 : sigaddset(&blocked, sigmaster.signals[i].signal);
69 :
70 6529 : pthread_sigmask(SIG_BLOCK, &blocked, setp);
71 :
72 : /* Now that the application's signals are blocked, test. */
73 6529 : ret = (sigmaster.caught != 0);
74 :
75 6529 : return ret;
76 : }
77 :
78 : /* check if signals have been caught and run appropriate handlers */
79 6971 : int frr_sigevent_process(void)
80 : {
81 6971 : struct frr_signal_t *sig;
82 6971 : int i;
83 : #ifdef SIGEVENT_BLOCK_SIGNALS
84 : /* shouldn't need to block signals, but potentially may be needed */
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) {
97 : flog_err_sys(EC_LIB_SYSTEM_CALL,
98 : "frr_signal_timer: couldnt block signals!");
99 : return -1;
100 : }
101 : #endif /* SIGEVENT_BLOCK_SIGNALS */
102 :
103 6971 : if (sigmaster.caught > 0) {
104 4 : 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 18 : for (i = 0; i < sigmaster.sigc; i++) {
110 16 : sig = &(sigmaster.signals[i]);
111 :
112 16 : if (sig->caught > 0) {
113 4 : sig->caught = 0;
114 4 : if (sig->handler)
115 4 : sig->handler();
116 : }
117 : }
118 : }
119 :
120 : #ifdef SIGEVENT_BLOCK_SIGNALS
121 : if (sigprocmask(SIG_UNBLOCK, &oldmask, NULL) < 0)
122 : return -1;
123 : #endif /* SIGEVENT_BLOCK_SIGNALS */
124 :
125 6969 : return 0;
126 : }
127 :
128 : #ifdef SIGEVENT_SCHEDULE_THREAD
129 : /* timer thread to check signals. shouldn't be needed */
130 : void frr_signal_timer(struct event *t)
131 : {
132 : struct frr_sigevent_master_t *sigm;
133 :
134 : sigm = EVENT_ARG(t);
135 : sigm->t = NULL;
136 : event_add_timer(sigm->t->master, frr_signal_timer, &sigmaster,
137 : FRR_SIGNAL_TIMER_INTERVAL, &sigm->t);
138 : frr_sigevent_process();
139 : }
140 : #endif /* SIGEVENT_SCHEDULE_THREAD */
141 :
142 : /* Initialization of signal handles. */
143 : /* Signal wrapper. */
144 16 : static int signal_set(int signo)
145 : {
146 16 : int ret;
147 16 : struct sigaction sig;
148 16 : struct sigaction osig;
149 :
150 16 : sig.sa_handler = &frr_signal_handler;
151 16 : sigfillset(&sig.sa_mask);
152 16 : sig.sa_flags = 0;
153 16 : if (signo == SIGALRM) {
154 : #ifdef SA_INTERRUPT
155 0 : sig.sa_flags |= SA_INTERRUPT; /* SunOS */
156 : #endif
157 : } else {
158 : #ifdef SA_RESTART
159 16 : sig.sa_flags |= SA_RESTART;
160 : #endif /* SA_RESTART */
161 : }
162 :
163 16 : ret = sigaction(signo, &sig, &osig);
164 16 : if (ret < 0)
165 : return ret;
166 : else
167 16 : return 0;
168 : }
169 :
170 : /* XXX This function should be enhanced to support more platforms
171 : (it currently works only on Linux/x86). */
172 0 : static void *program_counter(void *context)
173 : {
174 : #ifdef HAVE_UCONTEXT_H
175 : #ifdef GNU_LINUX
176 : /* these are from GNU libc, rather than Linux, strictly speaking */
177 : #if defined(REG_EIP)
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
183 : #endif
184 : #endif /* GNU_LINUX */
185 :
186 : #ifdef REG_INDEX
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 */
193 :
194 : #ifdef REGS
195 0 : if (context)
196 0 : 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);
201 : #endif /* REGS */
202 :
203 : #endif /* HAVE_UCONTEXT_H */
204 : return NULL;
205 : }
206 :
207 : static void __attribute__((noreturn))
208 0 : exit_handler(int signo, siginfo_t *siginfo, void *context)
209 : {
210 0 : void *pc = program_counter(context);
211 :
212 0 : zlog_signal(signo, "exiting...", siginfo, pc);
213 0 : _exit(128 + signo);
214 : }
215 :
216 : static void __attribute__((noreturn))
217 0 : core_handler(int signo, siginfo_t *siginfo, void *context)
218 : {
219 0 : void *pc = program_counter(context);
220 :
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 0 : struct sigaction sa_default = {.sa_handler = SIG_DFL};
224 :
225 0 : sigaction(SIGALRM, &sa_default, NULL);
226 0 : sigaction(signo, &sa_default, NULL);
227 :
228 0 : sigset_t sigset;
229 :
230 0 : sigemptyset(&sigset);
231 0 : sigaddset(&sigset, SIGALRM);
232 0 : sigprocmask(SIG_UNBLOCK, &sigset, NULL);
233 :
234 0 : alarm(1);
235 :
236 0 : zlog_signal(signo, "aborting...", siginfo, pc);
237 :
238 : /* dump memory stats on core */
239 0 : log_memstats(stderr, "core_handler");
240 :
241 0 : zlog_tls_buffer_fini();
242 :
243 : /* give the kernel a chance to generate a coredump */
244 0 : sigaddset(&sigset, signo);
245 0 : sigprocmask(SIG_UNBLOCK, &sigset, NULL);
246 0 : 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 0 : _exit(128 + signo);
252 : }
253 :
254 4 : static void trap_default_signals(void)
255 : {
256 4 : static const int core_signals[] = {
257 : SIGQUIT, SIGILL, SIGABRT,
258 : #ifdef SIGEMT
259 : SIGEMT,
260 : #endif
261 : SIGFPE, SIGBUS, SIGSEGV,
262 : #ifdef SIGSYS
263 : SIGSYS,
264 : #endif
265 : #ifdef SIGXCPU
266 : SIGXCPU,
267 : #endif
268 : #ifdef SIGXFSZ
269 : SIGXFSZ,
270 : #endif
271 : };
272 4 : static const int exit_signals[] = {
273 : SIGHUP, SIGINT, SIGALRM, SIGTERM, SIGUSR1, SIGUSR2,
274 : #ifdef SIGPOLL
275 : SIGPOLL,
276 : #endif
277 : #ifdef SIGVTALRM
278 : SIGVTALRM,
279 : #endif
280 : #ifdef SIGSTKFLT
281 : SIGSTKFLT,
282 : #endif
283 : };
284 4 : static const int ignore_signals[] = {
285 : SIGPIPE,
286 : };
287 4 : static const struct {
288 : const int *sigs;
289 : unsigned int nsigs;
290 : void (*handler)(int signo, siginfo_t *info, void *context);
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 : };
296 4 : unsigned int i;
297 :
298 16 : for (i = 0; i < array_size(sigmap); i++) {
299 : unsigned int j;
300 :
301 88 : for (j = 0; j < sigmap[i].nsigs; j++) {
302 76 : struct sigaction oact;
303 76 : if ((sigaction(sigmap[i].sigs[j], NULL, &oact) == 0)
304 76 : && (oact.sa_handler == SIG_DFL)) {
305 76 : struct sigaction act;
306 76 : sigfillset(&act.sa_mask);
307 76 : if (sigmap[i].handler == NULL) {
308 4 : act.sa_handler = SIG_IGN;
309 4 : act.sa_flags = 0;
310 : } else {
311 : /* Request extra arguments to signal
312 : * handler. */
313 72 : act.sa_sigaction = sigmap[i].handler;
314 72 : act.sa_flags = SA_SIGINFO;
315 : #ifdef SA_RESETHAND
316 : /* don't try to print backtraces
317 : * recursively */
318 72 : if (sigmap[i].handler == core_handler)
319 36 : act.sa_flags |= SA_RESETHAND;
320 : #endif
321 : }
322 76 : if (sigaction(sigmap[i].sigs[j], &act, NULL)
323 : < 0)
324 76 : flog_err(
325 : EC_LIB_SYSTEM_CALL,
326 : "Unable to set signal handler for signal %d: %s",
327 : sigmap[i].sigs[j],
328 : safe_strerror(errno));
329 : }
330 : }
331 : }
332 4 : }
333 :
334 4 : void signal_init(struct event_loop *m, int sigc, struct frr_signal_t signals[])
335 : {
336 :
337 4 : int i = 0;
338 4 : struct frr_signal_t *sig;
339 :
340 : /* First establish some default handlers that can be overridden by
341 : the application. */
342 4 : trap_default_signals();
343 :
344 20 : while (i < sigc) {
345 16 : sig = &signals[i];
346 16 : if (signal_set(sig->signal) < 0)
347 0 : exit(-1);
348 16 : i++;
349 : }
350 :
351 4 : sigmaster.sigc = sigc;
352 4 : sigmaster.signals = signals;
353 :
354 : #ifdef SIGEVENT_SCHEDULE_THREAD
355 : sigmaster.t = NULL;
356 : event_add_timer(m, frr_signal_timer, &sigmaster,
357 : FRR_SIGNAL_TIMER_INTERVAL, &sigmaster.t);
358 : #endif /* SIGEVENT_SCHEDULE_THREAD */
359 4 : }
|