xref: /openbmc/phosphor-objmgr/src/associations.cpp (revision 883a8929e3fba37460895d41435b9ae286c5406c)
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