blob: 110c6eb6a937724873cb82982ea89fae6fd15b32 [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>
5
Andrew Geisslera80a3af2019-02-04 14:01:49 -06006void removeAssociation(const std::string& sourcePath, const std::string& owner,
7 sdbusplus::asio::object_server& server,
Matt Spinlere2359fb2019-04-05 14:11:33 -05008 AssociationMaps& assocMaps)
Andrew Geisslera80a3af2019-02-04 14:01:49 -06009{
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
Matt Spinlere2359fb2019-04-05 14:11:33 -050020 auto owners = assocMaps.owners.find(sourcePath);
21 if (owners == assocMaps.owners.end())
Andrew Geisslera80a3af2019-02-04 14:01:49 -060022 {
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 {
Andrew Geissler5629ae82019-02-21 12:59:09 -060036 removeAssociationEndpoints(server, assocPath, endpointsToRemove,
Matt Spinlere2359fb2019-04-05 14:11:33 -050037 assocMaps);
Andrew Geisslera80a3af2019-02-04 14:01:49 -060038 }
39
40 // Remove the associationOwners entries for this owning path/service.
41 owners->second.erase(assocs);
42 if (owners->second.empty())
43 {
Matt Spinlere2359fb2019-04-05 14:11:33 -050044 assocMaps.owners.erase(owners);
Andrew Geisslera80a3af2019-02-04 14:01:49 -060045 }
Matt Spinlercb9bcdb2019-04-08 10:58:49 -050046
47 // If we were still waiting on the other side of this association to
48 // show up, cancel that wait.
49 removeFromPendingAssociations(sourcePath, assocMaps);
Andrew Geisslera80a3af2019-02-04 14:01:49 -060050}
Andrew Geisslerff5ce922019-02-21 12:43:09 -060051
52void removeAssociationEndpoints(
53 sdbusplus::asio::object_server& objectServer, const std::string& assocPath,
54 const boost::container::flat_set<std::string>& endpointsToRemove,
Matt Spinlere2359fb2019-04-05 14:11:33 -050055 AssociationMaps& assocMaps)
Andrew Geisslerff5ce922019-02-21 12:43:09 -060056{
Matt Spinlere2359fb2019-04-05 14:11:33 -050057 auto assoc = assocMaps.ifaces.find(assocPath);
58 if (assoc == assocMaps.ifaces.end())
Andrew Geisslerff5ce922019-02-21 12:43:09 -060059 {
60 return;
61 }
62
63 auto& endpointsInDBus = std::get<endpointsPos>(assoc->second);
64
65 for (const auto& endpointToRemove : endpointsToRemove)
66 {
67 auto e = std::find(endpointsInDBus.begin(), endpointsInDBus.end(),
68 endpointToRemove);
69
70 if (e != endpointsInDBus.end())
71 {
72 endpointsInDBus.erase(e);
73 }
74 }
75
76 if (endpointsInDBus.empty())
77 {
78 objectServer.remove_interface(std::get<ifacePos>(assoc->second));
79 std::get<ifacePos>(assoc->second) = nullptr;
80 std::get<endpointsPos>(assoc->second).clear();
81 }
82 else
83 {
84 std::get<ifacePos>(assoc->second)
85 ->set_property("endpoints", endpointsInDBus);
86 }
87}
Andrew Geissler7f1c44d2019-02-21 13:44:16 -060088
89void checkAssociationEndpointRemoves(
90 const std::string& sourcePath, const std::string& owner,
91 const AssociationPaths& newAssociations,
Matt Spinlere2359fb2019-04-05 14:11:33 -050092 sdbusplus::asio::object_server& objectServer, AssociationMaps& assocMaps)
Andrew Geissler7f1c44d2019-02-21 13:44:16 -060093{
94 // Find the services that have associations on this path.
Matt Spinlere2359fb2019-04-05 14:11:33 -050095 auto originalOwners = assocMaps.owners.find(sourcePath);
96 if (originalOwners == assocMaps.owners.end())
Andrew Geissler7f1c44d2019-02-21 13:44:16 -060097 {
98 return;
99 }
100
101 // Find the associations for this service
102 auto originalAssociations = originalOwners->second.find(owner);
103 if (originalAssociations == originalOwners->second.end())
104 {
105 return;
106 }
107
108 // Compare the new endpoints versus the original endpoints, and
109 // remove any of the original ones that aren't in the new list.
110 for (const auto& [originalAssocPath, originalEndpoints] :
111 originalAssociations->second)
112 {
113 // Check if this source even still has each association that
114 // was there previously, and if not, remove all of its endpoints
115 // from the D-Bus endpoints property which will cause the whole
116 // association path to be removed if no endpoints remain.
117 auto newEndpoints = newAssociations.find(originalAssocPath);
118 if (newEndpoints == newAssociations.end())
119 {
120 removeAssociationEndpoints(objectServer, originalAssocPath,
Matt Spinlere2359fb2019-04-05 14:11:33 -0500121 originalEndpoints, assocMaps);
Andrew Geissler7f1c44d2019-02-21 13:44:16 -0600122 }
123 else
124 {
125 // The association is still there. Check if the endpoints
126 // changed.
127 boost::container::flat_set<std::string> toRemove;
128
129 for (auto& originalEndpoint : originalEndpoints)
130 {
131 if (std::find(newEndpoints->second.begin(),
132 newEndpoints->second.end(),
133 originalEndpoint) == newEndpoints->second.end())
134 {
135 toRemove.emplace(originalEndpoint);
136 }
137 }
138 if (!toRemove.empty())
139 {
140 removeAssociationEndpoints(objectServer, originalAssocPath,
Matt Spinlere2359fb2019-04-05 14:11:33 -0500141 toRemove, assocMaps);
Andrew Geissler7f1c44d2019-02-21 13:44:16 -0600142 }
143 }
144 }
145}
Andrew Geissler4511b332019-02-21 15:40:40 -0600146
Matt Spinler11401e22019-04-08 13:13:25 -0500147void addEndpointsToAssocIfaces(
148 sdbusplus::asio::object_server& objectServer, const std::string& assocPath,
149 const boost::container::flat_set<std::string>& endpointPaths,
150 AssociationMaps& assocMaps)
151{
152 auto& iface = assocMaps.ifaces[assocPath];
153 auto& i = std::get<ifacePos>(iface);
154 auto& endpoints = std::get<endpointsPos>(iface);
155
156 // Only add new endpoints
157 for (auto& e : endpointPaths)
158 {
159 if (std::find(endpoints.begin(), endpoints.end(), e) == endpoints.end())
160 {
161 endpoints.push_back(e);
162 }
163 }
164
165 // If the interface already exists, only need to update
166 // the property value, otherwise create it
167 if (i)
168 {
169 i->set_property("endpoints", endpoints);
170 }
171 else
172 {
173 i = objectServer.add_interface(assocPath, XYZ_ASSOCIATION_INTERFACE);
174 i->register_property("endpoints", endpoints);
175 i->initialize();
176 }
177}
178
Andrew Geissler4511b332019-02-21 15:40:40 -0600179void associationChanged(sdbusplus::asio::object_server& objectServer,
180 const std::vector<Association>& associations,
181 const std::string& path, const std::string& owner,
Matt Spinlere0b0e3a2019-04-08 10:39:23 -0500182 const interface_map_type& interfaceMap,
Matt Spinlere2359fb2019-04-05 14:11:33 -0500183 AssociationMaps& assocMaps)
Andrew Geissler4511b332019-02-21 15:40:40 -0600184{
185 AssociationPaths objects;
186
187 for (const Association& association : associations)
188 {
189 std::string forward;
190 std::string reverse;
191 std::string endpoint;
192 std::tie(forward, reverse, endpoint) = association;
193
Andrew Geissler0a560a52019-03-22 10:59:07 -0500194 if (endpoint.empty())
195 {
196 std::cerr << "Found invalid association on path " << path << "\n";
197 continue;
198 }
Matt Spinlere0b0e3a2019-04-08 10:39:23 -0500199
200 // Can't create this association if the endpoint isn't on D-Bus.
201 if (interfaceMap.find(endpoint) == interfaceMap.end())
202 {
203 addPendingAssociation(endpoint, reverse, path, forward, owner,
204 assocMaps);
205 continue;
206 }
207
Andrew Geissler4511b332019-02-21 15:40:40 -0600208 if (forward.size())
209 {
210 objects[path + "/" + forward].emplace(endpoint);
211 }
212 if (reverse.size())
213 {
Andrew Geissler4511b332019-02-21 15:40:40 -0600214 objects[endpoint + "/" + reverse].emplace(path);
215 }
216 }
217 for (const auto& object : objects)
218 {
Matt Spinler11401e22019-04-08 13:13:25 -0500219 addEndpointsToAssocIfaces(objectServer, object.first, object.second,
220 assocMaps);
Andrew Geissler4511b332019-02-21 15:40:40 -0600221 }
222
223 // Check for endpoints being removed instead of added
224 checkAssociationEndpointRemoves(path, owner, objects, objectServer,
Matt Spinlere2359fb2019-04-05 14:11:33 -0500225 assocMaps);
Andrew Geissler4511b332019-02-21 15:40:40 -0600226
Matt Spinlere0b0e3a2019-04-08 10:39:23 -0500227 if (!objects.empty())
Andrew Geissler4511b332019-02-21 15:40:40 -0600228 {
Matt Spinlere0b0e3a2019-04-08 10:39:23 -0500229 // Update associationOwners with the latest info
230 auto a = assocMaps.owners.find(path);
231 if (a != assocMaps.owners.end())
Andrew Geissler4511b332019-02-21 15:40:40 -0600232 {
Matt Spinlere0b0e3a2019-04-08 10:39:23 -0500233 auto o = a->second.find(owner);
234 if (o != a->second.end())
235 {
236 o->second = std::move(objects);
237 }
238 else
239 {
240 a->second.emplace(owner, std::move(objects));
241 }
Andrew Geissler4511b332019-02-21 15:40:40 -0600242 }
243 else
244 {
Matt Spinlere0b0e3a2019-04-08 10:39:23 -0500245 boost::container::flat_map<std::string, AssociationPaths> owners;
246 owners.emplace(owner, std::move(objects));
247 assocMaps.owners.emplace(path, owners);
Andrew Geissler4511b332019-02-21 15:40:40 -0600248 }
249 }
Matt Spinlere0b0e3a2019-04-08 10:39:23 -0500250}
251
252void addPendingAssociation(const std::string& objectPath,
253 const std::string& type,
254 const std::string& endpointPath,
255 const std::string& endpointType,
256 const std::string& owner, AssociationMaps& assocMaps)
257{
258 Association assoc{type, endpointType, endpointPath};
259
260 auto p = assocMaps.pending.find(objectPath);
261 if (p == assocMaps.pending.end())
262 {
263 ExistingEndpoints ee;
264 ee.emplace_back(owner, std::move(assoc));
265 assocMaps.pending.emplace(objectPath, std::move(ee));
266 }
Andrew Geissler4511b332019-02-21 15:40:40 -0600267 else
268 {
Matt Spinlere0b0e3a2019-04-08 10:39:23 -0500269 // Already waiting on this path for another association,
270 // so just add this endpoint and owner.
271 auto& endpoints = p->second;
272 auto e =
273 std::find_if(endpoints.begin(), endpoints.end(),
274 [&assoc, &owner](const auto& endpoint) {
275 return (std::get<ownerPos>(endpoint) == owner) &&
276 (std::get<assocPos>(endpoint) == assoc);
277 });
278 if (e == endpoints.end())
279 {
280 endpoints.emplace_back(owner, std::move(assoc));
281 }
Andrew Geissler4511b332019-02-21 15:40:40 -0600282 }
283}
Matt Spinlercb9bcdb2019-04-08 10:58:49 -0500284
285void removeFromPendingAssociations(const std::string& endpointPath,
286 AssociationMaps& assocMaps)
287{
288 auto assoc = assocMaps.pending.begin();
289 while (assoc != assocMaps.pending.end())
290 {
291 auto endpoint = assoc->second.begin();
292 while (endpoint != assoc->second.end())
293 {
294 auto& e = std::get<assocPos>(*endpoint);
295 if (std::get<reversePathPos>(e) == endpointPath)
296 {
297 endpoint = assoc->second.erase(endpoint);
298 continue;
299 }
300
301 endpoint++;
302 }
303
304 if (assoc->second.empty())
305 {
306 assoc = assocMaps.pending.erase(assoc);
307 continue;
308 }
309
310 assoc++;
311 }
312}
Matt Spinler11401e22019-04-08 13:13:25 -0500313
314void addSingleAssociation(sdbusplus::asio::object_server& server,
315 const std::string& assocPath,
316 const std::string& endpoint, const std::string& owner,
317 const std::string& ownerPath,
318 AssociationMaps& assocMaps)
319{
320 boost::container::flat_set<std::string> endpoints{endpoint};
321
322 addEndpointsToAssocIfaces(server, assocPath, endpoints, assocMaps);
323
324 AssociationPaths objects;
325 boost::container::flat_set e{endpoint};
326 objects.emplace(assocPath, e);
327
328 auto a = assocMaps.owners.find(ownerPath);
329 if (a != assocMaps.owners.end())
330 {
331 auto o = a->second.find(owner);
332 if (o != a->second.end())
333 {
334 auto p = o->second.find(assocPath);
335 if (p != o->second.end())
336 {
337 p->second.emplace(endpoint);
338 }
339 else
340 {
341 o->second.emplace(assocPath, e);
342 }
343 }
344 else
345 {
346 a->second.emplace(owner, std::move(objects));
347 }
348 }
349 else
350 {
351 boost::container::flat_map<std::string, AssociationPaths> owners;
352 owners.emplace(owner, std::move(objects));
353 assocMaps.owners.emplace(endpoint, owners);
354 }
355}
356
357void checkIfPendingAssociation(const std::string& objectPath,
358 const interface_map_type& interfaceMap,
359 AssociationMaps& assocMaps,
360 sdbusplus::asio::object_server& server)
361{
362 auto pending = assocMaps.pending.find(objectPath);
363 if (pending == assocMaps.pending.end())
364 {
365 return;
366 }
367
368 if (interfaceMap.find(objectPath) == interfaceMap.end())
369 {
370 return;
371 }
372
373 auto endpoint = pending->second.begin();
374
375 while (endpoint != pending->second.end())
376 {
377 const auto& e = std::get<assocPos>(*endpoint);
378
379 // Ensure the other side of the association still exists
380 if (interfaceMap.find(std::get<reversePathPos>(e)) ==
381 interfaceMap.end())
382 {
383 endpoint++;
384 continue;
385 }
386
387 // Add both sides of the association:
388 // objectPath/forwardType and reversePath/reverseType
389 //
390 // The ownerPath is the reversePath - i.e. the endpoint that
391 // is on D-Bus and owns the org.openbmc.Associations iface.
392 //
393 const auto& ownerPath = std::get<reversePathPos>(e);
394 const auto& owner = std::get<ownerPos>(*endpoint);
395
396 auto assocPath = objectPath + '/' + std::get<forwardTypePos>(e);
397 auto endpointPath = ownerPath;
398
399 addSingleAssociation(server, assocPath, endpointPath, owner, ownerPath,
400 assocMaps);
401
402 // Now the reverse direction (still the same owner and ownerPath)
403 assocPath = endpointPath + '/' + std::get<reverseTypePos>(e);
404 endpointPath = objectPath;
405 addSingleAssociation(server, assocPath, endpointPath, owner, ownerPath,
406 assocMaps);
407
408 // Not pending anymore
409 endpoint = pending->second.erase(endpoint);
410 }
411
412 if (pending->second.empty())
413 {
414 assocMaps.pending.erase(objectPath);
415 }
416}
Matt Spinler7f8fd1f2019-04-08 15:21:59 -0500417
418void findAssociations(const std::string& endpointPath,
419 AssociationMaps& assocMaps,
420 FindAssocResults& associationData)
421{
422 for (const auto& [sourcePath, owners] : assocMaps.owners)
423 {
424 for (const auto& [owner, assocs] : owners)
425 {
426 for (const auto& [assocPath, endpoints] : assocs)
427 {
428 if (std::find(endpoints.begin(), endpoints.end(),
429 endpointPath) != endpoints.end())
430 {
431 // assocPath is <path>/<type> which tells us what is on the
432 // other side of the association.
433 auto pos = assocPath.rfind('/');
434 auto otherPath = assocPath.substr(0, pos);
435 auto otherType = assocPath.substr(pos + 1);
436
437 // Now we need to find the endpointPath/<type> ->
438 // [otherPath] entry so that we can get the type for
439 // endpointPath's side of the assoc. Do this by finding
440 // otherPath as an endpoint, and also checking for
441 // 'endpointPath/*' as the key.
442 auto a = std::find_if(
443 assocs.begin(), assocs.end(),
444 [&endpointPath, &otherPath](const auto& ap) {
445 const auto& endpoints = ap.second;
446 auto endpoint = std::find(
447 endpoints.begin(), endpoints.end(), otherPath);
448 if (endpoint != endpoints.end())
449 {
450 return boost::starts_with(ap.first,
451 endpointPath + '/');
452 }
453 return false;
454 });
455
456 if (a != assocs.end())
457 {
458 // Pull out the type from endpointPath/<type>
459 pos = a->first.rfind('/');
460 auto thisType = a->first.substr(pos + 1);
461
462 // Now we know the full association:
463 // endpointPath/thisType -> otherPath/otherType
464 Association association{thisType, otherType, otherPath};
465 associationData.emplace_back(owner, association);
466 }
467 }
468 }
469 }
470 }
471}
Matt Spinler9c3d2852019-04-08 15:57:19 -0500472
473/** @brief Remove an endpoint for a particular association from D-Bus.
474 *
475 * If the last endpoint is gone, remove the whole association interface,
476 * otherwise just update the D-Bus endpoints property.
477 *
478 * @param[in] assocPath - the association path
479 * @param[in] endpointPath - the endpoint path to find and remove
480 * @param[in,out] assocMaps - the association maps
481 * @param[in,out] server - sdbus system object
482 */
483void removeAssociationIfacesEntry(const std::string& assocPath,
484 const std::string& endpointPath,
485 AssociationMaps& assocMaps,
486 sdbusplus::asio::object_server& server)
487{
488 auto assoc = assocMaps.ifaces.find(assocPath);
489 if (assoc != assocMaps.ifaces.end())
490 {
491 auto& endpoints = std::get<endpointsPos>(assoc->second);
492 auto e = std::find(endpoints.begin(), endpoints.end(), endpointPath);
493 if (e != endpoints.end())
494 {
495 endpoints.erase(e);
496
497 if (endpoints.empty())
498 {
499 server.remove_interface(std::get<ifacePos>(assoc->second));
500 std::get<ifacePos>(assoc->second) = nullptr;
501 }
502 else
503 {
504 std::get<ifacePos>(assoc->second)
505 ->set_property("endpoints", endpoints);
506 }
507 }
508 }
509}
510
511/** @brief Remove an endpoint from the association owners map.
512 *
513 * For a specific association path and owner, remove the endpoint.
514 * Remove all remaining artifacts of that endpoint in the owners map
515 * based on what frees up after the erase.
516 *
517 * @param[in] assocPath - the association object path
518 * @param[in] endpointPath - the endpoint object path
519 * @param[in] owner - the owner of the association
520 * @param[in,out] assocMaps - the association maps
521 * @param[in,out] server - sdbus system object
522 */
523void removeAssociationOwnersEntry(const std::string& assocPath,
524 const std::string& endpointPath,
525 const std::string& owner,
526 AssociationMaps& assocMaps,
527 sdbusplus::asio::object_server& server)
528{
529 auto sources = assocMaps.owners.begin();
530 while (sources != assocMaps.owners.end())
531 {
532 auto owners = sources->second.find(owner);
533 if (owners != sources->second.end())
534 {
535 auto entry = owners->second.find(assocPath);
536 if (entry != owners->second.end())
537 {
538 auto e = std::find(entry->second.begin(), entry->second.end(),
539 endpointPath);
540 if (e != entry->second.end())
541 {
542 entry->second.erase(e);
543 if (entry->second.empty())
544 {
545 owners->second.erase(entry);
546 }
547 }
548 }
549
550 if (owners->second.empty())
551 {
552 sources->second.erase(owners);
553 }
554 }
555
556 if (sources->second.empty())
557 {
558 sources = assocMaps.owners.erase(sources);
559 continue;
560 }
561 sources++;
562 }
563}
564
565void moveAssociationToPending(const std::string& endpointPath,
566 AssociationMaps& assocMaps,
567 sdbusplus::asio::object_server& server)
568{
569 FindAssocResults associationData;
570
571 // Check which associations this path is an endpoint of, and
572 // then add them to the pending associations map and remove
573 // the associations objects.
574 findAssociations(endpointPath, assocMaps, associationData);
575
576 for (const auto& [owner, association] : associationData)
577 {
578 const auto& forwardPath = endpointPath;
579 const auto& forwardType = std::get<forwardTypePos>(association);
580 const auto& reversePath = std::get<reversePathPos>(association);
581 const auto& reverseType = std::get<reverseTypePos>(association);
582
583 addPendingAssociation(forwardPath, forwardType, reversePath,
584 reverseType, owner, assocMaps);
585
586 // Remove both sides of the association from assocMaps.ifaces
587 removeAssociationIfacesEntry(forwardPath + '/' + forwardType,
588 reversePath, assocMaps, server);
589 removeAssociationIfacesEntry(reversePath + '/' + reverseType,
590 forwardPath, assocMaps, server);
591
592 // Remove both sides of the association from assocMaps.owners
593 removeAssociationOwnersEntry(forwardPath + '/' + forwardType,
594 reversePath, owner, assocMaps, server);
595 removeAssociationOwnersEntry(reversePath + '/' + reverseType,
596 forwardPath, owner, assocMaps, server);
597 }
598}