#!/usr/bin/perl -w
use strict;
use warnings;
use Cwd 'abs_path';

sub prtmsg;

# doesn't pick up offline swap partitions

# deal with unexpected problems.
$SIG{__DIE__} = sub { print "@_"; exit 3; };

my $mount;
my $fstab;
my (%mounted_disks, %mounted_disks_options, %fstab_disks, %fstab_disks_options); # store the read details.
my (@ignore, $ign);
my $ignore_file = "/usr/local/nagios/etc/mount_ignore";

my $os = `uname`;
chomp $os;
if ($os eq 'Linux') {
	$mount = "/bin/mount";
	$fstab = "/etc/fstab";
}

elsif ($os eq 'SunOS')
{
	$mount = "/usr/sbin/mount";
	$fstab = "/etc/vfstab";
}

else {
	die "Unknown OS: $os";
}

## read the list of ignore prefixes if present

if (open( my $ignore_fh, $ignore_file)) {
  while (<$ignore_fh>) {
    chomp;
    push @ignore, $_;
  }

  close $ignore_fh;
}

########################################################
# get the device and mount point from fstab (what should be mounted)
########################################################

open( my $fstab_fh, $fstab)
  || die "Failed to open $fstab: $!";

while ( <$fstab_fh> ) {
  chomp;
  s/^\s+//;
  s/#.*$//;
  s/\s+$//;

  next if $_ eq '';

  my ($device, $mount_point, $opts);
  ($device, $mount_point, $opts) = (split(/\s+/, $_))[0, 1, 3] if $os eq 'Linux';
  ($device, $mount_point, $opts) = (split(/\s+/, $_))[0, 2, 5] if $os eq 'SunOS';
### print "$device $mount_point $opts\n";
  $device = abs_path("/dev/disk/by-uuid/" . readlink "/dev/disk/by-uuid/$1") if $device =~ m/UUID=(.*)/;
  next unless $device =~ m!/! and $mount_point =~ m!/!; # drop swap, etc. partitions
  $mount_point =~ s#/$## unless $mount_point =~ m#^/$#;
  next if $device =~ m!(^/dev/cdrom)|(^/dev/fd\d+)!;
  next if $os eq 'Linux' and $opts =~ /noauto/;
  next if $os eq 'SunOS' and $opts !~ /yes/;
  next if grep m#^$mount_point$#, @ignore;

  $fstab_disks{$mount_point} = $device;
  $fstab_disks_options{$mount_point} = [ split /,/, $opts ];
#print "%%%$device%%%%\n";
#print join "\n", @{$fstab_disks_options{$mount_point}};
#print "\n\n";
}

close $fstab_fh;
	### my $i;
	### foreach $i (keys %fstab_disks) {print "$i\n";}

$fstab_disks{"/"} = "/" if $os eq 'SunOS';	## root isn't in vfstab for solaris

########################################################
# get the device and mount point of the mounted disks
########################################################

open( my $mount_fh, "$mount |")
  || die "Failed to open $mount: $!";

while ( <$mount_fh> ) {
  my ($device, $mount_point, $mopt);
  ($device, $mount_point, $mopt) = (split(/\s+/, $_))[0,2,5] if $os eq 'Linux';
  ($device, $mount_point) = (split(/\s+/, $_))[2,0] if $os eq 'SunOS';
  next unless $device =~ m!/!;		## ignore pseudo-fs
  next if grep m#^$mount_point$#, @ignore;

### print "$device $mount_point\n";
  $mounted_disks{$mount_point} = $device;
  $mopt =~ s#[\(\)]##g;
  $mounted_disks_options{$mount_point} = [ split /,/, $mopt ];
#print "%%%$device%%%%\n";
#print join "\n", @{$mounted_disks_options{$mount_point}};
#print "\n\n";
}

close $mount_fh;

########################################################
# find inconsistant disks
########################################################

my @not_mounted    = sort grep { ! exists $mounted_disks{$_} } keys %fstab_disks;
my @not_persistent = sort grep { ! exists $fstab_disks{$_}   } keys %mounted_disks;

my (@ro_mts, @fstab_ro_mts);

foreach (keys %mounted_disks) {
#print "###$_###:\n";
#print "Mount##", join ",,,", @{$mounted_disks_options{$_}}, "##\n";
#print "Fstab##", join ",,,", @{$fstab_disks_options{$_}}, "##\n";
	grep (/^ro$/, @{$mounted_disks_options{$_}}) && ! grep (/^ro$/, @{$fstab_disks_options{$_}}) and push @ro_mts, $_;
	grep (/^ro$/, @{$fstab_disks_options{$_}}) && (! grep (/^ro$/, @{$mounted_disks_options{$_}}) || grep (/^rw$/, @{$mounted_disks_options{$_}})) and push @fstab_ro_mts, $_;
}
#print join "%%%%", @fstab_ro_mts;

########################################################
# Build output
########################################################

my %out = (
	'0' => 'OK: All disks are mounted correctly and persistent',
	'1' => 'WARNING: ',
	'2' => 'CRITICAL: ',
);

my $message = "";
my $exit_code = 0;

@fstab_ro_mts > 0 and do {
	$message = prtmsg \@fstab_ro_mts, " should be mounted read only (and it's not!)<br>\n", " should be mounted read only (and they are not!)<br>\n";
	$exit_code = 1;
};

@not_persistent > 0 and do {
	$message .= prtmsg \@not_persistent, " is not persistent<br>\n", " are not persistent<br>\n";
	$exit_code = 1;
};

@not_mounted > 0 and do {
	$message  .= prtmsg \@not_mounted, " is not mounted<br>\n", " are not mounted<br>\n";
	$exit_code = 2;
};

@ro_mts > 0 and do {
	$message .= prtmsg \@ro_mts, " is mounted read only (and it shouldn't be!)", " are all mounted read only (and they shouldn't be!)";
	$exit_code = 2;
};

print $out{$exit_code} . "$message\n";

exit $exit_code;

sub prtmsg {
	my $arr = shift;
	my $msgsingle = shift;
	my $msgplural = shift;

	if (@$arr == 1) {
		return "@$arr" . $msgsingle;
	} else {
		return join (", ", @$arr) . $msgplural;
	}
}
