blob: 329f3d4415c48910356a8da53fc79e97cf5654ed [file] [log] [blame]
Andrew Geisslera80a3af2019-02-04 14:01:49 -06001#include "associations.hpp"
2
Brad Bishop78b04862025-07-08 16:00:37 -04003#include "path.hpp"
4
Kallas, Pawel5b4357d2022-10-12 15:36:37 +02005#include <boost/asio/steady_timer.hpp>
Lei YUb89c6612021-07-22 15:59:52 +08006#include <sdbusplus/exception.hpp>
Andrew Geissler4511b332019-02-21 15:40:40 -06007
Brad Bishop23520882022-05-26 21:39:53 -04008#include <iostream>
Brad Bishop86d28802022-07-11 15:49:31 -04009#include <string>
Brad Bishop23520882022-05-26 21:39:53 -040010
Kallas, Pawel5b4357d2022-10-12 15:36:37 +020011void updateEndpointsOnDbus(sdbusplus::asio::object_server& objectServer,
12 const std::string& assocPath,
13 AssociationMaps& assocMaps)
14{
Benjamin Fair883a8922025-01-22 22:07:23 +000015 // Don't create an entry in assocMaps.ifaces if not needed.
16 auto iface = assocMaps.ifaces.find(assocPath);
17 if (iface == assocMaps.ifaces.end())
18 {
19 return;
20 }
21 auto& i = std::get<ifacePos>(iface->second);
22 auto& endpoints = std::get<endpointsPos>(iface->second);
Kallas, Pawel5b4357d2022-10-12 15:36:37 +020023
24 // If the interface already exists, only need to update
25 // the property value, otherwise create it
26 if (i)
27 {
28 if (endpoints.empty())
29 {
30 objectServer.remove_interface(i);
31 i = nullptr;
32 }
33 else
34 {
35 i->set_property("endpoints", endpoints);
36 }
37 }
Benjamin Fair883a8922025-01-22 22:07:23 +000038 else if (!endpoints.empty())
Kallas, Pawel5b4357d2022-10-12 15:36:37 +020039 {
Benjamin Fair883a8922025-01-22 22:07:23 +000040 i = objectServer.add_interface(assocPath, xyzAssociationInterface);
41 i->register_property("endpoints", endpoints);
42 i->initialize();
43 }
44
45 if (endpoints.empty())
46 {
47 assocMaps.ifaces.erase(iface);
Kallas, Pawel5b4357d2022-10-12 15:36:37 +020048 }
49}
50
Patrick Williams9052ebd2024-08-16 15:22:16 -040051void scheduleUpdateEndpointsOnDbus(
52 boost::asio::io_context& io, sdbusplus::asio::object_server& objectServer,
53 const std::string& assocPath, AssociationMaps& assocMaps)
Kallas, Pawel5b4357d2022-10-12 15:36:37 +020054{
55 static std::set<std::string> delayedUpdatePaths;
56
57 if (delayedUpdatePaths.contains(assocPath))
58 {
59 return;
60 }
61
Benjamin Fair883a8922025-01-22 22:07:23 +000062 auto iface = assocMaps.ifaces.find(assocPath);
63 if (iface == assocMaps.ifaces.end())
64 {
65 return;
66 }
67 auto& endpoints = std::get<endpointsPos>(iface->second);
Kallas, Pawel5b4357d2022-10-12 15:36:37 +020068
69 if (endpoints.size() > endpointsCountTimerThreshold)
70 {
71 delayedUpdatePaths.emplace(assocPath);
72 auto timer = std::make_shared<boost::asio::steady_timer>(
73 io, std::chrono::seconds(endpointUpdateDelaySeconds));
74 timer->async_wait([&objectServer, &assocMaps, timer,
75 assocPath](const boost::system::error_code& ec) {
76 if (!ec)
77 {
78 updateEndpointsOnDbus(objectServer, assocPath, assocMaps);
79 }
80 delayedUpdatePaths.erase(assocPath);
81 });
82 }
83 else
84 {
85 updateEndpointsOnDbus(objectServer, assocPath, assocMaps);
86 }
87}
88
89void removeAssociation(boost::asio::io_context& io,
90 const std::string& sourcePath, const std::string& owner,
Andrew Geisslera80a3af2019-02-04 14:01:49 -060091 sdbusplus::asio::object_server& server,
Matt Spinlere2359fb2019-04-05 14:11:33 -050092 AssociationMaps& assocMaps)
Andrew Geisslera80a3af2019-02-04 14:01:49 -060093{
94 // Use associationOwners to find the association paths and endpoints
95 // that the passed in object path and service own. Remove all of
96 // these endpoints from the actual association D-Bus objects, and if
97 // the endpoints property is then empty, the whole association object
98 // can be removed. Note there can be multiple services that own an
99 // association, and also that sourcePath is the path of the object
100 // that contains the org.openbmc.Associations interface and not the
101 // association path itself.
102
103 // Find the services that have associations for this object path
Matt Spinlere2359fb2019-04-05 14:11:33 -0500104 auto owners = assocMaps.owners.find(sourcePath);
105 if (owners == assocMaps.owners.end())
Andrew Geisslera80a3af2019-02-04 14:01:49 -0600106 {
107 return;
108 }
109
110 // Find the association paths and endpoints owned by this object
111 // path for this service.
112 auto assocs = owners->second.find(owner);
113 if (assocs == owners->second.end())
114 {
115 return;
116 }
117
118 for (const auto& [assocPath, endpointsToRemove] : assocs->second)
119 {
Kallas, Pawel5b4357d2022-10-12 15:36:37 +0200120 removeAssociationEndpoints(io, server, assocPath, endpointsToRemove,
Matt Spinlere2359fb2019-04-05 14:11:33 -0500121 assocMaps);
Andrew Geisslera80a3af2019-02-04 14:01:49 -0600122 }
123
124 // Remove the associationOwners entries for this owning path/service.
125 owners->second.erase(assocs);
126 if (owners->second.empty())
127 {
Matt Spinlere2359fb2019-04-05 14:11:33 -0500128 assocMaps.owners.erase(owners);
Andrew Geisslera80a3af2019-02-04 14:01:49 -0600129 }
Matt Spinlercb9bcdb2019-04-08 10:58:49 -0500130
131 // If we were still waiting on the other side of this association to
132 // show up, cancel that wait.
133 removeFromPendingAssociations(sourcePath, assocMaps);
Andrew Geisslera80a3af2019-02-04 14:01:49 -0600134}
Andrew Geisslerff5ce922019-02-21 12:43:09 -0600135
136void removeAssociationEndpoints(
Kallas, Pawel5b4357d2022-10-12 15:36:37 +0200137 boost::asio::io_context& io, sdbusplus::asio::object_server& objectServer,
138 const std::string& assocPath,
Andrew Geisslerff5ce922019-02-21 12:43:09 -0600139 const boost::container::flat_set<std::string>& endpointsToRemove,
Matt Spinlere2359fb2019-04-05 14:11:33 -0500140 AssociationMaps& assocMaps)
Andrew Geisslerff5ce922019-02-21 12:43:09 -0600141{
Matt Spinlere2359fb2019-04-05 14:11:33 -0500142 auto assoc = assocMaps.ifaces.find(assocPath);
143 if (assoc == assocMaps.ifaces.end())
Andrew Geisslerff5ce922019-02-21 12:43:09 -0600144 {
145 return;
146 }
147
148 auto& endpointsInDBus = std::get<endpointsPos>(assoc->second);
149
150 for (const auto& endpointToRemove : endpointsToRemove)
151 {
152 auto e = std::find(endpointsInDBus.begin(), endpointsInDBus.end(),
153 endpointToRemove);
154
155 if (e != endpointsInDBus.end())
156 {
157 endpointsInDBus.erase(e);
158 }
159 }
160
Kallas, Pawel5b4357d2022-10-12 15:36:37 +0200161 scheduleUpdateEndpointsOnDbus(io, objectServer, assocPath, assocMaps);
Andrew Geisslerff5ce922019-02-21 12:43:09 -0600162}
Andrew Geissler7f1c44d2019-02-21 13:44:16 -0600163
164void checkAssociationEndpointRemoves(
Kallas, Pawel5b4357d2022-10-12 15:36:37 +0200165 boost::asio::io_context& io, const std::string& sourcePath,
166 const std::string& owner, const AssociationPaths& newAssociations,
Matt Spinlere2359fb2019-04-05 14:11:33 -0500167 sdbusplus::asio::object_server& objectServer, AssociationMaps& assocMaps)
Andrew Geissler7f1c44d2019-02-21 13:44:16 -0600168{
169 // Find the services that have associations on this path.
Matt Spinlere2359fb2019-04-05 14:11:33 -0500170 auto originalOwners = assocMaps.owners.find(sourcePath);
171 if (originalOwners == assocMaps.owners.end())
Andrew Geissler7f1c44d2019-02-21 13:44:16 -0600172 {
173 return;
174 }
175
176 // Find the associations for this service
177 auto originalAssociations = originalOwners->second.find(owner);
178 if (originalAssociations == originalOwners->second.end())
179 {
180 return;
181 }
182
183 // Compare the new endpoints versus the original endpoints, and
184 // remove any of the original ones that aren't in the new list.
185 for (const auto& [originalAssocPath, originalEndpoints] :
186 originalAssociations->second)
187 {
188 // Check if this source even still has each association that
189 // was there previously, and if not, remove all of its endpoints
190 // from the D-Bus endpoints property which will cause the whole
191 // association path to be removed if no endpoints remain.
192 auto newEndpoints = newAssociations.find(originalAssocPath);
193 if (newEndpoints == newAssociations.end())
194 {
Kallas, Pawel5b4357d2022-10-12 15:36:37 +0200195 removeAssociationEndpoints(io, objectServer, originalAssocPath,
Matt Spinlere2359fb2019-04-05 14:11:33 -0500196 originalEndpoints, assocMaps);
Andrew Geissler7f1c44d2019-02-21 13:44:16 -0600197 }
198 else
199 {
200 // The association is still there. Check if the endpoints
201 // changed.
202 boost::container::flat_set<std::string> toRemove;
203
Brad Bishop1f623802022-05-31 18:22:10 -0400204 for (const auto& originalEndpoint : originalEndpoints)
Andrew Geissler7f1c44d2019-02-21 13:44:16 -0600205 {
206 if (std::find(newEndpoints->second.begin(),
Patrick Williams9052ebd2024-08-16 15:22:16 -0400207 newEndpoints->second.end(), originalEndpoint) ==
208 newEndpoints->second.end())
Andrew Geissler7f1c44d2019-02-21 13:44:16 -0600209 {
210 toRemove.emplace(originalEndpoint);
211 }
212 }
213 if (!toRemove.empty())
214 {
Kallas, Pawel5b4357d2022-10-12 15:36:37 +0200215 removeAssociationEndpoints(io, objectServer, originalAssocPath,
Matt Spinlere2359fb2019-04-05 14:11:33 -0500216 toRemove, assocMaps);
Andrew Geissler7f1c44d2019-02-21 13:44:16 -0600217 }
218 }
219 }
220}
Andrew Geissler4511b332019-02-21 15:40:40 -0600221
Matt Spinler11401e22019-04-08 13:13:25 -0500222void addEndpointsToAssocIfaces(
Kallas, Pawel5b4357d2022-10-12 15:36:37 +0200223 boost::asio::io_context& io, sdbusplus::asio::object_server& objectServer,
224 const std::string& assocPath,
Matt Spinler11401e22019-04-08 13:13:25 -0500225 const boost::container::flat_set<std::string>& endpointPaths,
226 AssociationMaps& assocMaps)
227{
228 auto& iface = assocMaps.ifaces[assocPath];
Matt Spinler11401e22019-04-08 13:13:25 -0500229 auto& endpoints = std::get<endpointsPos>(iface);
230
231 // Only add new endpoints
Brad Bishop1f623802022-05-31 18:22:10 -0400232 for (const auto& e : endpointPaths)
Matt Spinler11401e22019-04-08 13:13:25 -0500233 {
234 if (std::find(endpoints.begin(), endpoints.end(), e) == endpoints.end())
235 {
236 endpoints.push_back(e);
237 }
238 }
Kallas, Pawel5b4357d2022-10-12 15:36:37 +0200239 scheduleUpdateEndpointsOnDbus(io, objectServer, assocPath, assocMaps);
Matt Spinler11401e22019-04-08 13:13:25 -0500240}
241
Patrick Williams9052ebd2024-08-16 15:22:16 -0400242void associationChanged(
243 boost::asio::io_context& io, sdbusplus::asio::object_server& objectServer,
244 const std::vector<Association>& associations, const std::string& path,
245 const std::string& owner, const InterfaceMapType& interfaceMap,
246 AssociationMaps& assocMaps)
Andrew Geissler4511b332019-02-21 15:40:40 -0600247{
248 AssociationPaths objects;
249
250 for (const Association& association : associations)
251 {
252 std::string forward;
253 std::string reverse;
Brad Bishop1f623802022-05-31 18:22:10 -0400254 std::string objectPath;
255 std::tie(forward, reverse, objectPath) = association;
Andrew Geissler4511b332019-02-21 15:40:40 -0600256
Brad Bishop1f623802022-05-31 18:22:10 -0400257 if (objectPath.empty())
Andrew Geissler0a560a52019-03-22 10:59:07 -0500258 {
259 std::cerr << "Found invalid association on path " << path << "\n";
260 continue;
261 }
Matt Spinlere0b0e3a2019-04-08 10:39:23 -0500262
263 // Can't create this association if the endpoint isn't on D-Bus.
Brad Bishopa7257062025-07-29 14:12:35 -0400264 if (!interfaceMap.contains(objectPath))
Matt Spinlere0b0e3a2019-04-08 10:39:23 -0500265 {
Brad Bishop1f623802022-05-31 18:22:10 -0400266 addPendingAssociation(objectPath, reverse, path, forward, owner,
Matt Spinlere0b0e3a2019-04-08 10:39:23 -0500267 assocMaps);
268 continue;
269 }
270
Brad Bishop1f623802022-05-31 18:22:10 -0400271 if (!forward.empty())
Andrew Geissler4511b332019-02-21 15:40:40 -0600272 {
Brad Bishop78b04862025-07-08 16:00:37 -0400273 objects[appendPathSegment(path, forward)].emplace(objectPath);
Andrew Geissler4511b332019-02-21 15:40:40 -0600274 }
Brad Bishop1f623802022-05-31 18:22:10 -0400275 if (!reverse.empty())
Andrew Geissler4511b332019-02-21 15:40:40 -0600276 {
Brad Bishop78b04862025-07-08 16:00:37 -0400277 objects[appendPathSegment(objectPath, reverse)].emplace(path);
Andrew Geissler4511b332019-02-21 15:40:40 -0600278 }
279 }
280 for (const auto& object : objects)
281 {
Kallas, Pawel5b4357d2022-10-12 15:36:37 +0200282 addEndpointsToAssocIfaces(io, objectServer, object.first, object.second,
Matt Spinler11401e22019-04-08 13:13:25 -0500283 assocMaps);
Andrew Geissler4511b332019-02-21 15:40:40 -0600284 }
285
286 // Check for endpoints being removed instead of added
Kallas, Pawel5b4357d2022-10-12 15:36:37 +0200287 checkAssociationEndpointRemoves(io, path, owner, objects, objectServer,
Matt Spinlere2359fb2019-04-05 14:11:33 -0500288 assocMaps);
Andrew Geissler4511b332019-02-21 15:40:40 -0600289
Matt Spinlere0b0e3a2019-04-08 10:39:23 -0500290 if (!objects.empty())
Andrew Geissler4511b332019-02-21 15:40:40 -0600291 {
Matt Spinlere0b0e3a2019-04-08 10:39:23 -0500292 // Update associationOwners with the latest info
293 auto a = assocMaps.owners.find(path);
294 if (a != assocMaps.owners.end())
Andrew Geissler4511b332019-02-21 15:40:40 -0600295 {
Matt Spinlere0b0e3a2019-04-08 10:39:23 -0500296 auto o = a->second.find(owner);
297 if (o != a->second.end())
298 {
299 o->second = std::move(objects);
300 }
301 else
302 {
303 a->second.emplace(owner, std::move(objects));
304 }
Andrew Geissler4511b332019-02-21 15:40:40 -0600305 }
306 else
307 {
Matt Spinlere0b0e3a2019-04-08 10:39:23 -0500308 boost::container::flat_map<std::string, AssociationPaths> owners;
309 owners.emplace(owner, std::move(objects));
310 assocMaps.owners.emplace(path, owners);
Andrew Geissler4511b332019-02-21 15:40:40 -0600311 }
312 }
Matt Spinlere0b0e3a2019-04-08 10:39:23 -0500313}
314
Patrick Williams9052ebd2024-08-16 15:22:16 -0400315void addPendingAssociation(
316 const std::string& objectPath, const std::string& type,
317 const std::string& endpointPath, const std::string& endpointType,
318 const std::string& owner, AssociationMaps& assocMaps)
Matt Spinlere0b0e3a2019-04-08 10:39:23 -0500319{
320 Association assoc{type, endpointType, endpointPath};
321
322 auto p = assocMaps.pending.find(objectPath);
323 if (p == assocMaps.pending.end())
324 {
325 ExistingEndpoints ee;
326 ee.emplace_back(owner, std::move(assoc));
327 assocMaps.pending.emplace(objectPath, std::move(ee));
328 }
Andrew Geissler4511b332019-02-21 15:40:40 -0600329 else
330 {
Matt Spinlere0b0e3a2019-04-08 10:39:23 -0500331 // Already waiting on this path for another association,
332 // so just add this endpoint and owner.
333 auto& endpoints = p->second;
Patrick Williams9052ebd2024-08-16 15:22:16 -0400334 auto e =
335 std::find_if(endpoints.begin(), endpoints.end(),
336 [&assoc, &owner](const auto& endpoint) {
337 return (std::get<ownerPos>(endpoint) == owner) &&
338 (std::get<assocPos>(endpoint) == assoc);
339 });
Matt Spinlere0b0e3a2019-04-08 10:39:23 -0500340 if (e == endpoints.end())
341 {
342 endpoints.emplace_back(owner, std::move(assoc));
343 }
Andrew Geissler4511b332019-02-21 15:40:40 -0600344 }
345}
Matt Spinlercb9bcdb2019-04-08 10:58:49 -0500346
347void removeFromPendingAssociations(const std::string& endpointPath,
348 AssociationMaps& assocMaps)
349{
350 auto assoc = assocMaps.pending.begin();
351 while (assoc != assocMaps.pending.end())
352 {
353 auto endpoint = assoc->second.begin();
354 while (endpoint != assoc->second.end())
355 {
356 auto& e = std::get<assocPos>(*endpoint);
357 if (std::get<reversePathPos>(e) == endpointPath)
358 {
359 endpoint = assoc->second.erase(endpoint);
360 continue;
361 }
362
363 endpoint++;
364 }
365
366 if (assoc->second.empty())
367 {
368 assoc = assocMaps.pending.erase(assoc);
369 continue;
370 }
371
372 assoc++;
373 }
374}
Matt Spinler11401e22019-04-08 13:13:25 -0500375
Patrick Williams9052ebd2024-08-16 15:22:16 -0400376void addSingleAssociation(
377 boost::asio::io_context& io, sdbusplus::asio::object_server& server,
378 const std::string& assocPath, const std::string& endpoint,
379 const std::string& owner, const std::string& ownerPath,
380 AssociationMaps& assocMaps)
Matt Spinler11401e22019-04-08 13:13:25 -0500381{
382 boost::container::flat_set<std::string> endpoints{endpoint};
383
Kallas, Pawel5b4357d2022-10-12 15:36:37 +0200384 addEndpointsToAssocIfaces(io, server, assocPath, endpoints, assocMaps);
Matt Spinler11401e22019-04-08 13:13:25 -0500385
386 AssociationPaths objects;
387 boost::container::flat_set e{endpoint};
388 objects.emplace(assocPath, e);
389
390 auto a = assocMaps.owners.find(ownerPath);
391 if (a != assocMaps.owners.end())
392 {
393 auto o = a->second.find(owner);
394 if (o != a->second.end())
395 {
396 auto p = o->second.find(assocPath);
397 if (p != o->second.end())
398 {
399 p->second.emplace(endpoint);
400 }
401 else
402 {
403 o->second.emplace(assocPath, e);
404 }
405 }
406 else
407 {
408 a->second.emplace(owner, std::move(objects));
409 }
410 }
411 else
412 {
413 boost::container::flat_map<std::string, AssociationPaths> owners;
414 owners.emplace(owner, std::move(objects));
415 assocMaps.owners.emplace(endpoint, owners);
416 }
417}
418
Patrick Williams9052ebd2024-08-16 15:22:16 -0400419void checkIfPendingAssociation(
420 boost::asio::io_context& io, const std::string& objectPath,
421 const InterfaceMapType& interfaceMap, AssociationMaps& assocMaps,
422 sdbusplus::asio::object_server& server)
Matt Spinler11401e22019-04-08 13:13:25 -0500423{
424 auto pending = assocMaps.pending.find(objectPath);
425 if (pending == assocMaps.pending.end())
426 {
427 return;
428 }
429
Brad Bishopa7257062025-07-29 14:12:35 -0400430 if (!interfaceMap.contains(objectPath))
Matt Spinler11401e22019-04-08 13:13:25 -0500431 {
432 return;
433 }
434
435 auto endpoint = pending->second.begin();
436
437 while (endpoint != pending->second.end())
438 {
439 const auto& e = std::get<assocPos>(*endpoint);
440
441 // Ensure the other side of the association still exists
Brad Bishopa7257062025-07-29 14:12:35 -0400442 if (!interfaceMap.contains(std::get<reversePathPos>(e)))
Matt Spinler11401e22019-04-08 13:13:25 -0500443 {
444 endpoint++;
445 continue;
446 }
447
448 // Add both sides of the association:
449 // objectPath/forwardType and reversePath/reverseType
450 //
451 // The ownerPath is the reversePath - i.e. the endpoint that
452 // is on D-Bus and owns the org.openbmc.Associations iface.
453 //
454 const auto& ownerPath = std::get<reversePathPos>(e);
455 const auto& owner = std::get<ownerPos>(*endpoint);
456
457 auto assocPath = objectPath + '/' + std::get<forwardTypePos>(e);
458 auto endpointPath = ownerPath;
459
Lei YUb89c6612021-07-22 15:59:52 +0800460 try
461 {
Kallas, Pawel5b4357d2022-10-12 15:36:37 +0200462 addSingleAssociation(io, server, assocPath, endpointPath, owner,
Lei YUb89c6612021-07-22 15:59:52 +0800463 ownerPath, assocMaps);
Matt Spinler11401e22019-04-08 13:13:25 -0500464
Lei YUb89c6612021-07-22 15:59:52 +0800465 // Now the reverse direction (still the same owner and ownerPath)
466 assocPath = endpointPath + '/' + std::get<reverseTypePos>(e);
467 endpointPath = objectPath;
Kallas, Pawel5b4357d2022-10-12 15:36:37 +0200468 addSingleAssociation(io, server, assocPath, endpointPath, owner,
Lei YUb89c6612021-07-22 15:59:52 +0800469 ownerPath, assocMaps);
470 }
Brad Bishop11c0cc32025-07-08 16:04:25 -0400471 catch (const sdbusplus::exception_t& ex)
Lei YUb89c6612021-07-22 15:59:52 +0800472 {
473 // In some case the interface could not be created on DBus and an
474 // exception is thrown. mapper has no control of the interface/path
475 // of the associations, so it has to catch the error and drop the
476 // association request.
Brad Bishop1f623802022-05-31 18:22:10 -0400477 std::cerr << "Error adding association: assocPath " << assocPath
478 << ", endpointPath " << endpointPath
Brad Bishop11c0cc32025-07-08 16:04:25 -0400479 << ", what: " << ex.what() << "\n";
Lei YUb89c6612021-07-22 15:59:52 +0800480 }
Matt Spinler11401e22019-04-08 13:13:25 -0500481
482 // Not pending anymore
483 endpoint = pending->second.erase(endpoint);
484 }
485
486 if (pending->second.empty())
487 {
488 assocMaps.pending.erase(objectPath);
489 }
490}
Matt Spinler7f8fd1f2019-04-08 15:21:59 -0500491
492void findAssociations(const std::string& endpointPath,
493 AssociationMaps& assocMaps,
494 FindAssocResults& associationData)
495{
496 for (const auto& [sourcePath, owners] : assocMaps.owners)
497 {
498 for (const auto& [owner, assocs] : owners)
499 {
500 for (const auto& [assocPath, endpoints] : assocs)
501 {
502 if (std::find(endpoints.begin(), endpoints.end(),
503 endpointPath) != endpoints.end())
504 {
505 // assocPath is <path>/<type> which tells us what is on the
506 // other side of the association.
507 auto pos = assocPath.rfind('/');
508 auto otherPath = assocPath.substr(0, pos);
509 auto otherType = assocPath.substr(pos + 1);
510
511 // Now we need to find the endpointPath/<type> ->
512 // [otherPath] entry so that we can get the type for
513 // endpointPath's side of the assoc. Do this by finding
514 // otherPath as an endpoint, and also checking for
515 // 'endpointPath/*' as the key.
516 auto a = std::find_if(
517 assocs.begin(), assocs.end(),
518 [&endpointPath, &otherPath](const auto& ap) {
Brad Bishop11c0cc32025-07-08 16:04:25 -0400519 const auto& otherEndpoints = ap.second;
520 auto endpoint =
521 std::find(otherEndpoints.begin(),
522 otherEndpoints.end(), otherPath);
523 if (endpoint != otherEndpoints.end())
Patrick Williams9052ebd2024-08-16 15:22:16 -0400524 {
525 return ap.first.starts_with(endpointPath + '/');
526 }
527 return false;
528 });
Matt Spinler7f8fd1f2019-04-08 15:21:59 -0500529
530 if (a != assocs.end())
531 {
532 // Pull out the type from endpointPath/<type>
533 pos = a->first.rfind('/');
534 auto thisType = a->first.substr(pos + 1);
535
536 // Now we know the full association:
537 // endpointPath/thisType -> otherPath/otherType
538 Association association{thisType, otherType, otherPath};
539 associationData.emplace_back(owner, association);
540 }
541 }
542 }
543 }
544 }
545}
Matt Spinler9c3d2852019-04-08 15:57:19 -0500546
547/** @brief Remove an endpoint for a particular association from D-Bus.
548 *
549 * If the last endpoint is gone, remove the whole association interface,
550 * otherwise just update the D-Bus endpoints property.
551 *
552 * @param[in] assocPath - the association path
553 * @param[in] endpointPath - the endpoint path to find and remove
554 * @param[in,out] assocMaps - the association maps
555 * @param[in,out] server - sdbus system object
556 */
Patrick Williams9052ebd2024-08-16 15:22:16 -0400557void removeAssociationIfacesEntry(
558 boost::asio::io_context& io, const std::string& assocPath,
559 const std::string& endpointPath, AssociationMaps& assocMaps,
560 sdbusplus::asio::object_server& server)
Matt Spinler9c3d2852019-04-08 15:57:19 -0500561{
562 auto assoc = assocMaps.ifaces.find(assocPath);
563 if (assoc != assocMaps.ifaces.end())
564 {
565 auto& endpoints = std::get<endpointsPos>(assoc->second);
566 auto e = std::find(endpoints.begin(), endpoints.end(), endpointPath);
567 if (e != endpoints.end())
568 {
569 endpoints.erase(e);
570
Kallas, Pawel5b4357d2022-10-12 15:36:37 +0200571 scheduleUpdateEndpointsOnDbus(io, server, assocPath, assocMaps);
Matt Spinler9c3d2852019-04-08 15:57:19 -0500572 }
573 }
574}
575
576/** @brief Remove an endpoint from the association owners map.
577 *
578 * For a specific association path and owner, remove the endpoint.
579 * Remove all remaining artifacts of that endpoint in the owners map
580 * based on what frees up after the erase.
581 *
582 * @param[in] assocPath - the association object path
583 * @param[in] endpointPath - the endpoint object path
584 * @param[in] owner - the owner of the association
585 * @param[in,out] assocMaps - the association maps
Matt Spinler9c3d2852019-04-08 15:57:19 -0500586 */
Patrick Williams9052ebd2024-08-16 15:22:16 -0400587void removeAssociationOwnersEntry(
588 const std::string& assocPath, const std::string& endpointPath,
589 const std::string& owner, AssociationMaps& assocMaps)
Matt Spinler9c3d2852019-04-08 15:57:19 -0500590{
591 auto sources = assocMaps.owners.begin();
592 while (sources != assocMaps.owners.end())
593 {
594 auto owners = sources->second.find(owner);
595 if (owners != sources->second.end())
596 {
597 auto entry = owners->second.find(assocPath);
598 if (entry != owners->second.end())
599 {
600 auto e = std::find(entry->second.begin(), entry->second.end(),
601 endpointPath);
602 if (e != entry->second.end())
603 {
604 entry->second.erase(e);
605 if (entry->second.empty())
606 {
607 owners->second.erase(entry);
608 }
609 }
610 }
611
612 if (owners->second.empty())
613 {
614 sources->second.erase(owners);
615 }
616 }
617
618 if (sources->second.empty())
619 {
620 sources = assocMaps.owners.erase(sources);
621 continue;
622 }
623 sources++;
624 }
625}
626
Patrick Williams9052ebd2024-08-16 15:22:16 -0400627void moveAssociationToPending(
628 boost::asio::io_context& io, const std::string& endpointPath,
629 AssociationMaps& assocMaps, sdbusplus::asio::object_server& server)
Matt Spinler9c3d2852019-04-08 15:57:19 -0500630{
631 FindAssocResults associationData;
632
633 // Check which associations this path is an endpoint of, and
634 // then add them to the pending associations map and remove
635 // the associations objects.
636 findAssociations(endpointPath, assocMaps, associationData);
637
638 for (const auto& [owner, association] : associationData)
639 {
640 const auto& forwardPath = endpointPath;
641 const auto& forwardType = std::get<forwardTypePos>(association);
642 const auto& reversePath = std::get<reversePathPos>(association);
643 const auto& reverseType = std::get<reverseTypePos>(association);
644
645 addPendingAssociation(forwardPath, forwardType, reversePath,
646 reverseType, owner, assocMaps);
647
648 // Remove both sides of the association from assocMaps.ifaces
Brad Bishop78b04862025-07-08 16:00:37 -0400649 removeAssociationIfacesEntry(
650 io, appendPathSegment(forwardPath, forwardType), reversePath,
651 assocMaps, server);
652 removeAssociationIfacesEntry(
653 io, appendPathSegment(reversePath, reverseType), forwardPath,
654 assocMaps, server);
Matt Spinler9c3d2852019-04-08 15:57:19 -0500655
656 // Remove both sides of the association from assocMaps.owners
Brad Bishop78b04862025-07-08 16:00:37 -0400657 removeAssociationOwnersEntry(
658 appendPathSegment(forwardPath, forwardType), reversePath, owner,
659 assocMaps);
660 removeAssociationOwnersEntry(
661 appendPathSegment(reversePath, reverseType), forwardPath, owner,
662 assocMaps);
Matt Spinler9c3d2852019-04-08 15:57:19 -0500663 }
664}