RSyncSnapshot

Download File
#!/bin/bash
#
# Copyright (C) 2009-2011 Stonyx
# http://www.stonyx.com
#
# This script is free software. You can redistribute it and/or modify it
# under the terms of the GNU General Public License Version 2 (or at your
# option any later version) as published by The Free Software Foundation.
#
# This script is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# If you did not received a copy of the GNU General Public License along
# with this script see http://www.gnu.org/copyleft/gpl.html or write to
# The Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.

# ------------------------------
# RSnapshot type script
# ------------------------------
# Usage: RSyncSnapshot Source_Directory Target_Directory Number_of_Snapshots_to_Keep
#           Log_File

# ----- Modifiable RSync Options -----

# Explanation of default options
# a = archive mode; equals rlptgoD
#    r = recurse into directories
#    l = copy symlinks as symlinks
#    p = preserve permissions
#    t = preserve modification times
#    g = preserve group
#    o = preserve owner
#    D = preserve device and special files
# c = skip based on checksum, not mod-time & size
# v = increase verbosity
# x = don't cross filesystem boundaries
# H = preserve hard links
# S = handle sparse files efficiently
RSYNC_OPTIONS='-acvxHS'

# ----- Prepare Commands -----

# Uncomment the following section if running on FreeNAS and comment the next section
ECHO=/bin/echo
TEST=/bin/[
SLEEP=/bin/sleep
RM=/bin/rm
EXPR=/bin/expr
MV=/bin/mv
RSYNC=/usr/local/bin/rsync
TOUCH=/usr/bin/touch

# Uncomment the following section if running on Ubuntu and comment the previous section
# ECHO=/bin/echo
# TEST=[
# SLEEP=/bin/sleep
# RM=/bin/rm
# EXPR=/usr/bin/expr
# MV=/bin/mv
# RSYNC=/usr/bin/rsync
# TOUCH=/bin/touch

# ----- Initialize Variables -----

SOURCE=$1
TARGET=$2
SNAPSHOT_COUNT=$3
LOG_FILE=$4
LOCK_FILE='/mnt/.RSyncSnapshotLock'
RSYNC_LOG_FILE="--log-file=$LOG_FILE"
RSYNC_LINK_TARGET=

# ----- Script -----

# make things a bit more readable when we start echoing stuff
$ECHO

# make sure passed variables make sense
if $TEST ! "$SOURCE" ]
then
   $ECHO "Usage: RSyncSnapshot Source_Directory Target_Directory"
   $ECHO "   Number_of_Snapshots_to_Keep Optional_RSync_Option"
   $ECHO "   Optional_RSync_Option Optional_RSync_Option"
   $ECHO
   $ECHO "No source directory specified."
   $ECHO
   exit 1
fi
if $TEST ! -d "$SOURCE" ]
then
   $ECHO "Specified source directory (\"$SOURCE\") doesn't exist."
   $ECHO
   exit 1
fi

if $TEST ! "$TARGET" ]
then
   $ECHO "Usage: RSyncSnapshot Source_Directory Target_Directory"
   $ECHO "   Number_of_Snapshots_to_Keep Optional_RSync_Option"
   $ECHO "   Optional_RSync_Option Optional_RSync_Option"
   $ECHO
   $ECHO "No target directory specified."
   $ECHO
   exit 1
fi
if $TEST ! -d "$TARGET" ]
then
   $ECHO "Specified target directory (\"$TARGET\") doesn't exist."
   $ECHO
   exit 1
fi

if $TEST ! $SNAPSHOT_COUNT ]
then
   $ECHO "Usage: RSyncSnapshot Source_Directory Target_Directory"
   $ECHO "   Number_of_Snapshots_to_Keep Optional_RSync_Option"
   $ECHO "   Optional_RSync_Option Optional_RSync_Option"
   $ECHO
   $ECHO "Number of snapshots to keep not specified."
   $ECHO
   exit 1
fi
if $TEST "`$EXPR $SNAPSHOT_COUNT - $SNAPSHOT_COUNT 2> /dev/null`" != "0" ]
then
   $ECHO "Specified number of snapshots to keep has to be a numeric value."
   $ECHO
   exit 1
fi
if $TEST $SNAPSHOT_COUNT -lt 1 ]
then
   $ECHO "Specified number of snapshots to keep can not be less than one."
   $ECHO
   exit 1
fi

# make sure no other instance of this script is running
(set noclobber; : > "$LOCK_FILE") 2> /dev/null
if $TEST $? != "0" ]
then
   $ECHO "Another instance of the RSyncSnapshot script is already running."
   $ECHO "Press Ctrl+C to cancel this script or if no other copy of RSnapshot"
   $ECHO "is actually running delete the \"$LOCK_FILE\" file."
   $ECHO
   $ECHO -n "Waiting for the other instance of RSyncSnapshot to finish ..."
   $ECHO "RSyncSnapshot: Waiting for another instance of RSyncSnapshot to finish." \
      >> "$LOG_FILE"

   # check every 15 seconds if the other instance is done
   $SLEEP 15
   (set noclobber; : > "$LOCK_FILE") 2> /dev/null
   while $TEST $? != "0" ]
   do
      $ECHO -n "."
      $SLEEP 15
      (set noclobber; : > "$LOCK_FILE") 2> /dev/null
   done

   # make things pretty
   $ECHO
   $ECHO
fi

# delete oldest snapshot if it exists
if $TEST -e "$TARGET/Snapshot.$SNAPSHOT_COUNT" ]
then
   $ECHO "Removing oldest snapshot (number $SNAPSHOT_COUNT) ..."
   $ECHO "RSyncSnapshot: Removing oldest snapshot (number $SNAPSHOT_COUNT)." \
      >> "$LOG_FILE"
   $RM -rf "$TARGET/Snapshot.$SNAPSHOT_COUNT"
else
   $ECHO "Oldest snapshot (number $SNAPSHOT_COUNT) doesn't exist."
   $ECHO "RSyncSnapshot: Oldest snapshot (number $SNAPSHOT_COUNT) doesn't exist." \
      >> "$LOG_FILE"
fi

# make each snapshot one snapshot older
while $TEST $SNAPSHOT_COUNT -gt 0 ]
do
   # reduce SNAPSHOP_COUNT by 1 (since we've already dealt with one snapshot above)
   let SNAPSHOT_COUNT=$SNAPSHOT_COUNT-1

   # check if the snapshot might be a file instead of a directory since
   # this happens when rsync failed to run correctly the last time
   if $TEST -f "$TARGET/Snapshot.$SNAPSHOT_COUNT" ]
   then
      $ECHO "Removing invalid snapshot (number $SNAPSHOT_COUNT) ..."
      $ECHO "RSyncSnapshot: Removing invalid snapshot (number $SNAPSHOT_COUNT)." \
         >> "$LOG_FILE"
      $RM -f "$TARGET/Snapshot.$SNAPSHOT_COUNT"
   else
      # check if it's a directory
      if $TEST -d "$TARGET/Snapshot.$SNAPSHOT_COUNT" ]
      then
         $ECHO "Moving snapshot number $SNAPSHOT_COUNT to `$EXPR $SNAPSHOT_COUNT + 1` ..."
         $ECHO "RSyncSnapshot: Moving snapshot number $SNAPSHOT_COUNT to `$EXPR \
            $SNAPSHOT_COUNT + 1`." \
            >> "$LOG_FILE"
         $MV "$TARGET/Snapshot.$SNAPSHOT_COUNT" "$TARGET/Snapshot.`$EXPR $SNAPSHOT_COUNT + 1`"
      # do the following if it doesn't exist at all
      else
         $ECHO "Snapshot number $SNAPSHOT_COUNT doesn't exist."
         $ECHO "RSyncSnapshot: Snapshot number $SNAPSHOT_COUNT doesn't exist." \
            >> "$LOG_FILE"
      fi
   fi
done

# see if the directory we wanna link to exists already
if $TEST -d "$TARGET/Snapshot.1" ]
then
   RSYNC_LINK_TARGET='--link-dest=../Snapshot.1'
fi

# do the actual backup using rsync
$ECHO "Backing up \"$SOURCE\" to snapshot number 0 using the command below ..."
$ECHO "$RSYNC $RSYNC_OPTIONS $RSYNC_LINK_TARGET \\"
$ECHO "\"$RSYNC_LOG_FILE\" \"$SOURCE\" \"$TARGET/Snapshot.0\""
$ECHO
$ECHO "RSyncSnapshot: Backing up \"$SOURCE\" to snapshot number 0 using the command below:" \
   >> "$LOG_FILE"
$ECHO "RSyncSnapshot: $RSYNC $RSYNC_OPTIONS $RSYNC_LINK_TARGET \\" \
   >> "$LOG_FILE"
$ECHO "RSyncSnapshot: \"$RSYNC_LOG_FILE\" \"$SOURCE\" \"$TARGET/Snapshot.0\"" \
   >> "$LOG_FILE"
$RSYNC $RSYNC_OPTIONS $RSYNC_LINK_TARGET "$RSYNC_LOG_FILE" "$SOURCE" "$TARGET/Snapshot.0"

# update the time of the snapshot directory
$TOUCH "$TARGET/Snapshot.0"

# remove lock file
$RM -f "$LOCK_FILE"

# make things pretty
$ECHO

# all done
exit 0