mirror of
https://github.com/pandorafms/pandorafms.git
synced 2025-09-26 11:29:12 +02:00
When /usr/lib/perl5 is added to the front of the module search path it takes precedence over libraries distributed with binary versions of Pandora FMS. This can be fixed by pushing it to the back instead.
572 lines
20 KiB
Perl
572 lines
20 KiB
Perl
package PandoraFMS::SNMPServer;
|
|
##########################################################################
|
|
# Pandora FMS SNMP Console.
|
|
# Pandora FMS. the Flexible Monitoring System. http://www.pandorafms.org
|
|
##########################################################################
|
|
# Copyright (c) 2005-2021 Artica Soluciones Tecnologicas S.L
|
|
#
|
|
# This program is free software; you can redistribute it and/or
|
|
# modify it under the terms of the GNU Lesser General Public License
|
|
# as published by the Free Software Foundation; version 2
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, write to the Free Software
|
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
##########################################################################
|
|
|
|
use strict;
|
|
use warnings;
|
|
|
|
use threads;
|
|
use threads::shared;
|
|
use Thread::Semaphore;
|
|
|
|
use Time::Local;
|
|
use Time::HiRes qw(usleep);
|
|
use XML::Simple;
|
|
|
|
use Scalar::Util qw(looks_like_number);
|
|
|
|
# Default lib dir for RPM and DEB packages
|
|
BEGIN { push @INC, '/usr/lib/perl5'; }
|
|
|
|
use PandoraFMS::Tools;
|
|
use PandoraFMS::DB;
|
|
use PandoraFMS::Core;
|
|
use PandoraFMS::ProducerConsumerServer;
|
|
|
|
# Inherits from PandoraFMS::ProducerConsumerServer
|
|
our @ISA = qw(PandoraFMS::ProducerConsumerServer);
|
|
|
|
# Global variables
|
|
my @TaskQueue :shared;
|
|
my %PendingTasks :shared;
|
|
my $Sem :shared;
|
|
my $TaskSem :shared;
|
|
|
|
# Trap statistics by agent
|
|
my %AGENTS = ();
|
|
# Sources silenced by storm protection.
|
|
my %SILENCEDSOURCES = ();
|
|
|
|
# Index and buffer management for trap log files
|
|
my $SNMPTRAPD = { 'log_file' => '', 'fd' => undef, 'idx_file' => '', 'last_line' => 0, 'last_size' => 0, 'read_ahead_line' => '', 'read_ahead_pos' => 0 };
|
|
my $DATASERVER = { 'log_file' => '', 'fd' => undef, 'idx_file' => '', 'last_line' => 0, 'last_size' => 0, 'read_ahead_line' => '', 'read_ahead_pos' => 0 };
|
|
|
|
########################################################################################
|
|
# SNMP Server class constructor.
|
|
########################################################################################
|
|
sub new ($$$) {
|
|
my ($class, $config, $dbh) = @_;
|
|
|
|
return undef unless $config->{'snmpconsole'} == 1;
|
|
|
|
# Start snmptrapd
|
|
if (start_snmptrapd ($config) != 0) {
|
|
return undef;
|
|
}
|
|
|
|
# Wait for the SNMP log file to be available
|
|
$SNMPTRAPD->{'log_file'} = $config->{'snmp_logfile'};
|
|
sleep ($config->{'server_threshold'}) if (! -e $SNMPTRAPD->{'log_file'});
|
|
if (!open ($SNMPTRAPD->{'fd'}, $SNMPTRAPD->{'log_file'})) {
|
|
logger ($config, ' [E] Could not open the SNMP log file ' . $SNMPTRAPD->{'log_file'} . ".", 1);
|
|
print_message ($config, ' [E] Could not open the SNMP log file ' . $SNMPTRAPD->{'log_file'} . ".", 1);
|
|
return 1;
|
|
}
|
|
init_log_file($config, $SNMPTRAPD);
|
|
|
|
# Create the Data Server SNMP log file if it does not exist.
|
|
if (defined($config->{'snmp_extlog'}) && $config->{'snmp_extlog'} ne '') {
|
|
$DATASERVER->{'log_file'} = $config->{'snmp_extlog'};
|
|
open(TMPFD, '>', $DATASERVER->{'log_file'}) && close(TMPFD) if (! -e $DATASERVER->{'log_file'});
|
|
if (!open ($DATASERVER->{'fd'}, $DATASERVER->{'log_file'})) {
|
|
logger ($config, ' [E] Could not open the Data Server SNMP log file ' . $DATASERVER->{'log_file'} . ".", 1);
|
|
print_message ($config, ' [E] Could not open the Data Server SNMP log file ' . $DATASERVER->{'log_file'} . ".", 1);
|
|
return 1;
|
|
}
|
|
init_log_file($config, $DATASERVER);
|
|
}
|
|
|
|
# Initialize semaphores and queues
|
|
@TaskQueue = ();
|
|
%PendingTasks = ();
|
|
$Sem = Thread::Semaphore->new;
|
|
$TaskSem = Thread::Semaphore->new (0);
|
|
|
|
# Call the constructor of the parent class
|
|
my $self = $class->SUPER::new($config, SNMPCONSOLE, \&PandoraFMS::SNMPServer::data_producer, \&PandoraFMS::SNMPServer::data_consumer, $dbh);
|
|
|
|
# Save the path of snmptrapd
|
|
$self->{'snmp_trapd'} = $config->{'snmp_trapd'};
|
|
|
|
bless $self, $class;
|
|
return $self;
|
|
}
|
|
|
|
###############################################################################
|
|
# Run.
|
|
###############################################################################
|
|
sub run ($) {
|
|
my $self = shift;
|
|
my $pa_config = $self->getConfig ();
|
|
|
|
print_message ($pa_config, " [*] Starting " . $pa_config->{'rb_product_name'} . " SNMP Console.", 2);
|
|
|
|
# Set the initial date for storm protection.
|
|
$pa_config->{"__storm_ref__"} = time();
|
|
|
|
$self->setNumThreads ($pa_config->{'snmpconsole_threads'});
|
|
$self->SUPER::run (\@TaskQueue, \%PendingTasks, $Sem, $TaskSem);
|
|
}
|
|
|
|
###############################################################################
|
|
# Data producer.
|
|
###############################################################################
|
|
sub data_producer ($) {
|
|
my $self = shift;
|
|
my ($pa_config, $dbh) = ($self->getConfig (), $self->getDBH ());
|
|
|
|
my @tasks;
|
|
|
|
# Reset storm protection counters
|
|
my $curr_time = time ();
|
|
if ($pa_config->{"__storm_ref__"} + $pa_config->{"snmp_storm_timeout"} < $curr_time) {
|
|
$pa_config->{"__storm_ref__"} = $curr_time;
|
|
%AGENTS = ();
|
|
}
|
|
|
|
for my $fs (($SNMPTRAPD, $DATASERVER)) {
|
|
next unless defined($fs->{'fd'});
|
|
reset_if_truncated($pa_config, $fs);
|
|
while (my $line_with_pos = read_snmplogfile($fs)) {
|
|
my $line;
|
|
|
|
$fs->{'last_line'}++;
|
|
($fs->{'last_size'}, $line) = @$line_with_pos;
|
|
|
|
chomp ($line);
|
|
|
|
# Update index file
|
|
open(my $idxfd, '>' . $fs->{'idx_file'});
|
|
print $idxfd $fs->{'last_line'} . ' ' . $fs->{'last_size'};
|
|
close $idxfd;
|
|
set_file_permissions($pa_config, $fs->{'idx_file'}, "0666");
|
|
|
|
# Skip lines other than SNMP Trap logs
|
|
next unless ($line =~ m/^SNMPv[12]\[\*\*\]/);
|
|
|
|
# Storm protection.
|
|
my ($ver, $date, $time, $source, $null) = split(/\[\*\*\]/, $line, 5);
|
|
if ($ver eq "SNMPv2" || $pa_config->{'snmp_pdu_address'} eq '1' ) {
|
|
$source =~ s/(?:(?:TCP|UDP):\s*)?\[?([^] ]+)\]?(?::-?\d+)?(?:\s*->.*)?$/$1/;
|
|
}
|
|
|
|
next unless defined ($source);
|
|
if (! defined ($AGENTS{$source})) {
|
|
$AGENTS{$source}{'count'} = 1;
|
|
$AGENTS{$source}{'event'} = 0;
|
|
if (! defined ($SILENCEDSOURCES{$source})) {
|
|
$SILENCEDSOURCES{$source} = 0;
|
|
}
|
|
} else {
|
|
$AGENTS{$source}{'count'} += 1;
|
|
}
|
|
# Silence source.
|
|
if ((defined ($SILENCEDSOURCES{$source})) && ($SILENCEDSOURCES{$source} > $curr_time)) {
|
|
next;
|
|
}
|
|
if ($pa_config->{'snmp_storm_protection'} > 0 && $AGENTS{$source}{'count'} > $pa_config->{'snmp_storm_protection'}) {
|
|
if ($AGENTS{$source}{'event'} == 0) {
|
|
$SILENCEDSOURCES{$source} = $curr_time + $pa_config->{'snmp_storm_silence_period'};
|
|
my $silenced_time = ($pa_config->{'snmp_storm_silence_period'} eq 0 ? $pa_config->{"snmp_storm_timeout"} : $pa_config->{'snmp_storm_silence_period'});
|
|
pandora_event ($pa_config, "Too many traps coming from $source. Silenced for " . $silenced_time . " seconds.", 0, 0, 4, 0, 0, 'system', 0, $dbh);
|
|
}
|
|
$AGENTS{$source}{'event'} = 1;
|
|
next;
|
|
}
|
|
|
|
push (@tasks, $line);
|
|
}
|
|
}
|
|
|
|
return @tasks;
|
|
}
|
|
|
|
###############################################################################
|
|
# Data consumer.
|
|
###############################################################################
|
|
sub data_consumer ($$) {
|
|
my ($self, $task) = @_;
|
|
|
|
pandora_snmptrapd ($self->getConfig (), $task, $self->getServerID (), $self->getDBH ());
|
|
}
|
|
|
|
##########################################################################
|
|
# Process SNMP log file.
|
|
##########################################################################
|
|
sub pandora_snmptrapd {
|
|
my ($pa_config, $line, $server_id, $dbh) = @_;
|
|
|
|
(my $trap_ver, $line) = split(/\[\*\*\]/, $line, 2);
|
|
|
|
# Process SNMP filter
|
|
return if (matches_filter ($dbh, $pa_config, $line) == 1);
|
|
|
|
logger($pa_config, "Reading trap '$line'", 10);
|
|
my ($date, $time, $source, $oid, $type, $type_desc, $value, $data) = ('', '', '', '', '', '', '', '');
|
|
|
|
if ($trap_ver eq "SNMPv1") {
|
|
($date, $time, $source, $oid, $type, $type_desc, $value, $data) = split(/\[\*\*\]/, $line, 8);
|
|
|
|
$value = limpia_cadena ($value);
|
|
|
|
# Try to save as much information as possible if the trap could not be parsed
|
|
$oid = $type_desc if ($oid eq '' || $oid eq '.');
|
|
|
|
if (!defined($oid)) {
|
|
logger($pa_config, "[W] snmpTrapOID not found (Illegal SNMPv1 trap?)", 5);
|
|
return;
|
|
}
|
|
|
|
} elsif ($trap_ver eq "SNMPv2") {
|
|
($date, $time, $source, $data) = split(/\[\*\*\]/, $line, 4);
|
|
my @data = split(/\t/, $data);
|
|
|
|
shift @data; # Drop unused 1st data.
|
|
$oid = shift @data;
|
|
|
|
if (!defined($oid)) {
|
|
logger($pa_config, "[W] snmpTrapOID not found (Illegal SNMPv2 trap?)", 5);
|
|
return;
|
|
}
|
|
$oid =~ s/.* = OID: //;
|
|
if ($oid =~ m/^\.1\.3\.6\.1\.6\.3\.1\.1\.5\.([1-5])$/) {
|
|
$type = $1 - 1;
|
|
} else {
|
|
$type = 6;
|
|
}
|
|
$data = join("\t", @data);
|
|
}
|
|
|
|
if ($trap_ver eq "SNMPv2" || $pa_config->{'snmp_pdu_address'} eq '1' ) {
|
|
# extract IP address from %b part:
|
|
# * destination part (->[dest_ip]:dest_port) appears in Net-SNMP > 5.3
|
|
# * protocol name (TCP: or UDP:) and bracketted IP addr w/ port number appear in
|
|
# Net-SNMP > 5.1 (Net-SNMP 5.1 has IP addr only).
|
|
# * port number is signed (often negative) in Net-SNMP 5.2
|
|
$source =~ s/(?:(?:TCP|UDP):\s*)?\[?([^] ]+)\]?(?::-?\d+)?(?:\s*->.*)?$/$1/;
|
|
}
|
|
|
|
my $timestamp = $date . ' ' . $time;
|
|
my ($custom_oid, $custom_type, $custom_value) = ('', '', '');
|
|
|
|
# custom_type, custom_value is not used since 4.0 version, all custom data goes on custom_oid
|
|
$custom_oid = $data;
|
|
|
|
#Trap forwarding
|
|
if ($pa_config->{'snmp_forward_trap'}==1) {
|
|
my $trap_data_string = "";
|
|
|
|
#We loop through all the custom data of the received trap, creating the $trap_data_string string to forward the trap properly
|
|
while ($data =~ /([\.\d]+)\s=\s([^:]+):\s([\S ]+)/g) {
|
|
my ($trap_data, $trap_type, $trap_value) = ($1, $2, $3);
|
|
if ($trap_type eq "INTEGER") {
|
|
#FIX for translated traps from IF-MIB.txt MIB
|
|
$trap_value =~ s/\D//g;
|
|
$trap_data_string = $trap_data_string . "$trap_data i $trap_value ";
|
|
}
|
|
elsif ($trap_type eq "UNSIGNED"){
|
|
$trap_data_string = $trap_data_string . "$trap_data u $trap_value ";
|
|
}
|
|
elsif ($trap_type eq "COUNTER32"){
|
|
$trap_data_string = $trap_data_string . "$trap_data c $trap_value ";
|
|
}
|
|
elsif ($trap_type eq "STRING"){
|
|
$trap_data_string = $trap_data_string . "$trap_data s $trap_value ";
|
|
}
|
|
elsif ($trap_type eq "HEX STRING"){
|
|
$trap_data_string = $trap_data_string . "$trap_data x $trap_value ";
|
|
}
|
|
elsif ($trap_type eq "DECIMAL STRING"){
|
|
$trap_data_string = $trap_data_string . "$trap_data d $trap_value ";
|
|
}
|
|
elsif ($trap_type eq "NULLOBJ"){
|
|
$trap_data_string = $trap_data_string . "$trap_data n $trap_value ";
|
|
}
|
|
elsif ($trap_type eq "OBJID"){
|
|
$trap_data_string = $trap_data_string . "$trap_data o $trap_value ";
|
|
}
|
|
elsif ($trap_type eq "TIMETICKS"){
|
|
$trap_data_string = $trap_data_string . "$trap_data t $trap_value ";
|
|
}
|
|
elsif ($trap_type eq "IPADDRESS"){
|
|
$trap_data_string = $trap_data_string . "$trap_data a $trap_value ";
|
|
}
|
|
elsif ($trap_type eq "BITS"){
|
|
$trap_data_string = $trap_data_string . "$trap_data b $trap_value ";
|
|
}
|
|
}
|
|
|
|
#We distinguish between the three different kinds of SNMP forwarding
|
|
if ($pa_config->{'snmp_forward_version'} eq '3') {
|
|
system("snmptrap -v $pa_config->{'snmp_forward_version'} -n \"\" -a $pa_config->{'snmp_forward_authProtocol'} -A $pa_config->{'snmp_forward_authPassword'} -x $pa_config->{'snmp_forward_privProtocol'} -X $pa_config->{'snmp_forward_privPassword'} -l $pa_config->{'snmp_forward_secLevel'} -u $pa_config->{'snmp_forward_secName'} -e $pa_config->{'snmp_forward_engineid'} $pa_config->{'snmp_forward_ip'} '' $oid $trap_data_string");
|
|
}
|
|
elsif ($pa_config->{'snmp_forward_version'} eq '2' || $pa_config->{'snmp_forward_version'} eq '2c') {
|
|
system("snmptrap -v 2c -n \"\" -c $pa_config->{'snmp_forward_community'} $pa_config->{'snmp_forward_ip'} '' $oid $trap_data_string");
|
|
}
|
|
elsif ($pa_config->{'snmp_forward_version'} eq '1') {
|
|
#Because of tne SNMP v1 protocol, we must perform additional steps for creating the trap
|
|
my $value_sending = "";
|
|
my $type_sending = "";
|
|
|
|
if ($value eq ''){
|
|
$value_sending = "\"\"";
|
|
}
|
|
else {
|
|
$value_sending = $value;
|
|
$value_sending =~ s/[\$#@~!&*()\[\];.,:?^ `\\\/]+//g;
|
|
}
|
|
if ($type eq ''){
|
|
$type_sending = "\"\"";
|
|
}
|
|
else{
|
|
$type_sending = $type;
|
|
}
|
|
|
|
system("snmptrap -v 1 -c $pa_config->{'snmp_forward_community'} $pa_config->{'snmp_forward_ip'} $oid \"\" $type_sending $value_sending \"\" $trap_data_string");
|
|
}
|
|
}
|
|
|
|
# Insert the trap into the DB
|
|
if (! defined(enterprise_hook ('snmp_insert_trap', [$pa_config, $source, $oid, $type, $value, $custom_oid, $custom_value, $custom_type, $timestamp, $server_id, $dbh]))) {
|
|
my $trap_id = db_insert ($dbh, 'id_trap', 'INSERT INTO ttrap (timestamp, source, oid, type, value, oid_custom, value_custom, type_custom) VALUES (?, ?, ?, ?, ?, ?, ?, ?)',
|
|
$timestamp, $source, $oid, $type, $value, $custom_oid, $custom_value, $custom_type);
|
|
logger ($pa_config, "Received SNMP Trap from $source", 4);
|
|
|
|
# Evaluate alerts for this trap
|
|
pandora_evaluate_snmp_alerts ($pa_config, $trap_id, $source, $oid, $type, $oid, $value, $custom_oid, $dbh);
|
|
}
|
|
|
|
# Delay the consumption of the next task.
|
|
sleep($pa_config->{'snmp_delay'}) if ($pa_config->{'snmp_delay'} > 0);
|
|
}
|
|
|
|
########################################################################################
|
|
# Returns 1 if the given string matches any SNMP filter, 0 otherwise.
|
|
########################################################################################
|
|
sub matches_filter ($$$) {
|
|
my ($dbh, $pa_config, $string) = @_;
|
|
|
|
my @filter_unique_functions = get_db_rows ($dbh, 'SELECT DISTINCT(unified_filters_id) FROM tsnmp_filter ORDER BY unified_filters_id');
|
|
|
|
foreach my $filter_unique_func (@filter_unique_functions) {
|
|
# Get filters
|
|
my @filters = get_db_rows ($dbh, 'SELECT filter FROM tsnmp_filter WHERE unified_filters_id = ' . $filter_unique_func->{'unified_filters_id'});
|
|
|
|
my $eval_acum = 1;
|
|
foreach my $filter (@filters) {
|
|
my $regexp = safe_output($filter->{'filter'}) ;
|
|
my $eval_result;
|
|
|
|
# eval protects against server down (by invalid regular expressions)
|
|
$eval_result = eval {
|
|
$string =~ m/$regexp/i ;
|
|
};
|
|
|
|
if ($eval_result && $eval_acum) {
|
|
$eval_acum = 1;
|
|
}
|
|
else {
|
|
$eval_acum = 0;
|
|
last;
|
|
}
|
|
}
|
|
|
|
if ($eval_acum) {
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
########################################################################################
|
|
# Start snmptrapd, attempting to kill it if it is already running. Returns 0 if
|
|
# successful, 1 otherwise.
|
|
########################################################################################
|
|
sub start_snmptrapd ($) {
|
|
my ($config) = @_;
|
|
|
|
my $pid_file = '/var/run/pandora_snmptrapd.pid';
|
|
my $snmptrapd_running = 0;
|
|
|
|
# Manual start of snmptrapd
|
|
if ($config->{'snmp_trapd'} eq 'manual') {
|
|
logger ($config, "No SNMP trap daemon configured. Start snmptrapd manually.", 1);
|
|
print_message ($config, " [*] No SNMP trap daemon configured. Start snmptrapd manually.", 1);
|
|
|
|
if (! -f $config->{'snmp_logfile'}) {
|
|
logger ($config, "SNMP log file " . $config->{'snmp_logfile'} . " not found.", 1);
|
|
print_message ($config, " [E] SNMP log file " . $config->{'snmp_logfile'} . " not found.", 1);
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
if ( -e $pid_file && open (PIDFILE, $pid_file)) {
|
|
my $pid = <PIDFILE> + 0;
|
|
close PIDFILE;
|
|
|
|
# Check if snmptrapd is running
|
|
if ($snmptrapd_running = kill (0, $pid)) {
|
|
logger ($config, "snmptrapd (pid $pid) is already running, attempting to kill it...", 1);
|
|
print_message ($config, "snmptrapd (pid $pid) is already running, attempting to kill it...", 1);
|
|
kill (9, $pid);
|
|
}
|
|
}
|
|
|
|
# Ignore auth failure traps
|
|
my $snmp_ignore_authfailure = ($config->{'snmp_ignore_authfailure'} eq '1' ? ' -a' : '');
|
|
|
|
# Select agent-addr field of the PDU or PDU source address for V1 traps
|
|
my $address_format = ($config->{'snmp_pdu_address'} eq '0' ? '%a' : '%b');
|
|
|
|
my $snmptrapd_args = ' -t -On -n' . $snmp_ignore_authfailure . ' -Lf ' . $config->{'snmp_logfile'} . ' -p ' . $pid_file;
|
|
$snmptrapd_args .= ' --format1=SNMPv1[**]%4y-%02.2m-%l[**]%02.2h:%02.2j:%02.2k[**]' . $address_format . '[**]%N[**]%w[**]%W[**]%q[**]%v\\\n';
|
|
$snmptrapd_args .= ' --format2=SNMPv2[**]%4y-%02.2m-%l[**]%02.2h:%02.2j:%02.2k[**]%b[**]%v\\\n';
|
|
|
|
if (system ($config->{'snmp_trapd'} . $snmptrapd_args . " >$DEVNULL 2>&1") != 0) {
|
|
logger ($config, " [E] Could not start snmptrapd.", 1);
|
|
print_message ($config, " [E] Could not start snmptrapd.", 1);
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
###############################################################################
|
|
# Read SNMP Log file with buffering (to handle multi-line Traps).
|
|
# Return reference of array (file-pos, line-data) if successful, undef othersise.
|
|
###############################################################################
|
|
sub read_snmplogfile($) {
|
|
my ($fs) = @_;
|
|
my $line;
|
|
my $pos;
|
|
|
|
if(defined($fs->{'read_ahead_line'})) {
|
|
# Restore saved line
|
|
$line = $fs->{'read_ahead_line'};
|
|
$pos = $fs->{'read_ahead_pos'};
|
|
}
|
|
else {
|
|
# No saved line
|
|
my $fd = $fs->{'fd'};
|
|
$line = <$fd>;
|
|
$pos = tell($fs->{'fd'});
|
|
}
|
|
|
|
return undef if (! defined($line));
|
|
|
|
my $retry_count = 0;
|
|
|
|
# More lines ?
|
|
while(1) {
|
|
my $fd = $fs->{'fd'};
|
|
while($fs->{'read_ahead_line'} = <$fd>) {
|
|
|
|
# Get current file position
|
|
$fs->{'read_ahead_pos'} = tell($fs->{'fd'});
|
|
|
|
# Get out of the loop if you find another Trap
|
|
last if($fs->{'read_ahead_line'} =~ /^SNMP/ );
|
|
|
|
# $fs->{'read_ahead_line'} looks continued line...
|
|
|
|
# Append to the line and correct the position
|
|
chomp($line);
|
|
$line .= "$fs->{'read_ahead_line'}";
|
|
$pos = $fs->{'read_ahead_pos'};
|
|
}
|
|
|
|
# if $line looks incomplete, try to get continued line
|
|
# just within 10sec. After that, giving up to complete it
|
|
# and flush $line as it is.
|
|
last if(chomp($line) > 0 || $retry_count++ >= 10);
|
|
|
|
sleep(1);
|
|
}
|
|
|
|
# return fetched line with file position to be saved.
|
|
return [$pos, $line];
|
|
}
|
|
|
|
###############################################################################
|
|
# Initialize the fs structure for a trap log file.
|
|
###############################################################################
|
|
sub init_log_file($$$) {
|
|
my ($config, $fs) = @_;
|
|
|
|
# Process index file, if available
|
|
($fs->{'idx_file'}, $fs->{'last_line'}, $fs->{'last_size'}) = ($fs->{'log_file'} . '.index', 0, 0);
|
|
if (-e $fs->{'idx_file'}) {
|
|
open (my $idxfd, $fs->{'idx_file'}) or return;
|
|
my $idx_data = <$idxfd>;
|
|
close $idxfd;
|
|
($fs->{'last_line'}, $fs->{'last_size'}) = split(/\s+/, $idx_data);
|
|
}
|
|
my $log_size = (stat ($fs->{'log_file'}))[7];
|
|
|
|
# New SNMP log file found
|
|
if ($log_size < $fs->{'last_size'}) {
|
|
unlink ($fs->{'idx_file'});
|
|
($fs->{'last_line'}, $fs->{'last_size'}) = (0, 0);
|
|
}
|
|
|
|
# Skip already processed lines
|
|
read_snmplogfile($fs) for (1..$fs->{'last_line'});
|
|
}
|
|
|
|
###############################################################################
|
|
# Reset the index if the file has been truncated.
|
|
###############################################################################
|
|
sub reset_if_truncated($$) {
|
|
my ($pa_config, $fs) = @_;
|
|
|
|
my $log_size = (stat ($fs->{'log_file'}))[7];
|
|
|
|
# New SNMP log file found
|
|
if ($log_size < $fs->{'last_size'}) {
|
|
logger ($pa_config, 'File ' . $fs->{'log_file'} . ' was truncated.', 10);
|
|
unlink ($fs->{'idx_file'});
|
|
($fs->{'last_line'}, $fs->{'last_size'}) = (0, 0);
|
|
seek($fs->{'fd'}, 0, 0);
|
|
}
|
|
}
|
|
|
|
###############################################################################
|
|
# Clean-up when the server is destroyed.
|
|
###############################################################################
|
|
sub DESTROY {
|
|
my $self = shift;
|
|
|
|
if ($self->{'snmp_trapd'} ne 'manual') {
|
|
my $pid_file = '/var/run/pandora_snmptrapd.pid';
|
|
if (-e $pid_file) {
|
|
my $pid = `cat $pid_file 2>$DEVNULL`;
|
|
if (defined($pid) && ("$pid" ne "") && looks_like_number($pid)) {
|
|
system ("kill -9 $pid");
|
|
}
|
|
|
|
unlink ($pid_file);
|
|
}
|
|
}
|
|
}
|
|
|
|
1;
|
|
__END__
|