Dispatching container notifications 57/22657/18
authorMateusz Malicki <m.malicki2@samsung.com>
Mon, 9 Jun 2014 11:03:34 +0000 (13:03 +0200)
committerMateusz Malicki <m.malicki2@samsung.com>
Wed, 25 Jun 2014 12:41:27 +0000 (14:41 +0200)
[Bug/Feature]   Dispatching container notifications witch unit test
[Cause]         Container should be informed about other containers notification
[Solution]      Send dbus signal to other container
[Verification]  Build, install, launch "sc_launch_test.py security-containers-server-unit-tests -t
                ContainersManagerSuite/NotifyActiveContainerTest"

Change-Id: Ia101dee022e59ea7aef74e030eb3902a70c9f526

13 files changed:
server/container.cpp
server/container.hpp
server/containers-manager.cpp
server/containers-manager.hpp
tests/unit_tests/server/configs/ut-containers-manager/containers/console1-dbus.conf [new file with mode: 0644]
tests/unit_tests/server/configs/ut-containers-manager/containers/console2-dbus.conf [new file with mode: 0644]
tests/unit_tests/server/configs/ut-containers-manager/containers/console3-dbus.conf [new file with mode: 0644]
tests/unit_tests/server/configs/ut-containers-manager/libvirt-config/console1-dbus.xml [new file with mode: 0644]
tests/unit_tests/server/configs/ut-containers-manager/libvirt-config/console2-dbus.xml [new file with mode: 0644]
tests/unit_tests/server/configs/ut-containers-manager/libvirt-config/console3-dbus.xml [new file with mode: 0644]
tests/unit_tests/server/configs/ut-containers-manager/test-dbus-daemon.conf [new file with mode: 0644]
tests/unit_tests/server/configs/ut-containers-manager/ut-dbus.conf [new file with mode: 0644]
tests/unit_tests/server/ut-containers-manager.cpp

index c3361da..cebdb55 100644 (file)
@@ -42,6 +42,8 @@ namespace fs = boost::filesystem;
 
 namespace {
 
+typedef std::lock_guard<std::recursive_mutex> Lock;
+
 // TODO: move constants to the config file when default values are implemented there
 const int RECONNECT_RETRIES = 15;
 const int RECONNECT_DELAY = 1 * 1000;
@@ -67,7 +69,10 @@ Container::~Container()
     // Make sure all OnNameLostCallbacks get finished and no new will
     // get called before proceeding further. This guarantees no race
     // condition on the mReconnectThread.
-    mConnection.reset();
+    {
+        Lock lock(mReconnectMutex);
+        mConnection.reset();
+    }
 
     if (mReconnectThread.joinable()) {
         mReconnectThread.join();
@@ -76,6 +81,7 @@ Container::~Container()
 
 const std::string& Container::getId() const
 {
+    Lock lock(mReconnectMutex);
     return mAdmin->getId();
 }
 
@@ -86,12 +92,15 @@ int Container::getPrivilege() const
 
 void Container::start()
 {
+    Lock lock(mReconnectMutex);
     mConnectionTransport.reset(new ContainerConnectionTransport(mConfig.runMountPoint));
     mNetworkAdmin->start();
     mAdmin->start();
     mConnection.reset(new ContainerConnection(mConnectionTransport->acquireAddress(),
                                               std::bind(&Container::onNameLostCallback, this)));
-
+    if (mNotifyCallback) {
+        mConnection->setNotifyActiveContainerCallback(mNotifyCallback);
+    }
     // Send to the background only after we're connected,
     // otherwise it'd take ages.
     LOGD(getId() << ": DBUS connected, sending to the background");
@@ -100,6 +109,7 @@ void Container::start()
 
 void Container::stop()
 {
+    Lock lock(mReconnectMutex);
     mConnection.reset();
     mAdmin->stop();
     mNetworkAdmin->stop();
@@ -108,16 +118,19 @@ void Container::stop()
 
 void Container::goForeground()
 {
+    Lock lock(mReconnectMutex);
     mAdmin->setSchedulerLevel(SchedulerLevel::FOREGROUND);
 }
 
 void Container::goBackground()
 {
+    Lock lock(mReconnectMutex);
     mAdmin->setSchedulerLevel(SchedulerLevel::BACKGROUND);
 }
 
 void Container::setDetachOnExit()
 {
+    Lock lock(mReconnectMutex);
     mNetworkAdmin->setDetachOnExit();
     mAdmin->setDetachOnExit();
     mConnectionTransport->setDetachOnExit();
@@ -125,16 +138,19 @@ void Container::setDetachOnExit()
 
 bool Container::isRunning()
 {
+    Lock lock(mReconnectMutex);
     return mAdmin->isRunning();
 }
 
 bool Container::isStopped()
 {
+    Lock lock(mReconnectMutex);
     return mAdmin->isStopped();
 }
 
 bool Container::isPaused()
 {
+    Lock lock(mReconnectMutex);
     return mAdmin->isPaused();
 }
 
@@ -150,12 +166,16 @@ void Container::onNameLostCallback()
 
 void Container::reconnectHandler()
 {
-    mConnection.reset();
+    {
+        Lock lock(mReconnectMutex);
+        mConnection.reset();
+    }
 
     for (int i = 0; i < RECONNECT_RETRIES; ++i) {
         // This sleeps even before the first try to give DBUS some time to come back up
         std::this_thread::sleep_for(std::chrono::milliseconds(RECONNECT_DELAY));
 
+        Lock lock(mReconnectMutex);
         if (isStopped()) {
             LOGI(getId() << ": Has stopped, nothing to reconnect to, bailing out");
             return;
@@ -176,5 +196,26 @@ void Container::reconnectHandler()
     stop();
 }
 
+void Container::setNotifyActiveContainerCallback(const NotifyActiveContainerCallback& callback)
+{
+    Lock lock(mReconnectMutex);
+    mNotifyCallback = callback;
+    if (mConnection) {
+        mConnection->setNotifyActiveContainerCallback(mNotifyCallback);
+    }
+
+}
+
+void Container::sendNotification(const std::string& container,
+                                 const std::string& application,
+                                 const std::string& message)
+{
+    Lock lock(mReconnectMutex);
+    if (mConnection) {
+        mConnection->sendNotification(container, application, message);
+    } else {
+        LOGE(getId() << ": Can't send notification, no connection to DBUS");
+    }
+}
 
 } // namespace security_containers
index 640490f..a30708f 100644 (file)
@@ -47,6 +47,8 @@ public:
     Container(Container&&) = default;
     virtual ~Container();
 
+    typedef ContainerConnection::NotifyActiveContainerCallback NotifyActiveContainerCallback;
+
     /**
      * Get the container id
      */
@@ -106,6 +108,22 @@ public:
      */
     bool isPaused();
 
+    /**
+     * Register notification request callback
+     */
+    void setNotifyActiveContainerCallback(const NotifyActiveContainerCallback& callback);
+
+    /**
+     * Send notification signal to this container
+     *
+     * @param container   name of container in which the notification occurred
+     * @param application name of application that cause notification
+     * @param message     message to be send to container
+     */
+    void sendNotification(const std::string& container,
+                          const std::string& application,
+                          const std::string& message);
+
 private:
     ContainerConfig mConfig;
     std::unique_ptr<ContainerConnectionTransport> mConnectionTransport;
@@ -113,6 +131,8 @@ private:
     std::unique_ptr<ContainerAdmin> mAdmin;
     std::unique_ptr<ContainerConnection> mConnection;
     std::thread mReconnectThread;
+    mutable std::recursive_mutex mReconnectMutex;
+    NotifyActiveContainerCallback mNotifyCallback;
 
     void onNameLostCallback();
     void reconnectHandler();
index aeb4af9..d26ac06 100644 (file)
@@ -58,6 +58,12 @@ ContainersManager::ContainersManager(const std::string& managerConfigPath): mDet
         LOGD("Creating Container " << containerConfigPath);
         std::unique_ptr<Container> c(new Container(containerConfigPath));
         std::string id = c->getId();
+        using namespace std::placeholders;
+        c->setNotifyActiveContainerCallback(bind(&ContainersManager::notifyActiveContainerHandler,
+                                                 this,
+                                                 id,
+                                                 _1,
+                                                 _2));
         mContainers.insert(ContainerMap::value_type(id, std::move(c)));
     }
 
@@ -170,4 +176,20 @@ void ContainersManager::setContainersDetachOnExit()
     }
 }
 
+void ContainersManager::notifyActiveContainerHandler(const std::string& caller,
+                                                     const std::string& application,
+                                                     const std::string& message)
+{
+    LOGI("notifyActiveContainerHandler(" << caller << ", " << application << ", " << message
+         << ") called");
+    try {
+        const std::string activeContainer = getRunningForegroundContainerId();
+        if (!activeContainer.empty() && caller != activeContainer) {
+            mContainers[activeContainer]->sendNotification(caller, application, message);
+        }
+    } catch(const SecurityContainersException&) {
+        LOGE("Notification from " << caller << " hasn't been sent");
+    }
+}
+
 } // namespace security_containers
index 716f390..7fa3382 100644 (file)
@@ -82,6 +82,9 @@ private:
     bool mDetachOnExit;
 
     void switchingSequenceMonitorNotify();
+    void notifyActiveContainerHandler(const std::string& caller,
+                                      const std::string& appliaction,
+                                      const std::string& message);
 };
 
 
diff --git a/tests/unit_tests/server/configs/ut-containers-manager/containers/console1-dbus.conf b/tests/unit_tests/server/configs/ut-containers-manager/containers/console1-dbus.conf
new file mode 100644 (file)
index 0000000..fc12fec
--- /dev/null
@@ -0,0 +1,8 @@
+{
+    "privilege" : 20,
+    "config" : "../libvirt-config/console1-dbus.xml",
+    "networkConfig" : "../libvirt-config/network1.xml",
+    "cpuQuotaForeground" : -1,
+    "cpuQuotaBackground" : 1000,
+    "runMountPoint" : "/tmp/ut-containers-manager/console1-dbus"
+}
diff --git a/tests/unit_tests/server/configs/ut-containers-manager/containers/console2-dbus.conf b/tests/unit_tests/server/configs/ut-containers-manager/containers/console2-dbus.conf
new file mode 100644 (file)
index 0000000..41988e6
--- /dev/null
@@ -0,0 +1,8 @@
+{
+    "privilege" : 20,
+    "config" : "../libvirt-config/console2-dbus.xml",
+    "networkConfig" : "../libvirt-config/network2.xml",
+    "cpuQuotaForeground" : -1,
+    "cpuQuotaBackground" : 1000,
+    "runMountPoint" : "/tmp/ut-containers-manager/console2-dbus"
+}
diff --git a/tests/unit_tests/server/configs/ut-containers-manager/containers/console3-dbus.conf b/tests/unit_tests/server/configs/ut-containers-manager/containers/console3-dbus.conf
new file mode 100644 (file)
index 0000000..8ebdfaa
--- /dev/null
@@ -0,0 +1,8 @@
+{
+    "privilege" : 20,
+    "config" : "../libvirt-config/console3-dbus.xml",
+    "networkConfig" : "../libvirt-config/network3.xml",
+    "cpuQuotaForeground" : -1,
+    "cpuQuotaBackground" : 1000,
+    "runMountPoint" : "/tmp/ut-containers-manager/console3-dbus"
+}
diff --git a/tests/unit_tests/server/configs/ut-containers-manager/libvirt-config/console1-dbus.xml b/tests/unit_tests/server/configs/ut-containers-manager/libvirt-config/console1-dbus.xml
new file mode 100644 (file)
index 0000000..3df8595
--- /dev/null
@@ -0,0 +1,15 @@
+<domain type="lxc">
+    <name>ut-containers-manager-console1</name>
+    <uuid>58184009-b278-4d01-975d-708393690084</uuid>
+    <memory>102400</memory>
+    <os>
+        <type>exe</type>
+        <init>/bin/dbus-daemon</init>
+        <initarg>--nofork</initarg>
+        <initarg>--config-file=/usr/share/security-containers/tests/server/ut-containers-manager/ut-dbus.conf</initarg>
+        <initarg>--address=unix:path=/tmp/ut-containers-manager/console1-dbus/dbus/system_bus_socket</initarg>
+    </os>
+    <devices>
+        <console type="pty"/>
+    </devices>
+</domain>
diff --git a/tests/unit_tests/server/configs/ut-containers-manager/libvirt-config/console2-dbus.xml b/tests/unit_tests/server/configs/ut-containers-manager/libvirt-config/console2-dbus.xml
new file mode 100644 (file)
index 0000000..0fc7e83
--- /dev/null
@@ -0,0 +1,15 @@
+<domain type="lxc">
+    <name>ut-containers-manager-console2</name>
+    <uuid>3d18323e-4ada-4a1b-a907-836701891306</uuid>
+    <memory>102400</memory>
+    <os>
+        <type>exe</type>
+        <init>/bin/dbus-daemon</init>
+        <initarg>--nofork</initarg>
+        <initarg>--config-file=/usr/share/security-containers/tests/server/ut-containers-manager/ut-dbus.conf</initarg>
+        <initarg>--address=unix:path=/tmp/ut-containers-manager/console2-dbus/dbus/system_bus_socket</initarg>
+    </os>
+    <devices>
+        <console type="pty"/>
+    </devices>
+</domain>
diff --git a/tests/unit_tests/server/configs/ut-containers-manager/libvirt-config/console3-dbus.xml b/tests/unit_tests/server/configs/ut-containers-manager/libvirt-config/console3-dbus.xml
new file mode 100644 (file)
index 0000000..a341cb3
--- /dev/null
@@ -0,0 +1,15 @@
+<domain type="lxc">
+    <name>ut-containers-manager-console3</name>
+    <uuid>71cb8511-7474-4e90-865a-3360b7f77254</uuid>
+    <memory>102400</memory>
+    <os>
+        <type>exe</type>
+        <init>/bin/dbus-daemon</init>
+        <initarg>--nofork</initarg>
+        <initarg>--config-file=/usr/share/security-containers/tests/server/ut-containers-manager/ut-dbus.conf</initarg>
+        <initarg>--address=unix:path=/tmp/ut-containers-manager/console3-dbus/dbus/system_bus_socket</initarg>
+    </os>
+    <devices>
+        <console type="pty"/>
+    </devices>
+</domain>
diff --git a/tests/unit_tests/server/configs/ut-containers-manager/test-dbus-daemon.conf b/tests/unit_tests/server/configs/ut-containers-manager/test-dbus-daemon.conf
new file mode 100644 (file)
index 0000000..c403e04
--- /dev/null
@@ -0,0 +1,11 @@
+{
+    "containerConfigs" : ["containers/console1-dbus.conf",
+                          "containers/console2-dbus.conf",
+                          "containers/console3-dbus.conf"],
+    "foregroundId" : "ut-containers-manager-console1",
+    "inputConfig" : {"enabled" : false,
+                     "device" : "/dev/doesnotexist",
+                     "code" : 139,
+                     "numberOfEvents" : 2,
+                     "timeWindowMs" : 500}
+}
diff --git a/tests/unit_tests/server/configs/ut-containers-manager/ut-dbus.conf b/tests/unit_tests/server/configs/ut-containers-manager/ut-dbus.conf
new file mode 100644 (file)
index 0000000..865d15f
--- /dev/null
@@ -0,0 +1,18 @@
+<!-- This configuration file controls the containers message bus -->
+
+<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-Bus Bus Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+
+<busconfig>
+    <type>custom</type>
+    <listen>unix:path=/tmp/ut-containers-manager/dbus/system_bus_socket</listen>
+
+    <policy context="default">
+        <!-- Allow everything to be sent -->
+        <allow send_destination="*" eavesdrop="true"/>
+        <!-- Allow everything to be received -->
+        <allow eavesdrop="true"/>
+        <!-- Allow anyone to own anything -->
+        <allow own="*"/>
+    </policy>
+</busconfig>
index 46ec62c..f12c9d6 100644 (file)
 #include "ut.hpp"
 
 #include "containers-manager.hpp"
+#include "container-dbus-definitions.hpp"
 #include "exception.hpp"
 
+#include "dbus/connection.hpp"
 #include "utils/glib-loop.hpp"
 #include "config/exception.hpp"
+#include "utils/latch.hpp"
 
 #include <memory>
 #include <string>
+#include <algorithm>
+#include <functional>
 
 
 using namespace security_containers;
 using namespace security_containers::config;
+using namespace security_containers::utils;
+using namespace security_containers::dbus;
 
 namespace {
 
 const std::string TEST_CONFIG_PATH = SC_TEST_CONFIG_INSTALL_DIR "/server/ut-containers-manager/test-daemon.conf";
+const std::string TEST_DBUS_CONFIG_PATH = SC_TEST_CONFIG_INSTALL_DIR "/server/ut-containers-manager/test-dbus-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";
+const int EVENT_TIMEOUT = 5000;
+const int TEST_DBUS_CONNECTION_CONTAINERS_COUNT = 3;
+const std::string PREFIX_CONSOLE_NAME = "ut-containers-manager-console";
+const std::string TEST_APP_NAME = "testapp";
+const std::string TEST_MESSGAE = "testmessage";
+
+class DbusAccessory {
+public:
+    std::vector<std::string> mReceivedSignalsSource;
+
+    DbusAccessory(int id, Latch& signalEmittedLatch)
+        : mId(id),
+          mClient(DbusConnection::create(acquireAddress())),
+          mSignalEmittedLatch(signalEmittedLatch)
+    {
+    }
+
+    void signalSubscribe()
+    {
+        using namespace std::placeholders;
+        mClient->signalSubscribe(std::bind(&DbusAccessory::handler, this, _1, _2, _3, _4, _5),
+                                 api::BUS_NAME);
+    }
+
+    void callMethod()
+    {
+        GVariant* parameters = g_variant_new("(ss)", TEST_APP_NAME.c_str(), TEST_MESSGAE.c_str());
+        mClient->callMethod(api::BUS_NAME,
+                            api::OBJECT_PATH,
+                            api::INTERFACE,
+                            api::METHOD_NOTIFY_ACTIVE_CONTAINER,
+                            parameters,
+                            "()");
+    }
+
+    std::string getContainerName() const
+    {
+        return PREFIX_CONSOLE_NAME + std::to_string(mId);
+    }
+
+private:
+    const int mId;
+    DbusConnection::Pointer mClient;
+    Latch& mSignalEmittedLatch;
+
+    std::string acquireAddress() const
+    {
+        return "unix:path=/tmp/ut-containers-manager/console" + std::to_string(mId) +
+               "-dbus/dbus/system_bus_socket";
+    }
+
+    void handler(const std::string& /*senderBusName*/,
+                 const std::string& objectPath,
+                 const std::string& interface,
+                 const std::string& signalName,
+                 GVariant* parameters)
+    {
+        if (objectPath == api::OBJECT_PATH &&
+            interface == api::INTERFACE &&
+            signalName == api::SIGNAL_NOTIFICATION &&
+            g_variant_is_of_type(parameters, G_VARIANT_TYPE("(sss)"))) {
+
+            const gchar* container = NULL;
+            const gchar* application = NULL;
+            const gchar* message = NULL;
+            g_variant_get(parameters, "(&s&s&s)", &container, &application, &message);
+            mReceivedSignalsSource.push_back(container);
+            if (application == TEST_APP_NAME && message == TEST_MESSGAE) {
+                mSignalEmittedLatch.set();
+            }
+        }
+    }
+};
+
 
 struct Fixture {
     utils::ScopedGlibLoop mLoop;
@@ -119,4 +201,41 @@ BOOST_AUTO_TEST_CASE(FocusTest)
     BOOST_CHECK(cm.getRunningForegroundContainerId() == "ut-containers-manager-console3");
 }
 
+BOOST_AUTO_TEST_CASE(NotifyActiveContainerTest)
+{
+    Latch signalReceivedLatch;
+
+    ContainersManager cm(TEST_DBUS_CONFIG_PATH);
+    cm.startAll();
+
+    std::vector< std::unique_ptr<DbusAccessory> > dbuses;
+    for (int i = 1; i <= TEST_DBUS_CONNECTION_CONTAINERS_COUNT; ++i) {
+        dbuses.push_back(std::unique_ptr<DbusAccessory>(new DbusAccessory(i, signalReceivedLatch)));
+    }
+    for (auto& dbus : dbuses) {
+        dbus->signalSubscribe();
+    }
+    for (auto& dbus : dbuses) {
+        dbus->callMethod();
+    }
+
+    BOOST_CHECK(signalReceivedLatch.waitForN(dbuses.size() - 1, EVENT_TIMEOUT));
+    BOOST_CHECK(signalReceivedLatch.empty());
+
+    //check if there are no signals that was received more than once
+    for (const auto& source : dbuses[0]->mReceivedSignalsSource) {
+        BOOST_CHECK_EQUAL(std::count(dbuses[0]->mReceivedSignalsSource.begin(),
+                               dbuses[0]->mReceivedSignalsSource.end(),
+                               source), 1);
+    }
+    //check if all signals was received by active container
+    BOOST_CHECK_EQUAL(dbuses[0]->mReceivedSignalsSource.size(), dbuses.size() - 1);
+    //check if no signals was received by inactive container
+    for (size_t i = 1; i < dbuses.size(); ++i) {
+        BOOST_CHECK(dbuses[i]->mReceivedSignalsSource.empty());
+    }
+
+    dbuses.clear();
+}
+
 BOOST_AUTO_TEST_SUITE_END()