(updated
Launchd is Apple’s replacement in OS X for several Unix process management utilities, most notably cron. I use it to run several scripts at scheduled times or fixed intervals. Every day my computer is set to download my twitter statuses and check my library card for overdue books. Every morning my computer resets its volume to medium. Every week it backs up the WordPress database of my “family pictures” blog. It syncs my work files between my computer and the university file server.
Almost anything you can do with cron you can do with launchd, but with more power and flexibility. Unlike cron, launchd does not assume that your computer is always running. So if your computer happens to be sleeping at the time a job is scheduled, it will run the job when it wakes up. This is probably the best feature of launchd, because it allows me to run scripts on my iMac while still letting it sleep when I’m not using it.
I have pieced together what I know about using launchd to schedule jobs from tutorials across the internet, trial and error, and the manuals. This is my attempt to gather all my knowledge about this in one place. If there is something you think I should add or fix, let me know.
(This article has been updated from the original version, most notably with information about new software tools.)
Because this article is longer than usual.
The best way to get started is to buy LaunchControl.
LaunchControl doesn’t have to stay running in the background. It just sets up the job and gets out of the way, letting OS X do the rest. And since OS X already uses launchd to run just about everything, from Spotlight to ssh-agent to the bezel notifications that appear when you change the volume, scheduling jobs will not add any overhead.
Each launchd agent is stored in an xml plist file. The file contains
information about what program to run, when to run it, which arguments to use,
and other options. Although technically you can make things work no matter
where the plist file is saved, it is best to put it in
~/Library/LaunchAgents
, because plists in this folder are automatically
loaded into launchd when you log in.
Each agent has a label, which must be unique. Apple uses reverse domain syntax
com.apple.whatever
, but it doesn’t matter. The plist filename can be
anything, but you would be crazy to use anything other than the label, e.g.
com.apple.whatever.plist
. Sometimes agents are referred to by the file, and
sometimes by the label.
Warning: At some point you will use one of your plists as a template to create another. You will of course give them different filenames, but you will forget to change the label, which means only one of them will work. Hopefully this warning will then enter your mind and you will solve the problem.
Apple uses the terms load and unload to mean that an agent is in the
system, ready to go, and start or stop to talk about running or killing the
actual process. So all agents in your LaunchAgents
folder are loaded when the
computer starts up. Then it pays attention to when they are scheduled and
starts them at the appropriate time. If you create a new plist file you need to
load it manually. If you change a plist file, you need to unload it and load it
again.
Apple provides launchctl to manage your agents. The main commands you need are
launchctl load ~/Library/LaunchAgents/net.nathangrigg.archive-tweets.plist
launchctl unload ~/Library/LaunchAgents/net.nathangrigg.archive-tweets.plist
launchctl start net.nathangrigg.archive-tweets
launchctl stop net.nathangrigg.archive-tweets
launchctl list
Notice that load
and unload
require the filename, while start
and stop
require the label. The start
command will manually run the job, even if it
isn’t the right time. This can be useful for testing. The stop
command just
kills the process, but is convenient because you don’t need to know the pid.
The list
command shows all loaded agents, with the pid if they are currently
running and the exit code returned the last time they ran.
I mentioned LaunchControl earlier, and it is the tool I recommend. It can create and edit your plist files, giving you helpful information when you need it. It can start, stop, load, and unload your agents and help you debug them when they fail.
(LaunchControl was released after the first version of this article, and it is good enough that it makes a lot of what is written here unnecessary.)
Lingon is a simple tool for creating and managing launchd agents. It does not give you as much control as LaunchControl, but it has been around longer. When you use Lingon to edit an agent, pressing the Save button automatically loads and unloads the job.
One other useful tool is Lunchy by Mike Perham. This is a ruby script
that speeds up the loading and unloading of agents by allowing you to refer to
a plist by any unique substring of the filename. My only issue is that it uses
terminology that conflicts with Apple’s. Lunchy uses start
to mean load
and
stop
to mean unload
. It mostly compensates by providing very useful
commands restart
to unload and then load and edit
to edit the file in your
default editor.
Install it using gem install lunchy
and then edit the file using
lunchy edit archive-tweets
Now reload it using
lunchy restart archive-tweets
Here is a very basic plist file to run a script every 86,400 seconds.
|
|
There are several other keys you can insert between lines 4 and 13 to activate other options. If you want to see what is available, read the launchd.plist manual.
Note that I have provided the full path to the script, since globs aren’t
expanded by default. If you want to expand globs, you can include an
EnableGlobbing
key followed by <true/>
.
If your script requires arguments, you would supply these in extra string
tags after line 9.
If you download a script from the internet or write one yourself, make sure it is executable, or this might not work.
By default, anything written to standard out or standard error ends up in the
system log. If you would like it to be somewhere else, you can use the
StandardOutPath
and StandardErrorPath
keys.
The KeepAlive
key allows a script to be run multiple times, depending on
certain conditions. If you set it to <true/>
, then the script will be run
over and over again forever. The following snippet will rerun the script if it
returns a nonzero exit code. Read the xml as “Keep the process alive as long as
it doesn’t successfully exit”.
<key>KeepAlive</key>
<dict>
<key>SuccessfulExit</key>
<false/>
</dict>
In most cases, the system will wait 10 seconds between runs of the script to
save system resources. You can adjust this with a ThrottleInterval
key, which
takes an integer argument, and should be outside the KeepAlive
dictionary.
You can also set the agent to stay alive depending on the (non)existence of an
internet connection using NetworkState
or of a file using PathState
.
In older versions of OS X, there was an OnDemand
key which was required. It
is now obsolete and has been replaced by KeepAlive
, which is optional. Many
of the other examples on the internet still have an OnDemand
key, but you
don’t need it.
For security reasons, launchd will not run LaunchAgents whose plist files have
the wrong permissions. For example, they must not be writable by anyone other
than the owner. Root LaunchAgents stored in /Library/LaunchAgents
must be
owned by the root user.
Both launchctl
and lunchy
allow you to disable an agent using the -w
flag
with unload
/stop
. I do not recommend this. An agent that has been disabled
will not load when you log in and cannot be loaded using
load
/start
without using the -w
flag again. You will probably just be
confused later about why an agent is not loading even though it is in the right
place. Information about which agents have been disabled in this manner is
stored in a separate file. In Lion, this is the file
/var/db/launchd.db/com.apple.launchd.peruser.NNN/overrides.plist
where NNN
is your user id number (find it using id -u
).
Your launchd agents are loaded when you log in, but not unloaded when you log
out. So the only time your agents aren’t loaded is during the time between a
restart and when you log in. If you have multiple users and need something to
run no matter who is logged in, you should put it in
/Library/LaunchAgents
or /Library/LaunchDaemons
.
If your computer sleeps often, it will be asleep when jobs should run, which
means it will run them right when it wakes up, possibly before it connects to
the internet. I have experimented with KeepAlive
and NetworkState
to get a
job to repeat itself until there is a network connection. You could also use
SuccessfulExit
and write the script so that it only returns a nonzero code to
mean “run again in 10 seconds.” Either method would have the script running
(and presumably failing) every 10 seconds when you have no internet connection.
A better idea would be to just sleep 5 seconds at the beginning of your script.
Or you could double the run frequency and hope for the best.