summaryrefslogtreecommitdiff
path: root/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/vm/0008-Apply-async-dbus-API.patch
diff options
context:
space:
mode:
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.patch369
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
+