1a80a3af0SAndrew Geissler #include "associations.hpp"
2a80a3af0SAndrew Geissler
35b4357daSKallas, Pawel #include <boost/asio/steady_timer.hpp>
4b89c6614SLei YU #include <sdbusplus/exception.hpp>
54511b33fSAndrew Geissler
62352088eSBrad Bishop #include <iostream>
786d2880eSBrad Bishop #include <string>
82352088eSBrad Bishop
updateEndpointsOnDbus(sdbusplus::asio::object_server & objectServer,const std::string & assocPath,AssociationMaps & assocMaps)95b4357daSKallas, Pawel void updateEndpointsOnDbus(sdbusplus::asio::object_server& objectServer,
105b4357daSKallas, Pawel const std::string& assocPath,
115b4357daSKallas, Pawel AssociationMaps& assocMaps)
125b4357daSKallas, Pawel {
13*883a8929SBenjamin Fair // Don't create an entry in assocMaps.ifaces if not needed.
14*883a8929SBenjamin Fair auto iface = assocMaps.ifaces.find(assocPath);
15*883a8929SBenjamin Fair if (iface == assocMaps.ifaces.end())
16*883a8929SBenjamin Fair {
17*883a8929SBenjamin Fair return;
18*883a8929SBenjamin Fair }
19*883a8929SBenjamin Fair auto& i = std::get<ifacePos>(iface->second);
20*883a8929SBenjamin Fair auto& endpoints = std::get<endpointsPos>(iface->second);
215b4357daSKallas, Pawel
225b4357daSKallas, Pawel // If the interface already exists, only need to update
235b4357daSKallas, Pawel // the property value, otherwise create it
245b4357daSKallas, Pawel if (i)
255b4357daSKallas, Pawel {
265b4357daSKallas, Pawel if (endpoints.empty())
275b4357daSKallas, Pawel {
285b4357daSKallas, Pawel objectServer.remove_interface(i);
295b4357daSKallas, Pawel i = nullptr;
305b4357daSKallas, Pawel }
315b4357daSKallas, Pawel else
325b4357daSKallas, Pawel {
335b4357daSKallas, Pawel i->set_property("endpoints", endpoints);
345b4357daSKallas, Pawel }
355b4357daSKallas, Pawel }
36*883a8929SBenjamin Fair else if (!endpoints.empty())
375b4357daSKallas, Pawel {
385b4357daSKallas, Pawel i = objectServer.add_interface(assocPath, xyzAssociationInterface);
395b4357daSKallas, Pawel i->register_property("endpoints", endpoints);
405b4357daSKallas, Pawel i->initialize();
415b4357daSKallas, Pawel }
42*883a8929SBenjamin Fair
43*883a8929SBenjamin Fair if (endpoints.empty())
44*883a8929SBenjamin Fair {
45*883a8929SBenjamin Fair assocMaps.ifaces.erase(iface);
465b4357daSKallas, Pawel }
475b4357daSKallas, Pawel }
485b4357daSKallas, Pawel
scheduleUpdateEndpointsOnDbus(boost::asio::io_context & io,sdbusplus::asio::object_server & objectServer,const std::string & assocPath,AssociationMaps & assocMaps)499052ebd3SPatrick Williams void scheduleUpdateEndpointsOnDbus(
509052ebd3SPatrick Williams boost::asio::io_context& io, sdbusplus::asio::object_server& objectServer,
519052ebd3SPatrick Williams const std::string& assocPath, AssociationMaps& assocMaps)
525b4357daSKallas, Pawel {
535b4357daSKallas, Pawel static std::set<std::string> delayedUpdatePaths;
545b4357daSKallas, Pawel
555b4357daSKallas, Pawel if (delayedUpdatePaths.contains(assocPath))
565b4357daSKallas, Pawel {
575b4357daSKallas, Pawel return;
585b4357daSKallas, Pawel }
595b4357daSKallas, Pawel
60*883a8929SBenjamin Fair auto iface = assocMaps.ifaces.find(assocPath);
61*883a8929SBenjamin Fair if (iface == assocMaps.ifaces.end())
62*883a8929SBenjamin Fair {
63*883a8929SBenjamin Fair return;
64*883a8929SBenjamin Fair }
65*883a8929SBenjamin Fair auto& endpoints = std::get<endpointsPos>(iface->second);
665b4357daSKallas, Pawel
675b4357daSKallas, Pawel if (endpoints.size() > endpointsCountTimerThreshold)
685b4357daSKallas, Pawel {
695b4357daSKallas, Pawel delayedUpdatePaths.emplace(assocPath);
705b4357daSKallas, Pawel auto timer = std::make_shared<boost::asio::steady_timer>(
715b4357daSKallas, Pawel io, std::chrono::seconds(endpointUpdateDelaySeconds));
725b4357daSKallas, Pawel timer->async_wait([&objectServer, &assocMaps, timer,
735b4357daSKallas, Pawel assocPath](const boost::system::error_code& ec) {
745b4357daSKallas, Pawel if (!ec)
755b4357daSKallas, Pawel {
765b4357daSKallas, Pawel updateEndpointsOnDbus(objectServer, assocPath, assocMaps);
775b4357daSKallas, Pawel }
785b4357daSKallas, Pawel delayedUpdatePaths.erase(assocPath);
795b4357daSKallas, Pawel });
805b4357daSKallas, Pawel }
815b4357daSKallas, Pawel else
825b4357daSKallas, Pawel {
835b4357daSKallas, Pawel updateEndpointsOnDbus(objectServer, assocPath, assocMaps);
845b4357daSKallas, Pawel }
855b4357daSKallas, Pawel }
865b4357daSKallas, Pawel
removeAssociation(boost::asio::io_context & io,const std::string & sourcePath,const std::string & owner,sdbusplus::asio::object_server & server,AssociationMaps & assocMaps)875b4357daSKallas, Pawel void removeAssociation(boost::asio::io_context& io,
885b4357daSKallas, Pawel const std::string& sourcePath, const std::string& owner,
89a80a3af0SAndrew Geissler sdbusplus::asio::object_server& server,
90e2359fb7SMatt Spinler AssociationMaps& assocMaps)
91a80a3af0SAndrew Geissler {
92a80a3af0SAndrew Geissler // Use associationOwners to find the association paths and endpoints
93a80a3af0SAndrew Geissler // that the passed in object path and service own. Remove all of
94a80a3af0SAndrew Geissler // these endpoints from the actual association D-Bus objects, and if
95a80a3af0SAndrew Geissler // the endpoints property is then empty, the whole association object
96a80a3af0SAndrew Geissler // can be removed. Note there can be multiple services that own an
97a80a3af0SAndrew Geissler // association, and also that sourcePath is the path of the object
98a80a3af0SAndrew Geissler // that contains the org.openbmc.Associations interface and not the
99a80a3af0SAndrew Geissler // association path itself.
100a80a3af0SAndrew Geissler
101a80a3af0SAndrew Geissler // Find the services that have associations for this object path
102e2359fb7SMatt Spinler auto owners = assocMaps.owners.find(sourcePath);
103e2359fb7SMatt Spinler if (owners == assocMaps.owners.end())
104a80a3af0SAndrew Geissler {
105a80a3af0SAndrew Geissler return;
106a80a3af0SAndrew Geissler }
107a80a3af0SAndrew Geissler
108a80a3af0SAndrew Geissler // Find the association paths and endpoints owned by this object
109a80a3af0SAndrew Geissler // path for this service.
110a80a3af0SAndrew Geissler auto assocs = owners->second.find(owner);
111a80a3af0SAndrew Geissler if (assocs == owners->second.end())
112a80a3af0SAndrew Geissler {
113a80a3af0SAndrew Geissler return;
114a80a3af0SAndrew Geissler }
115a80a3af0SAndrew Geissler
116a80a3af0SAndrew Geissler for (const auto& [assocPath, endpointsToRemove] : assocs->second)
117a80a3af0SAndrew Geissler {
1185b4357daSKallas, Pawel removeAssociationEndpoints(io, server, assocPath, endpointsToRemove,
119e2359fb7SMatt Spinler assocMaps);
120a80a3af0SAndrew Geissler }
121a80a3af0SAndrew Geissler
122a80a3af0SAndrew Geissler // Remove the associationOwners entries for this owning path/service.
123a80a3af0SAndrew Geissler owners->second.erase(assocs);
124a80a3af0SAndrew Geissler if (owners->second.empty())
125a80a3af0SAndrew Geissler {
126e2359fb7SMatt Spinler assocMaps.owners.erase(owners);
127a80a3af0SAndrew Geissler }
128cb9bcdb1SMatt Spinler
129cb9bcdb1SMatt Spinler // If we were still waiting on the other side of this association to
130cb9bcdb1SMatt Spinler // show up, cancel that wait.
131cb9bcdb1SMatt Spinler removeFromPendingAssociations(sourcePath, assocMaps);
132a80a3af0SAndrew Geissler }
133ff5ce924SAndrew Geissler
removeAssociationEndpoints(boost::asio::io_context & io,sdbusplus::asio::object_server & objectServer,const std::string & assocPath,const boost::container::flat_set<std::string> & endpointsToRemove,AssociationMaps & assocMaps)134ff5ce924SAndrew Geissler void removeAssociationEndpoints(
1355b4357daSKallas, Pawel boost::asio::io_context& io, sdbusplus::asio::object_server& objectServer,
1365b4357daSKallas, Pawel const std::string& assocPath,
137ff5ce924SAndrew Geissler const boost::container::flat_set<std::string>& endpointsToRemove,
138e2359fb7SMatt Spinler AssociationMaps& assocMaps)
139ff5ce924SAndrew Geissler {
140e2359fb7SMatt Spinler auto assoc = assocMaps.ifaces.find(assocPath);
141e2359fb7SMatt Spinler if (assoc == assocMaps.ifaces.end())
142ff5ce924SAndrew Geissler {
143ff5ce924SAndrew Geissler return;
144ff5ce924SAndrew Geissler }
145ff5ce924SAndrew Geissler
146ff5ce924SAndrew Geissler auto& endpointsInDBus = std::get<endpointsPos>(assoc->second);
147ff5ce924SAndrew Geissler
148ff5ce924SAndrew Geissler for (const auto& endpointToRemove : endpointsToRemove)
149ff5ce924SAndrew Geissler {
150ff5ce924SAndrew Geissler auto e = std::find(endpointsInDBus.begin(), endpointsInDBus.end(),
151ff5ce924SAndrew Geissler endpointToRemove);
152ff5ce924SAndrew Geissler
153ff5ce924SAndrew Geissler if (e != endpointsInDBus.end())
154ff5ce924SAndrew Geissler {
155ff5ce924SAndrew Geissler endpointsInDBus.erase(e);
156ff5ce924SAndrew Geissler }
157ff5ce924SAndrew Geissler }
158ff5ce924SAndrew Geissler
1595b4357daSKallas, Pawel scheduleUpdateEndpointsOnDbus(io, objectServer, assocPath, assocMaps);
160ff5ce924SAndrew Geissler }
1617f1c44dcSAndrew Geissler
checkAssociationEndpointRemoves(boost::asio::io_context & io,const std::string & sourcePath,const std::string & owner,const AssociationPaths & newAssociations,sdbusplus::asio::object_server & objectServer,AssociationMaps & assocMaps)1627f1c44dcSAndrew Geissler void checkAssociationEndpointRemoves(
1635b4357daSKallas, Pawel boost::asio::io_context& io, const std::string& sourcePath,
1645b4357daSKallas, Pawel const std::string& owner, const AssociationPaths& newAssociations,
165e2359fb7SMatt Spinler sdbusplus::asio::object_server& objectServer, AssociationMaps& assocMaps)
1667f1c44dcSAndrew Geissler {
1677f1c44dcSAndrew Geissler // Find the services that have associations on this path.
168e2359fb7SMatt Spinler auto originalOwners = assocMaps.owners.find(sourcePath);
169e2359fb7SMatt Spinler if (originalOwners == assocMaps.owners.end())
1707f1c44dcSAndrew Geissler {
1717f1c44dcSAndrew Geissler return;
1727f1c44dcSAndrew Geissler }
1737f1c44dcSAndrew Geissler
1747f1c44dcSAndrew Geissler // Find the associations for this service
1757f1c44dcSAndrew Geissler auto originalAssociations = originalOwners->second.find(owner);
1767f1c44dcSAndrew Geissler if (originalAssociations == originalOwners->second.end())
1777f1c44dcSAndrew Geissler {
1787f1c44dcSAndrew Geissler return;
1797f1c44dcSAndrew Geissler }
1807f1c44dcSAndrew Geissler
1817f1c44dcSAndrew Geissler // Compare the new endpoints versus the original endpoints, and
1827f1c44dcSAndrew Geissler // remove any of the original ones that aren't in the new list.
1837f1c44dcSAndrew Geissler for (const auto& [originalAssocPath, originalEndpoints] :
1847f1c44dcSAndrew Geissler originalAssociations->second)
1857f1c44dcSAndrew Geissler {
1867f1c44dcSAndrew Geissler // Check if this source even still has each association that
1877f1c44dcSAndrew Geissler // was there previously, and if not, remove all of its endpoints
1887f1c44dcSAndrew Geissler // from the D-Bus endpoints property which will cause the whole
1897f1c44dcSAndrew Geissler // association path to be removed if no endpoints remain.
1907f1c44dcSAndrew Geissler auto newEndpoints = newAssociations.find(originalAssocPath);
1917f1c44dcSAndrew Geissler if (newEndpoints == newAssociations.end())
1927f1c44dcSAndrew Geissler {
1935b4357daSKallas, Pawel removeAssociationEndpoints(io, objectServer, originalAssocPath,
194e2359fb7SMatt Spinler originalEndpoints, assocMaps);
1957f1c44dcSAndrew Geissler }
1967f1c44dcSAndrew Geissler else
1977f1c44dcSAndrew Geissler {
1987f1c44dcSAndrew Geissler // The association is still there. Check if the endpoints
1997f1c44dcSAndrew Geissler // changed.
2007f1c44dcSAndrew Geissler boost::container::flat_set<std::string> toRemove;
2017f1c44dcSAndrew Geissler
2021f62380aSBrad Bishop for (const auto& originalEndpoint : originalEndpoints)
2037f1c44dcSAndrew Geissler {
2047f1c44dcSAndrew Geissler if (std::find(newEndpoints->second.begin(),
2059052ebd3SPatrick Williams newEndpoints->second.end(), originalEndpoint) ==
2069052ebd3SPatrick Williams newEndpoints->second.end())
2077f1c44dcSAndrew Geissler {
2087f1c44dcSAndrew Geissler toRemove.emplace(originalEndpoint);
2097f1c44dcSAndrew Geissler }
2107f1c44dcSAndrew Geissler }
2117f1c44dcSAndrew Geissler if (!toRemove.empty())
2127f1c44dcSAndrew Geissler {
2135b4357daSKallas, Pawel removeAssociationEndpoints(io, objectServer, originalAssocPath,
214e2359fb7SMatt Spinler toRemove, assocMaps);
2157f1c44dcSAndrew Geissler }
2167f1c44dcSAndrew Geissler }
2177f1c44dcSAndrew Geissler }
2187f1c44dcSAndrew Geissler }
2194511b33fSAndrew Geissler
addEndpointsToAssocIfaces(boost::asio::io_context & io,sdbusplus::asio::object_server & objectServer,const std::string & assocPath,const boost::container::flat_set<std::string> & endpointPaths,AssociationMaps & assocMaps)22011401e2eSMatt Spinler void addEndpointsToAssocIfaces(
2215b4357daSKallas, Pawel boost::asio::io_context& io, sdbusplus::asio::object_server& objectServer,
2225b4357daSKallas, Pawel const std::string& assocPath,
22311401e2eSMatt Spinler const boost::container::flat_set<std::string>& endpointPaths,
22411401e2eSMatt Spinler AssociationMaps& assocMaps)
22511401e2eSMatt Spinler {
22611401e2eSMatt Spinler auto& iface = assocMaps.ifaces[assocPath];
22711401e2eSMatt Spinler auto& endpoints = std::get<endpointsPos>(iface);
22811401e2eSMatt Spinler
22911401e2eSMatt Spinler // Only add new endpoints
2301f62380aSBrad Bishop for (const auto& e : endpointPaths)
23111401e2eSMatt Spinler {
23211401e2eSMatt Spinler if (std::find(endpoints.begin(), endpoints.end(), e) == endpoints.end())
23311401e2eSMatt Spinler {
23411401e2eSMatt Spinler endpoints.push_back(e);
23511401e2eSMatt Spinler }
23611401e2eSMatt Spinler }
2375b4357daSKallas, Pawel scheduleUpdateEndpointsOnDbus(io, objectServer, assocPath, assocMaps);
23811401e2eSMatt Spinler }
23911401e2eSMatt Spinler
associationChanged(boost::asio::io_context & io,sdbusplus::asio::object_server & objectServer,const std::vector<Association> & associations,const std::string & path,const std::string & owner,const InterfaceMapType & interfaceMap,AssociationMaps & assocMaps)2409052ebd3SPatrick Williams void associationChanged(
2419052ebd3SPatrick Williams boost::asio::io_context& io, sdbusplus::asio::object_server& objectServer,
2429052ebd3SPatrick Williams const std::vector<Association>& associations, const std::string& path,
2439052ebd3SPatrick Williams const std::string& owner, const InterfaceMapType& interfaceMap,
244e2359fb7SMatt Spinler AssociationMaps& assocMaps)
2454511b33fSAndrew Geissler {
2464511b33fSAndrew Geissler AssociationPaths objects;
2474511b33fSAndrew Geissler
2484511b33fSAndrew Geissler for (const Association& association : associations)
2494511b33fSAndrew Geissler {
2504511b33fSAndrew Geissler std::string forward;
2514511b33fSAndrew Geissler std::string reverse;
2521f62380aSBrad Bishop std::string objectPath;
2531f62380aSBrad Bishop std::tie(forward, reverse, objectPath) = association;
2544511b33fSAndrew Geissler
2551f62380aSBrad Bishop if (objectPath.empty())
2560a560a5cSAndrew Geissler {
2570a560a5cSAndrew Geissler std::cerr << "Found invalid association on path " << path << "\n";
2580a560a5cSAndrew Geissler continue;
2590a560a5cSAndrew Geissler }
260e0b0e3a2SMatt Spinler
261e0b0e3a2SMatt Spinler // Can't create this association if the endpoint isn't on D-Bus.
2621f62380aSBrad Bishop if (interfaceMap.find(objectPath) == interfaceMap.end())
263e0b0e3a2SMatt Spinler {
2641f62380aSBrad Bishop addPendingAssociation(objectPath, reverse, path, forward, owner,
265e0b0e3a2SMatt Spinler assocMaps);
266e0b0e3a2SMatt Spinler continue;
267e0b0e3a2SMatt Spinler }
268e0b0e3a2SMatt Spinler
2691f62380aSBrad Bishop if (!forward.empty())
2704511b33fSAndrew Geissler {
2711f62380aSBrad Bishop objects[path + "/" + forward].emplace(objectPath);
2724511b33fSAndrew Geissler }
2731f62380aSBrad Bishop if (!reverse.empty())
2744511b33fSAndrew Geissler {
2751f62380aSBrad Bishop objects[objectPath + "/" + reverse].emplace(path);
2764511b33fSAndrew Geissler }
2774511b33fSAndrew Geissler }
2784511b33fSAndrew Geissler for (const auto& object : objects)
2794511b33fSAndrew Geissler {
2805b4357daSKallas, Pawel addEndpointsToAssocIfaces(io, objectServer, object.first, object.second,
28111401e2eSMatt Spinler assocMaps);
2824511b33fSAndrew Geissler }
2834511b33fSAndrew Geissler
2844511b33fSAndrew Geissler // Check for endpoints being removed instead of added
2855b4357daSKallas, Pawel checkAssociationEndpointRemoves(io, path, owner, objects, objectServer,
286e2359fb7SMatt Spinler assocMaps);
2874511b33fSAndrew Geissler
288e0b0e3a2SMatt Spinler if (!objects.empty())
289e0b0e3a2SMatt Spinler {
2904511b33fSAndrew Geissler // Update associationOwners with the latest info
291e2359fb7SMatt Spinler auto a = assocMaps.owners.find(path);
292e2359fb7SMatt Spinler if (a != assocMaps.owners.end())
2934511b33fSAndrew Geissler {
2944511b33fSAndrew Geissler auto o = a->second.find(owner);
2954511b33fSAndrew Geissler if (o != a->second.end())
2964511b33fSAndrew Geissler {
2974511b33fSAndrew Geissler o->second = std::move(objects);
2984511b33fSAndrew Geissler }
2994511b33fSAndrew Geissler else
3004511b33fSAndrew Geissler {
3014511b33fSAndrew Geissler a->second.emplace(owner, std::move(objects));
3024511b33fSAndrew Geissler }
3034511b33fSAndrew Geissler }
3044511b33fSAndrew Geissler else
3054511b33fSAndrew Geissler {
3064511b33fSAndrew Geissler boost::container::flat_map<std::string, AssociationPaths> owners;
3074511b33fSAndrew Geissler owners.emplace(owner, std::move(objects));
308e2359fb7SMatt Spinler assocMaps.owners.emplace(path, owners);
3094511b33fSAndrew Geissler }
3104511b33fSAndrew Geissler }
311e0b0e3a2SMatt Spinler }
312e0b0e3a2SMatt Spinler
addPendingAssociation(const std::string & objectPath,const std::string & type,const std::string & endpointPath,const std::string & endpointType,const std::string & owner,AssociationMaps & assocMaps)3139052ebd3SPatrick Williams void addPendingAssociation(
3149052ebd3SPatrick Williams const std::string& objectPath, const std::string& type,
3159052ebd3SPatrick Williams const std::string& endpointPath, const std::string& endpointType,
316e0b0e3a2SMatt Spinler const std::string& owner, AssociationMaps& assocMaps)
317e0b0e3a2SMatt Spinler {
318e0b0e3a2SMatt Spinler Association assoc{type, endpointType, endpointPath};
319e0b0e3a2SMatt Spinler
320e0b0e3a2SMatt Spinler auto p = assocMaps.pending.find(objectPath);
321e0b0e3a2SMatt Spinler if (p == assocMaps.pending.end())
322e0b0e3a2SMatt Spinler {
323e0b0e3a2SMatt Spinler ExistingEndpoints ee;
324e0b0e3a2SMatt Spinler ee.emplace_back(owner, std::move(assoc));
325e0b0e3a2SMatt Spinler assocMaps.pending.emplace(objectPath, std::move(ee));
326e0b0e3a2SMatt Spinler }
327e0b0e3a2SMatt Spinler else
328e0b0e3a2SMatt Spinler {
329e0b0e3a2SMatt Spinler // Already waiting on this path for another association,
330e0b0e3a2SMatt Spinler // so just add this endpoint and owner.
331e0b0e3a2SMatt Spinler auto& endpoints = p->second;
3329052ebd3SPatrick Williams auto e =
3339052ebd3SPatrick Williams std::find_if(endpoints.begin(), endpoints.end(),
334e0b0e3a2SMatt Spinler [&assoc, &owner](const auto& endpoint) {
335e0b0e3a2SMatt Spinler return (std::get<ownerPos>(endpoint) == owner) &&
336e0b0e3a2SMatt Spinler (std::get<assocPos>(endpoint) == assoc);
337e0b0e3a2SMatt Spinler });
338e0b0e3a2SMatt Spinler if (e == endpoints.end())
339e0b0e3a2SMatt Spinler {
340e0b0e3a2SMatt Spinler endpoints.emplace_back(owner, std::move(assoc));
341e0b0e3a2SMatt Spinler }
342e0b0e3a2SMatt Spinler }
343e0b0e3a2SMatt Spinler }
344cb9bcdb1SMatt Spinler
removeFromPendingAssociations(const std::string & endpointPath,AssociationMaps & assocMaps)345cb9bcdb1SMatt Spinler void removeFromPendingAssociations(const std::string& endpointPath,
346cb9bcdb1SMatt Spinler AssociationMaps& assocMaps)
347cb9bcdb1SMatt Spinler {
348cb9bcdb1SMatt Spinler auto assoc = assocMaps.pending.begin();
349cb9bcdb1SMatt Spinler while (assoc != assocMaps.pending.end())
350cb9bcdb1SMatt Spinler {
351cb9bcdb1SMatt Spinler auto endpoint = assoc->second.begin();
352cb9bcdb1SMatt Spinler while (endpoint != assoc->second.end())
353cb9bcdb1SMatt Spinler {
354cb9bcdb1SMatt Spinler auto& e = std::get<assocPos>(*endpoint);
355cb9bcdb1SMatt Spinler if (std::get<reversePathPos>(e) == endpointPath)
356cb9bcdb1SMatt Spinler {
357cb9bcdb1SMatt Spinler endpoint = assoc->second.erase(endpoint);
358cb9bcdb1SMatt Spinler continue;
359cb9bcdb1SMatt Spinler }
360cb9bcdb1SMatt Spinler
361cb9bcdb1SMatt Spinler endpoint++;
362cb9bcdb1SMatt Spinler }
363cb9bcdb1SMatt Spinler
364cb9bcdb1SMatt Spinler if (assoc->second.empty())
365cb9bcdb1SMatt Spinler {
366cb9bcdb1SMatt Spinler assoc = assocMaps.pending.erase(assoc);
367cb9bcdb1SMatt Spinler continue;
368cb9bcdb1SMatt Spinler }
369cb9bcdb1SMatt Spinler
370cb9bcdb1SMatt Spinler assoc++;
371cb9bcdb1SMatt Spinler }
372cb9bcdb1SMatt Spinler }
37311401e2eSMatt Spinler
addSingleAssociation(boost::asio::io_context & io,sdbusplus::asio::object_server & server,const std::string & assocPath,const std::string & endpoint,const std::string & owner,const std::string & ownerPath,AssociationMaps & assocMaps)3749052ebd3SPatrick Williams void addSingleAssociation(
3759052ebd3SPatrick Williams boost::asio::io_context& io, sdbusplus::asio::object_server& server,
3769052ebd3SPatrick Williams const std::string& assocPath, const std::string& endpoint,
3779052ebd3SPatrick Williams const std::string& owner, const std::string& ownerPath,
37811401e2eSMatt Spinler AssociationMaps& assocMaps)
37911401e2eSMatt Spinler {
38011401e2eSMatt Spinler boost::container::flat_set<std::string> endpoints{endpoint};
38111401e2eSMatt Spinler
3825b4357daSKallas, Pawel addEndpointsToAssocIfaces(io, server, assocPath, endpoints, assocMaps);
38311401e2eSMatt Spinler
38411401e2eSMatt Spinler AssociationPaths objects;
38511401e2eSMatt Spinler boost::container::flat_set e{endpoint};
38611401e2eSMatt Spinler objects.emplace(assocPath, e);
38711401e2eSMatt Spinler
38811401e2eSMatt Spinler auto a = assocMaps.owners.find(ownerPath);
38911401e2eSMatt Spinler if (a != assocMaps.owners.end())
39011401e2eSMatt Spinler {
39111401e2eSMatt Spinler auto o = a->second.find(owner);
39211401e2eSMatt Spinler if (o != a->second.end())
39311401e2eSMatt Spinler {
39411401e2eSMatt Spinler auto p = o->second.find(assocPath);
39511401e2eSMatt Spinler if (p != o->second.end())
39611401e2eSMatt Spinler {
39711401e2eSMatt Spinler p->second.emplace(endpoint);
39811401e2eSMatt Spinler }
39911401e2eSMatt Spinler else
40011401e2eSMatt Spinler {
40111401e2eSMatt Spinler o->second.emplace(assocPath, e);
40211401e2eSMatt Spinler }
40311401e2eSMatt Spinler }
40411401e2eSMatt Spinler else
40511401e2eSMatt Spinler {
40611401e2eSMatt Spinler a->second.emplace(owner, std::move(objects));
40711401e2eSMatt Spinler }
40811401e2eSMatt Spinler }
40911401e2eSMatt Spinler else
41011401e2eSMatt Spinler {
41111401e2eSMatt Spinler boost::container::flat_map<std::string, AssociationPaths> owners;
41211401e2eSMatt Spinler owners.emplace(owner, std::move(objects));
41311401e2eSMatt Spinler assocMaps.owners.emplace(endpoint, owners);
41411401e2eSMatt Spinler }
41511401e2eSMatt Spinler }
41611401e2eSMatt Spinler
checkIfPendingAssociation(boost::asio::io_context & io,const std::string & objectPath,const InterfaceMapType & interfaceMap,AssociationMaps & assocMaps,sdbusplus::asio::object_server & server)4179052ebd3SPatrick Williams void checkIfPendingAssociation(
4189052ebd3SPatrick Williams boost::asio::io_context& io, const std::string& objectPath,
4199052ebd3SPatrick Williams const InterfaceMapType& interfaceMap, AssociationMaps& assocMaps,
42011401e2eSMatt Spinler sdbusplus::asio::object_server& server)
42111401e2eSMatt Spinler {
42211401e2eSMatt Spinler auto pending = assocMaps.pending.find(objectPath);
42311401e2eSMatt Spinler if (pending == assocMaps.pending.end())
42411401e2eSMatt Spinler {
42511401e2eSMatt Spinler return;
42611401e2eSMatt Spinler }
42711401e2eSMatt Spinler
42811401e2eSMatt Spinler if (interfaceMap.find(objectPath) == interfaceMap.end())
42911401e2eSMatt Spinler {
43011401e2eSMatt Spinler return;
43111401e2eSMatt Spinler }
43211401e2eSMatt Spinler
43311401e2eSMatt Spinler auto endpoint = pending->second.begin();
43411401e2eSMatt Spinler
43511401e2eSMatt Spinler while (endpoint != pending->second.end())
43611401e2eSMatt Spinler {
43711401e2eSMatt Spinler const auto& e = std::get<assocPos>(*endpoint);
43811401e2eSMatt Spinler
43911401e2eSMatt Spinler // Ensure the other side of the association still exists
44011401e2eSMatt Spinler if (interfaceMap.find(std::get<reversePathPos>(e)) ==
44111401e2eSMatt Spinler interfaceMap.end())
44211401e2eSMatt Spinler {
44311401e2eSMatt Spinler endpoint++;
44411401e2eSMatt Spinler continue;
44511401e2eSMatt Spinler }
44611401e2eSMatt Spinler
44711401e2eSMatt Spinler // Add both sides of the association:
44811401e2eSMatt Spinler // objectPath/forwardType and reversePath/reverseType
44911401e2eSMatt Spinler //
45011401e2eSMatt Spinler // The ownerPath is the reversePath - i.e. the endpoint that
45111401e2eSMatt Spinler // is on D-Bus and owns the org.openbmc.Associations iface.
45211401e2eSMatt Spinler //
45311401e2eSMatt Spinler const auto& ownerPath = std::get<reversePathPos>(e);
45411401e2eSMatt Spinler const auto& owner = std::get<ownerPos>(*endpoint);
45511401e2eSMatt Spinler
45611401e2eSMatt Spinler auto assocPath = objectPath + '/' + std::get<forwardTypePos>(e);
45711401e2eSMatt Spinler auto endpointPath = ownerPath;
45811401e2eSMatt Spinler
459b89c6614SLei YU try
460b89c6614SLei YU {
4615b4357daSKallas, Pawel addSingleAssociation(io, server, assocPath, endpointPath, owner,
462b89c6614SLei YU ownerPath, assocMaps);
46311401e2eSMatt Spinler
46411401e2eSMatt Spinler // Now the reverse direction (still the same owner and ownerPath)
46511401e2eSMatt Spinler assocPath = endpointPath + '/' + std::get<reverseTypePos>(e);
46611401e2eSMatt Spinler endpointPath = objectPath;
4675b4357daSKallas, Pawel addSingleAssociation(io, server, assocPath, endpointPath, owner,
468b89c6614SLei YU ownerPath, assocMaps);
469b89c6614SLei YU }
470cc8070baSPatrick Williams catch (const sdbusplus::exception_t& e)
471b89c6614SLei YU {
472b89c6614SLei YU // In some case the interface could not be created on DBus and an
473b89c6614SLei YU // exception is thrown. mapper has no control of the interface/path
474b89c6614SLei YU // of the associations, so it has to catch the error and drop the
475b89c6614SLei YU // association request.
4761f62380aSBrad Bishop std::cerr << "Error adding association: assocPath " << assocPath
4771f62380aSBrad Bishop << ", endpointPath " << endpointPath
4781f62380aSBrad Bishop << ", what: " << e.what() << "\n";
479b89c6614SLei YU }
48011401e2eSMatt Spinler
48111401e2eSMatt Spinler // Not pending anymore
48211401e2eSMatt Spinler endpoint = pending->second.erase(endpoint);
48311401e2eSMatt Spinler }
48411401e2eSMatt Spinler
48511401e2eSMatt Spinler if (pending->second.empty())
48611401e2eSMatt Spinler {
48711401e2eSMatt Spinler assocMaps.pending.erase(objectPath);
48811401e2eSMatt Spinler }
48911401e2eSMatt Spinler }
4907f8fd1faSMatt Spinler
findAssociations(const std::string & endpointPath,AssociationMaps & assocMaps,FindAssocResults & associationData)4917f8fd1faSMatt Spinler void findAssociations(const std::string& endpointPath,
4927f8fd1faSMatt Spinler AssociationMaps& assocMaps,
4937f8fd1faSMatt Spinler FindAssocResults& associationData)
4947f8fd1faSMatt Spinler {
4957f8fd1faSMatt Spinler for (const auto& [sourcePath, owners] : assocMaps.owners)
4967f8fd1faSMatt Spinler {
4977f8fd1faSMatt Spinler for (const auto& [owner, assocs] : owners)
4987f8fd1faSMatt Spinler {
4997f8fd1faSMatt Spinler for (const auto& [assocPath, endpoints] : assocs)
5007f8fd1faSMatt Spinler {
5017f8fd1faSMatt Spinler if (std::find(endpoints.begin(), endpoints.end(),
5027f8fd1faSMatt Spinler endpointPath) != endpoints.end())
5037f8fd1faSMatt Spinler {
5047f8fd1faSMatt Spinler // assocPath is <path>/<type> which tells us what is on the
5057f8fd1faSMatt Spinler // other side of the association.
5067f8fd1faSMatt Spinler auto pos = assocPath.rfind('/');
5077f8fd1faSMatt Spinler auto otherPath = assocPath.substr(0, pos);
5087f8fd1faSMatt Spinler auto otherType = assocPath.substr(pos + 1);
5097f8fd1faSMatt Spinler
5107f8fd1faSMatt Spinler // Now we need to find the endpointPath/<type> ->
5117f8fd1faSMatt Spinler // [otherPath] entry so that we can get the type for
5127f8fd1faSMatt Spinler // endpointPath's side of the assoc. Do this by finding
5137f8fd1faSMatt Spinler // otherPath as an endpoint, and also checking for
5147f8fd1faSMatt Spinler // 'endpointPath/*' as the key.
5157f8fd1faSMatt Spinler auto a = std::find_if(
5167f8fd1faSMatt Spinler assocs.begin(), assocs.end(),
5177f8fd1faSMatt Spinler [&endpointPath, &otherPath](const auto& ap) {
5187f8fd1faSMatt Spinler const auto& endpoints = ap.second;
5199052ebd3SPatrick Williams auto endpoint = std::find(
5209052ebd3SPatrick Williams endpoints.begin(), endpoints.end(), otherPath);
5217f8fd1faSMatt Spinler if (endpoint != endpoints.end())
5227f8fd1faSMatt Spinler {
52386d2880eSBrad Bishop return ap.first.starts_with(endpointPath + '/');
5247f8fd1faSMatt Spinler }
5257f8fd1faSMatt Spinler return false;
5267f8fd1faSMatt Spinler });
5277f8fd1faSMatt Spinler
5287f8fd1faSMatt Spinler if (a != assocs.end())
5297f8fd1faSMatt Spinler {
5307f8fd1faSMatt Spinler // Pull out the type from endpointPath/<type>
5317f8fd1faSMatt Spinler pos = a->first.rfind('/');
5327f8fd1faSMatt Spinler auto thisType = a->first.substr(pos + 1);
5337f8fd1faSMatt Spinler
5347f8fd1faSMatt Spinler // Now we know the full association:
5357f8fd1faSMatt Spinler // endpointPath/thisType -> otherPath/otherType
5367f8fd1faSMatt Spinler Association association{thisType, otherType, otherPath};
5377f8fd1faSMatt Spinler associationData.emplace_back(owner, association);
5387f8fd1faSMatt Spinler }
5397f8fd1faSMatt Spinler }
5407f8fd1faSMatt Spinler }
5417f8fd1faSMatt Spinler }
5427f8fd1faSMatt Spinler }
5437f8fd1faSMatt Spinler }
5449c3d2859SMatt Spinler
5459c3d2859SMatt Spinler /** @brief Remove an endpoint for a particular association from D-Bus.
5469c3d2859SMatt Spinler *
5479c3d2859SMatt Spinler * If the last endpoint is gone, remove the whole association interface,
5489c3d2859SMatt Spinler * otherwise just update the D-Bus endpoints property.
5499c3d2859SMatt Spinler *
5509c3d2859SMatt Spinler * @param[in] assocPath - the association path
5519c3d2859SMatt Spinler * @param[in] endpointPath - the endpoint path to find and remove
5529c3d2859SMatt Spinler * @param[in,out] assocMaps - the association maps
5539c3d2859SMatt Spinler * @param[in,out] server - sdbus system object
5549c3d2859SMatt Spinler */
removeAssociationIfacesEntry(boost::asio::io_context & io,const std::string & assocPath,const std::string & endpointPath,AssociationMaps & assocMaps,sdbusplus::asio::object_server & server)5559052ebd3SPatrick Williams void removeAssociationIfacesEntry(
5569052ebd3SPatrick Williams boost::asio::io_context& io, const std::string& assocPath,
5579052ebd3SPatrick Williams const std::string& endpointPath, AssociationMaps& assocMaps,
5589c3d2859SMatt Spinler sdbusplus::asio::object_server& server)
5599c3d2859SMatt Spinler {
5609c3d2859SMatt Spinler auto assoc = assocMaps.ifaces.find(assocPath);
5619c3d2859SMatt Spinler if (assoc != assocMaps.ifaces.end())
5629c3d2859SMatt Spinler {
5639c3d2859SMatt Spinler auto& endpoints = std::get<endpointsPos>(assoc->second);
5649c3d2859SMatt Spinler auto e = std::find(endpoints.begin(), endpoints.end(), endpointPath);
5659c3d2859SMatt Spinler if (e != endpoints.end())
5669c3d2859SMatt Spinler {
5679c3d2859SMatt Spinler endpoints.erase(e);
5689c3d2859SMatt Spinler
5695b4357daSKallas, Pawel scheduleUpdateEndpointsOnDbus(io, server, assocPath, assocMaps);
5709c3d2859SMatt Spinler }
5719c3d2859SMatt Spinler }
5729c3d2859SMatt Spinler }
5739c3d2859SMatt Spinler
5749c3d2859SMatt Spinler /** @brief Remove an endpoint from the association owners map.
5759c3d2859SMatt Spinler *
5769c3d2859SMatt Spinler * For a specific association path and owner, remove the endpoint.
5779c3d2859SMatt Spinler * Remove all remaining artifacts of that endpoint in the owners map
5789c3d2859SMatt Spinler * based on what frees up after the erase.
5799c3d2859SMatt Spinler *
5809c3d2859SMatt Spinler * @param[in] assocPath - the association object path
5819c3d2859SMatt Spinler * @param[in] endpointPath - the endpoint object path
5829c3d2859SMatt Spinler * @param[in] owner - the owner of the association
5839c3d2859SMatt Spinler * @param[in,out] assocMaps - the association maps
5849c3d2859SMatt Spinler */
removeAssociationOwnersEntry(const std::string & assocPath,const std::string & endpointPath,const std::string & owner,AssociationMaps & assocMaps)5859052ebd3SPatrick Williams void removeAssociationOwnersEntry(
5869052ebd3SPatrick Williams const std::string& assocPath, const std::string& endpointPath,
5879052ebd3SPatrick Williams const std::string& owner, AssociationMaps& assocMaps)
5889c3d2859SMatt Spinler {
5899c3d2859SMatt Spinler auto sources = assocMaps.owners.begin();
5909c3d2859SMatt Spinler while (sources != assocMaps.owners.end())
5919c3d2859SMatt Spinler {
5929c3d2859SMatt Spinler auto owners = sources->second.find(owner);
5939c3d2859SMatt Spinler if (owners != sources->second.end())
5949c3d2859SMatt Spinler {
5959c3d2859SMatt Spinler auto entry = owners->second.find(assocPath);
5969c3d2859SMatt Spinler if (entry != owners->second.end())
5979c3d2859SMatt Spinler {
5989c3d2859SMatt Spinler auto e = std::find(entry->second.begin(), entry->second.end(),
5999c3d2859SMatt Spinler endpointPath);
6009c3d2859SMatt Spinler if (e != entry->second.end())
6019c3d2859SMatt Spinler {
6029c3d2859SMatt Spinler entry->second.erase(e);
6039c3d2859SMatt Spinler if (entry->second.empty())
6049c3d2859SMatt Spinler {
6059c3d2859SMatt Spinler owners->second.erase(entry);
6069c3d2859SMatt Spinler }
6079c3d2859SMatt Spinler }
6089c3d2859SMatt Spinler }
6099c3d2859SMatt Spinler
6109c3d2859SMatt Spinler if (owners->second.empty())
6119c3d2859SMatt Spinler {
6129c3d2859SMatt Spinler sources->second.erase(owners);
6139c3d2859SMatt Spinler }
6149c3d2859SMatt Spinler }
6159c3d2859SMatt Spinler
6169c3d2859SMatt Spinler if (sources->second.empty())
6179c3d2859SMatt Spinler {
6189c3d2859SMatt Spinler sources = assocMaps.owners.erase(sources);
6199c3d2859SMatt Spinler continue;
6209c3d2859SMatt Spinler }
6219c3d2859SMatt Spinler sources++;
6229c3d2859SMatt Spinler }
6239c3d2859SMatt Spinler }
6249c3d2859SMatt Spinler
moveAssociationToPending(boost::asio::io_context & io,const std::string & endpointPath,AssociationMaps & assocMaps,sdbusplus::asio::object_server & server)6259052ebd3SPatrick Williams void moveAssociationToPending(
6269052ebd3SPatrick Williams boost::asio::io_context& io, const std::string& endpointPath,
6279052ebd3SPatrick Williams AssociationMaps& assocMaps, sdbusplus::asio::object_server& server)
6289c3d2859SMatt Spinler {
6299c3d2859SMatt Spinler FindAssocResults associationData;
6309c3d2859SMatt Spinler
6319c3d2859SMatt Spinler // Check which associations this path is an endpoint of, and
6329c3d2859SMatt Spinler // then add them to the pending associations map and remove
6339c3d2859SMatt Spinler // the associations objects.
6349c3d2859SMatt Spinler findAssociations(endpointPath, assocMaps, associationData);
6359c3d2859SMatt Spinler
6369c3d2859SMatt Spinler for (const auto& [owner, association] : associationData)
6379c3d2859SMatt Spinler {
6389c3d2859SMatt Spinler const auto& forwardPath = endpointPath;
6399c3d2859SMatt Spinler const auto& forwardType = std::get<forwardTypePos>(association);
6409c3d2859SMatt Spinler const auto& reversePath = std::get<reversePathPos>(association);
6419c3d2859SMatt Spinler const auto& reverseType = std::get<reverseTypePos>(association);
6429c3d2859SMatt Spinler
6439c3d2859SMatt Spinler addPendingAssociation(forwardPath, forwardType, reversePath,
6449c3d2859SMatt Spinler reverseType, owner, assocMaps);
6459c3d2859SMatt Spinler
6469c3d2859SMatt Spinler // Remove both sides of the association from assocMaps.ifaces
6475b4357daSKallas, Pawel removeAssociationIfacesEntry(io, forwardPath + '/' + forwardType,
6489c3d2859SMatt Spinler reversePath, assocMaps, server);
6495b4357daSKallas, Pawel removeAssociationIfacesEntry(io, reversePath + '/' + reverseType,
6509c3d2859SMatt Spinler forwardPath, assocMaps, server);
6519c3d2859SMatt Spinler
6529c3d2859SMatt Spinler // Remove both sides of the association from assocMaps.owners
6539c3d2859SMatt Spinler removeAssociationOwnersEntry(forwardPath + '/' + forwardType,
654a664690bSBrad Bishop reversePath, owner, assocMaps);
6559c3d2859SMatt Spinler removeAssociationOwnersEntry(reversePath + '/' + reverseType,
656a664690bSBrad Bishop forwardPath, owner, assocMaps);
6579c3d2859SMatt Spinler }
6589c3d2859SMatt Spinler }
659