I'm trying to accomplish the following with my tray app:
I have a problem that if user manually starts the app, then listing in
launchctl list com.myapp
doesn't show the running app.
If, however, I let the LaunchAgent launch app on user-login, then launchctl list com.myapp
properly shows the app, and will restarts it on crash (or any non-zero exit code).
Strangely enough, if user manually starts the app, launchd will try to launch it's own instance after several minutes. I can't even explain why this happens.
My LaunchAgent plist example:
<?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>KeepAlive</key>
<dict>
<key>SuccessfulExit</key>
<false/>
<key>AfterInitialDemand</key>
<true/>
</dict>
<key>RunAtLoad</key>
<false/>
<key>Label</key>
<string>com.myapp</string>
<key>ProgramArguments</key>
<array>
<string>/Applications/MyApp.app/Contents/MacOS/MyApp</string>
</array>
</dict>
</plist>
launchd
is responsible for only launching your app. If you want to relaunch your app on crash, I would suggest that you create a separate app which only runs in background and is responsible for launching your current app. This way, it is able to track the app it launched and relaunch if it crashed. Here is some sample C code if you want to try:
pid_t waitResult;
pid_t processId = <Get process id here>
int status = 0;
do {
waitResult = waitpid(processId, &status, WNOHANG | WUNTRACED);
sleep(1);
} while (waitResult == 0);
if (waitResult == processId) { //Process ends here
if (WIFEXITED(status)) {
//Child app ended normally
} else if (WIFSIGNALED(status) || WIFSTOPPED(status)) {
//Child app crashed. Restart here again.
}
}