diff options
| -rwxr-xr-x[-rw-r--r--] | rsyncshot | 47 | ||||
| -rw-r--r-- | rsyncshot.org | 114 |
2 files changed, 93 insertions, 68 deletions
diff --git a/rsyncshot b/rsyncshot index d12827e..5b01129 100644..100755 --- a/rsyncshot +++ b/rsyncshot @@ -1,4 +1,10 @@ #!/bin/bash +# rsyncshot + +# Craig Jennings craigmartinjennings@gmail.com +# Inspired by Mike Rubel: http://www.mikerubel.org/computers/rsync_snapshots/ + +# Debugging # uncomment next 4 lines for debugging output # exec 5> >(logger -t $0) @@ -6,6 +12,8 @@ # PS4='$LINENO: ' # set -x +# Default Locations For Setup + SCRIPTLOC=/usr/local/bin/rsyncshot; DESTINATION=/media/backup; @@ -15,10 +23,17 @@ LOGHOME=/var/log/rsyncshot.log; INCLUDES="$INSTALLHOME/include.txt"; EXCLUDES="$INSTALLHOME/exclude.txt"; +# Default Cron Job Entries +# CRON_H = hourly on minute 0 from 1am to 11pm +# CRON_D = daily at midnight, Monday - Saturday +# CRON_W = weekly at midnight on Sundays + CRON_H="0 1-23 * * * $SCRIPTLOC hourly 22"; CRON_D="0 0 * * 1-6 $SCRIPTLOC daily 6"; CRON_W="0 0 * * 7 $SCRIPTLOC weekly 51"; +# Help + help() { echo "" @@ -33,6 +48,8 @@ help() echo "Notes:" echo "- install and log locations defined in script." +# Error + error() { echo "ERROR: $0: $@" 1>&2; @@ -40,6 +57,8 @@ error() exit 1; } +# Setup + setup() { # copy this file to directory on path @@ -72,19 +91,31 @@ setup() echo "hourly, daily, and weekly cron jobs installed."; } +# Check Help Option + if [ "$TYPE" = "HELP" ]; then help; exit; fi +# Ensure Root + if [ "$EUID" -ne 0 ]; then error "This script must be run as root."; fi +# Display How Script Was Started + echo "rsyncshot invoked on `date -u` with: $0 $1 $2"; +# Parameter Validation + if ! [[ $1 =~ [a-zA-Z] ]]; then error "snapshot type not recognized."; fi TYPE=$(tr '[a-z]' '[A-Z]' <<< $1); if [ "$TYPE" = "SETUP" ]; then setup; exit; fi +# Validate Max Snapshots + if ! [[ $2 =~ [0-9] ]]; then error "max snapshots not a number."; fi MAX=$(($2-1)); +# Validate Include File (Source Directories) Exist + if [ ! -f "$INCLUDES" ]; then error "include file $INCLUDES not found."; fi SOURCES=$(<$INCLUDES); for SOURCE in $SOURCES @@ -92,8 +123,12 @@ do if [ ! -d "$SOURCE" ]; then error "source $SOURCE not found"; fi done +# Validate Exclude File (Exclusion Patterns) Exist + if [ ! -f "$EXCLUDES" ]; then error "Exclude file $EXCLUDES not found."; fi +# Sync Each Directory In Turn + for SOURCE in $SOURCES do rsync -avh -i --times \ @@ -102,10 +137,14 @@ do --update $SOURCE $DESTINATION/latest ; done +# Delete Max+1 Snapshot If Exists + if [ -d $DESTINATION/$TYPE.$MAX ]; then rm -rf $DESTINATION/$TYPE.$MAX; fi +# Rotate Snapshots Descending + for (( start=$(($MAX)); start>=0; start--)); do end=$(($start+1)); if [ -d $DESTINATION/$TYPE.$start ]; then @@ -113,11 +152,19 @@ for (( start=$(($MAX)); start>=0; start--)); do fi done +# Reset Directory Timestamp + touch $DESTINATION/latest +# Hard Link Only Copy to Destination + cp -al $DESTINATION/latest $DESTINATION/$TYPE.0; +# Make Directory Type Read-Only + chmod -w $DESTINATION/$TYPE.0 +# Print Time and Exit + echo "rsyncshot completed `date -u` "; exit 0; diff --git a/rsyncshot.org b/rsyncshot.org index 77797a2..830654b 100644 --- a/rsyncshot.org +++ b/rsyncshot.org @@ -1,25 +1,18 @@ * rsyncshot -:PROPERTIES: -:COMMENTS: org -:TANGLE: rsyncshot -:END: -Craig Jennings craigmartinjennings@gmail.com -Inspired by Mike Rubel: http://www.mikerubel.org/computers/rsync_snapshots/ -#+begin_SRC sh :tangle yes -#!/bin/bash -#+end_SRC -** Debugging -#+begin_SRC sh :tangle yes +#+begin_SRC sh :tangle rsyncshot :comments org :shebang "#!/bin/bash" +# Craig Jennings craigmartinjennings@gmail.com +# Inspired by Mike Rubel: http://www.mikerubel.org/computers/rsync_snapshots/ +#+end_SRC +* Debugging +#+begin_SRC sh :tangle rsyncshot :comments org # uncomment next 4 lines for debugging output # exec 5> >(logger -t $0) # BASH_XTRACEFD="5" # PS4='$LINENO: ' # set -x #+end_SRC -** Config -*** Default Locations -Default locations for setup -#+begin_SRC sh :tangle yes +* Default Locations For Setup +#+begin_SRC sh :tangle rsyncshot :comments org SCRIPTLOC=/usr/local/bin/rsyncshot; DESTINATION=/media/backup; @@ -29,19 +22,18 @@ LOGHOME=/var/log/rsyncshot.log; INCLUDES="$INSTALLHOME/include.txt"; EXCLUDES="$INSTALLHOME/exclude.txt"; #+end_SRC -*** Default Cron Job Entries -Default cron job entries +* Default Cron Job Entries CRON_H = hourly on minute 0 from 1am to 11pm CRON_D = daily at midnight, Monday - Saturday CRON_W = weekly at midnight on Sundays -#+begin_SRC sh :tangle yes +#+begin_SRC sh :tangle rsyncshot :comments org CRON_H="0 1-23 * * * $SCRIPTLOC hourly 22"; CRON_D="0 0 * * 1-6 $SCRIPTLOC daily 6"; CRON_W="0 0 * * 7 $SCRIPTLOC weekly 51"; #+end_SRC -** Functions -*** Help -#+begin_SRC sh :tangle yes +* Functions +** Help +#+begin_SRC sh :tangle rsyncshot :comments org help() { echo "" @@ -57,8 +49,8 @@ help() echo "- install and log locations defined in script." #+end_SRC -*** Error -#+begin_SRC sh :tangle yes +** Error +#+begin_SRC sh :tangle rsyncshot :comments org error() { echo "ERROR: $0: $@" 1>&2; @@ -66,8 +58,8 @@ error() exit 1; } #+end_SRC -*** Setup -#+begin_SRC sh :tangle yes +** Setup +#+begin_SRC sh :tangle rsyncshot :comments org setup() { # copy this file to directory on path @@ -100,38 +92,32 @@ setup() echo "hourly, daily, and weekly cron jobs installed."; } #+end_SRC -** Script -*** Check Help Option -If user requested help, display and exit -#+begin_SRC sh :tangle yes + +* Check Help Option +#+begin_SRC sh :tangle rsyncshot :comments org if [ "$TYPE" = "HELP" ]; then help; exit; fi #+end_SRC -*** Ensure Root -Ensure we're running as root -#+begin_SRC sh :tangle yes +* Ensure Root +#+begin_SRC sh :tangle rsyncshot :comments org if [ "$EUID" -ne 0 ]; then error "This script must be run as root."; fi #+end_SRC -*** Display How Script Was Started -Display how the script was started -#+begin_SRC sh :tangle yes +* Display How Script Was Started +#+begin_SRC sh :tangle rsyncshot :comments org echo "rsyncshot invoked on `date -u` with: $0 $1 $2"; #+end_SRC -*** Parameter Validation -Validate first arg is alpha chars and make it case insensitive. If user requested setup, call function and exit. -#+begin_SRC sh :tangle yes +* Parameter Validation +#+begin_SRC sh :tangle rsyncshot :comments org if ! [[ $1 =~ [a-zA-Z] ]]; then error "snapshot type not recognized."; fi TYPE=$(tr '[a-z]' '[A-Z]' <<< $1); if [ "$TYPE" = "SETUP" ]; then setup; exit; fi #+end_SRC -*** Validate Max Snapshots -Validate second arg (max snapshots) is numeric. -#+begin_SRC sh :tangle yes +* Validate Max Snapshots +#+begin_SRC sh :tangle rsyncshot :comments org if ! [[ $2 =~ [0-9] ]]; then error "max snapshots not a number."; fi MAX=$(($2-1)); #+end_SRC -*** Validate Include File -Validate include file (source directories) -#+begin_SRC sh :tangle yes +* Validate Include File (Source Directories) Exist +#+begin_SRC sh :tangle rsyncshot :comments org if [ ! -f "$INCLUDES" ]; then error "include file $INCLUDES not found."; fi SOURCES=$(<$INCLUDES); for SOURCE in $SOURCES @@ -139,15 +125,13 @@ do if [ ! -d "$SOURCE" ]; then error "source $SOURCE not found"; fi done #+end_SRC -*** Validate Exclude File -Validate exclude file (exclusion patterns) -#+begin_SRC sh :tangle yes +* Validate Exclude File (Exclusion Patterns) Exist +#+begin_SRC sh :tangle rsyncshot :comments org if [ ! -f "$EXCLUDES" ]; then error "Exclude file $EXCLUDES not found."; fi #+end_SRC -*** Sync -Perform the sync, each directory in turn -#+begin_SRC sh :tangle yes +* Sync Each Directory In Turn +#+begin_SRC sh :tangle rsyncshot :comments org for SOURCE in $SOURCES do rsync -avh -i --times \ @@ -156,16 +140,14 @@ do --update $SOURCE $DESTINATION/latest ; done #+end_SRC -*** Delete Old Snapshot -Delete the snapshot to go over max -#+begin_SRC sh :tangle yes +* Delete Max+1 Snapshot If Exists +#+begin_SRC sh :tangle rsyncshot :comments org if [ -d $DESTINATION/$TYPE.$MAX ]; then rm -rf $DESTINATION/$TYPE.$MAX; fi #+end_SRC -*** Rotate Snapshots -Rotate snapshots descending -#+begin_SRC sh :tangle yes +* Rotate Snapshots Descending +#+begin_SRC sh :tangle rsyncshot :comments org for (( start=$(($MAX)); start>=0; start--)); do end=$(($start+1)); if [ -d $DESTINATION/$TYPE.$start ]; then @@ -173,24 +155,20 @@ for (( start=$(($MAX)); start>=0; start--)); do fi done #+end_SRC -*** Set Directory Timestamp -Touch the directory for a timestamp -#+begin_SRC sh :tangle yes +* Reset Directory Timestamp +#+begin_SRC sh :tangle rsyncshot :comments org touch $DESTINATION/latest #+end_SRC -*** Hard Link to Destination -Make a hard-link-only copy into $TYPE.0 -#+begin_SRC sh :tangle yes +* Hard Link Only Copy to Destination +#+begin_SRC sh :tangle rsyncshot :comments org cp -al $DESTINATION/latest $DESTINATION/$TYPE.0; #+end_SRC -*** Make Directory Type Read-Only -Make the directory $TYPE.0 read-only -#+begin_SRC sh :tangle yes +* Make Directory Type Read-Only +#+begin_SRC sh :tangle rsyncshot :comments org chmod -w $DESTINATION/$TYPE.0 #+end_SRC -*** Print Time and Exit -Print end time and exit -#+begin_SRC sh :tangle yes +* Print Time and Exit +#+begin_SRC sh :tangle rsyncshot :comments org echo "rsyncshot completed `date -u` "; exit 0; #+end_SRC |
