Sunday, August 26

Automated on-demand backups with OS X

Update #2: Although the solution below is free and easy and relatively reliable, I eventually broke down and just purchased SuperDuper.


Update #1: In my haste, I forgot some important tidbits of information, such as where you put all these files...


I just [finally] got myself an external USB hard drive (Western Digital 160GB Passport) and I wanted to set up my Macbook Pro so that whenever I connected the drive to the laptop, the laptop would automatically back itself up, then give me a report of the backup and unmount the drive so I could immediately unplug it and put it away. That's how lazy I want to be about backing up.

So I scoured the 'net and finally figured out how to "git 'er done" just the way I like, thanks primarily to this old post. I'm posting this primarily for my own benefit because I know I'll have to do this again in the near future and I won't remember how I did it. So here's how it goes...

First of all you need to write yourself a fancy backup script (most comments are from the original author, not me) and place it in "~/Library/Scripts". I named my script "backup.com" to conform to the example in the original article:

#!/bin/bash

# Borrowed (and BASH'd) from...
# http://www.macresearch.org/tutorial_backups_with_launchd

# Convenience variables to specify what I want to
# backup and where I want to back it up to

HOME="/Users/ted"
VOLUME="/Volumes/WD Passport"
DEST="$VOLUME/"

# This sleep timer has been added to allow enough
# time for the system to mount the external drive
# On my PowerBook 30 sec. is more than enough time

sleep 20

# This check is added to test for cases when we are
# removing a drive from /Volumes or if the drive failed
# to mount in the first place

if [ ! -e "$VOLUME" ]; then
echo $VOLUME not mounted;
exit 0;
fi

# Create the folder to back up the data to (defined above)

if [ ! -e "$DEST" ]; then
echo $DEST does not exist;
exit 0;
fi

# Copy the files over. Here we are using rsync.

EXCLUDES="$HOME/.rsync-excludes"
RESULTS="$HOME/rsync-results.txt"

rsync --verbose --archive --modify-window=1 --exclude-from="$EXCLUDES" "$HOME" "$DEST" > "$RESULTS"
# --delete --delete-excluded

# Optionally, once the rsync is done, unmount the drive.

hdiutil detach "$VOLUME" >> "$RESULTS"

# Display the results

mate $RESULTS

exit 0

Then you need yourself a "plist" file to tell OS X's "launch control" [launchctl] service to (a) watch for the drive to be mounted [plugged in], and (b) run the aforementioned script when it is. This file goes in "~/Library/LaunchAgents" and in my case is named "net.rudiment.backup":

<?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">
<dict>
<key>Label</key>
<string>net.rudiment.backup</string>
<key>LowPriorityIO</key>
<true/>
<key>Program</key>
<string>/Users/ted/Library/Scripts/backup.com</string>
<key>ProgramArguments</key>
<array>
<string>backup.com</string>
</array>
<key>WatchPaths</key>
<array>
<string>/Volumes</string>
</array>
</dict>
</plist>

Now as the original article mentions, all you have to do is log out and log back in for this trigger to activate, but you can do it manually (and I did) like so:

Teflon-Ted:~ ted$ launchctl list
Teflon-Ted:~ ted$ launchctl load ~/Library/LaunchAgents
Teflon-Ted:~ ted$ launchctl list
net.rudiment.backup

That's it! You're golden.

And also for the sake of posterity, here's the contents of the "exclude" file referenced in the script:

.Trash/
.netbeans/
.netbeans-derby/
.gimp-2.2/
.thumbnails/
.svn/
Library/Caches/
Library/Mail/
Library/Logs/
.history/
iPod Photo Cache/
Printers/