]> arthur.barton.de Git - backup-script.git/commitdiff
Add "backup-audit" script
authorAlexander Barton <alex@barton.de>
Mon, 4 Jul 2016 14:25:03 +0000 (16:25 +0200)
committerAlexander Barton <alex@barton.de>
Mon, 4 Jul 2016 14:25:03 +0000 (16:25 +0200)
This script compares backup generations and shows "relevant" changes
in the system configuration.

At the moment this includes changes to some system files and in the
list of installed (Debian-) packages. More to come!

README.md
bin/Makefile
bin/backup-audit [new file with mode: 0755]

index b437ddffd7bfbe6f265db7a191bb979345f8f7a5..db48806cbbfc2979a5882c97b1253b5f2043f7e0 100644 (file)
--- a/README.md
+++ b/README.md
@@ -36,6 +36,17 @@ Options:
 
 - `-q`: *quick mode*, don't calculate backup sizes.
 
+### backup-audit
+
+Show "relevant" differences in system configuration between backup generations.
+
+Usage: `backup-audit [-q] [-v] [<system> [<system> [...]]]`
+
+Options:
+
+- `-q`: *quiet mode*, don't show systems without "relevant" changes.
+- `-v`: *verbose mode*, show all checks that are run.
+
 
 ## Configuration
 
index 492795b6a4f1870abc2dce3d09207e8c5d942808..365da7607619bb636bd37bfb41dd4395d3fdf555 100644 (file)
@@ -14,8 +14,11 @@ install-local:
         $(DESTDIR)$(PREFIX)/sbin/backup-script-wrapper
        install -o $(USER) -g $(GROUP) -m 755 backup-status \
         $(DESTDIR)$(PREFIX)/sbin/backup-status
+       install -o $(USER) -g $(GROUP) -m 755 backup-audit \
+        $(DESTDIR)$(PREFIX)/sbin/backup-audit
 
 check-local:
        ./backup-script --help | fgrep 'Usage: ' >/dev/null
        ./backup-script --help | fgrep 'Configuration file is "' >/dev/null
        ./backup-status --help | fgrep 'Usage: ' >/dev/null
+       ./backup-audit --help | fgrep 'Usage: ' >/dev/null
diff --git a/bin/backup-audit b/bin/backup-audit
new file mode 100755 (executable)
index 0000000..2a3b997
--- /dev/null
@@ -0,0 +1,278 @@
+#!/bin/bash
+#
+# backup-script system for cloning systems using rsync
+# Copyright (c)2008-2016 Alexander Barton, alex@barton.de
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+# Please read the file COPYING, README and AUTHORS for more information.
+#
+
+NAME=`basename $0`
+
+VERBOSE=0
+QUIET=0
+
+export LC_ALL=C
+
+# Default settings, can be overwritten in backup-script.conf:
+[ -d "/usr/local/etc/backup-script.d" ] \
+       && conf_d="/usr/local/etc/backup-script.d" \
+       || conf_d="/etc/backup-script.d"
+
+default_backup_type="rsync"
+default_files="running-config"
+default_generations=0
+default_target="/var/backups"
+
+# Search configuration file (last one is used as default!)
+for conf in \
+       "/usr/local/etc/backup-script.conf" \
+       "/etc/backup-script.conf" \
+       "${conf_d}/backup-script.conf" \
+       "/usr/local/etc/backup-script.conf" \
+; do
+       if [ -r "$conf" ]; then
+               # shellcheck source=/dev/null
+               source "$conf"
+               break
+       fi
+done
+
+Usage() {
+       echo "Usage: $NAME [-q|--quiet] [-v|--verbose] [<system> [<system> [...]]]"
+       echo "       $NAME <-d|--dirs> <dir1> <dir2>"
+       echo
+       exit 2
+}
+
+BeginDiff() {
+       echo "Differences in $*:"
+}
+
+PipeDiff() {
+       local line
+       IFS=
+       while read line; do
+               echo -e "     | $line"
+       done
+}
+
+EndDiff() {
+       :
+}
+
+HandleSystem() {
+       local fname="$1"
+
+       # Set global defaults
+       local backup_type="$default_backup_type"
+       local files="$default_files"
+       local generations="$default_generations"
+       local local=0
+       local system="$fname"
+       local target="$default_target"
+
+       # Read in system configuration file
+       # shellcheck source=/dev/null
+       source "$f"
+
+       target="$target/$(basename "$f")"
+
+       [[ -d "$target" ]] || return 0
+
+       # System name
+       [[ "$system" == "$fname" ]] \
+               && systxt="\"$system\"" \
+               || systxt="\"$fname\" [\"$system\"]"
+       [[ "$local" -eq 0 ]] \
+               && echo "Checking $systxt ..." \
+               || echo "Checking $systxt (local system) ..."
+
+       # Check if job is disabled
+       if [[ "$backup_type" == "disabled" ]]; then
+               echo "Job is DISABLED and will be skipped."
+               echo; return 0
+       fi
+
+       if [ $generations -lt 1 ]; then
+               echo "No generations configured, nothing to compare, skipping system!"
+               echo; return 1
+       fi
+
+       local latest_d="$target/latest"
+       if [[ ! -d "$latest_d" || ! -r "$latest_d/.stamp" ]]; then
+               echo "Failed to access latest backup generation in \"$latest_d\", skipping system!"
+               echo; return 1
+       fi
+       echo "Found latest generation in \"$latest_d\"."
+
+       declare -i code=-1
+       source "$latest_d/.stamp"
+
+       if [[ $code -ne 0 && $code -ne 24 ]]; then
+               echo "Last backup generation has errors, skipping system!"
+               echo; return 1
+       fi
+
+       # Search previous generation without errors
+       local previous_d=""
+       for d in $(ls -1dt $target/[0-9]*-[0-9]*); do
+               [[ -d "$d" && -r "$d/.stamp" ]] || return 0
+
+               declare -i code=-1
+               source "$d/.stamp"
+
+               if [[ $code -eq 0 || $code -eq 24 ]]; then
+                       previous_d="$d"
+                       break
+               fi
+       done
+       if [[ -z "$previous_d" || ! -d "$previous_d" || ! -r "$previous_d/.stamp" ]]; then
+               echo "Failed to find previous successfull backup generation, skipping system!"
+               echo; return 1
+       fi
+       echo "Comparing with generation in $previous_d ..."
+
+       DiffGenerations "$backup_type" "$previous_d" "$latest_d" "$files"
+       return_code=$?
+
+       echo
+       return $return_code
+}
+
+DiffGenerations() {
+       local backup_type="$1"
+       local gen1_d="$2"
+       local gen2_d="$3"
+       local files="$4"
+
+       local return_code=0
+
+       if [[ "$backup_type" == "rsync" ]]; then
+               # rsync Backup Type
+
+               for file in \
+                       /etc/passwd \
+                       /etc/shadow \
+                       /etc/group \
+                       /etc/gshadow \
+                       \
+                       /etc/fstab \
+                       /etc/hostname \
+                       /etc/hosts \
+                       /etc/machine-id \
+                       /etc/modules \
+                       /etc/network/interfaces \
+                       /etc/networks \
+               ; do
+                       [[ -r "${gen1_d}${file}" ]] || continue
+
+                       [[ $VERBOSE -ne 0 ]] && echo "Checking \"$file\" ..."
+                       diff -U 3 "${gen1_d}${file}" "${gen2_d}${file}" >"$tmp_diff"
+                       if [[ $? -ne 0 ]]; then
+                               BeginDiff "\"$file\""
+                               tail -n +3 "$tmp_diff" | PipeDiff
+                               EndDiff
+                               return_code=1
+                       fi
+               done
+
+               if [[ -d "${gen1_d}/var/lib/dpkg/info" && -d "${gen2_d}/var/lib/dpkg/info" ]]; then
+                       [[ $VERBOSE -ne 0 ]] && echo "Checking list of installed packages ..."
+                       chroot "${gen1_d}" dpkg --get-selections >"$tmp_1" || return 2
+                       chroot "${gen2_d}" dpkg --get-selections >"$tmp_2" || return 2
+                       diff -U 0 "$tmp_1" "$tmp_2" >"$tmp_diff"
+                       if [[ $? -ne 0 ]]; then
+                               BeginDiff "list of installed packages"
+                               tail -n +3 "$tmp_diff" | grep -v '^@@ ' | PipeDiff
+                               EndDiff
+                               return_code=1
+                       fi
+               fi
+       elif [[ "$backup_type" == "scp" ]]; then
+               # scp Backup type
+               file=$(basename "$files")
+               [[ $VERBOSE -ne 0 ]] && echo "Checking \"$file\" ..."
+               diff -U 3 "${gen1_d}/${file}" "${gen2_d}/${file}" >"$tmp_diff"
+               if [[ $? -ne 0 ]]; then
+                       BeginDiff "\"$file\""
+                       tail -n +3 "$tmp_diff" | PipeDiff
+                       EndDiff
+                       return_code=1
+               fi
+       else
+               echo "Backup type \"$backup_type\" undefined, \"$system\" skipped!"
+               echo; return 2
+       fi
+
+       return $return_code
+}
+
+MkTempFiles() {
+       tmp_1=$(mktemp /tmp/$NAME.XXXXXX) || exit 1
+       tmp_2=$(mktemp /tmp/$NAME.XXXXXX) || exit 1
+       tmp_diff=$(mktemp /tmp/$NAME.XXXXXX) || exit 1
+       tmp_out=$(mktemp /tmp/$NAME.XXXXXX) || exit 1
+}
+
+CleanUp() {
+       rm -f "$tmp_1" "$tmp_2" "$tmp_diff" "$tmp_out"
+}
+
+while [[ $# -gt 0 ]]; do
+       case "$1" in
+         "-d"|"--dirs")
+               shift
+               [[ $# -eq 2 ]] || Usage
+               MkTempFiles
+               DiffGenerations "$default_backup_type" "$1/" "$2/" "$default_files"
+               return_code=$?
+               CleanUp
+               exit $return_code
+               ;;
+         "-q"|"--quiet")
+               QUIET=1; shift
+               ;;
+         "-v"|"--verbose")
+               VERBOSE=1; shift
+               ;;
+         "-"*)
+               Usage
+               ;;
+         *)
+               break
+       esac
+done
+
+if [[ $# -ge 1 ]]; then
+       for s in "$@"; do
+               if [ ! -r "${conf_d}/$s" ]; then
+                       echo "$NAME: Can' read \"${conf_d}/$s\"!"
+                       exit 1
+               fi
+               sys+=("${conf_d}/$s")
+       done
+else
+       sys=("${conf_d}/"*)
+fi
+
+MkTempFiles
+for f in "${sys[@]}"; do
+       [[ -r "$f" && -f "$f" ]] || continue
+
+       fname=`basename $f`
+       case "$fname" in
+               "backup-script.conf"|*.sh)
+                       continue
+                       ;;
+       esac
+
+       HandleSystem "$fname" >"$tmp_out" 2>&1
+       [[ $QUIET -eq 0 || $? -ne 0 ]] && cat "$tmp_out"
+done
+CleanUp
+
+# -eof-