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