#!/usr/bin/perl -w
#
# $Id: xserve-raid-checkd.in,v 1.2 2005/05/11 01:37:59 rader Exp $
#

use strict;
use POSIX qw(setsid);

my $prefix = "/usr/local";
my $etc_dir = "$prefix/etc";
my $status_cmd = "$prefix/bin/xserve-raid-status";
my $conf_file = "xserve-raid-tools.conf";

my $mail = "/bin/mail";
my $hostname = `hostname -s`; chop $hostname;

my $port = "80";
my $debug = 0;
my $state = 'OKAY';
my $interval = 60;
my ($begin, $end, $sleep);
my $notify_count = 3;
my $notify_num = 0;
my $notify_now = 1;
my $first_pass = 1;

my %cf;
my $addr;
my $passwd;
my $status;
my $rcpts;

#------------------------------------------------------------------

&read_conf_file;
$addr = $cf{'addr'};
$passwd = $cf{'passwd'};
$rcpts = $cf{'recipients'};

#------------------------------------------------------------------

while ( $ARGV[0] && $ARGV[0] =~ /^-/ ) {
  if ( $ARGV[0] eq '-d' || $ARGV[0] eq '--debug' ) { 
    $debug++;
    $interval = 5;
    shift @ARGV;
    next;
  }
  if ( $ARGV[0] eq '-a' || $ARGV[0] eq '--address' ) { 
    shift @ARGV;
    if ( ! $ARGV[0] ) { &usage; exit 1; }
    $addr = $ARGV[0];
    shift @ARGV;
    next;
  }
  if ( $ARGV[0] eq '-p' || $ARGV[0] eq '--passwd' ) {
    shift @ARGV;
    if ( ! $ARGV[0] ) { &usage; exit 1; }
    $passwd = $ARGV[0];
    shift @ARGV;
    next;
  }
  if ( $ARGV[0] eq '-i' || $ARGV[0] eq '--interval' ) {
    shift @ARGV;
    if ( ! $ARGV[0] ) { &usage; exit 1; }
    $interval = $ARGV[0];
    shift @ARGV;
    next;
  }
  if ( $ARGV[0] eq '-n' || $ARGV[0] eq '--notify-count' ) {
    shift @ARGV;
    if ( ! $ARGV[0] ) { &usage; exit 1; }
    $notify_count = $ARGV[0];
    shift @ARGV;
    next;
  }
  &usage;
  exit 1;
}


#------------------------------------------------------------------
# daemonize

if ( ! $debug ) {
  chdir("/");
  open(STDIN,'/dev/null');
  open(STDOUT,'>/dev/null');
  open(STDERR,'>/dev/null');
  defined(my $pid = fork) or die "could not fork: $!";
  if ( $pid ) { exit; }
  setsid() or die "could not become child: $!";
}

#------------------------------------------------------------------

while (1) {
  if ( $debug ) { print scalar localtime(time), " ** DEBUG ** checking raid\n"; }
  undef $status;
  $begin = time();
  open(IN,"$status_cmd -a $addr -p $passwd|") || die "can not open $status_cmd -a $addr -p $passwd: $!";
  while(<IN>) { $status .= $_; }
  if ( close(IN) ) { 
    # okay...
    if ( $state ne 'OKAY' ) { 
      if ( $debug ) { print scalar localtime(time), " ** DEBUG ** STATE CHANGE to OKAY\n"; }
      $notify_now = 1;
    }
    $state = 'OKAY';
  } else {
    my $exit_status = int($?/256);  # wtf?
    if ( $debug ) { print scalar localtime(time), " ** DEBUG ** EXIT STATUS IS $exit_status\n"; }
    CASE: { 
      if ( $exit_status == 1 ) { 
        # warning...
        if ( $state ne 'REBUILDING' ) {
          if ( $debug ) { print scalar localtime(time), " ** DEBUG ** STATE CHANGE to REBUILDING\n"; }
          $notify_now = 1;
          $notify_num = 0;
          $state = 'REBUILDING';
        }
        last CASE;
      }
      if ( $exit_status == 2 ) { 
        # error...
        if ( $state ne 'DEGRADED' ) {
          if ( $debug ) { print scalar localtime(time), " ** DEBUG ** STATE CHANGE to DEGRADED\n"; }
          $notify_now = 1;
          $notify_num = 0;
          $state = 'DEGRADED';
        }
        last CASE;
      }
      if ( $exit_status == 3 ) {
        if ( $state ne 'DEAD' ) {
          if ( $debug ) { print scalar localtime(time), " ** DEBUG ** STATE CHANGE to DEAD\n"; }
          $notify_now = 1;
          $notify_num = 0;
          $state = 'DEAD';
        }
        last CASE;
      }
      if ( $exit_status == 4 ) {
        if ( $state ne 'STANDBY' ) {
          if ( $debug ) { print scalar localtime(time), " ** DEBUG ** STATE CHANGE to STANDBY\n"; }
          $notify_now = 1;
          $notify_num = 0;
          $state = 'STANDBY';
        }
        last CASE;
      }
      if ( $exit_status == 5 ) {
        if ( $state ne 'DOWN' ) {
          if ( $debug ) { print scalar localtime(time), " ** DEBUG ** STATE CHANGE to DOWN\n"; }
          $notify_now = 1;
          $notify_num = 0;
          $state = 'DOWN';
        }
        last CASE;
      }
    }
  }
  if ( $notify_now ) { 
    $notify_num++;
    if ( $notify_num > $notify_count ) { 
      $notify_now = 0;
      $notify_num = 0;
    } else {
      if ( $state eq 'OKAY' && $first_pass ) { 
        $notify_num = $notify_count + 1;
      } else {
        if ( $debug ) { print scalar localtime(time), " ** DEBUG ** NOTIFICATION $notify_num\n"; }
        &do_notify;
      }
    }
  }
  $first_pass = 0;
  $end = time();
  if ( ($interval - ($end - $begin)) < 1 ) { 
    $sleep = 1; 
  } else { 
    $sleep = $interval - ($end - $begin);
  }
  if ( $debug ) { 
    print scalar localtime(time); printf " ** DEBUG ** sleeping $sleep seconds\n";
  }
  sleep $sleep;
}

exit; 

#==============================================================================

sub do_notify {
 my $subj;
 CASE: {
   if ( $state eq 'OKAY' ) { $subj = $cf{'okay_subject_line'};  last CASE; }
   if ( $state eq 'REBUILDING' ) { $subj = $cf{'rebuilding_subject_line'};  last CASE; }
   if ( $state eq 'DEGRADED' ) { $subj = $cf{'degraded_subject_line'}; last CASE; }
   if ( $state eq 'DEAD' ) { $subj = $cf{'dead_subject_line'};  last CASE; }
   if ( $state eq 'STANDBY' ) { $subj = $cf{'standby_subject_line'};  last CASE; }
   if ( $state eq 'DOWN' ) { $subj = $cf{'down_subject_line'};  last CASE; }
 }
 $subj =~ s/_HOSTNAME_/$hostname/g;
 if ( $debug ) { 
   print scalar localtime(time), " ** DEBUG **   EMAIL TO $rcpts\n"; 
   print scalar localtime(time), " ** DEBUG **   SUBJECT IS \"$subj\"\n";
 }
 open(OUT,"|$mail -s \"$subj (notification $notify_num of $notify_count)\" $rcpts");
 print OUT $status;
 close(OUT);
}

#==============================================================================

sub usage {
print <<EOT
usage: xserve-raid-log [args] 
 --passwd passwd    encrypted passwd (default is $passwd aka "public")
 --address a.b.c.d  check raid at a.b.c.d (default is $addr)
 --interval secs    check raid every secs seconds (default is $interval)
 --notify-count n   send n notifications (default is $notify_count)
 --debug            for debug output (and no fork)
EOT
}

#==============================================================================

sub read_conf_file {
  my $s = '';
  my $cf_file = "$etc_dir/$conf_file";
  if ( $debug ) { print "** DEBUG CONF ** $cf_file **\n"; }
  if ( ! open(IN,"<$cf_file") ) {
    print "xserve-raid-status: $etc_dir/$conf_file: $!\n";
    exit 1;
  }
  while (<IN>) {
    if ( $_ =~ /^\s*$/ ) { next; }
    chop; $_ =~ s/(.*?)\#.*/$1/;
    $s .= $_;
    while ( $_ !~ /;/ && !(eof(IN)) ) {
      $_ = <IN>;
      chop; $_ =~ s/(.*?)\#.*/$1/;
      $s .= $_;
    }
    if ( $s !~ /;/ ) {
      print "xserve-raid-status: $cf_file: syntax error: no semicolon for the last entry\n";
      exit 5;
    }
    $s =~ s/(.*?);.*/$1/;
    if ( $s =~ /(\S+)\s*=\s*(.*)/ ) {
      my $k = $1; my $v = $2;
      if ( $k =~ /_list$/ ) {
        $cf{$k} = [ split(/\s+/,$v) ];
        if ( $debug > 1 ) {
          print "  \$cf{\'$k\'} = wq( ";
          print join " ", @{ $cf{$k} };
          print " );\n";
        }
      } else {
        $cf{$k} = $v;
        if ( $debug > 1 ) { print "  \$cf{\'$k\'} = $v;\n"; }
      }
    } else {
      print "xserve-raid-status: $cf_file: $s: syntax error\n";
      exit 5;
    }
    $s = '';
  }
  close(IN);
}

