1 #include "activation.hpp"
2 
3 #include "images.hpp"
4 #include "item_updater.hpp"
5 #include "serialize.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 <xyz/openbmc_project/Common/error.hpp>
12 
13 #ifdef WANT_SIGNATURE_VERIFY
14 #include "image_verify.hpp"
15 #endif
16 
17 namespace phosphor
18 {
19 namespace software
20 {
21 namespace updater
22 {
23 
24 namespace softwareServer = sdbusplus::xyz::openbmc_project::Software::server;
25 
26 using namespace phosphor::logging;
27 using sdbusplus::exception::SdBusError;
28 using InternalFailure =
29     sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
30 
31 #ifdef WANT_SIGNATURE_VERIFY
32 namespace control = sdbusplus::xyz::openbmc_project::Control::server;
33 #endif
34 
35 void Activation::subscribeToSystemdSignals()
36 {
37     auto method = this->bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH,
38                                             SYSTEMD_INTERFACE, "Subscribe");
39     try
40     {
41         this->bus.call_noreply(method);
42     }
43     catch (const SdBusError& e)
44     {
45         if (e.name() != nullptr &&
46             strcmp("org.freedesktop.systemd1.AlreadySubscribed", e.name()) == 0)
47         {
48             // If an Activation attempt fails, the Unsubscribe method is not
49             // called. This may lead to an AlreadySubscribed error if the
50             // Activation is re-attempted.
51         }
52         else
53         {
54             log<level::ERR>("Error subscribing to systemd",
55                             entry("ERROR=%s", e.what()));
56         }
57     }
58 
59     return;
60 }
61 
62 void Activation::unsubscribeFromSystemdSignals()
63 {
64     auto method = this->bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH,
65                                             SYSTEMD_INTERFACE, "Unsubscribe");
66     try
67     {
68         this->bus.call_noreply(method);
69     }
70     catch (const SdBusError& e)
71     {
72         log<level::ERR>("Error in unsubscribing from systemd signals",
73                         entry("ERROR=%s", e.what()));
74     }
75 
76     return;
77 }
78 
79 auto Activation::activation(Activations value) -> Activations
80 {
81 
82     if ((value != softwareServer::Activation::Activations::Active) &&
83         (value != softwareServer::Activation::Activations::Activating))
84     {
85         redundancyPriority.reset(nullptr);
86     }
87 
88     if (value == softwareServer::Activation::Activations::Activating)
89     {
90 #ifdef UBIFS_LAYOUT
91         if (rwVolumeCreated == false && roVolumeCreated == false)
92         {
93             // Enable systemd signals
94             Activation::subscribeToSystemdSignals();
95 
96             parent.freeSpace(*this);
97 
98             if (!activationProgress)
99             {
100                 activationProgress =
101                     std::make_unique<ActivationProgress>(bus, path);
102             }
103 
104             if (!activationBlocksTransition)
105             {
106                 activationBlocksTransition =
107                     std::make_unique<ActivationBlocksTransition>(bus, path);
108             }
109 
110 #ifdef WANT_SIGNATURE_VERIFY
111             fs::path uploadDir(IMG_UPLOAD_DIR);
112             if (!verifySignature(uploadDir / versionId, SIGNED_IMAGE_CONF_PATH))
113             {
114                 onVerifyFailed();
115                 // Stop the activation process, if fieldMode is enabled.
116                 if (parent.control::FieldMode::fieldModeEnabled())
117                 {
118                     // Cleanup
119                     activationBlocksTransition.reset(nullptr);
120                     activationProgress.reset(nullptr);
121                     return softwareServer::Activation::activation(
122                         softwareServer::Activation::Activations::Failed);
123                 }
124             }
125 #endif
126 
127             flashWrite();
128 
129             activationProgress->progress(10);
130         }
131         else if (rwVolumeCreated == true && roVolumeCreated == true)
132         {
133             if (ubootEnvVarsUpdated == false)
134             {
135                 activationProgress->progress(90);
136 
137                 if (!redundancyPriority)
138                 {
139                     redundancyPriority = std::make_unique<RedundancyPriority>(
140                         bus, path, *this, 0);
141                 }
142             }
143             else
144             {
145                 activationProgress->progress(100);
146 
147                 activationBlocksTransition.reset(nullptr);
148                 activationProgress.reset(nullptr);
149 
150                 rwVolumeCreated = false;
151                 roVolumeCreated = false;
152                 ubootEnvVarsUpdated = false;
153                 Activation::unsubscribeFromSystemdSignals();
154 
155                 // Remove version object from image manager
156                 Activation::deleteImageManagerObject();
157 
158                 // Create active association
159                 parent.createActiveAssociation(path);
160 
161                 if (Activation::checkApplyTimeImmediate() == true)
162                 {
163                     log<level::INFO>("Image Active. ApplyTime is immediate, "
164                                      "rebooting BMC.");
165                     Activation::rebootBmc();
166                 }
167 
168                 return softwareServer::Activation::activation(
169                     softwareServer::Activation::Activations::Active);
170             }
171         }
172 #else // !UBIFS_LAYOUT
173 
174 #ifdef WANT_SIGNATURE_VERIFY
175         fs::path uploadDir(IMG_UPLOAD_DIR);
176         if (!verifySignature(uploadDir / versionId, SIGNED_IMAGE_CONF_PATH))
177         {
178             onVerifyFailed();
179             // Stop the activation process, if fieldMode is enabled.
180             if (parent.control::FieldMode::fieldModeEnabled())
181             {
182                 return softwareServer::Activation::activation(
183                     softwareServer::Activation::Activations::Failed);
184             }
185         }
186 #endif
187         parent.freeSpace(*this);
188 
189         flashWrite();
190 
191         if (!redundancyPriority)
192         {
193             redundancyPriority =
194                 std::make_unique<RedundancyPriority>(bus, path, *this, 0);
195         }
196 
197         // Remove version object from image manager
198         Activation::deleteImageManagerObject();
199 
200         // Create active association
201         parent.createActiveAssociation(path);
202 
203         log<level::INFO>("BMC image ready, need reboot to get activated.");
204         return softwareServer::Activation::activation(
205             softwareServer::Activation::Activations::Active);
206 #endif
207     }
208     else
209     {
210         activationBlocksTransition.reset(nullptr);
211         activationProgress.reset(nullptr);
212     }
213     return softwareServer::Activation::activation(value);
214 }
215 
216 void Activation::deleteImageManagerObject()
217 {
218     // Call the Delete object for <versionID> inside image_manager
219     auto method = this->bus.new_method_call(VERSION_BUSNAME, path.c_str(),
220                                             "xyz.openbmc_project.Object.Delete",
221                                             "Delete");
222     try
223     {
224         bus.call_noreply(method);
225     }
226     catch (const SdBusError& e)
227     {
228         log<level::ERR>("Error in Deleting image from image manager",
229                         entry("VERSIONPATH=%s", path.c_str()));
230         return;
231     }
232 }
233 
234 auto Activation::requestedActivation(RequestedActivations value)
235     -> RequestedActivations
236 {
237     rwVolumeCreated = false;
238     roVolumeCreated = false;
239     ubootEnvVarsUpdated = false;
240 
241     if ((value == softwareServer::Activation::RequestedActivations::Active) &&
242         (softwareServer::Activation::requestedActivation() !=
243          softwareServer::Activation::RequestedActivations::Active))
244     {
245         if ((softwareServer::Activation::activation() ==
246              softwareServer::Activation::Activations::Ready) ||
247             (softwareServer::Activation::activation() ==
248              softwareServer::Activation::Activations::Failed))
249         {
250             Activation::activation(
251                 softwareServer::Activation::Activations::Activating);
252         }
253     }
254     return softwareServer::Activation::requestedActivation(value);
255 }
256 
257 uint8_t RedundancyPriority::priority(uint8_t value)
258 {
259     // Set the priority value so that the freePriority() function can order
260     // the versions by priority.
261     auto newPriority = softwareServer::RedundancyPriority::priority(value);
262     parent.parent.savePriority(parent.versionId, value);
263     parent.parent.freePriority(value, parent.versionId);
264     return newPriority;
265 }
266 
267 uint8_t RedundancyPriority::sdbusPriority(uint8_t value)
268 {
269     parent.parent.savePriority(parent.versionId, value);
270     return softwareServer::RedundancyPriority::priority(value);
271 }
272 
273 void Activation::unitStateChange(sdbusplus::message::message& msg)
274 {
275     if (softwareServer::Activation::activation() !=
276         softwareServer::Activation::Activations::Activating)
277     {
278         return;
279     }
280 
281     onStateChanges(msg);
282 
283     return;
284 }
285 
286 #ifdef WANT_SIGNATURE_VERIFY
287 bool Activation::verifySignature(const fs::path& imageDir,
288                                  const fs::path& confDir)
289 {
290     using Signature = phosphor::software::image::Signature;
291 
292     Signature signature(imageDir, confDir);
293 
294     return signature.verify();
295 }
296 
297 void Activation::onVerifyFailed()
298 {
299     log<level::ERR>("Error occurred during image validation");
300     report<InternalFailure>();
301 }
302 #endif
303 
304 void ActivationBlocksTransition::enableRebootGuard()
305 {
306     log<level::INFO>("BMC image activating - BMC reboots are disabled.");
307 
308     auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH,
309                                       SYSTEMD_INTERFACE, "StartUnit");
310     method.append("reboot-guard-enable.service", "replace");
311     bus.call_noreply(method);
312 }
313 
314 void ActivationBlocksTransition::disableRebootGuard()
315 {
316     log<level::INFO>("BMC activation has ended - BMC reboots are re-enabled.");
317 
318     auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH,
319                                       SYSTEMD_INTERFACE, "StartUnit");
320     method.append("reboot-guard-disable.service", "replace");
321     bus.call_noreply(method);
322 }
323 
324 bool Activation::checkApplyTimeImmediate()
325 {
326     auto service = utils::getService(bus, applyTimeObjPath, applyTimeIntf);
327     if (service.empty())
328     {
329         log<level::INFO>("Error getting the service name for BMC image "
330                          "ApplyTime. The BMC needs to be manually rebooted to "
331                          "complete the image activation if needed "
332                          "immediately.");
333     }
334     else
335     {
336 
337         auto method = bus.new_method_call(service.c_str(), applyTimeObjPath,
338                                           dbusPropIntf, "Get");
339         method.append(applyTimeIntf, applyTimeProp);
340 
341         try
342         {
343             auto reply = bus.call(method);
344 
345             sdbusplus::message::variant<std::string> result;
346             reply.read(result);
347             auto applyTime =
348                 sdbusplus::message::variant_ns::get<std::string>(result);
349             if (applyTime == applyTimeImmediate)
350             {
351                 return true;
352             }
353         }
354         catch (const SdBusError& e)
355         {
356             log<level::ERR>("Error in getting ApplyTime",
357                             entry("ERROR=%s", e.what()));
358         }
359     }
360     return false;
361 }
362 
363 void Activation::rebootBmc()
364 {
365     auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH,
366                                       SYSTEMD_INTERFACE, "StartUnit");
367     method.append("force-reboot.service", "replace");
368     try
369     {
370         auto reply = bus.call(method);
371     }
372     catch (const SdBusError& e)
373     {
374         log<level::ALERT>("Error in trying to reboot the BMC. "
375                           "The BMC needs to be manually rebooted to complete "
376                           "the image activation.");
377         report<InternalFailure>();
378     }
379 }
380 
381 } // namespace updater
382 } // namespace software
383 } // namespace phosphor
384