Files
cacti/lib/dsdebug.php
Thomas VincentandTheWitness 03f6241ad9 fix(errors): remove dead E_STRICT and always-true constant guards (#7546)
* fix(errors): drop E_STRICT references deprecated on PHP 8.4

No diagnostic has raised E_STRICT since PHP 8.0 and composer.json floors at 8.1, so the handler entries were dead weight that made 8.4 emit a deprecation for every error it handled.

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>

* style: align GPL header border and drop PHP 4 comment in aggregate error handler

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>

* fix(poller): drop the E_STRICT entry from the poller error handlers

Nothing has raised that level since PHP 8.0 and reading the constant is
deprecated on 8.4, so each handled error emitted a second deprecation.

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>

* test: name the E_STRICT level numerically with a reason

Spelling the constant would reintroduce the deprecation the change removes.

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>

* fix(errors): drop the E_RECOVERABLE_ERROR guards and fold the poller tests in

The constant has existed since PHP 5.2 and the floor is 8.1, so both conditions are constant.

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>

* fix(poller): fold the always-true constant guards into the error maps

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>

---------

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Co-authored-by: TheWitness <thewitness@cacti.net>
2026-08-05 09:40:56 -04:00

395 lines
12 KiB
PHP

<?php
/*
+-------------------------------------------------------------------------+
| Copyright (C) 2004-2026 The Cacti Group |
| |
| 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; either version 2 |
| of the License, or (at your option) any later version. |
| |
| 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. |
+-------------------------------------------------------------------------+
| Cacti: The Complete RRDtool-based Graphing Solution |
+-------------------------------------------------------------------------+
| This code is designed, written, and maintained by the Cacti Group. See |
| about.php and/or the AUTHORS file for specific developer information. |
+-------------------------------------------------------------------------+
| http://www.cacti.net/ |
+-------------------------------------------------------------------------+
*/
/**
* dsstats_debug - This simple routine print's a standard message to the console
* when running in debug mode.
*
* @param string $message
*
* @return void
*/
function dsdebug_debug(string $message) : void {
global $debug;
if ($debug) {
print 'DSDEBUG: ' . $message . "\n";
}
}
/**
* log_dsstats_statistics - provides generic timing message to both the Cacti log and the settings
* table so that the statistics can be graphed as well.
*
* @param string $type The type of statistics to log, either 'HOURLY', 'DAILY' or 'MAJOR'.
* @param int $checks The number of checks performed
* @param int $issues The number of issues found
*
* @return void
*/
function log_dsdebug_statistics(string $type, int $checks, int $issues) : void {
global $start;
// take time and log performance data
$end = microtime(true);
$cacti_stats = sprintf('ChecksPerformed:%d, TotalIssues:%d, Time:%01.4f ', $checks, $issues, round($end - $start,4));
// log to the database
set_config_option('stats_dsdebug_' . $type, $cacti_stats);
// log to the logfile
cacti_log('DSDEBUG STATS: Type:' . $type . ', ' . $cacti_stats , true, 'SYSTEM');
}
/**
* dsstats_error_handler - This routine logs all PHP error transactions
* to make sure they are properly logged.
*
* @param int $errno The errornum reported by the system
* @param string $errmsg The error message provides by the error
* @param string $filename The filename that encountered the error
* @param int $linenum The line number where the error occurred
* @param array $vars The current state of PHP variables.
*
* @return bool Always returns true for some reason
*/
function dsdebug_error_handler(int $errno, string $errmsg, string $filename, int $linenum, array $vars = []) : bool {
if (read_config_option('log_verbosity') >= POLLER_VERBOSITY_DEBUG) {
// define all error types
$errortype = [
E_ERROR => 'Error',
E_WARNING => 'Warning',
E_PARSE => 'Parsing Error',
E_NOTICE => 'Notice',
E_CORE_ERROR => 'Core Error',
E_CORE_WARNING => 'Core Warning',
E_COMPILE_ERROR => 'Compile Error',
E_COMPILE_WARNING => 'Compile Warning',
E_USER_ERROR => 'User Error',
E_USER_WARNING => 'User Warning',
E_USER_NOTICE => 'User Notice',
E_RECOVERABLE_ERROR => 'Catchable Fatal Error'
];
// create an error string for the log
$err = "ERRNO:'" . $errno . "' TYPE:'" . $errortype[$errno] .
"' MESSAGE:'" . $errmsg . "' IN FILE:'" . $filename .
"' LINE NO:'" . $linenum . "'";
// let's ignore some lesser issues
if (substr_count($errmsg, 'date_default_timezone')) {
return false;
}
if (substr_count($errmsg, 'Only variables')) {
return false;
}
// log the error to the Cacti log
cacti_log('PROGERR: ' . $err, false, 'DSDEBUG');
return false;
}
return true;
}
function dsdebug_poller_output(array &$rrd_update_array) : void {
// suppress warnings
if (defined('E_DEPRECATED')) {
error_reporting(E_ALL ^ E_DEPRECATED);
} else {
error_reporting(E_ALL);
}
// install the dsstats error handler
set_error_handler('dsdebug_error_handler');
// do not make any calculations unless enabled
$checks = db_fetch_assoc('SELECT * FROM data_debug WHERE `done` = 0');
if (cacti_sizeof($checks)) {
if (cacti_sizeof($rrd_update_array)) {
foreach ($checks as $c) {
foreach ($rrd_update_array as $item) {
if ($c['datasource'] == $item['local_data_id']) {
if (isset($item['times'][key($item['times'])])) {
$c['info'] = cacti_unserialize($c['info']);
$c['info']['last_result'] = $item['times'][key($item['times'])];
$c['info'] = serialize($c['info']);
db_execute_prepared('UPDATE data_debug SET `info` = ? WHERE `id` = ?', [$c['info'], $c['id']]);
}
}
}
}
}
}
// restore original error handler
restore_error_handler();
}
function dsdebug_poller_bottom() : bool {
global $start;
// install the dsstats error handler
set_error_handler('dsdebug_error_handler');
// take time and log performance data
$start = microtime(true);
if (!db_table_exists('data_debug')) {
return true;
}
$checks = db_fetch_assoc('SELECT *
FROM data_debug
WHERE `done` = 0');
if (!empty($checks)) {
clearstatcache();
$total_issues = 0;
foreach ($checks as $c) {
$c['issue'] = [];
$info = cacti_unserialize($c['info']);
$dtd = db_fetch_row_prepared('SELECT *
FROM data_template_data
WHERE local_data_id = ?',
[$c['datasource']]);
if (!isset($dtd['local_data_id'])) {
$c['issue'][] = __('Data Source ID %s does not exist', $c['datasource']);
$c['done'] = 1;
$total_issues++;
} else {
if (read_config_option('boost_rrd_update_enable') == 'on') {
boost_process_poller_output($c['datasource']);
}
$real_path = str_replace('<path_rra>', CACTI_PATH_RRA, $dtd['data_source_path']);
// rrd_folder_writable
$info['rrd_folder_writable'] = (is_resource_writable(dirname($real_path) . '/') ? 1 : 0);
// rrd_exists
$info['rrd_exists'] = (file_exists($real_path) ? 1 : 0);
// rrd_writable
$info['rrd_writable'] = (is_resource_writable($real_path) ? 1 : 0);
// active
$info['active'] = $dtd['active'];
// owner
if (CACTI_SERVER_OS == 'win32') {
$info['owner'] = '<unable to determine on windows>';
} else {
$o = posix_getpwuid(fileowner($real_path));
$o = $o['name'];
$g = posix_getgrgid(filegroup($real_path));
$g = $g['name'];
$info['owner'] = $o . ':' . $g;
}
// poller_runas
$info['runas_poller'] = get_running_user();
// convert_name
$info['convert_name'] = (!str_contains('|', get_data_source_title($c['datasource'])) ? 1 : 0);
// last_result (processed by hook)
if (is_array($info['last_result']) && !empty($info['last_result']) && $info['valid_data'] == '') {
$info['valid_data'] = 1;
foreach ($info['last_result'] as $l) {
if ($l == 'U') {
cacti_log('Bad Data Found for Data Source ID ' . $c['datasource'], false, 'DSDEBUG');
$info['valid_data'] = 0;
$total_issues++;
}
}
}
$rrdinfo = rrdtool_function_info($c['datasource']);
if (cacti_sizeof($rrdinfo)) {
$comp = rrdtool_cacti_compare($c['datasource'], $rrdinfo);
$info['rrd_match'] = (!cacti_sizeof($comp) ? 1 : 0);
$info['rrd_match_array'] = $comp;
$info['rrd_info'] = $rrdinfo;
// rra_timestamp
if ($info['rra_timestamp'] != ''
&& isset($rrdinfo['last_update'])
&& $info['rra_timestamp'] != $rrdinfo['last_update']) {
$info['rra_timestamp2'] = $rrdinfo['last_update'];
}
if (isset($rrdinfo['last_update']) && $info['rra_timestamp'] == '') {
$info['rra_timestamp'] = $rrdinfo['last_update'];
}
$c['done'] = 1;
$f = [];
foreach ($info as $k => $v) {
if ($v === '') {
$c['done'] = 0;
$f[] = $k;
}
}
if ($c['started'] < time() - ($dtd['rrd_step'] * 5)) {
$c['done'] = 1;
$c['issue'][] = __('Debug not completed after 5 pollings');
$c['issue'][] = __('Failed fields: ') . implode(', ', $f);
$total_issues++;
}
// Try to determine issue
// Not set as Active
// Log permanent fails first
if ($info['active'] != 'on') {
$c['issue'][] = __('Data Source is not set as Active');
$c['done'] = 1;
$total_issues++;
}
// File Permissions
if ((!$info['rrd_exists'] || !$info['rrd_writable']) && !$info['rrd_folder_writable']) {
$c['issue'][] = __('RRDfile Folder (rra) is not writable by Poller. Folder owner: %s. Poller runs as: %s', $info['owner'], $info['runas_poller']);
$c['done'] = 1;
$total_issues++;
} elseif (!$info['rrd_writable']) {
$c['issue'][] = __('RRDfile is not writable by Poller. RRDfile owner: %s. Poller runs as %s', $info['owner'], $info['runas_poller']);
$c['done'] = 1;
$total_issues++;
}
// For errors that only appear after so many errors next
if ($c['done'] == 1) {
if ($info['rrd_match'] == 0) {
$c['issue'][] = __('RRDfile does not match Data Source');
$total_issues++;
}
if ($info['rra_timestamp2'] == '') {
$c['issue'][] = __('RRDfile not updated after polling');
$total_issues++;
}
if (is_array($info['last_result']) && !empty($info['last_result'])) {
foreach ($info['last_result'] as $k => $l) {
if ($l == 'U') {
$c['issue'][] = __('Data Source returned Bad Results for ' . $k);
$total_issues++;
}
}
} elseif ($info['last_result'] == '') {
$c['issue'][] = __('Data Source was not polled');
$total_issues++;
}
if ($c['issue'] == '') {
$c['issue'][] = __('No issues found');
}
}
} else {
$c['issue'][] = __('RRDfile not created yet');
$total_issues++;
}
$info = serialize($info);
db_execute_prepared('UPDATE data_debug
SET `done` = ?, `info` = ?, `issue` = ?
WHERE id = ?',
[$c['done'], $info, trim(implode("\n", $c['issue'])), $c['id']]);
}
}
log_dsdebug_statistics('poller', cacti_sizeof($checks), $total_issues);
}
restore_error_handler();
return true;
}
function dsdebug_run_repair(int $id) : bool {
$check = db_fetch_row_prepared('SELECT *
FROM data_debug
WHERE datasource = ?',
[$id]);
if (cacti_sizeof($check)) {
$check['info'] = cacti_unserialize($check['info']);
if (isset($check['info']['rrd_match_array']['tune'])) {
$path = get_data_source_path($id, true);
if (is_writable($path)) {
$rrdtool_path = read_config_option('path_rrdtool');
$failures = 0;
$failure_data = '';
foreach ($check['info']['rrd_match_array']['tune'] as $options) {
$command = $rrdtool_path . ' tune ' . $options;
$output = rrdtool_execute('tune ' . $options, false, RRDTOOL_OUTPUT_RETURN_STDERR);
if ($output == '') {
cacti_log("RRDfile repair command succeeded for DS[$id] Command[$command]", false, 'DSDEBUG');
} else {
cacti_log("ERROR: RRDfile repair command failed for DS[$id] Command[$command] Output[$output]", false, 'DSDEBUG');
$failures++;
}
}
if ($failures == 0) {
return true;
}
} else {
cacti_log("ERROR: RRDfile Repair Command Failed for DS[$id] Output[Unable to write RRDfile]", false, 'DSDEBUG');
}
} else {
cacti_log("ERROR: RRDfile Repair Command Could not be run for DS[$id] Output[No tune recommendation found]", false, 'DSDEBUG');
}
} else {
cacti_log("ERROR: RRDfile Repair Command Could not be run for DS[$id] Output[No Data Source debug information found]", false, 'DSDEBUG');
}
return false;
}