blob: 4fef3b16eee8d2b42cb9819da514481d0acd0d83 [file] [log] [blame]
Willy Tuaba14d32023-01-31 14:19:59 -08001#include "handler.hpp"
2
3#include "types.hpp"
4
5#include <xyz/openbmc_project/Common/error.hpp>
6
7#include <algorithm>
8#include <string>
Willy Tu58881d02022-10-02 20:46:45 +00009#include <unordered_set>
Willy Tuaba14d32023-01-31 14:19:59 -080010#include <utility>
11#include <vector>
12
13void 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.
Patrick Williams670edd12023-02-15 15:06:52 -060022 auto entry = std::find_if(objectMap.begin(), objectMap.end(),
23 [&objectPath](const auto& i) {
24 return objectPath == i.first;
25 });
Willy Tuaba14d32023-01-31 14:19:59 -080026
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
40std::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
97ConnectionNames 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
139std::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
207std::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}
Willy Tu58881d02022-10-02 20:46:45 +0000280
281std::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
311std::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;
Patrick Williams1a19e6a2023-05-10 07:51:32 -0500339}