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 47 void removeAssociationEndpoints( 48 sdbusplus::asio::object_server& objectServer, const std::string& assocPath, 49 const boost::container::flat_set<std::string>& endpointsToRemove, 50 AssociationMaps& assocMaps) 51 { 52 auto assoc = assocMaps.ifaces.find(assocPath); 53 if (assoc == assocMaps.ifaces.end()) 54 { 55 return; 56 } 57 58 auto& endpointsInDBus = std::get<endpointsPos>(assoc->second); 59 60 for (const auto& endpointToRemove : endpointsToRemove) 61 { 62 auto e = std::find(endpointsInDBus.begin(), endpointsInDBus.end(), 63 endpointToRemove); 64 65 if (e != endpointsInDBus.end()) 66 { 67 endpointsInDBus.erase(e); 68 } 69 } 70 71 if (endpointsInDBus.empty()) 72 { 73 objectServer.remove_interface(std::get<ifacePos>(assoc->second)); 74 std::get<ifacePos>(assoc->second) = nullptr; 75 std::get<endpointsPos>(assoc->second).clear(); 76 } 77 else 78 { 79 std::get<ifacePos>(assoc->second) 80 ->set_property("endpoints", endpointsInDBus); 81 } 82 } 83 84 void checkAssociationEndpointRemoves( 85 const std::string& sourcePath, const std::string& owner, 86 const AssociationPaths& newAssociations, 87 sdbusplus::asio::object_server& objectServer, AssociationMaps& assocMaps) 88 { 89 // Find the services that have associations on this path. 90 auto originalOwners = assocMaps.owners.find(sourcePath); 91 if (originalOwners == assocMaps.owners.end()) 92 { 93 return; 94 } 95 96 // Find the associations for this service 97 auto originalAssociations = originalOwners->second.find(owner); 98 if (originalAssociations == originalOwners->second.end()) 99 { 100 return; 101 } 102 103 // Compare the new endpoints versus the original endpoints, and 104 // remove any of the original ones that aren't in the new list. 105 for (const auto& [originalAssocPath, originalEndpoints] : 106 originalAssociations->second) 107 { 108 // Check if this source even still has each association that 109 // was there previously, and if not, remove all of its endpoints 110 // from the D-Bus endpoints property which will cause the whole 111 // association path to be removed if no endpoints remain. 112 auto newEndpoints = newAssociations.find(originalAssocPath); 113 if (newEndpoints == newAssociations.end()) 114 { 115 removeAssociationEndpoints(objectServer, originalAssocPath, 116 originalEndpoints, assocMaps); 117 } 118 else 119 { 120 // The association is still there. Check if the endpoints 121 // changed. 122 boost::container::flat_set<std::string> toRemove; 123 124 for (auto& originalEndpoint : originalEndpoints) 125 { 126 if (std::find(newEndpoints->second.begin(), 127 newEndpoints->second.end(), 128 originalEndpoint) == newEndpoints->second.end()) 129 { 130 toRemove.emplace(originalEndpoint); 131 } 132 } 133 if (!toRemove.empty()) 134 { 135 removeAssociationEndpoints(objectServer, originalAssocPath, 136 toRemove, assocMaps); 137 } 138 } 139 } 140 } 141 142 void associationChanged(sdbusplus::asio::object_server& objectServer, 143 const std::vector<Association>& associations, 144 const std::string& path, const std::string& owner, 145 const interface_map_type& interfaceMap, 146 AssociationMaps& assocMaps) 147 { 148 AssociationPaths objects; 149 150 for (const Association& association : associations) 151 { 152 std::string forward; 153 std::string reverse; 154 std::string endpoint; 155 std::tie(forward, reverse, endpoint) = association; 156 157 if (endpoint.empty()) 158 { 159 std::cerr << "Found invalid association on path " << path << "\n"; 160 continue; 161 } 162 163 // Can't create this association if the endpoint isn't on D-Bus. 164 if (interfaceMap.find(endpoint) == interfaceMap.end()) 165 { 166 addPendingAssociation(endpoint, reverse, path, forward, owner, 167 assocMaps); 168 continue; 169 } 170 171 if (forward.size()) 172 { 173 objects[path + "/" + forward].emplace(endpoint); 174 } 175 if (reverse.size()) 176 { 177 objects[endpoint + "/" + reverse].emplace(path); 178 } 179 } 180 for (const auto& object : objects) 181 { 182 // the mapper exposes the new association interface but intakes 183 // the old 184 185 auto& iface = assocMaps.ifaces[object.first]; 186 auto& i = std::get<ifacePos>(iface); 187 auto& endpoints = std::get<endpointsPos>(iface); 188 189 // Only add new endpoints 190 for (auto& e : object.second) 191 { 192 if (std::find(endpoints.begin(), endpoints.end(), e) == 193 endpoints.end()) 194 { 195 endpoints.push_back(e); 196 } 197 } 198 199 // If the interface already exists, only need to update 200 // the property value, otherwise create it 201 if (i) 202 { 203 i->set_property("endpoints", endpoints); 204 } 205 else 206 { 207 i = objectServer.add_interface(object.first, 208 XYZ_ASSOCIATION_INTERFACE); 209 i->register_property("endpoints", endpoints); 210 i->initialize(); 211 } 212 } 213 214 // Check for endpoints being removed instead of added 215 checkAssociationEndpointRemoves(path, owner, objects, objectServer, 216 assocMaps); 217 218 if (!objects.empty()) 219 { 220 // Update associationOwners with the latest info 221 auto a = assocMaps.owners.find(path); 222 if (a != assocMaps.owners.end()) 223 { 224 auto o = a->second.find(owner); 225 if (o != a->second.end()) 226 { 227 o->second = std::move(objects); 228 } 229 else 230 { 231 a->second.emplace(owner, std::move(objects)); 232 } 233 } 234 else 235 { 236 boost::container::flat_map<std::string, AssociationPaths> owners; 237 owners.emplace(owner, std::move(objects)); 238 assocMaps.owners.emplace(path, owners); 239 } 240 } 241 } 242 243 void addPendingAssociation(const std::string& objectPath, 244 const std::string& type, 245 const std::string& endpointPath, 246 const std::string& endpointType, 247 const std::string& owner, AssociationMaps& assocMaps) 248 { 249 Association assoc{type, endpointType, endpointPath}; 250 251 auto p = assocMaps.pending.find(objectPath); 252 if (p == assocMaps.pending.end()) 253 { 254 ExistingEndpoints ee; 255 ee.emplace_back(owner, std::move(assoc)); 256 assocMaps.pending.emplace(objectPath, std::move(ee)); 257 } 258 else 259 { 260 // Already waiting on this path for another association, 261 // so just add this endpoint and owner. 262 auto& endpoints = p->second; 263 auto e = 264 std::find_if(endpoints.begin(), endpoints.end(), 265 [&assoc, &owner](const auto& endpoint) { 266 return (std::get<ownerPos>(endpoint) == owner) && 267 (std::get<assocPos>(endpoint) == assoc); 268 }); 269 if (e == endpoints.end()) 270 { 271 endpoints.emplace_back(owner, std::move(assoc)); 272 } 273 } 274 } 275