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