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