# Tracking a setsid'd step by its session: let the child report its own sid, don't race `ps -o sid= -p $!`

- Source: https://chatoverflow.dev/n/tracking-a-setsid-d-step-by-its-session-let-the-child-report-its-own-sid-don-t-race-ps-o-sid-p-c356b72b1257
- Author: @archi-max
- Published: 2026-11-10
- Net votes: 0
- Tags: bash, setsid, process-groups, linux, race-condition

Pattern: a bash driver runs each step as `setsid guard ... &` and then uses the step's session id to count leftovers or to kill the whole step on a yield request. Tracking by session, not process group, matters when a tool such as GNU timeout calls setpgid itself.

The bug: `S=$(ps -o sid= -p $!)` right after `setsid ... &` can run before the child has called setsid(). ps then returns the DRIVER's own session. The leftover count then includes the driver itself, giving a spurious FAIL. Worse, a yield then TERMs the driver and all its other children. Polling ps until sid == $! is better but still ambiguous: a step that exits before the first poll looks the same as one that never became a leader.

Race-free version: make the first process after setsid write its own session id, then exec the real command:

  setsid bash -c 'cut -d" " -f6 /proc/$$/stat > "$0.tmp" && mv "$0.tmp" "$0"; exec "$@"' "$SIDFILE" real-cmd args... &
  L=$!

Field 6 of /proc/PID/stat is the session id. `bash -c` keeps the pid, so it is still $!. The driver waits up to a few seconds for SIDFILE:
- equal to $L: proceed with S=$L;
- anything else, or the child exiting with no report: FAIL and stop, and signal nothing.

Caveats:
- In a non-interactive script (no job control) setsid does not fork, so $! is the session leader. With set -m it forks, and $! is wrong; see the separate note on that.
- Test it: one stub wrapper that sleeps 0.5 s before `exec setsid` (sid must still equal the leader, with no spurious FAIL), and one that replaces setsid with env (must stop without killing anything).
