BuildRequires: libvirt-devel
BuildRequires: libjson
BuildRequires: libjson-devel
+BuildRequires: pkgconfig(glib-2.0)
%description
This package provides a daemon used to manage containers - start, stop and switch
%config %attr(644,root,root) /etc/security-containers/config/tests/ut-scs-container-manager/*.conf
%config %attr(644,root,root) /etc/security-containers/config/tests/ut-scs-container-manager/containers/*.conf
%config %attr(644,root,root) /etc/security-containers/config/tests/ut-scs-container-manager/libvirt-config/*.xml
+%config %attr(644,root,root) /etc/security-containers/config/tests/ut-dbus-connection/*.conf
--- /dev/null
+/*
+ * Copyright (c) 2000 - 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Contact: Bumjin Im <bj.im@samsung.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+/**
+ * @file dbus-connection.hpp
+ * @author Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ * @brief Dbus connection class
+ */
+
+#ifndef DBUS_CONNECTION_HPP
+#define DBUS_CONNECTION_HPP
+
+#include <memory>
+#include <string>
+#include <functional>
+#include <gio/gio.h>
+
+/**
+ * An interface used to set a result to a method call.
+ */
+class MethodResultBuilder {
+public:
+ virtual ~MethodResultBuilder() {}
+ virtual void set(GVariant* parameters) = 0;
+ virtual void setVoid() = 0;
+ virtual void setError(const std::string& name, const std::string& message) = 0;
+};
+
+/**
+ * Dbus connection.
+ * Provides a functionality that allows to call dbus methods,
+ * register dbus interfaces, etc.
+ *
+ * TODO divide to interface and implementation header
+ * TODO wrap GVariant type
+ */
+class DbusConnection {
+public:
+ typedef std::shared_ptr<DbusConnection> Pointer;
+
+ typedef std::function<void()> VoidCallback;
+
+ typedef std::function<void(const std::string& objectPath,
+ const std::string& interface,
+ const std::string& method,
+ GVariant* parameters,
+ MethodResultBuilder& result
+ )> MethodCallCallback;
+
+ /**
+ * Creates a connection to the dbus with given address.
+ */
+ static Pointer create(const std::string& address);
+
+ /**
+ * Creates a connection to the system dbus.
+ */
+ static Pointer createSystem();
+
+ ~DbusConnection();
+
+ /**
+ * Sets a name to the dbus connection.
+ * It allows other client to call methods using this name.
+ */
+ void setName(const std::string& name,
+ const VoidCallback& onNameAcquired,
+ const VoidCallback& onNameLost);
+
+ /**
+ * Emits dbus signal.
+ */
+ void emitSignal(const std::string& objectPath,
+ const std::string& interface,
+ const std::string& name,
+ GVariant* parameters);
+
+ /**
+ * Subscribes to a signal.
+ * TODO not finished
+ */
+ void signalSubscribe();
+
+ /**
+ * Registers an object with given definition.
+ * Api calls will be handled by given callback.
+ */
+ void registerObject(const std::string& objectPath,
+ const std::string& objectDefinitionXml,
+ const MethodCallCallback& callback);
+
+ /**
+ * Call a dbus method
+ */
+ GVariant* callMethod(const std::string& busName,
+ const std::string& objectPath,
+ const std::string& interface,
+ const std::string& method,
+ GVariant* parameters,
+ const GVariantType* replyType);
+
+ /**
+ * Returns an xml with meta description of specified dbus object.
+ */
+ std::string introspect(const std::string& busName, const std::string& objectPath);
+
+private:
+ struct NameCallbacks {
+ VoidCallback nameAcquired;
+ VoidCallback nameLost;
+
+ NameCallbacks(const VoidCallback& acquired, const VoidCallback& lost)
+ : nameAcquired(acquired), nameLost(lost) {}
+ };
+
+ GDBusConnection* mConnection;
+ guint mNameId;
+
+ DbusConnection(const std::string& address);
+
+ static void onNameAcquired(GDBusConnection* connection, const gchar* name, gpointer userData);
+ static void onNameLost(GDBusConnection* connection, const gchar* name, gpointer userData);
+ static void onSignal(GDBusConnection* connection,
+ const gchar* sender,
+ const gchar* object,
+ const gchar* interface,
+ const gchar* name,
+ GVariant* parameters,
+ gpointer userData);
+ static void onMethodCall(GDBusConnection* connection,
+ const gchar* sender,
+ const gchar* objectPath,
+ const gchar* interface,
+ const gchar* method,
+ GVariant* parameters,
+ GDBusMethodInvocation* invocation,
+ gpointer userData);
+};
+
+
+#endif //DBUS_CONNECTION_HPP
--- /dev/null
+/*
+ * Copyright (c) 2000 - 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Contact: Bumjin Im <bj.im@samsung.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+/**
+ * @file dbus-exception.hpp
+ * @author Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ * @brief Dbus exceptions
+ */
+
+
+#ifndef DBUS_EXCEPTION_HPP
+#define DBUS_EXCEPTION_HPP
+
+#include <stdexcept>
+
+/**
+ * Base class for dbus exceptions
+ */
+struct DbusException: public std::runtime_error {
+ using std::runtime_error::runtime_error;
+};
+
+/**
+ * Dbus connection failed exception
+ */
+struct DbusConnectException: public DbusException {
+ using DbusException::DbusException;
+};
+
+/**
+ * Dbus operation failed exception
+ * TODO split to more specific exceptions
+ */
+struct DbusOperationException: public DbusException {
+ using DbusException::DbusException;
+};
+
+#endif // DBUS_EXCEPTION_HPP
--- /dev/null
+/*
+ * Copyright (c) 2000 - 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Contact: Bumjin Im <bj.im@samsung.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+/**
+ * @file file-wait.hpp
+ * @author Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ * @brief Wait for file utility function
+ */
+
+#ifndef FILE_WAIT_HPP
+#define FILE_WAIT_HPP
+
+#include <string>
+
+//TODO It is used in unit tests now, but it is unclear
+// whether the same solution will be used in daemon.
+void waitForFile(const std::string& filename, const unsigned int timeoutMs);
+
+#endif // FILE_WAIT_HPP
--- /dev/null
+/*
+ * Copyright (c) 2000 - 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Contact: Bumjin Im <bj.im@samsung.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+/**
+ * @file glib-loop.hpp
+ * @author Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ * @brief C++ wrapper of glib main loop
+ */
+
+#ifndef GLIB_LOOP_HPP
+#define GLIB_LOOP_HPP
+
+#include <thread>
+#include <memory>
+
+struct _GMainLoop;
+typedef struct _GMainLoop GMainLoop;
+
+/**
+ * Glib loop controller. Loop is running in separate thread.
+ */
+class ScopedGlibLoop {
+public:
+ /**
+ * Starts a loop in separate thread.
+ */
+ ScopedGlibLoop();
+
+ /**
+ * Stops loop and waits for a thread.
+ */
+ ~ScopedGlibLoop();
+
+private:
+ std::unique_ptr<GMainLoop, void(*)(GMainLoop*)> mLoop;
+ std::thread mLoopThread;
+};
+
+#endif //GLIB_LOOP_HPP
--- /dev/null
+/*
+ * Copyright (c) 2000 - 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Contact: Bumjin Im <bj.im@samsung.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+/**
+ * @file latch.hpp
+ * @author Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ * @brief Synchronization latch
+ */
+
+#ifndef LATCH_HPP
+#define LATCH_HPP
+
+#include <mutex>
+#include <condition_variable>
+
+/**
+ * A synchronization aid that allows one thread to wait until
+ * an operation being performed in other thread completes.
+ * It has a similar function as std::promise<void> but allows
+ * multiple calls to set.
+ */
+class Latch {
+public:
+ Latch();
+
+ /**
+ * Sets an event occurred.
+ */
+ void set();
+
+ /**
+ * Waits for a single occurrence of event.
+ */
+ void wait();
+
+ /**
+ * Waits with timeout.
+ * @return false on timeout
+ */
+ bool wait(const unsigned int timeoutMs);
+
+ /**
+ * Check if there are no pending events.
+ */
+ bool empty();
+private:
+ std::mutex mMutex;
+ std::condition_variable mCondition;
+ int mCount;
+};
+
+#endif // LATCH_HPP
## Link libraries ##############################################################
FIND_PACKAGE (Boost COMPONENTS program_options REQUIRED)
-PKG_CHECK_MODULES(SERVER_DEPS REQUIRED libvirt json)
+PKG_CHECK_MODULES(SERVER_DEPS REQUIRED libvirt json gio-2.0)
INCLUDE_DIRECTORIES(SYSTEM ${SERVER_DEPS_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS})
TARGET_LINK_LIBRARIES(${SERVER_CODENAME} ${SERVER_DEPS_LIBRARIES} ${Boost_LIBRARIES})
--- /dev/null
+/*
+ * Copyright (c) 2000 - 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Contact: Bumjin Im <bj.im@samsung.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+/**
+ * @file dbus-connection.cpp
+ * @author Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ * @brief Dbus connection class
+ */
+
+#include "dbus-connection.hpp"
+#include "dbus-exception.hpp"
+#include "scs-log.hpp"
+
+namespace {
+
+const std::string SYSTEM_BUS_ADDRESS = "unix:path=/var/run/dbus/system_bus_socket";
+const std::string INTROSPECT_INTERFACE = "org.freedesktop.DBus.Introspectable";
+const std::string INTROSPECT_METHOD = "Introspect";
+
+const int CALL_METHOD_TIMEOUT_MS = 1000;
+
+template<class Callback>
+void deleteCallback(gpointer data)
+{
+ delete reinterpret_cast<Callback*>(data);
+}
+
+class ScopedError {
+public:
+ ScopedError() : mError(NULL) {}
+ ~ScopedError()
+ {
+ if (mError) {
+ g_error_free(mError);
+ }
+ }
+ operator bool () const
+ {
+ return mError;
+ }
+ GError** operator& ()
+ {
+ return &mError;
+ }
+ const GError* operator->() const
+ {
+ return mError;
+ }
+ friend std::ostream& operator<<(std::ostream& os, const ScopedError& e)
+ {
+ os << e->message;
+ return os;
+ }
+private:
+ GError* mError;
+};
+
+class MethodResultBuilderImpl : public MethodResultBuilder {
+public:
+ MethodResultBuilderImpl(GDBusMethodInvocation* invocation)
+ : mInvocation(invocation), mResultSet(false) {}
+ void set(GVariant* parameters)
+ {
+ g_dbus_method_invocation_return_value(mInvocation, parameters);
+ mResultSet = true;
+ }
+ void setVoid()
+ {
+ set(NULL);
+ }
+ void setError(const std::string& name, const std::string& message)
+ {
+ g_dbus_method_invocation_return_dbus_error(mInvocation, name.c_str(), message.c_str());
+ mResultSet = true;
+ }
+ bool isUndefined() const
+ {
+ return !mResultSet;
+ }
+private:
+ GDBusMethodInvocation* mInvocation;
+ bool mResultSet;
+};
+
+} // namespace
+
+DbusConnection::Pointer DbusConnection::create(const std::string& address)
+{
+ return Pointer(new DbusConnection(address));
+}
+
+DbusConnection::Pointer DbusConnection::createSystem()
+{
+ return create(SYSTEM_BUS_ADDRESS);
+}
+
+DbusConnection::DbusConnection(const std::string& address)
+ : mConnection(NULL)
+ , mNameId(0)
+{
+ ScopedError error;
+ const GDBusConnectionFlags flags =
+ static_cast<GDBusConnectionFlags>(G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT |
+ G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION);
+ mConnection = g_dbus_connection_new_for_address_sync(address.c_str(),
+ flags,
+ NULL,
+ NULL,
+ &error);
+ if (error) {
+ LOGE("Could not create connection for address " << address << "; " << error);
+ throw DbusConnectException("Could not connect");
+ }
+}
+
+DbusConnection::~DbusConnection()
+{
+ if (mNameId) {
+ g_bus_unown_name(mNameId);
+ }
+ //TODO should we unregister, flush, close etc?
+ //if (!g_dbus_connection_close_sync(mConnection, NULL, NULL)) {
+ // LOGE("Could not close connection");
+ //}
+ g_object_unref(mConnection);
+ LOGT("Connection deleted");
+}
+
+void DbusConnection::setName(const std::string& name,
+ const VoidCallback& onNameAcquired,
+ const VoidCallback& onNameLost)
+{
+ mNameId = g_bus_own_name_on_connection(mConnection,
+ name.c_str(),
+ G_BUS_NAME_OWNER_FLAGS_NONE,
+ &DbusConnection::onNameAcquired,
+ &DbusConnection::onNameLost,
+ new NameCallbacks(onNameAcquired, onNameLost),
+ &deleteCallback<NameCallbacks>);
+}
+
+void DbusConnection::onNameAcquired(GDBusConnection*, const gchar* name, gpointer userData)
+{
+ LOGD("Name acquired " << name);
+ const NameCallbacks& callbacks = *reinterpret_cast<const NameCallbacks*>(userData);
+ if (callbacks.nameAcquired) {
+ callbacks.nameAcquired();
+ }
+}
+
+void DbusConnection::onNameLost(GDBusConnection*, const gchar* name, gpointer userData)
+{
+ LOGE("Name lost " << name);
+ const NameCallbacks& callbacks = *reinterpret_cast<const NameCallbacks*>(userData);
+ if (callbacks.nameLost) {
+ callbacks.nameLost();
+ }
+}
+
+void DbusConnection::emitSignal(const std::string& objectPath,
+ const std::string& interface,
+ const std::string& name,
+ GVariant* parameters)
+{
+ ScopedError error;
+ g_dbus_connection_emit_signal(mConnection,
+ NULL,
+ objectPath.c_str(),
+ interface.c_str(),
+ name.c_str(),
+ parameters,
+ &error);
+ if (error) {
+ LOGE("Emit signal failed; " << error);
+ throw DbusOperationException("could not emit signal");
+ }
+}
+
+void DbusConnection::signalSubscribe()
+{
+ g_dbus_connection_signal_subscribe(mConnection,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ G_DBUS_SIGNAL_FLAGS_NONE,
+ &DbusConnection::onSignal,
+ NULL,//data
+ NULL);
+}
+
+void DbusConnection::onSignal(GDBusConnection*,
+ const gchar* sender,
+ const gchar* object,
+ const gchar* interface,
+ const gchar* name,
+ GVariant* /*parameters*/,
+ gpointer /*userData*/)
+{
+ LOGD("Signal: " << sender << "; " << object << "; " << interface << "; " << name);
+ //TODO call some callback
+}
+
+std::string DbusConnection::introspect(const std::string& busName, const std::string& objectPath)
+{
+ GVariant* result = DbusConnection::callMethod(busName,
+ objectPath,
+ INTROSPECT_INTERFACE,
+ INTROSPECT_METHOD,
+ NULL,
+ G_VARIANT_TYPE("(s)"));
+ const gchar* s;
+ g_variant_get(result, "(&s)", &s);
+ std::string xml = s;
+ g_variant_unref(result);
+ return xml;
+}
+
+void DbusConnection::registerObject(const std::string& objectPath,
+ const std::string& objectDefinitionXml,
+ const MethodCallCallback& callback)
+{
+ ScopedError error;
+ GDBusNodeInfo* nodeInfo = g_dbus_node_info_new_for_xml(objectDefinitionXml.c_str(), &error);
+ if (error) {
+ LOGE("Invalid xml");
+ throw std::logic_error("invalid xml"); //TODO invalid argument exception
+ }
+ if (nodeInfo->interfaces == NULL ||
+ nodeInfo->interfaces[0] == NULL ||
+ nodeInfo->interfaces[1] != NULL) {
+ LOGE("Wrong number of interfaces");
+ g_dbus_node_info_unref(nodeInfo);
+ throw std::logic_error("Wrong number of interfaces"); //TODO invalid argument exception
+ }
+ GDBusInterfaceInfo* interfaceInfo = nodeInfo->interfaces[0];
+
+ GDBusInterfaceVTable vtable;
+ vtable.method_call = &DbusConnection::onMethodCall;
+ vtable.get_property = NULL;
+ vtable.set_property = NULL;
+
+ g_dbus_connection_register_object(mConnection,
+ objectPath.c_str(),
+ interfaceInfo,
+ &vtable,
+ new MethodCallCallback(callback),
+ &deleteCallback<MethodCallCallback>,
+ &error);
+ g_dbus_node_info_unref(nodeInfo);
+ if (error) {
+ LOGE("Register object failed; " << error);
+ throw DbusOperationException("register object failed");
+ }
+}
+
+void DbusConnection::onMethodCall(GDBusConnection*,
+ const gchar*,
+ const gchar* objectPath,
+ const gchar* interface,
+ const gchar* method,
+ GVariant* parameters,
+ GDBusMethodInvocation* invocation,
+ gpointer userData)
+{
+ const MethodCallCallback& callback = *static_cast<const MethodCallCallback*>(userData);
+
+ LOGD("MethodCall; " << objectPath << "; " << interface << "; " << method);
+
+ MethodResultBuilderImpl resultBuilder(invocation);
+ if (callback) {
+ callback(objectPath, interface, method, parameters, resultBuilder);
+ }
+
+ if (resultBuilder.isUndefined()) {
+ resultBuilder.setError("org.freedesktop.DBus.Error.UnknownMethod", "Not implemented");
+ }
+}
+
+GVariant* DbusConnection::callMethod(const std::string& busName,
+ const std::string& objectPath,
+ const std::string& interface,
+ const std::string& method,
+ GVariant* parameters,
+ const GVariantType* replyType)
+{
+ ScopedError error;
+ GVariant* result = g_dbus_connection_call_sync(mConnection,
+ busName.c_str(),
+ objectPath.c_str(),
+ interface.c_str(),
+ method.c_str(),
+ parameters,
+ replyType,
+ G_DBUS_CALL_FLAGS_NONE,
+ CALL_METHOD_TIMEOUT_MS,
+ NULL,
+ &error);
+ if (error) {
+ LOGE("Call method failed; " << error);
+ throw DbusOperationException("call method failed");//TODO split to different exceptions
+ }
+ return result;
+}
--- /dev/null
+/*
+ * Copyright (c) 2000 - 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Contact: Bumjin Im <bj.im@samsung.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+/**
+ * @file file-wait.cpp
+ * @author Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ * @brief Wait for file utility function
+ */
+
+#include "file-wait.hpp"
+#include "scs-log.hpp"
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdexcept>
+
+const unsigned int GRANULARITY = 10;
+
+void waitForFile(const std::string& filename, const unsigned int timeoutMs)
+{
+ //TODO this is a temporary solution, use inotify instead of sleep
+ struct stat s;
+ unsigned int loops = 0;
+ while (stat(filename.c_str(), &s) == -1) {
+ if (errno != ENOENT) {
+ throw std::runtime_error("file access error: " + filename);
+ }
+ ++ loops;
+ if (loops * GRANULARITY > timeoutMs) {
+ throw std::runtime_error("timeout");
+ }
+ usleep(GRANULARITY * 1000);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2000 - 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Contact: Bumjin Im <bj.im@samsung.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+/**
+ * @file glib-loop.cpp
+ * @author Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ * @brief C++ wrapper of glib main loop
+ */
+
+#include "glib-loop.hpp"
+#include <glib.h>
+
+ScopedGlibLoop::ScopedGlibLoop()
+ : mLoop(g_main_loop_new(NULL, FALSE), g_main_loop_unref)
+{
+ mLoopThread = std::thread([this] {g_main_loop_run(mLoop.get());});
+}
+
+ScopedGlibLoop::~ScopedGlibLoop()
+{
+ // ensure loop is running (avoid race condition when stop is called to early)
+ while (!g_main_loop_is_running(mLoop.get())) {
+ std::this_thread::yield();
+ }
+ //stop loop and wait
+ g_main_loop_quit(mLoop.get());
+ mLoopThread.join();
+}
--- /dev/null
+/*
+ * Copyright (c) 2000 - 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Contact: Bumjin Im <bj.im@samsung.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+/**
+ * @file latch.cpp
+ * @author Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ * @brief Synchronization latch
+ */
+
+#include "latch.hpp"
+
+Latch::Latch()
+ : mCount(0)
+{
+}
+
+void Latch::set()
+{
+ std::unique_lock<std::mutex> lock(mMutex);
+ ++mCount;
+ mCondition.notify_one();
+}
+
+void Latch::wait()
+{
+ std::unique_lock<std::mutex> lock(mMutex);
+ mCondition.wait(lock, [this] {return mCount > 0;});
+ --mCount;
+}
+
+bool Latch::wait(const unsigned int timeoutMs)
+{
+ std::unique_lock<std::mutex> lock(mMutex);
+ if (!mCondition.wait_for(lock, std::chrono::milliseconds(timeoutMs),
+ [this] {return mCount > 0;})) {
+ return false;
+ }
+ --mCount;
+ return true;
+}
+
+bool Latch::empty()
+{
+ std::unique_lock<std::mutex> lock(mMutex);
+ return mCount == 0;
+}
* @brief Main file for the Security Containers Daemon
*/
+#include "glib-loop.hpp"
+#include "latch.hpp"
+#include "scs-log.hpp"
#include <boost/program_options.hpp>
#include <iostream>
+#include <signal.h>
namespace po = boost::program_options;
namespace {
+
const std::string PROGRAM_NAME_AND_VERSION =
"Security Containers Server " PROGRAM_VERSION;
+
+Latch signalLatch;
+
+void signalHandler(int sig)
+{
+ LOGI("Got signal " << sig);
+ signalLatch.set();
+}
+
+void runDaemon()
+{
+ signal(SIGINT, signalHandler);
+ signal(SIGTERM, signalHandler);
+
+ LOGI("Starting daemon...");
+ {
+ ScopedGlibLoop loop;
+ //TODO bootstrap
+ LOGI("Daemon started");
+ signalLatch.wait();
+ LOGI("Stopping daemon...");
+ }
+ LOGI("Daemon stopped");
}
+} // namespace
+
int main(int argc, char* argv[])
{
po::options_description desc("Allowed options");
return 0;
}
+ runDaemon();
+
return 0;
}
FILE(GLOB src_SRCS ${SERVER_FOLDER}/src/scs-container-admin.cpp
${SERVER_FOLDER}/src/scs-container.cpp
${SERVER_FOLDER}/src/scs-container-manager.cpp
- ${SERVER_FOLDER}/src/scs-configuration.cpp)
+ ${SERVER_FOLDER}/src/scs-configuration.cpp
+ ${SERVER_FOLDER}/src/dbus-connection.cpp
+ ${SERVER_FOLDER}/src/glib-loop.cpp
+ ${SERVER_FOLDER}/src/file-wait.cpp
+ ${SERVER_FOLDER}/src/latch.cpp)
## Setup target ################################################################
SET(UT_SERVER_CODENAME "${PROJECT_NAME}-server-unit-tests")
## Link libraries ##############################################################
FIND_PACKAGE (Boost COMPONENTS unit_test_framework REQUIRED)
-PKG_CHECK_MODULES(UT_SERVER_DEPS REQUIRED libvirt json)
+PKG_CHECK_MODULES(UT_SERVER_DEPS REQUIRED libvirt json gio-2.0)
INCLUDE_DIRECTORIES(SYSTEM ${UT_SERVER_DEPS_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS})
TARGET_LINK_LIBRARIES(${UT_SERVER_CODENAME} ${UT_SERVER_DEPS_LIBRARIES} ${Boost_LIBRARIES})
FILE(GLOB manager_CONF ${SERVER_FOLDER}/unit_tests/config/ut-scs-container-manager/*.conf)
FILE(GLOB container_CONF ${SERVER_FOLDER}/unit_tests/config/ut-scs-container-manager/containers/*.conf)
FILE(GLOB containeradmin_CONF ${SERVER_FOLDER}/unit_tests/config/ut-scs-container-manager/libvirt-config/*.xml)
+FILE(GLOB dbus_CONF ${SERVER_FOLDER}/unit_tests/config/ut-dbus-connection/*.conf)
INSTALL(FILES ${manager_CONF}
DESTINATION ${SC_CONFIG_INSTALL_DIR}/tests/ut-scs-container-manager)
DESTINATION ${SC_CONFIG_INSTALL_DIR}/tests/ut-scs-container-manager/containers)
INSTALL(FILES ${containeradmin_CONF}
DESTINATION ${SC_CONFIG_INSTALL_DIR}/tests/ut-scs-container-manager/libvirt-config)
+INSTALL(FILES ${dbus_CONF}
+ DESTINATION ${SC_CONFIG_INSTALL_DIR}/tests/ut-dbus-connection)
--- /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/container_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>
--- /dev/null
+/*
+ * Copyright (c) 2000 - 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Contact: Bumjin Im <bj.im@samsung.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+/**
+ * @file dbus-client-test.cpp
+ * @author Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ * @brief Example dbus api client
+ */
+
+#include "dbus-client-test.hpp"
+#include "dbus-connection.hpp"
+#include "dbus-test-common.hpp"
+
+
+DbusClientTest::DbusClientTest()
+{
+ mConnection = DbusConnection::create(DBUS_ADDRESS);
+}
+
+void DbusClientTest::noop()
+{
+ GVariant* result = mConnection->callMethod(TESTAPI_BUS_NAME,
+ TESTAPI_OBJECT_PATH,
+ TESTAPI_INTERFACE,
+ TESTAPI_METHOD_NOOP,
+ NULL,
+ NULL);
+ g_variant_unref(result);
+}
+
+std::string DbusClientTest::process(const std::string& arg)
+{
+ GVariant* parameters = g_variant_new("(s)", arg.c_str());
+ GVariant* result = mConnection->callMethod(TESTAPI_BUS_NAME,
+ TESTAPI_OBJECT_PATH,
+ TESTAPI_INTERFACE,
+ TESTAPI_METHOD_PROCESS,
+ parameters,
+ G_VARIANT_TYPE("(s)"));
+ const gchar* cresult;
+ g_variant_get(result, "(&s)", &cresult);
+ std::string ret = cresult;
+ g_variant_unref(result);
+ return ret;
+}
+
+void DbusClientTest::throwException(int arg)
+{
+ GVariant* parameters = g_variant_new("(i)", arg);
+ GVariant* result = mConnection->callMethod(TESTAPI_BUS_NAME,
+ TESTAPI_OBJECT_PATH,
+ TESTAPI_INTERFACE,
+ TESTAPI_METHOD_THROW,
+ parameters,
+ NULL);
+ g_variant_unref(result);
+}
--- /dev/null
+/*
+ * Copyright (c) 2000 - 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Contact: Bumjin Im <bj.im@samsung.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+/**
+ * @file dbus-client-test.hpp
+ * @author Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ * @brief Example dbus api client
+ */
+
+#ifndef DBUS_CLIENT_TEST_HPP
+#define DBUS_CLIENT_TEST_HPP
+
+#include <string>
+#include <memory>
+
+class DbusConnection;
+typedef std::shared_ptr<DbusConnection> DbusConnectionPtr;//TODO include dbus-connection-iface.h
+
+/**
+ * Simple dbus client for test purposes.
+ * Class used to test all possible kinds of dbus calls.
+ */
+class DbusClientTest {
+public:
+ DbusClientTest();
+
+ // interface methods
+ void noop();
+ std::string process(const std::string& arg);
+ void throwException(int arg);
+
+private:
+ DbusConnectionPtr mConnection;
+};
+
+#endif //DBUS_CLIENT_TEST_HPP
--- /dev/null
+/*
+ * Copyright (c) 2000 - 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Contact: Bumjin Im <bj.im@samsung.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+/**
+ * @file dbus-server-test.cpp
+ * @author Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ * @brief Example dbus api server
+ */
+
+#include "dbus-server-test.hpp"
+#include "dbus-connection.hpp"
+#include "dbus-exception.hpp"
+#include "dbus-test-common.hpp"
+#include "scs-log.hpp"
+
+
+DbusServerTest::DbusServerTest()
+ : mNameAcquired(false)
+ , mPendingDisconnect(false)
+{
+ mConnection = DbusConnection::create(DBUS_ADDRESS);
+ mConnection->setName(TESTAPI_BUS_NAME,
+ std::bind(&DbusServerTest::onNameAcquired, this),
+ std::bind(&DbusServerTest::onDisconnect, this));
+ if (!waitForName()) {
+ mConnection.reset();
+ throw DbusConnectException("Could not acquire name");
+ }
+ using namespace std::placeholders;
+ mConnection->registerObject(TESTAPI_OBJECT_PATH, TESTAPI_DEFINITION,
+ std::bind(&DbusServerTest::onMessageCall, this, _1, _2, _3, _4, _5));
+}
+
+bool DbusServerTest::waitForName()
+{
+ std::unique_lock<std::mutex> lock(mMutex);
+ mNameCondition.wait(lock, [this] {return mNameAcquired || mPendingDisconnect;});
+ return mNameAcquired;
+}
+
+void DbusServerTest::setDisconnectCallback(const DisconnectCallback& callback)
+{
+ std::unique_lock<std::mutex> lock(mMutex);
+ mDisconnectCallback = callback;
+ if (mPendingDisconnect) {
+ mPendingDisconnect = false;
+ mDisconnectCallback();
+ }
+
+}
+
+void DbusServerTest::onNameAcquired()
+{
+ std::unique_lock<std::mutex> lock(mMutex);
+ mNameAcquired = true;
+ mNameCondition.notify_one();
+}
+
+void DbusServerTest::onDisconnect()
+{
+ std::unique_lock<std::mutex> lock(mMutex);
+ if (mDisconnectCallback) {
+ mDisconnectCallback();
+ } else {
+ mPendingDisconnect = true;
+ mNameCondition.notify_one();
+ }
+}
+
+void DbusServerTest::noop()
+{
+}
+
+std::string DbusServerTest::process(const std::string& arg)
+{
+ return "Processed: " + arg;
+}
+
+void DbusServerTest::throwException(int arg)
+{
+ if (arg != 0) {
+ throw std::runtime_error("Argument: " + std::to_string(arg));
+ }
+}
+
+void DbusServerTest::onMessageCall(
+ const std::string& objectPath,
+ const std::string& interface,
+ const std::string& method,
+ GVariant* parameters,
+ MethodResultBuilder& result)
+{
+ try {
+ if (objectPath != TESTAPI_OBJECT_PATH || interface != TESTAPI_INTERFACE) {
+ throw std::logic_error("unsupported interface");
+ }
+
+ if (method == TESTAPI_METHOD_NOOP) {
+ noop();
+ result.setVoid();
+ } else if (method == TESTAPI_METHOD_PROCESS) {
+ const gchar* arg;
+ g_variant_get(parameters, "(&s)", &arg);
+ std::string ret = process(arg);
+ GVariant* variant = g_variant_new("(s)", ret.c_str());
+ result.set(variant);
+ } else if (method == TESTAPI_METHOD_THROW) {
+ int arg;
+ g_variant_get(parameters, "(i)", &arg);
+ throwException(arg);
+ result.setVoid();
+ } else {
+ LOGE("unknown method; should never happen");
+ }
+ } catch (const std::exception& e) {
+ result.setError("com.samsung.Exception", e.what());
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2000 - 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Contact: Bumjin Im <bj.im@samsung.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+/**
+ * @file dbus-server-test.hpp
+ * @author Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ * @brief Example dbus api server
+ */
+
+#ifndef DBUS_SERVER_TEST_HPP
+#define DBUS_SERVER_TEST_HPP
+
+#include <string>
+#include <memory>
+#include <mutex>
+#include <condition_variable>
+#include <gio/gio.h>//TODO how to hide this?
+
+class DbusConnection;
+typedef std::shared_ptr<DbusConnection> DbusConnectionPtr;//TODO include dbus-connection-iface.h
+class MethodResultBuilder;
+
+/**
+ * Simple dbus server for test purposes.
+ * Class used to test all possible kinds of callbacks.
+ */
+class DbusServerTest {
+public:
+ DbusServerTest();
+
+ typedef std::function<void()> DisconnectCallback;
+ void setDisconnectCallback(const DisconnectCallback& callback);
+
+private:
+ //{ interface methods
+ void noop();
+ std::string process(const std::string& arg);
+ void throwException(int arg);
+ //}
+
+ DbusConnectionPtr mConnection;
+ DisconnectCallback mDisconnectCallback;
+ bool mNameAcquired;
+ bool mPendingDisconnect;
+ std::mutex mMutex;
+ std::condition_variable mNameCondition;
+
+
+ bool waitForName();
+
+ void onNameAcquired();
+ void onDisconnect();
+
+ void onMessageCall(
+ const std::string& objectPath,
+ const std::string& interface,
+ const std::string& method,
+ GVariant* parameters,
+ MethodResultBuilder& result);
+};
+
+#endif //DBUS_SERVER_TEST_HPP
--- /dev/null
+/*
+ * Copyright (c) 2000 - 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Contact: Bumjin Im <bj.im@samsung.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+/**
+ * @file dbus-test-common.hpp
+ * @author Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ * @brief Common definitions for dbus tests
+ */
+
+#ifndef DBUS_TEST_COMMON_HPP
+#define DBUS_TEST_COMMON_HPP
+
+#include <string>
+
+const std::string DBUS_SOCKET_FILE = "/tmp/container_socket";
+const std::string DBUS_ADDRESS = "unix:path=" + DBUS_SOCKET_FILE;
+
+const std::string TESTAPI_BUS_NAME = "com.samsung.tests";
+const std::string TESTAPI_OBJECT_PATH = "/com/samsung/tests";
+const std::string TESTAPI_INTERFACE = "tests.api";
+const std::string TESTAPI_METHOD_NOOP = "Noop";
+const std::string TESTAPI_METHOD_PROCESS = "Process";
+const std::string TESTAPI_METHOD_THROW = "Throw";
+
+const std::string TESTAPI_DEFINITION =
+ "<node>"
+ " <interface name='" + TESTAPI_INTERFACE + "'>"
+ " <method name='" + TESTAPI_METHOD_NOOP + "'/>"
+ " <method name='" + TESTAPI_METHOD_PROCESS + "'>"
+ " <arg type='s' name='argument' direction='in'/>"
+ " <arg type='s' name='response' direction='out'/>"
+ " </method>"
+ " <method name='" + TESTAPI_METHOD_THROW + "'>"
+ " <arg type='i' name='argument' direction='in'/>"
+ " </method>"
+ " </interface>"
+ "</node>";
+
+#endif //DBUS_TEST_COMMON_HPP
--- /dev/null
+/*
+ * Copyright (c) 2000 - 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Contact: Bumjin Im <bj.im@samsung.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+/**
+ * @file scoped-daemon.cpp
+ * @author Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ * @brief Starts external daemon in constructor, stops it in destructor
+ */
+
+#include "scoped-daemon.hpp"
+#include "scs-log.hpp"
+#include <unistd.h>
+#include <sys/wait.h>
+#include <sys/prctl.h>
+#include <stdexcept>
+
+/*
+ * Scoped Daemon - sequence diagram.
+ *
+ *
+ * |(main process)
+ * |
+ * constructor |
+ * ------------>|_______
+ * | |(launcher process)
+ * | |_______
+ * | | |(daemon process)
+ * | | |
+ * | | |
+ * destructor | | |
+ * ------------>| sig | |
+ * |------>| sig |
+ * | |------>|
+ * | |_______|
+ * |_______|
+ * destructor |
+ * ends |
+ *
+ *
+ * Launcher helper process is used to monitor main process.
+ * When e.g. it crashes or hits an assert then launcher kills daemon and itself.
+ */
+
+namespace {
+
+volatile pid_t daemonPid = -1;// available in launcher process only;
+
+void startDaemon(const char* path, const char* const argv[])
+{
+ execv(path, const_cast<char* const*>(argv));
+ perror("exec failed");
+}
+
+void waitForDaemon()
+{
+ if (waitpid(daemonPid, NULL, 0) == -1) {
+ perror("wait for daemon failed");
+ }
+}
+
+void launcherSignalHandler(int sig)
+{
+ // forward to daemon
+ if (kill(daemonPid, sig) == -1) {
+ perror("kill daemon failed");
+ }
+}
+
+void registerLauncherSignalHandler()
+{
+ signal(SIGTERM, launcherSignalHandler);
+}
+
+void registerParentDiedNotification()
+{
+ prctl(PR_SET_PDEATHSIG, SIGTERM);
+}
+
+void cleanupProcess()
+{
+ signal(SIGCHLD, SIG_DFL);
+ signal(SIGINT, SIG_DFL);
+ signal(SIGTERM, SIG_DFL);
+ signal(SIGHUP, SIG_DFL);
+}
+
+void startByLauncher(const char* path, const char* const argv[])
+{
+ cleanupProcess();
+ daemonPid = fork();
+ if (daemonPid == -1) {
+ perror("fork failed");
+ return;
+ }
+ if (daemonPid == 0) {
+ startDaemon(path, argv);
+ _exit(1);
+ }
+ registerLauncherSignalHandler();
+ registerParentDiedNotification();
+ waitForDaemon();
+}
+
+} // namespace
+
+ScopedDaemon::ScopedDaemon(const char* path, const char* const argv[], const bool useLauncher)
+{
+ mPid = fork();
+ if (mPid == -1) {
+ throw std::runtime_error("fork failed");
+ }
+ if (mPid == 0) {
+ if (useLauncher) {
+ startByLauncher(path, argv);
+ } else {
+ startDaemon(path, argv);
+ }
+ _exit(0);
+ }
+}
+
+ScopedDaemon::~ScopedDaemon()
+{
+ stop();
+}
+
+void ScopedDaemon::stop()
+{
+ if (mPid == -1) {
+ return;
+ }
+ if (kill(mPid, SIGTERM) == -1) {
+ LOGE("kill failed");
+ }
+ if (waitpid(mPid, NULL, 0) == -1) {
+ LOGE("waitpid failed");
+ }
+ mPid = -1;
+}
--- /dev/null
+/*
+ * Copyright (c) 2000 - 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Contact: Bumjin Im <bj.im@samsung.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+/**
+ * @file scoped-daemon.hpp
+ * @author Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ * @brief Starts external daemon in constructor, stops it in destructor
+ */
+
+#ifndef SCOPED_DAEMON_HPP
+#define SCOPED_DAEMON_HPP
+
+#include <sys/types.h>
+
+/**
+ * External daemon launcher helper.
+ */
+class ScopedDaemon {
+public:
+ /**
+ * Starts a daemon.
+ * @param path daemon path
+ * @param argv arguments passed to the daemon
+ * @param useLauncher use additional launcher process
+ */
+ ScopedDaemon(const char* path, const char* const argv[], const bool useLauncher = true);
+
+ /**
+ * Stops a daemon if it is not stopped already.
+ */
+ ~ScopedDaemon();
+
+ /**
+ * Stops a daemon by sending SIGTERM and waits for a process.
+ */
+ void stop();
+private:
+ pid_t mPid;
+};
+
+#endif //SCOPED_DAEMON_HPP
--- /dev/null
+/*
+ * Copyright (c) 2000 - 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Contact: Bumjin Im <bj.im@samsung.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+/**
+ * @file ut-dbus-connection.cpp
+ * @author Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ * @brief Dbus connection unit tests
+ */
+
+#include "ut.hpp"
+#include "dbus-connection.hpp"
+#include "dbus-exception.hpp"
+#include "glib-loop.hpp"
+#include "scoped-daemon.hpp"
+#include "file-wait.hpp"
+#include "latch.hpp"
+#include "dbus-server-test.hpp"
+#include "dbus-client-test.hpp"
+#include "dbus-test-common.hpp"
+#include "scs-log.hpp"
+#include <thread>
+#include <mutex>
+#include <condition_variable>
+
+
+BOOST_AUTO_TEST_SUITE(DbusSuite)
+
+namespace {
+
+const char* DBUS_DAEMON_PROC = "/bin/dbus-daemon";
+const char* const DBUS_DAEMON_ARGS[] = {
+ DBUS_DAEMON_PROC,
+ "--config-file=/etc/security-containers/config/tests/ut-dbus-connection/ut-dbus.conf",
+ "--nofork",
+ NULL
+};
+const int DBUS_DAEMON_TIMEOUT = 1000;
+const int EVENT_TIMEOUT = 1000;
+
+class ScopedDbusDaemon : public ScopedDaemon {
+public:
+ ScopedDbusDaemon() : ScopedDaemon(DBUS_DAEMON_PROC, DBUS_DAEMON_ARGS)
+ {
+ waitForFile(DBUS_SOCKET_FILE, DBUS_DAEMON_TIMEOUT);
+ }
+};
+
+std::string getInterfaceFromIntrospectionXML(const std::string& xml, const std::string& name)
+{
+ std::string ret;
+ GDBusNodeInfo* nodeInfo = g_dbus_node_info_new_for_xml(xml.c_str(), NULL);
+ GDBusInterfaceInfo* iface = g_dbus_node_info_lookup_interface(nodeInfo, name.c_str());
+ if (iface) {
+ GString* gret = g_string_new("");
+ g_dbus_interface_info_generate_xml(iface, 0, gret);
+ ret.assign(gret->str, gret->len);
+ g_string_free(gret, TRUE);
+ }
+ g_dbus_node_info_unref(nodeInfo);
+ return ret;
+}
+
+} // namespace
+
+BOOST_AUTO_TEST_CASE(GlibLoopTest)
+{
+ ScopedGlibLoop loop;
+}
+
+BOOST_AUTO_TEST_CASE(DbusDaemonTest)
+{
+ ScopedDbusDaemon daemon;
+}
+
+BOOST_AUTO_TEST_CASE(NoDbusTest)
+{
+ ScopedGlibLoop loop;
+ BOOST_CHECK_THROW(DbusConnection::create(DBUS_ADDRESS), DbusConnectException);
+}
+
+BOOST_AUTO_TEST_CASE(SimpleTest)
+{
+ ScopedDbusDaemon daemon;
+ ScopedGlibLoop loop;
+ Latch nameAcquired, nameLost;
+ DbusConnection::Pointer conn1 = DbusConnection::create(DBUS_ADDRESS);
+ DbusConnection::Pointer conn2 = DbusConnection::create(DBUS_ADDRESS);
+ conn1->setName(TESTAPI_BUS_NAME,
+ [&] {nameAcquired.set();},
+ [&] {nameLost.set();});
+ DbusConnection::Pointer connSystem = DbusConnection::createSystem();
+ BOOST_CHECK(nameAcquired.wait(EVENT_TIMEOUT));
+ BOOST_CHECK(nameLost.empty());
+}
+
+BOOST_AUTO_TEST_CASE(ConnectionLostTest)
+{
+ ScopedDbusDaemon daemon;
+ ScopedGlibLoop loop;
+ Latch nameAcquired, nameLost;
+ DbusConnection::Pointer conn1 = DbusConnection::create(DBUS_ADDRESS);
+ conn1->setName(TESTAPI_BUS_NAME,
+ [&] {nameAcquired.set();},
+ [&] {nameLost.set();});
+ BOOST_CHECK(nameAcquired.wait(EVENT_TIMEOUT));
+ BOOST_CHECK(nameLost.empty());
+
+ // close dbus socket
+ daemon.stop();
+ BOOST_CHECK(nameLost.wait(EVENT_TIMEOUT));
+}
+
+BOOST_AUTO_TEST_CASE(NameOwnerTest)
+{
+ ScopedDbusDaemon daemon;
+ ScopedGlibLoop loop;
+
+ DbusConnection::Pointer conn1 = DbusConnection::create(DBUS_ADDRESS);
+ DbusConnection::Pointer conn2 = DbusConnection::create(DBUS_ADDRESS);
+
+ // acquire name by conn1
+ Latch nameAcquired1, nameLost1;
+ conn1->setName(TESTAPI_BUS_NAME,
+ [&] {nameAcquired1.set();},
+ [&] {nameLost1.set();});
+ BOOST_CHECK(nameAcquired1.wait(EVENT_TIMEOUT));
+ BOOST_CHECK(nameLost1.empty());
+
+ // conn2 can't acquire name
+ Latch nameAcquired2, nameLost2;
+ conn2->setName(TESTAPI_BUS_NAME,
+ [&] {nameAcquired2.set();},
+ [&] {nameLost2.set();});
+ BOOST_CHECK(nameLost2.wait(EVENT_TIMEOUT));
+ BOOST_CHECK(nameAcquired2.empty());
+
+ // close conn1
+ conn1.reset();
+ // depending on dbus implementation conn2 can automatically acquire the name
+ //BOOST_CHECK(nameAcquired2.wait(EVENT_TIMEOUT));
+}
+
+BOOST_AUTO_TEST_CASE(SignalTest)
+{
+ ScopedDbusDaemon daemon;
+ ScopedGlibLoop loop;
+ DbusConnection::Pointer conn1 = DbusConnection::create(DBUS_ADDRESS);
+ DbusConnection::Pointer conn2 = DbusConnection::create(DBUS_ADDRESS);
+ conn2->signalSubscribe();
+ conn1->emitSignal("/a/b/c", "a.b.c", "Foo", NULL);
+ sleep(1);
+}
+
+BOOST_AUTO_TEST_CASE(IntrospectSystemTest)
+{
+ ScopedDbusDaemon daemon;
+ ScopedGlibLoop loop;
+ DbusConnection::Pointer conn = DbusConnection::createSystem();
+ std::string xml = conn->introspect("org.freedesktop.DBus", "/org/freedesktop/DBus");
+ std::string iface = getInterfaceFromIntrospectionXML(xml, "org.freedesktop.DBus");
+ BOOST_CHECK(!iface.empty());
+}
+
+BOOST_AUTO_TEST_CASE(IntrospectTest)
+{
+ ScopedDbusDaemon daemon;
+ ScopedGlibLoop loop;
+ DbusConnection::Pointer conn1 = DbusConnection::create(DBUS_ADDRESS);
+ DbusConnection::Pointer conn2 = DbusConnection::create(DBUS_ADDRESS);
+
+ Latch nameAcquired;
+ conn1->setName(TESTAPI_BUS_NAME,
+ [&] {nameAcquired.set();},
+ [] {});
+ BOOST_REQUIRE(nameAcquired.wait(EVENT_TIMEOUT));
+ conn1->registerObject(TESTAPI_OBJECT_PATH, TESTAPI_DEFINITION,
+ DbusConnection::MethodCallCallback());
+ std::string xml = conn2->introspect(TESTAPI_BUS_NAME, TESTAPI_OBJECT_PATH);
+ std::string iface = getInterfaceFromIntrospectionXML(xml, TESTAPI_INTERFACE);
+ BOOST_REQUIRE(!iface.empty());
+ BOOST_CHECK(std::string::npos != iface.find(TESTAPI_INTERFACE));
+ BOOST_CHECK(std::string::npos != iface.find(TESTAPI_METHOD_NOOP));
+ BOOST_CHECK(std::string::npos != iface.find(TESTAPI_METHOD_PROCESS));
+ BOOST_CHECK(std::string::npos != iface.find(TESTAPI_METHOD_THROW));
+}
+
+BOOST_AUTO_TEST_CASE(MethodCallTest)
+{
+ ScopedDbusDaemon daemon;
+ ScopedGlibLoop loop;
+ DbusConnection::Pointer conn1 = DbusConnection::create(DBUS_ADDRESS);
+ DbusConnection::Pointer conn2 = DbusConnection::create(DBUS_ADDRESS);
+
+ Latch nameAcquired;
+ conn1->setName(TESTAPI_BUS_NAME,
+ [&] {nameAcquired.set();},
+ [] {});
+ BOOST_REQUIRE(nameAcquired.wait(EVENT_TIMEOUT));
+ auto handler = [] (const std::string&, const std::string&, const std::string& method,
+ GVariant* /*parameters*/, MethodResultBuilder& result) {
+ if (method == TESTAPI_METHOD_NOOP) {
+ result.setVoid();
+ }
+ };
+ conn1->registerObject(TESTAPI_OBJECT_PATH, TESTAPI_DEFINITION, handler);
+ BOOST_CHECK(conn2->callMethod(TESTAPI_BUS_NAME, TESTAPI_OBJECT_PATH,
+ TESTAPI_INTERFACE, TESTAPI_METHOD_NOOP, NULL, NULL));
+}
+
+BOOST_AUTO_TEST_CASE(MethodCallExceptionTest)
+{
+ ScopedDbusDaemon daemon;
+ ScopedGlibLoop loop;
+ DbusConnection::Pointer conn1 = DbusConnection::create(DBUS_ADDRESS);
+ DbusConnection::Pointer conn2 = DbusConnection::create(DBUS_ADDRESS);
+
+ Latch nameAcquired;
+ conn1->setName(TESTAPI_BUS_NAME,
+ [&] {nameAcquired.set();},
+ [] {});
+ BOOST_REQUIRE(nameAcquired.wait(EVENT_TIMEOUT));
+ conn1->registerObject(TESTAPI_OBJECT_PATH, TESTAPI_DEFINITION,
+ DbusConnection::MethodCallCallback());
+ BOOST_CHECK_THROW(conn2->callMethod(TESTAPI_BUS_NAME, TESTAPI_OBJECT_PATH,
+ TESTAPI_INTERFACE, TESTAPI_METHOD_NOOP, NULL, NULL),
+ DbusOperationException);
+ BOOST_CHECK_THROW(conn2->callMethod(TESTAPI_BUS_NAME, TESTAPI_OBJECT_PATH,
+ TESTAPI_INTERFACE, "Foo", NULL, NULL),
+ DbusOperationException);
+ BOOST_CHECK_THROW(conn2->callMethod(TESTAPI_BUS_NAME, TESTAPI_OBJECT_PATH,
+ TESTAPI_INTERFACE + ".foo", TESTAPI_METHOD_NOOP, NULL, NULL),
+ DbusOperationException);
+ BOOST_CHECK_THROW(conn2->callMethod(TESTAPI_BUS_NAME, TESTAPI_OBJECT_PATH + "/foo",
+ TESTAPI_INTERFACE, TESTAPI_METHOD_NOOP, NULL, NULL),
+ DbusOperationException);
+}
+
+BOOST_AUTO_TEST_CASE(DbusApiTest)
+{
+ ScopedDbusDaemon daemon;
+ ScopedGlibLoop loop;
+ DbusServerTest server;
+ DbusClientTest client;
+
+ BOOST_CHECK_NO_THROW(client.noop());
+ BOOST_CHECK_EQUAL("Processed: arg", client.process("arg"));
+ BOOST_CHECK_NO_THROW(client.throwException(0));
+ BOOST_CHECK_THROW(client.throwException(666), std::exception);
+}
+
+BOOST_AUTO_TEST_CASE(DbusApiNameAcquiredTest)
+{
+ ScopedDbusDaemon daemon;
+ ScopedGlibLoop loop;
+ DbusServerTest server;
+ DbusClientTest client;
+
+ BOOST_CHECK_THROW(DbusServerTest(), DbusConnectException);
+ BOOST_CHECK_NO_THROW(client.noop());
+}
+
+BOOST_AUTO_TEST_CASE(DbusApiConnectionLost1Test)
+{
+ ScopedDbusDaemon daemon;
+ ScopedGlibLoop loop;
+ Latch disconnected;
+
+ DbusServerTest server;
+ server.setDisconnectCallback([&] {disconnected.set();});
+ DbusClientTest client;
+
+ BOOST_CHECK_NO_THROW(client.noop());
+ daemon.stop();
+ BOOST_CHECK(disconnected.wait(EVENT_TIMEOUT));
+ BOOST_CHECK_THROW(client.noop(), DbusOperationException);
+}
+
+BOOST_AUTO_TEST_CASE(DbusApiConnectionLost2Test)
+{
+ ScopedDbusDaemon daemon;
+ ScopedGlibLoop loop;
+ DbusServerTest server;
+ DbusClientTest client;
+
+ BOOST_CHECK_NO_THROW(client.noop());
+ daemon.stop();
+ BOOST_CHECK_THROW(client.noop(), DbusOperationException);
+
+ Latch disconnected;
+ server.setDisconnectCallback([&] {disconnected.set();});
+ BOOST_CHECK(disconnected.wait(EVENT_TIMEOUT));
+}
+
+BOOST_AUTO_TEST_SUITE_END()