Revert "N°4225 - Protect manual backups from cron (and vice versa) - minimal work ie protect manual backup from cron only"

This reverts commit e3dc1b77cc.
This commit is contained in:
odain
2021-08-13 11:16:48 +02:00
parent 7690e43b39
commit 280feca863
5 changed files with 100 additions and 315 deletions

View File

@@ -170,6 +170,10 @@ JS
require_once(dirname(__FILE__).'/dbrestore.class.inc.php');
$sEnvironment = utils::ReadParam('environment', 'production', false, 'raw_data');
$oRestoreMutex = new iTopMutex('restore.'.$sEnvironment);
IssueLog::Info("Backup Restore - Acquiring the LOCK 'restore.$sEnvironment'");
$oRestoreMutex->Lock();
IssueLog::Info('Backup Restore - LOCK acquired, executing...');
try
{
set_time_limit(0);
@@ -199,6 +203,7 @@ JS
finally
{
unlink($tokenRealPath);
$oRestoreMutex->Unlock();
}
$oPage->output();

View File

@@ -30,6 +30,9 @@ if (!defined('APPROOT'))
}
}
require_once(APPROOT.'application/application.inc.php');
require_once(APPROOT.'application/webpage.class.inc.php');
require_once(APPROOT.'application/csvpage.class.inc.php');
require_once(APPROOT.'application/clipage.class.inc.php');
require_once(APPROOT.'application/ajaxwebpage.class.inc.php');
require_once(APPROOT.'core/log.class.inc.php');

View File

@@ -28,8 +28,8 @@ class DBRestore extends DBBackup
{
parent::__construct($oConfig);
$this->sDBUser = $this->oConfig->Get('db_user');
$this->sDBPwd = $this->oConfig->Get('db_pwd');
$this->sDBUser = $oConfig->Get('db_user');
$this->sDBPwd = $oConfig->Get('db_pwd');
}
protected function LogInfo($sMsg)
@@ -127,89 +127,79 @@ class DBRestore extends DBBackup
*/
public function RestoreFromCompressedBackup($sFile, $sEnvironment = 'production')
{
$oRestoreMutex = new iTopMutex('restore.'.$sEnvironment);
IssueLog::Info("Backup Restore - Acquiring the LOCK 'restore.$sEnvironment'");
$oRestoreMutex->Lock();
$this->LogInfo("Starting restore of ".basename($sFile));
try {
IssueLog::Info('Backup Restore - LOCK acquired, executing...');
$bReadonlyBefore = SetupUtils::IsInReadOnlyMode();
SetupUtils::EnterReadOnlyMode(MetaModel::GetConfig());
$sNormalizedFile = strtolower(basename($sFile));
if (substr($sNormalizedFile, -4) == '.zip')
{
$this->LogInfo('zip file detected');
$oArchive = new ZipArchiveEx();
$oArchive->open($sFile);
}
elseif (substr($sNormalizedFile, -7) == '.tar.gz')
{
$this->LogInfo('tar.gz file detected');
$oArchive = new TarGzArchive($sFile);
}
else
{
throw new BackupException('Unsupported format for a backup file: '.$sFile);
}
try {
//safe zone for db backup => cron is stopped/ itop in readonly
$this->LogInfo("Starting restore of ".basename($sFile));
// Load the database
//
$sDataDir = APPROOT.'data/tmp-backup-'.rand(10000, getrandmax());
SetupUtils::builddir($sDataDir); // Here is the directory
$oArchive->extractTo($sDataDir);
$sNormalizedFile = strtolower(basename($sFile));
if (substr($sNormalizedFile, -4) == '.zip') {
$this->LogInfo('zip file detected');
$oArchive = new ZipArchiveEx();
$oArchive->open($sFile);
} elseif (substr($sNormalizedFile, -7) == '.tar.gz') {
$this->LogInfo('tar.gz file detected');
$oArchive = new TarGzArchive($sFile);
} else {
throw new BackupException('Unsupported format for a backup file: '.$sFile);
}
$sDataFile = $sDataDir.'/itop-dump.sql';
$this->LoadDatabase($sDataFile);
// Load the database
//
$sDataDir = APPROOT.'data/tmp-backup-'.rand(10000, getrandmax());
// Update the code
//
$sDeltaFile = APPROOT.'data/'.$sEnvironment.'.delta.xml';
SetupUtils::builddir($sDataDir); // Here is the directory
$oArchive->extractTo($sDataDir);
$sDataFile = $sDataDir.'/itop-dump.sql';
$this->LoadDatabase($sDataFile);
// Update the code
//
$sDeltaFile = APPROOT.'data/'.$sEnvironment.'.delta.xml';
if (is_file($sDataDir.'/delta.xml')) {
// Extract and rename delta.xml => <env>.delta.xml;
rename($sDataDir.'/delta.xml', $sDeltaFile);
} else {
@unlink($sDeltaFile);
}
if (is_dir(APPROOT.'data/production-modules/')) {
try {
SetupUtils::rrmdir(APPROOT.'data/production-modules/');
} catch (Exception $e) {
throw new BackupException("Can't remove production-modules dir", 0, $e);
}
}
if (is_dir($sDataDir.'/production-modules')) {
rename($sDataDir.'/production-modules', APPROOT.'data/production-modules/');
}
$sConfigFile = APPROOT.'conf/'.$sEnvironment.'/config-itop.php';
@chmod($sConfigFile, 0770); // Allow overwriting the file
rename($sDataDir.'/config-itop.php', $sConfigFile);
@chmod($sConfigFile, 0440); // Read-only
try {
SetupUtils::rrmdir($sDataDir);
} catch (Exception $e) {
throw new BackupException("Can't remove data dir", 0, $e);
}
$oEnvironment = new RunTimeEnvironment($sEnvironment);
$oEnvironment->CompileFrom($sEnvironment);
} finally {
if (! $bReadonlyBefore) {
SetupUtils::ExitReadOnlyMode();
} else {
//we are in the scope of main process that needs to handle/keep readonly mode.
$this->LogInfo("Keep readonly mode after restore");
}
if (is_file($sDataDir.'/delta.xml'))
{
// Extract and rename delta.xml => <env>.delta.xml;
rename($sDataDir.'/delta.xml', $sDeltaFile);
}
else
{
@unlink($sDeltaFile);
}
if (is_dir(APPROOT.'data/production-modules/'))
{
try
{
SetupUtils::rrmdir(APPROOT.'data/production-modules/');
}
catch (Exception $e)
{
throw new BackupException("Can't remove production-modules dir", 0, $e);
}
}
finally
if (is_dir($sDataDir.'/production-modules'))
{
IssueLog::Info('Backup Restore - LOCK released.');
$oRestoreMutex->Unlock();
rename($sDataDir.'/production-modules', APPROOT.'data/production-modules/');
}
$sConfigFile = APPROOT.'conf/'.$sEnvironment.'/config-itop.php';
@chmod($sConfigFile, 0770); // Allow overwriting the file
rename($sDataDir.'/config-itop.php', $sConfigFile);
@chmod($sConfigFile, 0440); // Read-only
try
{
SetupUtils::rrmdir($sDataDir);
}
catch (Exception $e)
{
throw new BackupException("Can't remove data dir", 0, $e);
}
$oEnvironment = new RunTimeEnvironment($sEnvironment);
$oEnvironment->CompileFrom($sEnvironment);
}
}

View File

@@ -1,203 +0,0 @@
<?php
/**
* Copyright (C) 2013-2021 Combodo SARL
*
* This file is part of iTop.
*
* iTop is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* iTop 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
*/
if (!defined('__DIR__')) define('__DIR__', dirname(__FILE__));
if (!defined('APPROOT'))
{
if (file_exists(__DIR__.'/../../approot.inc.php'))
{
require_once __DIR__.'/../../approot.inc.php'; // When in env-xxxx folder
}
else
{
require_once __DIR__.'/../../../approot.inc.php'; // When in datamodels/x.x folder
}
}
require_once(APPROOT.'application/application.inc.php');
require_once(APPROOT.'application/ajaxwebpage.class.inc.php');
require_once(APPROOT.'core/log.class.inc.php');
require_once(APPROOT.'application/startup.inc.php');
require_once(dirname(__FILE__).'/dbrestore.class.inc.php');
class MyDBRestore extends DBRestore
{
/** @var Page used to send log */
protected $oPage;
protected function LogInfo($sMsg)
{
$this->oPage->p($sMsg);
}
protected function LogError($sMsg)
{
$this->oPage->p('Error: '.$sMsg);
ToolsLog::Error($sMsg);
}
public function __construct($oPage)
{
$this->oPage = $oPage;
parent::__construct();
}
}
/**
* Checks if a parameter (possibly empty) was specified when calling this page
*/
function CheckParam($sParamName)
{
global $argv;
if (isset($_REQUEST[$sParamName])) return true; // HTTP parameter either GET or POST
if (!is_array($argv)) return false;
foreach($argv as $sArg)
{
if ($sArg == '--'.$sParamName) return true; // Empty command line parameter, long unix style
if ($sArg == $sParamName) return true; // Empty command line parameter, Windows style
if ($sArg == '-'.$sParamName) return true; // Empty command line parameter, short unix style
if (preg_match('/^--'.$sParamName.'=(.*)$/', $sArg, $aMatches)) return true; // Command parameter with a value
}
return false;
}
function Usage($oP)
{
$oP->p('Restore an iTop from a backup file');
$oP->p('Parameters:');
if (utils::IsModeCLI())
{
$oP->p('auth_user: login, must be administrator');
$oP->p('auth_pwd: ...');
}
$oP->p('backup_file [optional]: name of the file to store the backup into. Follows the PHP strftime format spec. The following placeholders are available: __HOST__, __DB__, __SUBNAME__');
$oP->p('mysql_bindir [optional]: specify the path for mysql executable');
if (utils::IsModeCLI())
{
$oP->p('Example: php -q restore.php --auth_user=admin --auth_pwd=myPassw0rd --backup_file=/tmp/backup.zip');
$oP->p('Known limitation: the current directory must be the directory of backup.php');
}
else
{
$oP->p('Example: .../restore.php?backup_file=/tmp/backup.zip');
}
}
function ExitError($oP, $sMessage)
{
ToolsLog::Error($sMessage);
$oP->p($sMessage);
$oP->output();
exit;
}
function ReadMandatoryParam($oP, $sParam)
{
$sValue = utils::ReadParam($sParam, null, true /* Allow CLI */, 'raw_data');
if (is_null($sValue))
{
ExitError($oP, "ERROR: Missing argument '$sParam'");
}
return trim($sValue);
}
/////////////////////////////////
// Main program
set_time_limit(0);
if (utils::IsModeCLI())
{
$oP = new CLIPage("iTop - iTop Restore");
SetupUtils::CheckPhpAndExtensionsForCli($oP);
}
else
{
$oP = new WebPage("iTop - iTop Restore");
}
try
{
utils::UseParamFile();
}
catch(Exception $e)
{
ExitError($oP, $e->GetMessage());
}
if (utils::IsModeCLI())
{
$oP->p(date('Y-m-d H:i:s')." - running backup utility");
$sAuthUser = ReadMandatoryParam($oP, 'auth_user');
$sAuthPwd = ReadMandatoryParam($oP, 'auth_pwd');
$sBackupFile = ReadMandatoryParam($oP, 'backup_file');
$bDownloadBackup = false;
if (UserRights::CheckCredentials($sAuthUser, $sAuthPwd))
{
UserRights::Login($sAuthUser); // Login & set the user's language
}
else
{
ExitError($oP, "Access restricted or wrong credentials ('$sAuthUser')");
}
if (!is_file($sBackupFile) && is_readable($sBackupFile)){
ExitError($oP, "Cannot access backup file ('$sBackupFile')");
}
}
else
{
require_once(APPROOT.'application/loginwebpage.class.inc.php');
LoginWebPage::DoLogin(); // Check user rights and prompt if needed
}
if (!UserRights::IsAdministrator())
{
ExitError($oP, "Access restricted to administors");
}
if (CheckParam('?') || CheckParam('h') || CheckParam('help'))
{
Usage($oP);
$oP->output();
exit;
}
// Interpret strftime specifications (like %Y) and database placeholders
$oRestore = new MyDBRestore($oP);
$oRestore->SetMySQLBinDir(MetaModel::GetConfig()->GetModuleSetting('itop-backup', 'mysql_bindir', ''));
$res = false;
if (MetaModel::GetConfig()->Get('demo_mode'))
{
$oP->p("Sorry, iTop is in demonstration mode: the feature is disabled");
}
else
{
$sEnvironment = utils::ReadParam('environment', 'production', false, 'raw_data');
$oRestore->RestoreFromCompressedBackup($sBackupFile, $sEnvironment);
}
$oP->output();

View File

@@ -172,43 +172,33 @@ class DBBackup
*/
public function CreateCompressedBackup($sTargetFile, $sSourceConfigFile = null)
{
$bReadonlyBefore = SetupUtils::IsInReadOnlyMode();
SetupUtils::EnterReadOnlyMode(MetaModel::GetConfig());
try {
//safe zone for db backup => cron is stopped/ itop in readonly
$bIsCmdbSourceInitialized = CMDBSource::GetMysqli() instanceof mysqli;
if (!$bIsCmdbSourceInitialized) {
$sErrorMsg = 'Cannot backup : CMDBSource not initialized !';
$this->LogError($sErrorMsg);
throw new CoreException($sErrorMsg);
}
$this->LogInfo("Creating backup: '$sTargetFile.tar.gz'");
$oArchive = new ITopArchiveTar($sTargetFile.'.tar.gz');
$sTmpFolder = APPROOT.'data/tmp-backup-'.rand(10000, getrandmax());
$aFiles = $this->PrepareFilesToBackup($sSourceConfigFile, $sTmpFolder);
$sFilesList = var_export($aFiles, true);
$this->LogInfo("backup: adding to archive files '$sFilesList'");
$bArchiveCreationResult = $oArchive->createModify($aFiles, '', $sTmpFolder);
if (!$bArchiveCreationResult) {
$sErrorMsg = 'Cannot backup : unable to create archive';
$this->LogError($sErrorMsg);
throw new BackupException($sErrorMsg);
}
$this->LogInfo("backup: removing tmp folder '$sTmpFolder'");
SetupUtils::rrmdir($sTmpFolder);
} finally {
if (! $bReadonlyBefore) {
SetupUtils::ExitReadOnlyMode();
} else {
//we are in the scope of main process that needs to handle/keep readonly mode (setup for example).
$this->LogInfo("Keep readonly mode after backup");
}
$bIsCmdbSourceInitialized = CMDBSource::GetMysqli() instanceof mysqli;
if (!$bIsCmdbSourceInitialized)
{
$sErrorMsg = 'Cannot backup : CMDBSource not initialized !';
$this->LogError($sErrorMsg);
throw new CoreException($sErrorMsg);
}
$this->LogInfo("Creating backup: '$sTargetFile.tar.gz'");
$oArchive = new ITopArchiveTar($sTargetFile.'.tar.gz');
$sTmpFolder = APPROOT.'data/tmp-backup-'.rand(10000, getrandmax());
$aFiles = $this->PrepareFilesToBackup($sSourceConfigFile, $sTmpFolder);
$sFilesList = var_export($aFiles, true);
$this->LogInfo("backup: adding to archive files '$sFilesList'");
$bArchiveCreationResult = $oArchive->createModify($aFiles, '', $sTmpFolder);
if (!$bArchiveCreationResult)
{
$sErrorMsg = 'Cannot backup : unable to create archive';
$this->LogError($sErrorMsg);
throw new BackupException($sErrorMsg);
}
$this->LogInfo("backup: removing tmp folder '$sTmpFolder'");
SetupUtils::rrmdir($sTmpFolder);
}
/**