diff options
| author | Craig Jennings <cjennings@citizen.lan> | 2021-04-04 15:12:34 -0500 |
|---|---|---|
| committer | Craig Jennings <cjennings@citizen.lan> | 2021-04-04 15:12:34 -0500 |
| commit | 8143a7e6890b3127968a45b839bcd1bc1b33cc8a (patch) | |
| tree | 8a03f5b5d55d84915740f3d2fec539ec02bc9671 | |
| parent | cb1c45a7cc4626ff88b84075916e3e2ba878aaff (diff) | |
Made rsyncshot "literate" (rsyncshot.org).
Comments coming back in once I get org properties working as expected.
| -rw-r--r--[-rwxr-xr-x] | rsyncshot | 35 | ||||
| -rw-r--r-- | rsyncshot.org | 196 |
2 files changed, 199 insertions, 32 deletions
diff --git a/rsyncshot b/rsyncshot index 7a4d0b5..d12827e 100755..100644 --- a/rsyncshot +++ b/rsyncshot @@ -1,8 +1,4 @@ #!/bin/bash -# -# rsyncshot -# Craig Jennings craigmartinjennings@gmail.com -# Inspired by Mike Rubel: http://www.mikerubel.org/computers/rsync_snapshots/ # uncomment next 4 lines for debugging output # exec 5> >(logger -t $0) @@ -10,7 +6,6 @@ # PS4='$LINENO: ' # set -x -# default locations for setup SCRIPTLOC=/usr/local/bin/rsyncshot; DESTINATION=/media/backup; @@ -20,28 +15,24 @@ LOGHOME=/var/log/rsyncshot.log; INCLUDES="$INSTALLHOME/include.txt"; EXCLUDES="$INSTALLHOME/exclude.txt"; -# default cron job entries -# hourly -- hourly on minute 0 from 1am to 11pm CRON_H="0 1-23 * * * $SCRIPTLOC hourly 22"; -# daily -- midnight, Monday - Saturday. CRON_D="0 0 * * 1-6 $SCRIPTLOC daily 6"; -# weekly -- Sundays at midnight CRON_W="0 0 * * 7 $SCRIPTLOC weekly 51"; -# print help help() { echo "" echo "rsyncshot - compact snapshots on Linux using rsync and hard links." + echo "" + echo "rsyncshot must be run as root" + echo "" echo "Usage: " echo "rsyncshot <name> <number of backups to retain>" echo " setup (installs rsyncshot and cron jobs)" echo " help (prints this info)" echo "Notes:" echo "- install and log locations defined in script." -} -# display error and exit error() { echo "ERROR: $0: $@" 1>&2; @@ -49,7 +40,6 @@ error() exit 1; } -# copy files, create exclude, and setup cron job setup() { # copy this file to directory on path @@ -82,27 +72,19 @@ setup() echo "hourly, daily, and weekly cron jobs installed."; } -# if user requested help, display and exit. if [ "$TYPE" = "HELP" ]; then help; exit; fi -# ensure running as root if [ "$EUID" -ne 0 ]; then error "This script must be run as root."; fi -# display how the script was started echo "rsyncshot invoked on `date -u` with: $0 $1 $2"; -# validate first arg is alpha chars and make it case insensitive if ! [[ $1 =~ [a-zA-Z] ]]; then error "snapshot type not recognized."; fi TYPE=$(tr '[a-z]' '[A-Z]' <<< $1); - -# if user requested setup, and exit. if [ "$TYPE" = "SETUP" ]; then setup; exit; fi -# Validate second arg is numeric if ! [[ $2 =~ [0-9] ]]; then error "max snapshots not a number."; fi MAX=$(($2-1)); -# validate include file (source directories) if [ ! -f "$INCLUDES" ]; then error "include file $INCLUDES not found."; fi SOURCES=$(<$INCLUDES); for SOURCE in $SOURCES @@ -110,10 +92,8 @@ do if [ ! -d "$SOURCE" ]; then error "source $SOURCE not found"; fi done -# validate exclude file (exclusion patters) if [ ! -f "$EXCLUDES" ]; then error "Exclude file $EXCLUDES not found."; fi -# sync each source directories in turn for SOURCE in $SOURCES do rsync -avh -i --times \ @@ -122,15 +102,10 @@ do --update $SOURCE $DESTINATION/latest ; done -# update the time of latest to reflect snapshot time -# touch $DESTINATION/latest; - -# delete the last snapshot if it 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 @@ -138,15 +113,11 @@ for (( start=$(($MAX)); start>=0; start--)); do fi done -# touch the directory for a timestamp touch $DESTINATION/latest -# make a hard-link-only copy into $TYPE.0 cp -al $DESTINATION/latest $DESTINATION/$TYPE.0; -# make the directory $TYPE.0 read-only chmod -w $DESTINATION/$TYPE.0 -# print end time and exit echo "rsyncshot completed `date -u` "; exit 0; diff --git a/rsyncshot.org b/rsyncshot.org new file mode 100644 index 0000000..77797a2 --- /dev/null +++ b/rsyncshot.org @@ -0,0 +1,196 @@ +* 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 +# 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 +SCRIPTLOC=/usr/local/bin/rsyncshot; +DESTINATION=/media/backup; + +INSTALLHOME=/etc/rsyncshot +LOGHOME=/var/log/rsyncshot.log; + +INCLUDES="$INSTALLHOME/include.txt"; +EXCLUDES="$INSTALLHOME/exclude.txt"; +#+end_SRC +*** 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 +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 +help() +{ + echo "" + echo "rsyncshot - compact snapshots on Linux using rsync and hard links." + echo "" + echo "rsyncshot must be run as root" + echo "" + echo "Usage: " + echo "rsyncshot <name> <number of backups to retain>" + echo " setup (installs rsyncshot and cron jobs)" + echo " help (prints this info)" + echo "Notes:" + echo "- install and log locations defined in script." + +#+end_SRC +*** Error +#+begin_SRC sh :tangle yes +error() +{ + echo "ERROR: $0: $@" 1>&2; + echo "See \"rsyncshot help\" for usage." + exit 1; +} +#+end_SRC +*** Setup +#+begin_SRC sh :tangle yes +setup() +{ + # copy this file to directory on path + cp -f $0 /usr/local/bin + echo "$0 copied to /usr/local/bin" + + # make install home if it doesn't exist; + if [ ! -d $INSTALLHOME ]; then + mkdir -p $INSTALLHOME; + "Created install home at $INSTALLHOME."; + fi + + # create includes file and add default entries + if [ -f $INCLUDES ]; then rm $INCLUDES; fi + printf "/home /etc /usr/local/bin" >> $INCLUDES; + echo "modify include file at $INCLUDES"; + + # create excludes file and add default entries + if [ -f $EXCLUDES ]; then rm $EXCLUDES; fi + printf "*.pyc\n*.pyo\n*.class\n*.elc\n*.o\n*.tmp\n.cache*" >> $EXCLUDES; + echo "modify exclude file at $EXCLUDES"; + + # write out current crontab, append default entries, and install + crontab -l > crontemp; + echo "$CRON_H >> $LOGHOME 2>&1" >> crontemp; + echo "$CRON_D >> $LOGHOME 2>&1" >> crontemp; + echo "$CRON_W >> $LOGHOME 2>&1">> crontemp; + crontab crontemp; + rm crontemp; + 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 +if [ "$TYPE" = "HELP" ]; then help; exit; fi +#+end_SRC +*** Ensure Root +Ensure we're running as root +#+begin_SRC sh :tangle yes +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 +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 +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 +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 +if [ ! -f "$INCLUDES" ]; then error "include file $INCLUDES not found."; fi +SOURCES=$(<$INCLUDES); +for SOURCE in $SOURCES +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 +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 +for SOURCE in $SOURCES +do + rsync -avh -i --times \ + --delete --delete-excluded \ + --exclude-from=$EXCLUDES \ + --update $SOURCE $DESTINATION/latest ; +done +#+end_SRC +*** Delete Old Snapshot +Delete the snapshot to go over max +#+begin_SRC sh :tangle yes +if [ -d $DESTINATION/$TYPE.$MAX ]; then + rm -rf $DESTINATION/$TYPE.$MAX; +fi +#+end_SRC +*** Rotate Snapshots +Rotate snapshots descending +#+begin_SRC sh :tangle yes +for (( start=$(($MAX)); start>=0; start--)); do + end=$(($start+1)); + if [ -d $DESTINATION/$TYPE.$start ]; then + mv $DESTINATION/$TYPE.$start $DESTINATION/$TYPE.$end; + fi +done +#+end_SRC +*** Set Directory Timestamp +Touch the directory for a timestamp +#+begin_SRC sh :tangle yes +touch $DESTINATION/latest +#+end_SRC +*** Hard Link to Destination +Make a hard-link-only copy into $TYPE.0 +#+begin_SRC sh :tangle yes +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 +chmod -w $DESTINATION/$TYPE.0 +#+end_SRC +*** Print Time and Exit +Print end time and exit +#+begin_SRC sh :tangle yes +echo "rsyncshot completed `date -u` "; +exit 0; +#+end_SRC |
