blob: 9abc0fcd238c1c1155d52ac2d5e374fb6a877d2d [file] [log] [blame]
Andrew Jeffery275f7c32024-01-31 12:41:14 +10301#include "MCTPEndpoint.hpp"
2#include "MCTPReactor.hpp"
3#include "Utils.hpp"
4
5#include <boost/asio/io_context.hpp>
6#include <boost/asio/post.hpp>
7#include <boost/asio/steady_timer.hpp>
8#include <phosphor-logging/lg2.hpp>
9#include <sdbusplus/asio/connection.hpp>
10#include <sdbusplus/asio/object_server.hpp>
11#include <sdbusplus/bus.hpp>
12#include <sdbusplus/bus/match.hpp>
13#include <sdbusplus/message.hpp>
14#include <sdbusplus/message/native_types.hpp>
15
16#include <chrono>
17#include <cstdlib>
18#include <format>
19#include <functional>
20#include <map>
21#include <memory>
22#include <optional>
23#include <set>
24#include <stdexcept>
25#include <system_error>
26#include <vector>
27
28PHOSPHOR_LOG2_USING;
29
30class DBusAssociationServer : public AssociationServer
31{
32 public:
33 DBusAssociationServer() = delete;
34 DBusAssociationServer(const DBusAssociationServer&) = delete;
35 DBusAssociationServer(DBusAssociationServer&&) = delete;
36 explicit DBusAssociationServer(
37 const std::shared_ptr<sdbusplus::asio::connection>& connection) :
38 server(connection)
39 {
40 server.add_manager("/xyz/openbmc_project/mctp");
41 }
42 ~DBusAssociationServer() override = default;
43 DBusAssociationServer& operator=(const DBusAssociationServer&) = delete;
44 DBusAssociationServer& operator=(DBusAssociationServer&&) = delete;
45
46 void associate(const std::string& path,
47 const std::vector<Association>& associations) override
48 {
49 auto [entry, _] = objects.emplace(
50 path, server.add_interface(path, association::interface));
51 std::shared_ptr<sdbusplus::asio::dbus_interface> iface = entry->second;
52 iface->register_property("Associations", associations);
53 iface->initialize();
54 }
55
56 void disassociate(const std::string& path) override
57 {
58 const auto entry = objects.find(path);
59 if (entry == objects.end())
60 {
61 throw std::logic_error(std::format(
62 "Attempted to untrack path that was not tracked: {}", path));
63 }
64 std::shared_ptr<sdbusplus::asio::dbus_interface> iface = entry->second;
65 server.remove_interface(entry->second);
66 objects.erase(entry);
67 }
68
69 private:
70 std::shared_ptr<sdbusplus::asio::connection> connection;
71 sdbusplus::asio::object_server server;
72 std::map<std::string, std::shared_ptr<sdbusplus::asio::dbus_interface>>
73 objects;
74};
75
76static std::shared_ptr<MCTPDevice> deviceFromConfig(
77 const std::shared_ptr<sdbusplus::asio::connection>& connection,
78 const SensorData& config)
79{
80 try
81 {
82 std::optional<SensorBaseConfigMap> iface;
83 // NOLINTNEXTLINE(bugprone-assignment-in-if-condition)
84 if ((iface = I2CMCTPDDevice::match(config)))
85 {
86 return I2CMCTPDDevice::from(connection, *iface);
87 }
88 }
89 catch (const std::invalid_argument& ex)
90 {
91 error("Unable to create device: {EXCEPTION}", "EXCEPTION", ex);
92 }
93
94 return {};
95}
96
97static void addInventory(
98 const std::shared_ptr<sdbusplus::asio::connection>& connection,
99 const std::shared_ptr<MCTPReactor>& reactor, sdbusplus::message_t& msg)
100{
101 auto [path,
102 exposed] = msg.unpack<sdbusplus::message::object_path, SensorData>();
103 try
104 {
105 reactor->manageMCTPDevice(path, deviceFromConfig(connection, exposed));
106 }
107 catch (const std::logic_error& e)
108 {
109 error(
110 "Addition of inventory at '{INVENTORY_PATH}' caused an invalid program state: {EXCEPTION}",
111 "INVENTORY_PATH", path, "EXCEPTION", e);
112 }
113 catch (const std::system_error& e)
114 {
115 error(
116 "Failed to manage device described by inventory at '{INVENTORY_PATH}: {EXCEPTION}'",
117 "INVENTORY_PATH", path, "EXCEPTION", e);
118 }
119}
120
121static void removeInventory(const std::shared_ptr<MCTPReactor>& reactor,
122 sdbusplus::message_t& msg)
123{
124 auto [path, removed] =
125 msg.unpack<sdbusplus::message::object_path, std::set<std::string>>();
126 try
127 {
128 if (I2CMCTPDDevice::match(removed))
129 {
130 reactor->unmanageMCTPDevice(path.str);
131 }
132 }
133 catch (const std::logic_error& e)
134 {
135 error(
136 "Removal of inventory at '{INVENTORY_PATH}' caused an invalid program state: {EXCEPTION}",
137 "INVENTORY_PATH", path, "EXCEPTION", e);
138 }
139 catch (const std::system_error& e)
140 {
141 error(
142 "Failed to unmanage device described by inventory at '{INVENTORY_PATH}: {EXCEPTION}'",
143 "INVENTORY_PATH", path, "EXCEPTION", e);
144 }
145}
146
147static void manageMCTPEntity(
148 const std::shared_ptr<sdbusplus::asio::connection>& connection,
149 const std::shared_ptr<MCTPReactor>& reactor, ManagedObjectType& entities)
150{
151 for (const auto& [path, config] : entities)
152 {
153 try
154 {
155 reactor->manageMCTPDevice(path,
156 deviceFromConfig(connection, config));
157 }
158 catch (const std::logic_error& e)
159 {
160 error(
161 "Addition of inventory at '{INVENTORY_PATH}' caused an invalid program state: {EXCEPTION}",
162 "INVENTORY_PATH", path, "EXCEPTION", e);
163 }
164 catch (const std::system_error& e)
165 {
166 error(
167 "Failed to manage device described by inventory at '{INVENTORY_PATH}: {EXCEPTION}'",
168 "INVENTORY_PATH", path, "EXCEPTION", e);
169 }
170 }
171}
172
173static void exitReactor(boost::asio::io_context* io, sdbusplus::message_t& msg)
174{
175 auto name = msg.unpack<std::string>();
176 info("Shutting down mctpreactor, lost dependency '{SERVICE_NAME}'",
177 "SERVICE_NAME", name);
178 io->stop();
179}
180
181int main()
182{
183 constexpr std::chrono::seconds period(5);
184
185 boost::asio::io_context io;
186 auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
187 DBusAssociationServer associationServer(systemBus);
188 auto reactor = std::make_shared<MCTPReactor>(associationServer);
189 boost::asio::steady_timer clock(io);
190
191 std::function<void(const boost::system::error_code&)> alarm =
192 [&](const boost::system::error_code& ec) {
193 if (ec)
194 {
195 return;
196 }
197 clock.expires_after(period);
198 clock.async_wait(alarm);
199 reactor->tick();
200 };
201 clock.expires_after(period);
202 clock.async_wait(alarm);
203
204 using namespace sdbusplus::bus::match;
205
206 const std::string entityManagerNameLostSpec =
207 rules::nameOwnerChanged("xyz.openbmc_project.EntityManager");
208
209 auto entityManagerNameLostMatch = sdbusplus::bus::match_t(
210 static_cast<sdbusplus::bus_t&>(*systemBus), entityManagerNameLostSpec,
211 std::bind_front(exitReactor, &io));
212
213 const std::string mctpdNameLostSpec =
214 rules::nameOwnerChanged("xyz.openbmc_project.MCTP");
215
216 auto mctpdNameLostMatch = sdbusplus::bus::match_t(
217 static_cast<sdbusplus::bus_t&>(*systemBus), mctpdNameLostSpec,
218 std::bind_front(exitReactor, &io));
219
220 const std::string interfacesRemovedMatchSpec =
221 rules::sender("xyz.openbmc_project.EntityManager") +
222 // Trailing slash on path: Listen for signals on the inventory subtree
223 rules::interfacesRemovedAtPath("/xyz/openbmc_project/inventory/");
224
225 auto interfacesRemovedMatch = sdbusplus::bus::match_t(
226 static_cast<sdbusplus::bus_t&>(*systemBus), interfacesRemovedMatchSpec,
227 std::bind_front(removeInventory, reactor));
228
229 const std::string interfacesAddedMatchSpec =
230 rules::sender("xyz.openbmc_project.EntityManager") +
231 // Trailing slash on path: Listen for signals on the inventory subtree
232 rules::interfacesAddedAtPath("/xyz/openbmc_project/inventory/");
233
234 auto interfacesAddedMatch = sdbusplus::bus::match_t(
235 static_cast<sdbusplus::bus_t&>(*systemBus), interfacesAddedMatchSpec,
236 std::bind_front(addInventory, systemBus, reactor));
237
238 systemBus->request_name("xyz.openbmc_project.MCTPReactor");
239
240 boost::asio::post(io, [reactor, systemBus]() {
241 auto gsc = std::make_shared<GetSensorConfiguration>(
242 systemBus, std::bind_front(manageMCTPEntity, systemBus, reactor));
243 gsc->getConfiguration({"MCTPI2CTarget"});
244 });
245
246 io.run();
247
248 return EXIT_SUCCESS;
249}