Passed
Push — develop ( 52d2d9...559379 )
by Nikolay
05:37
created

SysLogsManagementProcessor::getLogFromFile()   A

Complexity

Conditions 4
Paths 5

Size

Total Lines 37
Code Lines 29

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 29
dl 0
loc 37
rs 9.456
c 0
b 0
f 0
cc 4
nc 5
nop 4
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, 8 2020
7
 */
8
9
namespace MikoPBX\PBXCoreREST\Lib;
10
11
use MikoPBX\Core\System\Network;
12
use MikoPBX\Core\System\System;
13
use MikoPBX\Core\System\Util;
14
use MikoPBX\PBXCoreREST\Workers\WorkerMakeLogFilesArchive;
15
use Phalcon\Di;
16
use Phalcon\Di\Injectable;
17
18
class SysLogsManagementProcessor extends Injectable
19
{
20
    public const DEFAULT_FILENAME = 'asterisk/messages';
21
22
    /**
23
     * Processes System requests
24
     *
25
     * @param array $request
26
     *
27
     * @return \MikoPBX\PBXCoreREST\Lib\PBXApiResult
28
     * @throws \Exception
29
     */
30
    public static function syslogCallBack(array $request): PBXApiResult
31
    {
32
        $action         = $request['action'];
33
        $data           = $request['data'];
34
        $res            = new PBXApiResult();
35
        $res->processor = __METHOD__;
36
        switch ($action) {
37
            case 'getLogFromFile':
38
                $res = self::getLogFromFile($data['filename'], $data['filter'], $data['lines'], $data['offset']);
39
                break;
40
            case 'startLog':
41
                $res = self::startLog();
42
                break;
43
            case 'stopLog':
44
                $res = self::stopLog();
45
                break;
46
            case 'downloadLogsArchive':
47
                $res = self::downloadLogsArchive($data['filename']);
48
                break;
49
            case 'downloadLogFile':
50
                $res = self::downloadLogFile($data['filename']);
51
                break;
52
            case 'getLogsList':
53
                $res = self::getLogsList();
54
                break;
55
            default:
56
                $res->messages[] = "Unknown action - {$action} in syslogCallBack";
57
        }
58
59
        $res->function = $action;
60
61
        return $res;
62
    }
63
64
    /**
65
     * Gets log file content partially
66
     *
67
     * @param string $filename
68
     * @param string $filter
69
     * @param int    $lines
70
     * @param int    $offset
71
     *
72
     * @return PBXApiResult
73
     */
74
    private static function getLogFromFile(string $filename, string $filter = '', $lines = 500, $offset = 0): PBXApiResult
75
    {
76
        $res            = new PBXApiResult();
77
        $res->processor = __METHOD__;
78
        $filename       = System::getLogDir() . '/' . $filename;
79
        if ( ! file_exists($filename)) {
80
            $res->success    = false;
81
            $res->messages[] = 'No access to the file ' . $filename;
82
        } else {
83
            $res->success = true;
84
            $head         = Util::which('head');
85
            $grep         = Util::which('grep');
86
            $tail         = Util::which('tail');
87
            $filter       = escapeshellarg($filter);
88
            $offset       = (int)$offset;
89
            $lines        = (int)$lines;
90
            $linesPlusOffset = $lines+$offset;
91
92
            $di          = Di::getDefault();
93
            $dirsConfig  = $di->getShared('config');
94
            $filenameTmp = $dirsConfig->path('www.downloadCacheDir') . '/' . __FUNCTION__ . '_' . time() . '.log';
95
            if (empty($filter)){
96
                $cmd         = "{$tail} -n {$linesPlusOffset} {$filename}";
97
            } else {
98
                $cmd         = "{$grep} {$filter} {$filename} | $tail -n {$linesPlusOffset}";
99
            }
100
            if ($offset>0){
101
                $cmd .= " | {$head} -n {$lines}";
102
            }
103
            $cmd .= " > $filenameTmp";
104
105
            Util::mwExec("$cmd; chown www:www $filenameTmp");
106
            $res->data['cmd']=$cmd;
107
            $res->data['filename'] = $filenameTmp;
108
        }
109
110
        return $res;
111
    }
112
113
    /**
114
     * Стартует запись логов.
115
     *
116
     * @param int $timeout
117
     *
118
     * @return \MikoPBX\PBXCoreREST\Lib\PBXApiResult
119
     * @throws \Exception
120
     */
121
    private static function startLog($timeout = 300): PBXApiResult
122
    {
123
        $res            = new PBXApiResult();
124
        $res->processor = __METHOD__;
125
        $logDir         = System::getLogDir();
126
127
        // TCP dump
128
        $tcpDumpDir  = "{$logDir}/tcpDump";
129
        Util::mwMkdir($tcpDumpDir);
130
        $network     = new Network();
131
        $arr_eth     = $network->getInterfacesNames();
132
        $tcpdumpPath = Util::which('tcpdump');
133
        foreach ($arr_eth as $eth) {
134
            Util::mwExecBgWithTimeout(
135
                "{$tcpdumpPath} -i {$eth} -n -s 0 -vvv -w {$tcpDumpDir}/{$eth}.pcap",
136
                $timeout,
137
                "{$tcpDumpDir}/{$eth}_out.log"
138
            );
139
        }
140
        $res->success = true;
141
142
        return $res;
143
    }
144
145
    /**
146
     * Prepare log archive file
147
     *
148
     * @return \MikoPBX\PBXCoreREST\Lib\PBXApiResult
149
     * @throws \Exception
150
     */
151
    private static function stopLog(): PBXApiResult
152
    {
153
        $res            = new PBXApiResult();
154
        $res->processor = __METHOD__;
155
        $di             = Di::getDefault();
156
        $dirsConfig     = $di->getShared('config');
157
        $temp_dir       = $dirsConfig->path('core.tempDir');
158
159
        // Collect system info
160
        $logDir         = System::getLogDir();
161
        $systemInfoFile = "{$logDir}/system-information.log";
162
        file_put_contents($systemInfoFile, SysinfoManagementProcessor::prepareSysyinfoContent());
163
164
        $futureFileName        = $temp_dir . '/temp-all-log-' . time() . '.zip';
165
        $res->data['filename'] = $futureFileName;
166
        $res->success          = true;
167
168
        Util::killByName('timeout');
169
        Util::killByName('tcpdump');
170
171
        // Create background task
172
        $merge_settings                = [];
173
        $merge_settings['result_file'] = $futureFileName;
174
        $settings_file                 = "{$temp_dir}/logs_archive_settings.json";
175
        file_put_contents(
176
            $settings_file,
177
            json_encode($merge_settings, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT)
178
        );
179
        $phpPath               = Util::which('php');
180
        $workerFilesMergerPath = Util::getFilePathByClassName(WorkerMakeLogFilesArchive::class);
181
        Util::mwExecBg("{$phpPath} -f {$workerFilesMergerPath} '{$settings_file}'");
182
183
        return $res;
184
    }
185
186
    /**
187
     * Check if archive ready then create download link
188
     *
189
     * @param string $resultFile
190
     *
191
     * @return \MikoPBX\PBXCoreREST\Lib\PBXApiResult
192
     * @throws \Exception
193
     */
194
    private static function downloadLogsArchive(string $resultFile): PBXApiResult
195
    {
196
        $res            = new PBXApiResult();
197
        $res->processor = __METHOD__;
198
199
        $progress_file = "{$resultFile}.progress";
200
        if ( ! file_exists($progress_file)) {
201
            $res->messages[] = 'Archive does not exist. Try again!';
202
        } elseif (file_exists($progress_file) && file_get_contents($progress_file) === '100') {
203
            $uid          = Util::generateRandomString(36);
204
            $di           = Di::getDefault();
205
            $downloadLink = $di->getShared('config')->path('www.downloadCacheDir');
206
            $result_dir   = "{$downloadLink}/{$uid}";
207
            Util::mwMkdir($result_dir);
208
            $link_name = 'MikoPBXLogs_' . basename($resultFile);
209
            Util::createUpdateSymlink($resultFile, "{$result_dir}/{$link_name}");
210
            Util::addRegularWWWRights("{$result_dir}/{$link_name}");
211
            $res->success          = true;
212
            $res->data['status']   = "READY";
213
            $res->data['filename'] = "{$uid}/{$link_name}";
214
        } else {
215
            $res->success        = true;
216
            $res->data['status'] = "preparing";
217
        }
218
219
        return $res;
220
    }
221
222
    /**
223
     * Prepares downloadable log file
224
     *
225
     * @param string $filename
226
     *
227
     * @return \MikoPBX\PBXCoreREST\Lib\PBXApiResult
228
     * @throws \Exception
229
     */
230
    private static function downloadLogFile(string $filename): PBXApiResult
231
    {
232
        $res            = new PBXApiResult();
233
        $res->processor = __METHOD__;
234
        $filename       = System::getLogDir() . '/' . $filename;
235
        if ( ! file_exists($filename)) {
236
            $res->success    = false;
237
            $res->messages[] = 'File does not exist ' . $filename;
238
        } else {
239
            $uid          = Util::generateRandomString(36);
240
            $di           = Di::getDefault();
241
            $downloadLink = $di->getShared('config')->path('www.downloadCacheDir');
242
            $result_dir   = "{$downloadLink}/{$uid}";
243
            Util::mwMkdir($result_dir);
244
            $link_name = basename($filename);
245
            $lnPath    = Util::which('ln');
246
            $chownPath = Util::which('chown');
247
            Util::mwExec("{$lnPath} -s {$filename} {$result_dir}/{$link_name}");
248
            Util::mwExec("{$chownPath} www:www {$result_dir}/{$link_name}");
249
            $res->success          = true;
250
            $res->data['filename'] = "{$uid}/{$link_name}";
251
        }
252
253
        return $res;
254
    }
255
256
    /**
257
     * Returns list of log files to show them on web interface
258
     *
259
     * @return \MikoPBX\PBXCoreREST\Lib\PBXApiResult
260
     */
261
    private static function getLogsList(): PBXApiResult
262
    {
263
        $res            = new PBXApiResult();
264
        $res->processor = __METHOD__;
265
        $logDir         = System::getLogDir();
266
        $filesList      = [];
267
        $entries        = FilesManagementProcessor::scanDirRecursively($logDir);
268
        $entries        = Util::flattenArray($entries);
269
        foreach ($entries as $entry) {
270
            $fileSize = filesize($entry);
271
            $now      = time();
272
            if ($fileSize === 0
273
                || $now - filemtime($entry) > 604800 // Older than 10 days
274
            ) {
275
                continue;
276
            }
277
278
            $relativePath             = str_ireplace($logDir . '/', '', $entry);
279
            $fileSizeKB               = ceil($fileSize / 1024);
280
            $filesList[$relativePath] =
281
                [
282
                    'path'    => $relativePath,
283
                    'size'    => "{$fileSizeKB} kb",
284
                    'default' => ($relativePath === self::DEFAULT_FILENAME),
285
                ];
286
        }
287
288
        ksort($filesList);
289
        $res->success       = true;
290
        $res->data['files'] = $filesList;
291
292
        return $res;
293
    }
294
}