blob: d13e1234fe463c4809d5d59a853434985a693142 [file] [log] [blame]
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001#pragma once
2
3#include "constants.hpp"
4#include "logger.hpp"
5#include "types.hpp"
6
7#include <chrono>
8
9namespace vpd
10{
11/**
12 * @brief The namespace defines utlity methods for generic D-Bus operations.
13 */
14namespace dbusUtility
15{
16
17/**
18 * @brief An API to get Map of service and interfaces for an object path.
19 *
20 * The API returns a Map of service name and interfaces for a given pair of
21 * object path and interface list. It can be used to determine service name
22 * which implemets a particular object path and interface.
23 *
24 * Note: It will be caller's responsibility to check for empty map returned and
25 * generate appropriate error.
26 *
27 * @param [in] objectPath - Object path under the service.
28 * @param [in] interfaces - Array of interface(s).
29 * @return - A Map of service name to object to interface(s), if success.
30 * If failed, empty map.
31 */
32inline types::MapperGetObject getObjectMap(const std::string& objectPath,
33 std::span<const char*> interfaces)
34{
35 types::MapperGetObject getObjectMap;
36
37 // interface list is optional argument, hence no check required.
38 if (objectPath.empty())
39 {
40 logging::logMessage("Path value is empty, invalid call to GetObject");
41 return getObjectMap;
42 }
43
44 try
45 {
46 auto bus = sdbusplus::bus::new_default();
47 auto method = bus.new_method_call(
48 "xyz.openbmc_project.ObjectMapper",
49 "/xyz/openbmc_project/object_mapper",
50 "xyz.openbmc_project.ObjectMapper", "GetObject");
51
52 method.append(objectPath, interfaces);
53 auto result = bus.call(method);
54 result.read(getObjectMap);
55 }
56 catch (const sdbusplus::exception::SdBusError& e)
57 {
58 // logging::logMessage(e.what());
59 return getObjectMap;
60 }
61
62 return getObjectMap;
63}
64
65/**
66 * @brief An API to get property map for an interface.
67 *
68 * This API returns a map of property and its value with respect to a particular
69 * interface.
70 *
71 * Note: It will be caller's responsibility to check for empty map returned and
72 * generate appropriate error.
73 *
74 * @param[in] i_service - Service name.
75 * @param[in] i_objectPath - object path.
76 * @param[in] i_interface - Interface, for the properties to be listed.
77 *
78 * @return - A map of property and value of an interface, if success.
79 * if failed, empty map.
80 */
81inline types::PropertyMap getPropertyMap(const std::string& i_service,
82 const std::string& i_objectPath,
83 const std::string& i_interface)
84{
85 types::PropertyMap l_propertyValueMap;
86 if (i_service.empty() || i_objectPath.empty() || i_interface.empty())
87 {
88 logging::logMessage("Invalid parameters to get property map");
89 return l_propertyValueMap;
90 }
91
92 try
93 {
94 auto l_bus = sdbusplus::bus::new_default();
95 auto l_method =
96 l_bus.new_method_call(i_service.c_str(), i_objectPath.c_str(),
97 "org.freedesktop.DBus.Properties", "GetAll");
98 l_method.append(i_interface);
99 auto l_result = l_bus.call(l_method);
100 l_result.read(l_propertyValueMap);
101 }
102 catch (const sdbusplus::exception::SdBusError& l_ex)
103 {
104 logging::logMessage(l_ex.what());
105 }
106
107 return l_propertyValueMap;
108}
109
110/**
111 * @brief API to get object subtree from D-bus.
112 *
113 * The API returns the map of object, services and interfaces in the
114 * subtree that implement a certain interface. If no interfaces are provided
115 * then all the objects, services and interfaces under the subtree will
116 * be returned.
117 *
118 * Note: Depth can be 0 and interfaces can be null.
119 * It will be caller's responsibility to check for empty vector returned
120 * and generate appropriate error.
121 *
122 * @param[in] i_objectPath - Path to search for an interface.
123 * @param[in] i_depth - Maximum depth of the tree to search.
124 * @param[in] i_interfaces - List of interfaces to search.
125 *
126 * @return - A map of object and its related services and interfaces, if
127 * success. If failed, empty map.
128 */
129
Patrick Williams43fedab2025-02-03 14:28:05 -0500130inline types::MapperGetSubTree getObjectSubTree(
131 const std::string& i_objectPath, const int& i_depth,
132 const std::vector<std::string>& i_interfaces)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500133{
134 types::MapperGetSubTree l_subTreeMap;
135
136 if (i_objectPath.empty())
137 {
138 logging::logMessage("Object path is empty.");
139 return l_subTreeMap;
140 }
141
142 try
143 {
144 auto l_bus = sdbusplus::bus::new_default();
145 auto l_method = l_bus.new_method_call(
146 constants::objectMapperService, constants::objectMapperPath,
147 constants::objectMapperInf, "GetSubTree");
148 l_method.append(i_objectPath, i_depth, i_interfaces);
149 auto l_result = l_bus.call(l_method);
150 l_result.read(l_subTreeMap);
151 }
152 catch (const sdbusplus::exception::SdBusError& l_ex)
153 {
154 logging::logMessage(l_ex.what());
155 }
156
157 return l_subTreeMap;
158}
159
160/**
161 * @brief An API to read property from Dbus.
162 *
163 * The caller of the API needs to validate the validatity and correctness of the
164 * type and value of data returned. The API will just fetch and retun the data
165 * without any data validation.
166 *
167 * Note: It will be caller's responsibility to check for empty value returned
168 * and generate appropriate error if required.
169 *
170 * @param [in] serviceName - Name of the Dbus service.
171 * @param [in] objectPath - Object path under the service.
172 * @param [in] interface - Interface under which property exist.
173 * @param [in] property - Property whose value is to be read.
174 * @return - Value read from Dbus, if success.
175 * If failed, empty variant.
176 */
177inline types::DbusVariantType readDbusProperty(
178 const std::string& serviceName, const std::string& objectPath,
179 const std::string& interface, const std::string& property)
180{
181 types::DbusVariantType propertyValue;
182
183 // Mandatory fields to make a read dbus call.
184 if (serviceName.empty() || objectPath.empty() || interface.empty() ||
185 property.empty())
186 {
187 logging::logMessage(
188 "One of the parameter to make Dbus read call is empty.");
189 return propertyValue;
190 }
191
192 try
193 {
194 auto bus = sdbusplus::bus::new_default();
195 auto method =
196 bus.new_method_call(serviceName.c_str(), objectPath.c_str(),
197 "org.freedesktop.DBus.Properties", "Get");
198 method.append(interface, property);
199
200 auto result = bus.call(method);
201 result.read(propertyValue);
202 }
203 catch (const sdbusplus::exception::SdBusError& e)
204 {
205 return propertyValue;
206 }
207 return propertyValue;
208}
209
210/**
211 * @brief An API to write property on Dbus.
212 *
213 * The caller of this API needs to handle exception thrown by this method to
214 * identify any write failure. The API in no other way indicate write failure
215 * to the caller.
216 *
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500217 * @param [in] serviceName - Name of the Dbus service.
218 * @param [in] objectPath - Object path under the service.
219 * @param [in] interface - Interface under which property exist.
220 * @param [in] property - Property whose value is to be written.
221 * @param [in] propertyValue - The value to be written.
RekhaAparna01c532b182025-02-19 19:56:49 -0600222 * @return True if write on DBus is success, false otherwise.
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500223 */
RekhaAparna01c532b182025-02-19 19:56:49 -0600224inline bool writeDbusProperty(
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500225 const std::string& serviceName, const std::string& objectPath,
226 const std::string& interface, const std::string& property,
227 const types::DbusVariantType& propertyValue)
228{
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500229 try
230 {
RekhaAparna01c532b182025-02-19 19:56:49 -0600231 // Mandatory fields to make a write dbus call.
232 if (serviceName.empty() || objectPath.empty() || interface.empty() ||
233 property.empty())
234 {
235 throw std::runtime_error("Dbus write failed, Parameter empty");
236 }
237
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500238 auto bus = sdbusplus::bus::new_default();
239 auto method =
240 bus.new_method_call(serviceName.c_str(), objectPath.c_str(),
241 "org.freedesktop.DBus.Properties", "Set");
242 method.append(interface, property, propertyValue);
243 bus.call(method);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500244
RekhaAparna01c532b182025-02-19 19:56:49 -0600245 return true;
246 }
247 catch (const std::exception& l_ex)
248 {
249 logging::logMessage(
250 "DBus write failed, error: " + std::string(l_ex.what()));
251 return false;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500252 }
253}
254
255/**
256 * @brief API to publish data on PIM
257 *
258 * The API calls notify on PIM object to publlish VPD.
259 *
260 * @param[in] objectMap - Object, its interface and data.
261 * @return bool - Status of call to PIM notify.
262 */
263inline bool callPIM(types::ObjectMap&& objectMap)
264{
265 try
266 {
267 for (const auto& l_objectKeyValue : objectMap)
268 {
269 auto l_nodeHandle = objectMap.extract(l_objectKeyValue.first);
270
271 if (l_nodeHandle.key().str.find(constants::pimPath, 0) !=
272 std::string::npos)
273 {
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 */
304inline 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 */
Patrick Williams43fedab2025-02-03 14:28:05 -0500341inline types::BiosAttributeCurrentValue biosGetAttributeMethodCall(
342 const std::string& i_attributeName)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500343{
344 auto l_bus = sdbusplus::bus::new_default();
345 auto l_method = l_bus.new_method_call(
346 constants::biosConfigMgrService, constants::biosConfigMgrObjPath,
347 constants::biosConfigMgrInterface, "GetAttribute");
348 l_method.append(i_attributeName);
349
350 types::BiosGetAttrRetType l_attributeVal;
351 try
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 */
377inline 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 */
411inline 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 */
436inline 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 */
460inline 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 */
493inline 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
Priyanga Ramasamy1aad7832024-12-12 22:13:52 -0600561/**
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 */
572inline 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("CollectionStatus", 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}
Sunny Srivastava1a48f0c2025-02-19 19:02:03 +0530590
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 */
596inline 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}
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500610} // namespace dbusUtility
611} // namespace vpd