7ed64cce8872c867bcb49cfb3ae585d6f5bb4f79
[debian/amanda] / server-src / amverify.sh
1 #! @SHELL@
2 #
3 #       $Id: amverify.sh.in,v 1.38 2006/07/25 19:00:56 martinea Exp $
4 #
5 # (C) 1996 by ICEM Systems GmbH
6 # Author: Axel Zinser (fifi@icem.de)
7 #
8 # amverify: check amanda tapes and report errors
9 #
10
11 echo "amverify is deprecated -- use amcheckdump" >& 2
12
13 prefix="@prefix@"
14 exec_prefix="@exec_prefix@"
15 sbindir="@sbindir@"
16 amlibexecdir="@amlibexecdir@"
17 . "${amlibexecdir}/amanda-sh-lib.sh"
18
19 # add sbin and ucb dirs
20 PATH="$PATH:/usr/sbin:/sbin:/usr/ucb"
21 export PATH
22
23 USE_VERSION_SUFFIXES="@USE_VERSION_SUFFIXES@"
24 if [ "$USE_VERSION_SUFFIXES" = "yes" ]; then
25         SUF="-@VERSION@"
26 else
27         SUF=
28 fi
29
30 # If the shell/system echo support \r and \c, use them to write some
31 # status messages over the top of each other as things progress, otherwise
32 # use a normal echo and let them go on new lines.  Define $Echoe to be
33 # an echo that goes to stderr.  In the code, $Echoe is used and it may
34 # be either echoe or echone, the latter being code that knows about echon.
35
36 t=`echo "abc\r\c" | wc -c`
37 if [ $t -eq 4 ]; then
38         Echon=echon
39 else
40         Echon=echo
41 fi
42 Echoe=echoe
43 elen=0
44 echoe() {
45         echo "$@" >&2
46         Echoe=echoe
47 }
48 echon() {
49         newelen=`expr "$1" : '.*'`
50         blanks=
51         while [ $newelen -lt $elen ]; do
52                 blanks="$blanks "
53                 elen=`expr $elen - 1`
54         done
55         echo "$1""$blanks\r\c"
56         elen=$newelen
57         Echoe=echone
58 }
59 echone() {
60         echon
61         echoe "$@"
62         Echoe=echoe
63 }
64
65 report() {
66         $Echoe "$@"
67         echo "$@" >> $REPORT
68 }
69
70 getparm() {
71         $AMGETCONF $CONFIG $1 2>/dev/null
72 }
73
74 sendreport() {
75         if [ -f $REPORT -a X"$REPORTTO" != X"" ]; then
76                 (
77                 echo `_ 'Tapes: %s' "$TAPELIST"`
78                 if [ -s $DEFECTS ]; then
79                         echo `_ 'Errors found:'`
80                         cat $DEFECTS
81                 else
82                         echo `_ 'No errors found!'`
83                 fi
84                 echo
85
86                 [ -s $REPORT ] \
87                         && cat $REPORT
88                 ) | (
89                     if test -n "$MAIL"; then
90                         $MAIL -s "$ORG AMANDA VERIFY REPORT FOR$TAPELIST" $REPORTTO
91                     else
92                         cat >&2
93                     fi
94                 )
95         fi
96 }
97
98 ###
99 # This function is called to process one dump image.  Standard input is
100 # the dump image.  We parse the header and decide if it is a GNU tar
101 # dump or a system dump.  Then we do a catalog operation to /dev/null
102 # and finally a "cat" to /dev/null to soak up whatever data is still in
103 # the pipeline.
104 #
105 # In the case of a system restore catalogue, this does not fully check
106 # the integrity of the dump image because system restore programs stop
107 # as soon as they are done with the directories, which are all at the
108 # beginning.  But the trailing cat will at least make sure the whole
109 # image is readable.
110 ###
111
112 doonefile() {
113
114         ###
115         # The goal here is to collect the first 32 KBytes and save the
116         # first line.  But the pipe size coming in to us from amrestore
117         # is highly system dependent and "dd" does not do reblocking.
118         # So we pick a block size that is likely to always be available in
119         # the pipe and a count to take it up to 32 KBytes.  Worst case,
120         # this could be changed to "bs=1 count=32k".  We also have to
121         # soak up the rest of the output after the "head" so an EPIPE
122         # does not go back and terminate the "dd" early.
123         ###
124
125         HEADER=`$DD bs=512 count=64 2>/dev/null | ( sed 1q ; cat > /dev/null )`
126         CMD=
127         result=1
128         if [ X"$HEADER" = X"" ]; then
129                 echo `_ '** No header'` > $TEMP/errors
130         else
131                 set X $HEADER
132                 # XXX meh, while[] is dangerous, what about a bad header?
133                 while [ X"$1" != X"program" ]; do shift; done
134                 if [ X"$1" = X"program" -a X"$2" != X"" ]; then
135                         if [ X"$TAR" != X"" \
136                              -a \( X"`basename $2`" = X"`basename $TAR`" \
137                                    -o X"`basename $2`" = X"gtar" \
138                                    -o X"`basename $2`" = X"gnutar" \
139                                    -o X"`basename $2`" = X"tar" \) ]; then
140                                 CMD=$TAR
141                                 ARGS="tf -"
142                         elif [ X"$TAR" != X"" \
143                                -a X"$SAMBA_CLIENT" != X"" \
144                                -a X"$2" = X"$SAMBA_CLIENT" ]; then
145                                 CMD=$TAR
146                                 ARGS="tf -"
147                         elif [ X"$DUMP" != X"" -a X"$2" = X"$DUMP" ]; then
148                                 CMD=$RESTORE
149                                 if [ $IS_AIX -eq 1 ]; then
150                                         ARGS=-tB
151                                 else
152                                         ARGS="tbf 2 -"
153                                 fi
154                         elif [ X"$VDUMP" != X"" -a X"$2" = X"$VDUMP" ]; then
155                                 CMD=$VRESTORE
156                                 ARGS="tf -"
157                         elif [ X"$VXDUMP" != X"" -a X"$2" = X"$VXDUMP" ]; then
158                                 CMD=$VXRESTORE
159                                 ARGS="tbf 2 -"
160                         elif [ X"$XFSDUMP" != X"" -a X"$2" = X"$XFSDUMP" ]; then
161                                 CMD=$XFSRESTORE
162                                 ARGS="-t -v silent -"
163                         else
164                                 echo `_ '** Cannot do %s dumps' "$2"` > $TEMP/errors
165                                 result=999      # flag as not really an error
166                         fi
167                 else
168                         echo `_ '** Cannot find dump type'` > $TEMP/errors
169                 fi
170         fi
171         echo $CMD > $TEMP/onefile.cmd
172         if [ X"`echo $HEADER | grep '^AMANDA: SPLIT_FILE'`" != X"" ]; then
173             result=500
174             set X $HEADER
175             shift 7
176             echo $1 | cut -f7 -d' ' > $TEMP/onefile.partnum
177         elif [ X"$CMD" != X"" ]; then
178                 if [ -x $CMD ]; then
179                         $CMD $ARGS > /dev/null 2> $TEMP/errors
180                         result=$?
181                 else
182                         echo `_ '** Cannot execute %s' "$CMD"` > $TEMP/errors
183                 fi
184         fi
185         cat >/dev/null                          # soak up the rest of the image
186         echo $result
187 }
188
189 #
190 # some paths
191 #
192 #       CONFIG_DIR      directory in which the config file resides
193 #       AMRESTORE       full path name of amrestore
194 #       AMGETCONF       full path name of amgetconf
195 #       AMTAPE          full path name of amtape
196 #       TAR             ditto for GNU-tar
197 #       SAMBA_CLIENT    ditto for smbclient
198 #       DUMP            ditto for the system dump program
199 #       RESTORE         ditto for the system restore program
200 #       VDUMP           ditto for the system dump program
201 #       VRESTORE        ditto for the system restore program
202 #       VXDUMP          ditto for the system dump program
203 #       VXRESTORE       ditto for the system restore program
204 #       XFSDUMP         ditto for the system dump program
205 #       XFSRESTORE      ditto for the system restore program
206 #       DD              ditto for dd
207 #       MT              ditto for mt
208 #       MTF             flag given to MT to specify tape device: -f or -t
209 #       MAIL            mail program
210 #       IS_AIX          true if this is an AIX system
211
212 CONFIG_DIR=@CONFIG_DIR@
213 amlibexecdir=$amlibexecdir
214 sbindir=$sbindir
215 AMRESTORE=$sbindir/amrestore$SUF
216 AMGETCONF=$sbindir/amgetconf$SUF
217 AMTAPE=$sbindir/amtape$SUF
218 TAR=@GNUTAR@
219 SAMBA_CLIENT=@SAMBA_CLIENT@
220 DUMP=@DUMP@
221 RESTORE=@RESTORE@
222 VDUMP=@VDUMP@
223 VRESTORE=@VRESTORE@
224 VXDUMP=@VXDUMP@
225 VXRESTORE=@VXRESTORE@
226 XFSDUMP=@XFSDUMP@
227 XFSRESTORE=@XFSRESTORE@
228 MAIL=@MAILER@
229 DD=@DD@
230
231 . ${amlibexecdir}/chg-lib.sh
232
233 #
234 # config file
235 #
236 SLOT=0
237 CONFIG=$1
238 [ X"$CONFIG" = X"" ] \
239         && $Echoe "usage: amverify$SUF <config> [slot [ runtapes ] ]" \
240         && exit 1
241
242 AMCONFIG=$CONFIG_DIR/$CONFIG/amanda.conf
243 [ ! -f $AMCONFIG ] \
244         && $Echoe "Cannot find config file $AMCONFIG" \
245         && exit 1
246
247 TPCHANGER=`getparm tpchanger`
248 if [ X"$TPCHANGER" = X"" ]; then
249         $Echoe "No tape changer..."
250         DEVICE=`getparm tapedev`
251         [ X"$DEVICE" = X"" ] \
252                 && $Echoe "No tape device..." \
253                 && exit 1
254         $Echoe "Tape device is $DEVICE..."
255         SLOTS=1
256 else
257         CHANGER_SLOT=${2:-current}
258         $Echoe "Tape changer is $TPCHANGER..."
259         SLOTS=${3:-`getparm runtapes`}
260         [ X"$SLOTS" = X"" ] && SLOTS=1
261         if [ $SLOTS -eq 1 ]; then
262                 p=""
263         else
264                 p=s
265         fi
266         $Echoe "$SLOTS slot${p}..."
267         MAXRETRIES=2
268 fi
269
270 #
271 # check the accessability
272 #
273 [ X"$TAR" != X"" -a ! -x "$TAR" ] \
274         && $Echoe "GNU tar not found: $TAR"
275 [ X"$DUMP" != X"" -a \( X"$RESTORE" = X"" -o ! -x "$RESTORE" \) ] \
276         && $Echoe "System restore program not found: $RESTORE"
277 [ X"$VDUMP" != X"" -a \( X"$VRESTORE" = X"" -o ! -x "$VRESTORE" \) ] \
278         && $Echoe "System restore program not found: $VRESTORE"
279 [ X"$VXDUMP" != X"" -a \( X"$VXRESTORE" = X"" -o ! -x "$VXRESTORE" \) ] \
280         && $Echoe "System restore program not found: $VXRESTORE"
281 [ X"$XFSDUMP" != X"" -a \( X"$XFSRESTORE" = X"" -o ! -x "$XFSRESTORE" \) ] \
282         && $Echoe "System restore program not found: $XFSRESTORE"
283 [ ! -x $AMRESTORE ] \
284         && $Echoe "amrestore not found: $AMRESTORE" \
285         && exit 1
286
287 REPORTTO=`getparm mailto`
288 if [ X"$REPORTTO" = X"" ]; then
289         $Echoe "No notification by mail!"
290 else
291         $Echoe "Verify summary to $REPORTTO"
292 fi
293
294 ORG=`getparm org`
295 if [ X"$ORG" = X"" ]; then
296         $Echoe "No org in amanda.conf -- using $CONFIG"
297         ORG=$CONFIG
298 fi
299
300 #
301 # ok, let's do it
302 #
303 #       TEMP            directory for temporary tar archives and stderr
304 #       DEFECTS         defect list
305 #       REPORT          report for mail
306
307 if [ ! -d @AMANDA_TMPDIR@ ]; then
308   $Echoe "amverify: directory @AMANDA_TMPDIR@ does not exist."
309   exit 1
310 fi
311
312 cd @AMANDA_TMPDIR@ || exit 1
313
314 TEMP=@AMANDA_TMPDIR@/amverify.$$
315 trap 'rm -fr $TEMP' 0
316 if ( umask 077 ; mkdir $TEMP ) ; then
317         :
318 else
319         $Echoe "Cannot create $TEMP"
320         exit 1
321 fi
322 DEFECTS=$TEMP/defects; rm -f $DEFECTS
323 REPORT=$TEMP/report; rm -f $REPORT
324 TAPELIST=
325 EXITSTAT=$TEMP/amrecover.exit; rm -rf $EXITSTAT
326
327 trap 'report "aborted!"; echo `_ 'aborted!'` >> $DEFECTS; sendreport; rm -fr $TEMP; exit 1' 1 2 3 4 5 6 7 8 10 12 13 14 15
328
329 $Echoe "Defects file is $DEFECTS"
330 report "amverify $CONFIG"
331 report "`date`"
332 report ""
333
334 # ----------------------------------------------------------------------------
335
336 SPLIT_DUMPS= # this will keep track of split dumps that we'll tally later
337 while [ $SLOT -lt $SLOTS ]; do
338         SLOT=`expr $SLOT + 1`
339         #
340         # Tape Changer: dial slot
341         #
342         if [ X"$TPCHANGER" != X"" ]; then
343                 report "Loading ${CHANGER_SLOT} slot..."
344                 $AMTAPE $CONFIG slot $CHANGER_SLOT > $TEMP/amtape.out 2>&1
345                 THIS_SLOT=$CHANGER_SLOT
346                 CHANGER_SLOT=next
347                 RESULT=`grep "changed to slot" $TEMP/amtape.out`
348                 [ X"$RESULT" = X"" ] \
349                         && report "** Error loading slot $THIS_SLOT" \
350                         && report "`cat $TEMP/amtape.out`" \
351                         && cat $TEMP/amtape.out >> $DEFECTS \
352                         && continue
353                 DEVICE=`$AMTAPE $CONFIG device`
354         fi
355         report "Using device $DEVICE"
356         $Echon "Waiting for device to go ready..."
357         count=1800
358         while true; do
359             amdevcheck_output="`amdevcheck $CONFIG $DEVICE`"
360             amdevcheck_status=$?
361             if [ $amdevcheck_status -eq 0 ]; then
362                 break;
363             else
364                 if echo $amdevcheck_output | grep UNLABELED > /dev/null; then
365                     if [ count -lt 0 ]; then
366                         report "Device not ready"
367                         break;
368                     fi
369                     sleep 3
370                     count=`expr $count - 3`
371                 else
372                     report "Volume in $DEVICE unlabeled."
373                     break;
374                 fi
375             fi
376         done
377         $Echon "Processing label..."
378         amtape_output="`amtape $CONFIG current 2>&1`";
379         if echo "$amtape_output" | \
380             egrep "^slot +[0-9]+: time [^ ]+ +label [^ ]+" > /dev/null; then
381             : # everything is fine
382         else
383             report "Error reading tape label using amtape."
384             continue
385         fi
386         
387         set X $amtape_output
388         until [ "$1" = "time" ]; do
389             shift
390         done
391         
392         VOLUME=$4
393         DWRITTEN=$2
394         report "Volume $VOLUME, Date $DWRITTEN"
395         [ X"$DWRITTEN" = X"0" -o X"$DWRITTEN" = X"X" ] \
396                 && report "Fresh tape. Skipping..." \
397                 && continue
398         TAPELIST="$TAPELIST $VOLUME"
399
400         FILENO=0
401         ERG=0
402         ERRORS=0
403         while [ $ERG = 0 ]; do
404                 FILENO=`expr $FILENO + 1`
405 #            { cat <<EOF; dd if=/dev/zero bs=32k count=1; } | doonefile
406 #AMANDA: FILE 20070925205916 localhost /boot  lev 0 comp N program /bin/tar
407 #To restore, position tape at start of file and run:
408 #        dd if=<tape> bs=32k skip=1 |      /bin/tar -xpGf - ...
409 #EOF
410                 RESULT=`$AMRESTORE -h -p -f $FILENO $DEVICE \
411                             2> $TEMP/amrestore.out \
412                         | doonefile 2> $TEMP/onefile.errors`
413                 FILE=`grep restoring $TEMP/amrestore.out \
414                         | sed 's/^.*restoring //'`
415                 if [ X"$FILE" != X"" -a X"$RESULT" = X"0" ]; then
416                         report "Checked $FILE"
417                 elif [ X"$FILE" != X"" -a X"$RESULT" = X"500" ]; then
418                         report "Skipped `cat $TEMP/onefile.cmd` check on partial dump $FILE"
419                         dump="`echo $FILE | cut -d'.' -f'1,2,3,4'`"
420                         cat $TEMP/onefile.partnum >> $TEMP/$dump.parts
421                         if [ X"`echo $SPLIT_DUMPS | grep $dump`" = X"" ]; then
422                             SPLIT_DUMPS="$dump $SPLIT_DUMPS"
423                         fi
424                 elif [ X"$FILE" != X"" -a X"$RESULT" = X"999" ]; then
425                         report "Skipped $FILE (`cat $TEMP/errors`)"
426                 elif [ -z "$FILE" ]; then
427                         # Unless we went over, there is no extra output.
428                         report "End-of-Tape detected."
429                         break
430                 else
431                         report "** Error detected ($FILE)"
432                         echo "$VOLUME ($FILE):" >>$DEFECTS
433                         [ -s $TEMP/amrestore.out ] \
434                                 && report "`cat $TEMP/amrestore.out`" \
435                                 && cat $TEMP/amrestore.out >>$DEFECTS
436                         [ -s $TEMP/errors ] \
437                                 && report "`cat $TEMP/errors`" \
438                                 && cat $TEMP/errors >>$DEFECTS
439                         [ -s $TEMP/onefile.errors ] \
440                                 && report "`cat $TEMP/onefile.errors`" \
441                                 && cat $TEMP/onefile.errors >>$DEFECTS
442                         ERRORS=`expr $ERRORS + 1`
443                         [ $ERRORS -gt 5 ] \
444                                 && report "Too many errors." \
445                                 && break
446                 fi
447         done
448         rm -f $TEMP/header \
449               $TEMP/amtape.out \
450               $TEMP/amrestore.out \
451               $TEMP/errors \
452               $TEMP/onefile.cmd \
453               $TEMP/onefile.partnum \
454               $TEMP/onefile.errors
455 done
456
457 [ -s $DEFECTS ] \
458         && $Echoe "Errors found: " \
459         && cat $DEFECTS
460
461 # Work out whether any split dumps we saw had all their parts
462 for dump in $SPLIT_DUMPS;do
463     report ""
464     numparts=0
465     max=0
466     max_known=0
467     missing=0
468     # figure out 
469     for part in `cat $TEMP/$dump.parts`;do
470         cur="`echo $part | cut -d/ -f1`"
471         max="`echo $part | cut -d/ -f2`"
472         if [ $max != "UNKNOWN" ]; then
473             numparts=$max
474             max_known=1
475             break;
476         fi
477         if [ $cur -gt $numparts ]; then
478             numparts=$cur
479         fi
480     done
481     report "Split dump $dump should have $numparts total pieces"
482     if [ $max_known != 1 ]; then
483         report "NOTE: Header field for total pieces was UNKNOWN, $numparts is best guess"
484     fi
485     part=1
486     while [ $part -lt $numparts ];do
487         part=`expr $part + 1`
488         if [ X"`grep \"^$part/\" $TEMP/$dump.parts`" = X"" ];then
489             report "Spanning chunk part $part is missing!"
490             missing=`expr $missing + 1`
491         fi
492     done
493     if [ $missing = 0 ];then
494         report "All parts found"        
495     fi
496     rm -f $TEMP/$dump.parts
497 done
498
499 sendreport
500
501 exit 0