blob: 317836c9d54903b5681a78b8bb0ac3f66dec99f5 [file] [log] [blame]
#include "associations.hpp"
#include <iostream>
void removeAssociation(const std::string& sourcePath, const std::string& owner,
sdbusplus::asio::object_server& server,
AssociationMaps& assocMaps)
{
// Use associationOwners to find the association paths and endpoints
// that the passed in object path and service own. Remove all of
// these endpoints from the actual association D-Bus objects, and if
// the endpoints property is then empty, the whole association object
// can be removed. Note there can be multiple services that own an
// association, and also that sourcePath is the path of the object
// that contains the org.openbmc.Associations interface and not the
// association path itself.
// Find the services that have associations for this object path
auto owners = assocMaps.owners.find(sourcePath);
if (owners == assocMaps.owners.end())
{
return;
}
// Find the association paths and endpoints owned by this object
// path for this service.
auto assocs = owners->second.find(owner);
if (assocs == owners->second.end())
{
return;
}
for (const auto& [assocPath, endpointsToRemove] : assocs->second)
{
removeAssociationEndpoints(server, assocPath, endpointsToRemove,
assocMaps);
}
// Remove the associationOwners entries for this owning path/service.
owners->second.erase(assocs);
if (owners->second.empty())
{
assocMaps.owners.erase(owners);
}
}
void removeAssociationEndpoints(
sdbusplus::asio::object_server& objectServer, const std::string& assocPath,
const boost::container::flat_set<std::string>& endpointsToRemove,
AssociationMaps& assocMaps)
{
auto assoc = assocMaps.ifaces.find(assocPath);
if (assoc == assocMaps.ifaces.end())
{
return;
}
auto& endpointsInDBus = std::get<endpointsPos>(assoc->second);
for (const auto& endpointToRemove : endpointsToRemove)
{
auto e = std::find(endpointsInDBus.begin(), endpointsInDBus.end(),
endpointToRemove);
if (e != endpointsInDBus.end())
{
endpointsInDBus.erase(e);
}
}
if (endpointsInDBus.empty())
{
objectServer.remove_interface(std::get<ifacePos>(assoc->second));
std::get<ifacePos>(assoc->second) = nullptr;
std::get<endpointsPos>(assoc->second).clear();
}
else
{
std::get<ifacePos>(assoc->second)
->set_property("endpoints", endpointsInDBus);
}
}
void checkAssociationEndpointRemoves(
const std::string& sourcePath, const std::string& owner,
const AssociationPaths& newAssociations,
sdbusplus::asio::object_server& objectServer, AssociationMaps& assocMaps)
{
// Find the services that have associations on this path.
auto originalOwners = assocMaps.owners.find(sourcePath);
if (originalOwners == assocMaps.owners.end())
{
return;
}
// Find the associations for this service
auto originalAssociations = originalOwners->second.find(owner);
if (originalAssociations == originalOwners->second.end())
{
return;
}
// Compare the new endpoints versus the original endpoints, and
// remove any of the original ones that aren't in the new list.
for (const auto& [originalAssocPath, originalEndpoints] :
originalAssociations->second)
{
// Check if this source even still has each association that
// was there previously, and if not, remove all of its endpoints
// from the D-Bus endpoints property which will cause the whole
// association path to be removed if no endpoints remain.
auto newEndpoints = newAssociations.find(originalAssocPath);
if (newEndpoints == newAssociations.end())
{
removeAssociationEndpoints(objectServer, originalAssocPath,
originalEndpoints, assocMaps);
}
else
{
// The association is still there. Check if the endpoints
// changed.
boost::container::flat_set<std::string> toRemove;
for (auto& originalEndpoint : originalEndpoints)
{
if (std::find(newEndpoints->second.begin(),
newEndpoints->second.end(),
originalEndpoint) == newEndpoints->second.end())
{
toRemove.emplace(originalEndpoint);
}
}
if (!toRemove.empty())
{
removeAssociationEndpoints(objectServer, originalAssocPath,
toRemove, assocMaps);
}
}
}
}
void associationChanged(sdbusplus::asio::object_server& objectServer,
const std::vector<Association>& associations,
const std::string& path, const std::string& owner,
AssociationMaps& assocMaps)
{
AssociationPaths objects;
for (const Association& association : associations)
{
std::string forward;
std::string reverse;
std::string endpoint;
std::tie(forward, reverse, endpoint) = association;
if (endpoint.empty())
{
std::cerr << "Found invalid association on path " << path << "\n";
continue;
}
if (forward.size())
{
objects[path + "/" + forward].emplace(endpoint);
}
if (reverse.size())
{
objects[endpoint + "/" + reverse].emplace(path);
}
}
for (const auto& object : objects)
{
// the mapper exposes the new association interface but intakes
// the old
auto& iface = assocMaps.ifaces[object.first];
auto& i = std::get<ifacePos>(iface);
auto& endpoints = std::get<endpointsPos>(iface);
// Only add new endpoints
for (auto& e : object.second)
{
if (std::find(endpoints.begin(), endpoints.end(), e) ==
endpoints.end())
{
endpoints.push_back(e);
}
}
// If the interface already exists, only need to update
// the property value, otherwise create it
if (i)
{
i->set_property("endpoints", endpoints);
}
else
{
i = objectServer.add_interface(object.first,
XYZ_ASSOCIATION_INTERFACE);
i->register_property("endpoints", endpoints);
i->initialize();
}
}
// Check for endpoints being removed instead of added
checkAssociationEndpointRemoves(path, owner, objects, objectServer,
assocMaps);
// Update associationOwners with the latest info
auto a = assocMaps.owners.find(path);
if (a != assocMaps.owners.end())
{
auto o = a->second.find(owner);
if (o != a->second.end())
{
o->second = std::move(objects);
}
else
{
a->second.emplace(owner, std::move(objects));
}
}
else
{
boost::container::flat_map<std::string, AssociationPaths> owners;
owners.emplace(owner, std::move(objects));
assocMaps.owners.emplace(path, owners);
}
}