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 (endpoint.empty()) 160 { 161 std::cerr << "Found invalid association on path " << path << "\n"; 162 continue; 163 } 164 if (forward.size()) 165 { 166 objects[path + "/" + forward].emplace(endpoint); 167 } 168 if (reverse.size()) 169 { 170 objects[endpoint + "/" + reverse].emplace(path); 171 } 172 } 173 for (const auto& object : objects) 174 { 175 // the mapper exposes the new association interface but intakes 176 // the old 177 178 auto& iface = assocInterfaces[object.first]; 179 auto& i = std::get<ifacePos>(iface); 180 auto& endpoints = std::get<endpointsPos>(iface); 181 182 // Only add new endpoints 183 for (auto& e : object.second) 184 { 185 if (std::find(endpoints.begin(), endpoints.end(), e) == 186 endpoints.end()) 187 { 188 endpoints.push_back(e); 189 } 190 } 191 192 // If the interface already exists, only need to update 193 // the property value, otherwise create it 194 if (i) 195 { 196 i->set_property("endpoints", endpoints); 197 } 198 else 199 { 200 i = objectServer.add_interface(object.first, 201 XYZ_ASSOCIATION_INTERFACE); 202 i->register_property("endpoints", endpoints); 203 i->initialize(); 204 } 205 } 206 207 // Check for endpoints being removed instead of added 208 checkAssociationEndpointRemoves(path, owner, objects, objectServer, 209 assocOwners, assocInterfaces); 210 211 // Update associationOwners with the latest info 212 auto a = assocOwners.find(path); 213 if (a != assocOwners.end()) 214 { 215 auto o = a->second.find(owner); 216 if (o != a->second.end()) 217 { 218 o->second = std::move(objects); 219 } 220 else 221 { 222 a->second.emplace(owner, std::move(objects)); 223 } 224 } 225 else 226 { 227 boost::container::flat_map<std::string, AssociationPaths> owners; 228 owners.emplace(owner, std::move(objects)); 229 assocOwners.emplace(path, owners); 230 } 231 } 232