subprocess kills child processes but not the processes the child spawns
Asked Answered
D

1

6

I have been having an issue whereby I can kill the processes that spawns the nodes but the nodes do not get killed. Does anyone have any suggest how I can do this?

Some of my latest failed attempts to accomplish this are:

node.terminate()

and

node.send_signal(signal.SIGINT)

below is the code:

from subprocess import Popen
import json
import sys
import os
import signal
import requests

FNULL = open(os.devnull, 'w')

json_data = open('nodes.json', 'r').read()
data = json.loads(json_data)

port = data['port']

# launch hub
hub = Popen('java -jar selenium-server-standalone-2.37.0.jar -role hub -port %s' % port, stdout=FNULL, stderr=FNULL, shell=True)

#launch nodes
nodes = []
for node in data['nodes']:
    options = ''
    if node['name'] == 'CHROME':
        options += '-Dwebdriver.chrome.driver=../grid/chromedriver '
    #options += ' -browser browserName='+node['name']+' maxInstances='+str(node['maxInstances'])
    nodes.append(Popen('java -jar selenium-server-standalone-2.37.0.jar -role node -hub http://localhost:%i/grid/register %s' % (port, options), stdout=FNULL, stderr=FNULL, shell=True))

# wait for user input
print "type 'q' and ENTER to close the grid:"
while True:
    line = sys.stdin.readline()
    if line == 'q\n':
        break

# close nodes
for node in nodes:
    #node.terminate()
    node.send_signal(signal.SIGINT)

# close hub   
r = requests.get('http://localhost:'+str(port)+'/lifecycle-manager?action=shutdown')

As far as im aware, I'm basically forced to use shell=True, to get redirections to work Processing the child's stdout/stderr in the parent python process is not an option, since I couldn't find functionality for doing it in a non-waiting way (and the parent python process must do other things while the child is running)

# close nodes
for node in nodes:
    node.send_signal(signal.SIGINT)
    node.terminate()    

this seems to kill all the processes except for 1 of the nodes. Not always the same one

Dormie answered 13/1, 2014 at 2:5 Comment(14)
Try adding: node.wait()Elwandaelwee
@JamesMills adding node.wait() just seems to stall the shutdown, pressumably due to the fact that the nodes are still active (thus its just keeps waiting for them to finish)Dormie
What if you don't set shell=True?Winzler
That's what I thought! Okay, I think you're only other option is to use node.terminate() which sends a SIGKILL signal. But I think you tried that already? Try in combination with node.wait()Elwandaelwee
yea node.terminate() only kills the child not the grandchild. and using node.wait() in any combination causes it to stallDormie
To wit: Invoking the shell gives you some arg-specifying conveniences, but it also means there's another process between the parent and child. The system shell may not be passing the SIGINT to its children.Winzler
I suspect @Winzler is right. I didn't notice you specifying shell=True in your code :)Elwandaelwee
#2525437 <-- this could be useful? Perhaps sending SIGTERM rather than SIGINT might work here?Elwandaelwee
SIGTERM and SIGINT seem to have the same effect.Dormie
What OS are you on? I tried to write up an experiment on this in OS X Mavericks and found that sending the same signal to the process did two or three different things depending on how I sent it. (Which seems wrong.)Winzler
@Winzler running ubuntu 12.04Dormie
#4790337 might also be of use, it seems to cover similar situation (i.e. where use of shell=True is needed).Gentes
What process (the name) do you want to kill and which processes do you want to preserve? Could you create a complete minimal code example (even with dummy subprocesses) to illustrate your point? It is not clear what processes do you want to kill: is it shell, is it java, or child processes started by jvm?Rhapsody
Here is a well-formed question and a good answer: #4790337Biometrics
B
3

You could try using os.killpg. This function sends the signal to the process group, it should work if your processes do not change process group.

import os
import signal

os.killpg(os.getpgid(pid), signal.SIGINT)

Note, that process group will be changed if you are creating process under shell (bash, zsh, etc.), in that case more complicated technique should be used.

Bearish answered 25/3, 2014 at 13:20 Comment(2)
os.killpg(os.getpgid(pid), signal.SIGINT)Sunshade
Unfortunately, this kills the calling program too. You'll need to add preexec_fn=os.setsid to Popen.Biometrics

© 2022 - 2024 — McMap. All rights reserved.