]> arthur.barton.de Git - backup-script.git/blob - bin/backup-audit
0fc0916763209c31c9bee2ee83c8f565878bdae9
[backup-script.git] / bin / backup-audit
1 #!/bin/bash
2 #
3 # backup-script system for cloning systems using rsync
4 # Copyright (c)2008-2016 Alexander Barton, alex@barton.de
5 #
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
10 # Please read the file COPYING, README and AUTHORS for more information.
11 #
12
13 NAME=$(basename "$0")
14
15 VERBOSE=0
16 QUIET=0
17
18 export LC_ALL=C
19
20 # Default settings, can be overwritten in backup-script.conf:
21 [ -d "/usr/local/etc/backup-script.d" ] \
22         && conf_d="/usr/local/etc/backup-script.d" \
23         || conf_d="/etc/backup-script.d"
24
25 default_backup_type="rsync"
26 default_files="running-config"
27 default_generations=0
28 default_target="/var/backups"
29
30 # Search configuration file (last one is used as default!)
31 for conf in \
32         "/usr/local/etc/backup-script.conf" \
33         "/etc/backup-script.conf" \
34         "${conf_d}/backup-script.conf" \
35         "/usr/local/etc/backup-script.conf" \
36 ; do
37         if [ -r "$conf" ]; then
38                 # shellcheck source=/dev/null
39                 source "$conf"
40                 break
41         fi
42 done
43
44 Usage() {
45         echo "Usage: $NAME [-q|--quiet] [-v|--verbose] [<system> [<system> [...]]]"
46         echo "       $NAME <-d|--dirs> <dir1> <dir2>"
47         echo
48         exit 2
49 }
50
51 BeginDiff() {
52         echo "Differences in $*:"
53 }
54
55 PipeDiff() {
56         local line
57         IFS=
58         while read -r line; do
59                 echo -e "     | $line"
60         done
61 }
62
63 EndDiff() {
64         :
65 }
66
67 HandleSystem() {
68         local fname="$1"
69
70         # Set global defaults
71         local backup_type="$default_backup_type"
72         local files="$default_files"
73         local generations="$default_generations"
74         local local=0
75         local system="$fname"
76         local target="$default_target"
77
78         # Read in system configuration file
79         # shellcheck source=/dev/null
80         source "$f"
81
82         target="$target/$(basename "$f")"
83
84         [[ -d "$target" ]] || return 0
85
86         # System name
87         [[ "$system" == "$fname" ]] \
88                 && systxt="\"$system\"" \
89                 || systxt="\"$fname\" [\"$system\"]"
90         [[ "$local" -eq 0 ]] \
91                 && echo "Checking $systxt ..." \
92                 || echo "Checking $systxt (local system) ..."
93
94         # Check if job is disabled
95         if [[ "$backup_type" == "disabled" ]]; then
96                 echo "Job is DISABLED and will be skipped."
97                 echo; return 0
98         fi
99
100         if [ $generations -lt 1 ]; then
101                 echo "No generations configured, nothing to compare, skipping system!"
102                 echo; return 1
103         fi
104
105         local latest_d="$target/latest"
106         if [[ ! -d "$latest_d" || ! -r "$latest_d/.stamp" ]]; then
107                 echo "Failed to access latest backup generation in \"$latest_d\", skipping system!"
108                 echo; return 1
109         fi
110         echo "Found latest generation in \"$latest_d\"."
111
112         declare -i code=-1
113         # shellcheck source=/dev/null
114         source "$latest_d/.stamp"
115
116         if [[ $code -ne 0 && $code -ne 24 ]]; then
117                 echo "Last backup generation has errors, skipping system!"
118                 echo; return 1
119         fi
120
121         # Search previous generation without errors
122         local previous_d=""
123         # shellcheck disable=SC2045
124         for d in $(ls -1dt "$target/"[0-9]*-[0-9]* 2>/dev/null); do
125                 [[ -d "$d" && -r "$d/.stamp" ]] || return 0
126
127                 declare -i code=-1
128                 # shellcheck source=/dev/null
129                 source "$d/.stamp"
130
131                 if [[ $code -eq 0 || $code -eq 24 ]]; then
132                         previous_d="$d"
133                         break
134                 fi
135         done
136         if [[ -z "$previous_d" || ! -d "$previous_d" || ! -r "$previous_d/.stamp" ]]; then
137                 echo "Failed to find previous successfull backup generation, skipping system!"
138                 echo; return 1
139         fi
140         echo "Comparing with generation in $previous_d ..."
141
142         DiffGenerations "$backup_type" "$previous_d" "$latest_d" "$files"
143         return_code=$?
144
145         echo
146         return $return_code
147 }
148
149 DiffGenerations() {
150         local backup_type="$1"
151         local gen1_d="$2"
152         local gen2_d="$3"
153         local files="$4"
154
155         local return_code=0
156
157         if [[ "$backup_type" == "rsync" ]]; then
158                 # rsync Backup Type
159
160                 for file in \
161                         /etc/passwd \
162                         /etc/shadow \
163                         /etc/group \
164                         /etc/gshadow \
165                         \
166                         /boot/grub/grub.cfg \
167                         /etc/aliases \
168                         /etc/bash.bashrc \
169                         /etc/crontab \
170                         /etc/environment \
171                         /etc/fstab \
172                         /etc/hostname \
173                         /etc/hosts \
174                         /etc/hosts.allow \
175                         /etc/hosts.deny \
176                         /etc/inittab \
177                         /etc/ld.so.conf \
178                         /etc/login.defs \
179                         /etc/machine-id \
180                         /etc/modules \
181                         /etc/network/interfaces \
182                         /etc/networks \
183                         /etc/nsswitch.conf \
184                         /etc/profile \
185                         /etc/rc.local \
186                         /etc/resolv.conf \
187                         /etc/services \
188                         /etc/shells \
189                         /etc/ssh/sshd_config \
190                         /etc/sshd_config \
191                         /etc/sudoers \
192                         /etc/sysctl.conf \
193                 ; do
194                         [[ -r "${gen1_d}${file}" ]] || continue
195
196                         [[ $VERBOSE -ne 0 ]] && echo "Checking \"$file\" ..."
197                         diff -U 3 "${gen1_d}${file}" "${gen2_d}${file}" >"$tmp_diff"
198                         if [[ $? -ne 0 ]]; then
199                                 BeginDiff "\"$file\""
200                                 tail -n +3 "$tmp_diff" | PipeDiff
201                                 EndDiff
202                                 return_code=1
203                         fi
204                 done
205
206                 if [[ -d "${gen1_d}/var/lib/dpkg/info" && -d "${gen2_d}/var/lib/dpkg/info" ]]; then
207                         [[ $VERBOSE -ne 0 ]] && echo "Checking list of installed packages ..."
208                         chroot "${gen1_d}" dpkg --get-selections >"$tmp_1" || return 2
209                         chroot "${gen2_d}" dpkg --get-selections >"$tmp_2" || return 2
210                         diff -U 0 "$tmp_1" "$tmp_2" >"$tmp_diff"
211                         if [[ $? -ne 0 ]]; then
212                                 BeginDiff "list of installed packages"
213                                 tail -n +3 "$tmp_diff" | grep -v '^@@ ' | PipeDiff
214                                 EndDiff
215                                 return_code=1
216                         fi
217                 fi
218         elif [[ "$backup_type" == "scp" ]]; then
219                 # scp Backup type
220                 file=$(basename "$files")
221                 [[ $VERBOSE -ne 0 ]] && echo "Checking \"$file\" ..."
222                 diff -U 3 "${gen1_d}/${file}" "${gen2_d}/${file}" >"$tmp_diff"
223                 if [[ $? -ne 0 ]]; then
224                         BeginDiff "\"$file\""
225                         tail -n +3 "$tmp_diff" | PipeDiff
226                         EndDiff
227                         return_code=1
228                 fi
229         else
230                 echo "Backup type \"$backup_type\" undefined, \"$system\" skipped!"
231                 echo; return 2
232         fi
233
234         return $return_code
235 }
236
237 MkTempFiles() {
238         tmp_1=$(mktemp "/tmp/$NAME.XXXXXX") || exit 1
239         tmp_2=$(mktemp "/tmp/$NAME.XXXXXX") || exit 1
240         tmp_diff=$(mktemp "/tmp/$NAME.XXXXXX") || exit 1
241         tmp_out=$(mktemp "/tmp/$NAME.XXXXXX") || exit 1
242 }
243
244 CleanUp() {
245         rm -f "$tmp_1" "$tmp_2" "$tmp_diff" "$tmp_out"
246 }
247
248 while [[ $# -gt 0 ]]; do
249         case "$1" in
250           "-d"|"--dirs")
251                 shift
252                 [[ $# -eq 2 ]] || Usage
253                 MkTempFiles
254                 DiffGenerations "$default_backup_type" "$1" "$2" "$default_files"
255                 return_code=$?
256                 CleanUp
257                 exit $return_code
258                 ;;
259           "-q"|"--quiet")
260                 QUIET=1; shift
261                 ;;
262           "-v"|"--verbose")
263                 VERBOSE=1; shift
264                 ;;
265           "-"*)
266                 Usage
267                 ;;
268           *)
269                 break
270         esac
271 done
272
273 if [[ $# -ge 1 ]]; then
274         for s in "$@"; do
275                 if [ ! -r "${conf_d}/$s" ]; then
276                         echo "$NAME: Can' read \"${conf_d}/$s\"!"
277                         exit 1
278                 fi
279                 sys+=("${conf_d}/$s")
280         done
281 else
282         sys=("${conf_d}/"*)
283 fi
284
285 MkTempFiles
286 for f in "${sys[@]}"; do
287         [[ -r "$f" && -f "$f" ]] || continue
288
289         fname=$(basename "$f")
290         case "$fname" in
291                 "backup-script.conf"|*.sh)
292                         continue
293                         ;;
294         esac
295
296         HandleSystem "$fname" >"$tmp_out" 2>&1
297         [[ $QUIET -eq 0 || $? -ne 0 ]] && cat "$tmp_out"
298 done
299 CleanUp
300
301 # -eof-