928 lines
33 KiB
Perl
Executable File
928 lines
33 KiB
Perl
Executable File
#!/usr/bin/perl
|
|
##########################################################################
|
|
# Pandora FMS Server
|
|
# Pandora FMS. the Flexible Monitoring System. http://www.pandorafms.org
|
|
##########################################################################
|
|
# Copyright (c) 2005-2023 Pandora FMS
|
|
#
|
|
# This program is free software; you can redistribute it and/or
|
|
# modify it under the terms of the GNU 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 POSIX qw(strftime);
|
|
use threads;
|
|
use Digest::MD5 qw(md5_hex);
|
|
|
|
# Default lib dir for RPM and DEB packages
|
|
BEGIN { push @INC, '/usr/lib/perl5'; }
|
|
|
|
# Pandora Modules
|
|
use PandoraFMS::DB;
|
|
use PandoraFMS::Config;
|
|
use PandoraFMS::Tools;
|
|
use PandoraFMS::Core;
|
|
use PandoraFMS::AlertServer;
|
|
use PandoraFMS::DataServer;
|
|
use PandoraFMS::NetworkServer;
|
|
use PandoraFMS::SNMPServer;
|
|
use PandoraFMS::DiscoveryServer;
|
|
use PandoraFMS::WMIServer;
|
|
use PandoraFMS::PluginServer;
|
|
use PandoraFMS::PredictionServer;
|
|
use PandoraFMS::WebServer;
|
|
use PandoraFMS::InventoryServer;
|
|
|
|
|
|
# Global vars
|
|
my %Config :shared;
|
|
my @Servers;
|
|
my $DBH;
|
|
my $RUN :shared = 1;
|
|
my $MainThread = threads->self;
|
|
|
|
########################################################################################
|
|
# Server shutdown. Handler to do a controlled shutdown.
|
|
########################################################################################
|
|
sub pandora_shutdown () {
|
|
my $signal = shift;
|
|
|
|
logger (\%Config, $Config{'rb_product_name'} . ' Server \'' . $Config{'servername'} . '\' Caught SIG' . $signal . ' by thread(' . threads->self()->tid() . ')', 10);
|
|
|
|
if (!threads->self->equal($MainThread)) {
|
|
# deliver signal to the main thread since no other threads than main thread
|
|
# could disconnet $DBH properly
|
|
$MainThread->kill($signal);
|
|
return;
|
|
}
|
|
logger (\%Config, $Config{'rb_product_name'} . ' Server \'' . $Config{'servername'} . '\' Shutdown by signal ', 1);
|
|
|
|
# Stop servers
|
|
foreach my $server (@Servers) {
|
|
$server->downEvent ();
|
|
$server->stop ();
|
|
}
|
|
@Servers = ();
|
|
|
|
# Stop the netflow daemon
|
|
pandora_stop_netflow_daemon ();
|
|
|
|
# Stop the sflow daemon
|
|
pandora_stop_sflow_daemon ();
|
|
|
|
# Stop server threads.
|
|
stop_server_threads();
|
|
|
|
# Wait threads.
|
|
my $max_wait = 10;
|
|
my $waiting = 1;
|
|
my $start_waiting = time();
|
|
while ($waiting eq 1) {
|
|
$waiting = 0;
|
|
foreach my $thr (threads->list()) {
|
|
#my $tid = shift @{$self->{'_threads'}};
|
|
#my $thr = threads->object($tid);
|
|
|
|
if (defined($thr)) {
|
|
if ($thr->is_joinable()) {
|
|
$thr->join();
|
|
} else {
|
|
$thr->kill('KILL');
|
|
if (time() - $start_waiting < $max_wait) {
|
|
$waiting = 1;
|
|
} else {
|
|
# Some discovery external scripts tasks.
|
|
$thr->detach();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
sleep (1);
|
|
}
|
|
|
|
print_message (\%Config, ' [*] Shutting down ' . $Config{'servername'} . "(received signal)...\n", 1);
|
|
db_disconnect ($DBH);
|
|
if ($Config{'PID'} ne "") {
|
|
unlink($Config{'PID'}) or logger (\%Config, "[W] Could not remove PID file: $!",1);
|
|
}
|
|
exit (0);
|
|
}
|
|
|
|
########################################################################################
|
|
# Server startup.
|
|
########################################################################################
|
|
sub pandora_startup () {
|
|
|
|
# Start logging
|
|
pandora_start_log (\%Config);
|
|
|
|
# Connect to the DB
|
|
$DBH = db_connect ($Config{'dbengine'}, $Config{'dbname'}, $Config{'dbhost'}, $Config{'dbport'},
|
|
$Config{'dbuser'}, $Config{'dbpass'});
|
|
|
|
# Grab config tokens shared with the console and not in the .conf
|
|
pandora_get_sharedconfig (\%Config, $DBH);
|
|
|
|
# Kill any running server threads.
|
|
stop_server_threads();
|
|
|
|
# Start the task execution thread.
|
|
start_server_thread(\&pandora_server_tasks, [\%Config]);
|
|
|
|
# Start the policy queue thread.
|
|
start_server_thread(\&pandora_process_policy_queue, [\%Config]) if ($Config{'__enterprise_enabled'} == 1 && $Config{'policy_manager'} == 1);
|
|
|
|
# Start agent autoconfiguration thread.
|
|
start_server_thread(\&pandora_agent_autoconfiguration_scheduled, [\%Config]) if ($Config{'__enterprise_enabled'} == 1 && $Config{'autoconfigure_agents'} == 1);
|
|
|
|
pandora_audit (\%Config, $Config{'rb_product_name'} . ' Server Daemon starting', 'SYSTEM', 'System', $DBH);
|
|
|
|
# Load servers
|
|
if (!is_metaconsole(\%Config)) {
|
|
pandora_reset_server (\%Config, $DBH);
|
|
push (@Servers, new PandoraFMS::AlertServer (\%Config, $DBH));
|
|
push (@Servers, new PandoraFMS::DataServer (\%Config, $DBH));
|
|
push (@Servers, new PandoraFMS::NetworkServer (\%Config, $DBH));
|
|
push (@Servers, new PandoraFMS::DiscoveryServer (\%Config, $DBH));
|
|
push (@Servers, new PandoraFMS::SNMPServer (\%Config, $DBH));
|
|
push (@Servers, new PandoraFMS::WMIServer (\%Config, $DBH));
|
|
push (@Servers, new PandoraFMS::PluginServer (\%Config, $DBH));
|
|
push (@Servers, new PandoraFMS::PredictionServer (\%Config, $DBH));
|
|
push (@Servers, new PandoraFMS::WebServer (\%Config, $DBH));
|
|
push (@Servers, new PandoraFMS::InventoryServer (\%Config, $DBH));
|
|
|
|
} else {
|
|
# Metaconsole service modules are run by the prediction server
|
|
push (@Servers, new PandoraFMS::PredictionServer (\%Config, $DBH));
|
|
}
|
|
|
|
# There are enterprise metaconsole servers!
|
|
enterprise_hook('load_enterprise_servers', [\@Servers, \%Config, $DBH]);
|
|
|
|
# Start the netflow daemon if necessary
|
|
pandora_start_netflow_daemon ();
|
|
|
|
# Start the sflow daemon if necessary
|
|
pandora_start_sflow_daemon ();
|
|
|
|
# Remove disabled servers
|
|
@Servers = grep { defined ($_) } @Servers;
|
|
|
|
# Run
|
|
foreach my $server (@Servers) {
|
|
$server->run ();
|
|
}
|
|
}
|
|
|
|
########################################################################################
|
|
# Server restart.
|
|
########################################################################################
|
|
sub pandora_restart (;$) {
|
|
|
|
my $sleep_time = @_ > 0 ? $_[0] : $Config{'restart_delay'};
|
|
|
|
# Stop the servers
|
|
eval {
|
|
foreach my $server (@Servers) {
|
|
$server->stop ();
|
|
}
|
|
};
|
|
|
|
# Remove the servers
|
|
while (pop (@Servers)) {};
|
|
|
|
# Close STDERR, redirected by pandora_start_log
|
|
close (STDERR);
|
|
|
|
# Wait before trying to start again
|
|
sleep ($sleep_time);
|
|
|
|
# Start the servers
|
|
pandora_startup ();
|
|
}
|
|
|
|
########################################################################################
|
|
# Server crash. Handler to write in the log unhandled errors and write it to console
|
|
########################################################################################
|
|
sub pandora_crash () {
|
|
|
|
my $full_error = "";
|
|
|
|
# Avoid show messages about enterprise library loading failurem, VERY
|
|
# confussing, all of them are warnigs and not critical, and user should be
|
|
# worried about that. If perl has a more "clean" way to avoid this messages
|
|
# will be nice to replace this code, but at this time it's the only way I know
|
|
|
|
foreach my $error_line (@_) {
|
|
# Trap the XML error and exit without nasty messages
|
|
if ($error_line =~ m/XML\/Parser/) {
|
|
logger (\%Config, "Problem parsing XML file, XML file discarded: $error_line", 2);
|
|
return;
|
|
}
|
|
|
|
elsif ($error_line !~ m/Enterprise/i && $error_line !~ m/Format_XS/i && $error_line !~ m/ConfigLocal/i){
|
|
logger (\%Config, '[E] \'' . $Config{'servername'} . "': $error_line", 1);
|
|
}
|
|
|
|
|
|
else {
|
|
if ($error_line !~ m/Can\'t\slocate/) {
|
|
logger (\%Config, '[E] \'' . $Config{'servername'} . "': $error_line", 1);
|
|
}
|
|
else {
|
|
# Known errors of loading Enterprise, Format_XS and ConfigLocal
|
|
# modules, non fatal.
|
|
return;
|
|
}
|
|
}
|
|
$full_error .= $error_line;
|
|
}
|
|
|
|
# Could crash before parse configuration.
|
|
$Config{'rb_product_name'} = 'PandoraFMS' unless defined($Config{'rb_product_name'}) && $Config{'rb_product_name'} ne '';
|
|
logger (\%Config, $Config{'rb_product_name'} . ' Server \'' . $Config{'servername'} . '\' unhandled error.', 1);
|
|
|
|
# It's interesting show by console problems, not only in logs. This helps
|
|
# to solve stupid problems like Database credential problems for example
|
|
|
|
print_message (\%Config, ' [E] Unhandled error in "' . $Config{'servername'} . "\". See more information in logfiles at '/var/log/pandora' \n", 0);
|
|
print_message (\%Config, " Error description:\n", 0);
|
|
print_message (\%Config, $full_error, 0);
|
|
|
|
}
|
|
|
|
########################################################################################
|
|
# Start the netflow daemon if necessary.
|
|
########################################################################################
|
|
sub pandora_start_netflow_daemon () {
|
|
my $pid_file = '/var/run/pandora_nfcapd.pid';
|
|
|
|
# Check if netflow is enabled
|
|
if ($Config{'activate_netflow'} != 1) {
|
|
logger (\%Config, " [*] NetFlow daemon disabled.", 1);
|
|
print_message (\%Config, " [*] NetFlow daemon disabled.", 1);
|
|
return;
|
|
}
|
|
|
|
# Stop nfcapd if it's already running
|
|
my $pid = pandora_stop_netflow_daemon ();
|
|
if (pandora_stop_netflow_daemon () != 0) {
|
|
logger (\%Config, "nfcapd (pid $pid) is already running, attempting to kill it...", 1);
|
|
print_message (\%Config, "nfcapd (pid $pid) is already running, attempting to kill it...", 1);
|
|
}
|
|
|
|
# Start nfcapd
|
|
my $command = $Config{'netflow_daemon'} . ' -D -T all -w -t ' . $Config{'netflow_interval'} . ' -P ' . $pid_file . ' -l ' . $Config{'netflow_path'};
|
|
if (system ("$command >/dev/null 2>&1") != 0) {
|
|
logger (\%Config, " [E] Could not start nfcapd: $command", 1);
|
|
print_message (\%Config, " [E] Could not start nfcapd: $command", 1);
|
|
return;
|
|
}
|
|
|
|
logger (\%Config, "[*] NetFlow daemon started.", 1);
|
|
print_message (\%Config, "[*] NetFlow daemon started.", 1);
|
|
}
|
|
|
|
########################################################################################
|
|
# Stop the netflow daemon if it's running.
|
|
########################################################################################
|
|
sub pandora_stop_netflow_daemon () {
|
|
|
|
my $pid_file = '/var/run/pandora_nfcapd.pid';
|
|
|
|
# Open the pid file
|
|
if ( ! (-e $pid_file && open (PIDFILE, $pid_file))) {
|
|
return 0;
|
|
}
|
|
|
|
my $pid = <PIDFILE>;
|
|
close PIDFILE;
|
|
|
|
# Check if nfcapd is running
|
|
if (kill (0, $pid) > 0) {
|
|
kill (9, $pid);
|
|
return $pid;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
########################################################################################
|
|
# Start the sflow daemon if necessary.
|
|
########################################################################################
|
|
sub pandora_start_sflow_daemon () {
|
|
my $pid_file = '/var/run/pandora_sfcapd.pid';
|
|
|
|
# Check if sflow is enabled
|
|
if ($Config{'activate_sflow'} != 1) {
|
|
logger (\%Config, " [*] sFlow daemon disabled.", 1);
|
|
print_message (\%Config, " [*] sFlow daemon disabled.", 1);
|
|
return;
|
|
}
|
|
|
|
# Stop sfcapd if it's already running
|
|
my $pid = pandora_stop_sflow_daemon ();
|
|
if (pandora_stop_sflow_daemon () != 0) {
|
|
logger (\%Config, "sfcapd (pid $pid) is already running, attempting to kill it...", 1);
|
|
print_message (\%Config, "sfcapd (pid $pid) is already running, attempting to kill it...", 1);
|
|
}
|
|
|
|
# Start sfcapd
|
|
my $command = $Config{'sflow_daemon'} . ' -D -T all -w -t ' . $Config{'sflow_interval'} . ' -P ' . $pid_file . ' -l ' . $Config{'sflow_path'};
|
|
if (system ("$command >/dev/null 2>&1") != 0) {
|
|
logger (\%Config, " [E] Could not start sfcapd: $command", 1);
|
|
print_message (\%Config, " [E] Could not start sfcapd: $command", 1);
|
|
return;
|
|
}
|
|
|
|
logger (\%Config, "[*] sFlow daemon started.", 1);
|
|
print_message (\%Config, "[*] sFlow daemon started.", 1);
|
|
}
|
|
|
|
########################################################################################
|
|
# Stop the sflow daemon if it's running.
|
|
########################################################################################
|
|
sub pandora_stop_sflow_daemon () {
|
|
|
|
my $pid_file = '/var/run/pandora_sfcapd.pid';
|
|
|
|
# Open the pid file
|
|
if ( ! (-e $pid_file && open (PIDFILE, $pid_file))) {
|
|
return 0;
|
|
}
|
|
|
|
my $pid = <PIDFILE>;
|
|
close PIDFILE;
|
|
|
|
# Check if sfcapd is running
|
|
if (kill (0, $pid) > 0) {
|
|
kill (9, $pid);
|
|
return $pid;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
sub pandora_agent_autoconfiguration_scheduled($) {
|
|
my $pa_config = shift;
|
|
|
|
my %pa_config = %{$pa_config};
|
|
|
|
my $dbh = db_connect ($pa_config{'dbengine'}, $pa_config{'dbname'}, $pa_config{'dbhost'}, $pa_config{'dbport'},
|
|
$pa_config{'dbuser'}, $pa_config{'dbpass'});
|
|
|
|
while ($THRRUN == 1) {
|
|
eval {{
|
|
local $SIG{__DIE__};
|
|
|
|
my @autoconfig = get_db_rows (
|
|
$dbh,
|
|
'SELECT *, DATE_FORMAT(DATE_ADD(periodically_time_from, INTERVAL ' . $pa_config->{'autoconfigure_agents_threshold'} . ' SECOND), "%H:%i:%S") AS time_minutes
|
|
FROM tautoconfig WHERE executed = 0 AND type_execution LIKE "scheduled" AND disabled = 0'
|
|
);
|
|
|
|
# Get current time.
|
|
my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime(time());
|
|
my $time = sprintf ("%.2d:%.2d:%.2d", $hour, $min, $sec);
|
|
|
|
foreach my $conf (@autoconfig) {
|
|
if (($conf->{'type_periodicity'} eq 'daily') ||
|
|
($conf->{'type_periodicity'} eq 'monthly' && $conf->{'periodically_day_from'} eq $mday) ||
|
|
($conf->{'type_periodicity'} eq 'weekly' && (($conf->{'sunday'} eq 1 && $wday eq 0) ||
|
|
($conf->{'monday'} eq 1 && $wday eq 1) || ($conf->{'tuesday'} eq 1 && $wday eq 2) ||
|
|
($conf->{'wednesday'} eq 1 && $wday eq 3) || ($conf->{'thursday'} eq 1 && $wday eq 4) ||
|
|
($conf->{'friday'} eq 1 && $wday eq 5) || ($conf->{'saturday'} eq 1 && $wday eq 6)))
|
|
) {
|
|
if ($time ge $conf->{'periodically_time_from'} && $time le $conf->{'time_minutes'}) {
|
|
# Update executed.
|
|
db_process_update ($dbh, 'tautoconfig', {'executed' => 1}, {'id' => $conf->{'id'}});
|
|
# Get agents.
|
|
my @agents = get_db_rows(
|
|
$dbh,
|
|
'SELECT id_agente, alias, id_grupo, id_os, os_version, direccion, nombre AS agent_name FROM tagente
|
|
WHERE `disabled` = 0'
|
|
);
|
|
|
|
foreach my $agent (@agents) {
|
|
# Check if the agent matches the rules.
|
|
my $match = enterprise_hook('autoconf_evaluate_rules', [$pa_config, $dbh, $agent, $conf->{'id'}, $agent->{'id_agente'}, 1]);
|
|
if (defined($match) && $match > 0) {
|
|
enterprise_hook('autoconf_execute_actions', [$pa_config, $dbh, $agent->{'id_agente'}, $agent, $conf->{'id'}]);
|
|
}
|
|
}
|
|
|
|
# Update executed.
|
|
db_process_update ($dbh, 'tautoconfig', {'executed' => 0}, {'id' => $conf->{'id'}});
|
|
}
|
|
}
|
|
}
|
|
}};
|
|
|
|
sleep ($pa_config->{'autoconfigure_agents_threshold'});
|
|
}
|
|
|
|
db_disconnect($dbh);
|
|
}
|
|
|
|
########################################################################################
|
|
# Additional tasks executed periodically by the Pandora FMS Server
|
|
########################################################################################
|
|
sub pandora_server_tasks ($) {
|
|
my ($pa_config) = @_;
|
|
|
|
# Get the console DB connection
|
|
my $dbh = db_connect ($pa_config->{'dbengine'}, $pa_config->{'dbname'}, $pa_config->{'dbhost'}, $pa_config->{'dbport'},
|
|
$pa_config->{'dbuser'}, $pa_config->{'dbpass'});
|
|
my $counter = 0;
|
|
my $first_run = 1;
|
|
while ($THRRUN == 1) {
|
|
eval {
|
|
if (pandora_is_master($pa_config) == 1) {
|
|
|
|
# TASKS EXECUTED ONCE
|
|
# -------------------
|
|
if ($first_run == 1) {
|
|
$first_run = 0;
|
|
|
|
# Update the agent cache.
|
|
enterprise_hook('update_agent_cache', [\%Config]);
|
|
}
|
|
|
|
# TASKS EXECUTED EVERY 5 SECONDS (Low latency tasks)
|
|
# --------------------------------------------------
|
|
if (($counter % 5) == 0) {
|
|
|
|
# Update forced alerts
|
|
pandora_exec_forced_alerts ($pa_config, $dbh);
|
|
|
|
my @agents = get_db_rows ($dbh, 'SELECT id_agente, update_alert_count FROM tagente WHERE update_alert_count=1');
|
|
foreach my $agent (@agents) {
|
|
if ($agent->{'update_alert_count'} == 1) {
|
|
pandora_update_agent_alert_count ($pa_config, $dbh, $agent->{'id_agente'});
|
|
}
|
|
}
|
|
}
|
|
|
|
# TASKS EXECUTED EVERY 30 SECONDS (Mid latency tasks)
|
|
# ---------------------------------------------------
|
|
if (($counter % 30) == 0) {
|
|
|
|
# Update module status and fired alert counts
|
|
my @agents = get_db_rows ($dbh, 'SELECT id_agente, nombre, update_module_count, update_secondary_groups FROM tagente WHERE (update_module_count=1 OR update_secondary_groups=1)');
|
|
foreach my $agent (@agents) {
|
|
logger ($pa_config, "Updating module status and fired alert counts for agent " . $agent->{'nombre'}, 10);
|
|
|
|
if ($agent->{'update_module_count'} == 1) {
|
|
pandora_update_agent_module_count ($pa_config, $dbh, $agent->{'id_agente'});
|
|
}
|
|
|
|
if ($agent->{'update_secondary_groups'} == 1) {
|
|
pandora_update_secondary_groups_cache ($pa_config, $dbh, $agent->{'id_agente'});
|
|
}
|
|
}
|
|
|
|
# Keepalive module control.(very DB intensive, not run frecuently
|
|
pandora_module_keep_alive_nd ($pa_config, $dbh);
|
|
|
|
# Set the status of unknown modules
|
|
pandora_module_unknown ($pa_config, $dbh);
|
|
|
|
# Check if an autodisabled agent needs to be autodisable
|
|
pandora_disable_autodisable_agents ($pa_config, $dbh);
|
|
}
|
|
|
|
# TASKS EXECUTED EVERY 60 SECONDS (High latency tasks)
|
|
# ----------------------------------------------------
|
|
if (($counter % 60) == 0) {
|
|
# Downtimes are executed only 30 x Server Threshold secs
|
|
pandora_planned_downtime ($pa_config, $dbh);
|
|
|
|
# Realtime stats (Only master server!) - ( VERY HEAVY !)
|
|
# Realtimestats == 1, generated by WEB Console, not by server!
|
|
if (defined($pa_config->{"realtimestats"}) && $pa_config->{"realtimestats"} == 0){
|
|
|
|
# Check if I need to refresh stats
|
|
my $last_execution_stats = get_db_value ($dbh, "SELECT MAX(utimestamp) FROM tgroup_stat");
|
|
if (!defined($last_execution_stats) || $last_execution_stats < (time() - $pa_config->{"stats_interval"})){
|
|
pandora_group_statistics ($pa_config, $dbh);
|
|
pandora_server_statistics ($pa_config, $dbh);
|
|
}
|
|
}
|
|
|
|
# Check if snmptrapd is freeze.
|
|
pandora_snmptrapd_still_working ($pa_config, $dbh);
|
|
|
|
# Event auto-expiry
|
|
my $expiry_time = $pa_config->{"event_expiry_time"};
|
|
my $expiry_window = $pa_config->{"event_expiry_window"};
|
|
if ($expiry_time > 0 && $expiry_window > 0 && $expiry_window > $expiry_time) {
|
|
my $time_ref = time ();
|
|
my $expiry_limit = $time_ref - $expiry_time;
|
|
my $expiry_window = $time_ref - $expiry_window;
|
|
db_do ($dbh, 'UPDATE tevento SET estado=1, ack_utimestamp=? WHERE estado=0 AND utimestamp < ? AND utimestamp > ?', $time_ref, $expiry_limit, $expiry_window);
|
|
}
|
|
}
|
|
}
|
|
|
|
# COMMON TASKS (master and non-master)
|
|
# ---------------------------------------------------------------
|
|
if (($counter % 30) == 0) {
|
|
# Update configuration options from the console.
|
|
pandora_get_sharedconfig ($pa_config, $dbh);
|
|
|
|
# Rotate the log file.
|
|
pandora_rotate_logfile($pa_config);
|
|
|
|
# Set event storm protection
|
|
pandora_set_event_storm_protection (pandora_get_tconfig_token ($dbh, 'event_storm_protection', 0));
|
|
}
|
|
# Pandora self monitoring
|
|
if (defined($pa_config->{"self_monitoring"})
|
|
&& $pa_config->{"self_monitoring"} == 1
|
|
&& !is_metaconsole($pa_config)
|
|
&& $counter % $pa_config->{'self_monitoring_interval'} == 0) {
|
|
pandora_self_monitoring ($pa_config, $dbh);
|
|
pandora_installation_monitoring($pa_config, $dbh);
|
|
}
|
|
};
|
|
|
|
# Avoid counter overflow
|
|
if ($counter >= ~0){
|
|
$counter = 0;
|
|
}
|
|
else {
|
|
$counter++;
|
|
}
|
|
|
|
sleep (1);
|
|
}
|
|
|
|
db_disconnect($dbh);
|
|
}
|
|
|
|
################################################################
|
|
################################################################
|
|
## Main.
|
|
################################################################
|
|
################################################################
|
|
sub main() {
|
|
|
|
# Daemonize and put in background
|
|
if ($Config{'daemon'} == 1) {
|
|
print_message (\%Config, " [*] Backgrounding " . pandora_get_initial_product_name() . " Server process.\n", 1);
|
|
pandora_daemonize (\%Config);
|
|
}
|
|
|
|
# Load enterprise module
|
|
if (enterprise_load (\%Config) == 0) {
|
|
$Config{'__enterprise_enabled'} = 0;
|
|
print_message (\%Config, " [*] Pandora FMS Enterprise module not available.", 1);
|
|
logger (\%Config, " [*] Pandora FMS Enterprise module not available.", 1);
|
|
} else {
|
|
$Config{'__enterprise_enabled'} = 1;
|
|
print_message (\%Config, " [*] " . pandora_get_initial_product_name() . " Enterprise module loaded.", 1);
|
|
logger (\%Config, " [*] " . pandora_get_initial_product_name() . " Enterprise module loaded.", 1);
|
|
}
|
|
|
|
# Save the start time for warmup intervals.
|
|
$Config{'__start_utimestamp__'} = time();
|
|
|
|
# Start the servers
|
|
pandora_startup ();
|
|
|
|
if ($Config{'warmup_unknown_interval'} > 0) {
|
|
logger(\%Config, "Warmup mode for unknown modules started.", 3);
|
|
pandora_event (\%Config, "Warmup mode for unknown modules started.", 0, 0, 0, 0, 0, 'system', 0, $DBH);
|
|
}
|
|
if ($Config{'warmup_event_interval'} > 0) {
|
|
logger(\%Config, "Warmup mode for alerts started.", 3);
|
|
logger(\%Config, "Warmup mode for events started.", 3);
|
|
pandora_event (\%Config, "Warmup mode for alerts started.", 0, 0, 0, 0, 0, 'system', 0, $DBH);
|
|
pandora_event (\%Config, "Warmup mode for events started.", 0, 0, 0, 0, 0, 'system', 0, $DBH);
|
|
}
|
|
|
|
# Only if console_api_url was not defined
|
|
if( !defined($Config{"console_api_url"}) ) {
|
|
$Config{"console_api_url"} = get_console_api_url(\%Config, $DBH);
|
|
}
|
|
|
|
# Definition of configuration file
|
|
my $cfg_file = $Config{'pandora_path'};
|
|
my $cfg_file_output = $Config{'pandora_path'} . "_backup";
|
|
|
|
# Only if console_api_pass was not defined
|
|
if ( !defined($Config{"console_api_pass"}) ) {
|
|
my $console_api_pass = pandora_get_tconfig_token ($DBH, 'api_password', '');
|
|
# If api_password is empty in database
|
|
if ( $console_api_pass eq '' ) {
|
|
$console_api_pass = '1234';
|
|
db_process_update ($DBH, 'tconfig', {'value' => $console_api_pass}, {'token' => 'api_password'});
|
|
}
|
|
# Definition of console_api_pass in config
|
|
$Config{"console_api_pass"} = $console_api_pass;
|
|
# Watch if paramether is added or not (even if it is commented)
|
|
my $console_api_pass_control = undef;
|
|
if ( open (CFGin, "<$cfg_file") && open (CFGout, ">>$cfg_file_output") ) {
|
|
while(my $row = <CFGin>) {
|
|
if (chomp($row) =~ (m/^#\sconsole_api_pass\s(.*)/i)) {
|
|
$console_api_pass_control = 1;
|
|
print CFGout "\nconsole_api_pass " .$Config{"console_api_pass"} . "\n";
|
|
} else {
|
|
print CFGout "$row\n";
|
|
}
|
|
}
|
|
# Only if the parameter was not added
|
|
if ( !defined($console_api_pass_control) ) {
|
|
print CFGout "\n# console_api_pass: Console password\n";
|
|
print CFGout "console_api_pass " .$Config{"console_api_pass"} . "\n";
|
|
}
|
|
# Close both files
|
|
close (CFGin);
|
|
close (CFGout);
|
|
# Convert the output file in the original configuration file
|
|
rename $cfg_file_output, $cfg_file;
|
|
}
|
|
}
|
|
|
|
# Only if console_pass was not defined.
|
|
if ( !defined($Config{"console_pass"}) ){
|
|
# Randomized parametrization of console_pass.
|
|
if (open (CFG, ">>$cfg_file")) {
|
|
my $valid_chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
|
my $num_char = 8;
|
|
my $randomized_string = '';
|
|
for (my $i = 0; $i < $num_char; $i++) {
|
|
$randomized_string .= substr($valid_chars, rand(length($valid_chars)), 1);
|
|
}
|
|
|
|
$Config{"console_pass"} = $randomized_string;
|
|
print CFG "\n# console_pass: Console password\n";
|
|
print CFG "# To make sure console_api_url, console_api_pass, console_user and console_pass are properly configured run:\n";
|
|
print CFG "# curl '<console_api_url>?op=get&op2=test&apipass=<console_api_pass>&user=<console_user>&pass=<console_pass>'\n";
|
|
print CFG "# It should return a string similar to:\n";
|
|
print CFG "# OK,{VERSION},{BUILD}\n";
|
|
print CFG "console_pass " .$Config{"console_pass"} . "\n";
|
|
|
|
close (CFG);
|
|
|
|
} else {
|
|
logger(\%Config, "[WARNING] Error with configuration file when define `console_pass`: $!", 3);
|
|
}
|
|
}
|
|
|
|
# Only if console_user was not defined
|
|
if ( !defined($Config{"console_user"}) ) {
|
|
my $pandora_uid = pandora_get_tconfig_token ($DBH, 'pandora_uid', '');
|
|
|
|
if ( $pandora_uid ne '' && $pandora_uid ne 'OFFLINE' ) {
|
|
$Config{"console_user"} = "internal_API_$pandora_uid";
|
|
} else {
|
|
$Config{"console_user"} = "internal_API";
|
|
}
|
|
|
|
# If user not exists in DB, is necessary to create it
|
|
if ( get_user_exists($DBH, $Config{"console_user"}) == -1 ) {
|
|
|
|
# Definition of API user parameters
|
|
my $api_user_parameters = {};
|
|
$api_user_parameters->{'id_user'} = $Config{"console_user"};
|
|
$api_user_parameters->{'password'} = md5_hex($Config{"console_pass"});
|
|
$api_user_parameters->{'comments'} = "Internal user, used for generating reports and email attachments";
|
|
$api_user_parameters->{'is_admin'} = 0;
|
|
$api_user_parameters->{'not_login'} = 1;
|
|
|
|
# Profile creation for API purpouses
|
|
my $api_profile_parameters = {};
|
|
$api_profile_parameters->{'id_usuario'} = $Config{"console_user"};
|
|
$api_profile_parameters->{'id_perfil'} = 1;
|
|
$api_profile_parameters->{'id_grupo'} = 0;
|
|
$api_profile_parameters->{'assigned_by'} = "system";
|
|
$api_profile_parameters->{'id_policy'} = 0;
|
|
|
|
# Insert in DB
|
|
my $res_tusuario = db_process_insert($DBH, 'id_user', 'tusuario', $api_user_parameters);
|
|
my $res_tusuario_perfil = db_process_insert($DBH, 'id_user', 'tusuario_perfil', $api_profile_parameters);
|
|
|
|
# If the user was inserted in DB, must write it in configuration file
|
|
if ( $res_tusuario_perfil > 0 ) {
|
|
if (open (CFG, ">>$cfg_file")) {
|
|
print CFG "\n# Console User (created for API use)\n";
|
|
print CFG "console_user " . $Config{"console_user"} . "\n";
|
|
close (CFG);
|
|
|
|
} else {
|
|
logger(\%Config, "Warning. Was not possible edit configuration file for add console user", 3);
|
|
}
|
|
} else {
|
|
logger(\%Config, "Warning. Was not possible creating console user for API.", 3);
|
|
}
|
|
}
|
|
}
|
|
|
|
# Testing API url
|
|
my $curl_execution = "'".$Config{'console_api_url'}."?op=get&op2=test&apipass=".$Config{"console_api_pass"}."&user=".$Config{"console_user"}."&pass=".$Config{"console_pass"}."'";
|
|
# More than 30 secs is highly unrecommendated
|
|
my $command = $Config{'plugin_exec'}.' 30 curl --cookie-jar /tmp/cron-session-cookies '.$curl_execution.' 2>/dev/null';
|
|
my $exe_testing_api = `$command`;
|
|
my @res_testing_api = split(',', $exe_testing_api);
|
|
my $has_api_access = undef;
|
|
|
|
if ( $res_testing_api[0] ne 'OK' ) {
|
|
logger(\%Config, "Warning! The server does not have access to the API, this can trigger problems in the generation of reports and graphs.", 1);
|
|
pandora_event (\%Config, "The server " . $Config{'servername'} ." cannot access the Pandora FMS API through the supplied credentials. Please check the configuration, some components may fail due to this misconfiguration. Look in the manual for the configuration tokens related to console_api_pass.", 0, 0, 0, 0, 0, 'system', 0, $DBH);
|
|
$has_api_access = "0";
|
|
} else {
|
|
# Test successful.
|
|
pandora_set_tconfig_token($DBH, 'internal_user_pass',
|
|
pandora_input_password(\%Config, $Config{"console_pass"})
|
|
);
|
|
pandora_set_tconfig_token($DBH, 'internal_user', $Config{"console_user"});
|
|
$has_api_access = "1";
|
|
}
|
|
|
|
my $token = "has_api_access";
|
|
my $prev_access = pandora_get_config_value($DBH, $token);
|
|
|
|
if(defined($prev_access) && $prev_access ne "") {
|
|
db_update ($DBH, 'UPDATE tconfig SET value = ? WHERE token = ?', $has_api_access, $token);
|
|
}
|
|
else {
|
|
db_insert ($DBH, 'id_config', 'INSERT INTO tconfig (token, value) VALUES (?, ?)', $token, $has_api_access);
|
|
}
|
|
|
|
# Generate 'going up' events
|
|
foreach my $server (@Servers) {
|
|
$server->upEvent ();
|
|
}
|
|
|
|
# Check if the Data Server has too many threads
|
|
if ($Config{'dataserver_threads'} > 5) {
|
|
logger (\%Config, "[W] Server " . $Config{'servername'} . " have configured " . $Config{'dataserver_threads'}
|
|
. " threads for the data server. You should not use more than 5 threads for this server", 1);
|
|
print_message (\%Config, " [W] Server " . $Config{'servername'} . " have configured " . $Config{'dataserver_threads'}
|
|
. " threads for the data server. You should not use more than 5 threads for this server", 1);
|
|
pandora_event (\%Config, "Server " . $Config{'servername'} . " have configured "
|
|
. $Config{'dataserver_threads'} . " threads for the data server", 0, 0, 3, 0, 0, 'system', 0, $DBH);
|
|
}
|
|
|
|
# Check if the Pandora Server has too many threads
|
|
my $totalThreads = 0;
|
|
foreach my $server (@Servers) {
|
|
$totalThreads += $server->getNumThreads ();
|
|
}
|
|
if ($totalThreads > 40) {
|
|
logger (\%Config, '[W] Server ' . $Config{'servername'} . ' have configured a total of ' . $totalThreads
|
|
. ' threads. This setup is not recommended, you should reduce the number of total threads below 40', 1);
|
|
print_message (\%Config, ' [W] Server ' . $Config{'servername'} . ' have configured a total of ' . $totalThreads
|
|
. ' threads. This setup is not recommended, you should reduce the number of total threads below 40', 1);
|
|
pandora_event (\%Config, 'Server ' . $Config{'servername'} . ' have configured a total of ' . $totalThreads
|
|
. ' threads', 0, 0, 3, 0, 0, 'system', 0, $DBH);
|
|
}
|
|
|
|
# Check if the log verbosity is set to 10
|
|
if ($Config{'verbosity'} == 10) {
|
|
logger (\%Config, '[W] Log verbosity is set to 10. This will degrade the server performance. Please set to a lower value ASAP', 1);
|
|
print_message (\%Config, ' [W] Log verbosity is set to 10. This will degrade the server performance. Please set to a lower value ASAP', 1);
|
|
pandora_event (\%Config, 'Log verbosity is set to 10. This will degrade the server performance', 0, 0, 1, 0, 0, 'system', 0, $DBH);
|
|
}
|
|
|
|
# Main loop
|
|
my $time_ref = time ();
|
|
my $thr_time_ref = 0;
|
|
my $test_remote_interval = ($Config{'keepalive'}/$Config{'server_threshold'});
|
|
my $test_remote = 0;
|
|
while ($RUN == 1) {
|
|
|
|
eval {
|
|
|
|
# Update server status
|
|
foreach my $server (@Servers) {
|
|
die ($server->getErrStr ()) unless ($server->checkThreads () == 1);
|
|
$server->update();
|
|
}
|
|
|
|
# Make sure all server threads are running.
|
|
die("Server thread crashed.") unless (check_server_threads() == 1);
|
|
|
|
# Monitor server threads.
|
|
if (defined($Config{"self_monitoring"})
|
|
&& $Config{"self_monitoring"} == 1
|
|
&& !is_metaconsole(\%Config)
|
|
&& time() - $thr_time_ref > $Config{'self_monitoring_interval'}) {
|
|
# Update agent name token.
|
|
pandora_set_tconfig_token($DBH, 'self_monitoring_agent_name', $Config{'self_monitoring_agent_name'});
|
|
$thr_time_ref = time();
|
|
pandora_thread_monitoring (\%Config, $DBH, \@Servers);
|
|
}
|
|
|
|
db_do ($DBH,
|
|
"UPDATE tserver SET status = -1
|
|
WHERE UNIX_TIMESTAMP(now())-UNIX_TIMESTAMP(keepalive) > 2*server_keepalive
|
|
AND status != 0 AND server_type != ?",
|
|
PandoraFMS::Tools::SATELLITESERVER
|
|
);
|
|
|
|
# Set the master server
|
|
pandora_set_master(\%Config, $DBH);
|
|
};
|
|
|
|
# Restart on error or auto restart
|
|
if ($@) {
|
|
|
|
if ($Config{'restart'} eq '0') {
|
|
print_message (\%Config, $@, 1);
|
|
pandora_shutdown ();
|
|
}
|
|
|
|
# Generate 'restarting' events
|
|
foreach my $server (@Servers) {
|
|
$server->restartEvent ($@);
|
|
}
|
|
|
|
logger (\%Config, $Config{'rb_product_name'} . ' Server restarting (' . $@ . ') in ' . $Config{'restart_delay'} . ' seconds.', 1);
|
|
pandora_restart ();
|
|
}
|
|
elsif (($Config{'auto_restart'} > 0) && (time () - $time_ref > $Config{'auto_restart'})) {
|
|
$time_ref = time ();
|
|
|
|
# Mute
|
|
open(OLDOUT, ">&STDOUT");
|
|
open (STDOUT, '>/dev/null');
|
|
|
|
# Restart
|
|
pandora_restart ();
|
|
|
|
# Unmute
|
|
open(STDOUT, ">&OLDOUT");
|
|
close (OLDOUT);
|
|
}
|
|
|
|
if ($test_remote >= $test_remote_interval) {
|
|
if ($Config{'remote_config'} == 1 && enterprise_hook ('pandora_remote_config_server', [\%Config])) {
|
|
|
|
# Generate 'restarting' events
|
|
foreach my $server (@Servers) {
|
|
$server->restartEvent ($@);
|
|
}
|
|
|
|
logger (\%Config, $Config{'rb_product_name'} . ' Server restarting (' . $@ . ') in 5 seconds.', 1);
|
|
pandora_load_config (\%Config);
|
|
if (enterprise_load (\%Config) == 0) {
|
|
$Config{'__enterprise_enabled'} = 0;
|
|
print_message (\%Config, " [*] Pandora FMS Enterprise module not available.", 1);
|
|
logger (\%Config, " [*] Pandora FMS Enterprise module not available.", 1);
|
|
} else {
|
|
$Config{'__enterprise_enabled'} = 1;
|
|
print_message (\%Config, " [*] " . pandora_get_initial_product_name() . " Enterprise module loaded.", 1);
|
|
logger (\%Config, " [*] " . pandora_get_initial_product_name() . " Enterprise module loaded.", 1);
|
|
}
|
|
pandora_restart (5);
|
|
}
|
|
$test_remote = 0;
|
|
}
|
|
else {
|
|
$test_remote++;
|
|
}
|
|
|
|
threads->yield;
|
|
sleep ($Config{'server_threshold'});
|
|
}
|
|
|
|
pandora_shutdown();
|
|
}
|
|
|
|
$SIG{'TERM'} = 'pandora_shutdown';
|
|
$SIG{'INT'} = 'pandora_shutdown';
|
|
|
|
# Error handler needs to be reviewed, Enterprise not found errors are too nasty :(
|
|
$SIG{__DIE__} = 'pandora_crash';
|
|
|
|
# Prevent alarm from bombing the main thread when called within a thread
|
|
$SIG{'ALRM'} = 'IGNORE';
|
|
|
|
# Initialize
|
|
pandora_init(\%Config, pandora_get_initial_product_name() . ' Server');
|
|
pandora_load_config (\%Config);
|
|
|
|
|
|
# Run as a regular process.
|
|
main();
|
|
|
|
################################################################################
|
|
# Kill any scripts started by the Pandora FMS Server that are still running.
|
|
################################################################################
|
|
END {{
|
|
local $SIG{HUP} = "IGNORE";
|
|
kill("HUP", -$$);
|
|
}}
|
|
|