How to kill a python child process created with subprocess.check_output() when the parent dies? How to kill a python child process created with subprocess.check_output() when the parent dies? linux linux

How to kill a python child process created with subprocess.check_output() when the parent dies?


Yes, you can achieve this by two methods. Both of them require you to use Popen instead of check_output. The first is a simpler method, using try..finally, as follows:

from contextlib import contextmanager@contextmanagerdef run_and_terminate_process(*args, **kwargs):try:    p = subprocess.Popen(*args, **kwargs)    yield p        finally:    p.terminate() # send sigterm, or ...    p.kill()      # send sigkilldef main():    with run_and_terminate_process(args) as running_proc:        # Your code here, such as running_proc.stdout.readline()

This will catch sigint (keyboard interrupt) and sigterm, but not sigkill (if you kill your script with -9).

The other method is a bit more complex, and uses ctypes' prctl PR_SET_PDEATHSIG. The system will send a signal to the child once the parent exits for any reason (even sigkill).

import signalimport ctypeslibc = ctypes.CDLL("libc.so.6")def set_pdeathsig(sig = signal.SIGTERM):    def callable():        return libc.prctl(1, sig)    return callablep = subprocess.Popen(args, preexec_fn = set_pdeathsig(signal.SIGTERM))


Your problem is with using subprocess.check_output - you are correct, you can't get the child PID using that interface. Use Popen instead:

proc = subprocess.Popen(["ls", "-l"], stdout=PIPE, stderr=PIPE)# Here you can get the PIDglobal child_pidchild_pid = proc.pid# Now we can wait for the child to complete(output, error) = proc.communicate()if error:    print "error:", errorprint "output:", output

To make sure you kill the child on exit:

import osimport signaldef kill_child():    if child_pid is None:        pass    else:        os.kill(child_pid, signal.SIGTERM)import atexitatexit.register(kill_child)


Don't know the specifics, but the best way is still to catch errors (and perhaps even all errors) with signal and terminate any remaining processes there.

import signalimport sysimport subprocessimport osdef signal_handler(signal, frame):    sys.exit(0)signal.signal(signal.SIGINT, signal_handler)a = subprocess.check_output(["ls", "-l"], stderr=subprocess.STDOUT)while 1:    pass # Press Ctrl-C (breaks the application and is catched by signal_handler()

This is just a mockup, you'd need to catch more than just SIGINT but the idea might get you started and you'd need to check for spawned process somehow still.

http://docs.python.org/2/library/os.html#os.killhttp://docs.python.org/2/library/subprocess.html#subprocess.Popen.pidhttp://docs.python.org/2/library/subprocess.html#subprocess.Popen.kill

I'd recommend rewriting a personalized version of check_output cause as i just realized check_output is really just for simple debugging etc since you can't interact so much with it during executing..

Rewrite check_output:

from subprocess import Popen, PIPE, STDOUTfrom time import sleep, timedef checkOutput(cmd):    a = Popen('ls -l', shell=True, stdin=PIPE, stdout=PIPE, stderr=STDOUT)    print(a.pid)    start = time()    while a.poll() == None or time()-start <= 30: #30 sec grace period        sleep(0.25)    if a.poll() == None:        print('Still running, killing')        a.kill()    else:        print('exit code:',a.poll())    output = a.stdout.read()    a.stdout.close()    a.stdin.close()    return output

And do whatever you'd like with it, perhaps store the active executions in a temporary variable and kill them upon exit with signal or other means of intecepting errors/shutdowns of the main loop.

In the end, you still need to catch terminations in the main application in order to safely kill any childs, the best way to approach this is with try & except or signal.