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