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

NginxConf::reStart()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 14
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 11
c 0
b 0
f 0
dl 0
loc 14
rs 9.9
cc 3
nc 3
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\System\Configs;
21
22
23
use MikoPBX\Core\System\MikoPBXConfig;
24
use MikoPBX\Core\System\Network;
25
use MikoPBX\Core\System\Processes;
26
use MikoPBX\Core\System\Util;
27
use MikoPBX\Core\System\Verify;
28
use Phalcon\Di\Injectable;
29
30
class NginxConf extends Injectable
31
{
32
    public const  LOCATIONS_PATH = '/etc/nginx/mikopbx/locations';
33
    public const  MODULES_LOCATIONS_PATH = '/etc/nginx/mikopbx/modules_locations';
34
    private const PID_FILE = '/var/run/nginx.pid';
35
36
    private MikoPBXConfig $mikoPBXConfig;
37
38
    /**
39
     * NginxConf constructor.
40
     */
41
    public function __construct()
42
    {
43
        $this->mikoPBXConfig = new MikoPBXConfig();
44
    }
45
46
    /**
47
     * Restart Nginx gracefully
48
     * https://www.cyberciti.biz/faq/howto-unix-linux-gracefully-reload-restart-nginx-webserver/
49
     **/
50
    public function reStart(): void
51
    {
52
        $pid = self::getPid();
53
        if (!empty($pid)) {
54
            $killPath = Util::which('kill');
55
            // reload Nginx workers gracefully
56
            Processes::mwExec("{$killPath} -SIGHUP {$pid} ");
57
        } elseif (Util::isSystemctl()) {
58
            $systemCtrlPath = Util::which('systemctl');
59
            Processes::mwExec("{$systemCtrlPath} restart nginx.service");
60
        } else {
61
            $nginxPath = Util::which('nginx');
62
            Processes::killByName('nginx');
63
            Processes::mwExec($nginxPath);
64
        }
65
    }
66
67
    private static function getPid():string{
68
        if(file_exists(self::PID_FILE)) {
69
            $pid = file_get_contents(self::PID_FILE);
70
        }else{
71
            $nginxPath = Util::which('nginx');
72
            $pid       = Processes::getPidOfProcess($nginxPath);
73
        }
74
        return $pid;
75
    }
76
77
    /**
78
     * Write additional settings the nginx.conf
79
     *
80
     * @param bool $not_ssl
81
     * @param int  $level
82
     */
83
    public function generateConf($not_ssl = false, $level = 0): void
84
    {
85
        $configPath      = '/etc/nginx/mikopbx/conf.d';
86
        $httpConfigFile  = "{$configPath}/http-server.conf";
87
        $httpsConfigFile = "{$configPath}/https-server.conf";
88
        $dns_server      = '127.0.0.1';
89
90
        $net = new Network();
91
        $dns = $net->getHostDNS();
92
        foreach ($dns as $ns) {
93
            if (Verify::isIpAddress($ns)) {
94
                $dns_server = trim($ns);
95
                break;
96
            }
97
        }
98
99
        // HTTP
100
        $WEBPort      = $this->mikoPBXConfig->getGeneralSettings('WEBPort');
101
        $WEBHTTPSPort = $this->mikoPBXConfig->getGeneralSettings('WEBHTTPSPort');
102
103
        $config = file_get_contents("{$httpConfigFile}.original");
104
        $config = str_replace(['<DNS>', '<WEBPort>'], [$dns_server, $WEBPort], $config);
105
106
        $RedirectToHttps = $this->mikoPBXConfig->getGeneralSettings('RedirectToHttps');
107
        if ($RedirectToHttps === '1' && $not_ssl === false) {
108
            $conf_data = 'if ( $remote_addr != "127.0.0.1" ) {' . PHP_EOL
109
                . '        ' . 'return 301 https://$host:' . $WEBHTTPSPort . '$request_uri;' . PHP_EOL
110
                . '       }' . PHP_EOL;
111
            $config    = str_replace('include mikopbx/locations/*.conf;', $conf_data, $config);
112
        }
113
        file_put_contents($httpConfigFile, $config);
114
115
        // SSL
116
        $WEBHTTPSPublicKey  = $this->mikoPBXConfig->getGeneralSettings('WEBHTTPSPublicKey');
117
        $WEBHTTPSPrivateKey = $this->mikoPBXConfig->getGeneralSettings('WEBHTTPSPrivateKey');
118
        if (
119
            $not_ssl === false
120
            && ! empty($WEBHTTPSPublicKey)
121
            && ! empty($WEBHTTPSPrivateKey)
122
        ) {
123
            $public_filename  = '/etc/ssl/certs/nginx.crt';
124
            $private_filename = '/etc/ssl/private/nginx.key';
125
            file_put_contents($public_filename, $WEBHTTPSPublicKey);
126
            file_put_contents($private_filename, $WEBHTTPSPrivateKey);
127
            $config = file_get_contents("{$httpsConfigFile}.original");
128
            $config = str_replace(['<DNS>', '<WEBHTTPSPort>'], [$dns_server, $WEBHTTPSPort], $config);
129
            file_put_contents($httpsConfigFile, $config);
130
        } elseif (file_exists($httpsConfigFile)) {
131
            unlink($httpsConfigFile);
132
        }
133
134
        // Test work
135
        $currentConfigIsGood = $this->testCurrentNginxConfig();
136
        if ($level < 1 && ! $currentConfigIsGood) {
137
            ++$level;
138
            Util::sysLogMsg('nginx', 'Failed test config file. SSL will be disable...', LOG_ERR);
139
            $this->generateConf(true, $level);
140
        }
141
        // Add additional rules from modules
142
        $this->generateModulesConf();
143
    }
144
145
    /**
146
     * Test current nginx config on errors
147
     *
148
     * @return bool
149
     */
150
    private function testCurrentNginxConfig(): bool
151
    {
152
        $nginxPath = Util::which('nginx');
153
        $out       = [];
154
        Processes::mwExec("{$nginxPath} -t", $out);
155
        $res = implode($out);
156
157
        return (false === stripos($res, 'test failed'));
158
    }
159
160
    /**
161
     * Generate modules locations conf files
162
     */
163
    public function generateModulesConf(): void
164
    {
165
        $locationsPath     = self::MODULES_LOCATIONS_PATH;
166
        if (!is_dir($locationsPath)){
167
            Util::mwMkdir($locationsPath,true);
168
        }
169
        $additionalModules = $this->di->get('pbxConfModules');
170
        $rmPath            = Util::which('rm');
171
        Processes::mwExec("{$rmPath} -rf {$locationsPath}/*.conf");
172
        foreach ($additionalModules as $appClass) {
173
            if (!method_exists($appClass, 'createNginxLocations')) {
174
                continue;
175
            }
176
            $locationContent = $appClass->createNginxLocations();
177
            if (empty($locationContent)) {
178
                // Текст конфига не определен.
179
                continue;
180
            }
181
            $confFileName = "{$locationsPath}/{$appClass->moduleUniqueId}.conf";
182
            file_put_contents($confFileName, $locationContent);
183
            if ( $this->testCurrentNginxConfig()) {
184
                // Тест прошел успешно.
185
                continue;
186
            }
187
            // Откат конфига.
188
            Processes::mwExec("{$rmPath} {$confFileName}");
189
            Util::sysLogMsg('nginx', 'Failed test config file for module' . $appClass->moduleUniqueId, LOG_ERR);
190
        }
191
    }
192
}