Current File : //usr/local/nagios/libexec/check_disk_smb
#!/usr/bin/perl
#
#
# check_disk.pl <host> <share> <user> <pass> [warn] [critical] [port]
#
# Nagios host script to get the disk usage from a SMB share
#
# Changes and Modifications
# =========================
# 7-Aug-1999 - Michael Anthon
#  Created from check_disk.pl script provided with netsaint_statd (basically
#  cause I was too lazy (or is that smart?) to write it from scratch)
# 8-Aug-1999 - Michael Anthon
#  Modified [warn] and [critical] parameters to accept format of nnn[M|G] to
#  allow setting of limits in MBytes or GBytes.  Percentage settings for large
#  drives is a pain in the butt
# 2-May-2002 - SGhosh fix for embedded perl
# 13-Aug-2008 - Steve Huff
#  Rewrote using Nagios::Plugin
#
# $Id: check_disk_smb.pl,v 1.4 2008/08/13 19:08:20 shuff Exp $
#

require 5.004;
use POSIX;
use strict;
use warnings;
use FindBin qw($Bin);
use lib "$Bin/../perl/lib", "/usr/local/nagios/libexec";
use utils qw($TIMEOUT %ERRORS &print_revision &support &usage);

use Nagios::Plugin;
use Nagios::Plugin::Getopt;

my ($np) = Nagios::Plugin->new(
    shortname => 'check_disk_smb',
    timeout   => $TIMEOUT,
);

my ($ng) = Nagios::Plugin::Getopt->new(
    usage =>
        "Usage: %s -H <host> -s <share> -u <user> -p <password> -w <warn> -c <crit> [-W <workgroup>] [-P <port>] [--help] [--version]\n",
    version => '1.2',
    blurb   => 'Perl Check SMB Disk plugin for Nagios',
    extra =>
        "\nIf thresholds are followed by either a k, M, or G then check to see if that much disk space is available (kilobytes, Megabytes, Gigabytes)\n\nWarning percentage should be less than critical\nWarning (remaining) disk space should be greater than critical.",
    license => 'Copyright (c) 2000 Michael Anthon/Karl DeBisschop',
);

$ng->arg(
    spec     => 'hostname|H=s',
    help     => "NetBIOS name of the server",
    required => 1,
);

$ng->arg(
    spec     => 'share|s=s',
    help     => "Share name to be tested",
    required => 1,
);

$ng->arg(
    spec     => 'workgroup|W=s',
    help     => "Workgroup or Domain used (Defaults to \"WORKGROUP\")",
    required => 0,
    default  => 'WORKGROUP',
);

$ng->arg(
    spec     => 'user|u=s',
    help     => "Username to log in to server. (Defaults to \"guest\")",
    required => 0,
    default  => 'guest',
);

$ng->arg(
    spec => 'password|p=s',
    help => "Password to log in to server. (Defaults to an empty password)",
    required => 0,
    default  => '',
);

$ng->arg(
    spec => 'warning|w=s',
    help => [
        "Percent of used space at which a warning will be generated (Default: 85%)",
        "Amount of used space [k/M/G] at which a warning will be generated",
    ],
    required => 0,
    default  => '85',
    label    => [ 'PERCENT%', 'BYTES[kMG]', ],
);

$ng->arg(
    spec => 'critical|c=s',
    help => [
        "Percent of used space at which a critical will be generated (Default: 95%)",
        "Amount of used space [k/M/G] at which a critical will be generated",
    ],
    required => 0,
    default  => '95',
    label    => [ 'PERCENT%', 'BYTES[kMG]', ],
);

$ng->arg(
    spec => 'port|P=i',
    help =>
        "Port to be used to connect to. Some Windows boxes use 139, others 445 (Defaults to smbclient default)",
    required => 0,
);

$ng->getopts();

my ($host)      = $ng->hostname;
my ($share)     = $ng->share;
my ($port)      = $ng->port;
my ($user)      = $ng->user;
my ($pass)      = $ng->password;
my ($workgroup) = $ng->workgroup;
my ($warning)   = $ng->warning;
my ($critical)  = $ng->critical;

$0 = "$0 $host args hidden";

my ( $verbose, $warn, $crit );

$ENV{'PATH'}     = '';
$ENV{'BASH_ENV'} = '';
$ENV{'ENV'}      = '';

my $smbclient = "$utils::PATH_TO_SMBCLIENT";
my $smbclientoptions = $port ? "-p $port " : "";

if ( !$smbclient || !-x $smbclient ) {
    $np->nagios_die("Path to smbclient not configured correctly in utils.pm");
}

# Options checking

( $host =~ /^([-_.A-Za-z0-9]+\$?)$/ )
    or usage("Invalid host: $host\n");

( $share =~ /^([-_.A-Za-z0-9]+\$?)$/ )
    or usage("Invalid share: $share\n");

( $user =~ /^([-_.A-Za-z0-9\\]+)$/ )
    or usage("Invalid user: $user\n");

( $warning =~ /^([0-9]{1,2}\%?|100\%?|[0-9]+[kMG])$/ )
    or usage("Invalid warning threshold: $warning\n");

( $critical =~ /^([0-9]{1,2}\%?|100\%?|[0-9]+[kMG])$/ )
    or usage("Invalid critical threshold: $critical\n");

# split the type from the unit value
#Check $warn and $crit for type (%/M/G) and set up for tests
#P = Percent, K = KBytes
my $warn_type;
my $crit_type;

if ( $warning =~ /^([0-9]+)\%?$/ ) {
    $warn      = "$1";
    $warn_type = "P";
}
elsif ( $warning =~ /^([0-9]+)k$/ ) {
    $warn_type = "K";
    $warn      = $1;
}
elsif ( $warning =~ /^([0-9]+)M$/ ) {
    $warn_type = "K";
    $warn      = $1 * 1024;
}
elsif ( $warning =~ /^([0-9]+)G$/ ) {
    $warn_type = "K";
    $warn      = $1 * 1048576;
}
if ( $critical =~ /^([0-9]+)\%?$/ ) {
    $crit      = "$1";
    $crit_type = "P";
}
elsif ( $critical =~ /^([0-9]+)k$/ ) {
    $crit_type = "K";
    $crit      = $1;
}
elsif ( $critical =~ /^([0-9]+)M$/ ) {
    $crit_type = "K";
    $crit      = $1 * 1024;
}
elsif ( $critical =~ /^([0-9]+)G$/ ) {
    $crit_type = "K";
    $crit      = $1 * 1048576;
}

# check if both warning and critical are percentage or size
unless ( ( $warn_type eq "P" && $crit_type eq "P" )
    || ( $warn_type ne "P" && $crit_type ne "P" ) )
{
    $warning  =~ s/\%/\%\%/g;
    $critical =~ s/\%/\%\%/g;
    usage(
        "Both warning and critical should be same type- warning: $warning critical: $critical \n"
    );
}

# verify warning is less than critical
if ( $warn_type eq "K" ) {
    unless ( $warn > $crit ) {
        usage(
            "Disk size: warning ($warning) should be greater than critical ($critical) \n"
        );
    }
}
else {
    unless ( $warn < $crit ) {
        $warning  =~ s/\%/\%\%/g;
        $critical =~ s/\%/\%\%/g;
        usage(
            "Percentage: warning ($warning) should be less than critical ($critical) \n"
        );
    }
}

#$workgroup = $1 if (defined($workgroup) && $workgroup =~ /(.*)/);

# sanitize options that will be passed to shell
my ($hostname)  = $host;
my ($sharename) = $share;
$host             = quotemeta($host);
$share            = quotemeta($share);
$workgroup        = quotemeta($workgroup);
$user             = quotemeta($user);
$pass             = quotemeta($pass);
$smbclientoptions = quotemeta($smbclientoptions);

# end of options checking

my $state  = "OK";
my $answer = undef;
my $res    = undef;
my @lines  = undef;

# Just in case of problems, let's not hang Nagios
$SIG{'ALRM'} = sub {
    $np->nagios_exit( $ERRORS{'CRITICAL'}, "No answer from client" );
};
alarm($TIMEOUT);

# Execute an "ls" on the share using smbclient program
# get the results into $res
if ( defined($workgroup) ) {
    $res
        = qx/$smbclient \/\/$host\/$share -W $workgroup -U $user%$pass $smbclientoptions -c ls/;
}
else {
    print "$smbclient "
        . "\/\/$host\/$share"
        . " $pass -U $user $smbclientoptions -c ls\n"
        if ($verbose);
    $res
        = qx/$smbclient \/\/$host\/$share -U $user%$pass $smbclientoptions -c ls/;
}

#Turn off alarm
alarm(0);

#Split $res into an array of lines
@lines = split /\n/, $res;

#Get the last line into $_
$_ = $lines[$#lines];
($verbose) && ( print "$_\n" );

#Process the last line to get free space.
#If line does not match required regexp, return an UNKNOWN error
if (/\s*(\d*) blocks of size (\d*)\. (\d*) blocks available/) {

    my ($avail)       = ( $3 * $2 ) / 1024;
    my ($avail_bytes) = $avail;
    my ($capper)      = int( ( $3 / $1 ) * 100 );
    my ($mountpt)     = "\\\\$hostname\\$sharename";

    if ( int( $avail / 1024 ) > 0 ) {
        $avail = int( $avail / 1024 );
        if ( int( $avail / 1024 ) > 0 ) {
            $avail = ( int( ( $avail / 1024 ) * 100 ) ) / 100;
            $avail = $avail . "G";
        }
        else {
            $avail = $avail . "M";
        }
    }
    else {
        $avail = $avail . "K";
    }

    ($verbose) && ( print ":$warn:$warn_type:\n" );
    ($verbose) && ( print ":$crit:$crit_type:\n" );
    ($verbose) && ( print ":$avail:$avail_bytes:$capper:$mountpt:\n" );

    # set thresholds
    my ( $value, $uom );

    if ( ( $warn_type eq 'P' ) && ( $crit_type eq 'P' ) ) {

        # we're using percentages
        $np->set_thresholds(
            warning  => 100 - $warn . ':',
            critical => 100 - $crit . ':'
        );
        $value = $capper;
        $uom   = '%';
    }
    else {

        # we're using bytes
        $np->set_thresholds(
            warning  => $warn . ':',
            critical => $crit . ':'
        );
        $value = $avail_bytes;
        $uom   = 'B';
    }

    # check thresholds
    $state = $np->check_threshold($value);

    if ( $state > 0 ) {

        # a problem
        $answer = "Only $avail ($capper%) free on $mountpt\n";
    }
    else {

        # ok
        $answer = "$avail ($capper%) free on $mountpt\n";
    }

    # add performance data
    $np->add_perfdata(
        label     => 'free',
        value     => $value,
        uom       => $uom,
        threshold => $np->threshold,
    );
}
else {
    $answer = "Result from smbclient not suitable\n";
    $state  = "UNKNOWN";
    foreach (@lines) {
        if (/(Access denied|NT_STATUS_LOGON_FAILURE)/) {
            $answer = "Access Denied\n";
            $state  = "CRITICAL";
            last;
        }
        if (/(Unknown host \w*|Connection.*failed)/) {
            $answer = "$1\n";
            $state  = "CRITICAL";
            last;
        }
        if (/(You specified an invalid share name|NT_STATUS_BAD_NETWORK_NAME)/
            )
        {
            $answer = "Invalid share name \\\\$host\\$share\n";
            $state  = "CRITICAL";
            last;
        }
    }
}

# did we get a number back or a string?
my ($EXIT) = ( $state =~ /^\d$/ ) ? $state : $ERRORS{$state};

$np->nagios_exit( $EXIT, $answer );