blob: 30b7ba3f208b265555f989254aa6f45e9c94a8b0 [file] [log] [blame]
#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;
}