2013-08-01 Ramon Novoa <rnovoa@artica.es>

* lib/PandoraFMS/DataServer.pm,
	  lib/PandoraFMS/SNMPServer.pm,
	  lib/PandoraFMS/Server.pm,
	  lib/PandoraFMS/PluginServer.pm,
	  lib/PandoraFMS/ProducerConsumerServer.pm,
	  lib/PandoraFMS/PredictionServer.pm,
	  lib/PandoraFMS/ReconServer.pm,
	  lib/PandoraFMS/NetworkServer.pm,
	  lib/PandoraFMS/WMIServer.pm: Added clean-up code to avoid resource
	  leaks when there is a crash and the server has to auto-restart.



git-svn-id: https://svn.code.sf.net/p/pandora/code/trunk@8611 c3f86ba8-e40f-0410-aaad-9ba5e7f4b01f
This commit is contained in:
ramonn 2013-08-01 09:54:21 +00:00
parent 4557e364b9
commit 094defb906
10 changed files with 127 additions and 44 deletions

View File

@ -1,3 +1,16 @@
2013-08-01 Ramon Novoa <rnovoa@artica.es>
* lib/PandoraFMS/DataServer.pm,
lib/PandoraFMS/SNMPServer.pm,
lib/PandoraFMS/Server.pm,
lib/PandoraFMS/PluginServer.pm,
lib/PandoraFMS/ProducerConsumerServer.pm,
lib/PandoraFMS/PredictionServer.pm,
lib/PandoraFMS/ReconServer.pm,
lib/PandoraFMS/NetworkServer.pm,
lib/PandoraFMS/WMIServer.pm: Added clean-up code to avoid resource
leaks when there is a crash and the server has to auto-restart.
2013-07-25 Ramon Novoa <rnovoa@artica.es> 2013-07-25 Ramon Novoa <rnovoa@artica.es>
* lib/PandoraFMS/NetworkServer.pm: Fixed some parentheses that prevented * lib/PandoraFMS/NetworkServer.pm: Fixed some parentheses that prevented

View File

@ -46,10 +46,10 @@ our @ISA = qw(PandoraFMS::ProducerConsumerServer);
my @TaskQueue :shared; my @TaskQueue :shared;
my %PendingTasks :shared; my %PendingTasks :shared;
my %Agents :shared; my %Agents :shared;
my $Sem :shared = Thread::Semaphore->new; my $Sem :shared;
my $TaskSem :shared = Thread::Semaphore->new (0); my $TaskSem :shared;
my $AgentSem :shared = Thread::Semaphore->new (1); my $AgentSem :shared;
my $ModuleSem :shared = Thread::Semaphore->new (1); my $ModuleSem :shared;
######################################################################################## ########################################################################################
# Data Server class constructor. # Data Server class constructor.
@ -59,6 +59,15 @@ sub new ($$;$) {
return undef unless $config->{'dataserver'} == 1; return undef unless $config->{'dataserver'} == 1;
# Initialize semaphores and queues
@TaskQueue = ();
%PendingTasks = ();
%Agents = ();
$Sem = Thread::Semaphore->new;
$TaskSem = Thread::Semaphore->new (0);
$AgentSem = Thread::Semaphore->new (1);
$ModuleSem = Thread::Semaphore->new (1);
# Call the constructor of the parent class # Call the constructor of the parent class
my $self = $class->SUPER::new($config, 0, \&PandoraFMS::DataServer::data_producer, \&PandoraFMS::DataServer::data_consumer, $dbh); my $self = $class->SUPER::new($config, 0, \&PandoraFMS::DataServer::data_producer, \&PandoraFMS::DataServer::data_consumer, $dbh);

View File

@ -42,9 +42,8 @@ our @ISA = qw(PandoraFMS::ProducerConsumerServer);
# Global variables # Global variables
my @TaskQueue :shared; my @TaskQueue :shared;
my %PendingTasks :shared; my %PendingTasks :shared;
my $Sem :shared = new Thread::Semaphore; my $Sem :shared;
my $TaskSem :shared = new Thread::Semaphore (0); my $TaskSem :shared;
my $SNMPSem :shared = new Thread::Semaphore (1);
######################################################################################## ########################################################################################
# Network Server class constructor. # Network Server class constructor.
@ -60,6 +59,12 @@ sub new ($$$) {
return undef; return undef;
} }
# Initialize semaphores and queues
@TaskQueue = ();
%PendingTasks = ();
$Sem = Thread::Semaphore->new;
$TaskSem = Thread::Semaphore->new (0);
# Call the constructor of the parent class # Call the constructor of the parent class
my $self = $class->SUPER::new($config, 1, \&PandoraFMS::NetworkServer::data_producer, \&PandoraFMS::NetworkServer::data_consumer, $dbh); my $self = $class->SUPER::new($config, 1, \&PandoraFMS::NetworkServer::data_producer, \&PandoraFMS::NetworkServer::data_consumer, $dbh);

View File

@ -42,8 +42,8 @@ our @ISA = qw(PandoraFMS::ProducerConsumerServer);
# Global variables # Global variables
my @TaskQueue :shared; my @TaskQueue :shared;
my %PendingTasks :shared; my %PendingTasks :shared;
my $Sem :shared = Thread::Semaphore->new; my $Sem :shared;
my $TaskSem :shared = Thread::Semaphore->new (0); my $TaskSem :shared;
######################################################################################## ########################################################################################
# Plugin Server class constructor. # Plugin Server class constructor.
@ -60,6 +60,12 @@ sub new ($$;$) {
return undef; return undef;
} }
# Initialize semaphores and queues
@TaskQueue = ();
%PendingTasks = ();
$Sem = Thread::Semaphore->new;
$TaskSem = Thread::Semaphore->new (0);
# Call the constructor of the parent class # Call the constructor of the parent class
my $self = $class->SUPER::new($config, 4, \&PandoraFMS::PluginServer::data_producer, \&PandoraFMS::PluginServer::data_consumer, $dbh); my $self = $class->SUPER::new($config, 4, \&PandoraFMS::PluginServer::data_producer, \&PandoraFMS::PluginServer::data_consumer, $dbh);

View File

@ -45,8 +45,8 @@ our @ISA = qw(PandoraFMS::ProducerConsumerServer);
# Global variables # Global variables
my @TaskQueue :shared; my @TaskQueue :shared;
my %PendingTasks :shared; my %PendingTasks :shared;
my $Sem :shared = Thread::Semaphore->new; my $Sem :shared;
my $TaskSem :shared = Thread::Semaphore->new (0); my $TaskSem :shared;
######################################################################## ########################################################################
# Prediction Server class constructor. # Prediction Server class constructor.
@ -56,6 +56,12 @@ sub new ($$;$) {
return undef unless $config->{'predictionserver'} == 1; return undef unless $config->{'predictionserver'} == 1;
# Initialize semaphores and queues
@TaskQueue = ();
%PendingTasks = ();
$Sem = Thread::Semaphore->new;
$TaskSem = Thread::Semaphore->new (0);
# Call the constructor of the parent class # Call the constructor of the parent class
my $self = $class->SUPER::new($config, 5, \&PandoraFMS::PredictionServer::data_producer, \&PandoraFMS::PredictionServer::data_consumer, $dbh); my $self = $class->SUPER::new($config, 5, \&PandoraFMS::PredictionServer::data_producer, \&PandoraFMS::PredictionServer::data_consumer, $dbh);

View File

@ -31,9 +31,12 @@ use PandoraFMS::DB;
use PandoraFMS::Core; use PandoraFMS::Core;
use PandoraFMS::Server; use PandoraFMS::Server;
# inherits from PandoraFMS::Server # Inherits from PandoraFMS::Server
our @ISA = qw(PandoraFMS::Server); our @ISA = qw(PandoraFMS::Server);
# Tells the producer and consumers to keep running
my $RUN :shared;
######################################################################################## ########################################################################################
# ProducerConsumerServer class constructor. # ProducerConsumerServer class constructor.
######################################################################################## ########################################################################################
@ -48,6 +51,9 @@ sub new ($$$$$;$) {
$self->{'_producer'} = $producer; $self->{'_producer'} = $producer;
$self->{'_consumer'} = $consumer; $self->{'_consumer'} = $consumer;
# Run!
$RUN = 1;
bless $self, $class; bless $self, $class;
return $self; return $self;
} }
@ -101,14 +107,15 @@ sub run ($$$$$) {
sub data_producer ($$$$$) { sub data_producer ($$$$$) {
my ($self, $task_queue, $pending_tasks, $sem, $task_sem) = @_; my ($self, $task_queue, $pending_tasks, $sem, $task_sem) = @_;
my $pa_config = $self->getConfig (); my $pa_config = $self->getConfig ();
my $dbh;
eval { eval {
# Connect to the DB # Connect to the DB
my $dbh = db_connect ($pa_config->{'dbengine'}, $pa_config->{'dbname'}, $pa_config->{'dbhost'}, $pa_config->{'dbport'}, $dbh = db_connect ($pa_config->{'dbengine'}, $pa_config->{'dbname'}, $pa_config->{'dbhost'}, $pa_config->{'dbport'},
$pa_config->{'dbuser'}, $pa_config->{'dbpass'}); $pa_config->{'dbuser'}, $pa_config->{'dbpass'});
$self->setDBH ($dbh); $self->setDBH ($dbh);
while (1) { while ($RUN == 1) {
# Get pending tasks # Get pending tasks
my @tasks = &{$self->{'_producer'}}($self); my @tasks = &{$self->{'_producer'}}($self);
@ -140,6 +147,9 @@ sub data_producer ($$$$$) {
if ($@) { if ($@) {
$self->setErrStr ($@); $self->setErrStr ($@);
} }
$task_sem->up($self->getNumThreads ());
db_disconnect ($dbh);
} }
############################################################################### ###############################################################################
@ -149,13 +159,14 @@ sub data_consumer ($$$$$) {
my ($self, $task_queue, $pending_tasks, $sem, $task_sem) = @_; my ($self, $task_queue, $pending_tasks, $sem, $task_sem) = @_;
my $pa_config = $self->getConfig (); my $pa_config = $self->getConfig ();
my $dbh;
eval { eval {
# Connect to the DB # Connect to the DB
my $dbh = db_connect ($pa_config->{'dbengine'}, $pa_config->{'dbname'}, $pa_config->{'dbhost'}, $pa_config->{'dbport'}, $dbh = db_connect ($pa_config->{'dbengine'}, $pa_config->{'dbname'}, $pa_config->{'dbhost'}, $pa_config->{'dbport'},
$pa_config->{'dbuser'}, $pa_config->{'dbpass'}); $pa_config->{'dbuser'}, $pa_config->{'dbpass'});
$self->setDBH ($dbh); $self->setDBH ($dbh);
while (1) { while ($RUN == 1) {
# Wait for data # Wait for data
$task_sem->down; $task_sem->down;
@ -164,6 +175,9 @@ sub data_consumer ($$$$$) {
my $task = shift (@{$task_queue}); my $task = shift (@{$task_queue});
$sem->up; $sem->up;
# The consumer was waiting for data when the producer exited
last if ($RUN == 0);
# Execute task # Execute task
&{$self->{'_consumer'}}($self, $task); &{$self->{'_consumer'}}($self, $task);
@ -179,6 +193,17 @@ sub data_consumer ($$$$$) {
if ($@) { if ($@) {
$self->setErrStr ($@); $self->setErrStr ($@);
} }
db_disconnect ($dbh);
}
###############################################################################
# Clean-up when the server is destroyed.
###############################################################################
sub DESTROY {
my $self = shift;
$RUN = 0;
} }
1; 1;

View File

@ -45,8 +45,8 @@ our @ISA = qw(PandoraFMS::ProducerConsumerServer);
# Global variables # Global variables
my @TaskQueue :shared; my @TaskQueue :shared;
my %PendingTasks :shared; my %PendingTasks :shared;
my $Sem :shared = Thread::Semaphore->new; my $Sem :shared;
my $TaskSem :shared = Thread::Semaphore->new (0); my $TaskSem :shared;
######################################################################################## ########################################################################################
# Recon Server class constructor. # Recon Server class constructor.
@ -62,6 +62,12 @@ sub new ($$$$$$) {
return undef; return undef;
} }
# Initialize semaphores and queues
@TaskQueue = ();
%PendingTasks = ();
$Sem = Thread::Semaphore->new;
$TaskSem = Thread::Semaphore->new (0);
# Call the constructor of the parent class # Call the constructor of the parent class
my $self = $class->SUPER::new($config, 3, \&PandoraFMS::ReconServer::data_producer, \&PandoraFMS::ReconServer::data_consumer, $dbh); my $self = $class->SUPER::new($config, 3, \&PandoraFMS::ReconServer::data_producer, \&PandoraFMS::ReconServer::data_consumer, $dbh);

View File

@ -38,6 +38,9 @@ use PandoraFMS::Server;
# Inherits from PandoraFMS::Server # Inherits from PandoraFMS::Server
our @ISA = qw(PandoraFMS::Server); our @ISA = qw(PandoraFMS::Server);
# Tells the server to keep running
my $RUN :shared;
######################################################################################## ########################################################################################
# SNMP Server class constructor. # SNMP Server class constructor.
######################################################################################## ########################################################################################
@ -57,6 +60,9 @@ sub new ($$;$) {
# Save the path of snmptrapd # Save the path of snmptrapd
$self->{'snmp_trapd'} = $config->{'snmp_trapd'}; $self->{'snmp_trapd'} = $config->{'snmp_trapd'};
# Run!
$RUN = 1;
bless $self, $class; bless $self, $class;
return $self; return $self;
} }
@ -79,9 +85,10 @@ sub pandora_snmptrapd {
my $self = shift; my $self = shift;
my $pa_config = $self->getConfig (); my $pa_config = $self->getConfig ();
my $dbh;
eval { eval {
# Connect to the DB # Connect to the DB
my $dbh = db_connect ($pa_config->{'dbengine'}, $pa_config->{'dbname'}, $pa_config->{'dbhost'}, $dbh = db_connect ($pa_config->{'dbengine'}, $pa_config->{'dbname'}, $pa_config->{'dbhost'},
$pa_config->{'dbport'}, $pa_config->{'dbuser'}, $pa_config->{'dbpass'}); $pa_config->{'dbport'}, $pa_config->{'dbuser'}, $pa_config->{'dbpass'});
$self->setDBH ($dbh); $self->setDBH ($dbh);
@ -111,7 +118,7 @@ sub pandora_snmptrapd {
readline SNMPLOGFILE for (1..$last_line); readline SNMPLOGFILE for (1..$last_line);
# Main loop # Main loop
while (1) { while ($RUN == 1) {
while (my $line = <SNMPLOGFILE>) { while (my $line = <SNMPLOGFILE>) {
$last_line++; $last_line++;
$last_size = (stat ($log_file))[7]; $last_size = (stat ($log_file))[7];
@ -189,6 +196,8 @@ sub pandora_snmptrapd {
if ($@) { if ($@) {
$self->setErrStr ($@); $self->setErrStr ($@);
} }
db_disconnect ($dbh);
} }
######################################################################################## ########################################################################################
@ -287,5 +296,14 @@ sub start_snmptrapd ($) {
return 0; return 0;
} }
###############################################################################
# Clean-up when the server is destroyed.
###############################################################################
sub DESTROY {
my $self = shift;
$RUN = 0;
}
1; 1;
__END__ __END__

View File

@ -29,7 +29,7 @@ use lib '/usr/lib/perl5';
use PandoraFMS::DB; use PandoraFMS::DB;
use PandoraFMS::Core; use PandoraFMS::Core;
# defined in PandoraFMS::Core.pm # Defined in PandoraFMS::Core.pm
our @ServerSuffixes; our @ServerSuffixes;
######################################################################################## ########################################################################################
@ -52,12 +52,6 @@ sub new ($$$;$) {
share ($self->{'_queue_size'}); share ($self->{'_queue_size'});
share ($self->{'_errstr'}); share ($self->{'_errstr'});
# Thread kill signal handler
#$SIG{'KILL'} = sub {
# threads->exit() if threads->can('exit');
# exit();
#};
bless $self, $class; bless $self, $class;
return $self; return $self;
} }
@ -290,17 +284,12 @@ sub stop ($) {
0, $self->{'_server_type'}, 0, 0); 0, $self->{'_server_type'}, 0, 0);
}; };
# Kill server threads # Detach server threads
foreach my $tid (@{$self->{'_threads'}}) { foreach my $tid (@{$self->{'_threads'}}) {
my $thr = threads->object($tid); my $thr = threads->object($tid);
next unless defined ($thr); next unless defined ($thr);
# A kill method might not be available
#if ($thr->can('kill')) {
# $thr->kill('KILL')->detach();
#} else {
$thr->detach(); $thr->detach();
#}
} }
} }

View File

@ -41,8 +41,8 @@ our @ISA = qw(PandoraFMS::ProducerConsumerServer);
# Global variables # Global variables
my @TaskQueue :shared; my @TaskQueue :shared;
my %PendingTasks :shared; my %PendingTasks :shared;
my $Sem :shared = Thread::Semaphore->new; my $Sem :shared;
my $TaskSem :shared = Thread::Semaphore->new (0); my $TaskSem :shared;
######################################################################################## ########################################################################################
# NetworkServer class constructor. # NetworkServer class constructor.
@ -59,6 +59,12 @@ sub new ($$;$) {
return undef; return undef;
} }
# Initialize semaphores and queues
@TaskQueue = ();
%PendingTasks = ();
$Sem = Thread::Semaphore->new;
$TaskSem = Thread::Semaphore->new (0);
# Call the constructor of the parent class # Call the constructor of the parent class
my $self = $class->SUPER::new($config, 6, \&PandoraFMS::WMIServer::data_producer, \&PandoraFMS::WMIServer::data_consumer, $dbh); my $self = $class->SUPER::new($config, 6, \&PandoraFMS::WMIServer::data_producer, \&PandoraFMS::WMIServer::data_consumer, $dbh);
@ -138,7 +144,7 @@ sub data_consumer ($$) {
# Build command to execute # Build command to execute
my $wmi_command = ''; my $wmi_command = '';
if ($module->{'plugin_user'}) { if (defined ($module->{'plugin_pass'}) && $module->{'plugin_pass'} ne "") {
$wmi_command = $pa_config->{'wmi_client'} . ' -U "' . $module->{'plugin_user'} . '"%"' . $module->{'plugin_pass'} . '"'; $wmi_command = $pa_config->{'wmi_client'} . ' -U "' . $module->{'plugin_user'} . '"%"' . $module->{'plugin_pass'} . '"';
} }
else { else {