I was revisiting my metrics project, having used the first one as the prototype to refine requirements (nothing works better at getting real requirements out of people than showing them something that doesn’t quite do what they want).
When it came time to test a monitor, I tried to get one running under cron and it didnt actually work for me. I can’t remember if cron has ever worked for me on a mac, but didn’t have the time to figure out why and how. It was time to make the jump to launchd.
Launchd is billed as an init.d, /etc/rc, xinetd, .profiile, and crontab replacement, i.e. it can launch scripts at system startup, user login, or on a specified interval.
My use case was to do something cron like. This was not entirely straightforward, there is a difference between using StartCalendarInterval (to run things on a specified date, or every minute if no value is specified) and StartInterval (to run things at a specified interval, similar to specifying */5 for every 5 minutesin cron).
programs are loaded into launchd with launchctl, they are specified as plist files with a pretty simple key/value and/or key/dictionary of values XML format. Here is my .plist file for running something every 5 minutes:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>Label</key> <string>com.evri.metrics.deployment.cron</string> <key>ProgramArguments</key> <array> <string>/opt/local/bin/ruby</string> <string>/Users/arunjacob/hypertext/metrics_monitor/lib/tasks/deployment_monitoring/deployment_monitor_driver.rb</string> <string>deployment_aggregator</string> </array> <key>StandardErrorPath</key> <string>/dev/null</string> <key>StandardOutPath</key> <string>/dev/null</string> <key>StartInterval</key> <integer>300</integer> <key>RunAtLoad</key> <true/> </dict> </plist>
Note that in key value parlance, StartInterval takes an integer which specifies the # of seconds. If I wanted to run something every day at a specified time, I would use StartCalendarInterval, which takes a dictionary element that contains time intervals.
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple. com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>Label</key> <string>com.apple.periodic-daily</string> <key>ProgramArguments</key> <array> <string>/usr/sbin/periodic</string> <string>daily</string> </array> <key>LowPriorityIO</key> <true/> <key>Nice</key> <integer>1</integer> <key>StartCalendarInterval</key> <dict> <key>Hour</key> <integer>3</integer> <key>Minute</key> <integer>15</integer> </dict> </dict> </plist>
Note the difference between StartCalendarInterval syntax and StartInterval syntax — StartCalendarInterval takes a dict structure that contains key/value pairs. In other words it takes a hash. You can also use Arrays, as specified in the value for the ProgramArguments key. Just make sure your keys have the correct kind of values. as specified here.