#!/bin/bash # ---------------------------------------------------------------------- # e-head's backup script # (adapted from mikes handy rotating-filesystem-snapshot utility) # ---------------------------------------------------------------------- # intended to be run as a cron job # # ------------- defaults (edit this) ----------------------------------------- # if you feel safer "hardcoding" the source and snapshot directories do it here. # then just comment out the relavant sections of the while getopts loop below and # also comment out the error checking section # source directory, e.g. # SRC=/home/ # dest (snapshot location), e.g. # SNAPSHOTS=/mnt/hdb2/scratch/bak/prague/home # SNAPSHOTS=username@remotemachine.com:/path/to/destination/ # exclude these files EXCLUDES= # number of backups NUMBK=4 # confirm ? you will be prompted by default. y=false # remote shell to be used if the SRC is on another computer RSH= # ------------- commands used by this script -------------------- # I have no idea why everybody wacks the path in scripts like this ... # but mikes original did this, so I guess I will too ... unset PATH ID=/usr/bin/id ECHO=/bin/echo MOUNT=/bin/mount RM=/bin/rm MV=/bin/mv CP=/bin/cp TOUCH=/bin/touch RSYNC=/usr/bin/rsync EXPR=/usr/bin/expr MKDIR=/bin/mkdir # -------------- process options --------------------------------- while getopts yhm:s:d:n:x:e: opt do case "$opt" in s) SRC=${OPTARG%/}/;; d) SNAPSHOTS=$OPTARG;; n) NUMBK=$OPTARG;; e) RSH="--rsh=$OPTARG";; x) EXCLUDES="--exclude-from=$OPTARG";; y) y=true;; h | *) # unknown flag echo >&2 \ "usage: $0 [-hy] -s source -d snapshot_directory [-e remote_shell] [-n number_of_snapshots] [-x exclude_file]" exit 1;; esac done shift $(($OPTIND - 1)) # ------------- simple error checking ----------------------------- [ "$SRC" = "" ] || [ "$SNAPSHOTS" = "" ] || \ [ ! -d "$SNAPSHOTS" ] && { echo "usage: $0 [-hy] -s source -d snapshot_directory [-e remote_shell] [-n number_of_snapshots] [-x exclude_file]" exit 1 } [ "$y" = "false" ] && { echo -n \ " source $SRC snapshot dir $SNAPSHOTS # snapshots $NUMBK exclude switch $EXCLUDES does this look right (Y/n)? " read answer case "$answer" in n | N ) exit 0;; y | Y | * ) echo "let's go !";; esac } # ------------- back it up ! -------------------------------------- NUMBK=$($EXPR $NUMBK - 1) X=$($EXPR $NUMBK - 1) # make sure we're running as root if (( `$ID -u` != 0 )); then { $ECHO "Sorry, must be root. Exiting..."; exit; } fi # step 1: delete the oldest snapshot, if it exists: if [ -d "$SNAPSHOTS/snapshot.$NUMBK" ]; then $RM -rf "$SNAPSHOTS/snapshot.$NUMBK" fi # step 2: shift the middle snapshots(s) back by one, if they exist while [ "$X" -gt 0 ] do if [ -d "$SNAPSHOTS/snapshot.$X" ]; then $MV "$SNAPSHOTS/snapshot.$X" "$SNAPSHOTS/snapshot.$($EXPR $X + 1)" fi X=$(($X-1)) done # step 3: make a hard-link-only (except for dirs) copy of # snapshot.0, assuming that exists, into snapshot.1 if [ -d "$SNAPSHOTS/snapshot.0" ]; then $CP -al "$SNAPSHOTS/snapshot.0" "$SNAPSHOTS/snapshot.1" fi # step 4: rsync from the system into the latest snapshot (notice that # rsync behaves like cp --remove-destination by default, so the destination # is unlinked first. If it were not so, this would copy over the other # snapshot(s) too! [ ! -d "$SNAPSHOTS/snapshot.0" ] && $MKDIR "$SNAPSHOTS/snapshot.0" $RSYNC \ -va --delete $RSH $EXCLUDES \ "$SRC" "$SNAPSHOTS/snapshot.0" # step 5: update the mtime of hourly.0 to reflect the snapshot time $TOUCH "$SNAPSHOTS/snapshot.0"