Skip to content

Commit

Permalink
Misc fixes, bashisms removed
Browse files Browse the repository at this point in the history
* rsnapshot-rotate.sh - don't remount on each run
* rsnapshot-wrapper.sh - various modifications merged, TODO added

Signed-off-by: Christian Kujau <[email protected]>
  • Loading branch information
ckujau committed Nov 23, 2017
1 parent 34f0634 commit d6296b1
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 72 deletions.
5 changes: 2 additions & 3 deletions checksum_file.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@
#
# (c)2013 Christian Kujau <[email protected]>
#
# Generate checksums of files and store them
# via Extended Attributes
# Generate checksums of files and store them via Extended Attributes
#
# == Requirements ==
# Darwin: /sbin/md5 or openssl
Expand Down Expand Up @@ -40,7 +39,7 @@ print_usage()
echo "* check-set - sets a new checksum if none is found, verify checksum otherwise."
}

if [ $# -ne 2 -o ! -f "$2" ]; then
if [ $# -ne 2 ] || [ ! -f "$2" ]; then
print_usage
exit 1
else
Expand Down
12 changes: 6 additions & 6 deletions diff2html.sh
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ cat <<XX
<pre>
XX

echo -n '<span class="comment">'
printf '<span class="comment">'

first=1
diffseen=0
Expand All @@ -51,7 +51,7 @@ while [ $? -eq 0 ]; do

if [ $diffseen -eq 0 ]; then
diffseen=1
echo -n '</span>'
printf '</span>'
else
if [ $lastonly -eq 0 ]; then
echo "</div>"
Expand All @@ -67,7 +67,7 @@ while [ $? -eq 0 ]; do
cls='diff'
if [ $diffseen -eq 0 ]; then
diffseen=1
echo -n '</span>'
printf '</span>'
else
echo "</div>"
fi
Expand Down Expand Up @@ -110,16 +110,16 @@ while [ $? -eq 0 ]; do

# Output the line.
if [ "$cls" ]; then
echo -n '<span class="'${cls}'">'${s}'</span>'
printf '<span class="'${cls}'">'${s}'</span>'
else
echo -n ${s}
printf ${s}
fi
read -r s
done
IFS=$OIFS

if [ $diffseen -eq 0 && $onlyseen -eq 0 ]; then
echo -n '</span>'
printf '</span>'
else
echo "</div>"
fi
Expand Down
2 changes: 1 addition & 1 deletion rsnapshot/rsnapshot-ready.sh
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ for d in $@; do
# check if we're mounted
mount | grep "$DIR type" > /dev/null
if [ $? = 0 ]; then
$DEBUG touch "$FILE"
$DEBUG date > "$FILE"
else
# remove FILE if a single mountpoint was not mounted and bail out
$DEBUG rm -f "$FILE"
Expand Down
14 changes: 11 additions & 3 deletions rsnapshot/rsnapshot-rotate.sh
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
#!/bin/sh
#
# (c)2011 Christian Kujau <[email protected]>
#
# Forcefully rotate rsnapshot backups, intentionally discard older copies
# Forcefully rotate rsnapshot backups, intentionally discarding older copies
#
CONF="/etc/rsnapshot"

Expand All @@ -14,16 +13,25 @@ else
CONF="$CONF"/rsnapshot-"$1".conf
DIR=`awk '/^snapshot_root/ {print $2}' $CONF`

# Don't let rsnapshot-wrapper remount our backup store
WRAPPER_CONF="/usr/local/etc/rsnapshot-wrapper.conf"
NOMOUNT=$(awk -F= '/^NOMOUNT/ {print $2}' $WRAPPER_CONF)
touch "$NOMOUNT"

[ -f "$CONF" ] || exit 2
[ -d "$DIR" ] || exit 3
fi

for i in daily weekly monthly; do
C=`ls -d "$DIR"/"$i".* | wc -l`
C=`ls -d "$DIR"/"$i".* 2>/dev/null | wc -l`
j=0
while [ $j -le $C ]; do
echo "("$j"/"$C") rsnapshot -c "$CONF" "$i"..."
rsnapshot -c "$CONF" "$i"
j=$((j+1))
done
done

# Since this is not a normal operation and may take a long time, we will
# remove the NOMOUNT flag again, otherwise we may forget to remove it.
rm -f "$NOMOUNT"
145 changes: 86 additions & 59 deletions rsnapshot/rsnapshot-wrapper.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,28 @@
# Rsnapshot wrapper script. For this to work, the rsnapshot.conf
# file for each host must include the following directives:
#
# ##HOST=FQDN PORT=NN OS=Unix TAG=ABC RDY=/var/run/rsnapshot.ready
# ##HOST=FQDN NAME=alias PORT=NN OS=Unix TAG=ABC RDY=/var/run/rsnapshot.ready
#
# * The line must begin with two hashmarks (#)
# * HOST will be the FQDN of the machine to be backed up.
# * PORT is the portnumber of the SSH daemon, listening on
# remote machine
# * OS is the output of "uname -s" for the remote machine
# (if it's a Unix/Linux host)
# * TAG is
# * The line must begin with two hashmarks (##) and no space after that.
# * NAME is just an alias for the hostname, usually the same as HOST (optional)
# * HOST should be the FQDN of the machine to be backed up.
# * PORT should be the SSH port of the machine to connect to.
# * OS should be an identifier for the expected operating system on
# the remote machine:
# - For Unix/Linux the output of "hostid" is parsed
# - For Darwin the output of "uname -s" is parsed
# - For Windows, the output of "cygcheck.exe -s" is parsed
# - For BusyBox, the output of "busybox" is parsed
# * TAG is the expected output of the output above
# * RDY is a filename which must exist on the remote machine
# otherwise the backup will not be run.
#
# TODO:
# - run rsnapshot for just one config.
# - implement (real) DEBUG mode
# - recover from incomplete backups. That's really something
# rsnapshot itsself could do, but can't right now.
# - Remove -4 from the SSH & Rsync commands to make it IPv6 ready again
#
LANG=C
PATH=/usr/bin:/usr/sbin:/bin:/sbin
Expand All @@ -25,55 +36,67 @@ CONF=/usr/local/etc/rsnapshot-wrapper.conf
# DEBUG=echo

log() {
echo "`date +%F\ %H:%M:%S\ %Z`: $1"
echo "$(date +%F\ %H:%M:%S\ %Z): $1"
[ -n "$2" ] && exit "$2"
}

backup() {
INTERVAL="$1"
log "**** ======================================================="
log "**** $INTERVAL backup started..."
log "**** $INTERVAL backup started (PID: $$)..."

# Rsnapshot needs perl anyway but not all systems have GNU/date
BEGIN_G=`perl -e 'print time'`
# Not all systems have GNU/date, but Rsnapshot needs Perl anyway
BEGIN_G=$(perl -e 'print time')
for c in $CONFDIR/*.conf; do
echo
BEGIN=`perl -e 'print time'`
BEGIN=$(perl -e 'print time')

# We're parsing our configuration file for:
#
# - ROOT, RLOG - standard rsnapshot options
# - HOST, PORT, OS, TAG - mandatory for this wrapper
# - HOST, PORT, OS - FQDN, SSH port, operating system
# - NAME - hostname alias, used for logfile tagging
# - TAG - either the system's hostid or whatever "OS"
# is set to (e.g. "Windows", "BusyBox")
# - RDY - we shall only backup if this file exists on the remote host
#
# We're using them to identify the system correctly and will only
# backup if the client wants us to.
#
unset ROOT RLOG HOST PORT OS TAG RDY MSG
unset ROOT RLOG NAME HOST PORT OS TAG RDY MSG
eval `awk '/^snapshot_root/ {print "ROOT="$2}; /^logfile/ {print "RLOG="$2}' $c`
eval `awk -F\# '/^##HOST=/ {print $3}' "$c"`

# Set NAME to HOST if it wasn't set explicitly in the configuration file
[ -z "$NAME" ] && NAME="$HOST"

# As "test -w" would return true even when ROOT is mounted r/o, so let's touch(1) it.
if [ -d "$ROOT" ] && touch "$ROOT" 2>/dev/null; then
:
else
log "**** $ROOT is not a writable directory!"
continue
fi

# Only run if the last backup was successful
# FIXME: match on successful backups too, so that backups are run again once
# the problem has been fixed.
# egrep "${NAME}/${OS}.*finished" "$LOG" | tail -1 | grep "with errors" > /dev/null
# if [ $? = 0 ]; then
# log "**** The last backup of $NAME/$OS had errors - skipping!" | tee -a "$RLOG"
# continue
# fi

# Only run if there's no successful run today
# [07/Jun/2012:01:56:57] /usr/bin/rsnapshot -c /etc/rsnapshot/rsnapshot-len.conf daily: ERROR: \
# /usr/bin/rsnapshot -c /etc/rsnapshot/rsnapshot-len.conf daily: completed, but with some errors
egrep "`date +"%d/%b/%Y"`.*"$CYCLE".*(completed successfully|completed, but with some warnings)" "$RLOG" > /dev/null
egrep "$(date +%Y-%m-%d).*"$CYCLE".*(completed successfully|completed, but with some warnings)" "$RLOG" > /dev/null
if [ $? = 0 ]; then
log "**** $HOST/$OS already has a $INTERVAL backup from today, skipping." | tee -a "$RLOG"
log "**** $NAME/$OS already has a $INTERVAL backup from today, skipping." | tee -a "$RLOG"
continue
fi

# See if the remote system is up & running
nc -w1 -z $HOST $PORT > /dev/null
if [ ! $? = 0 ]; then
log "**** Host $HOST not responding on port $PORT, skipping!"
log "**** Host $NAME not responding on port $PORT, skipping!"
continue
fi

Expand All @@ -84,26 +107,29 @@ for c in $CONFDIR/*.conf; do
# and we have to figure out something else.
case "$OS" in
Darwin)
# The respective rsnapshot.conf needs TAG=Darwin for it to be
# recognized properly.
TAG2=`ssh -p$PORT $HOST "uname -s" 2>/dev/null`
# We need TAG=Darwin for this to work!
TAG2=$(ssh -4 -p$PORT $HOST "uname -s" 2>/dev/null)
;;

Windows)
# The respective rsnapshot.conf needs TAG=Windows for it to be
# recognized properly.
TAG2="`ssh -p$PORT $HOST \"cygcheck.exe -s\" | awk '/^Windows/ {print $1}'`"
# We need TAG=Windows for this to work!
TAG2="$(ssh -4 -p$PORT $HOST "cygcheck.exe -s" 2>/dev/null | awk '/^Windows/ {print $1}')"
;;

BusyBox)
# We need TAG=BusyBox for this to work!
TAG2="$(ssh -4 -p$PORT $HOST "ls --help" 2>&1 | awk '/BusyBox/ {print $1}')"
;;

*)
# Unix/Linux systems have "hostid"
TAG2=`ssh -p$PORT $HOST "hostid" 2>/dev/null`
# Most Unix/Linux systems have "hostid"
TAG2="$(ssh -4 -p$PORT $HOST "hostid" 2>/dev/null)"
;;
esac

# See if TAG2 matches the configured TAG
if [ ! "$TAG" = "$TAG2" ]; then
log "**** $HOST/$OS: we expected tag \""$TAG"\" but discovered \""$TAG2"\" instead, skipping!"
log "**** $NAME/$OS: we expected tag \""$TAG"\" but discovered \""$TAG2"\" instead, skipping!"
continue
fi

Expand All @@ -112,17 +138,16 @@ for c in $CONFDIR/*.conf; do

# See if the remote site wants us to backup
TEMP=`$DEBUG mktemp`
$DEBUG rsync --port="$PORT" $HOST:"$RDY" "$TEMP" 2>/dev/null
$DEBUG rsync -4 --port="$PORT" $HOST:"$RDY" "$TEMP" 2>/dev/null
if [ ! $? = 0 ]; then
log "**** File \""$HOST":"$RDY"\" ($OS) not found, skipping!"
log "**** File \""$NAME":"$RDY"\" ($OS) not found, skipping!"
$DEBUG rm -f "$TEMP"
echo
continue
else
$DEBUG rm -f "$TEMP"
fi
else
# The remote site wants us to backup, no matter what (no ready-file specified)
# The remote side specified no ready-file, we'll run the backup anyway.
:
fi

Expand All @@ -136,7 +161,7 @@ for c in $CONFDIR/*.conf; do
fi

# All tests passed, let's do this now
log "**** $HOST/$OS ($INTERVAL) started..." | tee -a "$RLOG"
log "**** $NAME/$OS ($INTERVAL) started..." | tee -a "$RLOG"
if [ -z "$DEBUG" ]; then
rsnapshot -c "$c" $INTERVAL >> "$RLOG" 2>&1
else
Expand All @@ -146,12 +171,12 @@ for c in $CONFDIR/*.conf; do
# See if we were successful
tail -1 "$RLOG" | grep 'completed successfully' 1>/dev/null || MSG="with errors "

END=`perl -e 'print time'`
DIFF=`echo "scale=2; ( $END - $BEGIN ) / 60" | bc -l`
log "**** $HOST/$OS ($INTERVAL) finished ${MSG}after $DIFF minutes."
END=$(perl -e 'print time')
DIFF=$(echo "scale=2; ( $END - $BEGIN ) / 60" | bc -l)
log "**** $NAME/$OS ($INTERVAL) finished ${MSG}after $DIFF minutes."
done
END_G=`perl -e 'print time'`
DIFF_G=`echo "scale=2; ( $END_G - $BEGIN_G ) / 60" | bc -l`
END_G=$(perl -e 'print time')
DIFF_G=$(echo "scale=2; ( $END_G - $BEGIN_G ) / 60" | bc -l)
log "**** $INTERVAL backup finished after $DIFF_G minutes"

# Sync disks
Expand All @@ -174,28 +199,30 @@ else
if [ ! $? = 0 ]; then
log "**** There are no configuration files in $CONFDIR!" 1
fi
fi

# run only once
PID=`cat "$PIDFILE" 2>/dev/null`
if [ -n "$PID" ]; then
ps -p"$PID" > /dev/null
if [ $? = 0 ]; then
log "**** There's another instance of `basename $0` running (PID: $PID)" 1
else
echo $$ > "$PIDFILE"
fi
run_only_once() {
PID=$(cat "$PIDFILE" 2>/dev/null)
if [ -n "$PID" ]; then
ps -p"$PID" > /dev/null
if [ $? = 0 ]; then
log "**** There's another instance of $(basename $0) running (PID: $PID)" 1
else
echo $$ > "$PIDFILE"
fi

else
echo $$ > "$PIDFILE"
fi
}

# Be nice to others
[ -n "$NICE" ] && renice $NICE $$ > /dev/null
[ -n "$NICE" ] && renice $NICE $$ > /dev/null
[ -n "$IDLE" ] && ionice -c 3 -p $$ > /dev/null

# Main
case $1 in
hourly|daily|weekly|monthly)
run_only_once
CYCLE="$1"
preexec 2>&1 | tee -a $LOG
backup "$CYCLE" 2>&1 | tee -a $LOG
Expand All @@ -204,22 +231,22 @@ case $1 in

stats)
# Not Y3K safe :-)
for h in `awk '/^2[0-9].*\((hourly|daily|weekly|monthly)\) finished/ {print $5}' "$LOG" | sort -u`; do
printf "Host $h took an average of "
egrep "${h}.*finished" "$LOG" | awk '{sum+=$9} END {print sum/NR " minutes to complete."}'
done
for h in `awk '/^2[0-9][0-9][0-9].*\((hourly|daily|weekly|monthly)\) finished/ {print $5}' "$LOG" | sort -u`; do
MINUTES=$(egrep "${h}.*finished" "$LOG" | awk '{sum+=$9} END {printf "%0.1f\n", sum/NR}')
echo "Host $h took an average of $MINUTES minutes to complete."
done | sort -nk7

echo "----"
for t in hourly daily weekly monthly; do
grep "$t" "$LOG" > /dev/null || continue
printf "The $t backup took an average of "
egrep "${t} backup finished after" "$LOG" | awk '{sum+=$9} END {print sum/NR " minutes to complete."}'
MINUTES=$(egrep "${t} backup finished after" "$LOG" | awk '{sum+=$9} END {printf "%0.1f\n", sum/NR}')
echo "The $t backup took an average of $MINUTES minutes to complete."
done
;;

*)
echo "Usage: `basename $0` [hourly|daily|weekly|monthly]"
echo " `basename $0` [stats]"
echo "Usage: $(basename $0) [hourly|daily|weekly|monthly]"
echo " $(basename $0) [stats]"
exit 1
;;
esac

0 comments on commit d6296b1

Please sign in to comment.