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 42namespace OHOS { 43namespace MMI { 44namespace { 45constexpr int32_t FILE_SIZE_MAX { 0x6C445 }; 46constexpr int32_t MAX_PRO_FILE_SIZE { 128000 }; 47constexpr int32_t INVALID_FILE_SIZE { -1 }; 48constexpr int32_t MIN_INTERVALTIME { 36 }; 49constexpr int32_t MAX_INTERVALTIME { 100 }; 50constexpr int32_t MIN_DELAYTIME { 300 }; 51constexpr int32_t MAX_DELAYTIME { 1000 }; 52constexpr int32_t COMMENT_SUBSCRIPT { 0 }; 53const std::string CONFIG_ITEM_REPEAT = "Key.autorepeat"; 54const std::string CONFIG_ITEM_DELAY = "Key.autorepeat.delaytime"; 55const std::string CONFIG_ITEM_INTERVAL = "Key.autorepeat.intervaltime"; 56const std::string CONFIG_ITEM_TYPE = "Key.keyboard.type"; 57const std::string CURSORSTYLE_PATH = "/system/etc/multimodalinput/mouse_icon/"; 58const std::string DATA_PATH = "/data"; 59const std::string INPUT_PATH = "/system/"; 60const std::string KEY_PATH = "/vendor/etc/keymap/"; 61constexpr size_t BUF_TID_SIZE { 10 }; 62constexpr size_t BUF_CMD_SIZE { 512 }; 63constexpr size_t PROGRAM_NAME_SIZE { 256 }; 64constexpr int32_t TIME_CONVERSION_UNIT { 1000 }; 65} // namespace 66 67int64_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 77int64_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 84static 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 102uint64_t GetThisThreadId() 103{ 104 std::string stid = GetThisThreadIdOfString(); 105 auto tid = std::atoll(stid.c_str()); 106 return tid; 107} 108 109static 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 138size_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 149std::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 161int32_t GetPid() 162{ 163 return static_cast<int32_t>(getpid()); 164} 165 166static 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 179const 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 228void SetThreadName(const std::string &name) 229{ 230 prctl(PR_SET_NAME, name.c_str()); 231} 232 233static bool IsFileExists(const std::string &fileName) 234{ 235 return (access(fileName.c_str(), F_OK) == 0); 236} 237 238static 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 248static 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 258static 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 273static bool IsValidPath(const std::string &rootDir, const std::string &filePath) 274{ 275 return (filePath.compare(0, rootDir.size(), rootDir) == 0); 276} 277 278bool IsValidJsonPath(const std::string &filePath) 279{ 280 return IsValidPath(DATA_PATH, filePath) || IsValidPath(INPUT_PATH, filePath); 281} 282 283static bool IsValidProPath(const std::string &filePath) 284{ 285 return IsValidPath(KEY_PATH, filePath); 286} 287 288static bool IsValidTomlPath(const std::string &filePath) 289{ 290 return IsValidPath(KEY_PATH, filePath); 291} 292 293void 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 323static inline bool IsNum(const std::string &str) 324{ 325 std::istringstream sin(str); 326 double num; 327 return (sin >> num) && sin.eof(); 328} 329 330void 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 378std::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 406static 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 437static 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 475int32_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 507int32_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 528std::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 545std::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 570bool 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 595void 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 635Aggregator::~Aggregator() 636{ 637 FlushRecords(MMI_LOG_HEADER); 638} 639 640} // namespace MMI 641} // namespace OHOS 642