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