From d8fedce7b248e5d57038882889354c617a08ab85 Mon Sep 17 00:00:00 2001 From: Piotr Bartosiewicz Date: Tue, 9 Dec 2014 17:08:00 +0100 Subject: [PATCH 01/16] Fix doxy comments and some other warnings [Bug/Feature] N/A [Cause] N/A [Solution] N/A [Verification] Build Change-Id: I5cff5b198d533ec256d551d9e8280a5c117e06d4 --- CMakeLists.txt | 1 - cli/main.cpp | 4 ++++ client/utils.cpp | 4 ++-- client/vasum-client-impl.cpp | 2 +- client/vasum-client.h | 4 ++-- common/ipc/client.hpp | 6 +++--- common/ipc/internals/event-queue.hpp | 8 ++++---- common/ipc/internals/processor.cpp | 2 -- common/ipc/service.hpp | 4 ++-- common/utils/initctl.cpp | 2 +- server/zones-manager.cpp | 6 +++--- tests/unit_tests/client/ut-client.cpp | 8 ++++---- tests/unit_tests/server/ut-zones-manager.cpp | 2 +- 13 files changed, 27 insertions(+), 26 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 34573cc..cd999d7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -77,7 +77,6 @@ ADD_DEFINITIONS(-DPROJECT_SOURCE_DIR="${PROJECT_SOURCE_DIR}") IF("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") # Warn about documentation problems ADD_DEFINITIONS("-Wdocumentation") - ADD_DEFINITIONS("-Wno-error=documentation") IF(ALL_WARNINGS) # turn on every -W flags except a few explicitly mentioned diff --git a/cli/main.cpp b/cli/main.cpp index e837d86..448d32d 100644 --- a/cli/main.cpp +++ b/cli/main.cpp @@ -32,6 +32,8 @@ using namespace vasum::cli; +namespace { + std::map commands = { { "set_active_zone", { @@ -93,6 +95,8 @@ void printUsage(std::ostream& out, const std::string& name) } } +} // namespace + int main(const int argc, const char** argv) { if (argc < 2) { diff --git a/client/utils.cpp b/client/utils.cpp index a5ff48e..305b848 100644 --- a/client/utils.cpp +++ b/client/utils.cpp @@ -87,8 +87,8 @@ void unescape(std::string& value) if (c == '-') { value[outPos++] = '/'; } else if (c == '\\' && value[inPos] == 'x') { - const char a = unhex(value[inPos+1]); - const char b = unhex(value[inPos+2]); + const int a = unhex(value[inPos+1]); + const int b = unhex(value[inPos+2]); value[outPos++] = (char) ((a << 4) | b); inPos += 3; } else { diff --git a/client/vasum-client-impl.cpp b/client/vasum-client-impl.cpp index c16c9d2..41898c7 100644 --- a/client/vasum-client-impl.cpp +++ b/client/vasum-client-impl.cpp @@ -113,7 +113,7 @@ VsmZoneState getZoneState(const char* state) } else if (strcmp(state, "ACTIVATING") == 0) { return ACTIVATING; } - assert(!"UNKNOWN STATE"); + assert(0 && "UNKNOWN STATE"); return (VsmZoneState)-1; } diff --git a/client/vasum-client.h b/client/vasum-client.h index 3e1f66d..eb4cbba 100644 --- a/client/vasum-client.h +++ b/client/vasum-client.h @@ -390,7 +390,7 @@ VsmStatus vsm_create_zone(VsmClient client, const char* id, const char* tname); * @param[in] force if 0 data will be kept, otherwise data will be lost * @return status of this function call */ -VsmStatus vsm_destroy_zone(VsmClient clent, const char* id, int force); +VsmStatus vsm_destroy_zone(VsmClient client, const char* id, int force); /** * Shutdown zone @@ -624,7 +624,7 @@ VsmStatus vsm_declare_file(VsmClient client, * @param[in] target mount point (path in zone) * @param[in] type filesystem type * @param[in] flags mount flags as in mount function - * @patam[in] data additional data as in mount function + * @param[in] data additional data as in mount function * @return status of this function call */ VsmStatus vsm_declare_mount(VsmClient client, diff --git a/common/ipc/client.hpp b/common/ipc/client.hpp index 5233e29..5751812 100644 --- a/common/ipc/client.hpp +++ b/common/ipc/client.hpp @@ -89,7 +89,7 @@ public: * the data will be parsed and passed to this callback. * * @param methodID API dependent id of the method - * @param methodCallback method handling implementation + * @param method method handling implementation */ template void addMethodHandler(const MethodID methodID, @@ -101,7 +101,7 @@ public: * the data will be parsed and passed to this callback. * * @param methodID API dependent id of the method - * @param SignalHandler signal handling implementation + * @param signal signal handling implementation * @tparam ReceivedDataType data type to serialize */ template @@ -134,7 +134,7 @@ public: * * * @param methodID API dependent id of the method - * @param sendCallback callback for data serialization + * @param data data to send * @param resultCallback callback for result serialization and handling */ template diff --git a/common/ipc/internals/event-queue.hpp b/common/ipc/internals/event-queue.hpp index 74a4923..2c591f7 100644 --- a/common/ipc/internals/event-queue.hpp +++ b/common/ipc/internals/event-queue.hpp @@ -57,14 +57,14 @@ public: int getFD() const; /** - * Send an event of a given value + * Send an event * - * @param value size of the buffer + * @param message mesage to send */ - void send(const MessageType& mess); + void send(const MessageType& message); /** - * Receives the signal. + * Receives the event. * Blocks if there is no event. * * @return event's value diff --git a/common/ipc/internals/processor.cpp b/common/ipc/internals/processor.cpp index aa21675..be1060d 100644 --- a/common/ipc/internals/processor.cpp +++ b/common/ipc/internals/processor.cpp @@ -340,8 +340,6 @@ bool Processor::handleInput(const Socket& socket) } } } - - return false; } std::shared_ptr Processor::onNewSignals(const FileDescriptor peerFD, diff --git a/common/ipc/service.hpp b/common/ipc/service.hpp index 5038398..1f9aee3 100644 --- a/common/ipc/service.hpp +++ b/common/ipc/service.hpp @@ -94,7 +94,7 @@ public: * the data will be parsed and passed to this callback. * * @param methodID API dependent id of the method - * @param methodCallback method handling implementation + * @param method method handling implementation */ template void addMethodHandler(const MethodID methodID, @@ -139,7 +139,7 @@ public: * * * @param methodID API dependent id of the method - * @param sendCallback callback for data serialization + * @param data data to send * @param resultCallback callback for result serialization and handling */ template diff --git a/common/utils/initctl.cpp b/common/utils/initctl.cpp index f4a778b..5986799 100644 --- a/common/utils/initctl.cpp +++ b/common/utils/initctl.cpp @@ -54,7 +54,7 @@ namespace { } return false; } - size -= r; + size -= static_cast(r); data = reinterpret_cast(data) + r; } return true; diff --git a/server/zones-manager.cpp b/server/zones-manager.cpp index 6744c30..981556b 100644 --- a/server/zones-manager.cpp +++ b/server/zones-manager.cpp @@ -593,7 +593,7 @@ void ZonesManager::handleDeclareFileCall(const std::string& zone, try { mZones.at(zone)->declareFile(type, path, flags, mode); result->setVoid(); - } catch (const std::out_of_range& ex) { + } catch (const std::out_of_range&) { LOGE("No zone with id=" << zone); result->setError(api::ERROR_INVALID_ID, "No such zone id"); } catch (const config::ConfigException& ex) { @@ -614,7 +614,7 @@ void ZonesManager::handleDeclareMountCall(const std::string& source, try { mZones.at(zone)->declareMount(source, target, type, flags, data); result->setVoid(); - } catch (const std::out_of_range& ex) { + } catch (const std::out_of_range&) { LOGE("No zone with id=" << zone); result->setError(api::ERROR_INVALID_ID, "No such zone id"); } catch (const config::ConfigException& ex) { @@ -632,7 +632,7 @@ void ZonesManager::handleDeclareLinkCall(const std::string& source, try { mZones.at(zone)->declareLink(source, target); result->setVoid(); - } catch (const std::out_of_range& ex) { + } catch (const std::out_of_range&) { LOGE("No zone with id=" << zone); result->setError(api::ERROR_INVALID_ID, "No such zone id"); } catch (const config::ConfigException& ex) { diff --git a/tests/unit_tests/client/ut-client.cpp b/tests/unit_tests/client/ut-client.cpp index e8060be..1e23e81 100644 --- a/tests/unit_tests/client/ut-client.cpp +++ b/tests/unit_tests/client/ut-client.cpp @@ -145,9 +145,9 @@ BOOST_AUTO_TEST_CASE(GetZoneDbusesTest) status = vsm_get_zone_dbuses(client, &keys, &values); BOOST_REQUIRE_EQUAL(VSMCLIENT_SUCCESS, status); - BOOST_CHECK_EQUAL(getArrayStringLength(keys, EXPECTED_DBUSES_STARTED.size() + 1), + BOOST_CHECK_EQUAL(getArrayStringLength(keys, EXPECTED_DBUSES_STARTED.size() + 1u), EXPECTED_DBUSES_STARTED.size()); - BOOST_CHECK_EQUAL(getArrayStringLength(values, EXPECTED_DBUSES_STARTED.size() + 1), + BOOST_CHECK_EQUAL(getArrayStringLength(values, EXPECTED_DBUSES_STARTED.size() + 1u), EXPECTED_DBUSES_STARTED.size()); std::map zones; @@ -166,7 +166,7 @@ BOOST_AUTO_TEST_CASE(GetZoneIdsTest) VsmArrayString values; status = vsm_get_zone_ids(client, &values); BOOST_REQUIRE_EQUAL(VSMCLIENT_SUCCESS, status); - BOOST_CHECK_EQUAL(getArrayStringLength(values, EXPECTED_DBUSES_STARTED.size() + 1), + BOOST_CHECK_EQUAL(getArrayStringLength(values, EXPECTED_DBUSES_STARTED.size() + 1u), EXPECTED_DBUSES_STARTED.size()); std::set zones; @@ -291,7 +291,7 @@ BOOST_AUTO_TEST_CASE(NotificationTest) BOOST_REQUIRE_EQUAL(VSMCLIENT_SUCCESS, status); } - BOOST_CHECK(callbackData.signalReceivedLatch.waitForN(clients.size() - 1, EVENT_TIMEOUT)); + BOOST_CHECK(callbackData.signalReceivedLatch.waitForN(clients.size() - 1u, EVENT_TIMEOUT)); BOOST_CHECK(callbackData.signalReceivedLatch.empty()); for (const auto& msg : callbackData.receivedSignalMsg) { diff --git a/tests/unit_tests/server/ut-zones-manager.cpp b/tests/unit_tests/server/ut-zones-manager.cpp index 33d7c50..07c03dd 100644 --- a/tests/unit_tests/server/ut-zones-manager.cpp +++ b/tests/unit_tests/server/ut-zones-manager.cpp @@ -620,7 +620,7 @@ BOOST_AUTO_TEST_CASE(NotifyActiveZoneTest) dbus.second->callMethodNotify(); } - BOOST_CHECK(signalReceivedLatch.waitForN(dbuses.size() - 1, EVENT_TIMEOUT)); + BOOST_CHECK(signalReceivedLatch.waitForN(dbuses.size() - 1u, EVENT_TIMEOUT)); BOOST_CHECK(signalReceivedLatch.empty()); //check if there are no signals that was received more than once -- 2.7.4 From 683b44bd6dc9cce82d8bba32e2988225ce52ddbe Mon Sep 17 00:00:00 2001 From: Mateusz Malicki Date: Wed, 10 Dec 2014 18:24:32 +0100 Subject: [PATCH 02/16] Support for boost version less then 1.54 -- disbale move in config union [Bug/Feature] Boost < 1.54 doesn't support rvalues in boost::any, better rvalues tests [Cause] ConfigUnionTest doesn't pass if compiled with boost 1.51 [Solution] Changes in libConfig; Disable moving in union [Verification] Build, install, run tests Change-Id: I3bb6af655fa9393bfab66d535705fa252ecf6174 --- tests/unit_tests/config/ut-configuration.cpp | 44 ++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 3 deletions(-) diff --git a/tests/unit_tests/config/ut-configuration.cpp b/tests/unit_tests/config/ut-configuration.cpp index 8aab028..e46bfd8 100644 --- a/tests/unit_tests/config/ut-configuration.cpp +++ b/tests/unit_tests/config/ut-configuration.cpp @@ -44,11 +44,31 @@ struct TestConfig { struct SubSubConfig { int intVal; + bool moved; CONFIG_REGISTER ( intVal ) + SubSubConfig() : moved(false) {} + SubSubConfig(const SubSubConfig& config) : intVal(config.intVal), moved(false) {} + SubSubConfig(SubSubConfig&& config) : intVal(std::move(config.intVal)), moved(false) { + config.moved = true; + } + SubSubConfig& operator=(const SubSubConfig& config) { + intVal = config.intVal; + moved = false; + return *this; + } + SubSubConfig& operator=(SubSubConfig&& config) { + intVal = std::move(config.intVal); + moved = false; + config.moved = true; + return *this; + } + bool isMoved() const { + return moved; + } }; int intVal; @@ -368,7 +388,7 @@ BOOST_AUTO_TEST_CASE(FromToFDTest) fs::remove(fifoPath); } -BOOST_AUTO_TEST_CASE(ConfigUnion) +BOOST_AUTO_TEST_CASE(ConfigUnionTest) { TestConfig testConfig; BOOST_REQUIRE_NO_THROW(loadFromString(jsonTestString, testConfig)); @@ -385,11 +405,29 @@ BOOST_AUTO_TEST_CASE(ConfigUnion) std::string out = saveToString(testConfig); BOOST_CHECK_EQUAL(out, jsonTestString); - //Check move and copy + //Check copy + std::vector unions(2); unions[0].set(2); + //set from const lvalue reference (copy) + unions[1].set(testConfig.unions[1].as()); + BOOST_CHECK(!testConfig.unions[1].as().subSubObj.isMoved()); + //set from lvalue reference (copy) + unions[1].set(testConfig.unions[1].as()); + BOOST_CHECK(!testConfig.unions[1].as().subSubObj.isMoved()); + //set from const rvalue reference (copy) + unions[1].set(std::move(testConfig.unions[1].as())); + BOOST_CHECK(!testConfig.unions[1].as().subSubObj.isMoved()); + //set rvalue reference (copy -- move is disabled) unions[1].set(std::move(testConfig.unions[1].as())); - BOOST_CHECK(testConfig.unions[1].as().intVector.empty()); + BOOST_CHECK(!testConfig.unions[1].as().subSubObj.isMoved()); + //assign lvalue reference (copy) + testConfig.unions[1] = unions[1]; + BOOST_CHECK(!unions[1].as().subSubObj.isMoved()); + //assign rvalue reference (copy -- move is disabled) + testConfig.unions[1] = std::move(unions[1]); + BOOST_CHECK(!unions[1].as().subSubObj.isMoved()); + testConfig.unions.clear(); testConfig.unions = unions; out = saveToString(testConfig); -- 2.7.4 From 7a5a6303f9d660195ef58f730b303eab23617f24 Mon Sep 17 00:00:00 2001 From: Dariusz Michaluk Date: Mon, 15 Dec 2014 15:20:26 +0100 Subject: [PATCH 03/16] Fix build error ('out' may be used uninitialized) [Bug/Feature] build error [Cause] uninitialized variable [Solution] initialize variable [Verification] build Change-Id: I15fa8909d96a5f61998fb774efbc698cb21744f8 Signed-off-by: Dariusz Michaluk --- client/vasum-client-impl.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/vasum-client-impl.cpp b/client/vasum-client-impl.cpp index 41898c7..3c9448b 100644 --- a/client/vasum-client-impl.cpp +++ b/client/vasum-client-impl.cpp @@ -364,7 +364,7 @@ VsmStatus Client::vsm_get_active_zone_id(VsmString* id) noexcept { assert(id); - GVariant* out; + GVariant* out = NULL; VsmStatus ret = callMethod(HOST_INTERFACE, api::host::METHOD_GET_ACTIVE_ZONE_ID, NULL, @@ -409,7 +409,7 @@ VsmStatus Client::vsm_lookup_zone_by_id(const char* id, VsmZone* zone) noexcept assert(id); assert(zone); - GVariant* out; + GVariant* out = NULL; GVariant* args_in = g_variant_new("(s)", id); VsmStatus ret = callMethod(HOST_INTERFACE, api::host::METHOD_GET_ZONE_INFO, @@ -636,7 +636,7 @@ VsmStatus Client::vsm_file_move_request(const char* destZone, const char* path) assert(destZone); assert(path); - GVariant* out; + GVariant* out = NULL; GVariant* args_in = g_variant_new("(ss)", destZone, path); VsmStatus ret = callMethod(ZONE_INTERFACE, api::zone::METHOD_FILE_MOVE_REQUEST, -- 2.7.4 From 1ea2e87dd7c56e65f918466cdb20ef5fd2baa434 Mon Sep 17 00:00:00 2001 From: Jan Olszak Date: Mon, 8 Dec 2014 16:48:21 +0100 Subject: [PATCH 04/16] IPC: Support for the external polling loop in Service [Bug/Feature] Using GMainLoop is possible [Cause] N/A [Solution] For glib > v.2.36 [Verification] Build, install, run tests Change-Id: Ic6d74688c322dd79b29195d94658a4f2ffe0aa83 --- common/ipc/internals/acceptor.cpp | 44 +++++++--- common/ipc/internals/acceptor.hpp | 25 ++++++ common/ipc/internals/processor.cpp | 84 +++++++++++------- common/ipc/internals/processor.hpp | 79 ++++++++++------- common/ipc/internals/utils.cpp | 4 +- common/ipc/ipc-gsource.cpp | 174 +++++++++++++++++++++++++++++++++++++ common/ipc/ipc-gsource.hpp | 150 ++++++++++++++++++++++++++++++++ common/ipc/service.cpp | 35 ++++++++ common/ipc/service.hpp | 17 ++++ common/ipc/types.cpp | 2 + tests/unit_tests/ipc/ut-ipc.cpp | 71 ++++++++++++--- 11 files changed, 599 insertions(+), 86 deletions(-) create mode 100644 common/ipc/ipc-gsource.cpp create mode 100644 common/ipc/ipc-gsource.hpp diff --git a/common/ipc/internals/acceptor.cpp b/common/ipc/internals/acceptor.cpp index 3a6c4cd..1eab1c2 100644 --- a/common/ipc/internals/acceptor.cpp +++ b/common/ipc/internals/acceptor.cpp @@ -25,21 +25,20 @@ #include "config.hpp" #include "ipc/exception.hpp" -#include "ipc/internals/utils.hpp" #include "ipc/internals/acceptor.hpp" #include "logger/logger.hpp" #include #include #include -#include #include namespace vasum { namespace ipc { Acceptor::Acceptor(const std::string& socketPath, const NewConnectionCallback& newConnectionCallback) - : mNewConnectionCallback(newConnectionCallback), + : mIsRunning(false), + mNewConnectionCallback(newConnectionCallback), mSocket(Socket::createSocket(socketPath)) { LOGT("Creating Acceptor for socket " << socketPath); @@ -88,9 +87,8 @@ void Acceptor::run() fds[1].fd = mSocket.getFD(); fds[1].events = POLLIN; - // Main loop - bool isRunning = true; - while (isRunning) { + mIsRunning = true; + while (mIsRunning) { LOGT("Waiting for new connections..."); int ret = ::poll(fds.data(), fds.size(), -1 /*blocking call*/); @@ -108,23 +106,41 @@ void Acceptor::run() // Check for incoming connections if (fds[1].revents & POLLIN) { fds[1].revents = 0; - std::shared_ptr tmpSocket = mSocket.accept(); - mNewConnectionCallback(tmpSocket); + handleConnection(); } // Check for incoming events if (fds[0].revents & POLLIN) { fds[0].revents = 0; - - if (mEventQueue.receive() == Event::FINISH) { - LOGD("Event FINISH"); - isRunning = false; - break; - } + handleEvent(); } } LOGT("Exiting run"); } +void Acceptor::handleConnection() +{ + std::shared_ptr tmpSocket = mSocket.accept(); + mNewConnectionCallback(tmpSocket); +} + +void Acceptor::handleEvent() +{ + if (mEventQueue.receive() == Event::FINISH) { + LOGD("Event FINISH"); + mIsRunning = false; + } +} + +FileDescriptor Acceptor::getEventFD() +{ + return mEventQueue.getFD(); +} + +FileDescriptor Acceptor::getConnectionFD() +{ + return mSocket.getFD(); +} + } // namespace ipc } // namespace vasum diff --git a/common/ipc/internals/acceptor.hpp b/common/ipc/internals/acceptor.hpp index 702a161..f87a0bb 100644 --- a/common/ipc/internals/acceptor.hpp +++ b/common/ipc/internals/acceptor.hpp @@ -29,6 +29,7 @@ #include "ipc/internals/socket.hpp" #include "ipc/internals/event-queue.hpp" +#include "ipc/types.hpp" #include #include @@ -67,11 +68,35 @@ public: */ void stop(); + /** + * Handle one incoming connection. + * Used with external polling + */ + void handleConnection(); + + /** + * Handle one event from the internal event's queue + * Used with external polling + */ + void handleEvent(); + + /** + * @return file descriptor of internal event's queue + */ + FileDescriptor getEventFD(); + + /** + * @return file descriptor for the connection socket + */ + FileDescriptor getConnectionFD(); + private: enum class Event : int { FINISH // Shutdown request }; + bool mIsRunning; + NewConnectionCallback mNewConnectionCallback; Socket mSocket; diff --git a/common/ipc/internals/processor.cpp b/common/ipc/internals/processor.cpp index be1060d..72a1788 100644 --- a/common/ipc/internals/processor.cpp +++ b/common/ipc/internals/processor.cpp @@ -112,6 +112,11 @@ void Processor::setRemovedPeerCallback(const PeerCallback& removedPeerCallback) mRemovedPeerCallback = removedPeerCallback; } +FileDescriptor Processor::getEventFD() +{ + return mEventQueue.getFD(); +} + void Processor::removeMethod(const MethodID methodID) { LOGT("Removing method " << methodID); @@ -128,9 +133,9 @@ FileDescriptor Processor::addPeer(const std::shared_ptr& socketPtr) peerFD = socketPtr->getFD(); SocketInfo socketInfo(peerFD, std::move(socketPtr)); mNewSockets.push(std::move(socketInfo)); + mEventQueue.send(Event::ADD_PEER); } LOGI("New peer added. Id: " << peerFD); - mEventQueue.send(Event::ADD_PEER); return peerFD; } @@ -143,9 +148,9 @@ void Processor::removePeer(const FileDescriptor peerFD) Lock lock(mSocketsMutex); RemovePeerRequest request(peerFD, conditionPtr); mPeersToDelete.push(std::move(request)); + mEventQueue.send(Event::REMOVE_PEER); } - mEventQueue.send(Event::REMOVE_PEER); auto isPeerDeleted = [&peerFD, this]()->bool { Lock lock(mSocketsMutex); @@ -204,6 +209,10 @@ void Processor::removePeerInternal(const FileDescriptor peerFD, Status status) void Processor::resetPolling() { + if (!isStarted()) { + return; + } + LOGI("Resetting polling"); // Setup polling on eventfd and sockets Lock lock(mSocketsMutex); @@ -251,20 +260,22 @@ void Processor::run() } // Check for incoming events - if (handleEvent()) { - // mFDs changed - continue; + if (mFDs[0].revents & POLLIN) { + mFDs[0].revents &= ~(POLLIN); + if (handleEvent()) { + // mFDs changed + continue; + } } + } cleanCommunication(); } - bool Processor::handleLostConnections() { std::vector peersToRemove; - { Lock lock(mSocketsMutex); for (unsigned int i = 1; i < mFDs.size(); ++i) { @@ -283,39 +294,61 @@ bool Processor::handleLostConnections() return !peersToRemove.empty(); } +bool Processor::handleLostConnection(const FileDescriptor peerFD) +{ + removePeerInternal(peerFD, Status::PEER_DISCONNECTED); + return true; +} + bool Processor::handleInputs() { - std::vector> socketsWithInput; + std::vector peersWithInput; { Lock lock(mSocketsMutex); for (unsigned int i = 1; i < mFDs.size(); ++i) { if (mFDs[i].revents & POLLIN) { mFDs[i].revents &= ~(POLLIN); - socketsWithInput.push_back(mSockets[mFDs[i].fd]); + peersWithInput.push_back(mFDs[i].fd); } } } bool pollChanged = false; // Handle input outside the critical section - for (const auto& socketPtr : socketsWithInput) { - pollChanged = pollChanged || handleInput(*socketPtr); + for (const FileDescriptor peerFD : peersWithInput) { + pollChanged = pollChanged || handleInput(peerFD); } return pollChanged; } -bool Processor::handleInput(const Socket& socket) +bool Processor::handleInput(const FileDescriptor peerFD) { LOGT("Handle incoming data"); + + std::shared_ptr socketPtr; + try { + socketPtr = mSockets.at(peerFD); + } catch (const std::out_of_range&) { + LOGE("No such peer: " << peerFD); + return false; + } + MethodID methodID; MessageID messageID; { - Socket::Guard guard = socket.getGuard(); - socket.read(&methodID, sizeof(methodID)); - socket.read(&messageID, sizeof(messageID)); + Socket::Guard guard = socketPtr->getGuard(); + try { + socketPtr->read(&methodID, sizeof(methodID)); + socketPtr->read(&messageID, sizeof(messageID)); + + } catch (const IPCException& e) { + LOGE("Error during reading the socket"); + removePeerInternal(socketPtr->getFD(), Status::NAUGHTY_PEER); + return true; + } if (methodID == RETURN_METHOD_ID) { - return onReturnValue(socket, messageID); + return onReturnValue(*socketPtr, messageID); } else { Lock lock(mCallsMutex); @@ -323,19 +356,19 @@ bool Processor::handleInput(const Socket& socket) // Method std::shared_ptr methodCallbacks = mMethodsCallbacks.at(methodID); lock.unlock(); - return onRemoteCall(socket, methodID, messageID, methodCallbacks); + return onRemoteCall(*socketPtr, methodID, messageID, methodCallbacks); } else if (mSignalsCallbacks.count(methodID)) { // Signal std::shared_ptr signalCallbacks = mSignalsCallbacks.at(methodID); lock.unlock(); - return onRemoteSignal(socket, methodID, messageID, signalCallbacks); + return onRemoteSignal(*socketPtr, methodID, messageID, signalCallbacks); } else { // Nothing lock.unlock(); LOGW("No method or signal callback for methodID: " << methodID); - removePeerInternal(socket.getFD(), Status::NAUGHTY_PEER); + removePeerInternal(socketPtr->getFD(), Status::NAUGHTY_PEER); return true; } } @@ -461,13 +494,6 @@ bool Processor::onRemoteCall(const Socket& socket, bool Processor::handleEvent() { - if (!(mFDs[0].revents & POLLIN)) { - // No event to serve - return false; - } - - mFDs[0].revents &= ~(POLLIN); - switch (mEventQueue.receive()) { case Event::FINISH: { LOGD("Event FINISH"); @@ -518,11 +544,11 @@ bool Processor::onNewPeer() // Broadcast the new signal to peers LOGW("Sending handled signals"); - std::list peersIDs; + std::list peersFDs; { Lock lock(mSocketsMutex); for (const auto kv : mSockets) { - peersIDs.push_back(kv.first); + peersFDs.push_back(kv.first); } } @@ -535,7 +561,7 @@ bool Processor::onNewPeer() } auto data = std::make_shared(ids); - for (const FileDescriptor peerFD : peersIDs) { + for (const FileDescriptor peerFD : peersFDs) { callInternal(REGISTER_SIGNAL_METHOD_ID, peerFD, data, diff --git a/common/ipc/internals/processor.hpp b/common/ipc/internals/processor.hpp index 6ce2688..d33d12b 100644 --- a/common/ipc/internals/processor.hpp +++ b/common/ipc/internals/processor.hpp @@ -75,6 +75,7 @@ const unsigned int DEFAULT_METHOD_TIMEOUT = 1000; * - new way to generate UIDs * - callbacks for serialization/parsing * - store Sockets in a vector, maybe SocketStore? +* - fix valgrind tests * * */ @@ -240,6 +241,35 @@ public: void signal(const MethodID methodID, const std::shared_ptr& data); + /** + * Removes one peer. + * Handler used in external polling. + * + * @param peerFD file description identifying the peer + * @return should the polling structure be rebuild + */ + bool handleLostConnection(const FileDescriptor peerFD); + + /** + * Handles input from one peer. + * Handler used in external polling. + * + * @param peerFD file description identifying the peer + * @return should the polling structure be rebuild + */ + bool handleInput(const FileDescriptor peerFD); + + /** + * Handle one event from the internal event's queue + * + * @return should the polling structure be rebuild + */ + bool handleEvent(); + + /** + * @return file descriptor for the internal event's queue + */ + FileDescriptor getEventFD(); private: typedef std::function& data)> SerializeCallback; @@ -383,13 +413,12 @@ private: static void discardResultHandler(Status, std::shared_ptr&) {} void run(); - bool handleEvent(); bool onCall(); bool onNewPeer(); bool onRemovePeer(); bool handleLostConnections(); bool handleInputs(); - bool handleInput(const Socket& socket); + bool onReturnValue(const Socket& socket, const MessageID messageID); bool onRemoteCall(const Socket& socket, @@ -494,26 +523,24 @@ void Processor::addSignalHandler(const MethodID methodID, mSignalsCallbacks[methodID] = std::make_shared(std::move(signalCall)); } - if (isStarted()) { - // Broadcast the new signal to peers - std::vector ids {methodID}; - auto data = std::make_shared(ids); + std::vector ids {methodID}; + auto data = std::make_shared(ids); - std::list peersIDs; - { - Lock lock(mSocketsMutex); - for (const auto kv : mSockets) { - peersIDs.push_back(kv.first); - } + std::list peersFDs; + { + Lock lock(mSocketsMutex); + for (const auto kv : mSockets) { + peersFDs.push_back(kv.first); } + } - for (const FileDescriptor peerFD : peersIDs) { - callSync(REGISTER_SIGNAL_METHOD_ID, - peerFD, - data, - DEFAULT_METHOD_TIMEOUT); - } + for (const FileDescriptor peerFD : peersFDs) { + callSync(REGISTER_SIGNAL_METHOD_ID, + peerFD, + data, + DEFAULT_METHOD_TIMEOUT); } + } template @@ -535,11 +562,6 @@ MessageID Processor::callAsync(const MethodID methodID, const std::shared_ptr& data, const typename ResultHandler::type& process) { - if (!isStarted()) { - LOGE("The Processor thread is not started. Can't send any data."); - throw IPCException("The Processor thread is not started. Can't send any data."); - } - return callInternal(methodID, peerFD, data, process); } @@ -600,18 +622,13 @@ template void Processor::signal(const MethodID methodID, const std::shared_ptr& data) { - if (!isStarted()) { - LOGE("The Processor thread is not started. Can't send any data."); - throw IPCException("The Processor thread is not started. Can't send any data."); - } - - std::list peersIDs; + std::list peersFDs; { Lock lock(mSocketsMutex); - peersIDs = mSignalsPeers[methodID]; + peersFDs = mSignalsPeers[methodID]; } - for (const FileDescriptor peerFD : peersIDs) { + for (const FileDescriptor peerFD : peersFDs) { Lock lock(mCallsMutex); mCalls.push(methodID, peerFD, data); mEventQueue.send(Event::CALL); diff --git a/common/ipc/internals/utils.cpp b/common/ipc/internals/utils.cpp index 88f8fc0..bd98c1b 100644 --- a/common/ipc/internals/utils.cpp +++ b/common/ipc/internals/utils.cpp @@ -122,8 +122,8 @@ void write(int fd, const void* bufferPtr, const size_t size, int timeoutMS) // Neglected errors LOGD("Retrying write"); } else { - LOGE("Error during reading: " + std::string(strerror(errno))); - throw IPCException("Error during reading: " + std::string(strerror(errno))); + LOGE("Error during writing: " + std::string(strerror(errno))); + throw IPCException("Error during writing: " + std::string(strerror(errno))); } if (nTotal >= size) { diff --git a/common/ipc/ipc-gsource.cpp b/common/ipc/ipc-gsource.cpp new file mode 100644 index 0000000..f5cdbb5 --- /dev/null +++ b/common/ipc/ipc-gsource.cpp @@ -0,0 +1,174 @@ +/* +* Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved +* +* Contact: Jan Olszak +* +* 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 + * @author Jan Olszak (j.olszak@samsung.com) + * @brief Class for creating a dedicated GSource + */ + + +#include "config.hpp" + +#include "ipc/ipc-gsource.hpp" + +#if GLIB_CHECK_VERSION(2,36,0) + +#include "logger/logger.hpp" +#include + +namespace vasum { +namespace ipc { + +namespace { + + +GIOCondition conditions = static_cast(G_IO_IN | + G_IO_ERR | + G_IO_HUP); +} + + +IPCGSource::IPCGSource(const std::vector fds, + const HandlerCallback& handlerCallback) + : mHandlerCallback(handlerCallback) +{ + LOGD("Constructing IPCGSource"); + for (const FileDescriptor fd : fds) { + addFD(fd); + } +} + +IPCGSource::~IPCGSource() +{ + LOGD("Destroying IPCGSource"); + g_source_destroy(&mGSource); + +} + +IPCGSource* IPCGSource::create(const std::vector& fds, + const HandlerCallback& handlerCallback) +{ + LOGD("Creating IPCGSource"); + + static GSourceFuncs funcs = { &IPCGSource::prepare, + &IPCGSource::check, + &IPCGSource::dispatch, + &IPCGSource::finalize, + nullptr, + nullptr + }; + + // New GSource + GSource* gSource = g_source_new(&funcs, sizeof(IPCGSource)); + g_source_set_priority(gSource, G_PRIORITY_HIGH); + + // Fill additional data + IPCGSource* source = reinterpret_cast(gSource); + return new(source) IPCGSource(fds, handlerCallback); +} + + +void IPCGSource::addFD(const FileDescriptor fd) +{ + if (!&mGSource) { + // In case it's called as a callback but the IPCGSource is destroyed + return; + } + + LOGD("Adding fd to glib"); + gpointer tag = g_source_add_unix_fd(&mGSource, + fd, + conditions); + FDInfo fdInfo(tag, fd); + mFDInfos.push_back(std::move(fdInfo)); +} + +void IPCGSource::removeFD(const FileDescriptor fd) +{ + if (!&mGSource) { + // In case it's called as a callback but the IPCGSource is destroyed + return; + } + + LOGD("Removing fd from glib"); + auto it = std::find(mFDInfos.begin(), mFDInfos.end(), fd); + if (it == mFDInfos.end()) { + LOGE("No such fd"); + return; + } + g_source_remove_unix_fd(&mGSource, it->tag); + mFDInfos.erase(it); +} + +guint IPCGSource::attach(GMainContext* context) +{ + LOGD("Attaching to GMainContext"); + return g_source_attach(&mGSource, context); +} + +gboolean IPCGSource::prepare(GSource* gSource, gint* timeout) +{ + if (!gSource) { + return FALSE; + } + + *timeout = -1; + + // TODO: Implement hasEvents() method in Client and Service and use it here as a callback: + // return source->hasEvents(); + return FALSE; +} + +gboolean IPCGSource::check(GSource* gSource) +{ + if (!gSource) { + return FALSE; + } + + return TRUE; +} + +gboolean IPCGSource::dispatch(GSource* gSource, + GSourceFunc /*callback*/, + gpointer /*userData*/) +{ + IPCGSource* source = reinterpret_cast(gSource); + + for (const FDInfo fdInfo : source->mFDInfos) { + GIOCondition cond = g_source_query_unix_fd(gSource, fdInfo.tag); + if (conditions & cond) { + source->mHandlerCallback(fdInfo.fd, cond); + } + } + + return TRUE; // Don't remove the GSource from the GMainContext +} + +void IPCGSource::finalize(GSource* gSource) +{ + if (gSource) { + IPCGSource* source = reinterpret_cast(gSource); + source->~IPCGSource(); + } +} + +} // namespace ipc +} // namespace vasum + +#endif // GLIB_CHECK_VERSION diff --git a/common/ipc/ipc-gsource.hpp b/common/ipc/ipc-gsource.hpp new file mode 100644 index 0000000..96e0a1a --- /dev/null +++ b/common/ipc/ipc-gsource.hpp @@ -0,0 +1,150 @@ +/* +* Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved +* +* Contact: Jan Olszak +* +* 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 + * @author Jan Olszak (j.olszak@samsung.com) + * @brief Class for creating a dedicated GSource + */ + +#ifndef COMMON_IPC_IPC_GSOURCE_HPP +#define COMMON_IPC_IPC_GSOURCE_HPP + +#include +#if GLIB_CHECK_VERSION(2,36,0) + +#include "ipc/service.hpp" +#include "ipc/types.hpp" +#include "utils/callback-wrapper.hpp" +#include + + +namespace vasum { +namespace ipc { + +/** + * Class for connecting to the glib's loop. + * Creates a dedicated GSource. + * + * It's supposed to be constructed ONLY with the static create method + * and destructed in a glib callback. + */ +struct IPCGSource { +public: + typedef std::function HandlerCallback; + + IPCGSource() = delete; + IPCGSource(const IPCGSource&) = delete; + IPCGSource& operator=(const IPCGSource&) = delete; + + /** + * New file descriptor to listen on. + * + * @param peerFD file descriptor + */ + void addFD(const FileDescriptor peerFD); + + /** + * Removes the file descriptor from the GSource + * + * @param peerFD file descriptor + */ + void removeFD(const FileDescriptor peerFD); + + /** + * Attach to the glib's GMainContext + * + * @param context where to connect + * @return result of the g_source_attach call + */ + guint attach(GMainContext* context = nullptr); + + /** + * Creates the IPCGSource class in the memory allocated by glib. + * Calls IPCGSource's constructor + * + * @param fds initial set of file descriptors + * @param handlerCallback event handling callback + * + * @return pointer to the IPCGSource + */ + static IPCGSource* create(const std::vector& fds, + const HandlerCallback& handlerCallback); + +private: + + /** + * GSourceFuncs' callback + */ + static gboolean prepare(GSource* source, gint* timeout); + + /** + * GSourceFuncs' callback + */ + static gboolean check(GSource* source); + + /** + * GSourceFuncs' callback + */ + static gboolean dispatch(GSource* source, + GSourceFunc callbacks, + gpointer userData); + + /** + * GSourceFuncs' callback + */ + static void finalize(GSource* source); + + + + // Called only from IPCGSource::create + IPCGSource(const std::vector fds, + const HandlerCallback& handlerCallback); + + // Called only from IPCGSource::finalize + ~IPCGSource(); + + struct FDInfo { + FDInfo(gpointer tag, FileDescriptor fd) + : tag(tag), fd(fd) {} + + bool operator==(const gpointer t) + { + return t == tag; + } + + bool operator==(const FileDescriptor f) + { + return f == fd; + } + + gpointer tag; + FileDescriptor fd; + }; + + GSource mGSource; + HandlerCallback mHandlerCallback; + std::vector mFDInfos; +}; + +} // namespace ipc +} // namespace vasum + +#endif // GLIB_CHECK_VERSION + +#endif // COMMON_IPC_IPC_GSOURCE_HPP diff --git a/common/ipc/service.cpp b/common/ipc/service.cpp index 5ee5fbd..be95cee 100644 --- a/common/ipc/service.cpp +++ b/common/ipc/service.cpp @@ -79,6 +79,41 @@ void Service::stop() LOGD("Stopped"); } +std::vector Service::getFDs() +{ + std::vector fds; + fds.push_back(mAcceptor.getEventFD()); + fds.push_back(mAcceptor.getConnectionFD()); + fds.push_back(mProcessor.getEventFD()); + + return fds; +} + +void Service::handle(const FileDescriptor fd, const short pollEvent) +{ + if (fd == mProcessor.getEventFD() && pollEvent & POLLIN) { + mProcessor.handleEvent(); + return; + + } else if (fd == mAcceptor.getConnectionFD() && pollEvent & POLLIN) { + mAcceptor.handleConnection(); + return; + + } else if (fd == mAcceptor.getEventFD() && pollEvent & POLLIN) { + mAcceptor.handleEvent(); + return; + + } else if (pollEvent & POLLIN) { + mProcessor.handleInput(fd); + return; + + } else if (pollEvent & POLLHUP) { + mProcessor.handleLostConnection(fd); + return; + } +} + + void Service::setNewPeerCallback(const PeerCallback& newPeerCallback) { mProcessor.setNewPeerCallback(newPeerCallback); diff --git a/common/ipc/service.hpp b/common/ipc/service.hpp index 1f9aee3..ed83606 100644 --- a/common/ipc/service.hpp +++ b/common/ipc/service.hpp @@ -75,6 +75,23 @@ public: void stop(); /** + * Used with an external polling loop + * + * @return vector of internal file descriptors + */ + std::vector getFDs(); + + /** + * Used with an external polling loop. + * Handles one event from the file descriptor. + * + * @param fd file descriptor + * @param pollEvent event on the fd. Defined in poll.h + * + */ + void handle(const FileDescriptor fd, const short pollEvent); + + /** * Set the callback called for each new connection to a peer * * @param newPeerCallback the callback diff --git a/common/ipc/types.cpp b/common/ipc/types.cpp index 18c769d..fa57648 100644 --- a/common/ipc/types.cpp +++ b/common/ipc/types.cpp @@ -22,6 +22,8 @@ * @brief Types definitions and helper functions */ +#include "config.hpp" + #include "ipc/types.hpp" #include "logger/logger.hpp" diff --git a/tests/unit_tests/ipc/ut-ipc.cpp b/tests/unit_tests/ipc/ut-ipc.cpp index c671db6..9ce131d 100644 --- a/tests/unit_tests/ipc/ut-ipc.cpp +++ b/tests/unit_tests/ipc/ut-ipc.cpp @@ -33,7 +33,9 @@ #include "ipc/service.hpp" #include "ipc/client.hpp" +#include "ipc/ipc-gsource.hpp" #include "ipc/types.hpp" +#include "utils/glib-loop.hpp" #include "config/fields.hpp" #include "logger/logger.hpp" @@ -47,6 +49,8 @@ using namespace vasum; using namespace vasum::ipc; +using namespace vasum::utils; +using namespace std::placeholders; namespace fs = boost::filesystem; namespace { @@ -132,30 +136,48 @@ std::shared_ptr longEchoCallback(const FileDescriptor, std::shared_ptr return data; } -FileDescriptor connect(Service& s, Client& c) +FileDescriptor connect(Service& s, Client& c, bool serviceUsesGlib = false) { // Connects the Client to the Service and returns Clients FileDescriptor - std::mutex mutex; std::condition_variable cv; FileDescriptor peerFD = 0; - auto newPeerCallback = [&cv, &peerFD, &mutex](const FileDescriptor newFileDescriptor) { + auto newPeerCallback = [&cv, &peerFD, &mutex](const FileDescriptor newFD) { std::unique_lock lock(mutex); - peerFD = newFileDescriptor; - cv.notify_one(); + peerFD = newFD; + cv.notify_all(); }; - s.setNewPeerCallback(newPeerCallback); - if (!s.isStarted()) { - s.start(); + if (!serviceUsesGlib) { + s.setNewPeerCallback(newPeerCallback); + + if (!s.isStarted()) { + s.start(); + } + } else { +#if GLIB_CHECK_VERSION(2,36,0) + + IPCGSource* serviceGSourcePtr = IPCGSource::create(s.getFDs(), std::bind(&Service::handle, &s, _1, _2)); + + auto agregateCallback = [&newPeerCallback, &serviceGSourcePtr](const FileDescriptor newFD) { + serviceGSourcePtr->addFD(newFD); + newPeerCallback(newFD); + }; + + s.setNewPeerCallback(agregateCallback); + s.setRemovedPeerCallback(std::bind(&IPCGSource::removeFD, serviceGSourcePtr, _1)); + + serviceGSourcePtr->attach(); +#endif // GLIB_CHECK_VERSION + } c.start(); std::unique_lock lock(mutex); - BOOST_CHECK(cv.wait_for(lock, std::chrono::milliseconds(1000), [&peerFD]() { + BOOST_CHECK(cv.wait_for(lock, std::chrono::milliseconds(2000), [&peerFD]() { return peerFD != 0; })); @@ -165,7 +187,7 @@ FileDescriptor connect(Service& s, Client& c) void testEcho(Client& c, const MethodID methodID) { std::shared_ptr sentData(new SendData(34)); - std::shared_ptr recvData = c.callSync(methodID, sentData); + std::shared_ptr recvData = c.callSync(methodID, sentData, 1000); BOOST_CHECK_EQUAL(recvData->intVal, sentData->intVal); } @@ -554,6 +576,35 @@ BOOST_AUTO_TEST_CASE(AddSignalOffline) } +#if GLIB_CHECK_VERSION(2,36,0) + +BOOST_AUTO_TEST_CASE(ServiceGSource) +{ + ScopedGlibLoop loop; + + std::atomic_bool isSignalCalled(false); + auto signalHandler = [&isSignalCalled](const FileDescriptor, std::shared_ptr&) { + isSignalCalled = true; + }; + + Service s(socketPath); + s.addMethodHandler(1, echoCallback); + + Client c(socketPath); + s.addSignalHandler(2, signalHandler); + connect(s, c, true); + + testEcho(c, 1); + + auto data = std::make_shared(1); + c.signal(2, data); + + std::this_thread::sleep_for(std::chrono::milliseconds(100)); //TODO wait_for + BOOST_CHECK(isSignalCalled); +} + +#endif // GLIB_CHECK_VERSION + // BOOST_AUTO_TEST_CASE(ConnectionLimitTest) // { // unsigned oldLimit = ipc::getMaxFDNumber(); -- 2.7.4 From 637f643f63b8c7de143a2eec4891d7b78f396264 Mon Sep 17 00:00:00 2001 From: Lukasz Kostyra Date: Mon, 15 Dec 2014 13:25:23 +0100 Subject: [PATCH 05/16] Add "enabled" file [Feature] File indicating that zones are running in the system. [Cause] Other external services (eg. security-manager) must know when zones are active in the system. [Solution] Provide "enabled" file, which will appear when zones are launched. The file will appear when first zone will be created, and file will disappear when last zone will be destroyed. [Verification] Build, install, run tests. Change-Id: I634e424e28c7d449276bbe1c2c80f3cb0e35bcb7 --- common/utils/fs.cpp | 13 +++++++++++++ common/utils/fs.hpp | 5 +++++ server/zones-manager.cpp | 15 +++++++++++++++ tests/unit_tests/utils/ut-fs.cpp | 7 +++++++ 4 files changed, 40 insertions(+) diff --git a/common/utils/fs.cpp b/common/utils/fs.cpp index 9254f0a..3330cbf 100644 --- a/common/utils/fs.cpp +++ b/common/utils/fs.cpp @@ -98,6 +98,19 @@ bool saveFileContent(const std::string& path, const std::string& content) return true; } +bool removeFile(const std::string& path) +{ + LOGD(path << ": exists, removing."); + if (::remove(path.c_str())) { + if (errno != ENOENT) { + LOGE(path << ": failed to delete: " << ::strerror(errno)); + return false; + } + } + + return true; +} + bool isCharDevice(const std::string& path) { struct stat s; diff --git a/common/utils/fs.hpp b/common/utils/fs.hpp index 91ea62a..35000c6 100644 --- a/common/utils/fs.hpp +++ b/common/utils/fs.hpp @@ -50,6 +50,11 @@ bool readFileContent(const std::string& path, std::string& content); bool saveFileContent(const std::string& path, const std::string& content); /** + * Remove file + */ +bool removeFile(const std::string& path); + +/** * Checks if a char device exists */ bool isCharDevice(const std::string& path); diff --git a/server/zones-manager.cpp b/server/zones-manager.cpp index 981556b..402fc7f 100644 --- a/server/zones-manager.cpp +++ b/server/zones-manager.cpp @@ -65,6 +65,7 @@ bool regexMatchVector(const std::string& str, const std::vector& v const std::string HOST_ID = "host"; const std::string ZONE_TEMPLATE_CONFIG_PATH = "template.conf"; +const std::string ENABLED_FILE_NAME = "enabled"; const boost::regex ZONE_NAME_REGEX("~NAME~"); const boost::regex ZONE_IP_THIRD_OCTET_REGEX("~IP~"); @@ -193,6 +194,14 @@ void ZonesManager::createZone(const std::string& zoneConfig) this, id, _1)); mZones.insert(ZoneMap::value_type(id, std::move(c))); + + // after zone is created successfully, put a file informing that zones are enabled + if (mZones.size() == 1) { + if (!utils::saveFileContent( + utils::createFilePath(mConfig.zonesPath, "/", ENABLED_FILE_NAME), "")) { + throw ZoneOperationException(ENABLED_FILE_NAME + ": cannot create."); + } + } } void ZonesManager::destroyZone(const std::string& zoneId) @@ -207,6 +216,12 @@ void ZonesManager::destroyZone(const std::string& zoneId) // TODO give back the focus it->second->setDestroyOnExit(); mZones.erase(it); + + if (mZones.size() == 0) { + if (!utils::removeFile(utils::createFilePath(mConfig.zonesPath, "/", ENABLED_FILE_NAME))) { + LOGE("Failed to remove enabled file."); + } + } } void ZonesManager::focus(const std::string& zoneId) diff --git a/tests/unit_tests/utils/ut-fs.cpp b/tests/unit_tests/utils/ut-fs.cpp index 5557e46..63b4f63 100644 --- a/tests/unit_tests/utils/ut-fs.cpp +++ b/tests/unit_tests/utils/ut-fs.cpp @@ -88,6 +88,13 @@ BOOST_AUTO_TEST_CASE(SaveFileContentTest) boost::filesystem::remove(FILE_PATH_RANDOM, ec); } +BOOST_AUTO_TEST_CASE(RemoveFileTest) +{ + BOOST_REQUIRE(saveFileContent(FILE_PATH_RANDOM, FILE_CONTENT)); + BOOST_REQUIRE(removeFile(FILE_PATH_RANDOM)); + BOOST_REQUIRE(!boost::filesystem::exists(FILE_PATH_RANDOM)); +} + BOOST_AUTO_TEST_CASE(MountPointTest) { bool result; -- 2.7.4 From 8f92cd9ec0ab3c34ac8add4c9b6e8f2a31227cb5 Mon Sep 17 00:00:00 2001 From: Piotr Bartosiewicz Date: Thu, 11 Dec 2014 17:54:13 +0100 Subject: [PATCH 06/16] Worker thread utility class [Bug/Feature] A utility class that wraps a queue of tasks executed in a dedicated thread. [Cause] N/A [Solution] N/A [Verification] Build, install, run tests Change-Id: I32788fd6321c2877cf4dafe7213c1a140c1d3fd2 --- common/utils/counting-map.hpp | 88 ++++++++++++++ common/utils/worker.cpp | 186 +++++++++++++++++++++++++++++ common/utils/worker.hpp | 74 ++++++++++++ server/zone.cpp | 37 ++---- server/zone.hpp | 13 +- server/zones-manager.cpp | 39 +++--- server/zones-manager.hpp | 2 + tests/unit_tests/server/ut-zone.cpp | 9 +- tests/unit_tests/utils/ut-counting-map.cpp | 81 +++++++++++++ tests/unit_tests/utils/ut-worker.cpp | 182 ++++++++++++++++++++++++++++ 10 files changed, 656 insertions(+), 55 deletions(-) create mode 100644 common/utils/counting-map.hpp create mode 100644 common/utils/worker.cpp create mode 100644 common/utils/worker.hpp create mode 100644 tests/unit_tests/utils/ut-counting-map.cpp create mode 100644 tests/unit_tests/utils/ut-worker.cpp diff --git a/common/utils/counting-map.hpp b/common/utils/counting-map.hpp new file mode 100644 index 0000000..da5f4c7 --- /dev/null +++ b/common/utils/counting-map.hpp @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * + * Contact: Piotr Bartosiewicz + * + * 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 + * @author Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com) + * @brief Counting map + */ + +#ifndef COMMON_UTILS_COUNTING_MAP_HPP +#define COMMON_UTILS_COUNTING_MAP_HPP + +#include + +namespace vasum { +namespace utils { + + +/** + * Structure used to count elements. + * It's like multiset + count but is more efficient. + */ +template +class CountingMap { +public: + size_t increment(const Key& key) + { + auto res = mMap.insert(typename Map::value_type(key, 1)); + if (!res.second) { + ++res.first->second; + } + return res.first->second; + } + + size_t decrement(const Key& key) + { + auto it = mMap.find(key); + if (it == mMap.end()) { + return 0; + } + if (--it->second == 0) { + mMap.erase(it); + return 0; + } + return it->second; + } + + void clear() + { + mMap.clear(); + } + + size_t get(const Key& key) const + { + auto it = mMap.find(key); + return it == mMap.end() ? 0 : it->second; + } + + bool empty() const + { + return mMap.empty(); + } +private: + typedef std::unordered_map Map; + Map mMap; +}; + + +} // namespace utils +} // namespace vasum + + +#endif // COMMON_UTILS_COUNTING_MAP_HPP diff --git a/common/utils/worker.cpp b/common/utils/worker.cpp new file mode 100644 index 0000000..2cb3284 --- /dev/null +++ b/common/utils/worker.cpp @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * + * Contact: Piotr Bartosiewicz + * + * 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 + * @author Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com) + * @brief A worker thread that executes tasks + */ + +#include "config.hpp" +#include "utils/worker.hpp" +#include "utils/counting-map.hpp" +#include "logger/logger.hpp" + +#include +#include +#include +#include +#include +#include + + +namespace vasum { +namespace utils { + + +class Worker::WorkerQueue { +public: + WorkerQueue() + : mLastGroupID(0), mEnding(false) + { + LOGT("Worker queue created"); + } + + ~WorkerQueue() + { + { + Lock lock(mMutex); + assert(mTaskQueue.empty()); + assert(mGroupCounter.empty()); + mEnding = true; + } + if (mThread.joinable()) { + mAddedCondition.notify_all(); + mThread.join(); + } + LOGT("Worker queue destroyed"); + } + + GroupID getNextGroupID() + { + return ++mLastGroupID; + } + + void addTask(const Worker::Task& task, GroupID groupID) + { + assert(task); + + Lock lock(mMutex); + LOGT("Adding task to subgroup " << groupID); + mTaskQueue.push_back(TaskInfo{task, groupID}); + mGroupCounter.increment(groupID); + mAddedCondition.notify_one(); + if (!mThread.joinable()) { + mThread = std::thread(&WorkerQueue::workerProc, this); + } + } + + void waitForGroupEmpty(GroupID groupID) + { + Lock lock(mMutex); + size_t count = mGroupCounter.get(groupID); + if (count > 0) { + LOGD("Waiting for " << count << " task in group " << groupID); + } + mEmptyGroupCondition.wait(lock, [this, groupID] { + return mGroupCounter.get(groupID) == 0; + }); + } +private: + typedef std::unique_lock Lock; + + struct TaskInfo { + Worker::Task task; + GroupID groupID; + }; + + std::atomic mLastGroupID; + std::condition_variable mAddedCondition; + std::condition_variable mEmptyGroupCondition; + std::thread mThread; + + std::mutex mMutex; // protects below member variables: + bool mEnding; + std::deque mTaskQueue; + CountingMap mGroupCounter; + + void workerProc() + { + LOGT("Worker thread started"); + for (;;) { + // wait for a task + GroupID groupID; + { + Lock lock(mMutex); + mAddedCondition.wait(lock, [this] { + return !mTaskQueue.empty() || mEnding; + }); + if (mTaskQueue.empty()) { + break; + } + TaskInfo taskInfo = std::move(mTaskQueue.front()); + mTaskQueue.pop_front(); + + lock.unlock(); + + // execute + execute(taskInfo); + groupID = taskInfo.groupID; + } + // remove from queue + { + Lock lock(mMutex); + if (mGroupCounter.decrement(groupID) == 0) { + mEmptyGroupCondition.notify_all(); + } + } + } + LOGT("Worker thread exited"); + } + + void execute(const TaskInfo& taskInfo) + { + try { + LOGT("Executing task from subgroup " << taskInfo.groupID); + taskInfo.task(); + } catch (const std::exception& e) { + LOGE("Unexpected exception while executing task: " << e.what()); + } + } +}; + + +Worker::Pointer Worker::create() +{ + return Pointer(new Worker(std::make_shared())); +} + +Worker::Worker(const std::shared_ptr& workerQueue) + : mWorkerQueue(workerQueue), mGroupID(workerQueue->getNextGroupID()) +{ +} + +Worker::~Worker() +{ + mWorkerQueue->waitForGroupEmpty(mGroupID); +} + +Worker::Pointer Worker::createSubWorker() +{ + return Pointer(new Worker(mWorkerQueue)); +} + +void Worker::addTask(const Task& task) +{ + mWorkerQueue->addTask(task, mGroupID); +} + + +} // namespace utils +} // namespace vasum diff --git a/common/utils/worker.hpp b/common/utils/worker.hpp new file mode 100644 index 0000000..0d951fb --- /dev/null +++ b/common/utils/worker.hpp @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * + * Contact: Piotr Bartosiewicz + * + * 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 + * @author Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com) + * @brief A worker thread that executes tasks + */ + +#ifndef COMMON_UTILS_WORKER_HPP +#define COMMON_UTILS_WORKER_HPP + +#include +#include + +namespace vasum { +namespace utils { + +/** + * A queue with tasks executed in a dedicated thread. + * Current implementation creates a thread on the first use. + */ +class Worker { +public: + typedef std::shared_ptr Pointer; + typedef std::function Task; + + ~Worker(); + + /** + * Creates a worker with its own thread + */ + static Pointer create(); + + /** + * Creates a worker that share a thread with its parent + */ + Pointer createSubWorker(); + + /** + * Adds a task to the queue. + */ + void addTask(const Task& task); + +private: + typedef unsigned int GroupID; + class WorkerQueue; + + const std::shared_ptr mWorkerQueue; + const GroupID mGroupID; + + Worker(const std::shared_ptr& workerQueue); +}; + +} // namespace utils +} // namespace vasum + + +#endif // COMMON_UTILS_WORKER_HPP diff --git a/server/zone.cpp b/server/zone.cpp index 2e4573c..5f6dce0 100644 --- a/server/zone.cpp +++ b/server/zone.cpp @@ -65,10 +65,12 @@ void declareUnit(const std::string& file, ZoneProvisioning::Unit&& unit) } // namespace -Zone::Zone(const std::string& zonesPath, - const std::string& zoneConfigPath, - const std::string& lxcTemplatePrefix, - const std::string& baseRunMountPointPath) +Zone::Zone(const utils::Worker::Pointer& worker, + const std::string& zonesPath, + const std::string& zoneConfigPath, + const std::string& lxcTemplatePrefix, + const std::string& baseRunMountPointPath) + : mWorker(worker) { config::loadFromFile(zoneConfigPath, mConfig); @@ -92,19 +94,9 @@ Zone::~Zone() { // Make sure all OnNameLostCallbacks get finished and no new will // get called before proceeding further. This guarantees no race - // condition on the mReconnectThread. - { - Lock lock(mReconnectMutex); - disconnect(); - } - - if (mReconnectThread.joinable()) { - mReconnectThread.join(); - } - - if (mStartThread.joinable()) { - mStartThread.join(); - } + // condition on the reconnect thread. + Lock lock(mReconnectMutex); + disconnect(); } const std::vector& Zone::getPermittedToSend() const @@ -146,10 +138,6 @@ void Zone::start() void Zone::startAsync(const StartAsyncResultCallback& callback) { - if (mStartThread.joinable()) { - mStartThread.join(); - } - auto startWrapper = [this, callback]() { bool succeeded = false; @@ -165,7 +153,7 @@ void Zone::startAsync(const StartAsyncResultCallback& callback) } }; - mStartThread = std::thread(startWrapper); + mWorker->addTask(startWrapper); } void Zone::stop() @@ -300,10 +288,7 @@ void Zone::onNameLostCallback() { LOGI(getId() << ": A connection to the DBUS server has been lost, reconnecting..."); - if (mReconnectThread.joinable()) { - mReconnectThread.join(); - } - mReconnectThread = std::thread(std::bind(&Zone::reconnectHandler, this)); + mWorker->addTask(std::bind(&Zone::reconnectHandler, this)); } void Zone::reconnectHandler() diff --git a/server/zone.hpp b/server/zone.hpp index 5ee95af..1fdc588 100644 --- a/server/zone.hpp +++ b/server/zone.hpp @@ -30,6 +30,7 @@ #include "zone-admin.hpp" #include "zone-connection.hpp" #include "zone-connection-transport.hpp" +#include "utils/worker.hpp" #include #include @@ -50,10 +51,11 @@ public: * @param lxcTemplatePrefix directory where templates are stored * @param baseRunMountPointPath base directory for run mount point */ - Zone(const std::string& zonesPath, - const std::string& zoneConfigPath, - const std::string& lxcTemplatePrefix, - const std::string& baseRunMountPointPath); + Zone(const utils::Worker::Pointer& worker, + const std::string& zonesPath, + const std::string& zoneConfigPath, + const std::string& lxcTemplatePrefix, + const std::string& baseRunMountPointPath); Zone(Zone&&) = default; virtual ~Zone(); @@ -253,14 +255,13 @@ public: const std::string& target); private: + utils::Worker::Pointer mWorker; ZoneConfig mConfig; std::vector mPermittedToSend; std::vector mPermittedToRecv; std::unique_ptr mConnectionTransport; std::unique_ptr mAdmin; std::unique_ptr mConnection; - std::thread mReconnectThread; - std::thread mStartThread; mutable std::recursive_mutex mReconnectMutex; NotifyActiveZoneCallback mNotifyCallback; DisplayOffCallback mDisplayOffCallback; diff --git a/server/zones-manager.cpp b/server/zones-manager.cpp index 402fc7f..2ce03d7 100644 --- a/server/zones-manager.cpp +++ b/server/zones-manager.cpp @@ -74,7 +74,8 @@ const unsigned int ZONE_IP_BASE_THIRD_OCTET = 100; } // namespace -ZonesManager::ZonesManager(const std::string& managerConfigPath): mDetachOnExit(false) +ZonesManager::ZonesManager(const std::string& managerConfigPath) + : mWorker(utils::Worker::create()), mDetachOnExit(false) { LOGD("Instantiating ZonesManager object..."); @@ -168,32 +169,33 @@ void ZonesManager::createZone(const std::string& zoneConfig) std::string zoneConfigPath = utils::getAbsolutePath(zoneConfig, baseConfigPath); LOGT("Creating Zone " << zoneConfigPath); - std::unique_ptr c(new Zone(mConfig.zonesPath, - zoneConfigPath, - mConfig.lxcTemplatePrefix, - mConfig.runMountPointPrefix)); - const std::string id = c->getId(); + std::unique_ptr zone(new Zone(mWorker->createSubWorker(), + mConfig.zonesPath, + zoneConfigPath, + mConfig.lxcTemplatePrefix, + mConfig.runMountPointPrefix)); + const std::string id = zone->getId(); if (id == HOST_ID) { throw ZoneOperationException("Cannot use reserved zone ID"); } using namespace std::placeholders; - c->setNotifyActiveZoneCallback(bind(&ZonesManager::notifyActiveZoneHandler, - this, id, _1, _2)); + zone->setNotifyActiveZoneCallback(bind(&ZonesManager::notifyActiveZoneHandler, + this, id, _1, _2)); - c->setDisplayOffCallback(bind(&ZonesManager::displayOffHandler, - this, id)); + zone->setDisplayOffCallback(bind(&ZonesManager::displayOffHandler, + this, id)); - c->setFileMoveRequestCallback(bind(&ZonesManager::handleZoneMoveFileRequest, - this, id, _1, _2, _3)); + zone->setFileMoveRequestCallback(bind(&ZonesManager::handleZoneMoveFileRequest, + this, id, _1, _2, _3)); - c->setProxyCallCallback(bind(&ZonesManager::handleProxyCall, - this, id, _1, _2, _3, _4, _5, _6, _7)); + zone->setProxyCallCallback(bind(&ZonesManager::handleProxyCall, + this, id, _1, _2, _3, _4, _5, _6, _7)); - c->setDbusStateChangedCallback(bind(&ZonesManager::handleDbusStateChanged, - this, id, _1)); + zone->setDbusStateChangedCallback(bind(&ZonesManager::handleDbusStateChanged, + this, id, _1)); - mZones.insert(ZoneMap::value_type(id, std::move(c))); + mZones.insert(ZoneMap::value_type(id, std::move(zone))); // after zone is created successfully, put a file informing that zones are enabled if (mZones.size() == 1) { @@ -836,8 +838,7 @@ void ZonesManager::handleDestroyZoneCall(const std::string& id, result->setVoid(); }; - std::thread thread(destroyer); - thread.detach(); //TODO fix it + mWorker->addTask(destroyer); } void ZonesManager::handleLockZoneCall(const std::string& id, diff --git a/server/zones-manager.hpp b/server/zones-manager.hpp index 2fc0305..b58da86 100644 --- a/server/zones-manager.hpp +++ b/server/zones-manager.hpp @@ -31,6 +31,7 @@ #include "host-connection.hpp" #include "input-monitor.hpp" #include "proxy-call-policy.hpp" +#include "utils/worker.hpp" #include #include @@ -105,6 +106,7 @@ public: void setZonesDetachOnExit(); private: + utils::Worker::Pointer mWorker; ZonesManagerConfig mConfig; std::string mConfigPath; HostConnection mHostConnection; diff --git a/tests/unit_tests/server/ut-zone.cpp b/tests/unit_tests/server/ut-zone.cpp index 7d80d66..80e73da 100644 --- a/tests/unit_tests/server/ut-zone.cpp +++ b/tests/unit_tests/server/ut-zone.cpp @@ -63,10 +63,11 @@ struct Fixture { std::unique_ptr create(const std::string& configPath) { - return std::unique_ptr(new Zone(ZONES_PATH, - configPath, - LXC_TEMPLATES_PATH, - "")); + return std::unique_ptr(new Zone(utils::Worker::create(), + ZONES_PATH, + configPath, + LXC_TEMPLATES_PATH, + "")); } void ensureStarted() diff --git a/tests/unit_tests/utils/ut-counting-map.cpp b/tests/unit_tests/utils/ut-counting-map.cpp new file mode 100644 index 0000000..702470f --- /dev/null +++ b/tests/unit_tests/utils/ut-counting-map.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * + * Contact: Piotr Bartosiewicz + * + * 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 + * @author Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com) + * @brief Unit tests of counting map + */ + +#include "config.hpp" +#include "ut.hpp" + +#include "utils/counting-map.hpp" + +BOOST_AUTO_TEST_SUITE(CountingMapSuite) + +using namespace vasum::utils; + +BOOST_AUTO_TEST_CASE(CountingTest) +{ + CountingMap map; + + BOOST_CHECK(map.empty()); + BOOST_CHECK_EQUAL(0, map.get("ala")); + + BOOST_CHECK_EQUAL(1, map.increment("ala")); + BOOST_CHECK_EQUAL(1, map.increment("ma")); + + BOOST_CHECK(!map.empty()); + BOOST_CHECK_EQUAL(1, map.get("ala")); + BOOST_CHECK_EQUAL(1, map.get("ma")); + BOOST_CHECK_EQUAL(0, map.get("kota")); + + BOOST_CHECK_EQUAL(2, map.increment("ala")); + BOOST_CHECK_EQUAL(2, map.increment("ma")); + BOOST_CHECK_EQUAL(3, map.increment("ma")); + + BOOST_CHECK(!map.empty()); + BOOST_CHECK_EQUAL(2, map.get("ala")); + BOOST_CHECK_EQUAL(3, map.get("ma")); + BOOST_CHECK_EQUAL(0, map.get("kota")); + + BOOST_CHECK_EQUAL(1, map.decrement("ala")); + BOOST_CHECK_EQUAL(0, map.decrement("kota")); + + BOOST_CHECK(!map.empty()); + BOOST_CHECK_EQUAL(1, map.get("ala")); + BOOST_CHECK_EQUAL(3, map.get("ma")); + BOOST_CHECK_EQUAL(0, map.get("kota")); + + BOOST_CHECK_EQUAL(0, map.decrement("ala")); + + BOOST_CHECK(!map.empty()); + BOOST_CHECK_EQUAL(0, map.get("ala")); + BOOST_CHECK_EQUAL(3, map.get("ma")); + BOOST_CHECK_EQUAL(0, map.get("kota")); + + BOOST_CHECK_EQUAL(2, map.decrement("ma")); + BOOST_CHECK_EQUAL(1, map.decrement("ma")); + BOOST_CHECK_EQUAL(0, map.decrement("ma")); + + BOOST_CHECK(map.empty()); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/unit_tests/utils/ut-worker.cpp b/tests/unit_tests/utils/ut-worker.cpp new file mode 100644 index 0000000..280889b --- /dev/null +++ b/tests/unit_tests/utils/ut-worker.cpp @@ -0,0 +1,182 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * + * Contact: Piotr Bartosiewicz + * + * 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 + * @author Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com) + * @brief Unit tests of worker thread + */ + +#include "config.hpp" +#include "ut.hpp" + +#include "utils/worker.hpp" +#include "utils/latch.hpp" + +#include +#include +#include + +BOOST_AUTO_TEST_SUITE(WorkerSuite) + +using namespace vasum::utils; + +const int unsigned TIMEOUT = 1000; + +BOOST_AUTO_TEST_CASE(NoTasksTest) +{ + Worker::Pointer worker = Worker::create(); +} + +BOOST_AUTO_TEST_CASE(NoTasks2Test) +{ + Worker::Pointer worker = Worker::create(); + Worker::Pointer sub1 = worker->createSubWorker(); + Worker::Pointer sub2 = worker->createSubWorker(); + Worker::Pointer sub3 = sub1->createSubWorker(); + + sub1.reset(); + worker.reset(); +} + +BOOST_AUTO_TEST_CASE(SimpleTest) +{ + Latch done; + + Worker::Pointer worker = Worker::create(); + worker->addTask([&] { + done.set(); + }); + + BOOST_CHECK(done.wait(TIMEOUT)); +} + +BOOST_AUTO_TEST_CASE(QueueTest) +{ + std::mutex mutex; + std::string result; + + Worker::Pointer worker = Worker::create(); + + for (int n=0; n<10; ++n) { + worker->addTask([&, n]{ + std::lock_guard lock(mutex); + result += std::to_string(n); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + }); + } + + worker.reset(); + + std::lock_guard lock(mutex); + BOOST_CHECK_EQUAL("0123456789", result); +} + +BOOST_AUTO_TEST_CASE(ThreadResumeTest) +{ + Latch done; + + const auto task = [&] { + done.set(); + }; + + Worker::Pointer worker = Worker::create(); + + worker->addTask(task); + + BOOST_CHECK(done.wait(TIMEOUT)); + + // make sure worker thread is in waiting state + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + worker->addTask(task); + + worker.reset(); + + BOOST_CHECK(done.wait(TIMEOUT)); +} + +BOOST_AUTO_TEST_CASE(SubWorkerTest) +{ + std::mutex mutex; + std::string result; + + Worker::Pointer worker = Worker::create(); + Worker::Pointer sub1 = worker->createSubWorker(); + Worker::Pointer sub2 = worker->createSubWorker(); + + auto addTask = [&](Worker::Pointer w, const std::string& id) { + w->addTask([&, id]{ + std::lock_guard lock(mutex); + result += id; + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + }); + }; + + for (int n=0; n<4; ++n) { + addTask(worker, "_w" + std::to_string(n)); + addTask(sub1, "_a" + std::to_string(n)); + } + + worker.reset(); + sub1.reset(); + + { + std::lock_guard lock(mutex); + BOOST_CHECK_EQUAL("_w0_a0_w1_a1_w2_a2_w3_a3", result); + result.clear(); + } + + addTask(sub2, "_b0"); + addTask(sub2, "_b1"); + + sub2.reset(); + + { + std::lock_guard lock(mutex); + BOOST_CHECK_EQUAL("_b0_b1", result); + } +} + +BOOST_AUTO_TEST_CASE(NoCopyTest) +{ + typedef std::atomic_int Counter; + + struct Task { + Counter& count; + + Task(Counter& c) : count(c) {}; + Task(const Task& t) : count(t.count) {++count;} + Task(Task&& r) : count(r.count) {} + Task& operator=(const Task&) = delete; + Task& operator=(Task&&) = delete; + void operator() () {} + + }; + + Counter copyCount(0); + + Worker::Pointer worker = Worker::create(); + worker->addTask(Task(copyCount)); + worker.reset(); + + BOOST_CHECK_EQUAL(1, copyCount); // one copy for creating std::function +} + +BOOST_AUTO_TEST_SUITE_END() -- 2.7.4 From 6de4eeceb5f669eb2fe71cf0a3d5d755e2663f8d Mon Sep 17 00:00:00 2001 From: Dariusz Michaluk Date: Mon, 15 Dec 2014 12:36:25 +0100 Subject: [PATCH 07/16] Adding support for --vt option in lxc-templates [Bug/Feature] N/A [Cause] N/A [Solution] N/A [Verification] Build, install, run tests Change-Id: I11049b06922f763e168449d43cf6dd9527ea9324 Signed-off-by: Dariusz Michaluk --- server/configs/lxc-templates/template.sh | 3 ++- server/zone-admin.cpp | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/server/configs/lxc-templates/template.sh b/server/configs/lxc-templates/template.sh index c73bd01..e7d5533 100755 --- a/server/configs/lxc-templates/template.sh +++ b/server/configs/lxc-templates/template.sh @@ -2,7 +2,7 @@ echo LXC template, args: $@ -options=$(getopt -o p:n: -l rootfs:,path:,name:,ipv4:,ipv4-gateway: -- "$@") +options=$(getopt -o p:n: -l path:,rootfs:,name:,vt:,ipv4:,ipv4-gateway: -- "$@") if [ $? -ne 0 ]; then exit 1 fi @@ -14,6 +14,7 @@ do -p|--path) path=$2; shift 2;; --rootfs) rootfs=$2; shift 2;; -n|--name) name=$2; shift 2;; + --vt) vt=$2; shift 2;; --ipv4) ipv4=$2; shift 2;; --ipv4-gateway) ipv4_gateway=$2; shift 2;; --) shift 1; break ;; diff --git a/server/zone-admin.cpp b/server/zone-admin.cpp index 42c71ea..46c4974 100644 --- a/server/zone-admin.cpp +++ b/server/zone-admin.cpp @@ -71,6 +71,11 @@ ZoneAdmin::ZoneAdmin(const std::string& zonesPath, args.add("--ipv4"); args.add(config.ipv4.c_str()); } + const std::string vt = std::to_string(config.vt); + if (config.vt > 0) { + args.add("--vt"); + args.add(vt.c_str()); + } if (!mZone.create(lxcTemplate, args.c_array())) { throw ZoneOperationException("Could not create zone"); } -- 2.7.4 From 3bf358a390639906a64f6d9c1487c86be2962ba2 Mon Sep 17 00:00:00 2001 From: Dariusz Michaluk Date: Mon, 15 Dec 2014 13:13:52 +0100 Subject: [PATCH 08/16] Change zonesPath and ExecStart command in service [Bug/Feature] N/A [Cause] N/A [Solution] N/A [Verification] Build, install, run tests Change-Id: Ie51e3bd436f039f926c93b92ea5343e27e7314c0 Signed-off-by: Dariusz Michaluk --- CMakeLists.txt | 4 ++++ packaging/vasum.spec | 3 +++ server/configs/CMakeLists.txt | 5 ++++- server/configs/{daemon.conf => daemon.conf.in} | 4 ++-- server/configs/systemd/vasum.service.in | 2 +- 5 files changed, 14 insertions(+), 4 deletions(-) rename server/configs/{daemon.conf => daemon.conf.in} (84%) diff --git a/CMakeLists.txt b/CMakeLists.txt index cd999d7..a12367f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -152,6 +152,10 @@ IF(NOT DEFINED SYSTEMD_UNIT_DIR) SET(SYSTEMD_UNIT_DIR "${LIB_INSTALL_DIR}/systemd/system") ENDIF(NOT DEFINED SYSTEMD_UNIT_DIR) +IF(NOT DEFINED DATA_DIR) + SET(DATA_DIR "${CMAKE_INSTALL_FULL_DATAROOTDIR}") +ENDIF(NOT DEFINED DATA_DIR) + SET(VSM_CONFIG_INSTALL_DIR ${SYSCONF_INSTALL_DIR}/vasum) SET(VSM_DATA_INSTALL_DIR ${SHARE_INSTALL_PREFIX}/vasum) diff --git a/packaging/vasum.spec b/packaging/vasum.spec index 78db5de..887409e 100644 --- a/packaging/vasum.spec +++ b/packaging/vasum.spec @@ -51,6 +51,7 @@ between them. A process from inside a zone can request a switch of context %{_unitdir}/vasum.service %{_unitdir}/multi-user.target.wants/vasum.service /etc/dbus-1/system.d/org.tizen.vasum.host.conf +%dir %{_datadir}/.zones %prep %setup -q @@ -67,6 +68,7 @@ between them. A process from inside a zone can request a switch of context -DCMAKE_BUILD_TYPE=%{build_type} \ -DSCRIPT_INSTALL_DIR=%{script_dir} \ -DSYSTEMD_UNIT_DIR=%{_unitdir} \ + -DDATA_DIR=%{_datadir} \ -DPYTHON_SITELIB=%{python_sitelib} \ -DVASUM_USER=%{vsm_user} \ -DINPUT_EVENT_GROUP=%{input_event_group} \ @@ -78,6 +80,7 @@ make -k %{?jobs:-j%jobs} %make_install mkdir -p %{buildroot}/%{_unitdir}/multi-user.target.wants ln -s ../vasum.service %{buildroot}/%{_unitdir}/multi-user.target.wants/vasum.service +mkdir -p %{buildroot}/%{_datadir}/.zones %clean rm -rf %{buildroot} diff --git a/server/configs/CMakeLists.txt b/server/configs/CMakeLists.txt index b5c9286..9c0fa4b 100644 --- a/server/configs/CMakeLists.txt +++ b/server/configs/CMakeLists.txt @@ -29,7 +29,10 @@ CONFIGURE_FILE(systemd/vasum.service.in ## Install ##################################################################### -INSTALL(FILES daemon.conf +CONFIGURE_FILE(daemon.conf.in + ${CMAKE_BINARY_DIR}/daemon.conf) + +INSTALL(FILES ${CMAKE_BINARY_DIR}/daemon.conf DESTINATION ${VSM_CONFIG_INSTALL_DIR}) # preprocess d-bus configs diff --git a/server/configs/daemon.conf b/server/configs/daemon.conf.in similarity index 84% rename from server/configs/daemon.conf rename to server/configs/daemon.conf.in index 714640e..296c1c7 100644 --- a/server/configs/daemon.conf +++ b/server/configs/daemon.conf.in @@ -1,7 +1,7 @@ { "zoneConfigs" : ["zones/private.conf", "zones/business.conf"], - "zonesPath" : "/opt/usr/zones", - "zoneImagePath" : "/opt/usr/zones/img/system-data.img", + "zonesPath" : "${DATA_DIR}/.zones", + "zoneImagePath" : "${DATA_DIR}/.zones/img/system-data.img", "zoneTemplatePath" : "templates", "zoneNewConfigPrefix" : "/var/lib/vasum", "runMountPointPrefix" : "/var/run/zones", diff --git a/server/configs/systemd/vasum.service.in b/server/configs/systemd/vasum.service.in index f36622a..8131ed5 100644 --- a/server/configs/systemd/vasum.service.in +++ b/server/configs/systemd/vasum.service.in @@ -4,7 +4,7 @@ ConditionVirtualization=no [Service] Type=simple -ExecStart=${CMAKE_INSTALL_PREFIX}/bin/vasum-server +ExecStart=${CMAKE_INSTALL_PREFIX}/bin/vasum-server -r Restart=on-failure ExecReload=/bin/kill -HUP $MAINPID -- 2.7.4 From 112dc09c274e68b577e2c3d76e6ff0fcac3d3289 Mon Sep 17 00:00:00 2001 From: Dariusz Michaluk Date: Mon, 15 Dec 2014 12:55:16 +0100 Subject: [PATCH 09/16] Update tizen common (with wayland) lxc template [Bug/Feature] N/A [Cause] N/A [Solution] N/A [Verification] Build, install, run tests Change-Id: Ib7621c70a708fea479dc0d2e7d4bbc528728c504 Signed-off-by: Dariusz Michaluk --- .../configs/lxc-templates/tizen-common-wayland.sh | 23 +++++++++------------- server/configs/zones/business.conf | 2 +- server/configs/zones/private.conf | 2 +- 3 files changed, 11 insertions(+), 16 deletions(-) diff --git a/server/configs/lxc-templates/tizen-common-wayland.sh b/server/configs/lxc-templates/tizen-common-wayland.sh index ed2df9b..12fb223 100755 --- a/server/configs/lxc-templates/tizen-common-wayland.sh +++ b/server/configs/lxc-templates/tizen-common-wayland.sh @@ -23,14 +23,14 @@ usage() cat < - [-p|--path=] [-r|--rootfs=] [-v|--vt=] + [-p|--path=] [--rootfs=] [--vt=] [--ipv4=] [--ipv4-gateway=] [-h|--help] Mandatory args: -n,--name zone name Optional args: - -p,--path path to zone config files, defaults to /var/lib/lxc - --rootfs path to zone rootfs, defaults to /var/lib/lxc/[NAME]/rootfs - -v,--vt zone virtual terminal + -p,--path path to zone config files + --rootfs path to zone rootfs + --vt zone virtual terminal --ipv4 zone IP address --ipv4-gateway zone gateway -h,--help print help @@ -38,7 +38,7 @@ EOF return 0 } -options=$(getopt -o hp:v:n: -l help,rootfs:,path:,vt:,name:,ipv4:,ipv4-gateway: -- "$@") +options=$(getopt -o hp:n: -l help,rootfs:,path:,vt:,name:,ipv4:,ipv4-gateway: -- "$@") if [ $? -ne 0 ]; then usage $(basename $0) exit 1 @@ -51,7 +51,7 @@ do -h|--help) usage $0 && exit 0;; --rootfs) rootfs=$2; shift 2;; -p|--path) path=$2; shift 2;; - -v|--vt) vt=$2; shift 2;; + --vt) vt=$2; shift 2;; -n|--name) name=$2; shift 2;; --ipv4) ipv4=$2; shift 2;; --ipv4-gateway) ipv4_gateway=$2; shift 2;; @@ -94,14 +94,12 @@ ${rootfs}/opt \ ${rootfs}/proc \ ${rootfs}/root \ ${rootfs}/run \ -${rootfs}/run/dbus \ -${rootfs}/run/memory \ -${rootfs}/run/systemd \ -${rootfs}/run/udev \ ${rootfs}/sbin \ ${rootfs}/sys \ ${rootfs}/tmp \ ${rootfs}/usr \ +${rootfs}/var \ +${rootfs}/var/run \ ${path}/hooks \ ${path}/scripts \ ${path}/systemd \ @@ -389,10 +387,7 @@ ${path}/systemd/user etc/systemd/user none ro,bind 0 0 /opt opt none rw,rbind 0 0 devtmpfs dev devtmpfs rw,relatime,mode=755 0 0 devpts dev/pts devpts rw,relatime,gid=5,mode=620,ptmxmode=000 0 0 -/run/dbus run/dbus none rw,bind 0 0 -/run/memory run/memory none rw,bind 0 0 -/run/systemd run/systemd none rw,bind 0 0 -/run/udev run/udev none rw,bind 0 0 /sys/fs/smackfs sys/fs/smackfs none rw,bind 0 0 +/var/run/zones/${name}/run var/run none rw,bind 0 0 #tmpfs run tmpfs rw,nosuid,nodev,mode=755 0 0 EOF diff --git a/server/configs/zones/business.conf b/server/configs/zones/business.conf index b211ce3..6a4bc50 100644 --- a/server/configs/zones/business.conf +++ b/server/configs/zones/business.conf @@ -1,6 +1,6 @@ { "name" : "business", - "lxcTemplate" : "template.sh", + "lxcTemplate" : "tizen-common-wayland.sh", "initWithArgs" : [], "ipv4Gateway" : "10.0.102.1", "ipv4" : "10.0.102.2", diff --git a/server/configs/zones/private.conf b/server/configs/zones/private.conf index b4fb56a..2a1147f 100644 --- a/server/configs/zones/private.conf +++ b/server/configs/zones/private.conf @@ -1,6 +1,6 @@ { "name" : "private", - "lxcTemplate" : "template.sh", + "lxcTemplate" : "tizen-common-wayland.sh", "initWithArgs" : [], "ipv4Gateway" : "10.0.101.1", "ipv4" : "10.0.101.2", -- 2.7.4 From 21d934cb08890c90816f9f94cbdc096599c548e2 Mon Sep 17 00:00:00 2001 From: Mateusz Malicki Date: Tue, 16 Dec 2014 11:40:03 +0100 Subject: [PATCH 10/16] Some cppcheck fixes [Bug] Some cppcheck fixes (const, uniused variable) [Cause] N/A [Solution] N/A [Verification] Build, install, run cppcheck Change-Id: I58fa2e3434c6a7546441ba310bb437b59c7fd6de --- client/vasum-client-impl.cpp | 4 ++-- client/vasum-client-impl.hpp | 4 ++-- server/input-monitor.cpp | 2 +- server/input-monitor.hpp | 2 +- server/proxy-call-policy.cpp | 2 +- server/proxy-call-policy.hpp | 2 +- server/zone-connection-transport.cpp | 2 +- server/zone-connection-transport.hpp | 2 +- server/zone.cpp | 2 +- server/zone.hpp | 2 +- server/zones-manager.cpp | 6 +++--- server/zones-manager.hpp | 6 +++--- tests/unit_tests/config/ut-configuration.cpp | 2 +- zone-daemon/main.cpp | 2 -- 14 files changed, 19 insertions(+), 21 deletions(-) diff --git a/client/vasum-client-impl.cpp b/client/vasum-client-impl.cpp index 3c9448b..23270e4 100644 --- a/client/vasum-client-impl.cpp +++ b/client/vasum-client-impl.cpp @@ -307,12 +307,12 @@ VsmStatus Client::signalUnsubscribe(VsmSubscriptionId id) return vsm_get_status(); } -const char* Client::vsm_get_status_message() noexcept +const char* Client::vsm_get_status_message() const noexcept { return mStatus.mMsg.c_str(); } -VsmStatus Client::vsm_get_status() noexcept +VsmStatus Client::vsm_get_status() const noexcept { return mStatus.mVsmStatus; } diff --git a/client/vasum-client-impl.hpp b/client/vasum-client-impl.hpp index 18eb726..66ffa82 100644 --- a/client/vasum-client-impl.hpp +++ b/client/vasum-client-impl.hpp @@ -96,12 +96,12 @@ public: /** * @see ::vsm_get_status_message */ - const char* vsm_get_status_message() noexcept; + const char* vsm_get_status_message() const noexcept; /** * @see ::vsm_get_status */ - VsmStatus vsm_get_status() noexcept; + VsmStatus vsm_get_status() const noexcept; /** * @see ::vsm_get_zone_dbuses diff --git a/server/input-monitor.cpp b/server/input-monitor.cpp index 8d641b1..7937840 100644 --- a/server/input-monitor.cpp +++ b/server/input-monitor.cpp @@ -148,7 +148,7 @@ bool isDeviceWithName(const boost::regex& deviceNameRegex, } } // namespace -std::string InputMonitor::getDevicePath() +std::string InputMonitor::getDevicePath() const { std::string device = mConfig.device; if (fs::path(device).is_absolute() diff --git a/server/input-monitor.hpp b/server/input-monitor.hpp index 93c2d75..2b0ed45 100644 --- a/server/input-monitor.hpp +++ b/server/input-monitor.hpp @@ -56,7 +56,7 @@ private: std::list mEventTimes; GIOChannel* mChannelPtr; - std::string getDevicePath(); + std::string getDevicePath() const; void createGIOChannel(const std::string& devicePath); // Internal callback to be registered at glib g_io_add_watch() diff --git a/server/proxy-call-policy.cpp b/server/proxy-call-policy.cpp index 281cb3a..9fed52b 100644 --- a/server/proxy-call-policy.cpp +++ b/server/proxy-call-policy.cpp @@ -51,7 +51,7 @@ bool ProxyCallPolicy::isProxyCallAllowed(const std::string& caller, const std::string& targetBusName, const std::string& targetObjectPath, const std::string& targetInterface, - const std::string& targetMethod) + const std::string& targetMethod) const { for (const ProxyCallRule& rule : mProxyCallRules) { if (match(rule.caller, caller) diff --git a/server/proxy-call-policy.hpp b/server/proxy-call-policy.hpp index 4176979..d6fbaed 100644 --- a/server/proxy-call-policy.hpp +++ b/server/proxy-call-policy.hpp @@ -44,7 +44,7 @@ public: const std::string& targetBusName, const std::string& targetObjectPath, const std::string& targetInterface, - const std::string& targetMethod); + const std::string& targetMethod) const; private: std::vector mProxyCallRules; diff --git a/server/zone-connection-transport.cpp b/server/zone-connection-transport.cpp index d209705..0757241 100644 --- a/server/zone-connection-transport.cpp +++ b/server/zone-connection-transport.cpp @@ -100,7 +100,7 @@ ZoneConnectionTransport::~ZoneConnectionTransport() } -std::string ZoneConnectionTransport::acquireAddress() +std::string ZoneConnectionTransport::acquireAddress() const { if (mRunMountPoint.empty()) { return std::string(); diff --git a/server/zone-connection-transport.hpp b/server/zone-connection-transport.hpp index 5729389..d651f3c 100644 --- a/server/zone-connection-transport.hpp +++ b/server/zone-connection-transport.hpp @@ -44,7 +44,7 @@ public: /** * Gets dbus addres. Will block until address is available. */ - std::string acquireAddress(); + std::string acquireAddress() const; /** * Set whether object should detach from transport filesystem on exit diff --git a/server/zone.cpp b/server/zone.cpp index 5f6dce0..199ecff 100644 --- a/server/zone.cpp +++ b/server/zone.cpp @@ -200,7 +200,7 @@ void Zone::disconnect() } } -std::string Zone::getDbusAddress() +std::string Zone::getDbusAddress() const { Lock lock(mReconnectMutex); return mDbusAddress; diff --git a/server/zone.hpp b/server/zone.hpp index 1fdc588..fba9399 100644 --- a/server/zone.hpp +++ b/server/zone.hpp @@ -226,7 +226,7 @@ public: /** * Get a dbus address */ - std::string getDbusAddress(); + std::string getDbusAddress() const; /** * Get id of VT diff --git a/server/zones-manager.cpp b/server/zones-manager.cpp index 2ce03d7..a1dcc71 100644 --- a/server/zones-manager.cpp +++ b/server/zones-manager.cpp @@ -305,7 +305,7 @@ bool ZonesManager::isRunning(const std::string& zoneId) return iter->second->isRunning(); } -std::string ZonesManager::getRunningForegroundZoneId() +std::string ZonesManager::getRunningForegroundZoneId() const { for (auto& zone : mZones) { if (zone.first == mConfig.foregroundId && @@ -523,7 +523,7 @@ void ZonesManager::handleProxyCall(const std::string& caller, asyncResultCallback); } -void ZonesManager::handleGetZoneDbuses(dbus::MethodResultBuilder::Pointer result) +void ZonesManager::handleGetZoneDbuses(dbus::MethodResultBuilder::Pointer result) const { std::vector entries; for (auto& zone : mZones) { @@ -542,7 +542,7 @@ void ZonesManager::handleDbusStateChanged(const std::string& zoneId, mHostConnection.signalZoneDbusState(zoneId, dbusAddress); } -void ZonesManager::handleGetZoneIdsCall(dbus::MethodResultBuilder::Pointer result) +void ZonesManager::handleGetZoneIdsCall(dbus::MethodResultBuilder::Pointer result) const { std::vector zoneIds; for(auto& zone: mZones){ diff --git a/server/zones-manager.hpp b/server/zones-manager.hpp index b58da86..41a3e7c 100644 --- a/server/zones-manager.hpp +++ b/server/zones-manager.hpp @@ -92,7 +92,7 @@ public: /** * @return id of the currently focused/foreground zone */ - std::string getRunningForegroundZoneId(); + std::string getRunningForegroundZoneId() const; /** * @return id of next to currently focused/foreground zone. If currently focused zone @@ -138,9 +138,9 @@ private: const std::string& targetMethod, GVariant* parameters, dbus::MethodResultBuilder::Pointer result); - void handleGetZoneDbuses(dbus::MethodResultBuilder::Pointer result); + void handleGetZoneDbuses(dbus::MethodResultBuilder::Pointer result) const; void handleDbusStateChanged(const std::string& zoneId, const std::string& dbusAddress); - void handleGetZoneIdsCall(dbus::MethodResultBuilder::Pointer result); + void handleGetZoneIdsCall(dbus::MethodResultBuilder::Pointer result) const; void handleGetActiveZoneIdCall(dbus::MethodResultBuilder::Pointer result); void handleGetZoneInfoCall(const std::string& id, dbus::MethodResultBuilder::Pointer result); void handleDeclareFileCall(const std::string& zone, diff --git a/tests/unit_tests/config/ut-configuration.cpp b/tests/unit_tests/config/ut-configuration.cpp index e46bfd8..4e6bedc 100644 --- a/tests/unit_tests/config/ut-configuration.cpp +++ b/tests/unit_tests/config/ut-configuration.cpp @@ -50,7 +50,7 @@ struct TestConfig { ( intVal ) - SubSubConfig() : moved(false) {} + SubSubConfig() : intVal(), moved(false) {} SubSubConfig(const SubSubConfig& config) : intVal(config.intVal), moved(false) {} SubSubConfig(SubSubConfig&& config) : intVal(std::move(config.intVal)), moved(false) { config.moved = true; diff --git a/zone-daemon/main.cpp b/zone-daemon/main.cpp index 846f431..f0c00c6 100644 --- a/zone-daemon/main.cpp +++ b/zone-daemon/main.cpp @@ -57,8 +57,6 @@ const std::string PROGRAM_NAME_AND_VERSION = int main(int argc, char* argv[]) { - std::string configPath ; - try { po::options_description desc("Allowed options"); -- 2.7.4 From 9d21e0db847cebff18406f16f5d3234ec0d9e0ec Mon Sep 17 00:00:00 2001 From: Piotr Bartosiewicz Date: Wed, 17 Dec 2014 13:41:13 +0100 Subject: [PATCH 11/16] Fix synchronization issues [Bug/Feature] Missing mutex [Cause] N/A [Solution] N/A [Verification] Build, install, run tests Change-Id: Iec7e016b62a0f3a671fd4843957e1bc6a265f84e --- server/zones-manager.cpp | 61 +++++++++++++++++++++++++++++++++++++++++++++++- server/zones-manager.hpp | 4 ++++ 2 files changed, 64 insertions(+), 1 deletion(-) diff --git a/server/zones-manager.cpp b/server/zones-manager.cpp index a1dcc71..a0cb274 100644 --- a/server/zones-manager.cpp +++ b/server/zones-manager.cpp @@ -195,6 +195,8 @@ void ZonesManager::createZone(const std::string& zoneConfig) zone->setDbusStateChangedCallback(bind(&ZonesManager::handleDbusStateChanged, this, id, _1)); + Lock lock(mMutex); + mZones.insert(ZoneMap::value_type(id, std::move(zone))); // after zone is created successfully, put a file informing that zones are enabled @@ -208,7 +210,8 @@ void ZonesManager::createZone(const std::string& zoneConfig) void ZonesManager::destroyZone(const std::string& zoneId) { - // TODO mutex for mZones access + Lock lock(mMutex); + auto it = mZones.find(zoneId); if (it == mZones.end()) { LOGE("Failed to destroy zone " << zoneId << ": no such zone"); @@ -228,6 +231,8 @@ void ZonesManager::destroyZone(const std::string& zoneId) void ZonesManager::focus(const std::string& zoneId) { + Lock lock(mMutex); + /* try to access the object first to throw immediately if it doesn't exist */ ZoneMap::mapped_type& foregroundZone = mZones.at(zoneId); @@ -249,6 +254,8 @@ void ZonesManager::startAll() { LOGI("Starting all zones"); + Lock lock(mMutex); + bool isForegroundFound = false; for (auto& zone : mZones) { @@ -279,6 +286,8 @@ void ZonesManager::stopAll() { LOGI("Stopping all zones"); + Lock lock(mMutex); + for (auto& zone : mZones) { zone.second->stop(); } @@ -286,6 +295,8 @@ void ZonesManager::stopAll() bool ZonesManager::isPaused(const std::string& zoneId) { + Lock lock(mMutex); + auto iter = mZones.find(zoneId); if (iter == mZones.end()) { LOGE("No such zone id: " << zoneId); @@ -297,6 +308,8 @@ bool ZonesManager::isPaused(const std::string& zoneId) bool ZonesManager::isRunning(const std::string& zoneId) { + Lock lock(mMutex); + auto iter = mZones.find(zoneId); if (iter == mZones.end()) { LOGE("No such zone id: " << zoneId); @@ -307,6 +320,8 @@ bool ZonesManager::isRunning(const std::string& zoneId) std::string ZonesManager::getRunningForegroundZoneId() const { + Lock lock(mMutex); + for (auto& zone : mZones) { if (zone.first == mConfig.foregroundId && zone.second->isRunning()) { @@ -318,6 +333,8 @@ std::string ZonesManager::getRunningForegroundZoneId() const std::string ZonesManager::getNextToForegroundZoneId() { + Lock lock(mMutex); + // handles case where there is no next zone if (mZones.size() < 2) { return std::string(); @@ -349,6 +366,8 @@ void ZonesManager::switchingSequenceMonitorNotify() void ZonesManager::setZonesDetachOnExit() { + Lock lock(mMutex); + mDetachOnExit = true; for (auto& zone : mZones) { @@ -362,6 +381,9 @@ void ZonesManager::notifyActiveZoneHandler(const std::string& caller, { LOGI("notifyActiveZoneHandler(" << caller << ", " << application << ", " << message << ") called"); + + Lock lock(mMutex); + try { const std::string activeZone = getRunningForegroundZoneId(); if (!activeZone.empty() && caller != activeZone) { @@ -375,6 +397,8 @@ void ZonesManager::notifyActiveZoneHandler(const std::string& caller, void ZonesManager::displayOffHandler(const std::string& /*caller*/) { // get config of currently set zone and switch if switchToDefaultAfterTimeout is true + Lock lock(mMutex); + const std::string activeZoneName = getRunningForegroundZoneId(); const auto& activeZone = mZones.find(activeZoneName); @@ -414,6 +438,8 @@ void ZonesManager::handleZoneMoveFileRequest(const std::string& srcZoneId, << "dst: " << dstZoneId << "\n" << "path: " << path); + Lock lock(mMutex); + ZoneMap::const_iterator srcIter = mZones.find(srcZoneId); if (srcIter == mZones.end()) { LOGE("Source zone '" << srcZoneId << "' not found"); @@ -507,6 +533,8 @@ void ZonesManager::handleProxyCall(const std::string& caller, return; } + Lock lock(mMutex); + ZoneMap::const_iterator targetIter = mZones.find(target); if (targetIter == mZones.end()) { LOGE("Target zone '" << target << "' not found"); @@ -525,6 +553,8 @@ void ZonesManager::handleProxyCall(const std::string& caller, void ZonesManager::handleGetZoneDbuses(dbus::MethodResultBuilder::Pointer result) const { + Lock lock(mMutex); + std::vector entries; for (auto& zone : mZones) { GVariant* zoneId = g_variant_new_string(zone.first.c_str()); @@ -544,6 +574,8 @@ void ZonesManager::handleDbusStateChanged(const std::string& zoneId, void ZonesManager::handleGetZoneIdsCall(dbus::MethodResultBuilder::Pointer result) const { + Lock lock(mMutex); + std::vector zoneIds; for(auto& zone: mZones){ zoneIds.push_back(g_variant_new_string(zone.first.c_str())); @@ -558,6 +590,9 @@ void ZonesManager::handleGetZoneIdsCall(dbus::MethodResultBuilder::Pointer resul void ZonesManager::handleGetActiveZoneIdCall(dbus::MethodResultBuilder::Pointer result) { LOGI("GetActiveZoneId call"); + + Lock lock(mMutex); + if (!mConfig.foregroundId.empty() && mZones[mConfig.foregroundId]->isRunning()){ result->set(g_variant_new("(s)", mConfig.foregroundId.c_str())); } else { @@ -569,6 +604,9 @@ void ZonesManager::handleGetZoneInfoCall(const std::string& id, dbus::MethodResultBuilder::Pointer result) { LOGI("GetZoneInfo call"); + + Lock lock(mMutex); + if (mZones.count(id) == 0) { LOGE("No zone with id=" << id); result->setError(api::ERROR_INVALID_ID, "No such zone id"); @@ -607,7 +645,10 @@ void ZonesManager::handleDeclareFileCall(const std::string& zone, dbus::MethodResultBuilder::Pointer result) { LOGI("DeclareFile call"); + try { + Lock lock(mMutex); + mZones.at(zone)->declareFile(type, path, flags, mode); result->setVoid(); } catch (const std::out_of_range&) { @@ -628,7 +669,10 @@ void ZonesManager::handleDeclareMountCall(const std::string& source, dbus::MethodResultBuilder::Pointer result) { LOGI("DeclareMount call"); + try { + Lock lock(mMutex); + mZones.at(zone)->declareMount(source, target, type, flags, data); result->setVoid(); } catch (const std::out_of_range&) { @@ -647,6 +691,8 @@ void ZonesManager::handleDeclareLinkCall(const std::string& source, { LOGI("DeclareLink call"); try { + Lock lock(mMutex); + mZones.at(zone)->declareLink(source, target); result->setVoid(); } catch (const std::out_of_range&) { @@ -662,6 +708,9 @@ void ZonesManager::handleSetActiveZoneCall(const std::string& id, dbus::MethodResultBuilder::Pointer result) { LOGI("SetActiveZone call; Id=" << id ); + + Lock lock(mMutex); + auto zone = mZones.find(id); if (zone == mZones.end()){ LOGE("No zone with id=" << id ); @@ -737,6 +786,8 @@ void ZonesManager::handleCreateZoneCall(const std::string& id, LOGI("Creating zone " << id); + Lock lock(mMutex); + // TODO: This solution is temporary. It utilizes direct access to config files when creating new // zones. Update this handler when config database will appear. namespace fs = boost::filesystem; @@ -819,6 +870,8 @@ void ZonesManager::handleCreateZoneCall(const std::string& id, void ZonesManager::handleDestroyZoneCall(const std::string& id, dbus::MethodResultBuilder::Pointer result) { + Lock lock(mMutex); + if (mZones.find(id) == mZones.end()) { LOGE("Failed to destroy zone - no such zone id: " << id); result->setError(api::ERROR_INVALID_ID, "No such zone id"); @@ -845,6 +898,9 @@ void ZonesManager::handleLockZoneCall(const std::string& id, dbus::MethodResultBuilder::Pointer result) { LOGI("LockZone call; Id=" << id ); + + Lock lock(mMutex); + auto iter = mZones.find(id); if (iter == mZones.end()) { LOGE("Failed to lock zone - no such zone id: " << id); @@ -875,6 +931,9 @@ void ZonesManager::handleUnlockZoneCall(const std::string& id, dbus::MethodResultBuilder::Pointer result) { LOGI("UnlockZone call; Id=" << id ); + + Lock lock(mMutex); + auto iter = mZones.find(id); if (iter == mZones.end()) { LOGE("Failed to unlock zone - no such zone id: " << id); diff --git a/server/zones-manager.hpp b/server/zones-manager.hpp index 41a3e7c..2ed5312 100644 --- a/server/zones-manager.hpp +++ b/server/zones-manager.hpp @@ -106,7 +106,11 @@ public: void setZonesDetachOnExit(); private: + typedef std::recursive_mutex Mutex; + typedef std::unique_lock Lock; + utils::Worker::Pointer mWorker; + mutable Mutex mMutex; // used to protect mZones ZonesManagerConfig mConfig; std::string mConfigPath; HostConnection mHostConnection; -- 2.7.4 From 01d4faf05df4e0be4b5daa7e9468583bc2992c06 Mon Sep 17 00:00:00 2001 From: Dariusz Michaluk Date: Wed, 17 Dec 2014 15:46:59 +0100 Subject: [PATCH 12/16] Rearrange create zone process [Bug/Feature] Remove start functionality, add VT support. [Cause] N/A [Solution] N/A [Verification] Build, install, run tests Change-Id: I67a7eebf6e45bb2cab29e833fbf53c1bdaeb88fb Signed-off-by: Dariusz Michaluk --- server/configs/daemon.conf.in | 2 +- server/configs/templates/template.conf | 4 ++-- server/zones-manager.cpp | 20 +++++++++----------- tests/unit_tests/server/ut-zones-manager.cpp | 6 +++--- 4 files changed, 15 insertions(+), 17 deletions(-) diff --git a/server/configs/daemon.conf.in b/server/configs/daemon.conf.in index 296c1c7..648d986 100644 --- a/server/configs/daemon.conf.in +++ b/server/configs/daemon.conf.in @@ -1,7 +1,7 @@ { "zoneConfigs" : ["zones/private.conf", "zones/business.conf"], "zonesPath" : "${DATA_DIR}/.zones", - "zoneImagePath" : "${DATA_DIR}/.zones/img/system-data.img", + "zoneImagePath" : "", "zoneTemplatePath" : "templates", "zoneNewConfigPrefix" : "/var/lib/vasum", "runMountPointPrefix" : "/var/run/zones", diff --git a/server/configs/templates/template.conf b/server/configs/templates/template.conf index 1939d51..fa6ef5c 100644 --- a/server/configs/templates/template.conf +++ b/server/configs/templates/template.conf @@ -1,13 +1,13 @@ { "name" : "~NAME~", - "lxcTemplate" : "template.sh", + "lxcTemplate" : "tizen-common-wayland.sh", "initWithArgs" : [], "ipv4Gateway" : "10.0.~IP~.1", "ipv4" : "10.0.~IP~.2", "cpuQuotaForeground" : -1, "cpuQuotaBackground" : 1000, "privilege" : 10, - "vt" : -1, + "vt" : ~VT~, "switchToDefaultAfterTimeout" : true, "enableDbusIntegration" : true, "runMountPoint" : "~NAME~/run", diff --git a/server/zones-manager.cpp b/server/zones-manager.cpp index a0cb274..eafbe4b 100644 --- a/server/zones-manager.cpp +++ b/server/zones-manager.cpp @@ -69,8 +69,10 @@ const std::string ENABLED_FILE_NAME = "enabled"; const boost::regex ZONE_NAME_REGEX("~NAME~"); const boost::regex ZONE_IP_THIRD_OCTET_REGEX("~IP~"); +const boost::regex ZONE_VT_REGEX("~VT~"); const unsigned int ZONE_IP_BASE_THIRD_OCTET = 100; +const unsigned int ZONE_VT_BASE = 1; } // namespace @@ -764,6 +766,12 @@ void ZonesManager::generateNewConfig(const std::string& id, LOGD("IP third octet: " << thirdOctetStr); resultConfig = boost::regex_replace(resultConfig, ZONE_IP_THIRD_OCTET_REGEX, thirdOctetStr); + // generate first free VT number + // TODO change algorithm after implementing removeZone + std::string freeVT = std::to_string(ZONE_VT_BASE + mZones.size() + 1); + LOGD("VT number: " << freeVT); + resultConfig = boost::regex_replace(resultConfig, ZONE_VT_REGEX, freeVT); + if (!utils::saveFileContent(resultPath, resultConfig)) { LOGE("Faield to save new config file."); throw ZoneOperationException("Failed to save new config file."); @@ -854,17 +862,7 @@ void ZonesManager::handleCreateZoneCall(const std::string& id, return; } - auto resultCallback = [this, id, result](bool succeeded) { - if (succeeded) { - focus(id); - result->setVoid(); - } else { - LOGE("Failed to start zone."); - // TODO removeZone - result->setError(api::ERROR_INTERNAL, "Failed to start zone"); - } - }; - mZones[id]->startAsync(resultCallback); + result->setVoid(); } void ZonesManager::handleDestroyZoneCall(const std::string& id, diff --git a/tests/unit_tests/server/ut-zones-manager.cpp b/tests/unit_tests/server/ut-zones-manager.cpp index 07c03dd..4db9434 100644 --- a/tests/unit_tests/server/ut-zones-manager.cpp +++ b/tests/unit_tests/server/ut-zones-manager.cpp @@ -1065,16 +1065,16 @@ BOOST_AUTO_TEST_CASE(CreateDestroyZoneTest) dbus.callAsyncMethodCreateZone(zone1, resultCallback); BOOST_REQUIRE(callDone.wait(EVENT_TIMEOUT)); - BOOST_CHECK_EQUAL(cm.getRunningForegroundZoneId(), zone1); - // create zone2 dbus.callAsyncMethodCreateZone(zone2, resultCallback); BOOST_REQUIRE(callDone.wait(EVENT_TIMEOUT)); - BOOST_CHECK_EQUAL(cm.getRunningForegroundZoneId(), zone2); //TODO is this valid? // create zone3 dbus.callAsyncMethodCreateZone(zone3, resultCallback); BOOST_REQUIRE(callDone.wait(EVENT_TIMEOUT)); + + cm.startAll(); + BOOST_CHECK_EQUAL(cm.getRunningForegroundZoneId(), zone3); // destroy zone2 -- 2.7.4 From fa7b65cc7179ef7a037f5d3114fe501147e0e72b Mon Sep 17 00:00:00 2001 From: Mateusz Malicki Date: Tue, 9 Dec 2014 15:56:40 +0100 Subject: [PATCH 13/16] Taking into account the provision configuration [Feature] Taking into account the provision configuration [Cause] N/A [Solution] N/A [Verification] Build, install, run tests Change-Id: I2b564bb3f9af2514781ff2233cabb7b1ca654e69 --- client/vasum-client-impl.cpp | 4 +- common/base-exception.cpp | 41 ++++ common/base-exception.hpp | 5 + common/utils/fs.cpp | 182 ++++++++++++++-- common/utils/fs.hpp | 33 +++ server/CMakeLists.txt | 1 + server/configs/templates/template.conf | 6 +- server/configs/zones/business.conf | 6 +- server/configs/zones/private.conf | 6 +- server/zone-config.hpp | 8 +- ...ioning-config.hpp => zone-provision-config.hpp} | 0 server/zone-provision.cpp | 214 ++++++++++++++++++ server/zone-provision.hpp | 95 ++++++++ server/zone.cpp | 57 ++--- server/zone.hpp | 8 +- server/zones-manager.cpp | 5 +- .../configs/ut-client/zones/console1-dbus.conf.in | 3 +- .../configs/ut-client/zones/console2-dbus.conf.in | 3 +- .../configs/ut-client/zones/console3-dbus.conf.in | 3 +- .../server/configs/ut-server/zones/zone1.conf | 3 +- .../server/configs/ut-server/zones/zone2.conf | 3 +- .../server/configs/ut-server/zones/zone3.conf | 3 +- .../server/configs/ut-zone-admin/zones/buggy.conf | 3 +- .../configs/ut-zone-admin/zones/missing.conf | 3 +- .../ut-zone-admin/zones/test-no-shutdown.conf | 3 +- .../server/configs/ut-zone-admin/zones/test.conf | 3 +- .../server/configs/ut-zone/zones/buggy.conf | 3 +- .../server/configs/ut-zone/zones/test-dbus.conf.in | 3 +- .../server/configs/ut-zone/zones/test.conf | 3 +- .../ut-zones-manager/templates/template.conf.in | 3 +- .../ut-zones-manager/zones/console1-dbus.conf.in | 3 +- .../configs/ut-zones-manager/zones/console1.conf | 3 +- .../ut-zones-manager/zones/console2-dbus.conf.in | 3 +- .../configs/ut-zones-manager/zones/console2.conf | 3 +- .../ut-zones-manager/zones/console3-dbus.conf.in | 3 +- .../configs/ut-zones-manager/zones/console3.conf | 3 +- tests/unit_tests/server/ut-zone-provision.cpp | 242 +++++++++++++++++++++ tests/unit_tests/server/ut-zones-manager.cpp | 151 ------------- zone-daemon/CMakeLists.txt | 3 +- 39 files changed, 894 insertions(+), 233 deletions(-) create mode 100644 common/base-exception.cpp rename server/{provisioning-config.hpp => zone-provision-config.hpp} (100%) create mode 100644 server/zone-provision.cpp create mode 100644 server/zone-provision.hpp create mode 100644 tests/unit_tests/server/ut-zone-provision.cpp diff --git a/client/vasum-client-impl.cpp b/client/vasum-client-impl.cpp index 23270e4..2aa923d 100644 --- a/client/vasum-client-impl.cpp +++ b/client/vasum-client-impl.cpp @@ -322,7 +322,7 @@ VsmStatus Client::vsm_get_zone_dbuses(VsmArrayString* keys, VsmArrayString* valu assert(keys); assert(values); - GVariant* out; + GVariant* out = NULL; VsmStatus ret = callMethod(HOST_INTERFACE, api::host::METHOD_GET_ZONE_DBUSES, NULL, @@ -343,7 +343,7 @@ VsmStatus Client::vsm_get_zone_ids(VsmArrayString* array) noexcept { assert(array); - GVariant* out; + GVariant* out = NULL; VsmStatus ret = callMethod(HOST_INTERFACE, api::host::METHOD_GET_ZONE_ID_LIST, NULL, diff --git a/common/base-exception.cpp b/common/base-exception.cpp new file mode 100644 index 0000000..bcfe600 --- /dev/null +++ b/common/base-exception.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * + * Contact: Mateusz Malicki + * + * 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 + * @author Mateusz Malicki (m.malicki2@samsung.com) + * @brief Vasum base exception implementation + */ + +#include "base-exception.hpp" + +#include +#include +#include + +namespace vasum { + +const int ERROR_MESSAGE_BUFFER_CAPACITY = 256; + +std::string getSystemErrorMessage() +{ + char buf[ERROR_MESSAGE_BUFFER_CAPACITY]; + return strerror_r(errno, buf, sizeof(buf)); +} + +} // namespace vasum diff --git a/common/base-exception.hpp b/common/base-exception.hpp index d501e3d..3969404 100644 --- a/common/base-exception.hpp +++ b/common/base-exception.hpp @@ -41,6 +41,11 @@ struct VasumException: public std::runtime_error { VasumException(const std::string& error = "") : std::runtime_error(error) {} }; +/** + * Return string describing error number + * it is wrapper for strerror_r + */ +std::string getSystemErrorMessage(); } // namespace vasum diff --git a/common/utils/fs.cpp b/common/utils/fs.cpp index 3330cbf..763bdfe 100644 --- a/common/utils/fs.cpp +++ b/common/utils/fs.cpp @@ -38,10 +38,13 @@ #include #include #include +#include #include +namespace fs = boost::filesystem; + namespace vasum { namespace utils { @@ -140,6 +143,36 @@ bool mountRun(const std::string& path) || utils::mountTmpfs(path, RUN_MOUNT_POINT_FLAGS, RUN_MOUNT_POINT_OPTIONS_NO_SMACK); } +bool mount(const std::string& source, + const std::string& target, + const std::string& filesystemtype, + unsigned long mountflags, + const std::string& data) +{ + int ret = ::mount(source.c_str(), + target.c_str(), + filesystemtype.c_str(), + mountflags, + data.c_str()); + if (ret < 0) { + LOGE("Mount operation failure: " + << "source path: " + << source + << ", target path: " + << target + << ", filesystemtype: " + << filesystemtype + << ", mountflags: " + << mountflags + << ", data: " + << data + << ", msg: " + << getSystemErrorMessage()); + return false; + } + return true; +} + bool umount(const std::string& path) { if (::umount(path.c_str()) != 0) { @@ -181,7 +214,6 @@ bool moveFile(const std::string& src, const std::string& dst) { bool bResult; - namespace fs = boost::filesystem; boost::system::error_code error; // The destination has to be a full path (including a file name) @@ -218,8 +250,6 @@ namespace { bool copyDirContentsRec(const boost::filesystem::path& src, const boost::filesystem::path& dst) { - namespace fs = boost::filesystem; - try { for (fs::directory_iterator file(src); file != fs::directory_iterator(); @@ -274,26 +304,34 @@ bool copyDirContentsRec(const boost::filesystem::path& src, const boost::filesys return true; } +boost::filesystem::perms getPerms(const mode_t& mode) +{ + return static_cast(mode); +} + +bool copySmackLabel(const std::string& /* src */, const std::string& /* dst */) +{ + //TODO: fill copySmackLabel function + return true; +} + + } // namespace bool copyDirContents(const std::string& src, const std::string& dst) { - namespace fs = boost::filesystem; - return copyDirContentsRec(fs::path(src), fs::path(dst)); } bool createDir(const std::string& path, uid_t uid, uid_t gid, boost::filesystem::perms mode) { - namespace fs = boost::filesystem; - fs::path dirPath(path); - boost::system::error_code ec; + boost::system::error_code errorCode; bool runDirCreated = false; if (!fs::exists(dirPath)) { - if (!fs::create_directory(dirPath, ec)) { + if (!fs::create_directory(dirPath, errorCode)) { LOGE("Failed to create directory '" << path << "': " - << ec.message()); + << errorCode.message()); return false; } runDirCreated = true; @@ -303,10 +341,10 @@ bool createDir(const std::string& path, uid_t uid, uid_t gid, boost::filesystem: } // set permissions - fs::permissions(dirPath, mode, ec); + fs::permissions(dirPath, mode, errorCode); if (fs::status(dirPath).permissions() != mode) { LOGE("Failed to set permissions to '" << path << "': " - << ec.message()); + << errorCode.message()); return false; } @@ -323,10 +361,36 @@ bool createDir(const std::string& path, uid_t uid, uid_t gid, boost::filesystem: return true; } -bool createEmptyDir(const std::string& path) +bool createDirs(const std::string& path, mode_t mode) { - namespace fs = boost::filesystem; + boost::filesystem::perms perms = getPerms(mode); + std::vector dirs; + fs::path prefix; + fs::path dirPath = fs::path(path); + for (const auto dir : dirPath) { + prefix /= dir; + if (!fs::exists(prefix)) { + bool created = createDir(prefix.string(), -1, -1, perms); + if (created) { + dirs.push_back(prefix); + } else { + LOGE("Failed to create dir"); + for (auto dir = dirs.rbegin(); dir != dirs.rend(); ++dir) { + boost::system::error_code errorCode; + fs::remove(*dir, errorCode); + if (errorCode) { + LOGE("Error during cleaning: dir: " << *dir << ", msg: " << errorCode.message()); + } + } + return false; + } + } + } + return true; +} +bool createEmptyDir(const std::string& path) +{ fs::path dirPath(path); boost::system::error_code ec; bool cleanDirCreated = false; @@ -353,5 +417,95 @@ bool createEmptyDir(const std::string& path) return true; } +bool createFile(const std::string& path, int flags, mode_t mode) +{ + // TODO: Check if we really need *flags* in the API + int ret = ::open(path.c_str(), flags, mode); + if (ret < 0) { + LOGE("Failed to create file: path=host:" + << path + << ", msg: " + << getSystemErrorMessage()); + return false; + } + close(ret); + return true; +} + +bool createFifo(const std::string& path, mode_t mode) +{ + int ret = ::mkfifo(path.c_str(), mode); + if (ret < 0) { + LOGE("Failed to make fifo: path=host:" << path); + return false; + } + return true; +} + +bool copyFile(const std::string& src, const std::string& dest) +{ + boost::system::error_code errorCode; + fs::copy_file(src, dest, errorCode); + if (errorCode) { + LOGE("Failed to copy file: msg: " + << errorCode.message() + << ", path=host:" + << src + << ", path=host:" + << dest); + return false; + } + bool retSmack = copySmackLabel(src, dest); + if (!retSmack) { + LOGE("Failed to copy file: msg: (can't copy smacklabel) " + << ", path=host:" + << src + << ", path=host:" + << dest); + fs::remove(src, errorCode); + if (errorCode) { + LOGE("Failed to clean after copy failure: path=host:" + << src + << ", msg: " + << errorCode.message()); + } + return false; + } + return true; +} + +bool createLink(const std::string& src, const std::string& dest) +{ + int retLink = ::link(src.c_str(), dest.c_str()); + if (retLink < 0) { + LOGE("Failed to hard link: path=host:" + << src + << ", path=host:" + << dest + << ", msg:" + << getSystemErrorMessage()); + return false; + } + bool retSmack = copySmackLabel(src, dest); + if (!retSmack) { + LOGE("Failed to copy smack label: path=host:" + << src + << ", path=host:" + << dest); + boost::system::error_code ec; + fs::remove(dest, ec); + if (!ec) { + LOGE("Failed to clean after hard link creation failure: path=host:" + << src + << ", to: " + << dest + << ", msg: " + << ec.message()); + } + return false; + } + return true; +} + } // namespace utils } // namespace vasum diff --git a/common/utils/fs.hpp b/common/utils/fs.hpp index 35000c6..b00f95a 100644 --- a/common/utils/fs.hpp +++ b/common/utils/fs.hpp @@ -70,6 +70,15 @@ bool listDir(const std::string& path, std::vector& files); bool mountRun(const std::string& path); /** + * Creates mount point + */ +bool mount(const std::string& source, + const std::string& target, + const std::string& filesystemtype, + unsigned long mountflags, + const std::string& data); + +/** * Umounts a filesystem */ bool umount(const std::string& path); @@ -102,12 +111,36 @@ bool copyDirContents(const std::string& src, const std::string& dst); bool createDir(const std::string& path, uid_t uid, uid_t gid, boost::filesystem::perms mode); /** + * Recursively creates a directory with specific permissions set. + */ +bool createDirs(const std::string& path, mode_t mode); + +/** * Creates an empty directory, ready to serve as mount point. * Succeeds either if path did not exist and was created successfully, or if already existing dir * under the same path is empty and is not a mount point. */ bool createEmptyDir(const std::string& path); +/** + * Creates an empty file + */ +bool createFile(const std::string& path, int flags, mode_t mode); + +/** + * Creates an FIFO special file + */ +bool createFifo(const std::string& path, mode_t mode); + +/** + * Copy an file + */ +bool copyFile(const std::string& src, const std::string& dest); + +/** + * Create hard link + */ +bool createLink(const std::string& src, const std::string& dest); } // namespace utils } // namespace vasum diff --git a/server/CMakeLists.txt b/server/CMakeLists.txt index 8c7604a..adfef3e 100644 --- a/server/CMakeLists.txt +++ b/server/CMakeLists.txt @@ -34,6 +34,7 @@ PKG_CHECK_MODULES(SERVER_DEPS REQUIRED lxc json gio-2.0 libsystemd-journal libsy libcap-ng libLogger libSimpleDbus libConfig) INCLUDE_DIRECTORIES(${COMMON_FOLDER}) +INCLUDE_DIRECTORIES(${CLIENT_FOLDER}) INCLUDE_DIRECTORIES(SYSTEM ${SERVER_DEPS_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS}) TARGET_LINK_LIBRARIES(${SERVER_CODENAME} ${SERVER_DEPS_LIBRARIES} ${Boost_LIBRARIES}) diff --git a/server/configs/templates/template.conf b/server/configs/templates/template.conf index fa6ef5c..e011a64 100644 --- a/server/configs/templates/template.conf +++ b/server/configs/templates/template.conf @@ -12,5 +12,9 @@ "enableDbusIntegration" : true, "runMountPoint" : "~NAME~/run", "permittedToSend" : [ "/tmp/.*" ], - "permittedToRecv" : [ "/tmp/.*" ] + "permittedToRecv" : [ "/tmp/.*" ], + "validLinkPrefixes" : [ "/tmp/", + "/run/", + "/opt/usr/data/", + "/opt/usr/dbsapce/" ] } diff --git a/server/configs/zones/business.conf b/server/configs/zones/business.conf index 6a4bc50..74c3bad 100644 --- a/server/configs/zones/business.conf +++ b/server/configs/zones/business.conf @@ -12,5 +12,9 @@ "switchToDefaultAfterTimeout" : true, "runMountPoint" : "business/run", "permittedToSend" : [ "/tmp/.*" ], - "permittedToRecv" : [ "/tmp/.*" ] + "permittedToRecv" : [ "/tmp/.*" ], + "validLinkPrefixes" : [ "/tmp/", + "/run/", + "/opt/usr/data/", + "/opt/usr/dbsapce/" ] } diff --git a/server/configs/zones/private.conf b/server/configs/zones/private.conf index 2a1147f..ffb7e85 100644 --- a/server/configs/zones/private.conf +++ b/server/configs/zones/private.conf @@ -12,5 +12,9 @@ "switchToDefaultAfterTimeout" : true, "runMountPoint" : "private/run", "permittedToSend" : [ "/tmp/.*" ], - "permittedToRecv" : [ "/tmp/.*" ] + "permittedToRecv" : [ "/tmp/.*" ], + "validLinkPrefixes" : [ "/tmp/", + "/run/", + "/opt/usr/data/", + "/opt/usr/dbsapce/" ] } diff --git a/server/zone-config.hpp b/server/zone-config.hpp index b801af8..f92ca8f 100644 --- a/server/zone-config.hpp +++ b/server/zone-config.hpp @@ -112,6 +112,11 @@ struct ZoneConfig { */ std::vector permittedToRecv; + /** + * Valid hard link prefixes. + */ + std::vector validLinkPrefixes; + CONFIG_REGISTER ( name, @@ -127,7 +132,8 @@ struct ZoneConfig { cpuQuotaBackground, runMountPoint, permittedToSend, - permittedToRecv + permittedToRecv, + validLinkPrefixes ) }; diff --git a/server/provisioning-config.hpp b/server/zone-provision-config.hpp similarity index 100% rename from server/provisioning-config.hpp rename to server/zone-provision-config.hpp diff --git a/server/zone-provision.cpp b/server/zone-provision.cpp new file mode 100644 index 0000000..3bff4b5 --- /dev/null +++ b/server/zone-provision.cpp @@ -0,0 +1,214 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * + * Contact: Mateusz Malicki + * + * 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 + * @author Mateusz Malicki (m.malicki2@samsung.com) + * @brief Implementation of class for managing zone provsion + */ + +#include "config.hpp" + +#include "zone-provision.hpp" +#include "zone-provision-config.hpp" + +#include "logger/logger.hpp" +#include "utils/fs.hpp" +#include "utils/exception.hpp" +#include "config/manager.hpp" +#include "vasum-client.h" + +#include + +#include +#include + +namespace fs = boost::filesystem; + +namespace vasum { + +namespace { + +const std::string ZONE_PROVISION_FILE = "provision.conf"; + +void declareUnit(const std::string& file, ZoneProvisioning::Unit&& unit) +{ + // TODO: Add to the dynamic configuration + ZoneProvisioning config; + if (fs::exists(file)) { + config::loadFromFile(file, config); + } + config.units.push_back(std::move(unit)); + config::saveToFile(file, config); +} + +} // namespace + +ZoneProvision::ZoneProvision(const std::string& zonePath, + const std::vector& validLinkPrefixes) +{ + mProvisionFile = (fs::path(zonePath) / fs::path(ZONE_PROVISION_FILE)).string(); + mRootPath = (zonePath / fs::path("rootfs")).string(); + mValidLinkPrefixes = validLinkPrefixes; +} + +std::string ZoneProvision::getRootPath() const +{ + return mRootPath; +} + + +void ZoneProvision::declareFile(const int32_t& type, + const std::string& path, + const int32_t& flags, + const int32_t& mode) +{ + ZoneProvisioning::Unit unit; + unit.set(ZoneProvisioning::File({type, path, flags, mode})); + + declareUnit(mProvisionFile, std::move(unit)); +} + +void ZoneProvision::declareMount(const std::string& source, + const std::string& target, + const std::string& type, + const int64_t& flags, + const std::string& data) +{ + ZoneProvisioning::Unit unit; + unit.set(ZoneProvisioning::Mount({source, target, type, flags, data})); + + declareUnit(mProvisionFile, std::move(unit)); +} + +void ZoneProvision::declareLink(const std::string& source, + const std::string& target) +{ + ZoneProvisioning::Unit unit; + unit.set(ZoneProvisioning::Link({source, target})); + + declareUnit(mProvisionFile, std::move(unit)); +} + +void ZoneProvision::start() noexcept +{ + if (fs::exists(mProvisionFile)) { + config::loadFromFile(mProvisionFile, mProvisioningConfig); + for (const auto& unit : mProvisioningConfig.units) { + try { + if (unit.is()) { + file(unit.as()); + } else if (unit.is()) { + mount(unit.as()); + } else if (unit.is()) { + link(unit.as()); + } + } catch (std::exception& ex) { + LOGE("Provsion error: " << ex.what()); + } + } + } +} + +void ZoneProvision::stop() noexcept +{ + for (auto it = mProvisioningConfig.units.rbegin(); + it != mProvisioningConfig.units.rend(); + ++it) { + if (it->is()) { + umount(it->as()); + } + } +} + +void ZoneProvision::file(const ZoneProvisioning::File& config) +{ + bool ret = false; + const fs::path hostPath = fs::path(mRootPath) / fs::path(config.path); + switch (config.type) { + case VSMFILE_DIRECTORY: + ret = utils::createDirs(hostPath.string(), config.mode); + if (!ret) { + throw UtilsException("Can't create dir: " + hostPath.string()); + } + break; + + case VSMFILE_FIFO: + ret = utils::createFifo(hostPath.string(), config.mode); + if (!ret) { + throw UtilsException("Failed to make fifo: " + config.path); + } + break; + + case VSMFILE_REGULAR: + if ((config.flags & O_CREAT)) { + ret = utils::createFile(hostPath.string(), config.flags, config.mode); + if (!ret) { + throw UtilsException("Failed to create file: " + config.path); + } + } else { + ret = utils::copyFile(config.path, hostPath.string()); + if (!ret) { + throw UtilsException("Failed to copy file: " + config.path); + } + } + break; + } +} + +void ZoneProvision::mount(const ZoneProvisioning::Mount& config) +{ + const fs::path hostPath = fs::path(mRootPath) / fs::path(config.target); + bool ret = utils::mount(config.source, + hostPath.string(), + config.type, + config.flags, + config.data); + if (!ret) { + throw UtilsException("Mount operation failure - source : " + config.source); + } +} + +void ZoneProvision::umount(const ZoneProvisioning::Mount& config) +{ + const fs::path hostPath = fs::path(mRootPath) / fs::path(config.target); + utils::umount(hostPath.string()); +} + +void ZoneProvision::link(const ZoneProvisioning::Link& config) +{ + const std::string srcHostPath = fs::path(config.source).normalize().string(); + for (const std::string& prefix : mValidLinkPrefixes) { + if (prefix.length() <= srcHostPath.length() + && srcHostPath.compare(0, prefix.length(), prefix) == 0) { + + const fs::path destHostPath = fs::path(mRootPath) / fs::path(config.target); + bool ret = utils::createLink(srcHostPath, destHostPath.string()); + if (!ret) { + throw UtilsException("Failed to create hard link: " + config.source); + } + return; + } + } + LOGE("Failed to create hard link: path=host: " + << srcHostPath + << ", msg: Path prefix is not valid path"); + throw UtilsException("Failed to hard link: path prefix is not valid"); +} + +} // namespace vasum diff --git a/server/zone-provision.hpp b/server/zone-provision.hpp new file mode 100644 index 0000000..d7cc1e4 --- /dev/null +++ b/server/zone-provision.hpp @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * + * Contact: Mateusz Malicki + * + * 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 + * @author Mateusz Malicki (m.malicki2@samsung.com) + * @brief Declaration of the class for managing zone provision + */ + + +#ifndef SERVER_ZONE_PROVISION_HPP +#define SERVER_ZONE_PROVISION_HPP + +#include "zone-provision-config.hpp" + +#include +#include + +namespace vasum { + + +/** + * Class is responsible for prepare filesystem for zone + * It allows to create directories, files, mount points and copying files from host + */ +class ZoneProvision { + +public: + /** + * ZoneProvision constructor + * @param zonesPath directory where zones are defined (lxc configs, rootfs etc) + */ + ZoneProvision(const std::string& zonePath, + const std::vector& validLinkPrefixes); + + /** + * Declare file, directory or pipe that will be created while zone startup + */ + void declareFile(const int32_t& type, + const std::string& path, + const int32_t& flags, + const int32_t& mode); + /** + * Declare mount that will be created while zone startup + */ + void declareMount(const std::string& source, + const std::string& target, + const std::string& type, + const int64_t& flags, + const std::string& data); + /** + * Declare link that will be created while zone startup + */ + void declareLink(const std::string& source, + const std::string& target); + + /** + * Get zone root path + */ + std::string getRootPath() const; + + void start() noexcept; + void stop() noexcept; + +private: + ZoneProvisioning mProvisioningConfig; + std::string mRootPath; + std::string mProvisionFile; + std::vector mValidLinkPrefixes; + + void mount(const ZoneProvisioning::Mount& config); + void umount(const ZoneProvisioning::Mount& config); + void file(const ZoneProvisioning::File& config); + void link(const ZoneProvisioning::Link& config); +}; + + +} // namespace vasum + +#endif // SERVER_ZONE_PROVISION_HPP diff --git a/server/zone.cpp b/server/zone.cpp index 199ecff..f6fb188 100644 --- a/server/zone.cpp +++ b/server/zone.cpp @@ -26,7 +26,6 @@ #include "zone.hpp" #include "base-exception.hpp" -#include "provisioning-config.hpp" #include "logger/logger.hpp" #include "utils/paths.hpp" @@ -38,7 +37,6 @@ #include #include - namespace vasum { namespace fs = boost::filesystem; @@ -50,18 +48,6 @@ typedef std::lock_guard 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; -const std::string ZONE_PROVISION_FILE = "provision.conf"; - -void declareUnit(const std::string& file, ZoneProvisioning::Unit&& unit) -{ - // TODO: Add to the dynamic configuration - ZoneProvisioning config; - if (fs::exists(file)) { - config::loadFromFile(file, config); - } - config.units.push_back(std::move(unit)); - config::saveToFile(file, config); -} } // namespace @@ -86,8 +72,8 @@ Zone::Zone(const utils::Worker::Pointer& worker, } mAdmin.reset(new ZoneAdmin(zonesPath, lxcTemplatePrefix, mConfig)); - const fs::path baseProvision = fs::path(zonesPath) / mAdmin->getId(); - mProvisionConfig = fs::absolute(ZONE_PROVISION_FILE, baseProvision).string(); + const fs::path zonePath = fs::path(zonesPath) / mAdmin->getId(); + mProvision.reset(new ZoneProvision(zonePath.string(), mConfig.validLinkPrefixes)); } Zone::~Zone() @@ -123,6 +109,7 @@ int Zone::getPrivilege() const void Zone::start() { Lock lock(mReconnectMutex); + mProvision->start(); if (mConfig.enableDbusIntegration) { mConnectionTransport.reset(new ZoneConnectionTransport(mRunMountPoint)); } @@ -162,6 +149,7 @@ void Zone::stop() disconnect(); mAdmin->stop(); mConnectionTransport.reset(); + mProvision->stop(); } void Zone::connect() @@ -211,6 +199,12 @@ int Zone::getVT() const return mConfig.vt; } +std::string Zone::getRootPath() const +{ + return mProvision->getRootPath(); +} + + bool Zone::activateVT() { Lock lock(mReconnectMutex); @@ -399,35 +393,26 @@ void Zone::proxyCallAsync(const std::string& busName, } void Zone::declareFile(const int32_t& type, - const std::string& path, - const int32_t& flags, - const int32_t& mode) + const std::string& path, + const int32_t& flags, + const int32_t& mode) { - ZoneProvisioning::Unit unit; - unit.set(std::move(ZoneProvisioning::File({type, path, flags, mode}))); - - declareUnit(mProvisionConfig, std::move(unit)); + mProvision->declareFile(type, path, flags, mode); } void Zone::declareMount(const std::string& source, - const std::string& target, - const std::string& type, - const int64_t& flags, - const std::string& data) + const std::string& target, + const std::string& type, + const int64_t& flags, + const std::string& data) { - ZoneProvisioning::Unit unit; - unit.set(std::move(ZoneProvisioning::Mount({source, target, type, flags, data}))); - - declareUnit(mProvisionConfig, std::move(unit)); + mProvision->declareMount(source, target, type, flags, data); } void Zone::declareLink(const std::string& source, - const std::string& target) + const std::string& target) { - ZoneProvisioning::Unit unit; - unit.set(std::move(ZoneProvisioning::Link({source, target}))); - - declareUnit(mProvisionConfig, std::move(unit)); + mProvision->declareLink(source, target); } } // namespace vasum diff --git a/server/zone.hpp b/server/zone.hpp index fba9399..dd0daca 100644 --- a/server/zone.hpp +++ b/server/zone.hpp @@ -30,6 +30,7 @@ #include "zone-admin.hpp" #include "zone-connection.hpp" #include "zone-connection-transport.hpp" +#include "zone-provision.hpp" #include "utils/worker.hpp" #include @@ -254,6 +255,11 @@ public: void declareLink(const std::string& source, const std::string& target); + /** + * Get zone root path + */ + std::string getRootPath() const; + private: utils::Worker::Pointer mWorker; ZoneConfig mConfig; @@ -262,6 +268,7 @@ private: std::unique_ptr mConnectionTransport; std::unique_ptr mAdmin; std::unique_ptr mConnection; + std::unique_ptr mProvision; mutable std::recursive_mutex mReconnectMutex; NotifyActiveZoneCallback mNotifyCallback; DisplayOffCallback mDisplayOffCallback; @@ -270,7 +277,6 @@ private: DbusStateChangedCallback mDbusStateChangedCallback; std::string mDbusAddress; std::string mRunMountPoint; - std::string mProvisionConfig; void onNameLostCallback(); void reconnectHandler(); diff --git a/server/zones-manager.cpp b/server/zones-manager.cpp index eafbe4b..1f9bef0 100644 --- a/server/zones-manager.cpp +++ b/server/zones-manager.cpp @@ -628,15 +628,12 @@ void ZonesManager::handleGetZoneInfoCall(const std::string& id, result->setError(api::ERROR_INTERNAL, "Unrecognized state of zone"); return; } - const auto zonePath = boost::filesystem::absolute(id, mConfig.zonesPath); - const auto rootfsDir = boost::filesystem::path("rootfs"); - const auto rootfsPath = zonePath / rootfsDir; result->set(g_variant_new("((siss))", id.c_str(), zone->getVT(), state, - rootfsPath.string().c_str())); + zone->getRootPath().c_str())); } void ZonesManager::handleDeclareFileCall(const std::string& zone, diff --git a/tests/unit_tests/client/configs/ut-client/zones/console1-dbus.conf.in b/tests/unit_tests/client/configs/ut-client/zones/console1-dbus.conf.in index 72fbdbf..46fb716 100644 --- a/tests/unit_tests/client/configs/ut-client/zones/console1-dbus.conf.in +++ b/tests/unit_tests/client/configs/ut-client/zones/console1-dbus.conf.in @@ -12,5 +12,6 @@ "cpuQuotaBackground" : 1000, "runMountPoint" : "/tmp/ut-run/ut-zones-manager-console1-dbus", "permittedToSend" : [ "/tmp/.*", "/etc/secret2" ], - "permittedToRecv" : [ "/tmp/.*" ] + "permittedToRecv" : [ "/tmp/.*" ], + "validLinkPrefixes" : [] } diff --git a/tests/unit_tests/client/configs/ut-client/zones/console2-dbus.conf.in b/tests/unit_tests/client/configs/ut-client/zones/console2-dbus.conf.in index 1386878..260d01f 100644 --- a/tests/unit_tests/client/configs/ut-client/zones/console2-dbus.conf.in +++ b/tests/unit_tests/client/configs/ut-client/zones/console2-dbus.conf.in @@ -12,5 +12,6 @@ "cpuQuotaBackground" : 1000, "runMountPoint" : "/tmp/ut-run/ut-zones-manager-console2-dbus", "permittedToSend" : [ "/tmp/.*" ], - "permittedToRecv" : [ "/tmp/.*", "/etc/secret1" ] + "permittedToRecv" : [ "/tmp/.*", "/etc/secret1" ], + "validLinkPrefixes" : [] } diff --git a/tests/unit_tests/client/configs/ut-client/zones/console3-dbus.conf.in b/tests/unit_tests/client/configs/ut-client/zones/console3-dbus.conf.in index f91754f..e20ffe9 100644 --- a/tests/unit_tests/client/configs/ut-client/zones/console3-dbus.conf.in +++ b/tests/unit_tests/client/configs/ut-client/zones/console3-dbus.conf.in @@ -12,5 +12,6 @@ "cpuQuotaBackground" : 1000, "runMountPoint" : "/tmp/ut-run/ut-zones-manager-console3-dbus", "permittedToSend" : [ "/tmp/.*" ], - "permittedToRecv" : [ "/tmp/.*" ] + "permittedToRecv" : [ "/tmp/.*" ], + "validLinkPrefixes" : [] } diff --git a/tests/unit_tests/server/configs/ut-server/zones/zone1.conf b/tests/unit_tests/server/configs/ut-server/zones/zone1.conf index 898eef2..2c2ccde 100644 --- a/tests/unit_tests/server/configs/ut-server/zones/zone1.conf +++ b/tests/unit_tests/server/configs/ut-server/zones/zone1.conf @@ -12,5 +12,6 @@ "cpuQuotaBackground" : 1000, "runMountPoint" : "", "permittedToSend" : [], - "permittedToRecv" : [] + "permittedToRecv" : [], + "validLinkPrefixes" : [] } diff --git a/tests/unit_tests/server/configs/ut-server/zones/zone2.conf b/tests/unit_tests/server/configs/ut-server/zones/zone2.conf index 8f4687a..1d500a7 100644 --- a/tests/unit_tests/server/configs/ut-server/zones/zone2.conf +++ b/tests/unit_tests/server/configs/ut-server/zones/zone2.conf @@ -12,5 +12,6 @@ "cpuQuotaBackground" : 1000, "runMountPoint" : "", "permittedToSend" : [], - "permittedToRecv" : [] + "permittedToRecv" : [], + "validLinkPrefixes" : [] } diff --git a/tests/unit_tests/server/configs/ut-server/zones/zone3.conf b/tests/unit_tests/server/configs/ut-server/zones/zone3.conf index fdb958f..a9856c7 100644 --- a/tests/unit_tests/server/configs/ut-server/zones/zone3.conf +++ b/tests/unit_tests/server/configs/ut-server/zones/zone3.conf @@ -12,5 +12,6 @@ "cpuQuotaBackground" : 1000, "runMountPoint" : "", "permittedToSend" : [], - "permittedToRecv" : [] + "permittedToRecv" : [], + "validLinkPrefixes" : [] } diff --git a/tests/unit_tests/server/configs/ut-zone-admin/zones/buggy.conf b/tests/unit_tests/server/configs/ut-zone-admin/zones/buggy.conf index 320dc00..e96dca8 100644 --- a/tests/unit_tests/server/configs/ut-zone-admin/zones/buggy.conf +++ b/tests/unit_tests/server/configs/ut-zone-admin/zones/buggy.conf @@ -12,5 +12,6 @@ "cpuQuotaBackground" : 1000, "runMountPoint" : "", "permittedToSend" : [], - "permittedToRecv" : [] + "permittedToRecv" : [], + "validLinkPrefixes" : [] } diff --git a/tests/unit_tests/server/configs/ut-zone-admin/zones/missing.conf b/tests/unit_tests/server/configs/ut-zone-admin/zones/missing.conf index 9aaf464..9a5ec68 100644 --- a/tests/unit_tests/server/configs/ut-zone-admin/zones/missing.conf +++ b/tests/unit_tests/server/configs/ut-zone-admin/zones/missing.conf @@ -12,5 +12,6 @@ "cpuQuotaBackground" : 1000, "runMountPoint" : "", "permittedToSend" : [], - "permittedToRecv" : [] + "permittedToRecv" : [], + "validLinkPrefixes" : [] } diff --git a/tests/unit_tests/server/configs/ut-zone-admin/zones/test-no-shutdown.conf b/tests/unit_tests/server/configs/ut-zone-admin/zones/test-no-shutdown.conf index b3b0141..be05697 100644 --- a/tests/unit_tests/server/configs/ut-zone-admin/zones/test-no-shutdown.conf +++ b/tests/unit_tests/server/configs/ut-zone-admin/zones/test-no-shutdown.conf @@ -12,5 +12,6 @@ "cpuQuotaBackground" : 1000, "runMountPoint" : "", "permittedToSend" : [], - "permittedToRecv" : [] + "permittedToRecv" : [], + "validLinkPrefixes" : [] } diff --git a/tests/unit_tests/server/configs/ut-zone-admin/zones/test.conf b/tests/unit_tests/server/configs/ut-zone-admin/zones/test.conf index ae046e5..45213d7 100644 --- a/tests/unit_tests/server/configs/ut-zone-admin/zones/test.conf +++ b/tests/unit_tests/server/configs/ut-zone-admin/zones/test.conf @@ -12,5 +12,6 @@ "cpuQuotaBackground" : 1000, "runMountPoint" : "", "permittedToSend" : [], - "permittedToRecv" : [] + "permittedToRecv" : [], + "validLinkPrefixes" : [] } diff --git a/tests/unit_tests/server/configs/ut-zone/zones/buggy.conf b/tests/unit_tests/server/configs/ut-zone/zones/buggy.conf index 55586b4..eaf0811 100644 --- a/tests/unit_tests/server/configs/ut-zone/zones/buggy.conf +++ b/tests/unit_tests/server/configs/ut-zone/zones/buggy.conf @@ -12,5 +12,6 @@ "cpuQuotaBackground" : 1000, "runMountPoint" : "", "permittedToSend" : [], - "permittedToRecv" : [] + "permittedToRecv" : [], + "validLinkPrefixes" : [] } diff --git a/tests/unit_tests/server/configs/ut-zone/zones/test-dbus.conf.in b/tests/unit_tests/server/configs/ut-zone/zones/test-dbus.conf.in index 4f8e6db..e4a18fa 100644 --- a/tests/unit_tests/server/configs/ut-zone/zones/test-dbus.conf.in +++ b/tests/unit_tests/server/configs/ut-zone/zones/test-dbus.conf.in @@ -12,5 +12,6 @@ "cpuQuotaBackground" : 1000, "runMountPoint" : "/tmp/ut-run/ut-zone-test-dbus", "permittedToSend" : [], - "permittedToRecv" : [] + "permittedToRecv" : [], + "validLinkPrefixes" : [] } diff --git a/tests/unit_tests/server/configs/ut-zone/zones/test.conf b/tests/unit_tests/server/configs/ut-zone/zones/test.conf index a8f9692..4386c8c 100644 --- a/tests/unit_tests/server/configs/ut-zone/zones/test.conf +++ b/tests/unit_tests/server/configs/ut-zone/zones/test.conf @@ -12,5 +12,6 @@ "cpuQuotaBackground" : 1000, "runMountPoint" : "", "permittedToSend" : [], - "permittedToRecv" : [] + "permittedToRecv" : [], + "validLinkPrefixes" : [ "/tmp" ] } diff --git a/tests/unit_tests/server/configs/ut-zones-manager/templates/template.conf.in b/tests/unit_tests/server/configs/ut-zones-manager/templates/template.conf.in index 15185d6..7c2f5e4 100644 --- a/tests/unit_tests/server/configs/ut-zones-manager/templates/template.conf.in +++ b/tests/unit_tests/server/configs/ut-zones-manager/templates/template.conf.in @@ -12,5 +12,6 @@ "cpuQuotaBackground" : 1000, "runMountPoint" : "/tmp/ut-run/~NAME~", "permittedToSend" : [ "/tmp/.*" ], - "permittedToRecv" : [ "/tmp/.*" ] + "permittedToRecv" : [ "/tmp/.*" ], + "validLinkPrefixes" : [] } diff --git a/tests/unit_tests/server/configs/ut-zones-manager/zones/console1-dbus.conf.in b/tests/unit_tests/server/configs/ut-zones-manager/zones/console1-dbus.conf.in index 72fbdbf..46fb716 100644 --- a/tests/unit_tests/server/configs/ut-zones-manager/zones/console1-dbus.conf.in +++ b/tests/unit_tests/server/configs/ut-zones-manager/zones/console1-dbus.conf.in @@ -12,5 +12,6 @@ "cpuQuotaBackground" : 1000, "runMountPoint" : "/tmp/ut-run/ut-zones-manager-console1-dbus", "permittedToSend" : [ "/tmp/.*", "/etc/secret2" ], - "permittedToRecv" : [ "/tmp/.*" ] + "permittedToRecv" : [ "/tmp/.*" ], + "validLinkPrefixes" : [] } diff --git a/tests/unit_tests/server/configs/ut-zones-manager/zones/console1.conf b/tests/unit_tests/server/configs/ut-zones-manager/zones/console1.conf index 3e88c3d..41071ad 100644 --- a/tests/unit_tests/server/configs/ut-zones-manager/zones/console1.conf +++ b/tests/unit_tests/server/configs/ut-zones-manager/zones/console1.conf @@ -12,5 +12,6 @@ "cpuQuotaBackground" : 1000, "runMountPoint" : "", "permittedToSend" : [ "/tmp/.*" ], - "permittedToRecv" : [ "/tmp/.*" ] + "permittedToRecv" : [ "/tmp/.*" ], + "validLinkPrefixes" : [] } diff --git a/tests/unit_tests/server/configs/ut-zones-manager/zones/console2-dbus.conf.in b/tests/unit_tests/server/configs/ut-zones-manager/zones/console2-dbus.conf.in index 1386878..260d01f 100644 --- a/tests/unit_tests/server/configs/ut-zones-manager/zones/console2-dbus.conf.in +++ b/tests/unit_tests/server/configs/ut-zones-manager/zones/console2-dbus.conf.in @@ -12,5 +12,6 @@ "cpuQuotaBackground" : 1000, "runMountPoint" : "/tmp/ut-run/ut-zones-manager-console2-dbus", "permittedToSend" : [ "/tmp/.*" ], - "permittedToRecv" : [ "/tmp/.*", "/etc/secret1" ] + "permittedToRecv" : [ "/tmp/.*", "/etc/secret1" ], + "validLinkPrefixes" : [] } diff --git a/tests/unit_tests/server/configs/ut-zones-manager/zones/console2.conf b/tests/unit_tests/server/configs/ut-zones-manager/zones/console2.conf index 22f7a39..f152092 100644 --- a/tests/unit_tests/server/configs/ut-zones-manager/zones/console2.conf +++ b/tests/unit_tests/server/configs/ut-zones-manager/zones/console2.conf @@ -12,5 +12,6 @@ "cpuQuotaBackground" : 1000, "runMountPoint" : "", "permittedToSend" : [ "/tmp/.*" ], - "permittedToRecv" : [ "/tmp/.*" ] + "permittedToRecv" : [ "/tmp/.*" ], + "validLinkPrefixes" : [] } diff --git a/tests/unit_tests/server/configs/ut-zones-manager/zones/console3-dbus.conf.in b/tests/unit_tests/server/configs/ut-zones-manager/zones/console3-dbus.conf.in index f91754f..e20ffe9 100644 --- a/tests/unit_tests/server/configs/ut-zones-manager/zones/console3-dbus.conf.in +++ b/tests/unit_tests/server/configs/ut-zones-manager/zones/console3-dbus.conf.in @@ -12,5 +12,6 @@ "cpuQuotaBackground" : 1000, "runMountPoint" : "/tmp/ut-run/ut-zones-manager-console3-dbus", "permittedToSend" : [ "/tmp/.*" ], - "permittedToRecv" : [ "/tmp/.*" ] + "permittedToRecv" : [ "/tmp/.*" ], + "validLinkPrefixes" : [] } diff --git a/tests/unit_tests/server/configs/ut-zones-manager/zones/console3.conf b/tests/unit_tests/server/configs/ut-zones-manager/zones/console3.conf index 2f99f23..4979dd2 100644 --- a/tests/unit_tests/server/configs/ut-zones-manager/zones/console3.conf +++ b/tests/unit_tests/server/configs/ut-zones-manager/zones/console3.conf @@ -12,5 +12,6 @@ "cpuQuotaBackground" : 1000, "runMountPoint" : "", "permittedToSend" : [ "/tmp/.*" ], - "permittedToRecv" : [ "/tmp/.*" ] + "permittedToRecv" : [ "/tmp/.*" ], + "validLinkPrefixes" : [] } diff --git a/tests/unit_tests/server/ut-zone-provision.cpp b/tests/unit_tests/server/ut-zone-provision.cpp new file mode 100644 index 0000000..6a6e4ef --- /dev/null +++ b/tests/unit_tests/server/ut-zone-provision.cpp @@ -0,0 +1,242 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * + * Contact: Mateusz Malicki + * + * 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 + * @author Mateusz Malicki (m.malicki2@samsung.com) + * @brief Unit tests of the ZoneProvsion class + */ + +#include "config.hpp" +#include "ut.hpp" + +#include "zone.hpp" + +#include "utils/glib-loop.hpp" +#include "utils/scoped-dir.hpp" +#include "config/manager.hpp" +#include "zone-provision-config.hpp" +#include "vasum-client.h" + +#include +#include + +#include +#include +#include + +using namespace vasum; +using namespace config; + +namespace fs = boost::filesystem; + +namespace { + +const std::string PROVISON_CONFIG_FILE = "provision.conf"; +const std::string ZONE = "ut-zone-test"; +const fs::path TEST_CONFIG_PATH = VSM_TEST_CONFIG_INSTALL_DIR "/server/ut-zone/zones/test.conf"; +const fs::path ZONES_PATH = "/tmp/ut-zones"; +const fs::path LXC_TEMPLATES_PATH = VSM_TEST_LXC_TEMPLATES_INSTALL_DIR; +const fs::path ZONE_PATH = ZONES_PATH / fs::path(ZONE); +const fs::path PROVISION_FILE_PATH = ZONE_PATH / fs::path(PROVISON_CONFIG_FILE); +const fs::path ROOTFS_PATH = ZONE_PATH / fs::path("rootfs"); + +struct Fixture { + utils::ScopedGlibLoop mLoop; + utils::ScopedDir mZonesPathGuard; + utils::ScopedDir mRunGuard; + utils::ScopedDir mRootfsPath; + + Fixture() + : mZonesPathGuard(ZONES_PATH.string()) + , mRunGuard("/tmp/ut-run") + , mRootfsPath(ROOTFS_PATH.string()) + { + } + + std::unique_ptr create(const std::string& configPath) + { + return std::unique_ptr(new Zone(utils::Worker::create(), + ZONES_PATH.string(), + configPath, + LXC_TEMPLATES_PATH.string(), + "")); + } +}; + +} // namespace + + +BOOST_FIXTURE_TEST_SUITE(ZoneProvisionSuite, Fixture) + +BOOST_AUTO_TEST_CASE(FileTest) +{ + //TODO: Test Fifo + const fs::path regularFile = fs::path("/opt/usr/data/ut-regular-file"); + const fs::path copyFile = PROVISION_FILE_PATH; + + ZoneProvisioning config; + ZoneProvisioning::Unit unit; + unit.set(ZoneProvisioning::File({VSMFILE_DIRECTORY, + regularFile.parent_path().string(), + 0, + 0777})); + config.units.push_back(unit); + + unit.set(ZoneProvisioning::File({VSMFILE_REGULAR, + regularFile.string(), + O_CREAT, + 0777})); + config.units.push_back(unit); + + unit.set(ZoneProvisioning::File({VSMFILE_DIRECTORY, + copyFile.parent_path().string(), + 0, + 0777})); + config.units.push_back(unit); + unit.set(ZoneProvisioning::File({VSMFILE_REGULAR, + copyFile.string(), + 0, + 0777})); + config.units.push_back(unit); + config::saveToFile(PROVISION_FILE_PATH.string(), config); + auto c = create(TEST_CONFIG_PATH.string()); + c->start(); + + BOOST_CHECK(fs::exists(ROOTFS_PATH / regularFile.parent_path())); + BOOST_CHECK(fs::exists(ROOTFS_PATH / regularFile)); + BOOST_CHECK(fs::exists(ROOTFS_PATH / copyFile.parent_path())); + BOOST_CHECK(fs::exists(ROOTFS_PATH / copyFile)); + + c->stop(); +} + +BOOST_AUTO_TEST_CASE(MountTest) +{ + //TODO: Test Fifo + const fs::path mountTarget = fs::path("/opt/usr/data/ut-from-host-provision"); + const fs::path mountSource = fs::path("/tmp/ut-provision"); + const fs::path sharedFile = fs::path("ut-regular-file"); + + utils::ScopedDir provisionfs(mountSource.string()); + + + ZoneProvisioning config; + ZoneProvisioning::Unit unit; + unit.set(ZoneProvisioning::File({VSMFILE_DIRECTORY, + mountTarget.string(), + 0, + 0777})); + config.units.push_back(unit); + unit.set(ZoneProvisioning::Mount({mountSource.string(), + mountTarget.string(), + "", + MS_BIND, + ""})); + config.units.push_back(unit); + unit.set(ZoneProvisioning::File({VSMFILE_REGULAR, + (mountTarget / sharedFile).string(), + O_CREAT, + 0777})); + config.units.push_back(unit); + + config::saveToFile(PROVISION_FILE_PATH.string(), config); + auto c = create(TEST_CONFIG_PATH.string()); + c->start(); + + BOOST_CHECK(fs::exists(ROOTFS_PATH / mountTarget)); + BOOST_CHECK(fs::exists(ROOTFS_PATH / mountTarget / sharedFile)); + BOOST_CHECK(fs::exists(mountSource / sharedFile)); + + c->stop(); +} + +BOOST_AUTO_TEST_CASE(LinkTest) +{ + const fs::path linkFile = fs::path("/ut-from-host-provision.conf"); + + ZoneProvisioning config; + ZoneProvisioning::Unit unit; + + unit.set(ZoneProvisioning::Link({PROVISION_FILE_PATH.string(), + linkFile.string()})); + config.units.push_back(unit); + config::saveToFile(PROVISION_FILE_PATH.string(), config); + auto c = create(TEST_CONFIG_PATH.string()); + c->start(); + + BOOST_CHECK(fs::exists(ROOTFS_PATH / linkFile)); + + c->stop(); +} + +BOOST_AUTO_TEST_CASE(DeclareFile) +{ + ZoneProvision zoneProvision(ZONE_PATH.string(), {}); + zoneProvision.declareFile(1, "path", 0747, 0777); + zoneProvision.declareFile(2, "path", 0747, 0777); + + ZoneProvisioning config; + BOOST_REQUIRE_NO_THROW(loadFromFile(PROVISION_FILE_PATH.string(), config)); + BOOST_REQUIRE_EQUAL(config.units.size(), 2); + BOOST_REQUIRE(config.units[0].is()); + BOOST_REQUIRE(config.units[1].is()); + const ZoneProvisioning::File& unit = config.units[0].as(); + BOOST_CHECK_EQUAL(unit.type, 1); + BOOST_CHECK_EQUAL(unit.path, "path"); + BOOST_CHECK_EQUAL(unit.flags, 0747); + BOOST_CHECK_EQUAL(unit.mode, 0777); +} + +BOOST_AUTO_TEST_CASE(DeclareMount) +{ + ZoneProvision zoneProvision(ZONE_PATH.string(), {}); + zoneProvision.declareMount("/fake/path1", "/fake/path2", "tmpfs", 077, "fake"); + zoneProvision.declareMount("/fake/path2", "/fake/path2", "tmpfs", 077, "fake"); + + ZoneProvisioning config; + BOOST_REQUIRE_NO_THROW(loadFromFile(PROVISION_FILE_PATH.string(), config)); + BOOST_REQUIRE_EQUAL(config.units.size(), 2); + BOOST_REQUIRE(config.units[0].is()); + BOOST_REQUIRE(config.units[1].is()); + const ZoneProvisioning::Mount& unit = config.units[0].as(); + BOOST_CHECK_EQUAL(unit.source, "/fake/path1"); + BOOST_CHECK_EQUAL(unit.target, "/fake/path2"); + BOOST_CHECK_EQUAL(unit.type, "tmpfs"); + BOOST_CHECK_EQUAL(unit.flags, 077); + BOOST_CHECK_EQUAL(unit.data, "fake"); +} + +BOOST_AUTO_TEST_CASE(DeclareLink) +{ + ZoneProvision zoneProvision(ZONE_PATH.string(), {}); + zoneProvision.declareLink("/fake/path1", "/fake/path2"); + zoneProvision.declareLink("/fake/path2", "/fake/path2"); + + ZoneProvisioning config; + BOOST_REQUIRE_NO_THROW(loadFromFile(PROVISION_FILE_PATH.string(), config)); + BOOST_REQUIRE_EQUAL(config.units.size(), 2); + BOOST_REQUIRE(config.units[0].is()); + BOOST_REQUIRE(config.units[1].is()); + const ZoneProvisioning::Link& unit = config.units[0].as(); + BOOST_CHECK_EQUAL(unit.source, "/fake/path1"); + BOOST_CHECK_EQUAL(unit.target, "/fake/path2"); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/unit_tests/server/ut-zones-manager.cpp b/tests/unit_tests/server/ut-zones-manager.cpp index 4db9434..3c62478 100644 --- a/tests/unit_tests/server/ut-zones-manager.cpp +++ b/tests/unit_tests/server/ut-zones-manager.cpp @@ -27,9 +27,6 @@ #include "ut.hpp" #include "zones-manager.hpp" -#include "zones-manager-config.hpp" -#include "zone-config.hpp" -#include "provisioning-config.hpp" #include "zone-dbus-definitions.hpp" #include "host-dbus-definitions.hpp" #include "test-dbus-definitions.hpp" @@ -40,7 +37,6 @@ #include "dbus/connection.hpp" #include "dbus/exception.hpp" #include "utils/glib-loop.hpp" -#include "config/manager.hpp" #include "config/exception.hpp" #include "utils/latch.hpp" #include "utils/fs.hpp" @@ -82,7 +78,6 @@ const std::string FILE_CONTENT = "File content\n" "Line 2\n"; const std::string NON_EXISTANT_ZONE_ID = "NON_EXISTANT_ZONE_ID"; const std::string ZONES_PATH = "/tmp/ut-zones"; // the same as in daemon.conf -const std::string PROVISON_CONFIG_FILE = "provision.conf"; class DbusAccessory { public: @@ -314,67 +309,6 @@ public: } - void callMethodDeclareFile(const std::string& zone, - const int32_t& type, - const std::string& path, - const int32_t& flags, - const int32_t& mode) - { - assert(isHost()); - GVariant* parameters = g_variant_new("(sisii)", - zone.c_str(), - type, - path.c_str(), - flags, - mode); - GVariantPtr result = mClient->callMethod(api::host::BUS_NAME, - api::host::OBJECT_PATH, - api::host::INTERFACE, - api::host::METHOD_DECLARE_FILE, - parameters, - "()"); - } - - void callMethodDeclareMount(const std::string& source, - const std::string& zone, - const std::string& target, - const std::string& type, - const uint64_t& flags, - const std::string& data) - { - assert(isHost()); - GVariant* parameters = g_variant_new("(ssssts)", - source.c_str(), - zone.c_str(), - target.c_str(), - type.c_str(), - flags, - data.c_str()); - GVariantPtr result = mClient->callMethod(api::host::BUS_NAME, - api::host::OBJECT_PATH, - api::host::INTERFACE, - api::host::METHOD_DECLARE_MOUNT, - parameters, - "()"); - } - - void callMethodDeclareLink(const std::string& source, - const std::string& zone, - const std::string& target) - { - assert(isHost()); - GVariant* parameters = g_variant_new("(sss)", - source.c_str(), - zone.c_str(), - target.c_str()); - GVariantPtr result = mClient->callMethod(api::host::BUS_NAME, - api::host::OBJECT_PATH, - api::host::INTERFACE, - api::host::METHOD_DECLARE_LINK, - parameters, - "()"); - } - void callAsyncMethodCreateZone(const std::string& id, const VoidResultCallback& result) { @@ -477,26 +411,6 @@ struct Fixture { {} }; -std::string getProvisionConfigPath(const std::string& zone) -{ - namespace fs = boost::filesystem; - ZonesManagerConfig managerConfig; - loadFromFile(TEST_CONFIG_PATH, managerConfig); - for (const auto& zonesPath : managerConfig.zoneConfigs) { - ZoneConfig zoneConfig; - const fs::path configConfigPath = fs::absolute(zonesPath, - fs::path(TEST_CONFIG_PATH).parent_path()); - - loadFromFile(configConfigPath.string(), zoneConfig); - if (zoneConfig.name == zone) { - const fs::path base = fs::path(managerConfig.zonesPath) / fs::path(zone); - return fs::absolute(PROVISON_CONFIG_FILE, base).string(); - } - } - BOOST_FAIL("There is no provision config file for " + zone); - return std::string(); -} - } // namespace @@ -1093,71 +1007,6 @@ BOOST_AUTO_TEST_CASE(CreateDestroyZoneTest) BOOST_CHECK_EQUAL(cm.getRunningForegroundZoneId(), ""); } -BOOST_AUTO_TEST_CASE(DeclareFile) -{ - const std::string zone = EXPECTED_DBUSES_NO_DBUS.begin()->first; - const std::string provisionConfigPath = getProvisionConfigPath(zone); - - ZonesManager cm(TEST_CONFIG_PATH); - DbusAccessory dbus(DbusAccessory::HOST_ID); - dbus.callMethodDeclareFile(zone, 1, "path", 0747, 0777); - dbus.callMethodDeclareFile(zone, 2, "path", 0747, 0777); - - ZoneProvisioning config; - BOOST_REQUIRE_NO_THROW(loadFromFile(provisionConfigPath, config)); - BOOST_REQUIRE_EQUAL(config.units.size(), 2); - BOOST_REQUIRE(config.units[0].is()); - BOOST_REQUIRE(config.units[1].is()); - const ZoneProvisioning::File& unit = config.units[0].as(); - BOOST_CHECK_EQUAL(unit.type, 1); - BOOST_CHECK_EQUAL(unit.path, "path"); - BOOST_CHECK_EQUAL(unit.flags, 0747); - BOOST_CHECK_EQUAL(unit.mode, 0777); -} - -BOOST_AUTO_TEST_CASE(DeclareMount) -{ - const std::string zone = EXPECTED_DBUSES_NO_DBUS.begin()->first; - const std::string provisionConfigPath = getProvisionConfigPath(zone); - - ZonesManager cm(TEST_CONFIG_PATH); - DbusAccessory dbus(DbusAccessory::HOST_ID); - dbus.callMethodDeclareMount("/fake/path1", zone, "/fake/path2", "tmpfs", 077, "fake"); - dbus.callMethodDeclareMount("/fake/path2", zone, "/fake/path2", "tmpfs", 077, "fake"); - - ZoneProvisioning config; - BOOST_REQUIRE_NO_THROW(loadFromFile(provisionConfigPath, config)); - BOOST_REQUIRE_EQUAL(config.units.size(), 2); - BOOST_REQUIRE(config.units[0].is()); - BOOST_REQUIRE(config.units[1].is()); - const ZoneProvisioning::Mount& unit = config.units[0].as(); - BOOST_CHECK_EQUAL(unit.source, "/fake/path1"); - BOOST_CHECK_EQUAL(unit.target, "/fake/path2"); - BOOST_CHECK_EQUAL(unit.type, "tmpfs"); - BOOST_CHECK_EQUAL(unit.flags, 077); - BOOST_CHECK_EQUAL(unit.data, "fake"); -} - -BOOST_AUTO_TEST_CASE(DeclareLink) -{ - const std::string zone = EXPECTED_DBUSES_NO_DBUS.begin()->first; - const std::string provisionConfigPath = getProvisionConfigPath(zone); - - ZonesManager cm(TEST_CONFIG_PATH); - DbusAccessory dbus(DbusAccessory::HOST_ID); - dbus.callMethodDeclareLink("/fake/path1", zone, "/fake/path2"); - dbus.callMethodDeclareLink("/fake/path2", zone, "/fake/path2"); - - ZoneProvisioning config; - BOOST_REQUIRE_NO_THROW(loadFromFile(provisionConfigPath, config)); - BOOST_REQUIRE_EQUAL(config.units.size(), 2); - BOOST_REQUIRE(config.units[0].is()); - BOOST_REQUIRE(config.units[1].is()); - const ZoneProvisioning::Link& unit = config.units[0].as(); - BOOST_CHECK_EQUAL(unit.source, "/fake/path1"); - BOOST_CHECK_EQUAL(unit.target, "/fake/path2"); -} - BOOST_AUTO_TEST_CASE(LockUnlockZoneTest) { ZonesManager cm(TEST_DBUS_CONFIG_PATH); diff --git a/zone-daemon/CMakeLists.txt b/zone-daemon/CMakeLists.txt index 3fd611f..50bfa1a 100644 --- a/zone-daemon/CMakeLists.txt +++ b/zone-daemon/CMakeLists.txt @@ -22,7 +22,8 @@ MESSAGE(STATUS "Generating makefile for the Zone Daemon...") FILE(GLOB project_SRCS *.cpp *.hpp) FILE(GLOB common_SRCS ${COMMON_FOLDER}/dbus/*.cpp ${COMMON_FOLDER}/dbus/*.hpp ${COMMON_FOLDER}/log/*.cpp ${COMMON_FOLDER}/log/*.hpp - ${COMMON_FOLDER}/utils/*.cpp ${COMMON_FOLDER}/utils/*.hpp) + ${COMMON_FOLDER}/utils/*.cpp ${COMMON_FOLDER}/utils/*.hpp + ${COMMON_FOLDER}/*.cpp) ## Setup target ################################################################ SET(ZONE_DAEMON_CODENAME "${PROJECT_NAME}-zone-daemon") -- 2.7.4 From 719daa453260d4cbcad85d0e6dbfd2434b779625 Mon Sep 17 00:00:00 2001 From: Krzysztof Dynowski Date: Sat, 6 Dec 2014 13:05:46 +0100 Subject: [PATCH 14/16] config (dynamic): unit tests, test config extracted [Bug/Feature] test loading config defaults from json [Cause] N/A [Solution] N/A [Verification] Build, install, run tests Change-Id: Ia9ad29af5e343642d9ca7ae26b126c3efe45beab --- tests/unit_tests/config/testconfig-example.hpp | 151 +++++++++++++++++++++++++ tests/unit_tests/config/ut-configuration.cpp | 122 +------------------- tests/unit_tests/config/ut-dynvisit.cpp | 100 ++++++++++++++++ 3 files changed, 252 insertions(+), 121 deletions(-) create mode 100644 tests/unit_tests/config/testconfig-example.hpp create mode 100644 tests/unit_tests/config/ut-dynvisit.cpp diff --git a/tests/unit_tests/config/testconfig-example.hpp b/tests/unit_tests/config/testconfig-example.hpp new file mode 100644 index 0000000..35e846e --- /dev/null +++ b/tests/unit_tests/config/testconfig-example.hpp @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * + * Contact: Krzysztof Dynowski (k.dynowski@samsumg.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 + * @author Krzysztof Dynowski (k.dynowski@samsumg.com) + * @brief Test configuration struct to be used in unit tests + */ + +#ifndef TESTCONFIG_EXAMPLE_HPP +#define TESTCONFIG_EXAMPLE_HPP + +#include "config/fields.hpp" +#include "config/fields-union.hpp" + +struct TestConfig { + // subtree class + struct SubConfig { + + struct SubSubConfig { + int intVal; + bool moved; + + CONFIG_REGISTER + ( + intVal + ) + SubSubConfig() : intVal(), moved(false) {} + SubSubConfig(const SubSubConfig& config) : intVal(config.intVal), moved(false) {} + SubSubConfig(SubSubConfig&& config) : intVal(std::move(config.intVal)), moved(false) { + config.moved = true; + } + SubSubConfig& operator=(const SubSubConfig& config) { + intVal = config.intVal; + moved = false; + return *this; + } + SubSubConfig& operator=(SubSubConfig&& config) { + intVal = std::move(config.intVal); + moved = false; + config.moved = true; + return *this; + } + bool isMoved() const { + return moved; + } + }; + + int intVal; + std::vector intVector; + SubSubConfig subSubObj; + + CONFIG_REGISTER + ( + intVal, + intVector, + subSubObj + ) + }; + + struct SubConfigOption { + CONFIG_DECLARE_UNION + ( + SubConfig, + int + ) + }; + + int intVal; + std::int64_t int64Val; + std::string stringVal; + double doubleVal; + bool boolVal; + + std::vector emptyIntVector; + std::vector intVector; + std::vector stringVector; + std::vector doubleVector; + + SubConfig subObj; + std::vector subVector; + + SubConfigOption union1; + SubConfigOption union2; + std::vector unions; + + CONFIG_REGISTER + ( + intVal, + int64Val, + stringVal, + doubleVal, + boolVal, + + emptyIntVector, + intVector, + stringVector, + doubleVector, + + subObj, + subVector, + + union1, + union2, + unions + ) +}; + +/** + * JSON string used in ConfigSuite test cases + * For the purpose of these tests the order of this string + * has to be equal to the above REGISTER order + */ +const std::string jsonTestString = + "{ \"intVal\": 12345, " + "\"int64Val\": -1234567890123456789, " + "\"stringVal\": \"blah\", " + "\"doubleVal\": -1.234000, " + "\"boolVal\": true, " + "\"emptyIntVector\": [ ], " + "\"intVector\": [ 1, 2, 3 ], " + "\"stringVector\": [ \"a\", \"b\" ], " + "\"doubleVector\": [ 0.000000, 1.000000, 2.000000 ], " + "\"subObj\": { \"intVal\": 54321, \"intVector\": [ 1, 2 ], \"subSubObj\": { \"intVal\": 234 } }, " + "\"subVector\": [ { \"intVal\": 123, \"intVector\": [ 3, 4 ], \"subSubObj\": { \"intVal\": 345 } }, " + "{ \"intVal\": 456, \"intVector\": [ 5, 6 ], \"subSubObj\": { \"intVal\": 567 } } ], " + "\"union1\": { \"type\": \"int\", \"value\": 2 }, " + "\"union2\": { \"type\": \"SubConfig\", \"value\": { \"intVal\": 54321, \"intVector\": [ 1 ], " + "\"subSubObj\": { \"intVal\": 234 } } }, " + "\"unions\": [ " + "{ \"type\": \"int\", \"value\": 2 }, " + "{ \"type\": \"SubConfig\", \"value\": { \"intVal\": 54321, \"intVector\": [ 1 ], " + "\"subSubObj\": { \"intVal\": 234 } } } ] }"; + +#endif diff --git a/tests/unit_tests/config/ut-configuration.cpp b/tests/unit_tests/config/ut-configuration.cpp index 4e6bedc..8e82905 100644 --- a/tests/unit_tests/config/ut-configuration.cpp +++ b/tests/unit_tests/config/ut-configuration.cpp @@ -25,8 +25,7 @@ #include "config.hpp" #include "ut.hpp" -#include "config/fields.hpp" -#include "config/fields-union.hpp" +#include "testconfig-example.hpp" #include "config/manager.hpp" #include #include @@ -38,125 +37,6 @@ using namespace config; BOOST_AUTO_TEST_SUITE(ConfigurationSuite) -struct TestConfig { - // subtree class - struct SubConfig { - - struct SubSubConfig { - int intVal; - bool moved; - - CONFIG_REGISTER - ( - intVal - ) - SubSubConfig() : intVal(), moved(false) {} - SubSubConfig(const SubSubConfig& config) : intVal(config.intVal), moved(false) {} - SubSubConfig(SubSubConfig&& config) : intVal(std::move(config.intVal)), moved(false) { - config.moved = true; - } - SubSubConfig& operator=(const SubSubConfig& config) { - intVal = config.intVal; - moved = false; - return *this; - } - SubSubConfig& operator=(SubSubConfig&& config) { - intVal = std::move(config.intVal); - moved = false; - config.moved = true; - return *this; - } - bool isMoved() const { - return moved; - } - }; - - int intVal; - std::vector intVector; - SubSubConfig subSubObj; - - CONFIG_REGISTER - ( - intVal, - intVector, - subSubObj - ) - }; - - struct SubConfigOption { - CONFIG_DECLARE_UNION - ( - SubConfig, - int - ) - }; - - int intVal; - std::int64_t int64Val; - std::string stringVal; - double doubleVal; - bool boolVal; - - std::vector emptyIntVector; - std::vector intVector; - std::vector stringVector; - std::vector doubleVector; - - SubConfig subObj; - std::vector subVector; - - SubConfigOption union1; - SubConfigOption union2; - std::vector unions; - - CONFIG_REGISTER - ( - intVal, - int64Val, - stringVal, - doubleVal, - boolVal, - - emptyIntVector, - intVector, - stringVector, - doubleVector, - - subObj, - subVector, - - union1, - union2, - unions - ) -}; - -/** - * JSON string used in ConfigSuite test cases - * For the purpose of these tests the order of this string - * has to be equal to the above REGISTER order - */ -const std::string jsonTestString = - "{ \"intVal\": 12345, " - "\"int64Val\": -1234567890123456789, " - "\"stringVal\": \"blah\", " - "\"doubleVal\": -1.234000, " - "\"boolVal\": true, " - "\"emptyIntVector\": [ ], " - "\"intVector\": [ 1, 2, 3 ], " - "\"stringVector\": [ \"a\", \"b\" ], " - "\"doubleVector\": [ 0.000000, 1.000000, 2.000000 ], " - "\"subObj\": { \"intVal\": 54321, \"intVector\": [ 1, 2 ], \"subSubObj\": { \"intVal\": 234 } }, " - "\"subVector\": [ { \"intVal\": 123, \"intVector\": [ 3, 4 ], \"subSubObj\": { \"intVal\": 345 } }, " - "{ \"intVal\": 456, \"intVector\": [ 5, 6 ], \"subSubObj\": { \"intVal\": 567 } } ], " - "\"union1\": { \"type\": \"int\", \"value\": 2 }, " - "\"union2\": { \"type\": \"SubConfig\", \"value\": { \"intVal\": 54321, \"intVector\": [ 1 ], " - "\"subSubObj\": { \"intVal\": 234 } } }, " - "\"unions\": [ " - "{ \"type\": \"int\", \"value\": 2 }, " - "{ \"type\": \"SubConfig\", \"value\": { \"intVal\": 54321, \"intVector\": [ 1 ], " - "\"subSubObj\": { \"intVal\": 234 } } } ] }"; - // Floating point tolerance as a number of rounding errors const int TOLERANCE = 1; diff --git a/tests/unit_tests/config/ut-dynvisit.cpp b/tests/unit_tests/config/ut-dynvisit.cpp new file mode 100644 index 0000000..0a24d5c --- /dev/null +++ b/tests/unit_tests/config/ut-dynvisit.cpp @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * + * Contact: Krzysztof Dynowski (k.dynowski@samsumg.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 + * @author Krzysztof Dynowski (k.dynowski@samsumg.com) + * @brief Unit test of combine kvstore with defaults from json + */ + +#include "config.hpp" +#include "ut.hpp" +#include "testconfig-example.hpp" +#include "config/manager.hpp" +#include + +#include "config/from-kvjson-visitor.hpp" + + +using namespace config; +namespace fs = boost::filesystem; + +struct Fixture { + std::string dbPath; + + Fixture() + : dbPath(fs::unique_path("/tmp/kvstore-%%%%.db3").string()) + { + fs::remove(dbPath); + } + ~Fixture() + { + fs::remove(dbPath); + } +}; + +BOOST_FIXTURE_TEST_SUITE(DynVisitSuite, Fixture) + +void checkJsonConfig(const TestConfig& cfg, const std::string& json) +{ + TestConfig cfg2; + loadFromString(json, cfg2); + BOOST_CHECK_EQUAL(cfg2.intVal, cfg.intVal); + BOOST_CHECK_EQUAL(cfg.int64Val, cfg.int64Val); + BOOST_CHECK_EQUAL(cfg2.boolVal, cfg.boolVal); + BOOST_CHECK_EQUAL(cfg2.stringVal, cfg.stringVal); + BOOST_CHECK_EQUAL(cfg2.intVector.size(), cfg.intVector.size()); + BOOST_CHECK_EQUAL(cfg2.subObj.intVal, cfg.subObj.intVal); +} + +void checkKVConfig(const TestConfig& cfg, const std::string& db) +{ + KVStore store(db); + BOOST_CHECK_EQUAL(store.get(".intVal"), cfg.intVal); + BOOST_CHECK_EQUAL(store.get(".int64Val"), cfg.int64Val); + BOOST_CHECK_EQUAL(store.get(".boolVal"), cfg.boolVal); + BOOST_CHECK_EQUAL(store.get(".stringVal"), cfg.stringVal); + BOOST_CHECK_EQUAL(store.get(".intVector"), cfg.intVector.size()); + BOOST_CHECK_EQUAL(store.get(".subObj.intVal"), cfg.subObj.intVal); +} + +BOOST_AUTO_TEST_CASE(ReadConfigDefaults) +{ + TestConfig cfg; + loadFromKVStoreWithJson(dbPath, jsonTestString, cfg); + checkJsonConfig(cfg, jsonTestString); +} + +BOOST_AUTO_TEST_CASE(ReadConfigNoDefaults) +{ + TestConfig cfg; + loadFromKVStoreWithJson(dbPath, jsonTestString, cfg); + // modify and save config + cfg.intVal += 5; + cfg.int64Val += 7777; + cfg.boolVal = !cfg.boolVal; + cfg.stringVal += "-changed"; + config::saveToKVStore(dbPath, cfg); + + TestConfig cfg2; + loadFromKVStoreWithJson(dbPath, jsonTestString, cfg2); + checkKVConfig(cfg2, dbPath); +} + +BOOST_AUTO_TEST_SUITE_END() -- 2.7.4 From fc5836a20906dc4a50f9639d03f84f6f704f1423 Mon Sep 17 00:00:00 2001 From: Dariusz Michaluk Date: Thu, 18 Dec 2014 09:56:02 +0100 Subject: [PATCH 15/16] Implement shutdown/start function in server, client and cli [Bug/Feature] Implement shutdown/start function in server, client and cli [Cause] N/A [Solution] N/A [Verification] Build, run appropriate function (through cli) Change-Id: Ib06feff13305db134cf3415c38e83d0e836f149a Signed-off-by: Dariusz Michaluk --- cli/command-line-interface.cpp | 22 ++++ cli/command-line-interface.hpp | 14 +++ cli/main.cpp | 16 +++ client/vasum-client-impl.cpp | 14 +-- server/host-connection.cpp | 38 ++++++- server/host-connection.hpp | 18 ++++ server/host-dbus-definitions.hpp | 20 ++-- server/zones-manager.cpp | 153 +++++++++++++++++++-------- server/zones-manager.hpp | 24 +++-- tests/unit_tests/client/ut-client.cpp | 14 +++ tests/unit_tests/server/ut-zones-manager.cpp | 96 +++++++++++++++-- 11 files changed, 347 insertions(+), 82 deletions(-) diff --git a/cli/command-line-interface.cpp b/cli/command-line-interface.cpp index d7d8439..8120ca8 100644 --- a/cli/command-line-interface.cpp +++ b/cli/command-line-interface.cpp @@ -165,6 +165,28 @@ void destroy_zone(int pos, int argc, const char** argv) one_shot(bind(vsm_destroy_zone, _1, argv[pos + 1], 1)); } +void shutdown_zone(int pos, int argc, const char** argv) +{ + using namespace std::placeholders; + + if (argc <= pos + 1) { + throw runtime_error("Not enough parameters"); + } + + one_shot(bind(vsm_shutdown_zone, _1, argv[pos + 1])); +} + +void start_zone(int pos, int argc, const char** argv) +{ + using namespace std::placeholders; + + if (argc <= pos + 1) { + throw runtime_error("Not enough parameters"); + } + + one_shot(bind(vsm_start_zone, _1, argv[pos + 1])); +} + void lock_zone(int pos, int argc, const char** argv) { using namespace std::placeholders; diff --git a/cli/command-line-interface.hpp b/cli/command-line-interface.hpp index d2fe6be..789abd6 100644 --- a/cli/command-line-interface.hpp +++ b/cli/command-line-interface.hpp @@ -118,6 +118,20 @@ void create_zone(int pos, int argc, const char** argv); void destroy_zone(int pos, int argc, const char** argv); /** + * Parses command line arguments and call vsm_shutdown_zone + * + * @see vsm_shutdown_zone + */ +void shutdown_zone(int pos, int argc, const char** argv); + +/** + * Parses command line arguments and call vsm_start_zone + * + * @see vsm_start_zone + */ +void start_zone(int pos, int argc, const char** argv); + +/** * Parses command line arguments and call vsm_lock_zone * * @see vsm_lock_zone diff --git a/cli/main.cpp b/cli/main.cpp index 448d32d..b91d4e1 100644 --- a/cli/main.cpp +++ b/cli/main.cpp @@ -60,6 +60,22 @@ std::map commands = { } }, { + "shutdown_zone", { + shutdown_zone, + "shutdown_zone zone_id", + "Shutdown zone", + {{"zone_id", "id zone name"}} + } + }, + { + "start_zone", { + start_zone, + "start_zone zone_id", + "Start zone", + {{"zone_id", "id zone name"}} + } + }, + { "lock_zone", { lock_zone, "lock_zone zone_id", diff --git a/client/vasum-client-impl.cpp b/client/vasum-client-impl.cpp index 2aa923d..749b155 100644 --- a/client/vasum-client-impl.cpp +++ b/client/vasum-client-impl.cpp @@ -460,16 +460,18 @@ VsmStatus Client::vsm_destroy_zone(const char* id) noexcept return callMethod(HOST_INTERFACE, api::host::METHOD_DESTROY_ZONE, args_in); } -VsmStatus Client::vsm_shutdown_zone(const char*) noexcept +VsmStatus Client::vsm_shutdown_zone(const char* id) noexcept { - mStatus = Status(VSMCLIENT_OTHER_ERROR, "Not implemented"); - return vsm_get_status(); + assert(id); + GVariant* args_in = g_variant_new("(s)", id); + return callMethod(HOST_INTERFACE, api::host::METHOD_SHUTDOWN_ZONE, args_in); } -VsmStatus Client::vsm_start_zone(const char*) noexcept +VsmStatus Client::vsm_start_zone(const char* id) noexcept { - mStatus = Status(VSMCLIENT_OTHER_ERROR, "Not implemented"); - return vsm_get_status(); + assert(id); + GVariant* args_in = g_variant_new("(s)", id); + return callMethod(HOST_INTERFACE, api::host::METHOD_START_ZONE, args_in); } VsmStatus Client::vsm_lock_zone(const char* id) noexcept diff --git a/server/host-connection.cpp b/server/host-connection.cpp index 1eed929..621e3a2 100644 --- a/server/host-connection.cpp +++ b/server/host-connection.cpp @@ -160,6 +160,16 @@ void HostConnection::setDestroyZoneCallback(const DestroyZoneCallback& callback) mDestroyZoneCallback = callback; } +void HostConnection::setShutdownZoneCallback(const ShutdownZoneCallback& callback) +{ + mShutdownZoneCallback = callback; +} + +void HostConnection::setStartZoneCallback(const StartZoneCallback& callback) +{ + mStartZoneCallback = callback; +} + void HostConnection::setLockZoneCallback(const LockZoneCallback& callback) { mLockZoneCallback = callback; @@ -172,10 +182,10 @@ void HostConnection::setUnlockZoneCallback(const UnlockZoneCallback& callback) void HostConnection::onMessageCall(const std::string& objectPath, - const std::string& interface, - const std::string& methodName, - GVariant* parameters, - dbus::MethodResultBuilder::Pointer result) + const std::string& interface, + const std::string& methodName, + GVariant* parameters, + dbus::MethodResultBuilder::Pointer result) { if (objectPath != api::host::OBJECT_PATH || interface != api::host::INTERFACE) { return; @@ -317,6 +327,24 @@ void HostConnection::onMessageCall(const std::string& objectPath, } } + if (methodName == api::host::METHOD_SHUTDOWN_ZONE) { + const gchar* id = NULL; + g_variant_get(parameters, "(&s)", &id); + + if (mShutdownZoneCallback){ + mShutdownZoneCallback(id, result); + } + } + + if (methodName == api::host::METHOD_START_ZONE) { + const gchar* id = NULL; + g_variant_get(parameters, "(&s)", &id); + + if (mStartZoneCallback){ + mStartZoneCallback(id, result); + } + } + if (methodName == api::host::METHOD_LOCK_ZONE) { const gchar* id = NULL; g_variant_get(parameters, "(&s)", &id); @@ -353,7 +381,7 @@ void HostConnection::proxyCallAsync(const std::string& busName, } void HostConnection::signalZoneDbusState(const std::string& zoneId, - const std::string& dbusAddress) + const std::string& dbusAddress) { GVariant* parameters = g_variant_new("(ss)", zoneId.c_str(), dbusAddress.c_str()); mDbusConnection->emitSignal(api::host::OBJECT_PATH, diff --git a/server/host-connection.hpp b/server/host-connection.hpp index 68e3a73..49e2a08 100644 --- a/server/host-connection.hpp +++ b/server/host-connection.hpp @@ -91,6 +91,12 @@ public: )> DestroyZoneCallback; typedef std::function ShutdownZoneCallback; + typedef std::function StartZoneCallback; + typedef std::function LockZoneCallback; typedef std::function" " " " " + " " + " " + " " + " " + " " + " " " " " " " " diff --git a/server/zones-manager.cpp b/server/zones-manager.cpp index 1f9bef0..dcfa04c 100644 --- a/server/zones-manager.cpp +++ b/server/zones-manager.cpp @@ -90,17 +90,17 @@ ZonesManager::ZonesManager(const std::string& managerConfigPath) mHostConnection.setProxyCallCallback(bind(&ZonesManager::handleProxyCall, this, HOST_ID, _1, _2, _3, _4, _5, _6, _7)); - mHostConnection.setGetZoneDbusesCallback(bind( - &ZonesManager::handleGetZoneDbuses, this, _1)); + mHostConnection.setGetZoneDbusesCallback(bind(&ZonesManager::handleGetZoneDbuses, + this, _1)); mHostConnection.setGetZoneIdsCallback(bind(&ZonesManager::handleGetZoneIdsCall, - this, _1)); + this, _1)); mHostConnection.setGetActiveZoneIdCallback(bind(&ZonesManager::handleGetActiveZoneIdCall, - this, _1)); + this, _1)); mHostConnection.setGetZoneInfoCallback(bind(&ZonesManager::handleGetZoneInfoCall, - this, _1, _2)); + this, _1, _2)); mHostConnection.setDeclareFileCallback(bind(&ZonesManager::handleDeclareFileCall, this, _1, _2, _3, _4, _5, _6)); @@ -112,19 +112,25 @@ ZonesManager::ZonesManager(const std::string& managerConfigPath) this, _1, _2, _3, _4)); mHostConnection.setSetActiveZoneCallback(bind(&ZonesManager::handleSetActiveZoneCall, - this, _1, _2)); + this, _1, _2)); mHostConnection.setCreateZoneCallback(bind(&ZonesManager::handleCreateZoneCall, - this, _1, _2)); + this, _1, _2)); mHostConnection.setDestroyZoneCallback(bind(&ZonesManager::handleDestroyZoneCall, - this, _1, _2)); + this, _1, _2)); + + mHostConnection.setShutdownZoneCallback(bind(&ZonesManager::handleShutdownZoneCall, + this, _1, _2)); + + mHostConnection.setStartZoneCallback(bind(&ZonesManager::handleStartZoneCall, + this, _1, _2)); mHostConnection.setLockZoneCallback(bind(&ZonesManager::handleLockZoneCall, - this, _1, _2)); + this, _1, _2)); mHostConnection.setUnlockZoneCallback(bind(&ZonesManager::handleUnlockZoneCall, - this, _1, _2)); + this, _1, _2)); for (auto& zoneConfig : mConfig.zoneConfigs) { createZone(zoneConfig); @@ -378,8 +384,8 @@ void ZonesManager::setZonesDetachOnExit() } void ZonesManager::notifyActiveZoneHandler(const std::string& caller, - const std::string& application, - const std::string& message) + const std::string& application, + const std::string& message) { LOGI("notifyActiveZoneHandler(" << caller << ", " << application << ", " << message << ") called"); @@ -412,9 +418,9 @@ void ZonesManager::displayOffHandler(const std::string& /*caller*/) } void ZonesManager::handleZoneMoveFileRequest(const std::string& srcZoneId, - const std::string& dstZoneId, - const std::string& path, - dbus::MethodResultBuilder::Pointer result) + const std::string& dstZoneId, + const std::string& path, + dbus::MethodResultBuilder::Pointer result) { // TODO: this implementation is only a placeholder. // There are too many unanswered questions and security concerns: @@ -493,13 +499,13 @@ void ZonesManager::handleZoneMoveFileRequest(const std::string& srcZoneId, } void ZonesManager::handleProxyCall(const std::string& caller, - const std::string& target, - const std::string& targetBusName, - const std::string& targetObjectPath, - const std::string& targetInterface, - const std::string& targetMethod, - GVariant* parameters, - dbus::MethodResultBuilder::Pointer result) + const std::string& target, + const std::string& targetBusName, + const std::string& targetObjectPath, + const std::string& targetInterface, + const std::string& targetMethod, + GVariant* parameters, + dbus::MethodResultBuilder::Pointer result) { if (!mProxyCallPolicy->isProxyCallAllowed(caller, target, @@ -569,7 +575,7 @@ void ZonesManager::handleGetZoneDbuses(dbus::MethodResultBuilder::Pointer result } void ZonesManager::handleDbusStateChanged(const std::string& zoneId, - const std::string& dbusAddress) + const std::string& dbusAddress) { mHostConnection.signalZoneDbusState(zoneId, dbusAddress); } @@ -603,7 +609,7 @@ void ZonesManager::handleGetActiveZoneIdCall(dbus::MethodResultBuilder::Pointer } void ZonesManager::handleGetZoneInfoCall(const std::string& id, - dbus::MethodResultBuilder::Pointer result) + dbus::MethodResultBuilder::Pointer result) { LOGI("GetZoneInfo call"); @@ -637,11 +643,11 @@ void ZonesManager::handleGetZoneInfoCall(const std::string& id, } void ZonesManager::handleDeclareFileCall(const std::string& zone, - const int32_t& type, - const std::string& path, - const int32_t& flags, - const int32_t& mode, - dbus::MethodResultBuilder::Pointer result) + const int32_t& type, + const std::string& path, + const int32_t& flags, + const int32_t& mode, + dbus::MethodResultBuilder::Pointer result) { LOGI("DeclareFile call"); @@ -660,12 +666,12 @@ void ZonesManager::handleDeclareFileCall(const std::string& zone, } void ZonesManager::handleDeclareMountCall(const std::string& source, - const std::string& zone, - const std::string& target, - const std::string& type, - const uint64_t& flags, - const std::string& data, - dbus::MethodResultBuilder::Pointer result) + const std::string& zone, + const std::string& target, + const std::string& type, + const uint64_t& flags, + const std::string& data, + dbus::MethodResultBuilder::Pointer result) { LOGI("DeclareMount call"); @@ -684,9 +690,9 @@ void ZonesManager::handleDeclareMountCall(const std::string& source, } void ZonesManager::handleDeclareLinkCall(const std::string& source, - const std::string& zone, - const std::string& target, - dbus::MethodResultBuilder::Pointer result) + const std::string& zone, + const std::string& target, + dbus::MethodResultBuilder::Pointer result) { LOGI("DeclareLink call"); try { @@ -704,7 +710,7 @@ void ZonesManager::handleDeclareLinkCall(const std::string& source, } void ZonesManager::handleSetActiveZoneCall(const std::string& id, - dbus::MethodResultBuilder::Pointer result) + dbus::MethodResultBuilder::Pointer result) { LOGI("SetActiveZone call; Id=" << id ); @@ -730,8 +736,8 @@ void ZonesManager::handleSetActiveZoneCall(const std::string& id, void ZonesManager::generateNewConfig(const std::string& id, - const std::string& templatePath, - const std::string& resultPath) + const std::string& templatePath, + const std::string& resultPath) { namespace fs = boost::filesystem; @@ -781,7 +787,7 @@ void ZonesManager::generateNewConfig(const std::string& id, } void ZonesManager::handleCreateZoneCall(const std::string& id, - dbus::MethodResultBuilder::Pointer result) + dbus::MethodResultBuilder::Pointer result) { if (id.empty()) { LOGE("Failed to add zone - invalid name."); @@ -863,7 +869,7 @@ void ZonesManager::handleCreateZoneCall(const std::string& id, } void ZonesManager::handleDestroyZoneCall(const std::string& id, - dbus::MethodResultBuilder::Pointer result) + dbus::MethodResultBuilder::Pointer result) { Lock lock(mMutex); @@ -889,8 +895,65 @@ void ZonesManager::handleDestroyZoneCall(const std::string& id, mWorker->addTask(destroyer); } +void ZonesManager::handleShutdownZoneCall(const std::string& id, + dbus::MethodResultBuilder::Pointer result) +{ + LOGI("ShutdownZone call; Id=" << id ); + + Lock lock(mMutex); + + if (mZones.find(id) == mZones.end()) { + LOGE("Failed to shutdown zone - no such zone id: " << id); + result->setError(api::ERROR_INVALID_ID, "No such zone id"); + return; + } + + LOGT("Shutdown zone " << id); + + auto shutdown = [id, result, this] { + try { + mZones[id]->stop(); + } catch (ZoneOperationException& e) { + LOGE("Error during zone shutdown: " << e.what()); + result->setError(api::ERROR_INTERNAL, "Failed to shutdown zone"); + return; + } + result->setVoid(); + }; + + mWorker->addTask(shutdown); +} + +void ZonesManager::handleStartZoneCall(const std::string& id, + dbus::MethodResultBuilder::Pointer result) +{ + LOGI("StartZone call; Id=" << id ); + + Lock lock(mMutex); + + if (mZones.find(id) == mZones.end()) { + LOGE("Failed to start zone - no such zone id: " << id); + result->setError(api::ERROR_INVALID_ID, "No such zone id"); + return; + } + + LOGT("Start zone " << id); + + auto resultCallback = [this, id, result](bool succeeded) { + if (succeeded) { + focus(id); + result->setVoid(); + } else { + LOGE("Failed to start zone."); + result->setError(api::ERROR_INTERNAL, "Failed to start zone"); + } + }; + + mZones[id]->startAsync(resultCallback); +} + void ZonesManager::handleLockZoneCall(const std::string& id, - dbus::MethodResultBuilder::Pointer result) + dbus::MethodResultBuilder::Pointer result) { LOGI("LockZone call; Id=" << id ); @@ -923,7 +986,7 @@ void ZonesManager::handleLockZoneCall(const std::string& id, } void ZonesManager::handleUnlockZoneCall(const std::string& id, - dbus::MethodResultBuilder::Pointer result) + dbus::MethodResultBuilder::Pointer result) { LOGI("UnlockZone call; Id=" << id ); diff --git a/server/zones-manager.hpp b/server/zones-manager.hpp index 2ed5312..ccf834a 100644 --- a/server/zones-manager.hpp +++ b/server/zones-manager.hpp @@ -127,13 +127,13 @@ private: const std::string& resultPath); void notifyActiveZoneHandler(const std::string& caller, - const std::string& appliaction, - const std::string& message); + const std::string& appliaction, + const std::string& message); void displayOffHandler(const std::string& caller); void handleZoneMoveFileRequest(const std::string& srcZoneId, - const std::string& dstZoneId, - const std::string& path, - dbus::MethodResultBuilder::Pointer result); + const std::string& dstZoneId, + const std::string& path, + dbus::MethodResultBuilder::Pointer result); void handleProxyCall(const std::string& caller, const std::string& target, const std::string& targetBusName, @@ -165,15 +165,19 @@ private: const std::string& target, dbus::MethodResultBuilder::Pointer result); void handleSetActiveZoneCall(const std::string& id, - dbus::MethodResultBuilder::Pointer result); + dbus::MethodResultBuilder::Pointer result); void handleCreateZoneCall(const std::string& id, - dbus::MethodResultBuilder::Pointer result); + dbus::MethodResultBuilder::Pointer result); void handleDestroyZoneCall(const std::string& id, - dbus::MethodResultBuilder::Pointer result); + dbus::MethodResultBuilder::Pointer result); + void handleShutdownZoneCall(const std::string& id, + dbus::MethodResultBuilder::Pointer result); + void handleStartZoneCall(const std::string& id, + dbus::MethodResultBuilder::Pointer result); void handleLockZoneCall(const std::string& id, - dbus::MethodResultBuilder::Pointer result); + dbus::MethodResultBuilder::Pointer result); void handleUnlockZoneCall(const std::string& id, - dbus::MethodResultBuilder::Pointer result); + dbus::MethodResultBuilder::Pointer result); }; diff --git a/tests/unit_tests/client/ut-client.cpp b/tests/unit_tests/client/ut-client.cpp index 1e23e81..180b5eb 100644 --- a/tests/unit_tests/client/ut-client.cpp +++ b/tests/unit_tests/client/ut-client.cpp @@ -221,6 +221,20 @@ BOOST_AUTO_TEST_CASE(CreateZoneTest) vsm_client_free(client); } +BOOST_AUTO_TEST_CASE(StartShutdownZoneTest) +{ + const std::string newActiveZoneId = "ut-zones-manager-console1-dbus"; + + VsmClient client = vsm_client_create(); + VsmStatus status = vsm_connect(client); + BOOST_REQUIRE_EQUAL(VSMCLIENT_SUCCESS, status); + status = vsm_shutdown_zone(client, newActiveZoneId.c_str()); + BOOST_REQUIRE_EQUAL(VSMCLIENT_SUCCESS, status); + status = vsm_start_zone(client, newActiveZoneId.c_str()); + BOOST_REQUIRE_EQUAL(VSMCLIENT_SUCCESS, status); + vsm_client_free(client); +} + BOOST_AUTO_TEST_CASE(LockUnlockZoneTest) { const std::string newActiveZoneId = "ut-zones-manager-console2-dbus"; diff --git a/tests/unit_tests/server/ut-zones-manager.cpp b/tests/unit_tests/server/ut-zones-manager.cpp index 3c62478..06af8eb 100644 --- a/tests/unit_tests/server/ut-zones-manager.cpp +++ b/tests/unit_tests/server/ut-zones-manager.cpp @@ -310,7 +310,7 @@ public: } void callAsyncMethodCreateZone(const std::string& id, - const VoidResultCallback& result) + const VoidResultCallback& result) { auto asyncResult = [result](dbus::AsyncMethodCallResult& asyncMethodCallResult) { BOOST_CHECK(g_variant_is_of_type(asyncMethodCallResult.get(), G_VARIANT_TYPE_UNIT)); @@ -329,7 +329,7 @@ public: } void callAsyncMethodDestroyZone(const std::string& id, - const VoidResultCallback& result) + const VoidResultCallback& result) { auto asyncResult = [result](dbus::AsyncMethodCallResult& asyncMethodCallResult) { BOOST_CHECK(g_variant_is_of_type(asyncMethodCallResult.get(), G_VARIANT_TYPE_UNIT)); @@ -347,6 +347,44 @@ public: asyncResult); } + void callAsyncMethodShutdownZone(const std::string& id, + const VoidResultCallback& result) + { + auto asyncResult = [result](dbus::AsyncMethodCallResult& asyncMethodCallResult) { + BOOST_CHECK(g_variant_is_of_type(asyncMethodCallResult.get(), G_VARIANT_TYPE_UNIT)); + result(); + }; + + assert(isHost()); + GVariant* parameters = g_variant_new("(s)", id.c_str()); + mClient->callMethodAsync(api::host::BUS_NAME, + api::host::OBJECT_PATH, + api::host::INTERFACE, + api::host::METHOD_SHUTDOWN_ZONE, + parameters, + "()", + asyncResult); + } + + void callAsyncMethodStartZone(const std::string& id, + const VoidResultCallback& result) + { + auto asyncResult = [result](dbus::AsyncMethodCallResult& asyncMethodCallResult) { + BOOST_CHECK(g_variant_is_of_type(asyncMethodCallResult.get(), G_VARIANT_TYPE_UNIT)); + result(); + }; + + assert(isHost()); + GVariant* parameters = g_variant_new("(s)", id.c_str()); + mClient->callMethodAsync(api::host::BUS_NAME, + api::host::OBJECT_PATH, + api::host::INTERFACE, + api::host::METHOD_START_ZONE, + parameters, + "()", + asyncResult); + } + void callMethodLockZone(const std::string& id) { assert(isHost()); @@ -904,8 +942,8 @@ BOOST_AUTO_TEST_CASE(GetZoneIdsTest) DbusAccessory dbus(DbusAccessory::HOST_ID); std::vector zoneIds = {"ut-zones-manager-console1-dbus", - "ut-zones-manager-console2-dbus", - "ut-zones-manager-console3-dbus"}; + "ut-zones-manager-console2-dbus", + "ut-zones-manager-console3-dbus"}; std::vector returnedIds = dbus.callMethodGetZoneIds(); BOOST_CHECK(std::is_permutation(returnedIds.begin(), @@ -921,8 +959,8 @@ BOOST_AUTO_TEST_CASE(GetActiveZoneIdTest) DbusAccessory dbus(DbusAccessory::HOST_ID); std::vector zoneIds = {"ut-zones-manager-console1-dbus", - "ut-zones-manager-console2-dbus", - "ut-zones-manager-console3-dbus"}; + "ut-zones-manager-console2-dbus", + "ut-zones-manager-console3-dbus"}; for (std::string& zoneId: zoneIds){ cm.focus(zoneId); @@ -941,8 +979,8 @@ BOOST_AUTO_TEST_CASE(SetActiveZoneTest) DbusAccessory dbus(DbusAccessory::HOST_ID); std::vector zoneIds = {"ut-zones-manager-console1-dbus", - "ut-zones-manager-console2-dbus", - "ut-zones-manager-console3-dbus"}; + "ut-zones-manager-console2-dbus", + "ut-zones-manager-console3-dbus"}; for (std::string& zoneId: zoneIds){ dbus.callMethodSetActiveZone(zoneId); @@ -1007,6 +1045,44 @@ BOOST_AUTO_TEST_CASE(CreateDestroyZoneTest) BOOST_CHECK_EQUAL(cm.getRunningForegroundZoneId(), ""); } +BOOST_AUTO_TEST_CASE(StartShutdownZoneTest) +{ + const std::string zone1 = "ut-zones-manager-console1-dbus"; + const std::string zone2 = "ut-zones-manager-console2-dbus"; + + ZonesManager cm(TEST_DBUS_CONFIG_PATH); + + Latch callDone; + auto resultCallback = [&]() { + callDone.set(); + }; + + DbusAccessory dbus(DbusAccessory::HOST_ID); + + // start zone1 + dbus.callAsyncMethodStartZone(zone1, resultCallback); + BOOST_REQUIRE(callDone.wait(EVENT_TIMEOUT)); + BOOST_CHECK(cm.isRunning(zone1)); + BOOST_CHECK_EQUAL(cm.getRunningForegroundZoneId(), zone1); + + // start zone2 + dbus.callAsyncMethodStartZone(zone2, resultCallback); + BOOST_REQUIRE(callDone.wait(EVENT_TIMEOUT)); + BOOST_CHECK(cm.isRunning(zone2)); + BOOST_CHECK_EQUAL(cm.getRunningForegroundZoneId(), zone2); + + // shutdown zone2 + dbus.callAsyncMethodShutdownZone(zone2, resultCallback); + BOOST_REQUIRE(callDone.wait(EVENT_TIMEOUT)); + BOOST_CHECK(!cm.isRunning(zone2)); + + // shutdown zone1 + dbus.callAsyncMethodShutdownZone(zone1, resultCallback); + BOOST_REQUIRE(callDone.wait(EVENT_TIMEOUT)); + BOOST_CHECK(!cm.isRunning(zone1)); + BOOST_CHECK_EQUAL(cm.getRunningForegroundZoneId(), ""); +} + BOOST_AUTO_TEST_CASE(LockUnlockZoneTest) { ZonesManager cm(TEST_DBUS_CONFIG_PATH); @@ -1015,8 +1091,8 @@ BOOST_AUTO_TEST_CASE(LockUnlockZoneTest) DbusAccessory dbus(DbusAccessory::HOST_ID); std::vector zoneIds = {"ut-zones-manager-console1-dbus", - "ut-zones-manager-console2-dbus", - "ut-zones-manager-console3-dbus"}; + "ut-zones-manager-console2-dbus", + "ut-zones-manager-console3-dbus"}; for (std::string& zoneId: zoneIds){ dbus.callMethodLockZone(zoneId); -- 2.7.4 From d35e4806694cbc6eadea4d0355b75b62de14fb2e Mon Sep 17 00:00:00 2001 From: Piotr Bartosiewicz Date: Fri, 19 Dec 2014 14:56:56 +0100 Subject: [PATCH 16/16] Implement grant and revoke device [Bug/Feature] Was not implemented [Cause] N/A [Solution] N/A [Verification] Run tests, run grant/revoke device cli commands Change-Id: I4cfa67d99cb45d4747cfd780776bc2340c5f4535 --- cli/command-line-interface.cpp | 24 +++++++ cli/command-line-interface.hpp | 17 ++++- cli/main.cpp | 18 +++++ client/vasum-client-impl.cpp | 18 +++-- client/vasum-client-impl.hpp | 6 +- client/vasum-client.cpp | 10 +-- client/vasum-client.h | 8 +-- common/lxc/cgroup.cpp | 120 ++++++++++++++++++++++++++++++++++ common/lxc/cgroup.hpp | 49 ++++++++++++++ server/host-connection.cpp | 37 +++++++++++ server/host-connection.hpp | 21 ++++++ server/host-dbus-definitions.hpp | 11 ++++ server/zones-manager.cpp | 86 ++++++++++++++++++++++++ server/zones-manager.hpp | 7 ++ tests/unit_tests/client/ut-client.cpp | 39 ++++++++++- 15 files changed, 451 insertions(+), 20 deletions(-) create mode 100644 common/lxc/cgroup.cpp create mode 100644 common/lxc/cgroup.hpp diff --git a/cli/command-line-interface.cpp b/cli/command-line-interface.cpp index 8120ca8..1222a86 100644 --- a/cli/command-line-interface.cpp +++ b/cli/command-line-interface.cpp @@ -31,6 +31,7 @@ #include #include #include +#include using namespace std; @@ -222,5 +223,28 @@ void lookup_zone_by_id(int pos, int argc, const char** argv) vsm_zone_free(zone); } +void grant_device(int pos, int argc, const char** argv) +{ + using namespace std::placeholders; + + if (argc <= pos + 2) { + throw runtime_error("Not enough parameters"); + } + + uint32_t flags = O_RDWR; + one_shot(bind(vsm_grant_device, _1, argv[pos + 1], argv[pos + 2], flags)); +} + +void revoke_device(int pos, int argc, const char** argv) +{ + using namespace std::placeholders; + + if (argc <= pos + 2) { + throw runtime_error("Not enough parameters"); + } + + one_shot(bind(vsm_revoke_device, _1, argv[pos + 1], argv[pos + 2])); +} + } // namespace cli } // namespace vasum diff --git a/cli/command-line-interface.hpp b/cli/command-line-interface.hpp index 789abd6..6a655d8 100644 --- a/cli/command-line-interface.hpp +++ b/cli/command-line-interface.hpp @@ -25,6 +25,7 @@ #define CLI_COMMAND_LINE_INTERFACE_HPP #include +#include #include #include #include @@ -46,7 +47,7 @@ public: /** * @see CommandLineInterface::CommandLineInterface */ - typedef std::map ArgsSpec; + typedef std::vector> ArgsSpec; /** * Dummy constructor (for stl usage) @@ -152,6 +153,20 @@ void unlock_zone(int pos, int argc, const char** argv); */ void lookup_zone_by_id(int pos, int argc, const char** argv); +/** + * Parses command line arguments and call vsm_grant_device + * + * @see vsm_grant_device + */ +void grant_device(int pos, int argc, const char** argv); + +/** + * Parses command line arguments and call vsm_revoke_device + * + * @see vsm_revoke_device + */ +void revoke_device(int pos, int argc, const char** argv); + } // namespace cli } // namespace vasum diff --git a/cli/main.cpp b/cli/main.cpp index b91d4e1..1afe134 100644 --- a/cli/main.cpp +++ b/cli/main.cpp @@ -98,6 +98,24 @@ std::map commands = { "Prints informations about zone", {{"zone_id", "id zone name"}} } + }, + { + "grant_device", { + grant_device, + "grant_device zone_id device_name", + "Grants access to the given device", + {{"zone_id", "id zone name"}, + {"device_name", " device name"}} + } + }, + { + "revoke_device", { + revoke_device, + "revoke_device zone_id device_name", + "Revokes access to the given device", + {{"zone_id", "id zone name"}, + {"device_name", " device name"}} + } } }; diff --git a/client/vasum-client-impl.cpp b/client/vasum-client-impl.cpp index 749b155..0af5269 100644 --- a/client/vasum-client-impl.cpp +++ b/client/vasum-client-impl.cpp @@ -515,16 +515,22 @@ VsmStatus Client::vsm_del_state_callback(VsmSubscriptionId subscriptionId) noexc return signalUnsubscribe(subscriptionId); } -VsmStatus Client::vsm_zone_grant_device(const char*, const char*, uint32_t) noexcept +VsmStatus Client::vsm_grant_device(const char* id, const char* device, uint32_t flags) noexcept { - mStatus = Status(VSMCLIENT_OTHER_ERROR, "Not implemented"); - return vsm_get_status(); + assert(id); + assert(device); + + GVariant* args_in = g_variant_new("(ssu)", id, device, flags); + return callMethod(HOST_INTERFACE, api::host::METHOD_GRANT_DEVICE, args_in); } -VsmStatus Client::vsm_revoke_device(const char*, const char*) noexcept +VsmStatus Client::vsm_revoke_device(const char* id, const char* device) noexcept { - mStatus = Status(VSMCLIENT_OTHER_ERROR, "Not implemented"); - return vsm_get_status(); + assert(id); + assert(device); + + GVariant* args_in = g_variant_new("(ss)", id, device); + return callMethod(HOST_INTERFACE, api::host::METHOD_REVOKE_DEVICE, args_in); } VsmStatus Client::vsm_zone_get_netdevs(const char*, VsmArrayString*) noexcept diff --git a/client/vasum-client-impl.hpp b/client/vasum-client-impl.hpp index 66ffa82..f545d77 100644 --- a/client/vasum-client-impl.hpp +++ b/client/vasum-client-impl.hpp @@ -183,9 +183,9 @@ public: /** * @see ::vsm_del_state_callback */ - VsmStatus vsm_zone_grant_device(const char* id, - const char* device, - uint32_t flags) noexcept; + VsmStatus vsm_grant_device(const char* id, + const char* device, + uint32_t flags) noexcept; /** * @see ::vsm_revoke_device diff --git a/client/vasum-client.cpp b/client/vasum-client.cpp index 2102432..00a4dfb 100644 --- a/client/vasum-client.cpp +++ b/client/vasum-client.cpp @@ -196,12 +196,12 @@ API VsmStatus vsm_del_state_callback(VsmClient client, VsmSubscriptionId subscri return getClient(client).vsm_del_state_callback(subscriptionId); } -API VsmStatus vsm_zone_grant_device(VsmClient client, - const char* id, - const char* device, - uint32_t flags) +API VsmStatus vsm_grant_device(VsmClient client, + const char* id, + const char* device, + uint32_t flags) { - return getClient(client).vsm_zone_grant_device(id, device, flags); + return getClient(client).vsm_grant_device(id, device, flags); } API VsmStatus vsm_revoke_device(VsmClient client, const char* id, const char* device) diff --git a/client/vasum-client.h b/client/vasum-client.h index eb4cbba..eba04e6 100644 --- a/client/vasum-client.h +++ b/client/vasum-client.h @@ -463,10 +463,10 @@ VsmStatus vsm_del_state_callback(VsmClient client, VsmSubscriptionId subscriptio * @param[in] flags access flags * @return status of this function call */ -VsmStatus vsm_zone_grant_device(VsmClient client, - const char* zone, - const char* device, - uint32_t flags); +VsmStatus vsm_grant_device(VsmClient client, + const char* zone, + const char* device, + uint32_t flags); /** * Revoke access to device diff --git a/common/lxc/cgroup.cpp b/common/lxc/cgroup.cpp new file mode 100644 index 0000000..807ddff --- /dev/null +++ b/common/lxc/cgroup.cpp @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * + * Contact: Piotr Bartosiewicz + * + * 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 + * @author Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com) + * @brief Lxc cgroup stuff + */ + + +#include "config.hpp" +#include "logger/logger.hpp" +#include "utils/fs.hpp" + +#include +#include + + +namespace vasum { +namespace lxc { + +namespace { + +std::string flagsToPermissions(bool grant, uint32_t flags) +{ + if (!grant) { + return "rwm"; + } + switch (flags) { + case O_RDWR: + return "rwm"; + case O_RDONLY: + return "rm"; + case O_WRONLY: + return "wm"; + default: + return std::string(); + } +} + +} // namespace + +bool setCgroup(const std::string& zoneName, + const std::string& cgroupName, + const std::string& value) +{ + const std::string path = "/sys/fs/cgroup/devices/lxc/" + zoneName + "/" + cgroupName; + LOGD("Set '" << value << "' to " << path); + return utils::saveFileContent(path, value); +} + +bool isDevice(const std::string& device) +{ + struct stat devStat; + if (::stat(device.c_str(), &devStat) == -1) { + LOGD("Cannot access: " << device); + return false; + } + if (!S_ISCHR(devStat.st_mode) && !S_ISBLK(devStat.st_mode)) { + LOGD("Not a device: " << device); + return false; + } + return true; +} + +bool setDeviceAccess(const std::string& zoneName, + const std::string& device, + bool grant, + uint32_t flags) +{ + struct stat devStat; + if (::stat(device.c_str(), &devStat) == -1) { + LOGD("Cannot access: " << device); + return false; + } + + char type; + if (S_ISCHR(devStat.st_mode)) { + type = 'c'; + } else if (S_ISBLK(devStat.st_mode)) { + type = 'b'; + } else { + LOGD("Not a device: " << device); + return false; + } + + std::string perm = flagsToPermissions(grant, flags); + if (perm.empty()) { + LOGD("Invalid flags"); + return false; + } + + int major = major(devStat.st_rdev); + int minor = minor(devStat.st_rdev); + + char value[100]; + snprintf(value, sizeof(value), "%c %d:%d %s", type, major, minor, perm.c_str()); + + std::string name = grant ? "devices.allow" : "devices.deny"; + return setCgroup(zoneName, name, value); +} + + +} // namespace lxc +} // namespace vasum diff --git a/common/lxc/cgroup.hpp b/common/lxc/cgroup.hpp new file mode 100644 index 0000000..2592054 --- /dev/null +++ b/common/lxc/cgroup.hpp @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * + * Contact: Piotr Bartosiewicz + * + * 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 + * @author Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com) + * @brief Lxc cgroup stuff + */ + +#ifndef COMMON_LXC_CGROUP_HPP +#define COMMON_LXC_CGROUP_HPP + +#include + +namespace vasum { +namespace lxc { + +bool setCgroup(const std::string& zoneName, + const std::string& cgroupName, + const std::string& value); + +bool isDevice(const std::string& device); + +bool setDeviceAccess(const std::string& zoneName, + const std::string& device, + bool grant, + uint32_t flags); + + +} // namespace lxc +} // namespace vasum + + +#endif // COMMON_LXC_CGROUP_HPP diff --git a/server/host-connection.cpp b/server/host-connection.cpp index 621e3a2..db6130f 100644 --- a/server/host-connection.cpp +++ b/server/host-connection.cpp @@ -180,6 +180,16 @@ void HostConnection::setUnlockZoneCallback(const UnlockZoneCallback& callback) mUnlockZoneCallback = callback; } +void HostConnection::setGrantDeviceCallback(const GrantDeviceCallback& callback) +{ + mGrantDeviceCallback = callback; +} + +void HostConnection::setRevokeDeviceCallback(const RevokeDeviceCallback& callback) +{ + mRevokeDeviceCallback = callback; +} + void HostConnection::onMessageCall(const std::string& objectPath, const std::string& interface, @@ -316,6 +326,7 @@ void HostConnection::onMessageCall(const std::string& objectPath, if (mCreateZoneCallback){ mCreateZoneCallback(id, result); } + return; } if (methodName == api::host::METHOD_DESTROY_ZONE) { @@ -325,6 +336,7 @@ void HostConnection::onMessageCall(const std::string& objectPath, if (mDestroyZoneCallback){ mDestroyZoneCallback(id, result); } + return; } if (methodName == api::host::METHOD_SHUTDOWN_ZONE) { @@ -352,6 +364,7 @@ void HostConnection::onMessageCall(const std::string& objectPath, if (mLockZoneCallback){ mLockZoneCallback(id, result); } + return; } if (methodName == api::host::METHOD_UNLOCK_ZONE) { @@ -361,6 +374,30 @@ void HostConnection::onMessageCall(const std::string& objectPath, if (mUnlockZoneCallback){ mUnlockZoneCallback(id, result); } + return; + } + + if (methodName == api::host::METHOD_GRANT_DEVICE) { + const gchar* id = NULL; + const gchar* device = NULL; + uint32_t flags; + g_variant_get(parameters, "(&s&su)", &id, &device, &flags); + + if (mGrantDeviceCallback){ + mGrantDeviceCallback(id, device, flags, result); + } + return; + } + + if (methodName == api::host::METHOD_REVOKE_DEVICE) { + const gchar* id = NULL; + const gchar* device = NULL; + g_variant_get(parameters, "(&s&s)", &id, &device); + + if (mRevokeDeviceCallback){ + mRevokeDeviceCallback(id, device, result); + } + return; } } diff --git a/server/host-connection.hpp b/server/host-connection.hpp index 49e2a08..db8cf34 100644 --- a/server/host-connection.hpp +++ b/server/host-connection.hpp @@ -101,6 +101,15 @@ public: typedef std::function UnlockZoneCallback; + typedef std::function GrantDeviceCallback; + typedef std::function RevokeDeviceCallback; /** * Register proxy call callback @@ -183,6 +192,16 @@ public: void setUnlockZoneCallback(const UnlockZoneCallback& callback); /** + * Register a callback called to grant device + */ + void setGrantDeviceCallback(const GrantDeviceCallback& callback); + + /** + * Register a callback called to revoke device + */ + void setRevokeDeviceCallback(const RevokeDeviceCallback& callback); + + /** * Make a proxy call */ void proxyCallAsync(const std::string& busName, @@ -213,6 +232,8 @@ private: StartZoneCallback mStartZoneCallback; LockZoneCallback mLockZoneCallback; UnlockZoneCallback mUnlockZoneCallback; + GrantDeviceCallback mGrantDeviceCallback; + RevokeDeviceCallback mRevokeDeviceCallback; void onNameAcquired(); void onNameLost(); diff --git a/server/host-dbus-definitions.hpp b/server/host-dbus-definitions.hpp index 74ba96d..e17261c 100644 --- a/server/host-dbus-definitions.hpp +++ b/server/host-dbus-definitions.hpp @@ -52,6 +52,8 @@ const std::string METHOD_SHUTDOWN_ZONE = "ShutdownZone"; const std::string METHOD_START_ZONE = "StartZone"; const std::string METHOD_LOCK_ZONE = "LockZone"; const std::string METHOD_UNLOCK_ZONE = "UnlockZone"; +const std::string METHOD_GRANT_DEVICE = "GrantDevice"; +const std::string METHOD_REVOKE_DEVICE = "RevokeDevice"; const std::string SIGNAL_ZONE_DBUS_STATE = "ZoneDbusState"; @@ -122,6 +124,15 @@ const std::string DEFINITION = " " " " " " + " " + " " + " " + " " + " " + " " + " " + " " + " " " " " " " " diff --git a/server/zones-manager.cpp b/server/zones-manager.cpp index dcfa04c..ff6fb7e 100644 --- a/server/zones-manager.cpp +++ b/server/zones-manager.cpp @@ -29,6 +29,7 @@ #include "zone-dbus-definitions.hpp" #include "zones-manager.hpp" #include "zone-admin.hpp" +#include "lxc/cgroup.hpp" #include "exception.hpp" #include "utils/paths.hpp" @@ -132,6 +133,12 @@ ZonesManager::ZonesManager(const std::string& managerConfigPath) mHostConnection.setUnlockZoneCallback(bind(&ZonesManager::handleUnlockZoneCall, this, _1, _2)); + mHostConnection.setGrantDeviceCallback(bind(&ZonesManager::handleGrantDeviceCall, + this, _1, _2, _3, _4)); + + mHostConnection.setRevokeDeviceCallback(bind(&ZonesManager::handleRevokeDeviceCall, + this, _1, _2, _3)); + for (auto& zoneConfig : mConfig.zoneConfigs) { createZone(zoneConfig); } @@ -1018,4 +1025,83 @@ void ZonesManager::handleUnlockZoneCall(const std::string& id, result->setVoid(); } +void ZonesManager::handleGrantDeviceCall(const std::string& id, + const std::string& device, + uint32_t flags, + dbus::MethodResultBuilder::Pointer result) +{ + LOGI("GrantDevice call; id=" << id << "; dev=" << device); + + Lock lock(mMutex); + + auto iter = mZones.find(id); + if (iter == mZones.end()) { + LOGE("Failed to grant device - no such zone id: " << id); + result->setError(api::ERROR_INVALID_ID, "No such zone id"); + return; + } + + auto& zone = *iter->second; + if (!zone.isRunning() && !zone.isPaused()) { + LOGE("Zone id=" << id << " is not running"); + result->setError(api::ERROR_INVALID_STATE, "Zone is not running"); + return; + } + + std::string devicePath = "/dev/" + device; + + if (!lxc::isDevice(devicePath)) { + LOGE("Failed to grant device - cannot acces device: " << device); + result->setError(api::ERROR_FORBIDDEN, "Cannot access device"); + return; + } + + // assume device node is created inside zone + if (!lxc::setDeviceAccess(id, devicePath, true, flags)) { + LOGE("Failed to grant device: " << device << " for zone: " << id); + result->setError(api::ERROR_INTERNAL, "Cannot grant device"); + return; + } + + result->setVoid(); +} + +void ZonesManager::handleRevokeDeviceCall(const std::string& id, + const std::string& device, + dbus::MethodResultBuilder::Pointer result) +{ + LOGI("RevokeDevice call; id=" << id << "; dev=" << device); + + Lock lock(mMutex); + + auto iter = mZones.find(id); + if (iter == mZones.end()) { + LOGE("Failed to revoke device - no such zone id: " << id); + result->setError(api::ERROR_INVALID_ID, "No such zone id"); + return; + } + + auto& zone = *iter->second; + if (!zone.isRunning() && !zone.isPaused()) { + LOGE("Zone id=" << id << " is not running"); + result->setError(api::ERROR_INVALID_STATE, "Zone is not running"); + return; + } + std::string devicePath = "/dev/" + device; + + if (!lxc::isDevice(devicePath)) { + LOGE("Failed to revoke device - cannot acces device: " << device); + result->setError(api::ERROR_FORBIDDEN, "Cannot access device"); + return; + } + + if (!lxc::setDeviceAccess(id, devicePath, false, 0)) { + LOGE("Failed to revoke device: " << device << " for zone: " << id); + result->setError(api::ERROR_INTERNAL, "Cannot revoke device"); + return; + } + + result->setVoid(); +} + } // namespace vasum diff --git a/server/zones-manager.hpp b/server/zones-manager.hpp index ccf834a..a0a2695 100644 --- a/server/zones-manager.hpp +++ b/server/zones-manager.hpp @@ -178,6 +178,13 @@ private: dbus::MethodResultBuilder::Pointer result); void handleUnlockZoneCall(const std::string& id, dbus::MethodResultBuilder::Pointer result); + void handleGrantDeviceCall(const std::string& id, + const std::string& device, + uint32_t flags, + dbus::MethodResultBuilder::Pointer result); + void handleRevokeDeviceCall(const std::string& id, + const std::string& device, + dbus::MethodResultBuilder::Pointer result); }; diff --git a/tests/unit_tests/client/ut-client.cpp b/tests/unit_tests/client/ut-client.cpp index 180b5eb..9536ccb 100644 --- a/tests/unit_tests/client/ut-client.cpp +++ b/tests/unit_tests/client/ut-client.cpp @@ -123,7 +123,22 @@ int getArrayStringLength(VsmArrayString astring, int max_len = -1) } // namespace -BOOST_FIXTURE_TEST_SUITE(Client, Fixture) +// make nice BOOST_*_EQUAL output +// (does not work inside anonymous namespace) +std::ostream& operator<<(std::ostream& out, VsmStatus status) +{ + switch(status) { + case VSMCLIENT_CUSTOM_ERROR: return out << "CUSTOM_ERROR"; + case VSMCLIENT_IO_ERROR: return out << "IO_ERROR"; + case VSMCLIENT_OPERATION_FAILED: return out << "OPERATION_FAILED"; + case VSMCLIENT_INVALID_ARGUMENT: return out << "INVALID_ARGUMENT"; + case VSMCLIENT_OTHER_ERROR: return out << "OTHER_ERROR"; + case VSMCLIENT_SUCCESS: return out << "SUCCESS"; + default: return out << "UNKNOWN(" << (int)status << ")"; + }; +} + +BOOST_FIXTURE_TEST_SUITE(ClientSuite, Fixture) BOOST_AUTO_TEST_CASE(NotRunningServerTest) { @@ -356,4 +371,26 @@ BOOST_AUTO_TEST_CASE(GetZoneIdByPidTest2) } } +BOOST_AUTO_TEST_CASE(GrantRevokeTest) +{ + const std::string zoneId = "ut-zones-manager-console2-dbus"; + const std::string dev = "tty3"; + + VsmClient client = vsm_client_create(); + BOOST_REQUIRE_EQUAL(VSMCLIENT_SUCCESS, vsm_connect(client)); + + BOOST_CHECK_EQUAL(VSMCLIENT_SUCCESS, vsm_grant_device(client, zoneId.c_str(), dev.c_str(), 0)); + BOOST_CHECK_EQUAL(VSMCLIENT_SUCCESS, vsm_revoke_device(client, zoneId.c_str(), dev.c_str())); + + BOOST_REQUIRE_EQUAL(VSMCLIENT_SUCCESS, vsm_lock_zone(client, zoneId.c_str())); + BOOST_CHECK_EQUAL(VSMCLIENT_SUCCESS, vsm_grant_device(client, zoneId.c_str(), dev.c_str(), 0)); + BOOST_REQUIRE_EQUAL(VSMCLIENT_SUCCESS, vsm_unlock_zone(client, zoneId.c_str())); + + BOOST_REQUIRE_EQUAL(VSMCLIENT_SUCCESS, vsm_shutdown_zone(client, zoneId.c_str())); + BOOST_CHECK_EQUAL(VSMCLIENT_CUSTOM_ERROR, vsm_grant_device(client, zoneId.c_str(), dev.c_str(), 0)); + BOOST_CHECK_EQUAL(VSMCLIENT_CUSTOM_ERROR, vsm_revoke_device(client, zoneId.c_str(), dev.c_str())); + + vsm_client_free(client); +} + BOOST_AUTO_TEST_SUITE_END() -- 2.7.4