#!/usr/bin/perl

 #########################################
 # Arguments:
 #
 # $1 = host name
 # $2 = host address
 # $3 = service description
 # $4 = state
 # $5 = state type (HARD or SOFT)
 # $6 = failure description
 #
 #########################################

use SNMP_Session;
use BER;
use Sys::Syslog qw (:DEFAULT setlogsock);

setlogsock 'unix';
openlog("subsys_event_handler", 'pid', 'daemon');


syslog('warning',  "Incomplete parameters"), exit 1 if (! defined $ARGV[5]);
syslog('info',  "$ARGV[0] $ARGV[1] $ARGV[2] $ARGV[3] $ARGV[4] $ARGV[5]");

$HOSTNAME=$ARGV[0];
$HOSTADDR=$ARGV[1];
$SERVICE=$ARGV[2];
$STATE=$ARGV[3];
$STATETYPE=$ARGV[4];
$DESCR=$ARGV[5];


$conf_file="/etc/nagios/subsys-dep.cfg";
$comm_file="/etc/nagios/subsys-comm.cfg";
$ignore_file="/etc/nagios/subsys-ignore.cfg";
$command_file="/var/spool/nagios/nagios.cmd";
$reparer_config="/etc/repairer/services.conf";
$status_log_file="/var/log/nagios/status.log";

sub snmpset {
	my($host, $community, @varList) = @_;
	my(@enoid, $response, $bindings, $binding, $inoid,$outoid,
		$upoid,$oid,@retvals);
  	my($type, $value);
	while (@varList) {
		$oid   = toOID(shift @varList);
		$type  = shift @varList;
		$value = shift @varList;
		($type eq 'string') && do {
			$value = encode_string($value);
			push @enoid, [$oid,$value];
			next;
		};
		($type eq 'int') && do {
			$value = encode_int($value);
			push @enoid, [$oid,$value];
			next;
		};
		die "Unknown SNMP type: $type";
	}
	srand();
	my $session = SNMP_Session->open($host , $community, 161);
	if ($session->set_request_response(@enoid)) {
		$response = $session->pdu_buffer;
		($bindings) = $session->decode_get_response($response);
		$session->close();
		while ($bindings) {
			($binding, $bindings) = decode_sequence($bindings);
			($oid, $value) = decode_by_template($binding, "%O%@");
			my $tempo = pretty_print($value);
			$tempo =~ s/\t/ /g;
			$tempo =~ s/\n/ /g;
			$tempo =~ s/^\s+//;
			$tempo =~ s/\s+$//;
			push @retvals, $tempo;
		}
		return (@retvals);
	} else {
		return (-1,-1);
	}
}

#
#  Given an OID in either ASN.1 or mixed text/ASN.1 notation, return an
#  encoded OID.
#
sub toOID {
	my $var = shift;
#	if ($var =~ /^([a-z]+[^\.]*)/i) {
#		my $oid = $OIDS{$1};
#		if ($oid) {
#			$var =~ s/$1/$oid/;
#		} else {
#			die "Unknown SNMP var $var\n"
#		}
#	}
	encode_oid((split /\./, $var));
}

sub get_subsys_for_service() {
    
    my($h, $s, $o) = @_;
    open (CONF, $conf_file);
    while ($line = <CONF>){
	chop ($line);
	next if $line =~ /^#/;
	next if $line =~ /^$/;
	my ($host, $service, $subsys, $output) = split(/[ \t]+/, $line, 4);
	if (($h eq $host) && ($s eq $service)){
	    close (CONF), return $subsys if (($output eq "")||(!defined $output));
	    close (CONF), return $subsys if ($o =~ /$output/);
	}
	
    }
    close (CONF); 
    return undef;
}

sub is_ignored_output() {
    
    my($o) = @_;
    open (IGNORE, $ignore_file);
    while ($line = <IGNORE>){
	chop ($line);
	next if $line =~ /^#/;
	next if $line =~ /^$/;
	close (IGNORE), return 1 if ($o =~ /$line/);
    }
    close (IGNORE); 
    return 0;
}

sub get_community(){
    my $h = shift;
    open (COMM, $comm_file);
    while ($line = <COMM>){
	chop ($line);
	next if $line =~ /^#/;
	next if $line =~ /^$/;
	my ($host, $community) = split(/[ \t]+/, $line);
	close(COMM), return $community if ($host eq $h);
    }
    close (COMM);
    return undef;
}

sub repair_service(){
    my ($hostaddr, $community, $subsys, $action) = @_;
    my $node;
    my $oid;
    open (CONF, $reparer_config);
    while ($line = <CONF>){
	next if $line =~ /^#/;
	next if $line =~ /^$/;
	chop ($line);
	($id, $subsys_r) = split(/[ \t]+/, $line);
	if ($subsys eq $subsys_r){
	    my $oid = ".1.3.6.1.4.1.7088.3.$id";
	    my ($response) = &snmpset($hostaddr, $community, $oid, 'string', $action);
	    close(CONF);
	    return 0;
	}
    }
    close(CONF);
    return 0;
}

sub schedule_check(){
    my ($h, $s) = @_;
    my $timestamp = time;
    my $command = "[$timestamp] SCHEDULE_FORCED_SVC_CHECK;$h;$s;$timestamp\n";
    if (open (CMD, ">$command_file")) {
	print CMD $command;
	close(CMD);
    } else {
	syslog('error', "Can't open cmd file");
    }
}

sub submit_passive_check(){
    my ($h, $s, $st, $desc) = @_;
    my $timestamp = time;
    my $command = "[$timestamp] PROCESS_SERVICE_CHECK_RESULT;$h;$s;$st;$desc\n";
    if (open (CMD, ">$command_file")) {
	print CMD $command;
	close(CMD);
    } else {
	syslog('error', "Can't open cmd file");
    }
}

sub is_host_booting {
    my ($host, $boot_time) = @_;
    # Find if host uptime is less than needed for all services to start
    $line = `grep 'HOST;$host' $status_log_file`;
    (undef, $host1, $status, $last_check, $last_state_change, undef) = split(/;/, $line);
    if (($host1 eq $host) && ($status eq "UP")) {
	$now = time();
	if (($now - $last_check) + ($last_check - $last_state_change) <= $boot_time){
	    return 1;
	}
    }
    return 0;
}

if (($STATETYPE eq "HARD") && ($STATE eq "CRITICAL")){
    if (&is_host_booting($HOSTNAME, 180) == 1) {
	syslog('info', "Ignoring HARD CRITICAL state for $SERVICE at $HOSTNAME ($DESCR), host is booting");
	&submit_passive_check($HOSTNAME, $SERVICE, 0, "(Assumed by boot-time check)");
	exit 0;
    }
    if (&is_ignored_output($DESCR)) {
	syslog('info', "Ignoring HARD CRITICAL state for $SERVICE at $HOSTNAME ($DESCR)");
	exit 0;
    }
    $subsys = &get_subsys_for_service($HOSTNAME, $SERVICE, $DESCR);
    if (!defined $subsys) { syslog('warning', "Can't find subsystem for $SERVICE at $HOSTNAME"); exit 0;}
    $community = &get_community($HOSTNAME);
    if (!defined $community) { syslog('warning', "Can't find community for host $HOSTNAME"); exit 0;}
    syslog('warning', "Handling HARD CRITICAL state for $SERVICE at $HOSTNAME ($DESCR), trying to restart $subsys");
    &repair_service($HOSTADDR, $community, $subsys, "restart");
    &schedule_check($HOSTNAME, $SERVICE);
}
