From ecd77dffba30aa3a7f38b217772de52c5a76e9b6 Mon Sep 17 00:00:00 2001 From: Ramon Novoa Date: Mon, 28 Mar 2022 20:56:05 +0200 Subject: [PATCH] Fix locking issues in pandora_db. Implement a persistent lock using Pandora FMS's database. Ref. pandora_enterprise#8565. --- pandora_server/lib/PandoraFMS/DB.pm | 48 +++++++++++++++++++++++++++++ pandora_server/util/pandora_db.pl | 21 +++++++++---- 2 files changed, 63 insertions(+), 6 deletions(-) diff --git a/pandora_server/lib/PandoraFMS/DB.pm b/pandora_server/lib/PandoraFMS/DB.pm index f7cd712293..ea29918e34 100644 --- a/pandora_server/lib/PandoraFMS/DB.pm +++ b/pandora_server/lib/PandoraFMS/DB.pm @@ -42,6 +42,7 @@ our @EXPORT = qw( db_disconnect db_do db_get_lock + db_get_pandora_lock db_insert db_insert_get_values db_insert_from_array_hash @@ -49,6 +50,7 @@ our @EXPORT = qw( db_process_insert db_process_update db_release_lock + db_release_pandora_lock db_string db_text db_update @@ -1575,6 +1577,52 @@ sub db_release_lock($$) { my ($lock) = $sth->fetchrow; } +######################################################################## +## Try to obtain a persistent lock using Pandora FMS's database. +######################################################################## +sub db_get_pandora_lock($$;$) { + my ($dbh, $lock_name, $lock_timeout) = @_; + my $rv; + + # Lock. + my $lock = db_get_lock($dbh, $lock_name, $lock_timeout); + if ($lock != 0) { + my $lock_value = get_db_value($dbh, "SELECT `value` FROM tconfig WHERE token = 'pandora_lock_$lock_name'"); + if (!defined($lock_value)) { + my $sth = $dbh->prepare('INSERT INTO tconfig (`token`, `value`) VALUES (?, ?)'); + $rv = $sth->execute('pandora_lock_' . $lock_name, '1'); + } elsif ($lock_value == 0) { + my $sth = $dbh->prepare('UPDATE tconfig SET `value`=? WHERE `token`=?'); + $rv = $sth->execute('1', 'pandora_lock_' . $lock_name); + } + db_release_lock($dbh, $lock_name); + } + + # Lock acquired. + if ($rv) { + return 1; + } + + # Something went wrong. + return 0; +} + +######################################################################## +## Release a persistent lock. +######################################################################## +sub db_release_pandora_lock($$;$) { + my ($dbh, $lock_name, $lock_timeout) = @_; + my $rv; + + # Lock. + my $lock = db_get_lock($dbh, $lock_name, $lock_timeout); + if ($lock != 0) { + my $sth = $dbh->prepare('UPDATE tconfig SET `value`=? WHERE `token`=?'); + $rv = $sth->execute('0', 'pandora_lock_' . $lock_name); + db_release_lock($dbh, $lock_name); + } +} + ######################################################################## ## Set SSL options globally for the module. ######################################################################## diff --git a/pandora_server/util/pandora_db.pl b/pandora_server/util/pandora_db.pl index f4fe7f88d7..6216f669ba 100755 --- a/pandora_server/util/pandora_db.pl +++ b/pandora_server/util/pandora_db.pl @@ -47,6 +47,9 @@ my $BIG_OPERATION_STEP = 100; # 100 is default #Increate to 3000~5000 in fast systems decrease to 500 or 250 on systems with locks my $SMALL_OPERATION_STEP = 1000; # 1000 is default +# Timeout for lock acquisition. +my $LOCK_TIMEOUT = 60; + # FLUSH in each IO $| = 1; @@ -1211,10 +1214,18 @@ if ($conf{'_force'} == 0 && pandora_is_master(\%conf) == 0) { exit 1; } -# Get a lock on dbname. +# Set the lock name for pandora_db. my $lock_name = $conf{'dbname'}; -my $lock = db_get_lock ($dbh, $lock_name); -if ($lock == 0 && $conf{'_force'} == 0) { + +# Release the database lock in forced mode. +if ($conf{'_force'} == 1) { + log_message ('', " [*] Releasing database lock.\n\n"); + db_release_pandora_lock($dbh, $lock_name, $LOCK_TIMEOUT); +} + +# Get a lock on dbname. +my $lock = db_get_pandora_lock ($dbh, $lock_name, $LOCK_TIMEOUT); +if ($lock == 0) { log_message ('', " [*] Another instance of DB Tool seems to be running.\n\n"); exit 1; } @@ -1257,9 +1268,7 @@ if (scalar(@types) != 0) { } # Release the lock -if ($lock == 1) { - db_release_lock ($dbh, $lock_name); -} +db_release_pandora_lock ($dbh, $lock_name); # Cleanup and exit db_disconnect ($history_dbh) if defined ($history_dbh);