#!/usr/bin/perl -w # ############################################################################## # a simple IRC bot which dispatches messages received via local domain sockets # ############################################################################## ###### ## THIS NEEDS TO BE PORTED TO THE NEW FRAMEWORKS! ## ## STICKY POINTS: the addfh() function doesn't exist in BasicBot or POE::Component::IRC ## ## people suggested we use Anyevent::IRC and POE::Kernel ->select_read and POE::Wheel namespace ## ## in the meantime, inspiration for extensions can be found here: http://svn.foswiki.org/trunk/WikiBot/mozbot.pl use strict; use File::Basename; BEGIN { unshift @INC, dirname($0); } my $VERSION = '0.2'; my $running = 1; # Read a configuration file # The arg can be a relative or full path, or # it can be a file located somewhere in @INC. sub ReadCfg { my $file = $_[0]; our $err; { # Put config data into a separate namespace package CFG; # Process the contents of the config file my $rc = do($file); # Check for errors if ($@) { $::err = "ERROR: Failure compiling '$file' - $@"; } elsif (! defined($rc)) { $::err = "ERROR: Failure reading '$file' - $!"; } elsif (! $rc) { $::err = "ERROR: Failure processing '$file'"; } } return ($err); } # Get our configuration information if (my $err = ReadCfg('/etc/nagios_nsa.cfg')) { print(STDERR $err, "\n"); exit(1); } use POSIX qw(setsid); use IO::Socket; use Net::IRC; sub new { my $self = { socket => undef, irc => undef, conn => undef, commandfile => undef, }; return bless($self, __PACKAGE__); } sub daemonize { my $self = shift; my $pid; chdir '/' or die "Can't chdir to /: $!"; open STDIN, '/dev/null' or die "Can't read /dev/null: $!"; open STDOUT, '>/dev/null' or die "Can't write to /dev/null: $!"; defined ($pid = fork) or die "Can't fork: $!"; if ($pid && $CFG::Nsa{'pidfile'}) { # write pid of child open PID, ">$CFG::Nsa{'pidfile'}" or die "Can't open pid file: $!"; print PID $pid; close PID; } exit if $pid; setsid or die "Can't start a new session: $!"; #open STDERR, '>&STDOUT' or die "Can't dup stdout: $!"; } sub run { my $self = shift; $self->{irc}->do_one_loop(); } sub shutdown { my $sig = shift; print STDERR "Received SIG$sig, shutting down...\n"; $running = 0; } sub socket_has_data { my $self = shift; $self->{socket}->recv(my $data, 1024); if ($CFG::Nsa{'usenotices'}) { $self->{conn}->notice($CFG::Nsa{'channel'}, $data); } else { $self->{conn}->privmsg($CFG::Nsa{'channel'}, $data); } } sub irc_on_connect { my $self = shift; print STDERR "Joining channel '$CFG::Nsa{'channel'}'...\n"; $self->join($CFG::Nsa{'channel'}); } sub irc_on_msg { my ($self, $event) = @_; my $data = join(' ', $event->args); #my $nick = quotemeta($nicks[$nick]); my $to = $event->to; #my $channel = &toToChannel($self, @$to); #my $cmdChar = $commandChar{default}; # FIXME: should look up for CURRENT channel first! #if ( exists $commandChar{$channel} ) { $cmdChar = $commandChar{$channel}; } my $msg = parse_msg($event); if (defined($msg)) { #$self->privmsg($event->nick, "alright, sending this message to nagios, hope it figures it out: $msg"); } else { $self->privmsg($event->nick, "can't parse $data, you want 'ack host service comment'\n"); } } sub irc_on_public { my ($self, $event) = @_; my $data = join(' ', $event->args); my $to = $event->to; if ($data =~ s/([^:]*):\s+//) { if ($1 eq $CFG::Nsa{'nickname'}) { my $msg = parse_msg($event); if (defined($msg)) { #$self->privmsg($event->to, "alright, sending this message to nagios, hope it figures it out: $msg"); } else { $self->privmsg($event->to, "can't parse $data, you want 'ack host service comment'\n"); } } else { #print STDERR "ignoring message $data, not for me (me being $1)\n"; } } else { #print STDERR "ignoring message $data\n"; } } sub parse_msg { my $event= shift; my $data = join(' ', $event->args); my $msg; if ($data =~ m/([^:]*:)?\s*ack(?:knowledge)?\s+([a-zA-Z0-9\-\.]+)(?:\s+([-\w\.]+)(?:\s+([\w\s]+))?)?/) { #print STDERR "writing to nagios scoket ". $CFG::Nsa{'commandfile'} . "\n"; open(my $cmdfile, ">", $CFG::Nsa{'commandfile'}) || die "Can't open Nagios commandfile: $CFG::Nsa{'commandfile'}!\n"; my $host = $2; my ($service, $comment) = (undef, "no comment (from irc)"); if ($4) { $service = $3; $comment = $4; } elsif ($3) { $service = $3; } my $user = $event->nick; $msg = '[' . time() . '] '; if (defined($service)) { $msg .= "ACKNOWLEDGE_SVC_PROBLEM;$host;$service;1;1;1;$user;$comment\n"; } else { $msg .= "ACKNOWLEDGE_HOST_PROBLEM;$host;1;1;1;$user;$comment\n"; } print {$cmdfile} $msg; close($cmdfile); } return $msg; } # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - my $bot = &new; if (-e $CFG::Nsa{'socket'}) { die "Socket '$CFG::Nsa{'socket'}' exists!\n"; } $bot->{socket} = IO::Socket::UNIX->new ( Local => $CFG::Nsa{'socket'}, Type => SOCK_DGRAM, Listen => 5 ) || die "Can't create socket '$CFG::Nsa{'socket'}'!\n"; $SIG{INT} = $SIG{TERM} = \&shutdown; $bot->daemonize(); $bot->{irc} = new Net::IRC; $bot->{conn} = $bot->{irc}->newconn ( Server => $CFG::Nsa{'server'}, Port => $CFG::Nsa{'port'}, Nick => $CFG::Nsa{'nickname'}, Username => $CFG::Nsa{'nickname'}, Password => $CFG::Nsa{'password'}, Ircname => $CFG::Nsa{'realname'} . " (NSA $VERSION)", ) || die "Can't connect to server '$CFG::Nsa{'server'}'!\n"; $bot->{conn}->add_global_handler(376, \&irc_on_connect); $bot->{conn}->add_global_handler('nomotd', \&irc_on_connect); $bot->{conn}->add_global_handler('msg', \&irc_on_msg); $bot->{conn}->add_global_handler('public', \&irc_on_public); #$bot->{conn}->add_global_handler('notice', \&irc_on_msg); $bot->{irc}->addfh($bot->{socket}, \&socket_has_data, 'r', $bot); while ($running) { $bot->run(); } close($bot->{socket}); unlink($CFG::Nsa{'socket'}); exit(0); 1; __END__