#!/usr/bin/perl -w

# Purpose: psadwatchd checks on an interval of every five seconds to make 
# sure that both kmsgsd and psad are running on the box.  If either
# daemon has died, psadwatchd will restart it notify each email
# address in @email_addresses that the daemon has been restarted.
#
# Copyright (C) 1999-2001 Michael B. Rash (mbr@cipherdyne.com)
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program; if not, write to the Free Software
#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
#    USA

use Psad;
use POSIX qw(setsid);
use Sys::Hostname "hostname";
use strict;

#==================== config ========================== ## do not remove this line (used by install.pl to preserve configs) ##
my $CHECK_INTERVAL  = 5;
my $EMAIL_LIMIT     = 10;
my @EMAIL_ADDRESSES = qw(root@localhost);

### system binaries ###
my $mailCmd = "/bin/mail";
my $kmsgsdCmd = "/usr/sbin/kmsgsd";
my $psadCmd = "/usr/sbin/psad";
my $diskmondCmd = "/usr/sbin/diskmond";
#==================== end config ====================== ## do not remove this line (used by install.pl to preserve configs) ##

my %Cmds = (
    "mail"       => $mailCmd,
    "psad"       => $psadCmd,
    "kmsgsd"     => $kmsgsdCmd,
    "diskmond"   => $diskmondCmd
);

%Cmds = &Psad::check_commands(\%Cmds);

### make sure this is the only psadwatchd running on this system
&Psad::unique_pid("/var/run/psadwatchd.pid");

my $pid = fork;
exit if $pid;
die "@@@@@  $0: Couldn't fork: $!" unless defined($pid);
POSIX::setsid() or die "@@@@@  $0: Can't start a new session: $!\n";

### write the pid to the pid file
&Psad::writepid("/var/run/psadwatchd.pid");

my $hostname = hostname;

### get the psad command line args
my $psad_Cmdline = &get_psad_Cmdline("/var/run/psad.cmd", $hostname, \@EMAIL_ADDRESSES);

my ($d_emails, $k_emails, $p_emails) = (0,0,0);
### main loop
for (;;) {
    &check_process("diskmond", "", "/var/run/diskmond.pid", $hostname,
                    \$d_emails, $EMAIL_LIMIT, \@EMAIL_ADDRESSES, \%Cmds);
    &check_process("kmsgsd", "", "/var/run/kmsgsd.pid", $hostname,
                    \$k_emails, $EMAIL_LIMIT, \@EMAIL_ADDRESSES, \%Cmds);
    &check_process("psad", $psad_Cmdline, "/var/run/psad.pid", $hostname,
                    \$p_emails, $EMAIL_LIMIT, \@EMAIL_ADDRESSES, \%Cmds);

    sleep $CHECK_INTERVAL;
}
exit 0;
#=================== end main ==================
sub check_process() {
    my ($pidname, $pidcmdline, $pidfile, $hostname, $email_count_ref,
                        $email_limit, $email_addresses_aref, $Cmds_href) = @_;
    if (-e $pidfile) {
        open PID, "< $pidfile" or print "Could not open $pidfile for $pidname\n" and return;
        my $pid = <PID>;
        close PID;
        unless (kill 0, $pid) {  ### the daemon is not running so start it with $pidcmdline args (which may be empty)
            if ($$email_count_ref > $email_limit) {
                ### this will exit the program
                &give_up($pidname, $email_addresses_aref, $Cmds_href);
            }
            system "$Cmds_href->{$pidname} $pidcmdline";  ### should check the rv of this system() call
            for my $email_address (@$email_addresses_aref) {
                system "$Cmds_href->{'mail'} $email_address -s \"psadwatchd: restarted $pidname on $hostname\" < /dev/null";
            }
            $$email_count_ref++;
            return;
        } else {  ### the program is running now, so reset the watch count to zero
            $$email_count_ref = 0;
        }
    } else {
        for my $email_address (@$email_addresses_aref) {
            system "$Cmds_href->{'mail'} $email_address -s \"psadwatchd: pid file does not exist for $pidname.  Starting $pidname daemon\" < /dev/null";
        }
        ### start $pidname
        system "$Cmds_href->{$pidname} $pidcmdline";
    }
    return;
}

sub get_psad_Cmdline() {
    my ($psad_cmd_file, $hostname, $email_addresses_aref) = @_;
    my $noexit=0;
    my $psad_Cmdline;
    while ($noexit < 100) {
        if (-e $psad_cmd_file) {
            open CMD, "< $psad_cmd_file";
            $psad_Cmdline = <CMD>;
            close CMD;
            return $psad_Cmdline;  ### there may be _no_ command line args
        } else {
            $noexit++;
        }
        sleep 1;
    }
    for my $email_address (@$email_addresses_aref) {
        system "$mailCmd $email_address -s \"psadwatchd: psad is not running on $hostname.  Please start it.\" < /dev/null";
    }
    exit 0;
}

sub give_up() {
    my ($pidname, $email_addresses_aref, $Cmds_href) = @_;
    for my $email_address (@$email_addresses_aref) {
        system "$Cmds_href->{'mail'} $email_address -s \"psadwatchd: restart limit reached for $pidname on $hostname!!!  Exiting.\" < /dev/null";
    }
    exit 0;
}
