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