1 #include "associations.hpp"
2 
3 #include <iostream>
4 
5 void removeAssociation(const std::string& sourcePath, const std::string& owner,
6                        sdbusplus::asio::object_server& server,
7                        AssociationMaps& assocMaps)
8 {
9     // Use associationOwners to find the association paths and endpoints
10     // that the passed in object path and service own.  Remove all of
11     // these endpoints from the actual association D-Bus objects, and if
12     // the endpoints property is then empty, the whole association object
13     // can be removed.  Note there can be multiple services that own an
14     // association, and also that sourcePath is the path of the object
15     // that contains the org.openbmc.Associations interface and not the
16     // association path itself.
17 
18     // Find the services that have associations for this object path
19     auto owners = assocMaps.owners.find(sourcePath);
20     if (owners == assocMaps.owners.end())
21     {
22         return;
23     }
24 
25     // Find the association paths and endpoints owned by this object
26     // path for this service.
27     auto assocs = owners->second.find(owner);
28     if (assocs == owners->second.end())
29     {
30         return;
31     }
32 
33     for (const auto& [assocPath, endpointsToRemove] : assocs->second)
34     {
35         removeAssociationEndpoints(server, assocPath, endpointsToRemove,
36                                    assocMaps);
37     }
38 
39     // Remove the associationOwners entries for this owning path/service.
40     owners->second.erase(assocs);
41     if (owners->second.empty())
42     {
43         assocMaps.owners.erase(owners);
44     }
45 }
46 
47 void removeAssociationEndpoints(
48     sdbusplus::asio::object_server& objectServer, const std::string& assocPath,
49     const boost::container::flat_set<std::string>& endpointsToRemove,
50     AssociationMaps& assocMaps)
51 {
52     auto assoc = assocMaps.ifaces.find(assocPath);
53     if (assoc == assocMaps.ifaces.end())
54     {
55         return;
56     }
57 
58     auto& endpointsInDBus = std::get<endpointsPos>(assoc->second);
59 
60     for (const auto& endpointToRemove : endpointsToRemove)
61     {
62         auto e = std::find(endpointsInDBus.begin(), endpointsInDBus.end(),
63                            endpointToRemove);
64 
65         if (e != endpointsInDBus.end())
66         {
67             endpointsInDBus.erase(e);
68         }
69     }
70 
71     if (endpointsInDBus.empty())
72     {
73         objectServer.remove_interface(std::get<ifacePos>(assoc->second));
74         std::get<ifacePos>(assoc->second) = nullptr;
75         std::get<endpointsPos>(assoc->second).clear();
76     }
77     else
78     {
79         std::get<ifacePos>(assoc->second)
80             ->set_property("endpoints", endpointsInDBus);
81     }
82 }
83 
84 void checkAssociationEndpointRemoves(
85     const std::string& sourcePath, const std::string& owner,
86     const AssociationPaths& newAssociations,
87     sdbusplus::asio::object_server& objectServer, AssociationMaps& assocMaps)
88 {
89     // Find the services that have associations on this path.
90     auto originalOwners = assocMaps.owners.find(sourcePath);
91     if (originalOwners == assocMaps.owners.end())
92     {
93         return;
94     }
95 
96     // Find the associations for this service
97     auto originalAssociations = originalOwners->second.find(owner);
98     if (originalAssociations == originalOwners->second.end())
99     {
100         return;
101     }
102 
103     // Compare the new endpoints versus the original endpoints, and
104     // remove any of the original ones that aren't in the new list.
105     for (const auto& [originalAssocPath, originalEndpoints] :
106          originalAssociations->second)
107     {
108         // Check if this source even still has each association that
109         // was there previously, and if not, remove all of its endpoints
110         // from the D-Bus endpoints property which will cause the whole
111         // association path to be removed if no endpoints remain.
112         auto newEndpoints = newAssociations.find(originalAssocPath);
113         if (newEndpoints == newAssociations.end())
114         {
115             removeAssociationEndpoints(objectServer, originalAssocPath,
116                                        originalEndpoints, assocMaps);
117         }
118         else
119         {
120             // The association is still there.  Check if the endpoints
121             // changed.
122             boost::container::flat_set<std::string> toRemove;
123 
124             for (auto& originalEndpoint : originalEndpoints)
125             {
126                 if (std::find(newEndpoints->second.begin(),
127                               newEndpoints->second.end(),
128                               originalEndpoint) == newEndpoints->second.end())
129                 {
130                     toRemove.emplace(originalEndpoint);
131                 }
132             }
133             if (!toRemove.empty())
134             {
135                 removeAssociationEndpoints(objectServer, originalAssocPath,
136                                            toRemove, assocMaps);
137             }
138         }
139     }
140 }
141 
142 void associationChanged(sdbusplus::asio::object_server& objectServer,
143                         const std::vector<Association>& associations,
144                         const std::string& path, const std::string& owner,
145                         AssociationMaps& assocMaps)
146 {
147     AssociationPaths objects;
148 
149     for (const Association& association : associations)
150     {
151         std::string forward;
152         std::string reverse;
153         std::string endpoint;
154         std::tie(forward, reverse, endpoint) = association;
155 
156         if (endpoint.empty())
157         {
158             std::cerr << "Found invalid association on path " << path << "\n";
159             continue;
160         }
161         if (forward.size())
162         {
163             objects[path + "/" + forward].emplace(endpoint);
164         }
165         if (reverse.size())
166         {
167             objects[endpoint + "/" + reverse].emplace(path);
168         }
169     }
170     for (const auto& object : objects)
171     {
172         // the mapper exposes the new association interface but intakes
173         // the old
174 
175         auto& iface = assocMaps.ifaces[object.first];
176         auto& i = std::get<ifacePos>(iface);
177         auto& endpoints = std::get<endpointsPos>(iface);
178 
179         // Only add new endpoints
180         for (auto& e : object.second)
181         {
182             if (std::find(endpoints.begin(), endpoints.end(), e) ==
183                 endpoints.end())
184             {
185                 endpoints.push_back(e);
186             }
187         }
188 
189         // If the interface already exists, only need to update
190         // the property value, otherwise create it
191         if (i)
192         {
193             i->set_property("endpoints", endpoints);
194         }
195         else
196         {
197             i = objectServer.add_interface(object.first,
198                                            XYZ_ASSOCIATION_INTERFACE);
199             i->register_property("endpoints", endpoints);
200             i->initialize();
201         }
202     }
203 
204     // Check for endpoints being removed instead of added
205     checkAssociationEndpointRemoves(path, owner, objects, objectServer,
206                                     assocMaps);
207 
208     // Update associationOwners with the latest info
209     auto a = assocMaps.owners.find(path);
210     if (a != assocMaps.owners.end())
211     {
212         auto o = a->second.find(owner);
213         if (o != a->second.end())
214         {
215             o->second = std::move(objects);
216         }
217         else
218         {
219             a->second.emplace(owner, std::move(objects));
220         }
221     }
222     else
223     {
224         boost::container::flat_map<std::string, AssociationPaths> owners;
225         owners.emplace(owner, std::move(objects));
226         assocMaps.owners.emplace(path, owners);
227     }
228 }
229