1 /*
2  * Copyright (c) 2021-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 "util.h"
17 
18 #include <array>
19 #include <chrono>
20 #include <cinttypes>
21 #include <cstdarg>
22 #include <fstream>
23 #include <iostream>
24 
25 #include <sys/prctl.h>
26 #include <sys/stat.h>
27 #include <sys/syscall.h>
28 #include <sys/time.h>
29 #include <sys/types.h>
30 #include <unistd.h>
31 
32 #include "aggregator.h"
33 #include "config_multimodal.h"
34 #include "define_multimodal.h"
35 #include "error_multimodal.h"
36 #include "mmi_log.h"
37 #include "securec.h"
38 
39 #undef MMI_LOG_TAG
40 #define MMI_LOG_TAG "Util"
41 
42 namespace OHOS {
43 namespace MMI {
44 namespace {
45 constexpr int32_t FILE_SIZE_MAX { 0x6C445 };
46 constexpr int32_t MAX_PRO_FILE_SIZE { 128000 };
47 constexpr int32_t INVALID_FILE_SIZE { -1 };
48 constexpr int32_t MIN_INTERVALTIME { 36 };
49 constexpr int32_t MAX_INTERVALTIME { 100 };
50 constexpr int32_t MIN_DELAYTIME { 300 };
51 constexpr int32_t MAX_DELAYTIME { 1000 };
52 constexpr int32_t COMMENT_SUBSCRIPT { 0 };
53 const std::string CONFIG_ITEM_REPEAT = "Key.autorepeat";
54 const std::string CONFIG_ITEM_DELAY = "Key.autorepeat.delaytime";
55 const std::string CONFIG_ITEM_INTERVAL = "Key.autorepeat.intervaltime";
56 const std::string CONFIG_ITEM_TYPE = "Key.keyboard.type";
57 const std::string CURSORSTYLE_PATH = "/system/etc/multimodalinput/mouse_icon/";
58 const std::string DATA_PATH = "/data";
59 const std::string INPUT_PATH = "/system/";
60 const std::string KEY_PATH = "/vendor/etc/keymap/";
61 constexpr size_t BUF_TID_SIZE { 10 };
62 constexpr size_t BUF_CMD_SIZE { 512 };
63 constexpr size_t PROGRAM_NAME_SIZE { 256 };
64 constexpr int32_t TIME_CONVERSION_UNIT { 1000 };
65 } // namespace
66 
GetSysClockTime()67 int64_t GetSysClockTime()
68 {
69     struct timespec ts = { 0, 0 };
70     if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) {
71         MMI_HILOGD("clock_gettime failed:%{public}d", errno);
72         return 0;
73     }
74     return (ts.tv_sec * TIME_CONVERSION_UNIT * TIME_CONVERSION_UNIT) + (ts.tv_nsec / TIME_CONVERSION_UNIT);
75 }
76 
GetMillisTime()77 int64_t GetMillisTime()
78 {
79     auto timeNow = std::chrono::time_point_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now());
80     auto tmp = std::chrono::duration_cast<std::chrono::milliseconds>(timeNow.time_since_epoch());
81     return tmp.count();
82 }
83 
GetThisThreadIdOfString()84 static std::string GetThisThreadIdOfString()
85 {
86     thread_local std::string threadLocalId;
87     if (threadLocalId.empty()) {
88         long tid = syscall(SYS_gettid);
89         char buf[BUF_TID_SIZE] = {};
90         const int32_t ret = sprintf_s(buf, BUF_TID_SIZE, "%06d", tid);
91         if (ret < 0) {
92             printf("ERR: in %s, #%d, call sprintf_s failed, ret = %d", __func__, __LINE__, ret);
93             return threadLocalId;
94         }
95         buf[BUF_TID_SIZE - 1] = '\0';
96         threadLocalId = buf;
97     }
98 
99     return threadLocalId;
100 }
101 
GetThisThreadId()102 uint64_t GetThisThreadId()
103 {
104     std::string stid = GetThisThreadIdOfString();
105     auto tid = std::atoll(stid.c_str());
106     return tid;
107 }
108 
StringToken(std::string & str,const std::string & sep,std::string & token)109 static size_t StringToken(std::string &str, const std::string &sep, std::string &token)
110 {
111     token = "";
112     if (str.empty()) {
113         return str.npos;
114     }
115     size_t pos = str.npos;
116     size_t tmp = 0;
117     for (auto &item : sep) {
118         tmp = str.find(item);
119         if (str.npos != tmp) {
120             pos = (std::min)(pos, tmp);
121         }
122     }
123     if (str.npos != pos) {
124         token = str.substr(0, pos);
125         if (str.npos != pos + 1) {
126             str = str.substr(pos + 1, str.npos);
127         }
128         if (pos == 0) {
129             return StringToken(str, sep, token);
130         }
131     } else {
132         token = str;
133         str = "";
134     }
135     return token.size();
136 }
137 
StringSplit(const std::string & str,const std::string & sep,std::vector<std::string> & vecList)138 size_t StringSplit(const std::string &str, const std::string &sep, std::vector<std::string> &vecList)
139 {
140     size_t size;
141     auto strs = str;
142     std::string token;
143     while (str.npos != (size = StringToken(strs, sep, token))) {
144         vecList.push_back(token);
145     }
146     return vecList.size();
147 }
148 
IdsListToString(const std::vector<int32_t> & list,const std::string & sep)149 std::string IdsListToString(const std::vector<int32_t> &list, const std::string &sep)
150 {
151     std::string str;
152     for (const auto &it : list) {
153         str += std::to_string(it) + sep;
154     }
155     if (str.size() > 0) {
156         str.resize(str.size() - sep.size());
157     }
158     return str;
159 }
160 
GetPid()161 int32_t GetPid()
162 {
163     return static_cast<int32_t>(getpid());
164 }
165 
GetFileName(const std::string & strPath)166 static std::string GetFileName(const std::string &strPath)
167 {
168     size_t nPos = strPath.find_last_of('/');
169     if (strPath.npos == nPos) {
170         nPos = strPath.find_last_of('\\');
171     }
172     if (strPath.npos == nPos) {
173         return strPath;
174     }
175 
176     return strPath.substr(nPos + 1, strPath.npos);
177 }
178 
GetProgramName()179 const char *GetProgramName()
180 {
181     static char programName[PROGRAM_NAME_SIZE] = {};
182     if (programName[0] != '\0') {
183         return programName;
184     }
185 
186     char buf[BUF_CMD_SIZE] = { 0 };
187     if (sprintf_s(buf, BUF_CMD_SIZE, "/proc/%d/cmdline", static_cast<int32_t>(getpid())) == -1) {
188         KMSG_LOGE("GetProcessInfo sprintf_s /proc/.../cmdline error");
189         return "";
190     }
191     FILE *fp = fopen(buf, "rb");
192     CHKPS(fp);
193     static constexpr size_t bufLineSize = 512;
194     char bufLine[bufLineSize] = { 0 };
195     if ((fgets(bufLine, bufLineSize, fp) == nullptr)) {
196         KMSG_LOGE("fgets failed");
197         if (fclose(fp) != 0) {
198             KMSG_LOGW("Close file:%s failed", buf);
199         }
200         fp = nullptr;
201         return "";
202     }
203     if (fclose(fp) != 0) {
204         KMSG_LOGW("Close file:%s failed", buf);
205     }
206     fp = nullptr;
207 
208     std::string tempName(bufLine);
209     tempName = GetFileName(tempName);
210     if (tempName.empty()) {
211         KMSG_LOGE("tempName is empty");
212         return "";
213     }
214     const size_t copySize = std::min(tempName.size(), PROGRAM_NAME_SIZE - 1);
215     if (copySize == 0) {
216         KMSG_LOGE("The copySize is 0");
217         return "";
218     }
219     errno_t ret = memcpy_s(programName, PROGRAM_NAME_SIZE, tempName.c_str(), copySize);
220     if (ret != EOK) {
221         return "";
222     }
223     KMSG_LOGI("GetProgramName success. programName = %s", programName);
224 
225     return programName;
226 }
227 
SetThreadName(const std::string & name)228 void SetThreadName(const std::string &name)
229 {
230     prctl(PR_SET_NAME, name.c_str());
231 }
232 
IsFileExists(const std::string & fileName)233 static bool IsFileExists(const std::string &fileName)
234 {
235     return (access(fileName.c_str(), F_OK) == 0);
236 }
237 
CheckFileExtendName(const std::string & filePath,const std::string & checkExtension)238 static bool CheckFileExtendName(const std::string &filePath, const std::string &checkExtension)
239 {
240     std::string::size_type pos = filePath.find_last_of('.');
241     if (pos == std::string::npos) {
242         MMI_HILOGE("File is not find extension");
243         return false;
244     }
245     return (filePath.substr(pos + 1, filePath.npos) == checkExtension);
246 }
247 
GetFileSize(const std::string & filePath)248 static int32_t GetFileSize(const std::string &filePath)
249 {
250     struct stat statbuf = { 0 };
251     if (stat(filePath.c_str(), &statbuf) != 0) {
252         MMI_HILOGE("Get file size error");
253         return INVALID_FILE_SIZE;
254     }
255     return statbuf.st_size;
256 }
257 
ReadFile(const std::string & filePath)258 static std::string ReadFile(const std::string &filePath)
259 {
260     FILE *fp = fopen(filePath.c_str(), "r");
261     CHKPS(fp);
262     std::string dataStr;
263     char buf[256] = {};
264     while (fgets(buf, sizeof(buf), fp) != nullptr) {
265         dataStr += buf;
266     }
267     if (fclose(fp) != 0) {
268         MMI_HILOGW("Close file failed");
269     }
270     return dataStr;
271 }
272 
IsValidPath(const std::string & rootDir,const std::string & filePath)273 static bool IsValidPath(const std::string &rootDir, const std::string &filePath)
274 {
275     return (filePath.compare(0, rootDir.size(), rootDir) == 0);
276 }
277 
IsValidJsonPath(const std::string & filePath)278 bool IsValidJsonPath(const std::string &filePath)
279 {
280     return IsValidPath(DATA_PATH, filePath) || IsValidPath(INPUT_PATH, filePath);
281 }
282 
IsValidProPath(const std::string & filePath)283 static bool IsValidProPath(const std::string &filePath)
284 {
285     return IsValidPath(KEY_PATH, filePath);
286 }
287 
IsValidTomlPath(const std::string & filePath)288 static bool IsValidTomlPath(const std::string &filePath)
289 {
290     return IsValidPath(KEY_PATH, filePath);
291 }
292 
ReadProFile(const std::string & filePath,int32_t deviceId,std::map<int32_t,std::map<int32_t,int32_t>> & configMap)293 void ReadProFile(const std::string &filePath, int32_t deviceId,
294     std::map<int32_t, std::map<int32_t, int32_t>> &configMap)
295 {
296     CALL_DEBUG_ENTER;
297     if (filePath.empty()) {
298         MMI_HILOGE("FilePath is empty");
299         return;
300     }
301     char realPath[PATH_MAX] = {};
302     CHKPV(realpath(filePath.c_str(), realPath));
303     if (!IsValidProPath(realPath)) {
304         MMI_HILOGE("File path is error");
305         return;
306     }
307     if (!IsFileExists(realPath)) {
308         MMI_HILOGE("File is not existent");
309         return;
310     }
311     if (!CheckFileExtendName(realPath, "pro")) {
312         MMI_HILOGE("Unable to parse files other than json format");
313         return;
314     }
315     auto fileSize = GetFileSize(realPath);
316     if ((fileSize == INVALID_FILE_SIZE) || (fileSize >= MAX_PRO_FILE_SIZE)) {
317         MMI_HILOGE("The configuration file size is incorrect");
318         return;
319     }
320     ReadProConfigFile(realPath, deviceId, configMap);
321 }
322 
IsNum(const std::string & str)323 static inline bool IsNum(const std::string &str)
324 {
325     std::istringstream sin(str);
326     double num;
327     return (sin >> num) && sin.eof();
328 }
329 
ReadProConfigFile(const std::string & realPath,int32_t deviceId,std::map<int32_t,std::map<int32_t,int32_t>> & configKey)330 void ReadProConfigFile(const std::string &realPath, int32_t deviceId,
331     std::map<int32_t, std::map<int32_t, int32_t>> &configKey)
332 {
333     CALL_DEBUG_ENTER;
334     std::ifstream reader(realPath);
335     if (!reader.is_open()) {
336         MMI_HILOGE("Failed to open config file");
337         return;
338     }
339     std::string strLine;
340     int32_t sysKeyValue;
341     int32_t nativeKeyValue;
342     int32_t elementKey = 0;
343     int32_t elementValue = 0;
344     std::map<int32_t, int32_t> tmpConfigKey;
345     while (std::getline(reader, strLine)) {
346         const char* line = strLine.c_str();
347         int32_t len = strlen(line);
348         char* realLine = static_cast<char*>(malloc(len + 1));
349         CHKPV(realLine);
350         if (strcpy_s(realLine, len + 1, line) != EOK) {
351             MMI_HILOGE("strcpy_s error");
352             free(realLine);
353             realLine = nullptr;
354             return;
355         }
356         *(realLine + len + 1) = '\0';
357         int32_t ret = ReadConfigInfo(realLine, len, &elementKey, &elementValue);
358         free(realLine);
359         realLine = nullptr;
360         if (ret != RET_OK) {
361             MMI_HILOGE("Failed to read from line of config info");
362             reader.close();
363             return;
364         }
365         nativeKeyValue = elementKey;
366         sysKeyValue = elementValue;
367         MMI_HILOGD("The nativeKeyValue is:%{public}d, sysKeyValue is:%{public}d", nativeKeyValue, sysKeyValue);
368         tmpConfigKey.insert(std::pair<int32_t, int32_t>(nativeKeyValue, sysKeyValue));
369     }
370     reader.close();
371     auto iter = configKey.insert(std::make_pair(deviceId, tmpConfigKey));
372     if (!iter.second) {
373         MMI_HILOGE("The file name is duplicated");
374         return;
375     }
376 }
377 
ReadJsonFile(const std::string & filePath)378 std::string ReadJsonFile(const std::string &filePath)
379 {
380     if (filePath.empty()) {
381         MMI_HILOGE("FilePath is empty");
382         return "";
383     }
384     char realPath[PATH_MAX] = {};
385     CHKPS(realpath(filePath.c_str(), realPath));
386     if (!IsValidJsonPath(realPath)) {
387         MMI_HILOGE("File path is error");
388         return "";
389     }
390     if (!CheckFileExtendName(realPath, "json")) {
391         MMI_HILOGE("Unable to parse files other than json format");
392         return "";
393     }
394     if (!IsFileExists(realPath)) {
395         MMI_HILOGE("File is not existent");
396         return "";
397     }
398     int32_t fileSize = GetFileSize(realPath);
399     if ((fileSize <= 0) || (fileSize > FILE_SIZE_MAX)) {
400         MMI_HILOGE("File size out of read range");
401         return "";
402     }
403     return ReadFile(filePath);
404 }
405 
ConfigItemSwitch(const std::string & configItem,const std::string & value,DeviceConfig & devConf)406 static int32_t ConfigItemSwitch(const std::string &configItem, const std::string &value, DeviceConfig &devConf)
407 {
408     CALL_DEBUG_ENTER;
409     if (configItem.empty() || value.empty()) {
410         MMI_HILOGE("Get key config item is invalid");
411         return RET_ERR;
412     }
413     if (!IsNum(value)) {
414         MMI_HILOGE("Get key config item is invalid");
415         return RET_ERR;
416     }
417     if (configItem == CONFIG_ITEM_REPEAT) {
418         devConf.autoSwitch = stoi(value);
419     } else if (configItem == CONFIG_ITEM_DELAY) {
420         devConf.delayTime = stoi(value);
421         if (devConf.delayTime < MIN_DELAYTIME || devConf.delayTime > MAX_DELAYTIME) {
422             MMI_HILOGE("Unusual the delaytime");
423             return RET_ERR;
424         }
425     } else if (configItem == CONFIG_ITEM_INTERVAL) {
426         devConf.intervalTime = stoi(value);
427         if (devConf.intervalTime < MIN_INTERVALTIME || devConf.intervalTime > MAX_INTERVALTIME) {
428             MMI_HILOGE("Unusual the intervaltime");
429             return RET_ERR;
430         }
431     } else if (configItem == CONFIG_ITEM_TYPE) {
432         devConf.keyboardType = stoi(value);
433     }
434     return RET_OK;
435 }
436 
ReadConfigFile(const std::string & realPath,DeviceConfig & devConf)437 static int32_t ReadConfigFile(const std::string &realPath, DeviceConfig &devConf)
438 {
439     CALL_DEBUG_ENTER;
440     std::ifstream cfgFile(realPath);
441     if (!cfgFile.is_open()) {
442         MMI_HILOGE("Failed to open config file");
443         return FILE_OPEN_FAIL;
444     }
445     std::string tmp;
446     while (std::getline(cfgFile, tmp)) {
447         RemoveSpace(tmp);
448         size_t pos = tmp.find('#');
449         if (pos != tmp.npos && pos != COMMENT_SUBSCRIPT) {
450             MMI_HILOGE("File format is error");
451             cfgFile.close();
452             return RET_ERR;
453         }
454         if (tmp.empty() || tmp.front() == '#') {
455             continue;
456         }
457         pos = tmp.find('=');
458         if ((pos == std::string::npos) || (tmp.back() == '=')) {
459             MMI_HILOGE("Find config item error");
460             cfgFile.close();
461             return RET_ERR;
462         }
463         std::string configItem = tmp.substr(0, pos);
464         std::string value = tmp.substr(pos + 1);
465         if (ConfigItemSwitch(configItem, value, devConf) == RET_ERR) {
466             MMI_HILOGE("Configuration item error");
467             cfgFile.close();
468             return RET_ERR;
469         }
470     }
471     cfgFile.close();
472     return RET_OK;
473 }
474 
ReadTomlFile(const std::string & filePath,DeviceConfig & devConf)475 int32_t ReadTomlFile(const std::string &filePath, DeviceConfig &devConf)
476 {
477     if (filePath.empty()) {
478         MMI_HILOGE("FilePath is empty");
479         return RET_ERR;
480     }
481     char realPath[PATH_MAX] = {};
482     CHKPR(realpath(filePath.c_str(), realPath), RET_ERR);
483     if (!IsValidTomlPath(realPath)) {
484         MMI_HILOGE("File path is error");
485         return RET_ERR;
486     }
487     if (!IsFileExists(realPath)) {
488         MMI_HILOGE("File is not existent");
489         return RET_ERR;
490     }
491     if (!CheckFileExtendName(realPath, "TOML")) {
492         MMI_HILOGE("Unable to parse files other than json format");
493         return RET_ERR;
494     }
495     int32_t fileSize = GetFileSize(realPath);
496     if ((fileSize <= 0) || (fileSize > FILE_SIZE_MAX)) {
497         MMI_HILOGE("File size out of read range");
498         return RET_ERR;
499     }
500     if (ReadConfigFile(realPath, devConf) == RET_ERR) {
501         MMI_HILOGE("Read device config file failed");
502         return RET_ERR;
503     }
504     return RET_OK;
505 }
506 
ReadCursorStyleFile(const std::string & filePath)507 int32_t ReadCursorStyleFile(const std::string &filePath)
508 {
509     CALL_DEBUG_ENTER;
510     if (filePath.empty()) {
511         MMI_HILOGE("FilePath is empty");
512         return RET_ERR;
513     }
514     if (!IsFileExists(filePath)) {
515         MMI_HILOGE("File is not existent");
516         return RET_ERR;
517     }
518     char realPath[PATH_MAX] = {};
519     CHKPR(realpath(filePath.c_str(), realPath), RET_ERR);
520     int32_t fileSize = GetFileSize(realPath);
521     if ((fileSize <= 0) || (fileSize > FILE_SIZE_MAX)) {
522         MMI_HILOGE("File size out of read range");
523         return RET_ERR;
524     }
525     return RET_OK;
526 }
527 
StringPrintf(const char * format,...)528 std::string StringPrintf(const char *format, ...)
529 {
530     char space[1024];
531 
532     va_list ap;
533     va_start(ap, format);
534     std::string result;
535     int32_t ret = vsnprintf_s(space, sizeof(space), sizeof(space) - 1, format, ap);
536     if (ret >= RET_OK && (size_t)ret < sizeof(space)) {
537         result = space;
538     } else {
539         MMI_HILOGE("The buffer is overflow");
540     }
541     va_end(ap);
542     return result;
543 }
544 
FileVerification(std::string & filePath,const std::string & checkExtension)545 std::string FileVerification(std::string &filePath, const std::string &checkExtension)
546 {
547     if (filePath.empty()) {
548         MMI_HILOGE("FilePath is empty");
549         return "";
550     }
551     char realPath[PATH_MAX] = {};
552     CHKPS(realpath(filePath.c_str(), realPath));
553     if (!IsFileExists(realPath)) {
554         MMI_HILOGE("File is not existent");
555         return "";
556     }
557     if (!CheckFileExtendName(realPath, checkExtension)) {
558         MMI_HILOGE("Unable to parse files other than json format");
559         return "";
560     }
561     int32_t fileSize = GetFileSize(realPath);
562     if ((fileSize <= 0) || (fileSize > FILE_SIZE_MAX)) {
563         MMI_HILOGE("File size out of read range");
564         return "";
565     }
566     return realPath;
567 }
568 
569 
Record(const LogHeader & lh,const std::string & key,const std::string & record)570 bool Aggregator::Record(const LogHeader &lh, const std::string &key, const std::string &record)
571 {
572     constexpr int32_t oneSecond = 1000;
573     if (timerId_ != -1) {
574         resetTimer_(timerId_);
575     } else {
576         timerId_ = addTimer_(oneSecond, 1, [this, lh]() {
577             FlushRecords(lh);
578             timerId_ = -1;
579         });
580     }
581     if (key == key_) {
582         auto now = std::chrono::system_clock::now();
583         records_.push_back({record, now});
584         if (records_.size() >= maxRecordCount_) {
585             FlushRecords(lh);
586         }
587         return true;
588     } else {
589         FlushRecords(lh, key, record);
590         key_ = key;
591         return false;
592     }
593 }
594 
FlushRecords(const LogHeader & lh,const std::string & key,const std::string & extraRecord)595 void Aggregator::FlushRecords(const LogHeader &lh, const std::string &key, const std::string &extraRecord)
596 {
597     constexpr uint32_t milliSecondWidth = 3;
598     constexpr uint32_t microToMilli = 1000;
599     size_t recordCount = records_.size();
600     std::ostringstream oss;
601     if (!records_.empty()) {
602         oss << key_;
603         oss << ", first: " << records_.front().record << "-(";
604         auto firstTime = records_.front().timestamp;
605         time_t firstTimeT = std::chrono::system_clock::to_time_t(firstTime);
606         std::tm *bt = std::localtime(&firstTimeT);
607         if (bt == nullptr) {
608             MMI_HILOGE("bt is nullptr, this is a invalid time");
609             return;
610         }
611         oss << std::put_time(bt, "%Y-%m-%d %H:%M:%S");
612         auto since_epoch = firstTime.time_since_epoch();
613         auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(since_epoch).count() % microToMilli;
614         oss << '.' << std::setfill('0') << std::setw(milliSecondWidth) << millis << "ms)";
615 
616         if (records_.size() > 1) {
617             size_t i = records_.size() - 1;
618             const auto &recordInfo = records_[i];
619             oss << ", " << recordInfo.record;
620         }
621         records_.clear();
622         oss << ", count: " << recordCount;
623     }
624     if (!extraRecord.empty()) {
625         if (!oss.str().empty()) {
626             oss << ", last: ";
627         }
628         oss << key << ": " << extraRecord;
629     }
630     if (!oss.str().empty()) {
631         MMI_HILOG_HEADER(LOG_INFO, lh, "%{public}s", oss.str().c_str());
632     }
633 }
634 
~Aggregator()635 Aggregator::~Aggregator()
636 {
637     FlushRecords(MMI_LOG_HEADER);
638 }
639 
640 } // namespace MMI
641 } // namespace OHOS
642