#!/usr/bin/perl

#
# By Cyril Menigoz
# cyril.menigoz@gmail.com
#

# imports
use strict;
use Nagios::Plugin;
# variables
my $shinken;
my $process = "";
my $name = "";
my $user = "";
my $userName="";
my @type= ();
my @warn = (70,70);
my @crit=(90,90);
my $operator="or";
my $ramUsed=0;
my $cpuUsed=0;
my $nbProcess=0;



my $ps;


# plugin setup
$shinken = Nagios::Plugin->new(
        plugin                   => 'check_process',
        shortname        => 'Processes activity ',
        version                => '1.0',
        usage                   => 'Usage: %s -C <check type>  -p <process> [-w <warning %>] [-c <critical %>]   [-n nickname] ] [-u <user>] [-o <operator between check type> ] ',
        blurb                    => 'This plugin checks the process activity using ps ',
        license                 => 'This nagios plugin is free software, and comes with ABSOLUTELY no WARRANTY!'
);

$shinken->add_arg(spec => 'checktype|C=s',
                          help => "can be  \"CPU\" or \"RAM\" or both separed by a comma",
                          required => 1);
$shinken->add_arg(spec => 'warning|w=s',
                          help => "warning threshold , default = 70\n \tIf you check both type put values in the same order as in the -C arg or put only one value  ",
                          required => 0);
$shinken->add_arg(spec => 'critical|c=s',
                          help => "Critical threshold, default = 90\n \tIf you check both type put values in the same order as in the -C arg or put only one value ",
                          required => 0);
$shinken->add_arg(spec => 'operator|o=s',
                          help => "Operator between check type \n \t can be \"and\" or \"or\",  default =\"or\"",
                          required => 0);
$shinken->add_arg(spec => 'process|p=s',
                          help => "process(es) to be check separed by comma",
                          required => 1);
$shinken->add_arg(spec => 'user|u=s',
                          help => "user to monitor ",
                          required => 0);
$shinken->add_arg(spec => 'name|n=s',
                          help => "name to show instead of the command name from the  \" -p \"  arg  ",
                          required => 0);

# main
$shinken->getopts;

@type =   split (',',$shinken->opts->checktype);


if(defined ($shinken->opts->critical))
{    
	    @warn = split (',',$shinken->opts->warning);
	    if ( scalar(@warn)==1)
	    {
			  @warn[1]=@warn[0];
	    }
}	    
if(defined ($shinken->opts->critical))
{
                    @crit = split (',',$shinken->opts->critical);
                    if  (scalar(@crit)==1)
                    {
                                    @crit[1]=@crit[0];
                    }
}

if (defined ($shinken->opts->operator)&&($shinken->opts->operator=~"and"))
{
	$operator="and";
}

if (defined ($shinken->opts->user))
{
	$user="-u " .$shinken->opts->user;
	$userName = " user : " . $shinken->opts->user;
}


if (defined ($shinken->opts->process))
{
	foreach my $entry (split(",", $shinken->opts->process)) {
	$process.=" -C " .$entry;
          }
        $name="  Processes checked : " ;
	if (defined ($shinken->opts->name))
          {
                 $name.= $shinken->opts->name;
           }
           else
           {
		  $name.=$shinken->opts->process;
	   }
	  $name.= "- ";
	   
}
else
{
	$shinken->nagios_die("");
}


=for comment
print "type : @type  \n" ;
print "warn : @warn  \n" ;
print "crit :  @crit  \n" ;
print "operator :  $operator  \n" ;
print "user : $user  \n" ;
print "process : $process  \n" ;
print "name : $name  \n" ;
=cut
$ps = `which ps 2> /dev/null`;
chop $ps;
if ( ! -e $ps ) {
        $shinken->nagios_die("could not find ps binary!!");
        }
my @line=();
#print $test;
foreach my $entry (split("\n",`$ps   u $user $process`)) 
{
	@line= split(" ",$entry);
	$cpuUsed+=@line[2];
	$ramUsed+=@line[3];
	$nbProcess++;
}
$nbProcess--; #removing the bad line from the first output of ps


#check threshold
#first exit on unknown if 0% CPU and 0% RAM

if(($cpuUsed==0)&&($ramUsed==0))
{
	$shinken->nagios_exit (3,$name ." ".$userName. " not running" );
	print ("$name  $userName  not running");
	exit;
}
my @code=();
my $retcode=0;
my $message="";
my $ramMax=`ps u|sort -k 4 -r |awk NR==2 |awk '{print \$11" "\$12" "\$13" "\$14" "\$15" "\$16" "\$17" "\$18" "\$19" "\$20}'`;
my $cpuMax=`ps u|sort -k 3 -r |awk NR==2 |awk '{print \$11" "\$12" "\$13" "\$14" "\$15" "\$16" "\$17" "\$18" "\$19" "\$20}'`;
#print $ramMax;
if (@type[0]=~"cpu")
{
	@code[0] = $shinken->check_threshold
	(
		    check => $cpuUsed,
		    warning =>@warn[0],
		    critical => @crit[0],
	  );
	if (@type[1]=~"ram")
	{
		    @code[1] = $shinken->check_threshold
		    (
			      check => $ramUsed,
			      warning =>@warn[1],
			      critical => @crit[1],
		    );
	}
}


elsif (@type[0]=~"ram")
{
	@code[0] = $shinken->check_threshold
	(
		    check => $ramUsed,
		    warning =>@warn[0],
		    critical => @crit[0],
	  );
	if(@type[1]=~"cpu")
	{
		    @code[1] = $shinken->check_threshold
		    (
			     check => $cpuUsed,
			      warning =>@warn[1],
			      critical => @crit[1],
		    );
	}
}
else
{
	 $shinken->nagios_die(" bad check type ! can be \"and\" or \"or\",  default =\"or\"");
 } 


# combination of threshold
=for comment

### BOOLEAN TABLE ###

### OR ###
or_ |_0_ |_1_|_2_|_3_|
_0_ | _0_|_1_|_2_|_3_|
_1_ | _1_|_1_|_2_|_1_|
_2_ | _2_|_2_|_2_|_2_|
_3_ | _3_|_1_|_2_|_3_|

because  i assume an unknown state between an ok state and a warning state as following

or_ |_0_ |_3_|_1_|_2_|
_0_ | _0_|_3_|_1_|_2_|
_3_ | _3_|_3_|_1_|_2_|
_1_ | _1_|_1_|_1_|_2_|
_2_ | _2_|_2_|_2_|_2_|

### AND ###

and|_0_ |_1_|_2_|_3_|
_0_ | _0_|_0_|_0_|_0_|
_1_ | _0_|_1_|_1_|_1_|
_2_ | _0_|_1_|_2_|_3_|
_3_ | _0_|_1_|_3_|_3_|

because  i assume an unknown state has 2/3 chance to hide a state worse or egual to a warning state

and|_2_ |_3_|_1_|_0_|
_2_ | _2_|_3_|_1_|_0_|
_3_ | _3_|_3_|_1_|_0_|
_1_ | _1_|_1_|_1_|_0_|
_0_ | _0_|_0_|_0_|_0_|

=cut

if (defined (@code[1]))
{
	 if($operator=~"or")
		    {
			    if((@code[0]==2)||(@code[1]==2))
			    {
				    $retcode=2;
			      }
			      elsif ((@code[0]==1)||(@code[1]==1))
			      {
				      $retcode=1;
			      }
			      elsif ((@code[0]==3)||(@code[1]==3))
			      {
				      $retcode=3;
			      }
			      else
			      {
				      $retcode=0;
			}
		}
			elsif($operator=~"and") 
		    {
			    if((@code[0]==2)&&(@code[1]==2))
			    {
				    $retcode=2;
			      }
			      elsif ((@code[0]==0)||(@code[1]==0))
			      {
				      $retcode=0;
			      }
			      elsif ((@code[0]==1)||(@code[1]==1))
			      {
				      $retcode=1;
			      }
			      else
			      {
				      $retcode=3;
			      }     
		    }      
			    
	  
	  
}
else 
{
	$retcode=@code[0];
}

$message= " $name -  $userName  - CPU :  $cpuUsed % -  RAM $ramUsed %";
$shinken->add_perfdata(
        label => "CPU ",
        value => $cpuUsed,
);
$shinken->add_perfdata(
        label => "RAM",
        value => $ramUsed,
);
$shinken->add_perfdata(
        label => "Process",
        value => $nbProcess,
);
print($message);
$shinken->nagios_exit($retcode, $message);

