#!/usr/bin/perl -w # Check the status of da-backup backups # Copyright 2007 Stephen Gran # Copyright 2008 Peter Palfrader # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. use strict; use warnings; use English; use Getopt::Long; use Fcntl qw(:seek); my $DABACKUP_CONF = '/etc/da-backup.conf'; my $MAX_AGE = 40*60*60; my $TOO_FRESH = 5*60; my $MAX_LOCK_AGE = 24*60*60; my $LOCKFILE = '/var/lock/da-backup.lock'; my %CODE = ( 'UNDEF' => -1, 'OK' => 0, 'WARNING' => 1, 'CRITICAL' => 2, 'UNKNOWN' => 3 ); $SIG{__DIE__ } = sub() { print shift; exit $CODE{'UNKNOWN'}; }; my $EXITCODE = 'UNDEF'; my %MESSAGE = (); sub problem($$$) { my ($code, $msg, $resource) = @_; $MESSAGE{$msg} = [] unless defined $MESSAGE{$msg}; push @{$MESSAGE{$msg}}, $resource; $EXITCODE = ($CODE{$code} > $CODE{$EXITCODE}) ? $code : $EXITCODE; }; sub help($$) { my ($exitcode, $fd) = @_; version ($fd, 0); print $fd "Usage: $PROGRAM_NAME --help\n"; print $fd "Usage: $PROGRAM_NAME [--fresh ] [--maxage ]"; exit $exitcode }; my $params = {}; Getopt::Long::config('bundling'); if (!GetOptions ( 'h|help' => \$params->{'help'}, 'f|fresh=i' => \$TOO_FRESH, 'm|maxage=i' => \$MAX_AGE, 'M|maxlockage=i' => \$MAX_LOCK_AGE, )) { die ("$PROGRAM_NAME: Usage: $PROGRAM_NAME [-fwhv]\n"); }; help(0, *STDOUT) if $params->{'help'}; help(1, *STDERR) if scalar @ARGV > 0; unless (-e $DABACKUP_CONF) { if (-e '/etc/da-backup') { print "WARNING: No $DABACKUP_CONF, but we have /etc/da-backup/\n"; exit $CODE{'WARNING'}; }; if (-e '/var/log/da-backup') { print "WARNING: No $DABACKUP_CONF, but we have /var/log/da-backup/\n"; exit $CODE{'WARNING'}; }; print "OK: da-backup not installed\n"; exit $CODE{'OK'}; }; my $confdir; my $logdir; open (FH, "< $DABACKUP_CONF") or die ("Cannot open $DABACKUP_CONF: $!\n"); while () { if (/confdir=(.*)/) { $confdir = $1; } elsif (/logdir=(.*)/) { $logdir = $1; }; }; die ("No confdir found in $DABACKUP_CONF") unless defined $confdir; die ("No logdir found in $DABACKUP_CONF") unless defined $logdir; opendir(DIR, $confdir) or die ("Cannot opendir $confdir: $!\n"); my %conffiles = map {$_ => 1} grep { !/^\./ && !/\.bak$/} readdir(DIR); closedir(DIR); opendir(DIR, $logdir) or die ("Cannot opendir $logdir: $!\n"); my %logfiles = map {$_ => 1} grep { !/^\./ && !/\.[0-9]+(\.gz)?$/} readdir(DIR); closedir(DIR); if (scalar keys %conffiles == 0) { print "WARNING: da-backup installed but no backups configured\n"; exit $CODE{'WARNING'}; }; for my $f (keys %conffiles) { unless (exists $logfiles{$f}) { problem('WARNING', 'no log', $f); } } FILE: for my $f (sort {$a cmp $b} keys %logfiles) { unless (exists $conffiles{$f}) { problem('WARNING', 'no config', $f); next; } my @stat = stat("$logdir/$f") or die ("Cannot stat $logdir/$f: $!\n"); my $age = time - $stat[10]; my $lockage = undef; if (my @lockstat = stat($LOCKFILE)) { $lockage = time - $lockstat[10]; } if ($age < $TOO_FRESH || (defined $lockage && $lockage < $MAX_LOCK_AGE)) { # File is too new, let's use the old one if (-e "$logdir/$f.0") { $f .= ".0"; my @stat = stat("$logdir/$f") or die ("Cannot stat $logdir/$f: $!\n"); $age = time - $stat[10]; }; }; if ($age < 0) { problem('WARNING', 'future timestamp', $f); next; } elsif ($age > $MAX_AGE) { my $hage; if ($age > 48 * 3600) { $hage = sprintf("%d days", $age / 24 / 3600); } else { $hage = sprintf("%d hours", $age / 3600); }; problem('WARNING', 'old', "$f ($hage)"); next; }; open(FH, "< $logdir/$f") or die ("Cannot open $logdir/$f: $!\n"); sysseek(FH, -1024, SEEK_END); # just try it - doesn't matter if it fails my $last2 = ''; my $last = ''; while () { chomp; if (/^sent\s+[\d,]+\s+bytes\s+received\s+[\d,]+\s+bytes\s+[\d,\.]+\s+bytes\/sec$/) { problem('OK', 'probably ok', $f); close(FH); next FILE; }; $last2 = $last; $last = $_; }; problem('CRITICAL', 'FAILED', "$f ($last2 $last)"); }; my $msg = join("; ", map {"$_: ".join(', ', @{$MESSAGE{$_}}) } (sort {$a cmp $b} keys %MESSAGE)); print $EXITCODE, ": ", $msg, "\n"; exit $CODE{$EXITCODE};