* Copyright (c) 2021-2023 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 <chrono>
#include <regex>
#include <thread>
#include <dirent.h>
#include <fcntl.h>
#include <sched.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <sys/syscall.h>
#include "applypatch/partition_record.h"
#include "flashd/flashd.h"
#include "log/log.h"
#include "misc_info/misc_info.h"
#include "package/pkg_manager.h"
#include "securec.h"
#include "updater/hardware_fault_retry.h"
#include "updater/updater.h"
#include "updater/updater_const.h"
#include "updater_main.h"
#include "utils.h"
namespace Updater {
using namespace Hpackage;
using namespace Updater::Utils;
void DeleteInstallTimeFile()
{
const std::string installTimeFilePath = std::string(UPDATER_PATH) + "/" + std::string(INSTALL_TIME_FILE);
if (access(installTimeFilePath.c_str(), F_OK) != -1) {
(void)DeleteFile(installTimeFilePath);
LOG(INFO) << "delete install time file";
}
}
void WriteInstallTime(UpdaterParams &upParams)
{
std::ofstream ofs;
ofs.open(std::string(UPDATER_PATH) + "/" + std::string(INSTALL_TIME_FILE), std::ios::app | std::ios::out);
if (!ofs.is_open()) {
LOG(ERROR) << "open install time file fail";
return;
}
ofs << DurationToString(upParams.installTime, upParams.pkgLocation) << "\n";
}
void ReadInstallTime(UpdaterParams &upParams)
{
std::ifstream ifs;
std::string buf;
ifs.open(std::string(UPDATER_PATH) + "/" + std::string(INSTALL_TIME_FILE), std::ios::in);
if (!ifs.is_open()) {
LOG(ERROR) << "read install time file fail";
return;
}
unsigned int index = 0;
while (getline(ifs, buf)) {
if (index >= upParams.pkgLocation) {
break;
}
double timeDistance = 0;
if (!Utils::ConvertToDouble(buf, timeDistance)) {
LOG(ERROR) << "ConvertToDouble failed";
return;
}
upParams.installTime[index++] = std::chrono::duration<double>(timeDistance);
}
}
bool DeleteUpdaterPath(const std::string &path)
{
auto pDir = std::unique_ptr<DIR, decltype(&closedir)>(opendir(path.c_str()), closedir);
if (pDir == nullptr) {
LOG(INFO) << "Can not open dir";
return true;
}
bool sdcardTmp = false;
if (path.find("sdcard") != std::string::npos) {
sdcardTmp = true;
}
struct dirent *dp = nullptr;
while ((dp = readdir(pDir.get())) != nullptr) {
std::string currentName(dp->d_name);
if (currentName[0] == '.' || (currentName.compare("log") == 0) ||
(currentName.compare(UPDATER_RESULT_FILE) == 0) ||
(currentName.compare(UPDATER_LOCALE_FILE) == 0) ||
(currentName.compare(MODULE_UPDATE_RESULT_FILE) == 0) ||
(currentName.compare(UPLOAD_LOG_TIME_FILE) == 0) ||
(currentName.compare(MANUAL_UPLOAD_LOG_TIME_FILE) == 0) ||
(currentName.compare(INSTALL_TIME_FILE) == 0) ||
(currentName.compare(ROLLBACK_FILE) == 0) ||
(currentName.compare(SINGULAR_UPDATE_NAME) == 0) ||
(currentName.compare(SINGULAR_UPDATE_RESULT) == 0)) {
continue;
}
if (sdcardTmp && currentName.find(SDCARD_PACKAGE_SUFFIX) != std::string::npos) {
continue;
}
std::string tmpName(path);
tmpName.append("/" + currentName);
if (IsDirExist(tmpName)) {
DeleteUpdaterPath(tmpName);
}
#ifndef UPDATER_UT
remove(tmpName.c_str());
#endif
}
return true;
}
bool ClearMisc()
{
struct UpdateMessage cleanBoot {};
if (!WriteUpdaterMiscMsg(cleanBoot)) {
LOG(ERROR) << "ClearMisc clear boot message to misc failed";
return false;
}
auto miscBlockDev = GetBlockDeviceByMountPoint(MISC_PATH);
if (miscBlockDev.empty()) {
LOG(INFO) << "cannot get block device of partition";
miscBlockDev = MISC_FILE;
}
LOG(INFO) << "ClearMisc::misc path : " << miscBlockDev;
auto fp = std::unique_ptr<FILE, decltype(&fclose)>(fopen(miscBlockDev.c_str(), "rb+"), fclose);
if (fp == nullptr) {
LOG(ERROR) << "WriteVersionCode fopen failed" << " : " << strerror(errno);
return false;
}
if (fseek(fp.get(), PARTITION_RECORD_OFFSET, SEEK_SET) != 0) {
LOG(ERROR) << "ClearMisc fseek failed";
return false;
}
off_t clearOffset = 0;
if (fwrite(&clearOffset, sizeof(off_t), 1, fp.get()) != 1) {
LOG(ERROR) << "ClearMisc write misc initOffset 0 failed" << " : " << strerror(errno);
return false;
}
struct PartitionRecordInfo cleanPartition {};
for (size_t tmpOffset = 0; tmpOffset < PARTITION_UPDATER_RECORD_MSG_SIZE; tmpOffset +=
sizeof(PartitionRecordInfo)) {
if (fseek(fp.get(), PARTITION_RECORD_START + tmpOffset, SEEK_SET) != 0) {
LOG(ERROR) << "ClearMisc fseek failed";
return false;
}
if (fwrite(&cleanPartition, sizeof(PartitionRecordInfo), 1, fp.get()) != 1) {
LOG(ERROR) << "ClearMisc write misc cleanPartition failed" << " : " << strerror(errno);
return false;
}
}
return true;
}
bool IsSDCardExist(const std::string &sdcardPath)
{
int save_errno = errno;
struct stat st {};
if (stat(sdcardPath.c_str(), &st) < 0) {
return false;
} else {
errno = save_errno;
return true;
}
}
bool IsMountDataAndSaveLogs(void)
{
bool isSdCardMode = CheckUpdateMode(SDCARD_MODE);
bool isUsbMode = CheckUpdateMode(USB_MODE);
bool isSdCardIntralMode = CheckUpdateMode(SDCARD_INTRAL_MODE);
bool isLogMounted = GetMountStatusForMountPoint("/log") == MountStatus::MOUNT_MOUNTED;
bool isDataAlreadyMounted = GetMountStatusForMountPoint("/data") == MountStatus::MOUNT_MOUNTED;
return (!(isSdCardMode || isUsbMode) && (isDataAlreadyMounted || !isLogMounted)) || isSdCardIntralMode;
}
static void DeleteUpdaterTmpFiles()
{
if (access(UPDATER_PATH, 0) == 0 && access(SDCARD_CARD_PATH, 0) != 0 && !DeleteUpdaterPath(UPDATER_PATH)) {
LOG(ERROR) << "DeleteUpdaterPath failed";
}
if (access(SDCARD_CARD_PATH, 0) == 0 && !DeleteUpdaterPath(SDCARD_CARD_PATH)) {
LOG(ERROR) << "Delete sdcard path failed";
}
if (access(Flashd::FLASHD_FILE_PATH, 0) == 0 && !DeleteUpdaterPath(Flashd::FLASHD_FILE_PATH)) {
LOG(ERROR) << "DeleteUpdaterPath failed";
}
}
void PostUpdater(bool clearMisc)
{
STAGE(UPDATE_STAGE_BEGIN) << "PostUpdater";
bool isMountDataAndSaveLogs = IsMountDataAndSaveLogs();
(void)SetupPartitions(isMountDataAndSaveLogs);
UpdaterInit::GetInstance().InvokeEvent(UPDATER_POST_INIT_EVENT);
if (clearMisc && !ClearMisc()) {
LOG(ERROR) << "PostUpdater clear misc failed";
}
RecordBatteryLevel();
if (!access(COMMAND_FILE, 0) && unlink(COMMAND_FILE) != 0) {
LOG(ERROR) << "Delete command failed";
}
if (!HardwareFaultRetry::GetInstance().IsRetry()) {
DeleteUpdaterTmpFiles();
}
if (isMountDataAndSaveLogs) {
SaveLogs();
}
}
void BootMode::InitMode(void) const
{
InitLogger(modeName, true);
#ifdef UPDATER_BUILD_VARIANT_USER
SetLogLevel(INFO);
#else
SetLogLevel(DEBUG);
#endif
LoadFstab(true);
STAGE(UPDATE_STAGE_OUT) << "Start " << modeName;
SetParameter(modePara.c_str(), "1");
}
bool IsUpdater(const UpdateMessage &boot)
{
return !IsFlashd(boot) && strncmp(boot.command, "boot_updater", sizeof("boot_updater") - 1) == 0;
}
bool IsFlashd(const UpdateMessage &boot)
{
return strncmp(boot.update, "boot_flash", sizeof("boot_flash") - 1) == 0;
}
std::vector<BootMode> &GetBootModes(void)
{
static std::vector<BootMode> bootModes {};
return bootModes;
}
void RegisterMode(const BootMode &mode)
{
GetBootModes().push_back(mode);
}
std::optional<BootMode> SelectMode(const UpdateMessage &boot)
{
const auto &modes = GetBootModes();
auto it = std::find_if(modes.begin(), modes.end(), [&boot] (const auto &bootMode) {
if (bootMode.cond != nullptr && bootMode.cond(boot)) {
LOG(INFO) << "condition for mode " << bootMode.modeName << " is satisfied";
return true;
}
LOG(WARNING) << "condition for mode " << bootMode.modeName << " is not satisfied";
return false;
});
if (it == modes.end() || it->entryFunc == nullptr) {
LOG(WARNING) << "find valid mode failed, enter updater Mode";
return std::nullopt;
}
LOG(INFO) << "enter " << it->modeName << " mode";
return *it;
}
bool SetCpuAffinityByPid(const UpdaterParams &upParams, unsigned int reservedCores)
{
static std::mutex setAffinityLock;
std::lock_guard<std::mutex> lock(setAffinityLock);
std::vector<std::string> binaryTids = upParams.binaryTids;
if (upParams.binaryPid == -1 || std::find(
binaryTids.begin(), binaryTids.end(), std::to_string(upParams.binaryPid)) == binaryTids.end()) {
LOG(WARNING) << "invalid binaryPid:" << upParams.binaryPid;
return false;
}
unsigned int coreCount = std::thread::hardware_concurrency();
LOG(INFO) << "coreCount:" << coreCount << ", reservedCores:" << reservedCores;
if (coreCount <= reservedCores) {
LOG(WARNING) << "coreCount:" << coreCount << " <= " << reservedCores;
return false;
}
cpu_set_t mask;
CPU_ZERO(&mask);
for (unsigned int i = 0; i < coreCount - reservedCores; i++) {
CPU_SET(i, &mask);
}
int syscallRes;
for (auto &str : binaryTids) {
int32_t temp = -1;
if (!Utils::ConvertToLong(str, temp)) {
LOG(ERROR) << "ConvertToLong failed:" << str;
continue;
}
pid_t tid = static_cast<pid_t>(temp);
syscallRes = syscall(__NR_sched_setaffinity, tid, sizeof(mask), &mask);
LOG(INFO) << "setaffinity tid:" << tid;
if (syscallRes != 0) {
LOG(ERROR) << "set affinity faild:" << syscallRes;
}
}
return true;
}
void UpdateBinaryTids(const std::vector<std::string> &output, UpdaterParams &upParams)
{
if (output.size() < DEFAULT_PROCESS_NUM) {
LOG(ERROR) << "check output fail";
return;
}
auto outputInfo = Trim(output[1]);
LOG(INFO) << "binary tids:" << outputInfo;
upParams.binaryTids = SplitString(outputInfo, ",");
if (upParams.isLoadReduction) {
unsigned int coreCount = std::thread::hardware_concurrency();
GetCpuCoresType(upParams);
unsigned int maskedCores = upParams.affinityCpuCores.has_value() ? *upParams.affinityCpuCores :
GetCpuCores(upParams, LITTLE_CORE_INDEX);
unsigned int reservedCores = coreCount > maskedCores ? coreCount - maskedCores : 0;
SetCpuAffinityByPid(upParams, reservedCores);
}
}
unsigned int GetCpuCores(UpdaterParams &upParams, int cpuType)
{
if (cpuType < 0 || cpuType >= static_cast<int>(upParams.cpuTypeCores.size())) {
return cpuType == BIG_CORE_INDEX ? DEFAULT_BIG_CORES : DEFAULT_LITTLE_CORES;
}
return upParams.cpuTypeCores[cpuType];
}
void GetCpuCoresType(UpdaterParams &upParams)
{
if (!upParams.cpuTypeCores.empty()) {
return;
}
upParams.cpuTypeCores.resize(CPU_CATEGORIES);
for (uint32_t i = 0; i < CPU_CATEGORIES; ++i) {
std::string cpuCoreNode = CPU_CORE_POLICY_NODE_DIR + std::to_string(i) + "/" + AFFECTED_CPUS;
std::ifstream fin(cpuCoreNode, std::ios::in);
if (!fin.is_open()) {
LOG(ERROR) << "open " << cpuCoreNode << " failed, error: " << errno;
upParams.cpuTypeCores[i] = 0;
continue;
}
std::string cpuCoreStr {std::istreambuf_iterator<char> {fin}, {}};
cpuCoreStr = Trim(cpuCoreStr);
LOG(INFO) << "node " << i << " content is: " << cpuCoreStr;
upParams.cpuTypeCores[i] = Utils::SplitString(cpuCoreStr, " ").size();
}
return;
}
}