/*
 * 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 "utils.h"
#include <algorithm>
#include <cerrno>
#include <cstdint>
#include <cstdlib>
#include <dirent.h>
#include <fcntl.h>
#include <limits>
#include <linux/reboot.h>
#include <string>
#include <sys/reboot.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <vector>
#include "fs_manager/mount.h"
#include "init_reboot.h"
#include "log/log.h"
#include "misc_info/misc_info.h"
#ifdef WITH_SELINUX
#include <policycoreutils.h>
#include "selinux/selinux.h"
#endif
#include "package/pkg_manager.h"
#include "securec.h"
#include "updater/updater_const.h"

namespace Updater {
using namespace Hpackage;

namespace Utils {
constexpr uint8_t SHIFT_RIGHT_FOUR_BITS = 4;
constexpr int MAX_TIME_SIZE = 20;
constexpr const char *PREFIX_PARTITION_NODE = "/dev/block/by-name/";

namespace {
void UpdateInfoInMisc(const std::string headInfo, const std::optional<int> message, bool isRemove)
{
    if (headInfo.empty()) {
        return;
    }
    std::vector<std::string> args = Utils::ParseParams(0, nullptr);
    struct UpdateMessage msg {};
    if (!ReadUpdaterMiscMsg(msg)) {
        LOG(ERROR) << "SetMessageToMisc read misc failed";
        return;
    }

    (void)memset_s(msg.update, sizeof(msg.update), 0, sizeof(msg.update));
    for (const auto& arg : args) {
        if (arg.find(headInfo) == std::string::npos) {
            if (strncat_s(msg.update, sizeof(msg.update), arg.c_str(), strlen(arg.c_str()) + 1) != EOK) {
                LOG(ERROR) << "SetMessageToMisc strncat_s failed";
                return;
            }
            if (strncat_s(msg.update, sizeof(msg.update), "\n", strlen("\n") + 1) != EOK) {
                LOG(ERROR) << "SetMessageToMisc strncat_s failed";
                return;
            }
        }
    }
    char buffer[128] {}; // 128 : set headInfo size
    if (isRemove) {
        LOG(INFO) << "remove --" << headInfo << " from misc";
    } else if (!message.has_value()) {
        if (snprintf_s(buffer, sizeof(buffer), sizeof(buffer) - 1, "--%s", headInfo.c_str()) == -1) {
            LOG(ERROR) << "SetMessageToMisc snprintf_s failed";
            return;
        }
    } else {
        if (snprintf_s(buffer, sizeof(buffer), sizeof(buffer) - 1, "--%s=%d",
            headInfo.c_str(), message.value()) == -1) {
            LOG(ERROR) << "SetMessageToMisc snprintf_s failed";
            return;
        }
    }
    if (strncat_s(msg.update, sizeof(msg.update), buffer, strlen(buffer) + 1) != EOK) {
        LOG(ERROR) << "SetMessageToMisc strncat_s failed";
        return;
    }
    if (WriteUpdaterMiscMsg(msg) != true) {
        LOG(ERROR) << "Write command to misc failed.";
    }
}
} // namespace

void SaveLogs()
{
    std::string updaterLogPath = std::string(UPDATER_LOG);
    std::string stageLogPath = std::string(UPDATER_STAGE_LOG);

    // save logs
    bool ret = CopyUpdaterLogs(TMP_LOG, updaterLogPath);
    if (!ret) {
        LOG(ERROR) << "Copy updater log failed!";
    }

    mode_t mode = 0660;
#ifndef __WIN32
    SetFileAttributes(updaterLogPath, USER_UPDATE_AUTHORITY, GROUP_UPDATE_AUTHORITY, mode);
#endif

    STAGE(UPDATE_STAGE_SUCCESS) << "PostUpdater";
    ret = CopyUpdaterLogs(TMP_STAGE_LOG, stageLogPath);
    chmod(stageLogPath.c_str(), mode);
    if (!ret) {
        LOG(ERROR) << "Copy stage log failed!";
    }
}

int32_t DeleteFile(const std::string& filename)
{
    if (filename.empty()) {
        LOG(ERROR) << "Invalid filename";
        return -1;
    }
    if (unlink(filename.c_str()) == -1 && errno != ENOENT) {
        LOG(ERROR) << "unlink " << filename << " failed";
        return -1;
    }
    return 0;
}

std::vector<std::string> SplitString(const std::string &str, const std::string del)
{
    std::vector<std::string> result;
    size_t found = std::string::npos;
    size_t start = 0;
    while (true) {
        found = str.find_first_of(del, start);
        result.push_back(str.substr(start, found - start));
        if (found == std::string::npos) {
            break;
        }
        start = found + 1;
    }
    return result;
}

std::string Trim(const std::string &str)
{
    if (str.empty()) {
        LOG(ERROR) << "str is empty";
        return str;
    }
    size_t start = 0;
    size_t end = str.size() - 1;
    while (start < str.size()) {
        if (!isspace(str[start])) {
            break;
        }
        start++;
    }
    while (start < end) {
        if (!isspace(str[end])) {
            break;
        }
        end--;
    }
    if (end < start) {
        return "";
    }
    return str.substr(start, end - start + 1);
}

std::string ConvertSha256Hex(const uint8_t* shaDigest, size_t length)
{
    const std::string hexChars = "0123456789abcdef";
    std::string haxSha256 = "";
    unsigned int c;
    for (size_t i = 0; i < length; ++i) {
        auto d = shaDigest[i];
        c = (d >> SHIFT_RIGHT_FOUR_BITS) & 0xf;     // last 4 bits
        haxSha256.push_back(hexChars[c]);
        haxSha256.push_back(hexChars[d & 0xf]);
    }
    return haxSha256;
}

bool SetRebootMisc(const std::string& rebootTarget, const std::string &extData, struct UpdateMessage &msg)
{
    static const int32_t maxCommandSize = 16;
    int result = 0;
    if (rebootTarget == "updater" && strcmp(msg.command, "boot_updater") != 0) {
        result = strcpy_s(msg.command, maxCommandSize, "boot_updater");
    } else if (rebootTarget == "flashd" && strcmp(msg.command, "flashd") != 0) {
        result = strcpy_s(msg.command, maxCommandSize, "boot_flash");
    } else if (rebootTarget == "bootloader" && strcmp(msg.command, "boot_loader") != 0) {
        result = strcpy_s(msg.command, maxCommandSize, "boot_loader");
    }
    if (result != EOK) {
        LOG(ERROR) << "reboot set misc strcpy failed";
        return false;
    }
    msg.command[maxCommandSize] = 0;
    if (extData.empty()) {
        (void)memset_s(msg.update, sizeof(msg.update), 0, sizeof(msg.update));
        return true;
    }
    if (strcpy_s(msg.update, sizeof(msg.update) - 1, extData.c_str()) != EOK) {
        LOG(ERROR) << "failed to copy update";
        return false;
    }
    msg.update[sizeof(msg.update) - 1] = 0;
    return true;
}

void UpdaterDoReboot(const std::string& rebootTarget, const std::string &extData)
{
    LOG(INFO) << ", rebootTarget: " << rebootTarget;
    LoadFstab();
    struct UpdateMessage msg = {};
    if (rebootTarget.empty()) {
        if (WriteUpdaterMiscMsg(msg) != true) {
            LOG(INFO) << "UpdaterDoReboot: WriteUpdaterMessage empty error";
            return;
        }
    } else {
        if (!ReadUpdaterMiscMsg(msg)) {
            LOG(ERROR) << "UpdaterDoReboot read misc failed";
            return;
        }
        if (!SetRebootMisc(rebootTarget, extData, msg)) {
            LOG(ERROR) << "UpdaterDoReboot set misc failed";
            return;
        }
        if (!WriteUpdaterMiscMsg(msg)) {
            LOG(INFO) << "UpdaterDoReboot: WriteUpdaterMiscMsg error";
            return;
        }
    }
    sync();
#ifndef UPDATER_UT
    DoReboot(rebootTarget.c_str());
    while (true) {
        pause();
    }
#else
    return;
#endif
}

void DoShutdown()
{
    UpdateMessage msg = {};
    if (!WriteUpdaterMiscMsg(msg)) {
        LOG(ERROR) << "DoShutdown: WriteUpdaterMessage empty error";
        return;
    }
    sync();
    DoReboot("shutdown");
}

std::string GetCertName()
{
#ifndef UPDATER_UT
    static std::string signingCertName = "/etc/certificate/signing_cert.crt";
#else
    static std::string signingCertName = "/data/updater/src/signing_cert.crt";
#endif
    return signingCertName;
}

bool WriteFully(int fd, const uint8_t *data, size_t size)
{
    ssize_t written = 0;
    size_t rest = size;

    while (rest > 0) {
        do {
            written = write(fd, data, rest);
        } while (written < 0 && errno == EINTR);

        if (written < 0) {
            return false;
        }
        data += written;
        rest -= static_cast<size_t>(written);
    }
    return true;
}

bool ReadFully(int fd, void *data, size_t size)
{
    auto p = reinterpret_cast<uint8_t *>(data);
    size_t remaining = size;
    while (remaining > 0) {
        ssize_t sread = read(fd, p, remaining);
        if (sread <= 0) {
            LOG(ERROR) << "Utils::ReadFully run error";
            return false;
        }
        p += sread;
        remaining -= static_cast<size_t>(sread);
    }
    return true;
}

bool ReadFileToString(int fd, std::string &content)
{
    struct stat sb {};
    if (fstat(fd, &sb) != -1 && sb.st_size > 0) {
        content.resize(static_cast<size_t>(sb.st_size));
    }
    ssize_t n;
    auto remaining = static_cast<size_t>(sb.st_size);
    auto p = reinterpret_cast<char *>(content.data());
    while (remaining > 0) {
        n = read(fd, p, remaining);
        if (n <= 0) {
            return false;
        }
        p += n;
        remaining -= static_cast<size_t>(n);
    }
    return true;
}

bool WriteStringToFile(int fd, const std::string& content)
{
    const char *p = content.data();
    size_t remaining = content.size();
    while (remaining > 0) {
        ssize_t n = write(fd, p, remaining);
        if (n == -1) {
            return false;
        }
        p += n;
        remaining -= static_cast<size_t>(n);
    }
    return true;
}

void SyncFile(const std::string &dst)
{
    int fd = open(dst.c_str(), O_RDWR);
    if (fd < 0) {
        LOG(ERROR) << "open " << dst << " failed! err " << strerror(errno);
        return;
    }
    fsync(fd);
    close(fd);
}

bool CopyFile(const std::string &src, const std::string &dest, bool isAppend)
{
    char realPath[PATH_MAX + 1] = {0};
    if (realpath(src.c_str(), realPath) == nullptr) {
        LOG(ERROR) << src << " get realpath fail";
        return false;
    }

    std::ios_base::openmode mode = isAppend ? std::ios::app | std::ios::out : std::ios_base::out;
    std::ifstream fin(realPath);
    std::ofstream fout(dest, mode);
    if (!fin.is_open() || !fout.is_open()) {
        return false;
    }

    fout << fin.rdbuf();
    if (fout.fail()) {
        fout.clear();
        return false;
    }
    fout.flush();
    fout.close();
    SyncFile(dest); // no way to get fd from ofstream, so reopen to sync this file
    return true;
}

std::string GetLocalBoardId()
{
    return "HI3516";
}

void CompressLogs(const std::string &logName)
{
    PkgManager::PkgManagerPtr pkgManager = PkgManager::CreatePackageInstance();
    if (pkgManager == nullptr) {
        LOG(ERROR) << "pkgManager is nullptr";
        return;
    }
    std::vector<std::pair<std::string, ZipFileInfo>> files;
    // Build the zip file to be packaged
    std::vector<std::string> testFileNames;
    std::string realName = logName.substr(logName.find_last_of("/") + 1);
    std::string logPath = logName.substr(0, logName.find_last_of("/"));
    testFileNames.push_back(realName);
    for (auto name : testFileNames) {
        ZipFileInfo file;
        file.fileInfo.identity = name;
        file.fileInfo.packMethod = PKG_COMPRESS_METHOD_ZIP;
        file.fileInfo.digestMethod = PKG_DIGEST_TYPE_CRC;
        std::string fileName = logName;
        files.push_back(std::pair<std::string, ZipFileInfo>(fileName, file));
    }

    PkgInfo pkgInfo;
    pkgInfo.signMethod = PKG_SIGN_METHOD_NONE;
    pkgInfo.digestMethod = PKG_SIGN_METHOD_NONE;
    pkgInfo.pkgType = PKG_PACK_TYPE_ZIP;

    char realTime[MAX_TIME_SIZE] = {0};
    auto sysTime = std::chrono::system_clock::now();
    auto currentTime = std::chrono::system_clock::to_time_t(sysTime);
    struct tm *localTime = std::localtime(&currentTime);
    if (localTime != nullptr) {
        std::strftime(realTime, sizeof(realTime), "%Y%m%d%H%M%S", localTime);
    }
    char pkgName[MAX_LOG_NAME_SIZE];
    if (snprintf_s(pkgName, MAX_LOG_NAME_SIZE, MAX_LOG_NAME_SIZE - 1,
        "%s/%s_%s.zip", logPath.c_str(), realName.c_str(), realTime) == -1) {
        PkgManager::ReleasePackageInstance(pkgManager);
        return;
    }
    int32_t ret = pkgManager->CreatePackage(pkgName, GetCertName(), &pkgInfo, files);
    if (ret != 0) {
        LOG(WARNING) << "CompressLogs failed";
        PkgManager::ReleasePackageInstance(pkgManager);
        return;
    }
    mode_t mode = 0660;
#ifndef __WIN32
    SetFileAttributes(pkgName, USER_UPDATE_AUTHORITY, GROUP_UPDATE_AUTHORITY, mode);
#endif
    (void)DeleteFile(logName);
    PkgManager::ReleasePackageInstance(pkgManager);
}

size_t GetFileSize(const std::string &filePath)
{
    int ret = 0;
    std::ifstream ifs(filePath, std::ios::binary | std::ios::in);
    if (ifs.is_open()) {
        ifs.seekg(0, std::ios::end);
        ret = ifs.tellg();
    }
    return ret;
}

bool RestoreconPath(const std::string &path)
{
    if (MountForPath(path) != 0) {
        LOG(ERROR) << "MountForPath " << path << " failed!";
        return false;
    }
#ifdef WITH_SELINUX
    if (RestoreconRecurse(path.c_str()) == -1) {
        LOG(WARNING) << "restore " << path << " failed";
    }
#endif // WITH_SELINUX
    if (UmountForPath(path) != 0) {
        LOG(WARNING) << "UmountForPath " << path << " failed!";
    }
    return true;
}

bool CopyUpdaterLogs(const std::string &sLog, const std::string &dLog)
{
    std::size_t found = dLog.find_last_of("/");
    if (found == std::string::npos) {
        LOG(ERROR) << "Dest filePath error";
        return false;
    }
    std::string destPath = dLog.substr(0, found);
    if (MountForPath(destPath) != 0) {
        LOG(WARNING) << "MountForPath /data/log failed!";
    }

    if (access(destPath.c_str(), 0) != 0) {
        if (MkdirRecursive(destPath.c_str(), S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) != 0) {
            LOG(ERROR) << "MkdirRecursive error!";
            return false;
        }
        #ifdef WITH_SELINUX
            RestoreconRecurse(UPDATER_PATH);
        #endif // WITH_SELINUX
    }

    if (Utils::GetFileSize(sLog) > MAX_LOG_SIZE) {
        LOG(ERROR) << "Size bigger for" << sLog;
        STAGE(UPDATE_STAGE_FAIL) << "Log file error, unable to copy";
        return false;
    }

    while (Utils::GetFileSize(sLog) + GetDirSizeForFile(dLog) > MAX_LOG_DIR_SIZE) {
        if (DeleteOldFile(destPath) != true) {
            break;
        }
    }

    if (!CopyFile(sLog, dLog, true)) {
        LOG(ERROR) << "copy log file failed.";
        return false;
    }
    if (GetFileSize(dLog) >= MAX_LOG_SIZE) {
        LOG(INFO) << "log size greater than 5M!";
        CompressLogs(dLog);
    }
    sync();
    return true;
}

bool CheckResultFail()
{
    std::ifstream ifs;
    const std::string resultPath = std::string(UPDATER_PATH) + "/" + std::string(UPDATER_RESULT_FILE);
    ifs.open(resultPath, std::ios::in);
    std::string buff;
    while (ifs.is_open() && getline(ifs, buff)) {
        if (buff.find("fail|") != std::string::npos) {
            ifs.close();
            return true;
        }
    }
    LOG(ERROR) << "open result file failed";
    return false;
}

void WriteDumpResult(const std::string &result, const std::string &fileName)
{
    if (access(UPDATER_PATH, 0) != 0) {
        if (MkdirRecursive(UPDATER_PATH, 0755) != 0) { // 0755: -rwxr-xr-x
            LOG(ERROR) << "MkdirRecursive error!";
            return;
        }
    }
    LOG(INFO) << "WriteDumpResult: " << result;
    const std::string resultPath = std::string(UPDATER_PATH) + "/" + fileName;
    FILE *fp = fopen(resultPath.c_str(), "w+");
    if (fp == nullptr) {
        LOG(ERROR) << "open result file failed";
        return;
    }
    char buf[MAX_RESULT_BUFF_SIZE] = "Pass\n";
    if (sprintf_s(buf, MAX_RESULT_BUFF_SIZE - 1, "%s\n", result.c_str()) < 0) {
        LOG(WARNING) << "sprintf status fialed";
    }
    if (fwrite(buf, 1, strlen(buf) + 1, fp) <= 0) {
        LOG(WARNING) << "write result file failed, err:" << errno;
    }
    if (fclose(fp) != 0) {
        LOG(WARNING) << "close result file failed";
    }

    (void)chown(resultPath.c_str(), USER_ROOT_AUTHORITY, GROUP_UPDATE_AUTHORITY);
    (void)chmod(resultPath.c_str(), 0660); // 0660: -rw-rw----
}

long long int GetDirSize(const std::string &folderPath)
{
    DIR* dir = opendir(folderPath.c_str());
    if (dir == nullptr) {
        LOG(ERROR) << "Failed to open folder: " << folderPath << std::endl;
        return 0;
    }

    struct dirent* entry;
    long long int totalSize = 0;
    while ((entry = readdir(dir)) != nullptr) {
        std::string fileName = entry->d_name;
        std::string filePath = folderPath + "/" + fileName;
        struct stat fileStat;
        if (stat(filePath.c_str(), &fileStat) != 0) {
            LOG(ERROR) << "Failed to get file status: " << filePath << std::endl;
            continue;
        }
        if (S_ISDIR(fileStat.st_mode)) {
            if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
                continue;
            }
            std::string subFolderPath = filePath;
            totalSize += GetDirSize(subFolderPath);
        } else {
            totalSize += fileStat.st_size;
        }
    }
    closedir(dir);
    return totalSize;
}

long long int GetDirSizeForFile(const std::string &filePath)
{
    std::size_t found = filePath.find_last_of("/");
    if (found == std::string::npos) {
        LOG(ERROR) << "filePath error";
        return -1;
    }
    return GetDirSize(filePath.substr(0, found));
}

bool DeleteOldFile(const std::string folderPath)
{
    DIR* dir = opendir(folderPath.c_str());
    if (dir == nullptr) {
        LOG(ERROR) << "Failed to open folder: " << folderPath << std::endl;
        return false;
    }

    struct dirent* entry;
    std::string oldestFilePath = "";
    time_t oldestFileTime = std::numeric_limits<time_t>::max();
    while ((entry = readdir(dir)) != nullptr) {
        std::string fileName = entry->d_name;
        std::string filePath = folderPath + "/" + fileName;
        struct stat fileStat;
        if (stat(filePath.c_str(), &fileStat) != 0) {
            LOG(ERROR) << "Failed to get file status: " << filePath;
            continue;
        }
        if (fileName == "." || fileName == "..") {
            continue;
        }
        if (fileStat.st_mtime < oldestFileTime) {
            oldestFileTime = fileStat.st_mtime;
            oldestFilePath = filePath;
        }
    }
    closedir(dir);
    if (oldestFilePath.empty()) {
        LOG(ERROR) << "Unable to delete file";
        return false;
    }
    if (remove(oldestFilePath.c_str()) != 0) {
        LOG(ERROR) << "Failed to delete file: " << oldestFilePath;
        return false;
    }
    return true;
}

std::vector<std::string> ParseParams(int argc, char **argv)
{
    struct UpdateMessage boot {};
    // read from misc
    if (!ReadUpdaterMiscMsg(boot)) {
        LOG(ERROR) << "ReadUpdaterMessage MISC_FILE failed!";
    }
    // if boot.update is empty, read from command.The Misc partition may have dirty data,
    // so strlen(boot.update) is not used, which can cause system exceptions.
    if (boot.update[0] == '\0' && !access(COMMAND_FILE, 0)) {
        if (!ReadUpdaterMessage(COMMAND_FILE, boot)) {
            LOG(ERROR) << "ReadUpdaterMessage COMMAND_FILE failed!";
        }
    }
    STAGE(UPDATE_STAGE_OUT) << "Init Params: " << boot.update;
    boot.update[sizeof(boot.update) - 1] = '\0';
    std::vector<std::string> parseParams = Utils::SplitString(boot.update, "\n");
    if (argc != 0 && argv != nullptr) {
        parseParams.insert(parseParams.begin(), argv, argv + argc);
    }
    return parseParams;
}

bool CheckUpdateMode(const std::string &mode)
{
    std::vector<std::string> args = ParseParams(0, nullptr);
    for (const auto &arg : args) {
        if (arg.find(mode) != std::string::npos) {
            return true;
        }
    }
    return false;
}

std::string DurationToString(std::vector<std::chrono::duration<double>> &durations, std::size_t pkgPosition,
    int precision)
{
    if (pkgPosition >= durations.size()) {
        LOG(ERROR) << "pkg position is " << pkgPosition << ", duration's size is " << durations.size();
        return "0";
    }
    std::ostringstream oss;
    oss << std::fixed << std::setprecision(precision) << durations[pkgPosition].count();
    return oss.str();
}

std::string GetRealPath(const std::string &path)
{
    char realPath[PATH_MAX + 1] = {0};
    auto ret = realpath(path.c_str(), realPath);
    return (ret == nullptr) ? "" : ret;
}
 
std::string GetPartitionRealPath(const std::string &name)
{
    return GetRealPath(PREFIX_PARTITION_NODE + name);
}

void SetMessageToMisc(const std::string &miscCmd, const int message, const std::string headInfo)
{
    if (headInfo.empty()) {
        return;
    }
    std::vector<std::string> args = ParseParams(0, nullptr);
    struct UpdateMessage msg {};
    if (!ReadUpdaterMiscMsg(msg)) {
        LOG(ERROR) << "SetMessageToMisc read misc failed";
        return;
    }
    (void)memset_s(msg.command, sizeof(msg.command), 0, sizeof(msg.command));
    if (strncpy_s(msg.command, sizeof(msg.command), miscCmd.c_str(), miscCmd.size() + 1) != EOK) {
        LOG(ERROR) << "SetMessageToMisc strncpy_s failed";
        return;
    }
    (void)memset_s(msg.update, sizeof(msg.update), 0, sizeof(msg.update));
    for (const auto& arg : args) {
        if (arg.find(headInfo) == std::string::npos) {
            if (strncat_s(msg.update, sizeof(msg.update), arg.c_str(), strlen(arg.c_str()) + 1) != EOK) {
                LOG(ERROR) << "SetMessageToMisc strncat_s failed";
                return;
            }
            if (strncat_s(msg.update, sizeof(msg.update), "\n", strlen("\n") + 1) != EOK) {
                LOG(ERROR) << "SetMessageToMisc strncat_s failed";
                return;
            }
        }
    }
    char buffer[128] {}; // 128 : set headInfo size
    if (headInfo == "sdcard_update") {
        if (snprintf_s(buffer, sizeof(buffer), sizeof(buffer) - 1, "--%s", headInfo.c_str()) == -1) {
            LOG(ERROR) << "SetMessageToMisc snprintf_s failed";
            return;
        }
    } else {
        if (snprintf_s(buffer, sizeof(buffer), sizeof(buffer) - 1, "--%s=%d", headInfo.c_str(), message) == -1) {
            LOG(ERROR) << "SetMessageToMisc snprintf_s failed";
            return;
        }
    }
    if (strncat_s(msg.update, sizeof(msg.update), buffer, strlen(buffer) + 1) != EOK) {
        LOG(ERROR) << "SetMessageToMisc strncat_s failed";
        return;
    }
    if (WriteUpdaterMiscMsg(msg) != true) {
        LOG(ERROR) << "Write command to misc failed.";
    }
}

void SetCmdToMisc(const std::string &miscCmd)
{
    struct UpdateMessage msg {};
    if (!ReadUpdaterMiscMsg(msg)) {
        LOG(ERROR) << "SetMessageToMisc read misc failed";
        return;
    }

    (void)memset_s(msg.command, sizeof(msg.command), 0, sizeof(msg.command));
    if (strncpy_s(msg.command, sizeof(msg.command), miscCmd.c_str(), miscCmd.size() + 1) != EOK) {
        LOG(ERROR) << "SetMessageToMisc strncpy_s failed";
        return;
    }

    if (WriteUpdaterMiscMsg(msg) != true) {
        LOG(ERROR) << "Write command to misc failed.";
    }
}

void AddUpdateInfoToMisc(const std::string headInfo, const std::optional<int> message)
{
    UpdateInfoInMisc(headInfo, message, false);
}

void RemoveUpdateInfoFromMisc(const std::string &headInfo)
{
    UpdateInfoInMisc(headInfo, std::nullopt, true);
}

void SetFaultInfoToMisc(const std::string &faultInfo)
{
    struct UpdateMessage msg {};
    if (!ReadUpdaterMiscMsg(msg)) {
        LOG(ERROR) << "SetMessageToMisc read misc failed";
        return;
    }

    (void)memset_s(msg.faultinfo, sizeof(msg.faultinfo), 0, sizeof(msg.faultinfo));
    if (strncpy_s(msg.faultinfo, sizeof(msg.faultinfo), faultInfo.c_str(), faultInfo.size() + 1) != EOK) {
        LOG(ERROR) << "SetMessageToMisc strncpy_s failed";
        return;
    }

    if (WriteUpdaterMiscMsg(msg) != true) {
        LOG(ERROR) << "Write fault info to misc failed.";
    }
}

bool CheckFaultInfo(const std::string &faultInfo)
{
    struct UpdateMessage msg = {};
    if (!ReadUpdaterMiscMsg(msg)) {
        LOG(ERROR) << "read misc data failed";
        return false;
    }

    if (strcmp(msg.faultinfo, faultInfo.c_str()) == 0) {
        return true;
    }
    return false;
}

void GetTagValInStr(const std::string &str, const std::string &tag, std::string &val)
{
    if (str.find(tag + "=") != std::string::npos) {
        val = str.substr(str.find("=") + 1, str.size() - str.find("="));
    }
}

bool IsValidHexStr(const std::string &str)
{
    for (const auto &ch : str) {
        if (isxdigit(ch) == 0) {
            return false;
        }
    }
    return true;
}

void TrimString(std::string &str)
{
    auto pos = str.find_last_not_of("\r\n");
    if (pos != std::string::npos) {
        str.erase(pos + 1, str.size() - pos);
    }
}

#ifndef __WIN32
void SetFileAttributes(const std::string& file, uid_t owner, gid_t group, mode_t mode)
{
#ifdef WITH_SELINUX
    RestoreconRecurse(file.c_str());
#endif // WITH_SELINUX
    if (chown(file.c_str(), USER_ROOT_AUTHORITY, GROUP_ROOT_AUTHORITY) != 0) {
        LOG(ERROR) << "Chown failed: " << file << " " << USER_ROOT_AUTHORITY << "," << GROUP_ROOT_AUTHORITY;
    }
    if (chmod(file.c_str(), mode) != EOK) {
        LOG(ERROR) << "chmod failed: " << file << " " << mode;
    }
    if (chown(file.c_str(), owner, group) != 0) {
        LOG(ERROR) << "Chown failed: " << file << " " << owner << "," << group;
    }
}
#endif
} // Utils
void __attribute__((weak)) InitLogger(const std::string &tag)
{
    if (Utils::IsUpdaterMode()) {
        InitUpdaterLogger(tag, TMP_LOG, TMP_STAGE_LOG, TMP_ERROR_CODE_PATH);
    } else {
        InitUpdaterLogger(tag, SYS_INSTALLER_LOG, UPDATER_STAGE_LOG, ERROR_CODE_PATH);
    }
}
} // namespace Updater