1 #include "associations.hpp" 2 3 #include <iostream> 4 5 void removeAssociation(const std::string& sourcePath, const std::string& owner, 6 sdbusplus::asio::object_server& server, 7 AssociationMaps& assocMaps) 8 { 9 // Use associationOwners to find the association paths and endpoints 10 // that the passed in object path and service own. Remove all of 11 // these endpoints from the actual association D-Bus objects, and if 12 // the endpoints property is then empty, the whole association object 13 // can be removed. Note there can be multiple services that own an 14 // association, and also that sourcePath is the path of the object 15 // that contains the org.openbmc.Associations interface and not the 16 // association path itself. 17 18 // Find the services that have associations for this object path 19 auto owners = assocMaps.owners.find(sourcePath); 20 if (owners == assocMaps.owners.end()) 21 { 22 return; 23 } 24 25 // Find the association paths and endpoints owned by this object 26 // path for this service. 27 auto assocs = owners->second.find(owner); 28 if (assocs == owners->second.end()) 29 { 30 return; 31 } 32 33 for (const auto& [assocPath, endpointsToRemove] : assocs->second) 34 { 35 removeAssociationEndpoints(server, assocPath, endpointsToRemove, 36 assocMaps); 37 } 38 39 // Remove the associationOwners entries for this owning path/service. 40 owners->second.erase(assocs); 41 if (owners->second.empty()) 42 { 43 assocMaps.owners.erase(owners); 44 } 45 46 // If we were still waiting on the other side of this association to 47 // show up, cancel that wait. 48 removeFromPendingAssociations(sourcePath, assocMaps); 49 } 50 51 void removeAssociationEndpoints( 52 sdbusplus::asio::object_server& objectServer, const std::string& assocPath, 53 const boost::container::flat_set<std::string>& endpointsToRemove, 54 AssociationMaps& assocMaps) 55 { 56 auto assoc = assocMaps.ifaces.find(assocPath); 57 if (assoc == assocMaps.ifaces.end()) 58 { 59 return; 60 } 61 62 auto& endpointsInDBus = std::get<endpointsPos>(assoc->second); 63 64 for (const auto& endpointToRemove : endpointsToRemove) 65 { 66 auto e = std::find(endpointsInDBus.begin(), endpointsInDBus.end(), 67 endpointToRemove); 68 69 if (e != endpointsInDBus.end()) 70 { 71 endpointsInDBus.erase(e); 72 } 73 } 74 75 if (endpointsInDBus.empty()) 76 { 77 objectServer.remove_interface(std::get<ifacePos>(assoc->second)); 78 std::get<ifacePos>(assoc->second) = nullptr; 79 std::get<endpointsPos>(assoc->second).clear(); 80 } 81 else 82 { 83 std::get<ifacePos>(assoc->second) 84 ->set_property("endpoints", endpointsInDBus); 85 } 86 } 87 88 void checkAssociationEndpointRemoves( 89 const std::string& sourcePath, const std::string& owner, 90 const AssociationPaths& newAssociations, 91 sdbusplus::asio::object_server& objectServer, AssociationMaps& assocMaps) 92 { 93 // Find the services that have associations on this path. 94 auto originalOwners = assocMaps.owners.find(sourcePath); 95 if (originalOwners == assocMaps.owners.end()) 96 { 97 return; 98 } 99 100 // Find the associations for this service 101 auto originalAssociations = originalOwners->second.find(owner); 102 if (originalAssociations == originalOwners->second.end()) 103 { 104 return; 105 } 106 107 // Compare the new endpoints versus the original endpoints, and 108 // remove any of the original ones that aren't in the new list. 109 for (const auto& [originalAssocPath, originalEndpoints] : 110 originalAssociations->second) 111 { 112 // Check if this source even still has each association that 113 // was there previously, and if not, remove all of its endpoints 114 // from the D-Bus endpoints property which will cause the whole 115 // association path to be removed if no endpoints remain. 116 auto newEndpoints = newAssociations.find(originalAssocPath); 117 if (newEndpoints == newAssociations.end()) 118 { 119 removeAssociationEndpoints(objectServer, originalAssocPath, 120 originalEndpoints, assocMaps); 121 } 122 else 123 { 124 // The association is still there. Check if the endpoints 125 // changed. 126 boost::container::flat_set<std::string> toRemove; 127 128 for (auto& originalEndpoint : originalEndpoints) 129 { 130 if (std::find(newEndpoints->second.begin(), 131 newEndpoints->second.end(), 132 originalEndpoint) == newEndpoints->second.end()) 133 { 134 toRemove.emplace(originalEndpoint); 135 } 136 } 137 if (!toRemove.empty()) 138 { 139 removeAssociationEndpoints(objectServer, originalAssocPath, 140 toRemove, assocMaps); 141 } 142 } 143 } 144 } 145 146 void addEndpointsToAssocIfaces( 147 sdbusplus::asio::object_server& objectServer, const std::string& assocPath, 148 const boost::container::flat_set<std::string>& endpointPaths, 149 AssociationMaps& assocMaps) 150 { 151 auto& iface = assocMaps.ifaces[assocPath]; 152 auto& i = std::get<ifacePos>(iface); 153 auto& endpoints = std::get<endpointsPos>(iface); 154 155 // Only add new endpoints 156 for (auto& e : endpointPaths) 157 { 158 if (std::find(endpoints.begin(), endpoints.end(), e) == endpoints.end()) 159 { 160 endpoints.push_back(e); 161 } 162 } 163 164 // If the interface already exists, only need to update 165 // the property value, otherwise create it 166 if (i) 167 { 168 i->set_property("endpoints", endpoints); 169 } 170 else 171 { 172 i = objectServer.add_interface(assocPath, XYZ_ASSOCIATION_INTERFACE); 173 i->register_property("endpoints", endpoints); 174 i->initialize(); 175 } 176 } 177 178 void associationChanged(sdbusplus::asio::object_server& objectServer, 179 const std::vector<Association>& associations, 180 const std::string& path, const std::string& owner, 181 const interface_map_type& interfaceMap, 182 AssociationMaps& assocMaps) 183 { 184 AssociationPaths objects; 185 186 for (const Association& association : associations) 187 { 188 std::string forward; 189 std::string reverse; 190 std::string endpoint; 191 std::tie(forward, reverse, endpoint) = association; 192 193 if (endpoint.empty()) 194 { 195 std::cerr << "Found invalid association on path " << path << "\n"; 196 continue; 197 } 198 199 // Can't create this association if the endpoint isn't on D-Bus. 200 if (interfaceMap.find(endpoint) == interfaceMap.end()) 201 { 202 addPendingAssociation(endpoint, reverse, path, forward, owner, 203 assocMaps); 204 continue; 205 } 206 207 if (forward.size()) 208 { 209 objects[path + "/" + forward].emplace(endpoint); 210 } 211 if (reverse.size()) 212 { 213 objects[endpoint + "/" + reverse].emplace(path); 214 } 215 } 216 for (const auto& object : objects) 217 { 218 addEndpointsToAssocIfaces(objectServer, object.first, object.second, 219 assocMaps); 220 } 221 222 // Check for endpoints being removed instead of added 223 checkAssociationEndpointRemoves(path, owner, objects, objectServer, 224 assocMaps); 225 226 if (!objects.empty()) 227 { 228 // Update associationOwners with the latest info 229 auto a = assocMaps.owners.find(path); 230 if (a != assocMaps.owners.end()) 231 { 232 auto o = a->second.find(owner); 233 if (o != a->second.end()) 234 { 235 o->second = std::move(objects); 236 } 237 else 238 { 239 a->second.emplace(owner, std::move(objects)); 240 } 241 } 242 else 243 { 244 boost::container::flat_map<std::string, AssociationPaths> owners; 245 owners.emplace(owner, std::move(objects)); 246 assocMaps.owners.emplace(path, owners); 247 } 248 } 249 } 250 251 void addPendingAssociation(const std::string& objectPath, 252 const std::string& type, 253 const std::string& endpointPath, 254 const std::string& endpointType, 255 const std::string& owner, AssociationMaps& assocMaps) 256 { 257 Association assoc{type, endpointType, endpointPath}; 258 259 auto p = assocMaps.pending.find(objectPath); 260 if (p == assocMaps.pending.end()) 261 { 262 ExistingEndpoints ee; 263 ee.emplace_back(owner, std::move(assoc)); 264 assocMaps.pending.emplace(objectPath, std::move(ee)); 265 } 266 else 267 { 268 // Already waiting on this path for another association, 269 // so just add this endpoint and owner. 270 auto& endpoints = p->second; 271 auto e = 272 std::find_if(endpoints.begin(), endpoints.end(), 273 [&assoc, &owner](const auto& endpoint) { 274 return (std::get<ownerPos>(endpoint) == owner) && 275 (std::get<assocPos>(endpoint) == assoc); 276 }); 277 if (e == endpoints.end()) 278 { 279 endpoints.emplace_back(owner, std::move(assoc)); 280 } 281 } 282 } 283 284 void removeFromPendingAssociations(const std::string& endpointPath, 285 AssociationMaps& assocMaps) 286 { 287 auto assoc = assocMaps.pending.begin(); 288 while (assoc != assocMaps.pending.end()) 289 { 290 auto endpoint = assoc->second.begin(); 291 while (endpoint != assoc->second.end()) 292 { 293 auto& e = std::get<assocPos>(*endpoint); 294 if (std::get<reversePathPos>(e) == endpointPath) 295 { 296 endpoint = assoc->second.erase(endpoint); 297 continue; 298 } 299 300 endpoint++; 301 } 302 303 if (assoc->second.empty()) 304 { 305 assoc = assocMaps.pending.erase(assoc); 306 continue; 307 } 308 309 assoc++; 310 } 311 } 312 313 void addSingleAssociation(sdbusplus::asio::object_server& server, 314 const std::string& assocPath, 315 const std::string& endpoint, const std::string& owner, 316 const std::string& ownerPath, 317 AssociationMaps& assocMaps) 318 { 319 boost::container::flat_set<std::string> endpoints{endpoint}; 320 321 addEndpointsToAssocIfaces(server, assocPath, endpoints, assocMaps); 322 323 AssociationPaths objects; 324 boost::container::flat_set e{endpoint}; 325 objects.emplace(assocPath, e); 326 327 auto a = assocMaps.owners.find(ownerPath); 328 if (a != assocMaps.owners.end()) 329 { 330 auto o = a->second.find(owner); 331 if (o != a->second.end()) 332 { 333 auto p = o->second.find(assocPath); 334 if (p != o->second.end()) 335 { 336 p->second.emplace(endpoint); 337 } 338 else 339 { 340 o->second.emplace(assocPath, e); 341 } 342 } 343 else 344 { 345 a->second.emplace(owner, std::move(objects)); 346 } 347 } 348 else 349 { 350 boost::container::flat_map<std::string, AssociationPaths> owners; 351 owners.emplace(owner, std::move(objects)); 352 assocMaps.owners.emplace(endpoint, owners); 353 } 354 } 355 356 void checkIfPendingAssociation(const std::string& objectPath, 357 const interface_map_type& interfaceMap, 358 AssociationMaps& assocMaps, 359 sdbusplus::asio::object_server& server) 360 { 361 auto pending = assocMaps.pending.find(objectPath); 362 if (pending == assocMaps.pending.end()) 363 { 364 return; 365 } 366 367 if (interfaceMap.find(objectPath) == interfaceMap.end()) 368 { 369 return; 370 } 371 372 auto endpoint = pending->second.begin(); 373 374 while (endpoint != pending->second.end()) 375 { 376 const auto& e = std::get<assocPos>(*endpoint); 377 378 // Ensure the other side of the association still exists 379 if (interfaceMap.find(std::get<reversePathPos>(e)) == 380 interfaceMap.end()) 381 { 382 endpoint++; 383 continue; 384 } 385 386 // Add both sides of the association: 387 // objectPath/forwardType and reversePath/reverseType 388 // 389 // The ownerPath is the reversePath - i.e. the endpoint that 390 // is on D-Bus and owns the org.openbmc.Associations iface. 391 // 392 const auto& ownerPath = std::get<reversePathPos>(e); 393 const auto& owner = std::get<ownerPos>(*endpoint); 394 395 auto assocPath = objectPath + '/' + std::get<forwardTypePos>(e); 396 auto endpointPath = ownerPath; 397 398 addSingleAssociation(server, assocPath, endpointPath, owner, ownerPath, 399 assocMaps); 400 401 // Now the reverse direction (still the same owner and ownerPath) 402 assocPath = endpointPath + '/' + std::get<reverseTypePos>(e); 403 endpointPath = objectPath; 404 addSingleAssociation(server, assocPath, endpointPath, owner, ownerPath, 405 assocMaps); 406 407 // Not pending anymore 408 endpoint = pending->second.erase(endpoint); 409 } 410 411 if (pending->second.empty()) 412 { 413 assocMaps.pending.erase(objectPath); 414 } 415 } 416