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