#!/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;
}
