I know not being a library has different considerations, but some ideas I used in timeout(1) to kill a process group may be useful. Tricky things like using sigsuspend() to avoid signal handling races.
https://github.com/coreutils/coreutils/blob/master/src/timeo...
cgroups might be another avenue to explore, being more modern and so having less compatibility baggage
>Linux is in the middle of adding new APIs like pidfd_send_signal, but none of them are aimed at improving the situation with grandchildren.
While my subreaper is vulnerable to pid reuse, but I think it could be fixed by having it do this:
loop
children = scan-for-children
for each child
if no pidfd for given child
pidfd open child ( dropping if error )
still-children = scan-for-children
close-and-drop any pidfd's that are no longer children
safely-kill-children-via-pidfds
If you kill the direct children, the grandchildren will become direct children since you are a subreaper, and then so on.In summary, a helper process using subreaper+pidfd should be able to properly and safely contain and kill grandchild processes.
There may be a better way to do this, but one way might be to open() /proc/<PID> and use the resulting FD to both to check /proc/<PID>/cgroup (using openat(FD, "cgroup", O_RDONLY|O_NOCTTY)) and then use the same FD as a PIDFD when calling pidfd_send_signal().
ISTM that systemd should use this method if its current method is just looping over PIDs.
Edit: Indeed both your and my methods were proposed to systemd: https://github.com/systemd/systemd/issues/13101 It's just waiting for someone to implement it.
So if you want to write software that can target MacOS or any of the BSDs, then you can't use cgroups.
In modern times wer'e doing more thing like containerizing a bunch of processes they are weakly related instead of using a VM for that so the use case became more pressing. There is cgroups for managing such a group of processes. I'm suprised to learn there is no way to reliably send a signal to all processes in a cgroup though.
> SIGKILL will actually leave lots of orphans around instead of cleaning everything.
Well actually orphan processes exist for a reason. Your supposed to "wait" / reap them to make them non orphan.
Maybe Linux should implement something like pidfd_getpfd() (returning the PID FD of the parent process) and pidfd_fork() (returning the PID FD of the child process).
You need to use a kernel feature intended for this purpose, which is what things like process groups (linked article), jobs (80's BSD feature) and cgroups (modern generalization of the idea) were designed for.