#!/bin/sh
#
# (C) 1996 by ICEM Systems GmbH
# Author: Axel Zinser (fifi@icem.de)
#
# amverify: check amanda tapes and report errors

prefix=/usr/local
exec_prefix=${prefix}
sbindir=${exec_prefix}/sbin
libexecdir=${exec_prefix}/libexec

PATH=$libexecdir:$sbindir:/usr/bin:/bin:/usr/sbin:/sbin:/usr/ucb
export PATH

USE_VERSION_SUFFIXES="no"
if [ "$USE_VERSION_SUFFIXES" = "yes" ]; then
	SUF="-2.4.1p1"
else
	SUF=
fi

report() {
	$ECHO "$@" >&2
	$ECHO "$@" >> $REPORT
}

getparm() {
	(cd $CONFIG_DIR/$CONFIG; $AMGETCONF $1 2>/dev/null) | grep -v BUGGY
}

sendreport() {
	TAPES=`cat $TAPELIST`
	if [ -f $REPORT -a X"$REPORTTO" != X"" ]; then
		(
		$ECHO "Tapes: $TAPES"
		if [ -s $DEFECTS ]; then
			$ECHO "Errors found: "
			cat $DEFECTS
		else
			$ECHO "No errors found!"
		fi
		$ECHO

		[ -s $REPORT ] \
			&& cat $REPORT
		) | $MAIL -s "$ORG AMANDA VERIFY REPORT FOR $TAPES" $REPORTTO
	fi
}

###
# This function is called to process one dump image.  Standard input is
# the dump image.  We parse the header and decide if it is a GNU tar
# dump or a system dump.  Then we do a catalog operation to /dev/null
# and finally a "cat" to /dev/null to soak up whatever data is still in
# the pipeline.
#
# In the case of a system restore catalogue, this does not fully check
# the integrity of the dump image because system restore programs stop
# as soon as they are done with the directories, which are all at the
# beginning.  But the trailing cat will at least make sure the whole
# image is readable.
###

doonefile() {

	###
	# The goal here is to collect the first 32 KBytes and save the
	# first line.  But the pipe size coming in to us from amrestore
	# is highly system dependent and "dd" does not do reblocking.
	# So we pick a block size that is likely to always be available in
	# the pipe and a count to take it up to 32 KBytes.  Worst case,
	# this could be changed to "bs=1 count=32k".  We also have to
	# soak up the rest of the output after the "head" so an EPIPE
	# does not go back and terminate the "dd" early.
	###

	HEADER=`$DD bs=512 count=64 | ( head -1 ; cat > /dev/null )`
	CMD=
	result=1
	if [ X"$HEADER" = X"" ]; then
		$ECHO "** No header" > $TEMP/errors
	else
		set X $HEADER
		shift
		shift 9
		if [ X"$1" = X"program" -a X"$2" != X"" ]; then
			if [ X"$TAR" != X"" -a X"$2" = X"$TAR" ]; then
				CMD=$TAR
				ARGS="tf -"
			elif [ X"$DUMP" != X"" -a X"$2" = X"$DUMP" ]; then
				CMD=$RESTORE
				if [ $IS_AIX -eq 1 ]; then
					ARGS=-tB
				else
					ARGS="tbf 2 -"
				fi
			else
				$ECHO "** Cannot do $2 dumps" > $TEMP/errors
				result=999	# flag as not really an error
			fi
		else
			$ECHO "** Cannot find dump type" > $TEMP/errors
		fi
	fi
	if [ X"$CMD" != X"" ]; then
		$CMD $ARGS > /dev/null 2> $TEMP/errors
		result=$?
	fi
	cat >/dev/null
	$ECHO $result
}

#
# some paths
#
#	CONFIG_DIR	directory in which the config file resides
#	AMRESTORE	full path name of amrestore
#	AMGETCONF	full path name of getconf
#	TAR		ditto for GNU-tar
#	DUMP		ditto for the system dump program
#	RESTORE		ditto for the system restore program
#	MT		ditto for mt
#	DD		ditto for dd
#	MAIL		mail program
#	ECHO		echo
#	ECHOARG		echo argument to supress line feed
#	ECHOSUF		echo meta character to supress line feed
#	MTOPT		flag given to MT to specify tape device: -f or -t
#	IS_AIX		true if this is an AIX system

CONFIG_DIR=/etc/amanda
libexecdir=$libexecdir
sbindir=$sbindir
AMRESTORE=$sbindir/amrestore$SUF
AMGETCONF=$libexecdir/getconf$SUF
AMTAPE=$sbindir/amtape$SUF
TAR=/usr/local/bin/gtar
DUMP=/sbin/dump
RESTORE=/sbin/restore
MT=/bin/mt
MAIL=/usr/bin/Mail
DD=/bin/dd
ECHO=/bin/echo
ECHOARG=""
ECHOSUF="\c"
MTOPT=-f
if [ `/bin/uname -s 2>/dev/null` = AIX ]; then
	IS_AIX=1
else
	IS_AIX=0
fi

#
# config file
#
SLOT=0
CONFIG=$1
[ X"$CONFIG" = X"" ] \
	&& $ECHO "usage: amverify$SUF <config>" >&2 \
	&& exit 1

AMCONFIG=$CONFIG_DIR/$CONFIG/amanda.conf
[ ! -f $AMCONFIG ] \
	&& $ECHO "Cannot find config file $AMCONFIG" >&2 \
	&& exit 1

cd $CONFIG_DIR/$CONFIG

TPCHANGER=`getparm tpchanger`
if [ X"$TPCHANGER" = X"" ]; then
	$ECHO "No tape changer..."
	DEVICE=`getparm tapedev`
	[ X"$DEVICE" = X"" ] \
		&& $ECHO "No tape device..." >&2 \
		&& exit 1
	[ ! -c $DEVICE ] \
		&& $ECHO "Not a character special device: $DEVICE" >&2 \
		&& exit 1
	$ECHO "Tape device is $DEVICE..."
	SLOTS=1
else
	CHANGER_SLOT=${2:-current}
	$ECHO "Tape changer is $TPCHANGER..."
	SLOTS=`getparm runtapes`
	[ X"$SLOTS" = X"" ] && SLOTS=1
	if [ $SLOTS -eq 1 ]; then
		p=""
	else
		p=s
	fi
	$ECHO "$SLOTS slot${p}..."
	MAXRETRIES=2
fi

#
# check the accessability
#
[ X"$TAR" != X"" -a ! -x "$TAR" ] \
	&& $ECHO "GNU tar not found: $TAR" >&2
[ X"$DUMP" != X"" -a \( X"$RESTORE" = X"" -o ! -x "$RESTORE" \) ] \
	&& $ECHO "System restore program not found: $RESTORE" >&2
[ ! -x $AMRESTORE ] \
	&& $ECHO "amrestore not found: $AMRESTORE" >&2 \
	&& exit 1

REPORTTO=`getparm mailto`
if [ X"$REPORTTO" = X"" ]; then
	$ECHO "No notification by mail!"
else
	$ECHO "Verify summary to $REPORTTO"
fi

ORG=`getparm org`
if [ X"$ORG" = X"" ]; then
	$ECHO "No org in amanda.conf -- using $CONFIG"
	ORG=$CONFIG
fi

#
# ok, let's do it
#
#	TEMP		directory for temporary tar archives and stderr
#	DEFECTS		defect list
#	REPORT		report for mail

TEMP=/tmp/amverify.$$
trap 'rm -fr $TEMP' 0
if ( umask 077 ; mkdir $TEMP ) ; then
	:
else
	$ECHO "Cannot create $TEMP" >&2
	exit 1
fi
DEFECTS=$TEMP/defects; rm -f $DEFECTS
REPORT=$TEMP/report; rm -f $REPORT
TAPELIST=$TEMP/tapelist; rm -f $TAPELIST
EXITSTAT=$TEMP/amrecover.exit; rm -rf $EXITSTAT

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

$ECHO "Defects file is $DEFECTS" >&2
report "amverify $CONFIG"
report "`date`"
report ""

# ----------------------------------------------------------------------------

while [ $SLOT -lt $SLOTS ]; do
	SLOT=`expr $SLOT + 1`
	#
	# Tape Changer: dial slot
	#
	if [ X"$TPCHANGER" != X"" ]; then
		report "Loading ${CHANGER_SLOT} slot..."
		$AMTAPE $CONFIG slot $CHANGER_SLOT > $TEMP/amtape.out 2>&1
		THIS_SLOT=$CHANGER_SLOT
		CHANGER_SLOT=next
		RESULT=`grep "changed to slot" $TEMP/amtape.out`
		[ X"$RESULT" = X"" ] \
			&& report "** Error loading slot $THIS_SLOT" \
			&& report "`cat $TEMP/amtape.out`" \
			&& cat $TEMP/amtape.out >> $DEFECTS \
			&& continue
		DEVICE=`$AMTAPE $CONFIG device`
	fi
	report "Using device $DEVICE"
	if [ $IS_AIX -eq 0 ]; then

		# The AIX "mt stat" function does not really do anything
		# w.r.t. checking the drive for ready, and in fact, will
		# fail under some conditions (e.g. if the tape "file"
		# is a symlink to the real device).  We let the rewind
		# right after this take care of the cases "mt stat"
		# does not catch.

		$ECHO $ECHOARG "Waiting for device to go ready...\r$ECHOSUF" >&2
		until $MT $MTOPT $DEVICE stat >/dev/null 2>&1; do
			sleep 3
		done
	fi
	$ECHO $ECHOARG "Rewinding...                             \r$ECHOSUF" >&2
	until $MT $MTOPT $DEVICE rewind; do
		sleep 3
	done
	$ECHO $ECHOARG "Processing label...\r$ECHOSUF" >&2
	$DD if=$DEVICE count=1 bs=32k 2> $TEMP/errors > $TEMP/header
	[ ! -s $TEMP/header ] \
		&& report "** Error reading label on tape" \
		&& cat $TEMP/errors >> $DEFECTS \
		&& continue
	TAPENDATE=`grep AMANDA: $TEMP/header | sed 's/^AMANDA: TAPESTART //'`
	[ X"$TAPENDATE" = X"" ] \
		&& report "** No amanda tape in slot" \
		&& continue
	set X $TAPENDATE
	shift
	VOLUME=$4
	DWRITTEN=$2
	report "Volume $VOLUME, Date $DWRITTEN"
	[ X"$DWRITTEN" = X"0" -o X"$DWRITTEN" = X"X" ] \
		&& report "Fresh tape. Skipping..." \
		&& continue
	$ECHO $ECHOARG "$VOLUME $ECHOSUF" >> $TAPELIST
	$ECHO $ECHOARG "Rewinding...\r$ECHOSUF" >&2
	until $MT $MTOPT $DEVICE rewind; do
		sleep 3
	done
	ERG=0
	ERRORS=0
	while [ $ERG = 0 ]; do
		$ECHO $ECHOARG "Reading...\r$ECHOSUF" >&2
		RESULT=`$AMRESTORE -h -p $DEVICE 2> $TEMP/amrestore.out \
			| doonefile 2> $TEMP/onefile.errors`
		FILE=`grep restoring $TEMP/amrestore.out | sed 's/^.*restoring //'`
		EOF=`grep "reached end of tape" $TEMP/amrestore.out`
		# amrestore:   0: restoring sundae._mnt_sol1_usr.19961127.1
		if [ X"$FILE" != X"" -a X"$RESULT" = X"0" ]; then
			report "Checked $FILE"
		elif [ X"$FILE" != X"" -a X"$RESULT" = X"999" ]; then
			report "Skipped $FILE (`cat $TEMP/errors`)"
		elif [ -n "$EOF" ]; then
			report "End-of-Tape detected."
			break
		else
			report "** Error detected ($FILE)"
			$ECHO "$VOLUME ($FILE):" >>$DEFECTS
			[ -s $TEMP/amrestore.out ] \
				&& report "`cat $TEMP/amrestore.out`" \
				&& cat $TEMP/amrestore.out >>$DEFECTS
			[ -s $TEMP/errors ] \
				&& report "`cat $TEMP/errors`" \
				&& cat $TEMP/errors >>$DEFECTS
			[ -s $TEMP/onefile.errors ] \
				&& report "`cat $TEMP/onefile.errors`" \
				&& cat $TEMP/onefile.errors >>$DEFECTS
			ERRORS=`expr $ERRORS + 1`
			[ $ERRORS -gt 5 ] \
				&& report "Too many errors." \
				&& break
		fi
	done
	$ECHO $ECHOARG "Rewinding...\r$ECHOSUF" >&2
	until $MT $MTOPT $DEVICE rewind; do
		sleep 3
	done
	$ECHO
	rm -f $TEMP/header \
	      $TEMP/amtape.out \
	      $TEMP/amrestore.out \
	      $TEMP/errors \
	      $TEMP/onefile.errors
done

if [ X"$TPCHANGER" != X"" ]; then
	report "Advancing past the last tape..."
	$AMTAPE $CONFIG slot advance 2> $TEMP/amtape.out
	RESULT=`grep "changed to slot" $TEMP/amtape.out`
	[ X"$RESULT" = X"" ] \
		&& report "** Error advancing after last slot" \
		&& report "`cat $TEMP/amtape.out`" \
		&& cat $TEMP/amtape.out >> $DEFECTS
fi

$ECHO >> $TAPELIST

[ -s $DEFECTS ] \
	&& $ECHO "Errors found: " \
	&& cat $DEFECTS

sendreport

exit 0
