xref: /openbmc/phosphor-objmgr/src/handler.cpp (revision 7dd2029eb1755a5ad59ac33bf462837d7e906dbf)
1 #include "handler.hpp"
2 
3 #include "path.hpp"
4 #include "types.hpp"
5 
6 #include <xyz/openbmc_project/Common/error.hpp>
7 
8 #include <algorithm>
9 #include <iterator>
10 #include <string>
11 #include <unordered_set>
12 #include <utility>
13 #include <vector>
14 
addObjectMapResult(std::vector<InterfaceMapType::value_type> & objectMap,const std::string & objectPath,const ConnectionNames::value_type & interfaceMap)15 void addObjectMapResult(std::vector<InterfaceMapType::value_type>& objectMap,
16                         const std::string& objectPath,
17                         const ConnectionNames::value_type& interfaceMap)
18 {
19     // Adds an object path/service name/interface list entry to
20     // the results of GetSubTree and GetAncestors.
21     // If an entry for the object path already exists, just add the
22     // service name and interfaces to that entry, otherwise create
23     // a new entry.
24     auto entry = std::find_if(
25         objectMap.begin(), objectMap.end(),
26         [&objectPath](const auto& i) { return objectPath == i.first; });
27 
28     if (entry != objectMap.end())
29     {
30         entry->second.emplace(interfaceMap);
31     }
32     else
33     {
34         InterfaceMapType::value_type object;
35         object.first = objectPath;
36         object.second.emplace(interfaceMap);
37         objectMap.push_back(object);
38     }
39 }
40 
getAncestors(const InterfaceMapType & interfaceMap,std::string reqPath,std::vector<std::string> & interfaces)41 std::vector<InterfaceMapType::value_type> getAncestors(
42     const InterfaceMapType& interfaceMap, std::string reqPath,
43     std::vector<std::string>& interfaces)
44 {
45     // Interfaces need to be sorted for intersect to function
46     std::sort(interfaces.begin(), interfaces.end());
47 
48     if (reqPath.ends_with("/"))
49     {
50         reqPath.pop_back();
51     }
52     if (!reqPath.empty() && !interfaceMap.contains(reqPath))
53     {
54         throw sdbusplus::xyz::openbmc_project::Common::Error::
55             ResourceNotFound();
56     }
57 
58     std::vector<InterfaceMapType::value_type> ret;
59     for (const auto& objectPath : interfaceMap)
60     {
61         const auto& thisPath = objectPath.first;
62 
63         if (reqPath == thisPath)
64         {
65             continue;
66         }
67 
68         if (reqPath.starts_with(thisPath))
69         {
70             if (interfaces.empty())
71             {
72                 ret.emplace_back(objectPath);
73             }
74             else
75             {
76                 for (const auto& connectionInterfaces : objectPath.second)
77                 {
78                     std::vector<std::string> output(std::min(
79                         interfaces.size(), connectionInterfaces.second.size()));
80                     // Return iterator points at the first output elemtn,
81                     // meaning that there are no intersections.
82                     if (std::set_intersection(
83                             interfaces.begin(), interfaces.end(),
84                             connectionInterfaces.second.begin(),
85                             connectionInterfaces.second.end(),
86                             output.begin()) != output.begin())
87                     {
88                         addObjectMapResult(ret, thisPath, connectionInterfaces);
89                     }
90                 }
91             }
92         }
93     }
94 
95     return ret;
96 }
97 
getObject(const InterfaceMapType & interfaceMap,const std::string & path,std::vector<std::string> & interfaces)98 ConnectionNames getObject(const InterfaceMapType& interfaceMap,
99                           const std::string& path,
100                           std::vector<std::string>& interfaces)
101 {
102     ConnectionNames results;
103 
104     // Interfaces need to be sorted for intersect to function
105     std::sort(interfaces.begin(), interfaces.end());
106     auto pathRef = interfaceMap.find(path);
107     if (pathRef == interfaceMap.end())
108     {
109         throw sdbusplus::xyz::openbmc_project::Common::Error::
110             ResourceNotFound();
111     }
112     if (interfaces.empty())
113     {
114         return pathRef->second;
115     }
116     for (const auto& connectionInterfaces : pathRef->second)
117     {
118         std::vector<std::string> output(
119             std::min(interfaces.size(), connectionInterfaces.second.size()));
120         // Return iterator points at the first output elemtn,
121         // meaning that there are no intersections.
122         if (std::set_intersection(interfaces.begin(), interfaces.end(),
123                                   connectionInterfaces.second.begin(),
124                                   connectionInterfaces.second.end(),
125                                   output.begin()) != output.begin())
126         {
127             results.emplace(connectionInterfaces.first,
128                             connectionInterfaces.second);
129         }
130     }
131 
132     if (results.empty())
133     {
134         throw sdbusplus::xyz::openbmc_project::Common::Error::
135             ResourceNotFound();
136     }
137 
138     return results;
139 }
140 
getSubTree(const InterfaceMapType & interfaceMap,std::string reqPath,int32_t depth,std::vector<std::string> & interfaces)141 std::vector<InterfaceMapType::value_type> getSubTree(
142     const InterfaceMapType& interfaceMap, std::string reqPath, int32_t depth,
143     std::vector<std::string>& interfaces)
144 {
145     if (depth <= 0)
146     {
147         depth = std::numeric_limits<int32_t>::max();
148     }
149     // Interfaces need to be sorted for intersect to function
150     std::sort(interfaces.begin(), interfaces.end());
151 
152     // reqPath is now guaranteed to have a trailing "/" while reqPathStripped
153     // will be guaranteed not to have a trailing "/"
154     if (!reqPath.ends_with("/"))
155     {
156         reqPath += "/";
157     }
158     std::string_view reqPathStripped =
159         std::string_view(reqPath).substr(0, reqPath.size() - 1);
160 
161     if (!reqPathStripped.empty() &&
162         interfaceMap.find(reqPathStripped) == interfaceMap.end())
163     {
164         throw sdbusplus::xyz::openbmc_project::Common::Error::
165             ResourceNotFound();
166     }
167 
168     std::vector<InterfaceMapType::value_type> ret;
169     for (const auto& objectPath : interfaceMap)
170     {
171         const auto& thisPath = objectPath.first;
172 
173         // Skip exact match on stripped search term
174         if (thisPath == reqPathStripped)
175         {
176             continue;
177         }
178 
179         if (thisPath.starts_with(reqPath))
180         {
181             // count the number of slashes past the stripped search term
182             auto thisDepth = std::count(
183                 thisPath.begin() + std::distance(reqPathStripped.begin(),
184                                                  reqPathStripped.end()),
185                 thisPath.end(), '/');
186             if (thisDepth <= depth)
187             {
188                 for (const auto& connectionInterfaces : objectPath.second)
189                 {
190                     std::vector<std::string> output(std::min(
191                         interfaces.size(), connectionInterfaces.second.size()));
192                     // Return iterator points at the first output elemtn,
193                     // meaning that there are no intersections.
194                     if (std::set_intersection(
195                             interfaces.begin(), interfaces.end(),
196                             connectionInterfaces.second.begin(),
197                             connectionInterfaces.second.end(),
198                             output.begin()) != output.begin() ||
199                         interfaces.empty())
200                     {
201                         addObjectMapResult(ret, thisPath, connectionInterfaces);
202                     }
203                 }
204             }
205         }
206     }
207 
208     return ret;
209 }
210 
getSubTreePaths(const InterfaceMapType & interfaceMap,std::string reqPath,int32_t depth,std::vector<std::string> & interfaces)211 std::vector<std::string> getSubTreePaths(const InterfaceMapType& interfaceMap,
212                                          std::string reqPath, int32_t depth,
213                                          std::vector<std::string>& interfaces)
214 {
215     if (depth <= 0)
216     {
217         depth = std::numeric_limits<int32_t>::max();
218     }
219     // Interfaces need to be sorted for intersect to function
220     std::sort(interfaces.begin(), interfaces.end());
221 
222     // reqPath is now guaranteed to have a trailing "/" while reqPathStripped
223     // will be guaranteed not to have a trailing "/"
224     if (!reqPath.ends_with("/"))
225     {
226         reqPath += "/";
227     }
228     std::string_view reqPathStripped =
229         std::string_view(reqPath).substr(0, reqPath.size() - 1);
230 
231     if (!reqPathStripped.empty() &&
232         interfaceMap.find(reqPathStripped) == interfaceMap.end())
233     {
234         throw sdbusplus::xyz::openbmc_project::Common::Error::
235             ResourceNotFound();
236     }
237 
238     std::vector<std::string> ret;
239     for (const auto& objectPath : interfaceMap)
240     {
241         const auto& thisPath = objectPath.first;
242 
243         // Skip exact match on stripped search term
244         if (thisPath == reqPathStripped)
245         {
246             continue;
247         }
248 
249         if (thisPath.starts_with(reqPath))
250         {
251             // count the number of slashes past the stripped search term
252             auto thisDepth = std::count(
253                 thisPath.begin() + std::distance(reqPathStripped.begin(),
254                                                  reqPathStripped.end()),
255                 thisPath.end(), '/');
256             if (thisDepth <= depth)
257             {
258                 bool add = interfaces.empty();
259                 for (const auto& connectionInterfaces : objectPath.second)
260                 {
261                     std::vector<std::string> output(std::min(
262                         interfaces.size(), connectionInterfaces.second.size()));
263                     // Return iterator points at the first output elemtn,
264                     // meaning that there are no intersections.
265                     if (std::set_intersection(
266                             interfaces.begin(), interfaces.end(),
267                             connectionInterfaces.second.begin(),
268                             connectionInterfaces.second.end(),
269                             output.begin()) != output.begin())
270                     {
271                         add = true;
272                         break;
273                     }
274                 }
275                 if (add)
276                 {
277                     // TODO(ed) this is a copy
278                     ret.emplace_back(thisPath);
279                 }
280             }
281         }
282     }
283 
284     return ret;
285 }
286 
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)287 std::vector<InterfaceMapType::value_type> getAssociatedSubTree(
288     const InterfaceMapType& interfaceMap,
289     const AssociationMaps& associationMaps,
290     const sdbusplus::message::object_path& associationPath,
291     const sdbusplus::message::object_path& reqPath, int32_t depth,
292     std::vector<std::string>& interfaces)
293 {
294     auto findEndpoint = associationMaps.ifaces.find(associationPath.str);
295     if (findEndpoint == associationMaps.ifaces.end())
296     {
297         return {};
298     }
299     const std::vector<std::string>& association =
300         std::get<endpointsPos>(findEndpoint->second);
301     std::unordered_set<std::string> associationSet(association.begin(),
302                                                    association.end());
303     const std::vector<InterfaceMapType::value_type> interfacePairs =
304         getSubTree(interfaceMap, reqPath, depth, interfaces);
305 
306     std::vector<InterfaceMapType::value_type> output;
307     for (const InterfaceMapType::value_type& interfacePair : interfacePairs)
308     {
309         if (associationSet.contains(interfacePair.first))
310         {
311             output.emplace_back(interfacePair);
312         }
313     }
314     return output;
315 }
316 
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)317 std::vector<std::string> getAssociatedSubTreePaths(
318     const InterfaceMapType& interfaceMap,
319     const AssociationMaps& associationMaps,
320     const sdbusplus::message::object_path& associationPath,
321     const sdbusplus::message::object_path& reqPath, int32_t depth,
322     std::vector<std::string>& interfaces)
323 {
324     auto findEndpoint = associationMaps.ifaces.find(associationPath.str);
325     if (findEndpoint == associationMaps.ifaces.end())
326     {
327         return {};
328     }
329     const std::vector<std::string>& association =
330         std::get<endpointsPos>(findEndpoint->second);
331     std::unordered_set<std::string> associationSet(association.begin(),
332                                                    association.end());
333     const std::vector<std::string> paths =
334         getSubTreePaths(interfaceMap, reqPath, depth, interfaces);
335 
336     std::vector<std::string> output;
337     for (const auto& path : paths)
338     {
339         if (associationSet.contains(path))
340         {
341             output.emplace_back(path);
342         }
343     }
344     return output;
345 }
346 
347 // This function works like getSubTreePaths() but only matching id with
348 // the leaf-name instead of full path.
getSubTreePathsById(const InterfaceMapType & interfaceMap,const std::string & id,const std::string & objectPath,std::vector<std::string> & interfaces)349 static std::vector<std::string> getSubTreePathsById(
350     const InterfaceMapType& interfaceMap, const std::string& id,
351     const std::string& objectPath, std::vector<std::string>& interfaces)
352 {
353     std::sort(interfaces.begin(), interfaces.end());
354 
355     std::string localObjectPath = objectPath;
356 
357     if (!localObjectPath.ends_with("/"))
358     {
359         localObjectPath += "/";
360     }
361     std::string_view objectPathStripped =
362         std::string_view(localObjectPath).substr(0, localObjectPath.size() - 1);
363 
364     if (!objectPathStripped.empty() &&
365         interfaceMap.find(objectPathStripped) == interfaceMap.end())
366     {
367         throw sdbusplus::xyz::openbmc_project::Common::Error::
368             ResourceNotFound();
369     }
370 
371     bool validId = false;
372     std::vector<std::string> output;
373     for (const auto& path : interfaceMap)
374     {
375         const auto& thisPath = path.first;
376 
377         // Skip the path does not end with the id
378         if (!thisPath.ends_with("/" + id))
379         {
380             continue;
381         }
382 
383         // Valid if id is matching
384         validId = true;
385 
386         // Skip exact match on stripped search term
387         if (thisPath == objectPathStripped)
388         {
389             continue;
390         }
391         if (thisPath.starts_with(objectPath))
392         {
393             for (const auto& connectionInterfaces : path.second)
394             {
395                 std::vector<std::string> tempoutput(std::min(
396                     interfaces.size(), connectionInterfaces.second.size()));
397                 if (std::set_intersection(interfaces.begin(), interfaces.end(),
398                                           connectionInterfaces.second.begin(),
399                                           connectionInterfaces.second.end(),
400                                           tempoutput.begin()) !=
401                     tempoutput.begin())
402                 {
403                     output.emplace_back(thisPath);
404                     break;
405                 }
406             }
407         }
408     }
409     if (!validId)
410     {
411         throw sdbusplus::xyz::openbmc_project::Common::Error::
412             ResourceNotFound();
413     }
414     return output;
415 }
416 
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)417 std::vector<InterfaceMapType::value_type> getAssociatedSubTreeById(
418     const InterfaceMapType& interfaceMap,
419     const AssociationMaps& associationMaps, const std::string& id,
420     const std::string& objectPath, std::vector<std::string>& subtreeInterfaces,
421     const std::string& association,
422     std::vector<std::string>& endpointInterfaces)
423 {
424     std::vector<std::string> subtreePaths =
425         getSubTreePathsById(interfaceMap, id, objectPath, subtreeInterfaces);
426 
427     std::vector<InterfaceMapType::value_type> output;
428     for (const auto& subtreePath : subtreePaths)
429     {
430         // Form the association path
431         std::string associationPathStr =
432             appendPathSegment(subtreePath, association);
433         sdbusplus::message::object_path associationPath(associationPathStr);
434 
435         auto associatedSubTree =
436             getAssociatedSubTree(interfaceMap, associationMaps, associationPath,
437                                  objectPath, 0, endpointInterfaces);
438 
439         output.insert(output.end(), associatedSubTree.begin(),
440                       associatedSubTree.end());
441     }
442     return output;
443 }
444 
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)445 std::vector<std::string> getAssociatedSubTreePathsById(
446     const InterfaceMapType& interfaceMap,
447     const AssociationMaps& associationMaps, const std::string& id,
448     const std::string& objectPath, std::vector<std::string>& subtreeInterfaces,
449     const std::string& association,
450     std::vector<std::string>& endpointInterfaces)
451 {
452     std::vector<std::string> subtreePaths =
453         getSubTreePathsById(interfaceMap, id, objectPath, subtreeInterfaces);
454     std::vector<std::string> output;
455     for (const auto& subtreePath : subtreePaths)
456     {
457         // Form the association path
458         std::string associationPathStr =
459             appendPathSegment(subtreePath, association);
460         sdbusplus::message::object_path associationPath(associationPathStr);
461 
462         auto associatedSubTree = getAssociatedSubTreePaths(
463             interfaceMap, associationMaps, associationPath, objectPath, 0,
464             endpointInterfaces);
465 
466         output.insert(output.end(), associatedSubTree.begin(),
467                       associatedSubTree.end());
468     }
469 
470     return output;
471 }
472