#!/usr/bin/perl5
#
# +---------------------------------------------------------------------+
# | sysdaemon                                                           |
# | Written By   : Trans-Euro I.T Ltd                                   |
# | Written On   : July 27th 1997                                       |
# |                                                                     |
# | Purpose        System monitor                                       |
# |                Reads the contents of :-                             |
# |                /usr/local/etc/sysdaemon/daemontab                   |
# |                the daemon names present are scanned for the results |
# |                being written back to :-                             |
# |                /var/log/sysmon.REPORT                               |
# |               If a failure occurs the appropriate support team are  |
# |               contacted by email.. also if any filestore partition  |
# |               reaches 99%, a pager message can also be sent.        |
# |               This daemon replaces sysmon, which was written in     |
# |               korn shell/bash.                                      |
# |               On recieving a kill -HUP signal the daemon will       |
# |               re-read its config file.                              |
# |               The definable nature of this daemon means...          |
# |               There is NO user changeable code.                     |  
# +---------------------------------------------------------------------+
# | Modified    : August 1st 1999                                       |
# |                                                                     |
# |               Output to a message -pipe that an additional server   |
# |               add-on utility can use to communicate with a win95    |
# |               monitoring utility.                                   | 
# |                                                                     |
# |               October 12th 1999                                     |
# |               Add optional restart and individual contacts          |
# |                                                                     |
# |               Add ability to define behaviour via config file.      |
# |               sysdaemon is now invoked via a config file            |
# |               supplied as a parameter to sysdaemon.                 |
# |                                                                     |
# | (c) 1997,1999 This script is the intellectual property of           |
# | Trans-Euro I.T Ltd                                                  |
# +---------------------------------------------------------------------+

use Config;

$VERSION="1.60";
$PROGRAM="sysdaemon";

$0=$PROGRAM;
$config=$ARGV[0];

if ( ! $config ) { print "usage: $PROGRAM <config_file>\n";
                   exit 1;
                 }

unless ( -s "$config") { die "$config is either missing or empty !!\n";}

$hup=0;

$pidfile="";
$tab="";
$report="";
$pipe="";
$date=get_date();
$time=get_time();
$node="";
$daemon_name="";
$status="";
$work1="/tmp/sysdaemon1.$$";
$work2="/tmp/sysdaemon2.$$";
$mailprog="";
$support="";
$fs_support="";
$filesystem="";
$capacity="";
$fstat="";
$restart_action="";

@daemontable=();
@proc=();
@fstab=();

format SYSDAEMON =
@<<<<<<<<<<<<<<<<<< : daemon is @<<<<<<<<<<<<<<<<<<<<<<<<:Status is @<<<<<<<<<<<
$time                           $daemon_name                        $status 
.

format SYSDAEMONHUP =
@<<<<<<<<<<<<<<<<<< : HUP re-reading @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
$time                                $tab
.

format SYSDAEMONFS =
@<<<<<<<<<<<<<<<<<< : @<<<<<<<<<<<<<<<<<<< : @<<<<       : @<<<<
$time                 $filesystem            $capacity     $fstat
.

                 
read_daemontab();
        
unless (fork) {
 unless (fork) {
   sleep until getppid == 1;

# +---------------------------------------------------------------------+
# | Main Daemon control loop                                            |
# +---------------------------------------------------------------------+

   write_pid();
   write_report_header();
       
   while ( ! $daemon_control) {

         # +---------------------+
         # | Catch signals here! |
         # +---------------------+

         $SIG{HUP} = \&hup;
         if ($hup) { read_daemontab();$hup=0;}

         # +---------------------+
         # | main process loop   |
         # +---------------------+

         $SIG{HUP} = \&hup;
        
         $date=get_date();
         $time=get_time();
         zero_pipe();
         gather_process_info();
         scan_process();          
         check_filestore();

         unlink($work1);
         unlink($work2);
         sleep 300;
                              }
            
 exit 0;
               }
exit 0;
              }

# +---------------------------------------------------------------------+
# | Sub programs                                                        |
# +---------------------------------------------------------------------+

sub hup {
  $hup=1;
 }

# +---------------------------------------------------------------------+

sub read_daemontab {

# read in main sysdaemon configuration first

if ($hup eq "1") { log_hup();}

 open(CONFIG,"$config") || die "Cannot open configuration $config!\n";
     while($config_line=<CONFIG>) { 
       $config_line=~s/\012//g;
       $config_line=~s/\ {1,}//g;
         SWITCH: {
          if ($config_line =~ /^PIDFILE=/)   {(undef,$pidfile)=split(/=/,$config_line)};
          if ($config_line =~ /^DAEMONTAB=/)   {(undef,$tab)=split(/=/,$config_line)};
          if ($config_line =~ /^REPORT=/){(undef,$report)=split(/=/,$config_line)};
          if ($config_line =~ /^PIPE=/)   {(undef,$pipe)=split(/=/,$config_line)};     
          if ($config_line =~ /^NODE=/)   {(undef,$node)=split(/=/,$config_line)};     
          if ($config_line =~ /^MAILPROG=/)   {(undef,$mailprog)=split(/=/,$config_line)};   
          if ($config_line =~ /^PS=/)        {(undef,$ps)=split(/=/,$config_line)};
          if ($config_line =~ /^PSFLAGS=/)   {(undef,$psflags)=split(/=/,$config_line)};
          if ($config_line =~ /^FS_SUPPORT=/)   {(undef,$fs_support)=split(/=/,$config_line)}
                 }
                                   }
    close(CONFIG);

open(T,"$tab") || die "Cannot open $tab!\n";
while (<T>) {
    chop;

# This is so we can have comments in the daemontab file.
# This code amended by Tom Lynch of cicso.com

    if (!/^#/){
        push (@daemontable, $_);
    }
}
close(T);

foreach $EOF (@daemontable) {
   $_=$EOF;
   s/\012//g;
   $EOF=$_;}
}

# +---------------------------------------------------------------------+

sub write_pid {
open(PID,">$pidfile") || die "Cannot open $pidfile!!\n";
print PID $$;
close(PID);
}

# +---------------------------------------------------------------------+

sub get_date {
my $date=`date`;
$_=$date;
s/\012//g;
$date=$_;
return $date;
}

# +---------------------------------------------------------------------+

sub write_report_header {
open(REP,">$report")|| die "Cannot open $report!!\n";
print REP "--------------------------------------------------------\n";
print REP "$PROGRAM : $VERSION starts at $date\n";
print REP "Trans-Euro I.T (c) 1997.\n";
print REP "Written by Stephen Martin\n";
print REP "--------------------------------------------------------\n";
close(REP);
} 

# +---------------------------------------------------------------------+

sub get_time {
my $time=`date +"%D : %T"`;
$_=$time;
s/\012//g;
$time=$_;
return $time;
}

# +---------------------------------------------------------------------+

sub gather_process_info {
open(WORK1,"|$ps $psflags > $work1")|| die "Cannot gather process info!\n";
close(WORK1);
open(WORK1,"$work1")|| die "Cannot read process workfile!\n";
@proc=<WORK1>;
close(WORK1);
}

# +---------------------------------------------------------------------+

sub scan_process {
    my $action="";

foreach $dproc (@daemontable){
   ($daemon,$daemon_name,$restart_action,$support)=split(/:/,$dproc);

   $support =~ s/\012//g;
   $restart_action =~ s/\012//g;

   if ( grep /$daemon/, @proc ) {log_daemon_status("$daemon_name","up");}
   else { 
          if ( $restart_action ) { $action="restarting";
                                   open(RESTART,"|$restart_action")|| warn "Cannot invoke restart action for $restart_action\n";
                                   close(RESTART);
                                  } else {
                                   $action="down";}

          log_daemon_status("$daemon_name","$action");
          mail_support("$daemon_name","$support","$action");}
                             } 
   
}

# +---------------------------------------------------------------------+

sub log_daemon_status {
    my ($daemon_name,$state) = @_;
    $status=$state;
    open(SYSDAEMON,">>$report")|| die "Cannot log results to $result!\n";
    write SYSDAEMON;
    close(SYSDAEMON);

    open(SYSDAEMON,">>$pipe")|| die "Cannot log results to $pipe!\n";    
    write SYSDAEMON;
    close(SYSDAEMON);

}

# +---------------------------------------------------------------------+

sub log_hup {
    open(SYSDAEMONHUP,">>$report")|| die "Cannot log HUP call to $result!\n";
    write SYSDAEMONHUP;
    close(SYSDAEMONHUP);
}

# +---------------------------------------------------------------------+

sub mail_support {

     my ($d_name) = $_[0];
     my ($s_name) = $_[1];
     my ($s_resp) = $_[2];

     open (MAIL, "| $mailprog $s_name") || die "Can't open $mailprog!\n";
     print MAIL "To: $s_name\n";
     print MAIL "From:$PROGRAM\n";
     print MAIL "Subject:$PROGRAM : $d_name failure\n";
     print MAIL  "------------------------------------------------------\n\n";
     print MAIL "$PROGRAM has detected $d_name missing on $node\n";

     if ( $s_resp =~ /restarting/) {
     print MAIL "Attempting restart action for $d_name on $node\n";
                                   }

     close (MAIL);
}

# +---------------------------------------------------------------------+

sub check_filestore {
my $line="";
   $d1="";
   $d2="";
   $d3="";
   $d4="";
   $fs="";
open(DF,"|df > $work2")||die "Cannot df!\n";
close(DF);

open(DFW,"$work2")||die "Cannot open $work2!\n";
    while(<DFW>) {
                  @fstab=<DFW>; 
                 }
close(DFW);

foreach $line (@fstab) {
         ($d1,$d2,$d3,$d4,$lim,$fs)=split(/\ {1,}/,$line);
             if ( $lim gt "98%" ) { 
                  log_fs("$fs","$lim","Full");
                  mail_support_fs("$fs","$lim","Full");
                  } else { log_fs("$fs","$lim","Ok")};
                      }                       

}

# +---------------------------------------------------------------------+

sub log_fs {
    my ($l1,$l2,$l3) = @_;
    $fstat="$l3";
    $filesystem=$l1;
    $capacity=$l2;
open(SYSDAEMONFS,">>$report") || die "Cannot open $report to log fs full\n";
write SYSDAEMONFS;
close(SYSDAEMONFS);

open(SYSDAEMONFS,">>$pipe") || die "Cannot open $pipe to log fs full\n";
write SYSDAEMONFS;
close(SYSDAEMONFS);
}

# +---------------------------------------------------------------------+


sub mail_support_fs {

    my ($l1,$l2,$l3) = @_;
    $fstat="$l3";
    $filesystem=$l1;
    $capacity=$l2;

    open (MAIL, "| $mailprog $fs_support") || die "Can't open $mailprog!\n";
    print MAIL "To: $fs_support\n";
    print MAIL "From:$PROGRAM\n";
    print MAIL "Subject:$PROGRAM : Filestore $node Full $filesystem\n";
    print MAIL  "------------------------------------------------------\n\n";
    print MAIL "$PROGRAM has detected that the filesystem $filesystem";
    print MAIL "on $node has become full.\n";
    print MAIL "$filesystem is currently at $capacity.\n";
    close (MAIL);
}

sub zero_pipe{
    open(SYSD,">$pipe") || die "Cannot open $pipe to create new pipe\n";
    print SYSD "\#\#\#\|$PROGRAM New Message\+$date $time \#\#\#\n";
    close(SYSD);
}
