1 #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
28 PHOSPHOR_LOG2_USING;
29
30 class DBusAssociationServer : public AssociationServer
31 {
32 public:
33 DBusAssociationServer() = delete;
34 DBusAssociationServer(const DBusAssociationServer&) = delete;
35 DBusAssociationServer(DBusAssociationServer&&) = delete;
DBusAssociationServer(const std::shared_ptr<sdbusplus::asio::connection> & connection)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
associate(const std::string & path,const std::vector<Association> & associations)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
disassociate(const std::string & path)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
deviceFromConfig(const std::shared_ptr<sdbusplus::asio::connection> & connection,const SensorData & config)76 static 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
addInventory(const std::shared_ptr<sdbusplus::asio::connection> & connection,const std::shared_ptr<MCTPReactor> & reactor,sdbusplus::message_t & msg)97 static 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
removeInventory(const std::shared_ptr<MCTPReactor> & reactor,sdbusplus::message_t & msg)121 static 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
manageMCTPEntity(const std::shared_ptr<sdbusplus::asio::connection> & connection,const std::shared_ptr<MCTPReactor> & reactor,ManagedObjectType & entities)147 static 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
exitReactor(boost::asio::io_context * io,sdbusplus::message_t & msg)173 static 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
main()181 int 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 }
250