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::map<std::string, 100 std::map<std::string, 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.Configuration.IBMCompatibleSystem" 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::exception& 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::exception& 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 private: 206 /** 207 * @brief sd event handler. 208 */ 209 sdeventplus::Event& event; 210 211 /** 212 * @brief The JSON config file 213 */ 214 fs::path confFile; 215 216 /** 217 * @brief The interfacesAdded match that is used to wait 218 * for the IBMCompatibleSystem 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 } // namespace led 226 } // namespace phosphor 227