* Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved.
* ubs-engine is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* http://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
#include "ubse_conf_module.h"
#include <cstddef>
#include <fstream>
#include <regex>
#include <vector>
#include "ubse_common_def.h"
#include "ubse_conf_manager.h"
#include "ubse_context.h"
#include "ubse_logger.h"
#include "ubse_security_module.h"
#include "ubse_str_util.h"
namespace ubse::config {
using namespace ubse::module;
using namespace ubse::context;
using namespace ubse::log;
using namespace ubse::common::def;
using namespace ubse::utils;
BASE_DYNAMIC_CREATE(UbseConfModule, ubse::security::UbseSecurityModule);
UBSE_DEFINE_THIS_MODULE("ubse");
const std::string CONFIG_DEFAULT_DIR = "/etc/ubse";
const std::string UB_FEATURE_PATH = "/sys/bus/ub/ub_feature";
std::tuple<std::string, std::string, std::string> TrimConf(const std::string& section, const std::string& configKey,
const std::string& configVal = "");
class RegisterFunc {
public:
static RegisterFunc& GetInstance()
{
static RegisterFunc _ins;
return _ins;
}
public:
void push(register_config_func& f)
{
funcs_.push_back(std::move(f));
}
const auto& GetFuncs()
{
return funcs_;
}
private:
RegisterFunc() = default;
std::vector<register_config_func> funcs_;
};
RegisterConfigHelper::RegisterConfigHelper(register_config_func f)
{
RegisterFunc::GetInstance().push(f);
}
UbseResult UbseConfModule::Initialize()
{
auto& confMgrRef = UbseConfigManager::GetInstance();
auto& ctxRef = UbseContext::GetInstance();
UbseResult ret = confMgrRef.Init(CONFIG_DEFAULT_DIR);
if (ret != UBSE_OK) {
return ret;
}
ctxRef.GetArgStr("f", confCliDir_);
ret = confMgrRef.Init(confCliDir_);
if (ret != UBSE_OK && ret != UBSE_CONF_ERROR_KEY_OFFSETDIR_OPEN_ERROR) {
return ret;
}
for (auto& f : RegisterFunc::GetInstance().GetFuncs()) {
auto result = f();
if (result.has_value()) {
confMgrRef.AddConfig(result->section, result->key, result->value);
}
}
LoadUbFeature();
return UBSE_OK;
}
void UbseConfModule::UnInitialize() {}
UbseResult UbseConfModule::Start()
{
return UbseConfigManager::GetInstance().Start();
}
void UbseConfModule::Stop()
{
UbseConfigManager::GetInstance().Stop();
}
UbseResult UbseConfModule::GetAllConfigWithPrefix(const std::string& sectionPrefix,
std::map<std::string, std::map<std::string, std::string>>& configVals)
{
auto trimPrefix = Trim(sectionPrefix);
return UbseConfigManager::GetInstance().GetAllConf(trimPrefix, configVals);
}
void UbseConfModule::LoadUbFeature()
{
ubFeature_.store(UB_FEATURE_ALL_MASK);
std::ifstream featureFile(UB_FEATURE_PATH);
if (!featureFile.is_open()) {
UBSE_LOG_WARN << "Unable to open " << UB_FEATURE_PATH << ", use default all ub features.";
return;
}
std::string featureValue;
if (!std::getline(featureFile, featureValue)) {
UBSE_LOG_WARN << "Unable to read " << UB_FEATURE_PATH << ", use default all ub features.";
return;
}
featureValue = Trim(featureValue);
try {
size_t pos = 0;
const uint64_t value = std::stoull(featureValue, &pos, 0);
if (pos != featureValue.size()) {
UBSE_LOG_WARN << "Invalid ub feature value=" << featureValue << ", use default all ub features.";
return;
}
ubFeature_.store(value);
UBSE_LOG_INFO << "Load ub feature success, value=" << value;
} catch (const std::invalid_argument&) {
UBSE_LOG_WARN << "Invalid ub feature value=" << featureValue << ", use default all ub features.";
} catch (const std::out_of_range&) {
UBSE_LOG_WARN << "Ub feature value out of range=" << featureValue << ", use default all ub features.";
}
}
bool UbseConfModule::IsUbFeatureSupported(uint64_t featureMask) const
{
const auto ubFeature = ubFeature_.load();
return (ubFeature & featureMask) == featureMask;
}
bool UbseConfModule::IsUrmaSupported() const
{
const auto ubFeature = ubFeature_.load();
return (ubFeature & UB_URMA_ALL_MASK) != 0;
}
bool UbseConfModule::IsMemBorrowNcSupported() const
{
return IsUbFeatureSupported(UB_MEM_BORROW_NC_MASK);
}
bool UbseConfModule::IsMemBorrowCcSupported() const
{
return IsUbFeatureSupported(UB_MEM_BORROW_CC_MASK);
}
bool UbseConfModule::IsMemShareNcSupported() const
{
return IsUbFeatureSupported(UB_MEM_SHARE_NC_MASK);
}
bool UbseConfModule::IsMemShareCcSupported() const
{
return IsUbFeatureSupported(UB_MEM_SHARE_CC_MASK);
}
bool UbseConfModule::IsMemBorrowSupported() const
{
return IsMemBorrowNcSupported() || IsMemBorrowCcSupported();
}
bool UbseConfModule::IsMemShareSupported() const
{
return IsMemShareNcSupported() || IsMemShareCcSupported();
}
bool UbseConfModule::IsMemSupported() const
{
return IsMemBorrowSupported() || IsMemShareSupported();
}
template <typename T>
UbseResult GetNumConf(const std::string& section, const std::string& configKey, T& configVal)
{
auto [trimSection, trimConfigKey, configValString] = TrimConf(section, configKey, "");
UbseResult ret = UbseConfigManager::GetInstance().GetConf(trimSection, trimConfigKey, configValString);
if (ret != UBSE_OK) {
PrintConfLog(ErrorType::CONFIG_RETURN_FAILURE, trimSection, trimConfigKey, ret);
return ret;
}
if (!IsValidNumber(configValString, !std::is_unsigned_v<T>)) {
PrintConfLog(ErrorType::CONFIG_CONVERSION_FAILED, trimSection, trimConfigKey,
UBSE_CONF_ERROR_KEY_OFFSETCONVERT_ERROR);
return UBSE_CONF_ERROR_KEY_OFFSETCONVERT_ERROR;
}
try {
if constexpr (std::is_same<T, float>::value) {
configVal = std::stof(configValString);
} else if constexpr (std::is_same<T, uint32_t>::value) {
configVal = static_cast<uint32_t>(std::stoul(configValString));
if (std::to_string(configVal) != configValString) {
throw std::invalid_argument("Invalid argument: Configuring non-integer types.");
}
} else if constexpr (std::is_same<T, uint64_t>::value) {
configVal = static_cast<uint64_t>(std::stoull(configValString));
if (std::to_string(configVal) != configValString) {
throw std::invalid_argument("Invalid argument: Configuring non-integer types.");
}
}
} catch (const std::invalid_argument&) {
PrintConfLog(ErrorType::CONFIG_CONVERSION_FAILED, trimSection, trimConfigKey,
UBSE_CONF_ERROR_KEY_OFFSETCONVERT_ERROR);
return UBSE_CONF_ERROR_KEY_OFFSETCONVERT_ERROR;
} catch (const std::out_of_range&) {
PrintConfLog(ErrorType::CONFIG_OUT_RANGE, trimSection, trimConfigKey,
UBSE_CONF_ERROR_KEY_OFFSETVALUE_OUT_OF_RANGE);
return UBSE_CONF_ERROR_KEY_OFFSETVALUE_OUT_OF_RANGE;
}
return UBSE_OK;
}
UbseResult UbseConfModule::GetUIntConf(const std::string& section, const std::string& configKey, uint32_t& configVal)
{
return GetNumConf(section, configKey, configVal);
}
UbseResult UbseConfModule::GetULongConf(const std::string& section, const std::string& configKey, uint64_t& configVal)
{
return GetNumConf(section, configKey, configVal);
}
UbseResult UbseConfModule::GetFloatConf(const std::string& section, const std::string& configKey, float& configVal)
{
return GetNumConf(section, configKey, configVal);
}
UbseResult UbseConfModule::GetStringConf(const std::string& section, const std::string& configKey,
std::string& configVal)
{
auto [trimSection, trimConfigKey, configValString] = TrimConf(section, configKey, "");
UbseResult ret = UbseConfigManager::GetInstance().GetConf(trimSection, trimConfigKey, configVal);
if (ret != UBSE_OK) {
PrintConfLog(ErrorType::CONFIG_RETURN_FAILURE, trimSection, trimConfigKey, ret);
return ret;
}
return UBSE_OK;
}
UbseResult UbseConfModule::GetBoolConf(const std::string& section, const std::string& configKey, bool& configVal)
{
auto [trimSection, trimConfigKey, configValString] = TrimConf(section, configKey, "");
UbseResult ret = UbseConfigManager::GetInstance().GetConf(trimSection, trimConfigKey, configValString);
if (ret != UBSE_OK) {
PrintConfLog(ErrorType::CONFIG_RETURN_FAILURE, trimSection, trimConfigKey, ret);
return ret;
}
if (configValString == "true") {
configVal = true;
} else if (configValString == "false") {
configVal = false;
} else {
PrintConfLog(ErrorType::CONFIG_CONVERSION_FAILED, trimSection, trimConfigKey, ret);
return UBSE_CONF_ERROR_KEY_OFFSETCONVERT_ERROR;
}
return UBSE_OK;
}
bool IsValidNumber(const std::string& str, bool allowFloating)
{
try {
if (allowFloating) {
std::regex re(R"(^-?(0|[1-9]\d*)?(\.\d+)?$)");
return std::regex_match(str, re);
}
std::regex re(R"(^[1-9]\d*$|^0$)");
return std::regex_match(str, re);
} catch (const std::regex_error& e) {
return false;
}
}
void PrintConfLog(ErrorType type, const std::string& section, const std::string& configKey, const UbseResult& result)
{
std::string errorMessage = "Unable to read key=" + configKey + " in section=" + section + ", ";
switch (type) {
case ErrorType::CONFIG_RETURN_FAILURE:
UBSE_LOG_WARN << errorMessage << FormatRetCode(result);
break;
case ErrorType::CONFIG_UNSUPPORTED_TYPE:
UBSE_LOG_WARN << errorMessage << "the query type is not supported, " << FormatRetCode(result);
break;
case ErrorType::CONFIG_CONVERSION_FAILED:
UBSE_LOG_WARN << errorMessage << "the query result can't be converted to specified type, "
<< FormatRetCode(result);
break;
case ErrorType::CONFIG_OUT_RANGE:
UBSE_LOG_WARN << errorMessage << "the query result exceeds the range, " << FormatRetCode(result);
break;
default:
UBSE_LOG_ERROR << "Unknown error.";
break;
}
}
std::tuple<std::string, std::string, std::string> TrimConf(const std::string& section, const std::string& configKey,
const std::string& configVal)
{
return {Trim(section), Trim(configKey), Trim(configVal)};
}
}