| Total Complexity | 216 | 
| Total Lines | 1445 | 
| Duplicated Lines | 0 % | 
| Changes | 18 | ||
| Bugs | 0 | Features | 0 | 
Complex classes like Storage 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 Storage, and based on these observations, apply Extract Interface, too.
| 1 | <?php  | 
            ||
| 44 | class Storage extends Di\Injectable  | 
            ||
| 45 | { | 
            ||
| 46 | /**  | 
            ||
| 47 | * Возвращает директорию для хранения файлов записей разговоров.  | 
            ||
| 48 | *  | 
            ||
| 49 | * @return string  | 
            ||
| 50 | */  | 
            ||
| 51 | public static function getMonitorDir(): string  | 
            ||
| 59 | }  | 
            ||
| 60 | |||
| 61 | /**  | 
            ||
| 62 | * Возвращает директорию для хранения media файлов.  | 
            ||
| 63 | *  | 
            ||
| 64 | * @return string  | 
            ||
| 65 | */  | 
            ||
| 66 | public static function getMediaDir(): string  | 
            ||
| 74 | }  | 
            ||
| 75 | |||
| 76 | |||
| 77 | /**  | 
            ||
| 78 | * Прверяем является ли диск хранилищем.  | 
            ||
| 79 | *  | 
            ||
| 80 | * @param $device  | 
            ||
| 81 | *  | 
            ||
| 82 | * @return bool  | 
            ||
| 83 | */  | 
            ||
| 84 | public static function isStorageDisk($device): bool  | 
            ||
| 85 |     { | 
            ||
| 86 | $result = false;  | 
            ||
| 87 |         if (!file_exists($device)) { | 
            ||
| 88 | return $result;  | 
            ||
| 89 | }  | 
            ||
| 90 | |||
| 91 | $tmp_dir = '/tmp/mnt_' . time();  | 
            ||
| 92 | Util::mwMkdir($tmp_dir);  | 
            ||
| 93 | $out = [];  | 
            ||
| 94 | |||
| 95 | $storage = new self();  | 
            ||
| 96 | $uid_part = 'UUID=' . $storage->getUuid($device) . '';  | 
            ||
| 97 | $format = $storage->getFsType($device);  | 
            ||
| 98 |         if ($format === '') { | 
            ||
| 99 | return false;  | 
            ||
| 100 | }  | 
            ||
| 101 |         $mountPath = Util::which('mount'); | 
            ||
| 102 |         $umountPath = Util::which('umount'); | 
            ||
| 103 |         $rmPath = Util::which('rm'); | 
            ||
| 104 | |||
| 105 |         Processes::mwExec("{$mountPath} -t {$format} {$uid_part} {$tmp_dir}", $out); | 
            ||
| 106 |         if (is_dir("{$tmp_dir}/mikopbx") && trim(implode('', $out)) === '') { | 
            ||
| 107 | // $out - пустая строка, ошибок нет  | 
            ||
| 108 | // присутствует каталог mikopbx.  | 
            ||
| 109 | $result = true;  | 
            ||
| 110 | }  | 
            ||
| 111 |         if (self::isStorageDiskMounted($device)) { | 
            ||
| 112 |             Processes::mwExec("{$umountPath} {$device}"); | 
            ||
| 113 | }  | 
            ||
| 114 | |||
| 115 |         if (!self::isStorageDiskMounted($device)) { | 
            ||
| 116 |             Processes::mwExec("{$rmPath} -rf '{$tmp_dir}'"); | 
            ||
| 117 | }  | 
            ||
| 118 | |||
| 119 | return $result;  | 
            ||
| 120 | }  | 
            ||
| 121 | |||
| 122 | /**  | 
            ||
| 123 | * Получение идентификатора устройства.  | 
            ||
| 124 | *  | 
            ||
| 125 | * @param $device  | 
            ||
| 126 | *  | 
            ||
| 127 | * @return string  | 
            ||
| 128 | */  | 
            ||
| 129 | public function getUuid($device): string  | 
            ||
| 130 |     { | 
            ||
| 131 |         if (empty($device)) { | 
            ||
| 132 | return '';  | 
            ||
| 133 | }  | 
            ||
| 134 |         $lsBlkPath = Util::which('lsblk'); | 
            ||
| 135 |         $busyboxPath = Util::which('busybox'); | 
            ||
| 136 | |||
| 137 |         $cmd = "{$lsBlkPath} -r -o NAME,UUID | {$busyboxPath} grep " . basename($device) . " | {$busyboxPath} cut -d ' ' -f 2"; | 
            ||
| 138 | $res = Processes::mwExec($cmd, $output);  | 
            ||
| 139 |         if ($res === 0 && count($output) > 0) { | 
            ||
| 140 | $result = $output[0];  | 
            ||
| 141 |         } else { | 
            ||
| 142 | $result = '';  | 
            ||
| 143 | }  | 
            ||
| 144 | |||
| 145 | return $result;  | 
            ||
| 146 | }  | 
            ||
| 147 | |||
| 148 | /**  | 
            ||
| 149 | * Возвращает тип файловой системы блочного устройства.  | 
            ||
| 150 | *  | 
            ||
| 151 | * @param $device  | 
            ||
| 152 | *  | 
            ||
| 153 | * @return string  | 
            ||
| 154 | */  | 
            ||
| 155 | public function getFsType($device): string  | 
            ||
| 175 | }  | 
            ||
| 176 | |||
| 177 | /**  | 
            ||
| 178 | * Moves predefined sound files to storage disk  | 
            ||
| 179 | * Changes SoundFiles records  | 
            ||
| 180 | */  | 
            ||
| 181 | public static function moveReadOnlySoundsToStorage(): void  | 
            ||
| 208 | }  | 
            ||
| 209 | |||
| 210 | /**  | 
            ||
| 211 | * Copies MOH sound files to storage and creates record on SoundFiles table  | 
            ||
| 212 | */  | 
            ||
| 213 | public static function copyMohFilesToStorage(): void  | 
            ||
| 239 | }  | 
            ||
| 240 | }  | 
            ||
| 241 | }  | 
            ||
| 242 | |||
| 243 | /**  | 
            ||
| 244 | * Проверка, смонтирован ли диск - хранилище.  | 
            ||
| 245 | *  | 
            ||
| 246 | * @param string $filter  | 
            ||
| 247 | * @param string $mount_dir  | 
            ||
| 248 | *  | 
            ||
| 249 | * @return bool  | 
            ||
| 250 | */  | 
            ||
| 251 | public static function isStorageDiskMounted($filter = '', &$mount_dir = ''): bool  | 
            ||
| 252 |     { | 
            ||
| 253 |         if (Util::isSystemctl() && file_exists('/storage/usbdisk1/')) { | 
            ||
| 254 | $mount_dir = '/storage/usbdisk1/';  | 
            ||
| 255 | return true;  | 
            ||
| 256 | }  | 
            ||
| 257 |         if ('' === $filter) { | 
            ||
| 258 | $di = Di::getDefault();  | 
            ||
| 259 |             if ($di !== null) { | 
            ||
| 260 |                 $varEtcDir = $di->getConfig()->path('core.varEtcDir'); | 
            ||
| 261 |             } else { | 
            ||
| 262 | $varEtcDir = '/var/etc';  | 
            ||
| 263 | }  | 
            ||
| 264 | |||
| 265 |             $filename = "{$varEtcDir}/storage_device"; | 
            ||
| 266 |             if (file_exists($filename)) { | 
            ||
| 267 | $filter = file_get_contents($filename);  | 
            ||
| 268 |             } else { | 
            ||
| 269 | $filter = 'usbdisk1';  | 
            ||
| 270 | }  | 
            ||
| 271 | }  | 
            ||
| 272 |         $grepPath   = Util::which('grep'); | 
            ||
| 273 |         $mountPath  = Util::which('mount'); | 
            ||
| 274 |         $awkPath    = Util::which('awk'); | 
            ||
| 275 | |||
| 276 | $filter = escapeshellarg($filter);  | 
            ||
| 277 |         $out        = shell_exec("$mountPath | $grepPath $filter | {$awkPath} '{print $3}'"); | 
            ||
| 278 | $mount_dir = trim($out);  | 
            ||
| 279 | return ($mount_dir !== '');  | 
            ||
| 280 | }  | 
            ||
| 281 | |||
| 282 | /**  | 
            ||
| 283 | * Монитирование каталога с удаленного сервера SFTP.  | 
            ||
| 284 | *  | 
            ||
| 285 | * @param $host  | 
            ||
| 286 | * @param int $port  | 
            ||
| 287 | * @param string $user  | 
            ||
| 288 | * @param string $pass  | 
            ||
| 289 | * @param string $remout_dir  | 
            ||
| 290 | * @param string $local_dir  | 
            ||
| 291 | *  | 
            ||
| 292 | * @return bool  | 
            ||
| 293 | */  | 
            ||
| 294 | public static function mountSftpDisk($host, $port, $user, $pass, $remout_dir, $local_dir): bool  | 
            ||
| 295 |     { | 
            ||
| 296 | Util::mwMkdir($local_dir);  | 
            ||
| 297 | |||
| 298 | $out = [];  | 
            ||
| 299 |         $timeoutPath = Util::which('timeout'); | 
            ||
| 300 |         $sshfsPath = Util::which('sshfs'); | 
            ||
| 301 | |||
| 302 |         $command = "{$timeoutPath} 3 {$sshfsPath} -p {$port} -o nonempty -o password_stdin -o 'StrictHostKeyChecking=no' " . "{$user}@{$host}:{$remout_dir} {$local_dir} << EOF\n" . "{$pass}\n" . "EOF\n"; | 
            ||
| 303 | Processes::mwExec($command, $out);  | 
            ||
| 304 |         $response = trim(implode('', $out)); | 
            ||
| 305 |         if ('Terminated' === $response) { | 
            ||
| 306 | // Удаленный сервер не ответил / или не корректно указан пароль.  | 
            ||
| 307 | unset($response);  | 
            ||
| 308 | }  | 
            ||
| 309 | |||
| 310 |         return self::isStorageDiskMounted("$local_dir "); | 
            ||
| 311 | }  | 
            ||
| 312 | |||
| 313 | /**  | 
            ||
| 314 | * Монитирование каталога с удаленного сервера FTP.  | 
            ||
| 315 | *  | 
            ||
| 316 | * @param $host  | 
            ||
| 317 | * @param $port  | 
            ||
| 318 | * @param $user  | 
            ||
| 319 | * @param $pass  | 
            ||
| 320 | * @param string $remout_dir  | 
            ||
| 321 | * @param $local_dir  | 
            ||
| 322 | *  | 
            ||
| 323 | * @return bool  | 
            ||
| 324 | */  | 
            ||
| 325 | public static function mountFtp($host, $port, $user, $pass, $remout_dir, $local_dir): bool  | 
            ||
| 326 |     { | 
            ||
| 327 | Util::mwMkdir($local_dir);  | 
            ||
| 328 | $out = [];  | 
            ||
| 329 | |||
| 330 | // Собираем строку подключения к ftp.  | 
            ||
| 331 | $auth_line = '';  | 
            ||
| 332 |         if (!empty($user)) { | 
            ||
| 333 | $auth_line .= 'user="' . $user;  | 
            ||
| 334 |             if (!empty($pass)) { | 
            ||
| 335 |                 $auth_line .= ":{$pass}"; | 
            ||
| 336 | }  | 
            ||
| 337 | $auth_line .= '",';  | 
            ||
| 338 | }  | 
            ||
| 339 | |||
| 340 | $connect_line = 'ftp://' . $host;  | 
            ||
| 341 |         if (!empty($port)) { | 
            ||
| 342 |             $connect_line .= ":{$port}"; | 
            ||
| 343 | }  | 
            ||
| 344 |         if (!empty($remout_dir)) { | 
            ||
| 345 | $connect_line .= "$remout_dir";  | 
            ||
| 346 | }  | 
            ||
| 347 | |||
| 348 |         $timeoutPath = Util::which('timeout'); | 
            ||
| 349 |         $curlftpfsPath = Util::which('curlftpfs'); | 
            ||
| 350 |         $command = "{$timeoutPath} 3 {$curlftpfsPath}  -o allow_other -o {$auth_line}fsname={$host} {$connect_line} {$local_dir}"; | 
            ||
| 351 | Processes::mwExec($command, $out);  | 
            ||
| 352 |         $response = trim(implode('', $out)); | 
            ||
| 353 |         if ('Terminated' === $response) { | 
            ||
| 354 | // Удаленный сервер не ответил / или не корректно указан пароль.  | 
            ||
| 355 | unset($response);  | 
            ||
| 356 | }  | 
            ||
| 357 | |||
| 358 |         return self::isStorageDiskMounted("$local_dir "); | 
            ||
| 359 | }  | 
            ||
| 360 | |||
| 361 | /**  | 
            ||
| 362 | * Монитирование каталога с удаленного сервера FTP.  | 
            ||
| 363 | *  | 
            ||
| 364 | * @param $host  | 
            ||
| 365 | * @param $user  | 
            ||
| 366 | * @param $pass  | 
            ||
| 367 | * @param $dstDir  | 
            ||
| 368 | * @param $local_dir  | 
            ||
| 369 | *  | 
            ||
| 370 | * @return bool  | 
            ||
| 371 | */  | 
            ||
| 372 | public static function mountWebDav($host, $user, $pass, $dstDir, $local_dir): bool  | 
            ||
| 373 |     { | 
            ||
| 374 | $host = trim($host);  | 
            ||
| 375 | $dstDir = trim($dstDir);  | 
            ||
| 376 |         if(substr($host, -1) === '/'){ | 
            ||
| 377 | $host = substr($host, 0, -1);  | 
            ||
| 378 | }  | 
            ||
| 379 |         if($dstDir[0] === '/'){ | 
            ||
| 380 | $dstDir = substr($dstDir, 1);  | 
            ||
| 381 | }  | 
            ||
| 382 | Util::mwMkdir($local_dir);  | 
            ||
| 383 | $out = [];  | 
            ||
| 384 | $conf = 'dav_user www'.PHP_EOL.  | 
            ||
| 385 | 'dav_group www'.PHP_EOL;  | 
            ||
| 386 | |||
| 387 |         file_put_contents('/etc/davfs2/secrets', "{$host}{$dstDir} $user $pass"); | 
            ||
| 388 |         file_put_contents('/etc/davfs2/davfs2.conf', $conf); | 
            ||
| 389 |         $timeoutPath = Util::which('timeout'); | 
            ||
| 390 |         $mount = Util::which('mount.davfs'); | 
            ||
| 391 |         $command = "$timeoutPath 3 yes | $mount {$host}{$dstDir} {$local_dir}"; | 
            ||
| 392 | Processes::mwExec($command, $out);  | 
            ||
| 393 |         $response = trim(implode('', $out)); | 
            ||
| 394 |         if ('Terminated' === $response) { | 
            ||
| 395 | // Удаленный сервер не ответил / или не корректно указан пароль.  | 
            ||
| 396 | unset($response);  | 
            ||
| 397 | }  | 
            ||
| 398 |         return self::isStorageDiskMounted("$local_dir "); | 
            ||
| 399 | }  | 
            ||
| 400 | |||
| 401 | /**  | 
            ||
| 402 | * Запускает процесс форматирования диска.  | 
            ||
| 403 | *  | 
            ||
| 404 | * @param $dev  | 
            ||
| 405 | *  | 
            ||
| 406 | * @return array|bool  | 
            ||
| 407 | */  | 
            ||
| 408 | public static function mkfs_disk($dev)  | 
            ||
| 409 |     { | 
            ||
| 410 |         if (!file_exists($dev)) { | 
            ||
| 411 |             $dev = "/dev/{$dev}"; | 
            ||
| 412 | }  | 
            ||
| 413 |         if (!file_exists($dev)) { | 
            ||
| 414 | return false;  | 
            ||
| 415 | }  | 
            ||
| 416 | $dir = '';  | 
            ||
| 417 | self::isStorageDiskMounted($dev, $dir);  | 
            ||
| 418 | |||
| 419 |         if (empty($dir) || self::umountDisk($dir)) { | 
            ||
| 420 | // Диск размонтирован.  | 
            ||
| 421 | $st = new Storage();  | 
            ||
| 422 | // Будет запущен процесс:  | 
            ||
| 423 | $st->formatDiskLocal($dev, true);  | 
            ||
| 424 | sleep(1);  | 
            ||
| 425 | |||
| 426 | return (self::statusMkfs($dev) === 'inprogress');  | 
            ||
| 427 | }  | 
            ||
| 428 | |||
| 429 | // Ошибка размонтирования диска.  | 
            ||
| 430 | return false;  | 
            ||
| 431 | }  | 
            ||
| 432 | |||
| 433 | /**  | 
            ||
| 434 | * Размонтирует диск. Удаляет каталог в случае успеха.  | 
            ||
| 435 | *  | 
            ||
| 436 | * @param $dir  | 
            ||
| 437 | *  | 
            ||
| 438 | * @return bool  | 
            ||
| 439 | */  | 
            ||
| 440 | public static function umountDisk($dir): bool  | 
            ||
| 441 |     { | 
            ||
| 442 |         $umountPath = Util::which('umount'); | 
            ||
| 443 |         $rmPath     = Util::which('rm'); | 
            ||
| 444 |         if (self::isStorageDiskMounted($dir)) { | 
            ||
| 445 |             Processes::mwExec("/sbin/shell_functions.sh 'killprocesses' '$dir' -TERM 0"); | 
            ||
| 446 |             Processes::mwExec("{$umountPath} {$dir}"); | 
            ||
| 447 | }  | 
            ||
| 448 | $result = ! self::isStorageDiskMounted($dir);  | 
            ||
| 449 |         if ($result && file_exists($dir)) { | 
            ||
| 450 | // Если диск не смонтирован, то удаляем каталог.  | 
            ||
| 451 |             Processes::mwExec("{$rmPath} -rf '{$dir}'"); | 
            ||
| 452 | }  | 
            ||
| 453 | |||
| 454 | return $result;  | 
            ||
| 455 | }  | 
            ||
| 456 | |||
| 457 | /**  | 
            ||
| 458 | * Разметка диска.  | 
            ||
| 459 | *  | 
            ||
| 460 | * @param string $device  | 
            ||
| 461 | * @param bool $bg  | 
            ||
| 462 | *  | 
            ||
| 463 | * @return mixed  | 
            ||
| 464 | */  | 
            ||
| 465 | public function formatDiskLocal($device, $bg = false)  | 
            ||
| 466 |     { | 
            ||
| 467 |         $partedPath = Util::which('parted'); | 
            ||
| 468 | $retVal = Processes::mwExec(  | 
            ||
| 469 |             "{$partedPath} --script --align optimal '{$device}' 'mklabel msdos mkpart primary ext4 0% 100%'" | 
            ||
| 470 | );  | 
            ||
| 471 |         Util::sysLogMsg(__CLASS__, "{$partedPath} returned {$retVal}"); | 
            ||
| 472 |         if (false === $bg) { | 
            ||
| 473 | sleep(1);  | 
            ||
| 474 | }  | 
            ||
| 475 | |||
| 476 | return $this->formatDiskLocalPart2($device, $bg);  | 
            ||
| 477 | }  | 
            ||
| 478 | |||
| 479 | /**  | 
            ||
| 480 | * Форматирование диска.  | 
            ||
| 481 | *  | 
            ||
| 482 | * @param string $device  | 
            ||
| 483 | * @param bool $bg  | 
            ||
| 484 | *  | 
            ||
| 485 | * @return mixed  | 
            ||
| 486 | */  | 
            ||
| 487 | private function formatDiskLocalPart2($device, $bg = false): bool  | 
            ||
| 488 |     { | 
            ||
| 489 |         if (is_numeric(substr($device, -1))) { | 
            ||
| 490 | $device_id = "";  | 
            ||
| 491 |         } else { | 
            ||
| 492 | $device_id = "1";  | 
            ||
| 493 | }  | 
            ||
| 494 | $format = 'ext4';  | 
            ||
| 495 |         $mkfsPath = Util::which("mkfs.{$format}"); | 
            ||
| 496 |         $cmd = "{$mkfsPath} {$device}{$device_id}"; | 
            ||
| 497 |         if ($bg === false) { | 
            ||
| 498 |             $retVal = (Processes::mwExec("{$cmd} 2>&1") === 0); | 
            ||
| 499 |             Util::sysLogMsg(__CLASS__, "{$mkfsPath} returned {$retVal}"); | 
            ||
| 500 |         } else { | 
            ||
| 501 | usleep(200000);  | 
            ||
| 502 | Processes::mwExecBg($cmd);  | 
            ||
| 503 | $retVal = true;  | 
            ||
| 504 | }  | 
            ||
| 505 | |||
| 506 | return $retVal;  | 
            ||
| 507 | }  | 
            ||
| 508 | |||
| 509 | /**  | 
            ||
| 510 | * Возвращает текущий статус форматирования диска.  | 
            ||
| 511 | *  | 
            ||
| 512 | * @param $dev  | 
            ||
| 513 | *  | 
            ||
| 514 | * @return string  | 
            ||
| 515 | */  | 
            ||
| 516 | public static function statusMkfs($dev): string  | 
            ||
| 517 |     { | 
            ||
| 518 |         if (!file_exists($dev)) { | 
            ||
| 519 |             $dev = "/dev/{$dev}"; | 
            ||
| 520 | }  | 
            ||
| 521 | $out = [];  | 
            ||
| 522 |         $psPath = Util::which('ps'); | 
            ||
| 523 |         $grepPath = Util::which('grep'); | 
            ||
| 524 |         Processes::mwExec("{$psPath} -A -f | {$grepPath} {$dev} | {$grepPath} mkfs | {$grepPath} -v grep", $out); | 
            ||
| 525 |         $mount_dir = trim(implode('', $out)); | 
            ||
| 526 | |||
| 527 | return empty($mount_dir) ? 'ended' : 'inprogress';  | 
            ||
| 528 | }  | 
            ||
| 529 | |||
| 530 | /**  | 
            ||
| 531 | * Clear cache folders from PHP sessions files  | 
            ||
| 532 | */  | 
            ||
| 533 | public static function clearSessionsFiles(): void  | 
            ||
| 534 |     { | 
            ||
| 535 | $di = Di::getDefault();  | 
            ||
| 536 |         if ($di === null) { | 
            ||
| 537 | return;  | 
            ||
| 538 | }  | 
            ||
| 539 |         $config = $di->getShared('config'); | 
            ||
| 540 |         $phpSessionDir = $config->path('www.phpSessionDir'); | 
            ||
| 541 |         if (!empty($phpSessionDir)) { | 
            ||
| 542 |             $rmPath = Util::which('rm'); | 
            ||
| 543 |             Processes::mwExec("{$rmPath} -rf {$phpSessionDir}/*"); | 
            ||
| 544 | }  | 
            ||
| 545 | }  | 
            ||
| 546 | |||
| 547 | /**  | 
            ||
| 548 | * Возвращает все подключенные HDD.  | 
            ||
| 549 | *  | 
            ||
| 550 | * @param bool $mounted_only  | 
            ||
| 551 | *  | 
            ||
| 552 | * @return array  | 
            ||
| 553 | */  | 
            ||
| 554 | public function getAllHdd($mounted_only = false): array  | 
            ||
| 555 |     { | 
            ||
| 556 | $res_disks = [];  | 
            ||
| 557 | |||
| 558 |         if (Util::isSystemctl()) { | 
            ||
| 559 | $out = [];  | 
            ||
| 560 |             $grepPath = Util::which('grep'); | 
            ||
| 561 |             $dfPath = Util::which('df'); | 
            ||
| 562 |             $awkPath = Util::which('awk'); | 
            ||
| 563 | Processes::mwExec(  | 
            ||
| 564 |                 "{$dfPath} -k /storage/usbdisk1 | {$awkPath}  '{ print $1 \"|\" $3 \"|\" $4} ' | {$grepPath} -v 'Available'", | 
            ||
| 565 | $out  | 
            ||
| 566 | );  | 
            ||
| 567 |             $disk_data = explode('|', implode(" ", $out)); | 
            ||
| 568 |             if (count($disk_data) === 3) { | 
            ||
| 569 | $m_size = round(($disk_data[1] + $disk_data[2]) / 1024, 1);  | 
            ||
| 570 | $res_disks[] = [  | 
            ||
| 571 | 'id' => $disk_data[0],  | 
            ||
| 572 | 'size' => "" . $m_size,  | 
            ||
| 573 | 'size_text' => "" . $m_size . " Mb",  | 
            ||
| 574 | 'vendor' => 'Debian',  | 
            ||
| 575 | 'mounted' => '/storage/usbdisk1',  | 
            ||
| 576 | 'free_space' => round($disk_data[2] / 1024, 1),  | 
            ||
| 577 | 'partitions' => [],  | 
            ||
| 578 | 'sys_disk' => true,  | 
            ||
| 579 | ];  | 
            ||
| 580 | }  | 
            ||
| 581 | |||
| 582 | return $res_disks;  | 
            ||
| 583 | }  | 
            ||
| 584 | |||
| 585 | $cd_disks = $this->cdromGetDevices();  | 
            ||
| 586 | $disks = $this->diskGetDevices();  | 
            ||
| 587 | |||
| 588 | $cf_disk = '';  | 
            ||
| 589 |         $varEtcDir = $this->config->path('core.varEtcDir'); | 
            ||
| 590 | |||
| 591 |         if (file_exists($varEtcDir . '/cfdevice')) { | 
            ||
| 592 | $cf_disk = trim(file_get_contents($varEtcDir . '/cfdevice'));  | 
            ||
| 593 | }  | 
            ||
| 594 | |||
| 595 |         foreach ($disks as $disk => $diskInfo) { | 
            ||
| 596 | $type = $diskInfo['fstype']??'';  | 
            ||
| 597 |             if($type === 'linux_raid_member'){ | 
            ||
| 598 | continue;  | 
            ||
| 599 | }  | 
            ||
| 600 |             if (in_array($disk, $cd_disks, true)) { | 
            ||
| 601 | // Это CD-ROM.  | 
            ||
| 602 | continue;  | 
            ||
| 603 | }  | 
            ||
| 604 | unset($temp_vendor, $temp_size, $original_size);  | 
            ||
| 605 | $mounted = self::diskIsMounted($disk);  | 
            ||
| 606 |             if ($mounted_only === true && $mounted === false) { | 
            ||
| 607 | continue;  | 
            ||
| 608 | }  | 
            ||
| 609 | $sys_disk = ($cf_disk === $disk);  | 
            ||
| 610 | |||
| 611 | $mb_size = 0;  | 
            ||
| 612 |             if (is_file("/sys/block/" . $disk . "/size")) { | 
            ||
| 613 |                 $original_size = trim(file_get_contents("/sys/block/" . $disk . "/size")); | 
            ||
| 614 | $original_size = ($original_size * 512 / 1024 / 1024);  | 
            ||
| 615 | $mb_size = $original_size;  | 
            ||
| 616 | }  | 
            ||
| 617 |             if ($mb_size > 100) { | 
            ||
| 618 |                 $temp_size   = sprintf("%.0f MB", $mb_size); | 
            ||
| 619 | $temp_vendor = $this->getVendorDisk($diskInfo);  | 
            ||
| 620 | $free_space = $this->getFreeSpace($disk);  | 
            ||
| 621 | |||
| 622 | $arr_disk_info = $this->determineFormatFs($diskInfo);  | 
            ||
| 623 | |||
| 624 |                 if (count($arr_disk_info) > 0) { | 
            ||
| 625 | $used = 0;  | 
            ||
| 626 |                     foreach ($arr_disk_info as $disk_info) { | 
            ||
| 627 | $used += $disk_info['used_space'];  | 
            ||
| 628 | }  | 
            ||
| 629 |                     if ($used > 0) { | 
            ||
| 630 | $free_space = $mb_size - $used;  | 
            ||
| 631 | }  | 
            ||
| 632 | }  | 
            ||
| 633 | |||
| 634 | $res_disks[] = [  | 
            ||
| 635 | 'id' => $disk,  | 
            ||
| 636 | 'size' => $mb_size,  | 
            ||
| 637 | 'size_text' => $temp_size,  | 
            ||
| 638 | 'vendor' => $temp_vendor,  | 
            ||
| 639 | 'mounted' => $mounted,  | 
            ||
| 640 | 'free_space' => $free_space,  | 
            ||
| 641 | 'partitions' => $arr_disk_info,  | 
            ||
| 642 | 'sys_disk' => $sys_disk,  | 
            ||
| 643 | ];  | 
            ||
| 644 | }  | 
            ||
| 645 | }  | 
            ||
| 646 | return $res_disks;  | 
            ||
| 647 | }  | 
            ||
| 648 | |||
| 649 | /**  | 
            ||
| 650 | * Получение массива подключенныйх cdrom.  | 
            ||
| 651 | *  | 
            ||
| 652 | * @return array  | 
            ||
| 653 | */  | 
            ||
| 654 | private function cdromGetDevices(): array  | 
            ||
| 655 |     { | 
            ||
| 656 | $disks = [];  | 
            ||
| 657 | $blockDevices = $this->getLsBlkDiskInfo();  | 
            ||
| 658 |         foreach ($blockDevices as $diskData) { | 
            ||
| 659 | $type = $diskData['type'] ?? '';  | 
            ||
| 660 | $name = $diskData['name'] ?? '';  | 
            ||
| 661 |             if ($type !== 'rom' || $name === '') { | 
            ||
| 662 | continue;  | 
            ||
| 663 | }  | 
            ||
| 664 | $disks[] = $name;  | 
            ||
| 665 | }  | 
            ||
| 666 | return $disks;  | 
            ||
| 667 | }  | 
            ||
| 668 | |||
| 669 | /**  | 
            ||
| 670 | * Получение массива подключенныйх HDD.  | 
            ||
| 671 | * @param false $diskOnly  | 
            ||
| 672 | * @return array  | 
            ||
| 673 | */  | 
            ||
| 674 | public function diskGetDevices($diskOnly = false): array  | 
            ||
| 675 |     { | 
            ||
| 676 | $disks = [];  | 
            ||
| 677 | $blockDevices = $this->getLsBlkDiskInfo();  | 
            ||
| 678 | |||
| 679 |         foreach ($blockDevices as $diskData) { | 
            ||
| 680 | $type = $diskData['type'] ?? '';  | 
            ||
| 681 | $name = $diskData['name'] ?? '';  | 
            ||
| 682 |             if ($type !== 'disk' || $name === '') { | 
            ||
| 683 | continue;  | 
            ||
| 684 | }  | 
            ||
| 685 | $disks[$name] = $diskData;  | 
            ||
| 686 |             if ($diskOnly === true) { | 
            ||
| 687 | continue;  | 
            ||
| 688 | }  | 
            ||
| 689 | $children = $diskData['children'] ?? [];  | 
            ||
| 690 | |||
| 691 |             foreach ($children as $child) { | 
            ||
| 692 | $childName = $child['name'] ?? '';  | 
            ||
| 693 |                 if ($childName === '') { | 
            ||
| 694 | continue;  | 
            ||
| 695 | }  | 
            ||
| 696 | $disks[$childName] = $child;  | 
            ||
| 697 | }  | 
            ||
| 698 | }  | 
            ||
| 699 | return $disks;  | 
            ||
| 700 | }  | 
            ||
| 701 | |||
| 702 | /**  | 
            ||
| 703 | * Возвращает информацию о дисках.  | 
            ||
| 704 | * @return array  | 
            ||
| 705 | */  | 
            ||
| 706 | private function getLsBlkDiskInfo(): array  | 
            ||
| 720 | }  | 
            ||
| 721 | |||
| 722 | /**  | 
            ||
| 723 | * Проверка, смонтирован ли диск.  | 
            ||
| 724 | *  | 
            ||
| 725 | * @param $disk  | 
            ||
| 726 | * @param $filter  | 
            ||
| 727 | *  | 
            ||
| 728 | * @return string|bool  | 
            ||
| 729 | */  | 
            ||
| 730 | public static function diskIsMounted($disk, $filter = '/dev/')  | 
            ||
| 744 | }  | 
            ||
| 745 | |||
| 746 | /**  | 
            ||
| 747 | * Получение сведений по диску.  | 
            ||
| 748 | *  | 
            ||
| 749 | * @param $diskInfo  | 
            ||
| 750 | *  | 
            ||
| 751 | * @return string  | 
            ||
| 752 | */  | 
            ||
| 753 | private function getVendorDisk($diskInfo): string  | 
            ||
| 754 |     { | 
            ||
| 755 | $temp_vendor = [];  | 
            ||
| 756 | $keys = ['vendor', 'model', 'type'];  | 
            ||
| 757 |         foreach ($keys as $key) { | 
            ||
| 758 | $data = $diskInfo[$key] ?? '';  | 
            ||
| 759 |             if ($data !== '') { | 
            ||
| 760 |                 $temp_vendor[] = trim(str_replace(',', '', $data)); | 
            ||
| 761 | }  | 
            ||
| 762 | }  | 
            ||
| 763 |         if (count($temp_vendor) === 0) { | 
            ||
| 764 | $temp_vendor[] = $diskInfo['name'] ?? 'ERROR: NoName';  | 
            ||
| 765 | }  | 
            ||
| 766 |         return implode(', ', $temp_vendor); | 
            ||
| 767 | }  | 
            ||
| 768 | |||
| 769 | /**  | 
            ||
| 770 | * Получаем свободное место на диске в Mb.  | 
            ||
| 771 | *  | 
            ||
| 772 | * @param $hdd  | 
            ||
| 773 | *  | 
            ||
| 774 | * @return mixed  | 
            ||
| 775 | */  | 
            ||
| 776 | public function getFreeSpace($hdd)  | 
            ||
| 793 | }  | 
            ||
| 794 | |||
| 795 | private function getDiskParted($diskName): array  | 
            ||
| 796 |     { | 
            ||
| 797 | $result = [];  | 
            ||
| 798 |         $lsBlkPath = Util::which('lsblk'); | 
            ||
| 799 |         Processes::mwExec("{$lsBlkPath} -J -b -o NAME,TYPE {$diskName}", $out); | 
            ||
| 800 |         try { | 
            ||
| 801 | $data = json_decode(implode(PHP_EOL, $out), true, 512, JSON_THROW_ON_ERROR);  | 
            ||
| 802 | $data = $data['blockdevices'][0] ?? [];  | 
            ||
| 803 |         } catch (\JsonException $e) { | 
            ||
| 804 | $data = [];  | 
            ||
| 816 | }  | 
            ||
| 817 | |||
| 818 | /**  | 
            ||
| 819 | * Определить формат файловой системы и размер дисков.  | 
            ||
| 820 | *  | 
            ||
| 821 | * @param $deviceInfo  | 
            ||
| 822 | *  | 
            ||
| 823 | * @return array|bool  | 
            ||
| 824 | */  | 
            ||
| 825 | public function determineFormatFs($deviceInfo)  | 
            ||
| 894 | }  | 
            ||
| 895 | |||
| 896 | /**  | 
            ||
| 897 | * Монтирует диск в указанный каталог.  | 
            ||
| 898 | *  | 
            ||
| 899 | * @param $dev  | 
            ||
| 900 | * @param $format  | 
            ||
| 901 | * @param $dir  | 
            ||
| 902 | *  | 
            ||
| 903 | * @return bool  | 
            ||
| 904 | */  | 
            ||
| 905 | public static function mountDisk($dev, $format, $dir): bool  | 
            ||
| 929 | }  | 
            ||
| 930 | |||
| 931 | /**  | 
            ||
| 932 | * Монтирование разделов диска с базой данных настроек.  | 
            ||
| 933 | */  | 
            ||
| 934 | public function configure(): void  | 
            ||
| 935 |     { | 
            ||
| 936 |         if(Util::isSystemctl()){ | 
            ||
| 937 |             $this->updateConfigWithNewMountPoint("/storage/usbdisk1"); | 
            ||
| 938 | $this->createWorkDirs();  | 
            ||
| 939 | PHPConf::setupLog();  | 
            ||
| 940 | return;  | 
            ||
| 941 | }  | 
            ||
| 942 | |||
| 943 | $cf_disk = '';  | 
            ||
| 944 |         $varEtcDir = $this->config->path('core.varEtcDir'); | 
            ||
| 945 |         $storage_dev_file = "{$varEtcDir}/storage_device"; | 
            ||
| 946 |         if (file_exists($storage_dev_file)) { | 
            ||
| 947 | unlink($storage_dev_file);  | 
            ||
| 948 | }  | 
            ||
| 949 | |||
| 950 |         if (file_exists($varEtcDir . '/cfdevice')) { | 
            ||
| 951 | $cf_disk = trim(file_get_contents($varEtcDir . '/cfdevice'));  | 
            ||
| 952 | }  | 
            ||
| 953 | $disks = $this->getDiskSettings();  | 
            ||
| 954 | $conf = '';  | 
            ||
| 955 |         foreach ($disks as $disk) { | 
            ||
| 956 | clearstatcache();  | 
            ||
| 957 | $dev = $this->getStorageDev($disk, $cf_disk);  | 
            ||
| 958 |             if (!$this->hddExists($dev)) { | 
            ||
| 959 | // Диск не существует.  | 
            ||
| 960 | continue;  | 
            ||
| 961 | }  | 
            ||
| 962 |             if ($disk['media'] === '1' || !file_exists($storage_dev_file)) { | 
            ||
| 963 |                 file_put_contents($storage_dev_file, "/storage/usbdisk{$disk['id']}"); | 
            ||
| 964 |                 $this->updateConfigWithNewMountPoint("/storage/usbdisk{$disk['id']}"); | 
            ||
| 965 | }  | 
            ||
| 966 | $formatFs = $this->getFsType($dev);  | 
            ||
| 967 |             if($formatFs !== $disk['filesystemtype'] && !($formatFs === 'ext4' && $disk['filesystemtype'] === 'ext2')){ | 
            ||
| 968 |                 Util::sysLogMsg('Storage', "The file system type has changed {$disk['filesystemtype']} -> {$formatFs}. The disk will not be connected."); | 
            ||
| 969 | continue;  | 
            ||
| 970 | }  | 
            ||
| 971 | $str_uid = 'UUID=' . $this->getUuid($dev);  | 
            ||
| 972 |             $conf .= "{$str_uid} /storage/usbdisk{$disk['id']} {$formatFs} async,rw 0 0\n"; | 
            ||
| 973 |             $mount_point = "/storage/usbdisk{$disk['id']}"; | 
            ||
| 974 | Util::mwMkdir($mount_point);  | 
            ||
| 975 | }  | 
            ||
| 976 | $this->saveFstab($conf);  | 
            ||
| 977 | $this->createWorkDirs();  | 
            ||
| 978 | PHPConf::setupLog();  | 
            ||
| 979 | }  | 
            ||
| 980 | |||
| 981 | /**  | 
            ||
| 982 | * Поиск Storage устройства.  | 
            ||
| 983 | * Возвращает полный путь к разделу.  | 
            ||
| 984 | * @param $disk  | 
            ||
| 985 | * @param $cf_disk  | 
            ||
| 986 | * @return string  | 
            ||
| 987 | */  | 
            ||
| 988 |     private function getStorageDev($disk, $cf_disk):string{ | 
            ||
| 989 |         if(!empty($disk['uniqid'])){ | 
            ||
| 990 | // Ищим имя раздела по UID.  | 
            ||
| 991 |             $lsBlkPath   = Util::which('lsblk'); | 
            ||
| 992 |             $busyboxPath = Util::which('busybox'); | 
            ||
| 993 |             $cmd = "{$lsBlkPath} -r -o NAME,UUID | {$busyboxPath} grep {$disk['uniqid']} | {$busyboxPath} cut -d ' ' -f 1"; | 
            ||
| 994 | $dev = '/dev/'.trim(shell_exec($cmd));  | 
            ||
| 995 |             if ($this->hddExists($dev)) { | 
            ||
| 996 | // Диск существует.  | 
            ||
| 997 | return $dev;  | 
            ||
| 998 | }  | 
            ||
| 999 | }  | 
            ||
| 1000 | // Определяем диск по его имени.  | 
            ||
| 1001 |         if ($disk['device'] !== "/dev/{$cf_disk}") { | 
            ||
| 1002 | // Если это обычный диск, то раздел 1.  | 
            ||
| 1003 | $part = "1";  | 
            ||
| 1004 |         } else { | 
            ||
| 1005 | // Если это системный диск, то пытаемся подключить раздел 4.  | 
            ||
| 1006 | $part = "4";  | 
            ||
| 1007 | }  | 
            ||
| 1008 | $devName = self::getDevPartName($disk['device'], $part);  | 
            ||
| 1009 | return '/dev/' . $devName;  | 
            ||
| 1010 | }  | 
            ||
| 1011 | |||
| 1012 | /**  | 
            ||
| 1013 | * Получаем настройки диска из базы данных.  | 
            ||
| 1014 | *  | 
            ||
| 1015 | * @param string $id  | 
            ||
| 1016 | *  | 
            ||
| 1017 | * @return array  | 
            ||
| 1018 | */  | 
            ||
| 1019 | public function getDiskSettings($id = ''): array  | 
            ||
| 1020 |     { | 
            ||
| 1021 | $data = [];  | 
            ||
| 1022 |         if ('' === $id) { | 
            ||
| 1023 | // Возвращаем данные до модификации.  | 
            ||
| 1024 | $data = StorageModel::find()->toArray();  | 
            ||
| 1025 |         } else { | 
            ||
| 1026 |             $pbxSettings = StorageModel::findFirst("id='$id'"); | 
            ||
| 1027 |             if ($pbxSettings !== null) { | 
            ||
| 1028 | $data = $pbxSettings->toArray();  | 
            ||
| 1029 | }  | 
            ||
| 1030 | }  | 
            ||
| 1031 | |||
| 1032 | return $data;  | 
            ||
| 1033 | }  | 
            ||
| 1034 | |||
| 1035 | /**  | 
            ||
| 1036 | * Проверяет, существует ли диск в массиве.  | 
            ||
| 1037 | *  | 
            ||
| 1038 | * @param $disk  | 
            ||
| 1039 | *  | 
            ||
| 1040 | * @return bool  | 
            ||
| 1041 | */  | 
            ||
| 1042 | private function hddExists($disk): bool  | 
            ||
| 1043 |     { | 
            ||
| 1044 | $result = false;  | 
            ||
| 1045 | $uid = $this->getUuid($disk);  | 
            ||
| 1046 |         if ($uid !== false && file_exists($disk)) { | 
            ||
| 1047 | $result = true;  | 
            ||
| 1048 | }  | 
            ||
| 1049 | return $result;  | 
            ||
| 1050 | }  | 
            ||
| 1051 | |||
| 1052 | /**  | 
            ||
| 1053 | * After mount storage we will change /mountpoint/ to new $mount_point value  | 
            ||
| 1054 | *  | 
            ||
| 1055 | * @param string $mount_point  | 
            ||
| 1056 | *  | 
            ||
| 1057 | */  | 
            ||
| 1058 | private function updateConfigWithNewMountPoint(string $mount_point): void  | 
            ||
| 1059 |     { | 
            ||
| 1060 | $staticSettingsFile = '/etc/inc/mikopbx-settings.json';  | 
            ||
| 1061 |         $staticSettingsFileOrig = appPath('config/mikopbx-settings.json'); | 
            ||
| 1062 | |||
| 1063 | $jsonString = file_get_contents($staticSettingsFileOrig);  | 
            ||
| 1064 |         try { | 
            ||
| 1065 | $data = json_decode($jsonString, true, 512, JSON_THROW_ON_ERROR);  | 
            ||
| 1066 |         } catch (JsonException $exception) { | 
            ||
| 1067 |             throw new Error("{$staticSettingsFileOrig} has broken format"); | 
            ||
| 1068 | }  | 
            ||
| 1069 |         foreach ($data as $rootKey => $rootEntry) { | 
            ||
| 1070 |             foreach ($rootEntry as $nestedKey => $entry) { | 
            ||
| 1071 |                 if (stripos($entry, '/mountpoint') !== false) { | 
            ||
| 1072 |                     $data[$rootKey][$nestedKey] = str_ireplace('/mountpoint', $mount_point, $entry); | 
            ||
| 1073 | }  | 
            ||
| 1074 | }  | 
            ||
| 1075 | }  | 
            ||
| 1076 | |||
| 1077 | $newJsonString = json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);  | 
            ||
| 1078 | file_put_contents($staticSettingsFile, $newJsonString);  | 
            ||
| 1079 | $this->updateEnvironmentAfterChangeMountPoint();  | 
            ||
| 1080 | }  | 
            ||
| 1081 | |||
| 1082 | |||
| 1083 | /**  | 
            ||
| 1084 | * Recreates DI services and reloads config from JSON file  | 
            ||
| 1085 | *  | 
            ||
| 1086 | */  | 
            ||
| 1087 | private function updateEnvironmentAfterChangeMountPoint(): void  | 
            ||
| 1088 |     { | 
            ||
| 1089 | // Update config variable  | 
            ||
| 1090 | ConfigProvider::recreateConfigProvider();  | 
            ||
| 1091 |         $this->config = $this->di->get('config'); | 
            ||
| 1092 | |||
| 1093 | // Reload classes from system and storage disks  | 
            ||
| 1094 | ClassLoader::init();  | 
            ||
| 1095 | |||
| 1096 | // Reload all providers  | 
            ||
| 1097 | RegisterDIServices::init();  | 
            ||
| 1098 | }  | 
            ||
| 1099 | |||
| 1100 | /**  | 
            ||
| 1101 | * Generates fstab file  | 
            ||
| 1102 | * Mounts volumes  | 
            ||
| 1103 | *  | 
            ||
| 1104 | * @param string $conf  | 
            ||
| 1105 | */  | 
            ||
| 1106 | public function saveFstab($conf = ''): void  | 
            ||
| 1107 |     { | 
            ||
| 1108 |         if(Util::isSystemctl()){ | 
            ||
| 1109 | // Не настраиваем.  | 
            ||
| 1110 | return;  | 
            ||
| 1111 | }  | 
            ||
| 1112 | |||
| 1113 |         $varEtcDir = $this->config->path('core.varEtcDir'); | 
            ||
| 1114 | // Точка монтирования доп. дисков.  | 
            ||
| 1115 |         Util::mwMkdir('/storage'); | 
            ||
| 1116 |         $chmodPath = Util::which('chmod'); | 
            ||
| 1117 |         Processes::mwExec("{$chmodPath} 755 /storage"); | 
            ||
| 1118 |         if (!file_exists($varEtcDir . '/cfdevice')) { | 
            ||
| 1119 | return;  | 
            ||
| 1120 | }  | 
            ||
| 1121 | $fstab = '';  | 
            ||
| 1122 | $file_data = file_get_contents($varEtcDir . '/cfdevice');  | 
            ||
| 1123 | $cf_disk = trim($file_data);  | 
            ||
| 1124 |         if ('' === $cf_disk) { | 
            ||
| 1125 | return;  | 
            ||
| 1126 | }  | 
            ||
| 1127 | $part2 = self::getDevPartName($cf_disk, '2');  | 
            ||
| 1128 | $part3 = self::getDevPartName($cf_disk, '3');  | 
            ||
| 1129 | |||
| 1130 |         $uid_part2 = 'UUID=' . $this->getUuid("/dev/{$part2}"); | 
            ||
| 1131 | $format_p2 = $this->getFsType($part2);  | 
            ||
| 1132 |         $uid_part3 = 'UUID=' . $this->getUuid("/dev/{$part3}"); | 
            ||
| 1133 | $format_p3 = $this->getFsType($part3);  | 
            ||
| 1134 | |||
| 1135 |         $fstab .= "{$uid_part2} /offload {$format_p2} ro 0 0\n"; | 
            ||
| 1136 |         $fstab .= "{$uid_part3} /cf {$format_p3} rw 1 1\n"; | 
            ||
| 1137 | $fstab .= $conf;  | 
            ||
| 1138 | |||
| 1139 |         file_put_contents("/etc/fstab", $fstab); | 
            ||
| 1140 | // Дублируем для работы vmtoolsd.  | 
            ||
| 1141 |         file_put_contents("/etc/mtab", $fstab); | 
            ||
| 1142 |         $mountPath = Util::which('mount'); | 
            ||
| 1143 |         Processes::mwExec("{$mountPath} -a 2> /dev/null"); | 
            ||
| 1144 |         Util::addRegularWWWRights('/cf'); | 
            ||
| 1145 | }  | 
            ||
| 1146 | |||
| 1147 | /**  | 
            ||
| 1148 | * Возвращает имя раздела диска по имени и номеру.  | 
            ||
| 1149 | * @param string $dev  | 
            ||
| 1150 | * @param string $part  | 
            ||
| 1151 | * @return string  | 
            ||
| 1152 | */  | 
            ||
| 1153 | public static function getDevPartName(string $dev, string $part): string  | 
            ||
| 1154 |     { | 
            ||
| 1155 |         $lsBlkPath = Util::which('lsblk'); | 
            ||
| 1156 |         $cutPath = Util::which('cut'); | 
            ||
| 1157 |         $grepPath = Util::which('grep'); | 
            ||
| 1158 |         $sortPath = Util::which('sort'); | 
            ||
| 1159 | |||
| 1160 |         $command = "{$lsBlkPath} -r | {$grepPath} ' part' | {$sortPath} -u | {$cutPath} -d ' ' -f 1 | {$grepPath} \"" . basename( | 
            ||
| 1161 | $dev  | 
            ||
| 1162 |             ) . "\" | {$grepPath} \"{$part}\$\""; | 
            ||
| 1163 | Processes::mwExec($command, $out);  | 
            ||
| 1164 |         $devName = trim(implode('', $out)); | 
            ||
| 1165 | return trim($devName);  | 
            ||
| 1166 | }  | 
            ||
| 1167 | |||
| 1168 | /**  | 
            ||
| 1169 | * Creates system folders according to config file  | 
            ||
| 1170 | *  | 
            ||
| 1171 | * @return void  | 
            ||
| 1172 | */  | 
            ||
| 1173 | private function createWorkDirs(): void  | 
            ||
| 1174 |     { | 
            ||
| 1175 | $path = '';  | 
            ||
| 1176 |         $mountPath = Util::which('mount'); | 
            ||
| 1177 |         Processes::mwExec("{$mountPath} -o remount,rw /offload 2> /dev/null"); | 
            ||
| 1178 | |||
| 1179 |         $isLiveCd = file_exists('/offload/livecd'); | 
            ||
| 1180 | // Create dirs  | 
            ||
| 1181 | $arrConfig = $this->config->toArray();  | 
            ||
| 1182 |         foreach ($arrConfig as $rootEntry) { | 
            ||
| 1183 |             foreach ($rootEntry as $key => $entry) { | 
            ||
| 1184 |                 if (stripos($key, 'path') === false && stripos($key, 'dir') === false) { | 
            ||
| 1185 | continue;  | 
            ||
| 1186 | }  | 
            ||
| 1187 |                 if (file_exists($entry)) { | 
            ||
| 1188 | continue;  | 
            ||
| 1189 | }  | 
            ||
| 1190 |                 if ($isLiveCd && strpos($entry, '/offload/') === 0) { | 
            ||
| 1191 | continue;  | 
            ||
| 1192 | }  | 
            ||
| 1193 | $path .= " $entry";  | 
            ||
| 1194 | }  | 
            ||
| 1195 | }  | 
            ||
| 1196 | |||
| 1197 |         if (!empty($path)) { | 
            ||
| 1198 | Util::mwMkdir($path);  | 
            ||
| 1199 | }  | 
            ||
| 1200 | |||
| 1201 |         $downloadCacheDir = appPath('sites/pbxcore/files/cache'); | 
            ||
| 1202 |         if (!$isLiveCd) { | 
            ||
| 1203 | Util::mwMkdir($downloadCacheDir);  | 
            ||
| 1204 |             Util::createUpdateSymlink($this->config->path('www.downloadCacheDir'), $downloadCacheDir); | 
            ||
| 1205 | }  | 
            ||
| 1206 | |||
| 1207 | $this->createAssetsSymlinks();  | 
            ||
| 1208 | |||
| 1209 |         Util::createUpdateSymlink($this->config->path('www.phpSessionDir'), '/var/lib/php/session'); | 
            ||
| 1210 |         Util::createUpdateSymlink($this->config->path('www.uploadDir'), '/ultmp'); | 
            ||
| 1211 | |||
| 1212 |         $filePath = appPath('src/Core/Asterisk/Configs/lua/extensions.lua'); | 
            ||
| 1213 | Util::createUpdateSymlink($filePath, '/etc/asterisk/extensions.lua');  | 
            ||
| 1214 | |||
| 1215 | // Create symlinks to AGI-BIN  | 
            ||
| 1216 |         $agiBinDir = $this->config->path('asterisk.astagidir'); | 
            ||
| 1217 |         if ($isLiveCd && strpos($agiBinDir, '/offload/') !== 0) { | 
            ||
| 1218 | Util::mwMkdir($agiBinDir);  | 
            ||
| 1219 | }  | 
            ||
| 1220 | |||
| 1221 |         $roAgiBinFolder = appPath('src/Core/Asterisk/agi-bin'); | 
            ||
| 1222 |         $files = glob("{$roAgiBinFolder}/*.{php}", GLOB_BRACE); | 
            ||
| 1223 |         foreach ($files as $file) { | 
            ||
| 1224 | $fileInfo = pathinfo($file);  | 
            ||
| 1225 |             $newFilename = "{$agiBinDir}/{$fileInfo['filename']}.{$fileInfo['extension']}"; | 
            ||
| 1226 | Util::createUpdateSymlink($file, $newFilename);  | 
            ||
| 1227 | }  | 
            ||
| 1228 | $this->clearCacheFiles();  | 
            ||
| 1229 | $this->applyFolderRights();  | 
            ||
| 1230 |         Processes::mwExec("{$mountPath} -o remount,ro /offload 2> /dev/null"); | 
            ||
| 1231 | }  | 
            ||
| 1232 | |||
| 1233 | /**  | 
            ||
| 1234 | * Creates JS, CSS, IMG cache folders and links  | 
            ||
| 1235 | *  | 
            ||
| 1236 | */  | 
            ||
| 1237 | public function createAssetsSymlinks(): void  | 
            ||
| 1238 |     { | 
            ||
| 1239 |         $jsCacheDir = appPath('sites/admin-cabinet/assets/js/cache'); | 
            ||
| 1240 |         Util::createUpdateSymlink($this->config->path('adminApplication.assetsCacheDir') . '/js', $jsCacheDir); | 
            ||
| 1241 | |||
| 1242 |         $cssCacheDir = appPath('sites/admin-cabinet/assets/css/cache'); | 
            ||
| 1243 |         Util::createUpdateSymlink($this->config->path('adminApplication.assetsCacheDir') . '/css', $cssCacheDir); | 
            ||
| 1244 | |||
| 1245 |         $imgCacheDir = appPath('sites/admin-cabinet/assets/img/cache'); | 
            ||
| 1246 |         Util::createUpdateSymlink($this->config->path('adminApplication.assetsCacheDir') . '/img', $imgCacheDir); | 
            ||
| 1247 | }  | 
            ||
| 1248 | |||
| 1249 | /**  | 
            ||
| 1250 | * Clears cache folders from old and orphaned files  | 
            ||
| 1251 | */  | 
            ||
| 1252 | public function clearCacheFiles(): void  | 
            ||
| 1253 |     { | 
            ||
| 1254 | $cacheDirs = [];  | 
            ||
| 1255 |         $cacheDirs[] = $this->config->path('www.uploadDir'); | 
            ||
| 1256 |         $cacheDirs[] = $this->config->path('www.downloadCacheDir'); | 
            ||
| 1257 |         $cacheDirs[] = $this->config->path('adminApplication.assetsCacheDir') . '/js'; | 
            ||
| 1258 |         $cacheDirs[] = $this->config->path('adminApplication.assetsCacheDir') . '/css'; | 
            ||
| 1259 |         $cacheDirs[] = $this->config->path('adminApplication.assetsCacheDir') . '/img'; | 
            ||
| 1260 |         $cacheDirs[] = $this->config->path('adminApplication.voltCacheDir'); | 
            ||
| 1261 |         $rmPath = Util::which('rm'); | 
            ||
| 1262 |         foreach ($cacheDirs as $cacheDir) { | 
            ||
| 1263 |             if (!empty($cacheDir)) { | 
            ||
| 1264 |                 Processes::mwExec("{$rmPath} -rf {$cacheDir}/*"); | 
            ||
| 1265 | }  | 
            ||
| 1266 | }  | 
            ||
| 1267 | |||
| 1268 | // Delete boot cache folders  | 
            ||
| 1269 |         if (is_dir('/mountpoint') && self::isStorageDiskMounted()) { | 
            ||
| 1270 |             Processes::mwExec("{$rmPath} -rf /mountpoint"); | 
            ||
| 1271 | }  | 
            ||
| 1272 | }  | 
            ||
| 1273 | |||
| 1274 | /**  | 
            ||
| 1275 | * Create system folders and links after upgrade and connect config DB  | 
            ||
| 1276 | */  | 
            ||
| 1277 | public function createWorkDirsAfterDBUpgrade(): void  | 
            ||
| 1284 | }  | 
            ||
| 1285 | |||
| 1286 | /**  | 
            ||
| 1287 | * Restore modules cache folders and symlinks  | 
            ||
| 1288 | */  | 
            ||
| 1289 | public function createModulesCacheSymlinks(): void  | 
            ||
| 1295 | }  | 
            ||
| 1296 | }  | 
            ||
| 1297 | |||
| 1298 | /**  | 
            ||
| 1299 | * Fixes permissions for Folder and Files  | 
            ||
| 1300 | */  | 
            ||
| 1301 | private function applyFolderRights(): void  | 
            ||
| 1302 |     { | 
            ||
| 1303 | // Add Rights to the WWW dirs plus some core dirs  | 
            ||
| 1304 | $www_dirs = [];  | 
            ||
| 1305 | $exec_dirs = [];  | 
            ||
| 1306 | |||
| 1307 | $arrConfig = $this->config->toArray();  | 
            ||
| 1308 |         foreach ($arrConfig as $key => $entry) { | 
            ||
| 1309 |             if (in_array($key, ['www', 'adminApplication'])) { | 
            ||
| 1310 |                 foreach ($entry as $subKey => $subEntry) { | 
            ||
| 1311 |                     if (stripos($subKey, 'path') === false && stripos($subKey, 'dir') === false) { | 
            ||
| 1312 | continue;  | 
            ||
| 1313 | }  | 
            ||
| 1314 | $www_dirs[] = $subEntry;  | 
            ||
| 1315 | }  | 
            ||
| 1316 | }  | 
            ||
| 1317 | }  | 
            ||
| 1318 | |||
| 1319 |         $www_dirs[] = $this->config->path('core.tempDir'); | 
            ||
| 1320 |         $www_dirs[] = $this->config->path('core.logsDir'); | 
            ||
| 1321 | |||
| 1322 | // Create empty log files with www rights  | 
            ||
| 1323 | $logFiles = [  | 
            ||
| 1324 |             $this->config->path('database.debugLogFile'), | 
            ||
| 1325 |             $this->config->path('cdrDatabase.debugLogFile'), | 
            ||
| 1326 |             $this->config->path('eventsLogDatabase.debugLogFile') | 
            ||
| 1327 | ];  | 
            ||
| 1328 | |||
| 1329 |         foreach ($logFiles as $logFile) { | 
            ||
| 1330 | $filename = (string)$logFile;  | 
            ||
| 1331 |             if (!file_exists($filename)) { | 
            ||
| 1332 | file_put_contents($filename, '');  | 
            ||
| 1333 | }  | 
            ||
| 1334 | $www_dirs[] = $filename;  | 
            ||
| 1335 | }  | 
            ||
| 1336 | |||
| 1337 | $www_dirs[] = '/etc/version';  | 
            ||
| 1338 |         $www_dirs[] = appPath('/'); | 
            ||
| 1339 | |||
| 1340 | // Add read rights  | 
            ||
| 1341 |         Util::addRegularWWWRights(implode(' ', $www_dirs)); | 
            ||
| 1342 | |||
| 1343 | // Add executable rights  | 
            ||
| 1344 |         $exec_dirs[] = appPath('src/Core/Asterisk/agi-bin'); | 
            ||
| 1345 |         $exec_dirs[] = appPath('src/Core/Rc'); | 
            ||
| 1346 |         Util::addExecutableRights(implode(' ', $exec_dirs)); | 
            ||
| 1347 | |||
| 1348 |         $mountPath = Util::which('mount'); | 
            ||
| 1349 |         Processes::mwExec("{$mountPath} -o remount,ro /offload 2> /dev/null"); | 
            ||
| 1350 | }  | 
            ||
| 1351 | |||
| 1352 | /**  | 
            ||
| 1353 | * Creates swap file on storage  | 
            ||
| 1354 | */  | 
            ||
| 1355 | public function mountSwap(): void  | 
            ||
| 1356 |     { | 
            ||
| 1357 |         if(Util::isSystemctl()){ | 
            ||
| 1358 | // Не настраиваем.  | 
            ||
| 1359 | return;  | 
            ||
| 1360 | }  | 
            ||
| 1361 |         $tempDir = $this->config->path('core.tempDir'); | 
            ||
| 1362 |         $swapFile = "{$tempDir}/swapfile"; | 
            ||
| 1363 | |||
| 1364 |         $swapOffCmd = Util::which('swapoff'); | 
            ||
| 1365 |         Processes::mwExec("{$swapOffCmd} {$swapFile}"); | 
            ||
| 1366 | |||
| 1367 | $this->makeSwapFile($swapFile);  | 
            ||
| 1368 |         if (!file_exists($swapFile)) { | 
            ||
| 1369 | return;  | 
            ||
| 1370 | }  | 
            ||
| 1371 |         $swapOnCmd = Util::which('swapon'); | 
            ||
| 1372 |         $result = Processes::mwExec("{$swapOnCmd} {$swapFile}"); | 
            ||
| 1373 |         Util::sysLogMsg('Swap', 'connect swap result: ' . $result, LOG_INFO); | 
            ||
| 1374 | }  | 
            ||
| 1375 | |||
| 1376 | /**  | 
            ||
| 1377 | * Создает swap файл на storage.  | 
            ||
| 1378 | * @param $swapFile  | 
            ||
| 1379 | */  | 
            ||
| 1380 | private function makeSwapFile($swapFile): void  | 
            ||
| 1381 |     { | 
            ||
| 1382 |         $swapLabel = Util::which('swaplabel'); | 
            ||
| 1383 |         if (Processes::mwExec("{$swapLabel} {$swapFile}") === 0) { | 
            ||
| 1384 | // Файл уже существует.  | 
            ||
| 1385 | return;  | 
            ||
| 1386 | }  | 
            ||
| 1387 |         if (file_exists($swapFile)) { | 
            ||
| 1388 | unlink($swapFile);  | 
            ||
| 1389 | }  | 
            ||
| 1390 | |||
| 1391 | $size = $this->getStorageFreeSpaceMb();  | 
            ||
| 1392 |         if ($size > 2000) { | 
            ||
| 1393 | $swapSize = 1024;  | 
            ||
| 1394 |         } elseif ($size > 1000) { | 
            ||
| 1395 | $swapSize = 512;  | 
            ||
| 1396 |         } else { | 
            ||
| 1397 | // Не достаточно свободного места.  | 
            ||
| 1398 | return;  | 
            ||
| 1399 | }  | 
            ||
| 1400 | $bs = 1024;  | 
            ||
| 1401 | $countBlock = $swapSize * $bs;  | 
            ||
| 1402 |         $ddCmd = Util::which('dd'); | 
            ||
| 1403 | |||
| 1404 |         Util::sysLogMsg('Swap', 'make swap ' . $swapFile, LOG_INFO); | 
            ||
| 1405 |         Processes::mwExec("{$ddCmd} if=/dev/zero of={$swapFile} bs={$bs} count={$countBlock}"); | 
            ||
| 1406 | |||
| 1407 |         $mkSwapCmd = Util::which('mkswap'); | 
            ||
| 1408 |         Processes::mwExec("{$mkSwapCmd} {$swapFile}"); | 
            ||
| 1409 | }  | 
            ||
| 1410 | |||
| 1411 | /**  | 
            ||
| 1412 | * Returns free space on mounted storage disk  | 
            ||
| 1413 | *  | 
            ||
| 1414 | * @return int size in megabytes  | 
            ||
| 1415 | */  | 
            ||
| 1416 | public function getStorageFreeSpaceMb(): int  | 
            ||
| 1417 |     { | 
            ||
| 1418 | $size = 0;  | 
            ||
| 1419 | $mntDir = '';  | 
            ||
| 1420 |         $mounted = self::isStorageDiskMounted('', $mntDir); | 
            ||
| 1421 |         if (!$mounted) { | 
            ||
| 1422 | return 0;  | 
            ||
| 1423 | }  | 
            ||
| 1424 | $hd = $this->getAllHdd(true);  | 
            ||
| 1425 |         foreach ($hd as $disk) { | 
            ||
| 1426 |             if ($disk['mounted'] === $mntDir) { | 
            ||
| 1427 | $size = $disk['free_space'];  | 
            ||
| 1428 | break;  | 
            ||
| 1429 | }  | 
            ||
| 1430 | }  | 
            ||
| 1431 | return $size;  | 
            ||
| 1432 | }  | 
            ||
| 1433 | |||
| 1434 | /**  | 
            ||
| 1435 | * Сохраняем новые данные диска.  | 
            ||
| 1436 | *  | 
            ||
| 1437 | * @param $data  | 
            ||
| 1438 | * @param string $id  | 
            ||
| 1439 | */  | 
            ||
| 1440 | public function saveDiskSettings($data, $id = '1'): void  | 
            ||
| 1463 | }  | 
            ||
| 1464 | }  | 
            ||
| 1465 | |||
| 1466 | /**  | 
            ||
| 1467 | * Получение имени диска, смонтированного на conf.recover.  | 
            ||
| 1468 | * @return string  | 
            ||
| 1469 | */  | 
            ||
| 1470 | public function getRecoverDiskName(): string  | 
            ||
| 1471 |     { | 
            ||
| 1472 | $disks = $this->diskGetDevices(true);  | 
            ||
| 1473 |         foreach ($disks as $disk => $diskInfo) { | 
            ||
| 1474 | // RAID содержит вложенный массив "children"  | 
            ||
| 1489 | }  | 
            ||
| 1490 | }  |