On a Linux system, the bash builtin command disown
can be used to remove jobs from the current session. How does bash implement this feature? Is setsid()
used here, and if so, how does bash trigger the child process to invoke setsid()
? Does it use signal?
-
Did you already look at the source code but can't understand it? – Kusalananda Sep 19 '19 at 13:18
1 Answers
No. disown
does not remove a job from the current session [1], does not detach it from the terminal, and does not affect which signals the kernel will send it when the session leader exits or when the controlling terminal is torn down.
disown
only works on bash's own job table, only changes bash's idea of which jobs it's in control of, and only affects bash's own behavior, namely which jobs it will resend a SIGHUP
received by the bash process. That SIGHUP
resending is an extra feature of bash
[2], not required by the standard and not related to the job control provided by the OS.
You can see it with a simple example, where I'm using script(1)
to create a pty and an interactive shell session running in it:
$ script /dev/null -qc bash
$ sh -c 'sleep 555 & sleep .1; kill -STOP $!; trap "echo hupped!" HUP; sleep 666' &
[1] 3837
$ disown -a
$ jobs
# no jobs known to bash
$ pgrep -as0
# show all processes from the current session
3836 bash
3837 sh -c sleep 555 & sleep .1; kill -STOP $!; trap "echo hupped!" HUP; sleep 666
3838 sleep 555
3841 sleep 666
$ kill -HUP $$
# seppuku the session leader
Hangup
hupped!
Here the kernel sends a SIGHUP
signal to the background process group (= job) because one of its processes is stopped, and disowning it will not prevent that from happening.
All processes from the sh -c '...'
are part of the same job, including the "background" sleep &
; shell scripts don't do job control by default.
If no members of the background process group are stopped, no SIGHUP
is sent:
$ script /dev/null -qc bash
$ sh -c 'sleep 555 & trap "echo hupped!" HUP; sleep 666' &
[1] 3270
$ disown -a
$ kill -HUP $$
# sleep 555, 666 and sh -c are still running
Finally, bash will send a SIGHUP
to all the jobs from its table (only those started by itself and not disowned), no matter if bash is the session leader or not, or if the jobs are running, stopped, etc:
$ bash
$ sh -c 'sleep 555 & trap "echo hupped!" HUP; sleep 666' &
[1] 3413
$ kill -HUP $$
Hangup
hupped!
Hangup
[1] which would be impossible to do anyway; setsid()
is only able to make into a new session a process which is not a process group leader, there is no way to move a whole job into a new or existing session.
[2] which is documented in the bash's manpage:
The shell exits by default upon receipt of a
SIGHUP
. Before exiting, an interactive shell resends theSIGHUP
to all jobs, running or stopped. Stopped jobs are sentSIGCONT
to ensure that they receive theSIGHUP
. To prevent the shell from sending the signal to a particular job, it should be removed from the jobs table with thedisown
builtin (see SHELL BUILTIN COMMANDS below) or marked to not receiveSIGHUP
usingdisown -h
.
There's is also shopt -s huponexit
which cause a login bash shell to send a HUP
to its jobs upon exiting (whether because of a signal or not), which again overlaps in confusing ways with the standard job control features of the OS.