pandorafms/pandora_server/bin/pandora_server

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", -$$);
}}