From 69dc3ccd8952a109bb83dfdc1a57590b8eed8fe0 Mon Sep 17 00:00:00 2001 From: Krzysztof Jackiewicz Date: Thu, 1 Feb 2018 15:02:34 +0100 Subject: [PATCH] Wait for unit to stop instead of sleeping Oded asks systemd to stop certain units before unmounting /opt/usr but it doesn't wait for confirmation. Instead it performs sleep(1). This commit implements a mechanism that waits for unit stop confirmation from systemd. Change-Id: I50d4ca8d234221b8af457852548a5d9230f4ec2b --- server/internal-encryption.cpp | 98 ++++++++++++++++++++++++++++++++-- 1 file changed, 94 insertions(+), 4 deletions(-) diff --git a/server/internal-encryption.cpp b/server/internal-encryption.cpp index 7c9051e..32f1463 100644 --- a/server/internal-encryption.cpp +++ b/server/internal-encryption.cpp @@ -16,6 +16,9 @@ #include #include #include +#include +#include +#include #include #include @@ -60,6 +63,83 @@ const std::vector wipeCommand = { "com.samsung.factoryreset.start.setting" }; + +// watches systemd jobs +class JobWatch { +public: + explicit JobWatch(dbus::Connection& systemDBus); + ~JobWatch(); + + bool waitForJob(const std::string& job); + +private: + void jobRemoved(const dbus::Variant& parameters); + + struct Job { + Job(uint32_t id, + const std::string& job, + const std::string& unit, + const std::string& result) : id(id), job(job), unit(unit), result(result) {} + uint32_t id; + std::string job; + std::string unit; + std::string result; + }; + + dbus::Connection::SubscriptionId id; + dbus::Connection& systemDBus; + std::list removedJobs; + std::mutex jobsMutex; + std::condition_variable jobsCv; +}; + +JobWatch::JobWatch(dbus::Connection& systemDBus) : systemDBus(systemDBus) { + auto callback = [this](dbus::Variant parameters) { + this->jobRemoved(parameters); + }; + + id = systemDBus.subscribeSignal("", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "JobRemoved", + callback); +} + +JobWatch::~JobWatch() { + systemDBus.unsubscribeSignal(id); +} + +bool JobWatch::waitForJob(const std::string& job) { + while(true) { + std::unique_lock lock(jobsMutex); + jobsCv.wait(lock, [this]{ return !removedJobs.empty(); }); + + while(!removedJobs.empty()) { + bool match = (removedJobs.front().job == job); + bool done = (removedJobs.front().result == "done"); + removedJobs.pop_front(); + if (match) + return done; + } + }; +} + +void JobWatch::jobRemoved(const dbus::Variant& parameters) +{ + uint32_t id; + const char* job; + const char* unit; + const char* result; + parameters.get("(uoss)", &id, &job, &unit, &result); + INFO(SINK, "id:" + std::to_string(id) + " job:" + job + " unit:" + unit + " result:" + result); + + { + std::lock_guard guard(jobsMutex); + removedJobs.emplace_back(id, job, unit, result); + } + jobsCv.notify_one(); +} + void stopKnownSystemdUnits() { std::vector knownSystemdUnits; @@ -98,16 +178,20 @@ void stopKnownSystemdUnits() } } + JobWatch watch(systemDBus); + for (const std::string& unit : knownSystemdUnits) { INFO(SINK, "Stopping unit: " + unit); + const char* job = NULL; systemDBus.methodcall("org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "StopUnit", - -1, "", "(ss)", unit.c_str(), "flush"); + -1, "(o)", "(ss)", unit.c_str(), "flush").get("(o)", &job); + INFO(SINK, "Waiting for job: " + std::string(job)); + if (!watch.waitForJob(job)) + throw runtime::Exception("Stopping unit: " + unit + " failed"); } - - sleep(1); // TODO wait for confirmation from systemd instead } void stopDependedSystemdUnits() @@ -131,13 +215,19 @@ void stopDependedSystemdUnits() } } + JobWatch watch(systemDBus); + for (const std::string& unit : unitsToStop) { INFO(SINK, "Stopping unit: " + unit); + const char* job = NULL; systemDBus.methodcall("org.freedesktop.systemd1", unit, "org.freedesktop.systemd1.Unit", "Stop", - -1, "", "(s)", "flush"); + -1, "(o)", "(s)", "flush").get("(o)", &job); + INFO(SINK, "Waiting for job: " + std::string(job)); + if (!watch.waitForJob(job)) + throw runtime::Exception("Stopping unit: " + unit + " failed"); } } -- 2.34.1