#! /bin/bash

#
# babarchive_check_all
#
# Copyright (C) 2006-2016 by John Heidemann  <johnh@isi.edu>
#
#    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.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License along
#    with this program; if not, write to the Free Software Foundation, Inc.,
#    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#

# default location for queue and scratch state
USER_ARCHIVE_DIR=$HOME/.config/babarchive
ROOT_ARCHIVE_DIR=/var/spool/babarchive
# was ~/.archive_check

usage () {
    cat 1>&2 <<END
usage: $0 [-n N] [-D S] [-c CONFIGDIR] [-v]

Babarchive_check_all tracks all babarchives (barchive-checksumed directory trees) on
this computer.  Checks one or more archive per invocation.
Each invocation checks different archives until all have been checked, then
it start over.

(As a hack, it allows .shasum.fsdb to have an incorrect checksum.)

This program assumes the locate(1) database is kept up-to-date,
and uses that database to find all existing archives on the current system.

By default it checks one archive.  However, -n and -D can limit the
number of archives to check and how long to run.

By default, it generates no output if all is well.
Run with -v to see more detail.

Options:

-n N   maximum number of archives to check

-D S   duration to check (in seconds): if given, we will stop after having cumulative
	checks have run longer than this duration, in seconds.
	(So one can check multiple things in 5 minutes with -n 100 -D 300)

-c CONFIG  specific a directory for CONFIGuration information,
	where statistics and logs are kept
	(default is $ARCHIVE_DIR when run by a user,
	or $ROOT_ARCHIVE_DIR when run by root)

-v    verbose output (show what's going on)

-m mailto@destination   Send mail to the given user if there is any output
                           (for example, root@localhost or sysadmin@example.com).

Bugs:

We no longer automatically check md5sum.jdb-checksumed archives.

END
    exit 1
}

MAIL_OUTPUT=undef
send_pending_mail() {
        test "x$MAIL_OUTPUT" = xundef && return
        test -s $MAIL_OUTPUT && {
                # sending mail is a bear
                # mailx, mail, ssmtp ... nothing is standard
                # We fall back on /usr/lib/sendmail (which also is emulated in postfix)
                # as the Most Standard.
                # But, ugh.
                {
                        echo "Subject: babarchive_check_all problem encountered"
                        echo "To: $MAIL_DESTINATION"
                        echo
                        cat $MAIL_OUTPUT
                } | /usr/lib/sendmail -oi -t
        }
}        

die () {
	echo "$@" 1>&2
        send_pending_mail
	exit 1
}

test "x$1" = 'x-?' && usage

cd -P `dirname $0`
SUBPROGRAM_DIR=$PWD
CHECK_ONE=$SUBPROGRAM_DIR/babarchive_check_one

DEBUG=false
VERBOSE=false
ARCHIVE_DIR=undef
COUNT=1
DURATION=undef
MAIL_DESTINATION=undef
while getopts c:dD:m:n:v ch
do
        case $ch in
	c) ARCHIVE_DIR=$OPTARG;;
        d) DEBUG=true;;
        D) DURATION=$OPTARG;;
	n) COUNT=$OPTARG;;
	m) MAIL_DESTINATION="$OPTARG";;
        v) VERBOSE=true;;
        *) usage;;
        esac
done
shift `expr $OPTIND - 1`

#
# mail output?
# (will get called back from the end)
#
if [ "x$MAIL_DESTINATION" != xundef ]
then
        MAIL_OUTPUT=`mktemp -t babarchive_check_all.XXXXXXXX~`
        exec >$MAIL_OUTPUT 2>&1
fi

#
# check our working directory
#
test "x$ARCHIVE_DIR" = xundef && 
	if [ "x`id -un`" = xroot ]
	then
		ARCHIVE_DIR=$ROOT_ARCHIVE_DIR
	else
		ARCHIVE_DIR=$USER_ARCHIVE_DIR
	fi
test -d $ARCHIVE_DIR || {
	mkdir -p $ARCHIVE_DIR || die "$0: cannot mkdir $ARCHIVE_DIR"
	$VERBOSE && echo "$0: mkdir new $ARCHIVE_DIR"
}
cd $ARCHIVE_DIR || die "$0: cannot cd $ARCHIVE_DIR"

#
# check what ones we have
#
{
	locate -r 'shasum.fsdb$';
	locate -r 'sha1sum.jdb$';
} | sort -f >new_archives
test -s new_archives || {
	$VERBOSE && echo "$0: no archives on system (maybe your locate(1) database is not complete?)"
	exit 0
	# no need to be noisy
	die "$0: no archives on the system"
}
test -e old_archives && {
	diff -u old_archives new_archives >archives.diff
	grep '^-' <archives.diff && {
		echo "$0: archives removed"
	}
	grep '^+' <archives.diff && {
		echo "$0: archives added"
	}
	$VERBOSE && {
		grep '^[-+]' <archives.diff >/dev/null || echo "$0: no change in archives on this system"
	}
}
mv new_archives old_archives

#
# figure out what to check
#

reloaded=false

# loop to actually check one
overall_start_time=`date +%s`
while true
do

	# reload, if necessary
	test -s check_queue || {
		# read them in reverse order, since new archives have largest dates
		tac < old_archives > check_queue
		$VERBOSE && echo "$0: reloading archives-to-check queue"
		echo "*** archive reload on `date`" >>check.log
		if $reloaded
		then
			echo "SECOND RELOAD ATTEMPTED, unable to find any good archives `date`" >>check.log
			die "$0: SECOND RELOAD ATTEMPTED"
			break
		fi
		reloaded=true
	}

	# get the first
	check_sha1=`head -1 check_queue`
	mv check_queue check_queue~
	cat check_queue~ | sed 1d >check_queue

	# check it
	check_dir=`dirname $check_sha1`
	$VERBOSE && echo "$0: checking archive $check_dir"
	echo $check_dir >check_current

	echo "" >>check.log 
	echo "$check_dir `date`" >>check.log

	# does it exist?
	test -d $check_dir/. || {
		echo "$0: archive $check_dir doesn't exist"
		echo "MISSING: $check_dir `date`" >>check.log
		# try again
		continue
	}

	start_time=`date +%s`
	$CHECK_ONE $check_dir >check.out 2>check.err || die "$0: check failed on archive $check_dir (see $ARCHIVE_DIR/check.{out,err} for details)"
	end_time=`date +%s`
	elapsed_time=$(( $end_time - $start_time ))
	echo "        in $elapsed_time seconds" >>check.log

	# allow .sha1sum.jdb to fail since it can change during prep_cdrom
	if grep 'FAILED$' check.out | grep -v '^\./\.sha1sum\.jdb' >>check.err
	then
		{ 
			echo "ERROR: the following files failed in $check_dir:"
			cat check.err
		} | tee -a check.log #also to stdout for emailing
	else
		$VERBOSE && echo "$0: archive $check_dir is OK"
	fi

	# more to do?
	COUNT=`expr $COUNT - 1`
	# exit the infinite loop
	test $COUNT -le 0 && break

	# how long have we run?
	overall_run_time=`date +%s`
	overall_elapsed_time=$(( $overall_run_time - $overall_start_time ))
        if [ "x$DURATION" = xundef ]
        then
		$VERBOSE && echo "$0: time $overall_elapsed_time but no duration given, so going back for more"
        elif [ $overall_elapsed_time -le $DURATION ]
	then
		$VERBOSE && echo "$0: time $overall_elapsed_time < $DURATION, so going back for more"
	else
		$VERBOSE && echo "$0: time $overall_elapsed_time > $DURATION, so time to die"
		break
	fi
done

send_pending_mail
exit 0
