blob: 106cb1073721ff925a69701e4e457c701c070a63 [file]
#include "MctpUtil.hpp"
#include "Utils.hpp"
#include "VariantVisitors.hpp"
#include <boost/asio/steady_timer.hpp>
#include <boost/container/flat_map.hpp>
#include <phosphor-logging/lg2.hpp>
#include <sdbusplus/bus/match.hpp>
#include <chrono>
#include <filesystem>
#include <iostream>
std::map<std::string, SensorData> mctpEndpointConfigMap;
enum class State : uint8_t
{
None,
Add,
Remove
};
struct EndpointState
{
State state = State::None;
bool querying = false;
};
static std::map<std::string, EndpointState> endpointStates;
static std::unique_ptr<sdbusplus::bus::match_t> associationMatch = nullptr;
static std::unique_ptr<sdbusplus::bus::match_t> associationRemoveMatch =
nullptr;
static void performEmConfigQuery(
const std::shared_ptr<sdbusplus::asio::connection>& conn,
const std::string& endpointPath, const std::string& emConfigPath)
{
std::filesystem::path p(emConfigPath);
std::string parentPath = p.parent_path().string();
conn->async_method_call(
[conn, endpointPath, emConfigPath](const boost::system::error_code& ec,
const ManagedObjectType& objects) {
auto& state = endpointStates[endpointPath];
state.querying = false;
State lastState = state.state;
state.state = State::None; // Reset for next query
if (lastState == State::Remove)
{
lg2::info("Ignoring query reply for removed endpoint {ENDPOINT}",
"ENDPOINT", endpointPath);
auto it = mctpEndpointConfigMap.find(endpointPath);
if (it != mctpEndpointConfigMap.end())
{
mctpEndpointConfigMap.erase(it);
}
return;
}
if (ec)
{
lg2::error("Failed to get managed objects for {PATH}: {ERROR}",
"PATH", emConfigPath, "ERROR", ec.message());
return;
}
auto objIt =
objects.find(sdbusplus::message::object_path(emConfigPath));
if (objIt == objects.end())
{
lg2::warning("EM config path {PATH} not found in managed objects",
"PATH", emConfigPath);
return;
}
if (!objIt->second.empty())
{
lg2::info(
"Recorded MCTP endpoint {ENDPOINT} with config from {PATH}",
"ENDPOINT", endpointPath, "PATH", emConfigPath);
mctpEndpointConfigMap[endpointPath] = objIt->second;
}
if (lastState == State::Add)
{
state.querying = true;
performEmConfigQuery(conn, endpointPath, emConfigPath);
}
},
"xyz.openbmc_project.EntityManager", parentPath,
"org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
}
void setupMctpEndpointListener(
const std::shared_ptr<sdbusplus::asio::connection>& conn)
{
// Match 1: Listen for Associations from mctp-reactor
const std::string associationMatchSpec =
"type='signal',interface='org.freedesktop.DBus.ObjectManager',member='InterfacesAdded',path_namespace='/au/com/codeconstruct/mctp1'";
associationMatch = std::make_unique<sdbusplus::bus::match_t>(
static_cast<sdbusplus::bus_t&>(*conn), associationMatchSpec,
[conn](sdbusplus::message_t& msg) {
sdbusplus::message::object_path path;
boost::container::flat_map<
std::string,
boost::container::flat_map<
std::string,
std::variant<std::string, std::vector<std::string>>>>
interfaces;
msg.read(path, interfaces);
auto it = interfaces.find("xyz.openbmc_project.Association");
if (it == interfaces.end())
{
return;
}
auto propIt = it->second.find("endpoints");
if (propIt == it->second.end())
{
return;
}
const auto* endpoints =
std::get_if<std::vector<std::string>>(&propIt->second);
if (!endpoints || endpoints->empty())
{
return;
}
std::string emConfigPath = endpoints->front();
std::string endpointPath = path.str;
constexpr std::string_view suffix = "/configured_by";
if (endpointPath.ends_with("/configured_by"))
{
endpointPath =
endpointPath.substr(0, endpointPath.size() - suffix.length());
}
auto& state = endpointStates[endpointPath];
if (state.querying)
{
state.state = State::Add;
return;
}
state.querying = true;
performEmConfigQuery(conn, endpointPath, emConfigPath);
});
// Match 2: Listen for Associations (Removed)
const std::string associationRemoveMatchSpec =
"type='signal',interface='org.freedesktop.DBus.ObjectManager',member='InterfacesRemoved',path_namespace='/au/com/codeconstruct/mctp1'";
associationRemoveMatch = std::make_unique<sdbusplus::bus::match_t>(
static_cast<sdbusplus::bus_t&>(*conn), associationRemoveMatchSpec,
[](sdbusplus::message_t& msg) {
sdbusplus::message::object_path path;
std::vector<std::string> interfaces;
msg.read(path, interfaces);
std::string endpointPath = path.str;
constexpr std::string_view suffix = "/configured_by";
if (endpointPath.ends_with("/configured_by"))
{
endpointPath =
endpointPath.substr(0, endpointPath.size() - suffix.length());
}
auto& state = endpointStates[endpointPath];
if (state.querying)
{
state.state = State::Remove;
}
else
{
auto it = mctpEndpointConfigMap.find(endpointPath);
if (it != mctpEndpointConfigMap.end())
{
lg2::info("Removing MCTP endpoint {ENDPOINT} from map",
"ENDPOINT", endpointPath);
mctpEndpointConfigMap.erase(it);
}
}
});
// Prime the cache: Query existing Associations
constexpr auto associationInterfaces =
std::to_array({"xyz.openbmc_project.Association"});
conn->async_method_call(
[conn](const boost::system::error_code& ec,
const GetSubTreeType& subtree) {
if (ec)
{
lg2::error("Failed to get associations from Object Mapper: {ERROR}",
"ERROR", ec.message());
return;
}
for (const auto& [path, services] : subtree)
{
if (!path.ends_with("/configured_by"))
{
continue;
}
conn->async_method_call(
[conn,
path](const boost::system::error_code& ec,
const std::variant<std::vector<std::string>>& value) {
if (ec)
{
lg2::error("Failed to get endpoints for {PATH}: {ERROR}",
"PATH", path, "ERROR", ec.message());
return;
}
const auto* endpoints =
std::get_if<std::vector<std::string>>(&value);
if (!endpoints || endpoints->empty())
{
return;
}
std::string emConfigPath = endpoints->front();
std::string endpointPath = path;
constexpr std::string_view suffix = "/configured_by";
if (endpointPath.ends_with("/configured_by"))
{
endpointPath = endpointPath.substr(0, endpointPath.size() -
suffix.length());
}
// Now query the EM config path for its properties
std::filesystem::path p(emConfigPath);
std::string parentPath = p.parent_path().string();
conn->async_method_call(
[endpointPath,
emConfigPath](const boost::system::error_code& ec,
const ManagedObjectType& objects) {
if (ec)
{
lg2::error(
"Failed to get managed objects for {PATH}: {ERROR}",
"PATH", emConfigPath, "ERROR", ec.message());
return;
}
auto objIt = objects.find(
sdbusplus::message::object_path(emConfigPath));
if (objIt == objects.end())
{
lg2::warning(
"EM config path {PATH} not found in managed objects",
"PATH", emConfigPath);
return;
}
if (!objIt->second.empty())
{
lg2::info(
"Primed match for endpoint {ENDPOINT} with config from {PATH}",
"ENDPOINT", endpointPath, "PATH", emConfigPath);
mctpEndpointConfigMap[endpointPath] = objIt->second;
}
},
"xyz.openbmc_project.EntityManager", parentPath,
"org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
},
"xyz.openbmc_project.ObjectMapper", path,
"org.freedesktop.DBus.Properties", "Get",
"xyz.openbmc_project.Association", "endpoints");
}
},
"xyz.openbmc_project.ObjectMapper",
"/xyz/openbmc_project/object_mapper",
"xyz.openbmc_project.ObjectMapper", "GetSubTree",
"/au/com/codeconstruct/mctp1", 0, associationInterfaces);
}