#!/usr/bin/perl
# ---------------------------------------------------------------------------- #
# program: send_trap_data.pl - version 1.0                                     #
# purpose: forwards selected SNMP traps to nagios for alerting.                #
# author:  @2008 frank4dd - 20081017                                           #
# ---------------------------------------------------------------------------- #

# ---------------------------------------------------------------------------- #
# Nagios event notification format:                                            #
# Unix timestamp enclosed in [] brackets, one space, the nagios command type   #
# PROCESS_SERVICE_CHECK_RESULT, the hostname the service is registered with,   #
# the service name registered under this host, the event severity where 0 = OK,#
# 1 = warning, 2 = critical, and finally the text message to submit. Example:  #
# [1141163054] PROCESS_SERVICE_CHECK_RESULT;ml08460;check_trap_ml08460;1;text1 #
# ---------------------------------------------------------------------------- #

use strict;
use Getopt::Long;
use vars qw($PROGNAME $opt_V $opt_t $opt_h $numArgs $testlog $cmdfile $utstamp
            $traphost @line $hostname $servicename $eventstr $snmp_rocommunity
            $snmp_sysname $severity $okdelay @fields $trapdetail $textdata
            $DEBUG);

# ---------------------------------------------------------------------------- #
# Global variables to be adjusted as needed                                    #
# ---------------------------------------------------------------------------- #

use lib "/home/app/nagios/libexec";            # the path to nagios utils.pm

$cmdfile="/home/app/nagios/var/rw/nagios.cmd"; # the named pipe to submit events
$snmp_rocommunity="SECro";                     # SNMP read community
$okdelay=30;                                   # delay to auto-OK the service

$DEBUG=1;                                      # if =1, write output to testlog
$testlog="/tmp/test3";                         # debug file to write a copy into

# ---------------------------------------------------------------------------- #
# No changes needed below this line                                            #
# ---------------------------------------------------------------------------- #

use utils qw(%ERRORS &print_revision &support &usage);

$PROGNAME = "send_trap_data.pl";
$ENV{'PATH'}='';
$ENV{'BASH_ENV'}='';
$ENV{'ENV'}='';

sub print_help();
sub print_usage();
sub ip_to_name();

Getopt::Long::Configure('bundling');
GetOptions ("V"   => \$opt_V, "version"    => \$opt_V,
            "h"   => \$opt_h, "help"       => \$opt_h );

if($opt_V) {
        print_revision($PROGNAME,'$Revision: 100 $');
        exit $ERRORS{'OK'};
}
if($opt_h) {print_help(); exit $ERRORS{'OK'};}

# ---------------------------------------------------------------------------- #
# main program starts here                                                     #
# ---------------------------------------------------------------------------- #

$hostname="unset";               # hostname initial value
$eventstr="empty";               # cmdstring initial value
$severity="0";                   # severity initial value

@line = <STDIN>;                 # pick up snmptrapd data

$traphost=$line[0];              # this could be IP if no reverse DNS is avail
chomp($traphost) if($traphost =~ /\n/);

$utstamp=`/bin/date +[%s]`;      # the event timestamp
chomp($utstamp);                 # remove newline from timestamp

if($DEBUG == 1) {
  open(TMPFILE, ">$testlog");
  print TMPFILE "trapline >@line<\n";
  print TMPFILE "traphost >$traphost<\n";
}

# ---------------------------------------------------------------------------- #
# If $traphost be 127.0.0.1, its value should be localhost and disregarded.    #
# If traphost is an IP instead of a name, we need to translate it into a name  #
# because nagios can only look up service association by name. IP check regex: #
# http://www.stedee.id.au/Learn_Regular_Expressions-Examples_IPAddresses       #
# ---------------------------------------------------------------------------- #
#if($traphost =~ /[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/) {
#  ip_to_name();
#} else {
# ---------------------------------------------------------------------------- #
# we got a DNS name, i.e. "proxy02.frank4dd.com". Strip the name from domain.  #
# ---------------------------------------------------------------------------- #
#  @fields=split('\.', $traphost);
#  $hostname=$fields[0];
#  turns out our DNS names do not match host names :-(, so we always reverse
#  via SNMP, picking up the IP from trapip "UDP: [192.168.100.184]:36896"
#}

# ---------------------------------------------------------------------------- #
# Our DNS is unreliable. Either we do not get any reverse DNS entries or they  #
# do not match the real hostname. Therefore we always iuse the trap IP address #
# for a hostname lookup through an SNMP syName get, assuming a trap sender has #
# SNMP read access using our standar SNMP RO community.                        #
# ---------------------------------------------------------------------------- #

$traphost=$line[1];
chomp($traphost);
@fields=split('\[',$traphost);
@fields=split('\]', $fields[1]);
$traphost=$fields[0];
ip_to_name();

if($DEBUG == 1) { print TMPFILE "hostname >$hostname<\n"; }

# ---------------------------------------------------------------------------- #
# if the hostname could not be found, we are screwed, the nagios log will tell.#
# Finally, we build the event message for submission to nagios. The event text #
# depends on the trap we got. This script should handle more then one event,   #
# we create a filter based on the trap content to send the correct text data.  #
# And we are generating the servicename, matching our schema used in nagios.   #
# ---------------------------------------------------------------------------- #

$severity = 1;
build_eventstr();

# ---------------------------------------------------------------------------- #
# We finally submit the data to nagios here (and to a debug file, if enabled). #
# ---------------------------------------------------------------------------- #

if($eventstr =~ /empty/) {
  if($DEBUG == 1) { print TMPFILE "No matching trapdetail found.\n"; }
} else {
  unless(open(CMDFILE, "+<$cmdfile")) {
    if($DEBUG == 1) { print TMPFILE "Could not open $cmdfile\n"; }
  }

  print CMDFILE $eventstr;
  close CMDFILE;
  if($DEBUG == 1) { print TMPFILE "Wrote eventstr to $cmdfile\n"; }

# ---------------------------------------------------------------------------- #
# We build another eventstr with a little delay and severity = 0 to set OK     #
# ---------------------------------------------------------------------------- #

  sleep $okdelay;
  $severity = 0;
  build_eventstr();
  unless(open(CMDFILE, "+<$cmdfile")) {
    if($DEBUG == 1) { print TMPFILE "Could not open $cmdfile\n"; }
  }
  print CMDFILE $eventstr;
  close CMDFILE;
  if($DEBUG == 1) { print TMPFILE "Wrote eventstr to $cmdfile\n"; }
  close CMDFILE;
}

if($DEBUG == 1) { print TMPFILE "End of $PROGNAME.\n"; close TMPFILE; }

# ---------------------------------------------------------------------------- #
# End of main(), subroutine definitions below.                                 #
# ---------------------------------------------------------------------------- #

sub print_usage() {
        print "Usage: $PROGNAME host ip trapdata";
}

sub print_help() {
        print_revision($PROGNAME,'$Revision: 100 $');
        print "Copyright (c) 2008 Frank4DD

This program translates SNMP traps into a Nagios external command.

";
}

# ---------------------------------------------------------------------------- #
# ip_to_name() converts $traphost IP address to a hostname by making a snmpget #
# request back to the trap sender. We assume the trap sender has SNMP enabled  #
# and responds to the SNMPv2-MIB::sysName.0 request with its correct hostname  #
# (in uppercase). We also assume a common SNMP community name for all systems. #
# This routine could also do a /etc/hosts or DNS lookup instead or in addition.#
# ---------------------------------------------------------------------------- #

sub ip_to_name() {

  $snmp_sysname=`/usr/bin/snmpget -c $snmp_rocommunity -v 1 -r 1 -t 1 $traphost SNMPv2-MIB::sysName.0`;
# ---------------------------------------------------------------------------- #
# snmpget -c SECro -v 1 -r1 -t 1 $trapip SNMPv2-MIB::sysName.0                 #
# returns: SNMPv2-MIB::sysName.0 = STRING: MLP09325                            #
# or:      Timeout: No Response from 192.168.203.145.                          #
# (if no SNMP is running, wrong community set, etc)                            #
# ---------------------------------------------------------------------------- #

  chomp($snmp_sysname);

  if($DEBUG) { print TMPFILE "snmpname >$snmp_sysname<\n"; }

  if($snmp_sysname =~ /SNMPv2-MIB::sysName\.0/) {
    @fields=split(" = ", $snmp_sysname);
    @fields=split(": ", $fields[1]);
    $hostname = lc($fields[1]);
  }
}

sub build_eventstr() {
  foreach $trapdetail (@line) {
    if($trapdetail =~ /SNMPv2-MIB::snmpTrapOID\.0 SNMPv2-MIB::coldStart/) {
      # $servicename="check_trap_".$hostname;
      $servicename="check_trap_coldstart";
      $eventstr="$utstamp PROCESS_SERVICE_CHECK_RESULT;$hostname;$servicename;";
      $textdata="System *reboot* or SNMP service restarted.";
      if($severity == 0) { $textdata=$textdata." auto-OK"; }
      $eventstr=$eventstr.$severity.";".$textdata;
      if($DEBUG == 1) { print TMPFILE "eventstr >$eventstr<\n"; }
    }
  }
}
