I have a program that can fork()
and exec()
multiple processes in a chain.
E.g.: process A --> fork, exec B --> fork, exec C --> fork, exec D. So A i
The POSIX way to do this is simply to use process groups. Descendant processes that explicitly change their process group / session are making a deliberate decision not to have their lifetime tracked by their original parent - they are specifically emancipating themselves from the parent's control. Such processes are not orphans - they are adults that have "flown the nest" and wish to exert control over their own lifetime.
I agree with caf's general sentiment: if a process calls setsid
, it's saying it wants to live on its own, no matter what . You need to think carefully as to whether you really want to kill them.
That being said, sometimes, you will want some form of “super-session” to contain a tree of processes. There is no tool that provides such super-sessions in the POSIX toolbox, but I'm going to propose a few solutions. Each solution has its own limitations, so it's likely that they won't all be applicable to your case, but hopefully one of them will be suitable.
A clean solution is to run the processes in their own virtualized environment. This could be a FreeBSD-style jail, Linux cgroups, or any other kind of virtualization technology. The limitations of this approach are that virtualization technologies are OS-dependant, and the processes will run in a somewhat different context.
If you only have a single instance of these processes on the system and you can get root involved, run the processes as a dedicated user. The super-session is defined as the processes running as the dedicated user. Kill the descendants with kill(-1, signum)
(note that this will kill the killer process itself unless it's blocked or handled the signal).
You can make the process open a unique file, making sure that the FD_CLOEXEC
flag is set on the file descriptor. All child processes will then inherit the open file unless they explicitly remove the FD_CLOEXEC
flag before calling execve
or close the file. Kill the processes with fuser -k
or by obtaining the list of process IDs with fuser
or lsof
(fuser
is in POSIX, but not fuser -k
.) Note that there's a race condition: a process may fork between the time you call fuser
and the time you kill it; therefore you need to call fuser
in a loop until no more processes appear (don't loop until all processes are dead, as this could be an infinite loop if one of the processes is blocking your signal).
You can generate a unique random string and define an environment variable with that name, or with a well-known name and that unique string as a value. It will be inherited by all descendant processes unless they choose to change their environment. There is no portable way to search for processes based on their environment, or even to obtain the environment of another process. On many unix variants, you can obtain the information with an option to ps
(such as ps -e
on *BSD or ps e
on Linux); the information may not be easy to parse, but the presence of the unique string is a sufficient indicator. As with fuser
above, note the need for a loop to avoid a race condition if a descendant calls fork
too late for you to notice its child but before you could kill the parent.
You can LD_PRELOAD
a small library that forks a thread that listens on a communication channel, and kills its process when notified. This may disrupt the process if it expects to know about all of its own threads; it's only a possibility on architectures where the standard library is always thread-safe, and you'll miss statically linked processes. The communication channel can be anything that allows the master process to broadcast the suicide order; one possibility is a pipe where each descendant process does a blocking read and the ancestor process closes the pipe to notify the descendants. Pass the file descriptor number through an environment variable.