Take the 2-minute tour ×
Ask Different is a question and answer site for power users of Apple hardware and software. It's 100% free, no registration required.

As it says in the title, "Can't quit app launched through launchd"—or more precisely, I can quit it, but it then automatically restarts again.


Background: I prefer to use BusyCal, not iCal, but there are some calendar-ish things that have to be done by iCal before BusyCal can get the results. I figured I'd create two launchd scripts: one to launch iCal every day at 5 am, and another to quit it a few minutes later.


Here's the plist to launch (in ~/Library/LaunchAgents/com.dori.iCalLaunch.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>KeepAlive</key>
    <false/>
    <key>Label</key>
    <string>com.dori.iCalLaunch</string>
    <key>OnDemand</key>
    <true/>
    <key>ProgramArguments</key>
    <array>
        <string>/Applications/iCal.app/Contents/MacOS/iCal</string>
    </array>
    <key>ServiceDescription</key>
    <string>iCal Launcher</string>
    <key>StartCalendarInterval</key>
    <dict>
        <key>Hour</key>
        <integer>5</integer>
    </dict>
</dict>
</plist>

My understanding was that I didn't need the KeepAlive and OnDemand keys, but I put them in anyway to try to stop the relaunching.

Here's the plist to quit (in ~/Library/LaunchAgents/com.dori.iCalQuit.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.dori.iCalQuit</string>
    <key>Program</key>
    <string>/usr/bin/osascript</string>
    <key>ProgramArguments</key>
    <array>
        <string>osascript</string>
        <string>-e</string>
        <string>tell application "iCal" to quit</string>
    </array>
    <key>ServiceDescription</key>
    <string>iCal Quitter</string>
    <key>StartCalendarInterval</key>
    <dict>
        <key>Hour</key>
        <integer>5</integer>
        <key>Minute</key>
        <integer>10</integer>
    </dict>
</dict>
</plist>

Based on my log files, they both run just fine—but after the Quit runs, iCal relaunches.

Any ideas for things to try, or things that might be causing this?

share|improve this question

3 Answers 3

If you don't specify <Minute> in the <StartCalendarInterval>, then (just like cron's *) it will run the job every minute.

share|improve this answer
    
Then why, when I quit iCal manually, does it wait to restart till 5:00am the next day? –  Dori Mar 18 '11 at 1:15
1  
It's 6am or after. To be pedantic: you specified Hour = 5, but no Minute, so it runs every minute from 5:00 to 5:59. If you were awake during that time period and using the machine, not only would it restart on you but it'd steal focus once a minute while running. Note that your quit job specifies both Hour and Minute, and doesn't keep running. –  geekosaur Mar 18 '11 at 1:32
    
launchd is built to start things on demand, once, or continuously. It's not set up to stop things except for log out or shut down. Since iCal doesn't have an internal mechanism to listen for a kill command, it's best to load and unload a job that is set to run always. See my answer for more info... –  bmike Apr 21 '11 at 23:00
    
@bmike But osascript -e 'tell application "iCal" to quit' does work. launchd might not be designed for stopping tasks, but you don't explain why this won't work. Also, in this case, launchd isn't stopping a task, it is starting a new one. (That new one happens to be a shell script that stops something, but that seems to be beside the point.). –  user588 Apr 23 '11 at 23:01
    
I totally agree and missed the most simple solution - killall or osascript. Kudos to @mankoff –  bmike Apr 24 '11 at 2:26

One answer to this problem is slightly counter-intuitive.

Simplify your iCal plist file and make iCal run forever by setting OnDemand to false. Here is a file I tested by storing it as ~/Library/DontLaunchAgents/com.dori.iCalForever.plist - don't leave this file in any directory launchd looks at unless you want to launch iCal every time you log in and respawn it should you ever choose to quit iCal.

<?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.dori.iCalForever</string>
    <key>OnDemand</key>
    <false/>
    <key>ProgramArguments</key>
    <array>
        <string>/Applications/iCal.app/Contents/MacOS/iCal</string>
    </array>
</dict>
</plist>

Now you need a single shell script that calls these two commands separated by a sleep long enough to accomplish whatever task iCal needs to perform.

launchctl load ~/Library/DontLaunchAgents/com.dori.iCalForever.plist
sleep 600 #sleep time in seconds
launchctl unload ~/Library/DontLaunchAgents/com.dori.iCalForever.plist

.. and Bob's your uncle.

share|improve this answer
    
I also figure you could have two launchd plists - one to call the launchctl load and another delayed to call the launchctl unload but I prefer the script solution since it behaves better if the mac is sleeping and wakes after the launch time has passed. Launchd will trigger events that should have run during the sleep time and you might get the load and unload quicker than you desire in that odd case. –  bmike Apr 21 '11 at 17:46

Something you might want to try if configuring this is becoming a pain is Lingon, it is a easy to use GUI editor for launchd, I would try to configure your job in it and see if you get the same results.

share|improve this answer
    
As awesome as Lingon is - it doesn't change the way launchd works. This doesn't help with the problem that launchd isn't set up to both start and then stop an app. iCal doesn't have an easy way to know it's supposed to kill itself, so the issue is tracking the PID of the iCal process so it can be unloaded/quit once the processing is done. See my answer for more details. –  bmike Apr 21 '11 at 16:47

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.