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