nosig

Manage signals before running programs (a generic "nohup")

View on GitHub

nosig(1): run a program with specified signals blocked

Synopsis

nosig [options...] [--] program [arguments...]
nosig [--ignore|--default] sigspec [...] program [...]
nosig [--add|--del] sigspec [--block|--unblock|--set] [...] program [...]

Description

The nosig program is used to quickly manage signal dispositions and the signal block mask. These are distinct settings, although most users will not care about the difference.

If you're familiar with the nohup(1) program, then nosig is like that but way more flexible. The equivalent to `nohup ...` is `nosig --ignore SIGHUP ...`. That is, nohup(1) ignores SIGHUP by setting its signal disposition to SIG_IGN before executing the specified program. It does not add the signal to the signal block mask.

Most users will be able to accomplish what they want using only the --ignore option. That will run programs with specific signals initially ignored.

nosig options operate as a state machine that take effect immediately. That means later options will always override earlier options, and all options may be combined as needed. This allows you to quickly reason about behavior as well as fully set up known states including fully resetting signal dispositions or the signal block mask.

Changing the signal block mask is a fairly advanced topic, and most users can skip over those options the vast majority of the time. They might be useful as a fallback when dealing with a particularly stubborn program; see the Signal block mask management and Examples sections below for more details.

Resetting state

nosig offers a bunch of options to quickly reset all signal state. Specifically, --reset will reset all signal dispositions & block masks. This can help when trying to debug a setup where signals are being initialized incorrectly and you want to guarantee good state before running another tool.

If you need a little more fine-grained selection, a variety of --xxx-all options are available. This way you don't have to verbosely list out every possible signal yourself.

Signal specifications (sigspec)

tl;dr: Use names like SIGINT or SIGTERM to specify signals.

nosig options operate on a simple specification (a.k.a. a sigspec).

These are symbolic signal names (strongly preferred), or system-specific numbers (strongly discouraged). The symbolic names are portable across systems while the numbers might not be stable even on the current system. This is due to signal handling in the system itself, not anything under the control of nosig. Support for numbers is largely a fallback if the symbolic signal is unknown on your system.

Most people will use common signal names like SIGINT, or omit the leading SIG prefix and simply use INT.

For realtime signals, you'll want to use the names SIGRTMIN and SIGRTMAX with offsets like SIGRTMIN+1 or SIGRTMAX-1.

The set of sigspecs that nosig supports can be displayed with the --list option.

Standard vs realtime signals

If you're not familiar with realtime signals, then you most likely can completely ignore them!

The signals users are most familiar with and utilize on a daily basis are the standard signals (or simply just "signals"). These have the signal names you're used to seeing like SIGINT or SIGTERM.

Realtime signals on the other hand only have two named signal bases, and all signals are derived from those as offsets. Those are SIGRTMIN and SIGRTMAX. Programs will then define their own internal names & uses for signals like SIGRTMIN and SIGRTMIN+1 (since POSIX only defines realtime signals as a range). That is why nosig uses those naming conventions — they hopefully align well with whatever program you're working with.

Keep in mind that, while the standard signals have names that reflect their meaning & intended use, the realtime signals have no predefined meaning. So the only way to know how a specific realtime signal (e.g. SIGRTMIN+5) is used, or whether it's used at all, is entirely specific to a program.

The --ignore-all and --block-all options will operate on all standard & realtime signals as a default. Additional flags are provided to select the respective subsets.

Signal block mask management

The signal block mask is used to block delivery of signals without having to change their signal disposition or handlers. Many programs never register handlers for signals, so simply changing their dispositions (using --ignore) is sufficient for most users to effectively "block a signal" for a program.

If you have a program that registers a signal handler for a signal that you're trying to block, then attempts to use --ignore will be overridden (see the Locking signal settings section below for more details). In that case, blocking the signal using the signal block mask might work.

The signal set options (--add, --del, --empty, --fill, --block, --unblock, --set) operate on a single signal set. The signal set always starts off empty. Using --empty as the first option would be redundant, but you might prefer it for clarity.

You then modify the signal set (adding, deleting, clearing, filling signals) before using the signal set (blocking, unblocking, setting signals). You may repeat these series of actions (modify, use, reset) as many times as makes sense for your setup; nosig has no limit here.

For example, you might want to block a few signals, unblock a few signals, but leave all the rest unchanged. You would modify the set (e.g. --add multiple times), use the set (e.g. --block), reset the set (e.g. --empty), modify the set (e.g. --add multiple times), then use the set again (e.g. --unblock).

If you want the inverse behavior (only unblocking one or two signals), then you probably want to take the opposite approach by adding all signals to the set (e.g. --fill), removing the few signals you care about (e.g. --del multiple times), and then setting the signal block mask (e.g. --set).

Options

Generic options

Signal disposition (signal(2)) options

Signal set management (sigsetops(3)) options

Signal set usage (sigprocmask(2)) options

Output options

Informational options

Notes

Unblockable/unignorable signals

There are a few signals that the OS might not allow you to modify. Most notably, SIGKILL and SIGSTOP usually may not be blocked or ignored. There is nothing nosig (or any other program) can do to workaround this OS restriction.

The OS will usually silently ignore requests to block them. nosig does not attempt to diagnose this for the user.

The OS might return errors to ignore these signals, but nosig will silently ignore these errors by default too.

This may also come up with the reserved realtime signals; see the Reserved realtime signals section for more details on those.

Reserved realtime signals

The signals SIGRTMIN & SIGRTMAX are not actually constant. Depending on the OS & runtime libraries, POSIX allows them to be dynamic. This allows the runtime to reserve a few signals for internal purposes.

Notably, GNU C library (glibc)'s native POSIX threads library (pthreads/NPTL) will reserve two signals for its own internal use. The nptl(7) man page goes into great detail here.

nosig will not attempt to bypass these reservations. It rarely (if ever) makes sense to do so, and certainly the vast majority of users would never want such behavior, let alone inadvertently or as a default. If you really want to take over the reserved signals, you will need to write our own code/tools to do so.

Alternative signal dispositions

It is not possible to change the signal behavior beyond ignore & the default disposition (i.e. make the signal trigger a core(5) or have it stop). This is simply how signals work and isn't really something nosig can workaround. Doing so would require changes to the OS, or executing code in the process itself which would require unreliable hackery like LD_PRELOAD via ld.so(8).

Locking signal settings

nosig only initializes the signal settings before handing off control to the program. The program still has full control over its own runtime signal settings, thus it may completely reset all signal dispositions or the signal block mask. There is no way to workaround this (see the Alternative signal dispositions section for similar details).

Examples

Common uses

# Ignore a single signal like `nohup`!
nosig --ignore SIGHUP <cmd>
alias nohup='nosig --ignore SIGHUP --'

# Ignore SIGINT (Ctrl-C) signals.
nosig --ignore SIGINT <cmd>

# Ignore SIGTSTP (Ctrl-Z) signals (i.e. background/suspend requests).
nosig --ignore SIGTSTP <cmd>

# Ignore SIGQUIT (Ctrl-\) signals.
nosig --ignore SIGQUIT <cmd>

# Ignore all signals except for SIGINT (Ctrl-C).
nosig --ignore-all --default SIGINT <cmd>

# Ignore all signals.  The command can only be killed with SIGKILL (kill -9)!
nosig --ignore-all <cmd>

Advanced signal block mask uses

NB: Manipulating the signal block mask is not common. Try the examples above first by ignoring signals.

# Block all signals.
nosig --block-all <cmd>
nosig --fill --block <cmd>

# Unblock all signals.
nosig --unblock-all <cmd>
nosig --fill --unblock <cmd>

# Block all signals except SIGUSR1.
nosig --block-all --add USR1 --unblock <cmd>

# Block all signals, but leave SIGUSR1 unchanged.
nosig --fill --del SIGUSR1 --block <cmd>

Exit Status

If program was executed, then the exit status will be of it.

Otherwise:
· 0 An informational nosig option (e.g. --version) was handled.
· 125 nosig itself exited.
· 126 program was found, but could not be executed.
· 127 program could not be found.

Reporting Bugs

Please report all bugs to the project page:
https://github.com/vapier/nosig/issues

Authors

Mike Frysinger <vapier@gmail.com>

See Also

nohup(1), sigaction(2), signal(2), sigprocmask(2), sigsetops(3), signal(7)