blob: 79a2ee48ed107266ada4eee7380e46a7a7456dd9 [file]
#pragma once
#include "common/types.hpp"
#include "common/utils.hpp"
#include <libpldm/base.h>
#include <phosphor-logging/lg2.hpp>
#include <filesystem>
#include <format>
#include <fstream>
#include <optional>
PHOSPHOR_LOG2_USING;
namespace pldm::fw_update
{
namespace fs = std::filesystem;
using Json = nlohmann::json;
// Helper structure for JSON deserialization
struct ComponentCondition
{
std::string component;
std::optional<std::string> preUpdateTarget;
std::optional<std::string> postUpdateTarget;
};
// Custom from_json for ComponentCondition
inline void from_json(const Json& j, ComponentCondition& cc)
{
if (!j.contains("Component") || !j["Component"].is_string() ||
j["Component"].get<std::string>().empty())
{
throw std::runtime_error("Missing or invalid 'Component' field");
}
cc.component = j["Component"].get<std::string>();
if (j.contains("PreUpdateTarget") && j["PreUpdateTarget"].is_string())
{
cc.preUpdateTarget = j["PreUpdateTarget"].get<std::string>();
}
if (j.contains("PostUpdateTarget") && j["PostUpdateTarget"].is_string())
{
cc.postUpdateTarget = j["PostUpdateTarget"].get<std::string>();
}
}
class ConditionConfigManager
{
public:
explicit ConditionConfigManager(fs::path jsonPath)
{
if (jsonPath.empty())
{
return;
}
if (!fs::exists(jsonPath))
{
error("Json file does not exist: {JSPATH}", "JSPATH", jsonPath);
return;
}
std::ifstream jsonFile(jsonPath);
if (!jsonFile.is_open())
{
error("Failed to open the json file: {JSPATH}", "JSPATH", jsonPath);
return;
}
try
{
Json data = Json::parse(jsonFile);
if (!data.contains("Components") || !data["Components"].is_array())
{
error("Bad Json format for parsing: {JSPATH}", "JSPATH",
jsonPath);
return;
}
for (const auto& componentJson : data["Components"])
{
try
{
ComponentCondition cc =
componentJson.get<ComponentCondition>();
ConditionPath prePath = cc.preUpdateTarget.value_or("");
ConditionPath postPath = cc.postUpdateTarget.value_or("");
// The schema requires a non-empty unit name and omission of
// the property to skip a condition. Warn on a present but
// empty name so a misconfiguration is not silently treated
// as an intentionally omitted hook.
if (prePath.empty() && cc.preUpdateTarget)
{
warning(
"Empty 'PreUpdateTarget' for component {COMPONENT}, treating it as no pre-condition",
"COMPONENT", cc.component);
}
if (postPath.empty() && cc.postUpdateTarget)
{
warning(
"Empty 'PostUpdateTarget' for component {COMPONENT}, treating it as no post-condition",
"COMPONENT", cc.component);
}
// Skip entries where both pre and post conditions are empty
if (!prePath.empty() || !postPath.empty())
{
conditionMap.insert_or_assign(
cc.component, ConditionPaths{prePath, postPath});
}
}
catch (const std::exception& e)
{
error("Failed to parse component: {ERROR}", "ERROR",
e.what());
continue;
}
}
}
catch (const Json::parse_error& e)
{
error("Failed to parse the json file: {JSPATH}, error: {ERROR}",
"JSPATH", jsonPath, "ERROR", e.what());
return;
}
}
ConditionPath preCondition(const ConditionIdentifier& name) const
{
if (conditionMap.contains(name))
{
return conditionMap.at(name).first;
}
return "";
}
ConditionPath postCondition(const ConditionIdentifier& name) const
{
if (conditionMap.contains(name))
{
return conditionMap.at(name).second;
}
return "";
}
ConditionPaths conditions(const ConditionIdentifier& name) const
{
if (conditionMap.contains(name))
{
return conditionMap.at(name);
}
return {};
}
private:
PrePostConditionMap conditionMap;
};
/**
* @brief Generate the static named arguments for a condition service
*
* All supported named arguments are passed to every parameterized condition
* service, so no per-component selection is needed. Only the arguments known
* ahead of an update are generated here; dynamic fields such as applyTime are
* appended once startUpdate provides them.
*
* @param[in] boardName - The board name value to use for boardName argument
* @return Generated argument string, empty when no value is available
*/
inline std::string generateArg(const std::string& boardName)
{
if (boardName.empty())
{
return "";
}
return std::format("boardName={}", boardName);
}
} // namespace pldm::fw_update