blob: 131e480057ecb7e30adc78f86282aeb48584558e [file] [log] [blame]
Lei YU12c9f4c2019-09-11 15:08:15 +08001#include "config.h"
2
Lei YU01539e72019-07-31 10:57:38 +08003#include "activation.hpp"
4
Lei YU12c9f4c2019-09-11 15:08:15 +08005#include "utils.hpp"
6
7#include <cassert>
8#include <filesystem>
Lei YUd0bbfa92019-09-11 16:10:54 +08009#include <phosphor-logging/elog-errors.hpp>
10#include <phosphor-logging/log.hpp>
Lei YU12c9f4c2019-09-11 15:08:15 +080011
Lei YU01539e72019-07-31 10:57:38 +080012namespace phosphor
13{
14namespace software
15{
16namespace updater
17{
18
Lei YU12c9f4c2019-09-11 15:08:15 +080019constexpr auto SYSTEMD_BUSNAME = "org.freedesktop.systemd1";
20constexpr auto SYSTEMD_PATH = "/org/freedesktop/systemd1";
21constexpr auto SYSTEMD_INTERFACE = "org.freedesktop.systemd1.Manager";
22
23namespace fs = std::filesystem;
Lei YU01539e72019-07-31 10:57:38 +080024namespace softwareServer = sdbusplus::xyz::openbmc_project::Software::server;
25
Lei YUd0bbfa92019-09-11 16:10:54 +080026using namespace phosphor::logging;
27using sdbusplus::exception::SdBusError;
Lei YU12c9f4c2019-09-11 15:08:15 +080028using SoftwareActivation = softwareServer::Activation;
29
30namespace internal
31{
32/** Construct the systemd service name */
33std::string getUpdateService(const std::string& psuInventoryPath,
34 const std::string& versionId)
35{
36 fs::path imagePath(IMG_DIR);
37 imagePath /= versionId;
38
39 // The systemd unit shall be escaped
40 std::string args = psuInventoryPath;
41 args += "\\x20";
42 args += imagePath;
43 std::replace(args.begin(), args.end(), '/', '-');
44
45 std::string service = PSU_UPDATE_SERVICE;
46 auto p = service.find('@');
47 assert(p != std::string::npos);
48 service.insert(p + 1, args);
49 return service;
50}
51
52} // namespace internal
Lei YU01539e72019-07-31 10:57:38 +080053auto Activation::activation(Activations value) -> Activations
54{
Lei YU12c9f4c2019-09-11 15:08:15 +080055 if (value == Status::Activating)
56 {
57 startActivation();
58 }
59 else
60 {
Lei YU81c67722019-09-11 16:47:29 +080061 activationBlocksTransition.reset();
Lei YU12c9f4c2019-09-11 15:08:15 +080062 }
63
64 return SoftwareActivation::activation(value);
Lei YU01539e72019-07-31 10:57:38 +080065}
66
67auto Activation::requestedActivation(RequestedActivations value)
68 -> RequestedActivations
69{
Lei YU12c9f4c2019-09-11 15:08:15 +080070 if ((value == SoftwareActivation::RequestedActivations::Active) &&
71 (SoftwareActivation::requestedActivation() !=
72 SoftwareActivation::RequestedActivations::Active))
73 {
74 if ((activation() == Status::Ready) || (activation() == Status::Failed))
75 {
76 activation(Status::Activating);
77 }
78 }
79 return SoftwareActivation::requestedActivation(value);
80}
81
82void Activation::unitStateChange(sdbusplus::message::message& msg)
83{
84 uint32_t newStateID{};
85 sdbusplus::message::object_path newStateObjPath;
86 std::string newStateUnit{};
87 std::string newStateResult{};
88
89 // Read the msg and populate each variable
90 msg.read(newStateID, newStateObjPath, newStateUnit, newStateResult);
91
92 if (newStateUnit == psuUpdateUnit)
93 {
94 if (newStateResult == "done")
95 {
96 finishActivation();
97 }
98 if (newStateResult == "failed" || newStateResult == "dependency")
99 {
100 activation(Status::Failed);
101 }
102 }
103}
104
105void Activation::startActivation()
106{
Lei YU81c67722019-09-11 16:47:29 +0800107 if (!activationBlocksTransition)
108 {
109 activationBlocksTransition =
110 std::make_unique<ActivationBlocksTransition>(bus, path);
111 }
112
Lei YU12c9f4c2019-09-11 15:08:15 +0800113 // TODO: for now only update one psu, future commits shall handle update
114 // multiple psus
115 auto psuPaths = utils::getPSUInventoryPath(bus);
116 if (psuPaths.empty())
117 {
118 return;
119 }
120
121 psuUpdateUnit = internal::getUpdateService(psuPaths[0], versionId);
122
123 auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH,
124 SYSTEMD_INTERFACE, "StartUnit");
125 method.append(psuUpdateUnit, "replace");
126 bus.call_noreply(method);
127}
128
129void Activation::finishActivation()
130{
Lei YU81c67722019-09-11 16:47:29 +0800131 activationBlocksTransition.reset();
132
Lei YU12c9f4c2019-09-11 15:08:15 +0800133 // TODO: delete the old software object
134 // TODO: create related associations
Lei YUd0bbfa92019-09-11 16:10:54 +0800135 deleteImageManagerObject();
Lei YU12c9f4c2019-09-11 15:08:15 +0800136 activation(Status::Active);
Lei YU01539e72019-07-31 10:57:38 +0800137}
138
Lei YUd0bbfa92019-09-11 16:10:54 +0800139void Activation::deleteImageManagerObject()
140{
141 // Get the Delete object for <versionID> inside image_manager
142 constexpr auto versionServiceStr = "xyz.openbmc_project.Software.Version";
143 constexpr auto deleteInterface = "xyz.openbmc_project.Object.Delete";
144 std::string versionService;
145 auto services = utils::getServices(bus, path.c_str(), deleteInterface);
146
147 // We need to find the phosphor-version-software-manager's version service
148 // to invoke the delete interface
149 for (const auto& service : services)
150 {
151 if (service.find(versionServiceStr) != std::string::npos)
152 {
153 versionService = service;
154 break;
155 }
156 }
157 if (versionService.empty())
158 {
159 log<level::ERR>("Error finding version service");
160 return;
161 }
162
163 // Call the Delete object for <versionID> inside image_manager
164 auto method = bus.new_method_call(versionService.c_str(), path.c_str(),
165 deleteInterface, "Delete");
166 try
167 {
168 bus.call(method);
169 }
170 catch (const SdBusError& e)
171 {
172 log<level::ERR>("Error performing call to Delete object path",
173 entry("ERROR=%s", e.what()),
174 entry("PATH=%s", path.c_str()));
175 }
176}
177
Lei YU01539e72019-07-31 10:57:38 +0800178} // namespace updater
179} // namespace software
180} // namespace phosphor