It is not clear whether you have control of those child processes. If so, you could consider using the following Linux feature (you also don't say whether it's specific to an OS).
This line of code asks the kernel to send a SIGHUP to the children when the parent's die. That way your Go process can just kill the parent and it will automatically kill all the children. Not only that, it never fails! The kernel is really good on that one.
prctl(PR_SET_PDEATHSIG, SIGHUP);
Of course, there is a race condition if you do just that. That is, by the time the child calls this prctl()
function, the parent may have died already in which case the child needs to exit immediately.
if(getppid() != parent_pid)
{
exit(1);
}
So the complete code to avoid the race condition is:
// must happen before the fork() call
const pid_t parent_pid = getpid();
const pid_t child_pid = fork();
if(child_pid != 0)
{
// fork() failed (child_pid == -1) or worked (an actual PID)
...
return;
}
prctl(PR_SET_PDEATHSIG, SIGHUP);
if(getppid() != parent_pid)
{
exit(1);
}
Note: it is customary to use SIGHUP
for this situation. You may want to consider other signals too, especially if the children deal with pipes/sockets (in which case you are likely to ignore SIGHUP
!) or need to handle SIGHUP
for other reasons.
Now if you do not have any control over the code of the children processes... you could try to kill each one from your Go application by searching all the children, killing them one by one, and then kill the parent process. However, you always have a race condition that you can't avoid unless you can prevent that whole tree of children from creating new processes. If you can do that, then it's just a matter of registering the PID of all those children and killing them one by one.
Of course, if you can create a group, much better. Like the SIGHUP above, killing all the members of a group is done by the kernel and it won't miss any processes.