#include "MCTPEndpoint.hpp" #include "MCTPReactor.hpp" #include "Utils.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include PHOSPHOR_LOG2_USING; class DBusAssociationServer : public AssociationServer { public: DBusAssociationServer() = delete; DBusAssociationServer(const DBusAssociationServer&) = delete; DBusAssociationServer(DBusAssociationServer&&) = delete; explicit DBusAssociationServer( const std::shared_ptr& connection) : server(connection) { server.add_manager("/xyz/openbmc_project/mctp"); } ~DBusAssociationServer() override = default; DBusAssociationServer& operator=(const DBusAssociationServer&) = delete; DBusAssociationServer& operator=(DBusAssociationServer&&) = delete; void associate(const std::string& path, const std::vector& associations) override { auto [entry, _] = objects.emplace( path, server.add_interface(path, association::interface)); std::shared_ptr iface = entry->second; iface->register_property("Associations", associations); iface->initialize(); } void disassociate(const std::string& path) override { const auto entry = objects.find(path); if (entry == objects.end()) { throw std::logic_error(std::format( "Attempted to untrack path that was not tracked: {}", path)); } std::shared_ptr iface = entry->second; server.remove_interface(entry->second); objects.erase(entry); } private: std::shared_ptr connection; sdbusplus::asio::object_server server; std::map> objects; }; static std::shared_ptr deviceFromConfig( const std::shared_ptr& connection, const SensorData& config) { try { std::optional iface; // NOLINTNEXTLINE(bugprone-assignment-in-if-condition) if ((iface = I2CMCTPDDevice::match(config))) { return I2CMCTPDDevice::from(connection, *iface); } } catch (const std::invalid_argument& ex) { error("Unable to create device: {EXCEPTION}", "EXCEPTION", ex); } return {}; } static void addInventory( const std::shared_ptr& connection, const std::shared_ptr& reactor, sdbusplus::message_t& msg) { auto [path, exposed] = msg.unpack(); try { reactor->manageMCTPDevice(path, deviceFromConfig(connection, exposed)); } catch (const std::logic_error& e) { error( "Addition of inventory at '{INVENTORY_PATH}' caused an invalid program state: {EXCEPTION}", "INVENTORY_PATH", path, "EXCEPTION", e); } catch (const std::system_error& e) { error( "Failed to manage device described by inventory at '{INVENTORY_PATH}: {EXCEPTION}'", "INVENTORY_PATH", path, "EXCEPTION", e); } } static void removeInventory(const std::shared_ptr& reactor, sdbusplus::message_t& msg) { auto [path, removed] = msg.unpack>(); try { if (I2CMCTPDDevice::match(removed)) { reactor->unmanageMCTPDevice(path.str); } } catch (const std::logic_error& e) { error( "Removal of inventory at '{INVENTORY_PATH}' caused an invalid program state: {EXCEPTION}", "INVENTORY_PATH", path, "EXCEPTION", e); } catch (const std::system_error& e) { error( "Failed to unmanage device described by inventory at '{INVENTORY_PATH}: {EXCEPTION}'", "INVENTORY_PATH", path, "EXCEPTION", e); } } static void manageMCTPEntity( const std::shared_ptr& connection, const std::shared_ptr& reactor, ManagedObjectType& entities) { for (const auto& [path, config] : entities) { try { reactor->manageMCTPDevice(path, deviceFromConfig(connection, config)); } catch (const std::logic_error& e) { error( "Addition of inventory at '{INVENTORY_PATH}' caused an invalid program state: {EXCEPTION}", "INVENTORY_PATH", path, "EXCEPTION", e); } catch (const std::system_error& e) { error( "Failed to manage device described by inventory at '{INVENTORY_PATH}: {EXCEPTION}'", "INVENTORY_PATH", path, "EXCEPTION", e); } } } static void exitReactor(boost::asio::io_context* io, sdbusplus::message_t& msg) { auto name = msg.unpack(); info("Shutting down mctpreactor, lost dependency '{SERVICE_NAME}'", "SERVICE_NAME", name); io->stop(); } int main() { constexpr std::chrono::seconds period(5); boost::asio::io_context io; auto systemBus = std::make_shared(io); DBusAssociationServer associationServer(systemBus); auto reactor = std::make_shared(associationServer); boost::asio::steady_timer clock(io); std::function alarm = [&](const boost::system::error_code& ec) { if (ec) { return; } clock.expires_after(period); clock.async_wait(alarm); reactor->tick(); }; clock.expires_after(period); clock.async_wait(alarm); using namespace sdbusplus::bus::match; const std::string entityManagerNameLostSpec = rules::nameOwnerChanged("xyz.openbmc_project.EntityManager"); auto entityManagerNameLostMatch = sdbusplus::bus::match_t( static_cast(*systemBus), entityManagerNameLostSpec, std::bind_front(exitReactor, &io)); const std::string mctpdNameLostSpec = rules::nameOwnerChanged("xyz.openbmc_project.MCTP"); auto mctpdNameLostMatch = sdbusplus::bus::match_t( static_cast(*systemBus), mctpdNameLostSpec, std::bind_front(exitReactor, &io)); const std::string interfacesRemovedMatchSpec = rules::sender("xyz.openbmc_project.EntityManager") + // Trailing slash on path: Listen for signals on the inventory subtree rules::interfacesRemovedAtPath("/xyz/openbmc_project/inventory/"); auto interfacesRemovedMatch = sdbusplus::bus::match_t( static_cast(*systemBus), interfacesRemovedMatchSpec, std::bind_front(removeInventory, reactor)); const std::string interfacesAddedMatchSpec = rules::sender("xyz.openbmc_project.EntityManager") + // Trailing slash on path: Listen for signals on the inventory subtree rules::interfacesAddedAtPath("/xyz/openbmc_project/inventory/"); auto interfacesAddedMatch = sdbusplus::bus::match_t( static_cast(*systemBus), interfacesAddedMatchSpec, std::bind_front(addInventory, systemBus, reactor)); systemBus->request_name("xyz.openbmc_project.MCTPReactor"); boost::asio::post(io, [reactor, systemBus]() { auto gsc = std::make_shared( systemBus, std::bind_front(manageMCTPEntity, systemBus, reactor)); gsc->getConfiguration({"MCTPI2CTarget"}); }); io.run(); return EXIT_SUCCESS; }