From 49e6d0678ef91938a8cc4125716b5189ccef6a88 Mon Sep 17 00:00:00 2001 From: Agata Olender Date: Mon, 13 Jan 2020 13:31:49 +0100 Subject: Gadget inactivity timeout MountPoints being inactive for time defined by InactivityTimeout will be unmounted. Inacitivity is measured based on USB gadget R/W statitics. InactivityTimeout must be set in json configuration and is common for all MountPoints. Remaining time to unmount inactive media is exposed per MountPoint on dbus using RemainingInactivityTimeout property. Change-Id: Ieb80e67dae6c3b4cb0482d801b5b4208884b0809 Signed-off-by: Agata Olender --- virtual-media/src/configuration.hpp | 9 +++++ virtual-media/src/main.cpp | 2 ++ virtual-media/src/state_machine.hpp | 66 +++++++++++++++++++++++++++++++++++++ virtual-media/src/system.hpp | 17 ++++++++++ 4 files changed, 94 insertions(+) diff --git a/virtual-media/src/configuration.hpp b/virtual-media/src/configuration.hpp index dd71837..89c9d02 100644 --- a/virtual-media/src/configuration.hpp +++ b/virtual-media/src/configuration.hpp @@ -34,6 +34,7 @@ class Configuration std::string endPointId; std::optional timeout; std::optional blocksize; + std::chrono::seconds remainingInactivityTimeout; Mode mode; static std::vector toArgs(const MountPoint& mp) @@ -59,6 +60,7 @@ class Configuration bool valid = false; boost::container::flat_map mountPoints; + static std::chrono::seconds inactivityTimeout; Configuration(const std::string& file) { @@ -94,6 +96,13 @@ class Configuration bool setupVariables(const nlohmann::json& config) { + inactivityTimeout = + std::chrono::seconds(config.value("InactivityTimeout", 0)); + if (inactivityTimeout == std::chrono::seconds(0)) + { + LogMsg(Logger::Error, "InactivityTimeout required, not set"); + } + for (const auto& item : config.items()) { if (item.key() == "MountPoints") diff --git a/virtual-media/src/main.cpp b/virtual-media/src/main.cpp index 3636e59..49dab24 100644 --- a/virtual-media/src/main.cpp +++ b/virtual-media/src/main.cpp @@ -19,6 +19,8 @@ #include #include +std::chrono::seconds Configuration::inactivityTimeout; + class App { public: diff --git a/virtual-media/src/state_machine.hpp b/virtual-media/src/state_machine.hpp index 973d7fa..d6ec1b4 100644 --- a/virtual-media/src/state_machine.hpp +++ b/virtual-media/src/state_machine.hpp @@ -80,6 +80,8 @@ struct MountPointStateMachine { machine.target.reset(); } + + machine.config.remainingInactivityTimeout = std::chrono::seconds(0); } }; @@ -115,6 +117,58 @@ struct MountPointStateMachine BasicState(state, __FUNCTION__), process{state.process} {}; std::weak_ptr process; + + virtual void onEnter() + { + timer = + std::make_shared(machine.ioc.get()); + handler = [this](const boost::system::error_code& ec) { + if (ec) + { + return; + } + + auto now = std::chrono::steady_clock::now(); + + auto stats = UsbGadget::getStats(machine.name); + if (stats && (*stats != lastStats)) + { + lastStats = std::move(*stats); + lastAccess = now; + } + + auto timeSinceLastAccess = + std::chrono::duration_cast( + now - lastAccess); + if (timeSinceLastAccess >= Configuration::inactivityTimeout) + { + LogMsg(Logger::Info, machine.name, + " Inactivity timer expired (", + Configuration::inactivityTimeout.count(), + "s) - Unmounting"); + // unmount media & stop retriggering timer + machine.emitUnmountEvent(); + return; + } + else + { + machine.config.remainingInactivityTimeout = + Configuration::inactivityTimeout - timeSinceLastAccess; + } + + timer->expires_from_now(std::chrono::seconds(1)); + timer->async_wait(handler); + }; + timer->expires_from_now(std::chrono::seconds(1)); + timer->async_wait(handler); + } + + private: + // timer wrapped in shared_ptr to allow making state copies + std::shared_ptr timer; + std::function handler; + std::chrono::time_point lastAccess; + std::string lastStats; }; struct WaitingForProcessEndState : public BasicState @@ -278,6 +332,18 @@ struct MountPointStateMachine iface->register_property("EndpointId", state.machine.config.endPointId); iface->register_property("Socket", state.machine.config.unixSocket); + iface->register_property( + "RemainingInactivityTimeout", 0, + [](const int& req, int& property) { + throw sdbusplus::exception::SdBusError( + EPERM, "Setting RemainingInactivityTimeout property is " + "not allowed"); + return -1; + }, + [& config = state.machine.config](const int& property) -> int { + return config.remainingInactivityTimeout.count(); + }); + iface->initialize(); } diff --git a/virtual-media/src/system.hpp b/virtual-media/src/system.hpp index ca9d17e..ae8a486 100644 --- a/virtual-media/src/system.hpp +++ b/virtual-media/src/system.hpp @@ -582,4 +582,21 @@ struct UsbGadget } return -1; } + + static std::optional getStats(const std::string& name) + { + const fs::path statsPath = + "/sys/kernel/config/usb_gadget/mass-storage-" + name + + "/functions/mass_storage.usb0/lun.0/stats"; + + std::ifstream ifs(statsPath); + if (!ifs.is_open()) + { + LogMsg(Logger::Error, name, "Failed to open ", statsPath); + return {}; + } + + return std::string{std::istreambuf_iterator(ifs), + std::istreambuf_iterator()}; + } }; -- cgit v1.2.3