xref: /openbmc/dbus-sensors/src/mctp/MCTPReactorMain.cpp (revision bd815c7f79301fe3932093bfd799122879122680)
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