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