Add yielding utility calls
utils.hpp provides a bunch of blocking D-Bus calls that are used
liberally in the ipmi handlers. By adding a yielding option that takes
the ipmi::Context::ptr, this can easily turn all the blocking calls into
yielding calls as the handlers get rewritten.
Tested: Used the upcoming modification of XYZ call:
Before:
ipmitool get session info
After:
ipmitool get session info
Signed-off-by: Vernon Mauery <vernon.mauery@linux.intel.com>
Change-Id: Ia537eeda060ea8e56b94b99ccb46b05f18372589
diff --git a/include/ipmid/utils.hpp b/include/ipmid/utils.hpp
index 3515eb6..cb71835 100644
--- a/include/ipmid/utils.hpp
+++ b/include/ipmid/utils.hpp
@@ -1,7 +1,9 @@
#pragma once
+#include <boost/system/error_code.hpp>
#include <chrono>
#include <ipmid/api-types.hpp>
+#include <ipmid/message.hpp>
#include <ipmid/types.hpp>
#include <optional>
#include <sdbusplus/server.hpp>
@@ -200,6 +202,180 @@
ObjectTree getAllAncestors(sdbusplus::bus::bus& bus, const std::string& path,
InterfaceList&& interfaces);
+/********* Begin co-routine yielding alternatives ***************/
+
+/** @brief Get the D-Bus Service name for the input D-Bus path
+ *
+ * @param[in] ctx - ipmi::Context::ptr
+ * @param[in] intf - D-Bus Interface
+ * @param[in] path - D-Bus Object Path
+ * @param[out] service - requested service name
+ * @return boost error code
+ *
+ */
+boost::system::error_code getService(Context::ptr ctx, const std::string& intf,
+ const std::string& path,
+ std::string& service);
+
+/** @brief Gets the D-Bus object info implementing the given interface
+ * from the given subtree.
+ * @param[in] ctx - ipmi::Context::ptr
+ * @param[in] interface - D-Bus interface.
+ * @param[in][optional] subtreePath - subtree from where the search starts.
+ * @param[in][optional] match - identifier for object.
+ * @param[out] D-Bus object with path and service name
+ * @return - boost error code object
+ */
+boost::system::error_code getDbusObject(Context::ptr ctx,
+ const std::string& interface,
+ const std::string& subtreePath,
+ const std::string& match,
+ DbusObjectInfo& dbusObject);
+
+// default for ROOT for subtreePath and std::string{} for match
+static inline boost::system::error_code
+ getDbusObject(Context::ptr ctx, const std::string& interface,
+ DbusObjectInfo& dbusObject)
+{
+ return getDbusObject(ctx, interface, ROOT, {}, dbusObject);
+}
+
+// default std::string{} for match
+static inline boost::system::error_code
+ getDbusObject(Context::ptr ctx, const std::string& interface,
+ const std::string& subtreePath, DbusObjectInfo& dbusObject)
+{
+ return getDbusObject(ctx, interface, subtreePath, {}, dbusObject);
+}
+
+/** @brief Gets the value associated with the given object
+ * and the interface.
+ * @param[in] ctx - ipmi::Context::ptr
+ * @param[in] service - D-Bus service name.
+ * @param[in] objPath - D-Bus object path.
+ * @param[in] interface - D-Bus interface.
+ * @param[in] property - name of the property.
+ * @param[out] propertyValue - value of the D-Bus property.
+ * @return - boost error code object
+ */
+template <typename Type>
+boost::system::error_code
+ getDbusProperty(Context::ptr ctx, const std::string& service,
+ const std::string& objPath, const std::string& interface,
+ const std::string& property, Type& propertyValue)
+{
+ boost::system::error_code ec;
+ auto variant = ctx->bus->yield_method_call<std::variant<Type>>(
+ ctx->yield, ec, service.c_str(), objPath.c_str(), PROP_INTF, METHOD_GET,
+ interface, property);
+ if (!ec)
+ {
+ Type* tmp = std::get_if<Type>(&variant);
+ if (tmp)
+ {
+ propertyValue = *tmp;
+ return ec;
+ }
+ // user requested incorrect type; make an error code for them
+ ec = boost::system::errc::make_error_code(
+ boost::system::errc::invalid_argument);
+ }
+ return ec;
+}
+
+/** @brief Gets all the properties associated with the given object
+ * and the interface.
+ * @param[in] ctx - ipmi::Context::ptr
+ * @param[in] service - D-Bus service name.
+ * @param[in] objPath - D-Bus object path.
+ * @param[in] interface - D-Bus interface.
+ * @param[out] properties - map of name value pair.
+ * @return - boost error code object
+ */
+boost::system::error_code getAllDbusProperties(Context::ptr ctx,
+ const std::string& service,
+ const std::string& objPath,
+ const std::string& interface,
+ PropertyMap& properties);
+
+/** @brief Sets the property value of the given object.
+ * @param[in] ctx - ipmi::Context::ptr
+ * @param[in] service - D-Bus service name.
+ * @param[in] objPath - D-Bus object path.
+ * @param[in] interface - D-Bus interface.
+ * @param[in] property - name of the property.
+ * @param[in] value - value which needs to be set.
+ * @return - boost error code object
+ */
+boost::system::error_code
+ setDbusProperty(Context::ptr ctx, const std::string& service,
+ const std::string& objPath, const std::string& interface,
+ const std::string& property, const Value& value);
+
+/** @brief Gets all the D-Bus objects from the given service root
+ * which matches the object identifier.
+ * @param[in] ctx - ipmi::Context::ptr
+ * @param[in] serviceRoot - Service root path.
+ * @param[in] interface - D-Bus interface.
+ * @param[in][optional] match - Identifier for a path.
+ * @param[out] objectree - map of object path and service info.
+ * @return - boost error code object
+ */
+boost::system::error_code getAllDbusObjects(Context::ptr ctx,
+ const std::string& serviceRoot,
+ const std::string& interface,
+ const std::string& match,
+ ObjectTree& objectTree);
+
+// default std::string{} for match
+static inline boost::system::error_code
+ getAllDbusObjects(Context::ptr ctx, const std::string& serviceRoot,
+ const std::string& interface, ObjectTree& objectTree)
+{
+ return getAllDbusObjects(ctx, serviceRoot, interface, {}, objectTree);
+}
+
+/** @brief Deletes all the D-Bus objects from the given service root
+ which matches the object identifier.
+ * @param[in] ctx - ipmi::Context::ptr
+ * @param[out] ec - boost error code object
+ * @param[in] serviceRoot - Service root path.
+ * @param[in] interface - D-Bus interface.
+ * @param[in] match - Identifier for object.
+ */
+boost::system::error_code deleteAllDbusObjects(Context::ptr ctx,
+ const std::string& serviceRoot,
+ const std::string& interface,
+ const std::string& match = {});
+
+/** @brief Gets all managed objects associated with the given object
+ * path and service.
+ * @param[in] ctx - ipmi::Context::ptr
+ * @param[out] objects - map of name value pair.
+ * @param[in] service - D-Bus service name.
+ * @param[in] objPath - D-Bus object path.
+ * @return - boost error code object
+ */
+boost::system::error_code getManagedObjects(Context::ptr ctx,
+ ObjectValueTree& objects,
+ const std::string& service,
+ const std::string& objPath);
+
+/** @brief Gets the ancestor objects of the given object
+ which implements the given interface.
+ * @param[in] ctx - ipmi::Context::ptr
+ * @param[in] path - Child D-Bus object path.
+ * @param[in] interfaces - D-Bus interface list.
+ * @param[out] ObjectTree - map of object path and service info.
+ * @return - boost error code object
+ */
+boost::system::error_code getAllAncestors(Context::ptr ctx,
+ const std::string& path,
+ const InterfaceList& interfaces,
+ ObjectTree& objectTree);
+
+/********* End co-routine yielding alternatives ***************/
+
/** @struct VariantToDoubleVisitor
* @brief Visitor to convert variants to doubles
* @details Performs a static cast on the underlying type
diff --git a/libipmid/utils.cpp b/libipmid/utils.cpp
index ed493d6..21397f9 100644
--- a/libipmid/utils.cpp
+++ b/libipmid/utils.cpp
@@ -335,18 +335,19 @@
}
}
+static inline std::string convertToString(const InterfaceList& interfaces)
+{
+ std::string intfStr;
+ for (const auto& intf : interfaces)
+ {
+ intfStr += "," + intf;
+ }
+ return intfStr;
+}
+
ObjectTree getAllAncestors(sdbusplus::bus::bus& bus, const std::string& path,
InterfaceList&& interfaces)
{
- auto convertToString = [](InterfaceList& interfaces) -> std::string {
- std::string intfStr;
- for (const auto& intf : interfaces)
- {
- intfStr += "," + intf;
- }
- return intfStr;
- };
-
auto mapperCall = bus.new_method_call(MAPPER_BUS_NAME, MAPPER_OBJ,
MAPPER_INTF, "GetAncestors");
mapperCall.append(path, interfaces);
@@ -400,6 +401,224 @@
}
} // namespace method_no_args
+
+/********* Begin co-routine yielding alternatives ***************/
+
+boost::system::error_code getService(Context::ptr ctx, std::string& service,
+ const std::string& intf,
+ const std::string& path)
+{
+ boost::system::error_code ec;
+ std::map<std::string, std::vector<std::string>> mapperResponse =
+ ctx->bus->yield_method_call<decltype(mapperResponse)>(
+ ctx->yield, ec, "xyz.openbmc_project.ObjectMapper",
+ "/xyz/openbmc_project/object_mapper",
+ "xyz.openbmc_project.ObjectMapper", "GetObject",
+ std::vector<std::string>({intf}));
+
+ if (!ec)
+ {
+ service = std::move(mapperResponse.begin()->first);
+ }
+ return ec;
+}
+
+boost::system::error_code getDbusObject(Context::ptr ctx,
+ const std::string& interface,
+ const std::string& subtreePath,
+ const std::string& match,
+ DbusObjectInfo& dbusObject)
+{
+ std::vector<DbusInterface> interfaces;
+ interfaces.emplace_back(interface);
+
+ auto depth = 0;
+ boost::system::error_code ec;
+ ObjectTree objectTree = ctx->bus->yield_method_call<ObjectTree>(
+ ctx->yield, ec, MAPPER_BUS_NAME, MAPPER_OBJ, MAPPER_INTF, "GetSubTree",
+ subtreePath, depth, interfaces);
+
+ if (ec)
+ {
+ return ec;
+ }
+
+ if (objectTree.empty())
+ {
+ log<level::ERR>("No Object has implemented the interface",
+ entry("INTERFACE=%s", interface.c_str()),
+ entry("NETFN=%x", ctx->netFn),
+ entry("CMD=%x,", ctx->cmd));
+ return boost::system::errc::make_error_code(
+ boost::system::errc::no_such_process);
+ }
+
+ // if match is empty then return the first object
+ if (match == "")
+ {
+ dbusObject = std::make_pair(
+ std::move(objectTree.begin()->first),
+ std::move(objectTree.begin()->second.begin()->first));
+ return ec;
+ }
+
+ // else search the match string in the object path
+ auto found = std::find_if(
+ objectTree.begin(), objectTree.end(), [&match](const auto& object) {
+ return (object.first.find(match) != std::string::npos);
+ });
+
+ if (found == objectTree.end())
+ {
+ log<level::ERR>("Failed to find object which matches",
+ entry("MATCH=%s", match.c_str()),
+ entry("NETFN=%x", ctx->netFn),
+ entry("CMD=%x,", ctx->cmd));
+ // set ec
+ return boost::system::errc::make_error_code(
+ boost::system::errc::no_such_file_or_directory);
+ }
+
+ dbusObject = std::make_pair(std::move(found->first),
+ std::move(found->second.begin()->first));
+ return ec;
+}
+
+boost::system::error_code getAllDbusProperties(Context::ptr ctx,
+ const std::string& service,
+ const std::string& objPath,
+ const std::string& interface,
+ PropertyMap& properties)
+{
+ boost::system::error_code ec;
+ properties = ctx->bus->yield_method_call<PropertyMap>(
+ ctx->yield, ec, service.c_str(), objPath.c_str(), PROP_INTF,
+ METHOD_GET_ALL, interface);
+ return ec;
+}
+
+boost::system::error_code
+ setDbusProperty(Context::ptr ctx, const std::string& service,
+ const std::string& objPath, const std::string& interface,
+ const std::string& property, const Value& value)
+{
+ boost::system::error_code ec;
+ ctx->bus->yield_method_call(ctx->yield, ec, service.c_str(),
+ objPath.c_str(), PROP_INTF, METHOD_SET,
+ interface, property, value);
+ return ec;
+}
+
+boost::system::error_code getAllDbusObjects(Context::ptr ctx,
+ const std::string& serviceRoot,
+ const std::string& interface,
+ const std::string& match,
+ ObjectTree& objectTree)
+{
+ boost::system::error_code ec;
+ std::vector<std::string> interfaces;
+ interfaces.emplace_back(interface);
+
+ auto depth = 0;
+
+ objectTree = ctx->bus->yield_method_call<ObjectTree>(
+ ctx->yield, ec, MAPPER_BUS_NAME, MAPPER_OBJ, MAPPER_INTF, "GetSubTree",
+ serviceRoot, depth, interfaces);
+
+ if (ec)
+ {
+ return ec;
+ }
+
+ for (auto it = objectTree.begin(); it != objectTree.end();)
+ {
+ if (it->first.find(match) == std::string::npos)
+ {
+ it = objectTree.erase(it);
+ }
+ else
+ {
+ ++it;
+ }
+ }
+
+ return ec;
+}
+
+boost::system::error_code deleteAllDbusObjects(Context::ptr ctx,
+ const std::string& serviceRoot,
+ const std::string& interface,
+ const std::string& match)
+{
+ ObjectTree objectTree;
+ boost::system::error_code ec =
+ getAllDbusObjects(ctx, serviceRoot, interface, match, objectTree);
+ if (ec)
+ {
+ return ec;
+ }
+
+ for (auto& object : objectTree)
+ {
+ ctx->bus->yield_method_call(ctx->yield, ec,
+ object.second.begin()->first, object.first,
+ DELETE_INTERFACE, "Delete");
+ if (ec)
+ {
+ log<level::ERR>("Failed to delete all objects",
+ entry("INTERFACE=%s", interface.c_str()),
+ entry("SERVICE=%s", serviceRoot.c_str()),
+ entry("NETFN=%x", ctx->netFn),
+ entry("CMD=%x,", ctx->cmd),
+ entry("ERROR=%s", ec.message().c_str()));
+ break;
+ }
+ }
+ return ec;
+}
+
+boost::system::error_code getManagedObjects(Context::ptr ctx,
+ const std::string& service,
+ const std::string& objPath,
+ ObjectValueTree& objects)
+{
+ boost::system::error_code ec;
+ objects = ctx->bus->yield_method_call<ipmi::ObjectValueTree>(
+ ctx->yield, ec, service.c_str(), objPath.c_str(),
+ "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
+ return ec;
+}
+
+boost::system::error_code getAllAncestors(Context::ptr ctx,
+ const std::string& path,
+ const InterfaceList& interfaces,
+ ObjectTree& objectTree)
+{
+ std::string interfaceList = convertToString(interfaces);
+
+ boost::system::error_code ec;
+ objectTree = ctx->bus->yield_method_call<ObjectTree>(
+ ctx->yield, ec, MAPPER_BUS_NAME, MAPPER_OBJ, MAPPER_INTF,
+ "GetAncestors", path, interfaceList);
+
+ if (ec)
+ {
+ return ec;
+ }
+
+ if (objectTree.empty())
+ {
+ log<level::ERR>("No Object has implemented the interface",
+ entry("PATH=%s", path.c_str()),
+ entry("INTERFACES=%s", interfaceList.c_str()));
+ elog<InternalFailure>();
+ }
+
+ return ec;
+}
+
+/********* End co-routine yielding alternatives ***************/
+
ipmi::Cc i2cWriteRead(std::string i2cBus, const uint8_t slaveAddr,
std::vector<uint8_t> writeData,
std::vector<uint8_t>& readBuf)
diff --git a/softoff/Makefile.am b/softoff/Makefile.am
index 00f78a5..ec938a3 100644
--- a/softoff/Makefile.am
+++ b/softoff/Makefile.am
@@ -23,8 +23,16 @@
$(SDBUSPLUS_LIBS) \
$(SDEVENTPLUS_LIBS) \
$(PHOSPHOR_LOGGING_LIBS) \
+ -lboost_coroutine \
$(PHOSPHOR_DBUS_INTERFACES_LIBS)
phosphor_softpoweroff_CXXFLAGS = \
+ -flto \
+ -Wno-psabi \
+ -DBOOST_ERROR_CODE_HEADER_ONLY \
+ -DBOOST_SYSTEM_NO_DEPRECATED \
+ -DBOOST_COROUTINES_NO_DEPRECATION_WARNING \
+ -DBOOST_ASIO_DISABLE_THREADS \
+ -DBOOST_ALL_NO_LIB
$(SYSTEMD_CFLAGS) \
$(SDBUSPLUS_CFLAGS) \
$(SDEVENTPLUS_CFLAGS) \