0) { $currentval = thold_build_cdef($thold_data['cdef'], $currentval, $thold_data['local_data_id'], $thold_data['data_template_rrd_id']); } break; case 2: if ($thold_data['percent_ds'] != '') { $currentval = thold_calculate_percent($thold_data, $currentval, $rrd_reindexed); } break; case 3: if ($thold_data['expression'] != '') { $currentval = thold_calculate_expression($thold_data, $currentval, $rrd_reindexed, $rrd_time_reindexed); } break; case 4: if ($thold_data['upper_ds'] != '') { $currentval = thold_calculate_lower_upper($thold_data, $currentval, $rrd_reindexed); } break; } if (is_numeric($currentval)) { $currentval = round($currentval, 4); } else { $currentval = ''; } thold_daemon_debug(sprintf('Checked Name:%s, Graph:%s, Value:%s, Time:%s', $thold_data['thold_name'], $thold_data['local_graph_id'], $currentval, $currenttime), $thread); if (!thold_daemon_persist_sample($thold_data, $item, $currentval, $currenttime)) { thold_cacti_log(sprintf('WARNING: Failed to persist threshold sample for ID %s.', $thold_data['thold_id']), $thread); } } $tholds = thold_get_thresholds_tholdcheck($thread, $start_time); $total_tholds = cacti_sizeof($tholds); thold_daemon_debug(sprintf('Found %u Thresholds to check for breech.', $total_tholds), $thread); if ($total_tholds) { foreach ($tholds as $thold) { thold_check_threshold($thold); db_execute_prepared('UPDATE thold_data SET tcheck = 0 WHERE id = ?', [$thold['id']] ); } } $end = microtime(true); if (read_config_option('remote_storage_method') == 1) { db_execute_prepared('DELETE ptdd FROM plugin_thold_daemon_data AS ptdd INNER JOIN thold_data AS td ON ptdd.id = td.id WHERE ptdd.poller_id = ? AND td.thread_id = ? AND ptdd.time <= FROM_UNIXTIME(?)', [$config['poller_id'], $thread, $start_time]); } else { db_execute_prepared('DELETE ptdd FROM plugin_thold_daemon_data AS ptdd INNER JOIN thold_data AS td ON ptdd.id = td.id WHERE td.thread_id = ? AND ptdd.time <= FROM_UNIXTIME(?)', [$thread, $start_time]); } } else { sleep(5); $cnn_id = thold_db_reconnect($cnn_id); heartbeat_process('thold', 'child', $thread); } } else { thold_daemon_debug('WARNING: Thold Database Connection Down. Sleeping 60 Seconds', $thread); sleep(60); } } /** * sig_handler - provides a generic means to catch exceptions to the Cacti log. * * @param $signo - (int) the signal that was thrown by the interface. * * @returns - null */ function sig_handler($signo) { global $thread; switch ($signo) { case SIGTERM: case SIGINT: thold_cacti_log('WARNING: Thold Daemon Child Process with PID[' . getmypid() . '] terminated by user', $thread); unregister_process('thold', 'child', $thread); exit; break; default: // ignore all other signals } } /** * Logs a debug message from a worker thread to the Cacti log, only * when global debug mode or the thold_daemon_debug setting is enabled. * * @param string $message The message to log. * @param mixed $thread The worker thread identifier, passed through * to thold_cacti_log() for log-line context. * * @return void * * @global bool $debug Whether debug output is enabled. */ function thold_daemon_debug($message, $thread) { global $debug; $daemon_debug = read_config_option('thold_daemon_debug'); if ($debug || $daemon_debug) { thold_cacti_log($message, $thread); } } /** * Fetches the set of "live" percent/expression-based thresholds * (thold_enabled and thold_per_enabled, flagged for a tcheck-style * evaluation) queued for a worker thread by the daemon's data * distribution step, restricted to hosts reporting as up. Uses the * remote-poller-aware host status join when remote storage is * configured. Filtered further via the 'thold_get_live_hosts' plugin * hook. * * @param int $thread The worker thread id to fetch queued * thresholds for. * @param float $start_time The batch's start timestamp; only rows * queued at or before this time are included. * * @return array The matching threshold records. * * @global array $config Cacti global configuration array; used when * remote storage is configured. */ function thold_get_thresholds_tholdcheck($thread, $start_time) { global $config; // check all thresholds if (read_config_option('remote_storage_method') == 1) { $sql_query = "SELECT td.*, h.hostname, h.description, h.notes AS dnotes, h.snmp_engine_id FROM plugin_thold_daemon_data AS tdd INNER JOIN thold_data AS td ON td.id = tdd.id LEFT JOIN data_template_rrd AS dtr ON dtr.id = td.data_template_rrd_id LEFT JOIN host as h ON td.host_id = h.id WHERE td.thread_id = ? AND tdd.poller_id = ? AND tdd.time <= FROM_UNIXTIME(?) AND td.thold_enabled = 'on' AND td.thold_per_enabled = 'on' AND td.tcheck = 1 AND h.status = 3"; $tholds = api_plugin_hook_function('thold_get_live_hosts', db_fetch_assoc_prepared($sql_query, [$thread, $config['poller_id'], $start_time]) ); } else { $sql_query = "SELECT td.*, h.hostname, h.description, h.notes AS dnotes, h.snmp_engine_id FROM plugin_thold_daemon_data AS tdd INNER JOIN thold_data AS td ON td.id = tdd.id LEFT JOIN data_template_rrd AS dtr ON dtr.id = td.data_template_rrd_id LEFT JOIN host as h ON td.host_id = h.id WHERE td.thread_id = ? AND tdd.time <= FROM_UNIXTIME(?) AND td.thold_enabled = 'on' AND td.thold_per_enabled = 'on' AND td.tcheck = 1 AND h.status = 3"; $tholds = api_plugin_hook_function('thold_get_live_hosts', db_fetch_assoc_prepared($sql_query, [$thread, $start_time]) ); } return $tholds; } /** * Fetches the set of thresholds (with their associated data source * details) queued for a worker thread by the daemon's data * distribution step, for standard (non-percent/expression) threshold * evaluation. Uses the remote-poller-aware query variant when remote * storage is configured. * * @param int $thread The worker thread id to fetch queued * thresholds for. * @param float $start_time The batch's start timestamp; only rows * queued at or before this time are included. * * @return array The matching threshold records joined with data source * details. * * @global array $config Cacti global configuration array; used when * remote storage is configured. */ function thold_get_thresholds_precheck($thread, $start_time) { global $config; if (read_config_option('remote_storage_method') == 1) { $sql_query = "SELECT tdd.id, tdd.rrd_reindexed, tdd.rrd_time_reindexed, td.id AS thold_id, td.name_cache AS thold_name, td.local_graph_id, td.percent_ds, td.expression, td.upper_ds, td.data_type, td.cdef, td.local_data_id, td.data_template_rrd_id, td.lastread, UNIX_TIMESTAMP(td.lasttime) AS lasttime, td.oldvalue, dtr.data_source_name AS name, dtr.data_source_type_id, dtd.rrd_step, dtr.rrd_maximum, dtr.rrd_heartbeat FROM plugin_thold_daemon_data AS tdd INNER JOIN thold_data AS td ON td.id = tdd.id LEFT JOIN data_template_rrd AS dtr ON dtr.id = td.data_template_rrd_id LEFT JOIN data_template_data AS dtd ON dtd.local_data_id = td.local_data_id WHERE td.thread_id = ? AND tdd.poller_id = ? AND tdd.time <= FROM_UNIXTIME(?) AND dtr.data_source_name != ''"; $tholds = db_fetch_assoc_prepared($sql_query, [$thread, $config['poller_id'], $start_time]); } else { $sql_query = "SELECT tdd.id, tdd.rrd_reindexed, tdd.rrd_time_reindexed, td.id AS thold_id, td.name_cache AS thold_name, td.local_graph_id, td.percent_ds, td.expression, td.upper_ds, td.data_type, td.cdef, td.local_data_id, td.data_template_rrd_id, td.lastread, UNIX_TIMESTAMP(td.lasttime) AS lasttime, td.oldvalue, dtr.data_source_name AS name, dtr.data_source_type_id, dtd.rrd_step, dtr.rrd_maximum, dtr.rrd_heartbeat FROM plugin_thold_daemon_data AS tdd INNER JOIN thold_data AS td ON td.id = tdd.id LEFT JOIN data_template_rrd AS dtr ON dtr.id = td.data_template_rrd_id LEFT JOIN data_template_data AS dtd ON dtd.local_data_id = td.local_data_id WHERE td.thread_id = ? AND tdd.time <= FROM_UNIXTIME(?) AND dtr.data_source_name != ''"; $tholds = db_fetch_assoc_prepared($sql_query, [$thread, $start_time]); } return $tholds; } /** * Checks whether the current database connection handle is a live, * usable connection by attempting a trivial query against it, ignoring * any resulting error via a temporary error handler swap. * * @return bool True if the connection is a valid object and the test * query succeeds, false otherwise. * * @global object $cnn_id The current database connection handle. */ function thold_db_connection() { global $cnn_id; if (is_object($cnn_id)) { // Avoid showing errors restore_error_handler(); set_error_handler('thold_error_handler'); $cacti_version = db_fetch_cell('SELECT cacti FROM version'); // Restore Cacti's Error handler restore_error_handler(); set_error_handler('CactiErrorHandler'); return is_null($cacti_version) ? false : true; } return false; } /** * Re-establishes a fresh database connection for a worker process, * closing the given (presumably stale/dead) connection handle first if * it's a valid object, then reloading Cacti's config.php to obtain * current connection credentials and reconnecting. * * @param object|null $cnn_id The existing (possibly stale) database * connection handle to close before * reconnecting (default null). * * @return object|resource The new database connection handle. */ function thold_db_reconnect($cnn_id = null) { chdir(__DIR__); include('../../include/config.php'); if (is_object($cnn_id)) { db_close($cnn_id); } // Avoid showing errors restore_error_handler(); set_error_handler('thold_error_handler'); // Connect to the database server $cnn_id = db_connect_real($database_hostname, $database_username, $database_password, $database_default, $database_type, $database_port, $database_ssl); // Restore Cacti's Error handler restore_error_handler(); set_error_handler('CactiErrorHandler'); return $cnn_id; } /** * Prints a timestamped debug message directly to stdout when global * debug mode is enabled. * * @param string $string The message to print. * * @return void * * @global bool $debug Whether debug output is enabled. */ function thold_cli_debug($string) { global $debug; if ($debug) { $output = date('Y-m-d H:i:s') . ' DEBUG: ' . trim($string); print $output . PHP_EOL; } } /** * Prints this script's name, version, and copyright banner to stdout. * * @return void * * @global array $config Cacti global configuration array; used to * locate setup.php when plugin_thold_version() * isn't already loaded. */ function display_version() { global $config; if (!function_exists('plugin_thold_version')) { include_once($config['base_path'] . '/plugins/thold/setup.php'); } $info = plugin_thold_version(); print 'Threshold Processor, Version ' . $info['version'] . ', ' . COPYRIGHT_YEARS . PHP_EOL; } /** * Prints this script's version banner followed by its command-line * usage instructions to stdout. * * @return void */ function display_help() { display_version(); print PHP_EOL . 'usage: thold_process.php --thread=N [--debug]' . PHP_EOL . PHP_EOL; print 'The main Threshold Processor for the Thold Plugin.' . PHP_EOL; }