Wait for unit to stop instead of sleeping 96/168996/5
authorKrzysztof Jackiewicz <k.jackiewicz@samsung.com>
Thu, 1 Feb 2018 14:02:34 +0000 (15:02 +0100)
committerDariusz Michaluk <d.michaluk@samsung.com>
Wed, 7 Feb 2018 09:07:53 +0000 (09:07 +0000)
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

index 7c9051e..32f1463 100644 (file)
@@ -16,6 +16,9 @@
 #include <set>
 #include <algorithm>
 #include <memory>
+#include <mutex>
+#include <condition_variable>
+#include <list>
 
 #include <fcntl.h>
 #include <signal.h>
@@ -60,6 +63,83 @@ const std::vector<std::string> 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<Job> 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<std::mutex> 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<std::mutex> guard(jobsMutex);
+               removedJobs.emplace_back(id, job, unit, result);
+       }
+       jobsCv.notify_one();
+}
+
 void stopKnownSystemdUnits()
 {
        std::vector<std::string> 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");
        }
 }