1 /*
2  * Copyright (c) 2021 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "log_persister.h"
17 
18 #include <sys/mman.h>
19 #include <sys/stat.h>
20 #include <sys/prctl.h>
21 
22 #include <fcntl.h>
23 #include <securec.h>
24 
25 #include <algorithm>
26 #include <array>
27 #include <chrono>
28 #include <climits>
29 #include <cstdio>
30 #include <cstdlib>
31 #include <cstring>
32 #include <dirent.h>
33 #include <iostream>
34 #include <mutex>
35 #include <regex>
36 #include <sstream>
37 #include <string>
38 #include <thread>
39 #include <unistd.h>
40 
41 #include <hilog_common.h>
42 #include <log_buffer.h>
43 #include <log_compress.h>
44 #include <log_print.h>
45 #include <log_utils.h>
46 
47 namespace OHOS {
48 namespace HiviewDFX {
49 using namespace std;
50 
51 static const int MAX_LOG_WRITE_INTERVAL = 5;
52 
IsEmptyThread(const std::thread & th)53 static bool IsEmptyThread(const std::thread& th)
54 {
55     static const std::thread EMPTY_THREAD;
56     return th.get_id() == EMPTY_THREAD.get_id();
57 }
58 
59 std::recursive_mutex LogPersister::s_logPersistersMtx;
60 std::list<std::shared_ptr<LogPersister>> LogPersister::s_logPersisters;
61 
CreateLogPersister(HilogBuffer & buffer)62 std::shared_ptr<LogPersister> LogPersister::CreateLogPersister(HilogBuffer &buffer)
63 {
64     return std::shared_ptr<LogPersister>(new LogPersister(buffer));
65 }
66 
LogPersister(HilogBuffer & buffer)67 LogPersister::LogPersister(HilogBuffer &buffer) : m_hilogBuffer(buffer)
68 {
69     m_mappedPlainLogFile = nullptr;
70     m_bufReader = m_hilogBuffer.CreateBufReader([this]() { NotifyNewLogAvailable(); });
71     m_startMsg = { 0 };
72 }
73 
~LogPersister()74 LogPersister::~LogPersister()
75 {
76     m_hilogBuffer.RemoveBufReader(m_bufReader);
77     Deinit();
78 }
79 
InitCompression()80 int LogPersister::InitCompression()
81 {
82     m_compressBuffer = std::make_unique<LogPersisterBuffer>();
83     if (!m_compressBuffer) {
84         return RET_FAIL;
85     }
86     switch (m_startMsg.compressAlg) {
87         case COMPRESS_TYPE_NONE:
88             m_compressor = std::make_unique<NoneCompress>();
89             break;
90         case COMPRESS_TYPE_ZLIB:
91             m_compressor = std::make_unique<ZlibCompress>();
92             break;
93         case COMPRESS_TYPE_ZSTD:
94             m_compressor = std::make_unique<ZstdCompress>();
95             break;
96         default:
97             break;
98     }
99     if (!m_compressor) {
100         return RET_FAIL;
101     }
102     return RET_SUCCESS;
103 }
104 
InitFileRotator(const PersistRecoveryInfo & info,bool restore)105 int LogPersister::InitFileRotator(const PersistRecoveryInfo& info, bool restore)
106 {
107     std::string fileSuffix = "";
108     switch (m_startMsg.compressAlg) {
109         case CompressAlg::COMPRESS_TYPE_ZSTD:
110             fileSuffix = ".zst";
111             break;
112         case CompressAlg::COMPRESS_TYPE_ZLIB:
113             fileSuffix = ".gz";
114             break;
115         default:
116             break;
117     }
118     m_fileRotator = std::make_unique<LogPersisterRotator>(m_startMsg.filePath,
119                     m_startMsg.jobId, m_startMsg.fileNum, fileSuffix);
120     if (!m_fileRotator) {
121         std::cerr << "Not enough memory!\n";
122         return RET_FAIL;
123     }
124     return m_fileRotator->Init(info, restore);
125 }
126 
Init(const PersistRecoveryInfo & info,bool restore)127 int LogPersister::Init(const PersistRecoveryInfo& info, bool restore)
128 {
129     std::cout << "Persist init begin\n";
130     std::lock_guard<decltype(m_initMtx)> lock(m_initMtx);
131     if (m_inited) {
132         return 0;
133     }
134     m_startMsg = info.msg;
135 
136     std::string path = m_startMsg.filePath;
137     size_t separatorPos = path.find_last_of('/');
138     if (separatorPos == std::string::npos) {
139         return ERR_LOG_PERSIST_FILE_PATH_INVALID;
140     }
141 
142     std::string parentPath = path.substr(0, separatorPos);
143     if (access(parentPath.c_str(), F_OK) != 0) {
144         perror("persister directory does not exist.");
145         return ERR_LOG_PERSIST_FILE_PATH_INVALID;
146     }
147 
148     // below guard is needed to have sure only one Path and Id is reqistered till end of init!
149     std::lock_guard<decltype(s_logPersistersMtx)> guard(s_logPersistersMtx);
150     if (CheckRegistered(m_startMsg.jobId, path)) {
151         return ERR_LOG_PERSIST_TASK_EXISTED;
152     }
153     if (InitCompression() !=  RET_SUCCESS) {
154         return ERR_LOG_PERSIST_COMPRESS_INIT_FAIL;
155     }
156     int ret = InitFileRotator(info, restore);
157     if (ret !=  RET_SUCCESS) {
158         return ret;
159     }
160 
161     if (int result = PrepareUncompressedFile(parentPath, restore)) {
162         return result;
163     }
164 
165     RegisterLogPersister(shared_from_this());
166     m_inited = true;
167     std::cout << " Persist init done\n";
168     return 0;
169 }
170 
Deinit()171 int LogPersister::Deinit()
172 {
173     std::cout << "LogPersist deinit begin\n";
174     std::lock_guard<decltype(m_initMtx)> lock(m_initMtx);
175     if (!m_inited) {
176         return 0;
177     }
178 
179     Stop();
180 
181     munmap(m_mappedPlainLogFile, sizeof(LogPersisterBuffer));
182     std::cout << "Removing unmapped plain log file: " << m_plainLogFilePath << "\n";
183     if (remove(m_plainLogFilePath.c_str())) {
184         std::cerr << "File: " << m_plainLogFilePath << " can't be removed. ";
185         PrintErrorno(errno);
186     }
187 
188     DeregisterLogPersister(shared_from_this());
189     m_inited = false;
190     std::cout << "LogPersist deinit done\n";
191     return 0;
192 }
193 
PrepareUncompressedFile(const std::string & parentPath,bool restore)194 int LogPersister::PrepareUncompressedFile(const std::string& parentPath, bool restore)
195 {
196     std::string fileName = std::string(".") + AUXILLARY_PERSISTER_PREFIX + std::to_string(m_startMsg.jobId);
197     m_plainLogFilePath = parentPath + "/" + fileName;
198     FILE* plainTextFile = fopen(m_plainLogFilePath.c_str(), restore ? "r+" : "w+");
199 
200     if (!plainTextFile) {
201         std::cerr << " Open uncompressed log file(" << m_plainLogFilePath << ") failed: ";
202         PrintErrorno(errno);
203         return ERR_LOG_PERSIST_FILE_OPEN_FAIL;
204     }
205 
206     if (!restore) {
207         ftruncate(fileno(plainTextFile), sizeof(LogPersisterBuffer));
208         fflush(plainTextFile);
209         fsync(fileno(plainTextFile));
210     }
211     m_mappedPlainLogFile = reinterpret_cast<LogPersisterBuffer*>(mmap(nullptr, sizeof(LogPersisterBuffer),
212         PROT_READ | PROT_WRITE, MAP_SHARED, fileno(plainTextFile), 0));
213     if (fclose(plainTextFile)) {
214         std::cerr << "File: " << plainTextFile << " can't be closed. ";
215         PrintErrorno(errno);
216     }
217     if (m_mappedPlainLogFile == MAP_FAILED) {
218         std::cerr << " mmap file failed: ";
219         PrintErrorno(errno);
220         return RET_FAIL;
221     }
222     if (restore) {
223 #ifdef DEBUG
224         std::cout << " Recovered persister, Offset=" << m_mappedPlainLogFile->offset << "\n";
225 #endif
226         // try to store previous uncompressed logs
227         auto compressionResult = m_compressor->Compress(*m_mappedPlainLogFile, *m_compressBuffer);
228         if (compressionResult != 0) {
229             std::cerr << " Compression error. Result:" << compressionResult << "\n";
230             return RET_FAIL;
231         }
232         WriteCompressedLogs();
233     } else {
234         m_mappedPlainLogFile->offset = 0;
235     }
236     return 0;
237 }
238 
NotifyNewLogAvailable()239 void LogPersister::NotifyNewLogAvailable()
240 {
241     m_receiveLogCv.notify_one();
242 }
243 
WriteUncompressedLogs(std::string & logLine)244 bool LogPersister::WriteUncompressedLogs(std::string& logLine)
245 {
246     uint16_t size = logLine.length();
247     uint32_t remainingSpace = MAX_PERSISTER_BUFFER_SIZE - m_mappedPlainLogFile->offset;
248     if (remainingSpace < size) {
249         return false;
250     }
251     char* currentContentPos = m_mappedPlainLogFile->content + m_mappedPlainLogFile->offset;
252     int r = memcpy_s(currentContentPos, remainingSpace, logLine.c_str(), logLine.length());
253     if (r != 0) {
254         std::cout << " Can't copy part of memory!\n";
255         return true;
256     }
257     m_mappedPlainLogFile->offset += logLine.length();
258     return true;
259 }
260 
WriteLogData(const HilogData & logData)261 int LogPersister::WriteLogData(const HilogData& logData)
262 {
263     LogContent content = {
264         .level = logData.level,
265         .type = logData.type,
266         .pid = logData.pid,
267         .tid = logData.tid,
268         .domain = logData.domain,
269         .tv_sec = logData.tv_sec,
270         .tv_nsec = logData.tv_nsec,
271         .mono_sec = logData.mono_sec,
272         .tag = logData.tag,
273         .log = logData.content
274     };
275     LogFormat format = {
276         .colorful = false,
277         .timeFormat = FormatTime::TIME,
278         .timeAccuFormat = FormatTimeAccu::MSEC,
279         .year = false,
280         .zone = false,
281     };
282     std::ostringstream oss;
283     LogPrintWithFormat(content, format, oss);
284     std::string formatedLogStr = oss.str();
285     // Firstly gather uncompressed logs in auxiliary file
286     if (WriteUncompressedLogs(formatedLogStr))
287         return 0;
288     // Try to compress auxiliary file
289     auto compressionResult = m_compressor->Compress(*m_mappedPlainLogFile, *m_compressBuffer);
290     if (compressionResult != 0) {
291         std::cerr <<  " Compression error. Result:" << compressionResult << "\n";
292         return RET_FAIL;
293     }
294     // Write compressed buffor and clear counters
295     WriteCompressedLogs();
296     // Try again write data that wasn't written at the beginning
297     // If again fail then these logs are skipped
298     return WriteUncompressedLogs(formatedLogStr) ? 0 : RET_FAIL;
299 }
300 
WriteCompressedLogs()301 inline void LogPersister::WriteCompressedLogs()
302 {
303     if (m_mappedPlainLogFile->offset == 0)
304         return;
305     m_fileRotator->Input(m_compressBuffer->content, m_compressBuffer->offset);
306     m_plainLogSize += m_mappedPlainLogFile->offset;
307     if (m_plainLogSize >= m_startMsg.fileSize) {
308         m_plainLogSize = 0;
309         m_fileRotator->FinishInput();
310     }
311     m_compressBuffer->offset = 0;
312     m_mappedPlainLogFile->offset = 0;
313 }
314 
Start()315 void LogPersister::Start()
316 {
317     {
318         std::lock_guard<decltype(m_initMtx)> lock(m_initMtx);
319         if (!m_inited) {
320             std::cerr << " Log persister wasn't inited!\n";
321             return;
322         }
323     }
324 
325     if (IsEmptyThread(m_persisterThread)) {
326         m_persisterThread = std::thread([shared = shared_from_this()]() {
327             shared->ReceiveLogLoop();
328         });
329     } else {
330         std::cout << " Persister thread already started!\n";
331     }
332 }
333 
ReceiveLogLoop()334 int LogPersister::ReceiveLogLoop()
335 {
336     prctl(PR_SET_NAME, "hilogd.pst");
337     std::cout << "Persist ReceiveLogLoop " << std::this_thread::get_id() << "\n";
338     for (;;) {
339         if (m_stopThread) {
340             break;
341         }
342         std::optional<HilogData> data = m_hilogBuffer.Query(m_startMsg.filter, m_bufReader);
343         if (data.has_value()) {
344             if (WriteLogData(data.value())) {
345                 std::cerr << " Can't write new log data!\n";
346             }
347         } else {
348             std::unique_lock<decltype(m_receiveLogCvMtx)> lk(m_receiveLogCvMtx);
349             static const std::chrono::seconds waitTime(MAX_LOG_WRITE_INTERVAL);
350             if (cv_status::timeout == m_receiveLogCv.wait_for(lk, waitTime)) {
351                 std::cout << "no log timeout, write log forcely" << std::endl;
352                 (void)m_compressor->Compress(*m_mappedPlainLogFile, *m_compressBuffer);
353                 WriteCompressedLogs();
354             }
355         }
356     }
357     // try to compress the remaining log in cache
358     (void)m_compressor->Compress(*m_mappedPlainLogFile, *m_compressBuffer);
359     WriteCompressedLogs();
360     m_fileRotator->FinishInput();
361     return 0;
362 }
363 
Query(std::list<LogPersistQueryResult> & results)364 int LogPersister::Query(std::list<LogPersistQueryResult> &results)
365 {
366     std::lock_guard<decltype(s_logPersistersMtx)> guard(s_logPersistersMtx);
367     for (auto& logPersister : s_logPersisters) {
368         LogPersistQueryResult response;
369         response.logType = logPersister->m_startMsg.filter.types;
370         logPersister->FillInfo(response);
371         results.push_back(response);
372     }
373     return 0;
374 }
375 
FillInfo(LogPersistQueryResult & response)376 void LogPersister::FillInfo(LogPersistQueryResult &response)
377 {
378     response.jobId = m_startMsg.jobId;
379     if (strcpy_s(response.filePath, FILE_PATH_MAX_LEN, m_startMsg.filePath)) {
380         return;
381     }
382     response.compressAlg = m_startMsg.compressAlg;
383     response.fileSize = m_startMsg.fileSize;
384     response.fileNum = m_startMsg.fileNum;
385 }
386 
Kill(uint32_t id)387 int LogPersister::Kill(uint32_t id)
388 {
389     auto logPersisterPtr = GetLogPersisterById(id);
390     if (logPersisterPtr) {
391         return logPersisterPtr->Deinit();
392     }
393     std::cerr << " Log persister with id: " << id << " does not exist.\n";
394     return ERR_LOG_PERSIST_JOBID_FAIL;
395 }
396 
Stop()397 void LogPersister::Stop()
398 {
399     std::cout << "Exiting LogPersister!\n";
400     if (IsEmptyThread(m_persisterThread)) {
401         std::cout << "Thread was exited or not started!\n";
402         return;
403     }
404 
405     m_stopThread = true;
406     m_receiveLogCv.notify_all();
407 
408     if (m_persisterThread.joinable()) {
409         m_persisterThread.join();
410     }
411 }
412 
Refresh(uint32_t id)413 int LogPersister::Refresh(uint32_t id)
414 {
415     auto logPersisterPtr = GetLogPersisterById(id);
416     if (logPersisterPtr) {
417         std::optional<HilogData> data = logPersisterPtr->m_hilogBuffer.Query(logPersisterPtr->m_startMsg.filter,
418             logPersisterPtr->m_bufReader);
419         if (data.has_value()) {
420             if (logPersisterPtr->WriteLogData(data.value())) {
421                 std::cerr << " Can't write new log data!\n";
422             }
423         } else {
424             std::unique_lock<decltype(logPersisterPtr->m_receiveLogCvMtx)> lk(logPersisterPtr->m_receiveLogCvMtx);
425             static const std::chrono::seconds waitTime(MAX_LOG_WRITE_INTERVAL);
426             if (cv_status::timeout == logPersisterPtr->m_receiveLogCv.wait_for(lk, waitTime)) {
427                 std::cout << "no log timeout, write log forcely" << std::endl;
428                 (void)logPersisterPtr->m_compressor->Compress(*(logPersisterPtr->m_mappedPlainLogFile),
429                     *(logPersisterPtr->m_compressBuffer));
430                 logPersisterPtr->WriteCompressedLogs();
431             }
432         }
433         return 0;
434     }
435     std::cerr << " Log persister with id: " << id << " does not exist.\n";
436     return ERR_LOG_PERSIST_JOBID_FAIL;
437 }
438 
Clear()439 void LogPersister::Clear()
440 {
441     std::regex hilogFilePattern("^hilog.*gz$");
442     DIR *dir = nullptr;
443     struct dirent *ent = nullptr;
444     if ((dir = opendir(HILOG_FILE_DIR)) != nullptr) {
445         while ((ent = readdir(dir)) != nullptr) {
446             size_t length = strlen(ent->d_name);
447             std::string dName(ent->d_name, length);
448             if (std::regex_match(dName, hilogFilePattern)) {
449                 remove((HILOG_FILE_DIR + dName).c_str());
450             }
451         }
452     }
453     if (dir != nullptr) {
454         closedir(dir);
455     }
456 }
457 
CheckRegistered(uint32_t id,const std::string & logPath)458 bool LogPersister::CheckRegistered(uint32_t id, const std::string& logPath)
459 {
460     std::lock_guard<decltype(s_logPersistersMtx)> lock(s_logPersistersMtx);
461     auto it = std::find_if(s_logPersisters.begin(), s_logPersisters.end(),
462         [&](const std::shared_ptr<LogPersister>& logPersister) {
463             if (logPersister->m_startMsg.jobId == id || logPersister->m_startMsg.filePath == logPath) {
464                 return true;
465             }
466             return false;
467         });
468     return it != s_logPersisters.end();
469 }
470 
GetLogPersisterById(uint32_t id)471 std::shared_ptr<LogPersister> LogPersister::GetLogPersisterById(uint32_t id)
472 {
473     std::lock_guard<decltype(s_logPersistersMtx)> guard(s_logPersistersMtx);
474 
475     auto it = std::find_if(s_logPersisters.begin(), s_logPersisters.end(),
476         [&](const std::shared_ptr<LogPersister>& logPersister) {
477             if (logPersister->m_startMsg.jobId == id) {
478                 return true;
479             }
480             return false;
481         });
482     if (it == s_logPersisters.end()) {
483         return std::shared_ptr<LogPersister>();
484     }
485     return *it;
486 }
487 
RegisterLogPersister(const std::shared_ptr<LogPersister> & obj)488 void LogPersister::RegisterLogPersister(const std::shared_ptr<LogPersister>& obj)
489 {
490     std::lock_guard<decltype(s_logPersistersMtx)> lock(s_logPersistersMtx);
491     s_logPersisters.push_back(obj);
492 }
493 
DeregisterLogPersister(const std::shared_ptr<LogPersister> & obj)494 void LogPersister::DeregisterLogPersister(const std::shared_ptr<LogPersister>& obj)
495 {
496     if (!obj) {
497         std::cerr << " Invalid invoke - this should never happened!\n";
498         return;
499     }
500     std::lock_guard<decltype(s_logPersistersMtx)> lock(s_logPersistersMtx);
501     auto it = std::find_if(s_logPersisters.begin(), s_logPersisters.end(),
502         [&](const std::shared_ptr<LogPersister>& logPersister) {
503             if (logPersister->m_startMsg.jobId == obj->m_startMsg.jobId) {
504                 return true;
505             }
506             return false;
507         });
508     if (it == s_logPersisters.end()) {
509         std::cerr << " Inconsistent data - this should never happened!\n";
510         return;
511     }
512     s_logPersisters.erase(it);
513 }
514 } // namespace HiviewDFX
515 } // namespace OHOS
516