* Copyright (C) 2021 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "base.h"
#include <algorithm>
#include <chrono>
#include <cstdio>
#include <regex>
#include <cstring>
#include <dirent.h>
#include <iomanip>
#include <openssl/bio.h>
#include <openssl/buffer.h>
#include <openssl/evp.h>
#include <openssl/md5.h>
#include <random>
#include <sstream>
#include <sys/stat.h>
#include <thread>
#include <vector>
#ifdef HDC_HILOG
#include "hilog/log.h"
#endif
#ifdef _WIN32
#include <windows.h>
#include <codecvt>
#include <wchar.h>
#include <wincrypt.h>
#else
#include <sys/wait.h>
#endif
#include <fstream>
#include "server_cmd_log.h"
using namespace std::chrono;
namespace Hdc {
namespace Base {
bool g_isBackgroundServer = false;
bool g_encryptTCPSwitch = false;
string g_tempDir = "";
uint16_t g_logFileCount = MAX_LOG_FILE_COUNT;
bool g_heartbeatSwitch = true;
constexpr int DEF_FILE_PERMISSION = 0750;
bool g_cmdlogSwitch = false;
std::vector<std::string> g_cmdLogsFilesStrings;
std::mutex g_threadCompressCmdLogsMutex;
std::shared_ptr<std::thread> g_compressCmdLogsThread;
std::atomic<bool> g_isServer = false;
[[maybe_unused]] static bool g_isSubserver = false;
static std::string g_subserverLogFileName = "";
static std::vector<std::string> g_subserverLogFileNames;
#ifndef _WIN32
constexpr int EXEC_FAILED_ERROR_CODE = 100;
#endif
uint8_t GetLogLevel()
{
return g_logLevel;
}
#ifndef HDC_HILOG
std::atomic<bool> g_logCache = true;
#endif
uint8_t g_logLevel = LOG_DEBUG;
void SetLogLevel(const uint8_t logLevel)
{
g_logLevel = logLevel;
}
uint8_t GetLogLevelByEnv()
{
char *env = getenv(ENV_SERVER_LOG.c_str());
if (!env) {
return GetLogLevel();
}
size_t len = strlen(env);
size_t maxLen = 1;
if (len > maxLen) {
WRITE_LOG(LOG_WARN, "OHOS_HDC_LOG_LEVEL %s is not in (0, 6] range", env);
return GetLogLevel();
}
for (size_t i = 0; i < len; i++) {
if (isdigit(env[i]) == 0) {
WRITE_LOG(LOG_WARN, "OHOS_HDC_LOG_LEVEL %s is not digit", env);
return GetLogLevel();
}
}
uint8_t logLevel = static_cast<uint8_t>(atoi(env));
if (logLevel < 0 || logLevel > LOG_LAST) {
WRITE_LOG(LOG_WARN, "OHOS_HDC_LOG_LEVEL %d is not in (0, 6] range", logLevel);
} else {
return logLevel;
}
return GetLogLevel();
}
#define ENABLE_DEBUGLOG
#ifdef ENABLE_DEBUGLOG
void GetLogDebugFunctionName(string &debugInfo, int line, string &threadIdString)
{
string tmpString = GetFileNameAny(debugInfo);
debugInfo = StringFormat("%s:%d", tmpString.c_str(), line);
if (g_logLevel < LOG_DEBUG) {
debugInfo = "";
threadIdString = "";
} else {
debugInfo = "[" + debugInfo + "]";
threadIdString = StringFormat("[%x]", std::hash<std::thread::id> {}(std::this_thread::get_id()));
}
}
void GetLogLevelAndTime(uint8_t logLevel, string &logLevelString, string &timeString)
{
system_clock::time_point timeNow = system_clock::now();
system_clock::duration sinceUnix0 = timeNow.time_since_epoch();
time_t sSinceUnix0 = duration_cast<seconds>(sinceUnix0).count();
std::tm *tim = std::localtime(&sSinceUnix0);
switch (logLevel) {
case LOG_FATAL:
logLevelString = "F";
break;
case LOG_INFO:
logLevelString = "I";
break;
case LOG_WARN:
logLevelString = "W";
break;
case LOG_DEBUG:
logLevelString = "D";
break;
default:
logLevelString = "A";
break;
}
string msTimeSurplus;
if (g_logLevel >= LOG_DEBUG) {
const auto sSinceUnix0Rest = duration_cast<milliseconds>(sinceUnix0).count() % TIME_BASE;
msTimeSurplus = StringFormat(".%03llu", sSinceUnix0Rest);
}
timeString = msTimeSurplus;
if (tim != nullptr) {
char buffer[TIME_BUF_SIZE];
(void)strftime(buffer, TIME_BUF_SIZE, "%Y-%m-%d %H:%M:%S", tim);
timeString = StringFormat("%s%s", buffer, msTimeSurplus.c_str());
}
}
bool CompareLogFileName(const string &a, const string &b)
{
return strcmp(a.c_str(), b.c_str()) > 0;
}
void GetTimeString(string &timeString)
{
system_clock::time_point timeNow = system_clock::now();
system_clock::duration sinceUnix0 = timeNow.time_since_epoch();
time_t sinceUnix0Time = duration_cast<seconds>(sinceUnix0).count();
std::tm *timeTm = std::localtime(&sinceUnix0Time);
const auto sinceUnix0Rest = duration_cast<milliseconds>(sinceUnix0).count() % TIME_BASE;
string msTimeSurplus = StringFormat("%03llu", sinceUnix0Rest);
timeString = msTimeSurplus;
if (timeTm != nullptr) {
char buffer[TIME_BUF_SIZE] = {0};
if (strftime(buffer, TIME_BUF_SIZE, "%Y%m%d-%H%M%S", timeTm) > 0) {
timeString = StringFormat("%s%s", buffer, msTimeSurplus.c_str());
}
}
}
#ifndef HDC_HILOG
void LogToPath(const char *path, const char *str)
{
#ifdef HDC_DEBUG_UART
static std::once_flag firstLog;
std::call_once(firstLog, [&]() { printf("log at %s\n", path); });
static std::unique_ptr<FILE, decltype(&fclose)> file(fopen(path, "w"), &fclose);
FILE *fp = file.get();
if (fp == nullptr) {
return;
}
if (fprintf(fp, "%s", str) > 0 && fflush(fp)) {
}
fclose(fp);
#else
static int flags = UV_FS_O_RDWR | UV_FS_O_APPEND | (!g_isSubserver ? UV_FS_O_CREAT : 0);
uv_fs_t req;
#ifdef HOST_OHOS
mode_t mode = (S_IWUSR | S_IRUSR | S_IWGRP | S_IRGRP);
#else
mode_t mode = (S_IWUSR | S_IRUSR);
#endif
int fd = uv_fs_open(nullptr, &req, path, flags, mode, nullptr);
if (fd < 0) {
if (!g_isSubserver) {
char buffer[BUF_SIZE_DEFAULT] = { 0 };
uv_strerror_r((int)req.result, buffer, BUF_SIZE_DEFAULT);
uv_fs_req_cleanup(&req);
PrintMessage("LogToPath uv_fs_open %s error %s", path, buffer);
}
return;
}
string text(str);
uv_buf_t wbf = uv_buf_init((char *)str, text.size());
uv_fs_req_cleanup(&req);
uv_fs_write(nullptr, &req, fd, &wbf, 1, -1, nullptr);
uv_fs_close(nullptr, &req, fd, nullptr);
#endif
}
bool CreateLogDir()
{
string errMsg;
if (!TryCreateDirectory(GetLogDirName(), errMsg)) {
WRITE_LOG(LOG_WARN, "[E002102]Create hdc_logs directory failed, the logs could not be saved into files.");
return false;
}
return true;
}
string GetCompressLogFileName(string fileName)
{
return fileName + LOG_FILE_COMPRESS_SUFFIX;
}
uint32_t GetLogOverCount(vector<string> files, uint64_t limitDirSize)
{
WRITE_LOG(LOG_DEBUG, "GetLogDirSize, file size: %d", files.size());
if (files.size() == 0) {
return 0;
}
uint64_t totalSize = 0;
uint32_t overCount = 0;
int value = -1;
for (auto name : files) {
uv_fs_t req;
string last = GetLogDirName() + name;
string utfName = UnicodeToUtf8(last.c_str(), false);
value = uv_fs_stat(nullptr, &req, utfName.c_str(), nullptr);
uv_fs_req_cleanup(&req);
if (value != 0) {
constexpr int bufSize = BUF_SIZE_DEFAULT;
char buf[bufSize] = { 0 };
uv_strerror_r(value, buf, bufSize);
uv_fs_req_cleanup(&req);
if (GetCaller() == Caller::SERVER) {
WRITE_LOG(LOG_FATAL, "GetLogDirSize error file %s not exist %s",
Hdc::MaskString(utfName).c_str(), buf);
} else {
WRITE_LOG(LOG_FATAL, "GetLogDirSize error file %s not exist %s", utfName.c_str(), buf);
}
}
if (req.result == 0) {
totalSize += req.statbuf.st_size;
}
if (totalSize > limitDirSize) {
overCount++;
}
}
WRITE_LOG(LOG_INFO, "overCount: %u", overCount);
return overCount;
}
#ifdef FEATURE_HOST_LOG_COMPRESS
static void ThreadCompressLog(string bakName)
{
string bakPath = GetLogDirName() + bakName;
if (bakName.empty()) {
WRITE_LOG(LOG_FATAL, "ThreadCompressLog file name empty");
return;
}
if ((access(bakPath.c_str(), F_OK) != 0) || !CompressLogFile(bakName)) {
if (GetCaller() == Caller::SERVER) {
WRITE_LOG(LOG_FATAL, "ThreadCompressLog file %s not exist", Hdc::MaskString(bakPath).c_str());
} else {
WRITE_LOG(LOG_FATAL, "ThreadCompressLog file %s not exist", bakPath.c_str());
}
return;
}
if (GetCaller() == Caller::SERVER) {
WRITE_LOG(LOG_INFO, "ThreadCompressLog file %s.tgz success", Hdc::MaskString(bakPath).c_str());
} else {
WRITE_LOG(LOG_INFO, "ThreadCompressLog file %s.tgz success", bakPath.c_str());
}
if (unlink(bakPath.c_str()) != 0) {
WRITE_LOG(LOG_FATAL, "Failed to unlink file or symlink, error is :%s", strerror(errno));
}
}
#endif
#ifdef _WIN32
bool CompressLogFile(string fileName)
{
bool retVal = false;
string full = GetLogDirName() + fileName;
if (access(full.c_str(), F_OK) != 0) {
if (GetCaller() == Caller::SERVER) {
WRITE_LOG(LOG_FATAL, "CompressLogFile file %s not exist", Hdc::MaskString(full).c_str());
} else {
WRITE_LOG(LOG_FATAL, "CompressLogFile file %s not exist", full.c_str());
}
return retVal;
}
if (GetCaller() == Caller::SERVER) {
WRITE_LOG(LOG_DEBUG, "compress log file, fileName: %s", Hdc::MaskString(fileName).c_str());
} else {
WRITE_LOG(LOG_DEBUG, "compress log file, fileName: %s", fileName.c_str());
}
char currentDir[BUF_SIZE_DEFAULT];
getcwd(currentDir, sizeof(currentDir));
char buf[BUF_SIZE_SMALL] = "";
if (sprintf_s(buf, sizeof(buf), "tar czfp %s %s", GetCompressLogFileName(fileName).c_str(),
fileName.c_str()) < 0) {
return retVal;
}
chdir(GetLogDirName().c_str());
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
if (!CreateProcess(GetTarBinFile().c_str(), buf, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) {
DWORD errorCode = GetLastError();
LPVOID messageBuffer;
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, errorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&messageBuffer, 0, NULL);
WRITE_LOG(LOG_FATAL, "compress log file failed, cmd: %s, error: %s", buf, (LPCTSTR)messageBuffer);
LocalFree(messageBuffer);
} else {
DWORD waitResult = WaitForSingleObject(pi.hProcess, INFINITE);
if (waitResult == WAIT_OBJECT_0) {
retVal = true;
} else if (waitResult == WAIT_TIMEOUT) {
retVal = true;
}
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
chdir(currentDir);
return retVal;
}
#else
bool CompressLogFile(string fileName)
{
bool retVal = false;
string full = GetLogDirName() + fileName;
if (access(full.c_str(), F_OK) != 0) {
if (GetCaller() == Caller::SERVER) {
WRITE_LOG(LOG_FATAL, "CompressLogFile file %s not exist", Hdc::MaskString(full).c_str());
} else {
WRITE_LOG(LOG_FATAL, "CompressLogFile file %s not exist", full.c_str());
}
return retVal;
}
if (GetCaller() == Caller::SERVER) {
WRITE_LOG(LOG_DEBUG, "compress log file, fileName: %s", Hdc::MaskString(fileName).c_str());
} else {
WRITE_LOG(LOG_DEBUG, "compress log file, fileName: %s", fileName.c_str());
}
char currentDir[BUF_SIZE_DEFAULT];
getcwd(currentDir, sizeof(currentDir));
pid_t pc = fork();
chdir(GetLogDirName().c_str());
if (pc < 0) {
WRITE_LOG(LOG_FATAL, "fork subprocess failed.");
} else if (pc == 0) {
if ((execlp(GetTarToolName().c_str(), GetTarToolName().c_str(), GetTarParams().c_str(),
GetCompressLogFileName(fileName).c_str(), fileName.c_str(), nullptr)) == -1) {
WRITE_LOG(LOG_FATAL, "CompressLogFile execlp failed.");
_exit(EXEC_FAILED_ERROR_CODE);
}
} else {
int status;
waitpid(pc, &status, 0);
if (WIFEXITED(status)) {
int exitCode = WEXITSTATUS(status);
WRITE_LOG(LOG_DEBUG, "subprocess exited with status %d", exitCode);
retVal = true;
} else {
if (GetCaller() == Caller::CLIENT) {
WRITE_LOG(LOG_FATAL, "compress log file failed, filename:%s, error: %s",
fileName.c_str(), strerror(errno));
} else {
WRITE_LOG(LOG_FATAL, "compress log file failed, filename:%s, error: %s",
Hdc::MaskString(fileName).c_str(), strerror(errno));
}
}
}
chdir(currentDir);
return retVal;
}
#endif
void CompressLogFiles()
{
vector<string> files = GetDirFileName();
WRITE_LOG(LOG_DEBUG, "search log dir files, get files count: %d", files.size());
if (files.size() == 0) {
return;
}
for (auto name : files) {
if (name.find(LOG_FILE_NAME) != string::npos) {
continue;
}
if (name.find(LOG_FILE_COMPRESS_SUFFIX) != string::npos) {
continue;
}
if ((name.find(LOG_FILE_SUFFIX) != string::npos && CompressLogFile(name))) {
string full = GetLogDirName() + name;
if (unlink(full.c_str()) != 0) {
WRITE_LOG(LOG_INFO, "Failed to unlink file or symlink, error is :%s", strerror(errno));
}
}
}
}
void UpdateLogLimitFileCountCache()
{
char *env = getenv(ENV_SERVER_LOG_LIMIT.c_str());
size_t maxLen = 5;
if (!env || strlen(env) > maxLen) {
g_logFileCount = MAX_LOG_FILE_COUNT;
return;
}
int limitCount = atoi(env);
if (limitCount <= 0) {
g_logFileCount = MAX_LOG_FILE_COUNT;
} else {
g_logFileCount = static_cast<uint16_t>(limitCount);
}
}
uint16_t GetLogLimitFileCount()
{
return g_logFileCount;
}
#ifdef _WIN32
void RemoveOlderLogFilesOnWindows()
{
if (g_isSubserver) {
return;
}
vector<string> files;
WIN32_FIND_DATA findData;
HANDLE hFind = FindFirstFile((GetLogDirName() + "/*").c_str(), &findData);
if (hFind == INVALID_HANDLE_VALUE) {
WRITE_LOG(LOG_WARN, "Failed to open TEMP dir");
return;
}
do {
if ((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) {
SetErrorMode(SEM_FAILCRITICALERRORS);
if (strncmp(findData.cFileName, LOG_FILE_NAME_PREFIX.c_str(), LOG_FILE_NAME_PREFIX.size()) == 0) {
files.push_back(findData.cFileName);
}
}
} while (FindNextFile(hFind, &findData));
FindClose(hFind);
if (files.size() <= MAX_LOG_FILE_COUNT) {
return;
}
sort(files.begin(), files.end(), CompareLogFileName);
uint16_t deleteCount = files.size() - MAX_LOG_FILE_COUNT;
WRITE_LOG(LOG_INFO, "will delete log file, count: %u", deleteCount);
uint16_t count = 0;
for (auto name : files) {
if (count >= deleteCount) {
break;
}
string deleteFile = GetLogDirName() + name;
LPCTSTR lpFileName = TEXT(deleteFile.c_str());
BOOL ret = DeleteFile(lpFileName);
if (GetCaller() == Caller::SERVER) {
WRITE_LOG(LOG_INFO, "delete: %s ret:%d", Hdc::MaskString(deleteFile).c_str(), ret);
} else {
WRITE_LOG(LOG_INFO, "delete: %s ret:%d", deleteFile.c_str(), ret);
}
count++;
}
}
#endif
vector<string> GetDirFileName()
{
vector<string> files;
#ifdef _WIN32
WIN32_FIND_DATA findData;
HANDLE hFind = FindFirstFile((GetLogDirName() + "/*").c_str(), &findData);
if (hFind == INVALID_HANDLE_VALUE) {
WRITE_LOG(LOG_WARN, "Failed to open log dir");
return files;
}
do {
if ((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) {
SetErrorMode(SEM_FAILCRITICALERRORS);
if (strncmp(findData.cFileName, LOG_FILE_NAME_PREFIX.c_str(), LOG_FILE_NAME_PREFIX.size()) == 0) {
files.push_back(findData.cFileName);
}
}
} while (FindNextFile(hFind, &findData));
FindClose(hFind);
#else
DIR* dir = opendir(GetLogDirName().c_str());
if (dir == nullptr) {
WRITE_LOG(LOG_WARN, "open log dir failed");
return files;
}
struct dirent* entry;
while ((entry = readdir(dir)) != nullptr) {
string fileName = entry->d_name;
if (strncmp(fileName.c_str(), LOG_FILE_NAME_PREFIX.c_str(), LOG_FILE_NAME_PREFIX.size()) == 0) {
files.push_back(fileName);
}
}
closedir(dir);
#endif
return files;
}
inline string GetLogDirName()
{
if (g_isSubserver) [[unlikely]] {
return GetTmpDir() + ".hdc_subserver" + GetPathSep();
}
#ifdef FEATURE_HOST_LOG_COMPRESS
return GetTmpDir() + LOG_DIR_NAME + GetPathSep();
#else
#ifdef HOST_OHOS
return GetTmpDir() + ".hdc" + GetPathSep();
#else
return GetTmpDir();
#endif
#endif
}
string GetLogNameWithTime()
{
string timeStr;
GetTimeString(timeStr);
return LOG_FILE_NAME_PREFIX + timeStr + LOG_FILE_SUFFIX;
}
void RemoveOlderLogFiles()
{
if (g_isSubserver) {
return;
}
vector<string> files = GetDirFileName();
#ifdef FEATURE_HOST_LOG_COMPRESS
uint16_t logLimitSize = GetLogLimitFileCount();
#else
uint16_t logLimitSize = MAX_LOG_FILE_COUNT;
#endif
if (files.size() <= logLimitSize) {
return;
}
sort(files.begin(), files.end(), CompareLogFileName);
#ifdef FEATURE_HOST_LOG_COMPRESS
uint32_t deleteCount = GetLogOverCount(files, MAX_LOG_DIR_SIZE);
WRITE_LOG(LOG_INFO, "log file count: %u, logLimit: %u", files.size(), logLimitSize);
if (deleteCount == 0 || files.size() < deleteCount) {
return;
}
WRITE_LOG(LOG_INFO, "will delete log file, count: %u", deleteCount);
uint32_t beginCount = files.size() - deleteCount;
#else
uint32_t deleteCount = files.size() - static_cast<uint32_t>(logLimitSize);
WRITE_LOG(LOG_INFO, "will delete log file, count: %u", deleteCount);
uint32_t beginCount = files.size() - deleteCount;
#endif
for (auto name = files.begin() + beginCount; name != files.end(); name++) {
string deleteFile = GetLogDirName() + *name;
if (GetCaller() == Caller::CLIENT) {
WRITE_LOG(LOG_INFO, "delete: %s", deleteFile.c_str());
} else {
WRITE_LOG(LOG_INFO, "delete: %s", Hdc::MaskString(deleteFile).c_str());
}
if (unlink(deleteFile.c_str()) != 0) {
WRITE_LOG(LOG_INFO, "Failed to unlink file or symlink, error is :%s", strerror(errno));
}
}
}
void LogToFile(const char *str)
{
string path = "";
if (!g_isSubserver) [[likely]] {
path = GetLogDirName() + LOG_FILE_NAME;
} else {
path = GetLogDirName() + g_subserverLogFileName;
}
RollLogFile(path.c_str());
LogToPath(path.c_str(), str);
}
void InitSubserverLogging(const std::string& logFileName)
{
g_subserverLogFileName = logFileName;
g_isSubserver = true;
if (logFileName.empty()) {
return;
}
string dirPath = GetTmpDir() + ".hdc_subserver";
string filePath = dirPath + GetPathSep() + logFileName;
string errMsg;
if (!TryCreateDirectory(dirPath, errMsg)) {
PrintMessage("InitSubserverLogging create dir failed: %s", errMsg.c_str());
return;
}
uv_fs_t req;
int flags = UV_FS_O_RDWR | UV_FS_O_APPEND | UV_FS_O_CREAT;
#ifdef HOST_OHOS
mode_t mode = (S_IWUSR | S_IRUSR | S_IWGRP | S_IRGRP);
#else
mode_t mode = (S_IWUSR | S_IRUSR);
#endif
int fd = uv_fs_open(nullptr, &req, filePath.c_str(), flags, mode, nullptr);
if (fd < 0) {
char buffer[BUF_SIZE_DEFAULT] = { 0 };
uv_strerror_r((int)req.result, buffer, BUF_SIZE_DEFAULT);
uv_fs_req_cleanup(&req);
PrintMessage("InitSubserverLogging uv_fs_open %s error %s", filePath.c_str(), buffer);
return;
}
uv_fs_close(nullptr, &req, fd, nullptr);
uv_fs_req_cleanup(&req);
PrintMessage("InitSubserverLogging create log file succ: %s", filePath.c_str());
}
void LogToCache(const char *str)
{
string path = GetLogDirName() + LOG_CACHE_NAME;
LogToPath(path.c_str(), str);
}
void TruncateSubserverLogFile(const char *path)
{
uv_fs_t openReq;
int fd = uv_fs_open(nullptr, &openReq, path, UV_FS_O_RDWR, 0, nullptr);
uv_fs_req_cleanup(&openReq);
if (fd < 0) {
char buffer[BUF_SIZE_DEFAULT] = { 0 };
uv_strerror_r(fd, buffer, BUF_SIZE_DEFAULT);
PrintMessage("RollLogFile open %s failed %s", path, buffer);
return;
}
uv_fs_t truncateReq;
int rc = uv_fs_ftruncate(nullptr, &truncateReq, fd, 0, nullptr);
if (rc != 0) {
char buffer[BUF_SIZE_DEFAULT] = { 0 };
uv_strerror_r(rc, buffer, BUF_SIZE_DEFAULT);
PrintMessage("RollLogFile truncate %s failed %s", path, buffer);
}
uv_fs_req_cleanup(&truncateReq);
uv_fs_t closeReq;
uv_fs_close(nullptr, &closeReq, fd, nullptr);
uv_fs_req_cleanup(&closeReq);
}
void RollLogFile(const char *path)
{
int value = -1;
uv_fs_t fs;
value = uv_fs_stat(nullptr, &fs, path, nullptr);
uv_fs_req_cleanup(&fs);
if (value != 0) {
if (!g_isSubserver) {
constexpr int bufSize = 1024;
char buf[bufSize] = { 0 };
uv_strerror_r(value, buf, bufSize);
PrintMessage("RollLogFile error log file %s not exist %s", path, buf);
}
return;
}
uint64_t size = fs.statbuf.st_size;
if (size < MAX_LOG_FILE_SIZE) {
return;
}
if (g_isSubserver) {
TruncateSubserverLogFile(path);
return;
}
string last = GetLogDirName() + GetLogNameWithTime();
value = uv_fs_rename(nullptr, &fs, path, last.c_str(), nullptr);
PrintMessage("rename %s to %s", path, last.c_str());
if (value != 0) {
constexpr int bufSize = 1024;
char buf[bufSize] = { 0 };
uv_strerror_r(value, buf, bufSize);
uv_fs_req_cleanup(&fs);
PrintMessage("RollLogFile error rename %s to %s %s", path, last.c_str(), buf);
return;
}
uv_fs_req_cleanup(&fs);
#ifdef FEATURE_HOST_LOG_COMPRESS
std::thread compressDirThread(CompressLogFiles);
compressDirThread.detach();
#endif
RemoveOlderLogFiles();
}
void ChmodLogFile()
{
string path = GetLogDirName() + LOG_FILE_NAME;
uv_fs_t req = {};
int rc = uv_fs_chmod(nullptr, &req, path.c_str(), 0664, nullptr);
if (rc < 0) {
char buffer[BUF_SIZE_DEFAULT] = { 0 };
uv_strerror_r(rc, buffer, BUF_SIZE_DEFAULT);
if (GetCaller() == Caller::SERVER) {
WRITE_LOG(LOG_FATAL, "uv_fs_chmod %s failed %s", Hdc::MaskString(path).c_str(), buffer);
} else {
WRITE_LOG(LOG_FATAL, "uv_fs_chmod %s failed %s", path.c_str(), buffer);
}
}
uv_fs_req_cleanup(&req);
}
#endif
#ifndef HDC_HILOG
static void EchoLog(string &buf)
{
if (g_isBackgroundServer) {
return;
}
printf("%s", buf.c_str());
fflush(stdout);
}
#endif
void PrintLogEx(const char *functionName, int line, uint8_t logLevel, const char *msg, ...)
{
if (logLevel > g_logLevel) {
return;
}
char buf[BUF_SIZE_DEFAULT4] = { 0 };
va_list vaArgs;
va_start(vaArgs, msg);
const int retSize = vsnprintf_s(buf, sizeof(buf), sizeof(buf) - 1, msg, vaArgs);
va_end(vaArgs);
if (retSize < 0) {
return;
}
#ifdef HDC_HILOG
string tmpPath = functionName;
string filePath = GetFileNameAny(tmpPath);
switch (static_cast<int>(logLevel)) {
case static_cast<int>(LOG_DEBUG):
HDC_LOG_INFO("[%{public}s:%{public}d] %{public}s",
filePath.c_str(), line, buf);
break;
case static_cast<int>(LOG_INFO):
HDC_LOG_INFO("[%{public}s:%{public}d] %{public}s",
filePath.c_str(), line, buf);
break;
case static_cast<int>(LOG_WARN):
HDC_LOG_WARN("[%{public}s:%{public}d] %{public}s",
filePath.c_str(), line, buf);
break;
case static_cast<int>(LOG_FATAL):
HDC_LOG_FATAL("[%{public}s:%{public}d] %{public}s",
filePath.c_str(), line, buf);
break;
default:
break;
}
#else
string logLevelString;
string threadIdString;
string sep = "\n";
string timeString;
if (string(buf).back() == '\n') {
sep = "\r\n";
}
string debugInfo = functionName;
GetLogDebugFunctionName(debugInfo, line, threadIdString);
GetLogLevelAndTime(logLevel, logLevelString, timeString);
#ifdef HOST_OHOS
string logBuf = StringFormat("[%s][%s][%u]%s%s %s%s", logLevelString.c_str(), timeString.c_str(),
getuid(), threadIdString.c_str(), debugInfo.c_str(), buf, sep.c_str());
#else
string logBuf = StringFormat("[%s][%s]%s%s %s%s", logLevelString.c_str(), timeString.c_str(),
threadIdString.c_str(), debugInfo.c_str(), buf, sep.c_str());
#endif
EchoLog(logBuf);
if (!g_logCache || g_isSubserver) {
LogToFile(logBuf.c_str());
} else {
LogToCache(logBuf.c_str());
}
#endif
return;
}
#else
void PrintLogEx(const char *functionName, int line, uint8_t logLevel, char *msg, ...)
{
}
#endif
void PrintMessage(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
if (vfprintf(stdout, fmt, ap) > 0) {
fprintf(stdout, "\n");
}
va_end(ap);
}
void PrintMessageAndWriteLog(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
if (vfprintf(stdout, fmt, ap) > 0) {
fprintf(stdout, "\n");
}
WRITE_LOG(LOG_WARN, fmt);
va_end(ap);
}
string GetFileNameAny(string &path)
{
string tmpString = path;
size_t tmpNum = tmpString.rfind('/');
if (tmpNum == std::string::npos) {
tmpNum = tmpString.rfind('\\');
if (tmpNum == std::string::npos) {
return tmpString;
}
}
tmpString = tmpString.substr(tmpNum + 1, tmpString.size() - tmpNum);
return tmpString;
}
#ifdef HOST_OHOS
void SetUdsOptions(uv_pipe_t *udsHandle, int bufMaxSize)
{
if (!udsHandle) {
return;
}
uv_recv_buffer_size((uv_handle_t *)udsHandle, &bufMaxSize);
uv_send_buffer_size((uv_handle_t *)udsHandle, &bufMaxSize);
}
#endif
void SetTcpOptions(uv_tcp_t *tcpHandle, int bufMaxSize)
{
if (!tcpHandle) {
return;
}
uv_tcp_keepalive(tcpHandle, 1, GLOBAL_TIMEOUT);
uv_recv_buffer_size((uv_handle_t *)tcpHandle, &bufMaxSize);
uv_send_buffer_size((uv_handle_t *)tcpHandle, &bufMaxSize);
}
void ReallocBuf(uint8_t **origBuf, int *nOrigSize, size_t sizeWanted)
{
if (*nOrigSize > 0) {
return;
}
if (sizeWanted <= 0 || sizeWanted >= HDC_BUF_MAX_BYTES) {
WRITE_LOG(LOG_WARN, "ReallocBuf failed, sizeWanted:%d", sizeWanted);
return;
}
*origBuf = new uint8_t[sizeWanted];
if (!*origBuf) {
WRITE_LOG(LOG_WARN, "ReallocBuf failed, origBuf is null. sizeWanted:%d", sizeWanted);
return;
}
*nOrigSize = sizeWanted;
}
void SendCallback(uv_write_t *req, int status)
{
StartTraceScope("Base::SendCallback");
if (status < 0) {
constexpr int bufSize = 1024;
char buf[bufSize] = { 0 };
uv_strerror_r(status, buf, bufSize);
WRITE_LOG(LOG_WARN, "SendCallback failed,status:%d %s", status, buf);
}
delete[]((uint8_t *)req->data);
delete req;
}
bool TryCloseLoop(uv_loop_t *ptrLoop, const char *callerName)
{
uint8_t closeRetry = 0;
bool ret = false;
constexpr int maxRetry = 3;
for (closeRetry = 0; closeRetry < maxRetry; ++closeRetry) {
if (uv_loop_close(ptrLoop) == UV_EBUSY) {
if (closeRetry > 2) {
WRITE_LOG(LOG_WARN, "%s close busy,try:%d", callerName, closeRetry);
}
if (ptrLoop->active_handles >= 2) {
WRITE_LOG(LOG_DEBUG, "TryCloseLoop issue");
}
auto clearLoopTask = [](uv_handle_t *handle, void* ) -> void { TryCloseHandle(handle); };
uv_walk(ptrLoop, clearLoopTask, nullptr);
if (!ptrLoop->active_handles) {
ret = true;
break;
}
if (!uv_run(ptrLoop, UV_RUN_ONCE)) {
ret = true;
break;
}
usleep(10000);
} else {
WRITE_LOG(LOG_DEBUG, "Try close loop success");
ret = true;
break;
}
}
return ret;
}
bool TryCloseChildLoop(uv_loop_t *ptrLoop, const char *callerName)
{
uint8_t closeRetry = 0;
bool ret = false;
constexpr int maxRetry = 3;
for (closeRetry = 0; closeRetry < maxRetry; ++closeRetry) {
if (uv_loop_close(ptrLoop) == UV_EBUSY) {
if (closeRetry > 2) {
WRITE_LOG(LOG_WARN, "%s close busy,try:%d", callerName, closeRetry);
}
if (ptrLoop->active_handles >= 2) {
WRITE_LOG(LOG_DEBUG, "TryCloseLoop issue");
}
auto clearLoopTask = [](uv_handle_t *handle, void* ) -> void { TryCloseHandle(handle); };
uv_walk(ptrLoop, clearLoopTask, nullptr);
#ifdef _WIN32
if (!ptrLoop->active_handles) {
ret = true;
break;
}
if (!uv_run(ptrLoop, UV_RUN_ONCE)) {
uv_loop_close(ptrLoop);
ret = true;
break;
}
usleep(10000);
#else
int r = 0;
int count = 0;
do {
count++;
r = uv_run(ptrLoop, UV_RUN_NOWAIT);
uv_sleep(MILL_SECONDS);
} while (r != 0 && count <= COUNT);
#endif
} else {
WRITE_LOG(LOG_DEBUG, "Try close loop success");
ret = true;
break;
}
}
return ret;
}
void TryCloseHandle(const uv_handle_t *handle)
{
TryCloseHandle(handle, nullptr);
}
void TryCloseHandle(const uv_handle_t *handle, uv_close_cb closeCallBack)
{
TryCloseHandle(handle, false, closeCallBack);
}
void TryCloseHandle(const uv_handle_t *handle, bool alwaysCallback, uv_close_cb closeCallBack)
{
bool hasCallClose = false;
if (handle->loop && !uv_is_closing(handle)) {
DispUvStreamInfo((const uv_stream_t *)handle, "before uv handle close");
uv_close((uv_handle_t *)handle, closeCallBack);
hasCallClose = true;
}
if (!hasCallClose && alwaysCallback) {
closeCallBack((uv_handle_t *)handle);
}
}
void DispUvStreamInfo(const uv_stream_t *handle, const char *prefix)
{
uv_handle_type type = handle->type;
string name = "unknown";
if (type == UV_TCP) {
name = "tcp";
} else if (type == UV_NAMED_PIPE) {
name = "named_pipe";
} else {
WRITE_LOG(LOG_DEBUG, "%s, the uv handle type is %d", prefix, type);
return;
}
size_t bufNotSended = uv_stream_get_write_queue_size(handle);
if (bufNotSended != 0) {
WRITE_LOG(LOG_DEBUG, "%s, the uv handle type is %s, has %u bytes data", prefix, name.c_str(), bufNotSended);
}
}
int SendToStream(uv_stream_t *handleStream, const uint8_t *buf, const int bufLen)
{
StartTraceScope("Base::SendToStream");
if (bufLen > static_cast<int>(HDC_BUF_MAX_BYTES)) {
return ERR_BUF_ALLOC;
}
uint8_t *pDynBuf = new uint8_t[bufLen];
if (!pDynBuf) {
WRITE_LOG(LOG_WARN, "SendToStream, alloc failed, size:%d", bufLen);
return ERR_BUF_ALLOC;
}
if (memcpy_s(pDynBuf, bufLen, buf, bufLen)) {
WRITE_LOG(LOG_WARN, "SendToStream, memory copy failed, size:%d", bufLen);
delete[] pDynBuf;
return ERR_BUF_COPY;
}
int rc = SendToStreamEx(handleStream, pDynBuf, bufLen, nullptr,
reinterpret_cast<void *>(SendCallback), reinterpret_cast<void *>(pDynBuf));
if (rc < 0) {
WRITE_LOG(LOG_WARN, "SendToStreamEx failed rc:%d bufLen:%d", rc, bufLen);
delete[] pDynBuf;
}
return rc;
}
int SendToStreamEx(uv_stream_t *handleStream, const uint8_t *buf, const int bufLen, uv_stream_t *handleSend,
const void *finishCallback, const void *pWriteReqData)
{
StartTraceScope("Base::SendToStreamEx");
int ret = ERR_GENERIC;
uv_write_t *reqWrite = new uv_write_t();
if (!reqWrite) {
WRITE_LOG(LOG_WARN, "SendToStreamEx, new write_t failed, size:%d", bufLen);
return ERR_BUF_ALLOC;
}
uv_buf_t bfr;
while (true) {
reqWrite->data = (void *)pWriteReqData;
bfr.base = (char *)buf;
bfr.len = bufLen;
if (!uv_is_writable(handleStream)) {
WRITE_LOG(LOG_WARN, "SendToStreamEx, uv_is_writable false, size:%d", bufLen);
delete reqWrite;
break;
}
if (handleSend) {
ret = uv_write2(reqWrite, handleStream, &bfr, 1, handleSend, (uv_write_cb)finishCallback);
} else {
ret = uv_write(reqWrite, handleStream, &bfr, 1, (uv_write_cb)finishCallback);
}
if (ret < 0) {
WRITE_LOG(LOG_WARN, "SendToStreamEx, uv_write false, size:%d", bufLen);
delete reqWrite;
ret = ERR_IO_FAIL;
break;
}
ret = bufLen;
break;
}
return ret;
}
int SendToPollFd(int fd, const uint8_t *buf, const int bufLen)
{
if (bufLen > static_cast<int>(HDC_BUF_MAX_BYTES)) {
return ERR_BUF_ALLOC;
}
uint8_t *pDynBuf = new uint8_t[bufLen];
if (!pDynBuf) {
WRITE_LOG(LOG_WARN, "SendToPollFd, alloc failed, size:%d", bufLen);
return ERR_BUF_ALLOC;
}
if (memcpy_s(pDynBuf, bufLen, buf, bufLen)) {
WRITE_LOG(LOG_WARN, "SendToPollFd, memory copy failed, size:%d", bufLen);
delete[] pDynBuf;
return ERR_BUF_COPY;
}
int ret = Base::WriteToFd(fd, pDynBuf, bufLen);
delete[] pDynBuf;
if (ret <= 0) {
hdc_strerrno(buf);
WRITE_LOG(LOG_WARN, "SendToPollFd, send %d bytes to fd %d failed [%d][%s]", bufLen, fd, ret, buf);
}
return ret;
}
uint64_t GetRuntimeMSec()
{
struct timespec times = { 0, 0 };
long time;
clock_gettime(CLOCK_MONOTONIC, ×);
time = times.tv_sec * TIME_BASE + times.tv_nsec / (TIME_BASE * TIME_BASE);
return time;
}
uint32_t GetRandomU32()
{
uint32_t ret;
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<uint32_t> dis(0, UINT32_MAX);
ret = dis(gen);
return ret;
}
uint64_t GetRandom(const uint64_t min, const uint64_t max)
{
#ifdef HARMONY_PROJECT
uint64_t ret;
uv_random(nullptr, nullptr, &ret, sizeof(ret), 0, nullptr);
#else
uint64_t ret;
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<uint64_t> dis(min, max);
ret = dis(gen);
#endif
return ret;
}
uint32_t GetSecureRandom(void)
{
uint32_t result = static_cast<uint32_t>(GetRandom());
#ifdef _WIN32
const int randomByteCount = 4;
const int MAX_RETRY_COUNT = 3;
int retryCount = 0;
HCRYPTPROV hCryptProv;
BYTE pbData[randomByteCount];
do {
if (!CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) {
if (GetLastError() != NTE_BAD_KEYSET) {
break;
}
if (!CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, CRYPT_NEWKEYSET)) {
break;
}
}
if (CryptGenRandom(hCryptProv, randomByteCount, pbData)) {
result = *(reinterpret_cast<uint32_t*>(pbData));
if (hCryptProv) {
CryptReleaseContext(hCryptProv, 0);
}
return result;
}
if (hCryptProv) {
CryptReleaseContext(hCryptProv, 0);
}
retryCount++;
} while (retryCount < MAX_RETRY_COUNT);
#else
std::ifstream randomFile("/dev/random", std::ios::binary);
do {
if (!randomFile.is_open()) {
break;
}
randomFile.read(reinterpret_cast<char*>(&result), sizeof(result));
} while (0);
randomFile.close();
#endif
return result;
}
string GetRandomString(const uint16_t expectedLen)
{
srand(static_cast<unsigned int>(GetRandom()));
string ret = string(expectedLen, '0');
std::stringstream val;
for (auto i = 0; i < expectedLen; ++i) {
val << std::hex << (rand() % BUF_SIZE_MICRO);
}
ret = val.str();
return ret;
}
#ifndef HDC_HOST
string GetSecureRandomString(const uint16_t expectedLen)
{
string ret = string(expectedLen, '0');
std::ifstream randomFile("/dev/random", std::ios::binary);
do {
if (!randomFile.is_open()) {
WRITE_LOG(LOG_FATAL, "open /dev/random failed");
break;
}
std::stringstream val;
unsigned char tmpByte;
for (auto i = 0; i < expectedLen; ++i) {
randomFile.read(reinterpret_cast<char*>(&tmpByte), 1);
val << std::hex << (tmpByte % BUF_SIZE_MICRO);
}
ret = val.str();
} while (0);
randomFile.close();
return ret;
}
#endif
int GetRandomNum(const int min, const int max)
{
return static_cast<int>(GetRandom(min, max));
}
int ConnectKey2IPPort(const char *connectKey, char *outIP, uint16_t *outPort, size_t outSize)
{
char bufString[BUF_SIZE_TINY] = "";
if (strncpy_s(bufString, sizeof(bufString), connectKey, strlen(connectKey))) {
return ERR_BUF_COPY;
}
char *p = strrchr(bufString, ':');
if (!p) {
return ERR_PARM_FORMAT;
}
*p = '\0';
if (!strlen(bufString) || strlen(bufString) > 40) {
return ERR_PARM_SIZE;
}
long wPort;
if (!StringToLong(std::string(p + 1), wPort)) {
return ERR_PARM_FORMAT;
}
if (wPort <= 0 || wPort > MAX_IP_PORT) {
return ERR_PARM_FORMAT;
}
if (EOK != strcpy_s(outIP, outSize, bufString)) {
return ERR_BUF_COPY;
}
*outPort = static_cast<uint16_t>(wPort);
return RET_SUCCESS;
}
void FinishWorkThread(uv_work_t *req, int )
{
delete req;
}
int StartWorkThread(uv_loop_t *loop, uv_work_cb pFuncWorkThread,
uv_after_work_cb pFuncAfterThread, void *pThreadData)
{
uv_work_t *workThread = new uv_work_t();
if (!workThread) {
return -1;
}
workThread->data = pThreadData;
uv_queue_work(loop, workThread, pFuncWorkThread, pFuncAfterThread);
return 0;
}
char **SplitCommandToArgs(const char *cmdStringLine, int *slotIndex)
{
constexpr int extraBufSize = 2;
char **argv;
char *temp = nullptr;
int argc = 0;
char a = 0;
size_t i = 0;
size_t j = 0;
size_t len = 0;
bool isQuoted = false;
bool isText = false;
bool isSpace = false;
len = strlen(cmdStringLine);
if (len < 1) {
return nullptr;
}
i = ((len + extraBufSize) / extraBufSize) * sizeof(void *) + sizeof(void *);
argv = reinterpret_cast<char **>(new(std::nothrow) char[i + (len + extraBufSize) * sizeof(char)]);
if (argv == nullptr) {
WRITE_LOG(LOG_FATAL, "SplitCommandToArgs new argv failed");
return nullptr;
}
temp = reinterpret_cast<char *>((reinterpret_cast<uint8_t *>(argv)) + i);
argc = 0;
argv[argc] = temp;
isQuoted = false;
isText = false;
isSpace = true;
i = 0;
j = 0;
while ((a = cmdStringLine[i]) != 0) {
if (isQuoted) {
if (a == '\"') {
isQuoted = false;
} else {
temp[j] = a;
++j;
}
} else {
switch (a) {
case '\"':
isQuoted = true;
isText = true;
if (isSpace) {
argv[argc] = temp + j;
++argc;
}
isSpace = false;
break;
case ' ':
case '\t':
case '\n':
case '\r':
if (isText) {
temp[j] = '\0';
++j;
}
isText = false;
isSpace = true;
break;
default:
isText = true;
if (isSpace) {
argv[argc] = temp + j;
++argc;
}
temp[j] = a;
++j;
isSpace = false;
break;
}
}
++i;
}
temp[j] = '\0';
argv[argc] = nullptr;
(*slotIndex) = argc;
return argv;
}
bool RunPipeComand(const char *cmdString, char *outBuf, uint16_t sizeOutBuf, bool ignoreTailLf)
{
if (cmdString == nullptr || outBuf == nullptr) {
return false;
}
FILE *pipeHandle = popen(cmdString, "r");
if (pipeHandle == nullptr) {
return false;
}
size_t bytesRead = 0;
size_t bytesOnce = 0;
while (!feof(pipeHandle)) {
bytesOnce = fread(outBuf, 1, sizeOutBuf - bytesRead, pipeHandle);
if (bytesOnce == 0) {
break;
}
bytesRead += bytesOnce;
}
if (bytesRead > 0 && bytesRead <= sizeOutBuf && ignoreTailLf) {
if (outBuf[bytesRead - 1] == '\n') {
outBuf[bytesRead - 1] = '\0';
}
}
pclose(pipeHandle);
return bytesRead > 0;
}
int ReadBinFile(const char *pathName, void **buf, const size_t bufLen)
{
uint8_t *pDst = nullptr;
int byteIO = 0;
uv_fs_t req;
int ret = uv_fs_stat(nullptr, &req, pathName, nullptr);
if (ret < 0) {
char buffer[BUF_SIZE_DEFAULT] = { 0 };
uv_strerror_r((int)req.result, buffer, BUF_SIZE_DEFAULT);
uv_fs_req_cleanup(&req);
if (GetCaller() == Caller::CLIENT) {
WRITE_LOG(LOG_FATAL, "ReadBinFile uv_fs_stat %s error %s", pathName, buffer);
} else {
WRITE_LOG(LOG_FATAL, "ReadBinFile uv_fs_stat %s error %s",
Hdc::MaskString(string(pathName)).c_str(), buffer);
}
return -1;
}
size_t nFileSize = req.statbuf.st_size;
size_t readMax = 0;
uint8_t dynamicBuf = 0;
ret = -3;
if (bufLen == 0) {
dynamicBuf = 1;
constexpr size_t maxFileSize = 10 * 1024 * 1024;
if (nFileSize > maxFileSize) {
uv_fs_req_cleanup(&req);
return -1;
}
pDst = new uint8_t[nFileSize + 1]();
if (!pDst) {
uv_fs_req_cleanup(&req);
return -1;
}
readMax = nFileSize;
} else {
if (nFileSize > bufLen) {
uv_fs_req_cleanup(&req);
return -2;
}
readMax = nFileSize;
pDst = reinterpret_cast<uint8_t *>(buf);
}
string srcPath(pathName);
string resolvedPath = CanonicalizeSpecPath(srcPath);
uv_buf_t rbf = uv_buf_init((char *)pDst, readMax);
uv_fs_req_cleanup(&req);
int fd = uv_fs_open(nullptr, &req, resolvedPath.c_str(), O_RDONLY, 0, nullptr);
if (fd < 0) {
char buffer[BUF_SIZE_DEFAULT] = { 0 };
uv_strerror_r((int)req.result, buffer, BUF_SIZE_DEFAULT);
if (GetCaller() == Caller::CLIENT) {
WRITE_LOG(LOG_FATAL, "ReadBinFile uv_fs_open %s error %s", resolvedPath.c_str(), buffer);
} else {
WRITE_LOG(LOG_FATAL, "ReadBinFile uv_fs_open %s error %s",
Hdc::MaskString(resolvedPath).c_str(), buffer);
}
goto ReadFileFromPath_Finish;
}
uv_fs_req_cleanup(&req);
byteIO = uv_fs_read(nullptr, &req, fd, &rbf, 1, 0, nullptr);
uv_fs_close(nullptr, nullptr, fd, nullptr);
if (byteIO < 0 || static_cast<size_t>(byteIO) != readMax) {
char buffer[BUF_SIZE_DEFAULT] = { 0 };
uv_strerror_r((int)req.result, buffer, BUF_SIZE_DEFAULT);
if (GetCaller() == Caller::CLIENT) {
WRITE_LOG(LOG_FATAL, "ReadBinFile uv_fs_read %s error %s byteIO:%d readMax:%llu",
resolvedPath.c_str(), buffer, byteIO, readMax);
} else {
WRITE_LOG(LOG_FATAL, "ReadBinFile uv_fs_read %s error %s byteIO:%d readMax:%llu",
Hdc::MaskString(resolvedPath).c_str(), buffer, byteIO, readMax);
}
goto ReadFileFromPath_Finish;
}
ret = 0;
ReadFileFromPath_Finish:
if (ret) {
if (dynamicBuf) {
delete[] pDst;
}
} else {
if (dynamicBuf) {
*buf = pDst;
}
ret = byteIO;
}
return ret;
}
int WriteBinFile(const char *pathName, const uint8_t *buf, const size_t bufLen, bool newFile)
{
string resolvedPath;
string srcPath(pathName);
int flags = 0;
if (newFile) {
flags = UV_FS_O_RDWR | UV_FS_O_CREAT | UV_FS_O_TRUNC;
if (srcPath.find("..") != string::npos) {
return ERR_FILE_PATH_CHECK;
}
resolvedPath = srcPath.c_str();
} else {
flags = UV_FS_O_RDWR | UV_FS_O_CREAT | UV_FS_O_APPEND;
resolvedPath = CanonicalizeSpecPath(srcPath);
}
uv_fs_t req;
#ifdef HOST_OHOS
mode_t mode = (S_IWUSR | S_IRUSR | S_IWGRP | S_IRGRP);
#else
mode_t mode = (S_IWUSR | S_IRUSR);
#endif
int fd = uv_fs_open(nullptr, &req, resolvedPath.c_str(), flags, mode, nullptr);
if (fd < 0) {
char buffer[BUF_SIZE_DEFAULT] = { 0 };
uv_strerror_r((int)req.result, buffer, BUF_SIZE_DEFAULT);
uv_fs_req_cleanup(&req);
if (GetCaller() == Caller::CLIENT) {
WRITE_LOG(LOG_FATAL, "WriteBinFile uv_fs_open %s error %s", resolvedPath.c_str(), buffer);
} else {
WRITE_LOG(LOG_FATAL, "WriteBinFile uv_fs_open %s error %s",
Hdc::MaskString(resolvedPath).c_str(), buffer);
}
return ERR_FILE_OPEN;
}
uv_buf_t wbf = uv_buf_init((char *)buf, bufLen);
uv_fs_req_cleanup(&req);
int bytesDone = uv_fs_write(nullptr, &req, fd, &wbf, 1, 0, nullptr);
uv_fs_close(nullptr, &req, fd, nullptr);
if (bytesDone < 0 || static_cast<size_t>(bytesDone) != bufLen) {
char buffer[BUF_SIZE_DEFAULT] = { 0 };
uv_strerror_r((int)req.result, buffer, BUF_SIZE_DEFAULT);
uv_fs_req_cleanup(&req);
if (GetCaller() == Caller::CLIENT) {
WRITE_LOG(LOG_FATAL, "WriteBinFile uv_fs_write %s error %s bytesDone:%d bufLen:%llu",
resolvedPath.c_str(), buffer, bytesDone, bufLen);
} else {
WRITE_LOG(LOG_FATAL, "WriteBinFile uv_fs_write %s error %s bytesDone:%d bufLen:%llu",
Hdc::MaskString(resolvedPath).c_str(), buffer, bytesDone, bufLen);
}
return ERR_BUF_SIZE;
}
return RET_SUCCESS;
}
void CloseIdleCallback(uv_handle_t *handle)
{
delete (uv_idle_t *)handle;
};
void CloseTimerCallback(uv_handle_t *handle)
{
delete (uv_timer_t *)handle;
};
int ProgramMutex(bool checkOrNew)
{
char bufPath[BUF_SIZE_DEFAULT] = "";
char buf[BUF_SIZE_DEFAULT] = "";
char pidBuf[BUF_SIZE_TINY] = "";
size_t size = sizeof(buf);
#ifdef HOST_OHOS
if (uv_os_homedir(buf, &size) < 0) {
WRITE_LOG(LOG_FATAL, "Homepath failed");
return ERR_API_FAIL;
}
#else
if (uv_os_tmpdir(buf, &size) < 0) {
WRITE_LOG(LOG_FATAL, "Tmppath failed");
return ERR_API_FAIL;
}
#endif
if (snprintf_s(bufPath, sizeof(bufPath), sizeof(bufPath) - 1, "%s%c.%s.pid",
buf, Base::GetPathSep(), SERVER_NAME.c_str()) < 0) {
return ERR_BUF_OVERFLOW;
}
int pid = static_cast<int>(getpid());
if (snprintf_s(pidBuf, sizeof(pidBuf), sizeof(pidBuf) - 1, "%d", pid) < 0) {
return ERR_BUF_OVERFLOW;
}
if (GetCaller() == Caller::CLIENT) {
WRITE_LOG(LOG_DEBUG, "bufPath:%s pidBuf:%s", bufPath, pidBuf);
} else {
WRITE_LOG(LOG_DEBUG, "bufPath:%s pidBuf:%s", Hdc::MaskString(string(bufPath)).c_str(), pidBuf);
}
umask(0);
uv_fs_t req;
int fd = uv_fs_open(nullptr, &req, bufPath, O_RDWR | O_CREAT, 0666, nullptr);
if (fd < 0) {
char buffer[BUF_SIZE_DEFAULT] = { 0 };
uv_strerror_r((int)req.result, buffer, BUF_SIZE_DEFAULT);
uv_fs_req_cleanup(&req);
if (GetCaller() == Caller::CLIENT) {
WRITE_LOG(LOG_DEBUG, "Open mutex file %s failed!!! %s", bufPath, buffer);
} else {
WRITE_LOG(LOG_DEBUG, "Open mutex file %s failed!!! %s",
Hdc::MaskString(string(bufPath)).c_str(), buffer);
}
return ERR_FILE_OPEN;
}
#ifdef _WIN32
if (snprintf_s(buf, sizeof(buf), sizeof(buf) - 1, "Global\\%s", SERVER_NAME.c_str()) < 0) {
uv_fs_close(nullptr, &req, fd, nullptr);
return ERR_BUF_OVERFLOW;
}
if (checkOrNew) {
uv_fs_close(nullptr, &req, fd, nullptr);
HANDLE hMutex = OpenMutex(SYNCHRONIZE, FALSE, buf);
if (hMutex != nullptr) {
CloseHandle(hMutex);
WRITE_LOG(LOG_DEBUG, "Mutex \"%s\" locked. Server already exist.", buf);
return 1;
} else {
WRITE_LOG(LOG_DEBUG, "Server is not exist %s error:%d", buf, GetLastError());
return 0;
}
} else {
SECURITY_DESCRIPTOR sd;
if (!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) {
WRITE_LOG(LOG_DEBUG, "init security descriptor error:%d", GetLastError());
return 1;
}
if (!SetSecurityDescriptorDacl(&sd, TRUE, NULL, FALSE)) {
WRITE_LOG(LOG_DEBUG, "set security descriptor dacl error:%d", GetLastError());
return 1;
}
SECURITY_ATTRIBUTES sa = { sizeof(SECURITY_ATTRIBUTES), &sd, FALSE };
HANDLE hMutex = CreateMutex(&sa, TRUE, buf);
DWORD dwError = GetLastError();
if (ERROR_ALREADY_EXISTS == dwError || ERROR_ACCESS_DENIED == dwError) {
uv_fs_close(nullptr, &req, fd, nullptr);
WRITE_LOG(LOG_DEBUG, "Creat mutex, \"%s\" locked. proc already exit!!!\n", SERVER_NAME.c_str());
return 1;
}
}
#else
struct flock fl;
fl.l_type = F_WRLCK;
fl.l_start = 0;
fl.l_whence = SEEK_SET;
fl.l_len = 0;
int retChild = fcntl(fd, F_SETLK, &fl);
if (retChild == -1) {
if (GetCaller() == Caller::CLIENT) {
WRITE_LOG(LOG_DEBUG, "File \"%s\" locked. proc already exit!!!\n", bufPath);
} else {
WRITE_LOG(LOG_DEBUG, "File \"%s\" locked. proc already exit!!!\n",
Hdc::MaskString(string(bufPath)).c_str());
}
uv_fs_close(nullptr, &req, fd, nullptr);
return 1;
}
#endif
int rc = 0;
uv_fs_req_cleanup(&req);
rc = uv_fs_ftruncate(nullptr, &req, fd, 0, nullptr);
if (rc == -1) {
char buffer[BUF_SIZE_DEFAULT] = { 0 };
uv_strerror_r((int)req.result, buffer, BUF_SIZE_DEFAULT);
uv_fs_close(nullptr, &req, fd, nullptr);
if (GetCaller() == Caller::CLIENT) {
WRITE_LOG(LOG_FATAL, "ftruncate file %s failed!!! %s", bufPath, buffer);
} else {
WRITE_LOG(LOG_FATAL, "ftruncate file %s failed!!! %s",
Hdc::MaskString(string(bufPath)).c_str(), buffer);
}
return ERR_FILE_STAT;
}
uv_buf_t wbf = uv_buf_init(pidBuf, strlen(pidBuf));
uv_fs_req_cleanup(&req);
rc = uv_fs_write(nullptr, &req, fd, &wbf, 1, 0, nullptr);
if (rc == -1) {
char buffer[BUF_SIZE_DEFAULT] = { 0 };
uv_strerror_r((int)req.result, buffer, BUF_SIZE_DEFAULT);
uv_fs_close(nullptr, &req, fd, nullptr);
if (GetCaller() == Caller::CLIENT) {
WRITE_LOG(LOG_FATAL, "write file %s failed!!! %s", bufPath, buffer);
} else {
WRITE_LOG(LOG_FATAL, "write file %s failed!!! %s", Hdc::MaskString(string(bufPath)).c_str(), buffer);
}
return ERR_FILE_WRITE;
}
if (GetCaller() == Caller::CLIENT) {
WRITE_LOG(LOG_DEBUG, "Write mutext to %s, pid:%s", bufPath, pidBuf);
} else {
WRITE_LOG(LOG_DEBUG, "Write mutext to %s, pid:%s", Hdc::MaskString(string(bufPath)).c_str(), pidBuf);
}
if (checkOrNew) {
uv_fs_close(nullptr, &req, fd, nullptr);
}
return RET_SUCCESS;
}
void SplitString(const string &origString, const string &seq, vector<string> &resultStrings)
{
string::size_type p1 = 0;
string::size_type p2 = origString.find(seq);
while (p2 != string::npos) {
if (p2 == p1) {
++p1;
p2 = origString.find(seq, p1);
continue;
}
resultStrings.push_back(origString.substr(p1, p2 - p1));
p1 = p2 + seq.size();
p2 = origString.find(seq, p1);
}
if (p1 != origString.size()) {
resultStrings.push_back(origString.substr(p1));
}
}
string GetShellPath()
{
struct stat filecheck;
string shellPath = "/bin/sh";
if (stat(shellPath.c_str(), &filecheck) < 0) {
shellPath = "/system/bin/sh";
if (stat(shellPath.c_str(), &filecheck) < 0) {
shellPath = "sh";
}
}
return shellPath;
}
uint64_t HostToNet(uint64_t val)
{
if (htonl(1) == 1) {
return val;
}
int offset = 32;
return ((static_cast<uint64_t>(htonl(val))) << offset) + htonl(val >> offset);
}
uint64_t NetToHost(uint64_t val)
{
if (htonl(1) == 1) {
return val;
}
int offset = 32;
return ((static_cast<uint64_t>(ntohl(val))) << offset) + ntohl(val >> offset);
}
char GetPathSep()
{
#ifdef _WIN32
const char sep = '\\';
#else
const char sep = '/';
#endif
return sep;
}
string GetFullFilePath(string &s)
{
while (s.back() == GetPathSep()) {
s.pop_back();
}
size_t i = s.rfind(GetPathSep(), s.length());
if (i != string::npos) {
return (s.substr(i + 1, s.length() - i));
}
return s;
}
string GetPathWithoutFilename(const string &s)
{
size_t i = s.rfind(GetPathSep(), s.length());
if (i != string::npos) {
return (s.substr(0, i + 1));
}
return s;
}
string GetHdcAbsolutePath()
{
char path[BUF_SIZE_DEFAULT4] = { 0 };
size_t nPathSize = sizeof(path);
int ret = uv_exepath(path, &nPathSize);
if (ret < 0) {
char buf[BUF_SIZE_DEFAULT] = { 0 };
uv_err_name_r(ret, buf, BUF_SIZE_DEFAULT);
WRITE_LOG(LOG_WARN, "uvexepath ret:%d error:%s", ret, buf);
return "";
}
return string(path);
}
int CreateSocketPair(int *fds)
{
#ifndef _WIN32
#ifdef HOST_MAC
int ret = socketpair(AF_UNIX, SOCK_STREAM, 0, fds);
if (ret == 0) {
for (auto i = 0; i < STREAM_SIZE; ++i) {
if (fcntl(fds[i], F_SETFD, FD_CLOEXEC) == -1) {
CloseFd(fds[0]);
CloseFd(fds[1]);
constexpr int bufSize = 1024;
char buf[bufSize] = { 0 };
strerror_r(errno, buf, bufSize);
WRITE_LOG(LOG_WARN, "fcntl failed to set FD_CLOEXEC: %s", buf);
return -1;
}
}
}
return ret;
#else
return socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, fds);
#endif
#else
int reuse = 1;
if (fds == 0) {
return -1;
}
int listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (listener == -1) {
return -2;
}
fds[0] = fds[1] = (int)-1;
do {
struct sockaddr_in addr;
(void)memset_s(&addr, sizeof(sockaddr_in), 0, sizeof(sockaddr_in));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
addr.sin_port = 0;
if (setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, (socklen_t)sizeof(reuse))) {
break;
}
if (::bind(listener, (struct sockaddr *)&addr, sizeof(addr))) {
break;
}
socklen_t addrlen = sizeof(addr);
if (getsockname(listener, (struct sockaddr *)&addr, &addrlen)) {
break;
}
if (listen(listener, 1)) {
break;
}
fds[0] = socket(AF_INET, SOCK_STREAM, 0);
if (fds[0] == -1) {
break;
}
if (connect(fds[0], (struct sockaddr *)&addr, sizeof(addr)) == -1) {
break;
}
fds[1] = accept(listener, nullptr, nullptr);
if (fds[1] == -1) {
break;
}
closesocket(listener);
return 0;
} while (0);
closesocket(listener);
closesocket(fds[0]);
closesocket(fds[1]);
return -1;
#endif
}
void CloseSocketPair(int *fds)
{
#ifndef _WIN32
CloseFd(fds[0]);
CloseFd(fds[1]);
#else
closesocket(fds[0]);
closesocket(fds[1]);
#endif
}
int StringEndsWith(string s, string sub)
{
return s.rfind(sub) == (s.length() - sub.length()) ? 1 : 0;
}
const char *GetFileType(mode_t mode)
{
switch (mode & S_IFMT) {
case S_IFDIR:
return "directory";
case S_IFLNK:
return "symlink";
case S_IFREG:
return "regular file";
#ifndef _WIN32
case S_IFBLK:
return "block device";
case S_IFCHR:
return "character device";
case S_IFIFO:
return "FIFO/pipe";
case S_IFSOCK:
return "socket";
#endif
default:
return "Unknown";
}
}
void BuildErrorString(const char *localPath, const char *op, const char *err, string &str)
{
str = op;
str += " ";
str += localPath;
str += " failed, ";
str += err;
}
bool CheckDirectoryOrPath(const char *localPath, bool pathOrDir, bool readWrite, string &errStr, mode_t &fm)
{
if (pathOrDir) {
uv_fs_t req;
mode_t mode;
fm = mode_t(~S_IFMT);
int r = uv_fs_lstat(nullptr, &req, localPath, nullptr);
if (r) {
constexpr int bufSize = 1024;
char buf[bufSize] = { 0 };
uv_strerror_r((int)req.result, buf, bufSize);
BuildErrorString(localPath, "lstat", buf, errStr);
}
mode = req.statbuf.st_mode;
uv_fs_req_cleanup(&req);
if ((r == 0) && (mode & S_IFDIR)) {
fm = S_IFDIR;
} else if ((r == 0) && (mode & S_IFREG)) {
uv_fs_access(nullptr, &req, localPath, readWrite ? R_OK : W_OK, nullptr);
if (req.result) {
const char *op = readWrite ? "access R_OK" : "access W_OK";
constexpr int bufSize = 1024;
char buf[bufSize] = { 0 };
uv_strerror_r((int)req.result, buf, bufSize);
BuildErrorString(localPath, op, buf, errStr);
}
uv_fs_req_cleanup(&req);
if (req.result == 0) {
return true;
}
} else if (r == 0) {
const char *type = GetFileType(mode);
errStr = "Not support ";
errStr += type;
errStr += ": ";
errStr += localPath;
}
} else {
errStr = "Not support dir: ";
errStr += localPath;
}
return false;
}
bool TryCreateDirectory(const string &path, string &err)
{
uv_fs_t req;
int r = uv_fs_lstat(nullptr, &req, path.c_str(), nullptr);
uv_fs_req_cleanup(&req);
if (r < 0) {
r = uv_fs_mkdir(nullptr, &req, path.c_str(), DEF_FILE_PERMISSION, nullptr);
if (GetCaller() == Caller::CLIENT) {
WRITE_LOG(LOG_DEBUG, "path not exist create dir = %s", path.c_str());
} else {
WRITE_LOG(LOG_DEBUG, "path not exist create dir = %s", Hdc::MaskString(path).c_str());
}
uv_fs_req_cleanup(&req);
if (r < 0) {
constexpr int bufSize = 1024;
char buf[bufSize] = { 0 };
uv_strerror_r((int)req.result, buf, bufSize);
if (GetCaller() == Caller::CLIENT) {
WRITE_LOG(LOG_WARN, "create dir %s failed %s", path.c_str(), buf);
} else {
WRITE_LOG(LOG_WARN, "create dir %s failed %s", Hdc::MaskString(path).c_str(), buf);
}
err = "[E005005] Error create directory: " + string(buf) + ", path:" + path;
return false;
}
} else {
mode_t mode = req.statbuf.st_mode;
if (!((mode & S_IFMT) == S_IFDIR)) {
if (GetCaller() == Caller::CLIENT) {
WRITE_LOG(LOG_WARN, "%s exist, not directory", path.c_str());
} else {
WRITE_LOG(LOG_WARN, "%s exist, not directory", Hdc::MaskString(path).c_str());
}
err = "File exists, path:";
err += path.c_str();
return false;
}
}
return true;
}
bool CheckDirectoryOrPath(const char *localPath, bool pathOrDir, bool readWrite)
{
string strUnused;
mode_t mode = mode_t(~S_IFMT);
return CheckDirectoryOrPath(localPath, pathOrDir, readWrite, strUnused, mode);
}
int Base64EncodeBuf(const uint8_t *input, const int length, uint8_t *bufOut)
{
return EVP_EncodeBlock(bufOut, input, length);
}
vector<uint8_t> Base64Encode(const uint8_t *input, const int length)
{
vector<uint8_t> retVec;
uint8_t *pBuf = nullptr;
while (true) {
if (static_cast<uint32_t>(length) > HDC_BUF_MAX_BYTES) {
break;
}
int base64Size = length * 1.4 + 256;
if (!(pBuf = new uint8_t[base64Size]())) {
break;
}
int childRet = Base64EncodeBuf(input, length, pBuf);
if (childRet <= 0) {
break;
}
retVec.insert(retVec.begin(), pBuf, pBuf + childRet);
break;
}
if (pBuf) {
delete[] pBuf;
}
return retVec;
}
inline int CalcDecodeLength(const uint8_t *b64input)
{
if (b64input == nullptr) {
return 0;
}
int len = strlen(reinterpret_cast<char *>(const_cast<uint8_t *>(b64input)));
if (len < LAST_EQUAL_NUM) {
return 0;
}
int padding = 0;
if (b64input[len - 1] == '=' && b64input[len - LAST_EQUAL_NUM] == '=') {
padding = 2;
} else if (b64input[len - 1] == '=') {
padding = 1;
}
return static_cast<int>(len * DECODE_SCALE - padding);
}
int Base64DecodeBuf(const uint8_t *input, const int length, uint8_t *bufOut)
{
int nRetLen = CalcDecodeLength(input);
if (!nRetLen) {
return 0;
}
if (EVP_DecodeBlock(bufOut, input, length) > 0) {
return nRetLen;
}
return 0;
}
string Base64Decode(const uint8_t *input, const int length)
{
string retString;
uint8_t *pBuf = nullptr;
while (true) {
if (static_cast<uint32_t>(length) > HDC_BUF_MAX_BYTES) {
break;
}
if (!(pBuf = new uint8_t[length]())) {
break;
}
int childRet = Base64DecodeBuf(input, length, pBuf);
if (childRet <= 0) {
break;
}
retString = (reinterpret_cast<char *>(pBuf));
break;
}
if (pBuf) {
delete[] pBuf;
}
return retString;
}
void ReverseBytes(void *start, int size)
{
uint8_t *istart = (uint8_t *)start;
uint8_t *iend = istart + size;
std::reverse(istart, iend);
}
string Convert2HexStr(uint8_t arr[], int length)
{
std::stringstream ss;
const int byteHexStrLen = 2;
for (int i = 0; i < length; i++) {
ss << std::hex << std::setw(byteHexStrLen) << std::setfill('0')
<< static_cast<int>(arr[i]);
if (i < length - 1) {
ss << ":";
}
}
string result = ss.str();
transform(result.begin(), result.end(), result.begin(), ::toupper);
return result;
}
const string StringFormat(const char * const formater, ...)
{
va_list vaArgs;
va_start(vaArgs, formater);
string ret = StringFormat(formater, vaArgs);
va_end(vaArgs);
return ret;
}
const string StringFormat(const char * const formater, va_list &vaArgs)
{
std::vector<char> args(GetMaxBufSizeStable());
const int retSize = vsnprintf_s(
args.data(), GetMaxBufSizeStable(), (args.size() >= 1) ? (args.size() - 1) : 0, formater, vaArgs);
if (retSize < 0) {
return std::string("");
} else {
return std::string(args.data(), retSize);
}
}
string GetVersion()
{
const uint8_t a = 'a';
uint8_t major = (HDC_VERSION_NUMBER >> 28) & 0xff;
uint8_t minor = (HDC_VERSION_NUMBER << 4 >> 24) & 0xff;
uint8_t version = (HDC_VERSION_NUMBER << 12 >> 24) & 0xff;
uint8_t fix = (HDC_VERSION_NUMBER << 20 >> 28) & 0xff;
string ver = StringFormat("%x.%x.%x%c", major, minor, version, a + fix);
return "Ver: " + ver;
}
bool IdleUvTask(uv_loop_t *loop, void *data, uv_idle_cb cb)
{
uv_idle_t *idle = new(std::nothrow) uv_idle_t();
if (idle == nullptr) {
return false;
}
idle->data = data;
uv_idle_init(loop, idle);
uv_idle_start(idle, cb);
return true;
}
bool TimerUvTask(uv_loop_t *loop, void *data, uv_timer_cb cb, int repeatTimeout)
{
uv_timer_t *timer = new(std::nothrow) uv_timer_t();
if (timer == nullptr) {
return false;
}
timer->data = data;
uv_timer_init(loop, timer);
uv_timer_start(timer, cb, 0, repeatTimeout);
return true;
}
bool DelayDo(uv_loop_t *loop, const int delayMs, const uint8_t flag, string msg, void *data,
std::function<void(const uint8_t, string &, const void *)> cb)
{
struct DelayDoParam {
uv_timer_t handle;
uint8_t flag;
string msg;
void *data;
std::function<void(const uint8_t, string &, const void *)> cb;
};
auto funcDelayDo = [](uv_timer_t *handle) -> void {
DelayDoParam *st = (DelayDoParam *)handle->data;
st->cb(st->flag, st->msg, st->data);
uv_close((uv_handle_t *)handle, [](uv_handle_t *handle) {
DelayDoParam *st = (DelayDoParam *)handle->data;
delete st;
});
};
DelayDoParam *st = new(std::nothrow) DelayDoParam();
if (st == nullptr) {
return false;
}
st->cb = cb;
st->flag = flag;
st->msg = msg;
st->data = data;
st->handle.data = st;
uv_timer_init(loop, &st->handle);
uv_timer_start(&st->handle, funcDelayDo, delayMs, 0);
return true;
}
string ReplaceAll(string str, const string from, const string to)
{
string::size_type startPos = 0;
while ((startPos = str.find(from, startPos)) != string::npos) {
str.replace(startPos, from.length(), to);
startPos += to.length();
}
return str;
}
string CanonicalizeSpecPath(string &src)
{
char resolvedPath[PATH_MAX] = { 0 };
#if defined(_WIN32)
if (!_fullpath(resolvedPath, src.c_str(), PATH_MAX)) {
if (GetCaller() == Caller::CLIENT) {
WRITE_LOG(LOG_FATAL, "_fullpath %s failed", src.c_str());
} else {
WRITE_LOG(LOG_FATAL, "_fullpath %s failed", Hdc::MaskString(src).c_str());
}
return "";
}
#else
if (realpath(src.c_str(), resolvedPath) == nullptr) {
if (GetCaller() == Caller::CLIENT) {
WRITE_LOG(LOG_FATAL, "realpath %s failed", src.c_str());
} else {
WRITE_LOG(LOG_FATAL, "realpath %s failed", Hdc::MaskString(src).c_str());
}
return "";
}
#endif
string res(resolvedPath);
return res;
}
string UnicodeToUtf8(const char *src, bool reverse)
{
#if defined(_WIN32)
UINT from = CP_ACP;
UINT to = CP_UTF8;
int count = 0;
if (reverse) {
from = CP_UTF8;
to = CP_ACP;
}
count = MultiByteToWideChar(from, 0, src, -1, nullptr, 0);
if (count <= 0) {
DWORD err = GetLastError();
WRITE_LOG(LOG_FATAL, "MultiByteToWideChar failed %s error:%lu", src, err);
return "";
}
wchar_t *wstr = new(std::nothrow) wchar_t[count + 1]();
if (wstr == nullptr) {
WRITE_LOG(LOG_FATAL, "new wstr failed count:%d", count);
return "";
}
count = MultiByteToWideChar(from, 0, src, -1, wstr, count);
if (count <= 0) {
DWORD err = GetLastError();
WRITE_LOG(LOG_FATAL, "MultiByteToWideChar failed to wstr %s error:%lu", src, err);
delete[] wstr;
return "";
}
count = WideCharToMultiByte(to, 0, wstr, -1, nullptr, 0, nullptr, nullptr);
if (count <= 0) {
DWORD err = GetLastError();
WRITE_LOG(LOG_FATAL, "WideCharToMultiByte failed %s error:%lu", wstr, err);
delete[] wstr;
return "";
}
char *ustr = new(std::nothrow) char[count + 1]();
if (ustr == nullptr) {
WRITE_LOG(LOG_FATAL, "new ustr failed count:%d", count);
delete[] wstr;
return "";
}
count = WideCharToMultiByte(to, 0, wstr, -1, ustr, count, nullptr, nullptr);
if (count <= 0) {
DWORD err = GetLastError();
WRITE_LOG(LOG_FATAL, "WideCharToMultiByte failed to ustr %s error:%lu", wstr, err);
delete[] wstr;
delete[] ustr;
return "";
}
string rc(ustr);
delete[] wstr;
delete[] ustr;
return rc;
#else
string rc(src);
return rc;
#endif
}
uint8_t CalcCheckSum(const uint8_t *data, int len)
{
uint8_t ret = 0;
for (int i = 0; i < len; ++i) {
ret += data[i];
}
return ret;
}
uv_os_sock_t DuplicateUvSocket(uv_tcp_t *tcp)
{
uv_os_sock_t dupFd = -1;
#ifdef _WIN32
WSAPROTOCOL_INFO info;
(void)memset_s(&info, sizeof(WSAPROTOCOL_INFO), 0, sizeof(WSAPROTOCOL_INFO));
if (WSADuplicateSocketA(tcp->socket, GetCurrentProcessId(), &info) < 0) {
return dupFd;
}
dupFd = WSASocketA(0, 0, 0, &info, 0, 0);
#else
uv_os_fd_t fdOs;
if (uv_fileno((const uv_handle_t *)tcp, &fdOs) < 0) {
return ERR_API_FAIL;
}
#ifdef HDC_HOST
dupFd = dup(uv_open_osfhandle(fdOs));
#else
dupFd = fcntl(uv_open_osfhandle(fdOs), F_DUPFD_CLOEXEC, uv_open_osfhandle(fdOs));
#endif
#endif
return dupFd;
}
#ifdef HOST_OHOS
uv_os_sock_t DuplicateUvPipe(uv_pipe_t *pipe)
{
uv_os_sock_t dupFd = -1;
uv_os_fd_t fdOs;
if (uv_fileno((const uv_handle_t *)pipe, &fdOs) < 0) {
return ERR_API_FAIL;
}
dupFd = dup(uv_open_osfhandle(fdOs));
return dupFd;
}
#endif
string GetCwd()
{
int value = -1;
char path[PATH_MAX] = "";
size_t size = sizeof(path);
string res;
value = uv_cwd(path, &size);
if (value < 0) {
constexpr int bufSize = 1024;
char buf[bufSize] = { 0 };
uv_strerror_r(value, buf, bufSize);
WRITE_LOG(LOG_FATAL, "get path failed: %s", buf);
return res;
}
size_t len = 0;
len = strlen(path);
if (len < 1 || len >= PATH_MAX - 1) {
WRITE_LOG(LOG_FATAL, "get path failed: buffer space max");
return res;
}
if (path[len - 1] != Base::GetPathSep()) {
path[len] = Base::GetPathSep();
}
res = path;
return res;
}
void UpdateTmpDirCache()
{
#ifdef HDC_HOST
int value = -1;
char path[PATH_MAX] = "";
size_t size = sizeof(path);
#ifdef HOST_OHOS
value = uv_os_homedir(path, &size);
#else
value = uv_os_tmpdir(path, &size);
#endif
if (value < 0) {
constexpr int bufSize = 1024;
char buf[bufSize] = { 0 };
uv_strerror_r(value, buf, bufSize);
return;
}
if (strlen(path) >= PATH_MAX - 1) {
return;
}
if (path[strlen(path) - 1] != Base::GetPathSep()) {
path[strlen(path)] = Base::GetPathSep();
}
g_tempDir = path;
#else
g_tempDir = "/data/local/tmp/";
#endif
}
string GetTmpDir()
{
return g_tempDir;
}
#ifndef HDC_HILOG
void SetLogCache(bool enable)
{
g_logCache = enable;
}
void RemoveLogFile()
{
if (g_isSubserver) {
return;
}
if (g_logCache) {
string path = GetLogDirName() + LOG_FILE_NAME;
string bakName = GetLogNameWithTime();
string bakPath = GetLogDirName() + bakName;
string cachePath = GetLogDirName() + LOG_CACHE_NAME;
if (rename(path.c_str(), bakPath.c_str()) != 0) {
WRITE_LOG(LOG_FATAL, "rename log file failed.");
}
if (rename(cachePath.c_str(), path.c_str()) != 0) {
WRITE_LOG(LOG_FATAL, "rename log cache file failed.");
}
#ifdef FEATURE_HOST_LOG_COMPRESS
std::thread compressThread(ThreadCompressLog, bakName);
compressThread.detach();
#endif
g_logCache = false;
std::thread removeThread(RemoveOlderLogFiles);
removeThread.detach();
}
}
void RemoveLogCache()
{
string cachePath = GetLogDirName() + LOG_CACHE_NAME;
(void)unlink(cachePath.c_str());
}
#endif
bool IsRoot()
{
#ifdef _WIN32
return true;
#else
if (getuid() == 0) {
return true;
}
#endif
return false;
}
bool IsAbsolutePath(string &path)
{
bool ret = false;
#ifdef _WIN32
ret = path.find(":\\") == 1 || path.find("\\\\") == 0;
#else
ret = path[0] == '/';
#endif
return ret;
}
int CloseFd(int &fd)
{
int rc = 0;
if (fd > 0) {
rc = close(fd);
if (rc < 0) {
char buffer[BUF_SIZE_DEFAULT] = { 0 };
#ifdef _WIN32
strerror_s(buffer, BUF_SIZE_DEFAULT, errno);
#else
strerror_r(errno, buffer, BUF_SIZE_DEFAULT);
#endif
WRITE_LOG(LOG_WARN, "close fd: %d failed errno:%d %s", fd, errno, buffer);
} else {
fd = -1;
}
}
return rc;
}
void CloseOpenFd(void)
{
#if !defined(_WIN32) && !defined(HOST_MAC)
pid_t pid = getpid();
char procPath[PATH_MAX];
int ret = sprintf_s(procPath, sizeof(procPath), "/proc/%d/fd", pid);
if (ret < 0) {
WRITE_LOG(LOG_FATAL, "get procPath failed, pid is %d", pid);
return;
}
DIR *dir = opendir("/proc/self/fd");
if (dir == nullptr) {
WRITE_LOG(LOG_FATAL, "open /proc/self/fd failed errno:%d", errno);
return;
}
char path[PATH_MAX] = { 0 };
char target[PATH_MAX] = { 0 };
struct dirent *dp = nullptr;
while ((dp = readdir(dir)) != nullptr) {
if (dp->d_type != DT_LNK) {
continue;
}
ret = sprintf_s(path, sizeof(path), "/proc/self/fd/%s", dp->d_name);
if (ret < 0) {
WRITE_LOG(LOG_FATAL, "get path failed, dp->d_name is %s", dp->d_name);
break;
}
int len = readlink(path, target, sizeof(target));
errno = 0;
int fd = static_cast<int>(strtol(dp->d_name, nullptr, 10));
if (strncmp(procPath, target, len) != 0 && errno == 0) {
CloseFd(fd);
}
}
closedir(dir);
return;
#elif defined(HOST_MAC)
int i;
const int maxFD = 1024;
for (i = 0; i < maxFD; ++i) {
int fd = i;
Base::CloseFd(fd);
}
#endif
}
void InitProcess(void)
{
#ifndef _WIN32
umask(0);
signal(SIGPIPE, SIG_IGN);
signal(SIGCHLD, SIG_IGN);
signal(SIGALRM, SIG_IGN);
signal(SIGTTIN, SIG_IGN);
signal(SIGTTOU, SIG_IGN);
#endif
}
void DeInitProcess(void)
{
#ifndef _WIN32
mode_t writePermission = 022;
umask(writePermission);
signal(SIGPIPE, SIG_DFL);
signal(SIGCHLD, SIG_DFL);
signal(SIGALRM, SIG_DFL);
signal(SIGTTIN, SIG_DFL);
signal(SIGTTOU, SIG_DFL);
#endif
}
int ReadFromFd(int fd, void *buf, size_t count)
{
#ifdef _WIN32
DWORD bytesRead = 0;
OVERLAPPED ov = {};
SOCKET s = fd;
BOOL bRead = ReadFile((HANDLE)s, buf, count, &bytesRead, &ov);
if (bRead) {
return bytesRead;
}
DWORD error = GetLastError();
if (error != ERROR_IO_PENDING) {
WRITE_LOG(LOG_DEBUG, "ReadFile error:%d", error);
return -1;
}
constexpr int ms = 3000;
bRead = GetOverlappedResultEx((HANDLE)s, &ov, &bytesRead, ms, FALSE);
if (bRead) {
return bytesRead;
}
error = GetLastError();
WRITE_LOG(LOG_DEBUG, "read GetOverlappedResult error:%d", error);
return -1;
#else
return TEMP_FAILURE_RETRY(read(fd, buf, count));
#endif
}
int WriteToFd(int fd, const void *buf, size_t count)
{
#ifdef _WIN32
DWORD bytesWrite = 0;
OVERLAPPED ov = {};
SOCKET s = fd;
BOOL bWrite = WriteFile((HANDLE)s, buf, count, &bytesWrite, &ov);
if (bWrite) {
return 1;
}
DWORD error = GetLastError();
if (error != ERROR_IO_PENDING) {
WRITE_LOG(LOG_DEBUG, "WriteFile error:%d", error);
return -1;
}
constexpr int ms = 3000;
bWrite = GetOverlappedResultEx((HANDLE)s, &ov, &bytesWrite, ms, FALSE);
if (bWrite) {
return 1;
}
error = GetLastError();
WRITE_LOG(LOG_DEBUG, "write GetOverlappedResult error:%d", error);
return -1;
#else
return TEMP_FAILURE_RETRY(write(fd, buf, count));
#endif
}
bool IsValidIpv4(const std::string& ip)
{
std::vector<int> segments;
std::stringstream ss(ip);
std::string segment;
const int ipCount = 4;
const int maxValue = 255;
while (std::getline(ss, segment, '.')) {
if (segments.size() >= ipCount) {
return false;
}
if (!IsDigitString(segment)) {
return false;
}
int num = static_cast<int>(strtol(segment.c_str(), nullptr, 10));
if (num < 0 || num > maxValue) {
return false;
}
if (segment.size() > 1 && segment[0] == '0' && segment != "0") {
return false;
}
segments.push_back(num);
}
return segments.size() == ipCount;
}
bool StringToLong(const std::string& str, long int &out)
{
if (str.empty()) {
WRITE_LOG(LOG_DEBUG, "empty string");
return false;
}
constexpr int base = 10;
char *endptr = nullptr;
errno = 0;
out = strtol(str.c_str(), &endptr, base);
if (*endptr != '\0') {
WRITE_LOG(LOG_DEBUG, "invalid string");
return false;
}
if (errno == ERANGE) {
WRITE_LOG(LOG_DEBUG, "an underflow or overflow occurs");
return false;
}
return true;
}
bool StringToInt(const std::string& str, int& out)
{
if (str.empty()) {
WRITE_LOG(LOG_DEBUG, "empty string");
return false;
}
constexpr int base = 10;
char* endptr = nullptr;
errno = 0;
long int longResult = strtol(str.c_str(), &endptr, base);
if (*endptr != '\0') {
WRITE_LOG(LOG_DEBUG, "invalid string");
return false;
}
if (errno == ERANGE || longResult > INT_MAX || longResult < INT_MIN) {
WRITE_LOG(LOG_DEBUG, "an underflow or overflow occurs");
return false;
}
out = static_cast<int>(longResult);
return true;
}
string &ShellCmdTrim(string &cmd)
{
const string pairedQuot("\"\"");
if (cmd.empty()) {
return cmd;
}
cmd = Trim(cmd);
if (cmd.length() < pairedQuot.length()) {
return cmd;
}
if (*cmd.begin() == '"' && cmd.back() == '"') {
cmd.erase(cmd.begin());
cmd.pop_back();
}
return cmd;
}
void TrimSubString(string &str, string substr)
{
std::string::size_type pos = 0;
while ((pos = str.find(substr, pos)) != std::string::npos) {
str.erase(pos, substr.length());
}
}
bool TlvAppend(string &tlv, string tag, string val)
{
if (tag.empty()) {
return false;
}
unsigned int tlen = tag.length();
if (tlen < TLV_TAG_LEN) {
tag.append(TLV_TAG_LEN - tlen, ' ');
}
tlv.append(tag);
string vallen = std::to_string(val.length());
unsigned int vlen = vallen.length();
if (vlen < TLV_VAL_LEN) {
vallen.append(TLV_VAL_LEN - vlen, ' ');
}
tlv.append(vallen);
tlv.append(val);
return true;
}
bool TlvToStringMap(string tlv, std::map<string, string> &tlvmap)
{
if (tlv.length() < TLV_MIN_LEN) {
return false;
}
while (tlv.length() >= TLV_MIN_LEN) {
string tag = tlv.substr(0, TLV_TAG_LEN);
TrimSubString(tag, " ");
tlv.erase(0, TLV_TAG_LEN);
string vallen = tlv.substr(0, TLV_VAL_LEN);
TrimSubString(vallen, " ");
int len = atoi(vallen.c_str());
if (len < 0) {
return false;
}
tlv.erase(0, TLV_VAL_LEN);
if (tlv.length() < static_cast<uint32_t>(len)) {
return false;
}
string val = tlv.substr(0, len);
tlv.erase(0, len);
tlvmap[tag] = val;
}
return true;
}
FILE *Fopen(const char *fileName, const char *mode)
{
#ifdef _WIN32
wchar_t resolvedPath[PATH_MAX + 1] = { 0 };
std::wstring_convert<std::codecvt_utf8<wchar_t>> converter;
std::wstring wideFileName = converter.from_bytes(fileName);
std::wstring wideMode = converter.from_bytes(mode);
if (!_wfullpath(resolvedPath, wideFileName.c_str(), PATH_MAX + 1)) {
if (GetCaller() == Caller::CLIENT) {
WRITE_LOG(LOG_FATAL, "_wfullpath %s failed", wideFileName.c_str());
} else {
WRITE_LOG(LOG_FATAL, "_wfullpath %s failed", Hdc::MaskString(fileName).c_str());
}
return nullptr;
}
return _wfopen(resolvedPath, wideMode.c_str());
#else
return fopen(fileName, mode);
#endif
}
std::set<uint32_t> g_deletedSessionIdSet;
std::queue<uint32_t> g_deletedSessionIdQueue;
std::mutex g_deletedSessionIdRecordMutex;
void AddDeletedSessionId(uint32_t sessionId)
{
std::lock_guard<std::mutex> lock(g_deletedSessionIdRecordMutex);
std::string sessionIdMaskStr = Hdc::MaskSessionIdToString(sessionId);
if (g_deletedSessionIdSet.find(sessionId) != g_deletedSessionIdSet.end()) {
WRITE_LOG(LOG_INFO, "SessionId:%s is already in the cache", sessionIdMaskStr.c_str());
return;
}
WRITE_LOG(LOG_INFO, "AddDeletedSessionId:%s", sessionIdMaskStr.c_str());
g_deletedSessionIdSet.insert(sessionId);
g_deletedSessionIdQueue.push(sessionId);
if (g_deletedSessionIdQueue.size() > MAX_DELETED_SESSION_ID_RECORD_COUNT) {
uint32_t id = g_deletedSessionIdQueue.front();
WRITE_LOG(LOG_INFO, "g_deletedSessionIdQueue size:%u, g_deletedSessionIdSet size:%u, pop session id:%u",
g_deletedSessionIdQueue.size(), g_deletedSessionIdSet.size(), id);
g_deletedSessionIdQueue.pop();
g_deletedSessionIdSet.erase(id);
}
}
bool IsSessionDeleted(uint32_t sessionId)
{
std::lock_guard<std::mutex> lock(g_deletedSessionIdRecordMutex);
if (g_deletedSessionIdSet.find(sessionId) != g_deletedSessionIdSet.end()) {
return true;
}
return false;
}
bool CheckBundleName(const string &bundleName)
{
if (bundleName.empty()) {
WRITE_LOG(LOG_WARN, "bundleName is empty");
return false;
}
size_t length = bundleName.size();
if (length < BUNDLE_MIN_SIZE) {
WRITE_LOG(LOG_WARN, "bundleName length:%d is less than %d", length, BUNDLE_MIN_SIZE);
return false;
}
if (length > BUNDLE_MAX_SIZE) {
WRITE_LOG(LOG_WARN, "bundleName length:%d is bigger than %d", length, BUNDLE_MAX_SIZE);
return false;
}
if (regex_match(bundleName, std::regex("^[0-9a-zA-Z_\\.]+$"))) {
return true;
}
if (GetCaller() == Caller::CLIENT) {
WRITE_LOG(LOG_WARN, "bundleName:%s contains invalid characters", bundleName.c_str());
} else {
WRITE_LOG(LOG_WARN, "bundleName:%s contains invalid characters", Hdc::MaskString(bundleName).c_str());
}
return false;
}
bool CanPrintCmd(const uint16_t command)
{
switch (command) {
case CMD_APP_DATA:
case CMD_FILE_DATA:
case CMD_FORWARD_DATA:
case CMD_SHELL_DATA:
case CMD_UNITY_BUGREPORT_DATA:
return false;
default:
return true;
}
return true;
}
void UpdateEnvCache()
{
UpdateTmpDirCache();
#ifndef HDC_HILOG
UpdateLogLimitFileCountCache();
#endif
UpdateHeartbeatSwitchCache();
UpdateCmdLogSwitch();
UpdateEncrpytTCPCache();
}
const HdcFeatureSet& GetSupportFeature(void)
{
static HdcFeatureSet feature {
FEATURE_HEARTBEAT,
FEATURE_ENCRYPT_TCP,
};
return feature;
}
std::string FeatureToString(const HdcFeatureSet& feature)
{
std::string result;
for (unsigned long i = 0; i < feature.size(); i++) {
result += feature[i];
if (i == (feature.size() - 1)) {
break;
}
result += ",";
}
return result;
}
void StringToFeatureSet(const std::string featureStr, HdcFeatureSet& features)
{
return SplitString(featureStr, ",", features);
}
bool IsSupportFeature(const HdcFeatureSet& features, std::string feature)
{
return std::find(std::begin(features), std::end(features), feature) != std::end(features);
}
void UpdateHeartbeatSwitchCache()
{
char *env = getenv(ENV_SERVER_HEARTBEAT.c_str());
if (!env || strlen(env) > 1) {
g_heartbeatSwitch = true;
return;
}
g_heartbeatSwitch = strncmp(env, "1", 1);
}
bool GetheartbeatSwitch()
{
WRITE_LOG(LOG_WARN, "turn %s heartbeatSwitch", g_heartbeatSwitch ? "On" : "Off");
return g_heartbeatSwitch;
}
void UpdateEncrpytTCPCache()
{
char *envValue = getenv(ENV_ENCRYPT_CHANNEL.c_str());
if (!envValue || strlen(envValue) > 1) {
g_encryptTCPSwitch = false;
return;
}
g_encryptTCPSwitch = !strncmp(envValue, "1", 1);
}
bool GetEncrpytTCPSwitch()
{
WRITE_LOG(LOG_WARN, "turn %s encryptTCPSwitch", g_encryptTCPSwitch ? "On" : "Off");
return g_encryptTCPSwitch;
}
bool WriteToFile(const std::string& fileName, const std::string &content, std::ios_base::openmode mode)
{
std::ofstream fileStream(fileName.c_str(), mode);
if (!fileStream.is_open()) {
if (GetCaller() == Caller::CLIENT) {
WRITE_LOG(LOG_FATAL, "open %s error", fileName.c_str());
} else {
WRITE_LOG(LOG_FATAL, "open %s error", Hdc::MaskString(fileName).c_str());
}
return false;
}
bool ret = true;
fileStream.write(content.data(), content.length());
if (fileStream.fail()) {
if (GetCaller() == Caller::CLIENT) {
WRITE_LOG(LOG_FATAL, "write %s error", fileName.c_str());
} else {
WRITE_LOG(LOG_FATAL, "write %s error", Hdc::MaskString(fileName).c_str());
}
ret = false;
}
fileStream.flush();
fileStream.close();
return ret;
}
void UpdateCmdLogSwitch()
{
char *env = getenv(ENV_SERVER_CMD_LOG.c_str());
if (!env) {
g_cmdlogSwitch = false;
return;
}
size_t envLen = strlen(env);
if (envLen > 1) {
g_cmdlogSwitch = false;
return;
}
constexpr size_t maxLen = 1;
if (envLen != maxLen) {
g_cmdlogSwitch = false;
return;
}
if (strncmp(env, "1", maxLen) == 0) {
g_cmdlogSwitch = true;
return;
}
g_cmdlogSwitch = false;
}
bool GetCmdLogSwitch()
{
return g_cmdlogSwitch;
}
inline std::string GetCmdLogDirName()
{
return GetTmpDir() + CMD_LOG_DIR_NAME + GetPathSep();
}
void TimeoutHandler(int )
{
_exit(1);
}
#ifdef _WIN32
bool CompressLogFile(const std::string& filePath,
const std::string& sourceFileName,
const std::string& targetFileName)
{
bool retVal = false;
string sourceFileNameFull = filePath + sourceFileName;
if (access(sourceFileNameFull.c_str(), F_OK) != 0) {
if (GetCaller() == Caller::CLIENT) {
WRITE_LOG(LOG_FATAL, "file %s not exist", sourceFileNameFull.c_str());
} else {
WRITE_LOG(LOG_FATAL, "file %s not exist", Hdc::MaskString(sourceFileNameFull).c_str());
}
return retVal;
}
if (targetFileName.empty()) {
WRITE_LOG(LOG_FATAL, "file %s is empty", targetFileName.c_str());
return retVal;
}
char currentDir[BUF_SIZE_DEFAULT] = { 0 };
getcwd(currentDir, sizeof(currentDir));
char buf[BUF_SIZE_SMALL] = { 0 };
if (sprintf_s(buf, sizeof(buf) - 1, "tar czfp %s %s", targetFileName.c_str(), sourceFileName.c_str()) < 0) {
WRITE_LOG(LOG_FATAL, "sprintf_s failed");
return retVal;
}
chdir(filePath.c_str());
STARTUPINFO si = { 0 };
PROCESS_INFORMATION pi = { 0 };
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
if (!CreateProcess(GetTarBinFile().c_str(), buf, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) {
DWORD errorCode = GetLastError();
LPVOID messageBuffer;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, errorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&messageBuffer, 0, NULL);
WRITE_LOG(LOG_FATAL, "compress file failed ,cmd:%s,error:%s", buf, (LPTSTR)messageBuffer);
LocalFree(messageBuffer);
} else {
DWORD waitResult = WaitForSingleObject(pi.hProcess, INFINITE);
if (waitResult == WAIT_OBJECT_0) {
retVal = true;
} else if (waitResult == WAIT_TIMEOUT) {
retVal = true;
}
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
chdir(currentDir);
return retVal;
}
#else
bool CompressLogFile(const std::string& filePath,
const std::string& sourceFileName,
const std::string& targetFileName)
{
bool retVal = false;
string sourceFileNameFull = filePath + sourceFileName;
if (access(sourceFileNameFull.c_str(), F_OK) != 0) {
if (GetCaller() == Caller::CLIENT) {
WRITE_LOG(LOG_FATAL, "file %s not exist", sourceFileNameFull.c_str());
} else {
WRITE_LOG(LOG_FATAL, "file %s not exist", Hdc::MaskString(sourceFileNameFull).c_str());
}
return retVal;
}
if (targetFileName.empty()) {
WRITE_LOG(LOG_FATAL, "file %s is empty", targetFileName.c_str());
return retVal;
}
char currentDir[BUF_SIZE_DEFAULT] = { 0 };
getcwd(currentDir, sizeof(currentDir));
pid_t pc = fork();
chdir(filePath.c_str());
if (pc < 0) {
WRITE_LOG(LOG_FATAL, "fork subprocess failed");
} else if (pc == 0) {
signal(SIGALRM, TimeoutHandler);
alarm(MAX_PROCESS_TIMEOUT);
if ((execlp(GetTarToolName().c_str(),
GetTarToolName().c_str(),
GetTarParams().c_str(),
targetFileName.c_str(),
sourceFileName.c_str(),
nullptr)) == -1) {
WRITE_LOG(LOG_FATAL, "CompressLogFile execlp failed ");
_exit(EXEC_FAILED_ERROR_CODE);
}
} else {
int status = 0;
waitpid(pc, &status, 0);
if (WIFEXITED(status)) {
int exitCode = WEXITSTATUS(status);
WRITE_LOG(LOG_DEBUG, "subprocess exited with status: %d", exitCode);
retVal = true;
} else {
WRITE_LOG(LOG_FATAL, "CompressLogFile failed, soiurceFileNameFull:%s, error:%s",
sourceFileNameFull.c_str(),
strerror(errno));
}
}
chdir(currentDir);
return retVal;
}
#endif
bool CompressCmdLogAndRemove(const std::string& pathName,
const std::string& fileName,
const std::string& targetFileName)
{
std::string sourceFileName = pathName + fileName;
if (sourceFileName.empty()) {
WRITE_LOG(LOG_FATAL, "file name is empty");
return false;
}
if (access(sourceFileName.c_str(), F_OK) != 0) {
if (GetCaller() == Caller::SERVER) {
WRITE_LOG(LOG_FATAL, "path %s not exist", Hdc::MaskString(pathName).c_str());
} else {
WRITE_LOG(LOG_FATAL, "path %s not exist", pathName.c_str());
}
return false;
}
if (!CompressLogFile(pathName, fileName, targetFileName)) {
if (GetCaller() == Caller::CLIENT) {
WRITE_LOG(LOG_FATAL, "compress %s failed", targetFileName.c_str());
} else {
WRITE_LOG(LOG_FATAL, "compress %s failed", Hdc::MaskString(targetFileName).c_str());
}
return false;
}
if (unlink(sourceFileName.c_str()) != 0) {
WRITE_LOG(LOG_FATAL, "Failed to unlink file or symlink, error is :%s", strerror(errno));
}
return true;
}
bool IsFileSizeLessThan(const std::string& fileName, const size_t fileMaxSize)
{
uv_fs_t fs;
int value = uv_fs_stat(nullptr, &fs, fileName.c_str(), nullptr);
uint64_t fileSize = fs.statbuf.st_size;
uv_fs_req_cleanup(&fs);
if (value != 0) {
constexpr int bufSize = 1024;
char buf[bufSize] = { 0 };
uv_strerror_r(value, buf, bufSize);
if (GetCaller() == Caller::CLIENT) {
WRITE_LOG(LOG_FATAL, "uv_fs_stat failed, file:%s error:%s", fileName.c_str(), buf);
} else {
WRITE_LOG(LOG_FATAL, "uv_fs_stat failed, file:%s error:%s", Hdc::MaskString(fileName).c_str(), buf);
}
return true;
}
if (fileSize < fileMaxSize) {
return true;
}
return false;
}
vector<string> GetDirFileNameFromPath(const std::string& path,
const std::string& matchPreStr,
const std::string& matchSufStr)
{
g_cmdLogsFilesStrings.clear();
#ifdef _WIN32
WIN32_FIND_DATAA findData;
std::string matchStr = matchPreStr + "*" + matchSufStr;
std::string findFileMatchStr = path + "/" + matchStr;
HANDLE hFind = FindFirstFile(findFileMatchStr.c_str(), &findData);
if (hFind == INVALID_HANDLE_VALUE) {
if (GetCaller() == Caller::SERVER) {
WRITE_LOG(LOG_FATAL, "FindFirstFile failed, path:%s", Hdc::MaskString(findFileMatchStr).c_str());
} else {
WRITE_LOG(LOG_FATAL, "FindFirstFile failed, path:%s", findFileMatchStr.c_str());
}
return g_cmdLogsFilesStrings;
}
do {
if ((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) {
SetErrorMode(SEM_FAILCRITICALERRORS);
g_cmdLogsFilesStrings.push_back(findData.cFileName);
}
} while (FindNextFile(hFind, &findData));
#else
DIR *dir = opendir(path.c_str());
if (dir == nullptr) {
if (GetCaller() == Caller::SERVER) {
WRITE_LOG(LOG_WARN, "open %s failed", Hdc::MaskString(path).c_str());
} else {
WRITE_LOG(LOG_WARN, "open %s failed", path.c_str());
}
return g_cmdLogsFilesStrings;
}
struct dirent *entry;
while ((entry = readdir(dir)) != nullptr) {
string fileName = entry->d_name;
if ((strncmp(fileName.c_str(), matchPreStr.c_str(), matchPreStr.length()) == 0) &&
(strncmp(fileName.c_str() + fileName.length() - matchSufStr.length(), matchSufStr.c_str(),
matchSufStr.length()) == 0)) {
g_cmdLogsFilesStrings.push_back(fileName);
}
}
closedir(dir);
#endif
return g_cmdLogsFilesStrings;
}
void ControlFilesByRegex(const std::string& path,
const std::string& matchPreStr,
const std::string& matchSufStr,
int maxFiles)
{
vector<string> files = GetDirFileNameFromPath(path, matchPreStr, matchSufStr);
if (files.size() <= static_cast<size_t>(maxFiles)) {
return;
}
sort(files.begin(), files.end(), CompareLogFileName);
uint32_t delCount = files.size() - static_cast<uint32_t>(maxFiles);
uint32_t beginCount = files.size() - delCount;
for (auto name = files.begin() + beginCount; name != files.end(); name++) {
string delFileName = path.c_str() + *name;
if (GetCaller() == Caller::CLIENT) {
WRITE_LOG(LOG_INFO, "delete file %s", delFileName.c_str());
} else {
WRITE_LOG(LOG_INFO, "delete file %s", Hdc::MaskString(delFileName).c_str());
}
if (unlink(delFileName.c_str()) != 0) {
WRITE_LOG(LOG_FATAL, "Failed to unlink file or symlink, error is :%s", strerror(errno));
}
}
return;
}
void ThreadProcessCmdLogs()
{
string path = GetCmdLogDirName();
string logFileName = path + CMD_LOG_FILE_NAME;
if (IsFileSizeLessThan(logFileName, MAX_COMPRESS_LOG_FILE_SIZE)) {
return;
}
string timeStr;
GetTimeString(timeStr);
string renameFileName = CMD_LOG_COMPRESS_FILE_NAME_PREFIX + timeStr + CMD_LOG_FILE_TYPE;
string renameFilePath = path + renameFileName;
if (rename(logFileName.c_str(), renameFilePath.c_str()) != 0) {
if (GetCaller() == Caller::CLIENT) {
WRITE_LOG(LOG_FATAL, "rename file %s failed", logFileName.c_str());
} else {
WRITE_LOG(LOG_FATAL, "rename file %s failed", Hdc::MaskString(logFileName).c_str());
}
}
string targetTarFileName = renameFileName + CMD_LOG_COMPRESS_FILE_NAME_SUFFIX;
if (!CompressCmdLogAndRemove(path, renameFileName, targetTarFileName)) {
if (GetCaller() == Caller::CLIENT) {
WRITE_LOG(LOG_FATAL, "compress and remove file %s failed", renameFileName.c_str());
} else {
WRITE_LOG(LOG_FATAL, "compress and remove file %s failed", Hdc::MaskString(renameFileName).c_str());
}
return;
}
ControlFilesByRegex(path,
CMD_LOG_COMPRESS_FILE_NAME_PREFIX,
CMD_LOG_COMPRESS_FILE_NAME_SUFFIX,
MAX_COMPRESS_LOG_FILE_COUNT);
}
void SaveLogToPath(const std::string& path, const std::string& str)
{
int flags = UV_FS_O_RDWR | UV_FS_O_CREAT | UV_FS_O_APPEND;
uv_fs_t req;
#ifdef HOST_OHOS
mode_t mode = (S_IWUSR | S_IRUSR | S_IWGRP | S_IRGRP);
#else
mode_t mode = (S_IWUSR | S_IRUSR);
#endif
int fd = uv_fs_open(nullptr, &req, path.c_str(), flags, mode, nullptr);
if (fd < 0) {
char buffer[BUF_SIZE_DEFAULT] = { 0 };
uv_strerror_r((int)req.result, buffer, BUF_SIZE_DEFAULT);
uv_fs_req_cleanup(&req);
if (GetCaller() == Caller::SERVER) {
WRITE_LOG(LOG_FATAL, "SaveLogToPath failed, path:%s error:%s", Hdc::MaskString(path).c_str(), buffer);
} else {
WRITE_LOG(LOG_FATAL, "SaveLogToPath failed, path:%s error:%s", path.c_str(), buffer);
}
return;
}
string text(str);
uv_buf_t wbf = uv_buf_init((char *)str.c_str(), text.size());
uv_fs_req_cleanup(&req);
uv_fs_write(nullptr, &req, fd, &wbf, 1, -1, nullptr);
uv_fs_close(nullptr, &req, fd, nullptr);
}
bool EnsureDirectoryExists(const std::string& directoryPath)
{
uv_fs_t req;
int result = uv_fs_stat(nullptr, &req, directoryPath.c_str(), nullptr);
if (result == 0) {
return true;
} else if (result == UV_ENOENT) {
uv_fs_req_cleanup(&req);
result = uv_fs_mkdir(nullptr, &req, directoryPath.c_str(), DEF_FILE_PERMISSION, nullptr);
if (result == 0) {
if (GetCaller() == Caller::CLIENT) {
WRITE_LOG(LOG_INFO, "Directory created: %s", directoryPath.c_str());
} else {
WRITE_LOG(LOG_INFO, "Directory created: %s", Hdc::MaskString(directoryPath).c_str());
}
return true;
} else {
if (GetCaller() == Caller::CLIENT) {
WRITE_LOG(LOG_FATAL, "Failed to create directory: %s:Error:%d", directoryPath.c_str(), result);
} else {
WRITE_LOG(LOG_FATAL, "Failed to create directory: %s:Error:%d",
Hdc::MaskString(directoryPath).c_str(), result);
}
return false;
}
} else {
if (GetCaller() == Caller::CLIENT) {
WRITE_LOG(LOG_FATAL, "Failed to check directory: %s:Error:%d", directoryPath.c_str(), result);
} else {
WRITE_LOG(LOG_FATAL, "Failed to check directory: %s:Error:%d",
Hdc::MaskString(directoryPath).c_str(), result);
}
return false;
}
}
bool SaveCmdLogsToFile()
{
std::string pathNames = GetCmdLogDirName();
std::string cmdLogFileName = pathNames + CMD_LOG_FILE_NAME;
std::string cmdLog = "";
if (!EnsureDirectoryExists(pathNames)) {
WRITE_LOG(LOG_FATAL, "EnsureDirectoryExists failed");
return false;
}
size_t loopCount = 0;
while (Hdc::ServerCmdLog::GetInstance().CmdLogStrSize() != 0) {
if (loopCount > MAX_SAVE_CMD_LOG_TO_FILE_COUNTS) {
break;
}
loopCount++;
cmdLog = Hdc::ServerCmdLog::GetInstance().PopCmdLogStr();
if (!cmdLog.empty()) {
SaveLogToPath(cmdLogFileName, cmdLog);
}
}
return true;
}
void ThreadCmdStrAndCmdLogs()
{
while (true) {
size_t cmdLogCount = 0;
cmdLogCount = Hdc::ServerCmdLog::GetInstance().CmdLogStrSize();
auto lastFlushTime = Hdc::ServerCmdLog::GetInstance().GetLastFlushTime();
size_t compareTime = static_cast<size_t>(std::chrono::duration_cast<std::chrono::seconds>(
std::chrono::system_clock::now() - lastFlushTime).count());
if ((cmdLogCount > MAX_SAVE_CMD_LOG_TO_FILE_COUNTS) || (compareTime > MAX_SAVE_CMD_LOG_TO_FILE_CYCLE)) {
SaveCmdLogsToFile();
ThreadProcessCmdLogs();
}
std::this_thread::sleep_for(std::chrono::seconds(1));
}
}
void ProcessCmdLogs()
{
if (!g_cmdlogSwitch) {
return;
}
std::unique_lock<std::mutex> lock(g_threadCompressCmdLogsMutex);
g_compressCmdLogsThread = std::make_shared<std::thread>(ThreadCmdStrAndCmdLogs);
}
std::string CmdLogStringFormat(uint32_t targetSessionId, const std::string& cmdStr)
{
string timeStr;
GetTimeString(timeStr);
return StringFormat("[%s] %u %s\n", timeStr.c_str(), targetSessionId, cmdStr.c_str());
}
void SetIsServerFlag(bool isServer)
{
g_isServer = isServer;
}
static uint32_t GetSubserverLogFileLimit()
{
const char* envKey = "OHOS_HDC_SUBSERVER_LOG_FILE";
char* env = getenv(envKey);
if (env == nullptr || strlen(env) == 0) {
return 0;
}
char* endptr = nullptr;
long value = strtol(env, &endptr, 10);
if (*endptr != '\0' || value < 0) {
return 0;
}
static constexpr uint32_t maxLimit = 20;
if (value > maxLimit) {
return maxLimit;
}
return static_cast<uint32_t>(value);
}
std::string GenerateSubserverLogFileName()
{
CleanupSubserverLogFiles();
if (GetSubserverLogFileLimit() == 0) {
return "";
}
string timeStr;
GetTimeString(timeStr);
std::random_device rd;
std::uniform_int_distribution<int> dis(100000, 999999);
int randomNum = dis(rd);
std::string fileName = "hdc_subserver_" + timeStr + "_" + std::to_string(randomNum) + ".log";
g_subserverLogFileNames.push_back(fileName);
return fileName;
}
vector<string> ListDirectoryFiles(const string& dirPath)
{
vector<string> files;
#ifdef _WIN32
WIN32_FIND_DATA findData;
HANDLE hFind = FindFirstFile((dirPath + "/*").c_str(), &findData);
if (hFind == INVALID_HANDLE_VALUE) {
return files;
}
do {
string fileName = findData.cFileName;
if (fileName != "." && fileName != "..") {
files.push_back(fileName);
}
} while (FindNextFile(hFind, &findData));
FindClose(hFind);
#else
DIR* dir = opendir(dirPath.c_str());
if (dir == nullptr) {
return files;
}
struct dirent* entry;
while ((entry = readdir(dir)) != nullptr) {
string fileName = entry->d_name;
if (fileName != "." && fileName != "..") {
files.push_back(fileName);
}
}
closedir(dir);
#endif
return files;
}
void RemoveOldLogFiles(const string& dirPath, uint32_t limit)
{
while (g_subserverLogFileNames.size() > limit) {
string oldestFile = g_subserverLogFileNames.front();
g_subserverLogFileNames.erase(g_subserverLogFileNames.begin());
string fullPath = dirPath + GetPathSep() + oldestFile;
unlink(fullPath.c_str());
}
}
void RemoveUnknownFiles(const string& dirPath, const vector<string>& files)
{
for (const string& file : files) {
if (std::find(g_subserverLogFileNames.begin(), g_subserverLogFileNames.end(), file)
== g_subserverLogFileNames.end()) {
string fullPath = dirPath + GetPathSep() + file;
unlink(fullPath.c_str());
}
}
}
void CleanupSubserverLogFiles()
{
string dirPath = GetTmpDir() + ".hdc_subserver";
uint32_t limit = GetSubserverLogFileLimit();
vector<string> files = ListDirectoryFiles(dirPath);
RemoveOldLogFiles(dirPath, limit);
RemoveUnknownFiles(dirPath, files);
if (limit == 0) {
uv_fs_t req;
uv_fs_rmdir(nullptr, &req, dirPath.c_str(), nullptr);
uv_fs_req_cleanup(&req);
g_subserverLogFileNames.clear();
}
}
Caller GetCaller()
{
#ifdef HDC_HOST
if (g_isServer) {
return Caller::SERVER;
} else {
return Caller::CLIENT;
}
#else
return Caller::DAEMON;
#endif
}
}
}