Passed
Push — master ( 90372d...e80252 )
by Nikolay
25:24
created

Storage   F

Complexity

Total Complexity 134

Size/Duplication

Total Lines 809
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 134
eloc 400
dl 0
loc 809
c 0
b 0
f 0
rs 2

28 Methods

Rating   Name   Duplication   Size   Complexity  
A get_monitor_dir() 0 4 1
A umount_disk() 0 11 4
A format_disk_local_part2() 0 20 3
B mount_ftp() 0 33 7
A mount_disk() 0 20 5
A get_fs_type() 0 7 2
A disable_need_check_storage() 0 12 3
A save_fstab() 0 33 5
A disk_is_mounted() 0 13 3
A mkfs_disk() 0 20 5
A status_mkfs() 0 9 3
B get_vendor_disk() 0 24 8
C determine_format_fs() 0 65 14
A hdd_exists() 0 7 3
A get_uuid() 0 13 4
C get_all_hdd() 0 66 12
A cdrom_get_devices() 0 2 1
B configure() 0 45 9
A get_free_space() 0 12 3
A is_storage_disk_mounted() 0 17 4
B check_free_space() 0 42 8
B is_storage_disk() 0 32 9
A mount_sftp_disk() 0 19 3
A save_disk_settings() 0 18 5
A disk_get_devices() 0 3 1
A get_disk_settings() 0 15 4
A get_media_dir() 0 11 3
A format_disk_local() 0 34 2

How to fix   Complexity   

Complex Class

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
2
/**
3
 * Copyright © MIKO LLC - All Rights Reserved
4
 * Unauthorized copying of this file, via any medium is strictly prohibited
5
 * Proprietary and confidential
6
 * Written by Alexey Portnov, 2 2020
7
 */
8
9
require_once 'globals.php';
10
/**
11
* Вспомогательные методы. 
12
*/
13
class Storage {
14
    /**
15
     * Получение массива подключенныйх HDD.
16
     * @return array
17
     */
18
    private function disk_get_devices() {
19
        //  TODO // Переписать через использование lsblk.
20
		return explode(" ", trim(exec("/bin/ls /dev | grep '^[a-z]d[a-z]' | tr \"\n\" \" \"")));
21
	}
22
23
    /**
24
     * Получение массива подключенныйх cdrom.
25
     * @return array
26
     */
27
	private function cdrom_get_devices() {
28
		return explode(" ", trim(exec('/sbin/sysctl -n dev.cdrom.info | /bin/busybox grep "drive name" | /bin/busybox cut -f 3') ));
29
	}
30
31
    /**
32
     * Получение сведений по диску.
33
     * @param $disk
34
     * @return string
35
     */
36
	private function get_vendor_disk($disk){
37
		$temp_vendor = array();
38
		if( is_file("/sys/block/".$disk."/device/vendor") ){
39
            $data = trim(file_get_contents("/sys/block/".$disk."/device/vendor"));
40
            if($data != ''){
41
                $temp_vendor[] = trim(str_replace(',',' ', $data));
42
            }
43
		}
44
		if( is_file("/sys/block/".$disk."/device/model") ){
45
			$data = trim(file_get_contents("/sys/block/".$disk."/device/model"));
46
            if($data != ''){
47
                $temp_vendor[] = trim(str_replace(',',' ', $data));
48
            }
49
		}
50
		if(count($temp_vendor) == 0){
51
			$temp_vendor[] = $disk;
52
		}
53
		if(is_file("/sys/block/".$disk."/device/type")){
54
            $data = trim(file_get_contents("/sys/block/".$disk."/device/type"));
55
            if($data != ''){
56
                $temp_vendor[] = trim(str_replace(',',' ', $data));
57
            }
58
		}
59
		return implode(', ', $temp_vendor);
60
	}
61
62
    /**
63
     * Проверка, смонтирован ли диск.
64
     * @param $disk
65
     * @param $filter
66
     * @return bool
67
     */
68
	static function disk_is_mounted($disk, $filter = '/dev/'){
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
69
		
70
		$out = [];
71
        Util::mwexec("mount | grep '{$filter}{$disk}'", $out);
72
		if(count($out)>0){
73
            $res_out = end($out);
74
        }else{
75
            $res_out = implode('', $out);
76
        }
77
		$data = explode(' ', trim($res_out));
78
				
79
		$result = (count($data) > 2) ? $data[2] : false;
80
		return $result;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $result also could return the type string which is incompatible with the documented return type boolean.
Loading history...
81
	}
82
83
    /**
84
     * Возвращает директорию для хранения media файлов.
85
     * @return string
86
     */
87
	static function get_media_dir(){
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
88
		$dir = "/var/asterisk/spool";
89
		if( Storage::is_storage_disk_mounted() ){
90
            $storage_dev_file = "{$GLOBALS['g']['varetc_path']}/storage_device";
91
            if(file_exists($storage_dev_file)){
92
                $dir = file_get_contents($storage_dev_file);
93
            }else{
94
                $dir = '/storage/usbdisk1';
95
            }
96
		}
97
		return $dir;
98
	}
99
100
    /**
101
     * Возвращает директорию для хранения файлов записей разговоров.
102
     * @return string
103
     */
104
	static function get_monitor_dir(){
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
105
	    global $g;
106
		$mount_point = Storage::get_media_dir();
107
		return "{$mount_point}/{$g['pt1c_pbx_name']}/voicemailarchive/monitor/";
108
	}
109
110
    /**
111
     * Получаем свободное место на диске в Mb.
112
     * @param $hdd
113
     * @return mixed
114
     */
115
	public function get_free_space($hdd){
116
        $out = [];
117
        $hdd = escapeshellarg($hdd);
118
        Util::mwexec("df -m | grep {$hdd} | awk '{print $4}'",$out);
119
        $result = 0;
120
        foreach ($out as $res){
121
            if(!is_numeric($res)){
122
                continue;
123
            }
124
            $result += (1*$res);
125
        }
126
        return $result;
127
    }
128
129
    /**
130
     * Проверка свободного места на дисках. Уведомление в случае проблем.
131
     */
132
    public function check_free_space(){
133
        $storage = new Storage();
134
        $util    = new Util();
135
        $hdd     = $storage->get_all_hdd( true );
136
        // Создание больщого файла для тестов.
137
        // head -c 1500MB /dev/urandom > /storage/usbdisk1/big_file.mp3
138
        foreach ($hdd as $disk){
139
            if($disk['sys_disk'] === true && !Storage::is_storage_disk_mounted("{$disk['id']}4")){
140
                // Это системный диск (4ый раздел). Он не смонтирован.
141
                continue;
142
            }
143
144
            $free = ($disk['free_space'] / $disk['size']*100);
145
            $need_alert = false; $test_alert = '';
146
            if($free < 5){
147
                $need_alert = true;
148
                $test_alert = "The {$disk['id']} has less than 5% of free space available.";
149
            }
150
151
            if($disk['free_space'] < 500){
152
                $need_alert = true;
153
                $test_alert = "The {$disk['id']} has less than 500MB of free space available.";
154
            }
155
156
            if($disk['free_space'] < 100){
157
                $need_alert = true;
158
                $test_alert = "The {$disk['id']} has less than 100MB of free space available. Old call records will be deleted.";
159
                Util::mwexec_bg('/usr/bin/php -f /etc/inc/workers/worker_remove_old_records.php');
160
            }
161
162
            if(!$need_alert){
163
                continue;
164
            }
165
166
            Util::sys_log_msg("STORAGE", $test_alert);
167
            $data = [
168
                'Device     - ' => "/dev/{$disk['id']}",
169
                'Directoire - ' => "{$disk['mounted']}",
170
                'Desciption - ' => $test_alert,
171
            ];
172
            // Добавляем задачу на уведомление.
173
            $util->add_job_to_beanstalk('notify_error_storage', $data);
174
        }
175
176
    }
177
178
    /**
179
     * Возвращает все подключенные HDD.
180
     * @param bool $mounted_only
181
     * @return array
182
     */
183
	public function get_all_hdd($mounted_only = false){
184
		global $g;
185
		$res_disks = [];
186
187
		$cd_disks= $this->cdrom_get_devices();
188
        $cd_disks= array_unique($cd_disks);
189
190
        // TODO Получение данных о дисках в формате JSON:
191
        // lsblk -J -b -o VENDOR,MODEL,SERIAL,LABEL,TYPE,FSTYPE,MOUNTPOINT,SUBSYSTEMS,NAME,UUID
192
        $disks 	 = $this->disk_get_devices();
193
		$disks   = array_unique($disks);
194
195
        $cf_disk = '';
196
        if(file_exists($g['varetc_path'].'/cfdevice')){
197
            $cf_disk = trim(file_get_contents($g['varetc_path'].'/cfdevice'));
198
        }
199
200
		foreach ($disks as $disk) {
201
		    if(in_array($disk, $cd_disks)){
202
		        // Это CD-ROM.
203
		        continue;
204
            }
205
			unset($temp_vendor, $temp_size, $original_size);
206
            $mounted  = Storage::disk_is_mounted("{$disk}");
207
            if($mounted_only == true && $mounted == false){
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
208
                continue;
209
            }
210
            $sys_disk = ($cf_disk == $disk)? true : false;
211
			
212
			$mb_size = 0;
213
			if(is_file("/sys/block/".$disk."/size")){
214
				$original_size = trim(file_get_contents("/sys/block/".$disk."/size"));
215
				$original_size = ($original_size*512/1024/1024);
216
				$mb_size = $original_size;
217
			}
218
			if($mb_size > 100){
219
				$temp_size 	 = sprintf("%.0f MB",$mb_size);
220
				$temp_vendor = $this->get_vendor_disk($disk);
221
				$free_space  = $this->get_free_space($disk);
222
223
				$arr_disk_info = $this->determine_format_fs($disk);
224
                if(count($arr_disk_info)>0){
225
                    $used = 0;
226
                    foreach ($arr_disk_info as $disk_info){
227
                        $used += $disk_info['used_space'];
228
                    }
229
                    if($used>0){
230
                        $free_space = $mb_size - $used;
231
                    }
232
                }
233
234
				$res_disks[] = [
235
					'id' 		=> $disk,
236
					'size' 		=> $mb_size,
237
					'size_text' => $temp_size,
238
					'vendor' 	=> $temp_vendor,
239
                    'mounted' 	=> $mounted,
240
                    'free_space'=> $free_space,
241
                    'partitions'=> $arr_disk_info,
242
					'sys_disk'	=> $sys_disk
243
				];
244
			}
245
			
246
		}
247
		
248
		return $res_disks;
249
	}
250
251
    /**
252
     * Проверка, смонтирован ли диск - хранилище.
253
     * @param string $filter
254
     * @param string $mount_dir
255
     * @return bool
256
     */
257
	static function is_storage_disk_mounted($filter='', & $mount_dir=''){
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
258
	    if('' == $filter){
259
            $res_disk = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $res_disk is dead and can be removed.
Loading history...
260
            $filename = "{$GLOBALS['g']['varetc_path']}/storage_device";
261
            if(file_exists($filename)){
262
                $filter = file_get_contents($filename);
263
            }else{
264
                $filter = 'usbdisk1';
265
            }
266
        }
267
        $filter = escapeshellarg($filter);
268
269
	    $out = [];
270
		Util::mwexec("mount | grep {$filter} | awk '{print $3}'", $out);
271
        $mount_dir = trim(implode('', $out));
272
			
273
		return ($mount_dir == '') ? false : true;
274
	}
275
276
    /**
277
     * Получение идентификатора устройства.
278
     * @param $device
279
     * @return bool
280
     */
281
	public function get_uuid($device){
282
		if (strlen($device) == 0) {
283
			return false;
284
		}
285
		$res = Util::mwexec("/sbin/blkid -ofull {$device} | /bin/busybox sed -r 's/[[:alnum:]]+=/\\n&/g' | /bin/busybox grep \"^UUID\" | /bin/busybox awk -F \"\\\"\" '{print $2}' | /usr/bin/head -n 1", $output);
286
		if ($res == 0 && count($output)>0) {
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $res of type integer|null to 0; this is ambiguous as not only 0 == 0 is true, but null == 0 is true, too. Consider using a strict comparison ===.
Loading history...
287
            $result = $output[0];
288
		}
289
		else {
290
            $result = false;
291
		}
292
293
        return $result;
294
	}
295
296
    /**
297
     * Форматирование диска.
298
     * @param string $device
299
     * @param bool   $bg
300
     * @return mixed
301
     */
302
	private function format_disk_local_part2($device, $bg = false){
303
		if (is_numeric(substr($device, -1))) {
304
			$device_id = "";
305
		}else {
306
			$device_id = "1";
307
		}
308
		$format = 'ext4';
309
		$cmd    = "/sbin/mkfs.{$format} {$device}{$device_id}";
310
		if($bg == false){
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
311
            openlog("storage_format_disk", LOG_NDELAY, LOG_DAEMON);
312
            Util::mwexec("$cmd 2>&1", $out, $retval);
313
            syslog(LOG_NOTICE, "mkfs.{$format} returned $retval");
314
            closelog();
315
        }else{
316
            usleep(200000);
317
		    Util::mwexec_bg("$cmd");
318
            $retval = true;
319
        }
320
321
		return $retval;
322
	}
323
324
    /**
325
     * Разметка диска.
326
     * @param string $device
327
     * @param bool   $bg
328
     * @return mixed
329
     */
330
	public function format_disk_local($device, $bg = false){
331
		openlog("storage", LOG_NDELAY, LOG_DAEMON);
332
		// overwrite with fresh DOS partition table
333
		Util::mwexec("echo \"o\n" .
334
		// create new
335
		"n\n" .
336
		// primary partition
337
		"p\n" .
338
		// number 1
339
		"1\n" .
340
		// from the beginning
341
		"\n" .
342
		// to the end
343
		"\n" .
344
		// change type
345
		/*
346
		"t\n" .
347
		// to FAT32
348
		"b\n" .
349
		// set active
350
		"a\n" .
351
		// partition 1
352
		"1\n" .
353
		*/
354
		// and write changes
355
		"w\n" .
356
		"\" | fdisk " . $device, $out, $retval);
357
		syslog(LOG_NOTICE, "fdisk returned " . $retval);
358
		closelog();
359
360
		if(false == $bg){
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
361
            sleep(1);
362
        }
363
        return $this->format_disk_local_part2($device, $bg);
364
	}
365
366
    /**
367
     * Монтирование разделов диска с базой данных настроек.
368
     */
369
	public function configure(){
370
371
	    $cf_disk = '';
372
        $storage_dev_file = "{$GLOBALS['g']['varetc_path']}/storage_device";
373
        if(file_exists($storage_dev_file)){
374
            unlink($storage_dev_file);
375
        }
376
377
        if(file_exists($GLOBALS['g']['varetc_path'].'/cfdevice')){
378
            $cf_disk = trim(file_get_contents($GLOBALS['g']['varetc_path'].'/cfdevice'));
379
        }
380
381
		$disks   = $this->get_disk_settings();
382
		$conf = '';
383
		foreach ($disks as $disk) {
384
            clearstatcache();
385
		    if( $disk['device'] !== "/dev/{$cf_disk}" ){
386
                // Если это обычный диск, то раздел 1.
387
		        $dev = "{$disk['device']}1";
388
            }else{
389
                // Если это системный диск, то пытаемся подключить раздел 4.
390
                $dev = "{$disk['device']}4";
391
            }
392
		    if( !$this->hdd_exists($dev) ){
393
                // Диск не существует.
394
                continue;
395
            }
396
            if($disk['media'] === '1' || !file_exists($storage_dev_file)){
397
                file_put_contents($storage_dev_file, "/storage/usbdisk{$disk['id']}");
398
            }
399
400
            $str_uid = 'UUID='.$this->get_uuid($dev).'';
401
            $format_p4 = self::get_fs_type($dev);
402
			$conf .= "{$str_uid} /storage/usbdisk{$disk['id']} {$format_p4} async,rw 0 0\n";
403
			$is_mounted = $this->is_storage_disk_mounted("/storage/usbdisk{$disk['id']}");
0 ignored issues
show
Unused Code introduced by
The assignment to $is_mounted is dead and can be removed.
Loading history...
404
            $mount_point = "/storage/usbdisk{$disk['id']}";
405
			if (!file_exists($mount_point)) {
406
				Util::mwexec("mkdir -p {$mount_point}");
407
			}
408
		}
409
		$this->save_fstab($conf);
410
		System::setup_php_log();
411
412
		$util = new Util();
413
        $util->create_work_dirs();
414
    }
415
416
    public static function disable_need_check_storage():void {
417
        try{
418
            // Проверка диска не требуется.
419
            $pbxSettings = \Models\Storage::find();
420
            /** @var \Models\Storage $pbxSettings */
421
            /** @var \Models\Storage $row */
422
            foreach ($pbxSettings as $row){
423
                $row->check_when_booting = 0;
424
                $row->save();
425
            }
426
        }catch (Exception $e){
427
            Util::sys_log_msg('Reboot: Storage ', $e->getMessage());
428
        }
429
    }
430
431
    /**
432
     * Проверяет, существует ли диск в массиве.
433
     * @param $disk
434
     * @return bool
435
     */
436
	private function hdd_exists($disk){
437
	    $result = false;
438
	    $uid = $this->get_uuid("{$disk}");
439
        if(file_exists("{$disk}") && $uid !== false){
440
            $result = true;
441
        }
442
	    return $result;
443
    }
444
445
    /**
446
     * Генерация файла fstab. Монтирование разделов.
447
     * @param string $conf
448
     */
449
    public function save_fstab($conf=''){
450
		global $g;
451
		// Точка монтирования доп. дисков.
452
		Util::mwexec("mkdir -p /storage/");
453
		if(! file_exists($g['varetc_path'].'/cfdevice')){
454
            return;
455
        }
456
        $fstab = '';
457
		$file_data  = file_get_contents($g['varetc_path'].'/cfdevice');
458
		$cf_disk    = trim($file_data);
459
		if('' == $cf_disk){
460
			return;
461
		}
462
		// $part1 	 = (strpos($cf_disk, "mmcblk") !== false)?"{$cf_disk}p1":"{$cf_disk}1"; // Boot
463
		$part2 	 = (strpos($cf_disk, "mmcblk") !== false)?"{$cf_disk}p2":"{$cf_disk}2"; // Offload
464
		$part3 	 = (strpos($cf_disk, "mmcblk") !== false)?"{$cf_disk}p3":"{$cf_disk}3"; // Conf
465
466
467
        $uid_part2 = 'UUID='.$this->get_uuid("/dev/{$part2}").'';
468
        $format_p2 = Storage::get_fs_type($part2);
469
        $uid_part3 = 'UUID='.$this->get_uuid("/dev/{$part3}").'';
470
        $format_p3 = Storage::get_fs_type($part3);
471
472
		// $fstab .= "/dev/{$part1} {$g['cf_path']} msdos ro 1 1\n"; // НЕ МОНТИРУЕМ!
473
		$fstab .= "{$uid_part2} /offload {$format_p2} ro 0 0\n";
474
		$fstab .= "{$uid_part3} {$g['cf_path']} {$format_p3} rw 1 1\n";
475
		$fstab .= $conf;
476
477
        file_put_contents("/etc/fstab", $fstab);
478
        // Дублируем для работы vmtoolsd.
479
        file_put_contents("/etc/mtab", $fstab);
480
		Util::mwexec("mount -a 2> /dev/null");
481
        Util::mwexec("chown -R www:www /cf/ > /dev/null 2> /dev/null");
482
	}
483
484
    /**
485
     * Получаем настройки диска из базы данных.
486
     * @param string $id
487
     * @return array
488
     */
489
	public function get_disk_settings($id=''){
490
        $data = array();
491
        if('' === $id){
492
            $pbxSettings = \Models\Storage::find();
493
            if($pbxSettings){
0 ignored issues
show
introduced by
$pbxSettings is of type Phalcon\Mvc\Model\ResultsetInterface, thus it always evaluated to true.
Loading history...
494
                // Возвращаем данные до модификации.
495
                $data = $pbxSettings->toArray();
496
            }
497
        }else{
498
            $pbxSettings = Models\Storage::findFirst("id='$id'");
499
            if($pbxSettings){
500
                $data = $pbxSettings->toArray();
501
            }
502
        }
503
        return $data;
504
	}
505
506
    /**
507
     * Сохраняем новые данные диска.
508
     * @param $data
509
     * @param int $id
510
     */
511
	public function save_disk_settings($data, $id = '1'){
512
		if(!is_array($data)) return;
513
		$disk_data = $this->get_disk_settings($id);
514
		if(count($disk_data) === 0){
515
            $uniqid = strtoupper('STORAGE-DISK-' . md5( time() ) );
516
		    $storage_settings = new Models\Storage();
517
            foreach ($data as $key => $val) {
518
                $storage_settings->writeAttribute($key, $val);
519
            }
520
            $storage_settings->writeAttribute('uniqid', $uniqid);
521
            $storage_settings->save();
522
523
        }else{
524
			$storage_settings = Models\Storage::findFirst("id = '$id'");
525
            foreach ($data as $key => $value){
526
                $storage_settings->writeAttribute($key, $value);
527
            }
528
            $storage_settings->save();
529
		}
530
	}
531
532
    /**
533
     * Прверяем является ли диск хранилищем.
534
     * @param $device
535
     * @return bool
536
     */
537
    static function is_storage_disk($device){
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
538
	    $result = false;
539
	    if(!file_exists("{$device}")){
540
	         return $result;
541
        }
542
543
	    $tmp_dir = '/tmp/mnt_'.time();
544
545
	    if(!file_exists($tmp_dir) && !mkdir($tmp_dir, 0777, true) && !is_dir($tmp_dir)){
546
            Util::sys_log_msg('Storage', 'Unable to create directory '. $tmp_dir);
547
            return $result;
548
        }
549
        $out = [];
550
551
        $storage = new Storage();
552
        $uid_part = 'UUID='.$storage->get_uuid($device).'';
553
        $format = self::get_fs_type($device);
554
555
        Util::mwexec("mount -t {$format} {$uid_part} {$tmp_dir}", $out);
556
        if(is_dir("{$tmp_dir}/mikopbx") && trim(implode('', $out)) === ''){
557
            // $out - пустая строка, ошибок нет
558
            // присутствует каталог mikopbx.
559
            $result = true;
560
        }
561
        if(self::is_storage_disk_mounted($device)){
562
            Util::mwexec("umount {$device}");
563
        }
564
565
        if(!self::is_storage_disk_mounted($device)){
566
            Util::mwexec("rm -rf '{$tmp_dir}'");
567
        }
568
        return $result;
569
    }
570
571
    /**
572
     * Определить формат файловой системы и размер дисков.
573
     * @param $device
574
     * @return array|bool
575
     */
576
    public function determine_format_fs($device){
577
        $allow_formats = ['ext2', 'ext4', 'fat', 'ntfs', 'msdos'];
578
        $device  = str_replace('/dev/', '', $device);
579
        $devices = explode(" ", trim(exec("/bin/ls /dev | grep '{$device}' | tr \"\n\" \" \"")));
580
581
        $result_data = [];
582
        foreach ($devices as $dev) {
583
            if(empty($dev) || (count($devices)>1 && $device == $dev) || is_dir("/sys/block/{$dev}")){
584
                continue;
585
            }
586
            $mb_size = 0;
587
            $path_size_info = '';
588
            $tmp_path = "/sys/block/{$device}/{$dev}/size";
589
            if(file_exists($tmp_path)) {
590
                $path_size_info = $tmp_path;
591
            }
592
            if(empty($path_size_info)){
593
                $tmp_path = "/sys/block/".substr($dev,0,3)."/{$dev}/size";
594
                if(file_exists($tmp_path)) {
595
                    $path_size_info = $tmp_path;
596
                }
597
            }
598
599
            if(!empty($path_size_info)){
600
                $original_size = trim(file_get_contents($path_size_info));
601
                $original_size = ($original_size*512/1024/1024);
602
                $mb_size = $original_size;
603
            }
604
605
            $tmp_dir = "/tmp/{$dev}_" . time();
606
            $out = [];
607
608
            $fs             = null;
609
            $need_unmount   = false;
610
            $mount_dir      = '';
611
            if(Storage::is_storage_disk_mounted("/dev/{$dev} ", $mount_dir)){
612
                Util::mwexec("mount | grep '/dev/{$dev}' | awk '{print $5}'", $out);
613
                $fs = trim(implode("", $out));
614
                $fs = ($fs == 'fuseblk')?'ntfs':$fs;
615
                $free_space = $this->get_free_space("/dev/{$dev} ");
616
                $used_space = $mb_size - $free_space;
617
            }else{
618
                $format = Storage::get_fs_type($device);
619
                if(in_array($format, $allow_formats)){
620
                    $fs = $format;
621
                }
622
                Storage::mount_disk($dev, $format, $tmp_dir);
623
624
                $need_unmount = true;
625
                $used_space = Util::get_size_file("$tmp_dir");
626
            }
627
            $result_data[] = [
628
                "dev"        => $dev,
629
                'size' 		 => round($mb_size, 2),
630
                "used_space" => round($used_space, 2),
631
                "free_space" => round($mb_size - $used_space, 2),
632
                "uuid"       => $this->get_uuid("/dev/{$dev} "),
633
                "fs"         => $fs,
634
            ];
635
            if($need_unmount){
636
                Storage::umount_disk($tmp_dir);
637
            }
638
        }
639
640
        return $result_data;
641
    }
642
643
    /**
644
     * Возвращает тип файловой системы блочного устройства.
645
     * @param $device
646
     * @return string
647
     */
648
    static function get_fs_type($device){
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
649
        $device  = str_replace('/dev/', '', $device);
650
        $out = [];
651
        Util::mwexec("/sbin/blkid -ofull /dev/{$device} | /bin/busybox sed -r 's/[[:alnum:]]+=/\\n&/g' | /bin/busybox grep \"^TYPE=\" | /bin/busybox awk -F \"\\\"\" '{print $2}'", $out);
652
        $format = implode('', $out);
653
        if($format == 'msdosvfat')  $format='msdos';
654
        return $format;
655
    }
656
657
    /**
658
     * Монтирует диск в указанный каталог.
659
     * @param $dev
660
     * @param $format
661
     * @param $dir
662
     * @return bool
663
     */
664
    static function mount_disk($dev, $format, $dir){
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
665
        if(Storage::is_storage_disk_mounted("/dev/{$dev} ")){
666
            return true;
667
        }
668
        if(!file_exists($dir)){
669
            @mkdir($dir, 0777, true);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for mkdir(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

669
            /** @scrutinizer ignore-unhandled */ @mkdir($dir, 0777, true);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
670
        }
671
        if(!file_exists($dir)){
672
            Util::sys_log_msg('Storage', "Unable mount $dev $format to $dir. Unable create dir.");
673
            return false;
674
        }
675
        $dev  = str_replace('/dev/', '', $dev);
676
        if('ntfs' == $format){
677
            Util::mwexec("mount.ntfs-3g /dev/{$dev} {$dir}", $out);
678
        }else{
679
            $storage = new Storage();
680
            $uid_part = 'UUID='.$storage->get_uuid("/dev/{$dev}").'';
681
            Util::mwexec("mount -t {$format} {$uid_part} {$dir}", $out);
682
        }
683
        return Storage::is_storage_disk_mounted("/dev/{$dev} ");
684
    }
685
686
    /**
687
     * Монитирование каталога с удаленного сервера SFTP.
688
     * @param        $host
689
     * @param int    $port
690
     * @param string $user
691
     * @param string $pass
692
     * @param string $remout_dir
693
     * @param string $local_dir
694
     * @return bool
695
     */
696
    static function mount_sftp_disk($host, $port, $user, $pass, $remout_dir, $local_dir){
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
697
        if(!file_exists($local_dir)){
698
            mkdir($local_dir, 0777, true);
699
        }
700
701
        $out  = [];
702
        $command =  "/usr/bin/timeout -t 3 /usr/bin/sshfs -p {$port} -o nonempty -o password_stdin -o 'StrictHostKeyChecking=no' ".
703
                    "{$user}@{$host}:{$remout_dir} {$local_dir} << EOF\n".
704
                    "{$pass}\n".
705
                    "EOF\n";
706
        // file_put_contents('/tmp/sshfs_'.$host, $command);
707
        Util::mwexec($command,$out);
708
        $response = trim(implode('', $out));
709
        if('Terminated' == $response){
710
            // Удаленный сервер не ответил / или не корректно указан пароль.
711
            unset($response);
712
        }
713
714
        return Storage::is_storage_disk_mounted("$local_dir ");
715
    }
716
717
    /**
718
     * Монитирование каталога с удаленного сервера FTP.
719
     * @param        $host
720
     * @param        $port
721
     * @param        $user
722
     * @param        $pass
723
     * @param string $remout_dir
724
     * @param        $local_dir
725
     * @return bool
726
     */
727
    static function mount_ftp($host, $port, $user, $pass, $remout_dir, $local_dir){
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
728
        if(!file_exists($local_dir)){
729
            mkdir($local_dir, 0777, true);
730
        }
731
        $out  = [];
732
733
        // Собираем строку подключения к ftp.
734
        $auth_line = '';
735
        if(!empty($user)){
736
            $auth_line.='user="'.$user;
737
            if(!empty($pass)){
738
                $auth_line.=":{$pass}";
739
            }
740
            $auth_line.='",';
741
        }
742
743
        $connect_line = 'ftp://'.$host;
744
        if(!empty($port)){
745
            $connect_line.=":{$port}";
746
        }
747
        if(!empty($remout_dir)){
748
            $connect_line.="$remout_dir";
749
        }
750
751
        $command =  "/usr/bin/timeout -t 3 /usr/bin/curlftpfs  -o allow_other -o {$auth_line}fsname={$host} {$connect_line} {$local_dir}";
752
        Util::mwexec($command,$out);
753
        $response = trim(implode('', $out));
754
        if('Terminated' === $response){
755
            // Удаленный сервер не ответил / или не корректно указан пароль.
756
            unset($response);
757
        }
758
759
        return Storage::is_storage_disk_mounted("$local_dir ");
760
    }
761
762
    /**
763
     * Размонтирует диск. Удаляет каталог в случае успеха.
764
     * @param $dir
765
     * @return bool
766
     */
767
    static function umount_disk($dir){
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
768
        if(self::is_storage_disk_mounted($dir)){
769
            Util::mwexec("/etc/rc/shell_functions.sh 'killprocesses' '$dir' -TERM 0");
770
            Util::mwexec("umount {$dir}");
771
        }
772
        $result = !self::is_storage_disk_mounted($dir);
773
        if($result && file_exists($dir)){
774
            // Если диск не смонтирован, то удаляем каталог.
775
            Util::mwexec("rm -rf '{$dir}'");
776
        }
777
        return $result;
778
    }
779
780
    /**
781
     * Запускает процесс форматирования диска.
782
     * @param $dev
783
     * @return array|bool
784
     */
785
    static function mkfs_disk($dev){
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
786
        if(!file_exists($dev)){
787
            $dev = "/dev/{$dev}";
788
        }
789
        if(!file_exists($dev)) {
790
            return false;
791
        }
792
        $dir = '';
793
        Storage::is_storage_disk_mounted("$dev", $dir);
794
795
        if(empty($dir) || Storage::umount_disk($dir)){
796
            // Диск размонтирован.
797
            $st = new Storage();
798
            // Будет запущен процесс:
799
            $st->format_disk_local($dev, true);
800
            sleep(1);
801
            return (Storage::status_mkfs($dev) == 'inprogress');
802
        }else{
803
            // Ошибка размонтирования диска.
804
            return false;
805
        }
806
    }
807
808
    /**
809
     * Возвращает текущий статус форматирования диска.
810
     * @param $dev
811
     * @return string
812
     */
813
    static function status_mkfs($dev){
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
814
        if(!file_exists($dev)){
815
            $dev = "/dev/{$dev}";
816
        }
817
        $out = array();
818
        Util::mwexec("ps -A -f | grep {$dev} | grep mkfs | grep -v grep", $out);
819
        $mount_dir = trim(implode('', $out));
820
821
        return ($mount_dir == '') ? 'ended' : 'inprogress';
822
    }
823
}