Function register

Source
pub unsafe fn register<F>(signal: c_int, action: F) -> Result<SigId, Error>
where F: Fn() + Sync + Send + 'static,
Expand description

Registers an arbitrary action for the given signal.

This makes sure there’s a signal handler for the given signal. It then adds the action to the ones called each time the signal is delivered. If multiple actions are set for the same signal, all are called, in the order of registration.

If there was a previous signal handler for the given signal, it is chained ‒ it will be called as part of this library’s signal handler, before any actions set through this function.

On success, the function returns an ID that can be used to remove the action again with unregister.

§Panics

If the signal is one of (see FORBIDDEN):

  • SIGKILL
  • SIGSTOP
  • SIGILL
  • SIGFPE
  • SIGSEGV

The first two are not possible to override (and the underlying C functions simply ignore all requests to do so, which smells of possible bugs, or return errors). The rest can be set, but generally needs very special handling to do so correctly (direct manipulation of the application’s address space, longjmp and similar). Unless you know very well what you’re doing, you’ll shoot yourself into the foot and this library won’t help you with that.

§Errors

Since the library manipulates signals using the low-level C functions, all these can return errors. Generally, the errors mean something like the specified signal does not exist on the given platform ‒ after a program is debugged and tested on a given OS, it should never return an error.

However, if an error is returned, there are no guarantees if the given action was registered or not.

§Safety

This function is unsafe, because the action is run inside a signal handler. While Rust is somewhat vague about the consequences of such, it is reasonably to assume that similar restrictions as specified in C or C++ apply.

In particular:

  • Calling any OS functions that are not async-signal-safe as specified as POSIX is not allowed.
  • Accessing globals or thread-locals without synchronization is not allowed (however, mutexes are not within the async-signal-safe functions, therefore the synchronization is limited to using atomics).

The underlying reason is, signals are asynchronous (they can happen at arbitrary time) and are run in context of arbitrary thread (with some limited control of at which thread they can run). As a consequence, things like mutexes are prone to deadlocks, memory allocators can likely contain mutexes and the compiler doesn’t expect the interruption during optimizations.

Things that generally are part of the async-signal-safe set (though check specifically) are routines to terminate the program, to further manipulate signals (by the low-level functions, not by this library) and to read and write file descriptors. The async-signal-safety is transitive - that is, a function composed only from computations (with local variables or with variables accessed with proper synchronizations) and other async-signal-safe functions is also safe.

As panicking from within a signal handler would be a panic across FFI boundary (which is undefined behavior), the passed handler must not panic.

Note that many innocently-looking functions do contain some of the forbidden routines (a lot of things lock or allocate).

If you find these limitations hard to satisfy, choose from the helper functions in the signal-hook crate ‒ these provide safe interface to use some common signal handling patters.

§Race condition

Upon registering the first hook for a given signal into this library, there’s a short race condition under the following circumstances:

  • The program already has a signal handler installed for this particular signal (through some other library, possibly).
  • Concurrently, some other thread installs a different signal handler while it is being installed by this library.
  • At the same time, the signal is delivered.

Under such conditions signal-hook might wrongly “chain” to the older signal handler for a short while (until the registration is fully complete).

Note that the exact conditions of the race condition might change in future versions of the library. The recommended way to avoid it is to register signals before starting any additional threads, or at least not to register signals concurrently.

Alternatively, make sure all signals are handled through this library.

§Performance

Even when it is possible to repeatedly install and remove actions during the lifetime of a program, the installation and removal is considered a slow operation and should not be done very often. Also, there’s limited (though huge) amount of distinct IDs (they are u128).

§Examples

extern crate signal_hook_registry;

use std::io::Error;
use std::process;

fn main() -> Result<(), Error> {
    let signal = unsafe {
        signal_hook_registry::register(signal_hook::consts::SIGTERM, || process::abort())
    }?;
    // Stuff here...
    signal_hook_registry::unregister(signal); // Not really necessary.
    Ok(())
}