Dbus API for sharing containers DBuses 53/24853/2
authorPiotr Bartosiewicz <p.bartosiewi@partner.samsung.com>
Wed, 23 Jul 2014 10:56:47 +0000 (12:56 +0200)
committerPiotr Bartosiewicz <p.bartosiewi@partner.samsung.com>
Thu, 24 Jul 2014 09:33:14 +0000 (11:33 +0200)
[Bug/Feature]   New method on host dbus interface for getting list of
                containers dbus addresses and new signals when
                containers dbus become available or unavailable.
[Cause]         N/A
[Solution]      N/A
[Verification]  Build, install, run tests

Change-Id: Ib37d47c8e2ffbdca58828c542d7b474e068ca138

server/container.cpp
server/container.hpp
server/containers-manager.cpp
server/containers-manager.hpp
server/host-connection.cpp
server/host-connection.hpp
server/host-dbus-definitions.hpp
tests/unit_tests/server/ut-containers-manager.cpp

index 0b61d65..4284fec 100644 (file)
@@ -82,7 +82,7 @@ Container::~Container()
     // condition on the mReconnectThread.
     {
         Lock lock(mReconnectMutex);
-        mConnection.reset();
+        disconnect();
     }
 
     if (mReconnectThread.joinable()) {
@@ -117,7 +117,28 @@ void Container::start()
     mConnectionTransport.reset(new ContainerConnectionTransport(mRunMountPoint));
     mNetworkAdmin->start();
     mAdmin->start();
-    mConnection.reset(new ContainerConnection(mConnectionTransport->acquireAddress(),
+    connect();
+
+    // Send to the background only after we're connected,
+    // otherwise it'd take ages.
+    LOGD(getId() << ": DBUS connected, sending to the background");
+    goBackground();
+}
+
+void Container::stop()
+{
+    Lock lock(mReconnectMutex);
+    disconnect();
+    mAdmin->stop();
+    mNetworkAdmin->stop();
+    mConnectionTransport.reset();
+}
+
+void Container::connect()
+{
+    // assume called under reconnect lock
+    mDbusAddress = mConnectionTransport->acquireAddress();
+    mConnection.reset(new ContainerConnection(mDbusAddress,
                                               std::bind(&Container::onNameLostCallback, this)));
     if (mNotifyCallback) {
         mConnection->setNotifyActiveContainerCallback(mNotifyCallback);
@@ -131,20 +152,28 @@ void Container::start()
     if (mProxyCallCallback) {
         mConnection->setProxyCallCallback(mProxyCallCallback);
     }
+    if (mDbusStateChangedCallback) {
+        mDbusStateChangedCallback(mDbusAddress);
+    }
+}
 
-    // Send to the background only after we're connected,
-    // otherwise it'd take ages.
-    LOGD(getId() << ": DBUS connected, sending to the background");
-    goBackground();
+void Container::disconnect()
+{
+    // assume called under reconnect lock
+    if (mConnection) {
+        mConnection.reset();
+        mDbusAddress.clear();
+        if (mDbusStateChangedCallback) {
+            // notify about invalid dbusAddress for this container
+            mDbusStateChangedCallback(std::string());
+        }
+    }
 }
 
-void Container::stop()
+std::string Container::getDbusAddress()
 {
     Lock lock(mReconnectMutex);
-    mConnection.reset();
-    mAdmin->stop();
-    mNetworkAdmin->stop();
-    mConnectionTransport.reset();
+    return mDbusAddress;
 }
 
 void Container::goForeground()
@@ -204,7 +233,7 @@ void Container::reconnectHandler()
 {
     {
         Lock lock(mReconnectMutex);
-        mConnection.reset();
+        disconnect();
     }
 
     for (int i = 0; i < RECONNECT_RETRIES; ++i) {
@@ -219,8 +248,7 @@ void Container::reconnectHandler()
 
         try {
             LOGT(getId() << ": Reconnect try " << i + 1);
-            mConnection.reset(new ContainerConnection(mConnectionTransport->acquireAddress(),
-                                                      std::bind(&Container::onNameLostCallback, this)));
+            connect();
             LOGI(getId() << ": Reconnected");
             return;
         } catch (SecurityContainersException&) {
@@ -283,6 +311,11 @@ void Container::setProxyCallCallback(const ProxyCallCallback& callback)
     }
 }
 
+void Container::setDbusStateChangedCallback(const DbusStateChangedCallback& callback)
+{
+    mDbusStateChangedCallback = callback;
+}
+
 void Container::proxyCallAsync(const std::string& busName,
                                const std::string& objectPath,
                                const std::string& interface,
index dff5d23..c29b860 100644 (file)
@@ -54,6 +54,8 @@ public:
     typedef ContainerConnection::FileMoveRequestCallback FileMoveRequestCallback;
     typedef ContainerConnection::ProxyCallCallback ProxyCallCallback;
 
+    typedef std::function<void(const std::string& address)> DbusStateChangedCallback;
+
     /**
      * Returns a vector of regexps defining files permitted to be
      * send to other containers using file move functionality
@@ -166,6 +168,11 @@ public:
     void setFileMoveRequestCallback(const FileMoveRequestCallback& callback);
 
     /**
+     * Register dbus state changed callback
+     */
+    void setDbusStateChangedCallback(const DbusStateChangedCallback& callback);
+
+    /**
      * Make a proxy call
      */
     void proxyCallAsync(const std::string& busName,
@@ -175,6 +182,11 @@ public:
                         GVariant* parameters,
                         const dbus::DbusConnection::AsyncMethodCallCallback& callback);
 
+    /**
+     * Get a dbus address
+     */
+    std::string getDbusAddress();
+
 private:
     ContainerConfig mConfig;
     std::vector<boost::regex> mPermittedToSend;
@@ -189,10 +201,14 @@ private:
     DisplayOffCallback mDisplayOffCallback;
     FileMoveRequestCallback mFileMoveCallback;
     ProxyCallCallback mProxyCallCallback;
+    DbusStateChangedCallback mDbusStateChangedCallback;
+    std::string mDbusAddress;
     std::string mRunMountPoint;
 
     void onNameLostCallback();
     void reconnectHandler();
+    void connect();
+    void disconnect();
 };
 
 
index 7bd22b3..dc6e637 100644 (file)
@@ -75,6 +75,9 @@ ContainersManager::ContainersManager(const std::string& managerConfigPath): mDet
     mHostConnection.setProxyCallCallback(bind(&ContainersManager::handleProxyCall,
                                               this, HOST_ID, _1, _2, _3, _4, _5, _6, _7));
 
+    mHostConnection.setGetContainerDbusesCallback(bind(
+                &ContainersManager::handleGetContainerDbuses, this, _1));
+
     for (auto& containerConfig : mConfig.containerConfigs) {
         std::string containerConfigPath;
 
@@ -105,6 +108,9 @@ ContainersManager::ContainersManager(const std::string& managerConfigPath): mDet
         c->setProxyCallCallback(bind(&ContainersManager::handleProxyCall,
                                      this, id, _1, _2, _3, _4, _5, _6, _7));
 
+        c->setDbusStateChangedCallback(bind(&ContainersManager::handleDbusStateChanged,
+                                            this, id, _1));
+
         mContainers.insert(ContainerMap::value_type(id, std::move(c)));
     }
 
@@ -391,5 +397,24 @@ void ContainersManager::handleProxyCall(const std::string& caller,
                                    asyncResultCallback);
 }
 
+void ContainersManager::handleGetContainerDbuses(dbus::MethodResultBuilder::Pointer result)
+{
+    std::vector<GVariant*> entries;
+    for (auto& container : mContainers) {
+        GVariant* containerId = g_variant_new_string(container.first.c_str());
+        GVariant* dbusAddress = g_variant_new_string(container.second->getDbusAddress().c_str());
+        GVariant* entry = g_variant_new_dict_entry(containerId, dbusAddress);
+        entries.push_back(entry);
+    }
+    GVariant* dict = g_variant_new_array(G_VARIANT_TYPE("{ss}"), entries.data(), entries.size());
+    result->set(g_variant_new("(*)", dict));
+}
+
+void ContainersManager::handleDbusStateChanged(const std::string& containerId,
+                                               const std::string& dbusAddress)
+{
+    mHostConnection.signalContainerDbusState(containerId, dbusAddress);
+}
+
 
 } // namespace security_containers
index 4354fdd..8a5c823 100644 (file)
@@ -103,6 +103,9 @@ private:
                          GVariant* parameters,
                          dbus::MethodResultBuilder::Pointer result);
 
+    void handleGetContainerDbuses(dbus::MethodResultBuilder::Pointer result);
+    void handleDbusStateChanged(const std::string& containerId, const std::string& dbusAddress);
+
 };
 
 
index 2821202..88ce5a4 100644 (file)
@@ -115,6 +115,11 @@ void HostConnection::setProxyCallCallback(const ProxyCallCallback& callback)
     mProxyCallCallback = callback;
 }
 
+void HostConnection::setGetContainerDbusesCallback(const GetContainerDbusesCallback& callback)
+{
+    mGetContainerDbusesCallback = callback;
+}
+
 void HostConnection::onMessageCall(const std::string& objectPath,
                                         const std::string& interface,
                                         const std::string& methodName,
@@ -125,6 +130,13 @@ void HostConnection::onMessageCall(const std::string& objectPath,
         return;
     }
 
+    if (methodName == hostapi::METHOD_GET_CONTAINER_DBUSES) {
+        if (mGetContainerDbusesCallback) {
+            mGetContainerDbusesCallback(result);
+        }
+        return;
+    }
+
     if (methodName == hostapi::METHOD_PROXY_CALL) {
         const gchar* target = NULL;
         const gchar* targetBusName = NULL;
@@ -151,6 +163,7 @@ void HostConnection::onMessageCall(const std::string& objectPath,
                                args.get(),
                                result);
         }
+        return;
     }
 }
 
@@ -170,5 +183,15 @@ void HostConnection::proxyCallAsync(const std::string& busName,
                                      callback);
 }
 
+void HostConnection::signalContainerDbusState(const std::string& containerId,
+                                              const std::string& dbusAddress)
+{
+    GVariant* parameters = g_variant_new("(ss)", containerId.c_str(), dbusAddress.c_str());
+    mDbusConnection->emitSignal(hostapi::OBJECT_PATH,
+                                hostapi::INTERFACE,
+                                hostapi::SIGNAL_CONTAINER_DBUS_STATE,
+                                parameters);
+}
+
 
 } // namespace security_containers
index 88cb480..3d9c493 100644 (file)
@@ -49,7 +49,11 @@ public:
                                const std::string& targetInterface,
                                const std::string& targetMethod,
                                GVariant* parameters,
-                               dbus::MethodResultBuilder::Pointer result)> ProxyCallCallback;
+                               dbus::MethodResultBuilder::Pointer result
+                              )> ProxyCallCallback;
+
+    typedef std::function<void(dbus::MethodResultBuilder::Pointer result
+                              )> GetContainerDbusesCallback;
 
     /**
      * Register proxy call callback
@@ -57,6 +61,16 @@ public:
     void setProxyCallCallback(const ProxyCallCallback& callback);
 
     /**
+     * Register get container dbuses callback
+     */
+    void setGetContainerDbusesCallback(const GetContainerDbusesCallback& callback);
+
+    /**
+     * Send signal describing dbus address state change
+     */
+    void signalContainerDbusState(const std::string& containerId, const std::string& dbusAddress);
+
+    /**
      * Make a proxy call
      */
     void proxyCallAsync(const std::string& busName,
@@ -73,6 +87,7 @@ private:
     bool mNameAcquired;
     bool mNameLost;
     ProxyCallCallback mProxyCallCallback;
+    GetContainerDbusesCallback mGetContainerDbusesCallback;
 
     void onNameAcquired();
     void onNameLost();
index f41da38..4594433 100644 (file)
@@ -37,6 +37,9 @@ const std::string OBJECT_PATH                       = "/org/tizen/containers/hos
 const std::string INTERFACE                         = "org.tizen.containers.host.manager";
 
 const std::string METHOD_PROXY_CALL                 = "ProxyCall";
+const std::string METHOD_GET_CONTAINER_DBUSES       = "GetContainerDbuses";
+
+const std::string SIGNAL_CONTAINER_DBUS_STATE       = "ContainerDbusState";
 
 const std::string DEFINITION =
     "<node>"
@@ -50,6 +53,13 @@ const std::string DEFINITION =
     "      <arg type='v' name='parameters' direction='in'/>"
     "      <arg type='v' name='result' direction='out'/>"
     "    </method>"
+    "    <method name='" + METHOD_GET_CONTAINER_DBUSES + "'>"
+    "      <arg type='a{ss}' name='dbuses' direction='out'/>"
+    "    </method>"
+    "    <signal name='" + SIGNAL_CONTAINER_DBUS_STATE + "'>"
+    "      <arg type='s' name='container'/>"
+    "      <arg type='s' name='dbusAddress'/>"
+    "    </signal>"
     "  </interface>"
     "</node>";
 
index 64659ef..43bc15a 100644 (file)
@@ -75,10 +75,14 @@ const std::string FILE_CONTENT = "File content\n"
 
 class DbusAccessory {
 public:
+    static const int HOST_ID = 0;
+
     typedef std::function<void(const std::string& argument,
                                MethodResultBuilder::Pointer result
                               )> TestApiMethodCallback;
 
+    typedef std::map<std::string, std::string> Dbuses;
+
     DbusAccessory(int id)
         : mId(id),
           mClient(DbusConnection::create(acquireAddress())),
@@ -122,7 +126,7 @@ public:
 
     void signalSubscribe(const DbusConnection::SignalCallback& callback)
     {
-        mClient->signalSubscribe(callback, api::BUS_NAME);
+        mClient->signalSubscribe(callback, isHost() ? hostapi::BUS_NAME : api::BUS_NAME);
     }
 
     void emitSignal(const std::string& objectPath,
@@ -218,6 +222,28 @@ public:
         g_variant_get(result.get(), "(v)", &unpackedResult);
         return GVariantPtr(unpackedResult, g_variant_unref);
     }
+
+    Dbuses callMethodGetContainerDbuses() {
+        assert(isHost());
+        Dbuses dbuses;
+        GVariantPtr result = mClient->callMethod(hostapi::BUS_NAME,
+                                                 hostapi::OBJECT_PATH,
+                                                 hostapi::INTERFACE,
+                                                 hostapi::METHOD_GET_CONTAINER_DBUSES,
+                                                 NULL,
+                                                 "(a{ss})");
+        GVariant* array = NULL;
+        g_variant_get(result.get(), "(*)", &array);
+        dbus::GVariantPtr autounref(array, g_variant_unref);
+        size_t count = g_variant_n_children(array);
+        for (size_t n = 0; n < count; ++n) {
+            const char* containerId = NULL;
+            const char* dbusAddress = NULL;
+            g_variant_get_child(array, n, "{&s&s}", &containerId, &dbusAddress);
+            dbuses.insert(Dbuses::value_type(containerId, dbusAddress));
+        }
+        return dbuses;
+    }
 private:
     const int mId;
     DbusConnection::Pointer mClient;
@@ -227,7 +253,7 @@ private:
     std::condition_variable mNameCondition;
 
     bool isHost() const {
-        return mId == 0;
+        return mId == HOST_ID;
     }
 
     std::string acquireAddress() const
@@ -644,5 +670,83 @@ BOOST_AUTO_TEST_CASE(ProxyCallTest)
                           expectedMessage("Proxy call forbidden"));
 }
 
+namespace {
+    const DbusAccessory::Dbuses EXPECTED_DBUSES_STOPPED = {
+        {"ut-containers-manager-console1-dbus", ""},
+        {"ut-containers-manager-console2-dbus", ""},
+        {"ut-containers-manager-console3-dbus", ""}};
+
+    const DbusAccessory::Dbuses EXPECTED_DBUSES_STARTED = {
+        {"ut-containers-manager-console1-dbus",
+         "unix:path=/tmp/ut-containers-manager/console1-dbus/dbus/system_bus_socket"},
+        {"ut-containers-manager-console2-dbus",
+         "unix:path=/tmp/ut-containers-manager/console2-dbus/dbus/system_bus_socket"},
+        {"ut-containers-manager-console3-dbus",
+         "unix:path=/tmp/ut-containers-manager/console3-dbus/dbus/system_bus_socket"}};
+} // namespace
+
+BOOST_AUTO_TEST_CASE(GetContainerDbusesTest)
+{
+    DbusAccessory host(DbusAccessory::HOST_ID);
+
+    ContainersManager cm(TEST_DBUS_CONFIG_PATH);
+
+    BOOST_CHECK(EXPECTED_DBUSES_STOPPED == host.callMethodGetContainerDbuses());
+
+    cm.startAll();
+
+    BOOST_CHECK(EXPECTED_DBUSES_STARTED == host.callMethodGetContainerDbuses());
+
+    cm.stopAll();
+
+    BOOST_CHECK(EXPECTED_DBUSES_STOPPED == host.callMethodGetContainerDbuses());
+}
+
+BOOST_AUTO_TEST_CASE(ContainerDbusesSignalsTest)
+{
+    DbusAccessory host(DbusAccessory::HOST_ID);
+
+    Latch signalLatch;
+    DbusAccessory::Dbuses collectedDbuses;
+
+    auto onSignal = [&] (const std::string& /*senderBusName*/,
+                         const std::string& objectPath,
+                         const std::string& interface,
+                         const std::string& signalName,
+                         GVariant* parameters) {
+        if (objectPath == hostapi::OBJECT_PATH &&
+            interface == hostapi::INTERFACE &&
+            signalName == hostapi::SIGNAL_CONTAINER_DBUS_STATE) {
+
+            const gchar* containerId = NULL;
+            const gchar* dbusAddress = NULL;
+            g_variant_get(parameters, "(&s&s)", &containerId, &dbusAddress);
+
+            collectedDbuses.insert(DbusAccessory::Dbuses::value_type(containerId, dbusAddress));
+            signalLatch.set();
+        }
+    };
+
+    host.signalSubscribe(onSignal);
+
+    {
+        ContainersManager cm(TEST_DBUS_CONFIG_PATH);
+
+        BOOST_CHECK(signalLatch.empty());
+        BOOST_CHECK(collectedDbuses.empty());
+
+        cm.startAll();
+
+        BOOST_CHECK(signalLatch.waitForN(TEST_DBUS_CONNECTION_CONTAINERS_COUNT, EVENT_TIMEOUT));
+        BOOST_CHECK(signalLatch.empty());
+        BOOST_CHECK(EXPECTED_DBUSES_STARTED == collectedDbuses);
+        collectedDbuses.clear();
+    }
+
+    BOOST_CHECK(signalLatch.waitForN(TEST_DBUS_CONNECTION_CONTAINERS_COUNT, EVENT_TIMEOUT));
+    BOOST_CHECK(signalLatch.empty());
+    BOOST_CHECK(EXPECTED_DBUSES_STOPPED == collectedDbuses);
+}
+
 
 BOOST_AUTO_TEST_SUITE_END()