mirror of
https://github.com/Cacti/cacti.git
synced 2026-10-23 11:36:53 +09:00
* 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>
395 lines
12 KiB
PHP
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;
|
|
}
|