Skip to content

Instantly share code, notes, and snippets.

@tobert
Created May 11, 2011 18:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tobert/966962 to your computer and use it in GitHub Desktop.
Save tobert/966962 to your computer and use it in GitHub Desktop.
Ancient 2-way SNPP script
#!/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;
}
@tobert
Copy link
Author

tobert commented May 11, 2011

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..

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment