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 std::bind(&JsonConfig::ifacesAddedCallback, this, 45 std::placeholders::_1)); 46 getFilePath(); 47 48 if (!confFile.empty()) 49 { 50 match.reset(); 51 } 52 } 53 54 /** 55 * @brief Get the configuration file 56 * 57 * @return filesystem path 58 */ 59 inline const fs::path& getConfFile() const 60 { 61 return confFile; 62 } 63 64 private: 65 /** @brief Check the file path exists 66 * 67 * @param[in] names - Vector of the confCompatible Property 68 * 69 * @return - True or False 70 */ 71 bool filePathExists(const std::vector<std::string>& names) 72 { 73 auto it = 74 std::find_if(names.begin(), names.end(), [this](const auto& name) { 75 auto configFileName = name + ".json"; 76 auto configFilePath = fs::path{confBasePath} / configFileName; 77 if (fs::exists(configFilePath)) 78 { 79 confFile = configFilePath; 80 return true; 81 } 82 return false; 83 }); 84 return it == names.end() ? false : true; 85 } 86 87 /** 88 * @brief The interfacesAdded callback function that looks for 89 * the xyz.openbmc_project.Inventory.Decorator.Compatible interface. 90 * If it finds it, it uses the Names property in the interface to find the 91 * JSON config file to use. 92 * 93 * @param[in] msg - The D-Bus message contents 94 */ 95 void ifacesAddedCallback(sdbusplus::message_t& msg) 96 { 97 sdbusplus::message::object_path path; 98 std::unordered_map< 99 std::string, 100 std::unordered_map<std::string, 101 std::variant<std::vector<std::string>>>> 102 interfaces; 103 104 msg.read(path, interfaces); 105 106 if (!interfaces.contains(confCompatibleInterface)) 107 { 108 return; 109 } 110 111 // Get the "Name" property value of the 112 // "xyz.openbmc_project.Inventory.Decorator.Compatible" interface 113 const auto& properties = interfaces.at(confCompatibleInterface); 114 115 if (!properties.contains(confCompatibleProperty)) 116 { 117 return; 118 } 119 auto names = std::get<std::vector<std::string>>( 120 properties.at(confCompatibleProperty)); 121 122 if (filePathExists(names)) 123 { 124 match.reset(); 125 126 // This results in event.loop() exiting in getSystemLedMap 127 event.exit(0); 128 } 129 } 130 131 /** 132 * Get the json configuration file. The first location found to contain the 133 * json config file from the following locations in order. 134 * confOverridePath: /etc/phosphor-led-manager/led-group-config.json 135 * confBasePath: /usr/shard/phosphor-led-manager/led-group-config.json 136 * the name property of the confCompatibleInterface: 137 * /usr/shard/phosphor-led-manager/${Name}/led-group-config.json 138 * 139 * @brief Get the configuration file to be used 140 * 141 * @return 142 */ 143 void getFilePath() 144 { 145 // Check override location 146 confFile = fs::path{confOverridePath} / confFileName; 147 if (fs::exists(confFile)) 148 { 149 return; 150 } 151 152 // If the default file is there, use it 153 confFile = fs::path{confBasePath} / confFileName; 154 if (fs::exists(confFile)) 155 { 156 return; 157 } 158 confFile.clear(); 159 160 try 161 { 162 // Get all objects implementing the compatible interface 163 auto objects = 164 dBusHandler.getSubTreePaths("/", confCompatibleInterface); 165 for (const auto& path : objects) 166 { 167 try 168 { 169 // Retrieve json config compatible relative path locations 170 auto value = dBusHandler.getProperty( 171 path, confCompatibleInterface, confCompatibleProperty); 172 173 auto confCompatValues = 174 std::get<std::vector<std::string>>(value); 175 176 // Look for a config file at each name relative to the base 177 // path and use the first one found 178 if (filePathExists(confCompatValues)) 179 { 180 // Use the first config file found at a listed location 181 break; 182 } 183 confFile.clear(); 184 } 185 catch (const sdbusplus::exception_t& e) 186 { 187 // Property unavailable on object. 188 lg2::error( 189 "Failed to get Names property, ERROR = {ERROR}, INTERFACES = {INTERFACES}, PATH = {PATH}", 190 "ERROR", e, "INTERFACE", confCompatibleInterface, 191 "PATH", path); 192 193 confFile.clear(); 194 } 195 } 196 } 197 catch (const sdbusplus::exception_t& e) 198 { 199 lg2::error( 200 "Failed to call the SubTreePaths method, ERROR = {ERROR}, INTERFACE = {INTERFACE}", 201 "ERROR", e, "INTERFACE", confCompatibleInterface); 202 } 203 return; 204 } 205 206 private: 207 /** 208 * @brief sd event handler. 209 */ 210 sdeventplus::Event& event; 211 212 /** 213 * @brief The JSON config file 214 */ 215 fs::path confFile; 216 217 /** 218 * @brief The interfacesAdded match that is used to wait 219 * for the xyz.openbmc_project.Inventory.Decorator.Compatible 220 * interface to show up. 221 */ 222 std::unique_ptr<sdbusplus::bus::match_t> match; 223 224 /** DBusHandler class handles the D-Bus operations */ 225 utils::DBusHandler dBusHandler; 226 }; 227 228 /** Blocking call to find the JSON Config from DBus. */ 229 auto 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 254 } // namespace led 255 } // namespace phosphor 256