1 #include "config.h" 2 3 #include "activation.hpp" 4 5 #include "item_updater.hpp" 6 7 #include <phosphor-logging/elog-errors.hpp> 8 #include <phosphor-logging/elog.hpp> 9 #include <phosphor-logging/log.hpp> 10 #include <sdbusplus/exception.hpp> 11 #include <sdbusplus/server.hpp> 12 #include <xyz/openbmc_project/Common/error.hpp> 13 14 #include <filesystem> 15 16 #ifdef WANT_SIGNATURE_VERIFY 17 #include "image_verify.hpp" 18 #endif 19 20 namespace openpower 21 { 22 namespace software 23 { 24 namespace updater 25 { 26 27 namespace softwareServer = sdbusplus::xyz::openbmc_project::Software::server; 28 29 using namespace phosphor::logging; 30 using InternalFailure = 31 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; 32 33 #ifdef WANT_SIGNATURE_VERIFY 34 // Field mode path and interface. 35 constexpr auto FIELDMODE_PATH("/xyz/openbmc_project/software"); 36 constexpr auto FIELDMODE_INTERFACE("xyz.openbmc_project.Control.FieldMode"); 37 #endif 38 39 constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1"; 40 constexpr auto SYSTEMD_OBJ_PATH = "/org/freedesktop/systemd1"; 41 42 void Activation::subscribeToSystemdSignals() 43 { 44 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH, 45 SYSTEMD_INTERFACE, "Subscribe"); 46 try 47 { 48 this->bus.call_noreply(method); 49 } 50 catch (const sdbusplus::exception_t& e) 51 { 52 if (e.name() != nullptr && 53 strcmp("org.freedesktop.systemd1.AlreadySubscribed", e.name()) == 0) 54 { 55 // If an Activation attempt fails, the Unsubscribe method is not 56 // called. This may lead to an AlreadySubscribed error if the 57 // Activation is re-attempted. 58 } 59 else 60 { 61 log<level::ERR>("Error subscribing to systemd", 62 entry("ERROR=%s", e.what())); 63 } 64 } 65 return; 66 } 67 68 void Activation::unsubscribeFromSystemdSignals() 69 { 70 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH, 71 SYSTEMD_INTERFACE, "Unsubscribe"); 72 this->bus.call_noreply(method); 73 74 return; 75 } 76 77 auto Activation::requestedActivation(RequestedActivations value) 78 -> RequestedActivations 79 { 80 if ((value == softwareServer::Activation::RequestedActivations::Active) && 81 (softwareServer::Activation::requestedActivation() != 82 softwareServer::Activation::RequestedActivations::Active)) 83 { 84 if ((softwareServer::Activation::activation() == 85 softwareServer::Activation::Activations::Ready) || 86 (softwareServer::Activation::activation() == 87 softwareServer::Activation::Activations::Failed)) 88 { 89 activation(softwareServer::Activation::Activations::Activating); 90 } 91 } 92 return softwareServer::Activation::requestedActivation(value); 93 } 94 95 void Activation::deleteImageManagerObject() 96 { 97 // Get the Delete object for <versionID> inside image_manager 98 constexpr auto versionServiceStr = "xyz.openbmc_project.Software.Version"; 99 constexpr auto deleteInterface = "xyz.openbmc_project.Object.Delete"; 100 std::string versionService; 101 auto method = this->bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH, 102 MAPPER_INTERFACE, "GetObject"); 103 104 method.append(path); 105 method.append(std::vector<std::string>({deleteInterface})); 106 107 std::map<std::string, std::vector<std::string>> mapperResponse; 108 109 try 110 { 111 auto mapperResponseMsg = bus.call(method); 112 mapperResponseMsg.read(mapperResponse); 113 if (mapperResponse.begin() == mapperResponse.end()) 114 { 115 log<level::ERR>("ERROR in reading the mapper response", 116 entry("VERSIONPATH=%s", path.c_str())); 117 return; 118 } 119 } 120 catch (const sdbusplus::exception_t& e) 121 { 122 log<level::ERR>("Error in Get Delete Object", 123 entry("VERSIONPATH=%s", path.c_str())); 124 return; 125 } 126 127 // We need to find the phosphor-software-manager's version service 128 // to invoke the delete interface 129 for (auto resp : mapperResponse) 130 { 131 if (resp.first.find(versionServiceStr) != std::string::npos) 132 { 133 versionService = resp.first; 134 } 135 } 136 137 if (versionService.empty()) 138 { 139 log<level::ERR>("Error finding version service"); 140 return; 141 } 142 143 // Call the Delete object for <versionID> inside image_manager 144 method = this->bus.new_method_call(versionService.c_str(), path.c_str(), 145 deleteInterface, "Delete"); 146 try 147 { 148 bus.call(method); 149 } 150 catch (const sdbusplus::exception_t& e) 151 { 152 if (e.name() != nullptr && strcmp("System.Error.ELOOP", e.name()) == 0) 153 { 154 // TODO: Error being tracked with openbmc/openbmc#3311 155 } 156 else 157 { 158 log<level::ERR>("Error performing call to Delete object path", 159 entry("ERROR=%s", e.what()), 160 entry("PATH=%s", path.c_str())); 161 } 162 return; 163 } 164 } 165 166 bool Activation::checkApplyTimeImmediate() 167 { 168 auto service = utils::getService(bus, applyTimeObjPath, applyTimeIntf); 169 if (service.empty()) 170 { 171 log<level::INFO>("Error getting the service name for Host image " 172 "ApplyTime. The Host needs to be manually rebooted to " 173 "complete the image activation if needed " 174 "immediately."); 175 } 176 else 177 { 178 auto method = bus.new_method_call(service.c_str(), applyTimeObjPath, 179 dbusPropIntf, "Get"); 180 method.append(applyTimeIntf, applyTimeProp); 181 182 try 183 { 184 auto reply = bus.call(method); 185 186 std::variant<std::string> result; 187 reply.read(result); 188 auto applyTime = std::get<std::string>(result); 189 if (applyTime == applyTimeImmediate) 190 { 191 return true; 192 } 193 } 194 catch (const sdbusplus::exception_t& e) 195 { 196 log<level::ERR>("Error in getting ApplyTime", 197 entry("ERROR=%s", e.what())); 198 } 199 } 200 return false; 201 } 202 203 void Activation::rebootHost() 204 { 205 auto service = utils::getService(bus, hostStateObjPath, hostStateIntf); 206 if (service.empty()) 207 { 208 log<level::ALERT>("Error in getting the service name to reboot the " 209 "Host. The Host needs to be manually rebooted to " 210 "complete the image activation."); 211 } 212 213 auto method = bus.new_method_call(service.c_str(), hostStateObjPath, 214 dbusPropIntf, "Set"); 215 std::variant<std::string> hostReboot = hostStateRebootVal; 216 method.append(hostStateIntf, hostStateRebootProp, hostReboot); 217 218 try 219 { 220 auto reply = bus.call(method); 221 } 222 catch (const sdbusplus::exception_t& e) 223 { 224 log<level::ALERT>("Error in trying to reboot the Host. " 225 "The Host needs to be manually rebooted to complete " 226 "the image activation.", 227 entry("ERROR=%s", e.what())); 228 report<InternalFailure>(); 229 } 230 } 231 232 uint8_t RedundancyPriority::priority(uint8_t value) 233 { 234 parent.parent.freePriority(value, parent.versionId); 235 return softwareServer::RedundancyPriority::priority(value); 236 } 237 238 #ifdef WANT_SIGNATURE_VERIFY 239 bool Activation::validateSignature(const std::string& pnorFileName) 240 { 241 using Signature = openpower::software::image::Signature; 242 std::filesystem::path imageDir(IMG_DIR); 243 244 Signature signature(imageDir / versionId, pnorFileName, 245 PNOR_SIGNED_IMAGE_CONF_PATH); 246 247 // Validate the signed image. 248 if (signature.verify()) 249 { 250 return true; 251 } 252 // Log error and continue activation process, if field mode disabled. 253 log<level::ERR>("Error occurred during image validation"); 254 report<InternalFailure>(); 255 256 try 257 { 258 if (!fieldModeEnabled()) 259 { 260 return true; 261 } 262 } 263 catch (const InternalFailure& e) 264 { 265 report<InternalFailure>(); 266 } 267 return false; 268 } 269 270 bool Activation::fieldModeEnabled() 271 { 272 auto fieldModeSvc = utils::getService(bus, FIELDMODE_PATH, 273 FIELDMODE_INTERFACE); 274 275 auto method = bus.new_method_call(fieldModeSvc.c_str(), FIELDMODE_PATH, 276 "org.freedesktop.DBus.Properties", "Get"); 277 278 method.append(FIELDMODE_INTERFACE, "FieldModeEnabled"); 279 280 std::variant<bool> fieldMode; 281 282 try 283 { 284 auto reply = bus.call(method); 285 reply.read(fieldMode); 286 return std::get<bool>(fieldMode); 287 } 288 catch (const sdbusplus::exception_t& e) 289 { 290 log<level::ERR>("Error in fieldModeEnabled getValue"); 291 elog<InternalFailure>(); 292 } 293 } 294 295 #endif 296 297 } // namespace updater 298 } // namespace software 299 } // namespace openpower 300