xref: /openbmc/phosphor-objmgr/src/handler.cpp (revision 7a93d516)
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