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 #define LOG_TAG "RdbFaultHiViewReporter"
16
17 #include "rdb_fault_hiview_reporter.h"
18
19 #include <iomanip>
20 #include <sstream>
21
22 #include <chrono>
23 #include <fcntl.h>
24 #include <unistd.h>
25 #include <unordered_map>
26 #include <ctime>
27 #include <mutex>
28 #include "connection.h"
29 #include "hisysevent_c.h"
30 #include "logger.h"
31 #include "rdb_errno.h"
32 #include "sqlite_global_config.h"
33 #include "sqlite_utils.h"
34 #include "ipc_skeleton.h"
35 #include "accesstoken_kit.h"
36
37 namespace OHOS::NativeRdb {
38 using namespace OHOS::Rdb;
39 using namespace Security::AccessToken;
40 static constexpr const char *EVENT_NAME = "DATABASE_CORRUPTED";
41 static constexpr const char *DISTRIBUTED_DATAMGR = "DISTDATAMGR";
42 static constexpr const char *DB_CORRUPTED_POSTFIX = ".corruptedflg";
43 static constexpr int MAX_TIME_BUF_LEN = 32;
44 static constexpr int MILLISECONDS_LEN = 3;
45 static constexpr int NANO_TO_MILLI = 1000000;
46 static constexpr int MILLI_PRE_SEC = 1000;
47 Connection::Collector RdbFaultHiViewReporter::collector_ = nullptr;
48
ReportFault(const RdbCorruptedEvent & eventInfo)49 void RdbFaultHiViewReporter::ReportFault(const RdbCorruptedEvent &eventInfo)
50 {
51 if (IsReportCorruptedFault(eventInfo.path)) {
52 RdbCorruptedEvent eventInfoAppend = eventInfo;
53 eventInfoAppend.appendix += Format(eventInfoAppend.debugInfos, "");
54 LOG_WARN("corrupted %{public}s errCode:0x%{public}x [%{public}s]",
55 SqliteUtils::Anonymous(eventInfoAppend.storeName).c_str(),
56 eventInfoAppend.errorCode,
57 eventInfoAppend.appendix.c_str());
58 Report(eventInfoAppend);
59 CreateCorruptedFlag(eventInfo.path);
60 }
61 }
62
ReportRestore(const RdbCorruptedEvent & eventInfo,bool repair)63 void RdbFaultHiViewReporter::ReportRestore(const RdbCorruptedEvent &eventInfo, bool repair)
64 {
65 if (IsReportCorruptedFault(eventInfo.path) && repair) {
66 return;
67 }
68 RdbCorruptedEvent eventInfoAppend = eventInfo;
69 eventInfoAppend.appendix += Format(eventInfoAppend.debugInfos, "");
70 LOG_INFO("restored %{public}s errCode:0x%{public}x [%{public}s]",
71 SqliteUtils::Anonymous(eventInfo.storeName).c_str(), eventInfo.errorCode, eventInfoAppend.appendix.c_str());
72 Report(eventInfoAppend);
73 DeleteCorruptedFlag(eventInfo.path);
74 }
75
Report(const RdbCorruptedEvent & eventInfo)76 void RdbFaultHiViewReporter::Report(const RdbCorruptedEvent &eventInfo)
77 {
78 std::string bundleName = GetBundleName(eventInfo);
79 std::string moduleName = eventInfo.moduleName;
80 std::string storeType = eventInfo.storeType;
81 std::string storeName = eventInfo.storeName;
82 uint32_t checkType = eventInfo.integrityCheck;
83 std::string appendInfo = eventInfo.appendix;
84 std::string occurTime = GetTimeWithMilliseconds(eventInfo.errorOccurTime, 0);
85 char *errorOccurTime = occurTime.data();
86 HiSysEventParam params[] = {
87 { .name = "BUNDLE_NAME", .t = HISYSEVENT_STRING, .v = { .s = bundleName.data() }, .arraySize = 0 },
88 { .name = "MODULE_NAME", .t = HISYSEVENT_STRING, .v = { .s = moduleName.data() }, .arraySize = 0 },
89 { .name = "STORE_TYPE", .t = HISYSEVENT_STRING, .v = { .s = storeType.data() }, .arraySize = 0 },
90 { .name = "STORE_NAME", .t = HISYSEVENT_STRING, .v = { .s = storeName.data() }, .arraySize = 0 },
91 { .name = "SECURITY_LEVEL", .t = HISYSEVENT_UINT32, .v = { .ui32 = eventInfo.securityLevel }, .arraySize = 0 },
92 { .name = "PATH_AREA", .t = HISYSEVENT_UINT32, .v = { .ui32 = eventInfo.pathArea }, .arraySize = 0 },
93 { .name = "ENCRYPT_STATUS", .t = HISYSEVENT_UINT32, .v = { .ui32 = eventInfo.encryptStatus }, .arraySize = 0 },
94 { .name = "INTEGRITY_CHECK", .t = HISYSEVENT_UINT32, .v = { .ui32 = checkType }, .arraySize = 0 },
95 { .name = "ERROR_CODE", .t = HISYSEVENT_UINT32, .v = { .ui32 = eventInfo.errorCode }, .arraySize = 0 },
96 { .name = "ERRNO", .t = HISYSEVENT_INT32, .v = { .i32 = eventInfo.systemErrorNo }, .arraySize = 0 },
97 { .name = "APPENDIX", .t = HISYSEVENT_STRING, .v = { .s = appendInfo.data() }, .arraySize = 0 },
98 { .name = "ERROR_TIME", .t = HISYSEVENT_STRING, .v = { .s = errorOccurTime }, .arraySize = 0 },
99 };
100 OH_HiSysEvent_Write(DISTRIBUTED_DATAMGR, EVENT_NAME, HISYSEVENT_FAULT, params, sizeof(params) / sizeof(params[0]));
101 }
102
GetFileStatInfo(const DebugInfo & debugInfo)103 std::string RdbFaultHiViewReporter::GetFileStatInfo(const DebugInfo &debugInfo)
104 {
105 std::stringstream oss;
106 const uint32_t permission = 0777;
107 oss << " dev:0x" << std::hex << debugInfo.dev_ << " ino:0x" << std::hex << debugInfo.inode_;
108 if (debugInfo.inode_ != debugInfo.oldInode_ && debugInfo.oldInode_ != 0) {
109 oss << "<>0x" << std::hex << debugInfo.oldInode_;
110 }
111 oss << " mode:0" << std::oct << (debugInfo.mode_ & permission) << " size:" << std::dec << debugInfo.size_
112 << " atim:" << GetTimeWithMilliseconds(debugInfo.atime_.sec_, debugInfo.atime_.nsec_)
113 << " mtim:" << GetTimeWithMilliseconds(debugInfo.mtime_.sec_, debugInfo.mtime_.nsec_)
114 << " ctim:" << GetTimeWithMilliseconds(debugInfo.ctime_.sec_, debugInfo.ctime_.nsec_);
115 return oss.str();
116 }
117
IsReportCorruptedFault(const std::string & dbPath)118 bool RdbFaultHiViewReporter::IsReportCorruptedFault(const std::string &dbPath)
119 {
120 if (dbPath.empty()) {
121 return false;
122 }
123
124 std::string flagFilename = dbPath + DB_CORRUPTED_POSTFIX;
125 if (access(flagFilename.c_str(), F_OK) == 0) {
126 return false;
127 }
128 return true;
129 }
130
CreateCorruptedFlag(const std::string & dbPath)131 void RdbFaultHiViewReporter::CreateCorruptedFlag(const std::string &dbPath)
132 {
133 if (dbPath.empty()) {
134 return;
135 }
136 std::string flagFilename = dbPath + DB_CORRUPTED_POSTFIX;
137 int fd = creat(flagFilename.c_str(), S_IRWXU | S_IRWXG);
138 if (fd == -1) {
139 LOG_WARN("creat corrupted flg fail, flgname=%{public}s, errno=%{public}d",
140 SqliteUtils::Anonymous(flagFilename).c_str(), errno);
141 return;
142 }
143 close(fd);
144 }
145
DeleteCorruptedFlag(const std::string & dbPath)146 void RdbFaultHiViewReporter::DeleteCorruptedFlag(const std::string &dbPath)
147 {
148 if (dbPath.empty()) {
149 return;
150 }
151 std::string flagFilename = dbPath + DB_CORRUPTED_POSTFIX;
152 int result = remove(flagFilename.c_str());
153 if (result != 0) {
154 LOG_WARN("remove corrupted flg fail, flgname=%{public}s, errno=%{public}d",
155 SqliteUtils::Anonymous(flagFilename).c_str(), errno);
156 }
157 }
158
GetTimeWithMilliseconds(time_t sec,int64_t nsec)159 std::string RdbFaultHiViewReporter::GetTimeWithMilliseconds(time_t sec, int64_t nsec)
160 {
161 std::stringstream oss;
162 char buffer[MAX_TIME_BUF_LEN] = { 0 };
163 std::tm local_time;
164 localtime_r(&sec, &local_time);
165 std::strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", &local_time);
166 oss << buffer << '.' << std::setfill('0') << std::setw(MILLISECONDS_LEN) << (nsec / NANO_TO_MILLI) % MILLI_PRE_SEC;
167 return oss.str();
168 }
169
Create(const RdbStoreConfig & config,int32_t errCode,const std::string & appendix)170 RdbCorruptedEvent RdbFaultHiViewReporter::Create(const RdbStoreConfig &config, int32_t errCode,
171 const std::string &appendix)
172 {
173 RdbCorruptedEvent eventInfo;
174 eventInfo.bundleName = config.GetBundleName();
175 eventInfo.moduleName = config.GetModuleName();
176 eventInfo.storeType = config.GetDBType() == DB_SQLITE ? "RDB" : "VECTOR DB";
177 eventInfo.storeName = config.GetName();
178 eventInfo.securityLevel = static_cast<uint32_t>(config.GetSecurityLevel());
179 eventInfo.pathArea = static_cast<uint32_t>(config.GetArea());
180 eventInfo.encryptStatus = static_cast<uint32_t>(config.IsEncrypt());
181 eventInfo.integrityCheck = static_cast<uint32_t>(config.GetIntegrityCheck());
182 eventInfo.errorCode = static_cast<uint32_t>(errCode);
183 eventInfo.systemErrorNo = errCode == E_OK ? 0 : errno;
184 eventInfo.appendix = appendix;
185 eventInfo.errorOccurTime = time(nullptr);
186 eventInfo.debugInfos = Connection::Collect(config);
187 SqliteGlobalConfig::GetDbPath(config, eventInfo.path);
188 if (collector_ != nullptr) {
189 Update(eventInfo, collector_(config));
190 }
191 return eventInfo;
192 }
193
RegCollector(Connection::Collector collector)194 bool RdbFaultHiViewReporter::RegCollector(Connection::Collector collector)
195 {
196 if (collector_ != nullptr) {
197 return false;
198 }
199 collector_ = collector;
200 return true;
201 }
202
Update(RdbCorruptedEvent & eventInfo,const std::map<std::string,DebugInfo> & infos)203 void RdbFaultHiViewReporter::Update(RdbCorruptedEvent &eventInfo, const std::map<std::string, DebugInfo> &infos)
204 {
205 auto &local = eventInfo.debugInfos;
206 auto lIt = local.begin();
207 auto rIt = infos.begin();
208 for (; lIt != local.end() && rIt != infos.end();) {
209 if (lIt->first == rIt->first) {
210 if (lIt->second.inode_ != rIt->second.inode_) {
211 lIt->second.oldInode_ = rIt->second.inode_;
212 }
213 ++lIt;
214 ++rIt;
215 continue;
216 }
217 if (lIt->first < rIt->first) {
218 ++lIt;
219 } else {
220 ++rIt;
221 }
222 }
223 }
224
GetBundleName(const RdbCorruptedEvent & eventInfo)225 std::string RdbFaultHiViewReporter::GetBundleName(const RdbCorruptedEvent &eventInfo)
226 {
227 if (!eventInfo.bundleName.empty()) {
228 return eventInfo.bundleName;
229 }
230 auto tokenId = IPCSkeleton::GetCallingTokenID();
231 auto tokenType = AccessTokenKit::GetTokenTypeFlag(tokenId);
232 if ((tokenType == TOKEN_NATIVE) || (tokenType == TOKEN_SHELL)) {
233 NativeTokenInfo tokenInfo;
234 if (AccessTokenKit::GetNativeTokenInfo(tokenId, tokenInfo) == 0) {
235 return tokenInfo.processName;
236 }
237 }
238 return SqliteUtils::Anonymous(eventInfo.storeName);
239 }
240
Format(const std::map<std::string,DebugInfo> & debugs,const std::string & header)241 std::string RdbFaultHiViewReporter::Format(const std::map<std::string, DebugInfo> &debugs, const std::string &header)
242 {
243 if (debugs.empty()) {
244 return "";
245 }
246 std::string appendix = header;
247 for (auto &[name, debugInfo] : debugs) {
248 appendix += "\n" + name + " :" + GetFileStatInfo(debugInfo);
249 }
250 return appendix;
251 }
252
FormatBrief(const std::map<std::string,DebugInfo> & debugs,const std::string & header)253 std::string RdbFaultHiViewReporter::FormatBrief(const std::map<std::string, DebugInfo> &debugs,
254 const std::string &header)
255 {
256 if (debugs.empty()) {
257 return "";
258 }
259 std::stringstream oss;
260 oss << header << ":";
261 for (auto &[name, debugInfo] : debugs) {
262 oss << "<" << name << ",0x" << std::hex << debugInfo.inode_ << "," << std::dec << debugInfo.size_ << ">";
263 }
264 return oss.str();
265 }
266 } // namespace OHOS::NativeRdb