1 /*
2  * Copyright (c) 2024 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 "hi_audit.h"
17 
18 #include <chrono>
19 #include <ctime>
20 #include <dirent.h>
21 #include <fcntl.h>
22 #include <iomanip>
23 #include <sstream>
24 #include <sys/time.h>
25 #include <unistd.h>
26 
27 #include "media_log.h"
28 #include "zip_util.h"
29 
30 namespace OHOS::Media {
31 struct HiAuditConfig {
32     std::string logPath;
33     std::string logName;
34     uint32_t logSize;
35     uint32_t fileSize;
36     uint32_t fileCount;
37 };
38 
39 const HiAuditConfig HIAUDIT_CONFIG = { "/data/storage/el2/log/audit/", "media_library", 2 * 1024, 3 * 1204 * 1024, 10 };
40 constexpr int8_t MILLISECONDS_LENGTH = 3;
41 constexpr int64_t SEC_TO_MILLISEC = 1000;
42 constexpr int MAX_TIME_BUFF = 64;
43 const std::string HIAUDIT_LOG_NAME = HIAUDIT_CONFIG.logPath + HIAUDIT_CONFIG.logName + "_audit.csv";
44 
HiAudit()45 HiAudit::HiAudit()
46 {
47     Init();
48 }
49 
~HiAudit()50 HiAudit::~HiAudit()
51 {
52     if (writeFd_ >= 0) {
53         close(writeFd_);
54     }
55 }
56 
GetInstance()57 HiAudit& HiAudit::GetInstance()
58 {
59     static HiAudit hiAudit;
60     return hiAudit;
61 }
62 
Init()63 void HiAudit::Init()
64 {
65     if (access(HIAUDIT_CONFIG.logPath.c_str(), F_OK) != 0) {
66         int ret = mkdir(HIAUDIT_CONFIG.logPath.c_str(), S_IRWXU | S_IRWXG | S_IRWXO);
67         if (ret != 0) {
68             MEDIA_ERR_LOG("Failed to create directory %{public}s.", HIAUDIT_CONFIG.logPath.c_str());
69         }
70     }
71     std::lock_guard<std::mutex> lock(mutex_);
72     writeFd_ = open(HIAUDIT_LOG_NAME.c_str(), O_CREAT | O_APPEND | O_RDWR,
73         S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
74     if (writeFd_ < 0) {
75         MEDIA_ERR_LOG("writeFd_ open error errno: %{public}d", errno);
76     }
77     struct stat st;
78     writeLogSize_ = stat(HIAUDIT_LOG_NAME.c_str(), &st) ? 0 : static_cast<uint64_t>(st.st_size);
79     MEDIA_INFO_LOG("writeLogSize: %{public}u", writeLogSize_.load());
80 }
81 
GetMilliseconds()82 uint64_t HiAudit::GetMilliseconds()
83 {
84     auto now = std::chrono::system_clock::now();
85     auto millisecs = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch());
86     return millisecs.count();
87 }
88 
GetFormattedTimestamp(time_t timeStamp,const std::string & format)89 std::string HiAudit::GetFormattedTimestamp(time_t timeStamp, const std::string& format)
90 {
91     auto seconds = timeStamp / SEC_TO_MILLISEC;
92     char date[MAX_TIME_BUFF] = {0};
93     struct tm result {};
94     if (localtime_r(&seconds, &result) != nullptr) {
95         strftime(date, MAX_TIME_BUFF, format.c_str(), &result);
96     }
97     return std::string(date);
98 }
99 
GetFormattedTimestampEndWithMilli()100 std::string HiAudit::GetFormattedTimestampEndWithMilli()
101 {
102     uint64_t milliSeconds = GetMilliseconds();
103     std::string formattedTimeStamp = GetFormattedTimestamp(milliSeconds, "%Y%m%d%H%M%S");
104     std::stringstream ss;
105     ss << formattedTimeStamp;
106     milliSeconds = milliSeconds % SEC_TO_MILLISEC;
107     ss << std::setfill('0') << std::setw(MILLISECONDS_LENGTH) << milliSeconds;
108     return ss.str();
109 }
110 
Write(const AuditLog & auditLog)111 void HiAudit::Write(const AuditLog& auditLog)
112 {
113     MEDIA_INFO_LOG("write");
114     std::lock_guard<std::mutex> lock(mutex_);
115     if (writeLogSize_ == 0) {
116         WriteToFile(auditLog.TitleString() + "\n");
117     }
118     std::string writeLog = GetFormattedTimestampEndWithMilli() + ", " +
119         HIAUDIT_CONFIG.logName + ", NO, " + auditLog.ToString();
120     MEDIA_INFO_LOG("write %{public}s.", writeLog.c_str());
121     if (writeLog.length() > HIAUDIT_CONFIG.logSize) {
122         writeLog = writeLog.substr(0, HIAUDIT_CONFIG.logSize);
123     }
124     writeLog = writeLog + "\n";
125     WriteToFile(writeLog);
126 }
127 
GetWriteFilePath()128 void HiAudit::GetWriteFilePath()
129 {
130     if (writeLogSize_ < HIAUDIT_CONFIG.fileSize) {
131         return;
132     }
133 
134     close(writeFd_);
135     ZipAuditLog();
136     CleanOldAuditFile();
137 
138     writeFd_ = open(HIAUDIT_LOG_NAME.c_str(), O_CREAT | O_TRUNC | O_RDWR,
139         S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
140     if (writeFd_ < 0) {
141         MEDIA_ERR_LOG("fd open error errno: %{public}d", errno);
142     }
143 
144     writeLogSize_ = 0;
145 }
146 
CleanOldAuditFile()147 void HiAudit::CleanOldAuditFile()
148 {
149     uint32_t zipFileSize = 0;
150     std::string oldestAuditFile;
151     DIR* dir = opendir(HIAUDIT_CONFIG.logPath.c_str());
152     while (true) {
153         struct dirent* ptr = readdir(dir);
154         if (ptr == nullptr) {
155             break;
156         }
157         if (std::string(ptr->d_name).find(HIAUDIT_CONFIG.logName) != std::string::npos &&
158             std::string(ptr->d_name).find("zip") != std::string::npos) {
159             zipFileSize = zipFileSize + 1;
160             if (oldestAuditFile.empty()) {
161                 oldestAuditFile = HIAUDIT_CONFIG.logPath + std::string(ptr->d_name);
162                 continue;
163             }
164             struct stat st;
165             stat((HIAUDIT_CONFIG.logPath + std::string(ptr->d_name)).c_str(), &st);
166             struct stat oldestSt;
167             stat(oldestAuditFile.c_str(), &oldestSt);
168             if (st.st_mtime < oldestSt.st_mtime) {
169                 oldestAuditFile = HIAUDIT_CONFIG.logPath + std::string(ptr->d_name);
170             }
171         }
172     }
173     closedir(dir);
174     if (zipFileSize > HIAUDIT_CONFIG.fileCount) {
175         remove(oldestAuditFile.c_str());
176     }
177 }
178 
WriteToFile(const std::string & content)179 void HiAudit::WriteToFile(const std::string& content)
180 {
181     GetWriteFilePath();
182     if (writeFd_ < 0) {
183         MEDIA_ERR_LOG("fd invalid.");
184         return;
185     }
186     write(writeFd_, content.c_str(), content.length());
187     writeLogSize_ = writeLogSize_ + content.length();
188 }
189 
ZipAuditLog()190 void HiAudit::ZipAuditLog()
191 {
192     std::string zipFileName = HIAUDIT_CONFIG.logPath + HIAUDIT_CONFIG.logName + "_audit_" +
193         GetFormattedTimestamp(GetMilliseconds(), "%Y%m%d%H%M%S");
194     std::rename(HIAUDIT_LOG_NAME.c_str(), (zipFileName + ".csv").c_str());
195     zipFile compressZip = Media::ZipUtil::CreateZipFile(zipFileName + ".zip");
196     if (compressZip == nullptr) {
197         MEDIA_WARN_LOG("open zip file failed.");
198         return;
199     }
200     if (Media::ZipUtil::AddFileInZip(compressZip, zipFileName + ".csv", Media::KEEP_NONE_PARENT_PATH) == 0) {
201         remove((zipFileName + ".csv").c_str());
202     }
203     Media::ZipUtil::CloseZipFile(compressZip);
204 }
205 }