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