aboutsummaryrefslogtreecommitdiff
path: root/libexec/check_mount
blob: 946eddce42f918f900964c3b5ff4388ca0ac7bd2 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
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.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;