+#! @BASH@
+###
+### Check that dumps have been made as expected.
+###
+### (c) 2013 Mark Wooding
+###
+
+###----- Licensing notice ---------------------------------------------------
+###
+### This file is part of the `rsync-backup' program.
+###
+### rsync-backup 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, or
+### (at your option) any later version.
+###
+### rsync-backup 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 rsync-backup; if not, write to the Free Software Foundation,
+### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+set -e
+
+quis=${0##*/}
+. @pkgdatadir@/lib.sh
+
+###--------------------------------------------------------------------------
+### Stubs for the configuration file.
+
+defhook () { :; }
+addhook () { :; }
+
+retain () { :; }
+snaptype () { :; }
+like () { :; }
+retry () { :; }
+user () { :; }
+
+###--------------------------------------------------------------------------
+### Output format switch.
+
+formats=:
+defformat () { formats=$formats$1:; }
+fmtstate=begin
+
+fmt () {
+ state=$1; shift
+
+ while :; do
+ ostate=$fmtstate
+ case $fmtstate in $state) break ;; esac
+ case "$fmtstate,$state" in
+ begin,end)
+ fmtstate=end
+ ;;
+ begin,*)
+ ${fmt}_header "$@"
+ fmtstate=hosttbl_begin
+ ;;
+ hosttbl_begin,hosttbl_*)
+ ${fmt}_hosttbl_begin "$@"
+ fmtstate=hosttbl_host
+ ;;
+ hosttbl_host,hosttbl_fs)
+ fmtstate=hosttbl_fs
+ ;;
+ hosttbl_fs,hosttbl_host)
+ ${fmt}_hosttbl_sep "$@"
+ fmtstate=hosttbl_host
+ ;;
+ hosttbl_begin,* | hosttbl_end,*)
+ fmtstate=logdump_begin
+ ;;
+ hosttbl_*,*)
+ ${fmt}_hosttbl_end "$@"
+ fmtstate=hosttbl_end
+ ;;
+ logdump_begin,logdump_*)
+ fmtstate=logdump_out
+ ;;
+ logdump_out,logdump_info | logdump_out,logdump_file)
+ fmtstate=$state
+ ;;
+ logdump_*,logdump_begin)
+ ${fmt}_logdump_end "$@"
+ fmtstate=logdump_begin
+ ;;
+ logdump_end,*)
+ fmtstate=footer
+ ;;
+ logdump_*,*)
+ fmtstate=logdump_end
+ ;;
+ footer,end)
+ ${fmt}_footer "$@"
+ fmtstate=end
+ ;;
+ esac
+ case $fmtstate in
+ "$ostate")
+ echo >&2 "$quis: FATAL! NO PROGRESS IN FMT STATE MACHINE"
+ exit 9
+ ;;
+ esac
+ done
+
+ ${fmt}_$fmtstate "$@"
+}
+
+###--------------------------------------------------------------------------
+### Plain text output.
+
+defformat txt
+
+txt_header () { :; }
+txt_hosttbl_begin () { :; }
+txt_hosttbl_host () { echo "HOST $1"; }
+txt_hosttbl_fs () { printf " %-23s %s\n" "$2" "$4"; }
+txt_hosttbl_sep () { echo; }
+txt_hosttbl_end () { :; }
+
+txt_logdump_begin () {
+ cat <<EOF
+
+-----------------------------------------------------------------------------
+Log tail for $1:$2
+
+EOF
+}
+
+txt_logdump_info () { echo "!!! $3"; }
+txt_logdump_file () { cat "$3"; }
+txt_logdump_end () { :; }
+
+txt_footer () { :; }
+txt_end () { :; }
+
+fmt=txt
+
+###--------------------------------------------------------------------------
+### HTML output.
+
+defformat html
+
+html_header () {
+ cat <<EOF
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
+ "http://www.w3c.org/TR/html4/strict.dtd">
+<html>
+<head>
+ <title>$now rsync-backup report</title>
+ <style type='text/css'><!--
+ body {
+ background: white;
+ color: black;
+ margin: 1ex 2em;
+ }
+
+ h1, h2 { font-family: sans-serif; font-weight: bold; }
+
+ h1 {
+ padding-bottom: 1ex;
+ border-bottom: solid medium black;
+ margin-bottom: 3ex;
+ font-size: 200%;
+ }
+
+ h2 {
+ border-top: solid thin black;
+ padding-top: 1ex;
+ margin-top: 3ex;
+ font-size: x-large;
+ }
+
+ h1 + h2 {
+ border-top: none;
+ padding-top: 0;
+ }
+
+ div.footer {
+ border-top: solid medium black;
+ margin-top: 3ex;
+ padding-top: 1ex;
+ font-size: small;
+ clear: both;
+ text-align: right;
+ font-style: italic;
+ }
+
+ table.hosttbl {
+ border-collapse: collapse;
+ display: inline-table;
+ margin: 1ex 1em;
+ }
+ table.hosttbl tr.fs td {
+ border: solid thin black;
+ }
+ table.hosttbl td { padding: 0.5ex 0.5em; }
+ table.hosttbl tr.host td {
+ padding-top: 2ex;
+ text-align: center;
+ font-weight: bold;
+ font-family: monospace;
+ }
+ table.hosttbl td.fs {
+ font-family: monospace;
+ }
+
+ table.hosttbl td.winning { background: #2f4; }
+ table.hosttbl td.failed { background: #f80; }
+ table.hosttbl td.broken { background: #f11; }
+ table.hosttbl td.never { background: #66f; }
+
+ pre {
+ clear: both;
+ margin: 3ex 2em;
+ padding: 1ex;
+ background: #eee;
+ border: solid thin black;
+ overflow-y: auto;
+ }
+
+ div.logdump-info {
+ margin: 3ex 2em;
+ padding: 1ex;
+ background: #f11;
+ border: solid thin black;
+ }
+ --></style>
+</head>
+<body>
+
+<h1><tt>rsync-backup</tt> report: $now</tt></h1>
+EOF
+}
+
+html_hosttbl_begin () {
+ cat <<EOF
+
+<h2>Overview</h2>
+EOF
+}
+
+html_hosttbl_host () {
+ cat <<EOF
+<table class=hosttbl>
+ <tr class=host><td colspan=2>$1</tc>
+EOF
+}
+
+html_hosttbl_fs () {
+ case $3 in
+ winning) link="" knil="" ;;
+ *) link="<a href='#log-$host:$fs'>" knil="</a>";;
+ esac
+ cat <<EOF
+ <tr class=fs>
+ <td class=fs>$2</td>
+ <td class=$3>$link$4$knil</td>
+EOF
+}
+
+html_hosttbl_sep () {
+ cat <<EOF
+</table>
+EOF
+}
+
+html_hosttbl_end () {
+ cat <<EOF
+</table>
+EOF
+}
+
+html_logdump_begin () {
+ cat <<EOF
+
+<h2><a name='log-$1:$2'>Log tail for <tt>$1:$2</tt></a></h2>
+EOF
+}
+
+html_logdump_info () {
+ cat <<EOF
+<div class=logdump-info>$3</div>
+EOF
+}
+
+html_logdump_file () {
+ cat <<EOF
+<pre class=logdump>
+EOF
+ cat "$3"
+ cat <<EOF
+</pre>
+EOF
+}
+
+html_logdump_end () { :; }
+
+html_footer () {
+ cat <<EOF
+
+<div class=footer>
+ Checked at $now $now_time.
+ <br><tt>rsync-backup</tt> $VERSION; © 2014 Mark Wooding
+</div>
+
+</body>
+</html>
+EOF
+}
+
+html_end () { :; }
+
+###--------------------------------------------------------------------------
+### Main checking.
+
+INDEXDB=@pkglocalstatedir@/index.db
+
+host () { host=$1; }
+
+patterns=
+showhost=
+failures=
+
+backup () {
+ for fs in "$@"; do
+ case $fs in *:*) fs=${fs%%:*} ;; esac
+ matchp=nil
+ for p in "${patterns[@]}"; do
+ case $host:$fs in $p) matchp=t; break ;; esac
+ done
+ case $matchp in nil) return ;; esac
+ when=$(sqlite3 $INDEXDB \
+ "SELECT MAX(date) FROM idx WHERE host = '$host' AND fs = '$fs';")
+ case $when in
+ "")
+ class=never
+ info="NEVER"
+ show=t
+ win=nil
+ ;;
+ "$now")
+ class=winning
+ info="today"
+ show=$verbose
+ win=t
+ ;;
+ *)
+ jdn=$(julian "$when")
+ ago=$(( $now_jdn - $jdn ))
+ case $ago in 1 | -1) days=day ;; *) days=days ;; esac
+ case $ago in
+ -*) class=future; info="${ago#-} $days in the FUTURE" ;;
+ 1 | 2) class=failed; info="$ago $days ago" ;;
+ *) class=broken; info="$ago $days ago" ;;
+ esac
+ show=t
+ win=nil
+ ;;
+ esac
+ case $show in
+ t)
+ case $showhost in
+ "$host") ;;
+ *)
+ fmt hosttbl_host $host
+ showhost=$host
+ ;;
+ esac
+ fmt hosttbl_fs $host $fs $class "$info"
+ case $win in
+ nil) failures="$failures $host:$fs" ;;
+ esac
+ ;;
+ esac
+ done
+}
+
+failure_logs () {
+ for fail in $failures; do
+ host=${fail%:*} fs=${fail##*:}
+ any=nil
+ for i in "$logdir/$host/$fs.$now#"*; do
+ if [ -f "$i" ]; then log=$i; any=t; fi
+ done
+ fmt logdump_begin $host $fs
+ case $any in
+ t) fmt logdump_file $host $fs "$log" ;;
+ nil) fmt logdump_info $host $fs "No log! No backup attempted." ;;
+ esac
+ done
+}
+
+###--------------------------------------------------------------------------
+### Read the configuration and we're done.
+
+usage () {
+ echo "usage: $quis [-v] [-c CONF] [-f FORMAT] [PATTERNS...]"
+}
+
+version () {
+ echo "$quis, rsync-backup version $VERSION"
+}
+
+verbose=nil
+
+while getopts "hVc:f:v" opt; do
+ case "$opt" in
+ h) usage; exit 0 ;;
+ V) version; config; exit 0 ;;
+ c) conf=$OPTARG ;;
+ f)
+ case $formats in
+ *":$OPTARG:"*) ;;
+ *) echo >&2 "$0: unknown format \`$OPTARG'"; exit 1 ;;
+ esac
+ fmt=$OPTARG
+ ;;
+ v) verbose=t ;;
+ *) exit 1 ;;
+ esac
+done
+shift $((OPTIND - 1))
+case $# in
+ 0) declare -a patterns=("*") ;;
+ *) declare -a patterns=("$@") ;;
+esac
+
+now=$(date +"%Y-%m-%d")
+now_time=$(date +"%H:%M:%S %z")
+now_jdn=$(julian $now)
+. "$conf"
+failure_logs
+fmt end
+
+###----- That's all, folks --------------------------------------------------