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( 23 objectMap.begin(), objectMap.end(), 24 [&objectPath](const auto& i) { return objectPath == i.first; }); 25 26 if (entry != objectMap.end()) 27 { 28 entry->second.emplace(interfaceMap); 29 } 30 else 31 { 32 InterfaceMapType::value_type object; 33 object.first = objectPath; 34 object.second.emplace(interfaceMap); 35 objectMap.push_back(object); 36 } 37 } 38 39 std::vector<InterfaceMapType::value_type> 40 getAncestors(const InterfaceMapType& interfaceMap, std::string reqPath, 41 std::vector<std::string>& interfaces) 42 { 43 // Interfaces need to be sorted for intersect to function 44 std::sort(interfaces.begin(), interfaces.end()); 45 46 if (reqPath.ends_with("/")) 47 { 48 reqPath.pop_back(); 49 } 50 if (!reqPath.empty() && interfaceMap.find(reqPath) == interfaceMap.end()) 51 { 52 throw sdbusplus::xyz::openbmc_project::Common::Error:: 53 ResourceNotFound(); 54 } 55 56 std::vector<InterfaceMapType::value_type> ret; 57 for (const auto& objectPath : interfaceMap) 58 { 59 const auto& thisPath = objectPath.first; 60 61 if (reqPath == thisPath) 62 { 63 continue; 64 } 65 66 if (reqPath.starts_with(thisPath)) 67 { 68 if (interfaces.empty()) 69 { 70 ret.emplace_back(objectPath); 71 } 72 else 73 { 74 for (const auto& interfaceMap : objectPath.second) 75 { 76 std::vector<std::string> output(std::min( 77 interfaces.size(), interfaceMap.second.size())); 78 // Return iterator points at the first output elemtn, 79 // meaning that there are no intersections. 80 if (std::set_intersection( 81 interfaces.begin(), interfaces.end(), 82 interfaceMap.second.begin(), 83 interfaceMap.second.end(), output.begin()) != 84 output.begin()) 85 { 86 addObjectMapResult(ret, thisPath, interfaceMap); 87 } 88 } 89 } 90 } 91 } 92 93 return ret; 94 } 95 96 ConnectionNames getObject(const InterfaceMapType& interfaceMap, 97 const std::string& path, 98 std::vector<std::string>& interfaces) 99 { 100 ConnectionNames results; 101 102 // Interfaces need to be sorted for intersect to function 103 std::sort(interfaces.begin(), interfaces.end()); 104 auto pathRef = interfaceMap.find(path); 105 if (pathRef == interfaceMap.end()) 106 { 107 throw sdbusplus::xyz::openbmc_project::Common::Error:: 108 ResourceNotFound(); 109 } 110 if (interfaces.empty()) 111 { 112 return pathRef->second; 113 } 114 for (const auto& interfaceMap : pathRef->second) 115 { 116 std::vector<std::string> output( 117 std::min(interfaces.size(), interfaceMap.second.size())); 118 // Return iterator points at the first output elemtn, 119 // meaning that there are no intersections. 120 if (std::set_intersection(interfaces.begin(), interfaces.end(), 121 interfaceMap.second.begin(), 122 interfaceMap.second.end(), output.begin()) != 123 output.begin()) 124 { 125 results.emplace(interfaceMap.first, interfaceMap.second); 126 } 127 } 128 129 if (results.empty()) 130 { 131 throw sdbusplus::xyz::openbmc_project::Common::Error:: 132 ResourceNotFound(); 133 } 134 135 return results; 136 } 137 138 std::vector<InterfaceMapType::value_type> 139 getSubTree(const InterfaceMapType& interfaceMap, std::string reqPath, 140 int32_t depth, std::vector<std::string>& interfaces) 141 { 142 if (depth <= 0) 143 { 144 depth = std::numeric_limits<int32_t>::max(); 145 } 146 // Interfaces need to be sorted for intersect to function 147 std::sort(interfaces.begin(), interfaces.end()); 148 149 // reqPath is now guaranteed to have a trailing "/" while reqPathStripped 150 // will be guaranteed not to have a trailing "/" 151 if (!reqPath.ends_with("/")) 152 { 153 reqPath += "/"; 154 } 155 std::string_view reqPathStripped = 156 std::string_view(reqPath).substr(0, reqPath.size() - 1); 157 158 if (!reqPathStripped.empty() && 159 interfaceMap.find(reqPathStripped) == interfaceMap.end()) 160 { 161 throw sdbusplus::xyz::openbmc_project::Common::Error:: 162 ResourceNotFound(); 163 } 164 165 std::vector<InterfaceMapType::value_type> ret; 166 for (const auto& objectPath : interfaceMap) 167 { 168 const auto& thisPath = objectPath.first; 169 170 // Skip exact match on stripped search term 171 if (thisPath == reqPathStripped) 172 { 173 continue; 174 } 175 176 if (thisPath.starts_with(reqPath)) 177 { 178 // count the number of slashes past the stripped search term 179 int32_t thisDepth = std::count( 180 thisPath.begin() + reqPathStripped.size(), thisPath.end(), '/'); 181 if (thisDepth <= depth) 182 { 183 for (const auto& interfaceMap : objectPath.second) 184 { 185 std::vector<std::string> output(std::min( 186 interfaces.size(), interfaceMap.second.size())); 187 // Return iterator points at the first output elemtn, 188 // meaning that there are no intersections. 189 if (std::set_intersection( 190 interfaces.begin(), interfaces.end(), 191 interfaceMap.second.begin(), 192 interfaceMap.second.end(), 193 output.begin()) != output.begin() || 194 interfaces.empty()) 195 { 196 addObjectMapResult(ret, thisPath, interfaceMap); 197 } 198 } 199 } 200 } 201 } 202 203 return ret; 204 } 205 206 std::vector<std::string> 207 getSubTreePaths(const InterfaceMapType& interfaceMap, std::string reqPath, 208 int32_t depth, std::vector<std::string>& interfaces) 209 { 210 if (depth <= 0) 211 { 212 depth = std::numeric_limits<int32_t>::max(); 213 } 214 // Interfaces need to be sorted for intersect to function 215 std::sort(interfaces.begin(), interfaces.end()); 216 217 // reqPath is now guaranteed to have a trailing "/" while reqPathStripped 218 // will be guaranteed not to have a trailing "/" 219 if (!reqPath.ends_with("/")) 220 { 221 reqPath += "/"; 222 } 223 std::string_view reqPathStripped = 224 std::string_view(reqPath).substr(0, reqPath.size() - 1); 225 226 if (!reqPathStripped.empty() && 227 interfaceMap.find(reqPathStripped) == interfaceMap.end()) 228 { 229 throw sdbusplus::xyz::openbmc_project::Common::Error:: 230 ResourceNotFound(); 231 } 232 233 std::vector<std::string> ret; 234 for (const auto& objectPath : interfaceMap) 235 { 236 const auto& thisPath = objectPath.first; 237 238 // Skip exact match on stripped search term 239 if (thisPath == reqPathStripped) 240 { 241 continue; 242 } 243 244 if (thisPath.starts_with(reqPath)) 245 { 246 // count the number of slashes past the stripped search term 247 int thisDepth = std::count( 248 thisPath.begin() + reqPathStripped.size(), thisPath.end(), '/'); 249 if (thisDepth <= depth) 250 { 251 bool add = interfaces.empty(); 252 for (const auto& interfaceMap : objectPath.second) 253 { 254 std::vector<std::string> output(std::min( 255 interfaces.size(), interfaceMap.second.size())); 256 // Return iterator points at the first output elemtn, 257 // meaning that there are no intersections. 258 if (std::set_intersection( 259 interfaces.begin(), interfaces.end(), 260 interfaceMap.second.begin(), 261 interfaceMap.second.end(), output.begin()) != 262 output.begin()) 263 { 264 add = true; 265 break; 266 } 267 } 268 if (add) 269 { 270 // TODO(ed) this is a copy 271 ret.emplace_back(thisPath); 272 } 273 } 274 } 275 } 276 277 return ret; 278 } 279 280 std::vector<InterfaceMapType::value_type> getAssociatedSubTree( 281 const InterfaceMapType& interfaceMap, 282 const AssociationMaps& associationMaps, 283 const sdbusplus::message::object_path& associationPath, 284 const sdbusplus::message::object_path& reqPath, int32_t depth, 285 std::vector<std::string>& interfaces) 286 { 287 auto findEndpoint = associationMaps.ifaces.find(associationPath.str); 288 if (findEndpoint == associationMaps.ifaces.end()) 289 { 290 return {}; 291 } 292 const std::vector<std::string>& association = 293 std::get<endpointsPos>(findEndpoint->second); 294 std::unordered_set<std::string> associationSet(association.begin(), 295 association.end()); 296 const std::vector<InterfaceMapType::value_type> interfacePairs = 297 getSubTree(interfaceMap, reqPath, depth, interfaces); 298 299 std::vector<InterfaceMapType::value_type> output; 300 for (const InterfaceMapType::value_type& interfacePair : interfacePairs) 301 { 302 if (associationSet.contains(interfacePair.first)) 303 { 304 output.emplace_back(interfacePair); 305 } 306 } 307 return output; 308 } 309 310 std::vector<std::string> getAssociatedSubTreePaths( 311 const InterfaceMapType& interfaceMap, 312 const AssociationMaps& associationMaps, 313 const sdbusplus::message::object_path& associationPath, 314 const sdbusplus::message::object_path& reqPath, int32_t depth, 315 std::vector<std::string>& interfaces) 316 { 317 auto findEndpoint = associationMaps.ifaces.find(associationPath.str); 318 if (findEndpoint == associationMaps.ifaces.end()) 319 { 320 return {}; 321 } 322 const std::vector<std::string>& association = 323 std::get<endpointsPos>(findEndpoint->second); 324 std::unordered_set<std::string> associationSet(association.begin(), 325 association.end()); 326 const std::vector<std::string> paths = 327 getSubTreePaths(interfaceMap, reqPath, depth, interfaces); 328 329 std::vector<std::string> output; 330 for (const auto& path : paths) 331 { 332 if (associationSet.contains(path)) 333 { 334 output.emplace_back(path); 335 } 336 } 337 return output; 338 } 339 340 // This function works like getSubTreePaths() but only matching id with 341 // the leaf-name instead of full path. 342 std::vector<std::string> getSubTreePathsById( 343 const InterfaceMapType& interfaceMap, const std::string& id, 344 const std::string& objectPath, std::vector<std::string>& interfaces) 345 { 346 std::sort(interfaces.begin(), interfaces.end()); 347 348 std::string localObjectPath = objectPath; 349 350 if (!localObjectPath.ends_with("/")) 351 { 352 localObjectPath += "/"; 353 } 354 std::string_view objectPathStripped = 355 std::string_view(localObjectPath).substr(0, localObjectPath.size() - 1); 356 357 if (!objectPathStripped.empty() && 358 interfaceMap.find(objectPathStripped) == interfaceMap.end()) 359 { 360 throw sdbusplus::xyz::openbmc_project::Common::Error:: 361 ResourceNotFound(); 362 } 363 364 std::vector<std::string> output; 365 for (const auto& path : interfaceMap) 366 { 367 const auto& thisPath = path.first; 368 369 // Skip exact match on stripped search term or 370 // the path does not end with the id 371 if (thisPath == objectPathStripped || !thisPath.ends_with("/" + id)) 372 { 373 continue; 374 } 375 376 if (thisPath.starts_with(objectPath)) 377 { 378 for (const auto& interfaceMap : path.second) 379 { 380 std::vector<std::string> tempoutput( 381 std::min(interfaces.size(), interfaceMap.second.size())); 382 if (std::set_intersection( 383 interfaces.begin(), interfaces.end(), 384 interfaceMap.second.begin(), interfaceMap.second.end(), 385 tempoutput.begin()) != tempoutput.begin()) 386 { 387 output.emplace_back(thisPath); 388 break; 389 } 390 } 391 } 392 } 393 return output; 394 } 395 396 std::vector<InterfaceMapType::value_type> getAssociatedSubTreeById( 397 const InterfaceMapType& interfaceMap, 398 const AssociationMaps& associationMaps, const std::string& id, 399 const std::string& objectPath, std::vector<std::string>& subtreeInterfaces, 400 const std::string& association, 401 std::vector<std::string>& endpointInterfaces) 402 { 403 std::vector<std::string> subtreePaths = 404 getSubTreePathsById(interfaceMap, id, objectPath, subtreeInterfaces); 405 406 std::vector<InterfaceMapType::value_type> output; 407 for (const auto& subtreePath : subtreePaths) 408 { 409 // Form the association path 410 std::string associationPathStr = subtreePath + "/" + association; 411 sdbusplus::message::object_path associationPath(associationPathStr); 412 413 auto associatedSubTree = 414 getAssociatedSubTree(interfaceMap, associationMaps, associationPath, 415 objectPath, 0, endpointInterfaces); 416 417 output.insert(output.end(), associatedSubTree.begin(), 418 associatedSubTree.end()); 419 } 420 return output; 421 } 422 423 std::vector<std::string> getAssociatedSubTreePathsById( 424 const InterfaceMapType& interfaceMap, 425 const AssociationMaps& associationMaps, const std::string& id, 426 const std::string& objectPath, std::vector<std::string>& subtreeInterfaces, 427 const std::string& association, 428 std::vector<std::string>& endpointInterfaces) 429 { 430 std::vector<std::string> subtreePaths = 431 getSubTreePathsById(interfaceMap, id, objectPath, subtreeInterfaces); 432 std::vector<std::string> output; 433 for (const auto& subtreePath : subtreePaths) 434 { 435 // Form the association path 436 std::string associationPathStr = subtreePath + "/" + association; 437 sdbusplus::message::object_path associationPath(associationPathStr); 438 439 auto associatedSubTree = getAssociatedSubTreePaths( 440 interfaceMap, associationMaps, associationPath, objectPath, 0, 441 endpointInterfaces); 442 443 output.insert(output.end(), associatedSubTree.begin(), 444 associatedSubTree.end()); 445 } 446 447 return output; 448 } 449