$args
* @return never
*/
public function __call($name, $args) {
throw new \RuntimeException('PHP_TESTING DB sentinel called: ' . $name);
}
}
}
// Helper for "is this a real DB handle" checks that must reject the sentinel.
// Defined inline so include/global.php remains self-contained.
if (!function_exists('_cacti_is_real_db_conn')) {
function _cacti_is_real_db_conn(mixed $x): bool {
return is_object($x) && !($x instanceof Cacti_TestDbSentinel);
}
}
// Resolve the test-bootstrap predicate once; both PHP_TESTING and
// CACTI_TEST_BOOTSTRAP=1 must be set for the sentinel branches to engage.
$is_test_bootstrap = cacti_is_test_bootstrap();
// set poller mode
global $local_db_cnn_id, $remote_db_cnn_id, $conn_mode;
$config['connection'] = 'online';
$ps = $config['is_web'] ? '' : '';
$sp = $config['is_web'] ? '
' : PHP_EOL;
$ul = $config['is_web'] ? '' : PHP_EOL;
$li = $config['is_web'] ? '- ' : PHP_EOL . ' - ';
$lu = $config['is_web'] ? '
' : '';
$il = $config['is_web'] ? '' : '';
/* Git checkouts and interrupted upgrades do not ship include/vendor. Without
this check the autoload require below produces a bare fatal error. */
// The installer can repair a missing vendor tree before application classes
// are loaded. All normal entry points still fail closed with an actionable
// message rather than emitting a raw require() fatal.
$vendor_autoload = CACTI_PATH_INCLUDE . '/vendor/autoload.php';
if (!is_file($vendor_autoload) && !defined('IN_CACTI_INSTALL')) {
print $ps . 'FATAL: The include/vendor directory is not populated. Cacti requires its Composer dependencies before it can start. From the Cacti directory, run:' . $sp;
print $ps . 'composer install' . $sp;
print $ps . 'Then reload this page. See the README for details.' . $sp;
exit(1);
}
if ($config['poller_id'] > 1 || isset($rdatabase_hostname)) {
if (!$is_test_bootstrap) {
$local_db_cnn_id = db_connect_real($database_hostname, $database_username, $database_password, $database_default, $database_type, $database_port, $database_retries, $database_ssl, $database_ssl_key, $database_ssl_cert, $database_ssl_ca, $database_ssl_capath, $database_ssl_verify_server_cert);
} else {
$local_db_cnn_id = new Cacti_TestDbSentinel();
}
if (!isset($rdatabase_retries)) {
$rdatabase_retries = 2;
}
if (!isset($rdatabase_ssl)) {
$rdatabase_ssl = false;
}
if (!isset($rdatabase_ssl_key)) {
$rdatabase_ssl_key = false;
}
if (!isset($rdatabase_ssl_cert)) {
$rdatabase_ssl_cert = false;
}
if (!isset($rdatabase_ssl_ca)) {
$rdatabase_ssl_ca = false;
}
if (!isset($rdatabase_ssl_capath)) {
$rdatabase_ssl_capath = false;
}
if (!isset($rdatabase_ssl_verify_server_cert)) {
$rdatabase_ssl_verify_server_cert = true;
}
// Check for recovery
if (_cacti_is_real_db_conn($local_db_cnn_id)) {
$boost_records = db_fetch_cell('SELECT COUNT(*)
FROM poller_output_boost', '', true, $local_db_cnn_id);
if ($boost_records > 0) {
$config['connection'] = 'recovery';
}
}
// gather the existing cactidb version (skip when running under the test sentinel).
// Seed '' first so the key is always set, matching pre-sentinel behavior where a
// failed db_fetch_cell returned ''; a remote poller with the local DB down then
// reads a defined value instead of raising an 'Undefined array key' warning.
$config['cacti_db_version'] = '';
if (_cacti_is_real_db_conn($local_db_cnn_id)) {
$config['cacti_db_version'] = db_fetch_cell('SELECT cacti FROM version LIMIT 1', '', false, $local_db_cnn_id);
}
/**
* If we have not been forced offline by the $conn_mode global and since we are
* a remote poller, let's attempt to get back online.
*/
if ($conn_mode != 'offline') {
if (!$is_test_bootstrap) {
$remote_db_cnn_id = db_connect_real($rdatabase_hostname, $rdatabase_username, $rdatabase_password, $rdatabase_default, $rdatabase_type, $rdatabase_port, $database_retries, $rdatabase_ssl, $rdatabase_ssl_key, $rdatabase_ssl_cert, $rdatabase_ssl_ca, $rdatabase_ssl_capath, $rdatabase_ssl_verify_server_cert);
} else {
$remote_db_cnn_id = new Cacti_TestDbSentinel();
}
}
if ($config['is_web'] && _cacti_is_real_db_conn($remote_db_cnn_id) && $config['connection'] != 'recovery' && $config['cacti_db_version'] != 'new_install' && !defined('IN_CACTI_INSTALL')) {
// Connection worked, so now override the default settings so that it will always utilize the remote connection
$database_default = $rdatabase_default;
$database_hostname = $rdatabase_hostname;
$database_username = $rdatabase_username;
$database_password = $rdatabase_password;
$database_port = $rdatabase_port;
$database_ssl = $rdatabase_ssl;
$database_ssl_key = $rdatabase_ssl_key;
$database_ssl_cert = $rdatabase_ssl_cert;
$database_ssl_ca = $rdatabase_ssl_ca;
$database_ssl_capath = $rdatabase_ssl_capath;
$database_ssl_verify_server_cert = $rdatabase_ssl_verify_server_cert;
} elseif (_cacti_is_real_db_conn($remote_db_cnn_id)) {
if ($config['connection'] != 'recovery') {
$config['connection'] = 'online';
}
} else {
$config['connection'] = 'offline';
}
} else {
if (!$is_test_bootstrap) {
if (!db_connect_real($database_hostname, $database_username, $database_password, $database_default, $database_type, $database_port, $database_retries, $database_ssl, $database_ssl_key, $database_ssl_cert, $database_ssl_ca, $database_ssl_capath, $database_ssl_verify_server_cert)) {
print $ps . 'FATAL: Connection to Cacti database failed. Please ensure: ' . $ul;
print $li . 'the PHP MySQL module is installed and enabled.' . $il;
print $li . 'the database is running.' . $il;
print $li . 'the credentials in config.php are valid.' . $il;
print $lu . $sp;
if (isrv('display_db_errors') && !empty($config['DATABASE_ERROR'])) { // @phpstan-ignore-line
print $ps . 'The following database errors occurred: ' . $ul;
foreach ($config['DATABASE_ERROR'] as $e) { // @phpstan-ignore-line
print $li . $e['Code'] . ': ' . $e['Error'] . $il;
}
print $lu . $sp;
}
exit;
}
} else {
$local_db_cnn_id = new Cacti_TestDbSentinel();
}
if (!$is_test_bootstrap) {
if (!db_table_exists('settings') || !db_table_exists('version')) {
print $ps . 'FATAL: Connection to Cacti database succeeded but `settings` table not found. Please ensure: ' . $ul;
print $li . 'the PHP MySQL module is installed and enabled.' . $il;
print $li . 'the database is running.' . $il;
print $li . 'the cacti.sql has been imported.' . $il;
print $li . 'the credentials in config.php are valid and correct.' . $il;
print $lu . $sp;
if (isrv('display_db_errors') && !empty($config['DATABASE_ERROR'])) { // @phpstan-ignore-line
print $ps . 'The following database errors occurred: ' . $ul;
foreach ($config['DATABASE_ERROR'] as $e) { // @phpstan-ignore-line
print $li . $e['Code'] . ': ' . $e['Error'] . $il;
}
print $lu . $sp;
}
exit;
}
}
// gather the existing cactidb version (skip when running under the test sentinel).
// Seed '' first so the key is always set even under the test bootstrap.
$config['cacti_db_version'] = '';
if (!$is_test_bootstrap) {
$config['cacti_db_version'] = db_fetch_cell('SELECT cacti FROM version LIMIT 1');
}
}
define('CACTI_CONNECTION', $config['connection']);
// check cacti log is available
$log_filename = cacti_log_file();
if (!is_resource_writable($log_filename)) {
print $ps . 'FATAL: System log file is not available for writing. Please ensure: ' . $ul;
print $li . 'the log folder is correctly set.' . $il;
if (CACTI_CLI) {
print $li . 'the script was run as the website user. ' . $il;
}
print $li . 'the log folder is writable by the website user.' . $il;
print $li . 'there is enough disk space.' . $il;
print $lu . $sp;
if (CACTI_CLI) {
print $ps . 'To run as the website user, use sudo -u php -q