blob: d8493b7e9d30910db5b1c0c5a0a2c95d0ad8609c [file] [log] [blame]
Alexander Hansencc372352025-01-14 14:15:39 +01001#include "software_manager.hpp"
2
Alexander Hansencec14752025-05-08 13:11:03 +02003#include <boost/container/flat_map.hpp>
Alexander Hansencc372352025-01-14 14:15:39 +01004#include <phosphor-logging/lg2.hpp>
5#include <sdbusplus/asio/object_server.hpp>
6#include <sdbusplus/async.hpp>
7#include <sdbusplus/async/context.hpp>
8#include <sdbusplus/bus.hpp>
Alexander Hansencec14752025-05-08 13:11:03 +02009#include <sdbusplus/bus/match.hpp>
Alexander Hansencc372352025-01-14 14:15:39 +010010#include <xyz/openbmc_project/Association/Definitions/server.hpp>
11#include <xyz/openbmc_project/ObjectMapper/client.hpp>
Alexander Hansende5e76f2025-02-20 16:30:11 +010012#include <xyz/openbmc_project/Software/Version/client.hpp>
Alexander Hansencc372352025-01-14 14:15:39 +010013#include <xyz/openbmc_project/State/Host/client.hpp>
14
15#include <cstdint>
16
17PHOSPHOR_LOG2_USING;
18
19using namespace phosphor::software::manager;
20
Alexander Hansencec14752025-05-08 13:11:03 +020021using AsyncMatch = sdbusplus::async::match;
22
23namespace RulesIntf = sdbusplus::bus::match::rules;
24static constexpr auto serviceNameEM = "xyz.openbmc_project.EntityManager";
25
26const auto matchRuleSender = RulesIntf::sender(serviceNameEM);
27const auto matchRulePath = RulesIntf::path("/xyz/openbmc_project/inventory");
28
Alexander Hansencc372352025-01-14 14:15:39 +010029SoftwareManager::SoftwareManager(sdbusplus::async::context& ctx,
30 const std::string& serviceNameSuffix) :
Alexander Hansencec14752025-05-08 13:11:03 +020031 ctx(ctx),
32 configIntfAddedMatch(ctx, RulesIntf::interfacesAdded() + matchRuleSender),
33 configIntfRemovedMatch(ctx, RulesIntf::interfacesRemoved() + matchRulePath),
34 serviceNameSuffix(serviceNameSuffix),
Alexander Hansende5e76f2025-02-20 16:30:11 +010035 manager(ctx, sdbusplus::client::xyz::openbmc_project::software::Version<>::
36 namespace_path)
Alexander Hansencc372352025-01-14 14:15:39 +010037{
38 const std::string serviceNameFull =
39 "xyz.openbmc_project.Software." + serviceNameSuffix;
40
41 debug("requesting dbus name {BUSNAME}", "BUSNAME", serviceNameFull);
42
Alexander Hansende5e76f2025-02-20 16:30:11 +010043 ctx.request_name(serviceNameFull.c_str());
Alexander Hansencc372352025-01-14 14:15:39 +010044
Alexander Hansende5e76f2025-02-20 16:30:11 +010045 debug("Initialized SoftwareManager");
Alexander Hansencc372352025-01-14 14:15:39 +010046}
47
Alexander Hansenf3d407b2025-05-08 09:52:31 +020048static sdbusplus::async::task<std::optional<SoftwareConfig>> getConfig(
49 sdbusplus::async::context& ctx, const std::string& service,
50 const std::string& objectPath, const std::string& interfacePrefix)
Alexander Hansenf3d407b2025-05-08 09:52:31 +020051{
52 auto client = sdbusplus::async::proxy()
53 .service(service)
54 .path(objectPath)
55 .interface("org.freedesktop.DBus.Properties");
56
57 uint64_t vendorIANA = 0;
58 std::string compatible{};
59 std::string configType{};
60 std::string configName{};
61
62 const std::string interfaceName = interfacePrefix + ".FirmwareInfo";
63
64 try
65 {
66 {
67 auto propVendorIANA = co_await client.call<std::variant<uint64_t>>(
68 ctx, "Get", interfaceName, "VendorIANA");
69
70 vendorIANA = std::get<uint64_t>(propVendorIANA);
71 }
72 {
73 auto propCompatible =
74 co_await client.call<std::variant<std::string>>(
75 ctx, "Get", interfaceName, "CompatibleHardware");
76
77 compatible = std::get<std::string>(propCompatible);
78 }
79 {
80 auto propConfigType =
81 co_await client.call<std::variant<std::string>>(
82 ctx, "Get", interfacePrefix, "Type");
83
84 configType = std::get<std::string>(propConfigType);
85 }
86 {
87 auto propConfigName =
88 co_await client.call<std::variant<std::string>>(
89 ctx, "Get", interfacePrefix, "Name");
90
91 configName = std::get<std::string>(propConfigName);
92 }
93 }
94 catch (std::exception& e)
95 {
96 error("Failed to get config with {ERROR}", "ERROR", e);
97 co_return std::nullopt;
98 }
99
100 co_return SoftwareConfig(objectPath, vendorIANA, compatible, configType,
101 configName);
102}
103
Alexander Hansencc372352025-01-14 14:15:39 +0100104sdbusplus::async::task<> SoftwareManager::initDevices(
105 const std::vector<std::string>& configurationInterfaces)
Alexander Hansencc372352025-01-14 14:15:39 +0100106{
Alexander Hansencec14752025-05-08 13:11:03 +0200107 ctx.spawn(interfaceAddedMatch(configurationInterfaces));
108 ctx.spawn(interfaceRemovedMatch(configurationInterfaces));
109
Alexander Hansencc372352025-01-14 14:15:39 +0100110 auto client = sdbusplus::client::xyz::openbmc_project::ObjectMapper<>(ctx)
111 .service("xyz.openbmc_project.ObjectMapper")
112 .path("/xyz/openbmc_project/object_mapper");
113
Alexander Hansen0a457ff2025-02-25 16:13:47 +0100114 auto res = co_await client.get_sub_tree("/xyz/openbmc_project/inventory", 0,
115 configurationInterfaces);
Alexander Hansencc372352025-01-14 14:15:39 +0100116
117 for (auto& iface : configurationInterfaces)
118 {
119 debug("[config] looking for dbus interface {INTF}", "INTF", iface);
120 }
121
122 for (auto& [path, v] : res)
123 {
124 for (auto& [service, interfaceNames] : v)
125 {
126 std::string interfaceFound;
127
128 for (std::string& interfaceName : interfaceNames)
129 {
130 for (auto& iface : configurationInterfaces)
131 {
132 if (interfaceName == iface)
133 {
134 interfaceFound = interfaceName;
135 }
136 }
137 }
138
139 if (interfaceFound.empty())
140 {
141 continue;
142 }
143
Alexander Hansen90174792025-05-08 10:14:54 +0200144 co_await handleInterfaceAdded(service, path, interfaceFound);
Alexander Hansencc372352025-01-14 14:15:39 +0100145 }
146 }
147
Alexander Hansenf3d407b2025-05-08 09:52:31 +0200148 debug("Done with initial configuration");
Alexander Hansencc372352025-01-14 14:15:39 +0100149}
Alexander Hansen90174792025-05-08 10:14:54 +0200150
Alexander Hansen90174792025-05-08 10:14:54 +0200151sdbusplus::async::task<void> SoftwareManager::handleInterfaceAdded(
152 const std::string& service, const std::string& path,
153 const std::string& interface)
Alexander Hansen90174792025-05-08 10:14:54 +0200154{
155 debug("Found configuration interface at {SERVICE}, {PATH}", "SERVICE",
156 service, "PATH", path);
157
158 auto optConfig = co_await getConfig(ctx, service, path, interface);
159
160 if (!optConfig.has_value())
161 {
162 error("Failed to get configuration from {PATH}", "PATH", path);
163 co_return;
164 }
165
Alexander Hansend73d5642025-07-23 14:05:06 +0200166 auto& config = optConfig.value();
167
168 if (devices.contains(config.objectPath))
Alexander Hansencec14752025-05-08 13:11:03 +0200169 {
170 error("Device configured from {PATH} is already known", "PATH",
Alexander Hansend73d5642025-07-23 14:05:06 +0200171 config.objectPath);
Alexander Hansencec14752025-05-08 13:11:03 +0200172 co_return;
173 }
174
Alexander Hansend73d5642025-07-23 14:05:06 +0200175 const bool accepted = co_await initDevice(service, path, config);
176
177 if (accepted && devices.contains(config.objectPath))
178 {
179 auto& device = devices[config.objectPath];
180
181 if (device->softwareCurrent)
182 {
183 co_await device->softwareCurrent->createInventoryAssociations(true);
Alexander Hansen0fdb6122025-07-09 14:33:13 +0200184
185 device->softwareCurrent->setActivation(
186 SoftwareActivation::Activations::Active);
Alexander Hansend73d5642025-07-23 14:05:06 +0200187 }
188 }
Alexander Hansen90174792025-05-08 10:14:54 +0200189
190 co_return;
191}
Alexander Hansencec14752025-05-08 13:11:03 +0200192
193using BasicVariantType =
194 std::variant<std::vector<std::string>, std::string, int64_t, uint64_t,
195 double, int32_t, uint32_t, int16_t, uint16_t, uint8_t, bool>;
196using InterfacesMap = boost::container::flat_map<std::string, BasicVariantType>;
197using ConfigMap = boost::container::flat_map<std::string, InterfacesMap>;
198
Alexander Hansencec14752025-05-08 13:11:03 +0200199sdbusplus::async::task<void> SoftwareManager::interfaceAddedMatch(
200 std::vector<std::string> interfaces)
Alexander Hansencec14752025-05-08 13:11:03 +0200201{
202 while (!ctx.stop_requested())
203 {
204 std::tuple<std::string, ConfigMap> nextResult("", {});
205 nextResult = co_await configIntfAddedMatch
206 .next<sdbusplus::message::object_path, ConfigMap>();
207
208 auto& [objPath, interfacesMap] = nextResult;
209
210 for (auto& interface : interfaces)
211 {
212 if (interfacesMap.contains(interface))
213 {
214 debug("detected interface {INTF} added on {PATH}", "INTF",
215 interface, "PATH", objPath);
216
217 co_await handleInterfaceAdded(serviceNameEM, objPath,
218 interface);
219 }
220 }
221 }
222}
223
Alexander Hansencec14752025-05-08 13:11:03 +0200224sdbusplus::async::task<void> SoftwareManager::interfaceRemovedMatch(
225 std::vector<std::string> interfaces)
Alexander Hansencec14752025-05-08 13:11:03 +0200226{
227 while (!ctx.stop_requested())
228 {
229 auto nextResult = co_await configIntfRemovedMatch.next<
230 sdbusplus::message::object_path, std::vector<std::string>>();
231
232 auto& [objPath, interfacesRemoved] = nextResult;
233
234 debug("detected interface removed on {PATH}", "PATH", objPath);
235
236 for (auto& interface : interfaces)
237 {
238 if (std::ranges::find(interfacesRemoved, interface) !=
239 interfacesRemoved.end())
240 {
241 debug("detected interface {INTF} removed on {PATH}", "INTF",
242 interface, "PATH", objPath);
243 co_await handleInterfaceRemoved(objPath);
244 }
245 }
246 }
247}
248
249sdbusplus::async::task<void> SoftwareManager::handleInterfaceRemoved(
250 const sdbusplus::message::object_path& objPath)
251{
252 if (!devices.contains(objPath))
253 {
254 debug("could not find a device to remove");
255 co_return;
256 }
257
258 if (devices[objPath]->updateInProgress)
259 {
260 // TODO: This code path needs to be cleaned up in the future to
261 // eventually remove the device.
262 debug(
263 "removal of device at {PATH} ignored because of in-progress update",
264 "PATH", objPath.str);
265 co_return;
266 }
267
268 debug("removing device at {PATH}", "PATH", objPath.str);
269 devices.erase(objPath);
270}