Backing up Mac OS X to a Windows machine

I know there’s plenty of backing up/synchronizing software for Mac. I know Time Machine is awesome. But none of the software I tried was able to do it my way :). Thus, as I have a Linux/FreeBSD background, I wanted to do it my way and the command line solution seamed to be a good one.

My office Windows machine has plenty of disk space and is rarely used. It was an ideal system (with not so ideal file system) to do backups on.

Prerequisites: 

  • First I needed to share a folder on my Windows machine to my Mac laptop. Here’s a nice tutorial on how to do this: How to mount a Windows shared folder on your Mac.
  • Next I needed the good backup software. I am fairly familiar with rdiff-backup. It’s doing incremental backups, it’s easy to use and it does its job really well. To install rdiff-backup I used MacPorts. Follow this guide to install MacPorts. It’s easy as installing every mac software.
  • I also wanted a notification window to pop up each time it tried to do a backup and informing me if it succeeded or not. I used growlnotify. Needless to say you have to install Growl as well.

1. step: install rdiff-backup

First I needed to install rdiff-backup:

$ sudo port install rdiff-backup

2. step: install growlnotify

Next I had to install growlnotify (moving into a folder of a mounted Growl-x.x.dmg image)

$ cd /Volumes/Growl-1.2/Extras/growlnotify/
$ ./install.sh

3. step: check local IP address

Then I had to check my IP to see if I am connected to my office network (wired network interface en0). This could be probably done in a simpler way but I came up with this idea in seconds (all of this could be done with awk or sed :=)) and is funny to see so many pipes.

$ ifconfig | sed -n '/en0/,/media/p' | grep -v inet6 | grep inet | awk '{print $2}'  

             I had few seconds and here’s a simpler version:

        $ ifconfig | awk '/en0/,/ether/ {if($1 == "inet") {print $2}}'

4. step: ping the Windows PC

Then I needed to check if my Windows machine was on and listening (only one ping should be enough):

$ ping -c 1 148.88.226.250

5. step: mount a shared samba folder

Then I checked if I could mount my Windows shared folder (samba). To
do this I wanted to use Mac OS X keychain for for password so it would not be written in my bash script. My shared folder is smb://mkljun@MKLJUNDESKTOP/mkljunHome where mkljun is my username, MKLJUNDESKTOP my Windows machine name (IP address should do the same) and mkljunHome my shared folder.

$ osascript -e "try" -e "mount volume "smb://mkljun@MKLJUNDESKTOP/mkljunHome"" -e "end try"

This command would on other systems look something like like:
$ mount -t smbfs //mkljundesktop/mkljunHome /Volumes/mkljunHome -o username=mkljun, password=XXX
.

6. step: rdiff-backup

Then I checked if rdiff-backup is working properly (backing up my home folder on Mac OS X to a folder on the Windows machine).

$ rdiff-backup /Users/mkljun /Volumes/mkljunHome/BackupMac

7. step: growlnotify

A check if growlnotify is working.

$ growlnotify -t "backup" -m "Your IP is XXX but backup computer YYY could not be reached"

8. step : The Script

And I was ready to write a bash script

#!/bin/bash
# my backup script in /Users/mkljun/backup.sh

DestinationIP="148.88.226.250"
SourceIP=`ifconfig | sed -n '/en0/,/media/p' | grep -v inet6 | grep inet | awk '{print $2}'`

# ping destination (backup) ip address; output redirected to /dev/null
ping -c 1 $DestinationIP > /dev/null
# check if ping successful
if [ $? != 0 ]
then
    # if ping not successful, print notification (echo should be in one line!!)
    echo -e "-t 'Backup failed' -m 'Your IP is $SourceIP. Backup PC $DestinationIP 
                could not be reached'" | xargs /usr/local/bin/growlnotify
else
    # mount samba drive (osascript should be in one line)
    osascript -e "try" -e "mount volume "smb://mkljun@MKLJUNDESKTOP/mkljunHome"" 
                 -e "end try" > /dev/null
    # check if mount was successful
    if [ $? == 0 ]; then
      # this echo should be in one line with a pipe
      echo "-t 'Backup' -m 'Your IP is $SourceIP. Starting backing up now'" 
                    | xargs /usr/local/bin/growlnotify
      rdiff-backup /Users/mkljun /Volumes/mkljunHome/BackupMac > /dev/null
      # check if rdiff-backup successful
      if [ $? == 0 ]; then
        echo "-t 'Backup' -m 'Backup completed successfully'" | xargs /usr/local/bin/growlnotify
      else
        echo "-t 'Backup failed' -m 'Something was wrong'" | xargs /usr/local/bin/growlnotify
      fi
      # delete all backups older than 2 weeks
      rdiff-backup --remove-older-than 2W /Volumes/mkljunHome/BackupMac > /dev/null
    fi
fi

It is probably not the nicest script out there but its working. I could and probably should redirect standard outputs to a log file (with a day as a title) to to check it once in a while to see if the script is working (and even check if the file grew too big and delete it if necessary). But so far it works flawlessly for few days.

9. step: cron

The last thing to do was setting up a cron job so the script would run without my intervention. Let’s say I want to run it every week day at 10AM when I’m probably in the office (there are two stars between 10 and 1-5 which are not showing up for some reason ‘0 10 * * 1-5’).

$ crontab -e
0 10 * * 1-5 /Users/mkljun/backup.sh

10. the last version of a script

UPDATE:
Here is the last version with logging to a file and all variables at the beginning of the script.