#!/usr/bin/perl -w

# cksum check module

# Version: $Revision: 0.2 $
# Author: Benjamin Oshrin
# Date: $Date: 2004/11/11 16:51:27 $
#
# Copyright (c) 2004
# The Trustees of Columbia University in the City of New York
# Academic Information Systems
# 
# License restrictions apply, see doc/license.html for details.

use strict;
use FindBin;
use lib "$FindBin::Bin/../common";
use Survivor::Check;

my %argformat = (# Configuration file
		 "cksumcf" => "optional file",
		 # Program to use
		 "sumutil" => "optional string default(cksum)",
		 # File to sum
		 "file" => "optional file",
		 # Expected sum of file
		 "sum" => "optional string");

sub CksumValidate {
    return(MODEXEC_OK, 0, 'Cksum OK');
}

sub CksumCheck {
    my ($self, $host) = @_;

    # We must have a configuration file or a file and sum
    my $cf = $self->Arg("cksumcf");
    my $afile = $self->Arg("file");
    my $asum = $self->Arg("sum");

    if(defined $cf) {
	open(CF, $cf) || return(MODEXEC_MISCONFIG, 0,
				"Unable to read configuration file $cf");
    } elsif(!defined $afile || !defined $asum) {
	return(MODEXEC_MISCONFIG, 0,
	       "Must provide 'cksumcf' or both 'file' and 'sum'");
    }

    my %sums;
    my %octets;
    my $failed = 0;
    my $columns = "sum";

    my $cksum = $self->Which($self->Arg("sumutil"));

    if(!defined $cksum) {
	$cksum = $self->Arg("sumutil");

	if(!-x $cksum) {	
	    return(MODEXEC_MISCONFIG, 0,
		   "sum utility " . $self->Arg("sumutil") . " not found");
	}
    }

    if($cksum =~ /md5sum$/) {
	$columns = "md5sum";
    }

    if(defined $cf) {
	# Parse cf
	
	if($columns eq "md5sum") {
	    # 2 columns
	    while(<CF>) {
		chomp;          # Toss newline
		s/#.*$//;       # Toss comments
		s/^\s+//;       # Toss initial spaces
		s/\s+$//;       # Toss final spaces
		my($sum,$path) = split(/ +/, $_, 2);
		
		if(defined $path && $path ne '') {
		    $sums{$path} = $sum;
		} elsif(defined $sum && $sum ne '') {
		    return(MODEXEC_MISCONFIG, 0,
			   "Unexpected token '$sum' in $cf");
		}
	    }
	} else {
	    # 3 columns
	    while(<CF>) {
		chomp;          # Toss newline
		s/#.*$//;       # Toss comments
		s/^\s+//;       # Toss initial spaces
		s/\s+$//;       # Toss final spaces
		my($sum,$oct,$path) = split(/ +/, $_, 3);
		
		if(defined $path && $path ne '') {
		    $sums{$path} = $sum;
		    $octets{$path} = $oct;
		} elsif(defined $sum && $sum ne '') {
		    return(MODEXEC_MISCONFIG, 0,
			   "Unexpected token '$sum' in $cf");
		}
	    }
	}
	
	close(CF);
    }

    if(defined $afile && defined $asum) {
	# Stuff the provided data into the hashes

	if($columns eq "md5sum") {
	    $sums{$afile} = $asum;
	} else {
	    ($sums{$afile}, $octets{$afile}) = split(/ +/, $asum, 2);
	}
    }

    # Now sum each requested file.  File not existing is a warning,
    # cksum failing is a problem.

    foreach my $key (keys %sums) {
	if(-r $key) {
	    open(CKSUM, "$cksum '$key' |");
	    my $indata = <CKSUM>;
	    close(CKSUM);

	    if(defined $indata) {
		chomp($indata);

		if($columns eq "md5sum") {
		    my($insum,$inpath) = split(/ +/, $indata, 2);
		    
		    if($insum ne $sums{$key}) {
			$self->AccResultAdd($host, MODEXEC_PROBLEM,
					    "Found $insum for expected " .
					    $sums{$key} . " for $key");
			$failed++;
		    }
		    # else OK
		} else {
		    my($insum,$inoct,$inpath) = split(/ +/, $indata, 3);
		    
		    if($insum ne $sums{$key}) {
			$self->AccResultAdd($host, MODEXEC_PROBLEM,
					    "Found $insum for expected " .
					    $sums{$key} . " for $key");
			$failed++;
		    } elsif($inoct ne $octets{$key}) {
			$self->AccResultAdd($host, MODEXEC_PROBLEM,
					    "Found $inoct for expected " .
					    $octets{$key} . " for $key");
			$failed++;
		    }
		    # else OK
		}
	    } else {
		$self->AccResultAdd($host, MODEXEC_PROBLEM,
				    "Received empty response for expected " .
				    $sums{$key} . " for $key");
	    }
	} else {
	    # File doesn't exist

	    $self->AccResultAdd($host, MODEXEC_PROBLEM,
				"$key does not exist or is not readable");
		$failed++;
	}
    }

    if($failed > 0) {
	return($self->AccResultGet($host, $failed));
    } else {
	return(MODEXEC_OK, 0, "OK");
    }
}

my $sc = new Survivor::Check();

$sc->GetOpt(\%argformat, \&CksumValidate);

my $r = $sc->Exec(\&CksumCheck);

exit($r);
