diff options
Diffstat (limited to 'lib/sigevent.c')
-rw-r--r-- | lib/sigevent.c | 990 |
1 files changed, 752 insertions, 238 deletions
diff --git a/lib/sigevent.c b/lib/sigevent.c index 18fcffb0..c7c1e134 100644 --- a/lib/sigevent.c +++ b/lib/sigevent.c @@ -19,11 +19,19 @@ * 02111-1307, USA. */ -#include <zebra.h> -#include <sigevent.h> -#include <log.h> - -#ifdef SA_SIGINFO +#include "zebra.h" +#include "misc.h" +#include "sigevent.h" +#include "log.h" +#include "vty.h" +#include "qpnexus.h" +#include "qpthreads.h" + +#include <stdarg.h> + +/*------------------------------------------------------------------------------ + * Want to get some context for core and exit handlers. + */ #ifdef HAVE_UCONTEXT_H #ifdef GNU_LINUX /* get REG_EIP from ucontext.h */ @@ -33,50 +41,407 @@ #endif /* GNU_LINUX */ #include <ucontext.h> #endif /* HAVE_UCONTEXT_H */ -#endif /* SA_SIGINFO */ +/*------------------------------------------------------------------------------ + * Use SA_SIGINFO type handlers throughout + */ +#ifndef SA_SIGINFO +#error Sorry... require SA_SIGINFO +#endif + +typedef void sig_handler(int signo, siginfo_t* info, void* context) ; + +/*============================================================================== + * Signal handling for Quagga. + * + * The objectives are: + * + * 1) to handle the abnormal terminations so that they are logged, and + * any available information logged with them. + * + * 2) to ignore a number of signals that have no significance + * + * 3) to catch some signals such that they are treated as events in either + * the qpthreads or the legacy threads worlds. + * + * For the qpthreads world, these are all handled in the main thread. + * + * These may not be any of the "hard" signals or any of the signals + * reserved by the library. + * + * 4) to catch some signals such that they cause an "interrupt" to, e.g. + * pselect(), but have no other effect. + * + * SIGUSR2 is reserved for this purpose for qpthreads world + * (aka SIG_INTERRUPT). + * + * Signal disposition is established early in the morning, and is static from + * then on. + */ + +/*============================================================================== + * Signal Sets. + * + * The following signal sets are initialised by signal_init(). + * + * * hard_signals -- signals that mean that the program has misbehaved, and + * should exit, now -- e.g. SIGSEGV or SIGILL. + * + * In the pthreaded world, these signals are handled + * (or at least, not blocked) by all threads, and it is + * expected that they will be given to the thread which + * has failed. + * + * These signals are not blocked. + * + * This includes SIGKILL and SIGSTOP, which cannot be + * blocked, caught or ignored. + * + * * core_signals -- signals that by default are terminate + core, so this + * more or less the hard_signals, except: + * + * * excludes SIGKILL and SIGSTOP + * + * * includes at least one (SIGQUIT) signal that is + * not a hard_signal. + * + * The default action is to catch these signals, log the + * event and then abort() -- having turned off the + * SIGABRT handler ! + * + * * exit_signals -- signals that by default are terminate, so this + * includes, e.g., SIGTERM or SIGINT. + * + * The default action is to catch these signals, log the + * event and then exit(). + * + * * ignore_signals -- signals which, by default, we wish to ignore, so are + * set to SIG_IGN. + * + * * qsig_signals -- signals which are caught, and are later signalled to + * quagga_sigevent_process(). + * + * Note that multiple signals may be seen before + * quagga_sigevent_process() is run, but they will only + * generate one event. + * + * * qsig_interrupts -- signals which are caught, but otherwise ignored, + * so their only function is to interrupt -- in particular + * to interrupt pselect(). + * + * * qsig_reserved -- signal which the library reserves for itself. + * + * In the pthreads world, all signals other than the hard_signals are blocked + * by all threads other than the main thread. + * + * Note that we leave SIGTRAP alone -- so do not disturb debuggger(s). + */ +static bool signals_initialised = false ; + +static sigset_t hard_signals[1] ; +static sigset_t core_signals[1] ; +static sigset_t exit_signals[1] ; +static sigset_t ignore_signals[1] ; +static sigset_t qsig_signals[1] ; +static sigset_t qsig_interrupts[1] ; +static sigset_t qsig_reserved[1] ; + +static void qsig_add(int signo, qsig_event* event) ; +static int signal_set_set(sigset_t* set, sig_handler* handler) ; +static int signal_set(int signo, sig_handler* handler) ; + +static void __attribute__ ((noreturn)) + core_handler(int signo, siginfo_t *info, void *context) ; +static void __attribute__ ((noreturn)) + exit_handler(int signo, siginfo_t* info, void* context) ; +static void + quagga_signal_handler(int signo, siginfo_t* info, void* context) ; +static void + quagga_interrupt_handler(int signo, siginfo_t* info, void* context) ; + +/*------------------------------------------------------------------------------ + * The following signals are not known to POSIX (2008) or are extensions. + */ +#ifndef SIGEMT +# define SIGEMT 0 +#endif +#ifndef SIGIO +# define SIGIO 0 +#endif +#ifndef SIGIOT +# define SIGIOT 0 +#endif +#ifndef SIGPOLL +# define SIGPOLL 0 +#endif +#ifndef SIGPROF +# define SIGPROF 0 +#endif +#ifndef SIGPWR +# define SIGPWR 0 +#endif +#ifndef SIGSTKFLT +# define SIGSTKFLT 0 +#endif +#ifndef SIGSYS +# define SIGSYS 0 +#endif +#ifndef SIGVTALRM +# define SIGVTALRM 0 +#endif +#ifndef SIGWINCH +# define SIGWINCH 0 +#endif +#ifndef SIGXRES +# define SIGXRES 0 +#endif + +/*------------------------------------------------------------------------------ + * The signal handling below assumes that no signal that we have any interest + * in will have a signal number greater than the following. + * + * All the signals we are interested in are initialised in signal_init, so + * asserts in the code below will trap, very early on, any signal with a + * larger number than this. + * + * This value is used to place a very outside limit on the signal numbers that + * will attempt to deal with. + */ +enum { SIG_MAX = 128, SIG_COUNT } ; + +/* The value is established at signal_init() time, as the maximum signal + * number to consider -- established by sigaddset() on an empty set. + */ +int sig_max = 0 ; + +/*------------------------------------------------------------------------------ + * Quagga signals descriptor struct + * + * Maps real signal numbers to "qso" ordinals... A relatively limited number + * of signals need to be fed into the event system, this mechanism minimises + * the work required to discover which signal has gone off. + * + * The qsig actions are held in a small vector in the static sigmaster + * structure. + */ +enum +{ + sig_null = 0, /* no real signal is this */ + sig_min = 1, /* first real signal is at least this */ +} ; +typedef uchar sig_num_t ; /* signal number */ +CONFIRM(SIG_MAX <= UCHAR_MAX) ; + +enum +{ + qso_null = 0, /* no qsig uses this */ + qso_min = 1, /* first qsig is this */ + qso_max = 10, /* only a handful are really required */ -/* master signals descriptor struct */ -struct quagga_sigevent_master_t + qso_count, /* number qs ordinals */ +} ; +typedef uchar qs_ord_t ; /* qs ordinal */ + +/*------------------------------------------------------------------------------ + * Static structure for all known qsig_signals. + * + * Note that the qsig_interrupts are not recorded here ! + */ +struct quagga_sigevent_master { + qs_ord_t qsigc ; /* number of signals known */ + volatile sig_atomic_t caught[qso_count] ; + + qsig_event* event[qso_count] ; /* how to deal with them */ + + qs_ord_t map[SIG_COUNT] ; /* real signal to qs ordinal */ + +#ifdef SIGEVENT_SCHEDULE_THREAD struct thread *t; +#endif + +} qsig_master ; + +/*------------------------------------------------------------------------------ + * Initialise signals. + * + * 1. construct the signal sets discussed above. + * + * 2. set default handlers for: core_signals -- core_handler() + * exit_signals -- exit_handler() + * ignore_signals -- SIG_IGN + * + * 3. set handlers for signals used by library + * + * 4. set handlers for signals used by the daemon. + * + * This is done once, and once only, early in the morning. + */ +extern void +signal_init (struct thread_master *m, int sigc, + struct quagga_signal_t signals[]) +{ + int i ; + + /* Set sig_max by experiment */ + { + sigset_t trial[1] ; + int signo ; + + sigemptyset(trial) ; + for (signo = sig_min ; signo <= SIG_COUNT ; ++signo) + { + if (sigaddset(trial, signo) < 0) + break ; + } ; + + --signo ; /* last acceptable signo */ + if ((signo < sig_min) || (signo > SIG_MAX)) + zabort("cannot establish reasonable 'sig_max'") ; + + sig_max = signo ; + } ; + + /* Construct the standard sets of signals. */ + sigmakeset(hard_signals, SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGKILL, + SIGSEGV, SIGSTOP, SIGXCPU, SIGXFSZ, + SIGSYS, + SIGEMT, SIGIOT, SIGXRES, + -1) ; + + sigcopyset(core_signals, hard_signals) ; + sigaddset(core_signals, SIGQUIT) ; + sigdelset(core_signals, SIGKILL) ; + sigdelset(core_signals, SIGSTOP) ; + + sigmakeset(exit_signals, SIGALRM, SIGHUP, SIGINT, SIGTERM, SIGUSR1, SIGUSR2, + SIGIO, SIGPOLL, SIGPROF, SIGPWR, SIGSTKFLT, + SIGVTALRM, + -1) ; + + sigmakeset(ignore_signals, SIGCHLD, SIGCONT, SIGPIPE, SIGTSTP, SIGTTIN, + SIGTTOU, SIGURG, SIGWINCH, + -1) ; + + /* Initialise the sig_master, qsig_signals and qsig_interrupts. + * + * Reserve all the hard_signals. + */ + memset(&qsig_master, 0, sizeof(qsig_master)) ; + + sigemptyset(qsig_signals) ; + sigemptyset(qsig_interrupts) ; + sigcopyset(qsig_reserved, hard_signals) ; + + /* The signals used by the library. + * + * Added to qsig_signals or qsig_interrupts and to qsig_reserved. + */ + qsig_add(SIGCHLD, vty_sigchld) ; + qsig_add(SIG_INTERRUPT, NULL) ; + + /* Now collect the daemon's own signals. + * + * Added to qsig_signals or qsig_interrupts and to qsig_reserved. + */ + for (i = 0 ; i < sigc ; ++i) + qsig_add(signals[i].signal, signals[i].handler) ; + + /* In case signals with different names are the same, and to make the + * required reservations, let qsig_reserved take precedence over exit_signals + * and those take precedence over ignore_signals. + */ + sigsubsets(exit_signals, qsig_reserved) ; + sigsubsets(ignore_signals, qsig_reserved) ; + sigsubsets(ignore_signals, exit_signals) ; + + /* Also, remove anything from core_signals which is now qsig_signals or + * qsig_interrupts (possibly SIGQUIT !). + */ + sigsubsets(core_signals, qsig_signals) ; + sigsubsets(core_signals, qsig_interrupts) ; + + /* Install handlers */ + signal_set_set(core_signals, core_handler) ; + signal_set_set(exit_signals, exit_handler) ; + signal_set_set(ignore_signals, NULL) ; + signal_set_set(qsig_signals, quagga_signal_handler) ; + signal_set_set(qsig_interrupts, quagga_interrupt_handler) ; + + /* If using a timer thread to scan for signal events, start that now. + */ +#ifdef SIGEVENT_SCHEDULE_THREAD + sig_master.t = + thread_add_timer (m, quagga_signal_timer, &sig_master, + QUAGGA_SIGNAL_TIMER_INTERVAL); +#endif /* SIGEVENT_SCHEDULE_THREAD */ - struct quagga_signal_t *signals; - int sigc; + /* Signals are now initialised */ + signals_initialised = true ; +} ; - volatile sig_atomic_t caught; -} sigmaster; +/*------------------------------------------------------------------------------ + * Get the hard_signals set + */ +extern const sigset_t* +signal_get_hard_set(void) +{ + assert(signals_initialised) ; + return hard_signals ; +} ; -/* Generic signal handler - * Schedules signal event thread +/*------------------------------------------------------------------------------ + * Add signal to those to be caught either for quagga_sigevent_process() or + * to then be dropped (so called interrupt signals). + * + * Checks that signal is not amongst the reserved signals. + * + * Add signal to the reserved signals. */ static void -quagga_signal_handler (int signo) +qsig_add(int signo, qsig_event* event) { - int i; - struct quagga_signal_t *sig; + int s ; + + s = sigismember(qsig_reserved, signo) ; + if ((s < 0) || (signo > sig_max)) + zabort("invalid or unknown signal number") ; + if (s > 0) + zabort("signal is reserved (or already set)") ; - for (i = 0; i < sigmaster.sigc; i++) + sigaddset(qsig_reserved, signo) ; + + if (event == NULL) + sigaddset(qsig_interrupts, signo) ; + else { - sig = &(sigmaster.signals[i]); + sigaddset(qsig_signals, signo) ; - if (sig->signal == signo) - sig->caught = 1; - } + if (qsig_master.qsigc >= qso_count) + zabort("too many signals to be caught") ; - sigmaster.caught = 1; -} + ++qsig_master.qsigc ; -/* check if signals have been caught and run appropriate handlers + qsig_master.map[signo] = qsig_master.qsigc ; + qsig_master.event[qsig_master.qsigc] = event ; + + } ; +} ; + +/*============================================================================== + * The event level handling of qsig_signals, delivered via qsig_master. + */ + +/*------------------------------------------------------------------------------ + * check if signals have been caught and run respective event functions * * Returns: 0 => nothing to do * -1 => failed * > 0 => done this many signals */ -int +extern int quagga_sigevent_process (void) { - struct quagga_signal_t *sig; int i; int done ; #ifdef SIGEVENT_BLOCK_SIGNALS @@ -100,21 +465,18 @@ quagga_sigevent_process (void) #endif /* SIGEVENT_BLOCK_SIGNALS */ done = 0 ; - if (sigmaster.caught > 0) + if (qsig_master.caught[qso_null] != 0) { - sigmaster.caught = 0; - /* must not read or set sigmaster.caught after here, + qsig_master.caught[qso_null] = 0; + /* must not read or set sigmaster.caught[0] after here, * race condition with per-sig caught flags if one does */ - - for (i = 0; i < sigmaster.sigc; i++) + for (i = 1 ; i <= qsig_master.qsigc ; i++) { - sig = &(sigmaster.signals[i]); - - if (sig->caught > 0) + if (qsig_master.caught[i] != 0) { - sig->caught = 0; - sig->handler (); + qsig_master.caught[i] = 0; + (qsig_master.event[i])() ; ++done ; } } @@ -128,6 +490,9 @@ quagga_sigevent_process (void) return done ; } +/*------------------------------------------------------------------------------ + * Optional timer thread to poll for signals + */ #ifdef SIGEVENT_SCHEDULE_THREAD /* timer thread to check signals. Shouldnt be needed */ int @@ -144,246 +509,395 @@ quagga_signal_timer (struct thread *t) } #endif /* SIGEVENT_SCHEDULE_THREAD */ -/* Initialization of signal handles. */ -/* Signal wrapper. */ -static int -signal_set (int signo) +/*============================================================================== + * The signal handlers. + */ +static void * program_counter(void *context) ; + +/*------------------------------------------------------------------------------ + * Terminate + Core + */ +static void __attribute__ ((noreturn)) +core_handler(int signo, siginfo_t *info, void *context) { - 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; + zlog_signal(signo, "aborting...", info, program_counter(context)) ; + zabort_abort(); } -#ifdef SA_SIGINFO +/*------------------------------------------------------------------------------ + * Terminate + */ +static void __attribute__ ((noreturn)) +exit_handler(int signo, siginfo_t* info, void* context) +{ + zlog_signal(signo, "exiting...", info, program_counter(context)); + _exit(128+signo); +} + +/*------------------------------------------------------------------------------ + * Generic signal handler -- captures signals in the sig_master caught vector. + * + * quagga_sigevent_process() deals with the caught signals. + */ +static void +quagga_signal_handler(int signo, siginfo_t* info, void* context) +{ + qs_ord_t qso ; + + if (sigismember(qsig_signals, signo) <= 0) + { + zlog_signal(signo, "quagga_signal_handler: unknown or invalid signal", + info, program_counter(context)) ; + zabort_abort(); + } ; + + qso = qsig_master.map[signo] ; + + /* Set individual caught flag before composite. + * + * This works if quagga_signal_handler() and quagga_sigevent_process() + * were to run at the same time -- unlikely though that may be. + * + * If quagga_sigevent_process() sees individual flag before the composite, + * that's fine -- worst that happens is will run through a further time + * and not find anything. + * + * If flags were set in the opposite order, could clear the composite + * and miss the individual, so lose the signal till the next time the + * composite is set. + */ + qsig_master.caught[qso] = 1 ; + qsig_master.caught[qso_null] = 1 ; +} ; + +/*------------------------------------------------------------------------------ + * Generic signal handler -- where signal is to be caught, and immediately + * dropped. + */ +static void +quagga_interrupt_handler(int signo, siginfo_t* info, void* context) +{ + if (sigismember(qsig_interrupts, signo) <= 0) + { + zlog_signal(signo, "quagga_interrupt_handler: unknown or invalid signal", + info, program_counter(context)) ; + zabort_abort(); + } ; +} ; -/* XXX This function should be enhanced to support more platforms - (it currently works only on Linux/x86). */ +/*------------------------------------------------------------------------------ + * Extract program counter from context. + * + * 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 +# 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 /* REG_EIP */ +# ifdef REG_RIP + if (context) + return (void *)(((ucontext_t *)context)->uc_mcontext.gregs[REG_RIP]); +# endif /* REG_RIP */ +# endif /* GNU_LINUX */ #endif /* HAVE_UCONTEXT_H */ return NULL; -} +} ; -#endif /* SA_SIGINFO */ +/*============================================================================== + * Signal clearing for abort() and fork()/vfork(). + */ -static void __attribute__ ((noreturn)) -exit_handler(int signo -#ifdef SA_SIGINFO - , siginfo_t *siginfo, void *context -#endif - ) +/*------------------------------------------------------------------------------ + * Set default sigaction for given signo + */ +static int +sigaction_set_default(int signo) { - zlog_signal(signo, "exiting..." -#ifdef SA_SIGINFO - , siginfo, program_counter(context) -#endif - ); - _exit(128+signo); -} + struct sigaction act[1] ; -static void __attribute__ ((noreturn)) -core_handler(int signo -#ifdef SA_SIGINFO - , siginfo_t *siginfo, void *context -#endif - ) + memset(act, 0, sizeof(act)) ; /* inter alia, clear sa_flags */ + act->sa_handler = SIG_DFL ; /* return to default state */ + sigemptyset(&act->sa_mask) ; /* no extra masking */ + + return sigaction(signo, act, NULL) ; +} ; + +/*------------------------------------------------------------------------------ + * When finally aborting, need to turn off the handling of SIGABRT, and need + * to make sure that the signal is not blocked. + */ +extern void +quagga_sigabrt_no_trap(void) { - zlog_signal(signo, "aborting..." -#ifdef SA_SIGINFO - , siginfo, program_counter(context) -#endif - ); - zabort_abort(); -} + sigset_t set[1] ; + + sigaction_set_default(SIGABRT) ; + + sigemptyset(set) ; + sigaddset(set, SIGABRT) ; + qpt_thread_sigmask(SIG_UNBLOCK, set, NULL) ; + /* sigprocmask() if !qpthreads_enabled */ +} ; -/* For the signals known to Quagga, and which are in their default state, - * set a Quagga default handler. +/*------------------------------------------------------------------------------ + * Having forked, make sure that all signals are in default state and that + * no signals are blocked. + * + * Expects not to fail. */ -static void -trap_default_signals(void) +extern void +quagga_signal_reset(void) { - static const int core_signals[] = { - SIGQUIT, - SIGILL, - SIGABRT, -#ifdef SIGEMT - SIGEMT, -#endif -#ifdef SIGIOT - SIGIOT, -#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 -#ifdef SA_SIGINFO - , siginfo_t *info, void *context -#endif - ); - } 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++) + sigset_t set[1] ; + int signo ; + + /* Before changing the handling of any signals, mask everything and + * clear out any pending signals. + */ + sigfillset(set) ; + sigprocmask(SIG_SETMASK, set, NULL) ; + + while (1) { - u_int j; + sigpending(set) ; + if (sighasmember(set) == 0) + break ; + sigwait(set, &signo) ; + } ; + + /* Set all signals to default handler. */ + for (signo = sig_min ; signo <= sig_max ; ++signo) + { + if ((signo == SIGKILL) || (signo == SIGSTOP)) + continue ; - for (j = 0; j < sigmap[i].nsigs; j++) - { - struct sigaction oact; - if (sigaction(sigmap[i].sigs[j], NULL, &oact) < 0) - zlog_warn("Unable to get signal handler for signal %d: %s", - sigmap[i].sigs[j], errtoa(errno, 0).str); - else { -#ifdef SA_SIGINFO - if (oact.sa_flags & SA_SIGINFO) - continue ; /* Don't set again */ + sigaction_set_default(signo) ; + } ; + + /* Unmask everything */ + sigemptyset(set) ; + sigprocmask(SIG_SETMASK, set, NULL) ; +} ; + +/*============================================================================== + * Functions to install signal handlers. + */ + +/*------------------------------------------------------------------------------ + * Set given handler for given set of signals. NULL handler => SIG_IGN. + * + * Returns: < 0 => failed -- value is - failing signo ! + */ +static int +signal_set_set(sigset_t* set, sig_handler* handler) +{ + int signo ; + + signo = 0 ; + for (signo = sig_min ; signo <= sig_max ; ++signo) + { + int s ; + s = sigismember(set, signo) ; + if (s < 0) + break ; + if (s > 0) + if (signal_set(signo, handler) < 0) + return -signo ; + } ; + + return 0 ; +} ; + +/*------------------------------------------------------------------------------ + * Set given handler for given signal. NULL handler => SIG_IGN. + * + * Returns: < 0 => failed + */ +#ifndef SA_INTERRUPT +# define SA_INTERRUPT 0 #endif - if (oact.sa_handler != SIG_DFL) - continue ; /* Don't set again */ - } - 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 - { -#ifdef SA_SIGINFO - /* Request extra arguments to signal handler. */ - act.sa_sigaction = sigmap[i].handler; - act.sa_flags = SA_SIGINFO; -#else - act.sa_handler = sigmap[i].handler; - act.sa_flags = 0; +#ifndef SA_RESTART +# define SA_RESTART 0 #endif - } - if (sigaction(sigmap[i].sigs[j], &act, NULL) < 0) - zlog_warn("Unable to set signal handler for signal %d: %s", - sigmap[i].sigs[j], errtoa(errno, 0).str); - } - } +static int +signal_set(int signo, sig_handler* handler) +{ + struct sigaction act[1] ; + + if (handler == NULL) + { + act->sa_handler = SIG_IGN ; + act->sa_flags = 0 ; } -} + else + { + act->sa_sigaction = handler ; + act->sa_flags = SA_SIGINFO ; + } ; -void -signal_init (struct thread_master *m, int sigc, - struct quagga_signal_t signals[]) + sigfillset (&act->sa_mask) ; /* mask everything */ + + if (signo == SIGALRM) + act->sa_flags |= SA_INTERRUPT ; /* want SIGALRM to interrupt */ + else + act->sa_flags |= SA_RESTART ; /* all others want restart */ + + act->sa_flags |= SA_NOCLDSTOP ; + + return sigaction (signo, act, NULL) ; +} ; + +/*============================================================================== + * Additional signal set support. + */ + +/*------------------------------------------------------------------------------ + * Make a signal set. + * + * Takes variable list of signal number arguments: + * + * * ignores zeros + * + * * stops on first value < 0 + */ +extern void +sigmakeset(sigset_t* set, ...) { + va_list va ; + int signo ; - int i = 0; - struct quagga_signal_t *sig; + va_start(va, set) ; - /* First establish some default handlers that can be overridden by - the application. */ - trap_default_signals(); + sigemptyset(set) ; + while ((signo = va_arg(va, int)) >= 0) + { + if (signo != 0) + if (sigaddset(set, signo) < 0) + zabort("invalid signal number") ; + } ; + + va_end(va) ; +} ; - while (i < sigc) +/*------------------------------------------------------------------------------ + * Copy a signal set. + */ +extern void +sigcopyset(sigset_t* dst, const sigset_t* src) +{ + *dst = *src ; +} ; + +/*------------------------------------------------------------------------------ + * Add signal set 'b' into set 'a'. + */ +extern void +sigaddsets(sigset_t* a, const sigset_t* b) +{ + int signo ; + + for (signo = sig_min ; signo < SIG_MAX ; ++signo) { - sig = &signals[i]; - if ( signal_set (sig->signal) < 0 ) - exit (-1); - i++; - } + int s ; + s = sigismember(b, signo) ; + if (s < 0) + break ; + if (s > 0) + sigaddset(a, signo) ; + } ; +} ; - sigmaster.sigc = sigc; - sigmaster.signals = signals; +/*------------------------------------------------------------------------------ + * Subtract signal set 'b' from set 'a'. + */ +extern void +sigsubsets(sigset_t* a, const sigset_t* b) +{ + int signo ; -#ifdef SIGEVENT_SCHEDULE_THREAD - sigmaster.t = - thread_add_timer (m, quagga_signal_timer, &sigmaster, - QUAGGA_SIGNAL_TIMER_INTERVAL); -#endif /* SIGEVENT_SCHEDULE_THREAD */ -} + for (signo = sig_min ; signo < SIG_MAX ; ++signo) + { + int s ; + s = sigismember(b, signo) ; + if (s < 0) + break ; + if (s > 0) + sigdelset(a, signo) ; + } ; +} ; -/* turn off trap for SIGABRT ! */ -extern void quagga_sigabrt_no_trap(void) +/*------------------------------------------------------------------------------ + * Make set 'a' be the inverse of set 'b' + */ +extern void +siginvset(sigset_t* a, const sigset_t* b) { - struct sigaction new_act ; - sigset_t set ; + int signo ; - sigfillset(&set) ; + sigfillset(a) ; - new_act.sa_handler = SIG_DFL ; - new_act.sa_mask = set ; - new_act.sa_flags = 0 ; - sigaction(SIGABRT, &new_act, NULL) ; + for (signo = sig_min ; signo < SIG_MAX ; ++signo) + { + int s ; + s = sigismember(b, signo) ; + if (s < 0) + break ; + if (s > 0) + sigdelset(a, signo) ; + } ; +} ; - sigemptyset(&set) ; - sigaddset(&set, SIGABRT) ; - sigprocmask(SIG_UNBLOCK, &set, NULL) ; +/*------------------------------------------------------------------------------ + * See if there is any intersection between two sets. + * + * Returns: first signo of intersection -- may be more ! + * 0 <=> none + */ +extern int +sigincommon(const sigset_t* a, const sigset_t* b) +{ + int signo ; + for (signo = sig_min ; signo < SIG_MAX ; ++signo) + { + int s ; + s = sigismember(a, signo) ; + if (s < 0) + return 0 ; + if ((s > 0) && (sigismember(b, signo) > 0)) + return signo ; + } ; + + return 0 ; } ; +/*------------------------------------------------------------------------------ + * See if there is anything in the given set. + * + * Returns: first signo found -- may be more ! + * 0 <=> none + */ +extern int +sighasmember(const sigset_t* a) +{ + int signo ; + + for (signo = sig_min ; signo < SIG_MAX ; ++signo) + { + int s ; + s = sigismember(a, signo) ; + if (s < 0) + return 0 ; + if (s > 0) + return signo ; + } ; + + return 0 ; +} ; |