#!/usr/bin/perl

use Getopt::Std;

$CFG_FILE = "/usr/local/nagios/etc/check_disk.cfg";
$CFG_FILE_LOCAL = "/usr/local/nagios/etc/check_disk_local.cfg";
$DFLT_WARN = 90;
$DFLT_CRIT = 95;
$DFLT_CLASS = "system";

$CURR_WARN = $DFLT_WARN;
$CURR_CRIT = $DFLT_CRIT;
$CURR_CLASS = $DFLT_CLASS;

$sys_type = `uname`;
chomp($sys_type);

%FSTypes = (
	ext2	=>	1,
	ext3	=>	1,
	ext4	=>	1,
	ufs	=>	1,
	vxfs	=>	1,
	zfs	=>	1,
	xfs	=>	1
);

# -p enable perf stats
# -d enable debugging output
# -c <crit> set default critical threshold
# -w <warn> set default warning threshold
# -f <config-file>
# -C <class>
# -t <type[,type...]> add additional file system types

getopts("pdc:w:f:C:t:");
$CURR_WARN = $opt_w if $opt_w ne '';
$CURR_CRIT = $opt_c if $opt_c ne '';
$opt_C = $CURR_CLASS if $opt_C eq '';

# get the types

if ($opt_t ne '') {
	@types = split(/,/, $opt_t);
	for $t (@types) {
		print "add type $t\n" if $opt_d;
		$FSTypes{$t} = 1;
	}
}

# get the list of file systems

&read_mounts;

# read the config file; if it's not there we take the defaults, except
# that if we specify a config file on the command line, we return UNKNOWN
# if the file isn't there

if ($opt_f ne '') {
	open (CFG_FILE, $opt_f) or unknown("Can't open config file: $opt_f");
	&read_config;
}

else {
	(open(CFG_FILE, $CFG_FILE_LOCAL) or open(CFG_FILE, $CFG_FILE)) and &read_config;
}


if ($opt_d) {
	for $fs (sort keys %Class) {
		print "fs $fs class $Class{$fs} warn $Warn{$fs} crit $Crit{$fs}\n";
	}
}


# if there are any file systems that are mounted but not mentioned in the config file,
# give them default class and thresholds

for $fs (keys %Type) {
	print "testing $fs: ", defined($Interesting{$fs}) ? "" : "not ", "interested\n" if $opt_d;
	if (! defined($Interesting{$fs})) {
		$Class{$fs} = $DFLT_CLASS;
		$Warn{$fs} = $DFLT_WARN;
		$Crit{$fs} = $DFLT_CRIT;
	}
}

if ($sys_type eq "Linux") {
	$cmd = "/bin/df -kP";
}

elsif ($sys_type eq "SunOS") {
	$cmd = "/usr/bin/df -k";
}

else {
	unknown("I don't grok system type $sys_type");
}

$fscount = 0;
for $fs (sort keys %Class) {
	## unknown("Non-existant FS ($fs) in config file, line $Line{$fs}") if $Type{$fs} eq '';
	push(@FSList, $fs);
}


for $fs (@FSList) {
	next if $Ignore{$fs};
	next if $Class{$fs} ne $opt_C;

	$cmd = $cmd . " " . $fs;
	$fscount++;
}

if ($fscount == 0) {
	unknown("no file systems in class \'$opt_C\'");
}

print "$cmd\n" if $opt_d;
$code = 0;
open(DF, "$cmd|") or unknown("Unable to exec df command");
$junk = <DF>;		# column headers
while (<DF>) {
	chop;
	($dev, $tot, $used, $free, $cap, $fs) = split;
	$p = int(($tot - $free) * 100 / $tot);
	print "$fs: $used/$tot $p\n" if $opt_d;
	$perf .= " $fs=$p\%";
	if ($p > $Crit{$fs}) {
		$crit_result .= " $fs: $p\% (C)";
		$code = 2;
	}

	elsif ($p > $Warn{$fs}) {
		$warn_result .= " $fs: $p\% (W)";
		$code = 1 if $code != 2;
	}

	else {
		$result .= " $fs: $p\% (OK)";
	}
}

print "perf $perf\n" if $opt_d;
@CODE = ("OK", "WARNING", "CRITICAL", "UNKNOWN");
print "DISK $CODE[$code]: class '$opt_C':$crit_result$warn_result$result";

print "|$perf" if $opt_p;
print "\n";
exit $code;


sub read_mounts
{
	open(MNTTAB, "/etc/mtab") or open(MNTTAB, "/etc/mnttab") or unknown("Can't open file system mount table");
	while (<MNTTAB>) {
		chop;
		@mnt = split;

		next if $FSTypes{$mnt[2]} != 1;
		$fs = $mnt[1];
		print "read from mtab: $_\n" if $opt_d;
		# push(@FSList, $fs);

		$Type{$fs} = $mnt[2];
		# $Class{$fs} = $DFLT_CLASS;
		# $Warn{$fs} = $DFLT_WARN;
		# $Crit{$fs} = $DFLT_CRIT;
	}
}


sub read_config
{
	my ($tmp_class, $tmp_warn, $tmp_crit);
	$line_num = 0;
	while (<CFG_FILE>) {
		$line_num++;
		chop;

		s/^[    ]*//;
		next if /^#/;
		next if $_ eq '';

		if (/^ignore\s/) {
			($junk, $fs) = split;
			$Ignore{$fs} = 1;
		}

		elsif (/^crit\s/) {
			($junk, $crit) = split;
			$CURR_CRIT = $crit;
		}

		elsif (/^warn\s/) {
			($junk, $warn) = split;
			$CURR_WARN = $warn;
		}

		elsif (/^class\s/) {
			($junk, $class) = split;
			$CURR_CLASS = $class;
		}

		elsif (/^fs\s/) {
			($junk, @a) = split;
			$fs = $a[0];

			@fslist = &match($fs);
			foreach $fs (@fslist) {
				$tmp_class = $CURR_CLASS;
				$tmp_warn = $CURR_WARN;
				$tmp_crit = $CURR_CRIT;

				@aa = @a;
				shift @aa;
				foreach $arg (@aa) {
					if    ($arg =~ /^class=/) { $tmp_class = substr($arg, 6); }
					elsif ($arg =~ /^warn=/)  { $tmp_warn = substr($arg, 5); }
					elsif ($arg =~ /^crit=/)  { $tmp_crit = substr($arg, 5); }

					else { unknown("Syntax error in config file, line $line_num"); }
				}

				# mark the file system as being of overall interest

				$Interesting{$fs} = 1;

				# Only set these values if the class of the file system is the one that
				# we are looking to check.  This allows us to put a file system in
				# multiple classes (in cases where more than on group wants to know
				# about a F.S.) and not have one definition overwritten by another one.

				print "about to test $fs now $Class{$fs} tmp $tmp_class dflt $CURR_CLASS opt $opt_C\n" if $opt_d;
				next if $tmp_class ne $opt_C;

				print "setting $fs to class $tmp_class\n" if $opt_d;
				$Class{$fs} = $tmp_class;
				$Warn{$fs} = $tmp_warn;
				$Crit{$fs} = $tmp_crit;
				$Line{$fs} = $line_num;
			}
		}

		else {          # syntax error
			unknown("Syntax error in config file, line $line_num");
		}
	}

	close(CFG_FILE);
}


sub match
{
	my ($fs) = @_;
	my @glist = glob($fs);
	my @rlist = ();

	print "Match glob list is " . join(' ', @glist) . "\n" if $opt_d;

	foreach $fs (@glist) {
		push @rlist, $fs if $Type{$fs} ne '';
	}

	print "Match return list is " . join(' ', @rlist) . "\n" if $opt_d;

	return @rlist;
}


sub unknown
{
	my ($reason) = @_;

	print "UNKNOWN: $reason\n";
	exit 3;
}

