#include <SealSignal.h>
Public Types | |
typedef bool(* | QuitHook )(int sig, siginfo_t *info, void *x) |
typedef bool(* | FatalHook )(int sig, siginfo_t *info, void *x) |
typedef void(* | FatalReturn )(int sig, siginfo_t *info, void *x) |
typedef void(* | HandlerType )(int sig, siginfo_t *info, void *extra) |
Static Public Member Functions | |
static const char * | name (int sig) |
static HandlerType | handler (int sig, sigset_t *mask=0) |
static HandlerType | handle (int sig, HandlerType handler, const sigset_t *blockMask=0) |
static void | revert (int sig) |
static void | ignore (int sig) |
static void | block (int sig, bool sense) |
static void | block (const sigset_t *mask, bool sense) |
static void | mask (const sigset_t *mask, sigset_t *old=0) |
static int | raise (int sig) |
static int | kill (pid_t process, int sig) |
static int | queue (int sig, int value=0) |
static int | queue (int sig, void *value) |
static int | queue (pid_t process, int sig, int value=0) |
static int | queue (pid_t process, int sig, void *value) |
static bool | pending (int sig) |
static void | pending (sigset_t *mask) |
static void | suspend (const sigset_t *mask) |
static bool | wait (int sig, siginfo_t *info=0, long msecs=-1) |
static int | wait (const sigset_t *mask, siginfo_t *info=0, long msecs=-1) |
static void | handleQuit (QuitHook hook=0) |
static QuitHook | handleQuitHook (void) |
static void | quit (int sig, siginfo_t *info, void *x) |
static void | handleFatal (const char *applicationName=0, IOFD fd=IOFD_INVALID, FatalHook hook=0, FatalReturn mainreturn=0, unsigned options=FATAL_DEFAULT) |
static IOFD | handleFatalFd (void) |
static FatalHook | handleFatalHook (void) |
static FatalReturn | handleFatalReturn (void) |
static unsigned | handleFatalOptions (void) |
static void | fatal (int sig, siginfo_t *info, void *x) |
static bool | fatalDump (int sig, siginfo_t *info, void *x) |
static int | fatalLevel (void) |
static bool | crashed (void) |
static void | dumpInfo (IOFD fd, char *buf, int sig, const siginfo_t *info) |
static void | dumpMemory (IOFD fd, char *buf, const void *data, size_t n) |
static void | dumpContext (IOFD fd, char *buf, const void *context) |
static const char * | describe (int sig, int code) |
Static Public Attributes | |
static const int | USR1_DUMP_CORE = 1 |
static const int | FATAL_ON_QUIT = 2 |
static const int | FATAL_ON_INT = 4 |
static const int | FATAL_DUMP_CORE = 8 |
static const int | FATAL_DUMP_SIG = 16 |
static const int | FATAL_DUMP_STACK = 32 |
static const int | FATAL_DUMP_LIBS = 64 |
static const int | FATAL_DUMP_CONTEXT = 128 |
static const int | FATAL_AUTO_EXIT = 256 |
static const int | FATAL_DEFAULT |
Utilities for handling signals and fatal errors.
FIXME: POSIX single-threaded vs. multi-threaded signals?
The fatal error handling is largely inspired by code in DDD, the Data Display Debugger, and by the examples in GNU libc manual.
typedef bool(* Athena::Signal::FatalHook)(int sig, siginfo_t *info, void *x) |
Application hook to run in fatal(). The hook should return true
if the signal handler should proceed to die. sig is the signal number, or its negative if core was dumped and, as far as can determined, successfully produced.
The fatal hooks should, if possible, perform clean-ups similar to QuitHook. The application may achieve this by actually using the quit by setting FATAL_AUTO_EXIT for handleFatal(), or it could reuse an internal function in both handlers.
typedef void(* Athena::Signal::FatalReturn)(int sig, siginfo_t *info, void *x) |
Application hook to jump back to the main program from a fatal signal, for example using siglongjmp. It must never return. sig is the signal number, or its negative if core was dumped and, as far as can determined, successfully produced.
typedef void(* Athena::Signal::HandlerType)(int sig, siginfo_t *info, void *extra) |
Signal handler type. This is defined explicitly and does not necessarily match the system's concept of signal handler type. If necessary, suitable trampolines are used internally to make sure the arguments make sense.
sig | The signal number. | |
info | Pointer to signal info. This pointer will be null on platforms that do not support POSIX signals. | |
extra | Extra argument, e.g. the fault address. This pointer will be null on platforms that do not support POSIX signals. |
typedef bool(* Athena::Signal::QuitHook)(int sig, siginfo_t *info, void *x) |
Application clean-up hook invoked before quit() exits from program termination signals (SIGHUP, SIGTERM or SIGQUIT).
The handler should return true
if the signal handler should proceed to exit the application. Note that certain options to handlFatal() cause this hook to be invoked for fatal signals. If such behaviour is enabled, be sure to check the crashed() status before deciding to let the application to continue.
The quit hook should take care of resetting terminal modes, killing child processes, removing lock files, and so forth.
void Athena::Signal::block | ( | const sigset_t * | mask, | |
bool | sense | |||
) | [static] |
Block or unblock the signals specified by mask. The signals are blocked if sense is true
, unblocked otherwise. This function is implemented only on systems with POSIX signals.
void Athena::Signal::block | ( | int | sig, | |
bool | sense | |||
) | [static] |
Block or unblock the signal number sig. The signal is blocked if sense is true
, unblocked otherwise. This function is implemented only on systems with POSIX signals.
bool Athena::Signal::crashed | ( | void | ) | [static] |
Return the crash status indicator: true
if a fatal signal has been received since the program started. Set if fatal() is entered with a fatal signal.
const char * Athena::Signal::describe | ( | int | sig, | |
int | code | |||
) | [static] |
Return the description for signal info code code for signal number sig. The code should come from siginfo_t::si_code
.
void Athena::Signal::dumpContext | ( | IOFD | fd, | |
char * | buf, | |||
const void * | context | |||
) | [static] |
Utility function to dump the process context, as obtained for instance through signal handler parameters, unix getcontext()
or Windows GetThreadContext()
. The output is written directly to the file descriptor fd, using buf as the formatting buffer.
Utility function to dump the signal info descriptor for signal sig, as obtained for instance through signal handler parameters or wait(). The output is written directly to the file descriptor fd, using buf as the formatting buffer.
void Athena::Signal::dumpMemory | ( | IOFD | fd, | |
char * | buf, | |||
const void * | data, | |||
size_t | n | |||
) | [static] |
Utility function to dump memory section from data for n bytes. Used to dump machine context on platforms where we don't know any better. The output is written directly to the file descriptor fd, using buf as the formatting buffer.
void Athena::Signal::fatal | ( | int | sig, | |
siginfo_t * | info, | |||
void * | x | |||
) | [static] |
The fatal signal handler.
This is the handler installed by handleFatal(). Please use handleFatal() and this method instead of installing your handlers with handle(). You should be able use the handler options to specify all the control you need.
The first thing this handler does is to reinstall itself for the benefit of platforms with single-delivery signals. Immediately after that it unblocks the delivery of that signal again, in case the signal handler itself gets in trouble. The next step is to check if the current crash level (the recursion of calls to fatal(), see fatalLevel()) exceeds the predefined limit of 4; if so, we give up and let the application die with this this signal. The handler then determines whether the signal is fatal: everything except SIGINT is, and SIGINT is fatal if FATAL_ON_INT was set. If the signal is fatal, crash indicator is set (see crashed()).
If this is not a nested fatal signal, the signal is fatal, and FATAL_DUMP_CORE is set, the handler tries dump a core file. Then the handler will either attempt to quit or to return to the main program depending on FATAL_AUTO_EXIT option setting. If it is set or this is a nested fatal signal, the handler will attempt to exit as follows: the application hook (or fatalDump() in its absence) is invoked. If the hook returns true
, quit() is called; otherwise the signal handler will return (and crash or get an infinite sequence of fatal signals). Note that if an application hook is registered, fataldump() is not called by default; the application hook must invoke it itself to get the dump.
If FATAL_AUTO_EXIT is not set, the application must have registered a main return hook, which will be invoked. The hook must not return, but do a siglongjmp
back to the main program (it should not throw unless all code is built with options that allow exceptions to be thrown from signal handlers). Note that the fatal signal may be asynchronous and may have arisen in code left in unsafe state, so returning back to the main program may not buy you much. It may make sense for a few things like rogue null pointer dereferences or floating point exceptions.
An interactive application using a main return hook should do something like this when the sigsetjmp
in the main loop returns:
Using a main return will most likely leak memory like a sieve, but in balance, the application just got a fatal signal and the leak is unlikely to be the greatest concern.
bool Athena::Signal::fatalDump | ( | int | sig, | |
siginfo_t * | info, | |||
void * | extra | |||
) | [static] |
Dump application state information on a fatal signal.
Use this method to dump program state on a delivery of a fatal signal. fatal() uses this function automatically if no fatal handler hook has not been registered by the application.
This function attempts to be as maximally robust given that it runs in a signal handler in conditions where the program by definition is unstable. In other words, it allocates no memory and writes its output directly to a file descriptor with direct system calls. For this reason some initialisation is required; use handleFatal() to register the current application name and an output file descriptor, preferably as early in the program as possible.
The dump will consist of the following items:
This always returns true
so it is convenient for the application fatal hook to return with a call to this function.
Note that this function will not flush std::cerr
or stderr
before producing output, for stability reasons. If the streams have unflushed output in their buffers, that output may get mixed with unbuffered direct output from this function. If you wish to avoid this mixup and are willing to take the risk that those calls might crash, install an application hook that flushes the streams and then calls this function.
int Athena::Signal::fatalLevel | ( | void | ) | [static] |
Return the depth to which fatal() is currently recursively entered, or zero if fatal() is not currently active. Use this method in application fatal hook to decide which operations are safe to perform. For example, if the attempts to notify the user result in further signals, it is best to avoid such attempts at deeper recursion levels. Currently fatal() ceases to call the application's hooks and forces termination if the nesting level reaches 4.
Signal::HandlerType Athena::Signal::handle | ( | int | sig, | |
HandlerType | handler, | |||
const sigset_t * | blockMask = 0 | |||
) | [static] |
Install a new signal handler handler for signal number sig and returns the old handler.
This method uses the POSIX signal handling primitives if they are available, failing which falling back to the C standard signal()
function.
When POSIX signals are used, signals other than sig are blocked according to blockMask (if null, no change is made) during the execution of handler. Note that the signal itself is always blocked during the handler execution and need not be mentioned in the mask explicitly. System calls are made restartable although this has little impact as this library always restarts interrupted system calls automatically despite the signal handling settings.
(FIXME: Expose option SA_NOCLDSTOP, SA_ONSTACK?) (FIXME: Threads vs. signals)
void Athena::Signal::handleFatal | ( | const char * | applicationName = 0 , |
|
IOFD | fd = IOFD_INVALID , |
|||
FatalHook | hook = 0 , |
|||
FatalReturn | mainreturn = 0 , |
|||
unsigned | options = FATAL_DEFAULT | |||
) | [static] |
Install default handler for fatal signals.
This method installs a handler for fatal signals such as floating point exceptions, illegal instructions, and memory violations. The behaviour is more precisely determined by options, a bitwise or of the option constants defined in the class declaration.
applicationName sets the application name to be used to report the signal in fatalDump(). fd sets the file descriptor to which the fatal signal message is written; by default this will be the standard error output. hook sets the pre-exit application hook to invoke, mainreturn sets the hook to return to back to the application "main loop" (i.e. ignore the signal by jumping out of the signal back to the somewhere higher up in the application).
Options left to default values will not change the current state. This allows one to re-install signal handlers without disturbing already registered information. Use this to restore handlers after some other library has meddled with the handlers.
This installs fatal() as the handler for fatal signals and on Windows for otherwise unhandled fatal structured exceptions. If FATAL_ON_QUIT is included in options, quitting related signals (see quit()) are also considered fatal. If FATAL_ON_INT is set, SIGINT is considered fatal---but see also fatal() documentation. If USR1_DUMP_CORE is set, DebugAids::coredump is registered as a handler for SIGUSR1 (please note the security risks of this option in its documentation).
A multi-threaded application should call this method in each thread. (FIXME: Calling this in one thread and blocking signals in others won't work on Linux, and in any case will probably produce non-sense stack traces (unless stacktrace can be fixed to dump the stacks of all the threads). Since the handler is always the same, I am not sure it will make the slightest difference which thread catches the signals, and on the other hand, it is best to dump the problems in the faulting thread if possible.)
IOFD Athena::Signal::handleFatalFd | ( | void | ) | [static] |
Return the file descriptor fataldump() uses for output. Registered through handleFatal().
Signal::FatalHook Athena::Signal::handleFatalHook | ( | void | ) | [static] |
Return the application fatal signal hook. Registered through handleFatal().
unsigned Athena::Signal::handleFatalOptions | ( | void | ) | [static] |
Return the current fatal signal handling options. Set on invocation to handleFatal().
Signal::FatalReturn Athena::Signal::handleFatalReturn | ( | void | ) | [static] |
Return the application fatal signal return hook. Registered through handleFatal().
Signal::QuitHook Athena::Signal::handleQuitHook | ( | void | ) | [static] |
Return the current application quit signal hook. Registered through handleQuit().
Signal::HandlerType Athena::Signal::handler | ( | int | sig, | |
sigset_t * | mask = 0 | |||
) | [static] |
Return the current handler for signal number sig and its blocked signals in mask (if non-null).
void Athena::Signal::ignore | ( | int | sig | ) | [static] |
Ignore the signal number sig.
int Athena::Signal::kill | ( | pid_t | process, | |
int | sig | |||
) | [static] |
Send the signal sig to process identified by process. Implemented only on unixen.
void Athena::Signal::mask | ( | const sigset_t * | mask, | |
sigset_t * | old = 0 | |||
) | [static] |
Set the list of currently blocked signals to mask and return the old setting in old (if non-null). This function is implemented only on systems with POSIX signals.
const char * Athena::Signal::name | ( | int | sig | ) | [static] |
Return the name of the signal number sig. The returned memory is statically allocated and must not be freed.
void Athena::Signal::pending | ( | sigset_t * | mask | ) | [static] |
Return in mask the list of signals pending for this process.
bool Athena::Signal::pending | ( | int | sig | ) | [static] |
Check if sig is pending for this process.
int Athena::Signal::queue | ( | pid_t | process, | |
int | sig, | |||
void * | value | |||
) | [static] |
Queue signal sig with additional data value for process. Implemented only on systems with POSIX real-time signals.
int Athena::Signal::queue | ( | pid_t | process, | |
int | sig, | |||
int | value = 0 | |||
) | [static] |
Queue signal sig with additional data value for process. Implemented only on systems with POSIX real-time signals.
int Athena::Signal::queue | ( | int | sig, | |
void * | value | |||
) | [static] |
Queue signal sig for this process with additional data value. Implemented only on systems with POSIX real-time signals.
int Athena::Signal::queue | ( | int | sig, | |
int | value = 0 | |||
) | [static] |
Queue signal sig for this process with additional data value. Implemented only on systems with POSIX real-time signals.
void Athena::Signal::quit | ( | int | sig, | |
siginfo_t * | info, | |||
void * | x | |||
) | [static] |
The quit signal handler.
This is the handler installed by handleQuit(). Please use handleQuit() and this method instead of installing your own handlers with handle().
This handler first invokes the application hook if one was given to handleQuit(). If the hook returns true
, the signal handler for this signal (number sig) is reset to its default handler, and the signal is re-raised. This causes the program to exit via the signal and have a the correct exit status.
The application should do whatever is necessary for a graceful shutdown. Note however that this signal may arrive asynchronously at any time, hence it probably isn't safe to allocate memory, use the standard output streams, and so forth. What you can do is to set a flag, return false
to return back to your application, detect the flag setting and drain your current event loop, and then quit. But do note that if FATAL_AUTO_EXIT was set in call to handleFatal(), fatal() will call quit() which in turn calls the application hook. Thus the hook should make sure it returns true
if the application has crashed as noted in the documentation for <<QuitHook>>.
int Athena::Signal::raise | ( | int | sig | ) | [static] |
void Athena::Signal::revert | ( | int | sig | ) | [static] |
Revert the signal number sig back to its default behaviour.
void Athena::Signal::suspend | ( | const sigset_t * | mask | ) | [static] |
Temporarily replace the signal mask of the process with mask and then suspend until a signal is received.
int Athena::Signal::wait | ( | const sigset_t * | mask, | |
siginfo_t * | info = 0 , |
|||
long | msecs = -1 | |||
) | [static] |
Suspend the thread waiting for signals specified by mask for at most msecs milliseconds. If msecs is negative (the default), waits until a signal is delivered. Otherwise waits up to the specified time limit. Returns the number of the signal that was received, or -1 if the time limit expired. If info is given, fills it with the information that the handler would have otherwise been given. Note that the signals must be blocked (in a multi-threaded application in all the threads, not just the calling one) and not be ignored before calling this function; if a handler is registered, it won't be called. Implemented only on systems with POSIX real-time signals.
bool Athena::Signal::wait | ( | int | sig, | |
siginfo_t * | info = 0 , |
|||
long | msecs = -1 | |||
) | [static] |
Suspend the thread waiting for signal sig at most msecs milliseconds. If msecs is negative (the default), waits until a signal is delivered. Otherwise waits up to the specified time limit. Returns true
if the signal was received. Note that the signal must be blocked (in a multi-threaded application in all the threads, not just the calling one) and not be ignored before calling this function; if a handler is registered, it won't be called. Implemented only on systems with POSIX real-time signals.
const int Athena::Signal::FATAL_AUTO_EXIT = 256 [static] |
const int Athena::Signal::FATAL_DEFAULT [static] |
(USR1_DUMP_CORE | FATAL_ON_INT | FATAL_DUMP_CORE | FATAL_DUMP_SIG | FATAL_DUMP_STACK | FATAL_DUMP_LIBS | FATAL_DUMP_CONTEXT | FATAL_AUTO_EXIT)
Default options to handleFatal().
const int Athena::Signal::FATAL_DUMP_CONTEXT = 128 [static] |
Option to make fataldump() (invoked by fatal()) to dump the machine context (registers etc.) from the fault position.
const int Athena::Signal::FATAL_DUMP_CORE = 8 [static] |
Option to make fatal() dump a core file before crashing.
const int Athena::Signal::FATAL_DUMP_LIBS = 64 [static] |
Option to make fataldump() (invoked by fatal()) to dump the list of currently loaded shared libraries.
const int Athena::Signal::FATAL_DUMP_SIG = 16 [static] |
const int Athena::Signal::FATAL_DUMP_STACK = 32 [static] |
Option to make fataldump() (invoked by fatal()) to dump stack backtrace for the offending code location.
const int Athena::Signal::FATAL_ON_INT = 4 [static] |
Option to make SIGINT fatal. It will still just quit, not crash.
const int Athena::Signal::FATAL_ON_QUIT = 2 [static] |
Option to make SIGHUP, SIGTERM and SIGQUIT fatal instead of just quit() signals.
const int Athena::Signal::USR1_DUMP_CORE = 1 [static] |
Option that instructs fatal() to call coredump() on SIGUSR1. This is merely a request to drop a core
; no attempt is made to guarantee success. Failure may result for example for lack of permissions, for lack of disk space, or due to low resource limits. Please note that core
files can only be created on unixen. Note also that dropping a core is a security risk and should never be enabled in setuid or setgid programs or for production applications.