A backup volume switcher for Apple’s TimeMachine

Posted by: gdelmatto  :  Category: Operating Systems, OS X, Programming, Shells

So here’s another piece of code I hacked up tonight.
Since I’m roaming around with my MacBook every now and then, the need arised, that I would need to switch my TimeMachine destination volumes based on location.

So while in the office, I’d like to backup to my external USB drive there.
Being at my home office, i’d like to backup to my NAS, while on the road, I’d love to habe my external mobile drive to kick in (and yes, I know about the “mobile backup feature” of OS X Lion, but that’s not the point …)

So I hacked up a script, which will check if a TimeMachine volume is actually connected or not. If none is connected, it will check which targets are available by either looking for connected USB drives (by the use of “diskutil”) or by checking connectivity to specified NAS servers. If it sees necessity to switch volumes it will also force creating an immediate backup in that case.

So, below is the code. I hope the comment section is clear enough. You’d need to change the USB_Volumes and NET_Volumes sections to fit your needs.
And yes, it works with the TimeMachine mobile feature enabled or disabled, that makes no difference.

#!/bin/bash
#
# tm_switcher -- a backup volume switchr for Apple's TimeMachine
#
# ####################################################################
# This script will help to automatically switch backup destination
# on Apple's TimeMachine according to available destination volumes.
# You can have one or more of both USB and network devices, of which
# the first available destination is used
# ####################################################################
# released to the public "as-is" under the terms of the GPL Version 2
# ####################################################################
# r0.1 2012/01/25
# - initial release 

# specify zero or more USB volumes as backup destinations
# you must give only the volume label
# specify multiple volumes like this:
# USB_Volumes=("Volume1" "Volume2")
#
USB_Volumes=("My Passport")

# specify zero or more NETWORK volumes as backup destinations
# you must give the full AFP volume path
# specify multiple volumes identically as described
# in the "USB Volumes" section above
#
NET_Volumes=("afp://TMUsername:TMPassword@HOSTorIPAddress/TMShare")

# get current TimeMachine directory
#
current_tm_dir=`/usr/bin/tmutil machinedirectory`

# check if we are disconnected
#
[ "$current_tm_dir" == "" ] && tm_status=CONNECT_NONE

# check if we are connected to an USB disk
#
for volume in "${USB_Volumes[@]}"; do
	echo $current_tm_dir | grep -e "$volume" > /dev/null 2>&1
	[ "$?" == "0" ] && tm_status=CONNECT_USB 
done

# check if we are connected to an Network Volume (TimeCapsule) 
#
for volume in "${NET_Volumes[@]}"; do
	_volume_basename=`basename ${volume}`
	/sbin/mount | grep " on /Volumes/${_volume_basename}" > /dev/null 2>&1	
	[ "$?" == "0" ] && tm_status=CONNECT_NET 
done


# check if we need to switch TimeMachine locations
#
if [ "${tm_status}" == "CONNECT_NONE" ]; then
	echo "NOTICE: TimeMachine is disconnected, current status is: ${tm_status}"

	# check if we can reach any of the NET_Volumes hosts given
	#
	for volume in "${NET_Volumes[@]}"; do
		_dest_server=`echo $volume | grep -P -o '@([[:digit:]]{1,3}\.[[:digit:]]{1,3}\.[[:digit:]]{1,3}\.[[:digit:]]{1,3}|(\b.*\b\.?)+)/' | sed -E 's:(@|/)::g'`
 
		# always recheck that TimeMachine is not yet connected
		#
		if [ "${tm_status}" == "CONNECT_NONE" ]; then
			# perform a simple connectivity test
			ping -c 1 $_dest_server > /dev/null 2>&1
			if [ "$?" == "0" ]; then
				echo "NOTICE: We found this volume to be available for backups: $volume"
				echo "        We will be switching TimeMachine volumes now ..."
				sudo tmutil setdestination "$volume" 
				tm_status=CONNECT_NET
			fi
		fi
	done


	# check if we can reach any of the NET_Volumes hosts given
	#
	for volume in "${USB_Volumes[@]}"; do
		# always recheck that TimeMachine is not yet connected
		#
		if [ "${tm_status}" == "CONNECT_NONE" ]; then
			# check if the volume is available 
			diskutil info "$volume" > /dev/null 2>&1
			if [ "$?" == "0" ]; then
				# try to mount the volume
				diskutil mount "/Volumes/$volume" > /dev/null 2>&1
				if [ "$?" == "0" ]; then	
					sleep 5
					echo "NOTICE: We found this volume to be available for backups: $volume"
					echo "        We will be switching TimeMachine volumes now ..."
					sudo tmutil setdestination "/Volumes/$volume" 
					tm_status=CONNECT_USB
				fi
			fi
		fi
	done


	# bail out if we failed on finding a proper backup volume
	#
	if [ "${tm_status}" == "CONNECT_NONE" ]; then
		echo "ERROR: Failed in finding a TimeMachine Voloume on USB or on the LAN available for backups."
		echo "       Exiting now."
		exit
	else
		echo "NOTICE: A suitable backup volume has been located for TimeMachine backups."
		echo "        Starting backup now ..."
		tmutil startbackup
	fi
else
	if [ "${tm_status}" == "CONNECT_USB" -o "${tm_status}" == "CONNECT_NET" ]; then
		echo "NOTICE: TimeMachine is already connected, current status is: ${tm_status}"
		echo "        TimeMachine destinations will not be switched!"
	else
		echo "ERROR: Unable to figure current TimeMachine status."
	fi
fi

exit

And here’s the script in action:

To complement this it’s a great idea to add this to Launch Daemon to have this script run on a recurring basis, so you wont need to run it manually.

Comments are closed.