|
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 MikoPBX\Common\Models\Codecs; |
|
23
|
|
|
use MikoPBX\Common\Models\PbxSettings; |
|
24
|
|
|
use MikoPBX\Common\Models\PbxSettingsConstants; |
|
25
|
|
|
use MikoPBX\Common\Providers\CDRDatabaseProvider; |
|
26
|
|
|
use MikoPBX\Common\Providers\PBXConfModulesProvider; |
|
27
|
|
|
use MikoPBX\Common\Providers\RegistryProvider; |
|
28
|
|
|
use MikoPBX\Core\Asterisk\CdrDb; |
|
29
|
|
|
use MikoPBX\Core\Asterisk\Configs\{AclConf, |
|
30
|
|
|
AsteriskConf, |
|
31
|
|
|
AsteriskConfigClass, |
|
32
|
|
|
AsteriskConfigInterface, |
|
33
|
|
|
ConferenceConf, |
|
34
|
|
|
ExtensionsConf, |
|
35
|
|
|
FeaturesConf, |
|
36
|
|
|
HttpConf, |
|
37
|
|
|
IAXConf, |
|
38
|
|
|
ManagerConf, |
|
39
|
|
|
ModulesConf, |
|
40
|
|
|
MusicOnHoldConf, |
|
41
|
|
|
RtpConf, |
|
42
|
|
|
SIPConf, |
|
43
|
|
|
VoiceMailConf}; |
|
44
|
|
|
use MikoPBX\Core\Workers\WorkerCallEvents; |
|
45
|
|
|
use MikoPBX\Modules\Config\SystemConfigInterface; |
|
46
|
|
|
use Phalcon\Di; |
|
47
|
|
|
use Phalcon\Di\Injectable; |
|
48
|
|
|
|
|
49
|
|
|
/** |
|
50
|
|
|
* Class PBX |
|
51
|
|
|
* |
|
52
|
|
|
* @package MikoPBX\Core\System |
|
53
|
|
|
*/ |
|
54
|
|
|
class PBX extends Injectable |
|
55
|
|
|
{ |
|
56
|
|
|
/** |
|
57
|
|
|
* Restarts the Asterisk process. |
|
58
|
|
|
*/ |
|
59
|
|
|
public static function restart(): void |
|
60
|
|
|
{ |
|
61
|
|
|
$pbx = new PBX(); |
|
62
|
|
|
$pbx->stop(); |
|
63
|
|
|
$pbx->start(); |
|
64
|
|
|
} |
|
65
|
|
|
|
|
66
|
|
|
/** |
|
67
|
|
|
* Stops the Asterisk process. |
|
68
|
|
|
*/ |
|
69
|
|
|
public function stop(): void |
|
70
|
|
|
{ |
|
71
|
|
|
Processes::killByName('safe_asterisk'); |
|
72
|
|
|
sleep(1); |
|
73
|
|
|
$asteriskPath = Util::which('asterisk'); |
|
74
|
|
|
Processes::mwExec("{$asteriskPath} -rx 'core stop now'"); |
|
75
|
|
|
Processes::processWorker('', '', WorkerCallEvents::class, 'stop'); |
|
76
|
|
|
Processes::killByName('asterisk'); |
|
77
|
|
|
} |
|
78
|
|
|
|
|
79
|
|
|
/** |
|
80
|
|
|
* Starts the Asterisk process. |
|
81
|
|
|
*/ |
|
82
|
|
|
public function start(): void |
|
83
|
|
|
{ |
|
84
|
|
|
Network::startSipDump(); |
|
85
|
|
|
$safe_asteriskPath = Util::which('safe_asterisk'); |
|
86
|
|
|
// The "-n" option disables color highlighting in Asterisk CLI. |
|
87
|
|
|
Processes::mwExecBg("{$safe_asteriskPath} -f"); |
|
88
|
|
|
// Send notifications to modules |
|
89
|
|
|
PBXConfModulesProvider::hookModulesMethod(SystemConfigInterface::ON_AFTER_PBX_STARTED); |
|
90
|
|
|
} |
|
91
|
|
|
|
|
92
|
|
|
/** |
|
93
|
|
|
* Rotates the PBX log files. |
|
94
|
|
|
*/ |
|
95
|
|
|
public static function logRotate(): void |
|
96
|
|
|
{ |
|
97
|
|
|
self::rotatePbxLog('messages'); |
|
98
|
|
|
self::rotatePbxLog('security_log'); |
|
99
|
|
|
self::rotatePbxLog('error'); |
|
100
|
|
|
self::rotatePbxLog('verbose'); |
|
101
|
|
|
} |
|
102
|
|
|
|
|
103
|
|
|
/** |
|
104
|
|
|
* Rotates the specified PBX log file. |
|
105
|
|
|
* @param string $fileName The name of the log file to rotate. |
|
106
|
|
|
*/ |
|
107
|
|
|
public static function rotatePbxLog($fileName): void |
|
108
|
|
|
{ |
|
109
|
|
|
$di = Di::getDefault(); |
|
110
|
|
|
$asteriskPath = Util::which('asterisk'); |
|
111
|
|
|
if ($di === null) { |
|
112
|
|
|
return; |
|
113
|
|
|
} |
|
114
|
|
|
$max_size = 10; |
|
115
|
|
|
$log_dir = System::getLogDir() . '/asterisk/'; |
|
|
|
|
|
|
116
|
|
|
$text_config = "{$log_dir}{$fileName} { |
|
117
|
|
|
nocreate |
|
118
|
|
|
nocopytruncate |
|
119
|
|
|
delaycompress |
|
120
|
|
|
nomissingok |
|
121
|
|
|
start 0 |
|
122
|
|
|
rotate 9 |
|
123
|
|
|
size {$max_size}M |
|
124
|
|
|
missingok |
|
125
|
|
|
noolddir |
|
126
|
|
|
postrotate |
|
127
|
|
|
{$asteriskPath} -rx 'logger reload' > /dev/null 2> /dev/null |
|
128
|
|
|
endscript |
|
129
|
|
|
}"; |
|
130
|
|
|
$varEtcDir = $di->getShared('config')->path('core.varEtcDir'); |
|
131
|
|
|
$path_conf = $varEtcDir . '/asterisk_logrotate_' . $fileName . '.conf'; |
|
132
|
|
|
file_put_contents($path_conf, $text_config); |
|
133
|
|
|
$mb10 = $max_size * 1024 * 1024; |
|
134
|
|
|
|
|
135
|
|
|
$options = ''; |
|
136
|
|
|
if (Util::mFileSize("{$log_dir}{$fileName}") > $mb10) { |
|
137
|
|
|
$options = '-f'; |
|
138
|
|
|
} |
|
139
|
|
|
$logrotatePath = Util::which('logrotate'); |
|
140
|
|
|
Processes::mwExecBg("{$logrotatePath} {$options} '{$path_conf}' > /dev/null 2> /dev/null"); |
|
141
|
|
|
} |
|
142
|
|
|
|
|
143
|
|
|
/** |
|
144
|
|
|
* Refreshes the features configs and reloads the features module. |
|
145
|
|
|
*/ |
|
146
|
|
|
public static function featuresReload(): void |
|
147
|
|
|
{ |
|
148
|
|
|
$featuresConf = new FeaturesConf(); |
|
149
|
|
|
$featuresConf->generateConfig(); |
|
150
|
|
|
$arr_out = []; |
|
151
|
|
|
$asteriskPath = Util::which('asterisk'); |
|
152
|
|
|
Processes::mwExec("{$asteriskPath} -rx 'module reload features'", $arr_out); |
|
153
|
|
|
} |
|
154
|
|
|
|
|
155
|
|
|
/** |
|
156
|
|
|
* Reloads the Asterisk core. |
|
157
|
|
|
*/ |
|
158
|
|
|
public static function coreReload(): void |
|
159
|
|
|
{ |
|
160
|
|
|
$featuresConf = new FeaturesConf(); |
|
161
|
|
|
$featuresConf->generateConfig(); |
|
162
|
|
|
|
|
163
|
|
|
$asteriskConf = new AsteriskConf(); |
|
164
|
|
|
$asteriskConf->generateConfig(); |
|
165
|
|
|
|
|
166
|
|
|
$arr_out = []; |
|
167
|
|
|
$asteriskPath = Util::which('asterisk'); |
|
168
|
|
|
Processes::mwExec("{$asteriskPath} -rx 'core reload'", $arr_out); |
|
169
|
|
|
} |
|
170
|
|
|
|
|
171
|
|
|
/** |
|
172
|
|
|
* Restarts the Asterisk core. |
|
173
|
|
|
*/ |
|
174
|
|
|
public static function coreRestart(): void |
|
175
|
|
|
{ |
|
176
|
|
|
$asteriskConf = new AsteriskConf(); |
|
177
|
|
|
$asteriskConf->generateConfig(); |
|
178
|
|
|
$asteriskPath = Util::which('asterisk'); |
|
179
|
|
|
Processes::mwExec("{$asteriskPath} -rx 'core restart now'"); |
|
180
|
|
|
} |
|
181
|
|
|
|
|
182
|
|
|
/** |
|
183
|
|
|
* Reloads the Asterisk manager interface module. |
|
184
|
|
|
*/ |
|
185
|
|
|
public static function managerReload(): void |
|
186
|
|
|
{ |
|
187
|
|
|
$managerCong = new ManagerConf(); |
|
188
|
|
|
$managerCong->generateConfig(); |
|
189
|
|
|
|
|
190
|
|
|
$httpConf = new HttpConf(); |
|
191
|
|
|
$httpConf->generateConfig(); |
|
192
|
|
|
|
|
193
|
|
|
$arr_out = []; |
|
194
|
|
|
$asteriskPath = Util::which('asterisk'); |
|
195
|
|
|
Processes::mwExec("{$asteriskPath} -rx 'module reload manager'", $arr_out); |
|
196
|
|
|
Processes::mwExec("{$asteriskPath} -rx 'module reload http'", $arr_out); |
|
197
|
|
|
} |
|
198
|
|
|
|
|
199
|
|
|
/** |
|
200
|
|
|
* Reloads the Asterisk music on hold module. |
|
201
|
|
|
*/ |
|
202
|
|
|
public static function musicOnHoldReload(): void |
|
203
|
|
|
{ |
|
204
|
|
|
$o = new MusicOnHoldConf(); |
|
205
|
|
|
$o->generateConfig(); |
|
206
|
|
|
$asteriskPath = Util::which('asterisk'); |
|
207
|
|
|
Processes::mwExec("{$asteriskPath} -rx 'moh reload'"); |
|
208
|
|
|
} |
|
209
|
|
|
|
|
210
|
|
|
/** |
|
211
|
|
|
* Reloads the Asterisk music on hold module. |
|
212
|
|
|
*/ |
|
213
|
|
|
public static function confBridgeReload(): void |
|
214
|
|
|
{ |
|
215
|
|
|
$o = new ConferenceConf(); |
|
216
|
|
|
$o->generateConfig(); |
|
217
|
|
|
$asteriskPath = Util::which('asterisk'); |
|
218
|
|
|
Processes::mwExec("$asteriskPath -rx 'module reload app_confbridge'"); |
|
219
|
|
|
} |
|
220
|
|
|
|
|
221
|
|
|
|
|
222
|
|
|
/** |
|
223
|
|
|
* Reloads the Asterisk voicemail module. |
|
224
|
|
|
*/ |
|
225
|
|
|
public static function voicemailReload(): void |
|
226
|
|
|
{ |
|
227
|
|
|
$o = new VoiceMailConf(); |
|
228
|
|
|
$o->generateConfig(); |
|
229
|
|
|
$arr_out = []; |
|
230
|
|
|
$asteriskPath = Util::which('asterisk'); |
|
231
|
|
|
Processes::mwExec("{$asteriskPath} -rx 'voicemail reload'", $arr_out); |
|
232
|
|
|
} |
|
233
|
|
|
|
|
234
|
|
|
/** |
|
235
|
|
|
* Reloads the Asterisk modules. |
|
236
|
|
|
* @return array |
|
237
|
|
|
*/ |
|
238
|
|
|
public static function modulesReload(): array |
|
239
|
|
|
{ |
|
240
|
|
|
$pbx = new ModulesConf(); |
|
241
|
|
|
$pbx->generateConfig(); |
|
242
|
|
|
$arr_out = []; |
|
243
|
|
|
$asteriskPath = Util::which('asterisk'); |
|
244
|
|
|
Processes::mwExec("{$asteriskPath} -rx 'core restart now'", $arr_out); |
|
245
|
|
|
|
|
246
|
|
|
return [ |
|
247
|
|
|
'result' => 'Success', |
|
248
|
|
|
'data' => '', |
|
249
|
|
|
]; |
|
250
|
|
|
} |
|
251
|
|
|
|
|
252
|
|
|
|
|
253
|
|
|
/** |
|
254
|
|
|
* Checks if a codec exists and creates it if not. |
|
255
|
|
|
* @param string $name The name of the codec. |
|
256
|
|
|
* @param string $desc The description of the codec. |
|
257
|
|
|
* @param string $type The type of the codec. |
|
258
|
|
|
*/ |
|
259
|
|
|
public static function checkCodec($name, $desc, $type): void |
|
260
|
|
|
{ |
|
261
|
|
|
$codec = Codecs::findFirst('name="' . $name . '"'); |
|
262
|
|
|
if ($codec === null) { |
|
263
|
|
|
/** @var \MikoPBX\Common\Models\Codecs $codec */ |
|
264
|
|
|
$codec = new Codecs(); |
|
265
|
|
|
$codec->name = $name; |
|
266
|
|
|
$codec->type = $type; |
|
267
|
|
|
$codec->description = $desc; |
|
268
|
|
|
$codec->save(); |
|
269
|
|
|
} |
|
270
|
|
|
} |
|
271
|
|
|
|
|
272
|
|
|
/** |
|
273
|
|
|
* Refreshes the SIP configurations and reloads the PJSIP module. |
|
274
|
|
|
*/ |
|
275
|
|
|
public static function sipReload():void |
|
276
|
|
|
{ |
|
277
|
|
|
$di = Di::getDefault(); |
|
278
|
|
|
if ($di === null) { |
|
279
|
|
|
return; |
|
280
|
|
|
} |
|
281
|
|
|
$sip = new SIPConf(); |
|
282
|
|
|
$needRestart = $sip->needAsteriskRestart(); |
|
283
|
|
|
$sip->generateConfig(); |
|
284
|
|
|
|
|
285
|
|
|
$acl = new AclConf(); |
|
286
|
|
|
$acl->generateConfig(); |
|
287
|
|
|
|
|
288
|
|
|
$asteriskPath = Util::which('asterisk'); |
|
289
|
|
|
if ($needRestart === false) { |
|
290
|
|
|
Processes::mwExec("{$asteriskPath} -rx 'module reload acl'"); |
|
291
|
|
|
Processes::mwExec("{$asteriskPath} -rx 'core reload'"); |
|
292
|
|
|
} else { |
|
293
|
|
|
SystemMessages::sysLogMsg('SIP RELOAD', 'Need reload asterisk',LOG_INFO); |
|
294
|
|
|
// Terminate channels. |
|
295
|
|
|
Processes::mwExec("{$asteriskPath} -rx 'channel request hangup all'"); |
|
296
|
|
|
usleep(500000); |
|
297
|
|
|
Processes::mwExec("{$asteriskPath} -rx 'core restart now'"); |
|
298
|
|
|
} |
|
299
|
|
|
} |
|
300
|
|
|
|
|
301
|
|
|
/** |
|
302
|
|
|
* Updates the RTP config file. |
|
303
|
|
|
*/ |
|
304
|
|
|
public static function rtpReload(): void |
|
305
|
|
|
{ |
|
306
|
|
|
$rtp = new RtpConf(); |
|
307
|
|
|
$rtp->generateConfig(); |
|
308
|
|
|
$asteriskPath = Util::which('asterisk'); |
|
309
|
|
|
Processes::mwExec("{$asteriskPath} -rx 'module reload res_rtp_asterisk'"); |
|
310
|
|
|
} |
|
311
|
|
|
|
|
312
|
|
|
/** |
|
313
|
|
|
* Refreshes the IAX configurations and reloads the iax2 module. |
|
314
|
|
|
*/ |
|
315
|
|
|
public static function iaxReload(): void |
|
316
|
|
|
{ |
|
317
|
|
|
$iax = new IAXConf(); |
|
318
|
|
|
$iax->generateConfig(); |
|
319
|
|
|
$asteriskPath = Util::which('asterisk'); |
|
320
|
|
|
Processes::mwExec("{$asteriskPath} -rx 'iax2 reload'"); |
|
321
|
|
|
} |
|
322
|
|
|
|
|
323
|
|
|
|
|
324
|
|
|
/** |
|
325
|
|
|
* Waits for Asterisk to fully boot. |
|
326
|
|
|
* @return bool True if Asterisk has fully booted, false otherwise. |
|
327
|
|
|
*/ |
|
328
|
|
|
public static function waitFullyBooted(): bool |
|
329
|
|
|
{ |
|
330
|
|
|
$time_start = microtime(true); |
|
331
|
|
|
$result = false; |
|
332
|
|
|
$out = []; |
|
333
|
|
|
$options = ''; |
|
334
|
|
|
|
|
335
|
|
|
$timeoutPath = Util::which('timeout'); |
|
336
|
|
|
$asteriskPath = Util::which('asterisk'); |
|
337
|
|
|
while (true) { |
|
338
|
|
|
$execResult = Processes::mwExec( |
|
339
|
|
|
"{$timeoutPath} {$options} 1 {$asteriskPath} -rx'core waitfullybooted'", |
|
340
|
|
|
$out |
|
341
|
|
|
); |
|
342
|
|
|
if ($execResult === 0 && implode('', $out) === 'Asterisk has fully booted.') { |
|
343
|
|
|
$result = true; |
|
344
|
|
|
break; |
|
345
|
|
|
} |
|
346
|
|
|
sleep(1); |
|
347
|
|
|
$time = microtime(true) - $time_start; |
|
348
|
|
|
if ($time > 60) { |
|
349
|
|
|
SystemMessages::sysLogMsg(__CLASS__, 'Error: Asterisk has not booted'); |
|
350
|
|
|
break; |
|
351
|
|
|
} |
|
352
|
|
|
} |
|
353
|
|
|
|
|
354
|
|
|
return $result; |
|
355
|
|
|
} |
|
356
|
|
|
|
|
357
|
|
|
/** |
|
358
|
|
|
* Configures Asterisk by generating all configuration files and (re)starts the Asterisk process. |
|
359
|
|
|
* @return array The result of the configuration process. |
|
360
|
|
|
*/ |
|
361
|
|
|
public function configure(): array |
|
362
|
|
|
{ |
|
363
|
|
|
if ( ! $this->di->getShared(RegistryProvider::SERVICE_NAME)->booting) { |
|
364
|
|
|
$this->stop(); |
|
365
|
|
|
} |
|
366
|
|
|
self::updateSavePeriod(); |
|
367
|
|
|
/** |
|
368
|
|
|
* Create configuration files. |
|
369
|
|
|
*/ |
|
370
|
|
|
$configClassObj = new AsteriskConfigClass(); |
|
371
|
|
|
$configClassObj->hookModulesMethod(AsteriskConfigInterface::GENERATE_CONFIG); |
|
372
|
|
|
|
|
373
|
|
|
self::dialplanReload(); |
|
374
|
|
|
if ($this->di->getShared(RegistryProvider::SERVICE_NAME)->booting) { |
|
375
|
|
|
$message = ' |- dialplan reload'; |
|
376
|
|
|
SystemMessages::echoToTeletype($message); |
|
377
|
|
|
SystemMessages::echoWithSyslog($message); |
|
378
|
|
|
SystemMessages::echoResult($message); |
|
379
|
|
|
SystemMessages::teletypeEchoResult($message); |
|
380
|
|
|
} |
|
381
|
|
|
// Create the call history database. |
|
382
|
|
|
/** @var \Phalcon\Db\Adapter\Pdo\Sqlite $connection */ |
|
383
|
|
|
$connection = $this->di->get(CDRDatabaseProvider::SERVICE_NAME); |
|
384
|
|
|
if ( ! $connection->tableExists('cdr')) { |
|
385
|
|
|
CDRDatabaseProvider::recreateDBConnections(); |
|
386
|
|
|
} else { |
|
387
|
|
|
CdrDb::checkDb(); |
|
388
|
|
|
} |
|
389
|
|
|
$result=[]; |
|
390
|
|
|
$result['result'] = 'Success'; |
|
391
|
|
|
|
|
392
|
|
|
return $result; |
|
393
|
|
|
} |
|
394
|
|
|
|
|
395
|
|
|
/** |
|
396
|
|
|
* Refreshes the extensions.conf file and reloads the Asterisk dialplan. |
|
397
|
|
|
*/ |
|
398
|
|
|
public static function dialplanReload(): void |
|
399
|
|
|
{ |
|
400
|
|
|
$di = Di::getDefault(); |
|
401
|
|
|
if ($di === null) { |
|
402
|
|
|
return; |
|
403
|
|
|
} |
|
404
|
|
|
if ($di->getShared(RegistryProvider::SERVICE_NAME)->booting !== true) { |
|
405
|
|
|
$extensions = new ExtensionsConf(); |
|
406
|
|
|
$extensions->generateConfig(); |
|
407
|
|
|
$path_asterisk = Util::which('asterisk'); |
|
408
|
|
|
Processes::mwExec("{$path_asterisk} -rx 'dialplan reload'"); |
|
409
|
|
|
Processes::mwExec("{$path_asterisk} -rx 'module reload pbx_lua.so'"); |
|
410
|
|
|
} |
|
411
|
|
|
} |
|
412
|
|
|
|
|
413
|
|
|
/** |
|
414
|
|
|
* Save information on the period of storage of conversation recordings. |
|
415
|
|
|
* @param string $value |
|
416
|
|
|
* @return void |
|
417
|
|
|
*/ |
|
418
|
|
|
public static function updateSavePeriod(string $value = ''):void{ |
|
419
|
|
|
if(empty($value)){ |
|
420
|
|
|
$value = PbxSettings::getValueByKey(PbxSettingsConstants::PBX_RECORD_SAVE_PERIOD); |
|
421
|
|
|
} |
|
422
|
|
|
$filename = '/var/etc/record-save-period'; |
|
423
|
|
|
file_put_contents($filename, $value); |
|
424
|
|
|
} |
|
425
|
|
|
|
|
426
|
|
|
} |
|
427
|
|
|
|
This function has been deprecated. The supplier of the function has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.