Source of file NextDomHelper.php
Size: 50,037 Bytes - Last Modified: 2020-10-24T02:46:31+00:00
/home/travis/build/NextDom/nextdom-core/src/Helpers/NextDomHelper.php
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348 | <?php /* This file is part of Jeedom. * * Jeedom is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Jeedom 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Jeedom. If not, see <http://www.gnu.org/licenses/>. */ /* This file is part of NextDom Software. * * NextDom is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * NextDom Software 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with NextDom Software. If not, see <http://www.gnu.org/licenses/>. */ namespace NextDom\Helpers; use NextDom\Enums\Common; use NextDom\Enums\ConfigKey; use NextDom\Enums\DateFormat; use NextDom\Enums\LogTarget; use NextDom\Enums\NextDomObj; use NextDom\Enums\SystemCron; use NextDom\Exceptions\CoreException; use NextDom\Managers\CacheManager; use NextDom\Managers\CmdManager; use NextDom\Managers\ConfigManager; use NextDom\Managers\CronManager; use NextDom\Managers\DataStoreManager; use NextDom\Managers\EqLogicManager; use NextDom\Managers\EventManager; use NextDom\Managers\JeeObjectManager; use NextDom\Managers\MessageManager; use NextDom\Managers\PlanHeaderManager; use NextDom\Managers\PluginManager; use NextDom\Managers\ScenarioExpressionManager; use NextDom\Managers\ScenarioManager; use NextDom\Managers\UpdateManager; use NextDom\Managers\UserManager; use NextDom\Managers\ViewManager; use NextDom\Model\DataClass\SystemHealth; use NextDom\Repo\RepoMarket; /** * Class NextDomHelper * @package NextDom\Helpers */ class NextDomHelper { /** * */ private static $nextdomConfiguration; /** * Add an entry in the history and keep only the last 200 * @param $data */ public static function addRemoveHistory($data) { $removeHistory = []; $removeHistoryPath = NEXTDOM_DATA . '/data/remove_history.json'; if (file_exists($removeHistoryPath)) { $removeHistory = json_decode(file_get_contents($removeHistoryPath), true); } $removeHistory[] = $data; $removeHistory = array_slice($removeHistory, -200, 200); file_put_contents($removeHistoryPath, json_encode($removeHistory)); } /** * Get all dead commands * * @return array * @throws \Exception */ public static function getDeadCmd() { global $NEXTDOM_INTERNAL_CONFIG; $result = []; $cmd = ConfigManager::byKey('interact::warnme::defaultreturncmd', 'core', ''); if ($cmd != '' && !CmdManager::byId(str_replace('#', '', $cmd))) { $result[] = ['detail' => 'Administration', 'help' => __('Commande retour interactions'), 'who' => $cmd]; } $cmd = ConfigManager::byKey('emailAdmin', 'core', ''); if ($cmd != '' && !CmdManager::byId(str_replace('#', '', $cmd))) { $result[] = ['detail' => 'Administration', 'help' => __('Commande information utilisateur'), 'who' => $cmd]; } foreach ($NEXTDOM_INTERNAL_CONFIG['alerts'] as $level => $value) { $cmds = ConfigManager::byKey('alert::' . $level . 'Cmd', 'core', ''); preg_match_all("/#([0-9]*)#/", $cmds, $matches); foreach ($matches[1] as $cmd_id) { if (CmdManager::byId($cmd_id)) { $result[] = ['detail' => 'Administration', 'help' => __('Commande sur ') . $value['name'], 'who' => '#' . $cmd_id . '#']; } } } return $result; } /** * Get system health * Test all functionnalities * * @return array Data about system health * @throws \Exception */ public static function health(): array { $okStr = __('common.ok'); $nokStr = __('common.nok'); $systemHealth = []; $state = true; $version = ''; $uname = SystemHelper::getSystemInformations(); if (SystemHelper::getDistrib() != 'debian') { $state = false; } else { $version = trim(strtolower(file_get_contents('/etc/debian_version'))); if (version_compare($version, '8', '<') && strpos($version, 'jessie') === false && strpos($version, 'stretch') === false && strpos($version, 'buster') === false) { $state = false; } } $systemHealth[] = new SystemHealth( 'fa-cogs', 'health.os-version', $state, ($state) ? $uname . ' [' . $version . ']' : $uname, ($state) ? '' : __('health.os-not-supported'), 'os::version'); $nbNeededUpdate = UpdateManager::nbNeedUpdate(); $state = ($nbNeededUpdate == 0) ? true : false; $systemHealth[] = new SystemHealth('fa-heartbeat', 'health.update-to-date', $state, ($state) ? $okStr : $nbNeededUpdate . ' ' . __('health.updates'), '', 'uptodate'); $state = ConfigManager::byKey(ConfigKey::ENABLE_CRON, 'core', 1, true) != 0; $systemHealth[] = new SystemHealth('fa-calendar-alt', 'health.cron-enabled', $state, ($state) ? $okStr : $nokStr, ($state) ? '' : __('health.cron-disabled'), 'cron::enable'); $state = !(ConfigManager::byKey(ConfigKey::ENABLE_SCENARIO) == 0 && count(ScenarioManager::all()) > 0); $systemHealth[] = new SystemHealth('fa-film', 'health.scenario-enabled', $state, ($state) ? $okStr : $nokStr, ($state) ? '' : __('health.scenario-disabled'), 'scenario::enable'); $state = self::isStarted(); $systemHealth[] = new SystemHealth('fa-play', 'health.product-started', $state, ($state) ? $okStr . ' - ' . file_get_contents(self::getStartedFilePath()) : $nokStr, '', 'isStarted'); $state = self::isDateOk(); $cache = CacheManager::byKey('hour'); $lastKnowDate = $cache->getValue(); $systemHealth[] = new SystemHealth('fa-clock', 'health.system-date', $state, ($state) ? $okStr . ' - ' . date(DateFormat::FULL) . ' (' . $lastKnowDate . ')' : date(DateFormat::FULL), ($state) ? '' : __('health.system-date-error'), 'hour'); $state = self::isCapable('sudo', true); $systemHealth[] = new SystemHealth('fa-user-secret', 'health.sudo-rights', ($state) ? 1 : 2, ($state) ? $okStr : $nokStr, ($state) ? '' : __('sudo-error'), 'sudo::right'); $systemHealth[] = new SystemHealth('fa-code-branch', 'health.product-version', true, self::getNextdomVersion(), '', 'nextdom::version'); $state = version_compare(phpversion(), '7.0', '>='); $systemHealth[] = new SystemHealth('fa-code', 'health.php-version', $state, phpversion(), ($state) ? '' : __('health.php-error'), 'php::version'); $version = DBHelper::getOne('select version()'); $systemHealth[] = new SystemHealth('fa-database', 'health.database-version', true, $version['version()'], ($state) ? '' : __('health.php-error'), 'database::version'); $value = self::checkSpaceLeft(); $systemHealth[] = new SystemHealth('fa-hdd', 'health.harddisk-freespace', ($value > 10), $value . ' %', __('health.need-more-than') . ': 10%', 'space::root'); $value = self::checkSpaceLeft(self::getTmpFolder()); $systemHealth[] = new SystemHealth('fa-hdd', 'health.tmp-space-left', ($value > 10), $value . ' %', __('health.tmp-problem') . ': 10%', 'space::tmp'); $values = SystemHelper::getMemInfo(); $value = round(($values['MemAvailable'] / $values['MemTotal']) * 100); $systemHealth[] = new SystemHealth('fa-th', 'health.available-memory', ($value > 15), $value . ' %', __('health.need-more-than') . ': 15%', 'meminfo'); $value = shell_exec('sudo dmesg | grep oom | grep -v deprecated | wc -l'); $systemHealth[] = new SystemHealth('fa-th-large', 'health.enough-memory', ($value == 0), ($state == 0) ? $nokStr : $okStr, ($value == 0) ? '' : __('health.processes-killed'), 'oom'); $value = shell_exec('sudo dmesg | grep "CRC error" | grep "mmcblk0" | grep "card status" | wc -l'); if (!is_numeric($value)) { $value = 0; } $value2 = @shell_exec('sudo dmesg | grep "I/O error" | wc -l'); if (is_numeric($value2)) { $value += $value2; } $systemHealth[] = new SystemHealth('fa fa-hearth', __('Erreur I/O'), $value == 0, $value, ($value == 0) ? '' : __('Il y a des erreurs disque, cela peut indiquer un soucis avec le disque ou un problème d\'alimentation'), 'io_error'); if ($values['SwapTotal'] != 0 && $values['SwapTotal'] !== null) { $value = round(($values['SwapFree'] / $values['SwapTotal']) * 100); $systemHealth[] = new SystemHealth('fa-hdd', 'health.available-swap', ($value > 15), $value . ' %', __('health.need-more-than') . ': 15%', 'swap'); } else { $systemHealth[] = new SystemHealth('fa-hdd', 'health.available-swap', 2, __('health.unknow'), '', 'swap'); } $value = shell_exec('sudo cat /proc/sys/vm/swappiness'); $state = ($value <= 20); if($values['MemTotal'] >= (1024*1024)){ $state = true; } $systemHealth[] = new SystemHealth('fa-hdd', 'health.swapiness', $state, $value.'%', ($state) ? '' :__('health.swapiness-config'), 'swapiness'); $values = sys_getloadavg(); $systemHealth[] = new SystemHealth('fa-fire', 'health.load', ($values[2] < 20), $values[0] . ' - ' . $values[1] . ' - ' . $values[2], __('health.need-less-than') . ': 20', 'load'); $state = NetworkHelper::test('internal'); $systemHealth[] = new SystemHealth('fa-plug', 'health.internal-network-conf', $state, ($state) ? $okStr : $nokStr, ($state) ? '' : __('health.network-config'), 'network::internal'); $state = NetworkHelper::test('external'); $systemHealth[] = new SystemHealth('fa-globe', 'health.external-network-conf', $state, ($state) ? $okStr : $nokStr, ($state) ? '' : __('health.network-config'), 'network::external'); $cacheHealth = new SystemHealth('fa-inbox', 'health.cache-persistence', false, '', '', 'cache::persit'); if (CacheManager::isPersistOk()) { if ((ConfigManager::byKey('cache::engine') != 'FilesystemCache') && (ConfigManager::byKey('cache::engine') != 'PhpFileCache')) { $cacheHealth->setState(true); $cacheHealth['result'] = $okStr; } else { $cache_path = CacheManager::getArchivePath(); $cache_time = date(DateFormat::FULL, filemtime($cache_path)); $cacheHealth->setState(true); $cacheHealth->setResult(sprintf("%s (%s)", $okStr, $cache_time)); } } else { $cacheHealth->setState(false) ->setResult($nokStr) ->setComment(__('health.cache-not-saved')); } $systemHealth[] = $cacheHealth; $state = shell_exec('systemctl show apache2 | grep PrivateTmp | grep yes | wc -l'); $systemHealth[] = new SystemHealth('fa-folder', 'health.apache-private-tmp', $state, ($state) ? $okStr : $nokStr, ($state) ? '' : __('health.apache-private-tmp-disabled'), 'apache2::privateTmp'); foreach (UpdateManager::listRepo() as $repo) { if ($repo['enable']) { $targetClass = $repo['class']; if (class_exists($targetClass) && method_exists($targetClass, 'health')) { $systemHealth += array_merge($systemHealth, $targetClass::health()); } } } return $systemHealth; } /** * Test if NextDom is started * * @return bool True if NextDom is started * @throws \Exception */ public static function isStarted(): bool { return file_exists(self::getStartedFilePath()); } /** * @return string * @throws \Exception */ public static function getStartedFilePath(): string { return sprintf("%s/started", self::getTmpFolder()); } /** * Get temporary folder and creates it if not exists * * @param string|null $subFolder Log subfolder * * @return string * @throws \Exception */ public static function getTmpFolder($subFolder = null) { $result = '/' . trim(ConfigManager::byKey('folder::tmp'), '/'); if ($subFolder !== null) { $result .= '/' . $subFolder; } if (!file_exists($result)) { mkdir($result, 0775, true); } return $result; } /** * Update time status and get it * * @return boolean Time status * @throws \Exception */ public static function isDateOk() { if (ConfigManager::byKey('ignoreHourCheck') == 1) { return true; } $cache = CacheManager::byKey('hour'); $lastKnowDate = $cache->getValue(); if (strtotime($lastKnowDate) > strtotime('now')) { self::forceSyncHour(); sleep(3); if (strtotime($lastKnowDate) > strtotime('now')) { return false; } } $minDateValue = new \DateTime('2017-01-01'); $mindate = strtotime($minDateValue->format('Y-m-d 00:00:00')); $maxDateValue = $minDateValue->modify('+6 year')->format('Y-m-d 00:00:00'); $maxdate = strtotime($maxDateValue); if (strtotime('now') < $mindate || strtotime('now') > $maxdate) { self::forceSyncHour(); sleep(3); if (strtotime('now') < $mindate || strtotime('now') > $maxdate) { LogHelper::addError('core', sprintf(__('core.incorrect-sys-date'), $minDateValue, $maxDateValue) . (new \DateTime())->format(DateFormat::FULL), 'dateCheckFailed'); return false; } } return true; } /** * Sync time */ public static function forceSyncHour() { if (ConfigManager::byKey('disable_ntp', Common::CORE, 0) == 1) { return false; } shell_exec(SystemHelper::getCmdSudo() . 'service ntp stop;' . SystemHelper::getCmdSudo() . 'ntpdate -s ' . ConfigManager::byKey('ntp::optionalServer', 'core', '0.debian.pool.ntp.org') . ';' . SystemHelper::getCmdSudo() . 'service ntp start'); } /** * Test if NextDom can call system function * * @param string $systemFunc Function to test * @param bool $forceRefresh Force refresh in configuration * * @return bool True if $systemFunc can be executed * @throws \Exception */ public static function isCapable($systemFunc, $forceRefresh = false) { global $NEXTDOM_COMPATIBILIY_CONFIG; if ($systemFunc == 'sudo') { if (!$forceRefresh) { $cache = CacheManager::byKey('nextdom::isCapable::sudo'); if ($cache->getValue(0) == 1) { return true; } } $result = shell_exec('sudo -l > /dev/null 2>&1; echo $?') == 0; CacheManager::set('nextdom::isCapable::sudo', $result); return $result; } $hardware = self::getHardwareName(); if (!isset($NEXTDOM_COMPATIBILIY_CONFIG[$hardware])) { return false; } if (in_array($systemFunc, $NEXTDOM_COMPATIBILIY_CONFIG[$hardware])) { return true; } return false; } /** * Get hostname * * @return string * @throws \Exception */ public static function getHardwareName() { if (ConfigManager::byKey(ConfigKey::HARDWARE_NAME) != '') { return ConfigManager::byKey(ConfigKey::HARDWARE_NAME); } $result = 'diy'; $uname = SystemHelper::getSystemInformations(); if (strpos(shell_exec('sudo cat /proc/1/cgroup'), 'docker') !== false) { $result = 'docker'; } else if (file_exists('/usr/bin/raspi-config')) { $result = 'rpi'; $hardware_revision = strtolower(shell_exec('cat /proc/cpuinfo | grep Revision')); global $NEXTDOM_RPI_HARDWARE; foreach ($NEXTDOM_RPI_HARDWARE as $key => $values) { foreach ($values as $value) { if (strpos($hardware_revision, $value) !== false) { $result = $key; } } } } else if (strpos($uname, 'cubox') !== false || strpos($uname, 'imx6') !== false || file_exists('/media/boot/multiboot/meson64_odroidc2.dtb.linux')) { $result = 'miniplus'; } if (file_exists('/media/boot/multiboot/meson64_odroidc2.dtb.linux')) { $result = 'smart'; } ConfigManager::save(ConfigKey::HARDWARE_NAME, $result); return ConfigManager::byKey(ConfigKey::HARDWARE_NAME); } /** * Get Nextdom version * * @return string */ public static function getNextdomVersion() { if (file_exists(NEXTDOM_DATA . '/config/Nextdom_version')) { return trim(file_get_contents(NEXTDOM_DATA . '/config/Nextdom_version')); } return ''; } /** * Check space left * * @param null $directory * @return float */ public static function checkSpaceLeft($directory = null): float { if ($directory == null) { $pathToCheck = NEXTDOM_ROOT; } else { $pathToCheck = $directory; } return round(disk_free_space($pathToCheck) / disk_total_space($pathToCheck) * 100); } /** * Get informations about system installation */ public static function sick() { $cmd = NEXTDOM_ROOT . '/scripts/sick.php'; $cmd .= ' >> ' . LogHelper::getPathToLog('sick') . ' 2>&1'; SystemHelper::php($cmd); } /** * Test if NextDom running right * * @return bool * @throws \Exception */ public static function isOk() { if (!self::isStarted()) { return false; } if (!self::isDateOk()) { return false; } if (ConfigManager::byKey(ConfigKey::ENABLE_SCENARIO) == 0 && count(ScenarioManager::all()) > 0) { return false; } if (!self::isCapable('sudo')) { return false; } if (ConfigManager::byKey(ConfigKey::ENABLE_CRON, 'core', 1, true) == 0) { return false; } return true; } /** * Start update * * @param array $options Options list of /install/update.php script * @throws \Exception */ public static function update($options = []) { LogHelper::clear('update'); $params = ''; foreach ($options as $key => $value) { $params .= '"' . $key . '"="' . $value . '" '; } SystemHelper::php(NEXTDOM_ROOT . '/install/update.php ' . $params . ' >> ' . LogHelper::getPathToLog('update') . ' &'); } /** * Get configuration informations or global configuration * * @param string $askedKey * @param mixed $defaultValue * * @return mixed * @throws \Exception */ public static function getConfiguration(string $askedKey = '', $defaultValue = false) { global $NEXTDOM_INTERNAL_CONFIG; if ($askedKey == '') { return $NEXTDOM_INTERNAL_CONFIG; } if (!is_array(self::$nextdomConfiguration)) { self::$nextdomConfiguration = []; } // @TODO: Bizarre if (!$defaultValue && isset(self::$nextdomConfiguration[$askedKey])) { return self::$nextdomConfiguration[$askedKey]; } $keys = explode(':', $askedKey); $result = $NEXTDOM_INTERNAL_CONFIG; foreach ($keys as $key) { if (isset($result[$key])) { $result = $result[$key]; } } if ($defaultValue) { return $result; } self::$nextdomConfiguration[$askedKey] = self::checkValueInConfiguration($askedKey, $result); return self::$nextdomConfiguration[$askedKey]; } /** * @TODO: ??? * * @param string $configKey * @param mixed $configValue * @return array|mixed|string * @throws \Exception */ public static function checkValueInConfiguration($configKey, $configValue) { if (!is_array(self::$nextdomConfiguration)) { self::$nextdomConfiguration = []; } if (isset(self::$nextdomConfiguration[$configKey])) { return self::$nextdomConfiguration[$configKey]; } if (is_array($configValue)) { foreach ($configValue as $key => $value) { $configValue[$key] = self::checkValueInConfiguration($configKey . ':' . $key, $value); } self::$nextdomConfiguration[$configKey] = $configValue; return $configValue; } else { $config = ConfigManager::byKey($configKey); return ($config == '') ? $configValue : $config; } } /** * Get Jeedom version * * @return string */ public static function getJeedomVersion() { if (file_exists(NEXTDOM_DATA . '/config/Jeedom_version')) { return trim(file_get_contents(NEXTDOM_DATA . '/config/Jeedom_version')); } return ''; } /** * Stop all cron tasks and scenarios * @param bool $isBackupProcess if true stop all except backup cron * @throws \Exception */ public static function stopSystem($isBackupProcess) { // $okStr = __('common.ok'); // echo __('core.disable-tasks'); ConfigManager::save('enableCron', 0); foreach (CronManager::all() as $cron) { if($isBackupProcess && $cron->getClass() === SystemCron::NEXTDOM_BACKUP[0] && $cron->getFunction() === SystemCron::NEXTDOM_BACKUP[1] ){ LogHelper::addInfo(LogTarget::MIGRATION, "Stopping all cron in backup context. Backup's cron won't be stop"); } else if ($cron->running()) { try { $cron->halt(); } catch (\Exception $e) { sleep(5); $cron->halt(); } } } // echo " $okStr\n"; /* * **********arrêt des crons********************* */ if (CronManager::jeeCronRun()) { $pid = CronManager::getPidFile(); SystemHelper::kill($pid); } /* * *********Arrêt des scénarios**************** */ ConfigManager::save('enableScenario', 0); foreach (ScenarioManager::all() as $scenario) { try { $scenario->stop(); } catch (\Exception $e) { sleep(5); $scenario->stop(); } } SystemHelper::stopService('cron'); } /** * Task started minutes */ public static function cron() { if (!self::isStarted()) { echo date(DateFormat::FULL) . ' starting NextDom'; LogHelper::addDebug(LogTarget::STARTING, __('Démarrage de nextdom')); try { LogHelper::addDebug(LogTarget::STARTING, __('Arrêt des crons')); foreach (CronManager::all() as $cron) { if ($cron->running() && $cron->getClass() != 'nextdom' && $cron->getFunction() != 'cron') { try { $cron->halt(); } catch (\Exception $e) { LogHelper::addError(LogTarget::STARTING, __('Erreur sur l\'arrêt d\'une tâche cron : ') . LogHelper::exception($e)); } } } } catch (\Exception $e) { LogHelper::addError(LogTarget::STARTING, __('Erreur sur l\'arrêt des tâches crons : ') . LogHelper::exception($e)); } try { LogHelper::addDebug(LogTarget::STARTING, __('Restauration du cache')); CacheManager::restore(); } catch (\Exception $e) { LogHelper::addError(LogTarget::STARTING, __('Erreur sur la restauration du CacheManager : ') . LogHelper::exception($e)); } try { LogHelper::addDebug(LogTarget::STARTING, __('Nettoyage du cache des péripheriques USB')); $cache = CacheManager::byKey('nextdom::usbMapping'); $cache->remove(); } catch (\Exception $e) { LogHelper::addError(LogTarget::STARTING, __('Erreur sur le nettoyage du CacheManager des péripheriques USB : ') . LogHelper::exception($e)); } try { LogHelper::addDebug(LogTarget::STARTING, __('Nettoyage du cache des péripheriques Bluetooth')); $cache = CacheManager::byKey('nextdom::bluetoothMapping'); $cache->remove(); } catch (\Exception $e) { LogHelper::addError(LogTarget::STARTING, __('Erreur sur le nettoyage du CacheManager des péripheriques Bluetooth : ') . LogHelper::exception($e)); } try { LogHelper::addDebug(LogTarget::STARTING, __('Démarrage des processus Internet de NextDom')); self::startSystem(); } catch (\Exception $e) { LogHelper::addError(LogTarget::STARTING, __('Erreur sur le démarrage interne de NextDom : ') . LogHelper::exception($e)); } try { LogHelper::addDebug(LogTarget::STARTING, __('Ecriture du fichier ') . self::getStartedFilePath()); if (file_put_contents(self::getStartedFilePath(), date(DateFormat::FULL)) === false) { LogHelper::addError(LogTarget::STARTING, __('Impossible d\'écrire ' . self::getStartedFilePath())); } } catch (\Exception $e) { LogHelper::addError(LogTarget::STARTING, __('Impossible d\'écrire ' . self::getStartedFilePath() . ' : ') . LogHelper::exception($e)); } if (!file_exists(self::getStartedFilePath())) { LogHelper::addCritical(LogTarget::STARTING, __('Impossible d\'écrire ' . self::getStartedFilePath() . ' pour une raison inconnue. NextDom ne peut démarrer')); return; } try { LogHelper::addDebug(LogTarget::STARTING, __('Vérification de la configuration réseau interne')); if (!NetworkHelper::test('internal')) { NetworkHelper::checkConf('internal'); } } catch (\Exception $e) { LogHelper::addError(LogTarget::STARTING, __('Erreur sur la configuration réseau interne : ') . LogHelper::exception($e)); } try { LogHelper::addDebug(LogTarget::STARTING, __('Envoi de l\'événement de démarrage')); self::event('start'); } catch (\Exception $e) { LogHelper::addError(LogTarget::STARTING, __('Erreur sur l\'envoi de l\'événement de démarrage : ') . LogHelper::exception($e)); } try { LogHelper::addDebug(LogTarget::STARTING, __('Démarrage des plugins')); PluginManager::start(); } catch (\Exception $e) { LogHelper::addError(LogTarget::STARTING, __('Erreur sur le démarrage des plugins : ') . LogHelper::exception($e)); } try { if (ConfigManager::byKey('market::enable') == 1) { LogHelper::addDebug(LogTarget::STARTING, __('Test de connexion au market')); RepoMarket::test(); } } catch (\Exception $e) { LogHelper::addError(LogTarget::STARTING, __('Erreur sur la connexion au market : ') . LogHelper::exception($e)); } LogHelper::addDebug(LogTarget::STARTING, __('Démarrage de nextdom fini avec succès')); EventManager::add('refresh'); } self::isDateOk(); } /** * Start all cron tasks and scenarios * * @throws \Exception */ public static function startSystem() { ConfigManager::save(ConfigKey::ENABLE_SCENARIO, 1); ConfigManager::save(ConfigKey::ENABLE_CRON, 1); // Create log folder self::getTmpFolder(); if (!is_dir('/tmp/jeedom')) { system('ln -s ' . ConfigManager::byKey(ConfigKey::TMP_FOLDER) . ' /tmp/jeedom'); } SystemHelper::startService('cron'); } /** * Check an event * * @param \event|string $event * @param bool $forceSyncMode * @throws \Exception */ public static function event($event, $forceSyncMode = false) { ScenarioManager::check($event, $forceSyncMode); } /** * Task started every 5 minutes */ public static function cron5() { try { NetworkHelper::cron5(); } catch (\Exception $e) { LogHelper::addError('network', 'network::cron : ' . $e->getMessage()); } try { foreach (UpdateManager::listRepo() as $name => $repo) { $repoClass = 'Repo' . $name; if (class_exists($repoClass) && method_exists($repoClass, 'cron5') && ConfigManager::byKey($name . '::enable') == 1) { $repoClass::cron5(); } } } catch (\Exception $e) { LogHelper::addError(LogTarget::NEXTDOM, $e->getMessage()); } try { EqLogicManager::checkAlive(); } catch (\Exception $e) { } } public static function cron10() { try { foreach (UpdateManager::listRepo() as $name => $repo) { $repoClass = 'Repo' . $name; if (class_exists($repoClass) && method_exists($repoClass, 'cron10') && ConfigManager::byKey($name . '::enable') == 1) { $repoClass::cron10(); } } } catch (\Exception $e) { LogHelper::addError(LogTarget::NEXTDOM, $e->getMessage()); } } /** * Task started every hours */ public static function cronHourly() { try { CacheManager::set('hour', date(DateFormat::FULL)); } catch (\Exception $e) { LogHelper::addError('nextdom', $e->getMessage()); } try { if ((ConfigManager::byKey('update::lastCheck') == '' || (strtotime('now') - strtotime(ConfigManager::byKey('update::lastCheck'))) > (23 * 3600))) { UpdateManager::checkAllUpdate(); $updates = UpdateManager::byStatus('update'); $toUpdate = ''; if (count($updates) > 0) { foreach ($updates as $update) { $toUpdate .= $update->getLogicalId() . ','; } } $updates = UpdateManager::byStatus('update'); if (count($updates) > 0) { MessageManager::add('update', __('De nouvelles mises à jour sont disponibles : ') . trim($toUpdate, ','), '', 'newUpdate'); } } } catch (\Exception $e) { LogHelper::addError('nextdom', $e->getMessage()); } try { foreach (UpdateManager::listRepo() as $name => $repo) { $repoClass = 'Repo' . $name; if (class_exists($repoClass) && method_exists($repoClass, 'cronHourly') && ConfigManager::byKey($name . '::enable') == 1) { $repoClass::cronHourly(); } } } catch (\Exception $e) { LogHelper::addError('nextdom', $e->getMessage()); } } /** * Task started everyday */ public static function cronDaily() { try { ScenarioManager::cleanTable(); ScenarioManager::consystencyCheck(); LogHelper::chunk(); CronManager::clean(); ReportHelper::clean(); DBHelper::optimize(); CacheManager::clean(); UserManager::regenerateHash(); foreach (UpdateManager::listRepo() as $repoCode => $repoData) { $classCode = 'repo_' . $repoCode; if(!empty($repoCode['class'])) { $fullClassName = $repoCode['class']; if (class_exists($fullClassName) && method_exists($fullClassName, 'cronDaily') && ConfigManager::byKey($classCode . '::enable') == 1) { $fullClassName::cronDaily(); } } } } catch (\Exception $e) { LogHelper::addError('nextdom', $e->getMessage()); } } /** * @TODO: ???? * @param array $_replaces * @throws \Exception */ public static function replaceTag(array $_replaces) { $datas = []; foreach ($_replaces as $key => $value) { $datas = array_merge($datas, CmdManager::searchConfiguration($key)); $datas = array_merge($datas, EqLogicManager::searchConfiguration($key)); $datas = array_merge($datas, JeeObjectManager::searchConfiguration($key)); $datas = array_merge($datas, ScenarioManager::searchByUse([['action' => '#' . $key . '#']])); $datas = array_merge($datas, ScenarioExpressionManager::searchExpression($key, $key, false)); $datas = array_merge($datas, ScenarioExpressionManager::searchExpression('variable(' . str_replace('#', '', $key) . ')')); $datas = array_merge($datas, ScenarioExpressionManager::searchExpression('variable', str_replace('#', '', $key), true)); } if (count($datas) > 0) { foreach ($datas as $data) { Utils::a2o($data, json_decode(str_replace(array_keys($_replaces), $_replaces, json_encode(Utils::o2a($data))), true)); $data->save(); } } } /** * @TODO: ??? * * @param $cmd * * @return string */ public static function checkOngoingThread(string $cmd): string { return shell_exec('(ps ax || ps w) | grep "' . $cmd . '$" | grep -v "grep" | wc -l'); } /** * Get command thread process id * * @param string $cmd Command to test * * @return string PID */ public static function retrievePidThread(string $cmd) { return shell_exec('(ps ax || ps w) | grep "' . $cmd . '$" | grep -v "grep" | awk \'{print $1}\''); } /** * @TODO: ?? * * @param $version * @param bool $lightMode * * @return mixed|string */ public static function versionAlias($version, bool $lightMode = true) { if (!$lightMode) { if ($version == 'dplan') { return 'plan'; } else if ($version == 'dview') { return 'view'; } else if ($version == 'mview') { return 'view'; } } $alias = [ 'dview' => 'dashboard', 'dplan' => 'dashboard', ]; return (isset($alias[$version])) ? $alias[$version] : $version; } /** * @TODO: ?? Ca fait plein de choses * * @param $input * * @return array * @throws \Exception */ public static function toHumanReadable($input) { return ScenarioManager::toHumanReadable(EqLogicManager::toHumanReadable(CmdManager::cmdToHumanReadable($input))); } /** * @TODO: ?? Ca aussi ça fait plein de choses * * @param $input * * @return array|mixed( * @throws \Exception */ public static function fromHumanReadable($input) { return ScenarioManager::fromHumanReadable(EqLogicManager::fromHumanReadable(CmdManager::humanReadableToCmd($input))); } /** * @TODO: Ca évalue des choses dans les expressions * * @param $input * @param null $scenario * * @return mixed|string */ public static function evaluateExpression($input, $scenario = null) { try { $input = ScenarioExpressionManager::setTags($input, $scenario, true); $result = Utils::evaluate($input); if (is_bool($result) || is_numeric($result)) { return $result; } return $input; } catch (\Exception $exc) { return $input; } } /** * @TODO: Calcul les stats de quelquechose * * @param $calcType * @param $values * * @return float|int|null */ public static function calculStat($calcType, $values) { switch ($calcType) { case 'sum': return array_sum($values); break; case 'avg': return array_sum($values) / count($values); break; } return null; } /** * Get type of entity in string * * @param string $testString * * @return array * @throws \Exception */ public static function getTypeUse($testString = '') { $results = [NextDomObj::CMD => [], NextDomObj::SCENARIO => [], NextDomObj::EQLOGIC => [], NextDomObj::DATASTORE => [], NextDomObj::PLAN => [], NextDomObj::VIEW => []]; // Look for human readable strings preg_match_all('/#(eqLogic|scenario)?(\d+)#/', $testString, $humanReadableResults); self::addTypeUseResults($humanReadableResults, $results); // Look in json string preg_match_all('/"(scenario|view|plan|eqLogic)(?:_id)?":"(\d+)"/', $testString, $jsonResults); self::addTypeUseResults($jsonResults, $results); preg_match_all('/variable\((.*?)\)/', $testString, $dataStoreResults); foreach ($dataStoreResults[1] as $variable) { if (isset($results[NextDomObj::DATASTORE][$variable])) { continue; } $dataStore = DataStoreManager::byTypeLinkIdKey(NextDomObj::SCENARIO, -1, trim($variable)); if (!is_object($dataStore)) { continue; } $results[NextDomObj::DATASTORE][$variable] = $dataStore; } return $results; } /** * @param $matches * @param $result * @throws \Exception */ private static function addTypeUseResults($matches, &$result) { for ($matchIndex = 0; $matchIndex < count($matches[0]); ++$matchIndex) { $typeName = $matches[1][$matchIndex]; if (empty($typeName)) { $typeName = NextDomObj::CMD; } $typeId = $matches[2][$matchIndex]; $target = null; if (isset($result[$typeName][$typeId])) { continue; } switch ($typeName[0]) { case 'c': $target = CmdManager::byId($typeId); break; case 'e': $target = EqLogicManager::byId($typeId); break; case 's': $target = ScenarioManager::byId($typeId); break; case 'p': $target = PlanHeaderManager::byId($typeId); break; case 'v': $target = ViewManager::byId($typeId); break; default: $target = null; break; } if (!is_object($target)) { continue; } $result[$typeName][$typeId] = $target; } } /** * @param string $testString * @return array * @throws \Exception */ public static function getTypeUseOld($testString = '') { $result = [NextDomObj::CMD => [], NextDomObj::SCENARIO => [], NextDomObj::EQLOGIC => [], NextDomObj::DATASTORE => [], NextDomObj::PLAN => [], NextDomObj::VIEW => []]; // Test commands human readable preg_match_all("/#([0-9]*)#/", $testString, $matches); foreach ($matches[1] as $cmdId) { if (isset($result['cmd'][$cmdId])) { continue; } $cmd = CmdManager::byId($cmdId); if (!is_object($cmd)) { continue; } $result[NextDomObj::CMD][$cmdId] = $cmd; } // Test scenarios from parameters preg_match_all('/"scenario_id":"([0-9]*)"/', $testString, $matches); foreach ($matches[1] as $scenario_id) { if (isset($result['scenario'][$scenario_id])) { continue; } $scenario = ScenarioManager::byId($scenario_id); if (!is_object($scenario)) { continue; } $result[NextDomObj::SCENARIO][$scenario_id] = $scenario; } // Test scenario human readable preg_match_all("/#scenario([0-9]*)#/", $testString, $matches); foreach ($matches[1] as $scenario_id) { if (isset($result[NextDomObj::SCENARIO][$scenario_id])) { continue; } $scenario = ScenarioManager::byId($scenario_id); if (!is_object($scenario)) { continue; } $result[NextDomObj::SCENARIO][$scenario_id] = $scenario; } // Test eqLogic human readable preg_match_all("/#eqLogic([0-9]*)#/", $testString, $matches); foreach ($matches[1] as $eqLogicId) { if (isset($result[NextDomObj::EQLOGIC][$eqLogicId])) { continue; } $eqLogic = EqLogicManager::byId($eqLogicId); if (!is_object($eqLogic)) { continue; } $result[NextDomObj::EQLOGIC][$eqLogicId] = $eqLogic; } // Test eqLogic from parameters preg_match_all('/"eqLogic":"([0-9]*)"/', $testString, $matches); foreach ($matches[1] as $eqLogicId) { if (isset($result[NextDomObj::EQLOGIC][$eqLogicId])) { continue; } $eqLogic = EqLogicManager::byId($eqLogicId); if (!is_object($eqLogic)) { continue; } $result[NextDomObj::EQLOGIC][$eqLogicId] = $eqLogic; } // Test variable preg_match_all('/variable\((.*?)\)/', $testString, $matches); foreach ($matches[1] as $variable) { if (isset($result[NextDomObj::DATASTORE][$variable])) { continue; } $dataStore = DataStoreManager::byTypeLinkIdKey('scenario', -1, trim($variable)); if (!is_object($dataStore)) { continue; } $result[NextDomObj::DATASTORE][$variable] = $dataStore; } // Test view id from parameters preg_match_all('/"view_id":"([0-9]*)"/', $testString, $matches); foreach ($matches[1] as $viewId) { if (isset($result[NextDomObj::VIEW][$viewId])) { continue; } $view = ViewManager::byId($viewId); if (!is_object($view)) { continue; } $result[NextDomObj::VIEW][$viewId] = $view; } // Test plan_id from parameters preg_match_all('/"plan_id":"([0-9]*)"/', $testString, $matches); foreach ($matches[1] as $planId) { if (isset($result[NextDomObj::PLAN][$planId])) { continue; } $plan = PlanHeaderManager::byId($planId); if (!is_object($plan)) { continue; } $result[NextDomObj::PLAN][$planId] = $plan; } return $result; } /** * Shutdown the system */ public static function haltSystem() { self::stopSystemAndExecuteCommand('shutdown -h now', __('Vous pouvez arrêter le système')); } /** * @TODO: ??? * * @param string $command Command to execute after stop preparation * @param string $errorMessage Message to show if actions failed * * @throws CoreException */ private static function stopSystemAndExecuteCommand($command, $errorMessage) { PluginManager::stop(); CacheManager::persist(); if (self::isCapable('sudo')) { exec(SystemHelper::getCmdSudo() . $command); } else { throw new CoreException($errorMessage); } } /** * Reboot the system */ public static function rebootSystem() { self::stopSystemAndExecuteCommand('reboot', __('Vous pouvez lancer le redémarrage du système')); } /** * Get hardware key * * @return bool|string * @throws \Exception */ public static function getHardwareKey() { $result = ConfigManager::byKey(ConfigKey::NEXTDOM_INSTALL_KEY); if ($result == '') { $result = substr(Utils::sha512(microtime() . ConfigManager::genKey()), 0, 63); ConfigManager::save(ConfigKey::NEXTDOM_INSTALL_KEY, $result); } return $result; } /** * Benchmark cache * * @return array * @throws \Exception */ public static function benchmark() { $result = []; $param = ['cache_write' => 5000, 'cache_read' => 5000, 'database_write_delete' => 1000, 'database_update' => 1000, 'database_replace' => 1000, 'database_read' => 50000, 'subprocess' => 200]; $startTime = Utils::getMicrotime(); for ($i = 0; $i < $param['cache_write']; $i++) { CacheManager::set('nextdom_benchmark', $i); } $result['cache_write_' . $param['cache_write']] = Utils::getMicrotime() - $startTime; $startTime = Utils::getMicrotime(); for ($i = 0; $i < $param['cache_read']; $i++) { $cache = CacheManager::byKey('nextdom_benchmark'); $cache->getValue(); } $result['cache_read_' . $param['cache_read']] = Utils::getMicrotime() - $startTime; $startTime = Utils::getMicrotime(); for ($i = 0; $i < $param['database_write_delete']; $i++) { $sql = 'DELETE FROM ' . ConfigManager::DB_CLASS_NAME . ' WHERE `key`="nextdom_benchmark" AND `plugin` = "core"'; try { DBHelper::exec($sql); } catch (\Exception $e) { } $sql = 'INSERT INTO ' . ConfigManager::DB_CLASS_NAME . ' SET `key` = "nextdom_benchmark", `plugin` = "core", `value` = "' . $i . '"'; try { DBHelper::exec($sql); } catch (\Exception $e) { } } $result['database_write_delete_' . $param['database_write_delete']] = Utils::getMicrotime() - $startTime; $sql = 'INSERT INTO ' . ConfigManager::DB_CLASS_NAME . ' SET `key` = "nextdom_benchmark", `plugin` = "core", `value` = "0"'; try { DBHelper::exec($sql); } catch (\Exception $e) { } $startTime = Utils::getMicrotime(); for ($i = 0; $i < $param['database_update']; $i++) { $sql = 'UPDATE ' . ConfigManager::DB_CLASS_NAME . ' SET `value`=:value WHERE `key` = "nextdom_benchmark" AND `plugin` = "core"'; try { DBHelper::exec($sql, ['value' => $i]); } catch (\Exception $e) { } } $result['database_update_' . $param['database_update']] = Utils::getMicrotime() - $startTime; $startTime = Utils::getMicrotime(); for ($i = 0; $i < $param['database_replace']; $i++) { ConfigManager::save('nextdom_benchmark', $i); } $result['database_replace_' . $param['database_replace']] = Utils::getMicrotime() - $startTime; $startTime = Utils::getMicrotime(); for ($i = 0; $i < $param['database_read']; $i++) { ConfigManager::byKey('nextdom_benchmark'); } $result['database_read_' . $param['database_read']] = Utils::getMicrotime() - $startTime; $startTime = Utils::getMicrotime(); for ($i = 0; $i < $param['subprocess']; $i++) { shell_exec('echo ' . $i); } $result['subprocess_' . $param['subprocess']] = Utils::getMicrotime() - $startTime; $total = 0; foreach ($result as $value) { $total += $value; } $result['total'] = $total; return $result; } public static function cleanDatabase() { LogHelper::clear('cleaningdb'); $cmd = __DIR__ . '/../../scripts/cleaningDb.php'; $cmd .= ' >> ' . LogHelper::getPathToLog('cleaningdb') . ' 2>&1 &'; SystemHelper::php($cmd, true); } } |