blob: 9fd72abad375fb061668fab6773a885aa3ecaa93 [file] [log] [blame]
Andrew Geisslera80a3af2019-02-04 14:01:49 -06001#include "associations.hpp"
2
Matt Spinler7f8fd1f2019-04-08 15:21:59 -05003#include <boost/algorithm/string/predicate.hpp>
Andrew Geissler4511b332019-02-21 15:40:40 -06004#include <iostream>
Lei YUb89c6612021-07-22 15:59:52 +08005#include <sdbusplus/exception.hpp>
Andrew Geissler4511b332019-02-21 15:40:40 -06006
Andrew Geisslera80a3af2019-02-04 14:01:49 -06007void removeAssociation(const std::string& sourcePath, const std::string& owner,
8 sdbusplus::asio::object_server& server,
Matt Spinlere2359fb2019-04-05 14:11:33 -05009 AssociationMaps& assocMaps)
Andrew Geisslera80a3af2019-02-04 14:01:49 -060010{
11 // Use associationOwners to find the association paths and endpoints
12 // that the passed in object path and service own. Remove all of
13 // these endpoints from the actual association D-Bus objects, and if
14 // the endpoints property is then empty, the whole association object
15 // can be removed. Note there can be multiple services that own an
16 // association, and also that sourcePath is the path of the object
17 // that contains the org.openbmc.Associations interface and not the
18 // association path itself.
19
20 // Find the services that have associations for this object path
Matt Spinlere2359fb2019-04-05 14:11:33 -050021 auto owners = assocMaps.owners.find(sourcePath);
22 if (owners == assocMaps.owners.end())
Andrew Geisslera80a3af2019-02-04 14:01:49 -060023 {
24 return;
25 }
26
27 // Find the association paths and endpoints owned by this object
28 // path for this service.
29 auto assocs = owners->second.find(owner);
30 if (assocs == owners->second.end())
31 {
32 return;
33 }
34
35 for (const auto& [assocPath, endpointsToRemove] : assocs->second)
36 {
Andrew Geissler5629ae82019-02-21 12:59:09 -060037 removeAssociationEndpoints(server, assocPath, endpointsToRemove,
Matt Spinlere2359fb2019-04-05 14:11:33 -050038 assocMaps);
Andrew Geisslera80a3af2019-02-04 14:01:49 -060039 }
40
41 // Remove the associationOwners entries for this owning path/service.
42 owners->second.erase(assocs);
43 if (owners->second.empty())
44 {
Matt Spinlere2359fb2019-04-05 14:11:33 -050045 assocMaps.owners.erase(owners);
Andrew Geisslera80a3af2019-02-04 14:01:49 -060046 }
Matt Spinlercb9bcdb2019-04-08 10:58:49 -050047
48 // If we were still waiting on the other side of this association to
49 // show up, cancel that wait.
50 removeFromPendingAssociations(sourcePath, assocMaps);
Andrew Geisslera80a3af2019-02-04 14:01:49 -060051}
Andrew Geisslerff5ce922019-02-21 12:43:09 -060052
53void removeAssociationEndpoints(
54 sdbusplus::asio::object_server& objectServer, const std::string& assocPath,
55 const boost::container::flat_set<std::string>& endpointsToRemove,
Matt Spinlere2359fb2019-04-05 14:11:33 -050056 AssociationMaps& assocMaps)
Andrew Geisslerff5ce922019-02-21 12:43:09 -060057{
Matt Spinlere2359fb2019-04-05 14:11:33 -050058 auto assoc = assocMaps.ifaces.find(assocPath);
59 if (assoc == assocMaps.ifaces.end())
Andrew Geisslerff5ce922019-02-21 12:43:09 -060060 {
61 return;
62 }
63
64 auto& endpointsInDBus = std::get<endpointsPos>(assoc->second);
65
66 for (const auto& endpointToRemove : endpointsToRemove)
67 {
68 auto e = std::find(endpointsInDBus.begin(), endpointsInDBus.end(),
69 endpointToRemove);
70
71 if (e != endpointsInDBus.end())
72 {
73 endpointsInDBus.erase(e);
74 }
75 }
76
77 if (endpointsInDBus.empty())
78 {
79 objectServer.remove_interface(std::get<ifacePos>(assoc->second));
80 std::get<ifacePos>(assoc->second) = nullptr;
81 std::get<endpointsPos>(assoc->second).clear();
82 }
83 else
84 {
85 std::get<ifacePos>(assoc->second)
86 ->set_property("endpoints", endpointsInDBus);
87 }
88}
Andrew Geissler7f1c44d2019-02-21 13:44:16 -060089
90void checkAssociationEndpointRemoves(
91 const std::string& sourcePath, const std::string& owner,
92 const AssociationPaths& newAssociations,
Matt Spinlere2359fb2019-04-05 14:11:33 -050093 sdbusplus::asio::object_server& objectServer, AssociationMaps& assocMaps)
Andrew Geissler7f1c44d2019-02-21 13:44:16 -060094{
95 // Find the services that have associations on this path.
Matt Spinlere2359fb2019-04-05 14:11:33 -050096 auto originalOwners = assocMaps.owners.find(sourcePath);
97 if (originalOwners == assocMaps.owners.end())
Andrew Geissler7f1c44d2019-02-21 13:44:16 -060098 {
99 return;
100 }
101
102 // Find the associations for this service
103 auto originalAssociations = originalOwners->second.find(owner);
104 if (originalAssociations == originalOwners->second.end())
105 {
106 return;
107 }
108
109 // Compare the new endpoints versus the original endpoints, and
110 // remove any of the original ones that aren't in the new list.
111 for (const auto& [originalAssocPath, originalEndpoints] :
112 originalAssociations->second)
113 {
114 // Check if this source even still has each association that
115 // was there previously, and if not, remove all of its endpoints
116 // from the D-Bus endpoints property which will cause the whole
117 // association path to be removed if no endpoints remain.
118 auto newEndpoints = newAssociations.find(originalAssocPath);
119 if (newEndpoints == newAssociations.end())
120 {
121 removeAssociationEndpoints(objectServer, originalAssocPath,
Matt Spinlere2359fb2019-04-05 14:11:33 -0500122 originalEndpoints, assocMaps);
Andrew Geissler7f1c44d2019-02-21 13:44:16 -0600123 }
124 else
125 {
126 // The association is still there. Check if the endpoints
127 // changed.
128 boost::container::flat_set<std::string> toRemove;
129
130 for (auto& originalEndpoint : originalEndpoints)
131 {
132 if (std::find(newEndpoints->second.begin(),
133 newEndpoints->second.end(),
134 originalEndpoint) == newEndpoints->second.end())
135 {
136 toRemove.emplace(originalEndpoint);
137 }
138 }
139 if (!toRemove.empty())
140 {
141 removeAssociationEndpoints(objectServer, originalAssocPath,
Matt Spinlere2359fb2019-04-05 14:11:33 -0500142 toRemove, assocMaps);
Andrew Geissler7f1c44d2019-02-21 13:44:16 -0600143 }
144 }
145 }
146}
Andrew Geissler4511b332019-02-21 15:40:40 -0600147
Matt Spinler11401e22019-04-08 13:13:25 -0500148void addEndpointsToAssocIfaces(
149 sdbusplus::asio::object_server& objectServer, const std::string& assocPath,
150 const boost::container::flat_set<std::string>& endpointPaths,
151 AssociationMaps& assocMaps)
152{
153 auto& iface = assocMaps.ifaces[assocPath];
154 auto& i = std::get<ifacePos>(iface);
155 auto& endpoints = std::get<endpointsPos>(iface);
156
157 // Only add new endpoints
158 for (auto& e : endpointPaths)
159 {
160 if (std::find(endpoints.begin(), endpoints.end(), e) == endpoints.end())
161 {
162 endpoints.push_back(e);
163 }
164 }
165
166 // If the interface already exists, only need to update
167 // the property value, otherwise create it
168 if (i)
169 {
170 i->set_property("endpoints", endpoints);
171 }
172 else
173 {
174 i = objectServer.add_interface(assocPath, XYZ_ASSOCIATION_INTERFACE);
175 i->register_property("endpoints", endpoints);
176 i->initialize();
177 }
178}
179
Andrew Geissler4511b332019-02-21 15:40:40 -0600180void associationChanged(sdbusplus::asio::object_server& objectServer,
181 const std::vector<Association>& associations,
182 const std::string& path, const std::string& owner,
Matt Spinlere0b0e3a2019-04-08 10:39:23 -0500183 const interface_map_type& interfaceMap,
Matt Spinlere2359fb2019-04-05 14:11:33 -0500184 AssociationMaps& assocMaps)
Andrew Geissler4511b332019-02-21 15:40:40 -0600185{
186 AssociationPaths objects;
187
188 for (const Association& association : associations)
189 {
190 std::string forward;
191 std::string reverse;
192 std::string endpoint;
193 std::tie(forward, reverse, endpoint) = association;
194
Andrew Geissler0a560a52019-03-22 10:59:07 -0500195 if (endpoint.empty())
196 {
197 std::cerr << "Found invalid association on path " << path << "\n";
198 continue;
199 }
Matt Spinlere0b0e3a2019-04-08 10:39:23 -0500200
201 // Can't create this association if the endpoint isn't on D-Bus.
202 if (interfaceMap.find(endpoint) == interfaceMap.end())
203 {
204 addPendingAssociation(endpoint, reverse, path, forward, owner,
205 assocMaps);
206 continue;
207 }
208
Andrew Geissler4511b332019-02-21 15:40:40 -0600209 if (forward.size())
210 {
211 objects[path + "/" + forward].emplace(endpoint);
212 }
213 if (reverse.size())
214 {
Andrew Geissler4511b332019-02-21 15:40:40 -0600215 objects[endpoint + "/" + reverse].emplace(path);
216 }
217 }
218 for (const auto& object : objects)
219 {
Matt Spinler11401e22019-04-08 13:13:25 -0500220 addEndpointsToAssocIfaces(objectServer, object.first, object.second,
221 assocMaps);
Andrew Geissler4511b332019-02-21 15:40:40 -0600222 }
223
224 // Check for endpoints being removed instead of added
225 checkAssociationEndpointRemoves(path, owner, objects, objectServer,
Matt Spinlere2359fb2019-04-05 14:11:33 -0500226 assocMaps);
Andrew Geissler4511b332019-02-21 15:40:40 -0600227
Matt Spinlere0b0e3a2019-04-08 10:39:23 -0500228 if (!objects.empty())
Andrew Geissler4511b332019-02-21 15:40:40 -0600229 {
Matt Spinlere0b0e3a2019-04-08 10:39:23 -0500230 // Update associationOwners with the latest info
231 auto a = assocMaps.owners.find(path);
232 if (a != assocMaps.owners.end())
Andrew Geissler4511b332019-02-21 15:40:40 -0600233 {
Matt Spinlere0b0e3a2019-04-08 10:39:23 -0500234 auto o = a->second.find(owner);
235 if (o != a->second.end())
236 {
237 o->second = std::move(objects);
238 }
239 else
240 {
241 a->second.emplace(owner, std::move(objects));
242 }
Andrew Geissler4511b332019-02-21 15:40:40 -0600243 }
244 else
245 {
Matt Spinlere0b0e3a2019-04-08 10:39:23 -0500246 boost::container::flat_map<std::string, AssociationPaths> owners;
247 owners.emplace(owner, std::move(objects));
248 assocMaps.owners.emplace(path, owners);
Andrew Geissler4511b332019-02-21 15:40:40 -0600249 }
250 }
Matt Spinlere0b0e3a2019-04-08 10:39:23 -0500251}
252
253void addPendingAssociation(const std::string& objectPath,
254 const std::string& type,
255 const std::string& endpointPath,
256 const std::string& endpointType,
257 const std::string& owner, AssociationMaps& assocMaps)
258{
259 Association assoc{type, endpointType, endpointPath};
260
261 auto p = assocMaps.pending.find(objectPath);
262 if (p == assocMaps.pending.end())
263 {
264 ExistingEndpoints ee;
265 ee.emplace_back(owner, std::move(assoc));
266 assocMaps.pending.emplace(objectPath, std::move(ee));
267 }
Andrew Geissler4511b332019-02-21 15:40:40 -0600268 else
269 {
Matt Spinlere0b0e3a2019-04-08 10:39:23 -0500270 // Already waiting on this path for another association,
271 // so just add this endpoint and owner.
272 auto& endpoints = p->second;
273 auto e =
274 std::find_if(endpoints.begin(), endpoints.end(),
275 [&assoc, &owner](const auto& endpoint) {
276 return (std::get<ownerPos>(endpoint) == owner) &&
277 (std::get<assocPos>(endpoint) == assoc);
278 });
279 if (e == endpoints.end())
280 {
281 endpoints.emplace_back(owner, std::move(assoc));
282 }
Andrew Geissler4511b332019-02-21 15:40:40 -0600283 }
284}
Matt Spinlercb9bcdb2019-04-08 10:58:49 -0500285
286void removeFromPendingAssociations(const std::string& endpointPath,
287 AssociationMaps& assocMaps)
288{
289 auto assoc = assocMaps.pending.begin();
290 while (assoc != assocMaps.pending.end())
291 {
292 auto endpoint = assoc->second.begin();
293 while (endpoint != assoc->second.end())
294 {
295 auto& e = std::get<assocPos>(*endpoint);
296 if (std::get<reversePathPos>(e) == endpointPath)
297 {
298 endpoint = assoc->second.erase(endpoint);
299 continue;
300 }
301
302 endpoint++;
303 }
304
305 if (assoc->second.empty())
306 {
307 assoc = assocMaps.pending.erase(assoc);
308 continue;
309 }
310
311 assoc++;
312 }
313}
Matt Spinler11401e22019-04-08 13:13:25 -0500314
315void addSingleAssociation(sdbusplus::asio::object_server& server,
316 const std::string& assocPath,
317 const std::string& endpoint, const std::string& owner,
318 const std::string& ownerPath,
319 AssociationMaps& assocMaps)
320{
321 boost::container::flat_set<std::string> endpoints{endpoint};
322
323 addEndpointsToAssocIfaces(server, assocPath, endpoints, assocMaps);
324
325 AssociationPaths objects;
326 boost::container::flat_set e{endpoint};
327 objects.emplace(assocPath, e);
328
329 auto a = assocMaps.owners.find(ownerPath);
330 if (a != assocMaps.owners.end())
331 {
332 auto o = a->second.find(owner);
333 if (o != a->second.end())
334 {
335 auto p = o->second.find(assocPath);
336 if (p != o->second.end())
337 {
338 p->second.emplace(endpoint);
339 }
340 else
341 {
342 o->second.emplace(assocPath, e);
343 }
344 }
345 else
346 {
347 a->second.emplace(owner, std::move(objects));
348 }
349 }
350 else
351 {
352 boost::container::flat_map<std::string, AssociationPaths> owners;
353 owners.emplace(owner, std::move(objects));
354 assocMaps.owners.emplace(endpoint, owners);
355 }
356}
357
358void checkIfPendingAssociation(const std::string& objectPath,
359 const interface_map_type& interfaceMap,
360 AssociationMaps& assocMaps,
361 sdbusplus::asio::object_server& server)
362{
363 auto pending = assocMaps.pending.find(objectPath);
364 if (pending == assocMaps.pending.end())
365 {
366 return;
367 }
368
369 if (interfaceMap.find(objectPath) == interfaceMap.end())
370 {
371 return;
372 }
373
374 auto endpoint = pending->second.begin();
375
376 while (endpoint != pending->second.end())
377 {
378 const auto& e = std::get<assocPos>(*endpoint);
379
380 // Ensure the other side of the association still exists
381 if (interfaceMap.find(std::get<reversePathPos>(e)) ==
382 interfaceMap.end())
383 {
384 endpoint++;
385 continue;
386 }
387
388 // Add both sides of the association:
389 // objectPath/forwardType and reversePath/reverseType
390 //
391 // The ownerPath is the reversePath - i.e. the endpoint that
392 // is on D-Bus and owns the org.openbmc.Associations iface.
393 //
394 const auto& ownerPath = std::get<reversePathPos>(e);
395 const auto& owner = std::get<ownerPos>(*endpoint);
396
397 auto assocPath = objectPath + '/' + std::get<forwardTypePos>(e);
398 auto endpointPath = ownerPath;
399
Lei YUb89c6612021-07-22 15:59:52 +0800400 try
401 {
402 addSingleAssociation(server, assocPath, endpointPath, owner,
403 ownerPath, assocMaps);
Matt Spinler11401e22019-04-08 13:13:25 -0500404
Lei YUb89c6612021-07-22 15:59:52 +0800405 // Now the reverse direction (still the same owner and ownerPath)
406 assocPath = endpointPath + '/' + std::get<reverseTypePos>(e);
407 endpointPath = objectPath;
408 addSingleAssociation(server, assocPath, endpointPath, owner,
409 ownerPath, assocMaps);
410 }
411 catch (const sdbusplus::exception::exception& e)
412 {
413 // In some case the interface could not be created on DBus and an
414 // exception is thrown. mapper has no control of the interface/path
415 // of the associations, so it has to catch the error and drop the
416 // association request.
417 fprintf(stderr,
418 "Error adding association: assocPath %s, endpointPath %s, "
419 "what: %s\n",
420 assocPath.c_str(), endpointPath.c_str(), e.what());
421 }
Matt Spinler11401e22019-04-08 13:13:25 -0500422
423 // Not pending anymore
424 endpoint = pending->second.erase(endpoint);
425 }
426
427 if (pending->second.empty())
428 {
429 assocMaps.pending.erase(objectPath);
430 }
431}
Matt Spinler7f8fd1f2019-04-08 15:21:59 -0500432
433void findAssociations(const std::string& endpointPath,
434 AssociationMaps& assocMaps,
435 FindAssocResults& associationData)
436{
437 for (const auto& [sourcePath, owners] : assocMaps.owners)
438 {
439 for (const auto& [owner, assocs] : owners)
440 {
441 for (const auto& [assocPath, endpoints] : assocs)
442 {
443 if (std::find(endpoints.begin(), endpoints.end(),
444 endpointPath) != endpoints.end())
445 {
446 // assocPath is <path>/<type> which tells us what is on the
447 // other side of the association.
448 auto pos = assocPath.rfind('/');
449 auto otherPath = assocPath.substr(0, pos);
450 auto otherType = assocPath.substr(pos + 1);
451
452 // Now we need to find the endpointPath/<type> ->
453 // [otherPath] entry so that we can get the type for
454 // endpointPath's side of the assoc. Do this by finding
455 // otherPath as an endpoint, and also checking for
456 // 'endpointPath/*' as the key.
457 auto a = std::find_if(
458 assocs.begin(), assocs.end(),
459 [&endpointPath, &otherPath](const auto& ap) {
460 const auto& endpoints = ap.second;
461 auto endpoint = std::find(
462 endpoints.begin(), endpoints.end(), otherPath);
463 if (endpoint != endpoints.end())
464 {
465 return boost::starts_with(ap.first,
466 endpointPath + '/');
467 }
468 return false;
469 });
470
471 if (a != assocs.end())
472 {
473 // Pull out the type from endpointPath/<type>
474 pos = a->first.rfind('/');
475 auto thisType = a->first.substr(pos + 1);
476
477 // Now we know the full association:
478 // endpointPath/thisType -> otherPath/otherType
479 Association association{thisType, otherType, otherPath};
480 associationData.emplace_back(owner, association);
481 }
482 }
483 }
484 }
485 }
486}
Matt Spinler9c3d2852019-04-08 15:57:19 -0500487
488/** @brief Remove an endpoint for a particular association from D-Bus.
489 *
490 * If the last endpoint is gone, remove the whole association interface,
491 * otherwise just update the D-Bus endpoints property.
492 *
493 * @param[in] assocPath - the association path
494 * @param[in] endpointPath - the endpoint path to find and remove
495 * @param[in,out] assocMaps - the association maps
496 * @param[in,out] server - sdbus system object
497 */
498void removeAssociationIfacesEntry(const std::string& assocPath,
499 const std::string& endpointPath,
500 AssociationMaps& assocMaps,
501 sdbusplus::asio::object_server& server)
502{
503 auto assoc = assocMaps.ifaces.find(assocPath);
504 if (assoc != assocMaps.ifaces.end())
505 {
506 auto& endpoints = std::get<endpointsPos>(assoc->second);
507 auto e = std::find(endpoints.begin(), endpoints.end(), endpointPath);
508 if (e != endpoints.end())
509 {
510 endpoints.erase(e);
511
512 if (endpoints.empty())
513 {
514 server.remove_interface(std::get<ifacePos>(assoc->second));
515 std::get<ifacePos>(assoc->second) = nullptr;
516 }
517 else
518 {
519 std::get<ifacePos>(assoc->second)
520 ->set_property("endpoints", endpoints);
521 }
522 }
523 }
524}
525
526/** @brief Remove an endpoint from the association owners map.
527 *
528 * For a specific association path and owner, remove the endpoint.
529 * Remove all remaining artifacts of that endpoint in the owners map
530 * based on what frees up after the erase.
531 *
532 * @param[in] assocPath - the association object path
533 * @param[in] endpointPath - the endpoint object path
534 * @param[in] owner - the owner of the association
535 * @param[in,out] assocMaps - the association maps
536 * @param[in,out] server - sdbus system object
537 */
538void removeAssociationOwnersEntry(const std::string& assocPath,
539 const std::string& endpointPath,
540 const std::string& owner,
541 AssociationMaps& assocMaps,
Brad Bishop2d41d6a2021-08-03 08:14:45 -0400542 sdbusplus::asio::object_server&)
Matt Spinler9c3d2852019-04-08 15:57:19 -0500543{
544 auto sources = assocMaps.owners.begin();
545 while (sources != assocMaps.owners.end())
546 {
547 auto owners = sources->second.find(owner);
548 if (owners != sources->second.end())
549 {
550 auto entry = owners->second.find(assocPath);
551 if (entry != owners->second.end())
552 {
553 auto e = std::find(entry->second.begin(), entry->second.end(),
554 endpointPath);
555 if (e != entry->second.end())
556 {
557 entry->second.erase(e);
558 if (entry->second.empty())
559 {
560 owners->second.erase(entry);
561 }
562 }
563 }
564
565 if (owners->second.empty())
566 {
567 sources->second.erase(owners);
568 }
569 }
570
571 if (sources->second.empty())
572 {
573 sources = assocMaps.owners.erase(sources);
574 continue;
575 }
576 sources++;
577 }
578}
579
580void moveAssociationToPending(const std::string& endpointPath,
581 AssociationMaps& assocMaps,
582 sdbusplus::asio::object_server& server)
583{
584 FindAssocResults associationData;
585
586 // Check which associations this path is an endpoint of, and
587 // then add them to the pending associations map and remove
588 // the associations objects.
589 findAssociations(endpointPath, assocMaps, associationData);
590
591 for (const auto& [owner, association] : associationData)
592 {
593 const auto& forwardPath = endpointPath;
594 const auto& forwardType = std::get<forwardTypePos>(association);
595 const auto& reversePath = std::get<reversePathPos>(association);
596 const auto& reverseType = std::get<reverseTypePos>(association);
597
598 addPendingAssociation(forwardPath, forwardType, reversePath,
599 reverseType, owner, assocMaps);
600
601 // Remove both sides of the association from assocMaps.ifaces
602 removeAssociationIfacesEntry(forwardPath + '/' + forwardType,
603 reversePath, assocMaps, server);
604 removeAssociationIfacesEntry(reversePath + '/' + reverseType,
605 forwardPath, assocMaps, server);
606
607 // Remove both sides of the association from assocMaps.owners
608 removeAssociationOwnersEntry(forwardPath + '/' + forwardType,
609 reversePath, owner, assocMaps, server);
610 removeAssociationOwnersEntry(reversePath + '/' + reverseType,
611 forwardPath, owner, assocMaps, server);
612 }
613}