diff options
Diffstat (limited to 'meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/vm/0008-Apply-async-dbus-API.patch')
-rw-r--r-- | meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/vm/0008-Apply-async-dbus-API.patch | 369 |
1 files changed, 369 insertions, 0 deletions
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/vm/0008-Apply-async-dbus-API.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/vm/0008-Apply-async-dbus-API.patch new file mode 100644 index 000000000..f05629776 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/vm/0008-Apply-async-dbus-API.patch @@ -0,0 +1,369 @@ +From 5512b7bcdb691133d0321779f7d20f5cc1eb3188 Mon Sep 17 00:00:00 2001 +From: Przemyslaw Czarnowski <przemyslaw.hawrylewicz.czarnowski@intel.com> +Date: Mon, 7 Mar 2022 19:22:18 +0100 +Subject: [PATCH] Apply async dbus API + +In order to support new asynchronous dbus API for Virtual Media generic +listener for signals has been introduced. +Using AsynsResp object lifetime expanded by listener waiting for signal +response is passed to user once signal arrives. +This patch covers mounting legacy mode and unmounting both legacy and +proxy mode. + +Tested: +Manually in companion with updated service signal is catched and redfish +releases connection with appropriate reply + +Signed-off-by: Przemyslaw Czarnowski <przemyslaw.hawrylewicz.czarnowski@intel.com> +--- + redfish-core/lib/virtual_media.hpp | 203 ++++++++++++++++++++++++----- + 1 file changed, 174 insertions(+), 29 deletions(-) + +diff --git a/redfish-core/lib/virtual_media.hpp b/redfish-core/lib/virtual_media.hpp +index 8da40eea..e6ee2b6a 100644 +--- a/redfish-core/lib/virtual_media.hpp ++++ b/redfish-core/lib/virtual_media.hpp +@@ -15,10 +15,18 @@ + */ + #pragma once + ++#include "logging.hpp" ++ + #include <app.hpp> ++#include <async_resp.hpp> ++#include <boost/asio/error.hpp> ++#include <boost/asio/io_context.hpp> ++#include <boost/asio/steady_timer.hpp> + #include <boost/container/flat_map.hpp> + #include <boost/process/async_pipe.hpp> + #include <boost/type_traits/has_dereference.hpp> ++#include <dbus_singleton.hpp> ++#include <sdbusplus/bus/match.hpp> + #include <sdbusplus/message/native_types.hpp> + #include <utils/json_utils.hpp> + // for GetObjectType and ManagedObjectType +@@ -28,10 +36,24 @@ + #include <registries/privilege_registry.hpp> + + #include <chrono> ++#include <memory> ++#include <optional> + + namespace redfish + { + ++const char* legacyMode = "Legacy"; ++const char* proxyMode = "Proxy"; ++ ++std::string getModeName(bool isLegacy) ++{ ++ if (isLegacy) ++ { ++ return legacyMode; ++ } ++ return proxyMode; ++} ++ + /** + * @brief Function parses getManagedObject response, finds item, makes generic + * validation and invokes callback handler on this item. +@@ -109,6 +131,7 @@ void findItemAndRunHandler(const std::shared_ptr<bmcweb::AsyncResp>& aResp, + + if (handler(service, aResp, item) == true) + { ++ + return; + } + } +@@ -382,7 +405,7 @@ inline void getVmData(const std::shared_ptr<bmcweb::AsyncResp>& aResp, + actionsId += "/Actions"; + + // Check if dbus path is Legacy type +- if (mode.filename() == "Legacy") ++ if (mode.filename() == legacyMode) + { + aResp->res.jsonValue["Actions"]["#VirtualMedia.InsertMedia"] + ["target"] = +@@ -760,6 +783,111 @@ class Pipe + Buffer buffer; + }; + ++/** ++ * @brief holder for dbus signal matchers ++ */ ++struct MatchWrapper ++{ ++ void stop() ++ { ++ timer->cancel(); ++ matcher = std::nullopt; ++ } ++ ++ ~MatchWrapper() ++ { ++ BMCWEB_LOG_DEBUG << "~MatchWrapper()"; ++ }; ++ std::optional<sdbusplus::bus::match::match> matcher{}; ++ std::optional<boost::asio::steady_timer> timer; ++}; ++ ++/** ++ * @brief Function starts waiting for signal completion ++ */ ++static inline std::shared_ptr<MatchWrapper> ++ doListenForCompletion(const std::string& name, ++ const std::string& objectPath, ++ const std::string& action, bool legacy, ++ std::shared_ptr<bmcweb::AsyncResp> asyncResp) ++{ ++ BMCWEB_LOG_DEBUG << "Start Listening for completion : " << action; ++ std::string matcherString = sdbusplus::bus::match::rules::type::signal(); ++ ++ std::string interface = ++ std::string("xyz.openbmc_project.VirtualMedia.") + getModeName(legacy); ++ ++ matcherString += sdbusplus::bus::match::rules::interface(interface); ++ matcherString += sdbusplus::bus::match::rules::member("Completion"); ++ matcherString += sdbusplus::bus::match::rules::sender( ++ "xyz.openbmc_project.VirtualMedia"); ++ matcherString += sdbusplus::bus::match::rules::path(objectPath); ++ ++ auto matchWrapper = std::make_shared<MatchWrapper>(); ++ auto matchHandler = [asyncResp = std::move(asyncResp), name, action, ++ objectPath, ++ matchWrapper](sdbusplus::message::message& m) { ++ int errorCode; ++ try ++ { ++ BMCWEB_LOG_INFO << "Completion signal from " << m.get_path() ++ << " has been received"; ++ ++ m.read(errorCode); ++ switch (errorCode) ++ { ++ case 0: // success ++ BMCWEB_LOG_INFO << "Signal received: Success"; ++ messages::success(asyncResp->res); ++ break; ++ case EPERM: ++ BMCWEB_LOG_ERROR << "Signal received: EPERM"; ++ messages::accessDenied( ++ asyncResp->res, crow::utility::urlFromPieces(action)); ++ break; ++ case EBUSY: ++ BMCWEB_LOG_ERROR << "Signal received: EAGAIN"; ++ messages::resourceInUse(asyncResp->res); ++ break; ++ default: ++ BMCWEB_LOG_ERROR << "Signal received: Other: " << errorCode; ++ messages::operationFailed(asyncResp->res); ++ break; ++ }; ++ } ++ catch (sdbusplus::exception::SdBusError& e) ++ { ++ BMCWEB_LOG_ERROR << e.what(); ++ }; ++ // postpone matcher deletion after callback finishes ++ boost::asio::post(crow::connections::systemBus->get_io_context(), ++ [name, matchWrapper = matchWrapper]() ++ ++ { ++ BMCWEB_LOG_DEBUG << "Removing matcher for " ++ << name << " node."; ++ matchWrapper->stop(); ++ }); ++ }; ++ matchWrapper->timer.emplace(crow::connections::systemBus->get_io_context()); ++ ++ // Safety valve. Clean itself after 3 minutes without signal ++ matchWrapper->timer->expires_after(std::chrono::minutes(3)); ++ matchWrapper->timer->async_wait( ++ [matchWrapper](const boost::system::error_code& ec) { ++ if (ec != boost::asio::error::operation_aborted) ++ { ++ BMCWEB_LOG_DEBUG << "Timer expired! Signal did not come"; ++ matchWrapper->matcher = std::nullopt; ++ return; ++ } ++ }); ++ ++ matchWrapper->matcher.emplace(*crow::connections::systemBus, matcherString, ++ matchHandler); ++ return matchWrapper; ++} ++ + /** + * @brief Function transceives data with dbus directly. + * +@@ -835,9 +963,15 @@ inline void doMountVmLegacy(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + return; + } + ++ const std::string objectPath = ++ "/xyz/openbmc_project/VirtualMedia/Legacy/" + name; ++ const std::string action = "VirtualMedia.Insert"; ++ auto wrapper = doListenForCompletion(name, objectPath, action, true, ++ asyncResp); ++ + crow::connections::systemBus->async_method_call_timed( +- [asyncResp, secretPipe](const boost::system::error_code ec, +- bool success) { ++ [asyncResp, secretPipe, name, action, wrapper, ++ objectPath](const boost::system::error_code ec, bool success) { + if (ec) + { + BMCWEB_LOG_ERROR << "Bad D-Bus request error: " << ec; +@@ -847,24 +981,25 @@ inline void doMountVmLegacy(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + } + else if (ec == boost::system::errc::permission_denied) + { +- messages::accessDenied(asyncResp->res, +- crow::utility::urlFromPieces( +- "VirtualMedia.Insert")); ++ messages::accessDenied( ++ asyncResp->res, ++ crow::utility::urlFromPieces(action)); + } + else + { + messages::internalError(asyncResp->res); + } ++ wrapper->stop(); + } + else if (!success) + { + BMCWEB_LOG_ERROR << "Service responded with error "; +- messages::generalError(asyncResp->res); ++ messages::operationFailed(asyncResp->res); ++ wrapper->stop(); + } + }, +- service, "/xyz/openbmc_project/VirtualMedia/Legacy/" + name, +- "xyz.openbmc_project.VirtualMedia.Legacy", "Mount", *timeout, +- imageUrl, rw, unixFd); ++ service, objectPath, "xyz.openbmc_project.VirtualMedia.Legacy", ++ "Mount", *timeout, imageUrl, rw, unixFd); + }, + service, "/xyz/openbmc_project/VirtualMedia/Legacy/" + name, + "org.freedesktop.DBus.Properties", "Get", +@@ -876,17 +1011,17 @@ inline void doMountVmLegacy(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + * + * All BMC state properties will be retrieved before sending reset request. + */ +-inline void doVmAction(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, +- const std::string& service, const std::string& name, +- bool legacy) ++inline void doEjectAction(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, ++ const std::string& service, const std::string& name, ++ bool legacy) + { +- const std::string vmMode = legacy ? "Legacy" : "Proxy"; ++ const std::string vmMode = getModeName(legacy); + const std::string objectPath = + "/xyz/openbmc_project/VirtualMedia/" + vmMode + "/" + name; + const std::string ifaceName = "xyz.openbmc_project.VirtualMedia." + vmMode; + + crow::connections::systemBus->async_method_call( +- [asyncResp, service, name, objectPath, ++ [asyncResp, service, name, objectPath, legacy, + ifaceName](const boost::system::error_code ec, + const std::variant<int> timeoutProperty) { + if (ec) +@@ -903,8 +1038,12 @@ inline void doVmAction(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + messages::internalError(asyncResp->res); + return; + } ++ std::string action = "VirtualMedia.Eject"; ++ auto wrapper = doListenForCompletion(name, objectPath, action, ++ legacy, asyncResp); + crow::connections::systemBus->async_method_call_timed( +- [asyncResp](const boost::system::error_code ec) { ++ [asyncResp, name, action, objectPath, ++ wrapper](const boost::system::error_code ec, bool success) { + if (ec) + { + BMCWEB_LOG_ERROR << "Bad D-Bus request error: " << ec; +@@ -914,15 +1053,20 @@ inline void doVmAction(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + } + else if (ec == boost::system::errc::permission_denied) + { +- messages::accessDenied(asyncResp->res, +- crow::utility::urlFromPieces( +- "VirtualMedia.Insert")); ++ messages::accessDenied( ++ asyncResp->res, ++ crow::utility::urlFromPieces(action)); + } + else + { + messages::internalError(asyncResp->res); + } +- return; ++ wrapper->stop(); ++ } ++ else if (!success) ++ { ++ messages::operationFailed(asyncResp->res); ++ wrapper->stop(); + } + }, + service, objectPath, ifaceName, "Unmount", *timeout); +@@ -951,7 +1095,7 @@ inline void requestNBDVirtualMediaRoutes(App& app) + auto mode = item.first.parent_path(); + auto type = mode.parent_path(); + // Check if dbus path is Legacy type +- if (mode.filename() == "Legacy") ++ if (mode.filename() == legacyMode) + { + BMCWEB_LOG_DEBUG << "InsertMedia only allowed " + "with POST method " +@@ -961,7 +1105,7 @@ inline void requestNBDVirtualMediaRoutes(App& app) + return true; + } + // Check if dbus path is Proxy type +- if (mode.filename() == "Proxy") ++ if (mode.filename() == proxyMode) + { + // Not possible in proxy mode + BMCWEB_LOG_DEBUG << "InsertMedia not " +@@ -1019,7 +1163,7 @@ inline void requestNBDVirtualMediaRoutes(App& app) + dbus::utility::DBusInteracesMap>& item) { + auto mode = item.first.parent_path(); + auto type = mode.parent_path(); +- if (mode.filename() == "Proxy") ++ if (mode.filename() == proxyMode) + { + // Not possible in proxy mode + BMCWEB_LOG_DEBUG << "InsertMedia not " +@@ -1131,20 +1275,21 @@ inline void requestNBDVirtualMediaRoutes(App& app) + + if (path.substr(lastIndex) == resName) + { +- lastIndex = path.rfind("Proxy"); ++ lastIndex = path.rfind(proxyMode); + if (lastIndex != std::string::npos) + { + // Proxy mode +- doVmAction(asyncResp, service, +- resName, false); ++ doEjectAction(asyncResp, service, ++ resName, false); + } + +- lastIndex = path.rfind("Legacy"); ++ lastIndex = path.rfind(legacyMode); ++ + if (lastIndex != std::string::npos) + { + // Legacy mode +- doVmAction(asyncResp, service, +- resName, true); ++ doEjectAction(asyncResp, service, ++ resName, true); + } + + return; +-- +2.25.1 + |