#!/usr/bin/perl -wT
#
#
# 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
#
# $Id: check_disk_smb.pl,v 1.10 2003/09/15 14:26:54 tonvoon Exp $
#

require 5.004;
use POSIX;
use strict;
use lib utils.pm ;
use Plugin;
use Plugin::Parameter qw ($helpparameterlist $versionparameterlist $hostnameparameter
			  $userparameter $passwordparameter $warningparameter
			  $criticalparameter $portparameter $timeoutparameter
			  $verboseparameter);
use Getopt::Long;
use vars qw($opt_P $opt_V $opt_h $opt_H $opt_s
	    $opt_W $opt_u $opt_p $opt_w $opt_c
	    $opt_t $opt_v $spacecheck $PROGNAME);
use lib $main::runtimedir;
use utils qw(%ERRORS &usage);

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

$hostnameparameter->description("NetBIOS name of the server");
my $shareparameter = new Plugin::Parameter(-name => "share", -flags => [ 's', 'share' ],
					   -optional => "no", -valueoptional => 0, -type => "SMBPATH",
					   -checker => \&Plugin::Parameter::_check_smbpath,
					   -description => "Share name to be tested" );
my $workgroupparameter = new Plugin::Parameter(-name => "workgroup", -flags => [ 'W', 'workgroup', 'domain' ],
					       -optional => "yes", -valueoptional => 0, -type => "WORKGROUP",
					       -default => "WORKGROUP",
					       -description => "Workgroup or Domain to be used");

my $sizechecker = sub { my ($opt, $parameter, $plugin) = @_;
			if ($$opt !~ /^(100(\.0+)?%?)$/ && $$opt !~ /^([1-9][0-9](\.[0-9]+)?%?)$/
			    && $$opt !~ /^([1-9][0-9]*[kMG])$/) {
			  $plugin->usage();
			  usage("$PROGNAME UNKNOWN: Invalid argument (--" . $parameter->name() ."): $$opt\n");
			} else {
			  $$opt = $1; # Untaint option
			}
		      };
$userparameter->default("guest");
$passwordparameter->default("guest");
$warningparameter->default(85);
$warningparameter->type("THRESHOLD");
$warningparameter->checker($sizechecker);
$warningparameter->description("Percentage of used space at which a warning status will be generated - May also specify amount of free space with k, M, G qualifier");
$criticalparameter->default(95);
$criticalparameter->type("THRESHOLD");
$criticalparameter->checker($sizechecker);
$warningparameter->description("Percentage of used space at which a critical status will be generated - May also specify amount of free space with k, M, G qualifier");
$portparameter->description("Port to connect to. Some Windows boxes use 139 others use 445 - use smbclient default if no specified");
@{$portparameter->flags()}[0] = 'P';

my @commandparameterlist = ( $hostnameparameter,
			     $shareparameter,
			     $workgroupparameter,
			     $userparameter,
			     $passwordparameter,
			     $warningparameter,
			     $criticalparameter,
			     $portparameter,
			     $timeoutparameter,
			     $verboseparameter );

my $plugin = new Plugin(-revision => '$Revision: 1.10 $',
			-copyright => "2000 Michael Anthon/Karl DeBisschop, 2004 Howard Wilkinson <howard\@cohtech.com>",
			-shortcomment => "Perl Check SMB Disk plugin for Nagios.",
			-longcomment  => "If thresholds are followed by either a k, M, or G then check to see if that much disk space is available (kilobytes, Megabytes, Gigabytes). Warning percentage should be less than critical. Warning (remaining) disk space should be greater than critical.",
			-checker => sub { my ($plugin) = @_;
					  # check if both warning and critical are percentage or size
					  if ($opt_w =~ /^(([0-9]){1,2})%?$/ && $opt_c =~ /^(([0-9]){1,2})%?$/ ) {
					    $opt_w =~ /^(\d+)%?$/;
					    $opt_w = $1;
					    $opt_c =~ /^(\d+)%?$/;
					    $opt_c = $1;
					    if ($opt_w >= $opt_c) {
					      $plugin->usage();
					      usage("$PROGNAME UNKNOWN: Percentage: warning ($opt_w) should be less than critical ($opt_c) \n");
					    }
					    $spacecheck = 0;
					  } elsif ($opt_w =~ /^(\d+)[kMG]$/ && $opt_c =~ /^(\d+)[kMG]$/) {
					    $opt_w =~ /^(\d+)([kMG])$/;
					    $opt_w = $1;
					    $opt_w *= 1024 if ($2 ne 'k');
					    $opt_w *= 1024 if ($2 ne 'M');
					    $opt_c =~ /^(\d+)([kMG])$/;
					    $opt_c = $1;
					    $opt_c *= 1024 if ($2 ne 'k');
					    $opt_c *= 1024 if ($2 ne 'M');
					    if ($opt_w <= $opt_c) {
					      $plugin->usage();
					      usage("$PROGNAME UNKNOWN: Disk size: warning ($opt_w) should be greater than critical ($opt_c) \n");
					    }
					    $spacecheck = 1;
					  } else {
					    $plugin->usage();
					    usage("$PROGNAME UNKNOWN: Both warning and critical should be same type - warning: $opt_w critical: $opt_c \n");
					  }
					},
			-parameterlists => [ \@commandparameterlist, $helpparameterlist, $versionparameterlist ] );

$plugin->init();

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

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

# Untaint opt_W which is not checked anywhere been a bit lazy
$opt_W =~ m/^(.*)$/;
$opt_W = $1;

$plugin->start_timeout($opt_t, "No answer from SMBClient");

# Execute an "ls" on the share using smbclient program
# get the results into $res
print "$smbclient " . "\/\/$opt_H\/$opt_s" ." '$opt_p' -W $opt_W -U $opt_u $smbclientoptions -c ls\n" if ($opt_v);
my $res = qx/$smbclient \/\/$opt_H\/$opt_s '$opt_p' -W $opt_W -U $opt_u $smbclientoptions -c ls/;

$plugin->stop_timeout();

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

#Get the last line into $_
$_ = $lines[$#lines];

#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) = "\\\\$opt_H\\$opt_s";

  #Check $warn and $crit for type (%/M/G) and set up for tests
  #P = Percent, K = KBytes

  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";
  }

  if (((!$spacecheck) && (100 - $capper) < $opt_w) || (($spacecheck) && ($avail_bytes > $opt_w))) {
    $answer = "Disk ok - $avail ($capper%) free on $mountpt\n";
  } elsif (((!$spacecheck) && (100 - $capper) < $opt_c) || (($spacecheck) && ($avail_bytes > $opt_c))) {
    $state = "WARNING";
    $answer = "WARNING: Only $avail ($capper%) free on $mountpt\n";
  } else {
    $state = "CRITICAL";
    $answer = "CRITICAL: Only $avail ($capper%) free on $mountpt\n";
  }
} 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 \\\\$opt_H\\$opt_s\n";
      $state = "CRITICAL";
      last;
    }
  }
}


print "$PROGNAME $state: $answer";

exit $ERRORS{$state};
