1 #include "config.h" 2 3 #include "item_updater.hpp" 4 5 #include "xyz/openbmc_project/Common/error.hpp" 6 7 #include <phosphor-logging/elog-errors.hpp> 8 #include <phosphor-logging/log.hpp> 9 10 #include <filesystem> 11 12 namespace openpower 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 24 void ItemUpdater::createActivation(sdbusplus::message::message& m) 25 { 26 using SVersion = server::Version; 27 using VersionPurpose = SVersion::VersionPurpose; 28 29 sdbusplus::message::object_path objPath; 30 std::map<std::string, std::map<std::string, std::variant<std::string>>> 31 interfaces; 32 m.read(objPath, interfaces); 33 34 std::string path(std::move(objPath)); 35 std::string filePath; 36 auto purpose = VersionPurpose::Unknown; 37 std::string version; 38 39 for (const auto& intf : interfaces) 40 { 41 if (intf.first == VERSION_IFACE) 42 { 43 for (const auto& property : intf.second) 44 { 45 if (property.first == "Purpose") 46 { 47 // Only process the Host and System images 48 auto value = SVersion::convertVersionPurposeFromString( 49 std::get<std::string>(property.second)); 50 51 if (value == VersionPurpose::Host || 52 value == VersionPurpose::System) 53 { 54 purpose = value; 55 } 56 } 57 else if (property.first == "Version") 58 { 59 version = std::get<std::string>(property.second); 60 } 61 } 62 } 63 else if (intf.first == FILEPATH_IFACE) 64 { 65 for (const auto& property : intf.second) 66 { 67 if (property.first == "Path") 68 { 69 filePath = std::get<std::string>(property.second); 70 } 71 } 72 } 73 } 74 if ((filePath.empty()) || (purpose == VersionPurpose::Unknown)) 75 { 76 return; 77 } 78 79 // Version id is the last item in the path 80 auto pos = path.rfind("/"); 81 if (pos == std::string::npos) 82 { 83 log<level::ERR>("No version id found in object path", 84 entry("OBJPATH=%s", path.c_str())); 85 return; 86 } 87 88 auto versionId = path.substr(pos + 1); 89 90 if (activations.find(versionId) == activations.end()) 91 { 92 // Determine the Activation state by processing the given image dir. 93 auto activationState = server::Activation::Activations::Invalid; 94 AssociationList associations = {}; 95 if (validateImage(filePath)) 96 { 97 activationState = server::Activation::Activations::Ready; 98 // Create an association to the host inventory item 99 associations.emplace_back(std::make_tuple( 100 ACTIVATION_FWD_ASSOCIATION, ACTIVATION_REV_ASSOCIATION, 101 HOST_INVENTORY_PATH)); 102 } 103 104 fs::path manifestPath(filePath); 105 manifestPath /= MANIFEST_FILE; 106 std::string extendedVersion = 107 (Version::getValue( 108 manifestPath.string(), 109 std::map<std::string, std::string>{{"extended_version", ""}})) 110 .begin() 111 ->second; 112 113 auto activation = createActivationObject( 114 path, versionId, extendedVersion, activationState, associations); 115 activations.emplace(versionId, std::move(activation)); 116 117 auto versionPtr = 118 createVersionObject(path, versionId, version, purpose, filePath); 119 versions.emplace(versionId, std::move(versionPtr)); 120 } 121 return; 122 } 123 124 void ItemUpdater::createActiveAssociation(const std::string& path) 125 { 126 assocs.emplace_back( 127 std::make_tuple(ACTIVE_FWD_ASSOCIATION, ACTIVE_REV_ASSOCIATION, path)); 128 associations(assocs); 129 } 130 131 void ItemUpdater::createUpdateableAssociation(const std::string& path) 132 { 133 assocs.emplace_back(std::make_tuple(UPDATEABLE_FWD_ASSOCIATION, 134 UPDATEABLE_REV_ASSOCIATION, path)); 135 associations(assocs); 136 } 137 138 void ItemUpdater::updateFunctionalAssociation(const std::string& versionId) 139 { 140 std::string path = std::string{SOFTWARE_OBJPATH} + '/' + versionId; 141 // remove all functional associations 142 for (auto iter = assocs.begin(); iter != assocs.end();) 143 { 144 if ((std::get<0>(*iter)).compare(FUNCTIONAL_FWD_ASSOCIATION) == 0) 145 { 146 iter = assocs.erase(iter); 147 } 148 else 149 { 150 ++iter; 151 } 152 } 153 assocs.emplace_back(std::make_tuple(FUNCTIONAL_FWD_ASSOCIATION, 154 FUNCTIONAL_REV_ASSOCIATION, path)); 155 associations(assocs); 156 } 157 158 void ItemUpdater::removeAssociation(const std::string& path) 159 { 160 for (auto iter = assocs.begin(); iter != assocs.end();) 161 { 162 if ((std::get<2>(*iter)).compare(path) == 0) 163 { 164 iter = assocs.erase(iter); 165 associations(assocs); 166 } 167 else 168 { 169 ++iter; 170 } 171 } 172 } 173 174 bool ItemUpdater::erase(std::string entryId) 175 { 176 if (isVersionFunctional(entryId) && isChassisOn()) 177 { 178 log<level::ERR>(("Error: Version " + entryId + 179 " is currently active and running on the host." 180 " Unable to remove.") 181 .c_str()); 182 return false; 183 } 184 185 // Removing entry in versions map 186 auto it = versions.find(entryId); 187 if (it == versions.end()) 188 { 189 log<level::ERR>(("Error: Failed to find version " + entryId + 190 " in item updater versions map." 191 " Unable to remove.") 192 .c_str()); 193 } 194 else 195 { 196 versions.erase(entryId); 197 } 198 199 // Removing entry in activations map 200 auto ita = activations.find(entryId); 201 if (ita == activations.end()) 202 { 203 log<level::ERR>(("Error: Failed to find version " + entryId + 204 " in item updater activations map." 205 " Unable to remove.") 206 .c_str()); 207 } 208 else 209 { 210 removeAssociation(ita->second->path); 211 activations.erase(entryId); 212 } 213 return true; 214 } 215 216 bool ItemUpdater::isChassisOn() 217 { 218 auto mapperCall = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH, 219 MAPPER_INTERFACE, "GetObject"); 220 221 mapperCall.append(CHASSIS_STATE_PATH, 222 std::vector<std::string>({CHASSIS_STATE_OBJ})); 223 224 std::map<std::string, std::vector<std::string>> mapperResponse; 225 226 try 227 { 228 auto mapperResponseMsg = bus.call(mapperCall); 229 mapperResponseMsg.read(mapperResponse); 230 if (mapperResponse.empty()) 231 { 232 log<level::ERR>("Invalid Response from mapper"); 233 elog<InternalFailure>(); 234 } 235 } 236 catch (const sdbusplus::exception::SdBusError& e) 237 { 238 log<level::ERR>("Error in Mapper call"); 239 elog<InternalFailure>(); 240 } 241 242 auto method = bus.new_method_call((mapperResponse.begin()->first).c_str(), 243 CHASSIS_STATE_PATH, 244 SYSTEMD_PROPERTY_INTERFACE, "Get"); 245 method.append(CHASSIS_STATE_OBJ, "CurrentPowerState"); 246 247 std::variant<std::string> currentChassisState; 248 249 try 250 { 251 auto response = bus.call(method); 252 response.read(currentChassisState); 253 auto strParam = std::get<std::string>(currentChassisState); 254 return (strParam != CHASSIS_STATE_OFF); 255 } 256 catch (const sdbusplus::exception::SdBusError& e) 257 { 258 log<level::ERR>("Error in fetching current Chassis State", 259 entry("MAPPERRESPONSE=%s", 260 (mapperResponse.begin()->first).c_str())); 261 elog<InternalFailure>(); 262 } 263 } 264 265 } // namespace updater 266 } // namespace software 267 } // namespace openpower 268