Skip to content

Commit 149ddd1

Browse files
authored
Merge pull request #3341 from jeedom/fix/add-safe-guard-scenario
Fix & improvement: add safe guard in scenario in "for" loop, "wait" & "sleep" actions
2 parents 4e75574 + 7d3743b commit 149ddd1

4 files changed

Lines changed: 46 additions & 41 deletions

File tree

core/class/config.class.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,9 @@ public static function getDefaultConfiguration(string $_plugin = 'core'): array
4444

4545
if (file_exists(__DIR__ . '/../../data/custom/custom.config.ini')) {
4646
$custom = parse_ini_file(__DIR__ . '/../../data/custom/custom.config.ini', true);
47-
self::$defaultConfiguration[$_plugin]['core'] = array_merge(self::$defaultConfiguration[$_plugin]['core'], $custom['core']);
47+
if (isset($custom['core'])) {
48+
self::$defaultConfiguration[$_plugin]['core'] = array_merge(self::$defaultConfiguration[$_plugin]['core'], $custom['core']);
49+
}
4850
}
4951
} else {
5052
self::$defaultConfiguration[$_plugin] = array();

core/class/scenarioElement.class.php

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -202,9 +202,15 @@ public function execute(&$_scenario = null) {
202202
if (!is_numeric($limits)) {
203203
throw new Exception(__('La condition pour une boucle doit être numérique :', __FILE__) . ' ' . $limits);
204204
}
205+
$endTime = time() + (int)config::byKey('scenario::element::maxExecutionTime', 'core', 3600);
205206
$return = false;
206207
for ($i = 1; $i <= $limits; $i++) {
207208
$return = $this->getSubElement('do')->execute($_scenario);
209+
if (time() > $endTime) {
210+
$_scenario->setLog(__('[For] Arrêt de la boucle pour cause de durée d\'exécution trop longue', __FILE__));
211+
return false;
212+
}
213+
sleep(1);
208214
}
209215
return $return;
210216
} elseif ($this->getType() == 'while') {
@@ -226,13 +232,13 @@ public function execute(&$_scenario = null) {
226232
message::add('scenario', $message, $action, $logicalId);
227233
return;
228234
}
229-
$endTime = time() + 3600;
235+
$endTime = time() + (int)config::byKey('scenario::element::maxExecutionTime', 'core', 3600);
230236
$return = false;
231237
while ($result) {
232238
$return = $this->getSubElement('do')->execute($_scenario);
233239
if (time() > $endTime) {
234240
$_scenario->setLog(__('[While] Arrêt de la boucle pour cause de durée d\'exécution trop longue', __FILE__));
235-
return;
241+
return false;
236242
}
237243
sleep(1);
238244
$result = $this->getSubElement('while')->execute($_scenario);

core/class/scenarioExpression.class.php

Lines changed: 32 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -468,21 +468,28 @@ public static function maxBetween($_cmd_id, $_startDate, $_endDate, $_round = 1)
468468
return round($historyStatistique['max'], $_round);
469469
}
470470

471-
public static function wait($_condition, $_timeout = 7200) {
471+
public static function wait($_condition, $_timeout = null, $_scenario = null) {
472472
$result = false;
473473
$occurence = 0;
474-
$limit = 7200;
475-
$timeout = jeedom::evaluateExpression($_timeout);
476-
$limit = (is_numeric($timeout)) ? $timeout : 7200;
477-
while ($result !== true) {
478-
$result = jeedom::evaluateExpression($_condition);
479-
if ($occurence > $limit) {
480-
return 0;
474+
475+
$limit = (int)config::byKey('scenario::element::maxExecutionTime', 'core', 3600);
476+
$timeout = is_string($_timeout) ? jeedom::evaluateExpression($_timeout, $_scenario) : $_timeout;
477+
478+
if (is_numeric($timeout) && $timeout > 0) {
479+
$limit = min((int) $timeout, $limit);
480+
}
481+
if ($_scenario !== null && is_object($_scenario)) {
482+
$_scenario->setLog(sprintf(__('[Wait] Début du wait, timeout: %ss', __FILE__), $limit));
483+
}
484+
while ($occurence < $limit) {
485+
$result = jeedom::evaluateExpression($_condition, $_scenario);
486+
if ($result === true) {
487+
return 1;
481488
}
482489
$occurence++;
483490
sleep(1);
484491
}
485-
return 1;
492+
return 0;
486493
}
487494

488495
public static function minBetween($_cmd_id, $_startDate, $_endDate, $_round = 1) {
@@ -1435,40 +1442,29 @@ public function execute(?scenario &$scenario = null) {
14351442
if (!isset($options['condition'])) {
14361443
return;
14371444
}
1438-
$result = false;
1439-
$occurence = 0;
1440-
$limit = 7200;
1441-
if (isset($options['timeout'])) {
1442-
$timeout = jeedom::evaluateExpression($options['timeout']);
1443-
$limit = (is_numeric($timeout)) ? $timeout : 7200;
1444-
}
1445-
while (!$result) {
1446-
$expression = self::setTags($options['condition'], $scenario, true);
1447-
$result = evaluate($expression);
1448-
if ($occurence > $limit) {
1449-
$this->setLog($scenario, __('[Wait] Condition valide par dépassement de temps :', __FILE__) . ' ' . $expression . ' => ' . $result);
1450-
return;
1451-
}
1452-
$occurence++;
1453-
sleep(1);
1445+
$result = self::wait($options['condition'], $options['timeout'], $scenario);
1446+
$expression = self::setTags($options['condition'], $scenario, true);
1447+
if ($result === 0) {
1448+
$this->setLog($scenario, sprintf(__('[Wait] Dépassement du timeout: %s', __FILE__), $expression));
1449+
} else {
1450+
$this->setLog($scenario, sprintf(__('[Wait] Condition valide: %s', __FILE__), $expression));
14541451
}
1455-
$this->setLog($scenario, __('[Wait] Condition valide :', __FILE__) . ' ' . $expression . ' => ' . $result);
14561452
return;
14571453
} elseif ($this->getExpression() == 'sleep') {
14581454
if (isset($options['duration'])) {
14591455
try {
1460-
$options['duration'] = floatval(evaluate($options['duration']));
1461-
} catch (Exception $e) {
1462-
} catch (Error $e) {
1463-
}
1464-
if ((is_float($options['duration']) || is_int($options['duration'])) && $options['duration'] > 0) {
1465-
$this->setLog($scenario, __('Pause de', __FILE__) . ' ' . $options['duration'] . ' ' . __('seconde(s)', __FILE__));
1466-
if ($options['duration'] < 1) {
1467-
usleep($options['duration'] * 1000000);
1456+
$duration = floatval(jeedom::evaluateExpression($options['duration'], $scenario));
1457+
if ($duration > 0) {
1458+
$seconds = min($duration, (int)config::byKey('scenario::element::maxExecutionTime', 'core', 3600));
1459+
$this->setLog($scenario, sprintf(__('Pause de %s seconde(s)', __FILE__), $seconds));
1460+
if ($seconds < 1) {
1461+
usleep((int) round($seconds * 1000000));
1462+
return;
1463+
}
1464+
sleep((int) floor($seconds));
14681465
return;
14691466
}
1470-
sleep($options['duration']);
1471-
return;
1467+
} catch (\Throwable $e) {
14721468
}
14731469
}
14741470
$this->setLog($scenario, $GLOBALS['JEEDOM_SCLOG_TEXT']['invalidDuration']['txt'] . $options['duration']);

core/config/default.config.ini

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,14 +99,15 @@ battery::warning = 30
9999
battery::danger = 5
100100
numberOfTryBeforeEqLogicDisable = 3
101101

102-
;deamon
102+
;daemon
103103
deamonsSleepTime = 1
104104

105105
;event
106106
event::waitPollingTime = 1
107107

108-
;Scénario
108+
;Scenario
109109
enableScenario = 1
110+
scenario::element::maxExecutionTime = 3600
110111

111112
;Update
112113
update::allowCore = 1

0 commit comments

Comments
 (0)