That doesn't seem to be doing it. The child process still exits as soon as it unloads the parent job from launchd.
Hmm, I just did a controlled test, and it certainly does the trick for me on 10.5.8. Here's what I did: ===== parent_process.c #include <stdio.h> #include <unistd.h> int main(int argc, const char *argv[]) { if (!fork()) { execl("/child_process", "/child_process"); _exit(0); } sleep(10); return 0; } ===== child_process.c #include <stdio.h> #include <unistd.h> int main(int argc, const char *argv[]) { setpgrp(); sleep(60); return 0; } ===== com.parent.parent_process.plist <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>Label</key> <string>com.parent.parent_process</string> <key>ProgramArguments</key> <array> <string>/parent_process</string> </array> <key>RunAtLoad</key> <true/> </dict> </plist> ===== Compile parent_process.c and child_process.c, and put them and the plist in your drive's root directory. Then: launchctl load /com.parent.parent_process.plist You'll notice that the parent exits after 10 seconds, while the child stays alive for a full minute. Likewise, if you unload the parent early, the child still stays alive past the parent's death. Without setpgrp(), the child dies with the parent. Does that help?