Skip to content

Commit

Permalink
Fixing Issues #5783 and #5784 - Database Issues
Browse files Browse the repository at this point in the history
* These issues are to fix an issue encountered when trying to support technologies such as MaxScale readonly and readwrite split connections from the same PHP session.

The first issue is to fix a bug where the main Cacti connection was being closed even when you were trying to close another connection.  The second enhancement is to enable logging of connects, disconnects and the first SQL execution from a connection.

* issue#5783: Cacti improperly handles connection closing leading to issues with third party connections
* feature#5784: Provide a means by which a user can track Cacti connections and disconnects from the php error_log
  • Loading branch information
TheWitness committed Jul 4, 2024
1 parent 9f82109 commit 5dcda9f
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 6 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,8 @@ Cacti CHANGELOG
-issue#5777: The Export .CSV only includes the 1st line of Notes
-issue#5780: Undefined array key "default" in file html_form.php
-issue#5782: Function file_get_contents in file link.php
-issue#5783: Cacti improperly handles connection closing leading to issues with third party connections
-feature#5784: Provide a means by which a user can track Cacti connections and disconnects from the php error_log

1.2.27
-security#GHSA-37x7-mfjv-mm7m: Authentication Bypass when using using older password hashes
Expand Down
11 changes: 10 additions & 1 deletion include/config.php.dist
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,16 @@ $disable_log_rotation = false;
# define('DEBUG_SQL_FLOW', true);
#}

/*
/**
* Debug the connect and disconnect activity
* and the first execute from each connection
* to the PHP error_log.
*/
#if (!defined('DEBUG_SQL_CONNECT')) {
# define('DEBUG_SQL_CONNECT', true);
#}

/**
* Allow the use of Proxy IPs when searching for client
* IP to be used
*
Expand Down
1 change: 1 addition & 0 deletions include/global.php
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@
$config['DEBUG_READ_CONFIG_OPTION_DB_OPEN'] = defined('DEBUG_READ_CONFIG_OPTION_DB_OPEN');
$config['DEBUG_SQL_CMD'] = defined('DEBUG_SQL_CMD');
$config['DEBUG_SQL_FLOW'] = defined('DEBUG_SQL_FLOW');
$config['DEBUG_SQL_CONNECT'] = defined('DEBUG_SQL_CONNECT');

/* check for an empty database port */
if (empty($database_port)) {
Expand Down
68 changes: 63 additions & 5 deletions lib/database.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ function db_connect_real($device, $user, $pass, $db_name, $db_type = 'mysql', $p
$i = 0;

if (isset($database_sessions["$device:$port:$db_name"])) {
if (!empty($config['DEBUG_SQL_CONNECT'])) {
error_log(sprintf('NOTE: Connect using cached connection %s:%s/%s.', $device, $port, $db_name));
}

return $database_sessions["$device:$port:$db_name"];
}

Expand Down Expand Up @@ -133,6 +137,10 @@ function db_connect_real($device, $user, $pass, $db_name, $db_type = 'mysql', $p
}
$cnn_id->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT);

if (!empty($config['DEBUG_SQL_CONNECT'])) {
error_log(sprintf('NOTE: New connection to %s:%s/%s.', $device, $port, $db_name));
}

$bad_modes = array(
'STRICT_TRANS_TABLES',
'STRICT_ALL_TABLES',
Expand Down Expand Up @@ -421,16 +429,44 @@ function db_get_active_replicas() {
*
* @return (bool) the result of the close command
*/
function db_close($db_conn = false) {
global $database_sessions, $database_default, $database_hostname, $database_port, $database_persist;
function db_close(&$db_conn = false) {
global $database_sessions, $error_logged, $database_default, $database_hostname, $database_port, $database_persist, $database_details;

/* check for a connection being passed, if not use legacy behavior */
if (!is_object($db_conn)) {
if (!empty($config['DEBUG_SQL_CONNECT'])) {
error_log(sprintf('NOTE: Disconnecting from %s:%s/%s.', $database_hostname, $database_port, $database_default));
}

$db_conn = $database_sessions["$database_hostname:$database_port:$database_default"];

if (!is_object($db_conn)) {
if (!empty($config['DEBUG_SQL_CONNECT'])) {
error_log(sprintf('WARNING: Disconnect issues. Non-object for %s:%s/%s.', $database_hostname, $database_port, $database_default));
}

return false;
}

$database_sessions["$database_hostname:$database_port:$database_default"] = null;

if (isset($error_logged["$database_hostname:$database_port:$database_default"])) {
unset($error_logged["$database_hostname:$database_port:$database_default"]);
}
} elseif (!empty($config['DEBUG_SQL_CONNECT'])) {
$id = spl_object_id($db_conn);
$hash = spl_object_hash($db_conn);
if (isset($database_details[$hash])) {
$det = $database_details[$hash];

error_log(sprintf('NOTE: Disconnecting from %s:%s/%s.', $det['database_hostname'], $det['database_port'], $det['database_default']));
} else {
error_log("WARNING: Disconnecting from unregistered Object ID: $id.");
}

if (isset($error_logged[$id])) {
unset($error_logged[$id]);
}
}

/* forcibly close connection if not persistent */
Expand All @@ -441,8 +477,6 @@ function db_close($db_conn = false) {
/* unset the variables which should do the same */
$db_conn = null;

$database_sessions["$database_hostname:$database_port:$database_default"] = null;

return true;
}

Expand Down Expand Up @@ -485,7 +519,7 @@ function db_execute($sql, $log = true, $db_conn = false) {
* @return (bool) '1' for success, false for failed
*/
function db_execute_prepared($sql, $params = array(), $log = true, $db_conn = false, $execute_name = 'Exec', $default_value = true, $return_func = 'no_return_function', $return_params = array()) {
global $database_sessions, $database_default, $config, $database_hostname, $database_port, $database_total_queries, $database_last_error, $database_log, $affected_rows;
global $database_sessions, $error_logged, $database_default, $config, $database_hostname, $database_port, $database_total_queries, $database_last_error, $database_log, $affected_rows, $database_details;

$database_total_queries++;

Expand All @@ -497,13 +531,37 @@ function db_execute_prepared($sql, $params = array(), $log = true, $db_conn = fa
if (!is_object($db_conn)) {
if (isset($database_sessions["$database_hostname:$database_port:$database_default"])) {
$db_conn = $database_sessions["$database_hostname:$database_port:$database_default"];
} elseif (!isset($error_logged["$database_hostname:$database_port:$database_default"])) {
if (!empty($config['DEBUG_SQL_CONNECT'])) {
error_log(sprintf('WARNING: Execute unable to find connection for %s:%s/%s.', $database_hostname, $database_port, $database_default));
$error_logged["$database_hostname:$database_port:$database_default"] = true;
}
}

if (!is_object($db_conn)) {
if (!empty($config['DEBUG_SQL_CONNECT'])) {
error_log('FATAL: Unable to find connection Object ID.');
}

$database_last_error = 'DB ' . $execute_name . ' -- No connection found';

return false;
}
} elseif (!empty($config['DEBUG_SQL_CONNECT'])) {
$id = spl_object_id($db_conn);
$hash = spl_object_hash($db_conn);

if (!isset($error_logged[$id])) {
if (isset($database_details[$hash])) {
$det = $database_details[$hash];

error_log(sprintf("NOTE: Execute Using %s:%s/%s.", $det['database_hostname'], $det['database_port'], $det['database_default']));
} else {
error_log("WARNING: Execute Using Object ID: $id.");
}

$error_logged[$id] = true;
}
}

$sql = db_strip_control_chars($sql);
Expand Down

0 comments on commit 5dcda9f

Please sign in to comment.