#!/usr/bin/perl -w package main; # RCS information (required by Perl::Critic) # enable substitution with: # $ svn propset svn:keywords "Id Revision HeadURL Source Date" # # $Id: check_dir 1259 2011-06-29 15:18:19Z corti $ # $Revision: 1259 $ # $HeadURL: https://svn.id.ethz.ch/nagios_plugins/check_dir/check_dir $ # $Date: 2011-06-29 17:18:19 +0200 (Wed, 29 Jun 2011) $ use 5.00800; use strict; use warnings; use English qw(-no_match_vars); use Nagios::Plugin; use Nagios::Plugin::Range; use Nagios::Plugin::Getopt; use Nagios::Plugin::Threshold; our $VERSION = '3.0.0'; # IMPORTANT: Nagios plugins could be executed using embedded perl in this case # the main routine would be executed as a subroutine and all the # declared subroutines would therefore be inner subroutines # This will cause all the global lexical variables not to stay shared # in the subroutines! # # All variables are therefore declared as package variables... # ## no critic (ProhibitPackageVars) use vars qw( $n $plugin $options $status $info_string $threshold ); ## use critc # the script is declared as a package so that it can be unit tested # but it should not be used as a module if ( !caller ) { run(); } ############################################################################## # subroutines ############################################################################## # Usage : exit_with_error( $status, $message) # Purpose : if a plugin object is available exits via ->nagios_exit # otherwise prints to the shell and exit normally # Returns : n/a # Arguments : n/a # Throws : n/a # Comments : n/a # See also : n/a sub exit_with_error { my $status = shift; my $message = shift; if ($plugin) { $plugin->nagios_exit( $status, $message ); } else { #<<< print "Error: $message"; ## no critic (RequireCheckedSyscalls) #>>> exit $status; } return; } ############################################################################## # Usage : max($i, $j) # Purpose : returns the maximum of two integers # Returns : the maximum of two integers # Arguments : the integers to compare # Throws : n/a # Comments : n/a # See also : n/a sub max { my ( $i, $j ) = @_; if ( $i > $j ) { return $i; } else { return $j; } } ############################################################################## # Usage : verbose("some message string", $optional_verbosity_level); # Purpose : write a message if the verbosity level is high enough # Returns : n/a # Arguments : message : message string # level : options verbosity level # Throws : n/a # Comments : n/a # See also : n/a sub verbose { # arguments my $message = shift; my $level = shift; if ( !defined $message ) { exit_with_error( UNKNOWN, q{Internal error: not enough parameters for 'verbose'} ); } if ( !defined $level ) { $level = 0; } if ( $options && $level < $options->verbose ) { print $message; ## no critic (RequireCheckedSyscalls) } return; } ############################################################################## # Usage : @files = get_entries("directory_name") # Purpose : reads the entries in the given directory # Returns : number of entries # Arguments : dirname: the name of the directory to check # Throws : n/a # Comments : n/a # See also : n/a sub get_entries { my $dirname = shift; ####################### # get directory listing verbose "Opening $dirname\n", 1; my $DIR; opendir $DIR, $dirname or exit_with_error( UNKNOWN, "Can't open $dirname: $OS_ERROR" ); # exclude .. and . from the count *and* recursion my @files = grep { $_ ne q{.} && $_ ne q{..} } readdir $DIR; closedir $DIR or exit_with_error( UNKNOWN, "Error closing $dirname: $OS_ERROR" ); return @files; } ############################################################################## # Usage : check_dir("directory_name") # Purpose : checks the number of files in the given directory # Returns : n/a # Arguments : dirname: the name of the directory to check # Throws : n/a # Comments : n/a # See also : n/a sub check_dir { my $dirname = shift; my $info; #################################### # check if the directory is readable $info = stat $dirname or exit_with_error( UNKNOWN, "Can't read stat for $dirname: $OS_ERROR" ); my @files = get_entries($dirname); my $n = @files; ################# # Additional info verbose "Directory '$dirname' has $n files\n"; ################# # Output (status) $plugin->add_perfdata( label => $dirname, value => $n, uom => q{}, threshold => $threshold, ); if ( defined $info_string ) { $info_string = "$info_string, $dirname=$n"; } else { $info_string = "$dirname=$n"; } $status = max( $status, $threshold->get_status($n) ); ########### # Recursive if ( $options->get('recursive') ) { foreach my $file (@files) { if ( -d "$dirname/$file" ) { my $error = check_permissions("$dirname/$file"); if ($error) { exit_with_error( UNKNOWN, "Error: $error" ); } check_dir("$dirname/$file"); } } } return; } ############################################################################## # Usage : check_permissions( $dirname ) # Purpose : checks if the directory is readable # Returns : undef if OK or error message if the directory is not readable # Arguments : dirname: the name of the directory to check # Throws : n/a # Comments : n/a # See also : n/a sub check_permissions { my $dirname = shift; if ( !-d $dirname ) { return "$dirname is not a directory"; } if ( !-r $dirname ) { return "$dirname is not readable"; } if ( !-x $dirname ) { return "$dirname is not executable"; } return; } ############################################################################## # Usage : run(); # Purpose : main method # Returns : n/a # Arguments : n/a # Throws : n/a # Comments : n/a # See also : n/a sub run { # initialization $plugin = Nagios::Plugin->new( shortname => 'CHECK_DIR' ); $status = 0; ######################## # Command line arguments $options = Nagios::Plugin::Getopt->new( usage => 'Usage: %s [OPTIONS]', version => $VERSION, url => 'https://trac.id.ethz.ch/projects/nagios_plugins', blurb => 'cheks the number of files in one or more directories.', ); $options->arg( spec => 'dir|d=s@', help => 'specify the directory (can be repeated)', required => 1, ); $options->arg( spec => 'critical|c=s', help => 'specify the critical number (or range) of files', required => 1, ); $options->arg( spec => 'warning|w=s', help => 'specify warning threshold (range) for the number of files', required => 1, ); $options->arg( spec => 'recursive|r', default => 0, help => 'perform recursive traversal (checks individual dirs not the total)', ); $options->getopts(); ############################# # Sanity checks: command line my $critical_range = Nagios::Plugin::Range->parse_range_string( $options->get('critical') ); if ( !$critical_range->is_set() ) { exit_with_error( UNKNOWN, 'Could not parse "critical"' ); } my $warning_range = Nagios::Plugin::Range->parse_range_string( $options->get('warning') ); if ( !$critical_range->is_set() ) { exit_with_error( UNKNOWN, 'Could not parse "warning"' ); } $threshold = Nagios::Plugin::Threshold->set_thresholds( warning => $warning_range, critical => $critical_range, ); ########################## # Sanity checks: directory foreach my $dirname ( @{ $options->get('dir') } ) { my $error = check_permissions($dirname); if ($error) { exit_with_error( UNKNOWN, "Error: $error" ); } } ############# # Process dir foreach my $dirname ( @{ $options->get('dir') } ) { check_dir($dirname); } exit_with_error( $status, $info_string ); return; } 1;