diff options
Diffstat (limited to 'libexec/check_mount')
-rw-r--r-- | libexec/check_mount | 152 |
1 files changed, 152 insertions, 0 deletions
diff --git a/libexec/check_mount b/libexec/check_mount new file mode 100644 index 0000000..31481c1 --- /dev/null +++ b/libexec/check_mount @@ -0,0 +1,152 @@ +#!perl +# +# This plugin takes a mountpoint and checks that a filesystem is mounted +# thereupon, once and once only. +# +# Author: Tom Ryder <tom@sanctum.geek.nz> +# License: MIT +# +package main; + +# Force me to write this properly +use strict; +use warnings; +use utf8; + +# Require at least this Perl version +use 5.010_001; + +# Import required modules +use English qw(-no_match_vars); +use Exception::Class ( PluginException => { alias => 'throw' } ); +use Monitoring::Plugin qw(%ERRORS); +use Path::Tiny; +use Quota; +use Try::Tiny; + +# Decree package version +our $VERSION = '1.00'; + +# Add description and license package variables +our $DESCRIPTION = <<'EOF'; +This plugin takes a mountpoint and checks that a filesystem is mounted +thereupon, once and once only. +EOF +our $LICENSE = <<'EOF'; +MIT License <https://opensource.org/licenses/MIT> +EOF + +# Custom plugin options +our @OPTS = ( + { + spec => 'mountpoint|m=s', + help => 'Path to mountpoint', + label => 'PATH', + required => 1, + }, +); + +# Build Monitoring::Plugin object +my $mp = Monitoring::Plugin->new( + usage => 'Usage: %s --mountpoint|-m PATH', + version => $VERSION, + blurb => $DESCRIPTION, + license => $LICENSE, +); + +# Anything that dies in here raises ->plugin_die() +try { + + # Add and read custom options + for my $opt (@OPTS) { + $mp->add_arg( %{$opt} ); + } + $mp->getopts(); + + # Start counting down to timeout + alarm $mp->opts->timeout(); + + # Get a cleaned-up and absolute path for the mountpoint + length $mp->opts->mountpoint + or throw 'Empty mountpoint path'; + my $point = path( $mp->opts->mountpoint )->realpath(); + $point->exists() + or throw "$point does not exist"; + $point->is_dir() + or throw "$point is not a directory"; + + # Read the mount table row by row and collect any entries that correspond + # to our mountpoint + my @mounts = grep { $_->{path} eq $point } mounts(); + + # Make a string for the message that describes the device and type for each + # of the matching mounts, comma-separated + my $devs = join q(, ), map { "$_->{dev} ($_->{type})" } @mounts; + + # One mount is ideal; exit OK, describing the mounted devices and the + # filesytem types + if ( @mounts == 1 ) { + $mp->add_message( $ERRORS{OK}, "Mounted on $point: $devs\n" ); + } + + # More than one mount is not ideal; exit WARNING, describing the mounted + # devices and the filesystem types + elsif ( @mounts > 1 ) { + $mp->add_message( $ERRORS{WARNING}, "Multi mounts on $point: $devs\n" ); + } + + # No matching mount at all is bad + else { + $mp->add_message( $ERRORS{CRITICAL}, "No mounts found on $point\n" ); + } + + # Exit with the selected code and message + $mp->plugin_exit( $mp->check_messages ); +} +catch { + $mp->plugin_die($_); +}; + +# Return a listref of hashrefs describing every mount table entry +sub mounts { + + # Open mount table for reading, throw on error (return great than zero) + Quota::setmntent() == 0 + or throw Quota::strerr(); + + # Iterate through each mount table entry to collect a list. The + # getmntent() function here returns an empty list when it's read the whole + # table, and undef on error in place of what would normally be the device, + # so this may seem a bit awkward on first reading. + # + my @mounts; + while ( my @mnt = Quota::getmntent() ) { + + # Check the first element; if it's undefined, there was an error, and + # we should bail out + defined $mnt[0] + or throw Quota::strerr(); + + # Otherwise, this really is a mountpoint entry; extract the device, + # mountpoint, and filesystem type + my ( $dev, $path, $type ) = @mnt; + length $path or next; + + # Build a hash structure including canonicalising and resolving the + # path, and add it as a reference to the mounts collection + my %mount = ( + dev => $dev || 'unknown', + path => path($path)->realpath(), + type => $type || 'unknown', + ); + push @mounts, \%mount; + } + + # Close mount table + Quota::endmntent(); + + # Return collected mounts + return @mounts; +} + +1; |