| /* Quagga signal handling functions. |
| * Copyright (C) 2004 Paul Jakma, |
| * |
| * This file is part of Quagga. |
| * |
| * Quagga is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License as published by the |
| * Free Software Foundation; either version 2, or (at your option) any |
| * later version. |
| * |
| * Quagga is distributed in the hope that it will be useful, but |
| * WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with Quagga; see the file COPYING. If not, write to the Free |
| * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA |
| * 02111-1307, USA. |
| */ |
| |
| #include <zebra.h> |
| #include <sigevent.h> |
| #include <log.h> |
| |
| #ifdef HAVE_UCONTEXT_H |
| #ifdef GNU_LINUX |
| /* get REG_EIP from ucontext.h */ |
| #define __USE_GNU |
| #endif /* GNU_LINUX */ |
| #include <ucontext.h> |
| #endif /* HAVE_UCONTEXT_H */ |
| |
| |
| /* master signals descriptor struct */ |
| struct quagga_sigevent_master_t |
| { |
| struct thread *t; |
| |
| struct quagga_signal_t *signals; |
| int sigc; |
| |
| volatile sig_atomic_t caught; |
| } sigmaster; |
| |
| /* Generic signal handler |
| * Schedules signal event thread |
| */ |
| static void |
| quagga_signal_handler (int signo) |
| { |
| int i; |
| struct quagga_signal_t *sig; |
| |
| for (i = 0; i < sigmaster.sigc; i++) |
| { |
| sig = &(sigmaster.signals[i]); |
| |
| if (sig->signal == signo) |
| sig->caught = 1; |
| } |
| |
| sigmaster.caught = 1; |
| } |
| |
| /* check if signals have been caught and run appropriate handlers */ |
| int |
| quagga_sigevent_process (void) |
| { |
| struct quagga_signal_t *sig; |
| int i; |
| #ifdef SIGEVENT_BLOCK_SIGNALS |
| /* shouldnt need to block signals, but potentially may be needed */ |
| sigset_t newmask, oldmask; |
| |
| /* |
| * Block most signals, but be careful not to defer SIGTRAP because |
| * doing so breaks gdb, at least on NetBSD 2.0. Avoid asking to |
| * block SIGKILL, just because we shouldn't be able to do so. |
| */ |
| sigfillset (&newmask); |
| sigdelset (&newmask, SIGTRAP); |
| sigdelset (&newmask, SIGKILL); |
| |
| if ( (sigprocmask (SIG_BLOCK, &newmask, &oldmask)) < 0) |
| { |
| zlog_err ("quagga_signal_timer: couldnt block signals!"); |
| return -1; |
| } |
| #endif /* SIGEVENT_BLOCK_SIGNALS */ |
| |
| if (sigmaster.caught > 0) |
| { |
| sigmaster.caught = 0; |
| /* must not read or set sigmaster.caught after here, |
| * race condition with per-sig caught flags if one does |
| */ |
| |
| for (i = 0; i < sigmaster.sigc; i++) |
| { |
| sig = &(sigmaster.signals[i]); |
| |
| if (sig->caught > 0) |
| { |
| sig->caught = 0; |
| sig->handler (); |
| } |
| } |
| } |
| |
| #ifdef SIGEVENT_BLOCK_SIGNALS |
| if ( sigprocmask (SIG_UNBLOCK, &oldmask, NULL) < 0 ); |
| return -1; |
| #endif /* SIGEVENT_BLOCK_SIGNALS */ |
| |
| return 0; |
| } |
| |
| #ifdef SIGEVENT_SCHEDULE_THREAD |
| /* timer thread to check signals. Shouldnt be needed */ |
| int |
| quagga_signal_timer (struct thread *t) |
| { |
| struct quagga_sigevent_master_t *sigm; |
| struct quagga_signal_t *sig; |
| int i; |
| |
| sigm = THREAD_ARG (t); |
| sigm->t = thread_add_timer (sigm->t->master, quagga_signal_timer, &sigmaster, |
| QUAGGA_SIGNAL_TIMER_INTERVAL); |
| return quagga_sigevent_process (); |
| } |
| #endif /* SIGEVENT_SCHEDULE_THREAD */ |
| |
| /* Initialization of signal handles. */ |
| /* Signal wrapper. */ |
| static int |
| signal_set (int signo) |
| { |
| int ret; |
| struct sigaction sig; |
| struct sigaction osig; |
| |
| sig.sa_handler = &quagga_signal_handler; |
| sigfillset (&sig.sa_mask); |
| sig.sa_flags = 0; |
| if (signo == SIGALRM) { |
| #ifdef SA_INTERRUPT |
| sig.sa_flags |= SA_INTERRUPT; /* SunOS */ |
| #endif |
| } else { |
| #ifdef SA_RESTART |
| sig.sa_flags |= SA_RESTART; |
| #endif /* SA_RESTART */ |
| } |
| |
| ret = sigaction (signo, &sig, &osig); |
| if (ret < 0) |
| return ret; |
| else |
| return 0; |
| } |
| |
| /* XXX This function should be enhanced to support more platforms |
| (it currently works only on Linux/x86). */ |
| static void * |
| program_counter(void *context) |
| { |
| #ifdef HAVE_UCONTEXT_H |
| #ifdef GNU_LINUX |
| #ifdef REG_EIP |
| if (context) |
| return (void *)(((ucontext_t *)context)->uc_mcontext.gregs[REG_EIP]); |
| #endif /* REG_EIP */ |
| #endif /* GNU_LINUX */ |
| #endif /* HAVE_UCONTEXT_H */ |
| return NULL; |
| } |
| |
| static void |
| exit_handler(int signo, siginfo_t *siginfo, void *context) |
| { |
| zlog_signal(signo, "exiting...", siginfo, program_counter(context)); |
| _exit(128+signo); |
| } |
| |
| static void |
| core_handler(int signo, siginfo_t *siginfo, void *context) |
| { |
| zlog_signal(signo, "aborting...", siginfo, program_counter(context)); |
| abort(); |
| } |
| |
| static void |
| trap_default_signals(void) |
| { |
| static const int core_signals[] = { |
| SIGQUIT, |
| SIGILL, |
| #ifdef SIGEMT |
| SIGEMT, |
| #endif |
| SIGFPE, |
| SIGBUS, |
| SIGSEGV, |
| #ifdef SIGSYS |
| SIGSYS, |
| #endif |
| #ifdef SIGXCPU |
| SIGXCPU, |
| #endif |
| #ifdef SIGXFSZ |
| SIGXFSZ, |
| #endif |
| }; |
| static const int exit_signals[] = { |
| SIGHUP, |
| SIGINT, |
| SIGALRM, |
| SIGTERM, |
| SIGUSR1, |
| SIGUSR2, |
| #ifdef SIGPOLL |
| SIGPOLL, |
| #endif |
| #ifdef SIGVTALRM |
| SIGVTALRM, |
| #endif |
| #ifdef SIGSTKFLT |
| SIGSTKFLT, |
| #endif |
| }; |
| static const int ignore_signals[] = { |
| SIGPIPE, |
| }; |
| static const struct { |
| const int *sigs; |
| u_int nsigs; |
| void (*handler)(int signo, siginfo_t *info, void *context); |
| } sigmap[] = { |
| { core_signals, sizeof(core_signals)/sizeof(core_signals[0]), core_handler}, |
| { exit_signals, sizeof(exit_signals)/sizeof(exit_signals[0]), exit_handler}, |
| { ignore_signals, sizeof(ignore_signals)/sizeof(ignore_signals[0]), NULL}, |
| }; |
| u_int i; |
| |
| for (i = 0; i < sizeof(sigmap)/sizeof(sigmap[0]); i++) |
| { |
| u_int j; |
| |
| for (j = 0; j < sigmap[i].nsigs; j++) |
| { |
| struct sigaction oact; |
| if ((sigaction(sigmap[i].sigs[j],NULL,&oact) == 0) && |
| (oact.sa_handler == SIG_DFL)) |
| { |
| struct sigaction act; |
| sigfillset (&act.sa_mask); |
| if (sigmap[i].handler == NULL) |
| { |
| act.sa_handler = SIG_IGN; |
| act.sa_flags = 0; |
| } |
| else |
| { |
| /* Request extra arguments to signal handler. */ |
| act.sa_sigaction = sigmap[i].handler; |
| act.sa_flags = SA_SIGINFO; |
| } |
| if (sigaction(sigmap[i].sigs[j],&act,NULL) < 0) |
| zlog_warn("Unable to set signal handler for signal %d: %s", |
| sigmap[i].sigs[j],safe_strerror(errno)); |
| |
| } |
| } |
| } |
| } |
| |
| void |
| signal_init (struct thread_master *m, int sigc, |
| struct quagga_signal_t signals[]) |
| { |
| |
| int i = 0; |
| struct quagga_signal_t *sig; |
| |
| /* First establish some default handlers that can be overridden by |
| the application. */ |
| trap_default_signals(); |
| |
| while (i < sigc) |
| { |
| sig = &signals[i]; |
| if ( signal_set (sig->signal) < 0 ) |
| exit (-1); |
| i++; |
| } |
| |
| sigmaster.sigc = sigc; |
| sigmaster.signals = signals; |
| |
| #ifdef SIGEVENT_SCHEDULE_THREAD |
| sigmaster.t = |
| thread_add_timer (m, quagga_signal_timer, &sigmaster, |
| QUAGGA_SIGNAL_TIMER_INTERVAL); |
| #endif /* SIGEVENT_SCHEDULE_THREAD */ |
| } |