I'm writing an application that kicks off a subprocess running a simple web server. I am using NSTask and communicating with it with pipes, and everything seems more or less fine. However, if my program crashes, the subprocess is left alive and the next time I launch the app there is a conflict between the old subprocess and the new one. Is there any way to ensure that subprocesses die when the owning app dies?
Your application delegate can implement the
- (void)applicationWillTerminate:(NSNotification *)aNotification
message, and terminate the NSTask there. However, it is not guaranteed that during a crash, this delegate will be called.
Two additional steps you can take:
- Shutdown an existing, orphaned subprocess during the launch of a new parent-process by writing down to disk the PID of the subprocess on creation and removing it during normal shutdown (sometimes not the safest behavior).
- Shutdown the subprocess if the NSPipe's end-point didn't send data for a specific amount of time (something like a heartbeat).
UPDATE: Now that I go to check this properly it doesn't work. Trying to set the process group fails with this error;
EPERM "The effective user ID of the requested process is different from that of the caller and the process is not a descendant of the calling process."
There is a more recent thread on this issue but no easy solution as far as I can tell
http://www.omnigroup.com/mailman/archive/macosx-dev/2009-March/062164.html
I've tried a suggestion by Robert Pointon on Cocoadev in my app. I haven't got around to testing it yet though.
http://www.cocoadev.com/index.pl?NSTaskTermination
The idea is to set the process group of the task to be the same as that of the process that launches the task (note: the code below is basically lifted from the thread above).
pid_t group = setsid();
if (group == -1) {
group = getpgrp();
}
[task launch];
if (setpgid([task processIdentifier], group) == -1) {
NSLog(@"unable to put task into same group as self");
[task terminate];
} else {
// handle running task
}