views:

621

answers:

3

I have a program spawning and communicating with cpu heavy, unstable processes, not created by me. If my app crashes or is killed by sigkill, I want the subprocesses to get killed as well, so the user don´t have to track them down and kill them manually.

I know this topic has been covered before, but I have tried all methods described, and none of them seams to live up to survive the test.

I know it must be possible, since terminals do it all the time. If I run something in a terminal, and kill the terminal, the stuff always dies.

I have tried atexit, double fork and ptys. atexit doenst work for sigkill; double fork doesnt work at all; and ptys I have found no way to work with using python.

Today I found out about prctl(PR_SET_PDEATHSIG, SIGKILL), which should be a way for childprocesses to order a kill on themselves, when their parent dies. I tried to use it in python, with popen, but it seams to have no effect at all:

import ctypes, subprocess
libc = ctypes.CDLL('/lib/libc.so.6')
PR_SET_PDEATHSIG = 1; TERM = 15
implant_bomb = lambda: libc.prctl(PR_SET_PDEATHSIG, TERM)
subprocess.Popen(['gnuchess'], preexec_fn=implant_bomb)

In the above the child is created and the parent exits. Now you would expect gnuchess to recieve a SIGKILL and die, but it doesnt. I can still find it in my process mamager using 100% cpu.

Can anybody tell me if there is something wrong with my use of prctl?, or do you know how terminals manage to kill their children?

A: 

I thought the double fork was to detach from a controlling terminal. I'm not sure how you are trying to use it.

It's a hack, but you could always call 'ps' and search for the process name your trying to kill.

BillMan
But how can I call ps when my controlling process is dead? That's the entire point. The user of my app won't know that the children aren't dead, and will just feel his/her computer being much slower until reboot.
Thomas Ahle
Can you wrap the program that is being created with your own executable and then write the PID to a file when its started?
BillMan
I could probably find a way to clean stuff up on restart, but I don't know if I will be restarted. Thus I need a way to clean up at the time I die.
Thomas Ahle
+1  A: 

I've seen very nasty ways of "clean-up" using things like ps xuawww | grep myApp | awk '{ print $1}' | xargs -n1 kill -9

The client process, if popened, can catch SIG_PIPE and die. There are many ways to go about this, but it really depends on a lot of factors. If you throw some ping code (ping to parent) in the child, you can ensure that a SIG_PIPE is issued on death. If it catches it, which it should, it'll terminate. You'd need bidirectional communication for this to work correctly... or to always block against the client as the originator of communication. If you don't want to modify the child, ignore this.

Assuming that you don't expect the actual Python interpreter to segfault, you could add each PID to a sequence, and then kill on exit. This should be safe for exiting and even uncaught exceptions. Python has facilities to perform exit code... for clean-up.

Here's some safer nasty: Append each child PID to a file, including your master process (separate file). Use file locking. Build a watchdog daemon that looks at the flock() state of your master pid. If it's not locked, kill every PID in your child PID list. Run the same code on startup.

More nasty: Write the PIDs to files, as above, then invoke your app in a sub-shell: (./myMaster; ./killMyChildren)

Pestilence
If you read my codesnap, you'll see that I run gnuchess in the new process, so there is now way I can run it as a thread.Does Popen not use fork?
Thomas Ahle
+1  A: 

prctl's PR_SET_DEATHSIG can only be set for this very process that's calling prctl -- not for any other process, including this specific process's children. The way the man page I'm pointing to expresses this is "This value is cleared upon a fork()" -- fork, of course, is the way other processes are spawned (in Linux and any other Unix-y OS).

If you have no control over the code you want to run in subprocesses (as would be the case, essentially, for your gnuchess example), I suggest you first spawn a separate small "monitor" process with the role of keeping track of all of its siblings (your parent process can let the monitor know about those siblings' pids as it spawns them) and sending them killer signals when the common parent dies (the monitor needs to poll for that, waking up every N seconds for some N of your choice to check if the parent's still alive; use select to wait for more info from the parent with a timeout of N seconds, within a loop).

Not trivial, but then such system tasks often aren't. Terminals do it differently (via the concept of a "controlling terminal" for a process group) but of course it's trivial for any child to block THAT off (double forks, nohup, and so on).

Alex Martelli
`preexec_fn` is called after `fork()`, and the man page doesn't say this flag is cleared on `exec()`.
Denis Otkidach
As Denis writes, I'm under the impression, that the preexec_fn parameter will place my prctl call between the fork and the exec calls.The idea of a monitor is actually quite good. Unless it crashes of course, so it has to be quite simple.Can I know its parrent is dead when it recieves SIGHUP? And then let it sigterm its brothers and sisters.Can you tell me more about spawning controlling terminals?
Thomas Ahle