blob: 473e37726cdbba34ebb7cea114d045bc33c75249 [file] [log] [blame]
Alexander Hansen0119cd72025-01-14 14:15:39 +01001#include "software.hpp"
2
3#include "device.hpp"
4#include "software_update.hpp"
5
6#include <phosphor-logging/lg2.hpp>
7#include <sdbusplus/async/context.hpp>
8#include <xyz/openbmc_project/Association/Definitions/server.hpp>
9#include <xyz/openbmc_project/Software/Activation/aserver.hpp>
10#include <xyz/openbmc_project/Software/Update/aserver.hpp>
11#include <xyz/openbmc_project/State/Host/client.hpp>
12
13SoftwareActivationProgress::SoftwareActivationProgress(
14 sdbusplus::async::context& ctx, const char* objPath) :
15 ActivationProgress(ctx, objPath)
16{
17 // This prevents "Conditional jump or move depends on uninitialised
18 // value(s)"
19 // when properties are updated for the first time
20 progress_ = 0;
21}
22
23Software::Software(sdbusplus::async::context& ctx, const std::string& swid,
24 Device& parent) :
25 sdbusplus::aserver::xyz::openbmc_project::software::Activation<Software>(
26 ctx, Software::getObjPathFromSwid(swid).c_str()),
27 swid(swid), parent(parent), ctx(ctx)
28{
29 // initialize the members of our base class to prevent
30 // "Conditional jump or move depends on uninitialised value(s)"
31 activation_ = Activations::NotReady;
32 requested_activation_ = RequestedActivations::None;
33
34 std::string objPath = Software::getObjPathFromSwid(swid);
35
36 if (!objPath.starts_with("/"))
37 {
38 throw std::invalid_argument(objPath + " is not an object path");
39 }
40
41 lg2::debug("{SWID}: created dbus interfaces on path {OBJPATH}", "SWID",
42 swid, "OBJPATH", objPath);
43};
44
45std::string Software::getObjPathFromSwid(const std::string& swid)
46{
47 std::string basepath = "/xyz/openbmc_project/software/";
48 return basepath + swid;
49}
50
51static long int getRandomId()
52{
53 struct timespec ts;
54 clock_gettime(CLOCK_REALTIME, &ts);
55 unsigned int seed = ts.tv_nsec ^ getpid();
56 srandom(seed);
57 return random() % 10000;
58}
59
60std::string Software::getRandomSoftwareId(Device& parent)
61{
62 // Swid = <DeviceX>_<RandomId>
63 // For same type devices, extend the Dbus path to specify device
64 // instance, for example,
65 // /xyz/openbmc_project/Software/<deviceX>_<InstanceNum>_<SwId>
66
67 // The problem here is that InstanceNum needs to always stay the same,
68 // so that the device can be identified in the redfish fw inventory.
69
70 // Since 'Name' property is already provided in EM config, we can insert
71 // that in place of 'InstanceNum'.
72
73 const std::string configType = parent.getEMConfigType();
74
75 // 'Name' property in EM config
76 const std::string nameEM = parent.config.configName;
77
78 return std::format("{}_{}_{}", configType, nameEM, getRandomId());
79}
80
81Device& Software::getParentDevice()
82{
83 return this->parent;
84}
85
86// NOLINTBEGIN
87sdbusplus::async::task<> Software::setAssociationDefinitionsRunningActivating(
88 bool isRunning, bool isActivating)
89// NOLINTEND
90{
91 lg2::debug("{SWID}: setting association definitions", "SWID", this->swid);
92
93 std::string endpoint = co_await parent.getInventoryItemObjectPath();
94
95 if (!this->optSoftwareAssociationDefinitions)
96 {
97 std::string path = this->getObjectPath();
98 this->optSoftwareAssociationDefinitions =
99 std::make_unique<SoftwareAssociationDefinitions>(ctx, path.c_str());
100 }
101
102 std::string forward;
103 std::string reverse;
104 std::vector<std::tuple<std::string, std::string, std::string>> assocs;
105
106 if (isRunning)
107 {
108 lg2::debug("{SWID}: creating 'running' association to {OBJPATH}",
109 "SWID", this->swid, "OBJPATH", endpoint);
110 forward = "running";
111 reverse = "ran_on";
112 std::tuple<std::string, std::string, std::string> assocRunning = {
113 forward, reverse, endpoint};
114 assocs.push_back(assocRunning);
115 }
116
117 if (isActivating)
118 {
119 lg2::debug("{SWID}: creating 'activating' association to {OBJPATH}",
120 "SWID", this->swid, "OBJPATH", endpoint);
121 forward = "activating";
122 reverse = "activated_on";
123 std::tuple<std::string, std::string, std::string> assocActivating = {
124 forward, reverse, endpoint};
125 assocs.push_back(assocActivating);
126 }
127
128 this->optSoftwareAssociationDefinitions->associations(assocs);
129
130 co_return;
131}
132
133void Software::setVersion(const std::string& versionStr)
134{
135 lg2::debug("{SWID}: set version {VERSION}", "SWID", this->swid, "VERSION",
136 versionStr);
137
138 if (this->optSoftwareVersion)
139 {
140 lg2::error("{SWID}: version was already set", "SWID", this->swid);
141 return;
142 }
143
144 std::string path = this->getObjectPath();
145 this->optSoftwareVersion =
146 std::make_unique<SoftwareVersion>(ctx, path.c_str());
147 this->optSoftwareVersion->version(versionStr);
148}
149
150void Software::setActivationBlocksTransition(bool enabled)
151{
152 if (!enabled)
153 {
154 this->optActivationBlocksTransition = nullptr;
155 return;
156 }
157
158 std::string path = this->getObjectPath();
159 this->optActivationBlocksTransition =
160 std::make_unique<sdbusplus::aserver::xyz::openbmc_project::software::
161 ActivationBlocksTransition<Software>>(
162 this->ctx, path.c_str());
163}
164
165void Software::setActivation(
166 sdbusplus::common::xyz::openbmc_project::software::Activation::Activations
167 act)
168{
169 this->activation(act);
170}
171
172sdbusplus::message::object_path Software::getObjectPath() const
173{
174 std::string objPathStr = Software::getObjPathFromSwid(swid);
175 return sdbusplus::message::object_path(objPathStr.c_str());
176}
177
178void Software::enableUpdate(
179 const std::set<RequestedApplyTimes>& allowedApplyTimes)
180{
181 std::string objPath = getObjectPath();
182
183 if (this->optSoftwareUpdate != nullptr)
184 {
185 lg2::error("[Software] update of {OBJPATH} has already been enabled",
186 "OBJPATH", objPath);
187 return;
188 }
189
190 lg2::info(
191 "[Software] enabling update of {OBJPATH} (adding the update interface)",
192 "OBJPATH", objPath);
193
194 optSoftwareUpdate = std::make_unique<SoftwareUpdate>(
195 this->ctx, objPath.c_str(), *this, allowedApplyTimes);
196}