led-use-json: Use specific config files
- When enabled `led-use-json`, it should use the JsonConfig class to
find the JSON file name, and do not start until JSON config is
found.
- If it has to wait for the IBMCompatible interface to show up on
D-bus first, it will wait for that.
Tested:
- If there is a `/etc/phosphor-led-manager/led-group-config.json`
path, congfile=/etc/phosphor-led-manager/led-group-config.json
- If there is a `/usr/share/phosphor-led-manager/led-group-config.json`
path and there is not `/etc/phosphor-led-manager/`,
congfile=/usr/share/phosphor-led-manager/led-group-config.json
- If the above two paths do not exist, get the `Name` property of the
`xyz.openbmc_project.Configuration.IBMCompatibleSystem` interface,
congfile=/usr/share/phosphor-led-manager/${Name}/led-group-config.json
Signed-off-by: George Liu <liuxiwei@inspur.com>
Change-Id: I1528a3d3711ef6b604868387ad42cb3c0afde119
diff --git a/json-config.hpp b/json-config.hpp
index e0448c5..462a84e 100644
--- a/json-config.hpp
+++ b/json-config.hpp
@@ -1,147 +1,229 @@
-#include "config.h"
+#include "utils.hpp"
-#include "ledlayout.hpp"
-
-#include <nlohmann/json.hpp>
#include <phosphor-logging/log.hpp>
+#include <sdbusplus/exception.hpp>
+#include <sdeventplus/event.hpp>
#include <filesystem>
#include <fstream>
-#include <iostream>
namespace fs = std::filesystem;
-using Json = nlohmann::json;
-using LedAction = std::set<phosphor::led::Layout::LedAction>;
-using LedMap = std::map<std::string, LedAction>;
+using namespace phosphor::logging;
-// Priority for a particular LED needs to stay SAME across all groups
-// phosphor::led::Layout::Action can only be one of `Blink` and `On`
-using PriorityMap = std::map<std::string, phosphor::led::Layout::Action>;
-
-/** @brief Parse LED JSON file and output Json object
- *
- * @param[in] path - path of LED JSON file
- *
- * @return const Json - Json object
- */
-const Json readJson(const fs::path& path)
+namespace phosphor
{
- using namespace phosphor::logging;
+namespace led
+{
- if (!fs::exists(path) || fs::is_empty(path))
+static constexpr auto confFileName = "led-group-config.json";
+static constexpr auto confOverridePath = "/etc/phosphor-led-manager";
+static constexpr auto confBasePath = "/usr/share/phosphor-led-manager";
+static constexpr auto confCompatibleInterface =
+ "xyz.openbmc_project.Configuration.IBMCompatibleSystem";
+static constexpr auto confCompatibleProperty = "Names";
+
+class JsonConfig
+{
+ public:
+ /**
+ * @brief Constructor
+ *
+ * Looks for the JSON config file. If it can't find one, then it
+ * will watch entity-manager for the IBMCompatibleSystem interface
+ * to show up.
+ *
+ * @param[in] bus - The D-Bus object
+ * @param[in] event - sd event handler
+ */
+ JsonConfig(sdbusplus::bus::bus& bus, sdeventplus::Event& event) :
+ event(event)
{
- log<level::ERR>("Incorrect File Path or empty file",
- entry("FILE_PATH=%s", path.c_str()));
- throw std::runtime_error("Incorrect File Path or empty file");
+ match = std::make_unique<sdbusplus::server::match::match>(
+ bus,
+ sdbusplus::bus::match::rules::interfacesAdded() +
+ sdbusplus::bus::match::rules::sender(
+ "xyz.openbmc_project.EntityManager"),
+ std::bind(&JsonConfig::ifacesAddedCallback, this,
+ std::placeholders::_1));
+ getFilePath();
+
+ if (!confFile.empty())
+ {
+ match.reset();
+ }
}
- try
+ /**
+ * @brief Get the configuration file
+ *
+ * @return filesystem path
+ */
+ inline const fs::path& getConfFile() const
{
- std::ifstream jsonFile(path);
- return Json::parse(jsonFile);
+ return confFile;
}
- catch (const std::exception& e)
+
+ private:
+ /** @brief Check the file path exists
+ *
+ * @param[in] names - Vector of the confCompatible Property
+ *
+ * @return - True or False
+ */
+ bool filePathExists(const std::vector<std::string>& names)
{
- log<level::ERR>("Failed to parse config file",
+ auto it =
+ std::find_if(names.begin(), names.end(), [this](const auto& name) {
+ auto tempConfFile =
+ fs::path{confBasePath} / name / confFileName;
+ if (fs::exists(tempConfFile))
+ {
+ confFile = tempConfFile;
+ return true;
+ }
+ return false;
+ });
+ return it == names.end() ? false : true;
+ }
+
+ /**
+ * @brief The interfacesAdded callback function that looks for
+ * the IBMCompatibleSystem interface. If it finds it,
+ * it uses the Names property in the interface to find
+ * the JSON config file to use.
+ *
+ * @param[in] msg - The D-Bus message contents
+ */
+ void ifacesAddedCallback(sdbusplus::message::message& msg)
+ {
+ sdbusplus::message::object_path path;
+ std::map<std::string,
+ std::map<std::string, std::variant<std::vector<std::string>>>>
+ interfaces;
+
+ msg.read(path, interfaces);
+
+ if (interfaces.find(confCompatibleInterface) == interfaces.end())
+ {
+ return;
+ }
+
+ // Get the "Name" property value of the
+ // "xyz.openbmc_project.Configuration.IBMCompatibleSystem" interface
+ const auto& properties = interfaces.at(confCompatibleInterface);
+
+ if (properties.find(confCompatibleProperty) == properties.end())
+ {
+ return;
+ }
+ auto names = std::get<std::vector<std::string>>(
+ properties.at(confCompatibleProperty));
+
+ if (filePathExists(names))
+ {
+ match.reset();
+
+ // This results in event.loop() exiting in getSystemLedMap
+ event.exit(0);
+ }
+ }
+
+ /**
+ * Get the json configuration file. The first location found to contain the
+ * json config file from the following locations in order.
+ * confOverridePath: /etc/phosphor-led-manager/led-group-config.json
+ * confBasePath: /usr/shard/phosphor-led-manager/led-group-config.json
+ * the name property of the confCompatibleInterface:
+ * /usr/shard/phosphor-led-manager/${Name}/led-group-config.json
+ *
+ * @brief Get the configuration file to be used
+ *
+ * @return
+ */
+ const void getFilePath()
+ {
+ // Check override location
+ confFile = fs::path{confOverridePath} / confFileName;
+ if (fs::exists(confFile))
+ {
+ return;
+ }
+
+ // If the default file is there, use it
+ confFile = fs::path{confBasePath} / confFileName;
+ if (fs::exists(confFile))
+ {
+ return;
+ }
+ confFile.clear();
+
+ try
+ {
+ // Get all objects implementing the compatible interface
+ auto objects =
+ dBusHandler.getSubTreePaths("/", confCompatibleInterface);
+ for (const auto& path : objects)
+ {
+ try
+ {
+ // Retrieve json config compatible relative path locations
+ auto value = dBusHandler.getProperty(
+ path, confCompatibleInterface, confCompatibleProperty);
+
+ auto confCompatValues =
+ std::get<std::vector<std::string>>(value);
+
+ // Look for a config file at each name relative to the base
+ // path and use the first one found
+ if (filePathExists(confCompatValues))
+ {
+ // Use the first config file found at a listed location
+ break;
+ }
+ confFile.clear();
+ }
+ catch (const sdbusplus::exception::SdBusError& e)
+ {
+ // Property unavailable on object.
+ log<level::ERR>(
+ "Failed to get Names property",
entry("ERROR=%s", e.what()),
- entry("FILE_PATH=%s", path.c_str()));
- throw std::runtime_error("Failed to parse config file");
- }
-}
+ entry("INTERFACE=%s", confCompatibleInterface),
+ entry("PATH=%s", path.c_str()));
-/** @brief Returns action enum based on string
- *
- * @param[in] action - action string
- *
- * @return Action - action enum (On/Blink)
- */
-phosphor::led::Layout::Action getAction(const std::string& action)
-{
- assert(action == "On" || action == "Blink");
-
- return action == "Blink" ? phosphor::led::Layout::Blink
- : phosphor::led::Layout::On;
-}
-
-/** @brief Validate the Priority of an LED is same across ALL groups
- *
- * @param[in] name - led name member of each group
- * @param[in] priority - member priority of each group
- * @param[out] priorityMap - std::map, key:name, value:priority
- *
- * @return
- */
-void validatePriority(const std::string& name,
- const phosphor::led::Layout::Action& priority,
- PriorityMap& priorityMap)
-{
- using namespace phosphor::logging;
-
- auto iter = priorityMap.find(name);
- if (iter == priorityMap.end())
- {
- priorityMap.emplace(name, priority);
+ confFile.clear();
+ }
+ }
+ }
+ catch (const sdbusplus::exception::SdBusError& e)
+ {
+ log<level::ERR>("Failed to call the SubTreePaths method",
+ entry("ERROR=%s", e.what()),
+ entry("INTERFACE=%s", confCompatibleInterface));
+ }
return;
}
- if (iter->second != priority)
- {
- log<level::ERR>("Priority of LED is not same across all",
- entry("Name=%s", name.c_str()),
- entry(" Old Priority=%d", iter->second),
- entry(" New priority=%d", priority));
+ private:
+ /**
+ * @brief sd event handler.
+ */
+ sdeventplus::Event& event;
- throw std::runtime_error(
- "Priority of at least one LED is not same across groups");
- }
-}
+ /**
+ * @brief The JSON config file
+ */
+ fs::path confFile;
-/** @brief Load JSON config and return led map
- *
- * @return LedMap - Generated an std::map of LedAction
- */
-const LedMap loadJsonConfig(const fs::path& path)
-{
- LedMap ledMap{};
- PriorityMap priorityMap{};
+ /**
+ * @brief The interfacesAdded match that is used to wait
+ * for the IBMCompatibleSystem interface to show up.
+ */
+ std::unique_ptr<sdbusplus::server::match::match> match;
- // define the default JSON as empty
- const Json empty{};
- auto json = readJson(path);
- auto leds = json.value("leds", empty);
-
- for (const auto& entry : leds)
- {
- fs::path tmpPath(std::string{OBJPATH});
- tmpPath /= entry.value("group", "");
- auto objpath = tmpPath.string();
- auto members = entry.value("members", empty);
-
- LedAction ledActions{};
- for (const auto& member : members)
- {
- auto name = member.value("Name", "");
- auto action = getAction(member.value("Action", ""));
- uint8_t dutyOn = member.value("DutyOn", 50);
- uint16_t period = member.value("Period", 0);
-
- // Since only have Blink/On and default priority is Blink
- auto priority = getAction(member.value("Priority", "Blink"));
-
- // Same LEDs can be part of multiple groups. However, their
- // priorities across groups need to match.
- validatePriority(name, priority, priorityMap);
-
- phosphor::led::Layout::LedAction ledAction{name, action, dutyOn,
- period, priority};
- ledActions.emplace(ledAction);
- }
-
- // Generated an std::map of LedGroupNames to std::set of LEDs
- // containing the name and properties.
- ledMap.emplace(objpath, ledActions);
- }
-
- return ledMap;
-}
+ /** DBusHandler class handles the D-Bus operations */
+ utils::DBusHandler dBusHandler;
+};
+} // namespace led
+} // namespace phosphor