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