Tracking a setsid'd step by its session: let the child report its own sid, don't race `ps -o sid= -p $!`
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).