1 #include "handler.hpp" 2 3 #include "types.hpp" 4 5 #include <xyz/openbmc_project/Common/error.hpp> 6 7 #include <algorithm> 8 #include <iterator> 9 #include <string> 10 #include <unordered_set> 11 #include <utility> 12 #include <vector> 13 14 void addObjectMapResult(std::vector<InterfaceMapType::value_type>& objectMap, 15 const std::string& objectPath, 16 const ConnectionNames::value_type& interfaceMap) 17 { 18 // Adds an object path/service name/interface list entry to 19 // the results of GetSubTree and GetAncestors. 20 // If an entry for the object path already exists, just add the 21 // service name and interfaces to that entry, otherwise create 22 // a new entry. 23 auto entry = std::find_if( 24 objectMap.begin(), objectMap.end(), 25 [&objectPath](const auto& i) { return objectPath == i.first; }); 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> getAncestors( 41 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& connectionInterfaces : objectPath.second) 76 { 77 std::vector<std::string> output(std::min( 78 interfaces.size(), connectionInterfaces.second.size())); 79 // Return iterator points at the first output elemtn, 80 // meaning that there are no intersections. 81 if (std::set_intersection( 82 interfaces.begin(), interfaces.end(), 83 connectionInterfaces.second.begin(), 84 connectionInterfaces.second.end(), 85 output.begin()) != output.begin()) 86 { 87 addObjectMapResult(ret, thisPath, connectionInterfaces); 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& connectionInterfaces : pathRef->second) 116 { 117 std::vector<std::string> output( 118 std::min(interfaces.size(), connectionInterfaces.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 connectionInterfaces.second.begin(), 123 connectionInterfaces.second.end(), 124 output.begin()) != output.begin()) 125 { 126 results.emplace(connectionInterfaces.first, 127 connectionInterfaces.second); 128 } 129 } 130 131 if (results.empty()) 132 { 133 throw sdbusplus::xyz::openbmc_project::Common::Error:: 134 ResourceNotFound(); 135 } 136 137 return results; 138 } 139 140 std::vector<InterfaceMapType::value_type> getSubTree( 141 const InterfaceMapType& interfaceMap, std::string reqPath, int32_t depth, 142 std::vector<std::string>& interfaces) 143 { 144 if (depth <= 0) 145 { 146 depth = std::numeric_limits<int32_t>::max(); 147 } 148 // Interfaces need to be sorted for intersect to function 149 std::sort(interfaces.begin(), interfaces.end()); 150 151 // reqPath is now guaranteed to have a trailing "/" while reqPathStripped 152 // will be guaranteed not to have a trailing "/" 153 if (!reqPath.ends_with("/")) 154 { 155 reqPath += "/"; 156 } 157 std::string_view reqPathStripped = 158 std::string_view(reqPath).substr(0, reqPath.size() - 1); 159 160 if (!reqPathStripped.empty() && 161 interfaceMap.find(reqPathStripped) == interfaceMap.end()) 162 { 163 throw sdbusplus::xyz::openbmc_project::Common::Error:: 164 ResourceNotFound(); 165 } 166 167 std::vector<InterfaceMapType::value_type> ret; 168 for (const auto& objectPath : interfaceMap) 169 { 170 const auto& thisPath = objectPath.first; 171 172 // Skip exact match on stripped search term 173 if (thisPath == reqPathStripped) 174 { 175 continue; 176 } 177 178 if (thisPath.starts_with(reqPath)) 179 { 180 // count the number of slashes past the stripped search term 181 auto thisDepth = std::count( 182 thisPath.begin() + std::distance(reqPathStripped.begin(), 183 reqPathStripped.end()), 184 thisPath.end(), '/'); 185 if (thisDepth <= depth) 186 { 187 for (const auto& connectionInterfaces : objectPath.second) 188 { 189 std::vector<std::string> output(std::min( 190 interfaces.size(), connectionInterfaces.second.size())); 191 // Return iterator points at the first output elemtn, 192 // meaning that there are no intersections. 193 if (std::set_intersection( 194 interfaces.begin(), interfaces.end(), 195 connectionInterfaces.second.begin(), 196 connectionInterfaces.second.end(), 197 output.begin()) != output.begin() || 198 interfaces.empty()) 199 { 200 addObjectMapResult(ret, thisPath, connectionInterfaces); 201 } 202 } 203 } 204 } 205 } 206 207 return ret; 208 } 209 210 std::vector<std::string> getSubTreePaths(const InterfaceMapType& interfaceMap, 211 std::string reqPath, int32_t depth, 212 std::vector<std::string>& interfaces) 213 { 214 if (depth <= 0) 215 { 216 depth = std::numeric_limits<int32_t>::max(); 217 } 218 // Interfaces need to be sorted for intersect to function 219 std::sort(interfaces.begin(), interfaces.end()); 220 221 // reqPath is now guaranteed to have a trailing "/" while reqPathStripped 222 // will be guaranteed not to have a trailing "/" 223 if (!reqPath.ends_with("/")) 224 { 225 reqPath += "/"; 226 } 227 std::string_view reqPathStripped = 228 std::string_view(reqPath).substr(0, reqPath.size() - 1); 229 230 if (!reqPathStripped.empty() && 231 interfaceMap.find(reqPathStripped) == interfaceMap.end()) 232 { 233 throw sdbusplus::xyz::openbmc_project::Common::Error:: 234 ResourceNotFound(); 235 } 236 237 std::vector<std::string> ret; 238 for (const auto& objectPath : interfaceMap) 239 { 240 const auto& thisPath = objectPath.first; 241 242 // Skip exact match on stripped search term 243 if (thisPath == reqPathStripped) 244 { 245 continue; 246 } 247 248 if (thisPath.starts_with(reqPath)) 249 { 250 // count the number of slashes past the stripped search term 251 auto thisDepth = std::count( 252 thisPath.begin() + std::distance(reqPathStripped.begin(), 253 reqPathStripped.end()), 254 thisPath.end(), '/'); 255 if (thisDepth <= depth) 256 { 257 bool add = interfaces.empty(); 258 for (const auto& connectionInterfaces : objectPath.second) 259 { 260 std::vector<std::string> output(std::min( 261 interfaces.size(), connectionInterfaces.second.size())); 262 // Return iterator points at the first output elemtn, 263 // meaning that there are no intersections. 264 if (std::set_intersection( 265 interfaces.begin(), interfaces.end(), 266 connectionInterfaces.second.begin(), 267 connectionInterfaces.second.end(), 268 output.begin()) != output.begin()) 269 { 270 add = true; 271 break; 272 } 273 } 274 if (add) 275 { 276 // TODO(ed) this is a copy 277 ret.emplace_back(thisPath); 278 } 279 } 280 } 281 } 282 283 return ret; 284 } 285 286 std::vector<InterfaceMapType::value_type> getAssociatedSubTree( 287 const InterfaceMapType& interfaceMap, 288 const AssociationMaps& associationMaps, 289 const sdbusplus::message::object_path& associationPath, 290 const sdbusplus::message::object_path& reqPath, int32_t depth, 291 std::vector<std::string>& interfaces) 292 { 293 auto findEndpoint = associationMaps.ifaces.find(associationPath.str); 294 if (findEndpoint == associationMaps.ifaces.end()) 295 { 296 return {}; 297 } 298 const std::vector<std::string>& association = 299 std::get<endpointsPos>(findEndpoint->second); 300 std::unordered_set<std::string> associationSet(association.begin(), 301 association.end()); 302 const std::vector<InterfaceMapType::value_type> interfacePairs = 303 getSubTree(interfaceMap, reqPath, depth, interfaces); 304 305 std::vector<InterfaceMapType::value_type> output; 306 for (const InterfaceMapType::value_type& interfacePair : interfacePairs) 307 { 308 if (associationSet.contains(interfacePair.first)) 309 { 310 output.emplace_back(interfacePair); 311 } 312 } 313 return output; 314 } 315 316 std::vector<std::string> getAssociatedSubTreePaths( 317 const InterfaceMapType& interfaceMap, 318 const AssociationMaps& associationMaps, 319 const sdbusplus::message::object_path& associationPath, 320 const sdbusplus::message::object_path& reqPath, int32_t depth, 321 std::vector<std::string>& interfaces) 322 { 323 auto findEndpoint = associationMaps.ifaces.find(associationPath.str); 324 if (findEndpoint == associationMaps.ifaces.end()) 325 { 326 return {}; 327 } 328 const std::vector<std::string>& association = 329 std::get<endpointsPos>(findEndpoint->second); 330 std::unordered_set<std::string> associationSet(association.begin(), 331 association.end()); 332 const std::vector<std::string> paths = 333 getSubTreePaths(interfaceMap, reqPath, depth, interfaces); 334 335 std::vector<std::string> output; 336 for (const auto& path : paths) 337 { 338 if (associationSet.contains(path)) 339 { 340 output.emplace_back(path); 341 } 342 } 343 return output; 344 } 345 346 // This function works like getSubTreePaths() but only matching id with 347 // the leaf-name instead of full path. 348 std::vector<std::string> getSubTreePathsById( 349 const InterfaceMapType& interfaceMap, const std::string& id, 350 const std::string& objectPath, std::vector<std::string>& interfaces) 351 { 352 std::sort(interfaces.begin(), interfaces.end()); 353 354 std::string localObjectPath = objectPath; 355 356 if (!localObjectPath.ends_with("/")) 357 { 358 localObjectPath += "/"; 359 } 360 std::string_view objectPathStripped = 361 std::string_view(localObjectPath).substr(0, localObjectPath.size() - 1); 362 363 if (!objectPathStripped.empty() && 364 interfaceMap.find(objectPathStripped) == interfaceMap.end()) 365 { 366 throw sdbusplus::xyz::openbmc_project::Common::Error:: 367 ResourceNotFound(); 368 } 369 370 bool validId = false; 371 std::vector<std::string> output; 372 for (const auto& path : interfaceMap) 373 { 374 const auto& thisPath = path.first; 375 376 // Skip the path does not end with the id 377 if (!thisPath.ends_with("/" + id)) 378 { 379 continue; 380 } 381 382 // Valid if id is matching 383 validId = true; 384 385 // Skip exact match on stripped search term 386 if (thisPath == objectPathStripped) 387 { 388 continue; 389 } 390 if (thisPath.starts_with(objectPath)) 391 { 392 for (const auto& connectionInterfaces : path.second) 393 { 394 std::vector<std::string> tempoutput(std::min( 395 interfaces.size(), connectionInterfaces.second.size())); 396 if (std::set_intersection(interfaces.begin(), interfaces.end(), 397 connectionInterfaces.second.begin(), 398 connectionInterfaces.second.end(), 399 tempoutput.begin()) != 400 tempoutput.begin()) 401 { 402 output.emplace_back(thisPath); 403 break; 404 } 405 } 406 } 407 } 408 if (!validId) 409 { 410 throw sdbusplus::xyz::openbmc_project::Common::Error:: 411 ResourceNotFound(); 412 } 413 return output; 414 } 415 416 std::vector<InterfaceMapType::value_type> getAssociatedSubTreeById( 417 const InterfaceMapType& interfaceMap, 418 const AssociationMaps& associationMaps, const std::string& id, 419 const std::string& objectPath, std::vector<std::string>& subtreeInterfaces, 420 const std::string& association, 421 std::vector<std::string>& endpointInterfaces) 422 { 423 std::vector<std::string> subtreePaths = 424 getSubTreePathsById(interfaceMap, id, objectPath, subtreeInterfaces); 425 426 std::vector<InterfaceMapType::value_type> output; 427 for (const auto& subtreePath : subtreePaths) 428 { 429 // Form the association path 430 std::string associationPathStr = subtreePath + "/" + association; 431 sdbusplus::message::object_path associationPath(associationPathStr); 432 433 auto associatedSubTree = 434 getAssociatedSubTree(interfaceMap, associationMaps, associationPath, 435 objectPath, 0, endpointInterfaces); 436 437 output.insert(output.end(), associatedSubTree.begin(), 438 associatedSubTree.end()); 439 } 440 return output; 441 } 442 443 std::vector<std::string> getAssociatedSubTreePathsById( 444 const InterfaceMapType& interfaceMap, 445 const AssociationMaps& associationMaps, const std::string& id, 446 const std::string& objectPath, std::vector<std::string>& subtreeInterfaces, 447 const std::string& association, 448 std::vector<std::string>& endpointInterfaces) 449 { 450 std::vector<std::string> subtreePaths = 451 getSubTreePathsById(interfaceMap, id, objectPath, subtreeInterfaces); 452 std::vector<std::string> output; 453 for (const auto& subtreePath : subtreePaths) 454 { 455 // Form the association path 456 std::string associationPathStr = subtreePath + "/" + association; 457 sdbusplus::message::object_path associationPath(associationPathStr); 458 459 auto associatedSubTree = getAssociatedSubTreePaths( 460 interfaceMap, associationMaps, associationPath, objectPath, 0, 461 endpointInterfaces); 462 463 output.insert(output.end(), associatedSubTree.begin(), 464 associatedSubTree.end()); 465 } 466 467 return output; 468 } 469