xref: /openbmc/openpower-vpd-parser/vpd-manager/include/utility/dbus_utility.hpp (revision 3faaeb46960b4f2af8449f523de1c8763bafbab4)
1 #pragma once
2 
3 #include "constants.hpp"
4 #include "exceptions.hpp"
5 #include "logger.hpp"
6 #include "types.hpp"
7 
8 #include <chrono>
9 
10 namespace vpd
11 {
12 /**
13  * @brief The namespace defines utlity methods for generic D-Bus operations.
14  */
15 namespace dbusUtility
16 {
17 
18 /**
19  * @brief An API to get Map of service and interfaces for an object path.
20  *
21  * The API returns a Map of service name and interfaces for a given pair of
22  * object path and interface list. It can be used to determine service name
23  * which implemets a particular object path and interface.
24  *
25  * Note: It will be caller's responsibility to check for empty map returned and
26  * generate appropriate error.
27  *
28  * @param [in] objectPath - Object path under the service.
29  * @param [in] interfaces - Vector of interface(s).
30  * @return - A Map of service name to object to interface(s), if success.
31  *           If failed,  empty map.
32  */
getObjectMap(const std::string & objectPath,const std::vector<std::string> & interfaces)33 inline types::MapperGetObject getObjectMap(
34     const std::string& objectPath, const std::vector<std::string>& interfaces)
35 {
36     types::MapperGetObject getObjectMap;
37 
38     // interface list is optional argument, hence no check required.
39     if (objectPath.empty())
40     {
41         logging::logMessage("Path value is empty, invalid call to GetObject");
42         return getObjectMap;
43     }
44 
45     try
46     {
47         auto bus = sdbusplus::bus::new_default();
48         auto method = bus.new_method_call(
49             "xyz.openbmc_project.ObjectMapper",
50             "/xyz/openbmc_project/object_mapper",
51             "xyz.openbmc_project.ObjectMapper", "GetObject");
52 
53         method.append(objectPath, interfaces);
54         auto result = bus.call(method);
55         result.read(getObjectMap);
56     }
57     catch (const sdbusplus::exception::SdBusError& e)
58     {
59         // logging::logMessage(e.what());
60         return getObjectMap;
61     }
62 
63     return getObjectMap;
64 }
65 
66 /**
67  * @brief An API to get property map for an interface.
68  *
69  * This API returns a map of property and its value with respect to a particular
70  * interface.
71  *
72  * Note: It will be caller's responsibility to check for empty map returned and
73  * generate appropriate error.
74  *
75  * @param[in] i_service - Service name.
76  * @param[in] i_objectPath - object path.
77  * @param[in] i_interface - Interface, for the properties to be listed.
78  *
79  * @return - A map of property and value of an interface, if success.
80  *           if failed, empty map.
81  */
getPropertyMap(const std::string & i_service,const std::string & i_objectPath,const std::string & i_interface)82 inline types::PropertyMap getPropertyMap(const std::string& i_service,
83                                          const std::string& i_objectPath,
84                                          const std::string& i_interface)
85 {
86     types::PropertyMap l_propertyValueMap;
87     if (i_service.empty() || i_objectPath.empty() || i_interface.empty())
88     {
89         logging::logMessage("Invalid parameters to get property map");
90         return l_propertyValueMap;
91     }
92 
93     try
94     {
95         auto l_bus = sdbusplus::bus::new_default();
96         auto l_method =
97             l_bus.new_method_call(i_service.c_str(), i_objectPath.c_str(),
98                                   "org.freedesktop.DBus.Properties", "GetAll");
99         l_method.append(i_interface);
100         auto l_result = l_bus.call(l_method);
101         l_result.read(l_propertyValueMap);
102     }
103     catch (const sdbusplus::exception::SdBusError& l_ex)
104     {
105         logging::logMessage(l_ex.what());
106     }
107 
108     return l_propertyValueMap;
109 }
110 
111 /**
112  * @brief API to get object subtree from D-bus.
113  *
114  * The API returns the map of object, services and interfaces in the
115  * subtree that implement a certain interface. If no interfaces are provided
116  * then all the objects, services and interfaces under the subtree will
117  * be returned.
118  *
119  * Note: Depth can be 0 and interfaces can be null.
120  * It will be caller's responsibility to check for empty vector returned
121  * and generate appropriate error.
122  *
123  * @param[in] i_objectPath - Path to search for an interface.
124  * @param[in] i_depth - Maximum depth of the tree to search.
125  * @param[in] i_interfaces - List of interfaces to search.
126  *
127  * @return - A map of object and its related services and interfaces, if
128  *           success. If failed, empty map.
129  */
130 
getObjectSubTree(const std::string & i_objectPath,const int & i_depth,const std::vector<std::string> & i_interfaces)131 inline types::MapperGetSubTree getObjectSubTree(
132     const std::string& i_objectPath, const int& i_depth,
133     const std::vector<std::string>& i_interfaces)
134 {
135     types::MapperGetSubTree l_subTreeMap;
136 
137     if (i_objectPath.empty())
138     {
139         logging::logMessage("Object path is empty.");
140         return l_subTreeMap;
141     }
142 
143     try
144     {
145         auto l_bus = sdbusplus::bus::new_default();
146         auto l_method = l_bus.new_method_call(
147             constants::objectMapperService, constants::objectMapperPath,
148             constants::objectMapperInf, "GetSubTree");
149         l_method.append(i_objectPath, i_depth, i_interfaces);
150         auto l_result = l_bus.call(l_method);
151         l_result.read(l_subTreeMap);
152     }
153     catch (const sdbusplus::exception::SdBusError& l_ex)
154     {
155         logging::logMessage(l_ex.what());
156     }
157 
158     return l_subTreeMap;
159 }
160 
161 /**
162  * @brief An API to read property from Dbus.
163  *
164  * The caller of the API needs to validate the validatity and correctness of the
165  * type and value of data returned. The API will just fetch and retun the data
166  * without any data validation.
167  *
168  * Note: It will be caller's responsibility to check for empty value returned
169  * and generate appropriate error if required.
170  *
171  * @param [in] serviceName - Name of the Dbus service.
172  * @param [in] objectPath - Object path under the service.
173  * @param [in] interface - Interface under which property exist.
174  * @param [in] property - Property whose value is to be read.
175  * @return - Value read from Dbus, if success.
176  *           If failed, empty variant.
177  */
readDbusProperty(const std::string & serviceName,const std::string & objectPath,const std::string & interface,const std::string & property)178 inline types::DbusVariantType readDbusProperty(
179     const std::string& serviceName, const std::string& objectPath,
180     const std::string& interface, const std::string& property)
181 {
182     types::DbusVariantType propertyValue;
183 
184     // Mandatory fields to make a read dbus call.
185     if (serviceName.empty() || objectPath.empty() || interface.empty() ||
186         property.empty())
187     {
188         logging::logMessage(
189             "One of the parameter to make Dbus read call is empty.");
190         return propertyValue;
191     }
192 
193     try
194     {
195         auto bus = sdbusplus::bus::new_default();
196         auto method =
197             bus.new_method_call(serviceName.c_str(), objectPath.c_str(),
198                                 "org.freedesktop.DBus.Properties", "Get");
199         method.append(interface, property);
200 
201         auto result = bus.call(method);
202         result.read(propertyValue);
203     }
204     catch (const sdbusplus::exception::SdBusError& e)
205     {
206         return propertyValue;
207     }
208     return propertyValue;
209 }
210 
211 /**
212  * @brief An API to write property on Dbus.
213  *
214  * The caller of this API needs to handle exception thrown by this method to
215  * identify any write failure. The API in no other way indicate write  failure
216  * to the caller.
217  *
218  * @param [in] serviceName - Name of the Dbus service.
219  * @param [in] objectPath - Object path under the service.
220  * @param [in] interface - Interface under which property exist.
221  * @param [in] property - Property whose value is to be written.
222  * @param [in] propertyValue - The value to be written.
223  * @return True if write on DBus is success, false otherwise.
224  */
writeDbusProperty(const std::string & serviceName,const std::string & objectPath,const std::string & interface,const std::string & property,const types::DbusVariantType & propertyValue)225 inline bool writeDbusProperty(
226     const std::string& serviceName, const std::string& objectPath,
227     const std::string& interface, const std::string& property,
228     const types::DbusVariantType& propertyValue)
229 {
230     try
231     {
232         // Mandatory fields to make a write dbus call.
233         if (serviceName.empty() || objectPath.empty() || interface.empty() ||
234             property.empty())
235         {
236             throw std::runtime_error("Dbus write failed, Parameter empty");
237         }
238 
239         auto bus = sdbusplus::bus::new_default();
240         auto method =
241             bus.new_method_call(serviceName.c_str(), objectPath.c_str(),
242                                 "org.freedesktop.DBus.Properties", "Set");
243         method.append(interface, property, propertyValue);
244         bus.call(method);
245 
246         return true;
247     }
248     catch (const std::exception& l_ex)
249     {
250         logging::logMessage(
251             "DBus write failed, error: " + std::string(l_ex.what()));
252         return false;
253     }
254 }
255 
256 /**
257  * @brief API to publish data on PIM
258  *
259  * The API calls notify on PIM object to publlish VPD.
260  *
261  * @param[in] objectMap - Object, its interface and data.
262  * @return bool - Status of call to PIM notify.
263  */
callPIM(types::ObjectMap && objectMap)264 inline bool callPIM(types::ObjectMap&& objectMap)
265 {
266     try
267     {
268         for (const auto& l_objectKeyValue : objectMap)
269         {
270             if (l_objectKeyValue.first.str.find(constants::pimPath, 0) !=
271                 std::string::npos)
272             {
273                 auto l_nodeHandle = objectMap.extract(l_objectKeyValue.first);
274                 l_nodeHandle.key() = l_nodeHandle.key().str.replace(
275                     0, std::strlen(constants::pimPath), "");
276                 objectMap.insert(std::move(l_nodeHandle));
277             }
278         }
279 
280         auto bus = sdbusplus::bus::new_default();
281         auto pimMsg =
282             bus.new_method_call(constants::pimServiceName, constants::pimPath,
283                                 constants::pimIntf, "Notify");
284         pimMsg.append(std::move(objectMap));
285         bus.call(pimMsg);
286     }
287     catch (const sdbusplus::exception::SdBusError& e)
288     {
289         return false;
290     }
291     return true;
292 }
293 
294 /*
295  * @brief API to update the VPD data on dbus.
296  *
297  * This method internally decides which method to call for dbus update
298  * based on the configuration.
299  * NOTE- In future, support else part to call other service/method.
300  *
301  * @param[in] i_objectMap - map of object and it's data
302  *
303  * @return bool - true if success, false otherwise.
304  */
publishVpdOnDBus(types::ObjectMap && i_objectMap)305 inline bool publishVpdOnDBus(types::ObjectMap&& i_objectMap)
306 {
307     // Initialise it as default, otherwise compiler fails it assuming if
308     // "if condition" doesn't match then it will be left uninitialised.
309     // Once other service will be supported, it will be overwritten in
310     // else section.
311     [[maybe_unused]] bool (*dBusCall)(types::ObjectMap&&) = callPIM;
312 
313 #if IBM_SYSTEM
314     dBusCall = callPIM;
315 #endif
316 
317     auto reply = dBusCall(move(i_objectMap));
318 
319     return reply;
320 }
321 
322 /**
323  * @brief API to check if a D-Bus service is running or not.
324  *
325  * Any failure in calling the method "NameHasOwner" implies that the service is
326  * not in a running state. Hence the API returns false in case of any exception
327  * as well.
328  *
329  * @param[in] i_serviceName - D-Bus service name whose status is to be checked.
330  * @return bool - True if the service is running, false otherwise.
331  */
isServiceRunning(const std::string & i_serviceName)332 inline bool isServiceRunning(const std::string& i_serviceName)
333 {
334     bool l_retVal = false;
335 
336     try
337     {
338         auto l_bus = sdbusplus::bus::new_default();
339         auto l_method = l_bus.new_method_call(
340             "org.freedesktop.DBus", "/org/freedesktop/DBus",
341             "org.freedesktop.DBus", "NameHasOwner");
342         l_method.append(i_serviceName);
343 
344         l_bus.call(l_method).read(l_retVal);
345     }
346     catch (const sdbusplus::exception::SdBusError& l_ex)
347     {
348         logging::logMessage(
349             "Call to check service status failed with exception: " +
350             std::string(l_ex.what()));
351     }
352 
353     return l_retVal;
354 }
355 
356 /**
357  * @brief API to call "GetAttribute" method uner BIOS manager.
358  *
359  * The API reads the given attribuute from BIOS and returns a tuple of both
360  * current as well as pending value for that attribute.
361  * The API return only the current attribute value if found.
362  * API returns an empty variant of type BiosAttributeCurrentValue in case of any
363  * error.
364  *
365  * @param[in] i_attributeName - Attribute to be read.
366  * @return Tuple of PLDM attribute Type, current attribute value and pending
367  * attribute value.
368  */
biosGetAttributeMethodCall(const std::string & i_attributeName)369 inline types::BiosAttributeCurrentValue biosGetAttributeMethodCall(
370     const std::string& i_attributeName) noexcept
371 {
372     types::BiosGetAttrRetType l_attributeVal;
373     try
374     {
375         auto l_bus = sdbusplus::bus::new_default();
376         auto l_method = l_bus.new_method_call(
377             constants::biosConfigMgrService, constants::biosConfigMgrObjPath,
378             constants::biosConfigMgrInterface, "GetAttribute");
379         l_method.append(i_attributeName);
380 
381         auto l_result = l_bus.call(l_method);
382         l_result.read(std::get<0>(l_attributeVal), std::get<1>(l_attributeVal),
383                       std::get<2>(l_attributeVal));
384     }
385     catch (const sdbusplus::exception::SdBusError& l_ex)
386     {
387         logging::logMessage(
388             "Failed to read BIOS Attribute: " + i_attributeName +
389             " due to error " + std::string(l_ex.what()));
390 
391         // TODO: Log an informational PEL here.
392     }
393 
394     return std::get<1>(l_attributeVal);
395 }
396 
397 /**
398  * @brief API to check if Chassis is powered on.
399  *
400  * This API queries Phosphor Chassis State Manager to know whether
401  * Chassis is powered on.
402  *
403  * @return true if chassis is powered on, false otherwise
404  */
isChassisPowerOn()405 inline bool isChassisPowerOn()
406 {
407     auto powerState = dbusUtility::readDbusProperty(
408         "xyz.openbmc_project.State.Chassis",
409         "/xyz/openbmc_project/state/chassis0",
410         "xyz.openbmc_project.State.Chassis", "CurrentPowerState");
411 
412     if (auto curPowerState = std::get_if<std::string>(&powerState))
413     {
414         if ("xyz.openbmc_project.State.Chassis.PowerState.On" == *curPowerState)
415         {
416             return true;
417         }
418         return false;
419     }
420 
421     /*
422         TODO: Add PEL.
423         Callout: Firmware callout
424         Type: Informational
425         Description: Chassis state can't be determined, defaulting to chassis
426         off. : e.what()
427     */
428     return false;
429 }
430 
431 /**
432  * @brief API to check if host is in running state.
433  *
434  * This API reads the current host state from D-bus and returns true if the host
435  * is running.
436  *
437  * @return true if host is in running state. false otherwise.
438  */
isHostRunning()439 inline bool isHostRunning()
440 {
441     const auto l_hostState = dbusUtility::readDbusProperty(
442         constants::hostService, constants::hostObjectPath,
443         constants::hostInterface, "CurrentHostState");
444 
445     if (const auto l_currHostState = std::get_if<std::string>(&l_hostState))
446     {
447         if (*l_currHostState == constants::hostRunningState)
448         {
449             return true;
450         }
451     }
452 
453     return false;
454 }
455 
456 /**
457  * @brief API to check if BMC is in ready state.
458  *
459  * This API reads the current state of BMC from D-bus and returns true if BMC is
460  * in ready state.
461  *
462  * @return true if BMC is ready, false otherwise.
463  */
isBMCReady()464 inline bool isBMCReady()
465 {
466     const auto l_bmcState = dbusUtility::readDbusProperty(
467         constants::bmcStateService, constants::bmcZeroStateObject,
468         constants::bmcStateInterface, constants::currentBMCStateProperty);
469 
470     if (const auto l_currBMCState = std::get_if<std::string>(&l_bmcState))
471     {
472         if (*l_currBMCState == constants::bmcReadyState)
473         {
474             return true;
475         }
476     }
477 
478     return false;
479 }
480 
481 /**
482  * @brief An API to enable BMC reboot guard
483  *
484  * This API does a D-Bus method call to enable BMC reboot guard.
485  *
486  * @return On success, returns 0, otherwise returns -1.
487  */
EnableRebootGuard()488 inline int EnableRebootGuard() noexcept
489 {
490     int l_rc{constants::FAILURE};
491     try
492     {
493         auto l_bus = sdbusplus::bus::new_default();
494         auto l_method = l_bus.new_method_call(
495             constants::systemdService, constants::systemdObjectPath,
496             constants::systemdManagerInterface, "StartUnit");
497         l_method.append("reboot-guard-enable.service", "replace");
498         l_bus.call_noreply(l_method);
499         l_rc = constants::SUCCESS;
500     }
501     catch (const sdbusplus::exception::SdBusError& l_ex)
502     {
503         std::string l_errMsg =
504             "D-Bus call to enable BMC reboot guard failed for reason: ";
505         l_errMsg += l_ex.what();
506 
507         logging::logMessage(l_errMsg);
508     }
509     return l_rc;
510 }
511 
512 /**
513  * @brief An API to disable BMC reboot guard
514  *
515  * This API disables BMC reboot guard. This API has an inbuilt re-try mechanism.
516  * If Disable Reboot Guard fails, this API attempts to Disable Reboot Guard for
517  * 3 more times at an interval of 333ms.
518  *
519  * @return On success, returns 0, otherwise returns -1.
520  */
DisableRebootGuard()521 inline int DisableRebootGuard() noexcept
522 {
523     int l_rc{constants::FAILURE};
524 
525     // A lambda which executes the DBus call to disable BMC reboot guard.
526     auto l_executeDisableRebootGuard = []() -> int {
527         int l_dBusCallRc{constants::FAILURE};
528         try
529         {
530             auto l_bus = sdbusplus::bus::new_default();
531             auto l_method = l_bus.new_method_call(
532                 constants::systemdService, constants::systemdObjectPath,
533                 constants::systemdManagerInterface, "StartUnit");
534             l_method.append("reboot-guard-disable.service", "replace");
535             l_bus.call_noreply(l_method);
536             l_dBusCallRc = constants::SUCCESS;
537         }
538         catch (const sdbusplus::exception::SdBusError& l_ex)
539         {}
540         return l_dBusCallRc;
541     };
542 
543     if (constants::FAILURE == l_executeDisableRebootGuard())
544     {
545         std::function<void()> l_retryDisableRebootGuard;
546 
547         // A lambda which tries to disable BMC reboot guard for 3 times at an
548         // interval of 333 ms.
549         l_retryDisableRebootGuard = [&]() {
550             constexpr int MAX_RETRIES{3};
551             static int l_numRetries{0};
552 
553             if (l_numRetries < MAX_RETRIES)
554             {
555                 l_numRetries++;
556                 if (constants::FAILURE == l_executeDisableRebootGuard())
557                 {
558                     // sleep for 333ms before next retry. This is just a random
559                     // value so that 3 re-tries * 333ms takes ~1 second in the
560                     // worst case.
561                     const std::chrono::milliseconds l_sleepTime{333};
562                     std::this_thread::sleep_for(l_sleepTime);
563                     l_retryDisableRebootGuard();
564                 }
565                 else
566                 {
567                     l_numRetries = 0;
568                     l_rc = constants::SUCCESS;
569                 }
570             }
571             else
572             {
573                 // Failed to Disable Reboot Guard even after 3 retries.
574                 logging::logMessage("Failed to Disable Reboot Guard after " +
575                                     std::to_string(MAX_RETRIES) + " re-tries");
576                 l_numRetries = 0;
577             }
578         };
579 
580         l_retryDisableRebootGuard();
581     }
582     else
583     {
584         l_rc = constants::SUCCESS;
585     }
586     return l_rc;
587 }
588 
589 /**
590  * @brief API to notify FRU VPD Collection status.
591  *
592  * This API uses PIM's Notify method to update the given FRU VPD collection
593  * status on D-bus.
594  *
595  * @param[in] i_inventoryPath - D-bus inventory path
596  * @param[in] i_fruCollectionStatus - FRU VPD collection status.
597  *
598  * @return true if update succeeds, false otherwise.
599  */
notifyFRUCollectionStatus(const std::string & i_inventoryPath,const std::string & i_fruCollectionStatus)600 inline bool notifyFRUCollectionStatus(const std::string& i_inventoryPath,
601                                       const std::string& i_fruCollectionStatus)
602 {
603     types::ObjectMap l_objectMap;
604     types::InterfaceMap l_interfaceMap;
605     types::PropertyMap l_propertyMap;
606 
607     l_propertyMap.emplace("Status", i_fruCollectionStatus);
608     l_interfaceMap.emplace(constants::vpdCollectionInterface, l_propertyMap);
609     l_objectMap.emplace(i_inventoryPath, l_interfaceMap);
610 
611     // Call method to update the dbus
612     if (!dbusUtility::publishVpdOnDBus(std::move(l_objectMap)))
613     {
614         return false;
615     }
616 
617     return true;
618 }
619 
620 /**
621  * @brief API to read IM keyword from Dbus.
622  *
623  * @return IM value read from Dbus, Empty in case of any error.
624  */
getImFromDbus()625 inline types::BinaryVector getImFromDbus()
626 {
627     const auto& l_retValue = dbusUtility::readDbusProperty(
628         constants::pimServiceName, constants::systemVpdInvPath,
629         constants::vsbpInf, constants::kwdIM);
630 
631     auto l_imValue = std::get_if<types::BinaryVector>(&l_retValue);
632     if (!l_imValue || (*l_imValue).size() != constants::VALUE_4)
633     {
634         return types::BinaryVector{};
635     }
636 
637     return *l_imValue;
638 }
639 
640 /**
641  * @brief API to return prefix of functional firmware image.
642  *
643  * Every functional image belongs to a series which is denoted by the first two
644  * characters of the image name. The API extracts that and returns it to the
645  * caller.
646  *
647  * @return Two character string, empty string in case of any error.
648  */
getImagePrefix()649 inline std::string getImagePrefix()
650 {
651     try
652     {
653         types::DbusVariantType l_retVal = readDbusProperty(
654             constants::objectMapperService, constants::functionalImageObjPath,
655             constants::associationInterface, "endpoints");
656 
657         auto l_listOfFunctionalPath =
658             std::get_if<std::vector<std::string>>(&l_retVal);
659 
660         if (!l_listOfFunctionalPath || (*l_listOfFunctionalPath).empty())
661         {
662             throw DbusException("failed to get functional image path.");
663         }
664 
665         for (const auto& l_imagePath : *l_listOfFunctionalPath)
666         {
667             types::DbusVariantType l_retValPriority =
668                 readDbusProperty(constants::imageUpdateService, l_imagePath,
669                                  constants::imagePrirotyInf, "Priority");
670 
671             auto l_imagePriority = std::get_if<uint8_t>(&l_retValPriority);
672             if (!l_imagePriority)
673             {
674                 throw DbusException(
675                     "failed to read functional image priority for path [" +
676                     l_imagePath + "]");
677             }
678 
679             // only interested in running image.
680             if (*l_imagePriority != constants::VALUE_0)
681             {
682                 continue;
683             }
684 
685             types::DbusVariantType l_retExVer = readDbusProperty(
686                 constants::imageUpdateService, l_imagePath,
687                 constants::imageExtendedVerInf, "ExtendedVersion");
688 
689             auto l_imageExtendedVersion = std::get_if<std::string>(&l_retExVer);
690             if (!l_imageExtendedVersion)
691             {
692                 throw DbusException(
693                     "Unable to read extended version for the functional image [" +
694                     l_imagePath + "]");
695             }
696 
697             if ((*l_imageExtendedVersion).empty() ||
698                 (*l_imageExtendedVersion).length() <= constants::VALUE_2)
699             {
700                 throw DbusException("Invalid extended version read for path [" +
701                                     l_imagePath + "]");
702             }
703 
704             // return first two character from image name.
705             return (*l_imageExtendedVersion)
706                 .substr(constants::VALUE_0, constants::VALUE_2);
707         }
708         throw std::runtime_error("No Image found with required priority.");
709     }
710     catch (const std::exception& l_ex)
711     {
712         logging::logMessage(l_ex.what());
713         return std::string{};
714     }
715 }
716 
717 /**
718  * @brief API to read DBus present property for the given inventory.
719  *
720  * @param[in] i_invObjPath - Inventory path.
721  * @return Present property value, false in case of any error.
722  */
isInventoryPresent(const std::string & i_invObjPath)723 inline bool isInventoryPresent(const std::string& i_invObjPath)
724 {
725     if (i_invObjPath.empty())
726     {
727         return false;
728     }
729 
730     const auto& l_retValue =
731         dbusUtility::readDbusProperty(constants::pimServiceName, i_invObjPath,
732                                       constants::inventoryItemInf, "Present");
733 
734     auto l_ptrPresence = std::get_if<bool>(&l_retValue);
735     if (!l_ptrPresence)
736     {
737         return false;
738     }
739 
740     return (*l_ptrPresence);
741 }
742 
743 /**
744  * @brief API to get list of sub tree paths for a given object path
745  *
746  * Given a DBus object path, this API returns a list of object paths under that
747  * object path in the DBus tree. This API calls DBus method GetSubTreePaths
748  * hosted by ObjectMapper DBus service.
749  *
750  * @param[in] i_objectPath - DBus object path.
751  * @param[in] i_depth - The maximum subtree depth for which results should be
752  * fetched. For unconstrained fetches use a depth of zero.
753  * @param[in] i_constrainingInterfaces - An array of result set constraining
754  * interfaces.
755  *
756  * @return On success, returns a std::vector<std::string> of object paths,
757  * else returns an empty vector.
758  *
759  * Note: The caller of this API should check for empty vector.
760  */
GetSubTreePaths(const std::string i_objectPath,const int i_depth=0,const std::vector<std::string> & i_constrainingInterfaces={})761 inline std::vector<std::string> GetSubTreePaths(
762     const std::string i_objectPath, const int i_depth = 0,
763     const std::vector<std::string>& i_constrainingInterfaces = {}) noexcept
764 {
765     std::vector<std::string> l_objectPaths;
766     try
767     {
768         auto l_bus = sdbusplus::bus::new_default();
769         auto l_method = l_bus.new_method_call(
770             constants::objectMapperService, constants::objectMapperPath,
771             constants::objectMapperInf, "GetSubTreePaths");
772 
773         l_method.append(i_objectPath, i_depth, i_constrainingInterfaces);
774 
775         auto l_result = l_bus.call(l_method);
776         l_result.read(l_objectPaths);
777     }
778     catch (const sdbusplus::exception::SdBusError& l_ex)
779     {
780         logging::logMessage(
781             "Error while getting GetSubTreePaths for path [" + i_objectPath +
782             "], error: " + std::string(l_ex.what()));
783     }
784     return l_objectPaths;
785 }
786 
787 /**
788  * @brief API to get Dbus service name for given connection identifier.
789  *
790  * @param[in] i_connectionId - Dbus connection ID.
791  *
792  * @return On success, returns the DBus service associated with given connection
793  * ID, empty string otherwise.
794  */
getServiceNameFromConnectionId(const std::string & i_connectionId)795 inline std::string getServiceNameFromConnectionId(
796     const std::string& i_connectionId) noexcept
797 {
798     try
799     {
800         if (i_connectionId.empty())
801         {
802             throw std::runtime_error("Empty connection ID");
803         }
804 
805         auto l_bus = sdbusplus::bus::new_default();
806 
807         // get PID corresponding to the connection ID
808         auto l_method = l_bus.new_method_call(
809             "org.freedesktop.DBus", "/org/freedesktop/DBus",
810             "org.freedesktop.DBus", "GetConnectionUnixProcessID");
811         l_method.append(i_connectionId);
812         auto l_result = l_bus.call(l_method);
813 
814         unsigned l_pid;
815         l_result.read(l_pid);
816 
817         // use PID to get corresponding unit object path
818         l_method = l_bus.new_method_call(
819             "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
820             "org.freedesktop.systemd1.Manager", "GetUnitByPID");
821         l_method.append(l_pid);
822         l_result = l_bus.call(l_method);
823 
824         sdbusplus::message::object_path l_unitObjectPath;
825         l_result.read(l_unitObjectPath);
826 
827         // use unit object path to get service name
828         l_method = l_bus.new_method_call(
829             "org.freedesktop.systemd1", std::string(l_unitObjectPath).c_str(),
830             "org.freedesktop.DBus.Properties", "Get");
831         l_method.append("org.freedesktop.systemd1.Unit", "Id");
832         l_result = l_bus.call(l_method);
833         types::DbusVariantType l_serviceNameVar;
834         l_result.read(l_serviceNameVar);
835 
836         if (auto l_serviceNameStr = std::get_if<std::string>(&l_serviceNameVar))
837         {
838             return *l_serviceNameStr;
839         }
840         else
841         {
842             throw std::runtime_error(
843                 "Invalid type received while reading service name.");
844         }
845     }
846     catch (const std::exception& l_ex)
847     {
848         logging::logMessage(
849             "Failed to get service name from connection ID: [" +
850             i_connectionId + "]. error: " + std::string(l_ex.what()));
851     }
852     return std::string{};
853 }
854 
855 /**
856  * @brief API to get the BMC position.
857  *
858  * This API queries dbus service to find the BMC position.
859  *
860  * @return BMC position on success, otherwise returns default value.
861  */
getBmcPosition()862 inline size_t getBmcPosition() noexcept
863 {
864     size_t l_bmcPosition = std::numeric_limits<size_t>::max();
865     try
866     {
867         // TODO: Return value based on data read from the cable management
868         // service.
869     }
870     catch (const sdbusplus::exception::SdBusError& l_ex)
871     {
872         std::string l_errMsg =
873             "Failed to query BMC position via DBus. Reason: ";
874         l_errMsg += l_ex.what();
875 
876         Logger::getLoggerInstance()->logMessage(l_errMsg);
877     }
878     return l_bmcPosition;
879 }
880 } // namespace dbusUtility
881 } // namespace vpd
882