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