Passed
Push — develop ( f5dd8a...1cf847 )
by Портнов
04:27
created

WorkerModelsEvents::reloadPHPFPM()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 1
dl 0
loc 3
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 (C) 2017-2020 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\Workers;
21
22
require_once 'Globals.php';
23
24
use MikoPBX\Common\Models\{AsteriskManagerUsers,
25
    CallQueueMembers,
26
    CallQueues,
27
    Codecs,
28
    ConferenceRooms,
29
    CustomFiles,
30
    DialplanApplications,
31
    ExtensionForwardingRights,
32
    Extensions,
33
    ExternalPhones,
34
    Fail2BanRules,
35
    FirewallRules,
36
    Iax,
37
    IncomingRoutingTable,
38
    IvrMenu,
39
    IvrMenuActions,
40
    LanInterfaces,
41
    NetworkFilters,
42
    OutgoingRoutingTable,
43
    OutWorkTimes,
44
    PbxExtensionModules,
45
    PbxSettings,
46
    Sip,
47
    SoundFiles
48
};
49
use MikoPBX\Common\Providers\BeanstalkConnectionModelsProvider;
50
use MikoPBX\Core\Asterisk\Configs\QueueConf;
51
use MikoPBX\Core\System\{BeanstalkClient,
52
    Configs\CronConf,
53
    Configs\IptablesConf,
54
    Configs\NatsConf,
55
    Configs\NginxConf,
56
    Configs\NTPConf,
57
    Configs\PHPConf,
58
    Configs\SSHConf,
59
    Configs\SyslogConf,
60
    PBX,
61
    Processes,
62
    System,
63
    Util};
64
use MikoPBX\PBXCoreREST\Workers\WorkerApiCommands;
65
use Phalcon\Di;
66
use Pheanstalk\Contract\PheanstalkInterface;
67
use Throwable;
68
69
ini_set('error_reporting', E_ALL);
70
ini_set('display_startup_errors', 1);
71
72
class WorkerModelsEvents extends WorkerBase
73
{
74
    private const R_MANAGERS = 'reloadManager';
75
76
    private const R_QUEUES = 'reloadQueues';
77
78
    private const R_DIALPLAN = 'reloadDialplan';
79
80
    private const R_CUSTOM_F = 'updateCustomFiles';
81
82
    private const R_FIREWALL = 'reloadFirewall';
83
84
    private const R_NETWORK = 'networkReload';
85
86
    private const R_IAX = 'reloadIax';
87
88
    private const R_SIP = 'reloadSip';
89
90
    private const R_FEATURES = 'reloadFeatures';
91
92
    private const R_CRON = 'reloadCron';
93
94
    public const  R_NGINX = 'reloadNginx';
95
96
    public const  R_NGINX_CONF = 'reloadNginxConf';
97
98
    private const R_PHP_FPM = 'reloadPHPFPM';
99
100
    private const R_TIMEZONE = 'updateTomeZone';
101
102
    private const R_SYSLOG   = 'restartSyslogD';
103
104
    private const R_SSH = 'reloadSSH';
105
106
    private const R_LICENSE = 'reloadLicense';
107
108
    private const R_NATS = 'reloadNats';
109
110
    private const R_VOICEMAIL = 'reloadVoicemail';
111
112
    private const R_REST_API_WORKER = 'reloadRestAPIWorker';
113
114
    private const R_CALL_EVENTS_WORKER = 'reloadWorkerCallEvents';
115
116
    private const R_PBX_EXTENSION_STATE = 'afterModuleStateChanged';
117
118
    private const R_MOH = 'reloadMoh';
119
120
    private const R_NTP = 'reloadNtp';
121
122
    private int $last_change;
123
    private array $modified_tables;
124
125
    private int $timeout = 2;
126
    private array $arrObject;
127
    private array $PRIORITY_R;
128
129
130
    /**
131
     * Entry point
132
     *
133
     * @param $argv
134
     */
135
    public function start($argv): void
136
    {
137
        $this->arrObject = $this->di->getShared('pbxConfModules');
138
139
        $this->PRIORITY_R = [
140
            self::R_TIMEZONE,
141
            self::R_SYSLOG,
142
            self::R_REST_API_WORKER,
143
            self::R_NETWORK,
144
            self::R_FIREWALL,
145
            self::R_SSH,
146
            self::R_LICENSE,
147
            self::R_NATS,
148
            self::R_NTP,
149
            self::R_PHP_FPM,
150
            self::R_NGINX,
151
            self::R_NGINX_CONF,
152
            self::R_CRON,
153
            self::R_FEATURES,
154
            self::R_SIP,
155
            self::R_IAX,
156
            self::R_DIALPLAN,
157
            self::R_QUEUES,
158
            self::R_MANAGERS,
159
            self::R_CUSTOM_F,
160
            self::R_VOICEMAIL,
161
            self::R_MOH,
162
            self::R_CALL_EVENTS_WORKER,
163
            self::R_PBX_EXTENSION_STATE,
164
        ];
165
166
        $this->modified_tables = [];
167
168
        /** @var BeanstalkClient $client */
169
        $client = $this->di->getShared(BeanstalkConnectionModelsProvider::SERVICE_NAME);
170
        $client->subscribe(self::class, [$this, 'processModelChanges']);
171
        $client->subscribe($this->makePingTubeName(self::class), [$this, 'pingCallBack']);
172
        $client->setTimeoutHandler([$this, 'timeoutHandler']);
173
174
        while ($this->needRestart === false) {
175
            $client->wait();
176
        }
177
        // Execute all collected changes before exit
178
        $this->timeoutHandler();
179
    }
180
181
    /**
182
     * Parses for received Beanstalk message
183
     *
184
     * @param BeanstalkClient $message
185
     * @throws \JsonException
186
     */
187
    public function processModelChanges(BeanstalkClient $message): void
188
    {
189
        $data = $message->getBody();
190
        $receivedMessage = null;
191
        if(in_array($data, $this->PRIORITY_R, true)){
192
            $this->modified_tables[$data] = true;
193
        }else{
194
            $receivedMessage = json_decode($message->getBody(), true, 512, JSON_THROW_ON_ERROR);
195
            $this->fillModifiedTables($receivedMessage);
196
197
        }
198
        $this->startReload();
199
        if(!$receivedMessage){
200
            return;
201
        }
202
        // Send information about models changes to additional modules
203
        foreach ($this->arrObject as $appClass) {
204
            $appClass->modelsEventChangeData($receivedMessage);
205
        }
206
    }
207
208
    /**
209
     * Collects changes to determine which modules must be reloaded or reconfigured
210
     *
211
     * @param array $data
212
     */
213
    private function fillModifiedTables(array $data): void
214
    {
215
216
        $count_changes = count($this->modified_tables);
217
        $called_class  = $data['model'] ?? '';
218
        Util::sysLogMsg(__METHOD__, "New changes ".$called_class, LOG_DEBUG);
219
220
        // Clear all caches on any changed models on backend
221
        PbxSettings::clearCache($called_class, false);
222
223
        // Get new settings gor dependence modules
224
        foreach ($this->arrObject as $appClass) {
225
            $dependencies = $appClass->dependenceModels();
226
            if (in_array($called_class, $dependencies, true)) {
227
                $appClass->getSettings();
228
            }
229
        }
230
231
        switch ($called_class) {
232
            case AsteriskManagerUsers::class:
233
                $this->modified_tables[self::R_MANAGERS] = true;
234
                break;
235
            case CallQueueMembers::class:
236
                $this->modified_tables[self::R_QUEUES] = true;
237
                break;
238
            case CallQueues::class:
239
                $this->modified_tables[self::R_QUEUES]   = true;
240
                $this->modified_tables[self::R_DIALPLAN] = true;
241
                break;
242
            case ExternalPhones::class:
243
            case Extensions::class:
244
            case DialplanApplications::class:
245
            case IncomingRoutingTable::class:
246
            case IvrMenu::class:
247
            case IvrMenuActions::class:
248
            case OutgoingRoutingTable::class:
249
            case OutWorkTimes::class:
250
            case ConferenceRooms::class:
251
                $this->modified_tables[self::R_DIALPLAN] = true;
252
                break;
253
            case CustomFiles::class:
254
                $this->modified_tables[self::R_CUSTOM_F] = true;
255
                break;
256
            case Sip::class:
257
            case ExtensionForwardingRights::class:
258
                $this->modified_tables[self::R_SIP]      = true;
259
                $this->modified_tables[self::R_DIALPLAN] = true;
260
                break;
261
            case FirewallRules::class:
262
            case Fail2BanRules::class:
263
                $this->modified_tables[self::R_FIREWALL] = true;
264
                break;
265
            case Iax::class:
266
                $this->modified_tables[self::R_IAX]      = true;
267
                $this->modified_tables[self::R_DIALPLAN] = true;
268
                break;
269
            case Codecs::class:
270
                $this->modified_tables[self::R_IAX] = true;
271
                $this->modified_tables[self::R_SIP] = true;
272
                break;
273
            case SoundFiles::class:
274
                $this->modified_tables[self::R_MOH]      = true;
275
                $this->modified_tables[self::R_DIALPLAN] = true;
276
                break;
277
            case LanInterfaces::class:
278
                $this->modified_tables[self::R_NETWORK] = true;
279
                $this->modified_tables[self::R_IAX]     = true;
280
                $this->modified_tables[self::R_SIP]     = true;
281
                break;
282
            case NetworkFilters::class:
283
                $this->modified_tables[self::R_FIREWALL] = true;
284
                $this->modified_tables[self::R_SIP]      = true;
285
                $this->modified_tables[self::R_MANAGERS] = true;
286
                break;
287
            case PbxSettings::class:
288
                $pbxSettings = PbxSettings::findFirstByKey($data['recordId']);
289
                if ($pbxSettings === null) {
290
                    return;
291
                }
292
                if ($pbxSettings->itHasFeaturesSettingsChanges()) {
293
                    $this->modified_tables[self::R_FEATURES] = true;
294
                    $this->modified_tables[self::R_DIALPLAN] = true;
295
                }
296
                if ($pbxSettings->itHasAMIParametersChanges()) {
297
                    $this->modified_tables[self::R_MANAGERS] = true;
298
                }
299
                if ($pbxSettings->itHasIaxParametersChanges()) {
300
                    $this->modified_tables[self::R_IAX] = true;
301
                }
302
                if ($pbxSettings->itHasSipParametersChanges()) {
303
                    $this->modified_tables[self::R_SIP] = true;
304
                }
305
                if ($pbxSettings->itHasSSHParametersChanges()) {
306
                    $this->modified_tables[self::R_SSH] = true;
307
                }
308
                if ($pbxSettings->itHasFirewallParametersChanges()) {
309
                    $this->modified_tables[self::R_FIREWALL] = true;
310
                }
311
                if ($pbxSettings->itHasWebParametersChanges()) {
312
                    $this->modified_tables[self::R_NGINX] = true;
313
                }
314
                if ($pbxSettings->itHasCronParametersChanges()) {
315
                    $this->modified_tables[self::R_CRON] = true;
316
                }
317
                if ($pbxSettings->itHasDialplanParametersChanges()) {
318
                    $this->modified_tables[self::R_DIALPLAN] = true;
319
                }
320
                if ($pbxSettings->itHasVoiceMailParametersChanges()) {
321
                    $this->modified_tables[self::R_VOICEMAIL] = true;
322
                }
323
                if ($pbxSettings->itHasVisualLanguageSettings()) {
324
                    $this->modified_tables[self::R_REST_API_WORKER] = true;
325
                }
326
                if ($pbxSettings->itHasLicenseSettings()) {
327
                    $this->modified_tables[self::R_LICENSE] = true;
328
                    $this->modified_tables[self::R_NATS]    = true;
329
                }
330
                if ($pbxSettings->itHasTimeZoneSettings()) {
331
                    $this->modified_tables[self::R_TIMEZONE]        = true;
332
                    $this->modified_tables[self::R_NGINX]           = true;
333
                    $this->modified_tables[self::R_PHP_FPM]         = true;
334
                    $this->modified_tables[self::R_REST_API_WORKER] = true;
335
                    $this->modified_tables[self::R_SYSLOG]        = true;
336
                }
337
                if ($pbxSettings->itHasNTPSettings()) {
338
                    $this->modified_tables[self::R_NTP] = true;
339
                }
340
                if ($pbxSettings->itHasCallRecordSettings()) {
341
                    $this->modified_tables[self::R_CALL_EVENTS_WORKER] = true;
342
                    $this->modified_tables[self::R_DIALPLAN]           = true;
343
                }
344
                break;
345
            case PbxExtensionModules::class:
346
                $moduleSettings                                                   = PbxExtensionModules::findFirstById(
347
                    $data['recordId']
348
                );
349
                $this->modified_tables[self::R_PBX_EXTENSION_STATE]               = true;
350
                $this->modified_tables['parameters'][self::R_PBX_EXTENSION_STATE] = $moduleSettings;
351
                $this->modified_tables[self::R_CRON]                              = true;
352
                break;
353
            default:
354
        }
355
356
        if ($count_changes === 0 && count($this->modified_tables) > 0) {
357
            // Начинаем отсчет времени при получении первой задачи.
358
            $this->last_change = time();
359
        }
360
    }
361
362
    /**
363
     * Apply changes
364
     *
365
     * @return void
366
     */
367
    private function startReload(): void
368
    {
369
        if (count($this->modified_tables) === 0) {
370
            return;
371
        }
372
        $delta = time() - $this->last_change;
373
        if ($delta < $this->timeout) {
374
            return;
375
        }
376
377
        foreach ($this->PRIORITY_R as $method_name) {
378
            $action     = $this->modified_tables[$method_name] ?? null;
379
            $parameters = $this->modified_tables['parameters'][$method_name] ?? null;
380
            if ($action === null) {
381
                continue;
382
            }
383
            if (method_exists($this, $method_name)) {
384
                Util::sysLogMsg(__METHOD__, "Process changes by {$method_name}", LOG_DEBUG);
385
                if ($parameters === null) {
386
                    $this->$method_name();
387
                } else {
388
                    $this->$method_name($parameters);
389
                }
390
            }
391
        }
392
393
        foreach ($this->arrObject as $appClass) {
394
            $appClass->modelsEventNeedReload($this->modified_tables);
395
        }
396
        $this->modified_tables = [];
397
    }
398
399
400
    /**
401
     * Restarts gnats queue server daemon
402
     */
403
    public function reloadNats(): void
404
    {
405
        $natsConf = new NatsConf();
406
        $natsConf->reStart();
407
    }
408
409
410
    /**
411
     * Reloads Asterisk dialplan
412
     */
413
    public function reloadDialplan(): void
414
    {
415
        PBX::dialplanReload();
416
    }
417
418
    /**
419
     * Reloads Asterisk manager interface module
420
     */
421
    public function reloadManager(): void
422
    {
423
        PBX::managerReload();
424
    }
425
426
    /**
427
     * Generates queue.conf and restart asterisk queue module
428
     */
429
    public function reloadQueues(): void
430
    {
431
        QueueConf::queueReload();
432
    }
433
434
    /**
435
     * Updates custom changes in config files
436
     */
437
    public function updateCustomFiles(): void
438
    {
439
        System::updateCustomFiles();
440
    }
441
442
    /**
443
     * Apply iptable settings and restart firewall
444
     */
445
    public function reloadFirewall(): void
446
    {
447
        IptablesConf::reloadFirewall();
448
    }
449
450
    /**
451
     *  Refresh networks configs and restarts network daemon
452
     */
453
    public function networkReload(): void
454
    {
455
        System::networkReload();
456
    }
457
458
    /**
459
     * Refresh IAX configs and reload iax2 module
460
     */
461
    public function reloadIax(): void
462
    {
463
        PBX::iaxReload();
464
    }
465
466
    /**
467
     * Reload MOH file list in Asterisk.
468
     */
469
    public function reloadMoh(): void
470
    {
471
        PBX::mohReload();
472
    }
473
474
    /**
475
     * Refresh SIP configs and reload PJSIP module
476
     */
477
    public function reloadSip(): void
478
    {
479
        PBX::sipReload();
480
    }
481
482
    /**
483
     *  Refresh features configs and reload features module
484
     */
485
    public function reloadFeatures(): void
486
    {
487
        PBX::featuresReload();
488
    }
489
490
    /**
491
     * Restarts CROND daemon
492
     */
493
    public function reloadCron(): void
494
    {
495
        $cron = new CronConf();
496
        $cron->reStart();
497
    }
498
499
    /**
500
     * Restarts NTP daemon
501
     */
502
    public function reloadNtp(): void
503
    {
504
        NTPConf::configure();
505
    }
506
507
    /**
508
     * Restarts Nginx daemon
509
     */
510
    public function reloadNginx(): void
511
    {
512
        $nginxConf = new NginxConf();
513
        $nginxConf->generateConf();
514
        $nginxConf->reStart();
515
    }
516
517
    /**
518
     * Restarts Nginx daemon
519
     */
520
    public function reloadNginxConf(): void
521
    {
522
        $nginxConf = new NginxConf();
523
        $nginxConf->reStart();
524
    }
525
526
    /**
527
     * Restarts PHP-FPM daemon
528
     */
529
    public function reloadPHPFPM(): void
530
    {
531
        PHPConf::reStart();
532
    }
533
534
    /**
535
     * Configure SSH settings
536
     */
537
    public function reloadSSH(): void
538
    {
539
        $sshConf = new SSHConf();
540
        $sshConf->configure();
541
    }
542
543
    /**
544
     * Reconfigure TomeZone settings
545
     */
546
    public function updateTomeZone(): void
547
    {
548
        System::timezoneConfigure();
549
    }
550
551
    /**
552
     * Перезапуск rsyslog.
553
     */
554
    public function restartSyslogD(): void
555
    {
556
        // Рестарт демона Syslog.
557
        $syslogConf = new SyslogConf();
558
        $syslogConf->reStart();
559
    }
560
561
    /**
562
     *  Reloads Asterisk voicemail module
563
     */
564
    public function reloadVoicemail(): void
565
    {
566
        PBX::voicemailReload();
567
    }
568
569
    /**
570
     *  Reloads WorkerApiCommands worker
571
     */
572
    public function reloadRestAPIWorker(): void
573
    {
574
        Processes::processPHPWorker(WorkerApiCommands::class);
575
    }
576
577
578
    /**
579
     *  Reloads WorkerCallEvents worker
580
     */
581
    public function reloadWorkerCallEvents(): void
582
    {
583
        Processes::processPHPWorker(WorkerCallEvents::class);
584
    }
585
586
587
    /**
588
     * Timeout handles
589
     */
590
    public function timeoutHandler(): void
591
    {
592
        $this->last_change = time() - $this->timeout;
593
        $this->startReload();
594
    }
595
596
597
    /**
598
     *  Process after PBXExtension state changes
599
     *
600
     * @param \MikoPBX\Common\Models\PbxExtensionModules $record
601
     */
602
    public function afterModuleStateChanged(PbxExtensionModules $record): void
603
    {
604
        $className       = str_replace('Module', '', $record->uniqid);
605
        $configClassName = "\\Modules\\{$record->uniqid}\\Lib\\{$className}Conf";
606
        if (class_exists($configClassName)) {
607
            $configClassName = new $configClassName();
608
            if ($record->disabled === '1' && method_exists($configClassName, 'onAfterModuleDisable')) {
609
                $configClassName->onAfterModuleDisable();
610
            } elseif ($record->disabled === '0' && method_exists($configClassName, 'onAfterModuleEnable')) {
611
                $configClassName->onAfterModuleEnable();
612
            }
613
        }
614
    }
615
616
    public static function invokeAction(string $action):void{
617
        $di = Di::getDefault();
618
        $queue = $di->getShared(BeanstalkConnectionModelsProvider::SERVICE_NAME);
619
        $queue->publish(
620
            $action,
621
            self::class,
622
            0,
623
            PheanstalkInterface::DEFAULT_DELAY,
624
            3600
625
        );
626
    }
627
}
628
629
/**
630
 * Start point
631
 */
632
$workerClassname = WorkerModelsEvents::class;
633
if (isset($argv) && count($argv) > 1 && $argv[1] === 'start') {
634
    cli_set_process_title($workerClassname);
635
    try {
636
        $worker = new $workerClassname();
637
        $worker->start($argv);
638
    } catch (Throwable $e) {
639
        global $errorLogger;
640
        $errorLogger->captureException($e);
641
        sleep(1);
642
    }
643
}