blob: 3b836a16541eb739c3289fd7b403248dd87e4145 [file] [log] [blame]
George Liu616a0712021-02-18 10:50:24 +08001#include "utils.hpp"
George Liudef5f5a2020-04-10 11:23:52 +08002
George Liue9fb5c62021-07-01 14:05:32 +08003#include <phosphor-logging/lg2.hpp>
George Liu616a0712021-02-18 10:50:24 +08004#include <sdbusplus/exception.hpp>
5#include <sdeventplus/event.hpp>
George Liudef5f5a2020-04-10 11:23:52 +08006
7#include <filesystem>
8#include <fstream>
George Liudef5f5a2020-04-10 11:23:52 +08009
10namespace fs = std::filesystem;
11
George Liu616a0712021-02-18 10:50:24 +080012namespace phosphor
George Liudef5f5a2020-04-10 11:23:52 +080013{
George Liu616a0712021-02-18 10:50:24 +080014namespace led
15{
George Liudef5f5a2020-04-10 11:23:52 +080016
George Liu616a0712021-02-18 10:50:24 +080017static constexpr auto confFileName = "led-group-config.json";
18static constexpr auto confOverridePath = "/etc/phosphor-led-manager";
19static constexpr auto confBasePath = "/usr/share/phosphor-led-manager";
20static constexpr auto confCompatibleInterface =
21 "xyz.openbmc_project.Configuration.IBMCompatibleSystem";
22static constexpr auto confCompatibleProperty = "Names";
23
24class JsonConfig
25{
26 public:
27 /**
28 * @brief Constructor
29 *
30 * Looks for the JSON config file. If it can't find one, then it
31 * will watch entity-manager for the IBMCompatibleSystem interface
32 * to show up.
33 *
34 * @param[in] bus - The D-Bus object
35 * @param[in] event - sd event handler
36 */
37 JsonConfig(sdbusplus::bus::bus& bus, sdeventplus::Event& event) :
38 event(event)
George Liudef5f5a2020-04-10 11:23:52 +080039 {
Patrick Williamsf178c0f2021-11-19 12:23:09 -060040 match = std::make_unique<sdbusplus::bus::match_t>(
George Liu616a0712021-02-18 10:50:24 +080041 bus,
42 sdbusplus::bus::match::rules::interfacesAdded() +
43 sdbusplus::bus::match::rules::sender(
44 "xyz.openbmc_project.EntityManager"),
45 std::bind(&JsonConfig::ifacesAddedCallback, this,
46 std::placeholders::_1));
47 getFilePath();
48
49 if (!confFile.empty())
50 {
51 match.reset();
52 }
George Liudef5f5a2020-04-10 11:23:52 +080053 }
54
George Liu616a0712021-02-18 10:50:24 +080055 /**
56 * @brief Get the configuration file
57 *
58 * @return filesystem path
59 */
60 inline const fs::path& getConfFile() const
George Liudef5f5a2020-04-10 11:23:52 +080061 {
George Liu616a0712021-02-18 10:50:24 +080062 return confFile;
George Liudef5f5a2020-04-10 11:23:52 +080063 }
George Liu616a0712021-02-18 10:50:24 +080064
65 private:
66 /** @brief Check the file path exists
67 *
68 * @param[in] names - Vector of the confCompatible Property
69 *
70 * @return - True or False
71 */
72 bool filePathExists(const std::vector<std::string>& names)
George Liudef5f5a2020-04-10 11:23:52 +080073 {
George Liu616a0712021-02-18 10:50:24 +080074 auto it =
75 std::find_if(names.begin(), names.end(), [this](const auto& name) {
76 auto tempConfFile =
77 fs::path{confBasePath} / name / confFileName;
78 if (fs::exists(tempConfFile))
79 {
80 confFile = tempConfFile;
81 return true;
82 }
83 return false;
84 });
85 return it == names.end() ? false : true;
86 }
87
88 /**
89 * @brief The interfacesAdded callback function that looks for
90 * the IBMCompatibleSystem interface. If it finds it,
91 * it uses the Names property in the interface to find
92 * the JSON config file to use.
93 *
94 * @param[in] msg - The D-Bus message contents
95 */
96 void ifacesAddedCallback(sdbusplus::message::message& msg)
97 {
98 sdbusplus::message::object_path path;
Patrick Williamsf2044032022-03-17 05:12:30 -050099 std::unordered_map<
100 std::string,
101 std::unordered_map<std::string,
102 std::variant<std::vector<std::string>>>>
George Liu616a0712021-02-18 10:50:24 +0800103 interfaces;
104
105 msg.read(path, interfaces);
106
George Liu7f53a032021-05-04 11:18:21 +0800107 if (!interfaces.contains(confCompatibleInterface))
George Liu616a0712021-02-18 10:50:24 +0800108 {
109 return;
110 }
111
112 // Get the "Name" property value of the
113 // "xyz.openbmc_project.Configuration.IBMCompatibleSystem" interface
114 const auto& properties = interfaces.at(confCompatibleInterface);
115
George Liu7f53a032021-05-04 11:18:21 +0800116 if (!properties.contains(confCompatibleProperty))
George Liu616a0712021-02-18 10:50:24 +0800117 {
118 return;
119 }
120 auto names = std::get<std::vector<std::string>>(
121 properties.at(confCompatibleProperty));
122
123 if (filePathExists(names))
124 {
125 match.reset();
126
127 // This results in event.loop() exiting in getSystemLedMap
128 event.exit(0);
129 }
130 }
131
132 /**
133 * Get the json configuration file. The first location found to contain the
134 * json config file from the following locations in order.
135 * confOverridePath: /etc/phosphor-led-manager/led-group-config.json
136 * confBasePath: /usr/shard/phosphor-led-manager/led-group-config.json
137 * the name property of the confCompatibleInterface:
138 * /usr/shard/phosphor-led-manager/${Name}/led-group-config.json
139 *
140 * @brief Get the configuration file to be used
141 *
142 * @return
143 */
George Liu4b062012020-10-13 15:26:58 +0800144 void getFilePath()
George Liu616a0712021-02-18 10:50:24 +0800145 {
146 // Check override location
147 confFile = fs::path{confOverridePath} / confFileName;
148 if (fs::exists(confFile))
149 {
150 return;
151 }
152
153 // If the default file is there, use it
154 confFile = fs::path{confBasePath} / confFileName;
155 if (fs::exists(confFile))
156 {
157 return;
158 }
159 confFile.clear();
160
161 try
162 {
163 // Get all objects implementing the compatible interface
164 auto objects =
165 dBusHandler.getSubTreePaths("/", confCompatibleInterface);
166 for (const auto& path : objects)
167 {
168 try
169 {
170 // Retrieve json config compatible relative path locations
171 auto value = dBusHandler.getProperty(
172 path, confCompatibleInterface, confCompatibleProperty);
173
174 auto confCompatValues =
175 std::get<std::vector<std::string>>(value);
176
177 // Look for a config file at each name relative to the base
178 // path and use the first one found
179 if (filePathExists(confCompatValues))
180 {
181 // Use the first config file found at a listed location
182 break;
183 }
184 confFile.clear();
185 }
Patrick Williams7152edc2021-09-02 09:41:54 -0500186 catch (const sdbusplus::exception::exception& e)
George Liu616a0712021-02-18 10:50:24 +0800187 {
188 // Property unavailable on object.
George Liue9fb5c62021-07-01 14:05:32 +0800189 lg2::error(
190 "Failed to get Names property, ERROR = {ERROR}, INTERFACES = {INTERFACES}, PATH = {PATH}",
191 "ERROR", e, "INTERFACE", confCompatibleInterface,
192 "PATH", path);
George Liudef5f5a2020-04-10 11:23:52 +0800193
George Liu616a0712021-02-18 10:50:24 +0800194 confFile.clear();
195 }
196 }
197 }
Patrick Williams7152edc2021-09-02 09:41:54 -0500198 catch (const sdbusplus::exception::exception& e)
George Liu616a0712021-02-18 10:50:24 +0800199 {
George Liue9fb5c62021-07-01 14:05:32 +0800200 lg2::error(
201 "Failed to call the SubTreePaths method, ERROR = {ERROR}, INTERFACE = {INTERFACE}",
202 "ERROR", e, "INTERFACE", confCompatibleInterface);
George Liu616a0712021-02-18 10:50:24 +0800203 }
George Liu17fcb4d2020-06-30 18:12:53 +0800204 return;
205 }
206
George Liu616a0712021-02-18 10:50:24 +0800207 private:
208 /**
209 * @brief sd event handler.
210 */
211 sdeventplus::Event& event;
George Liu17fcb4d2020-06-30 18:12:53 +0800212
George Liu616a0712021-02-18 10:50:24 +0800213 /**
214 * @brief The JSON config file
215 */
216 fs::path confFile;
George Liu17fcb4d2020-06-30 18:12:53 +0800217
George Liu616a0712021-02-18 10:50:24 +0800218 /**
219 * @brief The interfacesAdded match that is used to wait
220 * for the IBMCompatibleSystem interface to show up.
221 */
Patrick Williamsf178c0f2021-11-19 12:23:09 -0600222 std::unique_ptr<sdbusplus::bus::match_t> match;
George Liudef5f5a2020-04-10 11:23:52 +0800223
George Liu616a0712021-02-18 10:50:24 +0800224 /** DBusHandler class handles the D-Bus operations */
225 utils::DBusHandler dBusHandler;
226};
Patrick Williams7217c032022-03-16 16:26:09 -0500227
228/** Blocking call to find the JSON Config from DBus. */
229auto getJsonConfig()
230{
231 // Get a new Dbus
232 auto bus = sdbusplus::bus::new_bus();
233
234 // Get a new event loop
235 auto event = sdeventplus::Event::get_new();
236
237 // Attach the bus to sd_event to service user requests
238 bus.attach_event(event.get(), SD_EVENT_PRIORITY_IMPORTANT);
239 phosphor::led::JsonConfig jsonConfig(bus, event);
240
241 // The event loop will be terminated from inside of a function in JsonConfig
242 // after finding the configuration file
243 if (jsonConfig.getConfFile().empty())
244 {
245 event.loop();
246 }
247
248 // Detach the bus from its sd_event event loop object
249 bus.detach_event();
250
251 return jsonConfig.getConfFile();
252}
253
George Liu616a0712021-02-18 10:50:24 +0800254} // namespace led
255} // namespace phosphor