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 namespace msg = sdbusplus::message; 28 29 sdbusplus::message::object_path objPath; 30 std::map<std::string, std::map<std::string, msg::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::updateFunctionalAssociation(const std::string& versionId) 132 { 133 std::string path = std::string{SOFTWARE_OBJPATH} + '/' + versionId; 134 // remove all functional associations 135 for (auto iter = assocs.begin(); iter != assocs.end();) 136 { 137 if ((std::get<0>(*iter)).compare(FUNCTIONAL_FWD_ASSOCIATION) == 0) 138 { 139 iter = assocs.erase(iter); 140 } 141 else 142 { 143 ++iter; 144 } 145 } 146 assocs.emplace_back(std::make_tuple(FUNCTIONAL_FWD_ASSOCIATION, 147 FUNCTIONAL_REV_ASSOCIATION, path)); 148 associations(assocs); 149 } 150 151 void ItemUpdater::removeAssociation(const std::string& path) 152 { 153 for (auto iter = assocs.begin(); iter != assocs.end();) 154 { 155 if ((std::get<2>(*iter)).compare(path) == 0) 156 { 157 iter = assocs.erase(iter); 158 associations(assocs); 159 } 160 else 161 { 162 ++iter; 163 } 164 } 165 } 166 167 bool ItemUpdater::erase(std::string entryId) 168 { 169 if (isVersionFunctional(entryId) && isChassisOn()) 170 { 171 log<level::ERR>(("Error: Version " + entryId + 172 " is currently active and running on the host." 173 " Unable to remove.") 174 .c_str()); 175 return false; 176 } 177 178 // Removing entry in versions map 179 auto it = versions.find(entryId); 180 if (it == versions.end()) 181 { 182 log<level::ERR>(("Error: Failed to find version " + entryId + 183 " in item updater versions map." 184 " Unable to remove.") 185 .c_str()); 186 } 187 else 188 { 189 versions.erase(entryId); 190 } 191 192 // Removing entry in activations map 193 auto ita = activations.find(entryId); 194 if (ita == activations.end()) 195 { 196 log<level::ERR>(("Error: Failed to find version " + entryId + 197 " in item updater activations map." 198 " Unable to remove.") 199 .c_str()); 200 } 201 else 202 { 203 removeAssociation(ita->second->path); 204 activations.erase(entryId); 205 } 206 return true; 207 } 208 209 bool ItemUpdater::isChassisOn() 210 { 211 auto mapperCall = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH, 212 MAPPER_INTERFACE, "GetObject"); 213 214 mapperCall.append(CHASSIS_STATE_PATH, 215 std::vector<std::string>({CHASSIS_STATE_OBJ})); 216 217 std::map<std::string, std::vector<std::string>> mapperResponse; 218 219 try 220 { 221 auto mapperResponseMsg = bus.call(mapperCall); 222 mapperResponseMsg.read(mapperResponse); 223 if (mapperResponse.empty()) 224 { 225 log<level::ERR>("Invalid Response from mapper"); 226 elog<InternalFailure>(); 227 } 228 } 229 catch (const sdbusplus::exception::SdBusError& e) 230 { 231 log<level::ERR>("Error in Mapper call"); 232 elog<InternalFailure>(); 233 } 234 235 auto method = bus.new_method_call((mapperResponse.begin()->first).c_str(), 236 CHASSIS_STATE_PATH, 237 SYSTEMD_PROPERTY_INTERFACE, "Get"); 238 method.append(CHASSIS_STATE_OBJ, "CurrentPowerState"); 239 240 std::variant<std::string> currentChassisState; 241 242 try 243 { 244 auto response = bus.call(method); 245 response.read(currentChassisState); 246 auto strParam = std::get<std::string>(currentChassisState); 247 return (strParam != CHASSIS_STATE_OFF); 248 } 249 catch (const sdbusplus::exception::SdBusError& e) 250 { 251 log<level::ERR>("Error in fetching current Chassis State", 252 entry("MAPPERRESPONSE=%s", 253 (mapperResponse.begin()->first).c_str())); 254 elog<InternalFailure>(); 255 } 256 } 257 258 } // namespace updater 259 } // namespace software 260 } // namespace openpower 261