xref: /openbmc/openpower-vpd-parser/vpd-manager/include/utility/dbus_utility.hpp (revision 0755220bdae1b2972a335f904888ef691be37ea3)
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 - Array 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,std::span<const char * > interfaces)33 inline types::MapperGetObject getObjectMap(const std::string& objectPath,
34                                            std::span<const char*> 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 check if a D-Bus service is running or not.
296  *
297  * Any failure in calling the method "NameHasOwner" implies that the service is
298  * not in a running state. Hence the API returns false in case of any exception
299  * as well.
300  *
301  * @param[in] i_serviceName - D-Bus service name whose status is to be checked.
302  * @return bool - True if the service is running, false otherwise.
303  */
isServiceRunning(const std::string & i_serviceName)304 inline bool isServiceRunning(const std::string& i_serviceName)
305 {
306     bool l_retVal = false;
307 
308     try
309     {
310         auto l_bus = sdbusplus::bus::new_default();
311         auto l_method = l_bus.new_method_call(
312             "org.freedesktop.DBus", "/org/freedesktop/DBus",
313             "org.freedesktop.DBus", "NameHasOwner");
314         l_method.append(i_serviceName);
315 
316         l_bus.call(l_method).read(l_retVal);
317     }
318     catch (const sdbusplus::exception::SdBusError& l_ex)
319     {
320         logging::logMessage(
321             "Call to check service status failed with exception: " +
322             std::string(l_ex.what()));
323     }
324 
325     return l_retVal;
326 }
327 
328 /**
329  * @brief API to call "GetAttribute" method uner BIOS manager.
330  *
331  * The API reads the given attribuute from BIOS and returns a tuple of both
332  * current as well as pending value for that attribute.
333  * The API return only the current attribute value if found.
334  * API returns an empty variant of type BiosAttributeCurrentValue in case of any
335  * error.
336  *
337  * @param[in] i_attributeName - Attribute to be read.
338  * @return Tuple of PLDM attribute Type, current attribute value and pending
339  * attribute value.
340  */
biosGetAttributeMethodCall(const std::string & i_attributeName)341 inline types::BiosAttributeCurrentValue biosGetAttributeMethodCall(
342     const std::string& i_attributeName) noexcept
343 {
344     types::BiosGetAttrRetType l_attributeVal;
345     try
346     {
347         auto l_bus = sdbusplus::bus::new_default();
348         auto l_method = l_bus.new_method_call(
349             constants::biosConfigMgrService, constants::biosConfigMgrObjPath,
350             constants::biosConfigMgrInterface, "GetAttribute");
351         l_method.append(i_attributeName);
352 
353         auto l_result = l_bus.call(l_method);
354         l_result.read(std::get<0>(l_attributeVal), std::get<1>(l_attributeVal),
355                       std::get<2>(l_attributeVal));
356     }
357     catch (const sdbusplus::exception::SdBusError& l_ex)
358     {
359         logging::logMessage(
360             "Failed to read BIOS Attribute: " + i_attributeName +
361             " due to error " + std::string(l_ex.what()));
362 
363         // TODO: Log an informational PEL here.
364     }
365 
366     return std::get<1>(l_attributeVal);
367 }
368 
369 /**
370  * @brief API to check if Chassis is powered on.
371  *
372  * This API queries Phosphor Chassis State Manager to know whether
373  * Chassis is powered on.
374  *
375  * @return true if chassis is powered on, false otherwise
376  */
isChassisPowerOn()377 inline bool isChassisPowerOn()
378 {
379     auto powerState = dbusUtility::readDbusProperty(
380         "xyz.openbmc_project.State.Chassis",
381         "/xyz/openbmc_project/state/chassis0",
382         "xyz.openbmc_project.State.Chassis", "CurrentPowerState");
383 
384     if (auto curPowerState = std::get_if<std::string>(&powerState))
385     {
386         if ("xyz.openbmc_project.State.Chassis.PowerState.On" == *curPowerState)
387         {
388             return true;
389         }
390         return false;
391     }
392 
393     /*
394         TODO: Add PEL.
395         Callout: Firmware callout
396         Type: Informational
397         Description: Chassis state can't be determined, defaulting to chassis
398         off. : e.what()
399     */
400     return false;
401 }
402 
403 /**
404  * @brief API to check if host is in running state.
405  *
406  * This API reads the current host state from D-bus and returns true if the host
407  * is running.
408  *
409  * @return true if host is in running state. false otherwise.
410  */
isHostRunning()411 inline bool isHostRunning()
412 {
413     const auto l_hostState = dbusUtility::readDbusProperty(
414         constants::hostService, constants::hostObjectPath,
415         constants::hostInterface, "CurrentHostState");
416 
417     if (const auto l_currHostState = std::get_if<std::string>(&l_hostState))
418     {
419         if (*l_currHostState == constants::hostRunningState)
420         {
421             return true;
422         }
423     }
424 
425     return false;
426 }
427 
428 /**
429  * @brief API to check if BMC is in ready state.
430  *
431  * This API reads the current state of BMC from D-bus and returns true if BMC is
432  * in ready state.
433  *
434  * @return true if BMC is ready, false otherwise.
435  */
isBMCReady()436 inline bool isBMCReady()
437 {
438     const auto l_bmcState = dbusUtility::readDbusProperty(
439         constants::bmcStateService, constants::bmcZeroStateObject,
440         constants::bmcStateInterface, constants::currentBMCStateProperty);
441 
442     if (const auto l_currBMCState = std::get_if<std::string>(&l_bmcState))
443     {
444         if (*l_currBMCState == constants::bmcReadyState)
445         {
446             return true;
447         }
448     }
449 
450     return false;
451 }
452 
453 /**
454  * @brief An API to enable BMC reboot guard
455  *
456  * This API does a D-Bus method call to enable BMC reboot guard.
457  *
458  * @return On success, returns 0, otherwise returns -1.
459  */
EnableRebootGuard()460 inline int EnableRebootGuard() noexcept
461 {
462     int l_rc{constants::FAILURE};
463     try
464     {
465         auto l_bus = sdbusplus::bus::new_default();
466         auto l_method = l_bus.new_method_call(
467             constants::systemdService, constants::systemdObjectPath,
468             constants::systemdManagerInterface, "StartUnit");
469         l_method.append("reboot-guard-enable.service", "replace");
470         l_bus.call_noreply(l_method);
471         l_rc = constants::SUCCESS;
472     }
473     catch (const sdbusplus::exception::SdBusError& l_ex)
474     {
475         std::string l_errMsg =
476             "D-Bus call to enable BMC reboot guard failed for reason: ";
477         l_errMsg += l_ex.what();
478 
479         logging::logMessage(l_errMsg);
480     }
481     return l_rc;
482 }
483 
484 /**
485  * @brief An API to disable BMC reboot guard
486  *
487  * This API disables BMC reboot guard. This API has an inbuilt re-try mechanism.
488  * If Disable Reboot Guard fails, this API attempts to Disable Reboot Guard for
489  * 3 more times at an interval of 333ms.
490  *
491  * @return On success, returns 0, otherwise returns -1.
492  */
DisableRebootGuard()493 inline int DisableRebootGuard() noexcept
494 {
495     int l_rc{constants::FAILURE};
496 
497     // A lambda which executes the DBus call to disable BMC reboot guard.
498     auto l_executeDisableRebootGuard = []() -> int {
499         int l_dBusCallRc{constants::FAILURE};
500         try
501         {
502             auto l_bus = sdbusplus::bus::new_default();
503             auto l_method = l_bus.new_method_call(
504                 constants::systemdService, constants::systemdObjectPath,
505                 constants::systemdManagerInterface, "StartUnit");
506             l_method.append("reboot-guard-disable.service", "replace");
507             l_bus.call_noreply(l_method);
508             l_dBusCallRc = constants::SUCCESS;
509         }
510         catch (const sdbusplus::exception::SdBusError& l_ex)
511         {}
512         return l_dBusCallRc;
513     };
514 
515     if (constants::FAILURE == l_executeDisableRebootGuard())
516     {
517         std::function<void()> l_retryDisableRebootGuard;
518 
519         // A lambda which tries to disable BMC reboot guard for 3 times at an
520         // interval of 333 ms.
521         l_retryDisableRebootGuard = [&]() {
522             constexpr int MAX_RETRIES{3};
523             static int l_numRetries{0};
524 
525             if (l_numRetries < MAX_RETRIES)
526             {
527                 l_numRetries++;
528                 if (constants::FAILURE == l_executeDisableRebootGuard())
529                 {
530                     // sleep for 333ms before next retry. This is just a random
531                     // value so that 3 re-tries * 333ms takes ~1 second in the
532                     // worst case.
533                     const std::chrono::milliseconds l_sleepTime{333};
534                     std::this_thread::sleep_for(l_sleepTime);
535                     l_retryDisableRebootGuard();
536                 }
537                 else
538                 {
539                     l_numRetries = 0;
540                     l_rc = constants::SUCCESS;
541                 }
542             }
543             else
544             {
545                 // Failed to Disable Reboot Guard even after 3 retries.
546                 logging::logMessage("Failed to Disable Reboot Guard after " +
547                                     std::to_string(MAX_RETRIES) + " re-tries");
548                 l_numRetries = 0;
549             }
550         };
551 
552         l_retryDisableRebootGuard();
553     }
554     else
555     {
556         l_rc = constants::SUCCESS;
557     }
558     return l_rc;
559 }
560 
561 /**
562  * @brief API to notify FRU VPD Collection status.
563  *
564  * This API uses PIM's Notify method to update the given FRU VPD collection
565  * status on D-bus.
566  *
567  * @param[in] i_inventoryPath - D-bus inventory path
568  * @param[in] i_fruCollectionStatus - FRU VPD collection status.
569  *
570  * @return true if update succeeds, false otherwise.
571  */
notifyFRUCollectionStatus(const std::string & i_inventoryPath,const std::string & i_fruCollectionStatus)572 inline bool notifyFRUCollectionStatus(const std::string& i_inventoryPath,
573                                       const std::string& i_fruCollectionStatus)
574 {
575     types::ObjectMap l_objectMap;
576     types::InterfaceMap l_interfaceMap;
577     types::PropertyMap l_propertyMap;
578 
579     l_propertyMap.emplace("Status", i_fruCollectionStatus);
580     l_interfaceMap.emplace(constants::vpdCollectionInterface, l_propertyMap);
581     l_objectMap.emplace(i_inventoryPath, l_interfaceMap);
582 
583     if (!dbusUtility::callPIM(std::move(l_objectMap)))
584     {
585         return false;
586     }
587 
588     return true;
589 }
590 
591 /**
592  * @brief API to read IM keyword from Dbus.
593  *
594  * @return IM value read from Dbus, Empty in case of any error.
595  */
getImFromDbus()596 inline types::BinaryVector getImFromDbus()
597 {
598     const auto& l_retValue = dbusUtility::readDbusProperty(
599         constants::pimServiceName, constants::systemVpdInvPath,
600         constants::vsbpInf, constants::kwdIM);
601 
602     auto l_imValue = std::get_if<types::BinaryVector>(&l_retValue);
603     if (!l_imValue || (*l_imValue).size() != constants::VALUE_4)
604     {
605         return types::BinaryVector{};
606     }
607 
608     return *l_imValue;
609 }
610 
611 /**
612  * @brief API to return prefix of functional firmware image.
613  *
614  * Every functional image belongs to a series which is denoted by the first two
615  * characters of the image name. The API extracts that and returns it to the
616  * caller.
617  *
618  * @return Two character string, empty string in case of any error.
619  */
getImagePrefix()620 inline std::string getImagePrefix()
621 {
622     try
623     {
624         types::DbusVariantType l_retVal = readDbusProperty(
625             constants::objectMapperService, constants::functionalImageObjPath,
626             constants::associationInterface, "endpoints");
627 
628         auto l_listOfFunctionalPath =
629             std::get_if<std::vector<std::string>>(&l_retVal);
630 
631         if (!l_listOfFunctionalPath || (*l_listOfFunctionalPath).empty())
632         {
633             throw DbusException("failed to get functional image path.");
634         }
635 
636         for (const auto& l_imagePath : *l_listOfFunctionalPath)
637         {
638             types::DbusVariantType l_retValPriority =
639                 readDbusProperty(constants::imageUpdateService, l_imagePath,
640                                  constants::imagePrirotyInf, "Priority");
641 
642             auto l_imagePriority = std::get_if<uint8_t>(&l_retValPriority);
643             if (!l_imagePriority)
644             {
645                 throw DbusException(
646                     "failed to read functional image priority for path [" +
647                     l_imagePath + "]");
648             }
649 
650             // only interested in running image.
651             if (*l_imagePriority != constants::VALUE_0)
652             {
653                 continue;
654             }
655 
656             types::DbusVariantType l_retExVer = readDbusProperty(
657                 constants::imageUpdateService, l_imagePath,
658                 constants::imageExtendedVerInf, "ExtendedVersion");
659 
660             auto l_imageExtendedVersion = std::get_if<std::string>(&l_retExVer);
661             if (!l_imageExtendedVersion)
662             {
663                 throw DbusException(
664                     "Unable to read extended version for the functional image [" +
665                     l_imagePath + "]");
666             }
667 
668             if ((*l_imageExtendedVersion).empty() ||
669                 (*l_imageExtendedVersion).length() <= constants::VALUE_2)
670             {
671                 throw DbusException("Invalid extended version read for path [" +
672                                     l_imagePath + "]");
673             }
674 
675             // return first two character from image name.
676             return (*l_imageExtendedVersion)
677                 .substr(constants::VALUE_0, constants::VALUE_2);
678         }
679         throw std::runtime_error("No Image found with required priority.");
680     }
681     catch (const std::exception& l_ex)
682     {
683         logging::logMessage(l_ex.what());
684         return std::string{};
685     }
686 }
687 
688 /**
689  * @brief API to read DBus present property for the given inventory.
690  *
691  * @param[in] i_invObjPath - Inventory path.
692  * @return Present property value, false in case of any error.
693  */
isInventoryPresent(const std::string & i_invObjPath)694 inline bool isInventoryPresent(const std::string& i_invObjPath)
695 {
696     if (i_invObjPath.empty())
697     {
698         return false;
699     }
700 
701     const auto& l_retValue =
702         dbusUtility::readDbusProperty(constants::pimServiceName, i_invObjPath,
703                                       constants::inventoryItemInf, "Present");
704 
705     auto l_ptrPresence = std::get_if<bool>(&l_retValue);
706     if (!l_ptrPresence)
707     {
708         return false;
709     }
710 
711     return (*l_ptrPresence);
712 }
713 
714 /**
715  * @brief API to get list of sub tree paths for a given object path
716  *
717  * Given a DBus object path, this API returns a list of object paths under that
718  * object path in the DBus tree. This API calls DBus method GetSubTreePaths
719  * hosted by ObjectMapper DBus service.
720  *
721  * @param[in] i_objectPath - DBus object path.
722  * @param[in] i_depth - The maximum subtree depth for which results should be
723  * fetched. For unconstrained fetches use a depth of zero.
724  * @param[in] i_constrainingInterfaces - An array of result set constraining
725  * interfaces.
726  *
727  * @return On success, returns a std::vector<std::string> of object paths,
728  * else returns an empty vector.
729  *
730  * Note: The caller of this API should check for empty vector.
731  */
GetSubTreePaths(const std::string i_objectPath,const int i_depth=0,const std::vector<std::string> & i_constrainingInterfaces={})732 inline std::vector<std::string> GetSubTreePaths(
733     const std::string i_objectPath, const int i_depth = 0,
734     const std::vector<std::string>& i_constrainingInterfaces = {}) noexcept
735 {
736     std::vector<std::string> l_objectPaths;
737     try
738     {
739         auto l_bus = sdbusplus::bus::new_default();
740         auto l_method = l_bus.new_method_call(
741             constants::objectMapperService, constants::objectMapperPath,
742             constants::objectMapperInf, "GetSubTreePaths");
743 
744         l_method.append(i_objectPath, i_depth, i_constrainingInterfaces);
745 
746         auto l_result = l_bus.call(l_method);
747         l_result.read(l_objectPaths);
748     }
749     catch (const sdbusplus::exception::SdBusError& l_ex)
750     {
751         logging::logMessage(
752             "Error while getting GetSubTreePaths for path [" + i_objectPath +
753             "], error: " + std::string(l_ex.what()));
754     }
755     return l_objectPaths;
756 }
757 
758 /**
759  * @brief API to get Dbus service name for given connection identifier.
760  *
761  * @param[in] i_connectionId - Dbus connection ID.
762  *
763  * @return On success, returns the DBus service associated with given connection
764  * ID, empty string otherwise.
765  */
getServiceNameFromConnectionId(const std::string & i_connectionId)766 inline std::string getServiceNameFromConnectionId(
767     const std::string& i_connectionId) noexcept
768 {
769     try
770     {
771         if (i_connectionId.empty())
772         {
773             throw std::runtime_error("Empty connection ID");
774         }
775 
776         auto l_bus = sdbusplus::bus::new_default();
777 
778         // get PID corresponding to the connection ID
779         auto l_method = l_bus.new_method_call(
780             "org.freedesktop.DBus", "/org/freedesktop/DBus",
781             "org.freedesktop.DBus", "GetConnectionUnixProcessID");
782         l_method.append(i_connectionId);
783         auto l_result = l_bus.call(l_method);
784 
785         unsigned l_pid;
786         l_result.read(l_pid);
787 
788         // use PID to get corresponding unit object path
789         l_method = l_bus.new_method_call(
790             "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
791             "org.freedesktop.systemd1.Manager", "GetUnitByPID");
792         l_method.append(l_pid);
793         l_result = l_bus.call(l_method);
794 
795         sdbusplus::message::object_path l_unitObjectPath;
796         l_result.read(l_unitObjectPath);
797 
798         // use unit object path to get service name
799         l_method = l_bus.new_method_call(
800             "org.freedesktop.systemd1", std::string(l_unitObjectPath).c_str(),
801             "org.freedesktop.DBus.Properties", "Get");
802         l_method.append("org.freedesktop.systemd1.Unit", "Id");
803         l_result = l_bus.call(l_method);
804         types::DbusVariantType l_serviceNameVar;
805         l_result.read(l_serviceNameVar);
806 
807         if (auto l_serviceNameStr = std::get_if<std::string>(&l_serviceNameVar))
808         {
809             return *l_serviceNameStr;
810         }
811         else
812         {
813             throw std::runtime_error(
814                 "Invalid type received while reading service name.");
815         }
816     }
817     catch (const std::exception& l_ex)
818     {
819         logging::logMessage(
820             "Failed to get service name from connection ID: [" +
821             i_connectionId + "]. error: " + std::string(l_ex.what()));
822     }
823     return std::string{};
824 }
825 } // namespace dbusUtility
826 } // namespace vpd
827