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("/au/com/codeconstruct/mctp1");
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 iface = I2CMCTPDDevice::match(config);
84 if (iface)
85 {
86 info("Creating I2CMCTPDDevice");
87 return I2CMCTPDDevice::from(connection, *iface);
88 }
89
90 iface = I3CMCTPDDevice::match(config);
91 if (iface)
92 {
93 info("Creating I3CMCTPDDevice");
94 return I3CMCTPDDevice::from(connection, *iface);
95 }
96 }
97 catch (const std::invalid_argument& ex)
98 {
99 error("Unable to create device: {EXCEPTION}", "EXCEPTION", ex);
100 }
101
102 return {};
103 }
104
addInventory(const std::shared_ptr<sdbusplus::asio::connection> & connection,const std::shared_ptr<MCTPReactor> & reactor,sdbusplus::message_t & msg)105 static void addInventory(
106 const std::shared_ptr<sdbusplus::asio::connection>& connection,
107 const std::shared_ptr<MCTPReactor>& reactor, sdbusplus::message_t& msg)
108 {
109 auto [path,
110 exposed] = msg.unpack<sdbusplus::message::object_path, SensorData>();
111 try
112 {
113 reactor->manageMCTPDevice(path, deviceFromConfig(connection, exposed));
114 }
115 catch (const std::logic_error& e)
116 {
117 error(
118 "Addition of inventory at '{INVENTORY_PATH}' caused an invalid program state: {EXCEPTION}",
119 "INVENTORY_PATH", path, "EXCEPTION", e);
120 }
121 catch (const std::system_error& e)
122 {
123 error(
124 "Failed to manage device described by inventory at '{INVENTORY_PATH}: {EXCEPTION}'",
125 "INVENTORY_PATH", path, "EXCEPTION", e);
126 }
127 }
128
removeInventory(const std::shared_ptr<MCTPReactor> & reactor,sdbusplus::message_t & msg)129 static void removeInventory(const std::shared_ptr<MCTPReactor>& reactor,
130 sdbusplus::message_t& msg)
131 {
132 auto [path, removed] =
133 msg.unpack<sdbusplus::message::object_path, std::set<std::string>>();
134 try
135 {
136 if (I2CMCTPDDevice::match(removed) || I3CMCTPDDevice::match(removed))
137 {
138 reactor->unmanageMCTPDevice(path.str);
139 }
140 }
141 catch (const std::logic_error& e)
142 {
143 error(
144 "Removal of inventory at '{INVENTORY_PATH}' caused an invalid program state: {EXCEPTION}",
145 "INVENTORY_PATH", path, "EXCEPTION", e);
146 }
147 catch (const std::system_error& e)
148 {
149 error(
150 "Failed to unmanage device described by inventory at '{INVENTORY_PATH}: {EXCEPTION}'",
151 "INVENTORY_PATH", path, "EXCEPTION", e);
152 }
153 }
154
manageMCTPEntity(const std::shared_ptr<sdbusplus::asio::connection> & connection,const std::shared_ptr<MCTPReactor> & reactor,ManagedObjectType & entities)155 static void manageMCTPEntity(
156 const std::shared_ptr<sdbusplus::asio::connection>& connection,
157 const std::shared_ptr<MCTPReactor>& reactor, ManagedObjectType& entities)
158 {
159 for (const auto& [path, config] : entities)
160 {
161 try
162 {
163 reactor->manageMCTPDevice(path,
164 deviceFromConfig(connection, config));
165 }
166 catch (const std::logic_error& e)
167 {
168 error(
169 "Addition of inventory at '{INVENTORY_PATH}' caused an invalid program state: {EXCEPTION}",
170 "INVENTORY_PATH", path, "EXCEPTION", e);
171 }
172 catch (const std::system_error& e)
173 {
174 error(
175 "Failed to manage device described by inventory at '{INVENTORY_PATH}: {EXCEPTION}'",
176 "INVENTORY_PATH", path, "EXCEPTION", e);
177 }
178 }
179 }
180
exitReactor(boost::asio::io_context * io,sdbusplus::message_t & msg)181 static void exitReactor(boost::asio::io_context* io, sdbusplus::message_t& msg)
182 {
183 auto name = msg.unpack<std::string>();
184 info("Shutting down mctpreactor, lost dependency '{SERVICE_NAME}'",
185 "SERVICE_NAME", name);
186 io->stop();
187 }
188
main()189 int main()
190 {
191 constexpr std::chrono::seconds period(5);
192
193 boost::asio::io_context io;
194 auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
195 DBusAssociationServer associationServer(systemBus);
196 auto reactor = std::make_shared<MCTPReactor>(associationServer);
197 boost::asio::steady_timer clock(io);
198
199 std::function<void(const boost::system::error_code&)> alarm =
200 [&](const boost::system::error_code& ec) {
201 if (ec)
202 {
203 return;
204 }
205 clock.expires_after(period);
206 clock.async_wait(alarm);
207 reactor->tick();
208 };
209 clock.expires_after(period);
210 clock.async_wait(alarm);
211
212 using namespace sdbusplus::bus::match;
213
214 const std::string entityManagerNameLostSpec =
215 rules::nameOwnerChanged("xyz.openbmc_project.EntityManager");
216
217 auto entityManagerNameLostMatch = sdbusplus::bus::match_t(
218 static_cast<sdbusplus::bus_t&>(*systemBus), entityManagerNameLostSpec,
219 std::bind_front(exitReactor, &io));
220
221 const std::string mctpdNameLostSpec =
222 rules::nameOwnerChanged("au.com.codeconstruct.MCTP1");
223
224 auto mctpdNameLostMatch = sdbusplus::bus::match_t(
225 static_cast<sdbusplus::bus_t&>(*systemBus), mctpdNameLostSpec,
226 std::bind_front(exitReactor, &io));
227
228 const std::string interfacesRemovedMatchSpec =
229 rules::sender("xyz.openbmc_project.EntityManager") +
230 // Trailing slash on path: Listen for signals on the inventory subtree
231 rules::interfacesRemovedAtPath("/xyz/openbmc_project/inventory/");
232
233 auto interfacesRemovedMatch = sdbusplus::bus::match_t(
234 static_cast<sdbusplus::bus_t&>(*systemBus), interfacesRemovedMatchSpec,
235 std::bind_front(removeInventory, reactor));
236
237 const std::string interfacesAddedMatchSpec =
238 rules::sender("xyz.openbmc_project.EntityManager") +
239 // Trailing slash on path: Listen for signals on the inventory subtree
240 rules::interfacesAddedAtPath("/xyz/openbmc_project/inventory/");
241
242 auto interfacesAddedMatch = sdbusplus::bus::match_t(
243 static_cast<sdbusplus::bus_t&>(*systemBus), interfacesAddedMatchSpec,
244 std::bind_front(addInventory, systemBus, reactor));
245
246 systemBus->request_name("xyz.openbmc_project.MCTPReactor");
247
248 boost::asio::post(io, [reactor, systemBus]() {
249 auto gsc = std::make_shared<GetSensorConfiguration>(
250 systemBus, std::bind_front(manageMCTPEntity, systemBus, reactor));
251 gsc->getConfiguration({"MCTPI2CTarget", "MCTPI3CTarget"});
252 });
253
254 io.run();
255
256 return EXIT_SUCCESS;
257 }
258