Naftuli Kay
Naftuli Kay

Reputation: 91830

Kill a chain of sub processes on KeyboardInterrupt

I'm having a strange problem I've encountered as I wrote a script to start my local JBoss instance.

My code looks something like this:

with open("/var/run/jboss/jboss.pid", "wb") as f:
    process = subprocess.Popen(["/opt/jboss/bin/standalone.sh", "-b=0.0.0.0"])
    f.write(str(process.pid))

    try:
        process.wait()
    except KeyboardInterrupt:
        process.kill()

Should be fairly simple to understand, write the PID to a file while its running, once I get a KeyboardInterrupt, kill the child process.

The problem is that JBoss keeps running in the background after I send the kill signal, as it seems that the signal doesn't propagate down to the Java process started by standalone.sh.

I like the idea of using Python to write system management scripts, but there are a lot of weird edge cases like this where if I would have written it in Bash, everything would have just worked™.

How can I kill the entire subprocess tree when I get a KeyboardInterrupt?

Upvotes: 4

Views: 596

Answers (1)

Bakuriu
Bakuriu

Reputation: 102039

You can do this using the psutil library:

import psutil

#..

proc = psutil.Process(process.pid)
for child in proc.children(recursive=True):
    child.kill()

proc.kill()

As far as I know the subprocess module does not offer any API function to retrieve the children spawned by subprocesses, nor does the os module.


A better way of killing the processes would probably be the following:

proc = psutil.Process(process.pid)
procs = proc.children(recursive=True)
procs.append(proc)

for proc in procs:
    proc.terminate()

gone, alive = psutil.wait_procs(procs, timeout=1)
for p in alive:
    p.kill()

This would give a chance to the processes to terminate correctly and when the timeout ends the remaining processes will be killed.


Note that psutil also provides a Popen class that has the same interface of subprocess.Popen plus all the extra functionality of psutil.Process. You may want to simply use that instead of subprocess.Popen. It is also safer because psutil checks that PIDs don't get reused if a process terminates, while subprocess doesn't.

Upvotes: 3

Related Questions