mirror of
https://github.com/Cacti/cacti.git
synced 2026-10-23 03:27:02 +09:00
fix(installer): preserve upgrade state and propagate failures (#8266)
* fix(installer): preserve upgrade state and propagate failures Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * docs(changelog): record installer recovery fixes for #8266 Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * test(security): refresh installer sink inventories Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix(installer): retain merge precedence for browser parameters Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix(installer): defer debug settings until the process lock is held Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix(installer): enforce table conversion failures and postconditions Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * test(installer): refresh SQL audit baseline after clause refactor Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> --------- Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
This commit is contained in:
1 parent
8ebc7e8a1a
commit
06c32ab13f
15 files changed
+445
-162
No files matched your search
@@ -0,0 +1 @@
|
||||
Preserve authentication migration data and retryable upgrade versions on failures, propagate CLI and Composer errors, correct table conversion preconditions and engine/row-format changes, preserve requested CLI debug levels, and serialize installer settings changes across CLI and background workers
|
||||
+14
-8
@@ -110,7 +110,7 @@ if (cacti_sizeof($parms)) {
|
||||
break;
|
||||
case '--installer':
|
||||
$installer = true;
|
||||
require_once(__DIR__ . '../install/functions.php');
|
||||
require_once(__DIR__ . '/../install/functions.php');
|
||||
|
||||
break;
|
||||
case '--version':
|
||||
@@ -146,7 +146,7 @@ if (!($innodb || $utf8 || $latin)) {
|
||||
print_or_log($installer, 'ERROR: Must select either UTF8, LATIN1 or InnoDB conversion.' . PHP_EOL . PHP_EOL);
|
||||
display_help();
|
||||
|
||||
exit;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (!$local) {
|
||||
@@ -198,7 +198,7 @@ if ($innodb) {
|
||||
if (cacti_strtolower($file_per_table['Value']) != 'on') {
|
||||
print_or_log($installer, 'innodb_file_per_table not enabled');
|
||||
|
||||
exit;
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -232,6 +232,7 @@ if (cacti_sizeof($tables)) {
|
||||
$canInnoDB = true;
|
||||
} elseif ($table_data['ENGINE'] == 'Aria') {
|
||||
$canConvert = true;
|
||||
$canInnoDB = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -259,19 +260,24 @@ if (cacti_sizeof($tables)) {
|
||||
if ($table_data['TABLE_ROWS'] < $size || $force) {
|
||||
print_or_log($installer, "Converting Table > '$table'");
|
||||
|
||||
$sql = '';
|
||||
$clauses = [];
|
||||
|
||||
if ($dynamic) {
|
||||
$clauses[] = 'ROW_FORMAT=Dynamic';
|
||||
}
|
||||
|
||||
if ($utf8) {
|
||||
$sql .= ' CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci';
|
||||
$clauses[] = 'CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci';
|
||||
} elseif ($latin) {
|
||||
$sql .= ' CONVERT TO CHARACTER SET latin1';
|
||||
$clauses[] = 'CONVERT TO CHARACTER SET latin1';
|
||||
}
|
||||
|
||||
if ($innodb && $canInnoDB) {
|
||||
$sql .= (strlen($sql) ? ',' : '') . ' ENGINE=Innodb';
|
||||
$clauses[] = 'ENGINE=InnoDB';
|
||||
}
|
||||
|
||||
$status = db_execute("ALTER TABLE `$table`" . ($dynamic ? ' ROW_FORMAT=Dynamic, ' : '') . $sql);
|
||||
$sql = implode(', ', $clauses);
|
||||
$status = db_execute("ALTER TABLE `$table` $sql");
|
||||
|
||||
if ($status === false) {
|
||||
$conversion_failed = true;
|
||||
|
||||
+105
-86
@@ -34,6 +34,7 @@ $options = ['Runtime' => 'Cli'];
|
||||
|
||||
$should_install = false;
|
||||
$force_install = false;
|
||||
$log_options = [];
|
||||
|
||||
display_version();
|
||||
|
||||
@@ -73,10 +74,10 @@ if (cacti_sizeof($parms)) {
|
||||
if ($tmplevel !== false) {
|
||||
$level = $tmplevel;
|
||||
} else {
|
||||
$level = log_install_level($logname, POLLER_VERBOSITY_DEBUG) + 1;
|
||||
$level = ($log_options[$logname] ?? log_install_level($logname, POLLER_VERBOSITY_DEBUG)) + 1;
|
||||
}
|
||||
$level = log_install_level_sanitize($level);
|
||||
set_config_option($logname, $level);
|
||||
$level = log_install_level_sanitize($level);
|
||||
$log_options[$logname] = $level;
|
||||
|
||||
break;
|
||||
case '--version':
|
||||
@@ -197,98 +198,114 @@ if (cacti_sizeof($parms)) {
|
||||
}
|
||||
}
|
||||
|
||||
db_execute("DELETE FROM settings WHERE name like 'log_install%' or name = 'install_eula'");
|
||||
|
||||
include_once(CACTI_PATH_LIBRARY . '/api_automation.php');
|
||||
include_once(CACTI_PATH_LIBRARY . '/api_automation_tools.php');
|
||||
include_once(CACTI_PATH_LIBRARY . '/api_data_source.php');
|
||||
include_once(CACTI_PATH_LIBRARY . '/api_device.php');
|
||||
include_once(CACTI_PATH_LIBRARY . '/data_query.php');
|
||||
include_once(CACTI_PATH_LIBRARY . '/import.php');
|
||||
include_once(CACTI_PATH_LIBRARY . '/installer.php');
|
||||
require_once(CACTI_PATH_LIBRARY . '/poller.php');
|
||||
include_once(CACTI_PATH_LIBRARY . '/utility.php');
|
||||
|
||||
$options['Step'] = Installer::STEP_INSTALL_CONFIRM;
|
||||
if (!register_process_start('install', 'master', 0, 86400)) {
|
||||
print 'An Installation was already in progress' . PHP_EOL;
|
||||
|
||||
$results = ['Step' => $options['Step']];
|
||||
$update_char = 'o';
|
||||
|
||||
debug_install_array('Options', $options);
|
||||
$installer = new Installer($options);
|
||||
$results = $installer->jsonSerialize();
|
||||
debug_install_array('Result', $results);
|
||||
|
||||
process_install_errors($results);
|
||||
|
||||
$install_mode = 'no';
|
||||
|
||||
switch ($installer->getMode()) {
|
||||
case Installer::MODE_INSTALL:
|
||||
$install_mode = 'INSTALL CORE';
|
||||
|
||||
break;
|
||||
case Installer::MODE_POLLER:
|
||||
$install_mode = 'INSTALL POLLER';
|
||||
|
||||
break;
|
||||
case Installer::MODE_UPGRADE:
|
||||
$install_mode = 'UPGRADE';
|
||||
|
||||
break;
|
||||
case Installer::MODE_DOWNGRADE:
|
||||
$install_mode = 'DOWNGRADE';
|
||||
|
||||
break;
|
||||
exit(1);
|
||||
}
|
||||
log_install_always('cli', 'Installer prepared for ' . $install_mode . ' action');
|
||||
|
||||
$message = '';
|
||||
try {
|
||||
db_execute("DELETE FROM settings WHERE name like 'log_install%' or name = 'install_eula'");
|
||||
|
||||
if ($installer->getStep() == Installer::STEP_INSTALL_CONFIRM && $should_install) {
|
||||
$time = '';
|
||||
|
||||
if ($force_install) {
|
||||
$time = '-b';
|
||||
foreach ($log_options as $logname => $level) {
|
||||
set_config_option($logname, $level);
|
||||
}
|
||||
log_install_always('cli', 'Starting installation...');
|
||||
Installer::beginInstall($time, $installer);
|
||||
log_install_always('cli', 'Finished installation...');
|
||||
|
||||
include_once(CACTI_PATH_LIBRARY . '/api_automation.php');
|
||||
include_once(CACTI_PATH_LIBRARY . '/api_automation_tools.php');
|
||||
include_once(CACTI_PATH_LIBRARY . '/api_data_source.php');
|
||||
include_once(CACTI_PATH_LIBRARY . '/api_device.php');
|
||||
include_once(CACTI_PATH_LIBRARY . '/data_query.php');
|
||||
include_once(CACTI_PATH_LIBRARY . '/import.php');
|
||||
include_once(CACTI_PATH_LIBRARY . '/installer.php');
|
||||
include_once(CACTI_PATH_LIBRARY . '/utility.php');
|
||||
|
||||
$options['Step'] = Installer::STEP_INSTALL_CONFIRM;
|
||||
|
||||
$results = ['Step' => $options['Step']];
|
||||
$update_char = 'o';
|
||||
|
||||
debug_install_array('Options', $options);
|
||||
$installer = new Installer($options);
|
||||
$results = $installer->jsonSerialize();
|
||||
debug_install_array('Result', $results);
|
||||
|
||||
process_install_errors($results);
|
||||
|
||||
$install_mode = 'no';
|
||||
|
||||
switch ($installer->getMode()) {
|
||||
case Installer::MODE_INSTALL:
|
||||
$install_mode = 'INSTALL CORE';
|
||||
|
||||
break;
|
||||
case Installer::MODE_POLLER:
|
||||
$install_mode = 'INSTALL POLLER';
|
||||
|
||||
break;
|
||||
case Installer::MODE_UPGRADE:
|
||||
$install_mode = 'UPGRADE';
|
||||
|
||||
break;
|
||||
case Installer::MODE_DOWNGRADE:
|
||||
$install_mode = 'DOWNGRADE';
|
||||
|
||||
break;
|
||||
}
|
||||
log_install_always('cli', 'Installer prepared for ' . $install_mode . ' action');
|
||||
|
||||
$message = '';
|
||||
|
||||
if ($installer->getStep() == Installer::STEP_INSTALL_CONFIRM && $should_install) {
|
||||
$time = '';
|
||||
|
||||
if ($force_install) {
|
||||
$time = '-b';
|
||||
}
|
||||
log_install_always('cli', 'Starting installation...');
|
||||
|
||||
Installer::beginInstall($time, $installer);
|
||||
log_install_always('cli', 'Finished installation...');
|
||||
}
|
||||
|
||||
$step = $installer->getStep();
|
||||
$exitCode = 0;
|
||||
log_install_high('cli','getStep(): ' . $step);
|
||||
|
||||
switch ($installer->getStep()) {
|
||||
case Installer::STEP_INSTALL:
|
||||
log_install_always('cli', 'An Installation was already in progress');
|
||||
$exitCode = 1;
|
||||
|
||||
break;
|
||||
case Installer::STEP_INSTALL_CONFIRM:
|
||||
log_install_always('cli', 'No errors were detected. Install not performed as --install not specified');
|
||||
|
||||
break;
|
||||
case Installer::STEP_ERROR:
|
||||
log_install_always('cli', 'One or more errors occurred during install, please refer to log files');
|
||||
process_install_errors(['Errors'=>$installer->getErrors()]);
|
||||
$exitCode = 1;
|
||||
|
||||
break;
|
||||
case Installer::STEP_COMPLETE:
|
||||
log_install_always('cli', 'Installation has now completed, you may launch the web console');
|
||||
|
||||
break;
|
||||
default:
|
||||
log_install_always('cli', 'Unexpected step (' . $installer->getStep() . ')');
|
||||
$exitCode = 1;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
print PHP_EOL;
|
||||
} finally {
|
||||
unregister_process('install', 'master', 0);
|
||||
}
|
||||
|
||||
$step = $installer->getStep();
|
||||
$exitCode = 0;
|
||||
log_install_high('cli','getStep(): ' . $step);
|
||||
|
||||
switch ($installer->getStep()) {
|
||||
case Installer::STEP_INSTALL:
|
||||
log_install_always('cli', 'An Installation was already in progress');
|
||||
$exitCode = 1;
|
||||
|
||||
break;
|
||||
case Installer::STEP_INSTALL_CONFIRM:
|
||||
log_install_always('cli', 'No errors were detected. Install not performed as --install not specified');
|
||||
|
||||
break;
|
||||
case Installer::STEP_ERROR:
|
||||
log_install_always('cli', 'One or more errors occurred during install, please refer to log files');
|
||||
process_install_errors(['Errors'=>$installer->getErrors()]);
|
||||
$exitCode = 1;
|
||||
|
||||
break;
|
||||
case Installer::STEP_COMPLETE:
|
||||
log_install_always('cli', 'Installation has now completed, you may launch the web console');
|
||||
|
||||
break;
|
||||
default:
|
||||
log_install_always('cli', 'Unexpected step (' . $installer->getStep() . ')');
|
||||
$exitCode = 1;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
print PHP_EOL;
|
||||
|
||||
exit($exitCode);
|
||||
|
||||
/**
|
||||
@@ -443,6 +460,8 @@ function process_install_errors(array $results) : void {
|
||||
|
||||
print PHP_EOL . 'Unable to continue as ' . $count . ' issue' . ($count == 1 ? '' : 's') . ' in ' . $sections . ' section' . ($sections == 1 ? '' : 's') . ' were found.' . PHP_EOL;
|
||||
|
||||
// exit does not execute the caller's finally block.
|
||||
unregister_process('install', 'master', 0);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
+62
-41
@@ -128,72 +128,93 @@ if (cacti_version_compare($old_cacti_version,CACTI_VERSION,'=')) {
|
||||
exit_version_error($old_cacti_version, 'Unable to identify version, cannot upgrade.');
|
||||
}
|
||||
|
||||
print 'Upgrading from v' . get_cacti_version_text(false, $old_cacti_version) . PHP_EOL;
|
||||
if (!register_process_start('install', 'master', 0, 86400)) {
|
||||
exit_error('An installation or upgrade is already in progress.');
|
||||
}
|
||||
|
||||
$prev_cacti_version = $old_cacti_version;
|
||||
$orig_cacti_version = get_cacti_db_version();
|
||||
$upgrade_failed = false;
|
||||
|
||||
// loop through versions from old version to the current, performing updates for each version in the chain
|
||||
foreach ($cacti_version_codes as $cacti_upgrade_version => $hash_code) {
|
||||
// skip versions old than the database version
|
||||
if (cacti_version_compare($old_cacti_version, $cacti_upgrade_version, '>=')) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
print 'Upgrading from v' . get_cacti_version_text(false, $old_cacti_version) . PHP_EOL;
|
||||
|
||||
// construct version upgrade include path
|
||||
$upgrade_file = CACTI_PATH_INSTALL . '/upgrades/' . str_replace('.', '_', $cacti_upgrade_version) . '.php';
|
||||
$upgrade_function = 'upgrade_to_' . str_replace('.', '_', $cacti_upgrade_version);
|
||||
$prev_cacti_version = $old_cacti_version;
|
||||
$orig_cacti_version = get_cacti_db_version();
|
||||
|
||||
// check for upgrade version file, then include, check for function and execute
|
||||
if (file_exists($upgrade_file)) {
|
||||
print 'Performing Database Upgrade' . PHP_EOL;
|
||||
print ' - from v' . $prev_cacti_version . ' (DB ' . $orig_cacti_version . ')' . PHP_EOL;
|
||||
print ' to v' . $cacti_upgrade_version . PHP_EOL;
|
||||
include($upgrade_file);
|
||||
|
||||
if (function_exists($upgrade_function)) {
|
||||
call_user_func($upgrade_function);
|
||||
$status = db_install_errors($cacti_upgrade_version);
|
||||
} else {
|
||||
$status = DB_STATUS_ERROR;
|
||||
print 'Error: upgrade function (' . $upgrade_function . ') not found' . PHP_EOL;
|
||||
// loop through versions from old version to the current, performing updates for each version in the chain
|
||||
foreach ($cacti_version_codes as $cacti_upgrade_version => $hash_code) {
|
||||
// skip versions old than the database version
|
||||
if (cacti_version_compare($old_cacti_version, $cacti_upgrade_version, '>=')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($status == DB_STATUS_ERROR) {
|
||||
// construct version upgrade include path
|
||||
$upgrade_file = CACTI_PATH_INSTALL . '/upgrades/' . str_replace('.', '_', $cacti_upgrade_version) . '.php';
|
||||
$upgrade_function = 'upgrade_to_' . str_replace('.', '_', $cacti_upgrade_version);
|
||||
|
||||
// check for upgrade version file, then include, check for function and execute
|
||||
if (file_exists($upgrade_file)) {
|
||||
print 'Performing Database Upgrade' . PHP_EOL;
|
||||
print ' - from v' . $prev_cacti_version . ' (DB ' . $orig_cacti_version . ')' . PHP_EOL;
|
||||
print ' to v' . $cacti_upgrade_version . PHP_EOL;
|
||||
include($upgrade_file);
|
||||
|
||||
if (function_exists($upgrade_function)) {
|
||||
call_user_func($upgrade_function);
|
||||
$status = db_install_errors($cacti_upgrade_version);
|
||||
} else {
|
||||
$status = DB_STATUS_ERROR;
|
||||
print 'Error: upgrade function (' . $upgrade_function . ') not found' . PHP_EOL;
|
||||
}
|
||||
|
||||
if ($status <= DB_STATUS_WARNING) {
|
||||
$upgrade_failed = true;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (cacti_version_compare($orig_cacti_version, $cacti_upgrade_version, '<')) {
|
||||
if (!db_execute_prepared('UPDATE version SET cacti = ?', [$cacti_upgrade_version])) {
|
||||
$upgrade_failed = true;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
$orig_cacti_version = $cacti_upgrade_version;
|
||||
}
|
||||
|
||||
$prev_cacti_version = $cacti_upgrade_version;
|
||||
}
|
||||
|
||||
if (!db_execute_prepared('UPDATE version SET cacti = ?', [$cacti_upgrade_version])) {
|
||||
$upgrade_failed = true;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (cacti_version_compare($orig_cacti_version, $cacti_upgrade_version, '<')) {
|
||||
db_execute_prepared('UPDATE version SET cacti = ?', [$cacti_upgrade_version]);
|
||||
if (cacti_version_compare(CACTI_VERSION, $cacti_upgrade_version, '=')) {
|
||||
$upgrade_failed = !db_execute_prepared('UPDATE version SET cacti = ?', [CACTI_VERSION_FULL]);
|
||||
|
||||
$orig_cacti_version = $cacti_upgrade_version;
|
||||
break;
|
||||
}
|
||||
|
||||
$prev_cacti_version = $cacti_upgrade_version;
|
||||
}
|
||||
|
||||
db_execute_prepared('UPDATE version SET cacti = ?', [$cacti_upgrade_version]);
|
||||
|
||||
if (cacti_version_compare(CACTI_VERSION, $cacti_upgrade_version, '=')) {
|
||||
db_execute_prepared('UPDATE version SET cacti = ?', [CACTI_VERSION_FULL]);
|
||||
|
||||
break;
|
||||
}
|
||||
} finally {
|
||||
unregister_process('install', 'master', 0);
|
||||
}
|
||||
|
||||
print PHP_EOL;
|
||||
exit($upgrade_failed ? 1 : 0);
|
||||
|
||||
function exit_error(string $text) : void {
|
||||
print "ERROR: $text" . PHP_EOL;
|
||||
|
||||
exit;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
function exit_version_error(string $old_cacti_version, string $text) : void {
|
||||
exit_error($text . PHP_EOL . ' - from: v' . get_cacti_version_text(false, $old_cacti_version) . PHP_EOL . ' to: v' . CACTI_VERSION_BRIEF_FULL);
|
||||
}
|
||||
|
||||
function db_install_errors(string $cacti_version) : string {
|
||||
function db_install_errors(string $cacti_version) : int {
|
||||
global $database_upgrade_status, $debug, $database_statuses;
|
||||
|
||||
$error_status = DB_STATUS_SKIPPED;
|
||||
|
||||
+10
-1
@@ -29,6 +29,15 @@ ob_start();
|
||||
define('IN_CACTI_INSTALL', 1);
|
||||
chdir(__DIR__ . '/../');
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
http_response_code(405);
|
||||
header('Allow: POST');
|
||||
header('Content-Type: application/json');
|
||||
print json_encode(['error' => 'Method not allowed']);
|
||||
|
||||
exit;
|
||||
}
|
||||
|
||||
// set the json variable for request validation handling
|
||||
include_once('lib/functions.php');
|
||||
include_once('lib/html_utility.php');
|
||||
@@ -62,7 +71,7 @@ if (isrv('data') && gnrv('data')) {
|
||||
$json_level = log_install_level('json',POLLER_VERBOSITY_NONE);
|
||||
log_install_high('json','Start: ' . clean_up_lines(json_encode($initialData)));
|
||||
|
||||
$initialData = array_merge(['Runtime' => 'Web'], $initialData);
|
||||
$initialData = array_merge($initialData, ['Runtime' => 'Web']);
|
||||
|
||||
if (isset($initialData['step']) && $initialData['step'] == Installer::STEP_TEST_REMOTE) {
|
||||
$json = install_test_remote_database_connection();
|
||||
|
||||
@@ -951,20 +951,31 @@ function login_providers_convert_1_3_0() : void {
|
||||
return;
|
||||
}
|
||||
|
||||
$domains = db_fetch_assoc('SELECT * FROM user_domains');
|
||||
$domains = is_array($domains) ? $domains : [];
|
||||
$result = db_install_fetch_assoc('SELECT * FROM user_domains');
|
||||
|
||||
if ($result['status'] !== DB_STATUS_SUCCESS || !is_array($result['data'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$domains = $result['data'];
|
||||
$migration_failed = false;
|
||||
|
||||
foreach ($domains as $domain) {
|
||||
$parameters = [];
|
||||
|
||||
if (db_table_exists('user_domains_ldap')) {
|
||||
$ldap = db_fetch_row_prepared('SELECT *
|
||||
$result = db_install_fetch_row('SELECT *
|
||||
FROM user_domains_ldap
|
||||
WHERE domain_id = ?',
|
||||
[$domain['domain_id']]);
|
||||
|
||||
$ldap = is_array($ldap) ? $ldap : [];
|
||||
if ($result['status'] !== DB_STATUS_SUCCESS || !is_array($result['data'])) {
|
||||
$migration_failed = true;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$ldap = $result['data'];
|
||||
|
||||
if (cacti_sizeof($ldap)) {
|
||||
$parameters = [
|
||||
@@ -1031,9 +1042,9 @@ function login_providers_convert_1_3_0() : void {
|
||||
return;
|
||||
}
|
||||
|
||||
$migrated_count = db_fetch_cell('SELECT COUNT(*) FROM login_providers');
|
||||
$result = db_install_fetch_cell('SELECT COUNT(*) FROM login_providers');
|
||||
|
||||
if ((int) $migrated_count < cacti_sizeof($domains)) {
|
||||
if ($result['status'] !== DB_STATUS_SUCCESS || (int) $result['data'] < cacti_sizeof($domains)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
+17
-10
@@ -3666,11 +3666,11 @@ class Installer implements JsonSerializable {
|
||||
putenv('COMPOSER_HOME=' . sys_get_temp_dir() . '/cacti-composer');
|
||||
}
|
||||
|
||||
$composer = cacti_escapeshellcmd(str_replace('\\', '/', $composer));
|
||||
$base = cacti_escapeshellarg(CACTI_PATH_BASE);
|
||||
$check = shell_exec("$composer install --dry-run --no-interaction --working-dir=$base 2>&1");
|
||||
$command = [$composer, 'install', '--no-interaction', '--working-dir=' . CACTI_PATH_BASE];
|
||||
$result = $this->runCommand([...$command, '--dry-run']);
|
||||
$check = implode(PHP_EOL, $result['output']);
|
||||
|
||||
if ($check === null) {
|
||||
if ($result['exitCode'] !== 0) {
|
||||
log_install_always('', __('WARNING: Composer could not be executed. Run \'composer install\' from the Cacti directory manually and check its output.'));
|
||||
|
||||
return;
|
||||
@@ -3711,13 +3711,13 @@ class Installer implements JsonSerializable {
|
||||
|
||||
log_install_always('', __('Refreshing Composer dependencies in include/vendor.'));
|
||||
|
||||
$output = shell_exec("$composer install --no-interaction --no-progress --working-dir=$base 2>&1");
|
||||
$result = $this->runCommand([...$command, '--no-progress']);
|
||||
|
||||
if ($output !== null) {
|
||||
log_install_debug('composer', $output);
|
||||
if ($result['output'] !== []) {
|
||||
log_install_debug('composer', implode(PHP_EOL, $result['output']));
|
||||
}
|
||||
|
||||
if (!is_file(CACTI_PATH_INCLUDE . '/vendor/autoload.php')) {
|
||||
if ($result['exitCode'] !== 0 || !is_file(CACTI_PATH_INCLUDE . '/vendor/autoload.php')) {
|
||||
log_install_always('', __('WARNING: Composer refresh did not complete. Run \'composer install\' from the Cacti directory manually.'));
|
||||
} else {
|
||||
log_install_always('', __('Composer dependencies refreshed.'));
|
||||
@@ -4237,7 +4237,14 @@ class Installer implements JsonSerializable {
|
||||
return ['exitCode' => 127, 'output' => [__('Unable to execute the configured PHP binary or CLI script')]];
|
||||
}
|
||||
|
||||
$command = [$phpBinary, '-q', $script, ...$arguments];
|
||||
return $this->runCommand([$phpBinary, '-q', $script, ...$arguments]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param list<string> $command
|
||||
* @return array{exitCode: int, output: array<int, string>}
|
||||
*/
|
||||
private function runCommand(array $command) : array {
|
||||
$pipes = [];
|
||||
$errorFile = tmpfile();
|
||||
|
||||
@@ -4393,7 +4400,7 @@ class Installer implements JsonSerializable {
|
||||
$failure = $ver_status;
|
||||
}
|
||||
|
||||
if ($failure == DB_STATUS_ERROR) {
|
||||
if ($failure <= DB_STATUS_WARNING) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
/*
|
||||
+-------------------------------------------------------------------------+
|
||||
| Copyright (C) 2004-2026 The Cacti Group |
|
||||
+-------------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
require_once CACTI_PATH_INSTALL . '/functions.php';
|
||||
require_once CACTI_PATH_LIBRARY . '/installer.php';
|
||||
|
||||
test('installer commands preserve failed exit status and both output streams', function () {
|
||||
$installer = (new ReflectionClass(Installer::class))->newInstanceWithoutConstructor();
|
||||
$result = (new ReflectionMethod(Installer::class, 'runCommand'))->invoke($installer, [
|
||||
PHP_BINARY,
|
||||
'-r',
|
||||
'fwrite(STDOUT, $argv[1] . PHP_EOL); fwrite(STDERR, "download failed\n"); exit(7);',
|
||||
'literal argument; $(echo unexpected)',
|
||||
]);
|
||||
|
||||
expect($result['exitCode'])->toBe(7)
|
||||
->and($result['output'])->toContain('literal argument; $(echo unexpected)', 'download failed');
|
||||
});
|
||||
|
||||
test('installer rejects unsupported request methods before loading application state', function () {
|
||||
$installer = (new ReflectionClass(Installer::class))->newInstanceWithoutConstructor();
|
||||
$result = (new ReflectionMethod(Installer::class, 'runCommand'))->invoke($installer, [
|
||||
PHP_BINARY,
|
||||
'-r',
|
||||
'$_SERVER["REQUEST_METHOD"] = "GET"; register_shutdown_function(function () { echo " status=" . http_response_code(); }); require $argv[1];',
|
||||
CACTI_PATH_INSTALL . '/step_json.php',
|
||||
]);
|
||||
|
||||
expect($result['exitCode'])->toBe(0)
|
||||
->and(implode(PHP_EOL, $result['output']))->toContain('Method not allowed', 'status=405');
|
||||
});
|
||||
@@ -119,7 +119,7 @@ test('installer command-line adapters propagate failures and release locks', fun
|
||||
->and($installer)->toContain("['bypass_shell' => true]")
|
||||
->and($import)->toContain('if (file_exists($filename) && !unlink($filename)) {')
|
||||
->and($utility)->toContain('is_file($config_file) && is_readable($config_file)')
|
||||
->and(substr_count($installer, 'shell_exec('))->toBe(4)
|
||||
->and(substr_count($installer, 'shell_exec('))->toBe(2)
|
||||
->and($installer)->not->toContain("cacti_escapeshellarg(CACTI_PATH_CLI . '/add_tree.php')")
|
||||
->and($installer)->not->toContain("cacti_escapeshellarg(CACTI_PATH_CLI . '/convert_tables.php')");
|
||||
});
|
||||
|
||||
@@ -44,7 +44,7 @@ test('successful installer json responses roll the csrf token forward', function
|
||||
->and($stepJson)->toContain("header('Cache-Control: no-store')")
|
||||
->and($stepJson)->toContain("header('X-Content-Type-Options: nosniff')")
|
||||
->and(strpos($stepJson, "\$response['csrfMagicToken'] = csrf_get_tokens();"))
|
||||
->toBeLessThan(strpos($stepJson, "header('Content-Type: application/json')"));
|
||||
->toBeLessThan(strrpos($stepJson, "header('Content-Type: application/json')"));
|
||||
});
|
||||
|
||||
test('installer retries csrf timeouts once for every json action', function () use ($root) {
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
<?php
|
||||
/*
|
||||
+-------------------------------------------------------------------------+
|
||||
| Copyright (C) 2004-2026 The Cacti Group |
|
||||
+-------------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
// This fixture mutates schema and migration files in a disposable Docker copy.
|
||||
if (getenv('CACTI_INSTALLER_FAILURE_FIXTURE') !== '1') {
|
||||
fwrite(STDERR, "Run through installer_resilience_docker.sh only.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
require __DIR__ . '/../../include/cli_check.php';
|
||||
require_once CACTI_PATH_INSTALL . '/functions.php';
|
||||
require_once CACTI_PATH_LIBRARY . '/installer.php';
|
||||
|
||||
class InstallerFailureStatement extends PDOStatement {
|
||||
public static string $failQuery = '';
|
||||
|
||||
public function execute(?array $params = null) : bool {
|
||||
$query = implode(' ', preg_split('/\s+/', trim($this->queryString)) ?: []);
|
||||
|
||||
if (self::$failQuery !== '' && str_starts_with($query, self::$failQuery)) {
|
||||
throw new PDOException('Injected installer fixture read failure', 1142);
|
||||
}
|
||||
|
||||
return parent::execute($params);
|
||||
}
|
||||
}
|
||||
|
||||
function installer_fixture_check(bool $condition, string $message) : void {
|
||||
if (!$condition) {
|
||||
throw new RuntimeException($message);
|
||||
}
|
||||
}
|
||||
|
||||
$scenario = $_SERVER['argv'][1] ?? '';
|
||||
$key = "$database_hostname:$database_port:$database_default";
|
||||
$pdo = $database_sessions[$key];
|
||||
$version = db_fetch_cell('SELECT cacti FROM version');
|
||||
$backups = [];
|
||||
|
||||
try {
|
||||
if (in_array($scenario, ['domain-read', 'ldap-read', 'count-read'], true)) {
|
||||
require_once CACTI_PATH_INSTALL . '/upgrades/1_3_0.php';
|
||||
$pdo->exec('CREATE TABLE user_domains (domain_id INT PRIMARY KEY, domain_name VARCHAR(64), type INT, enabled VARCHAR(2), debug VARCHAR(2), defdomain INT, user_id INT)');
|
||||
$pdo->exec("INSERT INTO user_domains VALUES (987, 'Installer fixture', 1, 'on', '', 0, 0)");
|
||||
$pdo->exec('CREATE TABLE user_domains_ldap (domain_id INT PRIMARY KEY)');
|
||||
|
||||
// LDAP reads fail before accessing the deliberately minimal fixture row.
|
||||
if ($scenario === 'ldap-read') {
|
||||
$pdo->exec('INSERT INTO user_domains_ldap VALUES (987)');
|
||||
}
|
||||
$pdo->setAttribute(PDO::ATTR_STATEMENT_CLASS, [InstallerFailureStatement::class]);
|
||||
InstallerFailureStatement::$failQuery = match ($scenario) {
|
||||
'domain-read' => 'SELECT * FROM user_domains',
|
||||
'ldap-read' => 'SELECT * FROM user_domains_ldap',
|
||||
'count-read' => 'SELECT COUNT(*) FROM login_providers',
|
||||
};
|
||||
$database_upgrade_status = [];
|
||||
$cacti_upgrade_version = '1.3.0';
|
||||
login_providers_convert_1_3_0();
|
||||
InstallerFailureStatement::$failQuery = '';
|
||||
installer_fixture_check(count($pdo->query("SHOW TABLES LIKE 'user_domains%'")->fetchAll()) === 2, 'Failed read destroyed legacy authentication tables');
|
||||
installer_fixture_check(in_array(DB_STATUS_ERROR, array_column($database_upgrade_status['1.3.0'] ?? [], 'status'), true), 'Failed read was not recorded in migration status');
|
||||
installer_fixture_check((int) $pdo->query('SELECT COUNT(*) FROM user_domains')->fetchColumn() === 1, 'Legacy authentication row was lost');
|
||||
} elseif (in_array($scenario, ['upgrade-warning', 'cli-failure'], true)) {
|
||||
foreach (['1_2_29', '1_2_30'] as $target) {
|
||||
$path = CACTI_PATH_INSTALL . '/upgrades/' . $target . '.php';
|
||||
$backups[$path] = is_file($path) ? file_get_contents($path) : false;
|
||||
}
|
||||
file_put_contents(CACTI_PATH_INSTALL . '/upgrades/1_2_29.php', '<?php /* Fixture: missing migration function. */');
|
||||
file_put_contents(CACTI_PATH_INSTALL . '/upgrades/1_2_30.php', '<?php function upgrade_to_1_2_30(): void { db_install_execute("SELECT 1"); }');
|
||||
db_execute("UPDATE version SET cacti = '1.2.28'");
|
||||
|
||||
if ($scenario === 'upgrade-warning') {
|
||||
$cacti_version_codes = array_intersect_key($cacti_version_codes, array_flip(['1.2.29', '1.2.30']));
|
||||
$reflection = new ReflectionClass(Installer::class);
|
||||
$installer = $reflection->newInstanceWithoutConstructor();
|
||||
$reflection->getProperty('old_cacti_version')->setValue($installer, '1.2.28');
|
||||
$error = $reflection->getMethod('upgradeDatabase')->invoke($installer);
|
||||
installer_fixture_check($error !== '', 'Incomplete migration reported success');
|
||||
} else {
|
||||
$output = [];
|
||||
exec(escapeshellarg(PHP_BINARY) . ' ' . escapeshellarg(CACTI_PATH_CLI . '/upgrade_database.php') . ' --forcever=1.2.28 2>&1', $output, $status);
|
||||
installer_fixture_check($status !== 0, 'CLI migration failure returned success');
|
||||
installer_fixture_check(str_contains(implode(PHP_EOL, $output), 'not found'), 'CLI fixture did not exercise the missing migration');
|
||||
}
|
||||
installer_fixture_check(db_fetch_cell('SELECT cacti FROM version') === '1.2.28', 'Incomplete migration advanced the recorded version');
|
||||
} elseif ($scenario === 'composer-failure') {
|
||||
$composer = tempnam(sys_get_temp_dir(), 'installer-composer-');
|
||||
installer_fixture_check($composer !== false, 'Could not create Composer fixture');
|
||||
file_put_contents($composer, '#!' . PHP_BINARY . PHP_EOL . '<?php if (in_array("--dry-run", $argv, true)) { echo "Package operations: 1 install, 0 updates, 0 removals\n"; exit(0); } echo "download failed\n"; exit(7);');
|
||||
chmod($composer, 0700);
|
||||
set_config_option('path_composer', $composer);
|
||||
$logPath = read_config_option('path_cactilog');
|
||||
|
||||
if ($logPath === '') {
|
||||
$logPath = CACTI_PATH_LOG . '/cacti.log';
|
||||
}
|
||||
$offset = is_file($logPath) ? filesize($logPath) : 0;
|
||||
$reflection = new ReflectionClass(Installer::class);
|
||||
$installer = $reflection->newInstanceWithoutConstructor();
|
||||
$reflection->getMethod('refreshVendorDependencies')->invoke($installer);
|
||||
$log = substr(file_get_contents($logPath), $offset);
|
||||
installer_fixture_check(str_contains($log, 'Composer refresh did not complete'), 'Failed Composer command did not log a warning');
|
||||
installer_fixture_check(!str_contains($log, 'Composer dependencies refreshed.'), 'Failed Composer command reported success');
|
||||
unlink($composer);
|
||||
} elseif ($scenario === 'installer-lock') {
|
||||
installer_fixture_check(register_process_start('install', 'master', 0, 86400), 'Could not register lock fixture');
|
||||
set_config_option('install_eula', 'lock-owner-fixture');
|
||||
set_config_option('log_install_json', '3');
|
||||
|
||||
try {
|
||||
$output = [];
|
||||
exec(escapeshellarg(PHP_BINARY) . ' ' . escapeshellarg(CACTI_PATH_CLI . '/upgrade_database.php') . ' --forcever=1.2.28 2>&1', $output, $status);
|
||||
installer_fixture_check($status !== 0 && str_contains(implode(PHP_EOL, $output), 'already in progress'), 'CLI upgrader ignored an active installer');
|
||||
$arguments = ['--accept-eula', '--install', '--force', '--debug=json:5', '--path=php_binary:' . PHP_BINARY, '--path=rrdtool:/usr/bin/rrdtool', '--path=snmpwalk:/usr/bin/snmpwalk', '--path=snmpget:/usr/bin/snmpget', '--path=snmpbulkwalk:/usr/bin/snmpbulkwalk', '--path=snmpgetnext:/usr/bin/snmpgetnext', '--path=fping:/usr/bin/fping'];
|
||||
$output = [];
|
||||
exec(escapeshellarg(PHP_BINARY) . ' ' . escapeshellarg(CACTI_PATH_CLI . '/install_cacti.php') . ' ' . implode(' ', array_map('escapeshellarg', $arguments)) . ' 2>&1', $output, $status);
|
||||
installer_fixture_check($status !== 0 && str_contains(implode(PHP_EOL, $output), 'already in progress'), 'CLI installer ignored an active installer');
|
||||
installer_fixture_check(db_fetch_cell("SELECT value FROM settings WHERE name = 'install_eula'") === 'lock-owner-fixture', 'Blocked CLI installer overwrote the active installer settings');
|
||||
installer_fixture_check(db_fetch_cell("SELECT value FROM settings WHERE name = 'log_install_json'") === '3', 'Blocked CLI installer overwrote the active installer logging settings');
|
||||
} finally {
|
||||
unregister_process('install', 'master', 0);
|
||||
}
|
||||
} else {
|
||||
throw new RuntimeException('Unknown fixture scenario');
|
||||
}
|
||||
|
||||
print "PASS: $scenario" . PHP_EOL;
|
||||
} finally {
|
||||
InstallerFailureStatement::$failQuery = '';
|
||||
|
||||
foreach ($backups as $path => $contents) {
|
||||
if ($contents === false) {
|
||||
unlink($path);
|
||||
} else {
|
||||
file_put_contents($path, $contents);
|
||||
}
|
||||
}
|
||||
db_execute_prepared('UPDATE version SET cacti = ?', [$version]);
|
||||
}
|
||||
@@ -167,8 +167,34 @@ EXPECTED_TABLES="$(grep -c '^CREATE TABLE' cacti.sql)"
|
||||
|
||||
printf 'Scenario: fresh CLI installation\n'
|
||||
reset_database "$REPO_DIR/cacti.sql"
|
||||
run_installer
|
||||
run_installer --debug=json:3
|
||||
assert_complete_install "$EXPECTED_TABLES"
|
||||
[ "$(db_cacti -e "SELECT value FROM settings WHERE name = 'log_install_json';")" = '3' ] || fail 'installer reset the requested debug level'
|
||||
|
||||
printf 'Scenario: table conversion rejects missing conversion options\n'
|
||||
if docker exec "$WEB_CONTAINER" php /var/www/html/cacti/cli/convert_tables.php; then
|
||||
fail 'table converter returned success without a conversion option'
|
||||
fi
|
||||
|
||||
printf 'Scenario: table conversion rejects disabled file-per-table\n'
|
||||
db_root -e 'SET GLOBAL innodb_file_per_table = OFF;'
|
||||
if docker exec "$WEB_CONTAINER" php /var/www/html/cacti/cli/convert_tables.php --innodb --table=host; then
|
||||
fail 'table converter returned success with file-per-table disabled'
|
||||
fi
|
||||
db_root -e 'SET GLOBAL innodb_file_per_table = ON;'
|
||||
|
||||
printf 'Scenario: installer logging mode loads the conversion helpers\n'
|
||||
docker exec "$WEB_CONTAINER" php /var/www/html/cacti/cli/convert_tables.php --installer --utf8 --table=host
|
||||
|
||||
printf 'Scenario: row-format-only conversion does not require a charset change\n'
|
||||
db_cacti -e 'CREATE TABLE installer_conversion_fixture (id int unsigned NOT NULL PRIMARY KEY) ENGINE=InnoDB ROW_FORMAT=Compact DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;'
|
||||
docker exec "$WEB_CONTAINER" php /var/www/html/cacti/cli/convert_tables.php --innodb --dynamic --table=installer_conversion_fixture
|
||||
[ "$(db_cacti -e "SELECT ROW_FORMAT FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'installer_conversion_fixture';")" = 'Dynamic' ] || fail 'row-format-only conversion did not convert the fixture table'
|
||||
|
||||
printf 'Scenario: Aria conversion reaches the requested InnoDB engine\n'
|
||||
db_cacti -e 'ALTER TABLE installer_conversion_fixture ENGINE=Aria;'
|
||||
docker exec "$WEB_CONTAINER" php /var/www/html/cacti/cli/convert_tables.php --innodb --utf8 --table=installer_conversion_fixture
|
||||
[ "$(db_cacti -e "SELECT ENGINE FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'installer_conversion_fixture';")" = 'InnoDB' ] || fail 'table converter left the fixture table using Aria'
|
||||
|
||||
printf 'Scenario: 1.2.22 to %s CLI upgrade\n' "$CACTI_VERSION"
|
||||
reset_database "$OLD_SCHEMA"
|
||||
@@ -195,6 +221,7 @@ VERSION_STATUS=$?
|
||||
set -e
|
||||
printf '%s\n' "$VERSION_OUTPUT"
|
||||
[ "$VERSION_STATUS" -ne 0 ] || fail 'installer ignored a rejected version write'
|
||||
[ "$(db_cacti -e "SELECT COUNT(*) FROM processes WHERE tasktype = 'install' AND taskname = 'master';")" -eq 0 ] || fail 'failed installer retained its process registration'
|
||||
[ "$(db_cacti -e 'SELECT cacti FROM version;')" = 'new_install' ] || fail 'failed version transaction did not roll back'
|
||||
|
||||
printf 'Scenario: incomplete schema fails closed in CLI mode\n'
|
||||
@@ -206,6 +233,7 @@ CLI_STATUS=$?
|
||||
set -e
|
||||
printf '%s\n' "$CLI_OUTPUT"
|
||||
[ "$CLI_STATUS" -ne 0 ] || fail 'CLI installer accepted an incomplete schema'
|
||||
[ "$(db_cacti -e "SELECT COUNT(*) FROM processes WHERE tasktype = 'install' AND taskname = 'master';")" -eq 0 ] || fail 'schema validation failure retained its process registration'
|
||||
grep -q 'user_auth_row_cache' <<< "$CLI_OUTPUT" || fail 'CLI failure did not identify the missing table'
|
||||
[ "$(db_cacti -e 'SELECT cacti FROM version;')" = 'new_install' ] || fail 'failed install advanced the version row'
|
||||
|
||||
@@ -224,4 +252,11 @@ printf '%s\n' "$BACKGROUND_OUTPUT"
|
||||
[ "$BACKGROUND_STATUS" -ne 0 ] || fail 'background installer accepted an incomplete schema'
|
||||
[ "$(db_cacti -e "SELECT value FROM settings WHERE name = 'install_step';")" = '99' ] || fail 'background failure did not preserve STEP_ERROR'
|
||||
|
||||
for scenario in domain-read ldap-read count-read upgrade-warning cli-failure composer-failure installer-lock; do
|
||||
printf 'Scenario: %s\n' "$scenario"
|
||||
reset_database "$REPO_DIR/cacti.sql"
|
||||
docker exec -e CACTI_INSTALLER_FAILURE_FIXTURE=1 "$WEB_CONTAINER" \
|
||||
php /var/www/html/cacti/tests/e2e/installer_failure_paths.php "$scenario"
|
||||
done
|
||||
|
||||
printf 'PASS installer unit/integration Docker matrix\n'
|
||||
@@ -44,8 +44,6 @@ cmd_exec ./lib/functions.php return exec('whoami') ?: '';
|
||||
cmd_exec ./lib/functions.php * On Windows, exec()/shell_exec()/popen() route through cmd.exe, which ignores
|
||||
cmd_exec ./lib/installer.php $output = shell_exec(
|
||||
cmd_exec ./lib/installer.php $output = shell_exec(cacti_escapeshellcmd(str_replace('\\', '/', $path)) . ' --version 2>&1');
|
||||
cmd_exec ./lib/installer.php $check = shell_exec("$composer install --dry-run --no-interaction --working-dir=$base 2>&1");
|
||||
cmd_exec ./lib/installer.php $output = shell_exec("$composer install --no-interaction --no-progress --working-dir=$base 2>&1");
|
||||
cmd_exec ./lib/installer.php $process = proc_open(
|
||||
cmd_exec ./lib/ping.php $result = shell_exec('ping -6 -W ' . ceil($this->timeout / 1000) . ' -c ' . $this->retries . ' -p ' . $pattern . ' ' . cacti_escapeshellarg_cmd($this->host['hostname'], true, true));
|
||||
cmd_exec ./lib/ping.php $result = shell_exec('ping -W ' . ceil($this->timeout / 1000) . ' -c ' . $this->retries . ' -p ' . $pattern . ' ' . cacti_escapeshellarg_cmd($this->host['hostname'], true, true) . ' 2>&1');
|
||||
@@ -113,7 +111,7 @@ cmd_exec ./support.php $snmp_version = (string) shell_exec(cacti_escapeshell
|
||||
cmd_exec ./support.php exec(cacti_escapeshellcmd(read_config_option('path_spine')) . ' --version', $out_array);
|
||||
dynamic_include ./cli/audit_database.php require_once($plugin . '/includes/database.php');
|
||||
dynamic_include ./cli/audit_database.php require_once($plugin . '/setup.php');
|
||||
dynamic_include ./cli/upgrade_database.php include($upgrade_file);
|
||||
dynamic_include ./cli/upgrade_database.php include($upgrade_file);
|
||||
dynamic_include ./include/global.php require_once($vendor_autoload);
|
||||
dynamic_include ./include/global_languages.php require_once($providerFull);
|
||||
dynamic_include ./index.php require_once($file);
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 22 and column 251.
|
@@ -44,8 +44,6 @@ cmd_exec ./lib/functions.php return exec('whoami') ?: '';
|
||||
cmd_exec ./lib/functions.php * On Windows, exec()/shell_exec()/popen() route through cmd.exe, which ignores
|
||||
cmd_exec ./lib/installer.php $output = shell_exec(
|
||||
cmd_exec ./lib/installer.php $output = shell_exec(cacti_escapeshellcmd(str_replace('\\', '/', $path)) . ' --version 2>&1');
|
||||
cmd_exec ./lib/installer.php $check = shell_exec("$composer install --dry-run --no-interaction --working-dir=$base 2>&1");
|
||||
cmd_exec ./lib/installer.php $output = shell_exec("$composer install --no-interaction --no-progress --working-dir=$base 2>&1");
|
||||
cmd_exec ./lib/installer.php $process = proc_open(
|
||||
cmd_exec ./lib/ping.php $result = shell_exec('ping -6 -W ' . ceil($this->timeout / 1000) . ' -c ' . $this->retries . ' -p ' . $pattern . ' ' . cacti_escapeshellarg_cmd($this->host['hostname'], true, true));
|
||||
cmd_exec ./lib/ping.php $result = shell_exec('ping -W ' . ceil($this->timeout / 1000) . ' -c ' . $this->retries . ' -p ' . $pattern . ' ' . cacti_escapeshellarg_cmd($this->host['hostname'], true, true) . ' 2>&1');
|
||||
@@ -118,7 +116,7 @@ deserialize ./lib/functions.php return unserialize($strobj, ['allowed_classes'
|
||||
deserialize ./lib/reports.php $items = unserialize($unstripped, ['allowed_classes' => false]);
|
||||
dynamic_include ./cli/audit_database.php require_once($plugin . '/includes/database.php');
|
||||
dynamic_include ./cli/audit_database.php require_once($plugin . '/setup.php');
|
||||
dynamic_include ./cli/upgrade_database.php include($upgrade_file);
|
||||
dynamic_include ./cli/upgrade_database.php include($upgrade_file);
|
||||
dynamic_include ./include/global.php require_once($vendor_autoload);
|
||||
dynamic_include ./include/global_languages.php require_once($providerFull);
|
||||
dynamic_include ./index.php require_once($file);
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 22 and column 251.
|
@@ -7,7 +7,6 @@
|
||||
"cli/add_tree.php": 3,
|
||||
"cli/analyze_database.php": 2,
|
||||
"cli/audit_database.php": 7,
|
||||
"cli/convert_tables.php": 1,
|
||||
"cli/fix_mediumint.php": 1,
|
||||
"cli/repair_database.php": 1,
|
||||
"cli/repair_graphs.php": 3,
|
||||
|
||||
Reference in new issue
Block a user