From 30bae026ef679de2339d27fbc625bd632bc1fe89 Mon Sep 17 00:00:00 2001 From: risto Date: Fri, 26 Jan 2018 20:54:52 +0200 Subject: [PATCH] Update --- bsd-MONITOR.sec => bsd/bsd-MONITOR.sec | 0 bsd-PHYSMOD.sec => bsd/bsd-PHYSMOD.sec | 0 bsd-USERACT.sec => bsd/bsd-USERACT.sec | 0 bsd-general.sec => bsd/bsd-general.sec | 0 bsd-mpd.sec => bsd/bsd-mpd.sec | 0 cisco-syslog.sec => cisco/cisco-syslog.sec | 0 pix-general.sec => cisco/pix-general.sec | 0 pix-security.sec => cisco/pix-security.sec | 0 pix-url.sec => cisco/pix-url.sec | 0 xymon/01control.sr | 100 ++++ xymon/CEPoller.sr | 63 ++ xymon/README.pdf | Bin 0 -> 40984 bytes xymon/SimpleEventCorrelator.rc | 22 + xymon/sec2xym.pm | 635 +++++++++++++++++++++ xymon/xymon.sr | 18 + 15 files changed, 838 insertions(+) rename bsd-MONITOR.sec => bsd/bsd-MONITOR.sec (100%) rename bsd-PHYSMOD.sec => bsd/bsd-PHYSMOD.sec (100%) rename bsd-USERACT.sec => bsd/bsd-USERACT.sec (100%) rename bsd-general.sec => bsd/bsd-general.sec (100%) rename bsd-mpd.sec => bsd/bsd-mpd.sec (100%) rename cisco-syslog.sec => cisco/cisco-syslog.sec (100%) rename pix-general.sec => cisco/pix-general.sec (100%) rename pix-security.sec => cisco/pix-security.sec (100%) rename pix-url.sec => cisco/pix-url.sec (100%) create mode 100644 xymon/01control.sr create mode 100644 xymon/CEPoller.sr create mode 100644 xymon/README.pdf create mode 100644 xymon/SimpleEventCorrelator.rc create mode 100644 xymon/sec2xym.pm create mode 100644 xymon/xymon.sr diff --git a/bsd-MONITOR.sec b/bsd/bsd-MONITOR.sec similarity index 100% rename from bsd-MONITOR.sec rename to bsd/bsd-MONITOR.sec diff --git a/bsd-PHYSMOD.sec b/bsd/bsd-PHYSMOD.sec similarity index 100% rename from bsd-PHYSMOD.sec rename to bsd/bsd-PHYSMOD.sec diff --git a/bsd-USERACT.sec b/bsd/bsd-USERACT.sec similarity index 100% rename from bsd-USERACT.sec rename to bsd/bsd-USERACT.sec diff --git a/bsd-general.sec b/bsd/bsd-general.sec similarity index 100% rename from bsd-general.sec rename to bsd/bsd-general.sec diff --git a/bsd-mpd.sec b/bsd/bsd-mpd.sec similarity index 100% rename from bsd-mpd.sec rename to bsd/bsd-mpd.sec diff --git a/cisco-syslog.sec b/cisco/cisco-syslog.sec similarity index 100% rename from cisco-syslog.sec rename to cisco/cisco-syslog.sec diff --git a/pix-general.sec b/cisco/pix-general.sec similarity index 100% rename from pix-general.sec rename to cisco/pix-general.sec diff --git a/pix-security.sec b/cisco/pix-security.sec similarity index 100% rename from pix-security.sec rename to cisco/pix-security.sec diff --git a/pix-url.sec b/cisco/pix-url.sec similarity index 100% rename from pix-url.sec rename to cisco/pix-url.sec diff --git a/xymon/01control.sr b/xymon/01control.sr new file mode 100644 index 0000000..c7f9975 --- /dev/null +++ b/xymon/01control.sr @@ -0,0 +1,100 @@ +############################################################################### +# Rules to allow Windows implementations to be controlled when +# no signals are otherwise supported +############################################################################### +type=single +continue=dontcont +ptype=tvalue +pattern=TRUE +desc=don't apply these rules unless from control file. +context=! CONTROL +action=none + +############################################################################### +type=single +continue=takenext +ptype=tvalue +pattern=TRUE +desc=report event processed. +action=create EVENT_PROCESSED + +############################################################################### +type = single +continue=dontcont +desc = do a full restart of SEC +ptype = regexp +pattern = ^reset +action = lcall %r HUP -> ( sub { Sec2Xym::fake_signal_handler(@_) } );\ + if %r ( logonly %r ) + +############################################################################### +type = single +continue=dontcont +desc = do a soft restart of SEC +ptype = regexp +pattern = ^reload +action = lcall %r ABRT -> ( sub { Sec2Xym::fake_signal_handler(@_) } );\ + if %r ( logonly %r ) + +############################################################################### +type = single +continue=dontcont +desc = dynamically cycle through logging verbosity levels +ptype = regexp +pattern = ^verbose +action = lcall %r INT -> ( sub { Sec2Xym::fake_signal_handler(@_) } );\ + if %r ( logonly %r ) + +############################################################################### +type = single +continue=dontcont +desc = write info about SEC state to the dump file +ptype = regexp +pattern = ^dumpstats +action = lcall %r USR1 -> ( sub { Sec2Xym::fake_signal_handler(@_) } );\ + if %r ( logonly %r ) + +############################################################################### +type = single +continue=dontcont +desc = close and reopen output logs +ptype = regexp +pattern = ^logrotate +action = lcall %r USR2 -> ( sub { Sec2Xym::fake_signal_handler(@_) } );\ + if %r ( logonly %r ) + +############################################################################### +type = single +continue=dontcont +desc = implement fake signal handling +ptype = regexp +pattern = ^kill (TERM|HUP|ABRT|USR1|USR2|INT) +action = lcall %r $1 -> ( sub { Sec2Xym::fake_signal_handler(@_) } );\ + if %r ( logonly %r ) + +############################################################################### +type = single +continue=dontcont +desc = test upstream Xymon server +ptype = regexp +pattern = ^ping +action = lcall %r %XYMSRV 1984 ping -> ( sub { Sec2Xym::sendToXymon(@_) } );\ + logonly %r + +############################################################################### +type = single +continue=dontcont +desc = update configuration files stored on Xymon server +ptype = regexp +pattern = ^refresh_config +action = lcall %r %XYMDL/SimpleEventCorrelator.sr %XYMDL/01control.sr %XYMDL/xymon.sr -> ( sub { Sec2Xym::refresh_config(@_) } ) ; logonly %r + +############################################################################### +type = single +continue=dontcont +desc = toggle debug of Sec2Xym module +ptype = regexp +pattern = ^debug +action = lcall %r -> ( sub { Sec2Xym::toggle_debug(@_) } ) ; logonly %r + +############################################################################### diff --git a/xymon/CEPoller.sr b/xymon/CEPoller.sr new file mode 100644 index 0000000..a2d0767 --- /dev/null +++ b/xymon/CEPoller.sr @@ -0,0 +1,63 @@ +################################################################################ +# Simple Event Correlation rules for the CEPoller log +# v0.001 2017-10-09 First version +################################################################################ +# +# NB, you cannot use the # character within a rule, rules are terminated by a +# blank line or the # comment character. If you want to document within a +# rule, use the rem keyword instead. +# +################################################################################ + +################################################################################ +# SYNOPSIS RULE #1 +# dont apply these rules unless the context matches +############################################################################### +type=single +continue=dontcont +ptype=tvalue +pattern=TRUE +desc=don't apply these rules unless CE Poller log. +context=! CEPOLLER +action=none + +############################################################################### + +################################################################################ +# SYNOPSIS RULE #2 +# just process any lines observed on input containing the words FATAL or ERROR +################################################################################ +type=SingleWithSuppress +ptype=RegExp +pattern=(Mailbox Poller is already running)(.*) +desc=Fatal error +action= delete waiting_for_badstuff ;\ + add bad_stuff_happening %t ;\ + add bad_stuff_happening $0 ;\ + empty bad_stuff_happening %msg ;\ + lcall %r1 %null %COLUMN severe %LIFETIME %msg -> ( sub { Sec2Xym::XymonStatusUpdate(@_) } ) ;\ + logonly %r1 ;\ + set bad_stuff_happening 300 ( \ + add bad_stuff_happening %t ;\ + add bad_stuff_happening %COLUMN recovered;\ + add bad_stuff_happening No more badstuff observed in last 5 minutes;\ + copy bad_stuff_happening %msg ;\ + lcall %r1 %null %COLUMN green %LIFETIME %msg -> ( sub { Sec2Xym::XymonStatusUpdate(@_) } ) ;\ + logonly %r1 \ + ) +window=2 + +################################################################################ +# SYNOPSIS RULE #3 +# create hard link to newest log file in the parent directory +# NB the subroutine refresh_logpath takes 2 arguments, the first is the name +# of the link to be created and the second is the regex of candidate files +################################################################################ +# not used # type = calendar +# not used # time = * * * * * +# not used # desc = relink current file +# not used # action = lcall %relink D:\apps\tomcat\current\logs\pollerLog.log .*_log\.txt -> \ +# not used # ( sub { Sec2Xym::refresh_logpath(@_) } ) ;\ +# not used # if %relink ( logonly %relink ) + +################################################################################ diff --git a/xymon/README.pdf b/xymon/README.pdf new file mode 100644 index 0000000000000000000000000000000000000000..f286dd310bb0c89714b2c8c84bc0f2d6d3375a1e GIT binary patch literal 40984 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 B>V5zK literal 0 HcmV?d00001 diff --git a/xymon/SimpleEventCorrelator.rc b/xymon/SimpleEventCorrelator.rc new file mode 100644 index 0000000..33782e4 --- /dev/null +++ b/xymon/SimpleEventCorrelator.rc @@ -0,0 +1,22 @@ +# SimpleEventCorrelator.rc +# this file (SECRC) is read first, these settings are treated as +# though they had been entered on the command line + +# pull in additional conf files +--conf=etc/xymon.sr +--intevents + +# define the log file to be scanned +--conf=etc/CEPoller.sr +--input=D:\apps\tomcat\current\logs\pollerLog.log=CEPOLLER +--reopen-timeout=61 + +# other useful settings +--debug=6 +--dump=tmp/sec.dump + +--conf=etc/01control.sr +--input=etc/control=CONTROL + +#--conf=etc/calendar.sr + diff --git a/xymon/sec2xym.pm b/xymon/sec2xym.pm new file mode 100644 index 0000000..1fb984c --- /dev/null +++ b/xymon/sec2xym.pm @@ -0,0 +1,635 @@ +#ident "@(#)sec2xym.pm" +#****************************************************************************** +# $Id$ +# $Revision$ +# $Author$ +# $Date$ +# $HeadURL$ +#****************************************************************************** +# +# NAME +# sec2xym.pm +# +# DESCRIPTION +# +# This is a perl module containing subroutines that are intended exclusively +# for calling from SEC. The provide native (eg cross platform) access to +# some specific interfaces with an upstream Xymon server. +# +# CHANGES +# +# 0.5.0 2014-03-03 First version released to other environments. +# 0.6.0 2014-03-04 API for XymonStatusUpdate modified. +# external unlocker script moved to ext directory. +# 0.6.1 2014-03-04 bug fix: offline copy, +# defensive arrangement of code wrt version. +# 0.6.2 2014-03-13 bug fix: disable alarms in sendToXymon for Win32. +# 0.6.3 2014-03-21 bug fix: handle empty log directory. +# 0.7.0 2014-10-18 XymonStatusModify added. +# 0.7.1 2014-12-07 relink_logpath added (similar to refresh_logpath but +# uses soft (symbolic) link and optionaly alerts on mtime. +# Private functions added:- +# mysymlink,check_val,isnumeric,setworstcolour + +package Sec2Xym; +our $VERSION = '0.7.1'; + +use strict; +use warnings; +#no warnings; +use Cwd; +use File::Basename; +use File::Spec; +use Sys::Hostname; +use feature 'switch'; # needs perl 5.10 and later + +our $SELF = 'Sec2Xym'; +use base qw(Exporter); +our @EXPORT_OK = qw(init_sec2xym sendToXymon refresh_config refresh_logpath relink_logpath XymonStatusUpdate fake_signal_handler XymonStatusModify); + +use POSIX qw(strftime); + +# check if the platform is win32 +my $WIN32 = ($^O =~ /win/i && $^O !~ /cygwin/i && $^O !~ /darwin/i); + +my $SECSVC; +my $SECHOME; +my $SECUNLOCK; +my $XYMSRV; +my $PORT = 1984; +my $LIFETIME = 30; +my $opt_d = 0; +my $opt_n = 0; + +# Private Functions +# +sub rev_by_date { + $b->[9] <=> $a->[9]; +} + +sub toggle_debug { + if ( $opt_d ) { + $opt_d = 0; + } else { + $opt_d = 1; + } + return join ( " ", "$SELF", $VERSION, ($opt_d)? "debug is on" : "debug is off"); +} + +sub mysymlink { + # needs testing for OS, this is only required in Windows + my ($oldname,$newname) = @_; + my $oldfilename = File::Spec->catfile($oldname); + my $newfilename = File::Spec->catfile($newname); + if (-f $newfilename) { } else { + my @args = ("mklink", $newfilename, $oldfilename); + system(@args) == 0; + } +} + +sub isnumeric { + my $InputString = shift; + return 0 if (!defined($InputString)); + if ($InputString !~ /^[0-9|.]+$/) { + return 0; + } else { + return 1; + } +} + +sub setworstcolour { + # $1=worst $2=color, return worst + if (( $_[0] ne "red" ) && ( $_[0] ne "yellow" ) ) { + return $_[1]; + } elsif (( $_[0] ne "red" ) && ( $_[1] eq "yellow" )) { + return $_[1]; + } elsif ( $_[1] eq "red" ) { + return $_[1]; + } else { + return $_[0]; + } +} + +sub check_val { +my ($oldcol,$col,$tocheck,$mod,$val) = @_; + SWITCH: for ($mod) { + />/ && do { + if (( &isnumeric($val)) && (&isnumeric($tocheck))) { + $oldcol=&setworstcolour($oldcol,$col) if ($val>$tocheck); + } + last; + }; + / $server, + PeerPort => $port, + Proto => 'tcp', + ); + return "Could not create socket: $!" unless $sock; + print $sock $msg; + shutdown($sock, 1); + while ($response=<$sock>) { + push (@stream, $response); + } + close($sock); + if ( ! $WIN32 ) { + alarm (0); + } + }; + + # Handle timeout condition. + # + if ($@) { + return "Socket timeout"; + } + else { + # Return results to caller. + # + if (@stream && (grep ! /^\s*$/, @stream)) { + return @stream; + } + else { + return undef; + } + } +} + +# Subroutine to fetch a named list of files from the Xymon download directory. +# Used as a way to keep SEC rule files (*.sr) in sync. +sub refresh_config { + + my (@files) = @_ ; + my @output; + my $refresh = 0; + + chdir join ("/",$SECHOME,"tmp") or return "Cannot change to SEC tmp folder"; + + my $msg = "flush filecache"; + my @stream = sendToXymon($XYMSRV, $PORT, $msg); + + # foreach loop execution + foreach my $p (@files) { + my($a, $d) = fileparse($p); + $msg = "download $d$a"; + + @stream = sendToXymon($XYMSRV, $PORT, $msg); + next if ( ! @stream); + open (CONF, join ("/","..","etc",$a)) || return ("Can't open current $a for reading") ; + my @file1 = ; + close CONF; + + if (@file1 ~~ @stream) { + push (@output, "Current $a is up to date."); + } else { + if ( $opt_n || !open (CONF, join ("/",">..","etc",$a)) ) { + main::log_msg(main::LOG_NOTICE, "$SELF Can't open file $a for writing") if !$opt_n; + open (CONF, '>'.$a) || return ("Unable to create Offline copy of $a"); + } else { + # make certain we can create a backup + if ( !open (BACKUP, join ("/",">..","etc","${a}.bak") ) ) { + close CONF; + return ("Can't open file $a for backup") ; + } + # we are going to overwrite the file + $refresh++; + print BACKUP @file1; + close BACKUP; + } + print CONF @stream; + close CONF; + push (@output, ($opt_n?"Offline copy of":"New")." $a downloaded."); + } + } + + if ( $refresh ) { + main::log_msg(main::LOG_NOTICE, "$SELF detected central configuration change"); + main::abrt_handler; + } else { + main::log_msg(main::LOG_NOTICE, "$SELF central configuration check passed"); + } + + chdir $SECHOME or return "Cannot change to SEC home"; + + if ( @output && $opt_d ) { + return @output; + } + else { + return 0; + } +} + +# Subroutine to maintain the named (static) file hard linked to the newest +# object in a given folder +sub refresh_logpath { + + use Fcntl qw(:flock SEEK_END); + my @output; + + my ($logpath, $pattern) = @_ ; + my($hardlink, $DIR) = fileparse($logpath); + return "must provide a logpath" unless ($DIR); + #push ( @output, "logpath=$logpath, pattern=$pattern") ; + + my @files; + my @latest; + + chdir $DIR or return "Error changing to log folder $DIR"; + opendir(my $DH, '.') or return "Error opening $DIR: $!"; + while (defined (my $file = readdir($DH))) { + #my $path = $DIR . '/' . $file; + my $path = $file; + # ignore non-files - automatically does . and .. + next unless (-f $path ); + if ( !$pattern || $path =~ /$pattern/i ) { + # re-uses the stat results from '-f' + push(@files, [ stat(_), $path ]); + } + if ( $file eq $hardlink ) { + @latest= stat(_); + } + } + closedir($DH); + + my @sorted_files; + if ( @files ) { + @sorted_files = sort rev_by_date @files; + } + else { + return "Error, log folder $DIR is empty."; + } + + my @newest = @{$sorted_files[0]}; + my $name = pop(@newest); + if ( @latest ) { + #print join(", ", @latest) . "\n"; + if ( @latest ~~ @newest ) { + push (@output, "$hardlink->$name is up to date.") ; + } + else { + if ( unlink $hardlink ) { + # + push (@output, "unlinked old link, ") ; + } + elsif ( $WIN32 ) { + # if the log has been recreated externally and we couldnt + # unlink it, and if this is windows then most probably + # the parent writer still has this locked, so defer to + # an externally defined script which might execute a + # Windows unlocker program to delete the file for us. + if ( -f $SECUNLOCK ) { + #main::log_msg(main::LOG_NOTICE, "$SELF resorting to $SECUNLOCK on $hardlink") ; + my @args = ("$SECUNLOCK", "$DIR$hardlink" ); + system (@args) == 0 or return "Cannot delete old link '$hardlink'"; + # we will test if this worked when we try to recreate the + # link in the while loop below + } else { + return "No utility defined to delete old link '$hardlink'"; + } + } + else { + #return "Error unlinking old link '$hardlink' in $DIR"; + open(my $fh, "<", $hardlink) or return "Cannot open old link '$hardlink'"; + flock($fh, LOCK_UN) or return "Cannot unlock old link '$hardlink'"; + push (@output, "unlocked old link, ") ; + unlink $hardlink or return "Error unlinking old link '$hardlink' in $DIR"; + } + my $count = 10; + while ( $count > 0 ) { + if ( link ( $name, $hardlink ) ) { + push (@output, "Successfully refreshed link '$hardlink' in $DIR. ($count attempts remaining)") ; + main::log_msg(main::LOG_NOTICE, "$SELF successfully refreshed link '$hardlink' in $DIR. ($count attempts remaining)") ; + last; + } + select(undef, undef, undef, 0.50); # sleep of 500 milliseconds + $count--; + } + if ($count == 0 ) { + push(@output, "Error refreshing link '$hardlink' in $DIR"); + main::log_msg(main::LOG_NOTICE, "$SELF error refreshing link '$hardlink' in $DIR"); + } + } + } + elsif ( $name != $hardlink ) { + link ( $name, $hardlink ) or return "Error creating new link '$hardlink' in $DIR"; + push (@output, "Successfully created new link '$hardlink' in $DIR.") ; + } + else { + return "hardlink $hardlink in $DIR has been orpaned"; + } + + chdir $SECHOME or return "Cannot change to SEC home"; + if ( @output && $opt_d ) { + return @output; + } + else { + return 0; + } +} + +# Subroutine to maintain the named (static) file soft linked to the newest +# object in a given folder +sub relink_logpath { + + use Fcntl qw(:flock SEEK_END); + my @output; + + my ($logpath, $pattern, $criteria) = @_ ; + my($softlink, $DIR) = fileparse($logpath); + return "must provide a logpath" unless ($DIR); + #push ( @output, "logpath=$logpath, pattern=$pattern") ; + + my @files; + my @latest; + my %WarnColor; + my $col = "green"; + my $timestamp = localtime(); + + $WarnColor{'red'}="ALERT"; + $WarnColor{'yellow'}="WARNING"; + $WarnColor{'green'}="NORMAL"; + + chdir $DIR or return "Error changing to log folder $DIR"; + opendir(my $DH, '.') or return "Error opening $DIR: $!"; + while (defined (my $file = readdir($DH))) { + #my $path = $DIR . '/' . $file; + my $path = $file; + # ignore non-files - automatically does . and .. + next unless (-f $path ); + if ( !$pattern || $path =~ /$pattern/i ) { + # re-uses the stat results from '-f' + push(@files, [ stat(_), $path ]); + } + if ( $file eq $softlink ) { + @latest= stat(_); + } + } + closedir($DH); + + my @sorted_files; + if ( @files ) { + @sorted_files = sort rev_by_date @files; + } + else { + return "Error, log folder $DIR is empty."; + } + + my @newest = @{$sorted_files[0]}; + my $name = pop(@newest); + + if ($criteria) { + my ($keytest,$val1,$val2,$mod)= split(":",$criteria); + #my $key; my $test; + my ($key,$test)= split(",",$keytest); + my $val; + my $msg; + $mod="<" if (!$mod); + $key="mtime" if (!$key); + $test="files" if (!$test); + given ($key) { + when (/^mtime$/i) { $val=time()-$newest[9]; } + default: { + main::log_msg(main::LOG_WITHOUT_LEVEL, + "$SELF Unknown file criteria $key"); + return "Unknown file criteria $key"; + } + } + + $col=&check_val($col,"yellow",$val1,$mod,$val) if ($val1 || ($val1 =~/0/)); + $col=&check_val($col,"red",$val2,$mod,$val) if ($val2 || ($val2 =~/0/)); + if ($col !~ /green/) { + #$val=sprintf("time=%d,9=%d", time(),$newest[9]); + $msg=sprintf ("&%s %s - %s (%s) - %s (%s) has reached the %s level (%s%s) with check (%s)\n",$col,$timestamp,$softlink,File::Spec->catfile($DIR,$name),$key,$val,$WarnColor{$col},$mod,($col =~ /yellow/?$val1:$val2),$criteria); + } + else { + #$val=sprintf("time=%d,9=%d", time(),$newest[9]); + $msg=sprintf ("&%s %s - %s (%s) last modified %s second%s ago\n",$col,$timestamp,$softlink,File::Spec->catfile($DIR,$name),$val,$val==1?"":"s"); + } + #my @stream = XymonStatusModify("", "files" , $col ,"$SELF-$name" , $msg); + my @stream = XymonStatusModify("", $test , $col ,"" , $msg); + return $msg; + } + + if ( @latest ) { + #print join(", ", @latest) . "\n"; + if ( @latest ~~ @newest ) { + push (@output, "$softlink->$name is up to date.") ; + } + else { + if ( unlink $softlink ) { + # + push (@output, "unlinked old link, ") ; + } + elsif ( $WIN32 ) { + # if the log has been recreated externally and we couldnt + # unlink it, and if this is windows then most probably + # the parent writer still has this locked, so defer to + # an externally defined script which might execute a + # Windows unlocker program to delete the file for us. + if ( -f $SECUNLOCK ) { + #main::log_msg(main::LOG_NOTICE, "$SELF resorting to $SECUNLOCK on $softlink") ; + my @args = ("$SECUNLOCK", "$DIR$softlink" ); + system (@args) == 0 or return "Cannot delete old link '$softlink'"; + # we will test if this worked when we try to recreate the + # link in the while loop below + } else { + return "No utility defined to delete old link '$softlink'"; + } + } + else { + #return "Error unlinking old link '$softlink' in $DIR"; + open(my $fh, "<", $softlink) or return "Cannot open old link '$softlink'"; + flock($fh, LOCK_UN) or return "Cannot unlock old link '$softlink'"; + push (@output, "unlocked old link, ") ; + unlink $softlink or return "Error unlinking old link '$softlink' in $DIR"; + } + my $count = 10; + while ( $count > 0 ) { + if ( mysymlink ( $name, $softlink ) ) { + push (@output, "Successfully refreshed link '$softlink' in $DIR. ($count attempts remaining)") ; + main::log_msg(main::LOG_NOTICE, "$SELF successfully refreshed link '$softlink' in $DIR. ($count attempts remaining)") ; + last; + } + select(undef, undef, undef, 0.50); # sleep of 500 milliseconds + $count--; + } + if ($count == 0 ) { + push(@output, "Error refreshing link '$softlink' in $DIR"); + main::log_msg(main::LOG_NOTICE, "$SELF error refreshing link '$softlink' in $DIR"); + } + } + } + elsif ( $name != $softlink ) { + mysymlink ( $name, $softlink ) or return "Error creating new link '$softlink' in $DIR"; + push (@output, "Successfully created new link '$softlink' in $DIR.") ; + } + else { + return "softlink $softlink in $DIR has been orpaned"; + } + + chdir $SECHOME or return "Cannot change to SEC home"; + if ( @output && $opt_d ) { + return @output; + } + else { + return 0; + } +} + +# Subroutine to simplify sending a status update to Xymon by providing default +# settings for target name and timestamp and providing flexibility in severity +sub XymonStatusUpdate { + my ($target,$TEST, $severity, $lifetime, @MSG ) = @_; + + my $color = "green"; + my $timestamp = localtime(); + my @output; + + if ( !$lifetime ) { $lifetime=$LIFETIME; } + if ( !$target ) { $target = hostname(); } + $target =~ s/\./,/g; + + # Map the Severity to a valid Xymon state + given ($severity) { + when (/^GREEN$/i) { $color = "green"; } + when (/^Normal$/i) { $color = "green"; } + when (/^INFORMATIONAL$/i) { $color = "green"; } + when (/^YELLOW$/i) { $color = "yellow"; } + when (/^WARNING$/i) { $color = "yellow"; } + when (/^MINOR$/i) { $color = "yellow"; } + when (/^RED$/i) { $color = "red"; } + when (/^SEVERE$/i) { $color = "red"; } + when (/^MAJOR$/i) { $color = "red"; } + when (/^CRITICAL$/i) { $color = "red"; } + when (/^FATAL$/i) { $color = "red"; } + default: { $color = "clear"; } + } + + my $msg = join ( "\n","status+$lifetime ${target}.$TEST $color $timestamp", @MSG); + my @stream = sendToXymon($XYMSRV, $PORT, $msg); + # there is unlikely to be any return from a status update but we can + # arrange to provide debug feedback to SEC if required. + #push (@output, $msg); + #push (@output, "status+$lifetime ${target}.$TEST $color $timestamp"); + #if ( @output && $opt_d ) { + #if ( @stream || $opt_d ) { + if ( $opt_d ) { + push (@output, "update status+$lifetime ${target}.$TEST $color $timestamp"); + return @output; + } + else { + return 0; + } +} + +# Subroutine to simplify sending a modify update to Xymon by providing default +# settings for target name and source and providing flexibility in severity +sub XymonStatusModify { + my ($target,$TEST, $severity, $source, @MSG ) = @_; + + my $color = "green"; + my $timestamp = localtime(); + my @output; + + if ( !$source ) { $source=$SELF; } + if ( !$target ) { $target = hostname(); } + $target =~ s/\./,/g; + + # Map the Severity to a valid Xymon state + given ($severity) { + when (/^GREEN$/i) { $color = "green"; } + when (/^Normal$/i) { $color = "green"; } + when (/^INFORMATIONAL$/i) { $color = "green"; } + when (/^YELLOW$/i) { $color = "yellow"; } + when (/^WARNING$/i) { $color = "yellow"; } + when (/^MINOR$/i) { $color = "yellow"; } + when (/^RED$/i) { $color = "red"; } + when (/^SEVERE$/i) { $color = "red"; } + when (/^MAJOR$/i) { $color = "red"; } + when (/^CRITICAL$/i) { $color = "red"; } + when (/^FATAL$/i) { $color = "red"; } + default: { $color = "clear"; } + } + + my $msg = join ( "\n","modify ${target}.$TEST $color $source", @MSG); + my @stream = sendToXymon($XYMSRV, $PORT, $msg); + # there is unlikely to be any return from a status update but we can + # arrange to provide debug feedback to SEC if required. + #push (@output, $msg); + #push (@output, "status+$lifetime ${target}.$TEST $color $timestamp"); + #if ( @output && $opt_d ) { + #if ( @stream || $opt_d ) { + if ( $opt_d ) { + push (@output, "modify ${target}.$TEST $color $source"); + return @output; + } + else { + return 0; + } +} + +1; diff --git a/xymon/xymon.sr b/xymon/xymon.sr new file mode 100644 index 0000000..0ffa64a --- /dev/null +++ b/xymon/xymon.sr @@ -0,0 +1,18 @@ +# Load and configure the Sec2Xym module +type=Single +ptype=RegExp +pattern=(SEC_STARTUP|SEC_RESTART) +desc=Load and configure the Sec2Xym module +context=SEC_INTERNAL_EVENT +action=assign %XYMSRV ;\ + assign %XYMDL ;\ + assign %COLUMN sec ;\ + assign %LIFETIME 30 ;\ + eval %SECHOME ( $ENV {'SECHOME'} );\ + eval %SECSVC ( $ENV {'SECSVC'} );\ + eval %a (require 'lib/sec2xym.pm'); \ + if %a (\ + lcall %a %XYMSRV 1984 %LIFETIME %SECSVC %SECHOME 0 0 -> \ + ( sub { Sec2Xym::init_sec2xym(@_) } ) ;\ + logonly %a;\ + ) else ( eval %a exit(1) )