xref: /openbmc/phosphor-led-manager/manager/json-config.hpp (revision 349d22e358945cce4d6b1d6df808a00bc06e378c)
1953315d2SPatrick Williams #include "utils.hpp"
2953315d2SPatrick Williams 
3953315d2SPatrick Williams #include <phosphor-logging/lg2.hpp>
4953315d2SPatrick Williams #include <sdbusplus/exception.hpp>
5953315d2SPatrick Williams #include <sdeventplus/event.hpp>
6953315d2SPatrick Williams 
7953315d2SPatrick Williams #include <filesystem>
8953315d2SPatrick Williams #include <fstream>
9953315d2SPatrick Williams 
10953315d2SPatrick Williams namespace fs = std::filesystem;
11953315d2SPatrick Williams 
12953315d2SPatrick Williams namespace phosphor
13953315d2SPatrick Williams {
14953315d2SPatrick Williams namespace led
15953315d2SPatrick Williams {
16953315d2SPatrick Williams 
17953315d2SPatrick Williams static constexpr auto confFileName = "led-group-config.json";
18953315d2SPatrick Williams static constexpr auto confOverridePath = "/etc/phosphor-led-manager";
19953315d2SPatrick Williams static constexpr auto confBasePath = "/usr/share/phosphor-led-manager";
20953315d2SPatrick Williams static constexpr auto confCompatibleInterface =
217d887627SPriyanga Ramasamy     "xyz.openbmc_project.Inventory.Decorator.Compatible";
22953315d2SPatrick Williams static constexpr auto confCompatibleProperty = "Names";
23953315d2SPatrick Williams 
24953315d2SPatrick Williams class JsonConfig
25953315d2SPatrick Williams {
26953315d2SPatrick Williams   public:
27953315d2SPatrick Williams     /**
28953315d2SPatrick Williams      * @brief Constructor
29953315d2SPatrick Williams      *
30953315d2SPatrick Williams      * Looks for the JSON config file.  If it can't find one, then it
317d887627SPriyanga Ramasamy      * will watch entity-manager for the
327d887627SPriyanga Ramasamy      * xyz.openbmc_project.Inventory.Decorator.Compatible interface to show up.
33953315d2SPatrick Williams      *
34953315d2SPatrick Williams      * @param[in] bus       - The D-Bus object
35953315d2SPatrick Williams      * @param[in] event     - sd event handler
36953315d2SPatrick Williams      */
JsonConfig(sdbusplus::bus_t & bus,sdeventplus::Event & event)373e073ba6SPatrick Williams     JsonConfig(sdbusplus::bus_t& bus, sdeventplus::Event& event) : event(event)
38953315d2SPatrick Williams     {
39953315d2SPatrick Williams         match = std::make_unique<sdbusplus::bus::match_t>(
40953315d2SPatrick Williams             bus,
41953315d2SPatrick Williams             sdbusplus::bus::match::rules::interfacesAdded() +
42953315d2SPatrick Williams                 sdbusplus::bus::match::rules::sender(
43953315d2SPatrick Williams                     "xyz.openbmc_project.EntityManager"),
44226059b8SGeorge Liu             [this](sdbusplus::message_t& m) { ifacesAddedCallback(m); });
45953315d2SPatrick Williams         getFilePath();
46953315d2SPatrick Williams 
47953315d2SPatrick Williams         if (!confFile.empty())
48953315d2SPatrick Williams         {
49953315d2SPatrick Williams             match.reset();
50953315d2SPatrick Williams         }
51953315d2SPatrick Williams     }
52953315d2SPatrick Williams 
53953315d2SPatrick Williams     /**
54953315d2SPatrick Williams      * @brief Get the configuration file
55953315d2SPatrick Williams      *
56953315d2SPatrick Williams      * @return filesystem path
57953315d2SPatrick Williams      */
getConfFile() const58953315d2SPatrick Williams     inline const fs::path& getConfFile() const
59953315d2SPatrick Williams     {
60953315d2SPatrick Williams         return confFile;
61953315d2SPatrick Williams     }
62953315d2SPatrick Williams 
63953315d2SPatrick Williams   private:
64953315d2SPatrick Williams     /** @brief Check the file path exists
65953315d2SPatrick Williams      *
66953315d2SPatrick Williams      *  @param[in]  names   -  Vector of the confCompatible Property
67953315d2SPatrick Williams      *
68953315d2SPatrick Williams      *  @return             -  True or False
69953315d2SPatrick Williams      */
filePathExists(const std::vector<std::string> & names)70953315d2SPatrick Williams     bool filePathExists(const std::vector<std::string>& names)
71953315d2SPatrick Williams     {
72543ac9f1SPatrick Williams         auto it =
73543ac9f1SPatrick Williams             std::find_if(names.begin(), names.end(), [this](const auto& name) {
747d887627SPriyanga Ramasamy                 auto configFileName = name + ".json";
757d887627SPriyanga Ramasamy                 auto configFilePath = fs::path{confBasePath} / configFileName;
767d887627SPriyanga Ramasamy                 if (fs::exists(configFilePath))
77953315d2SPatrick Williams                 {
787d887627SPriyanga Ramasamy                     confFile = configFilePath;
79953315d2SPatrick Williams                     return true;
80953315d2SPatrick Williams                 }
81953315d2SPatrick Williams                 return false;
82953315d2SPatrick Williams             });
83*349d22e3SGeorge Liu         return it != names.end();
84953315d2SPatrick Williams     }
85953315d2SPatrick Williams 
86953315d2SPatrick Williams     /**
87953315d2SPatrick Williams      * @brief The interfacesAdded callback function that looks for
887d887627SPriyanga Ramasamy      *        the xyz.openbmc_project.Inventory.Decorator.Compatible interface.
897d887627SPriyanga Ramasamy      * If it finds it, it uses the Names property in the interface to find the
907d887627SPriyanga Ramasamy      * JSON config file to use.
91953315d2SPatrick Williams      *
92953315d2SPatrick Williams      * @param[in] msg - The D-Bus message contents
93953315d2SPatrick Williams      */
ifacesAddedCallback(sdbusplus::message_t & msg)943e073ba6SPatrick Williams     void ifacesAddedCallback(sdbusplus::message_t& msg)
95953315d2SPatrick Williams     {
96953315d2SPatrick Williams         sdbusplus::message::object_path path;
97f2044037SPatrick Williams         std::unordered_map<
98f2044037SPatrick Williams             std::string,
99f2044037SPatrick Williams             std::unordered_map<std::string,
100f2044037SPatrick Williams                                std::variant<std::vector<std::string>>>>
101953315d2SPatrick Williams             interfaces;
102953315d2SPatrick Williams 
103953315d2SPatrick Williams         msg.read(path, interfaces);
104953315d2SPatrick Williams 
105953315d2SPatrick Williams         if (!interfaces.contains(confCompatibleInterface))
106953315d2SPatrick Williams         {
107953315d2SPatrick Williams             return;
108953315d2SPatrick Williams         }
109953315d2SPatrick Williams 
110953315d2SPatrick Williams         // Get the "Name" property value of the
1117d887627SPriyanga Ramasamy         // "xyz.openbmc_project.Inventory.Decorator.Compatible" interface
112953315d2SPatrick Williams         const auto& properties = interfaces.at(confCompatibleInterface);
113953315d2SPatrick Williams 
114953315d2SPatrick Williams         if (!properties.contains(confCompatibleProperty))
115953315d2SPatrick Williams         {
116953315d2SPatrick Williams             return;
117953315d2SPatrick Williams         }
118953315d2SPatrick Williams         auto names = std::get<std::vector<std::string>>(
119953315d2SPatrick Williams             properties.at(confCompatibleProperty));
120953315d2SPatrick Williams 
121953315d2SPatrick Williams         if (filePathExists(names))
122953315d2SPatrick Williams         {
123953315d2SPatrick Williams             match.reset();
124953315d2SPatrick Williams 
125953315d2SPatrick Williams             // This results in event.loop() exiting in getSystemLedMap
126953315d2SPatrick Williams             event.exit(0);
127953315d2SPatrick Williams         }
128953315d2SPatrick Williams     }
129953315d2SPatrick Williams 
130953315d2SPatrick Williams     /**
131953315d2SPatrick Williams      * Get the json configuration file. The first location found to contain the
132953315d2SPatrick Williams      * json config file from the following locations in order.
133953315d2SPatrick Williams      * confOverridePath: /etc/phosphor-led-manager/led-group-config.json
134953315d2SPatrick Williams      * confBasePath: /usr/shard/phosphor-led-manager/led-group-config.json
135953315d2SPatrick Williams      * the name property of the confCompatibleInterface:
136953315d2SPatrick Williams      * /usr/shard/phosphor-led-manager/${Name}/led-group-config.json
137953315d2SPatrick Williams      *
138953315d2SPatrick Williams      * @brief Get the configuration file to be used
139953315d2SPatrick Williams      *
140953315d2SPatrick Williams      * @return
141953315d2SPatrick Williams      */
getFilePath()142953315d2SPatrick Williams     void getFilePath()
143953315d2SPatrick Williams     {
144953315d2SPatrick Williams         // Check override location
145953315d2SPatrick Williams         confFile = fs::path{confOverridePath} / confFileName;
146953315d2SPatrick Williams         if (fs::exists(confFile))
147953315d2SPatrick Williams         {
148953315d2SPatrick Williams             return;
149953315d2SPatrick Williams         }
150953315d2SPatrick Williams 
151953315d2SPatrick Williams         // If the default file is there, use it
152953315d2SPatrick Williams         confFile = fs::path{confBasePath} / confFileName;
153953315d2SPatrick Williams         if (fs::exists(confFile))
154953315d2SPatrick Williams         {
155953315d2SPatrick Williams             return;
156953315d2SPatrick Williams         }
157953315d2SPatrick Williams         confFile.clear();
158953315d2SPatrick Williams 
159953315d2SPatrick Williams         try
160953315d2SPatrick Williams         {
161953315d2SPatrick Williams             // Get all objects implementing the compatible interface
16216181d1dSGeorge Liu             auto objects = phosphor::led::utils::DBusHandler::getSubTreePaths(
16316181d1dSGeorge Liu                 "/", confCompatibleInterface);
164953315d2SPatrick Williams             for (const auto& path : objects)
165953315d2SPatrick Williams             {
166953315d2SPatrick Williams                 try
167953315d2SPatrick Williams                 {
168953315d2SPatrick Williams                     // Retrieve json config compatible relative path locations
16916181d1dSGeorge Liu                     auto value = phosphor::led::utils::DBusHandler::getProperty(
170953315d2SPatrick Williams                         path, confCompatibleInterface, confCompatibleProperty);
171953315d2SPatrick Williams 
172953315d2SPatrick Williams                     auto confCompatValues =
173953315d2SPatrick Williams                         std::get<std::vector<std::string>>(value);
174953315d2SPatrick Williams 
175953315d2SPatrick Williams                     // Look for a config file at each name relative to the base
176953315d2SPatrick Williams                     // path and use the first one found
177953315d2SPatrick Williams                     if (filePathExists(confCompatValues))
178953315d2SPatrick Williams                     {
179953315d2SPatrick Williams                         // Use the first config file found at a listed location
180953315d2SPatrick Williams                         break;
181953315d2SPatrick Williams                     }
182953315d2SPatrick Williams                     confFile.clear();
183953315d2SPatrick Williams                 }
1843e073ba6SPatrick Williams                 catch (const sdbusplus::exception_t& e)
185953315d2SPatrick Williams                 {
186953315d2SPatrick Williams                     // Property unavailable on object.
187953315d2SPatrick Williams                     lg2::error(
188953315d2SPatrick Williams                         "Failed to get Names property, ERROR = {ERROR}, INTERFACES = {INTERFACES}, PATH = {PATH}",
189953315d2SPatrick Williams                         "ERROR", e, "INTERFACE", confCompatibleInterface,
190953315d2SPatrick Williams                         "PATH", path);
191953315d2SPatrick Williams 
192953315d2SPatrick Williams                     confFile.clear();
193953315d2SPatrick Williams                 }
194953315d2SPatrick Williams             }
195953315d2SPatrick Williams         }
1963e073ba6SPatrick Williams         catch (const sdbusplus::exception_t& e)
197953315d2SPatrick Williams         {
198953315d2SPatrick Williams             lg2::error(
199953315d2SPatrick Williams                 "Failed to call the SubTreePaths method, ERROR = {ERROR}, INTERFACE = {INTERFACE}",
200953315d2SPatrick Williams                 "ERROR", e, "INTERFACE", confCompatibleInterface);
201953315d2SPatrick Williams         }
202953315d2SPatrick Williams         return;
203953315d2SPatrick Williams     }
204953315d2SPatrick Williams 
205953315d2SPatrick Williams     /**
206953315d2SPatrick Williams      * @brief sd event handler.
207953315d2SPatrick Williams      */
208953315d2SPatrick Williams     sdeventplus::Event& event;
209953315d2SPatrick Williams 
210953315d2SPatrick Williams     /**
211953315d2SPatrick Williams      * @brief The JSON config file
212953315d2SPatrick Williams      */
213953315d2SPatrick Williams     fs::path confFile;
214953315d2SPatrick Williams 
215953315d2SPatrick Williams     /**
216953315d2SPatrick Williams      * @brief The interfacesAdded match that is used to wait
2177d887627SPriyanga Ramasamy      *        for the xyz.openbmc_project.Inventory.Decorator.Compatible
2187d887627SPriyanga Ramasamy      * interface to show up.
219953315d2SPatrick Williams      */
220953315d2SPatrick Williams     std::unique_ptr<sdbusplus::bus::match_t> match;
221953315d2SPatrick Williams };
2227217c035SPatrick Williams 
2237217c035SPatrick Williams /** Blocking call to find the JSON Config from DBus. */
getJsonConfig()2247217c035SPatrick Williams auto getJsonConfig()
2257217c035SPatrick Williams {
2267217c035SPatrick Williams     // Get a new Dbus
2277217c035SPatrick Williams     auto bus = sdbusplus::bus::new_bus();
2287217c035SPatrick Williams 
2297217c035SPatrick Williams     // Get a new event loop
2307217c035SPatrick Williams     auto event = sdeventplus::Event::get_new();
2317217c035SPatrick Williams 
2327217c035SPatrick Williams     // Attach the bus to sd_event to service user requests
2337217c035SPatrick Williams     bus.attach_event(event.get(), SD_EVENT_PRIORITY_IMPORTANT);
2347217c035SPatrick Williams     phosphor::led::JsonConfig jsonConfig(bus, event);
2357217c035SPatrick Williams 
2367217c035SPatrick Williams     // The event loop will be terminated from inside of a function in JsonConfig
2377217c035SPatrick Williams     // after finding the configuration file
2387217c035SPatrick Williams     if (jsonConfig.getConfFile().empty())
2397217c035SPatrick Williams     {
2407217c035SPatrick Williams         event.loop();
2417217c035SPatrick Williams     }
2427217c035SPatrick Williams 
2437217c035SPatrick Williams     // Detach the bus from its sd_event event loop object
2447217c035SPatrick Williams     bus.detach_event();
2457217c035SPatrick Williams 
2467217c035SPatrick Williams     return jsonConfig.getConfFile();
2477217c035SPatrick Williams }
2487217c035SPatrick Williams 
249953315d2SPatrick Williams } // namespace led
250953315d2SPatrick Williams } // namespace phosphor
251