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.Configuration.IBMCompatibleSystem"; 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 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) 39 { 40 match = std::make_unique<sdbusplus::bus::match_t>( 41 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 } 53 } 54 55 /** 56 * @brief Get the configuration file 57 * 58 * @return filesystem path 59 */ 60 inline const fs::path& getConfFile() const 61 { 62 return confFile; 63 } 64 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) 73 { 74 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; 99 std::unordered_map< 100 std::string, 101 std::unordered_map<std::string, 102 std::variant<std::vector<std::string>>>> 103 interfaces; 104 105 msg.read(path, interfaces); 106 107 if (!interfaces.contains(confCompatibleInterface)) 108 { 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 116 if (!properties.contains(confCompatibleProperty)) 117 { 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 */ 144 void getFilePath() 145 { 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 } 186 catch (const sdbusplus::exception::exception& e) 187 { 188 // Property unavailable on object. 189 lg2::error( 190 "Failed to get Names property, ERROR = {ERROR}, INTERFACES = {INTERFACES}, PATH = {PATH}", 191 "ERROR", e, "INTERFACE", confCompatibleInterface, 192 "PATH", path); 193 194 confFile.clear(); 195 } 196 } 197 } 198 catch (const sdbusplus::exception::exception& e) 199 { 200 lg2::error( 201 "Failed to call the SubTreePaths method, ERROR = {ERROR}, INTERFACE = {INTERFACE}", 202 "ERROR", e, "INTERFACE", confCompatibleInterface); 203 } 204 return; 205 } 206 207 private: 208 /** 209 * @brief sd event handler. 210 */ 211 sdeventplus::Event& event; 212 213 /** 214 * @brief The JSON config file 215 */ 216 fs::path confFile; 217 218 /** 219 * @brief The interfacesAdded match that is used to wait 220 * for the IBMCompatibleSystem 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