#!/usr/pkg/bin/perl
#
# $Header: /home/vikas/src/nocol/utility/RCS/notifier.pl,v 1.3 1999/01/05 06:53:21 vikas Exp $
#
# 	notifier.pl  for NOCOL
#
# Search for SET_THIS and set the value of $pager_program.
#
# Run this script every 5-10 minutes from crontab. Adjust $crontime 
# accordingly.
#
# CAVEATS:
#	Only looks at CRITICAL events.
#	Sends one email/page per event instead of grouping them together
#
# AUTHOR:
#	Adrian Close (adrian@aba.net.au), 1998
#	vikas@navya.com - Cleaned up for release
#
$nocolroot =  "/tmp/pkgbuild/net/nocol/work/.destdir/usr/pkg" ;			# SET_THIS
push (@INC, "$nocolroot/bin");

# require 5.000;
# require "Time::Local";	# in perl v5
require "timelocal.pl";		# convert May 1998 into unix timestamp
require "nocollib.pl";

# set nocollib.pl debug level
$libdebug = 0;
$debug = 0;

# Set locations of extra configuration files. 'updatesfile' is maintained
# by 'webnocol'
# $updatesfile = "$etcdir/exceptions";
$updatesfile = "$etcdir/updates";
$cfile = "$etcdir/notifier-confg";

## Locations of helper programs
$pager_program = "/usr/local/sendpage/bin/sendpage";	# SET_THIS
$email_program =  "/usr/bin/mail" ;			# SET_THIS

-x $pager_program || undef ($pager_program);
-x $email_program or die "Cannot find $email_program.\n";

## the default user who should get email if there is NO config file.
#  remember to put a backslash in front of the '@'
#$OPSMAIL = "ops\@localhost";
$OPSMAIL =  "root\@localhost" ;			# SET_THIS

# The notifier should run every $crontime seconds (or less) to catch all
# events and avoid extra pages, email etc. This is the time 'window' used
# for catching new critical events (or repeats).
$crontime = 600;		# seconds	# SET_THIS

# Repeat notification interval
# (notify again for events that are a multiple of $repeat_interval secs old)
# Something like an hour is probably sane (i.e. 3600 secs)
$repeat_interval = 3600;	# seconds

##
#  Read configuration file
#
#	site:addr:variable  \t repeat \t <notify-action>
#  where 
#	notify-action = address:medium:min-age  (medium = 'mail' or 'page')
#	repeat = 0 or 1 (hourly page or not)
#	
#  e.g.
#	nfshost:12.1.2.3:ICMP-ping	0	adrian@abc.com:mail:3600,617383939:page:0
#
#
sub readconf {

  if (! open (CONFIG, "< $cfile")) {
    print STDERR "Unable to open $cfile.\n" if ($debug);

    $hrlyrepeat{'default'} = 1;	# for unlisted sites, do repeat notification
    $notify{'default'} = [ $OPSMAIL . ":email" . ":0" ];
    return;
  }

  while (<CONFIG>)
  {
    chomp;
    next if ( /^\s*\#/ || /^\s*$/ );	# comments & blank lines

    # site:addr[:var]  repeat  to:medium:minage
    if (/^(\S+)\s+([0-1])\s+(.+)/)
    {
      $hrlyrepeat{$1} = $2;		# 0 or 1
      $notify{$1} = [ split (/\,/, $3) ] ;	# hash of array values
    }
  }	# while config
  close (CONFIG);

}	# sub readconf()

##
# Read updates file (earlier called exceptions) for special comments on a
# site. This file is maintained by webnocol and its syntax is:
#
#	sitename:siteaddr:variable \t status
# If the status begins with a (H), then the site is hidden/ignored.
#
sub readupdates { 		

  if (open (UPDATESFILE, $updatesfile))
  {
    while (<UPDATESFILE>)
    {
      chomp;
      next if (/^\s*\#/);	# skip comments
      next if (/^\s*$/);	# skip blank lines
      $update{$1} = $2 if (/^(\S+)\s+(.+)/);	# Build $update array
    }
    close (UPDATESFILE);
  }

} #&readupdates


##
# Process Current Events
#

sub processevents {

  # get all data files from NOCOL data directory
  # (extract only those files that have '-output' as prefix)

  opendir(DATADIR, "$datadir") || die "\nCannot open input directory $datadir";
  @datafiles = sort(grep(/.+-output/i, readdir(DATADIR)));
  closedir(DATADIR);

  foreach $datafile (@datafiles)
  {
    open (DATAINPUT, "< $datadir/$datafile") || die "Cannot open $datafile.\n";
    $debug && print STDERR "Processing file: $datafile\n";

    # Get current month and year
    ($junk,$junk,$junk,$junk, $cmonth,$cyear, $junk,$junk,$junk) = 
      localtime(time);
    $esecs = time;		# Epoch seconds (seconds since 1/1/1970)

    $i = 1;	# index for readevent() (global in this file)
    while (&readevent (DATAINPUT, $i))
    {
      next if ($severity{$i} != $E_CRITICAL);	# not critical

      # we check for site:addr:var and then for site:addr generic index
      local ($id)  = "$sitename{$i}:$siteaddr{$i}";	# no variable name
      local ($idv) = "$sitename{$i}:$siteaddr{$i}:$varname{$i}"; # with varname
		
      # event is "hidden" , so ignore
      if ( $update{$idv} =~  m/^\(H\)/ || $update{$id} =~ m/^\(H\)/ ) {
	print  STDERR "$id{$i}: Hidden!\n" if ($debug > 1);
	next;
      }

      # since nocol doesnt store year, check if we are in next year. Remember
      # that $cmonth = 0..11, while $mon{} = 1..12
      local ($tyear) = $cyear;
      --$tyear if ($cmonth < ($mon{$i} - 1));
      # Determine age of event in seconds since epoch
      $eventage = $esecs - timelocal(0, $min{$i}, $hour{$i}, $day{$i},
				     $mon{$i} - 1, $tyear);
      print STDERR "Event age is negative, check.\n" if (($eventage - 60) < 0);
      print STDERR "Event $idv is $eventage secs old.\n" if ($debug > 2);

      if    ( defined($notify{$idv}) ) { &Notify($idv); }
      elsif ( defined($notify{$id}) ) { &Notify($id); }
      elsif ( defined($notify{'default'}) ) { &Notify('default'); }
      else  { print STDERR "No action for event $idv, skipping\n" if $debug; }

      # $i++;		# dont need to increment array index

    }	# while (&readevent)

    close DATAINPUT;

  }	# foreach ($datafile)

}	# sub processevents()


##
# Process addresses to be notified for current event's SITE:ADDRESS
# combination and send via appropriate medium. Check the minimum age
# of the notifier.
#
sub Notify {
  local ($id) = @_;
  local ($address, $medium, $minage);

  print STDERR "Notify: Processing $id\n" if ($debug > 2);
  # Process each address
  foreach $notify (@{ $notify{$id} })
  {
    print STDERR "  Action = $notify\n" if ($debug > 2);
    local ($donotify) = 0;	# assume dont need to page or mail
    ($address,$medium,$minage) = split /:/, $notify;
    $address =~ s/^\s*(.*?)\s*$/$1/;	# trim white space
    $minage = int($minage);
    $minage *= 60;	# convert to seconds
    # print STDERR "event age = $eventage, $address wants min age $minage\n";

    # Check the age of the event and if hourly repeat is set. We have
    # to consider granularity of how often this program is run to avoid
    # excessive notifications.
    # 	- If minage (+- crontime), then notify
    #	- If multiple of an hour, and hrlyrepeat, then notify

    if ($eventage > ($minage - $crontime) && 
	$eventage <= ($minage + $crontime + 1) ) { $donotify = 1; }
    elsif ( (($eventage - $minage) / $repeat_interval) >= 1 &&
	    (($eventage - $minage) % $repeat_interval) <= $crontime )
    {
      # notify only if config permits repeat-notifications
      if ( ( defined($hrlyrepeat{$id}) && $hrlyrepeat{$id} == 1) ||
	   (!defined($hrlyrepeat{$id}) && $hrlyrepeat{'default'} == 1) )
      {
	$donotify = 1;
      }
    }

    next if ($donotify == 0);
		       
    $debug && print STDERR "Notifying: $address by $medium about $sitename{$i}\n";
    $message = "$sitename{$i} $siteaddr{$i} $varname{$i} critical";
    $message .= "  since $hour{$i}:$min{$i} on $mon{$i}/$day{$i}\n";
    if ($medium =~ /sms/i || $medium =~ /page/i)  {
      &PageNotify($id, $address, $message) if (defined($pager_program));
    }
    elsif ($medium =~ /mail/i) {
      &EmailNotify($id, $address, $message);
    }
    else {
      print STDERR "Unknown notify medium- $medium\n";
    }
      
  }	# foreach
}

#
# Send  page
sub PageNotify {
  local ($id, $address, $message) = @_;

  open(XPIPE, "|$pager_program $address") ||
    die "Could not open $pager_program\n"; 
  print XPIPE "$message";
  if ($update{$id} ne "") { print XPIPE " - $update{$id}."; }
  close(XPIPE);
}

sub EmailNotify {		# Send email notification of fault
  local ($id, $address, $message) = @_;

  if ($^O eq 'netbsd' || $email_program =~ /ucb/) {
    open(XPIPE, "|$email_program -s \"NOCOL notification: $sitename{i} $siteaddr{$i}\" $address") ||
      die "Cannot open $email_program.\n";
  }
  else {	# not ucb/mail so cannot take -s command line option
    open(XPIPE, "|$email_program $address") || die "Cannot open $email_program.\n";
  }
  print XPIPE "$message";
  if ($update{$id} ne "") { print XPIPE "\n\t UPDATE: $update{$id}\n"; }
  close(XPIPE);
}


##
## Main program
##

&readconf;
&readupdates;
&processevents;


