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; 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 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 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 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 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 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 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