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
addObjectMapResult(std::vector<InterfaceMapType::value_type> & objectMap,const std::string & objectPath,const ConnectionNames::value_type & interfaceMap)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>
getAncestors(const InterfaceMapType & interfaceMap,std::string reqPath,std::vector<std::string> & interfaces)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
getObject(const InterfaceMapType & interfaceMap,const std::string & path,std::vector<std::string> & interfaces)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>
getSubTree(const InterfaceMapType & interfaceMap,std::string reqPath,int32_t depth,std::vector<std::string> & interfaces)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>
getSubTreePaths(const InterfaceMapType & interfaceMap,std::string reqPath,int32_t depth,std::vector<std::string> & interfaces)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
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)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
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)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.
getSubTreePathsById(const InterfaceMapType & interfaceMap,const std::string & id,const std::string & objectPath,std::vector<std::string> & interfaces)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
getAssociatedSubTreeById(const InterfaceMapType & interfaceMap,const AssociationMaps & associationMaps,const std::string & id,const std::string & objectPath,std::vector<std::string> & subtreeInterfaces,const std::string & association,std::vector<std::string> & endpointInterfaces)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
getAssociatedSubTreePathsById(const InterfaceMapType & interfaceMap,const AssociationMaps & associationMaps,const std::string & id,const std::string & objectPath,std::vector<std::string> & subtreeInterfaces,const std::string & association,std::vector<std::string> & endpointInterfaces)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