Passed
Push — develop ( 6ae8a1...209d0b )
by Nikolay
05:24
created

System::networkReload()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 6
dl 0
loc 9
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
1
<?php
2
/*
3
 * MikoPBX - free phone system for small business
4
 * Copyright © 2017-2023 Alexey Portnov and Nikolay Beketov
5
 *
6
 * This program is free software: you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License as published by
8
 * the Free Software Foundation; either version 3 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License along with this program.
17
 * If not, see <https://www.gnu.org/licenses/>.
18
 */
19
20
namespace MikoPBX\Core\System;
21
22
use DateTime;
23
use DateTimeZone;
24
use MikoPBX\Common\Models\CustomFiles;
25
use MikoPBX\Common\Models\PbxSettings;
26
use MikoPBX\Common\Models\PbxSettingsConstants;
27
use MikoPBX\Core\Asterisk\Configs\H323Conf;
28
use MikoPBX\Core\Asterisk\Configs\HepConf;
29
use MikoPBX\Core\System\Configs\CronConf;
30
use MikoPBX\Core\System\Configs\IptablesConf;
31
use MikoPBX\Core\System\Configs\PHPConf;
32
use MikoPBX\Core\System\Configs\NTPConf;
33
use MikoPBX\Core\Asterisk\Configs\QueueConf;
34
use Phalcon\Di;
35
36
37
/**
38
 * Class System
39
 *
40
 * This class provides various system-level functionalities.
41
 *
42
 * @package MikoPBX\Core\System
43
 * @property \Phalcon\Config config
44
 */
45
class System extends Di\Injectable
46
{
47
    private MikoPBXConfig $mikoPBXConfig;
48
49
    /**
50
     * System constructor - Instantiates MikoPBXConfig.
51
     */
52
    public function __construct()
53
    {
54
        $this->mikoPBXConfig = new MikoPBXConfig();
55
    }
56
57
    /**
58
     * Is the configuration default?
59
     * @return bool
60
     */
61
    public function isDefaultConf():bool
62
    {
63
        $di = Di::getDefault();
64
        if ($di === null) {
65
            return false;
66
        }
67
        $sqlite3 = Util::which('sqlite3');
68
        $md5sum = Util::which('md5sum');
69
        $busybox = Util::which('busybox');
70
        $md5_1 = shell_exec("$sqlite3 ".$di->getConfig()->path('database.dbfile')." .dump | $md5sum | $busybox cut -f 1 -d ' '");
71
        $md5_2 = shell_exec("$sqlite3 /conf.default/mikopbx.db .dump | $md5sum | $busybox cut -f 1 -d ' '");
72
        return $md5_1 === $md5_2;
73
    }
74
75
    /**
76
     * Trying to restore the backup
77
     * @return void
78
     */
79
    public function tryRestoreConf():void
80
    {
81
        $di = Di::getDefault();
82
        if ($di === null) {
83
            return;
84
        }
85
        $storage = new Storage();
86
        $storages = $storage->getStorageCandidate();
87
        $tmpMountDir = '/tmp/mnt';
88
        $backupDir   = str_replace(['/storage/usbdisk1','/mountpoint'],['',''],$di->getConfig()->path('core.confBackupDir'));
89
        $confFile    = $di->getConfig()->path('database.dbfile');
90
        foreach ($storages as $dev => $fs){
91
            Util::teletypeEcho("    - mount $dev ..."."\n");
92
            Util::mwMkdir($tmpMountDir."/$dev");
93
            $res = Storage::mountDisk($dev, $fs, $tmpMountDir."/$dev");
94
            if(!$res){
95
                Util::teletypeEcho("    - fail mount $dev ..."."\n");
96
            }
97
        }
98
        $pathBusybox = Util::which('busybox');
99
        $pathFind    = Util::which('find');
100
        $pathMount   = Util::which('umount');
101
        $pathRm    = Util::which('rm');
102
        $pathGzip    = Util::which('gzip');
103
        $pathSqlite3    = Util::which('sqlite3');
104
        $lastBackUp  = trim(shell_exec("$pathFind $tmpMountDir/dev/*$backupDir -type f -printf '%T@ %p\\n' | $pathBusybox sort -n | $pathBusybox tail -1 | $pathBusybox cut -f2- -d' '"));
105
        if(empty($lastBackUp)){
106
            return;
107
        }
108
        Util::teletypeEcho("    - Restore $lastBackUp ..."."\n");
109
        shell_exec("$pathRm -rf {$confFile}*");
110
        shell_exec("$pathGzip -c -d $lastBackUp | sqlite3 $confFile");
111
        Processes::mwExec("$pathSqlite3 $confFile 'select * from m_Storage'", $out, $ret);
112
        if($ret !== 0){
113
            Util::teletypeEcho("    - fail restore $lastBackUp ..."."\n");
114
            copy('/conf.default/mikopbx.db', $confFile);
115
        }elseif(!$this->isDefaultConf()){
116
            self::rebootSync();
117
        }
118
        foreach ($storages as $dev => $fs){
119
            shell_exec("$pathMount $dev");
120
        }
121
    }
122
123
    /**
124
     * Returns the directory where logs are stored.
125
     *
126
     * @return string - Directory path where logs are stored.
127
     */
128
    public static function getLogDir(): string
129
    {
130
        $di = Di::getDefault();
131
        if ($di !== null) {
132
            return $di->getConfig()->path('core.logsDir');
133
        }
134
135
        // Default logs directory
136
        return '/var/log';
137
    }
138
139
    /**
140
     * Refreshes networks configs and restarts network daemon.
141
     *
142
     * @return void
143
     */
144
    public static function networkReload(): void
145
    {
146
        // Create Network object and configure settings
147
        $network = new Network();
148
        $network->hostnameConfigure();
149
        $network->resolvConfGenerate();
150
        $network->loConfigure();
151
        $network->lanConfigure();
152
        $network->configureLanInDocker();
153
    }
154
    /**
155
     * Updates custom changes in config files
156
     *
157
     * @return void
158
     */
159
    public static function updateCustomFiles():void
160
    {
161
        $actions = [];
162
163
        // Find all custom files marked as changed
164
        /** @var CustomFiles $res_data */
165
        $res_data = CustomFiles::find("changed = '1'");
166
167
        // Process each changed file
168
        foreach ($res_data as $file_data) {
169
            // Always restart asterisk after any custom file change
170
            $actions['asterisk_core_reload'] = 100;
171
            $filename                        = basename($file_data->filepath);
172
173
            // Process based on file name
174
            switch ($filename) {
175
                // Set actions based on the name of the changed file
176
                case 'manager.conf':
177
                    $actions['manager'] = 10;
178
                    break;
179
                case 'musiconhold.conf':
180
                    $actions['musiconhold'] = 100;
181
                    break;
182
                case 'modules.conf':
183
                    $actions['modules'] = 10;
184
                    break;
185
                case 'http.conf':
186
                    $actions['manager'] = 10; //
187
                    break;
188
                case 'hep.conf':
189
                    $actions['hep'] = 10; //
190
                    break;
191
                case 'root': // crontabs
192
                    $actions['cron'] = 10;
193
                    break;
194
                case 'queues.conf':
195
                    $actions['queues'] = 10;
196
                    break;
197
                case 'features.conf':
198
                    $actions['features'] = 10;
199
                    break;
200
                case 'ntp.conf':
201
                    $actions['ntp'] = 100;
202
                    break;
203
                case 'ooh323.conf':
204
                    $actions['h323'] = 100;
205
                    break;
206
                case 'rtp.conf':
207
                    $actions['rtp'] = 10;
208
                    break;
209
                case 'static-routes':
210
                case 'openvpn.ovpn':
211
                    $actions['network'] = 100;
212
                    break;
213
                case 'firewall_additional':
214
                case 'jail.local':
215
                    $actions['firewall'] = 100;
216
                    break;
217
                default:
218
                    break;
219
            }
220
        }
221
222
        // Sort actions and invoke them
223
        asort($actions);
224
        self::invokeActions($actions);
225
226
        // After actions are invoked, reset the changed status and save the file data
227
        foreach ($res_data as $file_data) {
228
            /** @var CustomFiles $file_data */
229
            $file_data->writeAttribute("changed", '0');
230
            $file_data->save();
231
        }
232
    }
233
234
    /**
235
     * Restart modules or services based on the provided actions.
236
     *
237
     * @param array $actions - The actions to be performed.
238
     *
239
     * @return void
240
     */
241
    public static function invokeActions(array $actions): void
242
    {
243
244
        // Process each action
245
        foreach ($actions as $action => $value) {
246
            // Restart modules or services based on action
247
            switch ($action) {
248
                case 'manager':
249
                    PBX::managerReload();
250
                    break;
251
                case 'musiconhold':
252
                    PBX::musicOnHoldReload();
253
                    break;
254
                case 'rtp':
255
                    PBX::rtpReload();
256
                    break;
257
                case 'modules':
258
                    PBX::modulesReload();
259
                    break;
260
                case 'cron':
261
                    $cron = new CronConf();
262
                    $cron->reStart();
263
                    break;
264
                case 'queues':
265
                    QueueConf::queueReload();
266
                    break;
267
                case 'features':
268
                    PBX::managerReload(); //
269
                    break;
270
                case 'ntp':
271
                    NTPConf::configure();
272
                    break;
273
                case 'firewall':
274
                    IptablesConf::reloadFirewall();
275
                    break;
276
                case 'hep':
277
                    HepConf::reload();
278
                    break;
279
                case 'h323':
280
                    H323Conf::reload();
281
                    break;
282
                case 'network':
283
                    System::networkReload();
284
                    break;
285
                case 'asterisk_core_reload':
286
                    PBX::sipReload();
287
                    PBX::iaxReload();
288
                    PBX::dialplanReload();
289
                    PBX::coreReload();
290
                    break;
291
                default:
292
            }
293
        }
294
    }
295
296
    /**
297
     * Sets the system date and time based on timestamp and timezone.
298
     *
299
     * @param int    $timeStamp - Unix timestamp.
300
     * @param string $remote_tz - Timezone string.
301
     *
302
     * @return bool
303
     * @throws \Exception
304
     */
305
    public static function setDate(int $timeStamp, string $remote_tz): bool
306
    {
307
        $datePath = Util::which('date');
308
309
        // Fetch timezone from database
310
        $db_tz = PbxSettings::getValueByKey(PbxSettingsConstants::PBX_TIMEZONE);
311
        $origin_tz = '';
312
313
        // Read existing timezone from file if it exists
314
        if (file_exists('/etc/TZ')) {
315
            $origin_tz = file_get_contents("/etc/TZ");
316
        }
317
318
        // If the timezones are different, configure the timezone
319
        if ($origin_tz !== $db_tz){
320
            self::timezoneConfigure();
321
        }
322
323
        // Calculate the time offset and set the system time
324
        $origin_tz = $db_tz;
325
        $origin_dtz = new DateTimeZone($origin_tz);
326
        $remote_dtz = new DateTimeZone($remote_tz);
327
        $origin_dt  = new DateTime('now', $origin_dtz);
328
        $remote_dt  = new DateTime('now', $remote_dtz);
329
        $offset     = $origin_dtz->getOffset($origin_dt) - $remote_dtz->getOffset($remote_dt);
330
        $timeStamp  = $timeStamp - $offset;
331
332
        // Execute date command to set system time
333
        Processes::mwExec("{$datePath} +%s -s @{$timeStamp}");
334
335
        return true;
336
    }
337
338
    /**
339
     * Reboots the system after calling system_reboot_cleanup()
340
     *
341
     * @return void
342
     */
343
    public static function rebootSync(): void
344
    {
345
        $pbx_rebootPath = Util::which('pbx_reboot');
346
        Processes::mwExec("{$pbx_rebootPath} > /dev/null 2>&1");
347
    }
348
349
    /**
350
     * Reboots the system after calling system_reboot_cleanup()
351
     */
352
    public static function rebootSyncBg(): void
353
    {
354
        $pbx_rebootPath = Util::which('pbx_reboot');
355
        Processes::mwExecBg("{$pbx_rebootPath} > /dev/null 2>&1");
356
    }
357
358
    /**
359
     * Shutdown the system.
360
     */
361
    public static function shutdown(): void
362
    {
363
        $shutdownPath = Util::which('shutdown');
364
        Processes::mwExec("{$shutdownPath} > /dev/null 2>&1");
365
    }
366
367
    /**
368
     * Configures the system timezone according to the PBXTimezone setting.
369
     *
370
     * @return void
371
     */
372
    public static function timezoneConfigure(): void
373
    {
374
375
        // Get the timezone setting from the database
376
        $timezone = PbxSettings::getValueByKey(PbxSettingsConstants::PBX_TIMEZONE);
377
378
        // If /etc/TZ or /etc/localtime exist, delete them
379
        if (file_exists('/etc/TZ')) {
380
            unlink("/etc/TZ");
381
        }
382
        if (file_exists('/etc/localtime')) {
383
            unlink("/etc/localtime");
384
        }
385
386
        // If a timezone is set, configure it
387
        if ($timezone) {
388
389
            // The path to the zone file
390
            $zone_file = "/usr/share/zoneinfo/{$timezone}";
391
392
            // If the zone file exists, copy it to /etc/localtime
393
            if ( ! file_exists($zone_file)) {
394
                return;
395
            }
396
            $cpPath = Util::which('cp');
397
            Processes::mwExec("{$cpPath}  {$zone_file} /etc/localtime");
398
399
            // Write the timezone to /etc/TZ and set the TZ environment variable
400
            file_put_contents('/etc/TZ', $timezone);
401
            putenv("TZ={$timezone}");
402
403
            // Execute the export TZ command and configure PHP's timezone
404
            Processes::mwExec("export TZ;");
405
            PHPConf::phpTimeZoneConfigure();
406
        }
407
408
    }
409
410
    /**
411
     * Calculate the hash of SSL certificates and extract them from ca-certificates.crt.
412
     *
413
     * @return void
414
     */
415
    public static function sslRehash(): void
416
    {
417
        // Paths to system commands
418
        $openSslPath = Util::which('openssl');
419
        $cutPath     = Util::which('cut');
420
421
        // Get OpenSSL directory and cert file
422
        $openSslDir  = trim(shell_exec("$openSslPath version -d | $cutPath -d '\"' -f 2"));
423
        $certFile    = "$openSslDir/certs/ca-certificates.crt";
424
        $tmpFile     = tempnam('/tmp', 'cert-');
425
        $rawData     = file_get_contents($certFile);
426
        $certs       = explode(PHP_EOL.PHP_EOL, $rawData);
427
        foreach ($certs as $cert){
428
            if(strpos($cert, '-----BEGIN CERTIFICATE-----') === false){
429
                continue;
430
            }
431
            file_put_contents($tmpFile, $cert);
432
            $hash = trim(shell_exec("$openSslPath x509 -subject_hash -noout -in '$tmpFile'"));
433
            rename($tmpFile, "$openSslDir/certs/$hash.0");
434
        }
435
        if(file_exists($tmpFile)){
436
            unlink($tmpFile);
437
        }
438
    }
439
}
440