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