xref: /openbmc/phosphor-bmc-code-mgmt/common/src/software_update.cpp (revision bd5081f0b99a27dde8101576524b2c02a4a580b6)
1 #include "software_update.hpp"
2 
3 #include "device.hpp"
4 #include "software.hpp"
5 
6 #include <phosphor-logging/elog-errors.hpp>
7 #include <phosphor-logging/elog.hpp>
8 #include <phosphor-logging/lg2.hpp>
9 #include <sdbusplus/async/context.hpp>
10 #include <xyz/openbmc_project/Software/Update/aserver.hpp>
11 
12 PHOSPHOR_LOG2_USING;
13 
14 using Unavailable = sdbusplus::xyz::openbmc_project::Common::Error::Unavailable;
15 
16 using namespace phosphor::logging;
17 using namespace phosphor::software::update;
18 using namespace phosphor::software::device;
19 using namespace phosphor::software;
20 
21 namespace SoftwareLogging = phosphor::logging::xyz::openbmc_project::software;
22 namespace SoftwareErrors =
23     sdbusplus::error::xyz::openbmc_project::software::image;
24 
25 SoftwareUpdate::SoftwareUpdate(
26     sdbusplus::async::context& ctx, const char* path, Software& software,
27     const std::set<RequestedApplyTimes>& allowedApplyTimes) :
28     sdbusplus::aserver::xyz::openbmc_project::software::Update<SoftwareUpdate>(
29         ctx, path),
30     software(software), allowedApplyTimes(allowedApplyTimes)
31 {
32     emit_added();
33 }
34 
35 SoftwareUpdate::~SoftwareUpdate()
36 {
37     emit_removed();
38 }
39 
40 auto SoftwareUpdate::method_call(start_update_t /*unused*/, auto image,
41                                  auto applyTime)
42     -> sdbusplus::async::task<start_update_t::return_type>
43 {
44     debug("Requesting Image update with {FD}", "FD", image.fd);
45 
46     Device& device = software.parentDevice;
47 
48     if (device.updateInProgress)
49     {
50         error("An update is already in progress, cannot update.");
51         report<Unavailable>();
52         co_return sdbusplus::message::object_path();
53     }
54 
55     device.updateInProgress = true;
56 
57     // check if the apply time is allowed by our device
58     if (!allowedApplyTimes.contains(applyTime))
59     {
60         error(
61             "the selected apply time {APPLYTIME} is not allowed by the device",
62             "APPLYTIME", applyTime);
63         device.updateInProgress = false;
64         report<Unavailable>();
65         co_return sdbusplus::message::object_path();
66     }
67 
68     debug("started asynchronous update with fd {FD}", "FD", image.fd);
69 
70     int imageDup = dup(image.fd);
71 
72     if (imageDup < 0)
73     {
74         error("ERROR calling dup on fd: {ERR}", "ERR", strerror(errno));
75         device.updateInProgress = false;
76         co_return software.objectPath;
77     }
78 
79     debug("starting async update with FD: {FD}\n", "FD", imageDup);
80 
81     std::unique_ptr<Software> softwareInstance =
82         std::make_unique<Software>(ctx, device);
83 
84     softwareInstance->setActivation(ActivationInterface::Activations::NotReady);
85 
86     std::string newObjPath = softwareInstance->objectPath;
87 
88     ctx.spawn(
89         [](Device& device, int imageDup, RequestedApplyTimes applyTime,
90            std::unique_ptr<Software> swupdate) -> sdbusplus::async::task<> {
91             co_await device.startUpdateAsync(imageDup, applyTime,
92                                              std::move(swupdate));
93             device.updateInProgress = false;
94             close(imageDup);
95             co_return;
96         }(device, imageDup, applyTime, std::move(softwareInstance)));
97 
98     // We need the object path for the new software here.
99     // It must be the same as constructed during the update process.
100     // This is so that bmcweb and redfish clients can keep track of the update
101     // process.
102     co_return newObjPath;
103 }
104 
105 auto SoftwareUpdate::get_property(allowed_apply_times_t /*unused*/) const
106 {
107     return allowedApplyTimes;
108 }
109