1 #include "config.h" 2 3 #include "activation.hpp" 4 5 #include "item_updater.hpp" 6 7 #include <experimental/filesystem> 8 #include <phosphor-logging/elog-errors.hpp> 9 #include <phosphor-logging/elog.hpp> 10 #include <phosphor-logging/log.hpp> 11 #include <sdbusplus/exception.hpp> 12 #include <sdbusplus/server.hpp> 13 #include <xyz/openbmc_project/Common/error.hpp> 14 15 #ifdef WANT_SIGNATURE_VERIFY 16 #include "image_verify.hpp" 17 #endif 18 19 namespace openpower 20 { 21 namespace software 22 { 23 namespace updater 24 { 25 26 namespace fs = std::experimental::filesystem; 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 sdbusplus::message::variant<std::string> result; 189 reply.read(result); 190 auto applyTime = 191 sdbusplus::message::variant_ns::get<std::string>(result); 192 if (applyTime == applyTimeImmediate) 193 { 194 return true; 195 } 196 } 197 catch (const SdBusError& e) 198 { 199 log<level::ERR>("Error in getting ApplyTime", 200 entry("ERROR=%s", e.what())); 201 } 202 } 203 return false; 204 } 205 206 void Activation::rebootHost() 207 { 208 auto service = utils::getService(bus, hostStateObjPath, hostStateIntf); 209 if (service.empty()) 210 { 211 log<level::ALERT>("Error in getting the service name to reboot the " 212 "Host. The Host needs to be manually rebooted to " 213 "complete the image activation."); 214 } 215 216 auto method = bus.new_method_call(service.c_str(), hostStateObjPath, 217 dbusPropIntf, "Set"); 218 sdbusplus::message::variant<std::string> hostReboot = hostStateRebootVal; 219 method.append(hostStateIntf, hostStateRebootProp, hostReboot); 220 221 try 222 { 223 auto reply = bus.call(method); 224 } 225 catch (const SdBusError& e) 226 { 227 log<level::ALERT>("Error in trying to reboot the Host. " 228 "The Host needs to be manually rebooted to complete " 229 "the image activation.", 230 entry("ERROR=%s", e.what())); 231 report<InternalFailure>(); 232 } 233 } 234 235 uint8_t RedundancyPriority::priority(uint8_t value) 236 { 237 parent.parent.freePriority(value, parent.versionId); 238 return softwareServer::RedundancyPriority::priority(value); 239 } 240 241 #ifdef WANT_SIGNATURE_VERIFY 242 bool Activation::validateSignature(const std::string& pnorFileName) 243 { 244 using Signature = openpower::software::image::Signature; 245 fs::path imageDir(IMG_DIR); 246 247 Signature signature(imageDir / versionId, pnorFileName, 248 PNOR_SIGNED_IMAGE_CONF_PATH); 249 250 // Validate the signed image. 251 if (signature.verify()) 252 { 253 return true; 254 } 255 // Log error and continue activation process, if field mode disabled. 256 log<level::ERR>("Error occurred during image validation"); 257 report<InternalFailure>(); 258 259 try 260 { 261 if (!fieldModeEnabled()) 262 { 263 return true; 264 } 265 } 266 catch (const InternalFailure& e) 267 { 268 report<InternalFailure>(); 269 } 270 return false; 271 } 272 273 bool Activation::fieldModeEnabled() 274 { 275 auto fieldModeSvc = 276 utils::getService(bus, FIELDMODE_PATH, FIELDMODE_INTERFACE); 277 278 auto method = bus.new_method_call(fieldModeSvc.c_str(), FIELDMODE_PATH, 279 "org.freedesktop.DBus.Properties", "Get"); 280 281 method.append(FIELDMODE_INTERFACE, "FieldModeEnabled"); 282 283 sdbusplus::message::variant<bool> fieldMode; 284 285 try 286 { 287 auto reply = bus.call(method); 288 reply.read(fieldMode); 289 return sdbusplus::message::variant_ns::get<bool>(fieldMode); 290 } 291 catch (const SdBusError& e) 292 { 293 log<level::ERR>("Error in fieldModeEnabled getValue"); 294 elog<InternalFailure>(); 295 } 296 } 297 298 #endif 299 300 } // namespace updater 301 } // namespace software 302 } // namespace openpower 303