#!/usr/bin/perl
#
# check_viceversa.pl
# - A nagios plugin to monitor that a Vice Versa profile is running.
#
# Usage:
#  check_viceversa.pl [--user <user>] [--password <pass>] [--maxAge <hours>] <file>
# 
# where
#  <file>		Is the path of a viceversa logfile, which might be local or provided as a smb: URI
#				( e.g. smb://server/path/to/viceversa/logfile )
#  --date <date> 		Check for a completed run on <date> (defaults to yesterday)
#  --user <user> 		The username, if fetching logfile via smb
#  --password <pass>		The password, if fetching logfile via smb
#  --excludes <exclSpec> 	Specify the number of expected excludes
#				(Format:<NoOfDirExcl>:<NoOfFilesExcl>)
# 
# Returns 
#  CRITICAL if no copy job was finished on the given date,
#  WARNING 
# 	- if there were files/dirs which could not be copied
#	- if the expected number of excluded directories/files does not match.
#  OK otherwise.
#
# WARNING: If Vice Versa is unable to read a file/dir it is automatically excluded and not counted as
# 	errornous. So you should specify the number of expected excludes to be sure.
#
# Also provides performance data.
#
# Author: Moritz Bechler <mbechler@eenterphace.org> for Schmieder IT Solutions (http://www.schmieder.de)
# License: MIT License
#
# Copyright (c) 2010 Moritz Bechler
# 
# 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 utf8;
use FindBin;
use lib "$FindBin::Bin";
use File::Temp;
use Getopt::Long;
use Date::Parse;
use Date::Format;
use open ":encoding(utf16-le)";


my $user = '';
my $password = '';
my $dateStr = '';
my $excludeSpec = '';

GetOptions('user=s' => \$user, 'password=s' => \$password, 'date=s' => \$dateStr, 'excludes=s' => \$excludeSpec);

my $excludeDirs = -1;
my $excludeFiles = -1;

if($excludeSpec) {
	$excludeSpec =~ m/^([\d]*):([\d]*)$/ or die "Exclude specification format: <dirExcl>:<filesExcl>\n";

	if( $1 or $1 == '0') {
		$excludeDirs = int($1);
	}

	if( $1 or $2 == '0') {
		$excludeFiles = int($2);
	}
}


my $date = undef;
if($dateStr) {
	$date = str2time($dateStr);
} else {
	$date = time() - 24*60*60;
}

if(!$date) {
	die "Failed to parse date";
}

$dateStr = time2str('%Y-%m-%d', $date);

my $uri = shift;

if(!$uri) {
	print "Usage: $0 [--user <user>] [--password <pass>] [--date <date>]  smb://server/path/to/viceversa/logfile\n";
	die "No logfile URI provided";
}

my $tmpfile = '';

if(! -e $uri) {
	$tmpfile = tmpnam();

	my $cmd = "/usr/bin/smbget -q -n -o $tmpfile ";
	if($user) {
		$cmd .= "-u \"$user\" ";
	}
	if($password) {
		$cmd .= "-p \"$password\" ";
	}
	$cmd .= "\"$uri\"";

	if(!system($cmd)) {
		print "CRITICAL: Failed to fetch Vice Versa logfile\n";
		exit(2);
	}
} else {
	$tmpfile = $uri;
}

sub remove_dot { my $str = shift; $str =~ s/\.//; return $str; }

my $statusStr = '';
my $status = -1;

my $executeBlock = 0;


my $filesTotal = 0;
my $filesMatched = 0;
my $filesAdded = 0;
my $filesUpdated = 0;
my $filesDeleted = 0;
my $filesExcluded = 0;

my $dirsTotal = 0;
my $dirsMatched = 0;
my $dirsAdded = 0;
my $dirsDeleted = 0;
my $dirsExcluded = 0;

open TMPFILE, "<$tmpfile";

while(my $line = <TMPFILE>) {
	if(not $line =~ m/^\Q$dateStr\E\s+\d\d:\d\d:\d\d\s+:\s*(.*)$/m) {
		next;
	}
	my $rest = $1;

	if(not $executeBlock and not $rest =~ m/^---- Start Execution ----/) {
		next;
	} else {
		$executeBlock = 1;
	}

	if($rest =~ /^Exit Code: (\d+). (.*)\[?/) {
		$status = $1;
		$statusStr = $2;
	}

	if($rest =~ /^- Total Files - Source: ([\d\.]+) \([^\)]+\) - Target: ([\d\.]+) \([^\)]+\)/) {
                $filesTotal += remove_dot($1);
        }

	if($rest =~ /^- Total Subfolders - Source: ([\d\.]+) - Target: ([\d\.]+)/) {
                $dirsTotal += remove_dot($1);
        }

	if($rest =~ /^- Matched Files - Source: ([\d\.]+) \([^\)]+\) - Target: ([\d\.]+) \([^\)]+\)/) {
                $filesMatched += remove_dot($1);
        }

	if($rest =~ /^- Matched Subfolders - Source: ([\d\.]+) - Target: ([\d\.]+)/) {
                $dirsMatched += remove_dot($1);
        }

	if($rest =~ /^- Added Files - Source: ([\d\.]+) \([^\)]+\) - Target: ([\d\.]+) \([^\)]+\)/) {
		$filesAdded += remove_dot($2);
	}

	if($rest =~ /^- Updated Files - Source: ([\d\.]+) \([^\)]+\) - Target: ([\d\.]+) \([^\)]+\)/) {
		$filesUpdated += remove_dot($2);
	}

	if($rest =~ /^- Deleted Files - Source: ([\d\.]+) \([^\)]+\) - Target: ([\d\.]+) \([^\)]+\)/) {
		$filesDeleted += remove_dot($2);
	}

	if($rest =~ /^- Added Subfolders - Source: ([\d\.]+) - Target: ([\d\.]+)/) {
		$dirsAdded += remove_dot($2);
	}

	if($rest =~ /^- Deleted Subfolders - Source: ([\d\.]+) - Target: ([\d\.]+)/) {
		$dirsDeleted += remove_dot($2);
	}

	if($rest =~ /^- Excluded Files - Source: ([\d\.]+)/) {
		$filesExcluded = remove_dot($1);
	}

	if($rest =~ /^- Excluded Subfolders - Source: ([\d\.]+)/) {
		$dirsExcluded = remove_dot($1);
	}
	

}
close TMPFILE;

my $exitStatus = 3;
if(not $executeBlock) {
	print "CRITICAL: No profile execution found on $dateStr\n";
	exit 2;
} elsif(not $status eq 0) {
	print "WARNING: Backup contains errors: $statusStr";
	$exitStatus = 1;
} elsif(($excludeFiles >=0 and $filesExcluded != $excludeFiles) or ($excludeDirs >= 0 and $dirsExcluded != $excludeDirs)) {
	print "WARNING: Some files/dirs were automatically excluded";
	$exitStatus = 1;
} else {
	print "OK: Profile executed successfully"; 
	$exitStatus = 0;
}

print " | dirsTotal=$dirsTotal,filesTotal=$filesTotal,dirsMatched=$dirsMatched,filesMatched=$filesMatched,";
print "dirsExcluded=$dirsExcluded,filesExcluded=$filesExcluded,filesAdded=$filesAdded,filesUpdated=$filesUpdated,";
print "filesDeleted=$filesDeleted,dirsAdded=$dirsAdded,dirsDeleted=$dirsDeleted";

print "\n";
exit $exitStatus;
