# $Id: check_memory,v 1.3 2002/01/07 21:25:24 supermathie Exp $
# vim:syn=perl:expandtab:ts=3:sw=3:ai:si
# Register this routine with the plugin registry
$CHECKFUNCS{'memory'} = \&check_memory;

# This routine checks the memory usage on various systems. Since I can't find a
# nice way of doing things, we grab the output of top or free on linux. It's
# likely going to have to be configured for each OS it runs on. :-(

# $MEMCHECK   = "/usr/bin/top -b -n 1 "; # To use 'top'
# $MEMCHECK   = "/usr/bin/free";         # On linux systems

sub check_memory {
   if (!defined $MEMWARN or !defined $MEMCRIT) {
      $color = "yellow";
      $summary = "Warning levels not defined";
      $message = "\$MEMWARN or \$MEMCRIT not defined!";
   } elsif ($MEMCHECK =~ /free/) {
      ($color, $summary, $message) = &check_memory_free;
   } elsif ($MEMCHECK =~ /top/) {
      ($color, $summary, $message) = &check_memory_top;
   } else {
      $color = "yellow";
      $summary = "Don't know how to check memory";
      $message = "No suitable memory-checking command found\n".
                     "\$MEMCHECK=\"$MEMCHECK\"";
   }

   &debug( "memory - $color, $summary" );
   &status( $SPONGSERVER, $HOST, "memory", $color, $summary, $message );
}

sub check_memory_free {
   open CMD, "$MEMCHECK |";
   $message = <CMD>;

   $line = <CMD>;
   ($memtotal, $memused, $memfree, $shared, $buffers, $cached) =
      ( $line =~ /^Mem:\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)/ );
   $message .= $line;

   $line = <CMD>;
   ($hardmemused, $hardmemfree) =
      ( $line =~ /^[^:]*:\s+(\d+)\s+(\d+)/ );
   $message .= $line;
   
   $line = <CMD>;
   ($swaptotal, $swapused, $swapfree) =
      ( $line =~ /^Swap:\s+(\d+)\s+(\d+)\s+(\d+)/ );
   $message .= $line;

   $physpctused = floor($hardmemused/$memtotal*100);
   $virtpctused = floor(($hardmemused+$swapused)/($memtotal+$swaptotal)*100);
   $summary = "$physpctused% phys mem used, $virtpctused% virt mem used";

   $color = "green";
   if ($virtpctused > $MEMWARN) { $color = "yellow"; }
   if ($virtpctused > $MEMCRIT) { $color = "red"; }

   close CMD;
   return ($color, $summary, $message);
}

sub check_memory_top {
   $kmb='\s+(\d+[KM])';
   $message = "";
   open CMD, "$MEMCHECK |";
   while (<CMD>) {
      /PID/ && last;
      /^$/ && next;
      $message .= $_;
      if (/^Mem/) {
         # top from the procps on Linux has this format:
         if (/^Mem:$kmb av,$kmb used,$kmb free,$kmb shrd,$kmb buff/) {
            ($memtotal,$memused,$memfree,$shared,$buffers) = ($1,$2,$3,$4,$5);
            &to_KB($memtotal,$memused,$memfree,$shared,$buffers);
            $_ = <CMD>;
            $message .= $_;
            if (/^Swap:$kmb av,$kmb used,$kmb free$kmb cached/) {
               ($swaptotal, $swapused, $swapfree, $cached) = ($1,$2,$3,$4);
               &to_KB($swaptotal, $swapused, $swapfree, $cached);
            } else {
               close CMD;
               return ("yellow", "Can't parse memory line", $message);
            };
            $hardmemused = $memused-$buffers-$cached;
         } elsif (/^Memory:$kmb real,$kmb free,$kmb swap,$kmb free swap/) {
            # the top package supporting various unices has this format
            ($memtotal,$memfree,$swapused,$swapfree) = ($1,$2,$3,$4);
            &to_KB($memtotal,$memfree,$swapused,$swapfree);
            $hardmemused = $memtotal-$memfree;
            $swaptotal = $swapused+$swapfree;
         } elsif (/^Memory:$kmb real,$kmb free,$kmb swap in use,$kmb swap free/) {
         # the top package supporting various unices has this format
            ($memtotal,$memfree,$swapused,$swapfree) = ($1,$2,$3,$4);
            &to_KB($memtotal,$memfree,$swapused,$swapfree);
            $hardmemused = $memtotal-$memfree;
            $swaptotal = $swapused+$swapfree;
         } else {
            close CMD;
            return ("yellow", "Can't parse memory line", $message);
         }
         $physpctused = floor($hardmemused/$memtotal*100);
         $virtpctused = floor(($hardmemused+$swapused)/($memtotal+$swaptotal)*100);
      }
   }
   close CMD;
   $color = "green";
   $summary = "$physpctused% phys mem used, $virtpctused% virt mem used";
   if ($virtpctused > $MEMWARN) { $color = "yellow"; }
   if ($virtpctused > $MEMCRIT) { $color = "red"; }
   return ($color, $summary, $message);
}
   
# Convert each value to the appropriate number of kilobytes
sub to_KB {
   my $i;
   for ($i=0; $i <= (scalar(@_)-1); ++$i) {
      if ($_[$i] =~ /^(\d+)$/) { $_[$i] = floor($1/1024); next; }
      if ($_[$i] =~ /^(\d+)K$/) { $_[$i] = $1; next; }
      if ($_[$i] =~ /^(\d+)M$/) { $_[$i] = floor($1*1024); next; }
   }
   return @_;
}

# I'm included perl code, I need this.
1;
