// condition on the mReconnectThread.
{
Lock lock(mReconnectMutex);
- mConnection.reset();
+ disconnect();
}
if (mReconnectThread.joinable()) {
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);
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()
{
{
Lock lock(mReconnectMutex);
- mConnection.reset();
+ disconnect();
}
for (int i = 0; i < RECONNECT_RETRIES; ++i) {
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&) {
}
}
+void Container::setDbusStateChangedCallback(const DbusStateChangedCallback& callback)
+{
+ mDbusStateChangedCallback = callback;
+}
+
void Container::proxyCallAsync(const std::string& busName,
const std::string& objectPath,
const std::string& interface,
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
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,
GVariant* parameters,
const dbus::DbusConnection::AsyncMethodCallCallback& callback);
+ /**
+ * Get a dbus address
+ */
+ std::string getDbusAddress();
+
private:
ContainerConfig mConfig;
std::vector<boost::regex> mPermittedToSend;
DisplayOffCallback mDisplayOffCallback;
FileMoveRequestCallback mFileMoveCallback;
ProxyCallCallback mProxyCallCallback;
+ DbusStateChangedCallback mDbusStateChangedCallback;
+ std::string mDbusAddress;
std::string mRunMountPoint;
void onNameLostCallback();
void reconnectHandler();
+ void connect();
+ void disconnect();
};
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;
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)));
}
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
GVariant* parameters,
dbus::MethodResultBuilder::Pointer result);
+ void handleGetContainerDbuses(dbus::MethodResultBuilder::Pointer result);
+ void handleDbusStateChanged(const std::string& containerId, const std::string& dbusAddress);
+
};
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,
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;
args.get(),
result);
}
+ return;
}
}
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
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
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,
bool mNameAcquired;
bool mNameLost;
ProxyCallCallback mProxyCallCallback;
+ GetContainerDbusesCallback mGetContainerDbusesCallback;
void onNameAcquired();
void onNameLost();
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>"
" <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>";
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())),
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,
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;
std::condition_variable mNameCondition;
bool isHost() const {
- return mId == 0;
+ return mId == HOST_ID;
}
std::string acquireAddress() const
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()