I recently purchased an iPod, not only for the music, but also with the intention of using it to backup my files. I have about 12 GB of music and another 5 GB of miscellaneous files in my home directory, so the 30 GB (refurbished) model was a natural fit with room to expand.
iTunes makes synching my music collection extremely easy. But what about my files? I don't want to have to drag all my stuff over manually - that's a pain in the ass, and I'm likely to forget the various hidden ".something" config files and folders so often used by *NIX software. There are numerous backup utilities available for OS X, but which one is the right tool for this job?
Constraints
First I should specify the job itself. My goals are:
- Easy - I don't want to have to fiddle with settings every time I want to backup.
- Semiautomatic - Fully automated backups are great for when you have a second internal drive or an external one that is consistently hooked up to your computer. But an iPod is liable to only be connected to your computer from time to time, and not at any predictable time during the day. The best solution is to have the backup automatically begin when the iPod is connected.
- Fine-grained control - I want to backup my home directory,
~, but not my music. My music is stored in~/Music, which means that if I don't have control over what's included and what's not, the music will be backed up twice and the iPod's 30 GB will be insufficient. Plus, I don't want the various browser and other caches copied over, as they can be thousands of files and many megabytes in size. - "Strict" backup - I don't know what to call this, so I'm making up the word "strict." What I mean is that I want only the files that exist in
~to be backed up. Specifically, I want files to be deleted from the backup when I delete them from the original. - Cheap - I don't want to pay even more money for the privilege of keeping backups. I prefer free, open source software, if possible.
Options
Given the above constraints, I think the problem is well-defined enough that we can start looking at software. The ones I tried out are as follows:
- Backup 2 by Apple Computer
- I've used Backup in the past to back up to my iDisk and to an external hard drive. It scores very well in terms of fine-grainedness, giving you file-by-file control over what gets backed up. Unfortunately, it's pretty slow, and the UI for selecting and deselecting files is a bit clunky. It's very easy to use if you only want to back up a few things, such as the nifty QuickPick selections. They allow you to back up, for instance, all of the Word documents in your home folder. The thing that ruins Backup for me, however, is the fact that it won't delete files from the backup that are no longer in the source.
- SuperDuper! 1.2.3(v65) by Shirt Pocket
- SuperDuper! seems to have great street cred, for instance at the MacNN Software forum, so I decided to check it out. The UI is very nice, and it seems to offer a lot of flexibility, but it was about 2AM when I was doing this, and it just wasn't transparent enough. The real kicker is the price tag - I don't want to spend $20 on a backup tool. However, the developers are very nice, and they are working very hard to bring automation and other enhancements to SuperDuper! I'm looking forward to future versions.
- PsyncX 2.1.1 by David Baker
- PsyncX is a GUI wrapper around an open source command line program called
psync. PsyncX is free, but the GUI seemed limited. Also, from what I could glean from various Google searches,psyncitself has only a subset of the features found in another open source sync program calledrsync.
- PsyncX is a GUI wrapper around an open source command line program called
- RsyncX 2.1 by Kevin Boyd
- RsyncX is, like PsyncX, a free GUI wrapper for the open source command line program
rsync. I ended up settling on this one, despite problems I encountered (I'll get to that in a minute), though I imagine I probably could have comfortably gone withpsyncinstead.
- RsyncX is, like PsyncX, a free GUI wrapper for the open source command line program
- ChronoSync v2.0.7 by Econ Technologies
- Apparently ChronoSync can do exactly what I set out to do originally, but I only heard about it after-the-fact, and it costs US$30.
Setup
Note: Make sure your iPod is set to be mounted on the desktop. You can change the iPod's behavior in iTunes.
Open RsyncX. I want to back up my home directory to a folder called "Backup" on my iPod, which I named "aPod." That means the destination path is /Volumes/aPod/Backup. I figure it would be safer using the absolute path for my home folder, so the source path would then be /Users/amake. But things aren't quite right:
- In order to be a "strict" backup, we'll have to uncheck the "Add Files To Destination" option.
- Also, This Script: "Will Run On Local Machine Only."
- As such, the Network Transport should be "daemon (server)" - ssh is only needed for network transfers.
- I like to see progress reports, but nothing too fancy, so I'll turn up the Debugging Verbosity up one notch, and turn During Rsync Show to "No Progress Metering."
- I don't need to see how long things take, though, so I'll uncheck "Show Elapsed Time."
- Also, to make things more permanent, we're going to save the script as "iPodBackup" instead of always running it within RsyncX.
Now things should be looking like this:
Click "Generate," and the command will be saved in your home folder as iPodBackup.command. Double-clicking on this file will execute it in the Terminal. You can also execute it from the command line by entering ~/iPodBackup.command.
Tweaking
But wait! There are a couple problems here: The first is that if you installed rsync along with RsyncX, calling it from the Terminal will execute the version that ships with OS X, /usr/bin/rsync. This version doesn't understand the resource fork, which is an important part of some Mac files. The version installed with RsyncX, on the other hand, does understand resource forks, and it is located in /usr/local/bin/rsync. Open up iPodBackup.command in a text editor, such as TextEdit, and modify it to use the "smart" version, like so:
/usr/local/bin/rsync -a -v "/Users/amake" /Volumes/aPod/Backup --eahfs --delete
Note: The source and destination paths must be in the order shown here. The arguments that begin with - and --, on the other hand, can be in any order. This isn't necessarily true of all commands, though, so be careful.
I'll take a moment here to explain the various arguments of this command:
-ais equivalent to--archive, and it means that the backup will be recursive, and that it will preserve symlinks, permissions, times, groups, owners, and devices. This is pretty much required.-vis equivalent to--verbose, and it makesrsyncgive us a bit more information than usual about what's going on. Single-letter arguments can usually be combined in a command, so in the next step I will have changed-a -vto-avfor simplicity's sake.--eahfsenables support for resource forks and other features of HFS+. We need this for proper handling of Mac files.--deletedeletes files that don't exist on the sending side. In my words, it makes it a "strict" backup.
The second problem is that this command is still going to copy all of my home folder, including ~/Music and various caches. The solution to that, it turns out, is in the manual for rsync, accesible in the Terminal by entering man rsync.
The answer is the --exclude option. Adding the argument --exclude "Music/" to the command will exclude any directores titled "Music," which is exactly what I'm looking for. You can repeat the argument as many times as you need to weed out other directories. Let's throw in an --exclude "Cache/" too:
/usr/local/bin/rsync -av "/Users/amake" /Volumes/aPod/Backup \ --eahfs --delete --exclude "Music/" --exclude "Cache/"
Fancying it up
Now the command is complete! However, when you open it it will launch the Terminal, and when it's done processing you'll have to quit the Terminal. Plus, as it's not a true application, we're limited in our use of it in some ways. Luckily, there is a better way. Download Sveinbjorn Thordarson's Platypus, and we'll turn the command into a regular OS X application.
Platypus works most easily with shell scripts, so we're going to have to turn our .command file into a shell script. All you need to do is add #!/bin/sh to a new line at the beginning of the file, and for clarity's sake save the file as iPodBackup.sh.
Now we're going to add a few bells and whistles: As it is now, every time we want to exclude something new from the backup process, we'd have to start over from scratch. However, if we replace the --exclude arguments with --exclude-from file we can exclude files and folders based on a list stored in a file that we can easily edit. Let's call the file exclude.txt and the contents will be the things we want to exclude from the backup, each on its own line. (Lines beginning with # are ignored; enter man rsync in the Terminal, or see an online copy of the manual for a more in-depth discussion of the syntax of these rules.)
#exclude.txt #This document specifies which directories NOT to back up to the iPod Music/ Cache/ Caches/
The final product will be an OS X program bundle, i.e. a folder with a .app extension that contains all of the files necessary for the app to operate. Apparently the location of the bundle gets passed to the program as a variable, so exclude.txt will be inside the folder $1/Contents/Resources. We need to replace the --exclude arguments like so:
#!/bin/sh /usr/local/bin/rsync -av "/Users/amake" /Volumes/aPod/Backup \ --eahfs --delete --exclude-from $1/Contents/Resources/exclude.txt
Another bit of hoodoo we'll work here is a log to keep track of what exactly is going on. First we'll set the location of the log with LOGFILE="$HOME"/Library/Logs/iPodBackup.log. Now we want to use redirects to put data into the logfile. > will take the output of a command and create a file of it (careful! It can also overwrite existing files). >> will append the output of a command to a file. echo string will output a string of your choice, and date will output the current date. Also, we can generalize the source directory (my home folder) to "$HOME". Put all of this together for:
#!/bin/sh LOGFILE="$HOME"/Library/Logs/iPodBackup.log echo "Synching home directory to iPod..." > "$LOGFILE" date >> "$LOGFILE" /usr/local/bin/rsync -av "$HOME"/ /Volumes/aPod/Backup \ --eahfs --delete --exclude-from $1/Contents/Resources/exclude.txt >> "$LOGFILE"
Note: LOGFILE is the name of a variable. To use the value of the variable you have to reference $LOGFILE. This is true for all variables.
Note: The double quotations around the variables are necessary to handle paths with spaces or other weird characters in them.
Note: The final line of the file begins with /usr/local/bin/rsync. Yes, all of that is one big honkin' command, so make sure it's all on the same line.
Now there's just one problem left: This only works with my iPod. If your iPod is named something other than "aPod," this script will not work. Unfortunately, there is no super easy way to tell if an iPod is connected or not, or what its name is. We can make a good guess, though, by using the find command to look for an invisible folder called iPod_Control that is present, to the best of my knowledge, on every iPod. This folder probably contains information used by iTunes and the iPod OS. We're not going to mess with the contents of this; we're just using the existence of the folder as a probable indicator of an iPod.
The appropriate command here would be find /Volumes -name "iPod_Control" -maxdepth 2.
- The search is performed in
/Volumes, which is where all mounted volumes show up. - The
-name stringargument indicates what string we're searching for. -maxdepth 2ensures that we only find results 2 directories deep, so/Volumes/directory/matching file. This prevents against possible false positives caused by some weirdo putting a folder called "iPod_Control" in some random directory. It also speeds up the search by restricting its scope.
This command will return, in my case, /Volumes/aPod/iPod_Backup. But that's only if my iPod is mounted. We should plan for the possibility that the iPod isn't mounted at all, in which case we don't want the backup to proceed. The answer is an if statement. We're going to save the result of the search as the variable DESTINATION, and we're going to test -z "$DESTINATION". In other words, we're testing whether or not DESTINATION has a length of zero, which would be the case if no matches are found.
DESTINATION=`find /Volumes -name "iPod_Control" -maxdepth 2` if [ -z "$DESTINATION" ] then echo "There is no iPod!" exit 1 else # Proceed with the backup fi
Note: The ` marks are required so that the find command is evaluated. Otherwise the value of DESTINATION will be set to find /Volumes ....
This conditional statement ensures that the script will exit if the folder iPod_Control isn't found. In order to turn DESTINATION into the correct path for the backup folder, we just need to append /../, which sends us backwards one level, i.e. to /Volumes/aPod, and then the name of the backup folder, Backup.
DESTINATION=`find /Volumes -name "iPod_Control" -maxdepth 2` if [ -z "$DESTINATION" ] then echo "There is no iPod!" exit 1 else DESTINATION="$DESTINATION"/../Backup # Proceed with the backup fi
Finally, we need to incorporate this logic into the rest of our script. Make sure to change the destination path of the rsync command to "$DESTINATION". While we're at it, we'll spruce up the info heading to the log a little bit, and add some explanatory comments.
#!/bin/sh # Set the log file location to the appropriate place. LOGFILE="$HOME"/Library/Logs/iPodBackup.log # Test to see if an iPod is connected. If yes, proceed. If not, quit. DESTINATION=`find /Volumes -name "iPod_Control" -maxdepth 2` if [ -z "$DESTINATION" ] then echo "There is no iPod!" > "$LOGFILE" date >> "$LOGFILE" exit 1 else DESTINATION="$DESTINATION"/../Backup echo "Synching \"$HOME\" to \"$DESTINATION\"" > "$LOGFILE" date >> "$LOGFILE" /usr/local/bin/rsync -av "$HOME"/ "$DESTINATION" \ --eahfs --delete \ --exclude-from $1/Contents/Resources/exclude.txt >> "$LOGFILE" fi
If you've installed the RsyncX package, the above script is all you need. However, for the purposes of a distributable application capable of running on any OS X installation, we're going to go one step further. We need to package rsync itself within the application. That means we need to change its path to what it will be inside the .app bundle: $1/Contents/Resources/rsync.
Also, I decided that "Backup" is not that great a place to put things. If you have a copy of OS X installed on your iPod, you'd probably want the backup to go to the proper home folder location, which is /Users/$USER on the startup disk. Plus, this way you can back up multiple home folders to the iPod. So, the destination path should now be DESTINATION="$DESTINATION"/../Users/"$USER".
New in version 1.0.1: It turns out that rsync does not create the "Users" folder automatically, so we need to make it if it doesn't already exist. mkdir "$DESTINATION"/../Users should do the trick. Also, I made some minor changes to the logging system.
New in version 1.0.2: By default, "Users" folder is created with permissions such that other users can't write to it. To fix that, the folder is now created with permissions set to 777 (everyone can read, write, and execute) by adding the argument -m 777 to mkdir.
New in version 1.0.3: ~/Library/Logs does not exist for a newly created user. iPodBackup now creates it with mkdir -m 700 "$HOME"/Library/Logs if it doesn't already exist.
New in version 1.0.4: So that multiple iPods don't throw things off, we'll pipe the find command through head -n 1. This cuts out everything but the first line returned by find.
#!/bin/sh # iPodBackup version 1.0.4 (2005-01-09) by amake # Set the log file location to the appropriate place. mkdir -m 700 "$HOME"/Library/Logs LOGFILE="$HOME"/Library/Logs/iPodBackup.log date > "$LOGFILE" # Test to see if an iPod is connected. If yes, proceed. If not, quit. DESTINATION=`find /Volumes -name "iPod_Control" -maxdepth 2 | head -n 1` if [ -z "$DESTINATION" ] then echo "There is no iPod!" >> "$LOGFILE" exit 1 else mkdir -m 777 "$DESTINATION"/../Users DESTINATION="$DESTINATION"/../Users/"$USER" echo "Synching \"$HOME\" to \"$DESTINATION\"" >> "$LOGFILE" $1/Contents/Resources/rsync -av "$HOME"/ "$DESTINATION" \ --eahfs --delete \ --exclude-from $1/Contents/Resources/exclude.txt >> "$LOGFILE" echo "Sync completed successfully" >> "$LOGFILE" fi
Note: Do not copy and paste the above into your own script! I tried that, and the resulting file was completely fubar'd. This script doesn't erase anything, but if it goes haywire it could fill up your hard drive and make files in weird places. So instead of copying and pasting, download the script file if you're interested.
Note: As of version 1.0.1, iPodBackup does exactly what I originally intended for it to do. Versions higher than 1.0.x will focus on additional features. These features require significantly more complex code, so I won't be updating this walkthrough anymore. If, however, significant problems are found with 1.0.x, I will correct them in this walkthrough. I am no longer distributing 1.0.x, though.
Finishing touches
Again, assuming you've installed the RsyncX package, enter in the Terminal cp /usr/local/bin/rsync ~/Desktop to copy rsync to the desktop in preparation for inclusion in the app. Now we're ready to throw this mess through Platypus. Set it up as follows:
I like turning on "Require Administrator privileges" because I can prevent the sync from even starting, if I want to, by clicking the "Cancel" button on the authorization dialog. Now create the thing and put it somewhere smart, like on your iPod.
But wait! We never addressed the automation issue! Well, thanks to a very nifty shareware (US$6.95) app called Peripheral Vision, by Granted Software, this is a ridiculously easy matter. After registering, you can set Peripheral Vision to launch an app upon insertion of a device or mounting of a volume:
After the app launches, you will be prompted for your password. Enter it, and then open up iPodBackup.log to see the action.
You can also accomplish this kind of automation with iPod Launcher (US$4.95), or Do Something When (free). I have not used them, though, so I can't offer assistance with setting them up.
Note: The first time you backup it will take significantly longer than subsequent backups, since we've set rsync to only copy updated files.
If you'd like to adjust the exclude list, control-click on the iPodBackup app and choose "show package contents." exclude.txt is inside iPodBackup.app/Contents/Resources. You can also specify inclusions in this file; see man rsync for details.
And that's all she wrote. The script is completely general, except perhaps for the name of the target folder on the iPod. I don't think I can easily let the user choose that while retaining the automatic nature of the script, so I'm not going to bother.