| Andrew Geissler | a80a3af | 2019-02-04 14:01:49 -0600 | [diff] [blame] | 1 | #include "associations.hpp" | 
|  | 2 |  | 
| Kallas, Pawel | 5b4357d | 2022-10-12 15:36:37 +0200 | [diff] [blame] | 3 | #include <boost/asio/steady_timer.hpp> | 
| Lei YU | b89c661 | 2021-07-22 15:59:52 +0800 | [diff] [blame] | 4 | #include <sdbusplus/exception.hpp> | 
| Andrew Geissler | 4511b33 | 2019-02-21 15:40:40 -0600 | [diff] [blame] | 5 |  | 
| Brad Bishop | 2352088 | 2022-05-26 21:39:53 -0400 | [diff] [blame] | 6 | #include <iostream> | 
| Brad Bishop | 86d2880 | 2022-07-11 15:49:31 -0400 | [diff] [blame] | 7 | #include <string> | 
| Brad Bishop | 2352088 | 2022-05-26 21:39:53 -0400 | [diff] [blame] | 8 |  | 
| Kallas, Pawel | 5b4357d | 2022-10-12 15:36:37 +0200 | [diff] [blame] | 9 | void updateEndpointsOnDbus(sdbusplus::asio::object_server& objectServer, | 
|  | 10 | const std::string& assocPath, | 
|  | 11 | AssociationMaps& assocMaps) | 
|  | 12 | { | 
|  | 13 | auto& iface = assocMaps.ifaces[assocPath]; | 
|  | 14 | auto& i = std::get<ifacePos>(iface); | 
|  | 15 | auto& endpoints = std::get<endpointsPos>(iface); | 
|  | 16 |  | 
|  | 17 | // If the interface already exists, only need to update | 
|  | 18 | // the property value, otherwise create it | 
|  | 19 | if (i) | 
|  | 20 | { | 
|  | 21 | if (endpoints.empty()) | 
|  | 22 | { | 
|  | 23 | objectServer.remove_interface(i); | 
|  | 24 | i = nullptr; | 
|  | 25 | } | 
|  | 26 | else | 
|  | 27 | { | 
|  | 28 | i->set_property("endpoints", endpoints); | 
|  | 29 | } | 
|  | 30 | } | 
|  | 31 | else | 
|  | 32 | { | 
|  | 33 | if (!endpoints.empty()) | 
|  | 34 | { | 
|  | 35 | i = objectServer.add_interface(assocPath, xyzAssociationInterface); | 
|  | 36 | i->register_property("endpoints", endpoints); | 
|  | 37 | i->initialize(); | 
|  | 38 | } | 
|  | 39 | } | 
|  | 40 | } | 
|  | 41 |  | 
| Patrick Williams | 9052ebd | 2024-08-16 15:22:16 -0400 | [diff] [blame] | 42 | void scheduleUpdateEndpointsOnDbus( | 
|  | 43 | boost::asio::io_context& io, sdbusplus::asio::object_server& objectServer, | 
|  | 44 | const std::string& assocPath, AssociationMaps& assocMaps) | 
| Kallas, Pawel | 5b4357d | 2022-10-12 15:36:37 +0200 | [diff] [blame] | 45 | { | 
|  | 46 | static std::set<std::string> delayedUpdatePaths; | 
|  | 47 |  | 
|  | 48 | if (delayedUpdatePaths.contains(assocPath)) | 
|  | 49 | { | 
|  | 50 | return; | 
|  | 51 | } | 
|  | 52 |  | 
|  | 53 | auto& iface = assocMaps.ifaces[assocPath]; | 
|  | 54 | auto& endpoints = std::get<endpointsPos>(iface); | 
|  | 55 |  | 
|  | 56 | if (endpoints.size() > endpointsCountTimerThreshold) | 
|  | 57 | { | 
|  | 58 | delayedUpdatePaths.emplace(assocPath); | 
|  | 59 | auto timer = std::make_shared<boost::asio::steady_timer>( | 
|  | 60 | io, std::chrono::seconds(endpointUpdateDelaySeconds)); | 
|  | 61 | timer->async_wait([&objectServer, &assocMaps, timer, | 
|  | 62 | assocPath](const boost::system::error_code& ec) { | 
|  | 63 | if (!ec) | 
|  | 64 | { | 
|  | 65 | updateEndpointsOnDbus(objectServer, assocPath, assocMaps); | 
|  | 66 | } | 
|  | 67 | delayedUpdatePaths.erase(assocPath); | 
|  | 68 | }); | 
|  | 69 | } | 
|  | 70 | else | 
|  | 71 | { | 
|  | 72 | updateEndpointsOnDbus(objectServer, assocPath, assocMaps); | 
|  | 73 | } | 
|  | 74 | } | 
|  | 75 |  | 
|  | 76 | void removeAssociation(boost::asio::io_context& io, | 
|  | 77 | const std::string& sourcePath, const std::string& owner, | 
| Andrew Geissler | a80a3af | 2019-02-04 14:01:49 -0600 | [diff] [blame] | 78 | sdbusplus::asio::object_server& server, | 
| Matt Spinler | e2359fb | 2019-04-05 14:11:33 -0500 | [diff] [blame] | 79 | AssociationMaps& assocMaps) | 
| Andrew Geissler | a80a3af | 2019-02-04 14:01:49 -0600 | [diff] [blame] | 80 | { | 
|  | 81 | // Use associationOwners to find the association paths and endpoints | 
|  | 82 | // that the passed in object path and service own.  Remove all of | 
|  | 83 | // these endpoints from the actual association D-Bus objects, and if | 
|  | 84 | // the endpoints property is then empty, the whole association object | 
|  | 85 | // can be removed.  Note there can be multiple services that own an | 
|  | 86 | // association, and also that sourcePath is the path of the object | 
|  | 87 | // that contains the org.openbmc.Associations interface and not the | 
|  | 88 | // association path itself. | 
|  | 89 |  | 
|  | 90 | // Find the services that have associations for this object path | 
| Matt Spinler | e2359fb | 2019-04-05 14:11:33 -0500 | [diff] [blame] | 91 | auto owners = assocMaps.owners.find(sourcePath); | 
|  | 92 | if (owners == assocMaps.owners.end()) | 
| Andrew Geissler | a80a3af | 2019-02-04 14:01:49 -0600 | [diff] [blame] | 93 | { | 
|  | 94 | return; | 
|  | 95 | } | 
|  | 96 |  | 
|  | 97 | // Find the association paths and endpoints owned by this object | 
|  | 98 | // path for this service. | 
|  | 99 | auto assocs = owners->second.find(owner); | 
|  | 100 | if (assocs == owners->second.end()) | 
|  | 101 | { | 
|  | 102 | return; | 
|  | 103 | } | 
|  | 104 |  | 
|  | 105 | for (const auto& [assocPath, endpointsToRemove] : assocs->second) | 
|  | 106 | { | 
| Kallas, Pawel | 5b4357d | 2022-10-12 15:36:37 +0200 | [diff] [blame] | 107 | removeAssociationEndpoints(io, server, assocPath, endpointsToRemove, | 
| Matt Spinler | e2359fb | 2019-04-05 14:11:33 -0500 | [diff] [blame] | 108 | assocMaps); | 
| Andrew Geissler | a80a3af | 2019-02-04 14:01:49 -0600 | [diff] [blame] | 109 | } | 
|  | 110 |  | 
|  | 111 | // Remove the associationOwners entries for this owning path/service. | 
|  | 112 | owners->second.erase(assocs); | 
|  | 113 | if (owners->second.empty()) | 
|  | 114 | { | 
| Matt Spinler | e2359fb | 2019-04-05 14:11:33 -0500 | [diff] [blame] | 115 | assocMaps.owners.erase(owners); | 
| Andrew Geissler | a80a3af | 2019-02-04 14:01:49 -0600 | [diff] [blame] | 116 | } | 
| Matt Spinler | cb9bcdb | 2019-04-08 10:58:49 -0500 | [diff] [blame] | 117 |  | 
|  | 118 | // If we were still waiting on the other side of this association to | 
|  | 119 | // show up, cancel that wait. | 
|  | 120 | removeFromPendingAssociations(sourcePath, assocMaps); | 
| Andrew Geissler | a80a3af | 2019-02-04 14:01:49 -0600 | [diff] [blame] | 121 | } | 
| Andrew Geissler | ff5ce92 | 2019-02-21 12:43:09 -0600 | [diff] [blame] | 122 |  | 
|  | 123 | void removeAssociationEndpoints( | 
| Kallas, Pawel | 5b4357d | 2022-10-12 15:36:37 +0200 | [diff] [blame] | 124 | boost::asio::io_context& io, sdbusplus::asio::object_server& objectServer, | 
|  | 125 | const std::string& assocPath, | 
| Andrew Geissler | ff5ce92 | 2019-02-21 12:43:09 -0600 | [diff] [blame] | 126 | const boost::container::flat_set<std::string>& endpointsToRemove, | 
| Matt Spinler | e2359fb | 2019-04-05 14:11:33 -0500 | [diff] [blame] | 127 | AssociationMaps& assocMaps) | 
| Andrew Geissler | ff5ce92 | 2019-02-21 12:43:09 -0600 | [diff] [blame] | 128 | { | 
| Matt Spinler | e2359fb | 2019-04-05 14:11:33 -0500 | [diff] [blame] | 129 | auto assoc = assocMaps.ifaces.find(assocPath); | 
|  | 130 | if (assoc == assocMaps.ifaces.end()) | 
| Andrew Geissler | ff5ce92 | 2019-02-21 12:43:09 -0600 | [diff] [blame] | 131 | { | 
|  | 132 | return; | 
|  | 133 | } | 
|  | 134 |  | 
|  | 135 | auto& endpointsInDBus = std::get<endpointsPos>(assoc->second); | 
|  | 136 |  | 
|  | 137 | for (const auto& endpointToRemove : endpointsToRemove) | 
|  | 138 | { | 
|  | 139 | auto e = std::find(endpointsInDBus.begin(), endpointsInDBus.end(), | 
|  | 140 | endpointToRemove); | 
|  | 141 |  | 
|  | 142 | if (e != endpointsInDBus.end()) | 
|  | 143 | { | 
|  | 144 | endpointsInDBus.erase(e); | 
|  | 145 | } | 
|  | 146 | } | 
|  | 147 |  | 
| Kallas, Pawel | 5b4357d | 2022-10-12 15:36:37 +0200 | [diff] [blame] | 148 | scheduleUpdateEndpointsOnDbus(io, objectServer, assocPath, assocMaps); | 
| Andrew Geissler | ff5ce92 | 2019-02-21 12:43:09 -0600 | [diff] [blame] | 149 | } | 
| Andrew Geissler | 7f1c44d | 2019-02-21 13:44:16 -0600 | [diff] [blame] | 150 |  | 
|  | 151 | void checkAssociationEndpointRemoves( | 
| Kallas, Pawel | 5b4357d | 2022-10-12 15:36:37 +0200 | [diff] [blame] | 152 | boost::asio::io_context& io, const std::string& sourcePath, | 
|  | 153 | const std::string& owner, const AssociationPaths& newAssociations, | 
| Matt Spinler | e2359fb | 2019-04-05 14:11:33 -0500 | [diff] [blame] | 154 | sdbusplus::asio::object_server& objectServer, AssociationMaps& assocMaps) | 
| Andrew Geissler | 7f1c44d | 2019-02-21 13:44:16 -0600 | [diff] [blame] | 155 | { | 
|  | 156 | // Find the services that have associations on this path. | 
| Matt Spinler | e2359fb | 2019-04-05 14:11:33 -0500 | [diff] [blame] | 157 | auto originalOwners = assocMaps.owners.find(sourcePath); | 
|  | 158 | if (originalOwners == assocMaps.owners.end()) | 
| Andrew Geissler | 7f1c44d | 2019-02-21 13:44:16 -0600 | [diff] [blame] | 159 | { | 
|  | 160 | return; | 
|  | 161 | } | 
|  | 162 |  | 
|  | 163 | // Find the associations for this service | 
|  | 164 | auto originalAssociations = originalOwners->second.find(owner); | 
|  | 165 | if (originalAssociations == originalOwners->second.end()) | 
|  | 166 | { | 
|  | 167 | return; | 
|  | 168 | } | 
|  | 169 |  | 
|  | 170 | // Compare the new endpoints versus the original endpoints, and | 
|  | 171 | // remove any of the original ones that aren't in the new list. | 
|  | 172 | for (const auto& [originalAssocPath, originalEndpoints] : | 
|  | 173 | originalAssociations->second) | 
|  | 174 | { | 
|  | 175 | // Check if this source even still has each association that | 
|  | 176 | // was there previously, and if not, remove all of its endpoints | 
|  | 177 | // from the D-Bus endpoints property which will cause the whole | 
|  | 178 | // association path to be removed if no endpoints remain. | 
|  | 179 | auto newEndpoints = newAssociations.find(originalAssocPath); | 
|  | 180 | if (newEndpoints == newAssociations.end()) | 
|  | 181 | { | 
| Kallas, Pawel | 5b4357d | 2022-10-12 15:36:37 +0200 | [diff] [blame] | 182 | removeAssociationEndpoints(io, objectServer, originalAssocPath, | 
| Matt Spinler | e2359fb | 2019-04-05 14:11:33 -0500 | [diff] [blame] | 183 | originalEndpoints, assocMaps); | 
| Andrew Geissler | 7f1c44d | 2019-02-21 13:44:16 -0600 | [diff] [blame] | 184 | } | 
|  | 185 | else | 
|  | 186 | { | 
|  | 187 | // The association is still there.  Check if the endpoints | 
|  | 188 | // changed. | 
|  | 189 | boost::container::flat_set<std::string> toRemove; | 
|  | 190 |  | 
| Brad Bishop | 1f62380 | 2022-05-31 18:22:10 -0400 | [diff] [blame] | 191 | for (const auto& originalEndpoint : originalEndpoints) | 
| Andrew Geissler | 7f1c44d | 2019-02-21 13:44:16 -0600 | [diff] [blame] | 192 | { | 
|  | 193 | if (std::find(newEndpoints->second.begin(), | 
| Patrick Williams | 9052ebd | 2024-08-16 15:22:16 -0400 | [diff] [blame] | 194 | newEndpoints->second.end(), originalEndpoint) == | 
|  | 195 | newEndpoints->second.end()) | 
| Andrew Geissler | 7f1c44d | 2019-02-21 13:44:16 -0600 | [diff] [blame] | 196 | { | 
|  | 197 | toRemove.emplace(originalEndpoint); | 
|  | 198 | } | 
|  | 199 | } | 
|  | 200 | if (!toRemove.empty()) | 
|  | 201 | { | 
| Kallas, Pawel | 5b4357d | 2022-10-12 15:36:37 +0200 | [diff] [blame] | 202 | removeAssociationEndpoints(io, objectServer, originalAssocPath, | 
| Matt Spinler | e2359fb | 2019-04-05 14:11:33 -0500 | [diff] [blame] | 203 | toRemove, assocMaps); | 
| Andrew Geissler | 7f1c44d | 2019-02-21 13:44:16 -0600 | [diff] [blame] | 204 | } | 
|  | 205 | } | 
|  | 206 | } | 
|  | 207 | } | 
| Andrew Geissler | 4511b33 | 2019-02-21 15:40:40 -0600 | [diff] [blame] | 208 |  | 
| Matt Spinler | 11401e2 | 2019-04-08 13:13:25 -0500 | [diff] [blame] | 209 | void addEndpointsToAssocIfaces( | 
| Kallas, Pawel | 5b4357d | 2022-10-12 15:36:37 +0200 | [diff] [blame] | 210 | boost::asio::io_context& io, sdbusplus::asio::object_server& objectServer, | 
|  | 211 | const std::string& assocPath, | 
| Matt Spinler | 11401e2 | 2019-04-08 13:13:25 -0500 | [diff] [blame] | 212 | const boost::container::flat_set<std::string>& endpointPaths, | 
|  | 213 | AssociationMaps& assocMaps) | 
|  | 214 | { | 
|  | 215 | auto& iface = assocMaps.ifaces[assocPath]; | 
| Matt Spinler | 11401e2 | 2019-04-08 13:13:25 -0500 | [diff] [blame] | 216 | auto& endpoints = std::get<endpointsPos>(iface); | 
|  | 217 |  | 
|  | 218 | // Only add new endpoints | 
| Brad Bishop | 1f62380 | 2022-05-31 18:22:10 -0400 | [diff] [blame] | 219 | for (const auto& e : endpointPaths) | 
| Matt Spinler | 11401e2 | 2019-04-08 13:13:25 -0500 | [diff] [blame] | 220 | { | 
|  | 221 | if (std::find(endpoints.begin(), endpoints.end(), e) == endpoints.end()) | 
|  | 222 | { | 
|  | 223 | endpoints.push_back(e); | 
|  | 224 | } | 
|  | 225 | } | 
| Kallas, Pawel | 5b4357d | 2022-10-12 15:36:37 +0200 | [diff] [blame] | 226 | scheduleUpdateEndpointsOnDbus(io, objectServer, assocPath, assocMaps); | 
| Matt Spinler | 11401e2 | 2019-04-08 13:13:25 -0500 | [diff] [blame] | 227 | } | 
|  | 228 |  | 
| Patrick Williams | 9052ebd | 2024-08-16 15:22:16 -0400 | [diff] [blame] | 229 | void associationChanged( | 
|  | 230 | boost::asio::io_context& io, sdbusplus::asio::object_server& objectServer, | 
|  | 231 | const std::vector<Association>& associations, const std::string& path, | 
|  | 232 | const std::string& owner, const InterfaceMapType& interfaceMap, | 
|  | 233 | AssociationMaps& assocMaps) | 
| Andrew Geissler | 4511b33 | 2019-02-21 15:40:40 -0600 | [diff] [blame] | 234 | { | 
|  | 235 | AssociationPaths objects; | 
|  | 236 |  | 
|  | 237 | for (const Association& association : associations) | 
|  | 238 | { | 
|  | 239 | std::string forward; | 
|  | 240 | std::string reverse; | 
| Brad Bishop | 1f62380 | 2022-05-31 18:22:10 -0400 | [diff] [blame] | 241 | std::string objectPath; | 
|  | 242 | std::tie(forward, reverse, objectPath) = association; | 
| Andrew Geissler | 4511b33 | 2019-02-21 15:40:40 -0600 | [diff] [blame] | 243 |  | 
| Brad Bishop | 1f62380 | 2022-05-31 18:22:10 -0400 | [diff] [blame] | 244 | if (objectPath.empty()) | 
| Andrew Geissler | 0a560a5 | 2019-03-22 10:59:07 -0500 | [diff] [blame] | 245 | { | 
|  | 246 | std::cerr << "Found invalid association on path " << path << "\n"; | 
|  | 247 | continue; | 
|  | 248 | } | 
| Matt Spinler | e0b0e3a | 2019-04-08 10:39:23 -0500 | [diff] [blame] | 249 |  | 
|  | 250 | // Can't create this association if the endpoint isn't on D-Bus. | 
| Brad Bishop | 1f62380 | 2022-05-31 18:22:10 -0400 | [diff] [blame] | 251 | if (interfaceMap.find(objectPath) == interfaceMap.end()) | 
| Matt Spinler | e0b0e3a | 2019-04-08 10:39:23 -0500 | [diff] [blame] | 252 | { | 
| Brad Bishop | 1f62380 | 2022-05-31 18:22:10 -0400 | [diff] [blame] | 253 | addPendingAssociation(objectPath, reverse, path, forward, owner, | 
| Matt Spinler | e0b0e3a | 2019-04-08 10:39:23 -0500 | [diff] [blame] | 254 | assocMaps); | 
|  | 255 | continue; | 
|  | 256 | } | 
|  | 257 |  | 
| Brad Bishop | 1f62380 | 2022-05-31 18:22:10 -0400 | [diff] [blame] | 258 | if (!forward.empty()) | 
| Andrew Geissler | 4511b33 | 2019-02-21 15:40:40 -0600 | [diff] [blame] | 259 | { | 
| Brad Bishop | 1f62380 | 2022-05-31 18:22:10 -0400 | [diff] [blame] | 260 | objects[path + "/" + forward].emplace(objectPath); | 
| Andrew Geissler | 4511b33 | 2019-02-21 15:40:40 -0600 | [diff] [blame] | 261 | } | 
| Brad Bishop | 1f62380 | 2022-05-31 18:22:10 -0400 | [diff] [blame] | 262 | if (!reverse.empty()) | 
| Andrew Geissler | 4511b33 | 2019-02-21 15:40:40 -0600 | [diff] [blame] | 263 | { | 
| Brad Bishop | 1f62380 | 2022-05-31 18:22:10 -0400 | [diff] [blame] | 264 | objects[objectPath + "/" + reverse].emplace(path); | 
| Andrew Geissler | 4511b33 | 2019-02-21 15:40:40 -0600 | [diff] [blame] | 265 | } | 
|  | 266 | } | 
|  | 267 | for (const auto& object : objects) | 
|  | 268 | { | 
| Kallas, Pawel | 5b4357d | 2022-10-12 15:36:37 +0200 | [diff] [blame] | 269 | addEndpointsToAssocIfaces(io, objectServer, object.first, object.second, | 
| Matt Spinler | 11401e2 | 2019-04-08 13:13:25 -0500 | [diff] [blame] | 270 | assocMaps); | 
| Andrew Geissler | 4511b33 | 2019-02-21 15:40:40 -0600 | [diff] [blame] | 271 | } | 
|  | 272 |  | 
|  | 273 | // Check for endpoints being removed instead of added | 
| Kallas, Pawel | 5b4357d | 2022-10-12 15:36:37 +0200 | [diff] [blame] | 274 | checkAssociationEndpointRemoves(io, path, owner, objects, objectServer, | 
| Matt Spinler | e2359fb | 2019-04-05 14:11:33 -0500 | [diff] [blame] | 275 | assocMaps); | 
| Andrew Geissler | 4511b33 | 2019-02-21 15:40:40 -0600 | [diff] [blame] | 276 |  | 
| Matt Spinler | e0b0e3a | 2019-04-08 10:39:23 -0500 | [diff] [blame] | 277 | if (!objects.empty()) | 
| Andrew Geissler | 4511b33 | 2019-02-21 15:40:40 -0600 | [diff] [blame] | 278 | { | 
| Matt Spinler | e0b0e3a | 2019-04-08 10:39:23 -0500 | [diff] [blame] | 279 | // Update associationOwners with the latest info | 
|  | 280 | auto a = assocMaps.owners.find(path); | 
|  | 281 | if (a != assocMaps.owners.end()) | 
| Andrew Geissler | 4511b33 | 2019-02-21 15:40:40 -0600 | [diff] [blame] | 282 | { | 
| Matt Spinler | e0b0e3a | 2019-04-08 10:39:23 -0500 | [diff] [blame] | 283 | auto o = a->second.find(owner); | 
|  | 284 | if (o != a->second.end()) | 
|  | 285 | { | 
|  | 286 | o->second = std::move(objects); | 
|  | 287 | } | 
|  | 288 | else | 
|  | 289 | { | 
|  | 290 | a->second.emplace(owner, std::move(objects)); | 
|  | 291 | } | 
| Andrew Geissler | 4511b33 | 2019-02-21 15:40:40 -0600 | [diff] [blame] | 292 | } | 
|  | 293 | else | 
|  | 294 | { | 
| Matt Spinler | e0b0e3a | 2019-04-08 10:39:23 -0500 | [diff] [blame] | 295 | boost::container::flat_map<std::string, AssociationPaths> owners; | 
|  | 296 | owners.emplace(owner, std::move(objects)); | 
|  | 297 | assocMaps.owners.emplace(path, owners); | 
| Andrew Geissler | 4511b33 | 2019-02-21 15:40:40 -0600 | [diff] [blame] | 298 | } | 
|  | 299 | } | 
| Matt Spinler | e0b0e3a | 2019-04-08 10:39:23 -0500 | [diff] [blame] | 300 | } | 
|  | 301 |  | 
| Patrick Williams | 9052ebd | 2024-08-16 15:22:16 -0400 | [diff] [blame] | 302 | void addPendingAssociation( | 
|  | 303 | const std::string& objectPath, const std::string& type, | 
|  | 304 | const std::string& endpointPath, const std::string& endpointType, | 
|  | 305 | const std::string& owner, AssociationMaps& assocMaps) | 
| Matt Spinler | e0b0e3a | 2019-04-08 10:39:23 -0500 | [diff] [blame] | 306 | { | 
|  | 307 | Association assoc{type, endpointType, endpointPath}; | 
|  | 308 |  | 
|  | 309 | auto p = assocMaps.pending.find(objectPath); | 
|  | 310 | if (p == assocMaps.pending.end()) | 
|  | 311 | { | 
|  | 312 | ExistingEndpoints ee; | 
|  | 313 | ee.emplace_back(owner, std::move(assoc)); | 
|  | 314 | assocMaps.pending.emplace(objectPath, std::move(ee)); | 
|  | 315 | } | 
| Andrew Geissler | 4511b33 | 2019-02-21 15:40:40 -0600 | [diff] [blame] | 316 | else | 
|  | 317 | { | 
| Matt Spinler | e0b0e3a | 2019-04-08 10:39:23 -0500 | [diff] [blame] | 318 | // Already waiting on this path for another association, | 
|  | 319 | // so just add this endpoint and owner. | 
|  | 320 | auto& endpoints = p->second; | 
| Patrick Williams | 9052ebd | 2024-08-16 15:22:16 -0400 | [diff] [blame] | 321 | auto e = | 
|  | 322 | std::find_if(endpoints.begin(), endpoints.end(), | 
|  | 323 | [&assoc, &owner](const auto& endpoint) { | 
|  | 324 | return (std::get<ownerPos>(endpoint) == owner) && | 
|  | 325 | (std::get<assocPos>(endpoint) == assoc); | 
|  | 326 | }); | 
| Matt Spinler | e0b0e3a | 2019-04-08 10:39:23 -0500 | [diff] [blame] | 327 | if (e == endpoints.end()) | 
|  | 328 | { | 
|  | 329 | endpoints.emplace_back(owner, std::move(assoc)); | 
|  | 330 | } | 
| Andrew Geissler | 4511b33 | 2019-02-21 15:40:40 -0600 | [diff] [blame] | 331 | } | 
|  | 332 | } | 
| Matt Spinler | cb9bcdb | 2019-04-08 10:58:49 -0500 | [diff] [blame] | 333 |  | 
|  | 334 | void removeFromPendingAssociations(const std::string& endpointPath, | 
|  | 335 | AssociationMaps& assocMaps) | 
|  | 336 | { | 
|  | 337 | auto assoc = assocMaps.pending.begin(); | 
|  | 338 | while (assoc != assocMaps.pending.end()) | 
|  | 339 | { | 
|  | 340 | auto endpoint = assoc->second.begin(); | 
|  | 341 | while (endpoint != assoc->second.end()) | 
|  | 342 | { | 
|  | 343 | auto& e = std::get<assocPos>(*endpoint); | 
|  | 344 | if (std::get<reversePathPos>(e) == endpointPath) | 
|  | 345 | { | 
|  | 346 | endpoint = assoc->second.erase(endpoint); | 
|  | 347 | continue; | 
|  | 348 | } | 
|  | 349 |  | 
|  | 350 | endpoint++; | 
|  | 351 | } | 
|  | 352 |  | 
|  | 353 | if (assoc->second.empty()) | 
|  | 354 | { | 
|  | 355 | assoc = assocMaps.pending.erase(assoc); | 
|  | 356 | continue; | 
|  | 357 | } | 
|  | 358 |  | 
|  | 359 | assoc++; | 
|  | 360 | } | 
|  | 361 | } | 
| Matt Spinler | 11401e2 | 2019-04-08 13:13:25 -0500 | [diff] [blame] | 362 |  | 
| Patrick Williams | 9052ebd | 2024-08-16 15:22:16 -0400 | [diff] [blame] | 363 | void addSingleAssociation( | 
|  | 364 | boost::asio::io_context& io, sdbusplus::asio::object_server& server, | 
|  | 365 | const std::string& assocPath, const std::string& endpoint, | 
|  | 366 | const std::string& owner, const std::string& ownerPath, | 
|  | 367 | AssociationMaps& assocMaps) | 
| Matt Spinler | 11401e2 | 2019-04-08 13:13:25 -0500 | [diff] [blame] | 368 | { | 
|  | 369 | boost::container::flat_set<std::string> endpoints{endpoint}; | 
|  | 370 |  | 
| Kallas, Pawel | 5b4357d | 2022-10-12 15:36:37 +0200 | [diff] [blame] | 371 | addEndpointsToAssocIfaces(io, server, assocPath, endpoints, assocMaps); | 
| Matt Spinler | 11401e2 | 2019-04-08 13:13:25 -0500 | [diff] [blame] | 372 |  | 
|  | 373 | AssociationPaths objects; | 
|  | 374 | boost::container::flat_set e{endpoint}; | 
|  | 375 | objects.emplace(assocPath, e); | 
|  | 376 |  | 
|  | 377 | auto a = assocMaps.owners.find(ownerPath); | 
|  | 378 | if (a != assocMaps.owners.end()) | 
|  | 379 | { | 
|  | 380 | auto o = a->second.find(owner); | 
|  | 381 | if (o != a->second.end()) | 
|  | 382 | { | 
|  | 383 | auto p = o->second.find(assocPath); | 
|  | 384 | if (p != o->second.end()) | 
|  | 385 | { | 
|  | 386 | p->second.emplace(endpoint); | 
|  | 387 | } | 
|  | 388 | else | 
|  | 389 | { | 
|  | 390 | o->second.emplace(assocPath, e); | 
|  | 391 | } | 
|  | 392 | } | 
|  | 393 | else | 
|  | 394 | { | 
|  | 395 | a->second.emplace(owner, std::move(objects)); | 
|  | 396 | } | 
|  | 397 | } | 
|  | 398 | else | 
|  | 399 | { | 
|  | 400 | boost::container::flat_map<std::string, AssociationPaths> owners; | 
|  | 401 | owners.emplace(owner, std::move(objects)); | 
|  | 402 | assocMaps.owners.emplace(endpoint, owners); | 
|  | 403 | } | 
|  | 404 | } | 
|  | 405 |  | 
| Patrick Williams | 9052ebd | 2024-08-16 15:22:16 -0400 | [diff] [blame] | 406 | void checkIfPendingAssociation( | 
|  | 407 | boost::asio::io_context& io, const std::string& objectPath, | 
|  | 408 | const InterfaceMapType& interfaceMap, AssociationMaps& assocMaps, | 
|  | 409 | sdbusplus::asio::object_server& server) | 
| Matt Spinler | 11401e2 | 2019-04-08 13:13:25 -0500 | [diff] [blame] | 410 | { | 
|  | 411 | auto pending = assocMaps.pending.find(objectPath); | 
|  | 412 | if (pending == assocMaps.pending.end()) | 
|  | 413 | { | 
|  | 414 | return; | 
|  | 415 | } | 
|  | 416 |  | 
|  | 417 | if (interfaceMap.find(objectPath) == interfaceMap.end()) | 
|  | 418 | { | 
|  | 419 | return; | 
|  | 420 | } | 
|  | 421 |  | 
|  | 422 | auto endpoint = pending->second.begin(); | 
|  | 423 |  | 
|  | 424 | while (endpoint != pending->second.end()) | 
|  | 425 | { | 
|  | 426 | const auto& e = std::get<assocPos>(*endpoint); | 
|  | 427 |  | 
|  | 428 | // Ensure the other side of the association still exists | 
|  | 429 | if (interfaceMap.find(std::get<reversePathPos>(e)) == | 
|  | 430 | interfaceMap.end()) | 
|  | 431 | { | 
|  | 432 | endpoint++; | 
|  | 433 | continue; | 
|  | 434 | } | 
|  | 435 |  | 
|  | 436 | // Add both sides of the association: | 
|  | 437 | //  objectPath/forwardType and reversePath/reverseType | 
|  | 438 | // | 
|  | 439 | // The ownerPath is the reversePath - i.e. the endpoint that | 
|  | 440 | // is on D-Bus and owns the org.openbmc.Associations iface. | 
|  | 441 | // | 
|  | 442 | const auto& ownerPath = std::get<reversePathPos>(e); | 
|  | 443 | const auto& owner = std::get<ownerPos>(*endpoint); | 
|  | 444 |  | 
|  | 445 | auto assocPath = objectPath + '/' + std::get<forwardTypePos>(e); | 
|  | 446 | auto endpointPath = ownerPath; | 
|  | 447 |  | 
| Lei YU | b89c661 | 2021-07-22 15:59:52 +0800 | [diff] [blame] | 448 | try | 
|  | 449 | { | 
| Kallas, Pawel | 5b4357d | 2022-10-12 15:36:37 +0200 | [diff] [blame] | 450 | addSingleAssociation(io, server, assocPath, endpointPath, owner, | 
| Lei YU | b89c661 | 2021-07-22 15:59:52 +0800 | [diff] [blame] | 451 | ownerPath, assocMaps); | 
| Matt Spinler | 11401e2 | 2019-04-08 13:13:25 -0500 | [diff] [blame] | 452 |  | 
| Lei YU | b89c661 | 2021-07-22 15:59:52 +0800 | [diff] [blame] | 453 | // Now the reverse direction (still the same owner and ownerPath) | 
|  | 454 | assocPath = endpointPath + '/' + std::get<reverseTypePos>(e); | 
|  | 455 | endpointPath = objectPath; | 
| Kallas, Pawel | 5b4357d | 2022-10-12 15:36:37 +0200 | [diff] [blame] | 456 | addSingleAssociation(io, server, assocPath, endpointPath, owner, | 
| Lei YU | b89c661 | 2021-07-22 15:59:52 +0800 | [diff] [blame] | 457 | ownerPath, assocMaps); | 
|  | 458 | } | 
| Patrick Williams | cc8070b | 2022-07-22 19:26:55 -0500 | [diff] [blame] | 459 | catch (const sdbusplus::exception_t& e) | 
| Lei YU | b89c661 | 2021-07-22 15:59:52 +0800 | [diff] [blame] | 460 | { | 
|  | 461 | // In some case the interface could not be created on DBus and an | 
|  | 462 | // exception is thrown. mapper has no control of the interface/path | 
|  | 463 | // of the associations, so it has to catch the error and drop the | 
|  | 464 | // association request. | 
| Brad Bishop | 1f62380 | 2022-05-31 18:22:10 -0400 | [diff] [blame] | 465 | std::cerr << "Error adding association: assocPath " << assocPath | 
|  | 466 | << ", endpointPath " << endpointPath | 
|  | 467 | << ", what: " << e.what() << "\n"; | 
| Lei YU | b89c661 | 2021-07-22 15:59:52 +0800 | [diff] [blame] | 468 | } | 
| Matt Spinler | 11401e2 | 2019-04-08 13:13:25 -0500 | [diff] [blame] | 469 |  | 
|  | 470 | // Not pending anymore | 
|  | 471 | endpoint = pending->second.erase(endpoint); | 
|  | 472 | } | 
|  | 473 |  | 
|  | 474 | if (pending->second.empty()) | 
|  | 475 | { | 
|  | 476 | assocMaps.pending.erase(objectPath); | 
|  | 477 | } | 
|  | 478 | } | 
| Matt Spinler | 7f8fd1f | 2019-04-08 15:21:59 -0500 | [diff] [blame] | 479 |  | 
|  | 480 | void findAssociations(const std::string& endpointPath, | 
|  | 481 | AssociationMaps& assocMaps, | 
|  | 482 | FindAssocResults& associationData) | 
|  | 483 | { | 
|  | 484 | for (const auto& [sourcePath, owners] : assocMaps.owners) | 
|  | 485 | { | 
|  | 486 | for (const auto& [owner, assocs] : owners) | 
|  | 487 | { | 
|  | 488 | for (const auto& [assocPath, endpoints] : assocs) | 
|  | 489 | { | 
|  | 490 | if (std::find(endpoints.begin(), endpoints.end(), | 
|  | 491 | endpointPath) != endpoints.end()) | 
|  | 492 | { | 
|  | 493 | // assocPath is <path>/<type> which tells us what is on the | 
|  | 494 | // other side of the association. | 
|  | 495 | auto pos = assocPath.rfind('/'); | 
|  | 496 | auto otherPath = assocPath.substr(0, pos); | 
|  | 497 | auto otherType = assocPath.substr(pos + 1); | 
|  | 498 |  | 
|  | 499 | // Now we need to find the endpointPath/<type> -> | 
|  | 500 | // [otherPath] entry so that we can get the type for | 
|  | 501 | // endpointPath's side of the assoc.  Do this by finding | 
|  | 502 | // otherPath as an endpoint, and also checking for | 
|  | 503 | // 'endpointPath/*' as the key. | 
|  | 504 | auto a = std::find_if( | 
|  | 505 | assocs.begin(), assocs.end(), | 
|  | 506 | [&endpointPath, &otherPath](const auto& ap) { | 
| Patrick Williams | 9052ebd | 2024-08-16 15:22:16 -0400 | [diff] [blame] | 507 | const auto& endpoints = ap.second; | 
|  | 508 | auto endpoint = std::find( | 
|  | 509 | endpoints.begin(), endpoints.end(), otherPath); | 
|  | 510 | if (endpoint != endpoints.end()) | 
|  | 511 | { | 
|  | 512 | return ap.first.starts_with(endpointPath + '/'); | 
|  | 513 | } | 
|  | 514 | return false; | 
|  | 515 | }); | 
| Matt Spinler | 7f8fd1f | 2019-04-08 15:21:59 -0500 | [diff] [blame] | 516 |  | 
|  | 517 | if (a != assocs.end()) | 
|  | 518 | { | 
|  | 519 | // Pull out the type from endpointPath/<type> | 
|  | 520 | pos = a->first.rfind('/'); | 
|  | 521 | auto thisType = a->first.substr(pos + 1); | 
|  | 522 |  | 
|  | 523 | // Now we know the full association: | 
|  | 524 | // endpointPath/thisType -> otherPath/otherType | 
|  | 525 | Association association{thisType, otherType, otherPath}; | 
|  | 526 | associationData.emplace_back(owner, association); | 
|  | 527 | } | 
|  | 528 | } | 
|  | 529 | } | 
|  | 530 | } | 
|  | 531 | } | 
|  | 532 | } | 
| Matt Spinler | 9c3d285 | 2019-04-08 15:57:19 -0500 | [diff] [blame] | 533 |  | 
|  | 534 | /** @brief Remove an endpoint for a particular association from D-Bus. | 
|  | 535 | * | 
|  | 536 | * If the last endpoint is gone, remove the whole association interface, | 
|  | 537 | * otherwise just update the D-Bus endpoints property. | 
|  | 538 | * | 
|  | 539 | * @param[in] assocPath     - the association path | 
|  | 540 | * @param[in] endpointPath  - the endpoint path to find and remove | 
|  | 541 | * @param[in,out] assocMaps - the association maps | 
|  | 542 | * @param[in,out] server    - sdbus system object | 
|  | 543 | */ | 
| Patrick Williams | 9052ebd | 2024-08-16 15:22:16 -0400 | [diff] [blame] | 544 | void removeAssociationIfacesEntry( | 
|  | 545 | boost::asio::io_context& io, const std::string& assocPath, | 
|  | 546 | const std::string& endpointPath, AssociationMaps& assocMaps, | 
|  | 547 | sdbusplus::asio::object_server& server) | 
| Matt Spinler | 9c3d285 | 2019-04-08 15:57:19 -0500 | [diff] [blame] | 548 | { | 
|  | 549 | auto assoc = assocMaps.ifaces.find(assocPath); | 
|  | 550 | if (assoc != assocMaps.ifaces.end()) | 
|  | 551 | { | 
|  | 552 | auto& endpoints = std::get<endpointsPos>(assoc->second); | 
|  | 553 | auto e = std::find(endpoints.begin(), endpoints.end(), endpointPath); | 
|  | 554 | if (e != endpoints.end()) | 
|  | 555 | { | 
|  | 556 | endpoints.erase(e); | 
|  | 557 |  | 
| Kallas, Pawel | 5b4357d | 2022-10-12 15:36:37 +0200 | [diff] [blame] | 558 | scheduleUpdateEndpointsOnDbus(io, server, assocPath, assocMaps); | 
| Matt Spinler | 9c3d285 | 2019-04-08 15:57:19 -0500 | [diff] [blame] | 559 | } | 
|  | 560 | } | 
|  | 561 | } | 
|  | 562 |  | 
|  | 563 | /** @brief Remove an endpoint from the association owners map. | 
|  | 564 | * | 
|  | 565 | * For a specific association path and owner, remove the endpoint. | 
|  | 566 | * Remove all remaining artifacts of that endpoint in the owners map | 
|  | 567 | * based on what frees up after the erase. | 
|  | 568 | * | 
|  | 569 | * @param[in] assocPath     - the association object path | 
|  | 570 | * @param[in] endpointPath  - the endpoint object path | 
|  | 571 | * @param[in] owner         - the owner of the association | 
|  | 572 | * @param[in,out] assocMaps - the association maps | 
| Matt Spinler | 9c3d285 | 2019-04-08 15:57:19 -0500 | [diff] [blame] | 573 | */ | 
| Patrick Williams | 9052ebd | 2024-08-16 15:22:16 -0400 | [diff] [blame] | 574 | void removeAssociationOwnersEntry( | 
|  | 575 | const std::string& assocPath, const std::string& endpointPath, | 
|  | 576 | const std::string& owner, AssociationMaps& assocMaps) | 
| Matt Spinler | 9c3d285 | 2019-04-08 15:57:19 -0500 | [diff] [blame] | 577 | { | 
|  | 578 | auto sources = assocMaps.owners.begin(); | 
|  | 579 | while (sources != assocMaps.owners.end()) | 
|  | 580 | { | 
|  | 581 | auto owners = sources->second.find(owner); | 
|  | 582 | if (owners != sources->second.end()) | 
|  | 583 | { | 
|  | 584 | auto entry = owners->second.find(assocPath); | 
|  | 585 | if (entry != owners->second.end()) | 
|  | 586 | { | 
|  | 587 | auto e = std::find(entry->second.begin(), entry->second.end(), | 
|  | 588 | endpointPath); | 
|  | 589 | if (e != entry->second.end()) | 
|  | 590 | { | 
|  | 591 | entry->second.erase(e); | 
|  | 592 | if (entry->second.empty()) | 
|  | 593 | { | 
|  | 594 | owners->second.erase(entry); | 
|  | 595 | } | 
|  | 596 | } | 
|  | 597 | } | 
|  | 598 |  | 
|  | 599 | if (owners->second.empty()) | 
|  | 600 | { | 
|  | 601 | sources->second.erase(owners); | 
|  | 602 | } | 
|  | 603 | } | 
|  | 604 |  | 
|  | 605 | if (sources->second.empty()) | 
|  | 606 | { | 
|  | 607 | sources = assocMaps.owners.erase(sources); | 
|  | 608 | continue; | 
|  | 609 | } | 
|  | 610 | sources++; | 
|  | 611 | } | 
|  | 612 | } | 
|  | 613 |  | 
| Patrick Williams | 9052ebd | 2024-08-16 15:22:16 -0400 | [diff] [blame] | 614 | void moveAssociationToPending( | 
|  | 615 | boost::asio::io_context& io, const std::string& endpointPath, | 
|  | 616 | AssociationMaps& assocMaps, sdbusplus::asio::object_server& server) | 
| Matt Spinler | 9c3d285 | 2019-04-08 15:57:19 -0500 | [diff] [blame] | 617 | { | 
|  | 618 | FindAssocResults associationData; | 
|  | 619 |  | 
|  | 620 | // Check which associations this path is an endpoint of, and | 
|  | 621 | // then add them to the pending associations map and remove | 
|  | 622 | // the associations objects. | 
|  | 623 | findAssociations(endpointPath, assocMaps, associationData); | 
|  | 624 |  | 
|  | 625 | for (const auto& [owner, association] : associationData) | 
|  | 626 | { | 
|  | 627 | const auto& forwardPath = endpointPath; | 
|  | 628 | const auto& forwardType = std::get<forwardTypePos>(association); | 
|  | 629 | const auto& reversePath = std::get<reversePathPos>(association); | 
|  | 630 | const auto& reverseType = std::get<reverseTypePos>(association); | 
|  | 631 |  | 
|  | 632 | addPendingAssociation(forwardPath, forwardType, reversePath, | 
|  | 633 | reverseType, owner, assocMaps); | 
|  | 634 |  | 
|  | 635 | // Remove both sides of the association from assocMaps.ifaces | 
| Kallas, Pawel | 5b4357d | 2022-10-12 15:36:37 +0200 | [diff] [blame] | 636 | removeAssociationIfacesEntry(io, forwardPath + '/' + forwardType, | 
| Matt Spinler | 9c3d285 | 2019-04-08 15:57:19 -0500 | [diff] [blame] | 637 | reversePath, assocMaps, server); | 
| Kallas, Pawel | 5b4357d | 2022-10-12 15:36:37 +0200 | [diff] [blame] | 638 | removeAssociationIfacesEntry(io, reversePath + '/' + reverseType, | 
| Matt Spinler | 9c3d285 | 2019-04-08 15:57:19 -0500 | [diff] [blame] | 639 | forwardPath, assocMaps, server); | 
|  | 640 |  | 
|  | 641 | // Remove both sides of the association from assocMaps.owners | 
|  | 642 | removeAssociationOwnersEntry(forwardPath + '/' + forwardType, | 
| Brad Bishop | a664690 | 2022-06-02 19:05:34 -0400 | [diff] [blame] | 643 | reversePath, owner, assocMaps); | 
| Matt Spinler | 9c3d285 | 2019-04-08 15:57:19 -0500 | [diff] [blame] | 644 | removeAssociationOwnersEntry(reversePath + '/' + reverseType, | 
| Brad Bishop | a664690 | 2022-06-02 19:05:34 -0400 | [diff] [blame] | 645 | forwardPath, owner, assocMaps); | 
| Matt Spinler | 9c3d285 | 2019-04-08 15:57:19 -0500 | [diff] [blame] | 646 | } | 
|  | 647 | } |