1 #include "utils.hpp" 2 3 #include <phosphor-logging/lg2.hpp> 4 #include <sdbusplus/exception.hpp> 5 #include <sdeventplus/event.hpp> 6 7 #include <filesystem> 8 #include <fstream> 9 10 namespace fs = std::filesystem; 11 12 namespace phosphor 13 { 14 namespace led 15 { 16 17 static constexpr auto confFileName = "led-group-config.json"; 18 static constexpr auto confOverridePath = "/etc/phosphor-led-manager"; 19 static constexpr auto confBasePath = "/usr/share/phosphor-led-manager"; 20 static constexpr auto confCompatibleInterface = 21 "xyz.openbmc_project.Inventory.Decorator.Compatible"; 22 static constexpr auto confCompatibleProperty = "Names"; 23 24 class 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 32 * xyz.openbmc_project.Inventory.Decorator.Compatible interface to show up. 33 * 34 * @param[in] bus - The D-Bus object 35 * @param[in] event - sd event handler 36 */ 37 JsonConfig(sdbusplus::bus_t& bus, sdeventplus::Event& event) : event(event) 38 { 39 match = std::make_unique<sdbusplus::bus::match_t>( 40 bus, 41 sdbusplus::bus::match::rules::interfacesAdded() + 42 sdbusplus::bus::match::rules::sender( 43 "xyz.openbmc_project.EntityManager"), 44 [this](sdbusplus::message_t& m) { ifacesAddedCallback(m); }); 45 getFilePath(); 46 47 if (!confFile.empty()) 48 { 49 match.reset(); 50 } 51 } 52 53 /** 54 * @brief Get the configuration file 55 * 56 * @return filesystem path 57 */ 58 inline const fs::path& getConfFile() const 59 { 60 return confFile; 61 } 62 63 private: 64 /** @brief Check the file path exists 65 * 66 * @param[in] names - Vector of the confCompatible Property 67 * 68 * @return - True or False 69 */ 70 bool filePathExists(const std::vector<std::string>& names) 71 { 72 auto it = 73 std::find_if(names.begin(), names.end(), [this](const auto& name) { 74 auto configFileName = name + ".json"; 75 auto configFilePath = fs::path{confBasePath} / configFileName; 76 if (fs::exists(configFilePath)) 77 { 78 confFile = configFilePath; 79 return true; 80 } 81 return false; 82 }); 83 return it == names.end() ? false : true; 84 } 85 86 /** 87 * @brief The interfacesAdded callback function that looks for 88 * the xyz.openbmc_project.Inventory.Decorator.Compatible interface. 89 * If it finds it, it uses the Names property in the interface to find the 90 * JSON config file to use. 91 * 92 * @param[in] msg - The D-Bus message contents 93 */ 94 void ifacesAddedCallback(sdbusplus::message_t& msg) 95 { 96 sdbusplus::message::object_path path; 97 std::unordered_map< 98 std::string, 99 std::unordered_map<std::string, 100 std::variant<std::vector<std::string>>>> 101 interfaces; 102 103 msg.read(path, interfaces); 104 105 if (!interfaces.contains(confCompatibleInterface)) 106 { 107 return; 108 } 109 110 // Get the "Name" property value of the 111 // "xyz.openbmc_project.Inventory.Decorator.Compatible" interface 112 const auto& properties = interfaces.at(confCompatibleInterface); 113 114 if (!properties.contains(confCompatibleProperty)) 115 { 116 return; 117 } 118 auto names = std::get<std::vector<std::string>>( 119 properties.at(confCompatibleProperty)); 120 121 if (filePathExists(names)) 122 { 123 match.reset(); 124 125 // This results in event.loop() exiting in getSystemLedMap 126 event.exit(0); 127 } 128 } 129 130 /** 131 * Get the json configuration file. The first location found to contain the 132 * json config file from the following locations in order. 133 * confOverridePath: /etc/phosphor-led-manager/led-group-config.json 134 * confBasePath: /usr/shard/phosphor-led-manager/led-group-config.json 135 * the name property of the confCompatibleInterface: 136 * /usr/shard/phosphor-led-manager/${Name}/led-group-config.json 137 * 138 * @brief Get the configuration file to be used 139 * 140 * @return 141 */ 142 void getFilePath() 143 { 144 // Check override location 145 confFile = fs::path{confOverridePath} / confFileName; 146 if (fs::exists(confFile)) 147 { 148 return; 149 } 150 151 // If the default file is there, use it 152 confFile = fs::path{confBasePath} / confFileName; 153 if (fs::exists(confFile)) 154 { 155 return; 156 } 157 confFile.clear(); 158 159 try 160 { 161 // Get all objects implementing the compatible interface 162 auto objects = 163 dBusHandler.getSubTreePaths("/", confCompatibleInterface); 164 for (const auto& path : objects) 165 { 166 try 167 { 168 // Retrieve json config compatible relative path locations 169 auto value = dBusHandler.getProperty( 170 path, confCompatibleInterface, confCompatibleProperty); 171 172 auto confCompatValues = 173 std::get<std::vector<std::string>>(value); 174 175 // Look for a config file at each name relative to the base 176 // path and use the first one found 177 if (filePathExists(confCompatValues)) 178 { 179 // Use the first config file found at a listed location 180 break; 181 } 182 confFile.clear(); 183 } 184 catch (const sdbusplus::exception_t& e) 185 { 186 // Property unavailable on object. 187 lg2::error( 188 "Failed to get Names property, ERROR = {ERROR}, INTERFACES = {INTERFACES}, PATH = {PATH}", 189 "ERROR", e, "INTERFACE", confCompatibleInterface, 190 "PATH", path); 191 192 confFile.clear(); 193 } 194 } 195 } 196 catch (const sdbusplus::exception_t& e) 197 { 198 lg2::error( 199 "Failed to call the SubTreePaths method, ERROR = {ERROR}, INTERFACE = {INTERFACE}", 200 "ERROR", e, "INTERFACE", confCompatibleInterface); 201 } 202 return; 203 } 204 205 /** 206 * @brief sd event handler. 207 */ 208 sdeventplus::Event& event; 209 210 /** 211 * @brief The JSON config file 212 */ 213 fs::path confFile; 214 215 /** 216 * @brief The interfacesAdded match that is used to wait 217 * for the xyz.openbmc_project.Inventory.Decorator.Compatible 218 * interface to show up. 219 */ 220 std::unique_ptr<sdbusplus::bus::match_t> match; 221 222 /** DBusHandler class handles the D-Bus operations */ 223 utils::DBusHandler dBusHandler; 224 }; 225 226 /** Blocking call to find the JSON Config from DBus. */ 227 auto getJsonConfig() 228 { 229 // Get a new Dbus 230 auto bus = sdbusplus::bus::new_bus(); 231 232 // Get a new event loop 233 auto event = sdeventplus::Event::get_new(); 234 235 // Attach the bus to sd_event to service user requests 236 bus.attach_event(event.get(), SD_EVENT_PRIORITY_IMPORTANT); 237 phosphor::led::JsonConfig jsonConfig(bus, event); 238 239 // The event loop will be terminated from inside of a function in JsonConfig 240 // after finding the configuration file 241 if (jsonConfig.getConfFile().empty()) 242 { 243 event.loop(); 244 } 245 246 // Detach the bus from its sd_event event loop object 247 bus.detach_event(); 248 249 return jsonConfig.getConfFile(); 250 } 251 252 } // namespace led 253 } // namespace phosphor 254