What is the relation between TTY, sessions, process groups and child processes ? This hierarchy is used to propagate signals and select which process output is displayed by a termninal.

All processes spawned by Bash share the same session. They generally have their own process group except for :

Linux_Tty_Session.svg

How to kill all processes forked by a script

Using systemd-run

systemd-run allows to run a command in a transient unit file. All forked processes will be terminated when the service process ends or you call systemctl --user stop <unit>.

systemd-run --user \
  --name "<unit_name>" \
  --property=KillMode=mixed \
  --pty --same-dir --wait --collect --service-type=exec \
  bash "<script_path>"

# If you just want an interective terminal
systemd-run --user --shell

# All processes fored from the unit are in the same cgroup
cat "/sys/fs/cgroup/systemd/user.slice/.../<unit_name>/cgroup.procs"

Using current session id

# get the session leader, if using `setsid` then $$ == $leader
leader=`ps --pid $$ --no-headers -o sid | sed 's/ //g'`
# Small flaw : allsid[@] contains the transient `grep` process :-(
# sid 0 translates to the current session
all_in_session=( `pgrep -s 0 | grep -Ev "$$|$leader"` )
kill -9 "${all_in_session[@]}"
# Check there are no survivors out there
pgrep -s 0

All processes that have forked or are in the background should be killed. This should work indenpendently if you launch your script as :

Using PID namespaces

# Maps $USER to root in a new user namespace. The script will think it is running as root.
unshare --map-root --pid --mount --fork --kill-child bash "<script_path>"

# Requires actual root privileges. Although script will be run under $USER.
sudo unshare --pid --fork --kill-child sudo -u $USER bash "<script_path>"

# To check the process in that pid namespace
lsns --type pid
pgrep --ns "<script_ns_pid>"

For the script running inside the namespace.

# Only works if $USER is mapped to root
mount -t proc proc /proc
pgrep --ns 1