#!/usr/bin/perl
use 5.004;

eval 'require "./preferences.pl"';

sub DEBUG {
  return unless $ADEBUG;
  my $caller = (caller(1))[3];
  open(BUG, ">>$Vardir/debug.log") or return;
  print BUG 'autorecheck.pl:',$caller,': ',join("",@_),"\n";
  close(BUG);
}

&DEBUG("*** Main Start (pid=",$$,") ***\n",scalar(localtime(time)));

$email = $Email;
if (eval "use Mail::Mailer") {
    $email = "";
}

if ($DEMO) { print "Auto Recheck not available in DEMO mode.\n"; exit; }

($PIDFILE, $INTERVAL, $IP, $PORTMASK, $DISCOVER) = @ARGV;
$INTERVAL = $Autointerval if $INTERVAL > 0;
$restartcmd = "$0 '$PIDFILE' '$INTERVAL' '$IP' '$PORTMASK' '$DISCOVER'";
$recreate = $nPULSEcmd.' a=create >/dev/null';
$IP =~ s/\*$/1-254/ if $DISCOVER;
$INTERVAL *= 60;

unless ($PIDFILE) {
  &DEBUG("Invalid (blank) pidfile; exiting.");
  exit;
}

$lockfile = $Vardir."/autorecheck.lock";
if (-f $lockfile) {
  open(F, "<$lockfile");
  $lockpid = <F>;
  close(F);
  chomp $lockpid;
  if ($lockpid != $$) {
    &DEBUG("Lockfile exists, killing the lockfile process $lockpid");
    kill 'TERM', $lockpid;
    unlink($lockfile);
    &DEBUG("$lockfile deleted");
  }
}

unless ($DISCOVER) {
  unless (-f $Configfile) {
    &DEBUG("Unable to find configfile: $Configfile");
    unlink($PIDFILE);
    unlink($lockfile);
    &DEBUG("$PIDFILE and $lockfile deleted; exiting.");
    exit;
  }
}

if ($INTERVAL > 0) {
  fork && exit;  
  close(STDIN); close(STDERR); close(STDOUT);
  use POSIX;
  POSIX::setsid();
  &DEBUG("*** Forked process: ",$$);
}

sub CreateDir {
    my $filename = shift || die "Unable to create a null directory.";
    my @dirs = split(/\//, $filename);
    pop @dirs;     #Filename part
    unshift @dirs; #Root part
    my $dir = "";
    foreach (@dirs) {
      $dir .= "/" . $_;
      mkdir($dir,0775) unless -d $dir;
      die("Unable to create directory: $dir") unless -d $dir;
    }
    return 1;
}

sub ip2file {
    return $Basedir . '/' . join('/', split(/\./, shift));
}

sub SendMail {
    my($status, $ip, $email) = @_;
    return unless ($ip and $email);
    my $mail = new Mail::Mailer;
    eval "use Sys::Hostname; $hostname = hostname();";
    my $from = $myName.' <'.($< || 'root').'@'.($hostname || $ENV{'HOSTNAME'} || 'localhost').'>'; 
    my $type = ($status == $Stat_fail) ? "Failure" : "Recovery";
    $mail->open( { From => $from,
                   To   => $email,
                   Subject => "nPULSE Status Message (Site $type) from $ip" } ) || die $!;
    print $mail "Status message from $from\nPULSE $type reported for $ip\nAt: ",scalar(localtime(time)),"\n";
    close($mail);
}

sub ReadConfigfile {
    local(*C);
    open(C, "<$Configfile") || return;
    my @out = <C>;
    close(C);
    chomp @out;
    return wantarray ? @out : join("\n",@out);
}

sub signal_handler {
    $time_to_die = 1;
    $time_to_restart = 0;
    &DEBUG("TERM or INT signal received.");
}

sub signal_hup {
    $time_to_die = 1;
    $time_to_restart = 1;
    &DEBUG("HUP signal received.");
}

$SIG{INT} = $SIG{TERM} = \&signal_handler;
$SIG{HUP} = \&signal_hup;

open(F, ">$PIDFILE") || exit;
print F $$; close(F);
&DEBUG("$PIDFILE created");

until ($time_to_die) {

  open(F, ">$lockfile") || exit;
  print F $$; close(F);
  &DEBUG("$lockfile created");

  
  &DEBUG("**************************************\n   ",scalar(localtime(time)),"\n   DISCOVER    = $DISCOVER\n   PORTMASK    = $PORTMASK\n   IP          = $IP\n   email       = $email\n   INTERVAL    = $INTERVAL\n");

  undef @cmds;
  if ($DISCOVER)    { push(@cmds, $Nmapcmd." --min_rtt_timeout $MinRttTimeout -v -oM - -sT '".$IP."' 2>/dev/null | grep \"Host: \""); }
  elsif ($PORTMASK) { push(@cmds, $Nmapcmd." --min_rtt_timeout $MinRttTimeout -v -oM - -sT -p '".$PORTMASK."' '".$IP."' 2>/dev/null | grep \"Host: \""); } 
  elsif (!$IP)      { foreach (&ReadConfigfile()) { ($ipmask, $portmask) = split / /; push(@cmds, $Nmapcmd." --min_rtt_timeout $MinRttTimeout -v -oM - -sT -p '".$portmask."' '".$ipmask."' 2>/dev/null | grep \"Host: \""); } }
  else              { push(@cmds, $Nmapcmd." --min_rtt_timeout $MinRttTimeout -v -oM - -sT '".$IP."' 2>/dev/null | grep \"Host: \""); }
  
  undef @output;
  foreach $cmd (@cmds) {
    &DEBUG("Running cmd: $cmd");
    @out = `$cmd`;
    chomp @out;
    push(@output, @out);
  }
  &DEBUG("Finished commands");

  if ($DISCOVER) {
    open(F, ">$Configfile") || exit;
    $INTERVAL = 0;
    $email = "";
  } else {
    $tempCurrentfile = $Currentfile.'.tmp';
    open(F, ">$tempCurrentfile") || exit;
  }

  &DEBUG("Found ",($#output+1)," output items");

  foreach (@output) {
    ($host, @rest) = split /\t/;
    $host =~ s/^Host: //;
    ($ip, $name) = split(/ /, $host);
    $name =~ s/\(|\)//g;
    unless ($ip) { $ip = $name; $name = ""; }

    if ($DISCOVER) {
      $ports = $rest[0];
      next unless $ports =~ /^Ports/;
      $ports =~ s/^Ports: //;
      $portmask = "";
      foreach (split(/,\s*/, $ports)) { $portmask .= (split(/\//,$_))[0].","; }
      chop $portmask;
      print F $ip," ",$portmask,"\n";
      &DEBUG("Discovered ",$ip," ",$portmask);

    } else {
      $filename = $Basedir . '/' . join('/', split(/\./, $ip)) . ".hist";
      &CreateDir($filename) unless -f $filename;
      open(G, ">>$filename") || exit;
      print G time,"\tHost: ",$ip," (",$name,")\t",join("\t",@rest),"\n";
      close(G);
      print F "Host: ",$ip," (",$name,")\t",join("\t",@rest),"\n";
      if ($email) {
        $efilename = $Basedir . '/' . join('/', split(/\./, $ip)) . ".email";
        if ($rest[0] =~ /^Status: Down/) {
          &DEBUG("Down device: ",$host," -> ",$rest[0]);
          unless (-f $efilename) {
            &SendMail($Stat_fail, $ip, $email);
            open(E, ">$efilename"); print E time," Down"; close(E);
            &DEBUG("Mail sent to '$email' status = Down");
          }
        } elsif (-f $efilename) {
          &SendMail($Stat_ok, $ip, $email);
          &DEBUG("Mail sent to '$email' status = Up");
          unlink($efilename);
        }
      }
    }
  }
  close(F);
  rename $tempCurrentfile, $Currentfile;
  if (($INTERVAL > 0) and (!$DISCOVER)) {
    &DEBUG("Running recreate command: ",$recreate);
    system($recreate);
  }
  unlink($lockfile);
  &DEBUG("$lockfile deleted");
  
  if ($INTERVAL > 0) { &DEBUG("Sleeping for $INTERVAL seconds"); sleep($INTERVAL); }
  else               { $time_to_die = 1; }
}

unlink($PIDFILE);
unlink($lockfile);
&DEBUG("$PIDFILE and $lockfile deleted");
if ($time_to_restart) {
  &DEBUG("Restarting: ",$restartcmd);
  exec($restartcmd);
}
&DEBUG("Exiting");
exit;
