From c768e18e2b792838887a00c21862f1d4abd7529b Mon Sep 17 00:00:00 2001 From: Pierre Goiffon Date: Fri, 12 Jun 2020 16:31:50 +0200 Subject: [PATCH] =?UTF-8?q?N=C2=B02214=20Add=20PHP=20check=20in=20CLI=20sc?= =?UTF-8?q?ripts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It is quite common that the PHP interpreter that is launched in CLI is different that the one used by the webserver. So iTop code launched by CLI could run in a context that doesn't meet iTop requirements ! This adds in the following scripts the same control that is done on the setup wizard first step : * cron.php * backup, check-backup * export, exportv2 * bulk import * synchro-exec, synchro-import If the check throws at least one error then the script is stopped with an appropriate message, and a log is made (IssueLog, Error level, CLI channel) --- application/clipage.class.inc.php | 18 ++--- datamodels/2.x/itop-backup/backup.php | 2 + datamodels/2.x/itop-backup/check-backup.php | 2 + setup/setuputils.class.inc.php | 74 +++++++++++++++++++++ synchro/synchro_exec.php | 1 + synchro/synchro_import.php | 1 + webservices/cron.php | 9 ++- webservices/export-v2.php | 18 +++-- webservices/export.php | 19 ++++-- webservices/import.php | 1 + 10 files changed, 123 insertions(+), 22 deletions(-) diff --git a/application/clipage.class.inc.php b/application/clipage.class.inc.php index 25153d17e..db348322f 100644 --- a/application/clipage.class.inc.php +++ b/application/clipage.class.inc.php @@ -29,9 +29,13 @@ require_once(APPROOT."/application/webpage.class.inc.php"); class CLIPage implements Page { - function __construct($s_title) + /** @var string */ + public $s_title; + + function __construct($s_title) { - } + $this->s_title = $s_title; + } public function output() { @@ -48,22 +52,22 @@ class CLIPage implements Page public function add($sText) { echo $sText; - } + } public function p($sText) { echo $sText."\n"; - } + } public function pre($sText) { echo $sText."\n"; - } + } public function add_comment($sText) { echo "#".$sText."\n"; - } + } public function table($aConfig, $aData, $aParams = array()) { @@ -93,5 +97,3 @@ class CLIPage implements Page } } } - -?> diff --git a/datamodels/2.x/itop-backup/backup.php b/datamodels/2.x/itop-backup/backup.php index 116f17564..4b93379ef 100644 --- a/datamodels/2.x/itop-backup/backup.php +++ b/datamodels/2.x/itop-backup/backup.php @@ -134,6 +134,8 @@ set_time_limit(0); if (utils::IsModeCLI()) { $oP = new CLIPage("iTop - Database Backup"); + + SetupUtils::CheckPhpAndExtensionsForCli($oP); } else { diff --git a/datamodels/2.x/itop-backup/check-backup.php b/datamodels/2.x/itop-backup/check-backup.php index 4f8a5cfed..4f17b32e1 100644 --- a/datamodels/2.x/itop-backup/check-backup.php +++ b/datamodels/2.x/itop-backup/check-backup.php @@ -250,6 +250,8 @@ catch(Exception $e) if (utils::IsModeCLI()) { + SetupUtils::CheckPhpAndExtensionsForCli(new CLIPage('Check backup utility')); + echo date('Y-m-d H:i:s')." - running check-backup utility\n"; try { diff --git a/setup/setuputils.class.inc.php b/setup/setuputils.class.inc.php index 64bce6149..9d769c03c 100644 --- a/setup/setuputils.class.inc.php +++ b/setup/setuputils.class.inc.php @@ -39,6 +39,49 @@ class CheckResult $this->sLabel = $sLabel; $this->sDescription = $sDescription; } + + /** + * @return string + * @since 2.7.1 2.8.0 N°2214 + */ + public function __toString() + { + $sPrintDesc = (empty($this->sDescription)) ? '' : " ({$this->sDescription})"; + return "{$this->sLabel}$sPrintDesc"; + } + + /** + * @param \CheckResult[] $aResults + * + * @return \CheckResult[] only elements that are error (iSeverity===ERROR) + * + * @since 2.7.1 2.8.0 N°2214 + */ + public static function KeepOnlyErrors($aResults) + { + return array_filter($aResults, + static function ($v) + { + if ($v->iSeverity === CheckResult::ERROR) { + return $v; + } + }, + ARRAY_FILTER_USE_BOTH); + } + + /** + * @param \CheckResult[] $aResults + * @return string[] + * @uses \CheckResult::__toString + * + * @since 2.7.1 2.8.0 N°2214 + */ + public static function FromObjetsToStrings($aResults) + { + return array_map(function($value) { + return $value->__toString(); + }, $aResults); + } } /** @@ -370,6 +413,37 @@ class SetupUtils return $aResult; } + /** + * @param \CLIPage $oCliPage + * @param int $iExitCode + * + * @since 2.7.1 2.8.0 N°2214 + */ + public static function CheckPhpAndExtensionsForCli($oCliPage, $iExitCode = -1) + { + $aPhpCheckResults = self::CheckPhpAndExtensions(); + $aPhpCheckErrors = CheckResult::KeepOnlyErrors($aPhpCheckResults); + if (empty($aPhpCheckErrors)) + { + return; + } + + $sMessageTitle = 'Error: PHP minimum requirements are not met !'; + $oCliPage->p($sMessageTitle); + $aPhpCheckErrorsForPrint = CheckResult::FromObjetsToStrings($aPhpCheckErrors); + foreach ($aPhpCheckErrorsForPrint as $sError) + { + $oCliPage->p(' * '.$sError); + } + $oCliPage->output(); + + // some CLI scripts are launched automatically + // we need a log so that we don't miss errors after migration ! + IssueLog::Error($oCliPage->s_title.' '.$sMessageTitle, 'CLI', $aPhpCheckErrorsForPrint); + + exit($iExitCode); + } + /** * @param CheckResult[] $aResult checks log */ diff --git a/synchro/synchro_exec.php b/synchro/synchro_exec.php index d212ca776..df22e9219 100644 --- a/synchro/synchro_exec.php +++ b/synchro/synchro_exec.php @@ -72,6 +72,7 @@ function ReadMandatoryParam($oP, $sParam, $sSanitizationFilter = 'parameter') if (utils::IsModeCLI()) { $oP = new CLIPage(Dict::S("TitleSynchroExecution")); + SetupUtils::CheckPhpAndExtensionsForCli($oP, -2); } else { diff --git a/synchro/synchro_import.php b/synchro/synchro_import.php index 0c065d03a..84b905fc0 100644 --- a/synchro/synchro_import.php +++ b/synchro/synchro_import.php @@ -255,6 +255,7 @@ class CLILikeWebPage extends WebPage if (utils::IsModeCLI()) { $oP = new CLIPage(Dict::S('TitleSynchroExecution')); + SetupUtils::CheckPhpAndExtensionsForCli($oP, -2); } else { diff --git a/webservices/cron.php b/webservices/cron.php index 145c758fe..b7717cede 100644 --- a/webservices/cron.php +++ b/webservices/cron.php @@ -60,7 +60,7 @@ function ReadMandatoryParam($oP, $sParam, $sSanitizationFilter = 'parameter') function UsageAndExit($oP) { - $bModeCLI = utils::IsModeCLI(); + $bModeCLI = ($oP instanceof CLIPage); if ($bModeCLI) { @@ -477,9 +477,12 @@ function ReorderProcesses(&$aProcesses, $aTasks, $oNow, $bVerbose, &$oP) set_time_limit(0); // Some background actions may really take long to finish (like backup) -if (utils::IsModeCLI()) +$bIsModeCLI = utils::IsModeCLI(); +if ($bIsModeCLI) { $oP = new CLIPage("iTop - cron"); + + SetupUtils::CheckPhpAndExtensionsForCli($oP, EXIT_CODE_FATAL); } else { @@ -497,7 +500,7 @@ catch (Exception $e) exit(EXIT_CODE_FATAL); } -if (utils::IsModeCLI()) +if ($bIsModeCLI) { // Next steps: // specific arguments: 'csvfile' diff --git a/webservices/export-v2.php b/webservices/export-v2.php index 1fd7d6f01..8b193fc9a 100644 --- a/webservices/export-v2.php +++ b/webservices/export-v2.php @@ -32,6 +32,12 @@ require_once(APPROOT.'/core/bulkexport.class.inc.php'); require_once(APPROOT.'/application/startup.inc.php'); + +const EXIT_CODE_ERROR = -1; +const EXIT_CODE_FATAL = -2; + + + function ReportErrorAndExit($sErrorMessage) { if (utils::IsModeCLI()) @@ -39,14 +45,14 @@ function ReportErrorAndExit($sErrorMessage) $oP = new CLIPage("iTop - Export"); $oP->p('ERROR: '.$sErrorMessage); $oP->output(); - exit(-1); + exit(EXIT_CODE_ERROR); } else { $oP = new WebPage("iTop - Export"); $oP->p('ERROR: '.$sErrorMessage); $oP->output(); - exit(-1); + exit(EXIT_CODE_ERROR); } } @@ -58,7 +64,7 @@ function ReportErrorAndUsage($sErrorMessage) $oP->p('ERROR: '.$sErrorMessage); Usage($oP); $oP->output(); - exit(-1); + exit(EXIT_CODE_ERROR); } else { @@ -66,7 +72,7 @@ function ReportErrorAndUsage($sErrorMessage) $oP->p('ERROR: '.$sErrorMessage); Usage($oP); $oP->output(); - exit(-1); + exit(EXIT_CODE_ERROR); } } @@ -565,6 +571,8 @@ function DoExport(WebPage $oP, BulkExport $oExporter, $bInteractive = false) ///////////////////////////////////////////////////////////////////////////// if (utils::IsModeCLI()) { + SetupUtils::CheckPhpAndExtensionsForCli(new CLIPage('iTop - Export')); + try { // Do this before loging, in order to allow setting user credentials from within the file @@ -573,7 +581,7 @@ if (utils::IsModeCLI()) catch(Exception $e) { echo "Error: ".$e->GetMessage()."
\n"; - exit(-2); + exit(EXIT_CODE_FATAL); } $sAuthUser = utils::ReadParam('auth_user', null, true /* Allow CLI */, 'raw_data'); diff --git a/webservices/export.php b/webservices/export.php index ee127ca32..f19d03eaf 100644 --- a/webservices/export.php +++ b/webservices/export.php @@ -29,6 +29,11 @@ require_once(APPROOT.'/application/excelexporter.class.inc.php'); require_once(APPROOT.'/application/startup.inc.php'); + +const EXIT_CODE_ERROR = -1; +const EXIT_CODE_FATAL = -2; + + try { // Do this before loging, in order to allow setting user credentials from within the file @@ -37,12 +42,15 @@ try catch(Exception $e) { echo "Error: ".$e->GetMessage()."
\n"; - exit -2; + exit(EXIT_CODE_FATAL); } if (utils::IsModeCLI()) { - $sAuthUser = utils::ReadParam('auth_user', null, true /* Allow CLI */, 'raw_data'); + $oP = new CLIPage("iTop - Export"); + SetupUtils::CheckPhpAndExtensionsForCli($oP, EXIT_CODE_FATAL); + + $sAuthUser = utils::ReadParam('auth_user', null, true /* Allow CLI */, 'raw_data'); $sAuthPwd = utils::ReadParam('auth_pwd', null, true /* Allow CLI */, 'raw_data'); if (UserRights::CheckCredentials($sAuthUser, $sAuthPwd)) @@ -51,10 +59,9 @@ if (utils::IsModeCLI()) } else { - $oP = new CLIPage("iTop - Export"); - $oP->p("Access restricted or wrong credentials ('$sAuthUser')"); + $oP->p("Access restricted or wrong credentials ('$sAuthUser')"); $oP->output(); - exit -1; + exit(EXIT_CODE_ERROR); } } else @@ -74,7 +81,7 @@ if (utils::IsArchiveMode() && !UserRights::CanBrowseArchive()) $oP = new CLIPage("iTop - Export"); $oP->p("The user account is not authorized to access the archives"); $oP->output(); - exit -1; + exit(EXIT_CODE_ERROR); } $bLocalize = (utils::ReadParam('no_localize', 0) != 1); diff --git a/webservices/import.php b/webservices/import.php index c43071822..7907bcfcd 100644 --- a/webservices/import.php +++ b/webservices/import.php @@ -212,6 +212,7 @@ function ReadMandatoryParam($oP, $sParam, $sSanitizationFilter) if (utils::IsModeCLI()) { $oP = new CLIPage("iTop - Bulk import"); + SetupUtils::CheckPhpAndExtensionsForCli($oP, -2); } else {