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;
// 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();
const std::string& Container::getId() const
{
+ Lock lock(mReconnectMutex);
return mAdmin->getId();
}
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");
void Container::stop()
{
+ Lock lock(mReconnectMutex);
mConnection.reset();
mAdmin->stop();
mNetworkAdmin->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();
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();
}
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;
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
Container(Container&&) = default;
virtual ~Container();
+ typedef ContainerConnection::NotifyActiveContainerCallback NotifyActiveContainerCallback;
+
/**
* Get the container id
*/
*/
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;
std::unique_ptr<ContainerAdmin> mAdmin;
std::unique_ptr<ContainerConnection> mConnection;
std::thread mReconnectThread;
+ mutable std::recursive_mutex mReconnectMutex;
+ NotifyActiveContainerCallback mNotifyCallback;
void onNameLostCallback();
void reconnectHandler();
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)));
}
}
}
+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
bool mDetachOnExit;
void switchingSequenceMonitorNotify();
+ void notifyActiveContainerHandler(const std::string& caller,
+ const std::string& appliaction,
+ const std::string& message);
};
--- /dev/null
+{
+ "privilege" : 20,
+ "config" : "../libvirt-config/console1-dbus.xml",
+ "networkConfig" : "../libvirt-config/network1.xml",
+ "cpuQuotaForeground" : -1,
+ "cpuQuotaBackground" : 1000,
+ "runMountPoint" : "/tmp/ut-containers-manager/console1-dbus"
+}
--- /dev/null
+{
+ "privilege" : 20,
+ "config" : "../libvirt-config/console2-dbus.xml",
+ "networkConfig" : "../libvirt-config/network2.xml",
+ "cpuQuotaForeground" : -1,
+ "cpuQuotaBackground" : 1000,
+ "runMountPoint" : "/tmp/ut-containers-manager/console2-dbus"
+}
--- /dev/null
+{
+ "privilege" : 20,
+ "config" : "../libvirt-config/console3-dbus.xml",
+ "networkConfig" : "../libvirt-config/network3.xml",
+ "cpuQuotaForeground" : -1,
+ "cpuQuotaBackground" : 1000,
+ "runMountPoint" : "/tmp/ut-containers-manager/console3-dbus"
+}
--- /dev/null
+<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>
--- /dev/null
+<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>
--- /dev/null
+<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>
--- /dev/null
+{
+ "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}
+}
--- /dev/null
+<!-- 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>
#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;
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()