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