diff --git a/pandora_server/ChangeLog b/pandora_server/ChangeLog index ad7544e4ff..4b0d5358ac 100644 --- a/pandora_server/ChangeLog +++ b/pandora_server/ChangeLog @@ -1,3 +1,16 @@ +2013-08-01 Ramon Novoa + + * 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 * lib/PandoraFMS/NetworkServer.pm: Fixed some parentheses that prevented diff --git a/pandora_server/lib/PandoraFMS/DataServer.pm b/pandora_server/lib/PandoraFMS/DataServer.pm index a9e4f7efd2..8c78eb2fdd 100644 --- a/pandora_server/lib/PandoraFMS/DataServer.pm +++ b/pandora_server/lib/PandoraFMS/DataServer.pm @@ -46,10 +46,10 @@ our @ISA = qw(PandoraFMS::ProducerConsumerServer); my @TaskQueue :shared; my %PendingTasks :shared; my %Agents :shared; -my $Sem :shared = Thread::Semaphore->new; -my $TaskSem :shared = Thread::Semaphore->new (0); -my $AgentSem :shared = Thread::Semaphore->new (1); -my $ModuleSem :shared = Thread::Semaphore->new (1); +my $Sem :shared; +my $TaskSem :shared; +my $AgentSem :shared; +my $ModuleSem :shared; ######################################################################################## # Data Server class constructor. @@ -59,6 +59,15 @@ sub new ($$;$) { 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 my $self = $class->SUPER::new($config, 0, \&PandoraFMS::DataServer::data_producer, \&PandoraFMS::DataServer::data_consumer, $dbh); diff --git a/pandora_server/lib/PandoraFMS/NetworkServer.pm b/pandora_server/lib/PandoraFMS/NetworkServer.pm index 6db48a27d4..f43c8d3bad 100644 --- a/pandora_server/lib/PandoraFMS/NetworkServer.pm +++ b/pandora_server/lib/PandoraFMS/NetworkServer.pm @@ -42,9 +42,8 @@ our @ISA = qw(PandoraFMS::ProducerConsumerServer); # Global variables my @TaskQueue :shared; my %PendingTasks :shared; -my $Sem :shared = new Thread::Semaphore; -my $TaskSem :shared = new Thread::Semaphore (0); -my $SNMPSem :shared = new Thread::Semaphore (1); +my $Sem :shared; +my $TaskSem :shared; ######################################################################################## # Network Server class constructor. @@ -60,6 +59,12 @@ sub new ($$$) { return undef; } + # 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, 1, \&PandoraFMS::NetworkServer::data_producer, \&PandoraFMS::NetworkServer::data_consumer, $dbh); diff --git a/pandora_server/lib/PandoraFMS/PluginServer.pm b/pandora_server/lib/PandoraFMS/PluginServer.pm index 2ffdc9686a..efe60b0b8b 100644 --- a/pandora_server/lib/PandoraFMS/PluginServer.pm +++ b/pandora_server/lib/PandoraFMS/PluginServer.pm @@ -42,8 +42,8 @@ our @ISA = qw(PandoraFMS::ProducerConsumerServer); # Global variables my @TaskQueue :shared; my %PendingTasks :shared; -my $Sem :shared = Thread::Semaphore->new; -my $TaskSem :shared = Thread::Semaphore->new (0); +my $Sem :shared; +my $TaskSem :shared; ######################################################################################## # Plugin Server class constructor. @@ -59,7 +59,13 @@ sub new ($$;$) { print_message ($config, ' [E] ' . $config->{'plugin_exec'} . ' not found. Plugin Server not started.', 1); return undef; } - + + # 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, 4, \&PandoraFMS::PluginServer::data_producer, \&PandoraFMS::PluginServer::data_consumer, $dbh); diff --git a/pandora_server/lib/PandoraFMS/PredictionServer.pm b/pandora_server/lib/PandoraFMS/PredictionServer.pm index 3c4268d497..3075b049c2 100644 --- a/pandora_server/lib/PandoraFMS/PredictionServer.pm +++ b/pandora_server/lib/PandoraFMS/PredictionServer.pm @@ -45,8 +45,8 @@ our @ISA = qw(PandoraFMS::ProducerConsumerServer); # Global variables my @TaskQueue :shared; my %PendingTasks :shared; -my $Sem :shared = Thread::Semaphore->new; -my $TaskSem :shared = Thread::Semaphore->new (0); +my $Sem :shared; +my $TaskSem :shared; ######################################################################## # Prediction Server class constructor. @@ -55,10 +55,16 @@ sub new ($$;$) { my ($class, $config, $dbh) = @_; 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 my $self = $class->SUPER::new($config, 5, \&PandoraFMS::PredictionServer::data_producer, \&PandoraFMS::PredictionServer::data_consumer, $dbh); - + bless $self, $class; return $self; diff --git a/pandora_server/lib/PandoraFMS/ProducerConsumerServer.pm b/pandora_server/lib/PandoraFMS/ProducerConsumerServer.pm index 8cfe20c58c..8a8cd32a6f 100644 --- a/pandora_server/lib/PandoraFMS/ProducerConsumerServer.pm +++ b/pandora_server/lib/PandoraFMS/ProducerConsumerServer.pm @@ -31,9 +31,12 @@ use PandoraFMS::DB; use PandoraFMS::Core; use PandoraFMS::Server; -# inherits from PandoraFMS::Server +# Inherits from PandoraFMS::Server our @ISA = qw(PandoraFMS::Server); +# Tells the producer and consumers to keep running +my $RUN :shared; + ######################################################################################## # ProducerConsumerServer class constructor. ######################################################################################## @@ -48,6 +51,9 @@ sub new ($$$$$;$) { $self->{'_producer'} = $producer; $self->{'_consumer'} = $consumer; + # Run! + $RUN = 1; + bless $self, $class; return $self; } @@ -101,14 +107,15 @@ sub run ($$$$$) { sub data_producer ($$$$$) { my ($self, $task_queue, $pending_tasks, $sem, $task_sem) = @_; my $pa_config = $self->getConfig (); + my $dbh; eval { # 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'}); $self->setDBH ($dbh); - while (1) { + while ($RUN == 1) { # Get pending tasks my @tasks = &{$self->{'_producer'}}($self); @@ -140,6 +147,9 @@ sub data_producer ($$$$$) { if ($@) { $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 $pa_config = $self->getConfig (); + my $dbh; eval { # 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'}); $self->setDBH ($dbh); - while (1) { + while ($RUN == 1) { # Wait for data $task_sem->down; @@ -164,6 +175,9 @@ sub data_consumer ($$$$$) { my $task = shift (@{$task_queue}); $sem->up; + # The consumer was waiting for data when the producer exited + last if ($RUN == 0); + # Execute task &{$self->{'_consumer'}}($self, $task); @@ -179,6 +193,17 @@ sub data_consumer ($$$$$) { if ($@) { $self->setErrStr ($@); } + + db_disconnect ($dbh); +} + +############################################################################### +# Clean-up when the server is destroyed. +############################################################################### +sub DESTROY { + my $self = shift; + + $RUN = 0; } 1; diff --git a/pandora_server/lib/PandoraFMS/ReconServer.pm b/pandora_server/lib/PandoraFMS/ReconServer.pm index 26bf0f159e..ba173b8dc8 100644 --- a/pandora_server/lib/PandoraFMS/ReconServer.pm +++ b/pandora_server/lib/PandoraFMS/ReconServer.pm @@ -45,8 +45,8 @@ our @ISA = qw(PandoraFMS::ProducerConsumerServer); # Global variables my @TaskQueue :shared; my %PendingTasks :shared; -my $Sem :shared = Thread::Semaphore->new; -my $TaskSem :shared = Thread::Semaphore->new (0); +my $Sem :shared; +my $TaskSem :shared; ######################################################################################## # Recon Server class constructor. @@ -61,6 +61,12 @@ sub new ($$$$$$) { print_message ($config, ' [E] ' . $config->{'nmap'} . " needed by Pandora FMS Recon Server not found.", 1); return undef; } + + # 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, 3, \&PandoraFMS::ReconServer::data_producer, \&PandoraFMS::ReconServer::data_consumer, $dbh); diff --git a/pandora_server/lib/PandoraFMS/SNMPServer.pm b/pandora_server/lib/PandoraFMS/SNMPServer.pm index 95c33bfc73..e1dc7cee9b 100644 --- a/pandora_server/lib/PandoraFMS/SNMPServer.pm +++ b/pandora_server/lib/PandoraFMS/SNMPServer.pm @@ -38,6 +38,9 @@ use PandoraFMS::Server; # Inherits from PandoraFMS::Server our @ISA = qw(PandoraFMS::Server); +# Tells the server to keep running +my $RUN :shared; + ######################################################################################## # SNMP Server class constructor. ######################################################################################## @@ -50,13 +53,16 @@ sub new ($$;$) { if (start_snmptrapd ($config) != 0) { return undef; } - + # Call the constructor of the parent class my $self = $class->SUPER::new($config, 2, $dbh); # Save the path of snmptrapd $self->{'snmp_trapd'} = $config->{'snmp_trapd'}; - + + # Run! + $RUN = 1; + bless $self, $class; return $self; } @@ -79,9 +85,10 @@ sub pandora_snmptrapd { my $self = shift; my $pa_config = $self->getConfig (); + my $dbh; eval { # 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'}); $self->setDBH ($dbh); @@ -111,7 +118,7 @@ sub pandora_snmptrapd { readline SNMPLOGFILE for (1..$last_line); # Main loop - while (1) { + while ($RUN == 1) { while (my $line = ) { $last_line++; $last_size = (stat ($log_file))[7]; @@ -189,6 +196,8 @@ sub pandora_snmptrapd { if ($@) { $self->setErrStr ($@); } + + db_disconnect ($dbh); } ######################################################################################## @@ -287,5 +296,14 @@ sub start_snmptrapd ($) { return 0; } +############################################################################### +# Clean-up when the server is destroyed. +############################################################################### +sub DESTROY { + my $self = shift; + + $RUN = 0; +} + 1; __END__ diff --git a/pandora_server/lib/PandoraFMS/Server.pm b/pandora_server/lib/PandoraFMS/Server.pm index 3a239908cf..50fd17a91a 100644 --- a/pandora_server/lib/PandoraFMS/Server.pm +++ b/pandora_server/lib/PandoraFMS/Server.pm @@ -29,7 +29,7 @@ use lib '/usr/lib/perl5'; use PandoraFMS::DB; use PandoraFMS::Core; -# defined in PandoraFMS::Core.pm +# Defined in PandoraFMS::Core.pm our @ServerSuffixes; ######################################################################################## @@ -51,13 +51,7 @@ sub new ($$$;$) { # Share variables that may be set from different threads share ($self->{'_queue_size'}); share ($self->{'_errstr'}); - - # Thread kill signal handler - #$SIG{'KILL'} = sub { - # threads->exit() if threads->can('exit'); - # exit(); - #}; - + bless $self, $class; return $self; } @@ -290,17 +284,12 @@ sub stop ($) { 0, $self->{'_server_type'}, 0, 0); }; - # Kill server threads + # Detach server threads foreach my $tid (@{$self->{'_threads'}}) { my $thr = threads->object($tid); next unless defined ($thr); - # A kill method might not be available - #if ($thr->can('kill')) { - # $thr->kill('KILL')->detach(); - #} else { - $thr->detach(); - #} + $thr->detach(); } } diff --git a/pandora_server/lib/PandoraFMS/WMIServer.pm b/pandora_server/lib/PandoraFMS/WMIServer.pm index 5c13fc9dbb..9278dc5238 100644 --- a/pandora_server/lib/PandoraFMS/WMIServer.pm +++ b/pandora_server/lib/PandoraFMS/WMIServer.pm @@ -41,8 +41,8 @@ our @ISA = qw(PandoraFMS::ProducerConsumerServer); # Global variables my @TaskQueue :shared; my %PendingTasks :shared; -my $Sem :shared = Thread::Semaphore->new; -my $TaskSem :shared = Thread::Semaphore->new (0); +my $Sem :shared; +my $TaskSem :shared; ######################################################################################## # NetworkServer class constructor. @@ -58,7 +58,13 @@ sub new ($$;$) { print_message ($config, ' [E] ' . $config->{'wmi_client'} . " not found. Pandora FMS WMI Server needs a DCOM/WMI client.", 1); return undef; } - + + # 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, 6, \&PandoraFMS::WMIServer::data_producer, \&PandoraFMS::WMIServer::data_consumer, $dbh); @@ -138,7 +144,7 @@ sub data_consumer ($$) { # Build command to execute 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'} . '"'; } else {