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#include <cstdio>
16#include <dirent.h>
17#include <fstream>
18#include <iostream>
19#include <securec.h>
20#include <sstream>
21#include <sys/stat.h>
22#include <sys/time.h>
23#include <unistd.h>
24
25#include "log_persister_rotator.h"
26
27constexpr uint8_t MAX_TIME_BUF_SIZE = 32;
28constexpr uint8_t MAX_LOG_INDEX_LEN = 4;
29
30namespace OHOS {
31namespace HiviewDFX {
32std::string GetFileNameIndex(const int index)
33{
34    char res[MAX_LOG_INDEX_LEN];
35    (void)snprintf_s(res, sizeof(res), sizeof(res) - 1, "%03d", index % MAX_LOG_FILE_NUM);
36    std::string fileNameIndex(res);
37    return fileNameIndex;
38}
39
40bool LogPersisterRotator::IsOldFile(const std::string& logName, const int index)
41{
42    std::string fileNameHead = m_logsPath.substr(strlen(HILOG_FILE_DIR), m_logsPath.size());
43    fileNameHead = fileNameHead + "." + GetFileNameIndex(index + 1 - m_maxLogFileNum);
44    if (logName.find(fileNameHead) == std::string::npos) {
45        return false;
46    }
47    return true;
48}
49
50
51LogPersisterRotator::LogPersisterRotator(const std::string& logsPath, uint32_t id, uint32_t maxFiles,
52    const std::string& fileNameSuffix)
53    : m_maxLogFileNum(maxFiles), m_logsPath(logsPath), m_fileNameSuffix(fileNameSuffix), m_id(id)
54{
55}
56
57LogPersisterRotator::~LogPersisterRotator()
58{
59    m_infoFile.close();
60    remove(m_infoFilePath.c_str());
61}
62
63int LogPersisterRotator::Init(const PersistRecoveryInfo& info, bool restore)
64{
65    if (!m_infoFile.is_open()) {
66        if (int result = OpenInfoFile(); result != RET_SUCCESS) {
67            return result;
68        }
69    }
70
71    m_info = info;
72    SetFileIndex(m_info.index, restore);
73    UpdateRotateNumber();
74    return RET_SUCCESS;
75}
76
77int LogPersisterRotator::OpenInfoFile()
78{
79    auto lastSeparatorIdx = m_logsPath.find_last_of('/');
80    std::string parentDirPath = m_logsPath.substr(0, lastSeparatorIdx);
81    if (access(parentDirPath.c_str(), F_OK) != 0) {
82        if (errno == ENOENT) {
83            mkdir(parentDirPath.c_str(), S_IRUSR | S_IWUSR | S_IXUSR | S_IRWXG | S_IRWXO);
84        }
85    }
86    std::string infoFileName = std::string(".") + AUXILLARY_PERSISTER_PREFIX + std::to_string(m_id) + ".info";
87    m_infoFilePath = parentDirPath + "/" + infoFileName;
88    m_infoFile.open(m_infoFilePath, std::ios::binary | std::ios::out | std::ios::trunc);
89    return m_infoFile.is_open() ? RET_SUCCESS : RET_FAIL;
90}
91
92int LogPersisterRotator::Input(const char *buf, uint32_t length)
93{
94    if (length <= 0 || buf == nullptr) {
95        return ERR_LOG_PERSIST_COMPRESS_BUFFER_EXP;
96    }
97    if (m_needRotate) {
98        Rotate();
99        m_needRotate = false;
100    } else if ((access(m_currentLogFileName.c_str(), F_OK) != 0) || !m_currentLogOutput.is_open()) {
101        CreateLogFile();
102    }
103    m_currentLogOutput.write(buf, length);
104    m_currentLogOutput.flush();
105    return 0;
106}
107
108void LogPersisterRotator::RemoveOldFile()
109{
110    DIR *dir = nullptr;
111    struct dirent *ent = nullptr;
112    if ((dir = opendir(HILOG_FILE_DIR)) != nullptr) {
113        while ((ent = readdir(dir)) != nullptr) {
114            size_t length = strlen(ent->d_name);
115            std::string pPath(ent->d_name, length);
116            if (IsOldFile(pPath, m_currentLogFileIdx)) {
117                remove((HILOG_FILE_DIR + pPath).c_str());
118                break;
119            }
120        }
121    }
122    if (dir != nullptr) {
123        closedir(dir);
124    }
125}
126
127void LogPersisterRotator::Rotate()
128{
129    std::cout << __PRETTY_FUNCTION__ << "\n";
130    if (m_currentLogFileIdx + 1 >= m_maxLogFileNum) {
131        RemoveOldFile();
132    }
133    m_currentLogFileIdx++;
134    CreateLogFile();
135    UpdateRotateNumber();
136}
137
138void LogPersisterRotator::CreateLogFile()
139{
140    std::cout << __PRETTY_FUNCTION__ << "\n";
141    time_t tnow = time(nullptr);
142    struct tm *tmNow = localtime(&tnow);
143    char timeBuf[MAX_TIME_BUF_SIZE] = {0};
144    if (tmNow != nullptr) {
145        strftime(timeBuf, sizeof(timeBuf), "%Y%m%d-%H%M%S", tmNow);
146    }
147    std::stringstream newFile;
148    newFile << m_logsPath << "." << GetFileNameIndex(m_currentLogFileIdx) << "." << timeBuf << m_fileNameSuffix;
149    std::cout << "Filename: " << newFile.str() << std::endl;
150    m_currentLogFileName = newFile.str();
151    if (m_currentLogOutput.is_open()) {
152        m_currentLogOutput.close();
153    }
154    m_currentLogOutput.open(newFile.str(), std::ios::out | std::ios::trunc);
155}
156
157void LogPersisterRotator::UpdateRotateNumber()
158{
159    m_info.index = static_cast<uint32_t>(m_currentLogFileIdx);
160    WriteRecoveryInfo();
161}
162
163void LogPersisterRotator::FinishInput()
164{
165    std::cout << __PRETTY_FUNCTION__ << "\n";
166
167    m_currentLogOutput.close();
168    m_needRotate = true;
169}
170
171void LogPersisterRotator::SetFileIndex(uint32_t index, bool forceRotate)
172{
173    m_currentLogOutput.close();
174    m_currentLogFileIdx = index;
175    if (forceRotate) {
176        m_needRotate = true;
177    }
178}
179
180void LogPersisterRotator::WriteRecoveryInfo()
181{
182    if (!m_infoFile.is_open()) {
183        std::cerr << "LogPersisterRotator has not been initialized!\n";
184        return;
185    }
186
187    std::cout << "Save Info file!\n";
188    uint64_t hash = GenerateHash(reinterpret_cast<char *>(&m_info), sizeof(PersistRecoveryInfo));
189
190    m_infoFile.seekp(0);
191    m_infoFile.write(reinterpret_cast<const char*>(&m_info), sizeof(m_info));
192    m_infoFile.write(reinterpret_cast<const char*>(&hash), sizeof(hash));
193    m_infoFile.flush();
194    m_infoFile.sync();
195}
196} // namespace HiviewDFX
197} // namespace OHOS
198