Created
May 11, 2011 18:02
-
-
Save tobert/966962 to your computer and use it in GitHub Desktop.
Ancient 2-way SNPP script
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/local/bin/perl | |
use strict; | |
use Getopt::Std; | |
use Net::SNPP; | |
use Fcntl ':flock'; | |
use Sys::Syslog qw( :DEFAULT setlogsock ); | |
# This is some really ancient code I wrote in ~2003 to do 2-way SNPP | |
# with Nextel phones so a handset could ack notifications from Nagios | |
# before web access was widely available. | |
# May need some work to get things working. | |
# @AlTobey, tobert@gmail.com | |
# set up syslogging | |
setlogsock('unix'); | |
openlog( "nagios:", 'pid,cons,ndelay,nowait', 'daemon' ); | |
# process command-line options | |
our( $pins, $message, $opt_p, $opt_t, $opt_h, $opt_s, $opt_u, $opt_m ); | |
getopt( 'pthsum' ); | |
if ( !$opt_p || !$opt_t || !$opt_h || !$opt_u || !$opt_m ) { | |
die "invalid arguments"; | |
} | |
if ( !$opt_s && $opt_t ne 'HOST' ) { | |
die "must specify a service name if type is not HOST"; | |
} | |
if ( $opt_t !~ /^(HOST|SERVICE)$/ ) { | |
die "notification type must be HOST or SERVICE"; | |
} | |
# slurp the rest of ARGV into $opt_m | |
$opt_m .= ' '.join(' ', @ARGV); $opt_m =~ s/\s+$//; | |
# global configuration items | |
our $notification_dir = '/var/opt/nagios/notifications'; | |
our $nagios_contacts_cfg = '/etc/opt/nagios/nagios.cfg'; | |
our $snpp_server = 'snpp.nextel.com'; | |
our $nagios_fifo = '/var/opt/nagios/rw/nagios.cmd'; | |
our @ack_href_fields = qw( time type host service author status ); | |
eval { | |
$SIG{ALRM} = sub { die "SIGALRM Timeout while attempting SNPP 2WAY communication" }; | |
alarm(30); | |
my $snpp = snpp_connect(); | |
foreach my $pin ( split(/\./, $opt_p) ) { | |
syslog 'warning', "Paging PIN $pin with message '$opt_m.'"; | |
$resp = $snpp->command( "2WAY" ); chkresp(1, CMD_OK); | |
$resp = $snpp->command( "PAGE $pin" ); chkresp(2, 8 ); | |
$resp = $snpp->command( "DATA" ); chkresp(3, CMD_MORE); | |
$resp = $snpp->datasend( $opt_m ); chkresp(4, 'defined' ); | |
$resp = $snpp->dataend(); chkresp(5, 'defined' ); | |
$resp = $snpp->command( "MCRE 1 Acknowledge" ); chkresp(6, CMD_OK ); | |
$resp = $snpp->command( "MCRE 2 Decline" ); chkresp(7, CMD_OK ); | |
$resp = $snpp->command( "MCRE 3 Escalate" ); chkresp(8, CMD_OK ); | |
$resp = $snpp->command( "MCRE 4 Silence" ); chkresp(9, CMD_OK ); | |
$resp = $snpp->command( "MCRE 5 Silence ALL" ); chkresp(10, CMD_OK ); | |
$resp = $snpp->command( "SEND" ); chkresp(11, 9 ); | |
writeout( $snpp->message() ); | |
} | |
$snpp->quit(); | |
alarm(0); | |
}; | |
if ( $@ ) { | |
syslog 'warning', "SNPP paging error: $@"; | |
} | |
sub snpp_connect { | |
my $snpp = Net::SNPP->new( $snpp_server ) | |
|| die "could not connect to the SNPP server: $!"; | |
#$snpp->debug( 10 ); # uncomment for Net::SNPP debugging | |
return $snpp; | |
} | |
sub chkresp { | |
my $exit_status = shift; | |
my $expected = shift; | |
# if we get an object, extract the reponse from it | |
if ( $resp && ref($resp) eq 'Net::SNPP' ) { $resp = $resp->response() } | |
# return ok if we got the 'expected' value | |
if ( ($expected eq 'defined' && defined($exit_status)) | |
|| $resp == $expected ) { | |
$resp = undef; | |
return 1; | |
} | |
syslog 'warning', "Invalid response from SNPP server: expected '$expected' and got '$resp'"; | |
# end the session | |
$snpp->quit(); | |
# if the caller expected 0 == 0, we don't exit on error | |
die $exit_status unless $expected == 0; | |
} | |
sub writeout { | |
if ( $_[0] =~ /^(\d{10})\s+(\d+)\s+OK/ ) { | |
my $pin_msgid = $1.'_'.$2; | |
write_ack( $pin_msgid, $opt_t, $opt_h, $opt_s, $opt_u ); | |
} | |
else { | |
syslog 'warning', "error in SNPP communication with $snpp_server for $opt_p/'$opt_m'"; | |
die "did not get an acceptable 2way response from $snpp_server"; | |
} | |
} | |
## File/Array Format | |
# filename: PIN_MSGID | |
# 0: timestamp | |
# 1: (HOST|SERVICE) | |
# 2: hostname | |
# 3: service name | |
# 4: username | |
# 5: status | |
sub write_ack { | |
my $pin_msgid = shift; | |
# optional hashref or list input to this function | |
my( $host, $svc, $user, $status ) = (); | |
if ( ref($_[0]) eq 'HASH' ) { | |
( $type, $host, $svc, $user, $status ) = ( | |
$_[0]->{type}, $_[0]->{host}, $_[0]->{service}, | |
$_[0]->{author}, $_[0]->{status} ); | |
} | |
else { | |
( $type, $host, $svc, $user, $status ) = @_; | |
} | |
if ( !defined($svc) ) { $svc = ''; } | |
if ( !defined($status) ) { $status = 'UNKNOWN' } | |
$pin_msgid =~ s/\s+/_/g; | |
open( OUT, ">$notification_dir/$pin_msgid" ) | |
|| die "could not open $notification_dir/$pin_msgid for writing: $!"; | |
flock( OUT, LOCK_EX ); | |
print OUT time(), "\n$type\n$host\n$svc\n$user\n$status\n"; | |
syslog 'warning', "writing out notification to $notification_dir/$pin_msgid"; | |
syslog 'warning', "$pin_msgid: $type/$host/$svc/$user/$status"; | |
flock( OUT, LOCK_UN ); | |
close( OUT ); | |
} | |
sub read_ack { | |
my $file = shift; | |
my $flag = shift || undef; | |
my %retval = (); | |
open( IN, "<$notification_dir/$file" ) | |
|| die "could not open $notification_dir/$file for reading: $!"; | |
flock( IN, LOCK_EX ); | |
my $idx = 0; | |
while ( my $line = <IN> ) { | |
chomp( $line ); | |
$retval{$ack_href_fields[$idx]} = $line; | |
$idx++; | |
} | |
if ( !$flag ) { | |
flock( IN, LOCK_UN ); | |
close( IN ); | |
} | |
return \%retval; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I inlined a couple files and fixed a couple ancient perl5.005-isms (it had to run on really, really old Unix), so YMMV on compiling, etc..