#!/usr/bin/perl -w
########################################################
# Swap Monitor by Larry Long - larry@djslyde.com       #
# checkswap - 6/7 programs in checksuite v2.4	       #
#						       #
# This greps the current swap usage from the 'free'    #
# command and if it goes over a certain threshold, it  #
# can log and/or e-mail a notification.                #
########################################################
use strict;
use Getopt::Std;
use Net::SMTP;

# Options: -h (help) -l (log) -o (output to screen) -t (threshold) -e (email)
my %opt;getopts('hlot:e:', \%opt);
usage_info() unless defined @ARGV;
usage_info() if exists $opt{h};

# Localize variables throughout the rest of the program
my($email,$host,$threshold,$total,$used,@note,$notify,$logdate,$logfile,$logsnip,@used,$sused,$script,@ps_data,$threshold1,$subject);

# Define variables
$email = $opt{e};$email = 'root' unless defined $opt{e};
$host = `hostname`;
$logfile = "/var/log/checksuite";
$logdate = `date '+%m/%d/%Y %H:%M:%S' `;
$script = " - [checksuite] checkswap\n";
$logsnip = "----\n";
$notify = 0;
$subject = "[checksuite] advisory - swap memory usage is high on $host";
chomp $host;chomp $logdate;chomp $subject;

# Pull the swap usage
open(SWAP, "free -m|grep Swap:|");
while(<SWAP>)
   {
   @used = split(/\s+/, $_);
   $sused = $used[2];
   $threshold1 = $used[1];
   chomp $threshold1;chomp $sused;
   $threshold = $opt{t};$threshold = $threshold1 / 2 unless defined $opt{t};
   if($sused >= $threshold)
      {
      push(@note, "Current swap usage (MB) is above threshold ($threshold): $sused\n");
      $total = `free -m`;
      push(@note, "Current memory status:\n$total\n");
      push(@note, "Possible Contributions:\n");

# Let's single out the processes that could be contributing
      open(PSLIST, "ps -elf --no-headers|");
      while(<PSLIST>)
         {
         chop;
         @ps_data = split(/\s+/, $_);
         if($ps_data[5] != 0)
            {
            $ps_data[15] = " " unless defined $ps_data[15];
            $ps_data[16] = " " unless defined $ps_data[16];
            $ps_data[17] = " " unless defined $ps_data[17];
            $ps_data[18] = " " unless defined $ps_data[18];
            $ps_data[19] = " " unless defined $ps_data[19];
            $ps_data[20] = " " unless defined $ps_data[20];
            $ps_data[21] = " " unless defined $ps_data[21];
            push(@note, "pid: $ps_data[9] |cpu time: $ps_data[5] |user: $ps_data[2] |command: $ps_data[14] $ps_data[15] $ps_data[16] $ps_data[17] $ps_data[18] $ps_data[19] $ps_data[20] $ps_data[21]\n");
            }
         }
      close(PSLIST);
      $notify++;
      }
   }

# Define where the output goes
if($notify > 0)
   {
   log_data() if exists $opt{l};
   email_data() if exists $opt{e};
   screen_data() if exists $opt{o};
   }

# Subroutines
sub usage_info
   {
   my $usage = "
Usage: $0 [-h | -lo] [-t <threshold>] [-e <email>]
Options:
-h              display this help
-l              log the output to /var/log/checksuite
-o              force output to screen
-t              sets the threshold for notification
-e              e-mail the output to a specified e-mail address
Where:
<threshold>     threshold value - default is half of total swap
<email>         e-mail address of the recipient of the notification
\n";
   die $usage;
   }

sub log_data
   {
   open(LOG, ">>$logfile") or die "Can't open logfile!\n";
   print LOG $logdate,$script,@note,$logsnip;
   close(LOG);
   }

sub screen_data
   {
   print STDERR @note;
   }

sub email_data
   {
   my $smtp = Net::SMTP->new($host);
   if(! ref($smtp))
      {
      log_die("Cannot connect to SMTP\n");
      }
   $smtp->mail($email);
   $smtp->to($email);
   $smtp->data();
   $smtp->datasend("To: " . $email . "\n");
   $smtp->datasend("From: Checksuite Notification <root\@$host>\n");
   $smtp->datasend("Return-Path: " . $email. "\n");
   $smtp->datasend("Subject: " . $subject . "\n");
   $smtp->datasend("\n");
   $smtp->datasend(@note);
   $smtp->datasend();
   $smtp->quit();
   }
