views:

5578

answers:

8

Suppose I have a process which spawns exactly one child process. Now when the parent process exits for whatever reason (normally or abnormally, by kill, ^C, assert failure or anything else) I want the child process to die. How to do that correctly?


Some similar question on stackoverflow:

+1  A: 

I don't believe it's possible to guarantee that using only standard POSIX calls. Like real life, once a child is spawned, it has a life of its own.

It is possible for the parent process to catch most possible termination events, and attempt to kill the child process at that point, but there's always some that can't be caught.

For example, no process can catch a SIGKILL. When the kernel handles this signal it will kill the specified process with no notification to that process whatsoever.

To extend the analogy - the only other standard way of doing it is for the child to commit suicide when it finds that it no longer has a parent.

There is a Linux-only way of doing it with prctl(2) - see other answers.

Alnitak
+16  A: 

Child can ask kernel to deliver SIGHUP (or other signal) when parent dies by specifying option PR_SET_PDEATHSIG in prctl() syscall like this:

prctl(PR_SET_PDEATHSIG, SIGHUP);

See man 2 prctl for details.

Edit: This is Linux-only

qrdl
Nice spot - shame it's Linux only...
Alnitak
Yeah, Linux-specific. I don't think there is POSIX way to do it.
qrdl
Please edit to mention this is Linux-only.
John M
+2  A: 

Does the child process have a pipe to/from the parent process? If so, you'd receive a SIGPIPE if writing, or get EOF when reading - these conditions could be detected.

MarkR
I found this didn't happen reliably, at least on OS X.
Schof
+2  A: 

Install a trap handler to catch SIGINT, which kills off your child process if it's still alive, though other posters are correct that it won't catch SIGKILL.

Open a .lockfile with exclusive access and have the child poll on it trying to open it - if the open succeeds, the child process should exit

Paul Betts
+7  A: 

I have achieved this in the past by running the "original" code in the "child" and the "spawned" code in the "parent" (that is: you reverse the usual sense of the test after fork()). Then trap SIGCHLD in the "spawned" code...

May not be possible in your case, but cute when it works.

dmckee
Very nice solution, thanks! The currently accepted one is more generic, but yours is more portable.
phjr
+2  A: 

Under POSIX, the exit(), _exit() and _Exit() functions are defined to:

  • If the process is a controlling process, the SIGHUP signal shall be sent to each process in the foreground process group of the controlling terminal belonging to the calling process.

So, if you arrange for the parent process to be a controlling process for its process group, the child should get a SIGHUP signal when the parent exits. I'm not absolutely sure that happens when the parent crashes, but I think it does. Certainly, for the non-crash cases, it should work fine.

Note that you may have to read quite a lot of fine print - including the Base Definitions (Definitions) section, as well as the System Services information for exit() and setsid() and setpgrp() - to get the complete picture. (So would I!)

Jonathan Leffler
Hmm. The documentation is vague and contradictory on this, but it appears that the parent process must be the lead process for the session, not just the process group. The lead process for the session was always login, and getting my process to take over as lead process for a new session was beyond my abilities at the moment.
Schof
SIGHUP effectively only gets sent to child processes if the exiting process is a login shell.http://www.opengroup.org/onlinepubs/009695399/functions/exit.html"Termination of a process does not directly terminate its children. The sending of a SIGHUP signal as described below indirectly terminates children /in some circumstances/."
Rob K
@Rob: correct - that's what the quote I gave says, too: that only in some circumstances does the child process get a SIGHUP. And it is strictly an over-simplification to say that it is only a login shell that sends SIGHUP, though that is the most common case. If a process with multiple children sets itself up as the controlling process for itself and its children, then the SIGHUP will (conveniently) be sent to its children when the master dies. OTOH, processes seldom go to that much trouble - so I more more nit-picking than raising a really significant quibble.
Jonathan Leffler
@Jonathan, I fooled around with it for a couple of hours and couldn't get it to work. It would have nicely handled a case where I have a daemon with some children that all need to die when the parent exits.
Rob K
+2  A: 

I'm trying to solve the same problem, and since my program must run on OS X, the Linux-only solution didn't work for me.

I came to the same conclusion as the other people on this page -- there isn't a POSIX-compatible way of notifying a child when a parent dies. So I kludged up the next-best thing -- having the child poll.

When a parent process dies (for any reason) the child's parent process becomes process 1. If the child simply polls periodically, it can check if its parent is 1. If it is, the child should exit.

This isn't great, but it works, and it's easier than the TCP socket/lockfile polling solutions suggested elsewhere on this page.

Schof
Excellent solution. Continuesly invoking getppid() until it returns 1 and then exit. This is good and I now use it too. A non-pollig solution would be nice though. Thank you Schof.
neoneye
Just for info, on Solaris if you're in a zone, the `gettpid()` does not become 1 but gets the `pid` of the zone scheduler (process `zsched`).
tristopia
A: 

If you're unable to modify the child process, you can try something like the following:

int pipes[2];
pipe(pipes)
if (fork() == 0) {
    close(pipes[1]); /* Close the writer end in the child*/
    dup2(0, pipes[0]); /* Use reader end as stdin */
    exec("sh -c 'set -o monitor; child_process & read dummy; kill %1'")
}

close(pipes[0]); /* Close the reader end in the parent */

This runs the child from within a shell process with job control enabled. The child process is spawned in the background. The shell waits for a newline (or an EOF) then kills the child.

When the parent dies--no matter what the reason--it will close its end of the pipe. The child shell will get an EOF from the read and proceed to kill the backgrounded child process.

Phil Rutschman