[launchd-dev] Installing a LaunchAgent from a Cocoa App?

Damien Sorresso dsorresso at apple.com
Tue Dec 15 09:56:46 PST 2009


On Dec 15, 2009, at 9:35 AM, Karl Moskowski wrote:
> Hi, all!
> 
> I'm trying to create a launchd agent that runs weekly or monthly (or never) based on user prefs. It's part of a utility to check all installed Sparkle-enabled applications for updates. The BSD-licensed source is available at <http://bitbucket.org/kolpanic/coruscation/>.
> 
> It's a Snow Leopard application with a bundled LSUIElement Cocoa app that's used as the agent; it's to run according to the schedule and launch the enclosing main Cocoa application when necessary. The agent is built as a dependent target in Xcode and bundled into the main app as part of a copy files build phase.
> 
> When the app opens, it checks the output of "/bin/launchctl list -x" (via an NSTask) to see if the it's loaded, then checks ~/Library/LaunchAgents/com.voodooergonomics.CoruscationAgent.plist to get the frequency (weekly or monthly) so the prefs UI can be shown correctly.
> 
> When the user changes the schedule, it spawns an NSTask that invokes "/bin/launchctl unload -w" with the plist path, then deletes the plist. If the user has chosen weekly or monthly, it then rewrites the plist and spawns an NSTask that invokes "/bin/launchctl load -w" of the plist.
> 
> Here's a sample of the plist as written for a weekly schedule (for monthly, I use a Day value in StartCalendarInterval instead of Weekday):
> <?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.voodooergonomics.CoruscationAgent</string>
> 	<key>ProgramArguments</key>
> 	<array>
> 		<string>/usr/bin/open</string>
> 		<string>/Applications/Coruscation.app/Contents/MacOS/CoruscationAgent.app</string>
> 	</array>
> 	<key>RunAtLoad</key>
> 	<false/>
> 	<key>StartCalendarInterval</key>
> 	<dict>
> 		<key>Weekday</key>
> 		<integer>2</integer>
> 	</dict>
> </dict>
> </plist>
> 
> It all seems to work, but feels a bit kludgey, and leads to a few questions.
> 
> (1) Is there a way to get launchctl list to return the frequency too? Right now, it doesn't seem to include StartCalendarInterval among the keys it returns, and I have to also parse the plist.
> 
> (2) In the ProgramArguments, I originally tried to include the fill path to the agent executable (i.e., /Users/karl/Projects/build/Debug/Coruscation.app/Contents/MacOS/CoruscationAgent.app/Contents/MacOS/CoruscationAgent) but it didn't work. However, passing the agent's .app bundle to the open command does. Any idea why?
> 
> (3) Is there a way to remove the job from launchd without having the plist present? For example, it could happen that the plist was manually deleted but the job was not unloaded first.

Check out Snow Leopard's ServiceManagement framework.

> (4) Is there a better way of getting the enclosing app's path (to launch when necessary) other than getting the agent's bundle path and stripping off the last few components? (Sorry for something not strictly a launchd question, but it's related.)


I don't see what's wrong with that method. Are you finding it unreliable?
-- 
Damien Sorresso
BSD Engineering
Apple Inc.



More information about the launchd-dev mailing list