xref: /developtools/hdc/src/common/base.cpp (revision cc290419)
1/*
2 * Copyright (C) 2021 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#include "base.h"
16#include <algorithm>
17#include <chrono>
18#include <cstdio>
19#include <cstring>
20#include <dirent.h>
21#include <iomanip>
22#include <openssl/bio.h>
23#include <openssl/buffer.h>
24#include <openssl/evp.h>
25#include <openssl/md5.h>
26#include <random>
27#include <sstream>
28#include <sys/stat.h>
29#include <thread>
30#include <vector>
31#ifdef HDC_HILOG
32#include "hilog/log.h"
33#endif
34#ifdef _WIN32
35#include <windows.h>
36#include <codecvt>
37#include <wchar.h>
38#include <wincrypt.h>
39#else
40#include <sys/wait.h>
41#endif
42#include <fstream>
43using namespace std::chrono;
44
45namespace Hdc {
46namespace Base {
47    constexpr int DEF_FILE_PERMISSION = 0750;
48#ifndef _WIN32
49    sigset_t g_blockList;
50#endif
51    uint8_t GetLogLevel()
52    {
53        return g_logLevel;
54    }
55#ifndef  HDC_HILOG
56    std::atomic<bool> g_logCache = true;
57#endif
58    uint8_t g_logLevel = LOG_DEBUG;  // tmp set,now debugmode.LOG_OFF when release;;
59    void SetLogLevel(const uint8_t logLevel)
60    {
61        g_logLevel = logLevel;
62    }
63
64    uint8_t GetLogLevelByEnv()
65    {
66        char *env = getenv(ENV_SERVER_LOG.c_str());
67        if (!env) {
68            return GetLogLevel();
69        }
70        size_t len = strlen(env);
71
72        size_t maxLen = 1;
73        if (len > maxLen) {
74            WRITE_LOG(LOG_WARN, "OHOS_HDC_LOG_LEVEL %s is not in (0, 6] range", env);
75            return GetLogLevel();
76        }
77
78        for (size_t i = 0; i < len; i++) {
79            if (isdigit(env[i]) == 0) {
80                WRITE_LOG(LOG_WARN, "OHOS_HDC_LOG_LEVEL %s is not digit", env);
81                return GetLogLevel();
82            }
83        }
84
85        uint8_t logLevel = static_cast<uint8_t>(atoi(env));
86        if (logLevel < 0 || logLevel > LOG_LAST) {
87            WRITE_LOG(LOG_WARN, "OHOS_HDC_LOG_LEVEL %d is not in (0, 6] range", logLevel);
88        } else {
89            return logLevel;
90        }
91
92        return GetLogLevel();
93    }
94
95// Commenting the code will optimize and tune all log codes, and the compilation volume will be greatly reduced
96#define ENABLE_DEBUGLOG
97#ifdef ENABLE_DEBUGLOG
98    void GetLogDebugFunctionName(string &debugInfo, int line, string &threadIdString)
99    {
100        string tmpString = GetFileNameAny(debugInfo);
101        debugInfo = StringFormat("%s:%d", tmpString.c_str(), line);
102        if (g_logLevel < LOG_DEBUG) {
103            debugInfo = "";
104            threadIdString = "";
105        } else {
106            debugInfo = "[" + debugInfo + "]";
107            threadIdString = StringFormat("[%x]", std::hash<std::thread::id> {}(std::this_thread::get_id()));
108        }
109    }
110
111    void GetLogLevelAndTime(uint8_t logLevel, string &logLevelString, string &timeString)
112    {
113        system_clock::time_point timeNow = system_clock::now();          // now time
114        system_clock::duration sinceUnix0 = timeNow.time_since_epoch();  // since 1970
115        time_t sSinceUnix0 = duration_cast<seconds>(sinceUnix0).count();
116        std::tm *tim = std::localtime(&sSinceUnix0);
117        switch (logLevel) {
118            case LOG_FATAL:
119                logLevelString = "F";
120                break;
121            case LOG_INFO:
122                logLevelString = "I";
123                break;
124            case LOG_WARN:
125                logLevelString = "W";
126                break;
127            case LOG_DEBUG:  // will reduce performance
128                logLevelString = "D";
129                break;
130            default:  // all, just more IO/Memory information
131                logLevelString = "A";
132                break;
133        }
134        string msTimeSurplus;
135        if (g_logLevel >= LOG_DEBUG) {
136            const auto sSinceUnix0Rest = duration_cast<milliseconds>(sinceUnix0).count() % TIME_BASE;
137            msTimeSurplus = StringFormat(".%03llu", sSinceUnix0Rest);
138        }
139        timeString = msTimeSurplus;
140        if (tim != nullptr) {
141            char buffer[TIME_BUF_SIZE];
142            (void)strftime(buffer, TIME_BUF_SIZE, "%Y-%m-%d %H:%M:%S", tim);
143            timeString = StringFormat("%s%s", buffer, msTimeSurplus.c_str());
144        }
145    }
146
147#ifndef  HDC_HILOG
148    void LogToPath(const char *path, const char *str)
149    {
150        // logfile, not thread-safe
151#ifdef HDC_DEBUG_UART
152        // make a log path print.
153        static std::once_flag firstLog;
154        std::call_once(firstLog, [&]() { printf("log at %s\n", path); });
155        // better than open log file every time.
156        static std::unique_ptr<FILE, decltype(&fclose)> file(fopen(path, "w"), &fclose);
157        FILE *fp = file.get();
158        if (fp == nullptr) {
159            return;
160        }
161        if (fprintf(fp, "%s", str) > 0 && fflush(fp)) {
162            // make ci happy
163        }
164        fclose(fp);
165#else
166        int flags = UV_FS_O_RDWR | UV_FS_O_APPEND | UV_FS_O_CREAT;
167        uv_fs_t req;
168        int fd = uv_fs_open(nullptr, &req, path, flags, S_IWUSR | S_IRUSR, nullptr);
169        if (fd < 0) {
170            char buffer[BUF_SIZE_DEFAULT] = { 0 };
171            uv_strerror_r((int)req.result, buffer, BUF_SIZE_DEFAULT);
172            uv_fs_req_cleanup(&req);
173            PrintMessage("LogToPath uv_fs_open %s error %s", path, buffer);
174            return;
175        }
176        string text(str);
177        uv_buf_t wbf = uv_buf_init((char *)str, text.size());
178        uv_fs_req_cleanup(&req);
179        uv_fs_write(nullptr, &req, fd, &wbf, 1, -1, nullptr);
180        uv_fs_close(nullptr, &req, fd, nullptr);
181#endif
182    }
183
184    void GetTimeString(string &timeString)
185    {
186        system_clock::time_point timeNow = system_clock::now();
187        system_clock::duration sinceUnix0 = timeNow.time_since_epoch(); // since 1970
188        time_t sinceUnix0Time = duration_cast<seconds>(sinceUnix0).count();
189        std::tm *timeTm = std::localtime(&sinceUnix0Time);
190
191        const auto sinceUnix0Rest = duration_cast<milliseconds>(sinceUnix0).count() % TIME_BASE;
192        string msTimeSurplus = StringFormat("%03llu", sinceUnix0Rest);
193        timeString = msTimeSurplus;
194        if (timeTm != nullptr) {
195            char buffer[TIME_BUF_SIZE] = {0};
196            if (strftime(buffer, TIME_BUF_SIZE, "%Y%m%d-%H%M%S", timeTm) > 0) {
197                timeString = StringFormat("%s%s", buffer, msTimeSurplus.c_str());
198            }
199        }
200    }
201
202    bool CompareLogFileName(const string &a, const string &b)
203    {
204        return strcmp(a.c_str(), b.c_str()) > 0;
205    }
206
207    bool CreateLogDir()
208    {
209        string errMsg;
210        if (!TryCreateDirectory(GetLogDirName(), errMsg)) {
211            // Warning: no log dir, so the log here can not save into the log file.
212            WRITE_LOG(LOG_WARN, "[E002102]Create hdc_logs directory failed, the logs could not be saved into files.");
213            return false;
214        }
215        return true;
216    }
217
218    string GetCompressLogFileName(string fileName)
219    {
220        // example: hdc-20230228-123456789.log.tgz
221        return fileName + LOG_FILE_COMPRESS_SUFFIX;
222    }
223
224    uint32_t GetLogOverCount(vector<string> files, uint64_t limitDirSize)
225    {
226        WRITE_LOG(LOG_DEBUG, "GetLogDirSize, file size: %d", files.size());
227        if (files.size() == 0) {
228            return 0;
229        }
230        uint64_t totalSize = 0;
231        uint32_t overCount = 0;
232        int value = -1;
233        for (auto name : files) {
234            uv_fs_t req;
235            string last = GetLogDirName() + name;
236            string utfName = UnicodeToUtf8(last.c_str(), false);
237            value = uv_fs_stat(nullptr, &req, utfName.c_str(), nullptr);
238            uv_fs_req_cleanup(&req);
239            if (value != 0) {
240                constexpr int bufSize = BUF_SIZE_DEFAULT;
241                char buf[bufSize] = { 0 };
242                uv_strerror_r(value, buf, bufSize);
243                uv_fs_req_cleanup(&req);
244                WRITE_LOG(LOG_FATAL, "GetLogDirSize error file %s not exist %s", utfName.c_str(), buf);
245            }
246            if (req.result == 0) {
247                totalSize += req.statbuf.st_size;
248            }
249            if (totalSize > limitDirSize) {
250                overCount++;
251            }
252        }
253        WRITE_LOG(LOG_INFO, "overCount: %u", overCount);
254        return overCount;
255    }
256
257    static void ThreadCompressLog(string bakName)
258    {
259        string bakPath = GetLogDirName() + bakName;
260        if (bakName.empty()) {
261            WRITE_LOG(LOG_FATAL, "ThreadCompressLog file name empty");
262            return;
263        }
264        if ((access(bakPath.c_str(), F_OK) != 0) || !CompressLogFile(bakName)) {
265            WRITE_LOG(LOG_FATAL, "ThreadCompressLog file %s not exist", bakPath.c_str());
266            return;
267        }
268        WRITE_LOG(LOG_INFO, "ThreadCompressLog file %s.tgz success", bakPath.c_str());
269        unlink(bakPath.c_str());
270    }
271
272#ifdef _WIN32
273    bool CompressLogFile(string fileName)
274    {
275        bool retVal = false;
276        string full = GetLogDirName() + fileName;
277        if (access(full.c_str(), F_OK) != 0) {
278            WRITE_LOG(LOG_FATAL, "CompressLogFile file %s not exist", full.c_str());
279            return retVal;
280        }
281        WRITE_LOG(LOG_DEBUG, "compress log file, fileName: %s", fileName.c_str());
282        char currentDir[BUF_SIZE_DEFAULT];
283        getcwd(currentDir, sizeof(currentDir));
284
285        char buf[BUF_SIZE_SMALL] = "";
286        if (sprintf_s(buf, sizeof(buf), "tar czfp %s %s", GetCompressLogFileName(fileName).c_str(),
287            fileName.c_str()) < 0) {
288            return retVal;
289        }
290        chdir(GetLogDirName().c_str());
291        STARTUPINFO si;
292        PROCESS_INFORMATION pi;
293        ZeroMemory(&si, sizeof(si));
294        si.cb = sizeof(si);
295        ZeroMemory(&pi, sizeof(pi));
296        if (!CreateProcess(GetTarBinFile().c_str(), buf, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) {
297            DWORD errorCode = GetLastError();
298            LPVOID messageBuffer;
299            FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
300                NULL, errorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&messageBuffer, 0, NULL);
301            WRITE_LOG(LOG_FATAL, "compress log file failed, cmd: %s, error: %s", buf, (LPCTSTR)messageBuffer);
302            LocalFree(messageBuffer);
303        } else {
304            DWORD waitResult = WaitForSingleObject(pi.hProcess, INFINITE);
305            if (waitResult == WAIT_OBJECT_0) {
306                retVal = true;
307            } else if (waitResult == WAIT_TIMEOUT) {
308                retVal = true;
309            }
310            CloseHandle(pi.hProcess);
311            CloseHandle(pi.hThread);
312        }
313        chdir(currentDir);
314        return retVal;
315    }
316
317#else
318    bool CompressLogFile(string fileName)
319    {
320        bool retVal = false;
321        string full = GetLogDirName() + fileName;
322        if (access(full.c_str(), F_OK) != 0) {
323            WRITE_LOG(LOG_FATAL, "CompressLogFile file %s not exist", full.c_str());
324            return retVal;
325        }
326        WRITE_LOG(LOG_DEBUG, "compress log file, fileName: %s", fileName.c_str());
327        char currentDir[BUF_SIZE_DEFAULT];
328        getcwd(currentDir, sizeof(currentDir));
329        pid_t pc = fork();  // create process
330        chdir(GetLogDirName().c_str());
331        if (pc < 0) {
332            WRITE_LOG(LOG_WARN, "fork subprocess failed.");
333        } else if (!pc) {
334            if ((execlp(GetTarToolName().c_str(), GetTarToolName().c_str(), GetTarParams().c_str(),
335                GetCompressLogFileName(fileName).c_str(), fileName.c_str(), nullptr)) == -1) {
336                WRITE_LOG(LOG_WARN, "CompressLogFile execlp failed.");
337            }
338        } else {
339            int status;
340            waitpid(pc, &status, 0);
341            if (WIFEXITED(status)) {
342                int exitCode = WEXITSTATUS(status);
343                WRITE_LOG(LOG_DEBUG, "subprocess exited with status %d", exitCode);
344                retVal = true;
345            } else {
346                WRITE_LOG(LOG_FATAL, "compress log file failed, filename:%s, error: %s",
347                    fileName.c_str(), strerror(errno));
348            }
349        }
350        chdir(currentDir);
351        return retVal;
352    }
353#endif
354
355    void CompressLogFiles()
356    {
357        vector<string> files = GetDirFileName();
358        WRITE_LOG(LOG_DEBUG, "search log dir files, get files count: %d", files.size());
359        if (files.size() == 0) {
360            return;
361        }
362        for (auto name : files) {
363            if (name.find(LOG_FILE_NAME) != string::npos) {
364                continue;
365            }
366            if (name.find(LOG_FILE_COMPRESS_SUFFIX) != string::npos) {
367                continue;
368            }
369            if ((name.find(LOG_FILE_SUFFIX) != string::npos && CompressLogFile(name))) {
370                string full = GetLogDirName() + name;
371                unlink(full.c_str());
372            }
373        }
374    }
375
376    uint16_t GetLogLimitByEnv()
377    {
378        char *env = getenv(ENV_SERVER_LOG_LIMIT.c_str());
379        size_t maxLen = 5;
380        if (!env || strlen(env) > maxLen) {
381            return MAX_LOG_FILE_COUNT;
382        }
383        int limitCount = atoi(env);
384        WRITE_LOG(LOG_DEBUG, "get log limit count from env: %d", limitCount);
385        if (limitCount <= 0) {
386            WRITE_LOG(LOG_WARN, "invalid log limit count: %d", limitCount);
387            return MAX_LOG_FILE_COUNT;
388        } else {
389            return static_cast<uint16_t>(limitCount);
390        }
391    }
392
393
394#ifdef _WIN32
395    void RemoveOlderLogFilesOnWindows()
396    {
397        vector<string> files;
398        WIN32_FIND_DATA findData;
399        HANDLE hFind = FindFirstFile((GetLogDirName() + "/*").c_str(), &findData);
400        if (hFind == INVALID_HANDLE_VALUE) {
401            WRITE_LOG(LOG_WARN, "Failed to open TEMP dir");
402            return;
403        }
404
405        do {
406            if ((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) {
407                SetErrorMode(SEM_FAILCRITICALERRORS);
408                if (strncmp(findData.cFileName, LOG_FILE_NAME_PREFIX.c_str(), LOG_FILE_NAME_PREFIX.size()) == 0) {
409                    files.push_back(findData.cFileName);
410                }
411            }
412        } while (FindNextFile(hFind, &findData));
413        FindClose(hFind);
414
415        if (files.size() <= MAX_LOG_FILE_COUNT) {
416            return;
417        }
418
419        // Sort file names by time, with earlier ones coming first
420        sort(files.begin(), files.end(), CompareLogFileName);
421
422        uint16_t deleteCount = files.size() - MAX_LOG_FILE_COUNT;
423        WRITE_LOG(LOG_INFO, "will delete log file, count: %u", deleteCount);
424        uint16_t count = 0;
425        for (auto name : files) {
426            if (count >= deleteCount) {
427                break;
428            }
429            string deleteFile = GetLogDirName() + name;
430            LPCTSTR lpFileName = TEXT(deleteFile.c_str());
431            BOOL ret = DeleteFile(lpFileName);
432            WRITE_LOG(LOG_INFO, "delete: %s ret:%d", deleteFile.c_str(), ret);
433            count++;
434        }
435    }
436#endif
437
438    vector<string> GetDirFileName()
439    {
440        vector<string> files;
441#ifdef _WIN32
442        WIN32_FIND_DATA findData;
443        HANDLE hFind = FindFirstFile((GetLogDirName() + "/*").c_str(), &findData);
444        if (hFind == INVALID_HANDLE_VALUE) {
445            WRITE_LOG(LOG_WARN, "Failed to open log dir");
446            return;
447        }
448
449        do {
450            if ((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) {
451                SetErrorMode(SEM_FAILCRITICALERRORS);
452                if (strncmp(findData.cFileName, LOG_FILE_NAME_PREFIX.c_str(), LOG_FILE_NAME_PREFIX.size()) == 0) {
453                    files.push_back(findData.cFileName);
454                }
455            }
456        } while (FindNextFile(hFind, &findData));
457        FindClose(hFind);
458#else
459        DIR* dir = opendir(GetLogDirName().c_str());
460        if (dir == nullptr) {
461            WRITE_LOG(LOG_WARN, "open log dir failed");
462            return files;
463        }
464
465        struct dirent* entry;
466        while ((entry = readdir(dir)) != nullptr) {
467            string fileName = entry->d_name;
468            if (strncmp(fileName.c_str(), LOG_FILE_NAME_PREFIX.c_str(), LOG_FILE_NAME_PREFIX.size()) == 0) {
469                files.push_back(fileName);
470            }
471        }
472        closedir(dir);
473#endif
474        return files;
475    }
476
477    inline string GetLogDirName()
478    {
479        return GetTmpDir() + LOG_DIR_NAME + GetPathSep();
480    }
481
482    string GetLogNameWithTime()
483    {
484        string timeStr;
485        GetTimeString(timeStr);
486        // example: hdc-20230228-123456789.log
487        return LOG_FILE_NAME_PREFIX + timeStr + LOG_FILE_SUFFIX;
488    }
489
490    void RemoveOlderLogFiles()
491    {
492        vector<string> files = GetDirFileName();
493        uint16_t logLimitSize = GetLogLimitByEnv();
494        if (files.size() <= logLimitSize) {
495            return;
496        }
497        // Sort file names by time, with newer ones coming first
498        sort(files.begin(), files.end(), CompareLogFileName);
499        uint32_t deleteCount = GetLogOverCount(files, MAX_LOG_DIR_SIZE);
500        WRITE_LOG(LOG_INFO, "log file count: %u, logLimit: %u", files.size(), logLimitSize);
501        if (deleteCount == 0 || files.size() < deleteCount) {
502            return;
503        }
504        WRITE_LOG(LOG_INFO, "will delete log file, count: %u", deleteCount);
505        uint32_t count = 0;
506        uint32_t beginCount = files.size() - deleteCount;
507        for (auto name : files) {
508            count++;
509            if (count < beginCount) {
510                continue;
511            }
512            string deleteFile = GetLogDirName() + name;
513            WRITE_LOG(LOG_INFO, "delete: %s", deleteFile.c_str());
514            unlink(deleteFile.c_str());
515        }
516    }
517
518    void LogToFile(const char *str)
519    {
520        string path = GetLogDirName() + LOG_FILE_NAME;
521        RollLogFile(path.c_str());
522        LogToPath(path.c_str(), str);
523    }
524
525    void LogToCache(const char *str)
526    {
527        string path = GetLogDirName() + LOG_CACHE_NAME;
528        LogToPath(path.c_str(), str);
529    }
530
531    void RollLogFile(const char *path)
532    {
533        // Here cannot use WRITE_LOG, because WRITE_LOG will call RollLogFile again.
534        int value = -1;
535        uv_fs_t fs;
536        value = uv_fs_stat(nullptr, &fs, path, nullptr);
537        if (value != 0) {
538            constexpr int bufSize = 1024;
539            char buf[bufSize] = { 0 };
540            uv_strerror_r(value, buf, bufSize);
541            PrintMessage("RollLogFile error log file %s not exist %s", path, buf);
542            return;
543        }
544        uint64_t size = fs.statbuf.st_size;
545        if (size < MAX_LOG_FILE_SIZE) {
546            return;
547        }
548        string last = GetLogDirName() + GetLogNameWithTime();
549        value = uv_fs_rename(nullptr, &fs, path, last.c_str(), nullptr);
550        PrintMessage("rename %s to %s", path, last.c_str());
551        if (value != 0) {
552            constexpr int bufSize = 1024;
553            char buf[bufSize] = { 0 };
554            uv_strerror_r(value, buf, bufSize);
555            uv_fs_req_cleanup(&fs);
556            PrintMessage("RollLogFile error rename %s to %s %s", path, last.c_str(), buf);
557            return;
558        }
559        uv_fs_req_cleanup(&fs);
560        // creat thread
561        std::thread compressDirThread(CompressLogFiles);
562        compressDirThread.detach();
563        RemoveOlderLogFiles();
564    }
565
566    void ChmodLogFile()
567    {
568        string path = GetLogDirName() + LOG_FILE_NAME;
569        uv_fs_t req = {};
570        int rc = uv_fs_chmod(nullptr, &req, path.c_str(), 0664, nullptr);
571        if (rc < 0) {
572            char buffer[BUF_SIZE_DEFAULT] = { 0 };
573            uv_strerror_r(rc, buffer, BUF_SIZE_DEFAULT);
574            WRITE_LOG(LOG_FATAL, "uv_fs_chmod %s failed %s", path.c_str(), buffer);
575        }
576        uv_fs_req_cleanup(&req);
577    }
578#endif
579
580void PrintLogEx(const char *functionName, int line, uint8_t logLevel, const char *msg, ...)
581    {
582        if (logLevel > g_logLevel) {
583            return;
584        }
585
586        char buf[BUF_SIZE_DEFAULT4] = { 0 }; // only 4k to avoid stack overflow in 32bit or L0
587        va_list vaArgs;
588        va_start(vaArgs, msg);
589        const int retSize = vsnprintf_s(buf, sizeof(buf), sizeof(buf) - 1, msg, vaArgs);
590        va_end(vaArgs);
591        if (retSize < 0) {
592            return;
593        }
594
595#ifdef  HDC_HILOG
596        string tmpPath = functionName;
597        string filePath = GetFileNameAny(tmpPath);
598        switch (static_cast<int>(logLevel)) {
599            case static_cast<int>(LOG_DEBUG):
600                // Info level log can be printed default in hilog, debug can't
601                HDC_LOG_INFO("[%{public}s:%{public}d] %{public}s",
602                          filePath.c_str(), line, buf);
603                break;
604            case static_cast<int>(LOG_INFO):
605                HDC_LOG_INFO("[%{public}s:%{public}d] %{public}s",
606                         filePath.c_str(), line, buf);
607                break;
608            case static_cast<int>(LOG_WARN):
609                HDC_LOG_WARN("[%{public}s:%{public}d] %{public}s",
610                         filePath.c_str(), line, buf);
611                break;
612            case static_cast<int>(LOG_FATAL):
613                HDC_LOG_FATAL("[%{public}s:%{public}d] %{public}s",
614                          filePath.c_str(), line, buf);
615                break;
616            default:
617                break;
618        }
619#else
620        string logLevelString;
621        string threadIdString;
622        string sep = "\n";
623        string timeString;
624        if (string(buf).back() == '\n') {
625            sep = "\r\n";
626        }
627        string debugInfo = functionName;
628        GetLogDebugFunctionName(debugInfo, line, threadIdString);
629        GetLogLevelAndTime(logLevel, logLevelString, timeString);
630        string logBuf = StringFormat("[%s][%s]%s%s %s%s", logLevelString.c_str(), timeString.c_str(),
631                                     threadIdString.c_str(), debugInfo.c_str(), buf, sep.c_str());
632
633        printf("%s", logBuf.c_str());
634        fflush(stdout);
635
636        if (!g_logCache) {
637            LogToFile(logBuf.c_str());
638        } else {
639            LogToCache(logBuf.c_str());
640        }
641#endif
642        return;
643    }
644#else   // else ENABLE_DEBUGLOG.If disabled, the entire output code will be optimized by the compiler
645    void PrintLogEx(const char *functionName, int line, uint8_t logLevel, char *msg, ...)
646    {
647    }
648#endif  // ENABLE_DEBUGLOG
649
650    void PrintMessage(const char *fmt, ...)
651    {
652        va_list ap;
653        va_start(ap, fmt);
654        if (vfprintf(stdout, fmt, ap) > 0) {
655            fprintf(stdout, "\n");
656        }
657        va_end(ap);
658    }
659
660    // if can linkwith -lstdc++fs, use std::filesystem::path(path).filename();
661    string GetFileNameAny(string &path)
662    {
663        string tmpString = path;
664        size_t tmpNum = tmpString.rfind('/');
665        if (tmpNum == std::string::npos) {
666            tmpNum = tmpString.rfind('\\');
667            if (tmpNum == std::string::npos) {
668                return tmpString;
669            }
670        }
671        tmpString = tmpString.substr(tmpNum + 1, tmpString.size() - tmpNum);
672        return tmpString;
673    }
674
675    void SetTcpOptions(uv_tcp_t *tcpHandle, int bufMaxSize)
676    {
677        if (!tcpHandle) {
678            return;
679        }
680        uv_tcp_keepalive(tcpHandle, 1, GLOBAL_TIMEOUT);
681        // if MAX_SIZE_IOBUF==5k,bufMaxSize at least 40k. It must be set to io 8 times is more appropriate,
682        // otherwise asynchronous IO is too fast, a lot of IO is wasted on IOloop, transmission speed will decrease
683
684        uv_recv_buffer_size((uv_handle_t *)tcpHandle, &bufMaxSize);
685        uv_send_buffer_size((uv_handle_t *)tcpHandle, &bufMaxSize);
686    }
687
688    void ReallocBuf(uint8_t **origBuf, int *nOrigSize, size_t sizeWanted)
689    {
690        if (*nOrigSize > 0) {
691            return;
692        }
693        if (sizeWanted <= 0 || sizeWanted >= HDC_BUF_MAX_BYTES) {
694            WRITE_LOG(LOG_WARN, "ReallocBuf failed, sizeWanted:%d", sizeWanted);
695            return;
696        }
697        *origBuf = new uint8_t[sizeWanted];
698        if (!*origBuf) {
699            WRITE_LOG(LOG_WARN, "ReallocBuf failed, origBuf is null. sizeWanted:%d", sizeWanted);
700            return;
701        }
702        *nOrigSize = sizeWanted;
703    }
704
705    // As an uv_alloc_cb it must keep the same as prototype
706    void AllocBufferCallback(uv_handle_t *handle, size_t sizeSuggested, uv_buf_t *buf)
707    {
708        const int size = GetMaxBufSizeStable();
709        buf->base = (char *)new uint8_t[size]();
710        if (buf->base) {
711            buf->len = size - 1;
712        }
713    }
714
715    // As an uv_write_cb it must keep the same as prototype
716    void SendCallback(uv_write_t *req, int status)
717    {
718        StartTraceScope("Base::SendCallback");
719        if (status < 0) {
720            constexpr int bufSize = 1024;
721            char buf[bufSize] = { 0 };
722            uv_strerror_r(status, buf, bufSize);
723            WRITE_LOG(LOG_WARN, "SendCallback failed,status:%d %s", status, buf);
724        }
725        delete[]((uint8_t *)req->data);
726        delete req;
727    }
728
729    // xxx must keep sync with uv_loop_close/uv_walk etc.
730    bool TryCloseLoop(uv_loop_t *ptrLoop, const char *callerName)
731    {
732        // UV_RUN_DEFAULT: Runs the event loop until the reference count drops to zero. Always returns zero.
733        // UV_RUN_ONCE:    Poll for new events once. Note that this function blocks if there are no pending events.
734        //                 Returns zero when done (no active handles or requests left), or non-zero if more events are
735        //                 expected meaning you should run the event loop again sometime in the future).
736        // UV_RUN_NOWAIT:  Poll for new events once but don't block if there are no pending events.
737        uint8_t closeRetry = 0;
738        bool ret = false;
739        constexpr int maxRetry = 3;
740        for (closeRetry = 0; closeRetry < maxRetry; ++closeRetry) {
741            if (uv_loop_close(ptrLoop) == UV_EBUSY) {
742                if (closeRetry > 2) { // 2:try 2 times close,the 3rd try shows uv loop cannot close.
743                    WRITE_LOG(LOG_WARN, "%s close busy,try:%d", callerName, closeRetry);
744                }
745
746                if (ptrLoop->active_handles >= 2) { // 2:at least 2 handles for read & write.
747                    WRITE_LOG(LOG_DEBUG, "TryCloseLoop issue");
748                }
749                auto clearLoopTask = [](uv_handle_t *handle, void *arg) -> void { TryCloseHandle(handle); };
750                uv_walk(ptrLoop, clearLoopTask, nullptr);
751                // If all processing ends, Then return0,this call will block
752                if (!ptrLoop->active_handles) {
753                    ret = true;
754                    break;
755                }
756                if (!uv_run(ptrLoop, UV_RUN_ONCE)) {
757                    ret = true;
758                    break;
759                }
760                usleep(10000); // 10000:sleep for 10s
761            } else {
762                WRITE_LOG(LOG_DEBUG, "Try close loop success");
763                ret = true;
764                break;
765            }
766        }
767        return ret;
768    }
769
770    // xxx must keep sync with uv_loop_close/uv_walk etc.
771    bool TryCloseChildLoop(uv_loop_t *ptrLoop, const char *callerName)
772    {
773        // UV_RUN_DEFAULT: Runs the event loop until the reference count drops to zero. Always returns zero.
774        // UV_RUN_ONCE:    Poll for new events once. Note that this function blocks if there are no pending events.
775        //                 Returns zero when done (no active handles or requests left), or non-zero if more events are
776        //                 expected meaning you should run the event loop again sometime in the future).
777        // UV_RUN_NOWAIT:  Poll for new events once but don't block if there are no pending events.
778        uint8_t closeRetry = 0;
779        bool ret = false;
780        constexpr int maxRetry = 3;
781        for (closeRetry = 0; closeRetry < maxRetry; ++closeRetry) {
782            if (uv_loop_close(ptrLoop) == UV_EBUSY) {
783                if (closeRetry > 2) { // 2:try 2 times close,the 3rd try shows uv loop cannot close.
784                    WRITE_LOG(LOG_WARN, "%s close busy,try:%d", callerName, closeRetry);
785                }
786
787                if (ptrLoop->active_handles >= 2) { // 2:at least 2 handles for read & write.
788                    WRITE_LOG(LOG_DEBUG, "TryCloseLoop issue");
789                }
790                auto clearLoopTask = [](uv_handle_t *handle, void *arg) -> void { TryCloseHandle(handle); };
791                uv_walk(ptrLoop, clearLoopTask, nullptr);
792#ifdef _WIN32
793                // If all processing ends, Then return0,this call will block
794                if (!ptrLoop->active_handles) {
795                    ret = true;
796                    break;
797                }
798                if (!uv_run(ptrLoop, UV_RUN_ONCE)) {
799                    ret = true;
800                    break;
801                }
802                usleep(10000); // 10000:sleep for 10s
803#else
804                int r = 0;
805                int count = 0;
806                do {
807                    count++;
808                    r = uv_run(ptrLoop, UV_RUN_NOWAIT);
809                    uv_sleep(MILL_SECONDS); //10 millseconds
810                } while (r != 0 && count <= COUNT);
811#endif
812            } else {
813                WRITE_LOG(LOG_DEBUG, "Try close loop success");
814                ret = true;
815                break;
816            }
817        }
818        return ret;
819    }
820
821    // Some handles may not be initialized or activated yet or have been closed, skip the closing process
822    void TryCloseHandle(const uv_handle_t *handle)
823    {
824        TryCloseHandle(handle, nullptr);
825    }
826
827    void TryCloseHandle(const uv_handle_t *handle, uv_close_cb closeCallBack)
828    {
829        TryCloseHandle(handle, false, closeCallBack);
830    }
831
832    void TryCloseHandle(const uv_handle_t *handle, bool alwaysCallback, uv_close_cb closeCallBack)
833    {
834        bool hasCallClose = false;
835        if (handle->loop && !uv_is_closing(handle)) {
836            DispUvStreamInfo((const uv_stream_t *)handle, "before uv handle close");
837            uv_close((uv_handle_t *)handle, closeCallBack);
838            hasCallClose = true;
839        }
840        if (!hasCallClose && alwaysCallback) {
841            closeCallBack((uv_handle_t *)handle);
842        }
843    }
844
845    void DispUvStreamInfo(const uv_stream_t *handle, const char *prefix)
846    {
847        uv_handle_type type = handle->type;
848        string name = "unknown";
849        if (type == UV_TCP) {
850            name = "tcp";
851        } else if (type == UV_NAMED_PIPE) {
852            name = "named_pipe";
853        } else {
854            WRITE_LOG(LOG_DEBUG, "%s, the uv handle type is %d", prefix, type);
855            return;
856        }
857
858        size_t bufNotSended = uv_stream_get_write_queue_size(handle);
859        if (bufNotSended != 0) {
860            WRITE_LOG(LOG_DEBUG, "%s, the uv handle type is %s, has %u bytes data", prefix, name.c_str(), bufNotSended);
861        }
862    }
863    int SendToStream(uv_stream_t *handleStream, const uint8_t *buf, const int bufLen)
864    {
865        StartTraceScope("Base::SendToStream");
866        if (bufLen > static_cast<int>(HDC_BUF_MAX_BYTES)) {
867            return ERR_BUF_ALLOC;
868        }
869        uint8_t *pDynBuf = new uint8_t[bufLen];
870        if (!pDynBuf) {
871            WRITE_LOG(LOG_WARN, "SendToStream, alloc failed, size:%d", bufLen);
872            return ERR_BUF_ALLOC;
873        }
874        if (memcpy_s(pDynBuf, bufLen, buf, bufLen)) {
875            WRITE_LOG(LOG_WARN, "SendToStream, memory copy failed, size:%d", bufLen);
876            delete[] pDynBuf;
877            return ERR_BUF_COPY;
878        }
879        return SendToStreamEx(handleStream, pDynBuf, bufLen, nullptr,
880                              reinterpret_cast<void *>(SendCallback), reinterpret_cast<void *>(pDynBuf));
881    }
882
883    // handleSend is used for pipe thread sending, set nullptr for tcp, and dynamically allocated by malloc when buf
884    // is required
885    int SendToStreamEx(uv_stream_t *handleStream, const uint8_t *buf, const int bufLen, uv_stream_t *handleSend,
886                       const void *finishCallback, const void *pWriteReqData)
887    {
888        StartTraceScope("Base::SendToStreamEx");
889        int ret = ERR_GENERIC;
890        uv_write_t *reqWrite = new uv_write_t();
891        if (!reqWrite) {
892            WRITE_LOG(LOG_WARN, "SendToStreamEx, new write_t failed, size:%d", bufLen);
893            return ERR_BUF_ALLOC;
894        }
895        uv_buf_t bfr;
896        while (true) {
897            reqWrite->data = (void *)pWriteReqData;
898            bfr.base = (char *)buf;
899            bfr.len = bufLen;
900            if (!uv_is_writable(handleStream)) {
901                WRITE_LOG(LOG_WARN, "SendToStreamEx, uv_is_writable false, size:%d", bufLen);
902                delete reqWrite;
903                break;
904            }
905            // handleSend must be a TCP socket or pipe, which is a server or a connection (listening or
906            // connected state). Bound sockets or pipes will be assumed to be servers.
907            if (handleSend) {
908                ret = uv_write2(reqWrite, handleStream, &bfr, 1, handleSend, (uv_write_cb)finishCallback);
909            } else {
910                ret = uv_write(reqWrite, handleStream, &bfr, 1, (uv_write_cb)finishCallback);
911            }
912            if (ret < 0) {
913                WRITE_LOG(LOG_WARN, "SendToStreamEx, uv_write false, size:%d", bufLen);
914                delete reqWrite;
915                ret = ERR_IO_FAIL;
916                break;
917            }
918            ret = bufLen;
919            break;
920        }
921        return ret;
922    }
923
924    int SendToPollFd(int fd, const uint8_t *buf, const int bufLen)
925    {
926        if (bufLen > static_cast<int>(HDC_BUF_MAX_BYTES)) {
927            return ERR_BUF_ALLOC;
928        }
929        uint8_t *pDynBuf = new uint8_t[bufLen];
930        if (!pDynBuf) {
931            WRITE_LOG(LOG_WARN, "SendToPollFd, alloc failed, size:%d", bufLen);
932            return ERR_BUF_ALLOC;
933        }
934        if (memcpy_s(pDynBuf, bufLen, buf, bufLen)) {
935            WRITE_LOG(LOG_WARN, "SendToPollFd, memory copy failed, size:%d", bufLen);
936            delete[] pDynBuf;
937            return ERR_BUF_COPY;
938        }
939        int ret = Base::WriteToFd(fd, pDynBuf, bufLen);
940        delete[] pDynBuf;
941        if (ret <= 0) {
942            hdc_strerrno(buf);
943            WRITE_LOG(LOG_WARN, "SendToPollFd, send %d bytes to fd %d failed [%d][%s]", bufLen, fd, ret, buf);
944        }
945        return ret;
946    }
947
948    uint64_t GetRuntimeMSec()
949    {
950        struct timespec times = { 0, 0 };
951        long time;
952        clock_gettime(CLOCK_MONOTONIC, &times);
953        time = times.tv_sec * TIME_BASE + times.tv_nsec / (TIME_BASE * TIME_BASE);
954        return time;
955    }
956
957    uint32_t GetRandomU32()
958    {
959        uint32_t ret;
960        std::random_device rd;
961        std::mt19937 gen(rd());
962        std::uniform_int_distribution<uint32_t> dis(0, UINT32_MAX);
963        ret = dis(gen);
964        return ret;
965    }
966
967    uint64_t GetRandom(const uint64_t min, const uint64_t max)
968    {
969#ifdef HARMONY_PROJECT
970        uint64_t ret;
971        uv_random(nullptr, nullptr, &ret, sizeof(ret), 0, nullptr);
972#else
973        uint64_t ret;
974        std::random_device rd;
975        std::mt19937 gen(rd());
976        std::uniform_int_distribution<uint64_t> dis(min, max);
977        ret = dis(gen);
978#endif
979        return ret;
980    }
981
982    uint32_t GetSecureRandom(void)
983    {
984        uint32_t result = static_cast<uint32_t>(GetRandom());
985#ifdef _WIN32
986        const int randomByteCount = 4;
987        HCRYPTPROV hCryptProv;
988        BYTE pbData[randomByteCount];
989        do {
990            if (!CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, 0)) {
991                if (GetLastError() != NTE_BAD_KEYSET) {
992                    WRITE_LOG(LOG_FATAL, "CryptAcquireContext first failed %x", GetLastError());
993                    break;
994                }
995                if (!CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, CRYPT_NEWKEYSET)) {
996                    WRITE_LOG(LOG_FATAL, "CryptAcquireContext second failed %x", GetLastError());
997                    break;
998                }
999            }
1000            if (!CryptGenRandom(hCryptProv, randomByteCount, pbData)) {
1001                WRITE_LOG(LOG_FATAL, "CryptGenRandom failed %x", GetLastError());
1002            }
1003            if (hCryptProv) {
1004                CryptReleaseContext(hCryptProv, 0);
1005            }
1006            result = *(reinterpret_cast<uint32_t*>(pbData));
1007        } while (0);
1008#else
1009        std::ifstream randomFile("/dev/random", std::ios::binary);
1010        do {
1011            if (!randomFile.is_open()) {
1012                WRITE_LOG(LOG_FATAL, "open /dev/random failed");
1013                break;
1014            }
1015            randomFile.read(reinterpret_cast<char*>(&result), sizeof(result));
1016        } while (0);
1017        randomFile.close();
1018#endif
1019        return result;
1020    }
1021
1022    string GetRandomString(const uint16_t expectedLen)
1023    {
1024        srand(static_cast<unsigned int>(GetRandom()));
1025        string ret = string(expectedLen, '0');
1026        std::stringstream val;
1027        for (auto i = 0; i < expectedLen; ++i) {
1028            val << std::hex << (rand() % BUF_SIZE_MICRO);
1029        }
1030        ret = val.str();
1031        return ret;
1032    }
1033
1034#ifndef HDC_HOST
1035    string GetSecureRandomString(const uint16_t expectedLen)
1036    {
1037        string ret = string(expectedLen, '0');
1038        std::ifstream randomFile("/dev/random", std::ios::binary);
1039        do {
1040            if (!randomFile.is_open()) {
1041                WRITE_LOG(LOG_FATAL, "open /dev/random failed");
1042                break;
1043            }
1044            std::stringstream val;
1045            unsigned char tmpByte;
1046            for (auto i = 0; i < expectedLen; ++i) {
1047                randomFile.read(reinterpret_cast<char*>(&tmpByte), 1);
1048                val << std::hex << (tmpByte % BUF_SIZE_MICRO);
1049            }
1050            ret = val.str();
1051        } while (0);
1052        randomFile.close();
1053
1054        return ret;
1055    }
1056#endif
1057
1058    int GetRandomNum(const int min, const int max)
1059    {
1060        return static_cast<int>(GetRandom(min, max));
1061    }
1062
1063    int ConnectKey2IPPort(const char *connectKey, char *outIP, uint16_t *outPort, size_t outSize)
1064    {
1065        char bufString[BUF_SIZE_TINY] = "";
1066        if (strncpy_s(bufString, sizeof(bufString), connectKey, strlen(connectKey))) {
1067            return ERR_BUF_COPY;
1068        }
1069        char *p = strrchr(bufString, ':');
1070        if (!p) {
1071            return ERR_PARM_FORMAT;
1072        }
1073        *p = '\0';
1074        if (!strlen(bufString) || strlen(bufString) > 40) { // 40 : bigger than length of ipv6
1075            return ERR_PARM_SIZE;
1076        }
1077        uint16_t wPort = static_cast<uint16_t>(atoi(p + 1));
1078        if (EOK != strcpy_s(outIP, outSize, bufString)) {
1079            return ERR_BUF_COPY;
1080        }
1081        *outPort = wPort;
1082        return RET_SUCCESS;
1083    }
1084
1085    // After creating the session worker thread, execute it on the main thread
1086    void FinishWorkThread(uv_work_t *req, int status)
1087    {
1088        // This is operated in the main thread
1089        delete req;
1090    }
1091
1092    // at the finish of pFuncAfterThread must free uv_work_t*
1093    // clang-format off
1094    int StartWorkThread(uv_loop_t *loop, uv_work_cb pFuncWorkThread,
1095                        uv_after_work_cb pFuncAfterThread, void *pThreadData)
1096    {
1097        uv_work_t *workThread = new uv_work_t();
1098        if (!workThread) {
1099            return -1;
1100        }
1101        workThread->data = pThreadData;
1102        uv_queue_work(loop, workThread, pFuncWorkThread, pFuncAfterThread);
1103        return 0;
1104    }
1105    // clang-format on
1106
1107    char **SplitCommandToArgs(const char *cmdStringLine, int *slotIndex)
1108    {
1109        constexpr int extraBufSize = 2;
1110        char **argv;
1111        char *temp = nullptr;
1112        int argc = 0;
1113        char a = 0;
1114        size_t i = 0;
1115        size_t j = 0;
1116        size_t len = 0;
1117        bool isQuoted = false;
1118        bool isText = false;
1119        bool isSpace = false;
1120
1121        len = strlen(cmdStringLine);
1122        if (len < 1) {
1123            return nullptr;
1124        }
1125        i = ((len + extraBufSize) / extraBufSize) * sizeof(void *) + sizeof(void *);
1126        argv = reinterpret_cast<char **>(new(std::nothrow) char[i + (len + extraBufSize) * sizeof(char)]);
1127        if (argv == nullptr) {
1128            WRITE_LOG(LOG_FATAL, "SplitCommandToArgs new argv failed");
1129            return nullptr;
1130        }
1131        temp = reinterpret_cast<char *>((reinterpret_cast<uint8_t *>(argv)) + i);
1132        argc = 0;
1133        argv[argc] = temp;
1134        isQuoted = false;
1135        isText = false;
1136        isSpace = true;
1137        i = 0;
1138        j = 0;
1139
1140        while ((a = cmdStringLine[i]) != 0) {
1141            if (isQuoted) {
1142                if (a == '\"') {
1143                    isQuoted = false;
1144                } else {
1145                    temp[j] = a;
1146                    ++j;
1147                }
1148            } else {
1149                switch (a) {
1150                    case '\"':
1151                        isQuoted = true;
1152                        isText = true;
1153                        if (isSpace) {
1154                            argv[argc] = temp + j;
1155                            ++argc;
1156                        }
1157                        isSpace = false;
1158                        break;
1159                    case ' ':
1160                    case '\t':
1161                    case '\n':
1162                    case '\r':
1163                        if (isText) {
1164                            temp[j] = '\0';
1165                            ++j;
1166                        }
1167                        isText = false;
1168                        isSpace = true;
1169                        break;
1170                    default:
1171                        isText = true;
1172                        if (isSpace) {
1173                            argv[argc] = temp + j;
1174                            ++argc;
1175                        }
1176                        temp[j] = a;
1177                        ++j;
1178                        isSpace = false;
1179                        break;
1180                }
1181            }
1182            ++i;
1183        }
1184        temp[j] = '\0';
1185        argv[argc] = nullptr;
1186
1187        (*slotIndex) = argc;
1188        return argv;
1189    }
1190
1191    bool RunPipeComand(const char *cmdString, char *outBuf, uint16_t sizeOutBuf, bool ignoreTailLf)
1192    {
1193        FILE *pipeHandle = popen(cmdString, "r");
1194        if (pipeHandle == nullptr) {
1195            return false;
1196        }
1197        int bytesRead = 0;
1198        int bytesOnce = 0;
1199        while (!feof(pipeHandle)) {
1200            bytesOnce = fread(outBuf, 1, sizeOutBuf - bytesRead, pipeHandle);
1201            if (bytesOnce <= 0) {
1202                break;
1203            }
1204            bytesRead += bytesOnce;
1205        }
1206        if (bytesRead && ignoreTailLf) {
1207            if (outBuf[bytesRead - 1] == '\n') {
1208                outBuf[bytesRead - 1] = '\0';
1209            }
1210        }
1211        pclose(pipeHandle);
1212        return bytesRead;
1213    }
1214
1215    // bufLen == 0: alloc buffer in heap, need free it later
1216    // >0: read max nBuffLen bytes to *buff
1217    // ret value: <0 or bytes read
1218    int ReadBinFile(const char *pathName, void **buf, const size_t bufLen)
1219    {
1220        uint8_t *pDst = nullptr;
1221        size_t byteIO = 0;
1222        uv_fs_t req;
1223        int ret = uv_fs_stat(nullptr, &req, pathName, nullptr);
1224        if (ret < 0) {
1225            char buffer[BUF_SIZE_DEFAULT] = { 0 };
1226            uv_strerror_r((int)req.result, buffer, BUF_SIZE_DEFAULT);
1227            uv_fs_req_cleanup(&req);
1228            WRITE_LOG(LOG_FATAL, "ReadBinFile uv_fs_stat %s error %s", pathName, buffer);
1229            return -1;
1230        }
1231        size_t nFileSize = req.statbuf.st_size;
1232        size_t readMax = 0;
1233        uint8_t dynamicBuf = 0;
1234        ret = -3;  // -3:error for ReadBinFile
1235        if (bufLen == 0) {
1236            dynamicBuf = 1;
1237            pDst = new uint8_t[nFileSize + 1]();  // tail \0
1238            if (!pDst) {
1239                return -1;
1240            }
1241            readMax = nFileSize;
1242        } else {
1243            if (nFileSize > bufLen) {
1244                return -2;  // -2:error for bufLen
1245            }
1246            readMax = nFileSize;
1247            pDst = reinterpret_cast<uint8_t *>(buf);  // The first address of the static array is the array address
1248        }
1249
1250        string srcPath(pathName);
1251        string resolvedPath = CanonicalizeSpecPath(srcPath);
1252        uv_buf_t rbf = uv_buf_init((char *)pDst, readMax);
1253        uv_fs_req_cleanup(&req);
1254        int fd = uv_fs_open(nullptr, &req, resolvedPath.c_str(), O_RDONLY, 0, nullptr);
1255        if (fd < 0) {
1256            char buffer[BUF_SIZE_DEFAULT] = { 0 };
1257            uv_strerror_r((int)req.result, buffer, BUF_SIZE_DEFAULT);
1258            WRITE_LOG(LOG_FATAL, "ReadBinFile uv_fs_open %s error %s", resolvedPath.c_str(), buffer);
1259            goto ReadFileFromPath_Finish;
1260        }
1261        uv_fs_req_cleanup(&req);
1262        byteIO = uv_fs_read(nullptr, &req, fd, &rbf, 1, 0, nullptr);
1263        uv_fs_close(nullptr, nullptr, fd, nullptr);
1264        if (byteIO != readMax) {
1265            char buffer[BUF_SIZE_DEFAULT] = { 0 };
1266            uv_strerror_r((int)req.result, buffer, BUF_SIZE_DEFAULT);
1267            WRITE_LOG(LOG_FATAL, "ReadBinFile uv_fs_read %s error %s byteIO:%llu readMax:%llu",
1268                resolvedPath.c_str(), buffer, byteIO, readMax);
1269            goto ReadFileFromPath_Finish;
1270        }
1271        ret = 0;
1272    ReadFileFromPath_Finish:
1273        if (ret) {
1274            if (dynamicBuf) {
1275                delete[] pDst;
1276            }
1277        } else {
1278            if (dynamicBuf) {
1279                *buf = pDst;
1280            }
1281            ret = byteIO;
1282        }
1283        return ret;
1284    }
1285
1286    int WriteBinFile(const char *pathName, const uint8_t *buf, const size_t bufLen, bool newFile)
1287    {
1288        string resolvedPath;
1289        string srcPath(pathName);
1290        int flags = 0;
1291        if (newFile) {
1292            flags = UV_FS_O_RDWR | UV_FS_O_CREAT | UV_FS_O_TRUNC;
1293            // no std::fs supoort, else std::filesystem::canonical,-lstdc++fs
1294            if (srcPath.find("..") != string::npos) {
1295                return ERR_FILE_PATH_CHECK;
1296            }
1297            resolvedPath = srcPath.c_str();
1298        } else {
1299            flags = UV_FS_O_RDWR | UV_FS_O_CREAT | UV_FS_O_APPEND;
1300            resolvedPath = CanonicalizeSpecPath(srcPath);
1301        }
1302        uv_fs_t req;
1303        int fd = uv_fs_open(nullptr, &req, resolvedPath.c_str(), flags, S_IWUSR | S_IRUSR, nullptr);
1304        if (fd < 0) {
1305            char buffer[BUF_SIZE_DEFAULT] = { 0 };
1306            uv_strerror_r((int)req.result, buffer, BUF_SIZE_DEFAULT);
1307            uv_fs_req_cleanup(&req);
1308            WRITE_LOG(LOG_FATAL, "WriteBinFile uv_fs_open %s error %s", resolvedPath.c_str(), buffer);
1309            return ERR_FILE_OPEN;
1310        }
1311        uv_buf_t wbf = uv_buf_init((char *)buf, bufLen);
1312        uv_fs_req_cleanup(&req);
1313        size_t bytesDone = uv_fs_write(nullptr, &req, fd, &wbf, 1, 0, nullptr);
1314        uv_fs_close(nullptr, &req, fd, nullptr);
1315        if (bytesDone != bufLen) {
1316            char buffer[BUF_SIZE_DEFAULT] = { 0 };
1317            uv_strerror_r((int)req.result, buffer, BUF_SIZE_DEFAULT);
1318            uv_fs_req_cleanup(&req);
1319            WRITE_LOG(LOG_FATAL, "WriteBinFile uv_fs_write %s error %s bytesDone:%llu bufLen:%llu",
1320                resolvedPath.c_str(), buffer, bytesDone, bufLen);
1321            return ERR_BUF_SIZE;
1322        }
1323        return RET_SUCCESS;
1324    }
1325
1326    void CloseIdleCallback(uv_handle_t *handle)
1327    {
1328        delete (uv_idle_t *)handle;
1329    };
1330
1331    void CloseTimerCallback(uv_handle_t *handle)
1332    {
1333        delete (uv_timer_t *)handle;
1334    };
1335
1336    // return value: <0 error; 0 can start new server instance; >0 server already exists
1337    int ProgramMutex(const char *procname, bool checkOrNew)
1338    {
1339        char bufPath[BUF_SIZE_DEFAULT] = "";
1340        char buf[BUF_SIZE_DEFAULT] = "";
1341        char pidBuf[BUF_SIZE_TINY] = "";
1342        size_t size = sizeof(buf);
1343        if (uv_os_tmpdir(buf, &size) < 0) {
1344            WRITE_LOG(LOG_FATAL, "Tmppath failed");
1345            return ERR_API_FAIL;
1346        }
1347        if (snprintf_s(bufPath, sizeof(bufPath), sizeof(bufPath) - 1, "%s%c.%s.pid", buf, Base::GetPathSep(), procname)
1348            < 0) {
1349            return ERR_BUF_OVERFLOW;
1350        }
1351        int pid = static_cast<int>(getpid());
1352        if (snprintf_s(pidBuf, sizeof(pidBuf), sizeof(pidBuf) - 1, "%d", pid) < 0) {
1353            return ERR_BUF_OVERFLOW;
1354        }
1355        // no need to CanonicalizeSpecPath, else not work
1356        umask(0);
1357        uv_fs_t req;
1358        int fd = uv_fs_open(nullptr, &req, bufPath, O_RDWR | O_CREAT, 0666, nullptr);  // 0666:permission
1359        if (fd < 0) {
1360            char buffer[BUF_SIZE_DEFAULT] = { 0 };
1361            uv_strerror_r((int)req.result, buffer, BUF_SIZE_DEFAULT);
1362            uv_fs_req_cleanup(&req);
1363            WRITE_LOG(LOG_DEBUG, "Open mutex file %s failed!!! %s", bufPath, buffer);
1364            return ERR_FILE_OPEN;
1365        }
1366#ifdef _WIN32
1367        if (snprintf_s(buf, sizeof(buf), sizeof(buf) - 1, "Global\\%s", procname) < 0) {
1368            uv_fs_close(nullptr, &req, fd, nullptr);
1369            return ERR_BUF_OVERFLOW;
1370        }
1371        if (checkOrNew) {
1372            // CheckOrNew is true means to confirm whether the service is running
1373            uv_fs_close(nullptr, &req, fd, nullptr);
1374            HANDLE hMutex = OpenMutex(SYNCHRONIZE, FALSE, buf);
1375            if (hMutex != nullptr) {
1376                CloseHandle(hMutex);
1377                WRITE_LOG(LOG_DEBUG, "Mutex \"%s\" locked. Server already exist.", procname);
1378                return 1;
1379            } else {
1380                WRITE_LOG(LOG_DEBUG, "Server is not exist");
1381                return 0;
1382            }
1383        } else {
1384            HANDLE hMutex = CreateMutex(nullptr, TRUE, buf);
1385            DWORD dwError = GetLastError();
1386            if (ERROR_ALREADY_EXISTS == dwError || ERROR_ACCESS_DENIED == dwError) {
1387                uv_fs_close(nullptr, &req, fd, nullptr);
1388                WRITE_LOG(LOG_DEBUG, "Creat mutex, \"%s\" locked. proc already exit!!!\n", procname);
1389                return 1;
1390            }
1391        }
1392#else
1393        struct flock fl;
1394        fl.l_type = F_WRLCK;
1395        fl.l_start = 0;
1396        fl.l_whence = SEEK_SET;
1397        fl.l_len = 0;
1398        int retChild = fcntl(fd, F_SETLK, &fl);
1399        if (retChild == -1) {
1400            WRITE_LOG(LOG_DEBUG, "File \"%s\" locked. proc already exit!!!\n", bufPath);
1401            uv_fs_close(nullptr, &req, fd, nullptr);
1402            return 1;
1403        }
1404#endif
1405        int rc = 0;
1406        uv_fs_req_cleanup(&req);
1407        rc = uv_fs_ftruncate(nullptr, &req, fd, 0, nullptr);
1408        if (rc == -1) {
1409            char buffer[BUF_SIZE_DEFAULT] = { 0 };
1410            uv_strerror_r((int)req.result, buffer, BUF_SIZE_DEFAULT);
1411            uv_fs_close(nullptr, &req, fd, nullptr);
1412            WRITE_LOG(LOG_FATAL, "ftruncate file %s failed!!! %s", bufPath, buffer);
1413            return ERR_FILE_STAT;
1414        }
1415        uv_buf_t wbf = uv_buf_init(pidBuf, strlen(pidBuf));
1416        uv_fs_req_cleanup(&req);
1417        rc = uv_fs_write(nullptr, &req, fd, &wbf, 1, 0, nullptr);
1418        if (rc == -1) {
1419            char buffer[BUF_SIZE_DEFAULT] = { 0 };
1420            uv_strerror_r((int)req.result, buffer, BUF_SIZE_DEFAULT);
1421            uv_fs_close(nullptr, &req, fd, nullptr);
1422            WRITE_LOG(LOG_FATAL, "write file %s failed!!! %s", bufPath, buffer);
1423            return ERR_FILE_WRITE;
1424        }
1425        WRITE_LOG(LOG_DEBUG, "Write mutext to %s, pid:%s", bufPath, pidBuf);
1426        if (checkOrNew) {
1427            // close it for check only
1428            uv_fs_close(nullptr, &req, fd, nullptr);
1429        }
1430        // Do not close the file descriptor, the process will be mutext effect under no-Win32 OS
1431        return RET_SUCCESS;
1432    }
1433
1434    void SplitString(const string &origString, const string &seq, vector<string> &resultStrings)
1435    {
1436        string::size_type p1 = 0;
1437        string::size_type p2 = origString.find(seq);
1438
1439        while (p2 != string::npos) {
1440            if (p2 == p1) {
1441                ++p1;
1442                p2 = origString.find(seq, p1);
1443                continue;
1444            }
1445            resultStrings.push_back(origString.substr(p1, p2 - p1));
1446            p1 = p2 + seq.size();
1447            p2 = origString.find(seq, p1);
1448        }
1449
1450        if (p1 != origString.size()) {
1451            resultStrings.push_back(origString.substr(p1));
1452        }
1453    }
1454
1455    string GetShellPath()
1456    {
1457        struct stat filecheck;
1458        string shellPath = "/bin/sh";
1459        if (stat(shellPath.c_str(), &filecheck) < 0) {
1460            shellPath = "/system/bin/sh";
1461            if (stat(shellPath.c_str(), &filecheck) < 0) {
1462                shellPath = "sh";
1463            }
1464        }
1465        return shellPath;
1466    }
1467
1468    // Not supported on some platforms, Can only be achieved manually
1469    uint64_t HostToNet(uint64_t val)
1470    {
1471        if (htonl(1) == 1) {
1472            return val;
1473        }
1474        int offset = 32;
1475        return ((static_cast<uint64_t>(htonl(val))) << offset) + htonl(val >> offset);
1476    }
1477
1478    uint64_t NetToHost(uint64_t val)
1479    {
1480        if (htonl(1) == 1) {
1481            return val;
1482        }
1483        int offset = 32;
1484        return ((static_cast<uint64_t>(ntohl(val))) << offset) + ntohl(val >> offset);
1485    }
1486
1487    char GetPathSep()
1488    {
1489#ifdef _WIN32
1490        const char sep = '\\';
1491#else
1492        const char sep = '/';
1493#endif
1494        return sep;
1495    }
1496
1497    string GetFullFilePath(string &s)
1498    {  // cannot use s.rfind(std::filesystem::path::preferred_separator
1499        // remove last sep, and update input
1500        while (s.back() == GetPathSep()) {
1501            s.pop_back();
1502        }
1503
1504        size_t i = s.rfind(GetPathSep(), s.length());
1505        if (i != string::npos) {
1506            return (s.substr(i + 1, s.length() - i));
1507        }
1508        return s;
1509    }
1510
1511    string GetPathWithoutFilename(const string &s)
1512    {
1513        size_t i = s.rfind(GetPathSep(), s.length());
1514        if (i != string::npos) {
1515            return (s.substr(0, i + 1));
1516        }
1517        return s;
1518    }
1519
1520    string GetHdcAbsolutePath()
1521    {
1522        char path[BUF_SIZE_DEFAULT4] = { 0 };
1523        size_t nPathSize = sizeof(path);
1524        int ret = uv_exepath(path, &nPathSize);
1525        if (ret < 0) {
1526            char buf[BUF_SIZE_DEFAULT] = { 0 };
1527            uv_err_name_r(ret, buf, BUF_SIZE_DEFAULT);
1528            WRITE_LOG(LOG_WARN, "uvexepath ret:%d error:%s", ret, buf);
1529            return "";
1530        }
1531
1532        return string(path);
1533    }
1534
1535    int CreateSocketPair(int *fds)
1536    {
1537#ifndef _WIN32
1538#ifdef HOST_MAC
1539        int ret = socketpair(AF_UNIX, SOCK_STREAM, 0, fds);
1540        if (ret == 0) {
1541            for (auto i = 0; i < STREAM_SIZE; ++i) {
1542                if (fcntl(fds[i], F_SETFD, FD_CLOEXEC) == -1) {
1543                    CloseFd(fds[0]);
1544                    CloseFd(fds[1]);
1545                    constexpr int bufSize = 1024;
1546                    char buf[bufSize] = { 0 };
1547                    strerror_r(errno, buf, bufSize);
1548                    WRITE_LOG(LOG_WARN, "fcntl failed to set FD_CLOEXEC: %s", buf);
1549                    return -1;
1550                }
1551            }
1552        }
1553        return ret;
1554#else
1555        return socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, fds);
1556#endif
1557#else
1558        struct sockaddr_in addr;
1559        socklen_t addrlen = sizeof(addr);
1560        int reuse = 1;
1561        if (fds == 0) {
1562            return -1;
1563        }
1564        int listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1565        if (listener == -1) {
1566            return -2;  // -2:sockets error
1567        }
1568        Base::ZeroStruct(addr);
1569        addr.sin_family = AF_INET;
1570        addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
1571        addr.sin_port = 0;
1572        fds[0] = fds[1] = (int)-1;
1573        do {
1574            if (setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, (socklen_t)sizeof(reuse))) {
1575                break;
1576            }
1577            if (::bind(listener, (struct sockaddr *)&addr, sizeof(addr))) {
1578                break;
1579            }
1580            if (getsockname(listener, (struct sockaddr *)&addr, &addrlen)) {
1581                break;
1582            }
1583            if (listen(listener, 1)) {
1584                break;
1585            }
1586            fds[0] = socket(AF_INET, SOCK_STREAM, 0);
1587            if (fds[0] == -1) {
1588                break;
1589            }
1590            if (connect(fds[0], (struct sockaddr *)&addr, sizeof(addr)) == -1) {
1591                break;
1592            }
1593            fds[1] = accept(listener, nullptr, nullptr);
1594            if (fds[1] == -1) {
1595                break;
1596            }
1597            closesocket(listener);
1598            return 0;
1599        } while (0);
1600
1601        closesocket(listener);
1602        closesocket(fds[0]);
1603        closesocket(fds[1]);
1604        return -1;
1605#endif
1606    }
1607
1608    void CloseSocketPair(int *fds)
1609    {
1610#ifndef _WIN32
1611        CloseFd(fds[0]);
1612        CloseFd(fds[1]);
1613#else
1614        closesocket(fds[0]);
1615        closesocket(fds[1]);
1616#endif
1617    }
1618
1619    int StringEndsWith(string s, string sub)
1620    {
1621        return s.rfind(sub) == (s.length() - sub.length()) ? 1 : 0;
1622    }
1623
1624    const char *GetFileType(mode_t mode)
1625    {
1626        switch (mode & S_IFMT) {
1627            case S_IFDIR:
1628                return "directory";
1629            case S_IFLNK:
1630                return "symlink";
1631            case S_IFREG:
1632                return "regular file";
1633#ifndef _WIN32
1634            case S_IFBLK:
1635                return "block device";
1636            case S_IFCHR:
1637                return "character device";
1638            case S_IFIFO:
1639                return "FIFO/pipe";
1640            case S_IFSOCK:
1641                return "socket";
1642#endif
1643            default:
1644                return "Unknown";
1645        }
1646    }
1647
1648    void BuildErrorString(const char *localPath, const char *op, const char *err, string &str)
1649    {
1650        // avoid to use stringstream
1651        str = op;
1652        str += " ";
1653        str += localPath;
1654        str += " failed, ";
1655        str += err;
1656    }
1657
1658    // Both absolute and relative paths support
1659    bool CheckDirectoryOrPath(const char *localPath, bool pathOrDir, bool readWrite, string &errStr, mode_t &fm)
1660    {
1661        if (pathOrDir) {  // filepath
1662            uv_fs_t req;
1663            mode_t mode;
1664            fm = mode_t(~S_IFMT);
1665            int r = uv_fs_lstat(nullptr, &req, localPath, nullptr);
1666            if (r) {
1667                constexpr int bufSize = 1024;
1668                char buf[bufSize] = { 0 };
1669                uv_strerror_r((int)req.result, buf, bufSize);
1670                BuildErrorString(localPath, "lstat", buf, errStr);
1671            }
1672
1673            mode = req.statbuf.st_mode;
1674            uv_fs_req_cleanup(&req);
1675
1676            if ((r == 0) && (mode & S_IFDIR)) {
1677                fm = S_IFDIR;
1678            } else if ((r == 0) && (mode & S_IFREG)) {  // is file
1679                uv_fs_access(nullptr, &req, localPath, readWrite ? R_OK : W_OK, nullptr);
1680                if (req.result) {
1681                    const char *op = readWrite ? "access R_OK" : "access W_OK";
1682                    constexpr int bufSize = 1024;
1683                    char buf[bufSize] = { 0 };
1684                    uv_strerror_r((int)req.result, buf, bufSize);
1685                    BuildErrorString(localPath, op, buf, errStr);
1686                }
1687                uv_fs_req_cleanup(&req);
1688                if (req.result == 0) {
1689                    return true;
1690                }
1691            } else if (r == 0) {
1692                const char *type = GetFileType(mode);
1693                errStr = "Not support ";
1694                errStr += type;
1695                errStr += ": ";
1696                errStr += localPath;
1697            }
1698        } else {  // dir
1699            errStr = "Not support dir: ";
1700            errStr += localPath;
1701        }
1702        return false;
1703    }
1704
1705    bool TryCreateDirectory(const string &path, string &err)
1706    {
1707        uv_fs_t req;
1708        int r = uv_fs_lstat(nullptr, &req, path.c_str(), nullptr);
1709        mode_t mode = req.statbuf.st_mode;
1710        uv_fs_req_cleanup(&req);
1711        if (r < 0) {
1712            r = uv_fs_mkdir(nullptr, &req, path.c_str(), DEF_FILE_PERMISSION, nullptr);
1713            WRITE_LOG(LOG_DEBUG, "path not exist create dir = %s", path.c_str());
1714            uv_fs_req_cleanup(&req);
1715            if (r < 0) {
1716                constexpr int bufSize = 1024;
1717                char buf[bufSize] = { 0 };
1718                uv_strerror_r((int)req.result, buf, bufSize);
1719                WRITE_LOG(LOG_WARN, "create dir %s failed %s", path.c_str(), buf);
1720                err = "Error create directory, path:";
1721                err += path.c_str();
1722                return false;
1723            }
1724        } else {
1725            if (!((mode & S_IFMT) == S_IFDIR)) {
1726                WRITE_LOG(LOG_WARN, "%s exist, not directory", path.c_str());
1727                err = "File exists, path:";
1728                err += path.c_str();
1729                return false;
1730            }
1731        }
1732        return true;
1733    }
1734
1735    bool CheckDirectoryOrPath(const char *localPath, bool pathOrDir, bool readWrite)
1736    {
1737        string strUnused;
1738        mode_t mode = mode_t(~S_IFMT);
1739        return CheckDirectoryOrPath(localPath, pathOrDir, readWrite, strUnused, mode);
1740    }
1741
1742    // Using openssl encryption and decryption method, high efficiency; when encrypting more than 64 bytes,
1743    // the carriage return will not be added, and the tail padding 00 is removed when decrypting
1744    // The return value is the length of the string after Base64
1745    int Base64EncodeBuf(const uint8_t *input, const int length, uint8_t *bufOut)
1746    {
1747        return EVP_EncodeBlock(bufOut, input, length);
1748    }
1749
1750    vector<uint8_t> Base64Encode(const uint8_t *input, const int length)
1751    {
1752        vector<uint8_t> retVec;
1753        uint8_t *pBuf = nullptr;
1754        while (true) {
1755            if (static_cast<uint32_t>(length) > HDC_BUF_MAX_BYTES) {
1756                break;
1757            }
1758            int base64Size = length * 1.4 + 256;
1759            if (!(pBuf = new uint8_t[base64Size]())) {
1760                break;
1761            }
1762            int childRet = Base64EncodeBuf(input, length, pBuf);
1763            if (childRet <= 0) {
1764                break;
1765            }
1766            retVec.insert(retVec.begin(), pBuf, pBuf + childRet);
1767            break;
1768        }
1769        if (pBuf) {
1770            delete[] pBuf;
1771        }
1772
1773        return retVec;
1774    }
1775
1776    inline int CalcDecodeLength(const uint8_t *b64input)
1777    {
1778        int len = strlen(reinterpret_cast<char *>(const_cast<uint8_t *>(b64input)));
1779        if (!len) {
1780            return 0;
1781        }
1782        int padding = 0;
1783        if (b64input[len - 1] == '=' && b64input[len - LAST_EQUAL_NUM] == '=') {
1784            // last two chars are =
1785            padding = 2;  // 2 : last two chars
1786        } else if (b64input[len - 1] == '=') {
1787            // last char is =
1788            padding = 1;
1789        }
1790        return static_cast<int>(len * DECODE_SCALE - padding);
1791    }
1792
1793    // return -1 error; >0 decode size
1794    int Base64DecodeBuf(const uint8_t *input, const int length, uint8_t *bufOut)
1795    {
1796        int nRetLen = CalcDecodeLength(input);
1797        if (!nRetLen) {
1798            return 0;
1799        }
1800
1801        if (EVP_DecodeBlock(bufOut, input, length) > 0) {
1802            return nRetLen;
1803        }
1804        return 0;
1805    }
1806
1807    string Base64Decode(const uint8_t *input, const int length)
1808    {
1809        string retString;
1810        uint8_t *pBuf = nullptr;
1811        while (true) {
1812            if (static_cast<uint32_t>(length) > HDC_BUF_MAX_BYTES) {
1813                break;
1814            }
1815            // must less than length
1816            if (!(pBuf = new uint8_t[length]())) {
1817                break;
1818            }
1819            int childRet = Base64DecodeBuf(input, length, pBuf);
1820            if (childRet <= 0) {
1821                break;
1822            }
1823            retString = (reinterpret_cast<char *>(pBuf));
1824            break;
1825        }
1826        if (pBuf) {
1827            delete[] pBuf;
1828        }
1829        return retString;
1830    }
1831
1832    void ReverseBytes(void *start, int size)
1833    {
1834        uint8_t *istart = (uint8_t *)start;
1835        uint8_t *iend = istart + size;
1836        std::reverse(istart, iend);
1837    }
1838
1839    string Convert2HexStr(uint8_t arr[], int length)
1840    {
1841        std::stringstream ss;
1842        const int byteHexStrLen = 2;
1843        for (int i = 0; i < length; i++) {
1844            ss << std::hex << std::setw(byteHexStrLen) << std::setfill('0')
1845                << static_cast<int>(arr[i]);
1846            if (i < length - 1) {
1847                ss << ":";
1848            }
1849        }
1850        string result = ss.str();
1851        transform(result.begin(), result.end(), result.begin(), ::toupper);
1852        return result;
1853    }
1854
1855    // clang-format off
1856    const string StringFormat(const char * const formater, ...)
1857    {
1858        va_list vaArgs;
1859        va_start(vaArgs, formater);
1860        string ret = StringFormat(formater, vaArgs);
1861        va_end(vaArgs);
1862        return ret;
1863    }
1864
1865    const string StringFormat(const char * const formater, va_list &vaArgs)
1866    {
1867        std::vector<char> args(GetMaxBufSizeStable());
1868        const int retSize = vsnprintf_s(
1869            args.data(), GetMaxBufSizeStable(), (args.size() >= 1) ? (args.size() - 1) : 0, formater, vaArgs);
1870        if (retSize < 0) {
1871            return std::string("");
1872        } else {
1873            return std::string(args.data(), retSize);
1874        }
1875    }
1876    // clang-format on
1877
1878    string GetVersion()
1879    {
1880        const uint8_t a = 'a';
1881        uint8_t major = (HDC_VERSION_NUMBER >> 28) & 0xff;
1882        uint8_t minor = (HDC_VERSION_NUMBER << 4 >> 24) & 0xff;
1883        uint8_t version = (HDC_VERSION_NUMBER << 12 >> 24) & 0xff;
1884        uint8_t fix = (HDC_VERSION_NUMBER << 20 >> 28) & 0xff;  // max 16, tail is p
1885        string ver = StringFormat("%x.%x.%x%c", major, minor, version, a + fix);
1886        return "Ver: " + ver;
1887    }
1888
1889    bool IdleUvTask(uv_loop_t *loop, void *data, uv_idle_cb cb)
1890    {
1891        uv_idle_t *idle = new(std::nothrow) uv_idle_t();
1892        if (idle == nullptr) {
1893            return false;
1894        }
1895        idle->data = data;
1896        uv_idle_init(loop, idle);
1897        uv_idle_start(idle, cb);
1898        // delete by callback
1899        return true;
1900    }
1901
1902    bool TimerUvTask(uv_loop_t *loop, void *data, uv_timer_cb cb, int repeatTimeout)
1903    {
1904        uv_timer_t *timer = new(std::nothrow) uv_timer_t();
1905        if (timer == nullptr) {
1906            return false;
1907        }
1908        timer->data = data;
1909        uv_timer_init(loop, timer);
1910        uv_timer_start(timer, cb, 0, repeatTimeout);
1911        // delete by callback
1912        return true;
1913    }
1914
1915    // callback, uint8_t flag, string msg, const void * data
1916    bool DelayDo(uv_loop_t *loop, const int delayMs, const uint8_t flag, string msg, void *data,
1917                 std::function<void(const uint8_t, string &, const void *)> cb)
1918    {
1919        struct DelayDoParam {
1920            uv_timer_t handle;
1921            uint8_t flag;
1922            string msg;
1923            void *data;
1924            std::function<void(const uint8_t, string &, const void *)> cb;
1925        };
1926        auto funcDelayDo = [](uv_timer_t *handle) -> void {
1927            DelayDoParam *st = (DelayDoParam *)handle->data;
1928            st->cb(st->flag, st->msg, st->data);
1929            uv_close((uv_handle_t *)handle, [](uv_handle_t *handle) {
1930                DelayDoParam *st = (DelayDoParam *)handle->data;
1931                delete st;
1932            });
1933        };
1934        DelayDoParam *st = new(std::nothrow) DelayDoParam();
1935        if (st == nullptr) {
1936            return false;
1937        }
1938        st->cb = cb;
1939        st->flag = flag;
1940        st->msg = msg;
1941        st->data = data;
1942        st->handle.data = st;
1943        uv_timer_init(loop, &st->handle);
1944        uv_timer_start(&st->handle, funcDelayDo, delayMs, 0);
1945        return true;
1946    }
1947
1948    string ReplaceAll(string str, const string from, const string to)
1949    {
1950        string::size_type startPos = 0;
1951        while ((startPos = str.find(from, startPos)) != string::npos) {
1952            str.replace(startPos, from.length(), to);
1953            startPos += to.length();  // Handles case where 'to' is a substring of 'from'
1954        }
1955        return str;
1956    }
1957
1958    string CanonicalizeSpecPath(string &src)
1959    {
1960        char resolvedPath[PATH_MAX] = { 0 };
1961#if defined(_WIN32)
1962        if (!_fullpath(resolvedPath, src.c_str(), PATH_MAX)) {
1963            WRITE_LOG(LOG_FATAL, "_fullpath %s failed", src.c_str());
1964            return "";
1965        }
1966#else
1967        if (realpath(src.c_str(), resolvedPath) == nullptr) {
1968            WRITE_LOG(LOG_FATAL, "realpath %s failed", src.c_str());
1969            return "";
1970        }
1971#endif
1972        string res(resolvedPath);
1973        return res;
1974    }
1975
1976    string UnicodeToUtf8(const char *src, bool reverse)
1977    {
1978#if defined(_WIN32)
1979        UINT from = CP_ACP;
1980        UINT to = CP_UTF8;
1981        int count = 0;
1982        if (reverse) {
1983            from = CP_UTF8;
1984            to = CP_ACP;
1985        }
1986        count = MultiByteToWideChar(from, 0, src, -1, nullptr, 0);
1987        if (count <= 0) {
1988            DWORD err = GetLastError();
1989            WRITE_LOG(LOG_FATAL, "MultiByteToWideChar failed %s error:%lu", src, err);
1990            return "";
1991        }
1992        wchar_t *wstr = new(std::nothrow) wchar_t[count + 1]();
1993        if (wstr == nullptr) {
1994            WRITE_LOG(LOG_FATAL, "new wstr failed count:%d", count);
1995            return "";
1996        }
1997        count = MultiByteToWideChar(from, 0, src, -1, wstr, count);
1998        if (count <= 0) {
1999            DWORD err = GetLastError();
2000            WRITE_LOG(LOG_FATAL, "MultiByteToWideChar failed to wstr %s error:%lu", src, err);
2001            delete[] wstr;
2002            return "";
2003        }
2004        count = WideCharToMultiByte(to, 0, wstr, -1, nullptr, 0, nullptr, nullptr);
2005        if (count <= 0) {
2006            DWORD err = GetLastError();
2007            WRITE_LOG(LOG_FATAL, "WideCharToMultiByte failed %s error:%lu", wstr, err);
2008            delete[] wstr;
2009            return "";
2010        }
2011        char *ustr = new(std::nothrow) char[count + 1]();
2012        if (ustr == nullptr) {
2013            WRITE_LOG(LOG_FATAL, "new ustr failed count:%d", count);
2014            delete[] wstr;
2015            return "";
2016        }
2017        count = WideCharToMultiByte(to, 0, wstr, -1, ustr, count, nullptr, nullptr);
2018        if (count <= 0) {
2019            DWORD err = GetLastError();
2020            WRITE_LOG(LOG_FATAL, "WideCharToMultiByte failed to ustr %s error:%lu", wstr, err);
2021            delete[] wstr;
2022            delete[] ustr;
2023            return "";
2024        }
2025        string rc(ustr);
2026        delete[] wstr;
2027        delete[] ustr;
2028        return rc;
2029#else
2030        string rc(src);
2031        return rc;
2032#endif
2033    }
2034
2035    uint8_t CalcCheckSum(const uint8_t *data, int len)
2036    {
2037        uint8_t ret = 0;
2038        for (int i = 0; i < len; ++i) {
2039            ret += data[i];
2040        }
2041        return ret;
2042    }
2043
2044    uv_os_sock_t DuplicateUvSocket(uv_tcp_t *tcp)
2045    {
2046        uv_os_sock_t dupFd = -1;
2047#ifdef _WIN32
2048        WSAPROTOCOL_INFO info;
2049        ZeroStruct(info);
2050        if (WSADuplicateSocketA(tcp->socket, GetCurrentProcessId(), &info) < 0) {
2051            return dupFd;
2052        }
2053        dupFd = WSASocketA(0, 0, 0, &info, 0, 0);
2054#else
2055        uv_os_fd_t fdOs;
2056        if (uv_fileno((const uv_handle_t *)tcp, &fdOs) < 0) {
2057            return ERR_API_FAIL;
2058        }
2059#ifdef HDC_HOST
2060        dupFd = dup(uv_open_osfhandle(fdOs));
2061#else
2062        dupFd = fcntl(uv_open_osfhandle(fdOs), F_DUPFD_CLOEXEC, uv_open_osfhandle(fdOs));
2063#endif // HDC_HOST
2064#endif
2065        return dupFd;
2066    }
2067
2068    string GetCwd()
2069    {
2070        int value = -1;
2071        char path[PATH_MAX] = "";
2072        size_t size = sizeof(path);
2073        string res;
2074        value = uv_cwd(path, &size);
2075        if (value < 0) {
2076            constexpr int bufSize = 1024;
2077            char buf[bufSize] = { 0 };
2078            uv_strerror_r(value, buf, bufSize);
2079            WRITE_LOG(LOG_FATAL, "get path failed: %s", buf);
2080            return res;
2081        }
2082        size_t len = 0;
2083        len = strlen(path);
2084        if (len < 1 || len >= PATH_MAX - 1) {
2085            WRITE_LOG(LOG_FATAL, "get path failed: buffer space max");
2086            return res;
2087        }
2088        if (path[len - 1] != Base::GetPathSep()) {
2089            path[len] = Base::GetPathSep();
2090        }
2091        res = path;
2092        return res;
2093    }
2094
2095    string GetTmpDir()
2096    {
2097        string res;
2098#ifdef HDC_HOST
2099        int value = -1;
2100        char path[PATH_MAX] = "";
2101        size_t size = sizeof(path);
2102        value = uv_os_tmpdir(path, &size);
2103        if (value < 0) {
2104            constexpr int bufSize = 1024;
2105            char buf[bufSize] = { 0 };
2106            uv_strerror_r(value, buf, bufSize);
2107            WRITE_LOG(LOG_FATAL, "get tmppath failed: %s", buf);
2108            return res;
2109        }
2110        if (strlen(path) >= PATH_MAX - 1) {
2111            WRITE_LOG(LOG_FATAL, "get tmppath failed: buffer space max");
2112            return res;
2113        }
2114        if (path[strlen(path) - 1] != Base::GetPathSep()) {
2115            path[strlen(path)] = Base::GetPathSep();
2116        }
2117        res = path;
2118#else
2119        res = "/data/local/tmp/";
2120#endif
2121        return res;
2122    }
2123
2124#ifndef  HDC_HILOG
2125    void SetLogCache(bool enable)
2126    {
2127        g_logCache = enable;
2128    }
2129
2130    void RemoveLogFile()
2131    {
2132        if (g_logCache) {
2133            string path = GetLogDirName() + LOG_FILE_NAME;
2134            string bakName = GetLogNameWithTime();
2135            string bakPath = GetLogDirName() + bakName;
2136            string cachePath = GetLogDirName() + LOG_CACHE_NAME;
2137            if (rename(path.c_str(), bakPath.c_str()) != 0) {
2138                WRITE_LOG(LOG_FATAL, "rename log file failed.");
2139            }
2140            if (rename(cachePath.c_str(), path.c_str()) != 0) {
2141                WRITE_LOG(LOG_FATAL, "rename log cache file failed.");
2142            }
2143            std::thread compressThread(ThreadCompressLog, bakName);
2144            compressThread.detach();
2145            g_logCache = false;
2146            std::thread removeThread(RemoveOlderLogFiles);
2147            removeThread.detach();
2148        }
2149    }
2150
2151    void RemoveLogCache()
2152    {
2153        string cachePath = GetLogDirName() + LOG_CACHE_NAME;
2154        unlink(cachePath.c_str());
2155    }
2156#endif
2157
2158    bool IsRoot()
2159    {
2160#ifdef _WIN32
2161        // reserve
2162        return true;
2163#else
2164        if (getuid() == 0) {
2165            return true;
2166        }
2167#endif
2168        return false;
2169    }
2170
2171    bool IsAbsolutePath(string &path)
2172    {
2173        bool ret = false;
2174#ifdef _WIN32
2175        // shlwapi.h PathIsRelativeA not link in harmony project
2176        // c:\ or UNC path '\\hostname\share\file'
2177        ret = path.find(":\\") == 1 || path.find("\\\\") == 0;
2178#else
2179        ret = path[0] == '/';
2180#endif
2181        return ret;
2182    }
2183
2184    int CloseFd(int &fd)
2185    {
2186        int rc = 0;
2187        if (fd > 0) {
2188            rc = close(fd);
2189            if (rc < 0) {
2190                char buffer[BUF_SIZE_DEFAULT] = { 0 };
2191#ifdef _WIN32
2192                strerror_s(buffer, BUF_SIZE_DEFAULT, errno);
2193#else
2194                strerror_r(errno, buffer, BUF_SIZE_DEFAULT);
2195#endif
2196                WRITE_LOG(LOG_WARN, "close fd: %d failed errno:%d %s", fd, errno, buffer);
2197            } else {
2198                fd = -1;
2199            }
2200        }
2201        return rc;
2202    }
2203
2204    void InitProcess(void)
2205    {
2206#ifndef _WIN32
2207        umask(0);
2208        signal(SIGPIPE, SIG_IGN);
2209        signal(SIGCHLD, SIG_IGN);
2210        signal(SIGALRM, SIG_IGN);
2211        signal(SIGTTIN, SIG_IGN);
2212        signal(SIGTTOU, SIG_IGN);
2213        sigemptyset(&g_blockList);
2214        constexpr int crashSignal = 35;
2215        sigaddset(&g_blockList, crashSignal);
2216        sigprocmask(SIG_BLOCK, &g_blockList, nullptr);
2217#endif
2218    }
2219
2220    void DeInitProcess(void)
2221    {
2222#ifndef _WIN32
2223        mode_t writePermission = 022;
2224        umask(writePermission);
2225        signal(SIGPIPE, SIG_DFL);
2226        signal(SIGCHLD, SIG_DFL);
2227        signal(SIGALRM, SIG_DFL);
2228        signal(SIGTTIN, SIG_DFL);
2229        signal(SIGTTOU, SIG_DFL);
2230#endif
2231    }
2232
2233    int ReadFromFd(int fd, void *buf, size_t count)
2234    {
2235#ifdef _WIN32
2236        DWORD bytesRead = 0;
2237        OVERLAPPED ov = {};
2238        SOCKET s = fd;
2239        BOOL bWriteStat = ReadFile((HANDLE)s, buf, count, &bytesRead, &ov);
2240        if (bWriteStat) {
2241            return bytesRead;
2242        } else {
2243            return -1;
2244        }
2245#else
2246        return TEMP_FAILURE_RETRY(read(fd, buf, count));
2247#endif
2248    }
2249
2250    int WriteToFd(int fd, const void *buf, size_t count)
2251    {
2252#ifdef _WIN32
2253        DWORD bytesWrite = 0;
2254        OVERLAPPED ov = {};
2255        SOCKET s = fd;
2256        BOOL bWriteStat = WriteFile((HANDLE)s, buf, count, &bytesWrite, &ov);
2257        if (bWriteStat) {
2258            return 1;
2259        } else {
2260            return -1;
2261        }
2262#else
2263        return TEMP_FAILURE_RETRY(write(fd, buf, count));
2264#endif
2265    }
2266
2267    bool IsValidIpv4(const std::string& ip)
2268    {
2269        std::vector<int> segments;
2270        std::stringstream ss(ip);
2271        std::string segment;
2272        const int ipCount = 4;
2273        const int maxValue = 255;
2274
2275        // 分解字符串为四部分
2276        while (std::getline(ss, segment, '.')) {
2277            if (segments.size() >= ipCount) {
2278                return false;
2279            }
2280            if (!IsDigitString(segment)) {
2281                return false;
2282            }
2283            int num = std::stoi(segment);
2284            if (num < 0 || num > maxValue) {
2285                return false;
2286            }
2287            if (segment.size() > 1 && segment[0] == '0' && segment != "0") {
2288                return false;
2289            }
2290            segments.push_back(num);
2291        }
2292        // 必须正好有四部分
2293        return segments.size() == ipCount;
2294    }
2295
2296    // Trim from both sides and paired
2297    string &ShellCmdTrim(string &cmd)
2298    {
2299        const string pairedQuot("\"\"");
2300        if (cmd.empty()) {
2301            return cmd;
2302        }
2303        cmd = Trim(cmd);
2304        if (cmd.length() < pairedQuot.length()) {
2305            return cmd;
2306        }
2307        if (*cmd.begin() == '"' && cmd.back() == '"') {
2308            cmd.erase(cmd.begin());
2309            cmd.pop_back();
2310        }
2311        return cmd;
2312    }
2313
2314    void TrimSubString(string &str, string substr)
2315    {
2316        std::string::size_type pos = 0;
2317        while ((pos = str.find(substr, pos)) != std::string::npos) {
2318            str.erase(pos, substr.length());
2319        }
2320    }
2321    // first 16 bytes is tag
2322    // second 16 bytes is length
2323    // flow the value
2324    bool TlvAppend(string &tlv, string tag, string val)
2325    {
2326        if (tag.empty()) {
2327            return false;
2328        }
2329        unsigned int tlen = tag.length();
2330        if (tlen < TLV_TAG_LEN) {
2331            tag.append(TLV_TAG_LEN - tlen, ' ');
2332        }
2333        tlv.append(tag);
2334        string vallen = std::to_string(val.length());
2335        unsigned int vlen = vallen.length();
2336        if (vlen < TLV_VAL_LEN) {
2337            vallen.append(TLV_VAL_LEN - vlen, ' ');
2338        }
2339        tlv.append(vallen);
2340        tlv.append(val);
2341        return true;
2342    }
2343    bool TlvToStringMap(string tlv, std::map<string, string> &tlvmap)
2344    {
2345        if (tlv.length() < TLV_MIN_LEN) {
2346            return false;
2347        }
2348        while (tlv.length() >= TLV_MIN_LEN) {
2349            string tag = tlv.substr(0, TLV_TAG_LEN);
2350            TrimSubString(tag, " ");
2351            tlv.erase(0, TLV_TAG_LEN);
2352
2353            string vallen = tlv.substr(0, TLV_VAL_LEN);
2354            TrimSubString(vallen, " ");
2355            int len = atoi(vallen.c_str());
2356            if (len < 0) {
2357                return false;
2358            }
2359            tlv.erase(0, TLV_VAL_LEN);
2360
2361            if (tlv.length() < static_cast<uint32_t>(len)) {
2362                return false;
2363            }
2364            string val = tlv.substr(0, len);
2365            tlv.erase(0, len);
2366
2367            tlvmap[tag] = val;
2368        }
2369        return true;
2370    }
2371
2372    // NOTE: This function relies on the caller to guarantee that
2373    // the input parameter is not null and to check the opened handle state.
2374    FILE *Fopen(const char *fileName, const char *mode)
2375    {
2376#ifdef _WIN32
2377        wchar_t resolvedPath[PATH_MAX + 1] = { 0 };
2378        // windows platform open file with wide char
2379        std::wstring_convert<std::codecvt_utf8<wchar_t>> converter;
2380        std::wstring wideFileName = converter.from_bytes(fileName);
2381        std::wstring wideMode = converter.from_bytes(mode);
2382        if (!_wfullpath(resolvedPath, wideFileName.c_str(), PATH_MAX + 1)) {
2383            WRITE_LOG(LOG_FATAL, "_wfullpath %s failed", wideFileName.c_str());
2384            return nullptr;
2385        }
2386        return _wfopen(resolvedPath, wideMode.c_str());
2387#else
2388        // unix paltform open file with default char
2389        return fopen(fileName, mode);
2390#endif
2391    }
2392
2393    std::set<uint32_t> g_deletedSessionIdSet;
2394    std::queue<uint32_t> g_deletedSessionIdQueue;
2395    std::mutex g_deletedSessionIdRecordMutex;
2396    void AddDeletedSessionId(uint32_t sessionId)
2397    {
2398        std::lock_guard<std::mutex> lock(g_deletedSessionIdRecordMutex);
2399        if (g_deletedSessionIdSet.find(sessionId) != g_deletedSessionIdSet.end()) {
2400            WRITE_LOG(LOG_INFO, "SessionId:%u is already in the cache", sessionId);
2401            return;
2402        }
2403        WRITE_LOG(LOG_INFO, "AddDeletedSessionId:%u", sessionId);
2404        g_deletedSessionIdSet.insert(sessionId);
2405        g_deletedSessionIdQueue.push(sessionId);
2406
2407        // Delete old records and only save MAX_DELETED_SESSION_ID_RECORD_COUNT records
2408        if (g_deletedSessionIdQueue.size() > MAX_DELETED_SESSION_ID_RECORD_COUNT) {
2409            uint32_t id = g_deletedSessionIdQueue.front();
2410            WRITE_LOG(LOG_INFO, "g_deletedSessionIdQueue size:%u, g_deletedSessionIdSet size:%u, pop session id:%u",
2411                g_deletedSessionIdQueue.size(), g_deletedSessionIdSet.size(), id);
2412            g_deletedSessionIdQueue.pop();
2413            g_deletedSessionIdSet.erase(id);
2414        }
2415    }
2416
2417    bool IsSessionDeleted(uint32_t sessionId)
2418    {
2419        std::lock_guard<std::mutex> lock(g_deletedSessionIdRecordMutex);
2420        if (g_deletedSessionIdSet.find(sessionId) != g_deletedSessionIdSet.end()) {
2421            return true;
2422        }
2423        return false;
2424    }
2425}
2426}  // namespace Hdc
2427