3

Ensuring child processes terminate when the parent terminates is tricky. I've found a robust solution from Python in Linux is to instruct the kernel to kill a child process when the parent terminates using prctl(PR_SET_PDEATHSIG, SIGTERM). The same is presumably possible from C or C++, considering that prctl() is from the C standard library.

There are several questions about how to kill child processes in Java, see e.g. [1]. However, nothing I've found is as robust as the method using prctl(). The majority of solutions will fail if the parent receives SIGKILL, or they require modifying the child code. By getting the OS to do the job for us, the child will always be killed successfully, no matter what happens to the parent, and the child code does not need to be modified.

How can I call prctl(PR_SET_PDEATHSIG, SIGTERM) in a child process spawned from a Java ProcessBuilder?

Douglas B. Staple
  • 10,510
  • 8
  • 31
  • 58

1 Answers1

7

You can do it with a bit of C, as long as you're not worried about a few conditions, and understand that it will have limitations.

The prctl(PR_SET_PDEATHSIG, …) system call is part of the linux kernel, and has a couple of rules - it doesn't survive fork(), it doesn't survive exec() into a setuid/setgid binary.

limitations are:

  • This is a simple program, it will kill things horribly if you launch something else from the child process unless you put in some limits.
  • it won't work across executable models (e.g. 32->64bit) which is relatively rare
  • it won't work with statically linked binaries (rare)

With these limitations in mind, we can make a small preload library from some C that will invoke this system call. So, for example:

#include <sys/prctl.h>
#include <sys/types.h>
#include <signal.h>

__attribute__((constructor))
static void on_load() {
    prctl(PR_SET_PDEATHSIG, SIGTERM);
}

compiled using gcc -fPIC -shared -o term_death.so term_death.c

when you use the LD_PRELOAD environment variable with the full path to this binary, any programs that are launched will be sent a SIGTERM when their parent process is killed.

This is the setup from the C side - we now have a helper library that will allow the behaviour you're asking for.

Getting this to work from the java side.

We need to inject the full path to the .so into the LD_PRELOAD environment variable for the ProcessBuilder, so something like:

ProcessBuilder pb = new ProcessBuilder();
Map<String, String> env = pb.environment();
env.put("LD_PRELOAD", "/home/me/development/experiments/term_death.so");

again, you need to specify the path to the .so so that it can be loaded.

When you pb.start() it will inherit the LD_PRELOAD environment variable. On loading the executable, it runs the on_load code (because it's marked as a constructor) and says that the process will receive a SIGTERM when the parent process terminates.

This is somewhat ugly, but should address the issue.

Anya Shenanigans
  • 91,618
  • 3
  • 107
  • 122