| #include "handler.hpp" |
| |
| #include "types.hpp" |
| |
| #include <xyz/openbmc_project/Common/error.hpp> |
| |
| #include <algorithm> |
| #include <string> |
| #include <unordered_set> |
| #include <utility> |
| #include <vector> |
| |
| void addObjectMapResult(std::vector<InterfaceMapType::value_type>& objectMap, |
| const std::string& objectPath, |
| const ConnectionNames::value_type& interfaceMap) |
| { |
| // Adds an object path/service name/interface list entry to |
| // the results of GetSubTree and GetAncestors. |
| // If an entry for the object path already exists, just add the |
| // service name and interfaces to that entry, otherwise create |
| // a new entry. |
| auto entry = std::find_if( |
| objectMap.begin(), objectMap.end(), |
| [&objectPath](const auto& i) { return objectPath == i.first; }); |
| |
| if (entry != objectMap.end()) |
| { |
| entry->second.emplace(interfaceMap); |
| } |
| else |
| { |
| InterfaceMapType::value_type object; |
| object.first = objectPath; |
| object.second.emplace(interfaceMap); |
| objectMap.push_back(object); |
| } |
| } |
| |
| std::vector<InterfaceMapType::value_type> |
| getAncestors(const InterfaceMapType& interfaceMap, std::string reqPath, |
| std::vector<std::string>& interfaces) |
| { |
| // Interfaces need to be sorted for intersect to function |
| std::sort(interfaces.begin(), interfaces.end()); |
| |
| if (reqPath.ends_with("/")) |
| { |
| reqPath.pop_back(); |
| } |
| if (!reqPath.empty() && interfaceMap.find(reqPath) == interfaceMap.end()) |
| { |
| throw sdbusplus::xyz::openbmc_project::Common::Error:: |
| ResourceNotFound(); |
| } |
| |
| std::vector<InterfaceMapType::value_type> ret; |
| for (const auto& objectPath : interfaceMap) |
| { |
| const auto& thisPath = objectPath.first; |
| |
| if (reqPath == thisPath) |
| { |
| continue; |
| } |
| |
| if (reqPath.starts_with(thisPath)) |
| { |
| if (interfaces.empty()) |
| { |
| ret.emplace_back(objectPath); |
| } |
| else |
| { |
| for (const auto& interfaceMap : objectPath.second) |
| { |
| std::vector<std::string> output(std::min( |
| interfaces.size(), interfaceMap.second.size())); |
| // Return iterator points at the first output elemtn, |
| // meaning that there are no intersections. |
| if (std::set_intersection( |
| interfaces.begin(), interfaces.end(), |
| interfaceMap.second.begin(), |
| interfaceMap.second.end(), output.begin()) != |
| output.begin()) |
| { |
| addObjectMapResult(ret, thisPath, interfaceMap); |
| } |
| } |
| } |
| } |
| } |
| |
| return ret; |
| } |
| |
| ConnectionNames getObject(const InterfaceMapType& interfaceMap, |
| const std::string& path, |
| std::vector<std::string>& interfaces) |
| { |
| ConnectionNames results; |
| |
| // Interfaces need to be sorted for intersect to function |
| std::sort(interfaces.begin(), interfaces.end()); |
| auto pathRef = interfaceMap.find(path); |
| if (pathRef == interfaceMap.end()) |
| { |
| throw sdbusplus::xyz::openbmc_project::Common::Error:: |
| ResourceNotFound(); |
| } |
| if (interfaces.empty()) |
| { |
| return pathRef->second; |
| } |
| for (const auto& interfaceMap : pathRef->second) |
| { |
| std::vector<std::string> output( |
| std::min(interfaces.size(), interfaceMap.second.size())); |
| // Return iterator points at the first output elemtn, |
| // meaning that there are no intersections. |
| if (std::set_intersection(interfaces.begin(), interfaces.end(), |
| interfaceMap.second.begin(), |
| interfaceMap.second.end(), output.begin()) != |
| output.begin()) |
| { |
| results.emplace(interfaceMap.first, interfaceMap.second); |
| } |
| } |
| |
| if (results.empty()) |
| { |
| throw sdbusplus::xyz::openbmc_project::Common::Error:: |
| ResourceNotFound(); |
| } |
| |
| return results; |
| } |
| |
| std::vector<InterfaceMapType::value_type> |
| getSubTree(const InterfaceMapType& interfaceMap, std::string reqPath, |
| int32_t depth, std::vector<std::string>& interfaces) |
| { |
| if (depth <= 0) |
| { |
| depth = std::numeric_limits<int32_t>::max(); |
| } |
| // Interfaces need to be sorted for intersect to function |
| std::sort(interfaces.begin(), interfaces.end()); |
| |
| // reqPath is now guaranteed to have a trailing "/" while reqPathStripped |
| // will be guaranteed not to have a trailing "/" |
| if (!reqPath.ends_with("/")) |
| { |
| reqPath += "/"; |
| } |
| std::string_view reqPathStripped = |
| std::string_view(reqPath).substr(0, reqPath.size() - 1); |
| |
| if (!reqPathStripped.empty() && |
| interfaceMap.find(reqPathStripped) == interfaceMap.end()) |
| { |
| throw sdbusplus::xyz::openbmc_project::Common::Error:: |
| ResourceNotFound(); |
| } |
| |
| std::vector<InterfaceMapType::value_type> ret; |
| for (const auto& objectPath : interfaceMap) |
| { |
| const auto& thisPath = objectPath.first; |
| |
| // Skip exact match on stripped search term |
| if (thisPath == reqPathStripped) |
| { |
| continue; |
| } |
| |
| if (thisPath.starts_with(reqPath)) |
| { |
| // count the number of slashes past the stripped search term |
| int32_t thisDepth = std::count( |
| thisPath.begin() + reqPathStripped.size(), thisPath.end(), '/'); |
| if (thisDepth <= depth) |
| { |
| for (const auto& interfaceMap : objectPath.second) |
| { |
| std::vector<std::string> output(std::min( |
| interfaces.size(), interfaceMap.second.size())); |
| // Return iterator points at the first output elemtn, |
| // meaning that there are no intersections. |
| if (std::set_intersection( |
| interfaces.begin(), interfaces.end(), |
| interfaceMap.second.begin(), |
| interfaceMap.second.end(), |
| output.begin()) != output.begin() || |
| interfaces.empty()) |
| { |
| addObjectMapResult(ret, thisPath, interfaceMap); |
| } |
| } |
| } |
| } |
| } |
| |
| return ret; |
| } |
| |
| std::vector<std::string> |
| getSubTreePaths(const InterfaceMapType& interfaceMap, std::string reqPath, |
| int32_t depth, std::vector<std::string>& interfaces) |
| { |
| if (depth <= 0) |
| { |
| depth = std::numeric_limits<int32_t>::max(); |
| } |
| // Interfaces need to be sorted for intersect to function |
| std::sort(interfaces.begin(), interfaces.end()); |
| |
| // reqPath is now guaranteed to have a trailing "/" while reqPathStripped |
| // will be guaranteed not to have a trailing "/" |
| if (!reqPath.ends_with("/")) |
| { |
| reqPath += "/"; |
| } |
| std::string_view reqPathStripped = |
| std::string_view(reqPath).substr(0, reqPath.size() - 1); |
| |
| if (!reqPathStripped.empty() && |
| interfaceMap.find(reqPathStripped) == interfaceMap.end()) |
| { |
| throw sdbusplus::xyz::openbmc_project::Common::Error:: |
| ResourceNotFound(); |
| } |
| |
| std::vector<std::string> ret; |
| for (const auto& objectPath : interfaceMap) |
| { |
| const auto& thisPath = objectPath.first; |
| |
| // Skip exact match on stripped search term |
| if (thisPath == reqPathStripped) |
| { |
| continue; |
| } |
| |
| if (thisPath.starts_with(reqPath)) |
| { |
| // count the number of slashes past the stripped search term |
| int thisDepth = std::count( |
| thisPath.begin() + reqPathStripped.size(), thisPath.end(), '/'); |
| if (thisDepth <= depth) |
| { |
| bool add = interfaces.empty(); |
| for (const auto& interfaceMap : objectPath.second) |
| { |
| std::vector<std::string> output(std::min( |
| interfaces.size(), interfaceMap.second.size())); |
| // Return iterator points at the first output elemtn, |
| // meaning that there are no intersections. |
| if (std::set_intersection( |
| interfaces.begin(), interfaces.end(), |
| interfaceMap.second.begin(), |
| interfaceMap.second.end(), output.begin()) != |
| output.begin()) |
| { |
| add = true; |
| break; |
| } |
| } |
| if (add) |
| { |
| // TODO(ed) this is a copy |
| ret.emplace_back(thisPath); |
| } |
| } |
| } |
| } |
| |
| return ret; |
| } |
| |
| std::vector<InterfaceMapType::value_type> getAssociatedSubTree( |
| const InterfaceMapType& interfaceMap, |
| const AssociationMaps& associationMaps, |
| const sdbusplus::message::object_path& associationPath, |
| const sdbusplus::message::object_path& reqPath, int32_t depth, |
| std::vector<std::string>& interfaces) |
| { |
| auto findEndpoint = associationMaps.ifaces.find(associationPath.str); |
| if (findEndpoint == associationMaps.ifaces.end()) |
| { |
| return {}; |
| } |
| const std::vector<std::string>& association = |
| std::get<endpointsPos>(findEndpoint->second); |
| std::unordered_set<std::string> associationSet(association.begin(), |
| association.end()); |
| const std::vector<InterfaceMapType::value_type>& interfacePairs = |
| getSubTree(interfaceMap, reqPath, depth, interfaces); |
| |
| std::vector<InterfaceMapType::value_type> output; |
| for (const InterfaceMapType::value_type& interfacePair : interfacePairs) |
| { |
| if (associationSet.contains(interfacePair.first)) |
| { |
| output.emplace_back(interfacePair); |
| } |
| } |
| return output; |
| } |
| |
| std::vector<std::string> getAssociatedSubTreePaths( |
| const InterfaceMapType& interfaceMap, |
| const AssociationMaps& associationMaps, |
| const sdbusplus::message::object_path& associationPath, |
| const sdbusplus::message::object_path& reqPath, int32_t depth, |
| std::vector<std::string>& interfaces) |
| { |
| auto findEndpoint = associationMaps.ifaces.find(associationPath.str); |
| if (findEndpoint == associationMaps.ifaces.end()) |
| { |
| return {}; |
| } |
| const std::vector<std::string>& association = |
| std::get<endpointsPos>(findEndpoint->second); |
| std::unordered_set<std::string> associationSet(association.begin(), |
| association.end()); |
| const std::vector<std::string>& paths = |
| getSubTreePaths(interfaceMap, reqPath, depth, interfaces); |
| |
| std::vector<std::string> output; |
| for (const auto& path : paths) |
| { |
| if (associationSet.contains(path)) |
| { |
| output.emplace_back(path); |
| } |
| } |
| return output; |
| } |