#!/usr/bin/perl -w

####################################################
##
## Author: Bruno Miranda
## E-mail: bruno.miranda@opservices.com.br
##
## Date: 16/06/2012
##
####################################################

use strict;
use Getopt::Long;
use File::Basename;
use IPC::Open3;
use version;
use bignum ( p => -2 );

my $VERSION=1.1;
my $REFERENCE_FREE_VERSION = version->parse('3.3.10');
my $CURRENT_FREE_VERSION = getFreeVersion();

$CURRENT_FREE_VERSION = version->parse($CURRENT_FREE_VERSION);

####################################################
##
## Display help
##
####################################################
sub help {
	print "This plugin uses the command \"free -m\" to check the memory usage of the machine.\n";
	usage();
	print "\n    -c | --critical       Critical threshold in %.";
	print "\n    -w | --warning        Warning threshold in %.";
	print "\n    -u                    Type of memory monitored, can be defined as: ";
	print "\n                       total, swap, cached and buffer.";
	print "\n    -h | --help           Display this help.";
	print "\n    -V | --version        Shows the version of this plugin.";
	print "\n    -v | --verbose        Verbose level [0,2,3]. Default is 0.";
	print "\n";
}

###################################################
##
## Display usage
##
####################################################
sub usage {
	print "Usage:\n";
	print " ", basename($0), " -u <total|swap|cached|buffer> [OPTIONS]...\n";
}

###################################################
##
## Display version
##
####################################################
sub version {
	print "Version: ", $VERSION, "\n";
}

####################################################
##
## Get arguments
##
####################################################
sub getOption  {
	my ($opt_crit, $opt_warn, $opt_help, $opt_type, $opt_version, $opt_verbose);
    $opt_verbose = '';

	Getopt::Long::Configure('bundling');
	GetOptions(
		'c|critical=f' => \$opt_crit,
		'u=s'          => \$opt_type,
		'w|warning=f'  => \$opt_warn,
		'h|help'       => \$opt_help,
		'V|version'    => \$opt_version,
		'v|verbose=f'  => \$opt_verbose
	);

	$opt_verbose = 0 if (!defined($opt_verbose));

	if (defined($opt_help)) {
		help();
		exit(0);
	}

	if (defined($opt_version)){
		version();
		exit(0);
	}

    $opt_verbose = 0 if (! $opt_verbose);

	if (($opt_verbose != 0) && ($opt_verbose != 2) && ($opt_verbose != 3)) {
		help();
		exit(3);
	}

	if (((!defined($opt_crit)) && (defined($opt_warn))) || ((defined($opt_crit)) && (!defined($opt_warn)))){
		help();
		exit(3);
	}

	if ((!defined($opt_type)) || (($opt_type !~ /^cached$/)
			&& ($opt_type !~ /^buffer$/) && ($opt_type !~ /^swap$/)
			&& ($opt_type !~ /^total$/))) {
		usage();
		exit(3);
	}

	$opt_type = "used" if ($opt_type eq "total");
	$opt_type = "swap_used" if ($opt_type eq "swap");

	return ($opt_crit,$opt_warn,$opt_type,$opt_verbose);
}

####################################################
##
##   Use the command "free" to get the memory usage
## of the system.
##
####################################################
sub getMemInfo {
 	my %meminfo;
	my @ret_command;
	my @temp;
	my($wtr, $rdr, $err);

	open3($wtr, $rdr, $err, "/usr/bin/free","-m");
	while (<$rdr>) { push(@ret_command, $_) };

	if ($CURRENT_FREE_VERSION < $REFERENCE_FREE_VERSION) {
		@temp = split(' ', $ret_command[1]);
		$meminfo{'total'} = $temp[1];
		$meminfo{'buffer'} = $temp[5];
		$meminfo{'cached'} = $temp[6];

		@temp = split(' ', $ret_command[2]);
		$meminfo{'used'} = $temp[2];
		$meminfo{'free'} = $temp[3];

		@temp = split(' ', $ret_command[3]);
		$meminfo{'swap_total'} = $temp[1];
		$meminfo{'swap_used'} = $temp[2];
		$meminfo{'swap_free'} = $temp[3];

	} else {
		@temp = split(' ', $ret_command[1]);
		$meminfo{'total'} = $temp[1];
		$meminfo{'buffer'} = $temp[5];
		$meminfo{'cached'} = $temp[5];
		$meminfo{'used'} = $temp[2];
		$meminfo{'free'} = $temp[3];

		@temp = split(' ', $ret_command[2]);
		$meminfo{'swap_total'} = $temp[1];
		$meminfo{'swap_used'} = $temp[2];
		$meminfo{'swap_free'} = $temp[3];

	}

	return %meminfo;
}

####################################################
##
##   Defines the status returned by the plugin
## (OK, WARNING or CRITICAL).
##
####################################################
sub defineStatus {
	my $opt_type = shift;
	my $opt_crit = shift;
	my $opt_warn = shift;
	my %meminfo_perc = @_;
	my $status = "OK";

	if ((defined($opt_crit)) && (defined($opt_warn))) {
		if ($meminfo_perc{$opt_type} >= $opt_crit) {
			$status = "CRITICAL";
		} elsif ($meminfo_perc{$opt_type} >= $opt_warn){
			$status = "WARNING";
		}
	}

	return $status;
}

####################################################
##
##  Display verbose informations.
##
####################################################
sub verbose {
	my $opt_verbose = shift;
	my $meminfo = shift;
	my $meminfo_perc = shift;
	my($wtr, $rdr, $err);

	print "\nOutput of the command 'free -m':\n\n";
	open3($wtr, $rdr, $err, "/usr/bin/free","-m");
	while (<$rdr>) { print $_ };
	print "\n\n";

	if ($opt_verbose == 3) {
		print "Hash ID               Memory in MB     Memory em %\n";
		print "-------------------------------------------------\n";
		foreach my $key (keys %$meminfo) {
			print $key, "    \t-\t", $meminfo->{$key}, " MB    \t", $meminfo_perc->{$key}, " %\n";
		}
		print "\n";
	}
}

####################################################
##
##   Returns hash of memory in percentage.
##
####################################################
sub calcPercent {
	my %meminfo = @_;
	my %meminfo_perc;

	$meminfo_perc{'used'} = $meminfo{ "used" } * 100 / $meminfo{ "total" };
	$meminfo_perc{'free'} = $meminfo{ "free" } * 100 / $meminfo{ "total" };
	$meminfo_perc{'buffer'} = $meminfo{ "buffer" } * 100 / $meminfo{ "total" };
	$meminfo_perc{'cached'} = $meminfo{ "cached" } * 100 / $meminfo{ "total" };
	$meminfo_perc{'total'} = 100;
	$meminfo_perc{'swap_total'} = 100;

	if ($meminfo{ "swap_total" } != 0) {
		$meminfo_perc{'swap_used'} = $meminfo{ "swap_used" } * 100 / $meminfo{ "swap_total" };
		$meminfo_perc{'swap_free'} = $meminfo{ "swap_free" } * 100 / $meminfo{ "swap_total" };
	} else {
		$meminfo_perc{'swap_used'} = 0;
		$meminfo_perc{'swap_free'} = 0;
	}

	return %meminfo_perc;
}

####################################################
##
##   Returns the information collected to OpMon.
##
####################################################
sub displayInfo {
	my $opt_type = shift;
	my $opt_crit = shift;
	my $opt_warn = shift;
	my $status = shift;
	my $meminfo = shift;
	my $meminfo_perc = shift;
	my $swap_index = "";
	my $pos_total = "total";

	$opt_warn = 0 if (!defined($opt_warn));
	$opt_crit = 0 if (!defined($opt_crit));

	print $status,": ", $meminfo_perc->{ $opt_type }, "% of";

	print " memory" if ($opt_type eq "used");
	print " ", $opt_type if (($opt_type eq "buffer") || ($opt_type eq "cached"));
	if ($opt_type eq "swap_used") {
		print "  swap";
		$swap_index = "swap_";
		$pos_total = "swap_total";
	}
	print " used (", $meminfo->{ $opt_type }, "MB out of ", $meminfo->{ $pos_total }, "MB)";

	print " | ";

	print ucfirst($opt_type),"=",$meminfo->{ $opt_type },"MB;0;0;0;", $meminfo->{ $pos_total }, " ";
	print ucfirst($opt_type),"_Percent=", $meminfo_perc->{ $opt_type },"%;",$opt_warn ,";", $opt_crit,";0;100 ";
	print "Free=", $meminfo->{ $swap_index.'free' }, "MB;0;0;0;", $meminfo->{ $swap_index.'total' }, " ";
	print "Free_Percent=", $meminfo_perc->{ $swap_index.'free' },"%;0;0;0;100\n";
}

####################################################
##
##   Get operatin system informations from
## /etc/os-release.
##
####################################################
sub getFreeVersion {
	my @ret_command;
	my ($wtr, $rdr, $err);
    my $version;

    open3($wtr, $rdr, $err, '/usr/bin/free', '-V');
    while (<$rdr>) { push(@ret_command, $_) };

    $ret_command[0] =~ s/.* //;
    chomp($ret_command[0]);

	return $ret_command[0];
}

sub main {
	my ($opt_crit, $opt_warn, $opt_type, $opt_verbose) = getOption();
	my %meminfo = getMemInfo();
	my %meminfo_perc = calcPercent(%meminfo);
	my $status = defineStatus($opt_type, $opt_crit, $opt_warn, %meminfo_perc);

	verbose($opt_verbose, \%meminfo, \%meminfo_perc) if ($opt_verbose != 0);
	displayInfo($opt_type, $opt_crit, $opt_warn, $status, \%meminfo, \%meminfo_perc);

	if ($status =~ /WARNING/) {
		exit (1);
	} elsif ($status =~ /CRITICAL/) {
		exit (2);
	}
	exit(0); 
} &main
