~/LaunchAgent + KeepAlive=true = Login Jail
Greetings, I have a user launch agent (~/Library/LaunchAgents) that has KeepAlive set to true. The launch agent is a Cocoa application, UIElement=YES. It provides a small status window to the user. I want this application to run all of the time, which is why the KeepAlive property was set. Whenever my application quits, launchd restarts it (as one would expect). The problem is that when the user tries to log out, the agent quits, launchd immediately restarts it, and the user gets a nasty message that the logout/restart/shutdown was canceled by my application. [There's clearly an amusing race condition: Once it's down to the Finder, launchd, my application and maybe one or two other small applications, the system manages to log out by killing my app and launchd before launchd has a chance to restart my application again.] Anyway, is there a workaround for this or do I just have to abandon KeepAlive? -- James Bucanek
Nathan Duran <mailto:launchd@khiltd.com> wrote (Friday, January 25, 2008 11:32 AM -0800):
On Jan 25, 2008, at 10:21 AM, James Bucanek wrote:
Anyway, is there a workaround for this or do I just have to abandon KeepAlive?
Why not have your application unload the launchd job when it receives the shutdown event?
That's a interesting idea. Will the unload be forgotten the next time the user logs back in? -- James Bucanek
Nathan Duran <mailto:launchd@khiltd.com> wrote (Friday, January 25, 2008 11:32 AM -0800):
On Jan 25, 2008, at 10:21 AM, James Bucanek wrote:
Anyway, is there a workaround for this or do I just have to abandon KeepAlive?
Why not have your application unload the launchd job when it receives the shutdown event?
The more I think about this, the more it appears that this is really no different than just leaving out the KeepAlive property. The only difference is that KeepAlive would restart my background app if it crashed. Fortunately (through the brilliant programming efforts of its creator, I'm sure), this particular app simply doesn't crash -- knock on wood. What I really wanted is the ability to restart the app simply by asking it to quit, or restarting the app if the user quit it. Since my app can't tell the difference between a Quit for a Shutdown and a Quit just for the sake of quitting, I think I'll have to give up on the KeepAlive flag for this version. -- James Bucanek
At 23:16 -0700 27/1/08, James Bucanek wrote:
The only difference is that KeepAlive would restart my background app if it crashed. Fortunately (through the brilliant programming efforts of its creator, I'm sure), this particular app simply doesn't crash -- knock on wood.
One option is to set "KeepAlive" to a dictionary containing "SuccessfulExit" set to false. That way, launchd won't restart you if you quit cleanly. OTOH, if you crash, you'll be relaunched. S+E -- Quinn "The Eskimo!" <http://www.apple.com/developer/> Apple Developer Relations, Developer Technical Support, Core OS/Hardware
Quinn <mailto:eskimo1@apple.com> wrote (Monday, January 28, 2008 4:33 AM -0000):
At 23:16 -0700 27/1/08, James Bucanek wrote:
The only difference is that KeepAlive would restart my background app if it crashed. Fortunately (through the brilliant programming efforts of its creator, I'm sure), this particular app simply doesn't crash -- knock on wood.
One option is to set "KeepAlive" to a dictionary containing "SuccessfulExit" set to false. That way, launchd won't restart you if you quit cleanly. OTOH, if you crash, you'll be relaunched.
Thanks for the suggestion, Quinn, but it still boils down to almost the same thing. I want the process to be restarted if the user or my client application quits it. The program is a simple background Cocoa app that displays a single window with status information. It's really simple and (virtually) never crashes or locks up. It does, however, sometimes accumulate stale process connections and such. It's sometime nice to restart it just by telling it to quit -- either from my client application or by letting the user quit it using the Activity Monitor. About the only thing I can think of that might make this work is to add a function that would cause the application to exit with a non-zero status and call that when I want to restart it. This doesn't solve the problem of the user quiting it (becuase that would use the standard/clean quit request), but that's probably as close as I'm going to get. Cheers, James -- James Bucanek
On Jan 28, 2008 6:16 AM, James Bucanek <subscriber@gloaming.com> wrote:
Since my app can't tell the difference between a Quit for a Shutdown and a Quit just for the sake of quitting, I think I'll have to give up on the KeepAlive flag for this version.
You might try listening for distributed notifications associated with shutdown and restart: com.apple.restartInitiated com.apple.shutdownInitiated com.apple.logoutCancelled (I found these on Leopard with Notification Watcher: http://www.tildesoft.com/Programs.html) If you get a Quit after receiving either of the first two but without having received the third, it's a Quit for a Shutdown rather than a Quit just for the sake of quitting. Hamish
On Jan 28, 2008, at 8:25 AM, Hamish Allan wrote:
You might try listening for distributed notifications associated with shutdown and restart:
com.apple.restartInitiated com.apple.shutdownInitiated com.apple.logoutCancelled
I imagine you would also be able to determine whether the Quit event came from user interaction with your own menus or was an AppleEvent sent by the system. If not through Cocoa directly, then possibly through a CGEventTap.
At 16:25 +0000 28/1/08, Hamish Allan wrote:
You might try listening for distributed notifications associated with shutdown and restart:
com.apple.restartInitiated com.apple.shutdownInitiated com.apple.logoutCancelled
(I found these on Leopard with Notification Watcher: http://www.tildesoft.com/Programs.html)
These are not considered supported API. Worse yet, determining the state of login window by tracking these events is tricky.
If you get a Quit after receiving either of the first two but without having received the third, it's a Quit for a Shutdown rather than a Quit just for the sake of quitting.
A better solution is to look at the kAEWhyAmIBeingLoggedOutAttr ('why?') attribute of the kAEQuitApplication ('quit') Apple event. If you're being quit by loginwindow, this will contain one of four different codes indicating why (kAEQuitAll, kAEShutDown, kAERestart, kAEReallyLogOut). If you're being quit by the user (or by an AppleScript or any other 'quit' Apple event mechanism), this attribute won't be present. I could've sworn that this attribute was publicly documented, but I looked long and hard and couldn't find anything. Well, it /should/ be publicly documented IMHO <rdar://problem/5712210>. S+E -- Quinn "The Eskimo!" <http://www.apple.com/developer/> Apple Developer Relations, Developer Technical Support, Core OS/Hardware
On Jan 29, 2008, at 2:45 AM, Quinn wrote:
I could've sworn that this attribute was publicly documented, but I looked long and hard and couldn't find anything. Well, it /should/ be publicly documented IMHO <rdar://problem/5712210>.
I thought there was another way, too, but I know I probably haven't looked at whatever it was since OS 8, so I figured I was either imagining things or it went away.
Quinn <mailto:eskimo1@apple.com> wrote (Tuesday, January 29, 2008 3:45 AM -0000):
A better solution is to look at the kAEWhyAmIBeingLoggedOutAttr ('why?') attribute of the kAEQuitApplication ('quit') Apple event. If you're being quit by loginwindow, this will contain one of four different codes indicating why (kAEQuitAll, kAEShutDown, kAERestart, kAEReallyLogOut). If you're being quit by the user (or by an AppleScript or any other 'quit' Apple event mechanism), this attribute won't be present.
Thanks, Quinn. That's a excellent solution. I did, indeed, search the documentation, developer.apple.com, and the various lists for any mention of determining if a Quit event was being sent because of a shutdown, restart, or logout. So this isn't just undocumented, it's below the radar. Inside Apple, you're probably using header files we don't have. ;) grepping of all of the 10.4 & 10.5 SDK header files, I can't find any declaration for kAEWhyAmIBeingLoggedOutAttr. However, there is a declaration for kEventParamReason = 'why?' in CarbonEvents.h. That's good enough for me. So I think I have this working nicely now. I added the SuccessfulExit=false to the KeepAlive property and added the following code to my Cocoa application static void dirtyExit( void ) { // At the very last moment possible, force an abnormal exit _exit(2); } - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication*)sender { #pragma unused(sender) // Find out if the application is quitting for a Log Out, Shutdown, or Restart // This has to be detemined in applicationShouldTerminate: becuase the AppleEvent goes away soon. NSAppleEventDescriptor* quitEventDesc = [[NSAppleEventManager sharedAppleEventManager] currentAppleEvent]; // NSLog(@"%p%s %@",self,__func__,quitEventDesc); NSAppleEventDescriptor* whyDesc = [quitEventDesc attributeDescriptorForKeyword:kEventParamReason]; OSType why = [whyDesc typeCodeValue]; if ( !(why==kAEQuitAll || why==kAEShutDown || why==kAERestart || why==kAEReallyLogOut) ) { //NSLog(@"application will exit abnormally"); // If the monitor is not exiting cleanly, register a program exit handler that will throw an error following normal termination atexit(dirtyExit); } return (YES); } Thanks again. I'll file a bug report on the documentation. James -- James Bucanek
Please file a bug: http://bugreport.apple.com/ Thanks! davez On Jan 25, 2008, at 10:21 AM, James Bucanek wrote:
Greetings,
I have a user launch agent (~/Library/LaunchAgents) that has KeepAlive set to true. The launch agent is a Cocoa application, UIElement=YES. It provides a small status window to the user.
I want this application to run all of the time, which is why the KeepAlive property was set. Whenever my application quits, launchd restarts it (as one would expect). The problem is that when the user tries to log out, the agent quits, launchd immediately restarts it, and the user gets a nasty message that the logout/restart/shutdown was canceled by my application.
[There's clearly an amusing race condition: Once it's down to the Finder, launchd, my application and maybe one or two other small applications, the system manages to log out by killing my app and launchd before launchd has a chance to restart my application again.]
Anyway, is there a workaround for this or do I just have to abandon KeepAlive?
-- James Bucanek
_______________________________________________ launchd-dev mailing list launchd-dev@lists.macosforge.org http://lists.macosforge.org/mailman/listinfo/launchd-dev
participants (5)
-
Dave Zarzycki
-
Hamish Allan
-
James Bucanek
-
Nathan Duran
-
Quinn