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