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– Kusalananda ♦2019-09-19 13:18:43 +00:00Commented Sep 19, 2019 at 13:18
1 Answer
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 theSIGHUPto all jobs, running or stopped. Stopped jobs are sentSIGCONTto 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 thedisownbuiltin (see SHELL BUILTIN COMMANDS below) or marked to not receiveSIGHUPusingdisown -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.