aboutsummaryrefslogblamecommitdiff
path: root/libexec/check_mount
blob: 946eddce42f918f900964c3b5ff4388ca0ac7bd2 (plain) (tree)


























                                                                        
                      



























































































































                                                                                
#!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.01';

# 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;