Does enabling huponexit ensure background jobs receive SIGHUP on shell exit, or does disown override it?
0 reputation · 15 Jan 2024, 14:38 UTC
0 reputation · 15 Jan 2024, 14:38 UTC
When migrating a small application without downtime using Bash, a common pattern is to replace the current shell with a new binary via exec while trapping SIGTERM and SIGHUP to perform cleanup and relaunch the service.
The reliability of this approach hinges on how background jobs are treated when the parent shell exits. Bash's huponexit option controls whether the shell sends SIGHUP to its jobs, but the disown builtin can remove jobs from the job table, potentially altering signal delivery.
Because the interaction between huponexit and disown varies across Bash versions, it is unclear which setting guarantees that a long-running child process will continue to receive (or avoid) SIGHUP during a seamless restart.
Does setting huponexit alone ensure that background jobs receive SIGHUP on shell exit, or does prior use of disown nullify that effect?
Can a migration script rely on the default huponexit state across Bash 3.x to 5.x to predict job-signal behavior?
31275 reputation · 16 Jan 2024, 02:29 UTC
Enabling huponexit ensures background jobs receive SIGHUP on shell exit only if they remain in the shell's job table. The disown builtin removes a job from that table entirely, making it immune to huponexit regardless of the option's state. A migration script cannot rely on the default huponexit state (which is off in Bash 3.x–5.x) to deliver SIGHUP; explicit set -o huponexit is required, and any prior disown on the target job nullifies the effect.
huponexit WorksWhen huponexit is enabled (set -o huponexit or set -huponexit), an interactive login shell sends SIGHUP to every job still tracked in its job table upon exit. This includes stopped jobs and background jobs started with & that have not been disowned. The option is disabled by default in all Bash versions 3.0 through 5.2+, so background jobs receive no SIGHUP on shell exit unless the script explicitly enables it.
disown Overrides huponexitdisown (with or without -h) removes the specified job from the shell's job table. Once removed, the job is no longer a "shell job" and the shell has no record of it at exit. Consequently, huponexit has no target to signal. This behavior is absolute and consistent across Bash 3.x, 4.x, and 5.x. Even disown -h (which only marks the job to suppress SIGHUP from the shell) keeps the job in the table but prevents SIGHUP delivery; a plain disown removes it entirely.
The interaction between huponexit and disown has not changed materially between Bash 3.0 (2004) and 5.2 (2022). Both builtins operate on the same job-table data structure. Scripts written for this mechanism will behave identically across that range, provided they run in an interactive login shell (or a shell with huponexit enabled in a context where it takes effect). Non-interactive shells ignore huponexit entirely.
Confirm behavior in your target Bash version:
Test huponexit delivery (no disown):
bash -c 'set -o huponexit; trap "echo SIGHUP received" HUP; sleep 10 & exit'The sleep process should receive SIGHUP and terminate when the shell exits.
Test disown suppression:
bash -c 'set -o huponexit; sleep 10 & disown; exit'The sleep process should continue running after shell exit (verify with ps).
Inspect job table before exit:
jobs -lOnly jobs listed here are subject to huponexit.
For a zero-downtime restart using exec:
disown the child process you intend to signal.set -o huponexit in the parent shell before launching the child.exec the new binary.If the child must survive shell exit without SIGHUP, use disown (or start it via nohup/setsid) and do not enable huponexit.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.