Mac crontab – Mac OS X startup jobs with crontab, er, launchd   1 comment

Mac OS X crontab FAQ: How do I run a Unix job (or shell script) through the Mac crontab facility? I keep trying to edit my Mac crontab file, but my Mac OS X system won’t save my crontab changes, or run my program (or shell script).

Mac OS X – crontab, launchd, and launchctl

Here’s the short story about the Mac crontab facility: As of this writing (updated in 2011), the Mac crontab command seems to be deprecated on Mac OS X, and the Apple documentation encourages you to use their “Mac launchd” facility. Here’s a blurb from the Mac OS X crontab man page:

Darwin note: Although cron(8) and crontab(5) are officially supported under Darwin, their functionality has been absorbed into launchd(8), which provides a more flexible way of automatically executing commands. See launchctl(1) for more information.

That being said, it looks like you can still use the Mac crontab facility, as implied by this note in the Mac OS X “cron” man page:

The cron utility is launched by launchd(8) when it sees the existence of /etc/crontab or files in /usr/lib/cron/tabs. There should be no need to start it manually.

(Follow-up note: I have not been able to get crontab to work under Mac OS X 10.6.)

In this tutorial I’m going with Apple’s suggestion, and I’m going to show you how to run your Unix shell scripts and commands with the Mac OS X launchd facility using thelaunchctl command.

Running a simple command every minute with Mac launchd

For my purposes, I want to run a shell script every minute to ping my websites. If the sites don’t respond, I want to be able to notify myself of the problem, perhaps bydisplaying a dialog from the Mac OS X Unix shell.

To get this running, I followed the steps shown here.

(1) Move to the $HOME/Library/LaunchAgents directory

First, open a Terminal, and use the cd command to change to this directory:

$HOME/Library/LaunchAgents

When I dug around in the Apple documentation, I found there are three main directories you can use with launchd, and that’s how I learned about this directory. Here are your three options:

/Library/LaunchDaemons Put your plist scripts in this folder if your job needs to run even when no users are logged in.

/Library/LaunchAgents Put your plist scripts in this folder if the job is only useful when users are logged in. (Note: I learned that this has the side-effect of your job being run as ‘root’ after a system reboot.)

$HOME/Library/LaunchAgents Put your plist files in this folder if the job is only useful when users are logged in. (When your plist configuration file is placed here, your job will be run under your username.)

Note that when you use the first two directories shown here, you must use the “sudo” command to edit your files.

To keep this simple and just see how things work initially, my advice is to use theLibrary/LaunchAgents folder in your home directory until you see how things work, then use the other two system folders, if/when necessary.

(2) Create a Mac plist file to describe your job

Next, in this folder you need to create a Mac plist file to describe the job you want to run. In my case I fired up vi to edit my file:

vi com.devdaily.crontabtest.plist

And then following Apple’s documentation (and after many errors), I ended up with these contents in my Mac plist file:

<?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.devdaily.crontabtest</string>

  <key>ProgramArguments</key>
  <array>
    <string>/Users/al/bin/crontab-test.sh</string>
  </array>

  <key>Nice</key>
  <integer>1</integer>

  <key>StartInterval</key>
  <integer>60</integer>

  <key>RunAtLoad</key>
  <true/>

  <key>StandardErrorPath</key>
  <string>/tmp/AlTest1.err</string>

  <key>StandardOutPath</key>
  <string>/tmp/AlTest1.out</string>
</dict>
</plist>

This Mac plist file can be read as “Run the script /Users/al/bin/crontab-test.sh every 60 seconds, and redirect the standard output and standard error as shown”.

A note about the naming convention: Apple strongly encourages you to use the naming convention I’ve shown here for (a) your filename and (b) your “label” value. When using the commands that I’m about to show you, you’ll refer to the filename and this “label”, and they encourage you to follow this naming convention to avoid namespace collisions. Having programmed in Java, this is just like the Java package naming convention, and I have no problems following it.

(You’ll see other Mac launchd jobs running when you use the launchctl “list” command below, and I think after that, you’ll agree that naming convetion is a good idea.)

(3) Tell OS X about your Mac plist launchd file

Next, it’s important to know that your Mac OS X system won’t pick up on this change immediately. You have to tell the Mac launchd daemon to “load” it, using the Mac launchctl command, like this:

launchctl load com.devdaily.crontabtest.plist

In my case, I just had my /Users/al/bin/crontab-test.sh shell script write some output to a file in the /tmp directory so I could debug this process, like this:

date >> /tmp/MyLaunchdTest.out

After issuing the “launchctl load” command I started getting output from my datecommand. I let it run for several minutes while I checked that everything was working, and then turned it off using this “unload” command:

launchctl unload com.devdaily.crontabtest.plist

(4) How Mac launchd works with system reboots

Next, I tested how this works with Mac OS X system reboots.

In my tests, I found that just the presence of my file in the $HOME/Library/LaunchAgentsdirectory was enough to make Mac OS X load the file and begin running my script every minute. To test this properly, I issued the Mac “launchctl unload” command, like this:

launchctl unload com.devdaily.crontabtest.plist

and then I rebooted my system.

After logging into my Mac after the reboot and checking my output files, I found that my plist script had begun executing every minute.

It’s important to note that after a reboot you have to use a slightly different “unload” command than what I showed earlier. In my previous example you didn’t need to include the full path to your plist file, but now that the system has started your job for you, you need to unload it by specifically the full path to the file, like this:

launchctl unload /Users/Al/Library/LaunchAgents/com.devdaily.crontabtest.plist

Mac launchd and plist – An important note about root and sudo access

It’s also very important to note that if you placed your Mac plist file in one of the two system directories (/Library/LaunchDaemons, /Library/LaunchAgents), your job will be running as the “root” user after a system reboot. This means a couple of things:

First, output files created by your script will be owned by root.

Second, you’ll need to use “sudo” before any of your launchctl commands, as shown here:

sudo launchctl list | grep 'devdaily'

If you issue that launchctl command like this, without the sudo command:

launchctl list | grep 'devdaily'

you’ll never see that your job is running, even though it is. (Because it’s owned by root, you’re not allowed to see it.)

Mac OS X launchd, launchctl, and plist resources

I used the following Mac OS X launchd, launchctl, and plist resources while researching this tutorial:

Mac OS X startup jobs: cron and crontab, launchd and launchctl

In summary, the Mac OS X launchd facility appears to be a replacement for the standard Unix cron/crontab facility. I believe you can enable crontab to work on Mac OS X, and I’ll show how to do that in a future tutorial. In the meantime, the Mac OS X launchd/launchctl facility seems to be the future for launching startup jobs, and other jobs we have traditionally run through the cron facility. As such, if you’re going to be working a lot on Mac OS X systems, it behooves you to learn about this new approach.

 

http://www.devdaily.com/mac-os-x/mac-osx-startup-crontab-launchd-jobs

Posted 2011年11月20日 by gw8310 in mac os

One response to “Mac crontab – Mac OS X startup jobs with crontab, er, launchd

Subscribe to comments with RSS.

  1. launchctl Tutorial

    launchctl allows users to start / stop applications that is typically processed via launchd. (MacOS’s equivalent of cron).

    launchctl command configurations are stored as XML formatted .plist files located in directories /System/Library/LaunchAgents or /System/Library/LaunchDaemons for system wide start items, for user specific items the plist files are stored in ~/Library/LaunchAgents or ~/Library/LaunchDaemons directories.

    Agents or Daemons?
    An agent is a program that requires access to specific users’ information. A daemon is a program that runs in the background and requires generally no input from any user.

    For more a comparison between Agents and Daemons refer to this Apple technote.

    What is in a .plist?
    A .plist file must contain at the very least the keys Label, ProgramArguments array and a key to tell launchd how the application is run eg KeepAlive, RunAtLoad for one time operations. or StartonMount, StartInterval, StartCalendarInterval for repeating occurrences.

    A complete dictionary of all the property keys can be found here. A few things it can do is to monitor modified paths (via the key WatchPath), HardResourceLimits etc.

    Here is an example for com.companyname.agentorapplicationname.plist, which runs the application full/path/to/binary every 60 seconds:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20

    Label
    com.companyname.agentorapplicationname
    ProgramArguments

    /full/path/to/binary
    -firstargument
    valueoffirstargument
    -secondargument
    valueofsecondargument

    StartInterval 60

    launctl operations

    Command Description
    launchctl list Lists the PID, status and name of loaded processes
    launchctl list com.com.exampleco.eg Output the runtime information (eg. PATH) of com.exampleco.eg
    launchctl start com.exampleco.eg Starts com.exampleco.eg
    launchctl stop com.exampleco.eg Stop com.exampleco.eg
    launchctl loads -w /path/example.plist Loads a process by its plist filename
    launchctl unloads /path/example.plist Stop and unload a process by its plist filename
    submit -l labelname -p /path/eg/binary -o /path2/stdout -e /path2/sterr Manually run binary under the label labelname with to specified stdout and sterr devices/files

发表评论

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / 更改 )

Twitter picture

You are commenting using your Twitter account. Log Out / 更改 )

Facebook photo

You are commenting using your Facebook account. Log Out / 更改 )

Google+ photo

You are commenting using your Google+ account. Log Out / 更改 )

Connecting to %s

%d 博主赞过: