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
subscribeToSystemdSignals()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
unsubscribeFromSystemdSignals()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
requestedActivation(RequestedActivations value)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
deleteImageManagerObject()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
checkApplyTimeImmediate()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
rebootHost()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
priority(uint8_t value)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
validateSignature(const std::string & pnorFileName)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
fieldModeEnabled()270 bool Activation::fieldModeEnabled()
271 {
272 auto fieldModeSvc =
273 utils::getService(bus, FIELDMODE_PATH, 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