| Total Complexity | 133 |
| Total Lines | 1094 |
| Duplicated Lines | 0 % |
| Changes | 4 | ||
| Bugs | 0 | Features | 0 |
Complex classes like System often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use System, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 22 | class System |
||
| 23 | { |
||
| 24 | private MikoPBXConfig $mikoPBXConfig; |
||
| 25 | /** |
||
| 26 | * @var mixed|\Phalcon\Di\DiInterface|null |
||
| 27 | */ |
||
| 28 | private $di; |
||
| 29 | |||
| 30 | /** |
||
| 31 | * System constructor. |
||
| 32 | */ |
||
| 33 | public function __construct() |
||
| 39 | } |
||
| 40 | |||
| 41 | /** |
||
| 42 | * Relocate PHP error log to storage mount |
||
| 43 | */ |
||
| 44 | public static function setupPhpLog(): void |
||
| 55 | } |
||
| 56 | |||
| 57 | /** |
||
| 58 | * @return string |
||
| 59 | */ |
||
| 60 | public static function getPhpFile(): string |
||
| 61 | { |
||
| 62 | $logdir = self::getLogDir() . '/php'; |
||
| 63 | Util::mwMkdir($logdir); |
||
| 64 | return "$logdir/error.log"; |
||
| 65 | } |
||
| 66 | |||
| 67 | /** |
||
| 68 | * @return string |
||
| 69 | */ |
||
| 70 | public static function getLogDir(): string |
||
| 71 | { |
||
| 72 | $di = Di::getDefault(); |
||
| 73 | if ($di !== null){ |
||
| 74 | return $di->getConfig()->path('core.logsPath'); |
||
| 75 | } |
||
| 76 | return '/var/log'; |
||
| 77 | } |
||
| 78 | |||
| 79 | /** |
||
| 80 | * |
||
| 81 | */ |
||
| 82 | public static function rotatePhpLog(): void |
||
| 83 | { |
||
| 84 | $asteriskPath = Util::which('asterisk'); |
||
| 85 | $logrotatePath = Util::which('logrotate'); |
||
| 86 | |||
| 87 | $max_size = 2; |
||
| 88 | $f_name = self::getPhpFile(); |
||
| 89 | $text_config = (string)($f_name) . " { |
||
| 90 | nocreate |
||
| 91 | nocopytruncate |
||
| 92 | delaycompress |
||
| 93 | nomissingok |
||
| 94 | start 0 |
||
| 95 | rotate 9 |
||
| 96 | size {$max_size}M |
||
| 97 | missingok |
||
| 98 | noolddir |
||
| 99 | postrotate |
||
| 100 | {$asteriskPath} -rx 'logger reload' > /dev/null 2> /dev/null |
||
| 101 | endscript |
||
| 102 | }"; |
||
| 103 | $di = Di::getDefault(); |
||
| 104 | if ($di !== null){ |
||
| 105 | $varEtcPath = $di->getConfig()->path('core.varEtcPath'); |
||
| 106 | } else { |
||
| 107 | $varEtcPath = '/var/etc'; |
||
| 108 | } |
||
| 109 | $path_conf = $varEtcPath . '/php_logrotate_' . basename($f_name) . '.conf'; |
||
| 110 | file_put_contents($path_conf, $text_config); |
||
| 111 | $mb10 = $max_size * 1024 * 1024; |
||
| 112 | |||
| 113 | $options = ''; |
||
| 114 | if (Util::mFileSize($f_name) > $mb10) { |
||
| 115 | $options = '-f'; |
||
| 116 | } |
||
| 117 | Util::mwExecBg("{$logrotatePath} {$options} '{$path_conf}' > /dev/null 2> /dev/null"); |
||
| 118 | } |
||
| 119 | |||
| 120 | /** |
||
| 121 | * |
||
| 122 | */ |
||
| 123 | public static function gnatsLogRotate(): void |
||
| 165 | } |
||
| 166 | } |
||
| 167 | |||
| 168 | /** |
||
| 169 | * Старт сервера обработки очередей задач. |
||
| 170 | * |
||
| 171 | * @return array |
||
| 172 | */ |
||
| 173 | public function gnatsStart(): array |
||
| 174 | { |
||
| 175 | $confdir = '/etc/nats'; |
||
| 176 | Util::mwMkdir($confdir); |
||
| 177 | $logdir = self::getLogDir() . '/nats'; |
||
| 178 | Util::mwMkdir($logdir); |
||
| 179 | |||
| 180 | $pid_file = '/var/run/gnatsd.pid'; |
||
| 181 | $settings = [ |
||
| 182 | 'port' => '4223', |
||
| 183 | 'http_port' => '8223', |
||
| 184 | 'debug' => 'false', |
||
| 185 | 'trace' => 'false', |
||
| 186 | 'logtime' => 'true', |
||
| 187 | 'pid_file' => $pid_file, |
||
| 188 | 'max_connections' => '1000', |
||
| 189 | 'max_payload' => '1000000', |
||
| 190 | 'max_control_line' => '512', |
||
| 191 | 'sessions_path' => $logdir, |
||
| 192 | 'log_file' => "{$logdir}/gnatsd.log", |
||
| 193 | ]; |
||
| 194 | $config = ''; |
||
| 195 | foreach ($settings as $key => $val) { |
||
| 196 | $config .= "{$key}: {$val} \n"; |
||
| 197 | } |
||
| 198 | $conf_file = "{$confdir}/natsd.conf"; |
||
| 199 | Util::fileWriteContent($conf_file, $config); |
||
| 200 | |||
| 201 | $lic = $this->mikoPBXConfig->getGeneralSettings('PBXLicense'); |
||
| 202 | file_put_contents($logdir . '/license.key', $lic); |
||
| 203 | |||
| 204 | if (file_exists($pid_file)) { |
||
| 205 | $killPath = Util::which('kill'); |
||
| 206 | $catPath = Util::which('kill'); |
||
| 207 | Util::mwExec("{$killPath} $({$catPath} {$pid_file})"); |
||
| 208 | } |
||
| 209 | $gnatsdPath = Util::which('gnatsd'); |
||
| 210 | Util::mwExecBg("{$gnatsdPath} --config {$conf_file}", "{$logdir}/gnats_process.log"); |
||
| 211 | |||
| 212 | return [ |
||
| 213 | 'result' => 'Success', |
||
| 214 | ]; |
||
| 215 | } |
||
| 216 | |||
| 217 | /** |
||
| 218 | * Reboots the system after calling system_reboot_cleanup() |
||
| 219 | * |
||
| 220 | */ |
||
| 221 | public static function rebootSync(): void |
||
| 222 | { |
||
| 223 | $mikopbx_rebootPath = Util::which('mikopbx_reboot'); |
||
| 224 | Util::mwExec("{$mikopbx_rebootPath} > /dev/null 2>&1"); |
||
| 225 | } |
||
| 226 | |||
| 227 | /** |
||
| 228 | * Reboots the system after calling system_reboot_cleanup() |
||
| 229 | * |
||
| 230 | */ |
||
| 231 | public static function rebootSyncBg(): void |
||
| 232 | { |
||
| 233 | $mikopbx_rebootPath = Util::which('mikopbx_reboot'); |
||
| 234 | Util::mwExecBg("{$mikopbx_rebootPath} > /dev/null 2>&1"); |
||
| 235 | } |
||
| 236 | |||
| 237 | /** |
||
| 238 | * Shutdown the system. |
||
| 239 | */ |
||
| 240 | public static function shutdown(): void |
||
| 241 | { |
||
| 242 | $shutdownPath = Util::which('shutdown'); |
||
| 243 | Util::mwExec("{$shutdownPath} > /dev/null 2>&1"); |
||
| 244 | } |
||
| 245 | |||
| 246 | /** |
||
| 247 | * Рестарт сетевых интерфейсов. |
||
| 248 | */ |
||
| 249 | public static function networkReload(): void |
||
| 250 | { |
||
| 251 | $system = new System(); |
||
| 252 | $system->hostnameConfigure(); |
||
| 253 | |||
| 254 | $network = new Network(); |
||
| 255 | $network->resolvConfGenerate(); |
||
| 256 | $network->loConfigure(); |
||
| 257 | $network->lanConfigure(); |
||
| 258 | } |
||
| 259 | |||
| 260 | /** |
||
| 261 | * Устанавливаем имя хост текущей машины. |
||
| 262 | **/ |
||
| 263 | public function hostnameConfigure(): int |
||
| 264 | { |
||
| 265 | $data = Network::getHostName(); |
||
| 266 | $hosts_conf = "127.0.0.1 localhost\n" . |
||
| 267 | "127.0.0.1 {$data['hostname']}\n"; |
||
| 268 | if ( ! empty($data['domain'])) { |
||
| 269 | $hosts_conf .= "127.0.0.1 {$data['hostname']}.{$data['domain']}\n"; |
||
| 270 | } |
||
| 271 | Util::fileWriteContent('/etc/hosts', $hosts_conf); |
||
| 272 | |||
| 273 | $hostnamePath = Util::which('hostname'); |
||
| 274 | return Util::mwExec($hostnamePath.' '. escapeshellarg("{$data['hostname']}")); |
||
|
|
|||
| 275 | } |
||
| 276 | |||
| 277 | |||
| 278 | /** |
||
| 279 | * Получение сведений о системе. |
||
| 280 | * |
||
| 281 | * @return array |
||
| 282 | */ |
||
| 283 | public static function getInfo(): array |
||
| 299 | } |
||
| 300 | |||
| 301 | /** |
||
| 302 | * Возвращает информацию по загрузке CPU. |
||
| 303 | */ |
||
| 304 | public static function getCpu() |
||
| 305 | { |
||
| 306 | $ut = []; |
||
| 307 | $grepPath = Util::which('grep'); |
||
| 308 | $mpstatPath = Util::which('mpstat'); |
||
| 309 | Util::mwExec("{$mpstatPath} | {$grepPath} all", $ut); |
||
| 310 | preg_match("/^.*\s+all\s+.*\s+.*\s+.*\s+.*\s+.*\s+.*\s+.*\s+.*\s+(.*)\s*.*/i", $ut[0], $matches); |
||
| 311 | $rv = 100 - $matches[1]; |
||
| 312 | |||
| 313 | if (100 < $rv) { |
||
| 314 | $rv = 100; |
||
| 315 | } |
||
| 316 | |||
| 317 | return round($rv, 2); |
||
| 318 | } |
||
| 319 | |||
| 320 | /** |
||
| 321 | * Получаем информацию по времени работы ПК. |
||
| 322 | */ |
||
| 323 | public static function getUpTime(): string |
||
| 324 | { |
||
| 325 | $ut = []; |
||
| 326 | $uptimePath = Util::which('uptime'); |
||
| 327 | $awkPath = Util::which('awk'); |
||
| 328 | Util::mwExec("{$uptimePath} | {$awkPath} -F \" |,\" '{print $5}'", $ut); |
||
| 329 | |||
| 330 | return implode('', $ut); |
||
| 331 | } |
||
| 332 | |||
| 333 | /** |
||
| 334 | * Получаем информацию по оперативной памяти. |
||
| 335 | */ |
||
| 336 | public static function getMemInfo(): array |
||
| 351 | } |
||
| 352 | |||
| 353 | /** |
||
| 354 | * Обновление кофнигурации кастомных файлов. |
||
| 355 | * |
||
| 356 | * @return array |
||
| 357 | */ |
||
| 358 | public static function updateCustomFiles() |
||
| 359 | { |
||
| 360 | $actions = []; |
||
| 361 | /** @var \MikoPBX\Common\Models\CustomFiles $res_data */ |
||
| 362 | $res_data = CustomFiles::find("changed = '1'"); |
||
| 363 | foreach ($res_data as $file_data) { |
||
| 364 | // Всегда рестрартуем все модули asterisk (только чтение конфигурации). |
||
| 365 | $actions['asterisk_coreReload'] = 100; |
||
| 366 | $filename = basename($file_data->filepath); |
||
| 367 | switch ($filename) { |
||
| 368 | case 'manager.conf': |
||
| 369 | $actions['manager'] = 10; |
||
| 370 | break; |
||
| 371 | case 'musiconhold.conf': |
||
| 372 | $actions['musiconhold'] = 100; |
||
| 373 | break; |
||
| 374 | case 'modules.conf': |
||
| 375 | $actions['modules'] = 10; |
||
| 376 | break; |
||
| 377 | case 'http.conf': |
||
| 378 | $actions['manager'] = 10; |
||
| 379 | break; |
||
| 380 | case 'root': // crontabs |
||
| 381 | $actions['cron'] = 10; |
||
| 382 | break; |
||
| 383 | case 'queues.conf': |
||
| 384 | $actions['queues'] = 10; |
||
| 385 | break; |
||
| 386 | case 'features.conf': |
||
| 387 | $actions['features'] = 10; |
||
| 388 | break; |
||
| 389 | case 'ntp.conf': |
||
| 390 | $actions['systemtime'] = 100; |
||
| 391 | break; |
||
| 392 | case 'jail.local': // fail2ban |
||
| 393 | $actions['firewall'] = 100; |
||
| 394 | break; |
||
| 395 | } |
||
| 396 | } |
||
| 397 | |||
| 398 | asort($actions); |
||
| 399 | $result = self::invokeActions($actions); |
||
| 400 | if ($result['result'] !== 'ERROR') { |
||
| 401 | // Зафиксируем результат работы. |
||
| 402 | foreach ($res_data as $file_data) { |
||
| 403 | /** @var \MikoPBX\Common\Models\CustomFiles $file_data */ |
||
| 404 | $file_data->writeAttribute("changed", '0'); |
||
| 405 | $file_data->save(); |
||
| 406 | } |
||
| 407 | } |
||
| 408 | |||
| 409 | return $result; |
||
| 410 | } |
||
| 411 | |||
| 412 | /** |
||
| 413 | * Выполнение набора действий по рестарту модулей системы. |
||
| 414 | * |
||
| 415 | * @param $actions |
||
| 416 | * |
||
| 417 | * @return array|mixed |
||
| 418 | */ |
||
| 419 | public static function invokeActions($actions) |
||
| 420 | { |
||
| 421 | $result = [ |
||
| 422 | 'result' => 'Success', |
||
| 423 | ]; |
||
| 424 | foreach ($actions as $action => $value) { |
||
| 425 | $res = null; |
||
| 426 | switch ($action) { |
||
| 427 | case 'manager': |
||
| 428 | $res = PBX::managerReload(); |
||
| 429 | break; |
||
| 430 | case 'musiconhold': |
||
| 431 | $res = PBX::musicOnHoldReload(); |
||
| 432 | break; |
||
| 433 | case 'modules': |
||
| 434 | $res = PBX::modulesReload(); |
||
| 435 | break; |
||
| 436 | case 'cron': |
||
| 437 | $system = new System(); |
||
| 438 | $system->cronConfigure(); |
||
| 439 | break; |
||
| 440 | case 'queues': |
||
| 441 | $res = QueueConf::queueReload(); |
||
| 442 | break; |
||
| 443 | case 'features': |
||
| 444 | $res = PBX::managerReload(); |
||
| 445 | break; |
||
| 446 | case 'systemtime': |
||
| 447 | $res = TimeManagement::setDate(''); |
||
| 448 | break; |
||
| 449 | case 'firewall': |
||
| 450 | $res = Firewall::reloadFirewall(); |
||
| 451 | break; |
||
| 452 | case 'asterisk_coreReload': |
||
| 453 | PBX::sipReload(); |
||
| 454 | PBX::iaxReload(); |
||
| 455 | PBX::dialplanReload(); |
||
| 456 | $res = PBX::coreReload(); |
||
| 457 | break; |
||
| 458 | } |
||
| 459 | if ($res !== null && $res['result'] === 'ERROR') { |
||
| 460 | $result = $res['result']; |
||
| 461 | break; |
||
| 462 | } |
||
| 463 | } |
||
| 464 | |||
| 465 | return $result; |
||
| 466 | } |
||
| 467 | |||
| 468 | /** |
||
| 469 | * Настройка cron. Запуск демона. |
||
| 470 | * |
||
| 471 | * @return int |
||
| 472 | */ |
||
| 473 | public function cronConfigure(): int |
||
| 474 | { |
||
| 475 | $booting = $this->di->getRegistry()->booting; |
||
| 476 | $this->cronGenerate($booting); |
||
| 477 | if (Util::isSystemctl()) { |
||
| 478 | $systemctlPath = Util::which('systemctl'); |
||
| 479 | Util::mwExec("{$systemctlPath} restart cron"); |
||
| 480 | } else { |
||
| 481 | $crondPath = Util::which('crond'); |
||
| 482 | Util::killByName($crondPath); |
||
| 483 | Util::mwExec("{$crondPath} -L /dev/null -l 8"); |
||
| 484 | } |
||
| 485 | return 0; |
||
| 486 | } |
||
| 487 | |||
| 488 | /** |
||
| 489 | * Генератор конфига cron. |
||
| 490 | * |
||
| 491 | * @param bool $boot |
||
| 492 | */ |
||
| 493 | private function cronGenerate($boot = true): void |
||
| 544 | } |
||
| 545 | |||
| 546 | public static function convertConfig($config_file = ''): array |
||
| 547 | { |
||
| 548 | $result = [ |
||
| 549 | 'result' => 'Success', |
||
| 550 | 'message' => '', |
||
| 551 | ]; |
||
| 552 | $di = Di::getDefault(); |
||
| 553 | if ($di !== null){ |
||
| 554 | $tempDir = $di->getConfig()->path('core.tempPath'); |
||
| 584 | } |
||
| 585 | |||
| 586 | /** |
||
| 587 | * Upgrade MikoPBX from uploaded IMG file |
||
| 588 | * |
||
| 589 | * @param string $tempFilename path to uploaded image |
||
| 590 | * |
||
| 591 | * @return array |
||
| 592 | */ |
||
| 593 | public static function upgradeFromImg(string $tempFilename): array |
||
| 594 | { |
||
| 595 | $result = [ |
||
| 596 | 'result' => 'Success', |
||
| 597 | 'message' => 'In progress...', |
||
| 598 | ]; |
||
| 599 | |||
| 600 | if (!file_exists($tempFilename)){ |
||
| 601 | return [ |
||
| 602 | 'result' => 'ERROR', |
||
| 603 | 'data' => "Update file '{$tempFilename}' not found.", |
||
| 604 | ]; |
||
| 605 | } |
||
| 606 | |||
| 607 | if ( ! file_exists('/var/etc/cfdevice')) { |
||
| 608 | return [ |
||
| 609 | 'result' => 'ERROR', |
||
| 610 | 'data' => "The system is not installed", |
||
| 611 | 'path'=> $tempFilename |
||
| 612 | ]; |
||
| 613 | } |
||
| 614 | $dev = trim(file_get_contents('/var/etc/cfdevice')); |
||
| 615 | |||
| 616 | $link = '/tmp/firmware_update.img'; |
||
| 617 | Util::createUpdateSymlink($tempFilename, $link); |
||
| 618 | $mikopbx_firmwarePath = Util::which('mikopbx_firmware'); |
||
| 619 | Util::mwExecBg("{$mikopbx_firmwarePath} recover_upgrade {$link} /dev/{$dev}"); |
||
| 620 | |||
| 621 | return $result; |
||
| 622 | } |
||
| 623 | |||
| 624 | /** |
||
| 625 | * Download IMG from MikoPBX repository |
||
| 626 | * |
||
| 627 | * @param $data |
||
| 628 | * |
||
| 629 | * @return mixed |
||
| 630 | */ |
||
| 631 | public static function downloadNewFirmware($data) |
||
| 632 | { |
||
| 633 | $di = Di::getDefault(); |
||
| 634 | if ($di !== null){ |
||
| 635 | $tempDir = $di->getConfig()->path('core.uploadPath'); |
||
| 636 | } else { |
||
| 637 | $tempDir = '/tmp'; |
||
| 638 | } |
||
| 639 | $rmPath = Util::which('rm'); |
||
| 640 | $module = 'NewFirmware'; |
||
| 641 | if ( ! file_exists($tempDir . "/{$module}")) { |
||
| 642 | Util::mwMkdir($tempDir . "/{$module}"); |
||
| 643 | } else { |
||
| 644 | // Чистим файлы, загруженные онлайн. |
||
| 645 | Util::mwExec("{$rmPath} -rf {$tempDir}/{$module}/* "); |
||
| 646 | } |
||
| 647 | if (file_exists("{$tempDir}/update.img")) { |
||
| 648 | // Чистим вручную загруженный файл. |
||
| 649 | Util::mwExec("{$rmPath} -rf {$tempDir}/update.img"); |
||
| 650 | } |
||
| 651 | |||
| 652 | $download_settings = [ |
||
| 653 | 'res_file' => "{$tempDir}/{$module}/update.img", |
||
| 654 | 'url' => $data['url'], |
||
| 655 | 'module' => $module, |
||
| 656 | 'md5' => $data['md5'], |
||
| 657 | 'action' => $module, |
||
| 658 | ]; |
||
| 659 | |||
| 660 | $workerDownloaderPath = Util::getFilePathByClassName(WorkerDownloader::class); |
||
| 661 | |||
| 662 | file_put_contents($tempDir . "/{$module}/progress", '0'); |
||
| 663 | file_put_contents( |
||
| 664 | $tempDir . "/{$module}/download_settings.json", |
||
| 665 | json_encode($download_settings, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) |
||
| 666 | ); |
||
| 667 | $phpPath = Util::which('php'); |
||
| 668 | Util::mwExecBg("{$phpPath} -f {$workerDownloaderPath} " . $tempDir . "/{$module}/download_settings.json"); |
||
| 669 | $result = []; |
||
| 670 | $result['result'] = 'Success'; |
||
| 671 | |||
| 672 | return $result; |
||
| 673 | } |
||
| 674 | |||
| 675 | /** |
||
| 676 | * Return download Firnware from remote repository progress |
||
| 677 | * |
||
| 678 | * @return array |
||
| 679 | */ |
||
| 680 | public static function firmwareDownloadStatus(): array |
||
| 681 | { |
||
| 682 | clearstatcache(); |
||
| 683 | $result = [ |
||
| 684 | 'result' => 'Success', |
||
| 685 | ]; |
||
| 686 | $di = Di::getDefault(); |
||
| 687 | if ($di !== null){ |
||
| 688 | $tempDir = $di->getConfig()->path('core.uploadPath'); |
||
| 689 | } else { |
||
| 690 | $tempDir = '/tmp'; |
||
| 691 | } |
||
| 692 | $modulesDir = $tempDir . '/NewFirmware'; |
||
| 693 | $progress_file = $modulesDir . '/progress'; |
||
| 694 | |||
| 695 | $error = ''; |
||
| 696 | if (file_exists($modulesDir . '/error')) { |
||
| 697 | $error = trim(file_get_contents($modulesDir . '/error')); |
||
| 698 | } |
||
| 699 | |||
| 700 | if ( ! file_exists($progress_file)) { |
||
| 701 | $result['d_status_progress'] = '0'; |
||
| 702 | $result['d_status'] = 'NOT_FOUND'; |
||
| 703 | } elseif ('' !== $error) { |
||
| 704 | $result['d_status'] = 'DOWNLOAD_ERROR'; |
||
| 705 | $result['d_status_progress'] = file_get_contents($progress_file); |
||
| 706 | $result['d_error'] = $error; |
||
| 707 | } elseif ('100' === file_get_contents($progress_file)) { |
||
| 708 | $result['d_status_progress'] = '100'; |
||
| 709 | $result['d_status'] = 'DOWNLOAD_COMPLETE'; |
||
| 710 | $result['filePath'] = "{$tempDir}/NewFirmware/update.img"; |
||
| 711 | } else { |
||
| 712 | $result['d_status_progress'] = file_get_contents($progress_file); |
||
| 713 | $d_pid = Util::getPidOfProcess($tempDir . '/NewFirmware/download_settings.json'); |
||
| 714 | if (empty($d_pid)) { |
||
| 715 | $result['d_status'] = 'DOWNLOAD_ERROR'; |
||
| 716 | $error = ''; |
||
| 717 | if (file_exists($modulesDir . '/error')) { |
||
| 718 | $error = file_get_contents($modulesDir . '/error'); |
||
| 719 | } |
||
| 720 | $result['d_error'] = $error; |
||
| 721 | } else { |
||
| 722 | $result['d_status'] = 'DOWNLOAD_IN_PROGRESS'; |
||
| 723 | } |
||
| 724 | } |
||
| 725 | |||
| 726 | return $result; |
||
| 727 | } |
||
| 728 | |||
| 729 | /** |
||
| 730 | * Запуск процесса фоновой загрузки доп. модуля АТС. |
||
| 731 | * |
||
| 732 | * @param $module |
||
| 733 | * @param $url |
||
| 734 | * @param $md5 |
||
| 735 | * |
||
| 736 | * @return void |
||
| 737 | */ |
||
| 738 | public static function moduleStartDownload($module, $url, $md5): void |
||
| 739 | { |
||
| 740 | $di = Di::getDefault(); |
||
| 741 | if ($di !== null){ |
||
| 742 | $tempDir = $di->getConfig()->path('core.uploadPath'); |
||
| 743 | } else { |
||
| 744 | $tempDir = '/tmp'; |
||
| 745 | } |
||
| 746 | |||
| 747 | $moduleDirTmp = "{$tempDir}/{$module}"; |
||
| 748 | Util::mwMkdir($moduleDirTmp); |
||
| 749 | |||
| 750 | $download_settings = [ |
||
| 751 | 'res_file' => "$moduleDirTmp/modulefile.zip", |
||
| 752 | 'url' => $url, |
||
| 753 | 'module' => $module, |
||
| 754 | 'md5' => $md5, |
||
| 755 | 'action' => 'moduleInstall', |
||
| 756 | ]; |
||
| 757 | if (file_exists("$moduleDirTmp/error")) { |
||
| 758 | unlink("$moduleDirTmp/error"); |
||
| 759 | } |
||
| 760 | if (file_exists("$moduleDirTmp/installed")) { |
||
| 761 | unlink("$moduleDirTmp/installed"); |
||
| 762 | } |
||
| 763 | file_put_contents("$moduleDirTmp/progress", '0'); |
||
| 764 | file_put_contents( |
||
| 765 | "$moduleDirTmp/download_settings.json", |
||
| 766 | json_encode($download_settings, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) |
||
| 767 | ); |
||
| 768 | $workerDownloaderPath = Util::getFilePathByClassName(WorkerDownloader::class); |
||
| 769 | $phpPath = Util::which('php'); |
||
| 770 | Util::mwExecBg("{$phpPath} -f {$workerDownloaderPath} $moduleDirTmp/download_settings.json"); |
||
| 771 | } |
||
| 772 | |||
| 773 | /** |
||
| 774 | * Возвращает статус скачивания модуля. |
||
| 775 | * |
||
| 776 | * @param $moduleUniqueID |
||
| 777 | * |
||
| 778 | * @return array |
||
| 779 | */ |
||
| 780 | public static function moduleDownloadStatus(string $moduleUniqueID): array |
||
| 781 | { |
||
| 782 | clearstatcache(); |
||
| 783 | $result = [ |
||
| 784 | 'result' => 'Success', |
||
| 785 | 'data' => null, |
||
| 786 | ]; |
||
| 787 | $di = Di::getDefault(); |
||
| 788 | if ($di !== null){ |
||
| 789 | $tempDir = $di->getConfig()->path('core.uploadPath'); |
||
| 790 | } else { |
||
| 791 | $tempDir = '/tmp'; |
||
| 792 | } |
||
| 793 | $moduleDirTmp = $tempDir . '/' . $moduleUniqueID; |
||
| 794 | $progress_file = $moduleDirTmp . '/progress'; |
||
| 795 | $error = ''; |
||
| 796 | if (file_exists($moduleDirTmp . '/error')) { |
||
| 797 | $error = trim(file_get_contents($moduleDirTmp . '/error')); |
||
| 798 | } |
||
| 799 | |||
| 800 | // Ожидание запуска процесса загрузки. |
||
| 801 | $d_pid = Util::getPidOfProcess("{$moduleDirTmp}/download_settings.json"); |
||
| 802 | if (empty($d_pid)) { |
||
| 803 | usleep(500000); |
||
| 804 | } |
||
| 805 | |||
| 806 | if ( ! file_exists($progress_file)) { |
||
| 807 | $result['d_status_progress'] = '0'; |
||
| 808 | $result['d_status'] = 'NOT_FOUND'; |
||
| 809 | } elseif ('' !== $error) { |
||
| 810 | $result['d_status'] = 'DOWNLOAD_ERROR'; |
||
| 811 | $result['d_status_progress'] = file_get_contents($progress_file); |
||
| 812 | $result['d_error'] = $error; |
||
| 813 | } elseif ('100' === file_get_contents($progress_file)) { |
||
| 814 | $result['d_status_progress'] = '100'; |
||
| 815 | $result['d_status'] = 'DOWNLOAD_COMPLETE'; |
||
| 816 | $result['filePath'] = "$moduleDirTmp/modulefile.zip"; |
||
| 817 | } else { |
||
| 818 | $result['d_status_progress'] = file_get_contents($progress_file); |
||
| 819 | $d_pid = Util::getPidOfProcess($moduleDirTmp . '/download_settings.json'); |
||
| 820 | if (empty($d_pid)) { |
||
| 821 | $result['d_status'] = 'DOWNLOAD_ERROR'; |
||
| 822 | $error = ''; |
||
| 823 | if (file_exists($moduleDirTmp . '/error')) { |
||
| 824 | $error = file_get_contents($moduleDirTmp . '/error'); |
||
| 825 | } |
||
| 826 | $result['d_error'] = $error; |
||
| 827 | } else { |
||
| 828 | $result['d_status'] = 'DOWNLOAD_IN_PROGRESS'; |
||
| 829 | } |
||
| 830 | } |
||
| 831 | |||
| 832 | return $result; |
||
| 833 | } |
||
| 834 | |||
| 835 | |||
| 836 | /** |
||
| 837 | * Получение информации о публичном IP. |
||
| 838 | * |
||
| 839 | * @return array |
||
| 840 | */ |
||
| 841 | public static function getExternalIpInfo(): array |
||
| 842 | { |
||
| 843 | $result = [ |
||
| 844 | 'result' => 'Error', |
||
| 845 | 'message' => null, |
||
| 846 | 'data' => null, |
||
| 847 | ]; |
||
| 848 | $curl = curl_init(); |
||
| 849 | if($curl === false){ |
||
| 850 | $result['message'] = 'Can not init cURL'; |
||
| 851 | return $result; |
||
| 852 | } |
||
| 853 | $url = 'https://ipinfo.io/json'; |
||
| 854 | curl_setopt($curl, CURLOPT_URL, $url); |
||
| 855 | curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); |
||
| 856 | curl_setopt($curl, CURLOPT_TIMEOUT, 2); |
||
| 857 | |||
| 858 | try { |
||
| 859 | $resultrequest = curl_exec($curl); |
||
| 860 | } catch (Exception $e) { |
||
| 861 | $result['message'] = $e->getMessage(); |
||
| 862 | return $result; |
||
| 863 | } |
||
| 864 | curl_close($curl); |
||
| 865 | if (Util::isJson($resultrequest)) { |
||
| 866 | $result['result'] = 'Success'; |
||
| 867 | $result['data'] = json_decode($resultrequest, true); |
||
| 868 | } else { |
||
| 869 | $result['message'] = 'Error format data ' . $resultrequest; |
||
| 870 | } |
||
| 871 | |||
| 872 | return $result; |
||
| 873 | } |
||
| 874 | |||
| 875 | /** |
||
| 876 | * Подгрузка дополнительных модулей ядра. |
||
| 877 | */ |
||
| 878 | public function loadKernelModules(): void |
||
| 879 | { |
||
| 880 | $modprobePath = Util::which('modprobe'); |
||
| 881 | $ulimitPath = Util::which('ulimit'); |
||
| 882 | |||
| 883 | Util::mwExec("{$modprobePath} -q dahdi"); |
||
| 884 | Util::mwExec("{$modprobePath} -q dahdi_transcode"); |
||
| 885 | Util::mwExec("{$ulimitPath} -n 4096"); |
||
| 886 | Util::mwExec("{$ulimitPath} -p 4096"); |
||
| 887 | } |
||
| 888 | |||
| 889 | /** |
||
| 890 | * Start Nginx and php-fpm |
||
| 891 | **/ |
||
| 892 | public function nginxStart(): void |
||
| 893 | { |
||
| 894 | if (Util::isSystemctl()) { |
||
| 895 | Util::mwExec('systemctl restart php7.4-fpm'); |
||
| 896 | Util::mwExec('systemctl restart nginx.service'); |
||
| 897 | } else { |
||
| 898 | Util::killByName('php-fpm'); |
||
| 899 | Util::killByName('nginx'); |
||
| 900 | Util::mwExec('php-fpm -c /etc/php.ini'); |
||
| 901 | Util::mwExec('nginx'); |
||
| 902 | } |
||
| 903 | } |
||
| 904 | |||
| 905 | /** |
||
| 906 | * Write additional settings the nginx.conf |
||
| 907 | * |
||
| 908 | * @param bool $not_ssl |
||
| 909 | * @param int $level |
||
| 910 | */ |
||
| 911 | public function nginxGenerateConf($not_ssl = false, $level = 0): void |
||
| 912 | { |
||
| 913 | $configPath = '/etc/nginx/mikopbx/conf.d'; |
||
| 914 | $httpConfigFile = "{$configPath}/http-server.conf"; |
||
| 915 | $httpsConfigFile = "{$configPath}/https-server.conf"; |
||
| 916 | |||
| 917 | $dns_server = '127.0.0.1'; |
||
| 918 | |||
| 919 | $net = new Network(); |
||
| 920 | $dns = $net->getHostDNS(); |
||
| 921 | foreach ($dns as $ns) { |
||
| 922 | if (Verify::isIpAddress($ns)) { |
||
| 923 | $dns_server = trim($ns); |
||
| 924 | break; |
||
| 925 | } |
||
| 926 | } |
||
| 927 | |||
| 928 | // HTTP |
||
| 929 | $WEBPort = $this->mikoPBXConfig->getGeneralSettings('WEBPort'); |
||
| 930 | $WEBHTTPSPort = $this->mikoPBXConfig->getGeneralSettings('WEBHTTPSPort'); |
||
| 931 | |||
| 932 | $config = file_get_contents("{$httpConfigFile}.original"); |
||
| 933 | $config = str_replace(['<DNS>', '<WEBPort>'], [$dns_server, $WEBPort], $config); |
||
| 934 | |||
| 935 | $RedirectToHttps = $this->mikoPBXConfig->getGeneralSettings('RedirectToHttps'); |
||
| 936 | if ($RedirectToHttps === '1' && $not_ssl === false) { |
||
| 937 | $conf_data = 'if ( $remote_addr != "127.0.0.1" ) {' . PHP_EOL |
||
| 938 | . ' ' . 'return 301 https://$host:' . $WEBHTTPSPort . '$request_uri;' . PHP_EOL |
||
| 939 | . ' }' . PHP_EOL; |
||
| 940 | $config = str_replace('include mikopbx/locations/*.conf;', $conf_data, $config); |
||
| 941 | } |
||
| 942 | file_put_contents($httpConfigFile, $config); |
||
| 943 | |||
| 944 | // SSL |
||
| 945 | $WEBHTTPSPublicKey = $this->mikoPBXConfig->getGeneralSettings('WEBHTTPSPublicKey'); |
||
| 946 | $WEBHTTPSPrivateKey = $this->mikoPBXConfig->getGeneralSettings('WEBHTTPSPrivateKey'); |
||
| 947 | if ( |
||
| 948 | $not_ssl === false |
||
| 949 | && ! empty($WEBHTTPSPublicKey) |
||
| 950 | && ! empty($WEBHTTPSPrivateKey) |
||
| 951 | ) { |
||
| 952 | $public_filename = '/etc/ssl/certs/nginx.crt'; |
||
| 953 | $private_filename = '/etc/ssl/private/nginx.key'; |
||
| 954 | file_put_contents($public_filename, $WEBHTTPSPublicKey); |
||
| 955 | file_put_contents($private_filename, $WEBHTTPSPrivateKey); |
||
| 956 | $config = file_get_contents("{$httpsConfigFile}.original"); |
||
| 957 | $config = str_replace(['<DNS>', '<WEBHTTPSPort>'], [$dns_server, $WEBHTTPSPort], $config); |
||
| 958 | file_put_contents($httpsConfigFile, $config); |
||
| 959 | } elseif (file_exists($httpsConfigFile)) { |
||
| 960 | unlink($httpsConfigFile); |
||
| 961 | } |
||
| 962 | |||
| 963 | // Test work |
||
| 964 | $out = []; |
||
| 965 | Util::mwExec('nginx -t', $out); |
||
| 966 | $res = implode($out); |
||
| 967 | if ($level < 1 && false !== strpos($res, 'test failed')) { |
||
| 968 | ++$level; |
||
| 969 | Util::sysLogMsg('nginx', 'Failed test config file. SSL will be disable...'); |
||
| 970 | $this->nginxGenerateConf(true, $level); |
||
| 971 | } |
||
| 972 | } |
||
| 973 | |||
| 974 | public function syslogDaemonStart(): void |
||
| 975 | { |
||
| 976 | $syslog_file = '/var/log/messages'; |
||
| 977 | $log_file = self::getSyslogFile(); |
||
| 978 | if ( ! file_exists($syslog_file)) { |
||
| 979 | file_put_contents($syslog_file, ''); |
||
| 980 | } |
||
| 981 | $syslogdPath = Util::which('syslogd'); |
||
| 982 | $busyboxPath = Util::which('busybox'); |
||
| 983 | $logreadPath = Util::which('logread'); |
||
| 984 | $killPath = Util::which('kill'); |
||
| 985 | $pid = Util::getPidOfProcess($syslogdPath); |
||
| 986 | if ( ! empty($pid)) { |
||
| 987 | $options = file_exists($log_file) ? '>' : ''; |
||
| 988 | Util::mwExec("{$busyboxPath} {$logreadPath} 2> /dev/null >" . $options . $log_file); |
||
| 989 | // Завершаем процесс. |
||
| 990 | Util::mwExec("{$busyboxPath} {$killPath} '$pid'"); |
||
| 991 | } |
||
| 992 | |||
| 993 | Util::createUpdateSymlink($log_file, $syslog_file); |
||
| 994 | Util::mwExec("{$syslogdPath} -O {$log_file} -b 10 -s 10240"); |
||
| 995 | } |
||
| 996 | |||
| 997 | public static function getSyslogFile(): string |
||
| 1002 | } |
||
| 1003 | |||
| 1004 | /** |
||
| 1005 | * Будет вызван после старта asterisk. |
||
| 1006 | */ |
||
| 1007 | public function onAfterPbxStarted(): void |
||
| 1013 | } |
||
| 1014 | } |
||
| 1015 | |||
| 1016 | /** |
||
| 1017 | * Запуск SSH сервера. |
||
| 1018 | **/ |
||
| 1019 | public function sshdConfigure(): array |
||
| 1020 | { |
||
| 1021 | $result = [ |
||
| 1022 | 'result' => 'Success', |
||
| 1023 | ]; |
||
| 1024 | $dropbear_dir = '/etc/dropbear'; |
||
| 1025 | Util::mwMkdir($dropbear_dir); |
||
| 1026 | |||
| 1027 | $keytypes = [ |
||
| 1028 | "rsa" => "SSHRsaKey", |
||
| 1029 | "dss" => "SSHDssKey", |
||
| 1030 | "ecdsa" => "SSHecdsaKey" // SSHecdsaKey // SSHEcdsaKey |
||
| 1031 | ]; |
||
| 1032 | // Получаем ключ из базы данных. |
||
| 1033 | // $config = array(); |
||
| 1034 | foreach ($keytypes as $keytype => $db_key) { |
||
| 1035 | $res_keyfilepath = "{$dropbear_dir}/dropbear_" . $keytype . "_host_key"; |
||
| 1036 | $key = $this->mikoPBXConfig->getGeneralSettings($db_key); |
||
| 1037 | $key = (isset($key) && is_string($key)) ? trim($key) : ""; |
||
| 1038 | if (strlen($key) > 100) { |
||
| 1039 | // Сохраняем ключ в файл. |
||
| 1040 | file_put_contents($res_keyfilepath, base64_decode($key)); |
||
| 1041 | } |
||
| 1042 | // Если ключ не существует, создадим его и обновим информацию в базе данных. |
||
| 1043 | if ( ! file_exists($res_keyfilepath)) { |
||
| 1044 | // Генерация. |
||
| 1045 | $dropbearkeyPath = Util::which('dropbearkey'); |
||
| 1046 | Util::mwExec("{$dropbearkeyPath} -t $keytype -f $res_keyfilepath"); |
||
| 1047 | // Сохранение. |
||
| 1048 | $new_key = base64_encode(file_get_contents($res_keyfilepath)); |
||
| 1049 | $this->mikoPBXConfig->setGeneralSettings("$db_key", "$new_key"); |
||
| 1050 | } |
||
| 1051 | } |
||
| 1052 | $ssh_port = escapeshellcmd($this->mikoPBXConfig->getGeneralSettings('SSHPort')); |
||
| 1053 | // Перезапускаем сервис dropbear; |
||
| 1054 | Util::killByName('dropbear'); |
||
| 1055 | usleep(500000); |
||
| 1056 | Util::mwExec("dropbear -p '{$ssh_port}' -c /etc/rc/hello > /var/log/dropbear_start.log"); |
||
| 1057 | $this->generateAuthorizedKeys(); |
||
| 1058 | |||
| 1059 | $result['data'] = @file_get_contents('/var/log/dropbear_start.log'); |
||
| 1060 | if ( ! empty($result['data'])) { |
||
| 1061 | $result['result'] = 'ERROR'; |
||
| 1062 | } |
||
| 1063 | |||
| 1064 | // Устанавливаем пароль на пользователя ОС. |
||
| 1065 | $this->updateShellPassword(); |
||
| 1066 | |||
| 1067 | return $result; |
||
| 1068 | } |
||
| 1069 | |||
| 1070 | /** |
||
| 1071 | * Сохранение ключей аторизации. |
||
| 1072 | */ |
||
| 1073 | public function generateAuthorizedKeys(): void |
||
| 1074 | { |
||
| 1075 | $ssh_dir = '/root/.ssh'; |
||
| 1076 | Util::mwMkdir($ssh_dir); |
||
| 1077 | |||
| 1078 | |||
| 1079 | $conf_data = $this->mikoPBXConfig->getGeneralSettings('SSHAuthorizedKeys'); |
||
| 1080 | file_put_contents("{$ssh_dir}/authorized_keys", $conf_data); |
||
| 1081 | } |
||
| 1082 | |||
| 1083 | /** |
||
| 1084 | * Устанавливаем пароль для пользователя системы. |
||
| 1085 | **/ |
||
| 1086 | public function updateShellPassword(): void |
||
| 1087 | { |
||
| 1088 | $password = $this->mikoPBXConfig->getGeneralSettings('SSHPassword'); |
||
| 1089 | $echoPath = Util::which('echo'); |
||
| 1090 | $chpasswdPath = Util::which('chpasswd'); |
||
| 1091 | Util::mwExec("{$echoPath} \"root:$password\" | {$chpasswdPath}"); |
||
| 1092 | } |
||
| 1093 | |||
| 1094 | /** |
||
| 1095 | * Запуск open vmware tools. |
||
| 1096 | */ |
||
| 1097 | public function vmwareToolsConfigure(): void |
||
| 1116 | } |
||
| 1117 | } |
||
| 1118 | |||
| 1119 | } |
||
| 1120 |