#!/usr/bin/perl -w
#
# check_inodes - plugin to check inode usage across all partitions.
#   Designed to be a workalike for check_disk.
#

use strict;
use File::Basename;
BEGIN {
  use lib dirname($0);
  use Nagios::Plugin;
}

my $VERSION = '0.2 [http://www.openfusion.com.au/labs/nagios/]';

my ($warning, $critical, $local);
my (@path, @exclude_device, @exclude_type, $report_usage, $errors_only);
my $USAGE = qq(usage: %s -w limit -c limit [-p path | [-x device] [-X type]]
                 [-t timeout] [-l] [-u] [-v]\n);
my @SPEC = (
  "warning|w=s" => [ \$warning, 
  q(-w, --warning=INTEGER
   Exit with WARNING status if less than INTEGER inodes are free
 -w, --warning=PERCENT%
   Exit with WARNING status if less than PERCENT of inodes is free), 
    'REQUIRED' ],
  "critical|c=s"=> [ \$critical, 
  q(-c, --critical=INTEGER
   Exit with CRITICAL status if less than INTEGER inodes are free
 -c, --critical=PERCENT%
   Exit with CRITICAL status if less than PERCENT of inodes is free),
    'REQUIRED' ],
  "local|l" => [ \$local, 
  q(-l, --local
   Only check local filesystems) ],
  "path|partition|p=s" => [ \@path, 
  q(-p, --path=PATH, --partition=PARTITION
   Path or partition (may be repeated)) ],
  "exclude-device|x=s"  => [ \@exclude_device, 
  q(-x, --exclude_device=PATH <STRING>
   Ignore device (may be repeated; only works if -p unspecified)) ],
  "exclude-type|X=s" => [ \@exclude_type, 
  q(-X, --exclude-type=TYPE <STRING>
   Ignore all filesystems of indicated type (may be repeated; only works 
     if -p unspecified)) ],
  "report-usage|u" => [ \$report_usage, 
  q(-u, --report-usage
   Report usage statistics, rather than free statistics) ],
  "errors-only|e" => [ \$errors_only, 
  q(-e, --errors-only
   Display only devices/mountpoints with errors) ],
);

parse_args(
  version => $VERSION,
  usage => $USAGE,
  spec => \@SPEC,
);

my $DF = '/bin/df';
my $DF_ARGS = '-i -T';

exit("UNKNOWN", "cannot find $DF") unless -f $DF && -x $DF;

# Do the df
$DF_ARGS .= " -l" if $local;
alarm($timeout);
exit("UNKNOWN", "No output from '$DF $DF_ARGS'") 
  unless open DF, "$DF $DF_ARGS |";
my @lines = <DF>;
close DF;

# Parse the output
shift @lines;
my @part = ();
my $join = '';
for (@lines) {
  my %entry = ();
  # df can split entries over 2 lines - if $join is set, we need to add it in
  if ($join) {
    $_ = "$join $_";
    $join = '';
  }
  @entry{qw(fs type inodes iused ifree ipct mountpoint)} = split /\s+/;
  # If type is empty, then line must be split - save fs to $join and continue
  if (! $entry{type}) {
    $join = $entry{fs};
    next;
  }
  $entry{ipct} = substr($entry{ipct},0,-1) if substr($entry{ipct},-1) eq '%';
  $entry{ipct_free} = $entry{ipct} =~ m/^\d+$/ ? 100 - $entry{ipct} : 100;
  push @part, \%entry;
}

# Check partitions for warnings/criticals
my @critical = ();
my @warning = ();
my $results = $report_usage ? 'inodes used: ' : 'free inodes: ';
my $critical_pct = 0;
my $warning_pct = 0;
if (substr($critical,-1) eq '%') {
  $critical = substr($critical,0,-1);
  $critical_pct = 1;
}
if (substr($warning,-1) eq '%') {
  $warning = substr($warning,0,-1);
  $warning_pct = 1;
}
for my $part (@part) {
  # Check specific inclusions/exclusions
  if (@path) {
    next unless grep { $_ eq $part->{fs} || $_ eq $part->{mountpoint} } @path;
  }
  elsif (@exclude_device || @exclude_type) {
    next if grep { $_ eq $part->{fs} || $_ eq $part->{mountpoint} } @exclude_device;
    next if grep { $_ eq $part->{type} } @exclude_type;
  }
  # Add to @critical/@warning if under thresholds
  my $report = 1 unless $errors_only;
  my $actual = $critical_pct ? $part->{ipct_free} : $part->{ifree};
  if ($actual <= $critical) {
    push @critical, $part;
    $report ||= 1;
  }
  $actual = $warning_pct ? $part->{ipct_free} : $part->{ifree};
  if ($actual <= $warning) {
    push @warning, $part;
    $report ||= 1;
  }
  if ($report) {
    if ($report_usage) {
      $results .= sprintf "%s %d (%s); ", 
        $part->{mountpoint}, $part->{iused}, $part->{ipct} . '%';
    } else {
      $results .= sprintf "%s %d (%s); ", 
        $part->{mountpoint}, $part->{ifree}, $part->{ipct_free} . '%';
    }
  }
}

exit_results(CRITICAL => \@critical, WARNING => \@warning, results => $results);


# arch-tag: 30fdd013-3ef1-4c12-a35b-849e83e125fd
# vim:ft=perl
