#!/usr/bin/perl

#################################################################################################
##
## check_file_content.pl version 1.0
## 2013 Alexandre Frandemiche
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
## The GNU GPL V2 is available at http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
##
## contact the author directly for more information at:
## slobberbone4884 at gmail.com or on http://www.slobberbone.net
##
##################################################################################################
##
## Check if a string or a chain provided by -s option is present in a file
## which is critical when the number of founded line(s) is bigger than one provided by -c option.
## It works with local file or with file on cifs sharing with smb protocol
## This plugin requires that perl, perl-Filesys-SmbClient package are installed on the system.
## Under RHEL/CentOS or Fedora : yum --enablerepo=rpmforge install perl-Filesys-SmbClient
## Exit 0 on success, providing some information
## Exit 2 on failure.
##

########## IMPORT PACKAGES ##########

use strict;
use POSIX;
use Getopt::Long;
use Filesys::SmbClient;
use Encode;

########## NAGIOS CODE ##########

my %ERRORS=('OK'=>0,'WARNING'=>1,'CRITICAL'=>2,'UNKNOWN'=>3,'DEPENDENT'=>4);

########## VARIABLES ##########
my $Version='1.1';
my $Name=$0;
my $count = 0;
my $state = "OK";
my $status = "";
my $temp_file=		undef;

my $o_file =		undef; 		# path to file 
my $o_protocol =	undef; 		# protocol 
my $o_help=		undef; 		# want some help ?
my $o_user= 		undef; 		# user
my $o_password=		undef;          # password
my $o_domain=		undef;          # domain
my @o_search=		undef;		# word or chain we search
my @o_exception=	undef;		# exception word or chain we not search
my $o_check_type=	undef;		# check type
my $o_version= 		undef;  	# print version
my $o_warn_level=	undef;  	# number cause a warning
my $o_crit_level=	1;  		# number cause an error

########## MAIN ##########

check_options();

# Treatment for copy temporary smb sharing file
if ($o_protocol eq "SMB") {
	$temp_file = copy_smb_file($o_user,$o_password,$o_domain,$o_file);
}

# Treatment for analys file content
my $exception=1;
if ($o_exception[1] eq "") { $exception=0; }

if (!defined $temp_file) {
	$count = analyse_file($o_file,\@o_search,\@o_exception,$o_check_type,$o_crit_level,$exception);
}
else {
	$count = analyse_file($temp_file,\@o_search,\@o_exception,$o_check_type,$o_crit_level,$exception);
	unlink($temp_file);
}
if ($o_check_type eq "INCLUDE") {
	if (($count > $o_crit_level) && ($count > $o_warn_level)) {
		$state = "OK";
	}
	elsif (($count > $o_crit_level) && ($count < $o_warn_level)) {
		$state = "WARNING";
	}
	else
	{
		$state = "CRITICAL";			
	}
}
elsif ($o_check_type eq "EXCLUDE")
{
	if ($count < $o_crit_level)
	{
		$state = "OK";
	}
	else
	{
		$state = "CRITICAL";			
	}
}
else { $state = "UNKNOWN"; }

if ($ERRORS{$state} >= 1) {
	$status = "$state for $o_file ($count found)\n";
} else {
	if ($o_check_type eq "INCLUDE") {
		$status = "$state on $o_file. Found $count for $o_crit_level\n";
	} else {
		$status = "$state on $o_file.\n";
	}
}

# Return code to Nagios
print $status;
exit $ERRORS{$state};

########## FUNCTIONS ##########

# Display plugin's version
sub show_versioninfo { print "$Name version : $Version\n"; }

# Display plugin's usage
sub print_usage {
  print "Usage: $Name -f <file> -P <protocol> [-u <user> -p <password> -d <domain>] -s <\"search_expression\"> -t <check_type> [-w <warn_level> -c <crit_level>] -V\n";
}


# Check parameters passed to the plugin
sub check_options {
  Getopt::Long::Configure ("bundling");
  GetOptions(
      'h'     => \$o_help,        'help'          => \$o_help,
      'f=s'   => \$o_file,        'file:s'	  => \$o_file,  
      'P=s'   => \$o_protocol,    'protocol:s'	  => \$o_protocol,  
      'u:s'   => \$o_user,        'user:s'	  => \$o_user,
      'p:s'   => \$o_password,    'password:s'	  => \$o_password,
      'd:s'   => \$o_domain,      'domain:s'	  => \$o_domain,
      's=s'   => \@o_search,      'search:s'	  => \@o_search,
      'x=s'   => \@o_exception,   'exception:s'	  => \@o_exception,
      't:s'   => \$o_check_type,  'check_type:s'  => \$o_check_type,
      'w:i'   => \$o_warn_level,  'warn:i'	  => \$o_warn_level,
      'c:i'   => \$o_crit_level,  'critical:i'	  => \$o_crit_level,
      'V'     => \$o_version,     'version'       => \$o_version,

  );

  if (defined ($o_help)) { help(); exit $ERRORS{"UNKNOWN"}};
  if (defined($o_version)) { show_versioninfo(); exit $ERRORS{"UNKNOWN"}};
  if (((!defined($o_warn_level) && !defined($o_crit_level)) || (!defined($o_crit_level)) || (defined($o_warn_level) && defined($o_crit_level) && ($o_warn_level < 0 || $o_crit_level < 0)))) { 
  	print "Check warn and crit!\n"; print_usage(); exit $ERRORS{"UNKNOWN"}
  }
  if (!defined($o_protocol) || !defined($o_file) || !defined($o_check_type)) {
	print "Check main information like file, protocol or check_type !\n"; print_usage(); exit $ERRORS{"UNKNOWN"}
  }
  if (defined($o_check_type)) {
	if ((!$o_check_type eq "INCLUDE") && (!$o_check_type eq "EXCLUDE")) {
		print "Check_type must be INCLUDE or EXCLUDE!\n"; print_usage(); exit $ERRORS{"UNKNOWN"}
	}
  }
  if (defined($o_protocol)) {
	if ($o_protocol eq "SMB") {
		if (!defined($o_user) || !defined($o_password) || !defined($o_user)) {
			print "Check authentication information, if not the case, use --protocol=LOCAL !\n";
			print_usage(); exit $ERRORS{"UNKNOWN"};
		}
	}
  }
}

# Display help and plugin's features
sub help {
  print "Check_file_content for Nagios, version ",$Version,"\n";
  print "GPL licence, (c)2013 Slobberbone\n\n";
  print "Site http://www.slobberbone.net\n\n";
  print_usage();
  print <<EOT;
-h, --help
   print this help message
-f, --filepath=PATHTOFILENAME
   full path to file to analyze
-P, --protocol=PROTOCOL
   protocol used to acess file :
	LOCAL for local file (default),
	SMB for CIFS file
-u, --user=USER
   user to login
-p, --password=PASSWORD
   password
-d, --domain=DOMAIN
   domain name
-s, --search="EXPRESSION", expression to search in the specified file if check_type INCLUDE, if not, this is the expression to not find
-x, --exception="EXPRESSION", exception expression to search in the specified file if check_type INCLUDE, if not, this is the expression to not search
-t, --check_type=TYPE
   Type of search :
	INCLUDE for include at least the critical option, then the warning option
	EXCLUDE for exclude with a maximum of founded expression smaller than critical option
-w, --warn=INTEGER
   number that will cause a warning
-c, --critical=INTEGER
   number that will cause an error (Default: $o_crit_level)
-V, --version
   prints version number
Note :
  The script will return
    * With warn and critical options:
        OK       if we are able to found expression and the count result respect <warn_level> and depending check_type,
        WARNING  if we are able to found expression and the count result unrespect <warn_level> and depending check_type,
        CRITICAL if we are able to found expression and the count result unrespect <crit_level> and depending check_type,
        UNKNOWN  if we aren't able to read the file 

EOT
}

# Copy file from Windows sharing using smbclient
sub copy_smb_file
{
  my $o_user=shift;
  my $o_password=shift;
  my $o_domain=shift;
  my $o_file=shift;
  my $timestamp = time;
  my $buffer;

  # By default libsmbclient will attempt to create a smb.conf file in
  # the home directory as specified from the environment variable HOME.
  # So, we have to create it for nagios user
  if (!$ENV{'HOME'} || ! -w $ENV{'HOME'}) {
  	$ENV{'HOME'} = '/tmp';
  }
  if (!-e "$ENV{HOME}/.smb/smb.conf") {
  	# Attempt to create a smb.conf file for libsmbclient...
  	mkdir "$ENV{HOME}/.smb", 0755 unless (-e "$ENV{HOME}/.smb");

  	if (!open(SMB_CONF_FILE, ">$ENV{HOME}/.smb/smb.conf")) {
        print "Cannot create $ENV{HOME}/.smb/smb.conf: $!";
	exit $ERRORS{"UNKNOWN"};
    }
    close(SMB_CONF_FILE);
}
  my $smb = new Filesys::SmbClient(username  => $o_user, password  => $o_password, workgroup => $o_domain) or print "Can't connect:$!\n" && exit $ERRORS{"UNKNOWN"};

  # Manage accent and ohter special character
  my $filepath = decode_utf8($o_file);

  my $remote = $smb->open($filepath) or  print "Can't open $filepath: $!\n" && exit $ERRORS{"UNKNOWN"};
  open my $local, '>', "/tmp/check_file_content.$timestamp" or print "Can't open writted file:$!\n" && exit $ERRORS{"UNKNOWN"};
  while ( defined( my $buffer = $smb->read($remote) ) ) {
  	last if $buffer eq '';
  	print {$local} $buffer;
  }
  $smb->close($remote);

  return "/tmp/check_file_content.$timestamp";
}

# Analyse the content of a file for returned a number to compare
sub analyse_file
{
  my $o_file=shift;
  my $ref_o_search=shift;
  my $ref_o_exception=shift;
  my @o_search = @$ref_o_search;
  my @o_exception = @$ref_o_exception;
  my $o_check_type=shift;
  my $o_crit_level=shift;
  my $exception=shift;
  my $count=0;

  # open file stream
  if (!open(FILER, "<$o_file"))
  {
  	print "Can't open $o_file: $!\n";
  	exit $ERRORS{"CRITICAL"};
  }
  # browse file content
  while(<FILER>) {
	chomp($_);
	my $line=$_;
	my $found=0;

	# Should match
	foreach (@o_search) {
		if ($line =~ m/@o_search/) {
			if ($exception==1) { 
				if ($line =~ m/@o_exception/) {
					$found=0;
				} else {
					$found=1;
				}
			} else {
				$found=1;
			}
			last;
		}
	}

	$count++ if ($found == 1);
  }

  # close file stream
  close(FILER);

  return $count;
}
