My new server’s predecessor ran Linux. I had a few scheduled jobs, like the ones that back up this blog, its database, and so on. On Linux the tool used for scheduling jobs is cron. Lo and behold, Apple has deprecated cron on their systems in lieu of an Apple-grown tool named launched. Launchd’s job is to replace cron, init, the rc scripts, the file alteration monitor, and a whole host of other normal Unix utilities in favor of one huge behemoth process that starts at boot. I’ve got mixed feelings about this concept, as it goes against the Unix philosophy of “doing one thing and doing it well”, but I chose OSX and have tried to learn their way of doing things.
I found several articles describing how one creates the plist files used by launchd. These XML files contain the information on the process you wish to run, including when to run it, how to run it, and who is allowed to run it. Here I will detail how I used it to merely run a scheduled job every night at midnight.
To start with, I have a simple script that backs up the database on the server:
#!/bin/sh BACKUPDIR=$HOME/Backups/lawrence.littleprojects.org/db REMHOST=lawrence.littleprojects.org OUTPUTFILE=$REMHOST.sql # if we don't have a backup directory, make it if [ ! -e $BACKUPDIR ]; then mkdir -p $BACKUPDIR fi cd $BACKUPDIR # run the remote database dump command ssh $REMHOST "mysqldump --verbose --user=root --password='XXXXXXXXXXXX' --all-databases" > $OUTPUTFILE # if the zipped output file already exists, move it before zipping the new one if [ -f $OUTPUTFILE.bz2 ]; then mv $OUTPUTFILE.bz2 old-$OUTPUTFILE.bz2 fi bzip2 $OUTPUTFILE chown smj:staff $OUTPUTFILE.bz2
This script is creatively entitled backup-database
. I want it to run every night so I have an exact copy of this website’s database in case the web server goes down and I have to reinstall everything.
Under cron, I would run crontab -e
and then put the following line into the editor that is brought up:
0 * * * * $HOME/bin/backup-database
Apple made this simple step a lot more complicated than, in my opinion, it needed to be; but they gave me a tool that is a lot more powerful that mere cron.
To use launchd, I needed to make a file in the directory /Library/LaunchDaemons
named org.littleprojects.backup.lawrence.db.plist
that contains the following XML code:
<!--?xml version="1.0" encoding="UTF-8"?-->
Label
org.littleprojects.backup.lawrence.db
ProgramArguments
/Users/smj/bin/backup-database
StartCalendarInterval
Hour
0
Minute
0
I’m a little annoyed at how Apple handled the whole key-value syntax, but I’ll let that be. The file reads as follows: create a job named org.littleprojects.backup.lawrence.db
that will run the program /Users/smj/bin/backup-database
at the 0th hour and 0th minute of ever day (midnight). I’m not sure if this is any less difficult to read than the cron syntax, but it is more difficult to write to be sure.
Once I created the file, I then needed to load it into launchd, which could be done by running the following commands:
sudo launchctl load org.littleprojects.backup.lawrence.db.plist
If you do this you, do not need to reboot as others claim. This is Unix and reboots should only be necessary for major operating system changes. Rebooting in order to create a scheduled job is not only ridiculous, but time-wasting and leads to error-prone behavior like making many untested scheduled jobs at once in order to save on reboots. I always try to find a way around reboots to avoid error-prone behaviors!
So, I did the same thing for other scripts I wanted to run at certain times. I understand why apple decided to use launchd, I just wish they would have made it easier to configure. Lingon exists in the app store for the express purpose of helping you create these plist XML files, but it will not load them for you and instead recommends you reboot after each edit, which I noted my distaste for above.
In the future I will be exploring how to best use launchd for other purposes, like restarting services, and scheduling scripts to run when other system events occur.
References:
[…] littleprojects.org — for example this post […]