1 #include "config.h" 2 3 #include "item_updater.hpp" 4 5 #include "utils.hpp" 6 7 #include <filesystem> 8 #include <phosphor-logging/elog-errors.hpp> 9 #include <phosphor-logging/log.hpp> 10 #include <xyz/openbmc_project/Common/error.hpp> 11 12 namespace 13 { 14 constexpr auto EXTENDED_VERSION = "extended_version"; 15 } 16 17 namespace phosphor 18 { 19 namespace software 20 { 21 namespace updater 22 { 23 namespace server = sdbusplus::xyz::openbmc_project::Software::server; 24 namespace fs = std::filesystem; 25 26 using namespace sdbusplus::xyz::openbmc_project::Common::Error; 27 using namespace phosphor::logging; 28 using SVersion = server::Version; 29 using VersionPurpose = SVersion::VersionPurpose; 30 31 void ItemUpdater::createActivation(sdbusplus::message::message& m) 32 { 33 namespace msg = sdbusplus::message; 34 namespace variant_ns = msg::variant_ns; 35 36 sdbusplus::message::object_path objPath; 37 std::map<std::string, std::map<std::string, msg::variant<std::string>>> 38 interfaces; 39 m.read(objPath, interfaces); 40 41 std::string path(std::move(objPath)); 42 std::string filePath; 43 auto purpose = VersionPurpose::Unknown; 44 std::string version; 45 46 for (const auto& [interfaceName, propertyMap] : interfaces) 47 { 48 if (interfaceName == VERSION_IFACE) 49 { 50 for (const auto& [propertyName, propertyValue] : propertyMap) 51 { 52 if (propertyName == "Purpose") 53 { 54 // Only process the PSU images 55 auto value = SVersion::convertVersionPurposeFromString( 56 variant_ns::get<std::string>(propertyValue)); 57 58 if (value == VersionPurpose::PSU) 59 { 60 purpose = value; 61 } 62 } 63 else if (propertyName == VERSION) 64 { 65 version = variant_ns::get<std::string>(propertyValue); 66 } 67 } 68 } 69 else if (interfaceName == FILEPATH_IFACE) 70 { 71 const auto& it = propertyMap.find("Path"); 72 if (it != propertyMap.end()) 73 { 74 filePath = variant_ns::get<std::string>(it->second); 75 } 76 } 77 } 78 if ((filePath.empty()) || (purpose == VersionPurpose::Unknown)) 79 { 80 return; 81 } 82 83 // Version id is the last item in the path 84 auto pos = path.rfind("/"); 85 if (pos == std::string::npos) 86 { 87 log<level::ERR>("No version id found in object path", 88 entry("OBJPATH=%s", path.c_str())); 89 return; 90 } 91 92 auto versionId = path.substr(pos + 1); 93 94 if (activations.find(versionId) == activations.end()) 95 { 96 // Determine the Activation state by processing the given image dir. 97 AssociationList associations; 98 auto activationState = server::Activation::Activations::Ready; 99 100 associations.emplace_back(std::make_tuple(ACTIVATION_FWD_ASSOCIATION, 101 ACTIVATION_REV_ASSOCIATION, 102 PSU_INVENTORY_PATH_BASE)); 103 104 fs::path manifestPath(filePath); 105 manifestPath /= MANIFEST_FILE; 106 std::string extendedVersion; 107 auto values = 108 Version::getValues(manifestPath.string(), {EXTENDED_VERSION}); 109 const auto it = values.find(EXTENDED_VERSION); 110 if (it != values.end()) 111 { 112 extendedVersion = it->second; 113 } 114 115 auto activation = 116 createActivationObject(path, versionId, extendedVersion, 117 activationState, associations, filePath); 118 activations.emplace(versionId, std::move(activation)); 119 120 auto versionPtr = 121 createVersionObject(path, versionId, version, purpose); 122 versions.emplace(versionId, std::move(versionPtr)); 123 } 124 return; 125 } 126 127 void ItemUpdater::erase(const std::string& versionId) 128 { 129 auto it = versions.find(versionId); 130 if (it == versions.end()) 131 { 132 log<level::ERR>(("Error: Failed to find version " + versionId + 133 " in item updater versions map." 134 " Unable to remove.") 135 .c_str()); 136 } 137 else 138 { 139 versions.erase(versionId); 140 } 141 142 // Removing entry in activations map 143 auto ita = activations.find(versionId); 144 if (ita == activations.end()) 145 { 146 log<level::ERR>(("Error: Failed to find version " + versionId + 147 " in item updater activations map." 148 " Unable to remove.") 149 .c_str()); 150 } 151 else 152 { 153 activations.erase(versionId); 154 } 155 } 156 157 void ItemUpdater::createActiveAssociation(const std::string& path) 158 { 159 assocs.emplace_back( 160 std::make_tuple(ACTIVE_FWD_ASSOCIATION, ACTIVE_REV_ASSOCIATION, path)); 161 associations(assocs); 162 } 163 164 void ItemUpdater::addFunctionalAssociation(const std::string& path) 165 { 166 assocs.emplace_back(std::make_tuple(FUNCTIONAL_FWD_ASSOCIATION, 167 FUNCTIONAL_REV_ASSOCIATION, path)); 168 associations(assocs); 169 } 170 171 void ItemUpdater::removeAssociation(const std::string& path) 172 { 173 for (auto iter = assocs.begin(); iter != assocs.end();) 174 { 175 if ((std::get<2>(*iter)).compare(path) == 0) 176 { 177 iter = assocs.erase(iter); 178 associations(assocs); 179 } 180 else 181 { 182 ++iter; 183 } 184 } 185 } 186 187 std::unique_ptr<Activation> ItemUpdater::createActivationObject( 188 const std::string& path, const std::string& versionId, 189 const std::string& extVersion, 190 sdbusplus::xyz::openbmc_project::Software::server::Activation::Activations 191 activationStatus, 192 const AssociationList& assocs, const std::string& filePath) 193 { 194 return std::make_unique<Activation>(bus, path, versionId, extVersion, 195 activationStatus, assocs, this, 196 filePath); 197 } 198 199 void ItemUpdater::createPsuObject(const std::string& psuInventoryPath, 200 const std::string& psuVersion) 201 { 202 auto versionId = utils::getVersionId(psuVersion); 203 auto path = std::string(SOFTWARE_OBJPATH) + "/" + versionId; 204 205 auto it = activations.find(versionId); 206 if (it != activations.end()) 207 { 208 // The versionId is already created, associate the path 209 auto associations = it->second->associations(); 210 associations.emplace_back(std::make_tuple(ACTIVATION_FWD_ASSOCIATION, 211 ACTIVATION_REV_ASSOCIATION, 212 psuInventoryPath)); 213 it->second->associations(associations); 214 psuPathActivationMap.emplace(psuInventoryPath, it->second); 215 } 216 else 217 { 218 // Create a new object for running PSU inventory 219 AssociationList associations; 220 auto activationState = server::Activation::Activations::Active; 221 222 associations.emplace_back(std::make_tuple(ACTIVATION_FWD_ASSOCIATION, 223 ACTIVATION_REV_ASSOCIATION, 224 psuInventoryPath)); 225 226 auto activation = createActivationObject( 227 path, versionId, "", activationState, associations, ""); 228 activations.emplace(versionId, std::move(activation)); 229 psuPathActivationMap.emplace(psuInventoryPath, activations[versionId]); 230 231 auto versionPtr = createVersionObject(path, versionId, psuVersion, 232 VersionPurpose::PSU); 233 versions.emplace(versionId, std::move(versionPtr)); 234 235 createActiveAssociation(path); 236 addFunctionalAssociation(path); 237 } 238 } 239 240 void ItemUpdater::removePsuObject(const std::string& psuInventoryPath) 241 { 242 auto it = psuPathActivationMap.find(psuInventoryPath); 243 if (it == psuPathActivationMap.end()) 244 { 245 log<level::ERR>("No Activation found for PSU", 246 entry("PSUPATH=%s", psuInventoryPath.c_str())); 247 return; 248 } 249 const auto& activationPtr = it->second; 250 psuPathActivationMap.erase(psuInventoryPath); 251 252 auto associations = activationPtr->associations(); 253 for (auto iter = associations.begin(); iter != associations.end();) 254 { 255 if ((std::get<2>(*iter)).compare(psuInventoryPath) == 0) 256 { 257 iter = associations.erase(iter); 258 } 259 else 260 { 261 ++iter; 262 } 263 } 264 if (associations.empty()) 265 { 266 // Remove the activation 267 erase(activationPtr->getVersionId()); 268 } 269 else 270 { 271 // Update association 272 activationPtr->associations(associations); 273 } 274 } 275 276 std::unique_ptr<Version> ItemUpdater::createVersionObject( 277 const std::string& objPath, const std::string& versionId, 278 const std::string& versionString, 279 sdbusplus::xyz::openbmc_project::Software::server::Version::VersionPurpose 280 versionPurpose) 281 { 282 auto version = std::make_unique<Version>( 283 bus, objPath, versionId, versionString, versionPurpose, 284 std::bind(&ItemUpdater::erase, this, std::placeholders::_1)); 285 return version; 286 } 287 288 void ItemUpdater::onPsuInventoryChangedMsg(sdbusplus::message::message& msg) 289 { 290 using Interface = std::string; 291 Interface interface; 292 Properties properties; 293 std::string psuPath = msg.get_path(); 294 295 msg.read(interface, properties); 296 onPsuInventoryChanged(psuPath, properties); 297 } 298 299 void ItemUpdater::onPsuInventoryChanged(const std::string& psuPath, 300 const Properties& properties) 301 { 302 bool present; 303 std::string version; 304 305 // Only present property is interested 306 auto p = properties.find(PRESENT); 307 if (p == properties.end()) 308 { 309 return; 310 } 311 present = sdbusplus::message::variant_ns::get<bool>(p->second); 312 313 if (present) 314 { 315 version = utils::getVersion(psuPath); 316 if (!version.empty()) 317 { 318 createPsuObject(psuPath, version); 319 } 320 } 321 else 322 { 323 // Remove object or association 324 removePsuObject(psuPath); 325 } 326 } 327 328 void ItemUpdater::processPSUImage() 329 { 330 auto paths = utils::getPSUInventoryPath(bus); 331 for (const auto& p : paths) 332 { 333 auto service = utils::getService(bus, p.c_str(), ITEM_IFACE); 334 auto present = utils::getProperty<bool>(bus, service.c_str(), p.c_str(), 335 ITEM_IFACE, PRESENT); 336 auto version = utils::getVersion(p); 337 if (present && !version.empty()) 338 { 339 createPsuObject(p, version); 340 } 341 // Add matches for PSU Inventory's property changes 342 psuMatches.emplace_back( 343 bus, MatchRules::propertiesChanged(p, ITEM_IFACE), 344 std::bind(&ItemUpdater::onPsuInventoryChangedMsg, this, 345 std::placeholders::_1)); 346 } 347 } 348 349 } // namespace updater 350 } // namespace software 351 } // namespace phosphor 352