Mon, 19 Jan 2009
automatic backups on a removable device
These scripts allow automatically running rsnapshot when the right USB hard disk is plugged in.
If you do not want so much automation then just let gnome-volume-manager do its work and then run the last script from the command line.
/etc/udev/rules.d/68-automount.rules:
ACTION=="add", SUBSYSTEM=="block", \
ENV{ID_FS_USAGE}=="filesystem", ENV{ID_FS_LABEL}=="red", \
RUN+="backup.agent $env{ID_FS_LABEL}"
/lib/udev/backup.agent:
#!/bin/sh -e
##############################################################################
wait_for_fs() {
local fs_path="$1"
local count="$2"
if [ -z "$count" ]; then count=60; fi
while ! mountpoint -q $fs_path; do
sleep 1
count=$(($count - 1))
if [ $count -eq 0 ]; then
echo "$fs_path has not been mounted after $count seconds!" 1>&2
return 1
fi
done
return 0
}
##############################################################################
do_stuff() {
if [ -e /tmp/nobackup ]; then exit 0; fi
wait_for_fs $FSPATH || exit 1
nice -n 19 ionice -c 3 sh $FSPATH/doit
rsnapshot -v diff daily.0 daily.1 | mail -s "rsnapshot diff" md
if [ -e /tmp/noumount ]; then exit 0; fi
pumount $FSPATH
}
##############################################################################
if [ ! -t 0 ]; then
exec >> /var/log/backup.log 2>&1
fi
if [ "$1" ]; then
FSNAME="$1"
else
echo "Usage: $0 name"
exit 1
fi
FSPATH="/media/$FSNAME"
# the script may be called multiple times due to multiple change events
# being fired by the kernel
if mountpoint -q $FSPATH; then exit 0; fi
# daemonize
do_stuff &
A while loop which waits for the file system to appear is not elegant, but you cannot do this the right way™ with only a shell script because it requires watching /proc/mounts using inotify(3).
/media/red/doit:
#!/bin/sh -e
##############################################################################
file_older_than_days() {
local filename="$1"
local days="$2"
# consider a day passed if it will end in at most 6 hours
local more=$((60 * 60 * 6))
local now=$(date +%s)
local filetime=$(stat --format=%Y "$filename" 2> /dev/null || echo 0)
local age=$(($now - $filetime))
if [ "$age" -gt $((60 * 60 * 24 * $days - $more)) ]; then
return 0
else
return 1
fi
}
##############################################################################
RSNAPSHOT_ROOT=$(awk '/^snapshot_root[\t ]/ { print $2 }' /etc/rsnapshot.conf)
if [ -z "$RSNAPSHOT_ROOT" ]; then
echo "Cannot determine snapshot_root!"
exit 1
elif [ ! -d "$RSNAPSHOT_ROOT" ]; then
echo "snapshot_root is not a directory: $RSNAPSHOT_ROOT"
exit 1
fi
if file_older_than_days "$RSNAPSHOT_ROOT/daily.0/" 1; then
if file_older_than_days "$RSNAPSHOT_ROOT/weekly.0/" 14; then
if file_older_than_days "$RSNAPSHOT_ROOT/monthly.0/" 60; then
/usr/bin/rsnapshot monthly
fi
/usr/bin/rsnapshot weekly
fi
/usr/bin/rsnapshot daily
fi