Add libvirt's event listeners and use them to implement a graceful stop
authorLukasz Pawelczyk <l.pawelczyk@partner.samsung.com>
Thu, 8 May 2014 10:24:42 +0000 (12:24 +0200)
committerJan Olszak <j.olszak@samsung.com>
Mon, 19 May 2014 11:47:16 +0000 (13:47 +0200)
[Bug/Feature]   Orginize container's shutdown process
[Cause]         Burdello
[Solution]      Implemented listeners for libvirt's events (lifecycle and reboot)
                Added libvirt-glib dependency to use glib main loop for those events.
                Used those listeners to implement a synchronous graceful stop of
                the container: "try to shutdown, if it wont in 10 seconds, destroy it".
                Added thread ID to the logger.
                Organized container related logs a little.
[Verification]  Built, installed, run tests and the daemon.

Change-Id: I3be53a2a46cd130cf414e89b0c47eb1cce74e6b5
Signed-off-by: Lukasz Pawelczyk <l.pawelczyk@partner.samsung.com>
26 files changed:
common/libvirt/helpers.cpp
common/libvirt/helpers.hpp
common/log/formatter.cpp
common/log/formatter.hpp
packaging/security-containers.spec
server/CMakeLists.txt
server/container-admin.cpp
server/container-admin.hpp
server/container-connection-transport.cpp
server/container-connection.cpp
server/container.cpp
server/container.hpp
server/containers-manager.cpp
unit_tests/CMakeLists.txt
unit_tests/server/configs/ut-container-admin/libvirt-config/test.xml
unit_tests/server/configs/ut-container/libvirt-config/test-dbus.xml.in
unit_tests/server/configs/ut-container/libvirt-config/test.xml
unit_tests/server/configs/ut-containers-manager/libvirt-config/console1.xml
unit_tests/server/configs/ut-containers-manager/libvirt-config/console2.xml
unit_tests/server/configs/ut-containers-manager/libvirt-config/console3.xml
unit_tests/server/configs/ut-server/libvirt-config/container1.xml
unit_tests/server/configs/ut-server/libvirt-config/container2.xml
unit_tests/server/configs/ut-server/libvirt-config/container3.xml
unit_tests/server/ut-container-admin.cpp
unit_tests/server/ut-container.cpp
unit_tests/server/ut-containers-manager.cpp

index b8f5230..36b49e5 100644 (file)
@@ -27,6 +27,7 @@
 
 #include <mutex>
 #include <libvirt/virterror.h>
+#include <libvirt-glib/libvirt-glib-event.h>
 
 
 namespace security_containers {
@@ -57,6 +58,7 @@ void libvirtInitialize(void)
     std::call_once(gInitFlag, []() {
             virInitialize();
             virSetErrorFunc(NULL, &libvirtErrorFunction);
+            gvir_event_register();
         });
 }
 
@@ -72,6 +74,143 @@ std::string libvirtFormatError(void)
     return "Libvirt error: " + std::string(error->message);
 }
 
+std::string libvirtEventToString(const int eventId)
+{
+    switch(eventId) {
+    case VIR_DOMAIN_EVENT_DEFINED:
+        return "Defined";
+    case VIR_DOMAIN_EVENT_UNDEFINED:
+        return "Undefined";
+    case VIR_DOMAIN_EVENT_STARTED:
+        return "Started";
+    case VIR_DOMAIN_EVENT_SUSPENDED:
+        return "Suspended";
+    case VIR_DOMAIN_EVENT_RESUMED:
+        return "Resumed";
+    case VIR_DOMAIN_EVENT_STOPPED:
+        return "Stopped";
+    case VIR_DOMAIN_EVENT_SHUTDOWN:
+        return "Shutdown";
+    case VIR_DOMAIN_EVENT_PMSUSPENDED:
+        return "PM Suspended";
+    case VIR_DOMAIN_EVENT_CRASHED:
+        return "Crashed";
+    default:
+        return "Unknown EventId";
+    }
+}
+
+std::string libvirtEventDetailToString(const int eventId, const int detailId)
+{
+    switch (eventId) {
+    case VIR_DOMAIN_EVENT_DEFINED:
+        switch (detailId) {
+        case VIR_DOMAIN_EVENT_DEFINED_ADDED:
+            return "Added";
+        case VIR_DOMAIN_EVENT_DEFINED_UPDATED:
+            return "Updated";
+        default:
+            return "Unknown detail";
+        }
+    case VIR_DOMAIN_EVENT_UNDEFINED:
+        switch (detailId) {
+        case VIR_DOMAIN_EVENT_UNDEFINED_REMOVED:
+            return "Removed";
+        default:
+            return "Unknown detail";
+        }
+    case VIR_DOMAIN_EVENT_STARTED:
+        switch (detailId) {
+        case VIR_DOMAIN_EVENT_STARTED_BOOTED:
+            return "Booted";
+        case VIR_DOMAIN_EVENT_STARTED_MIGRATED:
+            return "Migrated";
+        case VIR_DOMAIN_EVENT_STARTED_RESTORED:
+            return "Restored";
+        case VIR_DOMAIN_EVENT_STARTED_FROM_SNAPSHOT:
+            return "From Snapshot";
+        case VIR_DOMAIN_EVENT_STARTED_WAKEUP:
+            return "Wakeup";
+        default:
+            return "Unknown detail";
+        }
+    case VIR_DOMAIN_EVENT_SUSPENDED:
+        switch (detailId) {
+        case VIR_DOMAIN_EVENT_SUSPENDED_PAUSED:
+            return "Paused";
+        case VIR_DOMAIN_EVENT_SUSPENDED_MIGRATED:
+            return "Migrated";
+        case VIR_DOMAIN_EVENT_SUSPENDED_IOERROR:
+            return "IO Error";
+        case VIR_DOMAIN_EVENT_SUSPENDED_WATCHDOG:
+            return "Watchdog";
+        case VIR_DOMAIN_EVENT_SUSPENDED_RESTORED:
+            return "Restored";
+        case VIR_DOMAIN_EVENT_SUSPENDED_FROM_SNAPSHOT:
+            return "From Snapshot";
+        case VIR_DOMAIN_EVENT_SUSPENDED_API_ERROR:
+            return "API Error";
+        default:
+            return "Unknown detail";
+        }
+    case VIR_DOMAIN_EVENT_RESUMED:
+        switch (detailId) {
+        case VIR_DOMAIN_EVENT_RESUMED_UNPAUSED:
+            return "Unpaused";
+        case VIR_DOMAIN_EVENT_RESUMED_MIGRATED:
+            return "Migrated";
+        case VIR_DOMAIN_EVENT_RESUMED_FROM_SNAPSHOT:
+            return "From Snapshot";
+        default:
+            return "Unknown detail";
+        }
+    case VIR_DOMAIN_EVENT_STOPPED:
+        switch (detailId) {
+        case VIR_DOMAIN_EVENT_STOPPED_SHUTDOWN:
+            return "Shutdown";
+        case VIR_DOMAIN_EVENT_STOPPED_DESTROYED:
+            return "Destroyed";
+        case VIR_DOMAIN_EVENT_STOPPED_CRASHED:
+            return "Crashed";
+        case VIR_DOMAIN_EVENT_STOPPED_MIGRATED:
+            return "Migrated";
+        case VIR_DOMAIN_EVENT_STOPPED_SAVED:
+            return "Failed";
+        case VIR_DOMAIN_EVENT_STOPPED_FAILED:
+            return "Failed";
+        case VIR_DOMAIN_EVENT_STOPPED_FROM_SNAPSHOT:
+            return "From Snapshot";
+        default:
+            return "Unknown detail";
+        }
+    case VIR_DOMAIN_EVENT_SHUTDOWN:
+        switch (detailId) {
+        case VIR_DOMAIN_EVENT_SHUTDOWN_FINISHED:
+            return "Finished";
+        default:
+            return "Unknown detail";
+        }
+    case VIR_DOMAIN_EVENT_PMSUSPENDED:
+        switch (detailId) {
+        case VIR_DOMAIN_EVENT_PMSUSPENDED_MEMORY:
+            return "Memory";
+        case VIR_DOMAIN_EVENT_PMSUSPENDED_DISK:
+            return "Disk";
+        default:
+            return "Unknown detail";
+        }
+    case VIR_DOMAIN_EVENT_CRASHED:
+        switch (detailId) {
+        case VIR_DOMAIN_EVENT_CRASHED_PANICKED:
+            return "Panicked";
+        default:
+            return "Unknown detail";
+        }
+    default:
+        return "Unknown event";
+    }
+}
+
 
 } // namespace libvirt
 } // namespace security_containers
index 35cac9e..b68dd6e 100644 (file)
@@ -42,6 +42,16 @@ void libvirtInitialize(void);
  */
 std::string libvirtFormatError(void);
 
+/**
+ * Converts an event ID to an event name.
+ */
+std::string libvirtEventToString(const int event);
+
+/**
+ * Converts an event's detail ID to an event's detail name.
+ */
+std::string libvirtEventDetailToString(const int event, const int detail);
+
 
 } // namespace libvirt
 } // namespace security_containers
index 8ac3581..1bfe905 100644 (file)
 #include <cassert>
 #include <sstream>
 #include <iomanip>
+#include <thread>
+#include <atomic>
 
 namespace security_containers {
 namespace log {
 
 const int TIME_COLUMN_LENGTH = 12;
 const int SEVERITY_COLUMN_LENGTH = 8;
-const int FILE_COLUMN_LENGTH = 52;
+const int THREAD_COLUMN_LENGTH = 3;
+const int FILE_COLUMN_LENGTH = 60;
+
+std::atomic<unsigned int> gNextThreadId(1);
+thread_local unsigned int gThisThreadId(0);
+
+unsigned int LogFormatter::getCurrentThread(void)
+{
+    unsigned int id = gThisThreadId;
+    if (id == 0) {
+        gThisThreadId = id = gNextThreadId++;
+    }
+
+    return id;
+}
 
 std::string LogFormatter::getCurrentTime(void)
 {
@@ -93,6 +109,7 @@ std::string LogFormatter::getHeader(LogLevel logLevel,
     std::ostringstream logLine;
     logLine << getCurrentTime() << ' '
             << std::left << std::setw(SEVERITY_COLUMN_LENGTH) << '[' + toString(logLevel) + ']'
+            << std::right << std::setw(THREAD_COLUMN_LENGTH) << getCurrentThread() << ": "
             << std::left << std::setw(FILE_COLUMN_LENGTH)
             << file + ':' + std::to_string(line) + ' ' + func + ':';
     return logLine.str();
@@ -100,4 +117,3 @@ std::string LogFormatter::getHeader(LogLevel logLevel,
 
 } // namespace log
 } // namespace security_containers
-
index 3667ecd..7f83ebe 100644 (file)
@@ -34,6 +34,7 @@ namespace log {
 
 class LogFormatter {
 public:
+    static unsigned int getCurrentThread(void);
     static std::string getCurrentTime(void);
     static std::string getConsoleColor(LogLevel logLevel);
     static std::string getDefaultConsoleColor(void);
index 707bdcb..c7e2df0 100644 (file)
@@ -12,6 +12,7 @@ BuildRequires: libvirt-devel
 BuildRequires: libjson-devel
 BuildRequires: pkgconfig(glib-2.0)
 BuildRequires: pkgconfig(libsystemd-journal)
+BuildRequires: pkgconfig(libvirt-glib-1.0)
 
 %description
 This package provides a daemon used to manage containers - start, stop and switch
index b1ce9f8..020724c 100644 (file)
@@ -30,7 +30,7 @@ ADD_EXECUTABLE(${SERVER_CODENAME} ${project_SRCS} ${common_SRCS})
 ## Link libraries ##############################################################
 FIND_PACKAGE (Boost COMPONENTS program_options REQUIRED)
 
-PKG_CHECK_MODULES(SERVER_DEPS REQUIRED libvirt json gio-2.0 libsystemd-journal)
+PKG_CHECK_MODULES(SERVER_DEPS REQUIRED libvirt libvirt-glib-1.0 json gio-2.0 libsystemd-journal)
 INCLUDE_DIRECTORIES(${COMMON_FOLDER})
 INCLUDE_DIRECTORIES(SYSTEM ${SERVER_DEPS_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS})
 TARGET_LINK_LIBRARIES(${SERVER_CODENAME} ${SERVER_DEPS_LIBRARIES} ${Boost_LIBRARIES})
index f499326..0365ba8 100644 (file)
@@ -28,6 +28,8 @@
 #include "libvirt/helpers.hpp"
 #include "log/logger.hpp"
 #include "utils/fs.hpp"
+#include "utils/latch.hpp"
+#include "utils/callback-wrapper.hpp"
 
 #include <cassert>
 #include <cstring>
@@ -41,6 +43,9 @@ namespace security_containers {
 
 namespace {
 
+// TODO: this should be in container's configuration file
+const int SHUTDOWN_WAIT = 10 * 1000;
+
 std::string getDomainName(virDomainPtr dom)
 {
     assert(dom != NULL);
@@ -61,25 +66,62 @@ const std::uint64_t DEFAULT_CPU_SHARES = 1024;
 const std::uint64_t DEFAULT_VCPU_PERIOD_MS = 100000;
 
 ContainerAdmin::ContainerAdmin(ContainerConfig& config)
-    : mConfig(config), mDom(utils::readFileContent(mConfig.config)), mId(getDomainName(mDom.get()))
+    : mConfig(config),
+      mDom(utils::readFileContent(mConfig.config)),
+      mId(getDomainName(mDom.get())),
+      mLifecycleCallbackId(-1),
+      mRebootCallbackId(-1),
+      mNextIdForListener(0)
 {
     LOGD(mId << ": Instantiating ContainerAdmin object");
+
+    // ContainerAdmin owns those callbacks
+    mLifecycleCallbackId = virConnectDomainEventRegisterAny(virDomainGetConnect(mDom.get()),
+                                                            mDom.get(),
+                                                            VIR_DOMAIN_EVENT_ID_LIFECYCLE,
+                                                            VIR_DOMAIN_EVENT_CALLBACK(&ContainerAdmin::libvirtLifecycleCallback),
+                                                            utils::createCallbackWrapper(this, mLibvirtGuard.spawn()),
+                                                            &utils::deleteCallbackWrapper<ContainerAdmin*>);
+
+    if (mLifecycleCallbackId < 0) {
+        LOGE(mId << ": Failed to register a libvirt lifecycle callback");
+        throw ContainerOperationException(mId + ": Failed to register a libvirt lifecycle callback");
+    }
+
+    mRebootCallbackId = virConnectDomainEventRegisterAny(virDomainGetConnect(mDom.get()),
+                                                         mDom.get(),
+                                                         VIR_DOMAIN_EVENT_ID_REBOOT,
+                                                         VIR_DOMAIN_EVENT_CALLBACK(&ContainerAdmin::libvirtRebootCallback),
+                                                         utils::createCallbackWrapper(this, mLibvirtGuard.spawn()),
+                                                         &utils::deleteCallbackWrapper<ContainerAdmin*>);
+
+    if (mRebootCallbackId < 0) {
+        LOGE(mId << ": Failed to register a libvirt reboot callback");
+        virConnectDomainEventDeregisterAny(virDomainGetConnect(mDom.get()),
+                                           mLifecycleCallbackId);
+        throw ContainerOperationException(mId + ": Failed to register a libvirt reboot callback");
+    }
 }
 
 
 ContainerAdmin::~ContainerAdmin()
 {
-    // Try to shutdown
-    LOGD(mId << ": Destroying ContainerAdmin object...");
-    try {
-        shutdown();
-    } catch (ServerException&) {}
+    // Deregister callbacks
+    if (mLifecycleCallbackId >= 0) {
+        virConnectDomainEventDeregisterAny(virDomainGetConnect(mDom.get()),
+                                           mLifecycleCallbackId);
+    }
+    if (mRebootCallbackId >= 0) {
+        virConnectDomainEventDeregisterAny(virDomainGetConnect(mDom.get()),
+                                           mRebootCallbackId);
+    }
 
     // Try to forcefully stop
+    LOGD(mId << ": Destroying ContainerAdmin object...");
     try {
-        stop();
+        destroy();
     } catch (ServerException&) {
-        LOGE(mId << ": Failed to destroy the container!");
+        LOGE(mId << ": Failed to destroy the container");
     }
 
     LOGD(mId << ": ContainerAdmin object destroyed");
@@ -121,22 +163,60 @@ void ContainerAdmin::stop()
 {
     assert(mDom);
 
-    LOGD(mId << ": Stopping...");
+    LOGD(mId << ": Stopping procedure started...");
+    if (isStopped()) {
+        LOGD(mId << ": Already crashed/down/off - nothing to do");
+        return;
+    }
+
+    utils::Latch stoppedOccured;
+
+    LifecycleListener setStopped = [&](const int eventId, const int detailId) {
+        if (eventId == VIR_DOMAIN_EVENT_STOPPED) {
+            if (detailId != VIR_DOMAIN_EVENT_STOPPED_SHUTDOWN) {
+                LOGW(mId << ": shutdown requested, but the container stopped with a different status: "
+                     << libvirt::libvirtEventDetailToString(eventId, detailId));
+            }
+            stoppedOccured.set();
+        }
+    };
+
+    ListenerId id = registerListener(setStopped, nullptr);
+    shutdown();
+    bool stopped = stoppedOccured.wait(SHUTDOWN_WAIT);
+    removeListener<LifecycleListener>(id);
+
+    if (!stopped) {
+        LOGD(mId << ": waiting for shutdown timed out, destroying");
+        destroy();
+    }
+
+    LOGD(mId << ": Stopping procedure ended");
+}
+
+
+void ContainerAdmin::destroy()
+{
+    assert(mDom);
+
+    LOGD(mId << ": Destroying...");
     if (isStopped()) {
-        LOGD(mId << ": Already crashed/down/off - nothing to do...");
+        LOGD(mId << ": Already crashed/down/off - nothing to do");
         return;
     }
 
+    setSchedulerLevel(SchedulerLevel::FOREGROUND);
+
     // Forceful termination of the guest
     u_int flags = VIR_DOMAIN_DESTROY_DEFAULT;
 
     if (virDomainDestroyFlags(mDom.get(), flags) < 0) {
-        LOGE(mId << ": Error while stopping the container:\n"
+        LOGE(mId << ": Error while destroying the container:\n"
              << libvirt::libvirtFormatError());
         throw ContainerOperationException();
     }
 
-    LOGD(mId << ": Stopped");
+    LOGD(mId << ": Destroyed");
 }
 
 
@@ -146,11 +226,11 @@ void ContainerAdmin::shutdown()
 
     LOGD(mId << ": Shutting down...");
     if (isStopped()) {
-        LOGD(mId << ": Already crashed/down/off - nothing to do...");
+        LOGD(mId << ": Already crashed/down/off - nothing to do");
         return;
     }
 
-    resume();
+    setSchedulerLevel(SchedulerLevel::FOREGROUND);
 
     if (virDomainShutdown(mDom.get()) < 0) {
         LOGE(mId << ": Error while shutting down the container:\n"
@@ -158,7 +238,7 @@ void ContainerAdmin::shutdown()
         throw ContainerOperationException();
     }
 
-    LOGD(mId << ": Shut down");
+    LOGD(mId << ": Shut down initiated (async)");
 }
 
 
@@ -317,5 +397,58 @@ std::int64_t ContainerAdmin::getSchedulerQuota()
     return quota;
 }
 
+int ContainerAdmin::libvirtLifecycleCallback(virConnectPtr /*con*/,
+                                             virDomainPtr /*dom*/,
+                                             int event,
+                                             int detail,
+                                             void* opaque)
+{
+    ContainerAdmin* thisPtr = utils::getCallbackFromPointer<ContainerAdmin*>(opaque);
+
+    LOGI(thisPtr->getId()
+         << ": Lifecycle event: "
+         << libvirt::libvirtEventToString(event)
+         << ": "
+         << libvirt::libvirtEventDetailToString(event, detail));
+
+    std::unique_lock<std::mutex> lock(thisPtr->mListenerMutex);
+    for (auto& it : thisPtr->mLifecycleListeners) {
+        LifecycleListener f = it.second.get();
+        f(event, detail);
+    }
+
+    // ignored, libvirt's legacy
+    return 0;
+}
+
+void ContainerAdmin::libvirtRebootCallback(virConnectPtr /*con*/,
+                                           virDomainPtr /*dom*/,
+                                           void* opaque)
+{
+    ContainerAdmin* thisPtr = utils::getCallbackFromPointer<ContainerAdmin*>(opaque);
+
+    LOGI(thisPtr->getId() << ": Reboot event");
+
+    std::unique_lock<std::mutex> lock(thisPtr->mListenerMutex);
+    for (auto& it : thisPtr->mRebootListeners) {
+        RebootListener f = it.second.get();
+        f();
+    }
+}
+
+template<>
+ContainerAdmin::ListenerMap<ContainerAdmin::LifecycleListener>&
+ContainerAdmin::getListenerMap<ContainerAdmin::LifecycleListener>()
+{
+    return mLifecycleListeners;
+}
+
+template<>
+ContainerAdmin::ListenerMap<ContainerAdmin::RebootListener>&
+ContainerAdmin::getListenerMap<ContainerAdmin::RebootListener>()
+{
+    return mRebootListeners;
+}
+
 
 } // namespace security_containers
index b01d9e0..add8f9a 100644 (file)
 
 #include "container-config.hpp"
 
+#include "utils/callback-guard.hpp"
+#include "utils/callback-wrapper.hpp"
 #include "libvirt/connection.hpp"
 #include "libvirt/domain.hpp"
 
+#include <map>
+#include <mutex>
+#include <atomic>
 #include <string>
 #include <cstdint>
 #include <libvirt/libvirt.h>
@@ -47,6 +52,21 @@ enum class SchedulerLevel {
 class ContainerAdmin {
 
 public:
+    /**
+     * A listener ID type.
+     */
+    typedef unsigned int ListenerId;
+
+    /**
+     * A function type used for the lifecycle listener
+     */
+    typedef std::function<void(const int eventId, const int detailId)> LifecycleListener;
+
+    /**
+     * A function type used for the reboot listener
+     */
+    typedef std::function<void()> RebootListener;
+
     ContainerAdmin(ContainerConfig& config);
     virtual ~ContainerAdmin();
 
@@ -61,14 +81,18 @@ public:
     void start();
 
     /**
-     * Forcefully stop the container.
+     * Try to shutdown the container, if failed, destroy it.
      */
     void stop();
 
     /**
+     * Forcefully stop the container.
+     */
+    void destroy();
+
+    /**
      * Gracefully shutdown the container.
-     * This method will NOT block until container is shut down,
-     * because some configurations may ignore this.
+     * This method will NOT block until container is shut down.
      */
     void shutdown();
 
@@ -115,6 +139,23 @@ public:
      */
     std::int64_t getSchedulerQuota();
 
+    /**
+     * Sets a listener for a specific event.
+     * It's a caller's responsibility to remove the listener
+     * prior to destroying the object.
+     *
+     * @return listener ID that can be used to remove.
+     */
+    template <typename Listener>
+    ListenerId registerListener(const Listener& listener,
+                                const utils::CallbackGuard::Tracker& tracker);
+
+    /**
+     * Remove a previously registered listener.
+     */
+    template <typename Listener>
+    void removeListener(const ListenerId id);
+
 private:
     ContainerConfig& mConfig;
     libvirt::LibvirtDomain mDom;
@@ -123,10 +164,63 @@ private:
     int getState();   // get the libvirt's domain state
     void setSchedulerParams(std::uint64_t cpuShares, std::uint64_t vcpuPeriod, std::int64_t vcpuQuota);
 
+    // for handling libvirt callbacks
+    utils::CallbackGuard mLibvirtGuard;
+    int mLifecycleCallbackId;
+    int mRebootCallbackId;
+
+    // virConnectDomainEventCallback
+    static int libvirtLifecycleCallback(virConnectPtr con,
+                                        virDomainPtr dom,
+                                        int event,
+                                        int detail,
+                                        void* opaque);
+
+    // virConnectDomainEventGenericCallback
+    static void libvirtRebootCallback(virConnectPtr con,
+                                      virDomainPtr dom,
+                                      void* opaque);
+
+    // for handling external listeners triggered from libvirt callbacks
+    // TODO, the Listener type might not be unique, reimplement using proper listeners
+    template <typename Listener>
+    using ListenerMap = std::map<ListenerId, utils::CallbackWrapper<Listener>>;
+
+    std::mutex mListenerMutex;
+    std::atomic<unsigned int> mNextIdForListener;
+    ListenerMap<LifecycleListener> mLifecycleListeners;
+    ListenerMap<RebootListener> mRebootListeners;
+
+    template <typename Listener>
+    ListenerMap<Listener>& getListenerMap();
 };
 
+template <typename Listener>
+unsigned int ContainerAdmin::registerListener(const Listener& listener,
+                                              const utils::CallbackGuard::Tracker& tracker)
+{
 
+    ListenerMap<Listener>& map = getListenerMap<Listener>();
+    unsigned int id = mNextIdForListener++;
+    utils::CallbackWrapper<Listener> wrap(listener, tracker);
+
+    std::unique_lock<std::mutex> lock(mListenerMutex);
+    map.emplace(id, std::move(wrap));
+
+    return id;
 }
 
+template <typename Listener>
+void ContainerAdmin::removeListener(const ContainerAdmin::ListenerId id)
+{
+    ListenerMap<Listener>& map = getListenerMap<Listener>();
+
+    std::unique_lock<std::mutex> lock(mListenerMutex);
+    map.erase(id);
+}
+
+
+} // namespace security_containers
+
 
 #endif // SERVER_CONTAINER_ADMIN_HPP
index 5842c81..7f4fb5e 100644 (file)
@@ -36,6 +36,7 @@ namespace {
 
 // Timeout in ms for waiting for dbus transport.
 // Should be very long to ensure dbus in container is ready.
+// TODO: this should be in container's configuration file
 const unsigned int TRANSPORT_READY_TIMEOUT = 2 * 60 * 1000;
 
 } // namespace
index d3176e5..b1c1df0 100644 (file)
@@ -35,6 +35,7 @@ namespace {
 
 // Timeout in ms for waiting for dbus name.
 // Can happen if glib loop is busy or not present.
+// TODO: this should be in container's configuration file
 const unsigned int NAME_ACQUIRED_TIMEOUT = 5 * 1000;
 
 } // namespace
index 443b356..4717429 100644 (file)
@@ -82,7 +82,7 @@ void Container::start()
 
     // Send to the background only after we're connected,
     // otherwise it'd take ages.
-    LOGD(getId() << ": Sending to the background");
+    LOGD(getId() << ": DBUS connected, sending to the background");
     goBackground();
 }
 
@@ -138,7 +138,7 @@ void Container::reconnectHandler()
         address = mConnectionTransport->acquireAddress();
     } catch (SecurityContainersException&) {
         LOGE(getId() << "The socket does not exist anymore, something went terribly wrong, stopping the container");
-        stop(); // TODO: shutdownOrStop()
+        stop();
         return;
     }
 
@@ -147,7 +147,7 @@ void Container::reconnectHandler()
         LOGI(getId() << ": Reconnected");
     } catch (SecurityContainersException&) {
         LOGE(getId() << ": Reconnecting to the DBUS has failed, stopping the container");
-        stop(); // TODO: shutdownOrStop()
+        stop();
         return;
     }
 }
index e464ffb..5607715 100644 (file)
@@ -62,7 +62,7 @@ public:
     void start();
 
     /**
-     * Forcefully stop the container.
+     * Try to shutdown the container, if failed, destroy it.
      */
     void stop();
 
index 000b014..288bdef 100644 (file)
@@ -65,7 +65,7 @@ ContainersManager::~ContainersManager()
 {
     LOGD("Destroying ContainersManager object...");
     try {
-        stopAll(); // TODO: shutdownOrStop()
+        stopAll();
     } catch (ServerException&) {
         LOGE("Failed to stop all of the containers");
     }
@@ -99,6 +99,7 @@ void ContainersManager::startAll()
 
         if (container.first == mConfig.foregroundId) {
             isForegroundFound = true;
+            LOGI(container.second->getId() << ": set as the foreground container");
             container.second->goForeground();
         }
     }
@@ -110,6 +111,7 @@ void ContainersManager::startAll()
                                                    });
 
         mConfig.foregroundId = foregroundIterator->second->getId();
+        LOGI(mConfig.foregroundId << ": no foreground container configured, setting one with highest priority");
         foregroundIterator->second->goForeground();
     }
 }
index 87cb4c8..e865049 100644 (file)
@@ -34,7 +34,7 @@ ADD_EXECUTABLE(${UT_SERVER_CODENAME} ${project_SRCS} ${common_SRCS} ${server_SRC
 ## Link libraries ##############################################################
 FIND_PACKAGE (Boost COMPONENTS unit_test_framework REQUIRED)
 
-PKG_CHECK_MODULES(UT_SERVER_DEPS REQUIRED libvirt json gio-2.0 libsystemd-journal)
+PKG_CHECK_MODULES(UT_SERVER_DEPS REQUIRED libvirt libvirt-glib-1.0 json gio-2.0 libsystemd-journal)
 INCLUDE_DIRECTORIES(${COMMON_FOLDER} ${SERVER_FOLDER} ${UNIT_TESTS_FOLDER})
 INCLUDE_DIRECTORIES(SYSTEM ${UT_SERVER_DEPS_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS})
 TARGET_LINK_LIBRARIES(${UT_SERVER_CODENAME} ${UT_SERVER_DEPS_LIBRARIES} ${Boost_LIBRARIES})
index 91974d9..ac9916d 100644 (file)
@@ -2,10 +2,11 @@
     <name>ut-container-admin-test</name>
     <uuid>a1299273-bce2-4d1f-8369-54b75a791279</uuid>
     <memory>102400</memory>
-    <on_poweroff>destroy</on_poweroff>
     <os>
         <type>exe</type>
         <init>/bin/sh</init>
+        <initarg>-c</initarg>
+        <initarg>trap exit SIGTERM; read</initarg>
     </os>
     <devices>
         <console type="pty"/>
index a4b2d73..678cd20 100644 (file)
@@ -2,12 +2,11 @@
     <name>ut-container-test-dbus</name>
     <uuid>35bb7989-f222-4b63-b0b1-facbdb05b495</uuid>
     <memory>102400</memory>
-    <on_poweroff>destroy</on_poweroff>
     <os>
         <type>exe</type>
-        <init>/bin/dbus-daemon</init>
-        <initarg>--config-file=@SC_TEST_CONFIG_INSTALL_DIR@/server/ut-container/ut-dbus.conf</initarg>
-        <initarg>--nofork</initarg>
+        <init>/bin/sh</init>
+        <initarg>-c</initarg>
+        <initarg>trap exit SIGTERM; /bin/dbus-daemon --config-file=@SC_TEST_CONFIG_INSTALL_DIR@/server/ut-container/ut-dbus.conf --fork; read</initarg>
     </os>
     <devices>
         <console type="pty"/>
index 06155ae..24b61fe 100644 (file)
@@ -2,10 +2,11 @@
     <name>ut-container-test</name>
     <uuid>be2e7a5e-c59f-4264-aeab-390cedf47922</uuid>
     <memory>102400</memory>
-    <on_poweroff>destroy</on_poweroff>
     <os>
         <type>exe</type>
         <init>/bin/sh</init>
+        <initarg>-c</initarg>
+        <initarg>trap exit SIGTERM; read</initarg>
     </os>
     <devices>
         <console type="pty"/>
index b95b2e0..f732480 100644 (file)
@@ -5,6 +5,8 @@
     <os>
         <type>exe</type>
         <init>/bin/sh</init>
+        <initarg>-c</initarg>
+        <initarg>trap exit SIGTERM; read</initarg>
     </os>
     <devices>
         <console type="pty"/>
index abe7e12..880d675 100644 (file)
@@ -5,6 +5,8 @@
     <os>
         <type>exe</type>
         <init>/bin/sh</init>
+        <initarg>-c</initarg>
+        <initarg>trap exit SIGTERM; read</initarg>
     </os>
     <devices>
         <console type="pty"/>
index b30e42a..2f5916f 100644 (file)
@@ -5,6 +5,8 @@
     <os>
         <type>exe</type>
         <init>/bin/sh</init>
+        <initarg>-c</initarg>
+        <initarg>trap exit SIGTERM; read</initarg>
     </os>
     <devices>
         <console type="pty"/>
index e2ea46f..0a67236 100644 (file)
@@ -5,6 +5,8 @@
     <os>
         <type>exe</type>
         <init>/bin/sh</init>
+        <initarg>-c</initarg>
+        <initarg>trap exit SIGTERM; read</initarg>
     </os>
     <devices>
         <console type="pty"/>
index 91bdf05..253c1ba 100644 (file)
@@ -5,6 +5,8 @@
     <os>
         <type>exe</type>
         <init>/bin/sh</init>
+        <initarg>-c</initarg>
+        <initarg>trap exit SIGTERM; read</initarg>
     </os>
     <devices>
         <console type="pty"/>
index 711fd49..3d65238 100644 (file)
@@ -5,6 +5,8 @@
     <os>
         <type>exe</type>
         <init>/bin/sh</init>
+        <initarg>-c</initarg>
+        <initarg>trap exit SIGTERM; read</initarg>
     </os>
     <devices>
         <console type="pty"/>
index 1beb30d..0d8cd23 100644 (file)
@@ -28,6 +28,7 @@
 #include "container-admin.hpp"
 #include "exception.hpp"
 
+#include "utils/glib-loop.hpp"
 #include "utils/exception.hpp"
 #include "libvirt/exception.hpp"
 
@@ -93,6 +94,8 @@ BOOST_AUTO_TEST_CASE(StartTest)
 
 BOOST_AUTO_TEST_CASE(StopTest)
 {
+    utils::ScopedGlibLoop loop;
+
     ContainerConfig config; config.parseFile(TEST_CONFIG_PATH);
     ContainerAdmin ca(config);
     BOOST_REQUIRE_NO_THROW(ca.start());
index 69d1a4a..ee71fe0 100644 (file)
 #include <string>
 
 
-BOOST_AUTO_TEST_SUITE(ContainerSuite)
-
 using namespace security_containers;
 using namespace security_containers::config;
-using namespace security_containers::utils;
 
 namespace {
 
@@ -48,8 +45,15 @@ const std::string TEST_DBUS_CONFIG_PATH = SC_TEST_CONFIG_INSTALL_DIR "/server/ut
 const std::string BUGGY_CONFIG_PATH = SC_TEST_CONFIG_INSTALL_DIR "/server/ut-container/containers/buggy.conf";
 const std::string MISSING_CONFIG_PATH = "/this/is/a/missing/file/path/config.conf";
 
+struct Fixture {
+    utils::ScopedGlibLoop loop;
+};
+
 } // namespace
 
+
+BOOST_FIXTURE_TEST_SUITE(ContainerSuite, Fixture)
+
 BOOST_AUTO_TEST_CASE(ConstructorTest)
 {
     BOOST_REQUIRE_NO_THROW(Container c(TEST_CONFIG_PATH));
@@ -73,8 +77,6 @@ BOOST_AUTO_TEST_CASE(MissingConfigTest)
 
 BOOST_AUTO_TEST_CASE(StartStopTest)
 {
-    ScopedGlibLoop loop;
-
     std::unique_ptr<Container> c(new Container(TEST_CONFIG_PATH));
     BOOST_REQUIRE_NO_THROW(c->start());
     BOOST_REQUIRE_NO_THROW(c->stop());
@@ -82,8 +84,6 @@ BOOST_AUTO_TEST_CASE(StartStopTest)
 
 BOOST_AUTO_TEST_CASE(DbusConnectionTest)
 {
-    ScopedGlibLoop loop;
-
     std::unique_ptr<Container> c;
     BOOST_REQUIRE_NO_THROW(c.reset(new Container(TEST_DBUS_CONFIG_PATH)));
     BOOST_REQUIRE_NO_THROW(c->start());
index 350154d..ab2bb75 100644 (file)
 #include "containers-manager.hpp"
 #include "exception.hpp"
 
+#include "utils/glib-loop.hpp"
+
 #include <memory>
 #include <string>
 
 
-BOOST_AUTO_TEST_SUITE(ContainersManagerSuite)
-
 using namespace security_containers;
 using namespace security_containers::config;
 
+namespace {
+
 const std::string TEST_CONFIG_PATH = SC_TEST_CONFIG_INSTALL_DIR "/server/ut-containers-manager/test-daemon.conf";
 const std::string BUGGY_CONFIG_PATH = SC_TEST_CONFIG_INSTALL_DIR "/server/ut-containers-manager/buggy-daemon.conf";
 const std::string BUGGY_FOREGROUND_CONFIG_PATH = SC_TEST_CONFIG_INSTALL_DIR "/server/ut-containers-manager/buggy-foreground-daemon.conf";
 const std::string MISSING_CONFIG_PATH = "/this/is/a/missing/file/path/missing-daemon.conf";
 
+struct Fixture {
+    utils::ScopedGlibLoop loop;
+};
+
+} // namespace
+
+
+BOOST_FIXTURE_TEST_SUITE(ContainersManagerSuite, Fixture)
 
 BOOST_AUTO_TEST_CASE(ConstructorTest)
 {