1 #include "handler.hpp" 2 3 #include "types.hpp" 4 5 #include <xyz/openbmc_project/Common/error.hpp> 6 7 #include <algorithm> 8 #include <string> 9 #include <unordered_set> 10 #include <utility> 11 #include <vector> 12 13 void addObjectMapResult(std::vector<InterfaceMapType::value_type>& objectMap, 14 const std::string& objectPath, 15 const ConnectionNames::value_type& interfaceMap) 16 { 17 // Adds an object path/service name/interface list entry to 18 // the results of GetSubTree and GetAncestors. 19 // If an entry for the object path already exists, just add the 20 // service name and interfaces to that entry, otherwise create 21 // a new entry. 22 auto entry = std::find_if(objectMap.begin(), objectMap.end(), 23 [&objectPath](const auto& i) { 24 return objectPath == i.first; 25 }); 26 27 if (entry != objectMap.end()) 28 { 29 entry->second.emplace(interfaceMap); 30 } 31 else 32 { 33 InterfaceMapType::value_type object; 34 object.first = objectPath; 35 object.second.emplace(interfaceMap); 36 objectMap.push_back(object); 37 } 38 } 39 40 std::vector<InterfaceMapType::value_type> 41 getAncestors(const InterfaceMapType& interfaceMap, std::string reqPath, 42 std::vector<std::string>& interfaces) 43 { 44 // Interfaces need to be sorted for intersect to function 45 std::sort(interfaces.begin(), interfaces.end()); 46 47 if (reqPath.ends_with("/")) 48 { 49 reqPath.pop_back(); 50 } 51 if (!reqPath.empty() && interfaceMap.find(reqPath) == interfaceMap.end()) 52 { 53 throw sdbusplus::xyz::openbmc_project::Common::Error:: 54 ResourceNotFound(); 55 } 56 57 std::vector<InterfaceMapType::value_type> ret; 58 for (const auto& objectPath : interfaceMap) 59 { 60 const auto& thisPath = objectPath.first; 61 62 if (reqPath == thisPath) 63 { 64 continue; 65 } 66 67 if (reqPath.starts_with(thisPath)) 68 { 69 if (interfaces.empty()) 70 { 71 ret.emplace_back(objectPath); 72 } 73 else 74 { 75 for (const auto& interfaceMap : objectPath.second) 76 { 77 std::vector<std::string> output(std::min( 78 interfaces.size(), interfaceMap.second.size())); 79 // Return iterator points at the first output elemtn, 80 // meaning that there are no intersections. 81 if (std::set_intersection(interfaces.begin(), 82 interfaces.end(), 83 interfaceMap.second.begin(), 84 interfaceMap.second.end(), 85 output.begin()) != output.begin()) 86 { 87 addObjectMapResult(ret, thisPath, interfaceMap); 88 } 89 } 90 } 91 } 92 } 93 94 return ret; 95 } 96 97 ConnectionNames getObject(const InterfaceMapType& interfaceMap, 98 const std::string& path, 99 std::vector<std::string>& interfaces) 100 { 101 ConnectionNames results; 102 103 // Interfaces need to be sorted for intersect to function 104 std::sort(interfaces.begin(), interfaces.end()); 105 auto pathRef = interfaceMap.find(path); 106 if (pathRef == interfaceMap.end()) 107 { 108 throw sdbusplus::xyz::openbmc_project::Common::Error:: 109 ResourceNotFound(); 110 } 111 if (interfaces.empty()) 112 { 113 return pathRef->second; 114 } 115 for (const auto& interfaceMap : pathRef->second) 116 { 117 std::vector<std::string> output( 118 std::min(interfaces.size(), interfaceMap.second.size())); 119 // Return iterator points at the first output elemtn, 120 // meaning that there are no intersections. 121 if (std::set_intersection(interfaces.begin(), interfaces.end(), 122 interfaceMap.second.begin(), 123 interfaceMap.second.end(), 124 output.begin()) != output.begin()) 125 { 126 results.emplace(interfaceMap.first, interfaceMap.second); 127 } 128 } 129 130 if (results.empty()) 131 { 132 throw sdbusplus::xyz::openbmc_project::Common::Error:: 133 ResourceNotFound(); 134 } 135 136 return results; 137 } 138 139 std::vector<InterfaceMapType::value_type> 140 getSubTree(const InterfaceMapType& interfaceMap, std::string reqPath, 141 int32_t depth, std::vector<std::string>& interfaces) 142 { 143 if (depth <= 0) 144 { 145 depth = std::numeric_limits<int32_t>::max(); 146 } 147 // Interfaces need to be sorted for intersect to function 148 std::sort(interfaces.begin(), interfaces.end()); 149 150 // reqPath is now guaranteed to have a trailing "/" while reqPathStripped 151 // will be guaranteed not to have a trailing "/" 152 if (!reqPath.ends_with("/")) 153 { 154 reqPath += "/"; 155 } 156 std::string_view reqPathStripped = 157 std::string_view(reqPath).substr(0, reqPath.size() - 1); 158 159 if (!reqPathStripped.empty() && 160 interfaceMap.find(reqPathStripped) == interfaceMap.end()) 161 { 162 throw sdbusplus::xyz::openbmc_project::Common::Error:: 163 ResourceNotFound(); 164 } 165 166 std::vector<InterfaceMapType::value_type> ret; 167 for (const auto& objectPath : interfaceMap) 168 { 169 const auto& thisPath = objectPath.first; 170 171 // Skip exact match on stripped search term 172 if (thisPath == reqPathStripped) 173 { 174 continue; 175 } 176 177 if (thisPath.starts_with(reqPath)) 178 { 179 // count the number of slashes past the stripped search term 180 int32_t thisDepth = std::count( 181 thisPath.begin() + reqPathStripped.size(), thisPath.end(), '/'); 182 if (thisDepth <= depth) 183 { 184 for (const auto& interfaceMap : objectPath.second) 185 { 186 std::vector<std::string> output(std::min( 187 interfaces.size(), interfaceMap.second.size())); 188 // Return iterator points at the first output elemtn, 189 // meaning that there are no intersections. 190 if (std::set_intersection( 191 interfaces.begin(), interfaces.end(), 192 interfaceMap.second.begin(), 193 interfaceMap.second.end(), 194 output.begin()) != output.begin() || 195 interfaces.empty()) 196 { 197 addObjectMapResult(ret, thisPath, interfaceMap); 198 } 199 } 200 } 201 } 202 } 203 204 return ret; 205 } 206 207 std::vector<std::string> getSubTreePaths(const InterfaceMapType& interfaceMap, 208 std::string reqPath, int32_t depth, 209 std::vector<std::string>& interfaces) 210 { 211 if (depth <= 0) 212 { 213 depth = std::numeric_limits<int32_t>::max(); 214 } 215 // Interfaces need to be sorted for intersect to function 216 std::sort(interfaces.begin(), interfaces.end()); 217 218 // reqPath is now guaranteed to have a trailing "/" while reqPathStripped 219 // will be guaranteed not to have a trailing "/" 220 if (!reqPath.ends_with("/")) 221 { 222 reqPath += "/"; 223 } 224 std::string_view reqPathStripped = 225 std::string_view(reqPath).substr(0, reqPath.size() - 1); 226 227 if (!reqPathStripped.empty() && 228 interfaceMap.find(reqPathStripped) == interfaceMap.end()) 229 { 230 throw sdbusplus::xyz::openbmc_project::Common::Error:: 231 ResourceNotFound(); 232 } 233 234 std::vector<std::string> ret; 235 for (const auto& objectPath : interfaceMap) 236 { 237 const auto& thisPath = objectPath.first; 238 239 // Skip exact match on stripped search term 240 if (thisPath == reqPathStripped) 241 { 242 continue; 243 } 244 245 if (thisPath.starts_with(reqPath)) 246 { 247 // count the number of slashes past the stripped search term 248 int thisDepth = std::count( 249 thisPath.begin() + reqPathStripped.size(), thisPath.end(), '/'); 250 if (thisDepth <= depth) 251 { 252 bool add = interfaces.empty(); 253 for (const auto& interfaceMap : objectPath.second) 254 { 255 std::vector<std::string> output(std::min( 256 interfaces.size(), interfaceMap.second.size())); 257 // Return iterator points at the first output elemtn, 258 // meaning that there are no intersections. 259 if (std::set_intersection(interfaces.begin(), 260 interfaces.end(), 261 interfaceMap.second.begin(), 262 interfaceMap.second.end(), 263 output.begin()) != output.begin()) 264 { 265 add = true; 266 break; 267 } 268 } 269 if (add) 270 { 271 // TODO(ed) this is a copy 272 ret.emplace_back(thisPath); 273 } 274 } 275 } 276 } 277 278 return ret; 279 } 280 281 std::vector<InterfaceMapType::value_type> 282 getAssociatedSubTree(const InterfaceMapType& interfaceMap, 283 const AssociationMaps& associationMaps, 284 const sdbusplus::message::object_path& associationPath, 285 const sdbusplus::message::object_path& reqPath, 286 int32_t depth, std::vector<std::string>& interfaces) 287 { 288 auto findEndpoint = associationMaps.ifaces.find(associationPath.str); 289 if (findEndpoint == associationMaps.ifaces.end()) 290 { 291 return {}; 292 } 293 const std::vector<std::string>& association = 294 std::get<endpointsPos>(findEndpoint->second); 295 std::unordered_set<std::string> associationSet(association.begin(), 296 association.end()); 297 const std::vector<InterfaceMapType::value_type>& interfacePairs = 298 getSubTree(interfaceMap, reqPath, depth, interfaces); 299 300 std::vector<InterfaceMapType::value_type> output; 301 for (const InterfaceMapType::value_type& interfacePair : interfacePairs) 302 { 303 if (associationSet.contains(interfacePair.first)) 304 { 305 output.emplace_back(interfacePair); 306 } 307 } 308 return output; 309 } 310 311 std::vector<std::string> getAssociatedSubTreePaths( 312 const InterfaceMapType& interfaceMap, 313 const AssociationMaps& associationMaps, 314 const sdbusplus::message::object_path& associationPath, 315 const sdbusplus::message::object_path& reqPath, int32_t depth, 316 std::vector<std::string>& interfaces) 317 { 318 auto findEndpoint = associationMaps.ifaces.find(associationPath.str); 319 if (findEndpoint == associationMaps.ifaces.end()) 320 { 321 return {}; 322 } 323 const std::vector<std::string>& association = 324 std::get<endpointsPos>(findEndpoint->second); 325 std::unordered_set<std::string> associationSet(association.begin(), 326 association.end()); 327 const std::vector<std::string>& paths = 328 getSubTreePaths(interfaceMap, reqPath, depth, interfaces); 329 330 std::vector<std::string> output; 331 for (const auto& path : paths) 332 { 333 if (associationSet.contains(path)) 334 { 335 output.emplace_back(path); 336 } 337 } 338 return output; 339 }