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