Automatic backup and .command files

My iBook has the “master” copies of my family’s iTunes and iPhoto libraries. I use rsync to back these up to an older Linux machine that I have running as a server in the basement. This is how I’ve automated the process.

First, I created a shell script that runs the necessary rsync commands:

#!/bin/bash
echo "Syncing iTunes library..."
rsync -avz --delete /Users/my-name/Music/iTunes\
  server-ip:/home/media/
echo "Syncing iPhoto library..."
rsync -avz --delete /Users/my-name/Pictures/iPhoto\
  server-ip:/home/media/
echo "Finished!"
echo `date`

where my-name and server-ip are replaced by my login name and the IP address of the Linux server, respectively. Also, I’ve split the rsync lines to make them fit within the space of the blog; in the actual script those are all one line and there is no backslash. Because spaces in file and directory names cause problems in shell scripts, I have renamed my iPhoto library from iPhoto Library to simply iPhoto. Also, you should know that I use the same username on the iBook and the Linux server. If I didn’t, the rsync commands would need an -e ssh -l my-sever-login option.

At this point, I can run this script, which I call syncmedia, from the Terminal on my iBook and it will sync the libraries on the Linux server to those on my iBook. Because rsync is pretty smart, only files and directories that have changed since the last sync are copied to the server. Unfortunately, rsync, which uses ssh for authentication, asks for my password on the server both times it is invoked by the script. This is a bit annoying, but more important, it means syncmedia cannot be run automatically.

To get ssh authentication without entering a password, two things are required:

  1. the server must know the iBook’s public ssh key; and
  2. a program called ssh-agent, which acts as a secure repository for ssh keys, must be running on the iBook.

The first step is covered in many places on the web. Basically, I put the contents of ~/.ssh/id_dsa.pub on my iBook into ~/.ssh/authorized_keys on the server. Now, instead of asking for my password, the server asks for my ssh passphrase each time rsync is invoked. This seems like a step backwards—and it is—but the next step will get us to passwordless logins.

Ssh-agent is a program that runs in the background and, in effect, automatically types in your ssh passphrase whenever it’s requested. (You don’t see any of the typing, it’s all done as program-to-program communication.) Getting it to work smoothly with rsync is a bit of a trick, involving two environment variables and another program, ssh-add, which gives ssh-agent your passphrase.

There is a nice Mac program called SSHKeychain that handles the ssh-agent stuff as you work as a normal user (and can also give you one-click access to ssh tunnels to other services on remote servers), but it doesn’t seem to provide your ssh keys to your cron jobs. Since cron is how I plan to automate the syncing, SSHKeychain is out.

On Linux machines, the usual program to allow ssh key access to cron jobs is keychain from the Gentoo project. You can get a Mac version of it from Fink. The keychain man page says:

Typically, one uses keychain by adding the following to the top of their ~/.bash_profile (or ~/.zlogin, in case of zsh):

keychain ~/.ssh/id_rsa ~/.ssh/id_dsa
. ~/.keychain/${HOSTNAME}-sh

which isn’t appropriate for a Mac, as ~/.bash_profile is only run when the Terminal program is started, not whenever the user logs in. Instead, what we need is a shell script with those two lines added to my Login Items list in the Accounts preference pane.

The script is

#!/bin/bash

keychain ~/.ssh/id_dsa
source ~/.keychain/${HOSTNAME}-sh

and it’s saved in the file ~/bin/keychain-start.command. The .command extension is supposed to make the script run when double-clicked, which is what you want for a Login Item. I’ve learned, however, that this doesn’t always work. I use BBEdit as my editor, and when I write a .command file with it, double-clicking the .command file just opens the file in BBEdit—it doesn’t run it. This is apparently because the File Creator code that BBEdit gives the file (a legacy of the pre-OS X Mac) overrides the .command extension. To get rid of the File Creator, I used the SetFile command from the Developer Tools:

/Developer/Tools/SetFile -c "" ~/bin/keychain-start.command

The -c "" part is what erases the File Creator code. In the Finder, the file’s icon changes from a BBEdit document to a “Command” document. If you haven’t installed the Developer Tools, which are on the OS X DVD, you can use any number of utility programs to do the same thing.

Adding ~/bin/keychain-start.command to the Login Items list in my Account preference pane is the final step. Now when I log in, a Terminal window opens and runs that script. If I’ve just turned on or restarted the machine, I’m prompted for my passphrase. Otherwise, I’m simply told that ssh-agent is already running.

Now I just need to add one more line to my syncmedia script, changing it to

#!/bin/bash
source $HOME/.keychain/${HOSTNAME}-sh
echo "Syncing iTunes library..."
rsync -avz --delete /Users/my-name/Music/iTunes\
  server-ip:/home/media/
echo "Syncing iPhoto library..."
rsync -avz --delete /Users/my-name/Pictures/iPhoto\
  server-ip:/home/media/
echo "Finished!"
echo `date`

The added line, source $HOME/.keychain/${HOSTNAME}-sh, is the same as the final line of the Login Item script. The file it sources exports two environment variables that ssh-agent needs to feed the ssh keys to rsync.

The crontab line that schedules my iTunes and iPhoto backup is

00 22 * * * $HOME/bin/syncmedia

which has syncmedia run at 10:00 pm every day, a time that the iBook is typically running.

Phew! It’s actually much easier to do than to describe.