From 9f9fbe48b66186939013584f675af127f30e1792 Mon Sep 17 00:00:00 2001 From: Jan Olszak Date: Wed, 12 Nov 2014 15:36:09 +0100 Subject: [PATCH 01/16] IPC: Pass error to return the value callback [Bug/Feature] Return value callback takes status enum Peer's socket is locket when processing the callbacks. [Cause] N/A [Solution] N/A [Verification] Build, install, run tests Change-Id: I74f30713e7c4fa6d8f35b79c2485137d1c9119c3 --- common/ipc/exception.hpp | 18 +++ common/ipc/internals/acceptor.cpp | 1 - common/ipc/internals/processor.cpp | 227 +++++++++++++++++++++++-------------- common/ipc/internals/processor.hpp | 38 +++++-- common/ipc/service.hpp | 4 +- common/ipc/types.cpp | 64 +++++++++++ common/ipc/types.hpp | 19 +++- tests/unit_tests/ipc/ut-ipc.cpp | 92 ++++++++++++++- 8 files changed, 359 insertions(+), 104 deletions(-) create mode 100644 common/ipc/types.cpp diff --git a/common/ipc/exception.hpp b/common/ipc/exception.hpp index 67d9c86..f9e8322 100644 --- a/common/ipc/exception.hpp +++ b/common/ipc/exception.hpp @@ -38,7 +38,25 @@ struct IPCException: public SecurityContainersException { IPCException(const std::string& error) : SecurityContainersException(error) {} }; +struct IPCParsingException: public IPCException { + IPCParsingException(const std::string& error) : IPCException(error) {} +}; + +struct IPCSerializationException: public IPCException { + IPCSerializationException(const std::string& error) : IPCException(error) {} +}; + +struct IPCPeerDisconnectedException: public IPCException { + IPCPeerDisconnectedException(const std::string& error) : IPCException(error) {} +}; + +struct IPCNaughtyPeerException: public IPCException { + IPCNaughtyPeerException(const std::string& error) : IPCException(error) {} +}; +struct IPCTimeoutException: public IPCException { + IPCTimeoutException(const std::string& error) : IPCException(error) {} +}; } diff --git a/common/ipc/internals/acceptor.cpp b/common/ipc/internals/acceptor.cpp index 9738546..193da61 100644 --- a/common/ipc/internals/acceptor.cpp +++ b/common/ipc/internals/acceptor.cpp @@ -103,7 +103,6 @@ void Acceptor::run() } LOGE("Error in poll: " << std::string(strerror(errno))); throw IPCException("Error in poll: " + std::string(strerror(errno))); - break; } // Check for incoming connections diff --git a/common/ipc/internals/processor.cpp b/common/ipc/internals/processor.cpp index 0465574..50327c0 100644 --- a/common/ipc/internals/processor.cpp +++ b/common/ipc/internals/processor.cpp @@ -36,10 +36,21 @@ #include #include - namespace security_containers { namespace ipc { +#define IGNORE_EXCEPTIONS(expr) \ + try \ + { \ + expr; \ + } \ + catch (const std::exception& e){ \ + LOGE("Callback threw an error: " << e.what()); \ + } + + + + const Processor::MethodID Processor::RETURN_METHOD_ID = std::numeric_limits::max(); Processor::Processor(const PeerCallback& newPeerCallback, @@ -98,10 +109,7 @@ Processor::PeerID Processor::addPeer(const std::shared_ptr& socketPtr) { Lock lock(mSocketsMutex); peerID = getNextPeerID(); - SocketInfo socketInfo; - socketInfo.peerID = peerID; - socketInfo.socketPtr = std::move(socketPtr); - mNewSockets.push(std::move(socketInfo)); + mNewSockets.emplace(peerID, std::move(socketPtr)); } LOGI("New peer added. Id: " << peerID); mEventQueue.send(Event::NEW_PEER); @@ -109,13 +117,29 @@ Processor::PeerID Processor::addPeer(const std::shared_ptr& socketPtr) return peerID; } -void Processor::removePeer(PeerID peerID) +void Processor::removePeer(const PeerID peerID, Status status) { LOGW("Removing naughty peer. ID: " << peerID); { Lock lock(mSocketsMutex); mSockets.erase(peerID); } + + { + // Erase associated return value callbacks + Lock lock(mReturnCallbacksMutex); + + std::shared_ptr data; + for (auto it = mReturnCallbacks.begin(); it != mReturnCallbacks.end();) { + if (it->second.peerID == peerID) { + IGNORE_EXCEPTIONS(it->second.process(status, data)); + it = mReturnCallbacks.erase(it); + } else { + ++it; + } + } + } + resetPolling(); } @@ -175,6 +199,7 @@ void Processor::run() } } + bool Processor::handleLostConnections() { std::list peersToRemove; @@ -190,14 +215,10 @@ bool Processor::handleLostConnections() } } - for (const auto peerID : peersToRemove) { - LOGT("Removing peer. ID: " << peerID); - mSockets.erase(peerID); - } } - if (!peersToRemove.empty()) { - resetPolling(); + for (const PeerID peerID : peersToRemove) { + removePeer(peerID, Status::PEER_DISCONNECTED); } return !peersToRemove.empty(); @@ -231,77 +252,102 @@ bool Processor::handleInput(const PeerID peerID, const Socket& socket) MethodID methodID; MessageID messageID; { - LOGI("Locking"); Socket::Guard guard = socket.getGuard(); socket.read(&methodID, sizeof(methodID)); socket.read(&messageID, sizeof(messageID)); - LOGI("Locked"); if (methodID == RETURN_METHOD_ID) { - LOGI("Return value for messageID: " << messageID); - ReturnCallbacks returnCallbacks; - try { - Lock lock(mReturnCallbacksMutex); - LOGT("Getting the return callback"); - returnCallbacks = std::move(mReturnCallbacks.at(messageID)); - mReturnCallbacks.erase(messageID); - } catch (const std::out_of_range&) { - LOGW("No return callback for messageID: " << messageID); - return false; - } + return onReturnValue(peerID, socket, messageID); + } else { + return onRemoteCall(peerID, socket, methodID, messageID); + } + } - std::shared_ptr data; - try { - LOGT("Parsing incoming return data"); - data = returnCallbacks.parse(socket.getFD()); - } catch (const IPCException&) { - removePeer(peerID); - return true; - } + return false; +} - guard.unlock(); +bool Processor::onReturnValue(const PeerID peerID, + const Socket& socket, + const MessageID messageID) +{ + LOGI("Return value for messageID: " << messageID); + ReturnCallbacks returnCallbacks; + try { + Lock lock(mReturnCallbacksMutex); + LOGT("Getting the return callback"); + returnCallbacks = std::move(mReturnCallbacks.at(messageID)); + mReturnCallbacks.erase(messageID); + } catch (const std::out_of_range&) { + LOGW("No return callback for messageID: " << messageID); + removePeer(peerID, Status::NAUGHTY_PEER); + return true; + } - LOGT("Process callback for methodID: " << methodID << "; messageID: " << messageID); - returnCallbacks.process(data); + std::shared_ptr data; + try { + LOGT("Parsing incoming return data"); + data = returnCallbacks.parse(socket.getFD()); + } catch (const std::exception& e) { + LOGE("Exception during parsing: " << e.what()); + IGNORE_EXCEPTIONS(returnCallbacks.process(Status::PARSING_ERROR, data)); + removePeer(peerID, Status::PARSING_ERROR); + return true; + } - } else { - LOGI("Remote call; methodID: " << methodID << " messageID: " << messageID); - std::shared_ptr methodCallbacks; - try { - Lock lock(mCallsMutex); - methodCallbacks = mMethodsCallbacks.at(methodID); - } catch (const std::out_of_range&) { - LOGW("No method callback for methodID: " << methodID); - removePeer(peerID); - return true; - } + LOGT("Process return value callback for messageID: " << messageID); + IGNORE_EXCEPTIONS(returnCallbacks.process(Status::OK, data)); - std::shared_ptr data; - try { - LOGT("Parsing incoming data"); - data = methodCallbacks->parse(socket.getFD()); - } catch (const IPCException&) { - removePeer(peerID); - return true; - } + return false; +} - guard.unlock(); - - LOGT("Process callback for methodID: " << methodID << "; messageID: " << messageID); - std::shared_ptr returnData = methodCallbacks->method(data); - - LOGT("Sending return data; methodID: " << methodID << "; messageID: " << messageID); - try { - // Send the call with the socket - Socket::Guard guard = socket.getGuard(); - socket.write(&RETURN_METHOD_ID, sizeof(RETURN_METHOD_ID)); - socket.write(&messageID, sizeof(messageID)); - methodCallbacks->serialize(socket.getFD(), returnData); - } catch (const IPCException&) { - removePeer(peerID); - return true; - } - } +bool Processor::onRemoteCall(const PeerID peerID, + const Socket& socket, + const MethodID methodID, + const MessageID messageID) +{ + LOGI("Remote call; methodID: " << methodID << " messageID: " << messageID); + + std::shared_ptr methodCallbacks; + try { + Lock lock(mCallsMutex); + methodCallbacks = mMethodsCallbacks.at(methodID); + } catch (const std::out_of_range&) { + LOGW("No method callback for methodID: " << methodID); + removePeer(peerID, Status::NAUGHTY_PEER); + return true; + } + + std::shared_ptr data; + try { + LOGT("Parsing incoming data"); + data = methodCallbacks->parse(socket.getFD()); + } catch (const std::exception& e) { + LOGE("Exception during parsing: " << e.what()); + removePeer(peerID, Status::PARSING_ERROR); + return true; + } + + LOGT("Process callback for methodID: " << methodID << "; messageID: " << messageID); + std::shared_ptr returnData; + try { + returnData = methodCallbacks->method(data); + } catch (const std::exception& e) { + LOGE("Exception in method handler: " << e.what()); + removePeer(peerID, Status::NAUGHTY_PEER); + return true; + } + + LOGT("Sending return data; methodID: " << methodID << "; messageID: " << messageID); + try { + // Send the call with the socket + Socket::Guard guard = socket.getGuard(); + socket.write(&RETURN_METHOD_ID, sizeof(RETURN_METHOD_ID)); + socket.write(&messageID, sizeof(messageID)); + methodCallbacks->serialize(socket.getFD(), returnData); + } catch (const std::exception& e) { + LOGE("Exception during serialization: " << e.what()); + removePeer(peerID, Status::SERIALIZATION_ERROR); + return true; } return false; @@ -325,8 +371,7 @@ bool Processor::handleEvent() case Event::CALL: { LOGD("Event CALL"); - handleCall(); - return false; + return handleCall(); } case Event::NEW_PEER: { @@ -340,15 +385,16 @@ bool Processor::handleEvent() if (mSockets.size() > mMaxNumberOfPeers) { LOGE("There are too many peers. I don't accept the connection with " << socketInfo.peerID); - + return false; } if (mSockets.count(socketInfo.peerID) != 0) { LOGE("There already was a socket for peerID: " << socketInfo.peerID); + return false; } - mSockets[socketInfo.peerID] = socketInfo.socketPtr; + + mSockets.emplace(socketInfo.peerID, std::move(socketInfo.socketPtr)); } resetPolling(); - if (mNewPeerCallback) { // Notify about the new user. mNewPeerCallback(socketInfo.peerID); @@ -384,23 +430,20 @@ Processor::Call Processor::getCall() return call; } -void Processor::handleCall() +bool Processor::handleCall() { - LOGT("Handle call from another thread"); + LOGT("Handle call (from another thread) to send a message."); Call call = getCall(); - ReturnCallbacks returnCallbacks; - returnCallbacks.parse = call.parse; - returnCallbacks.process = call.process; - std::shared_ptr socketPtr; try { - // Get the addressee's socket + // Get the peer's socket Lock lock(mSocketsMutex); socketPtr = mSockets.at(call.peerID); } catch (const std::out_of_range&) { LOGE("Peer disconnected. No socket with a peerID: " << call.peerID); - return; + IGNORE_EXCEPTIONS(call.process(Status::PEER_DISCONNECTED, call.data)); + return false; } MessageID messageID = getNextMessageID(); @@ -411,7 +454,9 @@ void Processor::handleCall() if (mReturnCallbacks.count(messageID) != 0) { LOGE("There already was a return callback for messageID: " << messageID); } - mReturnCallbacks[messageID] = std::move(returnCallbacks); + mReturnCallbacks.emplace(messageID, ReturnCallbacks(call.peerID, + std::move(call.parse), + std::move(call.process))); } try { @@ -422,12 +467,20 @@ void Processor::handleCall() call.serialize(socketPtr->getFD(), call.data); } catch (const std::exception& e) { LOGE("Error during sending a message: " << e.what()); + + // Inform about the error + IGNORE_EXCEPTIONS(mReturnCallbacks[messageID].process(Status::SERIALIZATION_ERROR, call.data)); + { Lock lock(mReturnCallbacksMutex); mReturnCallbacks.erase(messageID); } - // TODO: User should get the error code. + + removePeer(call.peerID, Status::SERIALIZATION_ERROR); + return true; } + + return false; } } // namespace ipc diff --git a/common/ipc/internals/processor.hpp b/common/ipc/internals/processor.hpp index cb14a67..3df3f9c 100644 --- a/common/ipc/internals/processor.hpp +++ b/common/ipc/internals/processor.hpp @@ -71,6 +71,8 @@ const unsigned int DEFAULT_MAX_NUMBER_OF_PEERS = 500; * - don't throw timeout if the message is already processed * - naming convention or methods that just commissions the PROCESS thread to do something * - removePeer API function +* - error handling - special message type +* - some mutexes may not be needed */ class Processor { public: @@ -214,6 +216,10 @@ private: ReturnCallbacks(ReturnCallbacks&&) = default; ReturnCallbacks& operator=(ReturnCallbacks &&) = default; + ReturnCallbacks(PeerID peerID, const ParseCallback& parse, const ResultHandler::type& process) + : peerID(peerID), parse(parse), process(process) {} + + PeerID peerID; ParseCallback parse; ResultHandler::type process; }; @@ -225,8 +231,11 @@ private: SocketInfo(SocketInfo&&) = default; SocketInfo& operator=(SocketInfo &&) = default; - std::shared_ptr socketPtr; + SocketInfo(const PeerID peerID, const std::shared_ptr& socketPtr) + : peerID(peerID), socketPtr(socketPtr) {} + PeerID peerID; + std::shared_ptr socketPtr; }; enum class Event : int { @@ -268,15 +277,22 @@ private: void run(); bool handleEvent(); - void handleCall(); + bool handleCall(); bool handleLostConnections(); bool handleInputs(); bool handleInput(const PeerID peerID, const Socket& socket); + bool onReturnValue(const PeerID peerID, + const Socket& socket, + const MessageID messageID); + bool onRemoteCall(const PeerID peerID, + const Socket& socket, + const MethodID methodID, + const MessageID messageID); void resetPolling(); MessageID getNextMessageID(); PeerID getNextPeerID(); Call getCall(); - void removePeer(PeerID peerID); + void removePeer(const PeerID peerID, Status status); }; @@ -352,9 +368,9 @@ void Processor::callAsync(const MethodID methodID, config::saveToFD(fd, *std::static_pointer_cast(data)); }; - call.process = [process](std::shared_ptr& data)->void { + call.process = [process](Status status, std::shared_ptr& data)->void { std::shared_ptr tmpData = std::static_pointer_cast(data); - return process(tmpData); + return process(status, tmpData); }; { @@ -387,8 +403,10 @@ std::shared_ptr Processor::callSync(const MethodID methodID, std::mutex mtx; std::unique_lock lck(mtx); std::condition_variable cv; + Status returnStatus = ipc::Status::UNDEFINED; - auto process = [&result, &cv](std::shared_ptr returnedData) { + auto process = [&result, &cv, &returnStatus](Status status, std::shared_ptr returnedData) { + returnStatus = status; result = returnedData; cv.notify_one(); }; @@ -399,15 +417,17 @@ std::shared_ptr Processor::callSync(const MethodID methodID, data, process); - auto isResultInitialized = [&result]() { - return static_cast(result); + auto isResultInitialized = [&returnStatus]() { + return returnStatus != ipc::Status::UNDEFINED; }; if (!cv.wait_for(lck, std::chrono::milliseconds(timeoutMS), isResultInitialized)) { LOGE("Function call timeout; methodID: " << methodID); - throw IPCException("Function call timeout; methodID: " + std::to_string(methodID)); + throw IPCTimeoutException("Function call timeout; methodID: " + std::to_string(methodID)); } + throwOnError(returnStatus); + return result; } diff --git a/common/ipc/service.hpp b/common/ipc/service.hpp index 08f7ec9..873673d 100644 --- a/common/ipc/service.hpp +++ b/common/ipc/service.hpp @@ -103,7 +103,7 @@ public: std::shared_ptr callSync(const MethodID methodID, const PeerID peerID, const std::shared_ptr& data, - unsigned int timeoutMS); + unsigned int timeoutMS = 500); /** * Asynchronous method call. The return callback will be called on @@ -140,7 +140,7 @@ template std::shared_ptr Service::callSync(const MethodID methodID, const PeerID peerID, const std::shared_ptr& data, - unsigned int timeoutMS = 500) + unsigned int timeoutMS) { LOGD("Sync calling method: " << methodID << " for user: " << peerID); return mProcessor.callSync(methodID, peerID, data, timeoutMS); diff --git a/common/ipc/types.cpp b/common/ipc/types.cpp new file mode 100644 index 0000000..e0ffc5b --- /dev/null +++ b/common/ipc/types.cpp @@ -0,0 +1,64 @@ +/* +* 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 Types definitions and helper functions + */ + +#include "ipc/types.hpp" +#include "logger/logger.hpp" + + +namespace security_containers { +namespace ipc { + +std::string toString(const Status status) +{ + switch (status) { + case Status::OK: return "No error, everything is OK"; + case Status::PARSING_ERROR: return "Exception during reading/parsing data from the socket"; + case Status::SERIALIZATION_ERROR: return "Exception during writing/serializing data to the socket"; + case Status::PEER_DISCONNECTED: return "No such peer. Might got disconnected."; + case Status::NAUGHTY_PEER: return "Peer performed a forbidden action."; + case Status::UNDEFINED: return "Undefined state"; + default: return "Unknown status"; + } +} + +void throwOnError(const Status status) +{ + if (status == Status::OK) { + return; + } + + std::string message = toString(status); + LOGE(message); + + switch (status) { + case Status::PARSING_ERROR: throw IPCParsingException(message); + case Status::SERIALIZATION_ERROR: throw IPCSerializationException(message); + case Status::PEER_DISCONNECTED: throw IPCPeerDisconnectedException(message); + case Status::NAUGHTY_PEER: throw IPCNaughtyPeerException(message); + case Status::UNDEFINED: throw IPCException(message); + default: return throw IPCException(message); + } +} +} // namespace ipc +} // namespace security_containers diff --git a/common/ipc/types.hpp b/common/ipc/types.hpp index 4269664..c07e504 100644 --- a/common/ipc/types.hpp +++ b/common/ipc/types.hpp @@ -19,18 +19,33 @@ /** * @file * @author Jan Olszak (j.olszak@samsung.com) - * @brief Handler types definitions + * @brief Types definitions */ #ifndef COMMON_IPC_HANDLERS_HPP #define COMMON_IPC_HANDLERS_HPP +#include "ipc/exception.hpp" + #include #include +#include namespace security_containers { namespace ipc { +enum class Status : int { + OK = 0, + PARSING_ERROR, + SERIALIZATION_ERROR, + PEER_DISCONNECTED, + NAUGHTY_PEER, + UNDEFINED +}; + +std::string toString(const Status status); +void throwOnError(const Status status); + template struct MethodHandler { typedef std::function(std::shared_ptr&)> type; @@ -39,7 +54,7 @@ struct MethodHandler { template struct ResultHandler { - typedef std::function&)> type; + typedef std::function&)> type; }; } // namespace ipc diff --git a/tests/unit_tests/ipc/ut-ipc.cpp b/tests/unit_tests/ipc/ut-ipc.cpp index 48ad1e5..ea70b45 100644 --- a/tests/unit_tests/ipc/ut-ipc.cpp +++ b/tests/unit_tests/ipc/ut-ipc.cpp @@ -75,6 +75,21 @@ struct EmptyData { CONFIG_REGISTER_EMPTY }; +struct ThrowOnAcceptData { + template + void accept(Visitor) + { + LOGE("Serialization and parsing failed"); + throw std::exception(); + } + template + void accept(Visitor) const + { + LOGE("Const Serialization and parsing failed"); + throw std::exception(); + } +}; + std::shared_ptr returnEmptyCallback(std::shared_ptr&) { return std::shared_ptr(new EmptyData()); @@ -90,6 +105,12 @@ std::shared_ptr echoCallback(std::shared_ptr& data) return data; } +std::shared_ptr longEchoCallback(std::shared_ptr& data) +{ + std::this_thread::sleep_for(std::chrono::seconds(1)); + return data; +} + void testEcho(Client& c, const Client::MethodID methodID) { std::shared_ptr sentData(new SendData(34)); @@ -283,7 +304,8 @@ BOOST_AUTO_TEST_CASE(AsyncClientToServiceEchoTest) //Async call std::shared_ptr sentData(new SendData(34)); std::shared_ptr recvData; - auto dataBack = [&cv, &recvData](std::shared_ptr& data) { + auto dataBack = [&cv, &recvData](ipc::Status status, std::shared_ptr& data) { + BOOST_CHECK(status == ipc::Status::OK); recvData = data; cv.notify_one(); }; @@ -324,7 +346,8 @@ BOOST_AUTO_TEST_CASE(AsyncServiceToClientEchoTest) std::shared_ptr sentData(new SendData(56)); std::shared_ptr recvData; - auto dataBack = [&cv, &recvData](std::shared_ptr& data) { + auto dataBack = [&cv, &recvData](ipc::Status status, std::shared_ptr& data) { + BOOST_CHECK(status == ipc::Status::OK); recvData = data; cv.notify_one(); }; @@ -343,15 +366,78 @@ BOOST_AUTO_TEST_CASE(AsyncServiceToClientEchoTest) BOOST_AUTO_TEST_CASE(SyncTimeoutTest) { Service s(socketPath); + s.addMethodHandler(1, longEchoCallback); + + s.start(); + Client c(socketPath); + c.start(); + + std::shared_ptr sentData(new SendData(78)); + + BOOST_CHECK_THROW((c.callSync(1, sentData, 10)), IPCException); +} + +BOOST_AUTO_TEST_CASE(SerializationErrorTest) +{ + Service s(socketPath); + s.addMethodHandler(1, echoCallback); + s.start(); + + Client c(socketPath); + c.start(); + + std::shared_ptr throwingData(new ThrowOnAcceptData()); + + BOOST_CHECK_THROW((c.callSync(1, throwingData)), IPCSerializationException); + +} + +BOOST_AUTO_TEST_CASE(ParseErrorTest) +{ + Service s(socketPath); s.addMethodHandler(1, echoCallback); + s.start(); + + Client c(socketPath); + c.start(); + + std::shared_ptr sentData(new SendData(78)); + BOOST_CHECK_THROW((c.callSync(1, sentData, 10000)), IPCParsingException); +} + +BOOST_AUTO_TEST_CASE(DisconnectedPeerErrorTest) +{ + Service s(socketPath); + + auto method = [](std::shared_ptr&) { + return std::shared_ptr(new SendData(1)); + }; + // Method will throw during serialization and disconnect automatically + s.addMethodHandler(1, method); s.start(); + Client c(socketPath); c.start(); + std::mutex mtx; + std::unique_lock lck(mtx); + std::condition_variable cv; + ipc::Status retStatus = ipc::Status::UNDEFINED; + + auto dataBack = [&cv, &retStatus](ipc::Status status, std::shared_ptr&) { + retStatus = status; + cv.notify_one(); + }; + std::shared_ptr sentData(new SendData(78)); + c.callAsync(1, sentData, dataBack); - BOOST_CHECK_THROW((c.callSync(1, sentData, 1)), IPCException); + // Wait for the response + BOOST_CHECK(cv.wait_for(lck, std::chrono::seconds(10), [&retStatus]() { + return retStatus != ipc::Status::UNDEFINED; + })); + BOOST_CHECK(retStatus == ipc::Status::PEER_DISCONNECTED); } -- 2.7.4 From 40e9e5c6518f1821e406eb3877ed2fc8dee6f3b5 Mon Sep 17 00:00:00 2001 From: Piotr Bartosiewicz Date: Tue, 18 Nov 2014 13:34:58 +0100 Subject: [PATCH 02/16] Lxc networking [Bug/Feature] Add lxc network config. Remove dead network code. [Cause] N/A [Solution] N/A [Verification] Build, install, run Change-Id: I2883858dbd571a01c93f6cc8c6b47cffe970a42a --- packaging/security-containers.spec | 1 + server/configs/lxc-templates/business.sh | 34 ++++- server/configs/lxc-templates/private.sh | 34 ++++- server/container.cpp | 11 -- server/container.hpp | 3 - server/network-admin.cpp | 152 --------------------- server/network-admin.hpp | 83 ----------- tests/unit_tests/server/configs/CMakeLists.txt | 13 -- .../ut-network-admin/containers/buggy.conf.in | 11 -- .../ut-network-admin/containers/missing.conf | 11 -- .../ut-network-admin/containers/test.conf.in | 11 -- tests/unit_tests/server/ut-network-admin.cpp | 85 ------------ 12 files changed, 67 insertions(+), 382 deletions(-) delete mode 100644 server/network-admin.cpp delete mode 100644 server/network-admin.hpp delete mode 100644 tests/unit_tests/server/configs/ut-network-admin/containers/buggy.conf.in delete mode 100644 tests/unit_tests/server/configs/ut-network-admin/containers/missing.conf delete mode 100644 tests/unit_tests/server/configs/ut-network-admin/containers/test.conf.in delete mode 100644 tests/unit_tests/server/ut-network-admin.cpp diff --git a/packaging/security-containers.spec b/packaging/security-containers.spec index 11e7c99..99c0d1d 100644 --- a/packaging/security-containers.spec +++ b/packaging/security-containers.spec @@ -28,6 +28,7 @@ BuildRequires: pkgconfig(glib-2.0) BuildRequires: pkgconfig(libsystemd-journal) BuildRequires: pkgconfig(sqlite3) Requires(post): libcap-tools +Requires: bridge-utils %description This package provides a daemon used to manage containers - start, stop and switch diff --git a/server/configs/lxc-templates/business.sh b/server/configs/lxc-templates/business.sh index 09d67ca..21f7d2e 100755 --- a/server/configs/lxc-templates/business.sh +++ b/server/configs/lxc-templates/business.sh @@ -19,6 +19,9 @@ do esac done +br_name="virbr-${name}" +sub_net="101" # TODO from param + # XXX assume rootfs if mounted from iso # Prepare container configuration file @@ -35,6 +38,35 @@ lxc.pts = 256 lxc.tty = 0 lxc.mount.auto = proc sys cgroup -lxc.mount.entry = /var/run/containers/business/run var/run none rw,bind 0 0 +lxc.mount.entry = /var/run/containers/${name}/run var/run none rw,bind 0 0 + +lxc.network.type = veth +lxc.network.link = ${br_name} +lxc.network.flags = up +lxc.network.name = eth0 +lxc.network.veth.pair = veth-${name} +lxc.network.ipv4.gateway = 10.0.${sub_net}.1 +lxc.network.ipv4 = 10.0.${sub_net}.2/24 + +lxc.hook.pre-start = ${path}/pre-start.sh + +#lxc.loglevel = TRACE +#lxc.logfile = /tmp/${name}.log +EOF + +# prepare pre start hook +cat <> ${path}/pre-start.sh +if [ -z "\$(/usr/sbin/brctl show | /bin/grep -P "${br_name}\t")" ] +then + /usr/sbin/brctl addbr ${br_name} + /usr/sbin/brctl setfd ${br_name} 0 + /sbin/ifconfig ${br_name} 10.0.${sub_net}.1 netmask 255.255.255.0 up +fi +if [ -z "\$(/usr/sbin/iptables -t nat -S | /bin/grep MASQUERADE)" ] +then + /bin/echo 1 > /proc/sys/net/ipv4/ip_forward + /usr/sbin/iptables -t nat -A POSTROUTING -s 10.0.0.0/16 ! -d 10.0.0.0/16 -j MASQUERADE +fi EOF +chmod 755 ${path}/pre-start.sh diff --git a/server/configs/lxc-templates/private.sh b/server/configs/lxc-templates/private.sh index 731ff72..542093a 100755 --- a/server/configs/lxc-templates/private.sh +++ b/server/configs/lxc-templates/private.sh @@ -19,6 +19,9 @@ do esac done +br_name="virbr-${name}" +sub_net="102" # TODO from param + # XXX assume rootfs if mounted from iso # Prepare container configuration file @@ -35,6 +38,35 @@ lxc.pts = 256 lxc.tty = 0 lxc.mount.auto = proc sys cgroup -lxc.mount.entry = /var/run/containers/private/run var/run none rw,bind 0 0 +lxc.mount.entry = /var/run/containers/${name}/run var/run none rw,bind 0 0 + +lxc.network.type = veth +lxc.network.link = ${br_name} +lxc.network.flags = up +lxc.network.name = eth0 +lxc.network.veth.pair = veth-${name} +lxc.network.ipv4.gateway = 10.0.${sub_net}.1 +lxc.network.ipv4 = 10.0.${sub_net}.2/24 + +lxc.hook.pre-start = ${path}/pre-start.sh + +#lxc.loglevel = TRACE +#lxc.logfile = /tmp/${name}.log +EOF + +# prepare pre start hook +cat <> ${path}/pre-start.sh +if [ -z "\$(/usr/sbin/brctl show | /bin/grep -P "${br_name}\t")" ] +then + /usr/sbin/brctl addbr ${br_name} + /usr/sbin/brctl setfd ${br_name} 0 + /sbin/ifconfig ${br_name} 10.0.${sub_net}.1 netmask 255.255.255.0 up +fi +if [ -z "\$(/usr/sbin/iptables -t nat -S | /bin/grep MASQUERADE)" ] +then + /bin/echo 1 > /proc/sys/net/ipv4/ip_forward + /usr/sbin/iptables -t nat -A POSTROUTING -s 10.0.0.0/16 ! -d 10.0.0.0/16 -j MASQUERADE +fi EOF +chmod 755 ${path}/pre-start.sh diff --git a/server/container.cpp b/server/container.cpp index 17d383e..3fe421f 100644 --- a/server/container.cpp +++ b/server/container.cpp @@ -66,18 +66,10 @@ Container::Container(const std::string& containersPath, mPermittedToRecv.push_back(boost::regex(r)); } - //const std::string baseConfigPath = utils::dirName(containerConfigPath); - //mConfig.config = fs::absolute(mConfig.config, baseConfigPath).string(); - //mConfig.networkConfig = fs::absolute(mConfig.networkConfig, baseConfigPath).string(); - //mConfig.networkFilterConfig = fs::absolute(mConfig.networkFilterConfig, - // baseConfigPath).string(); if (!mConfig.runMountPoint.empty()) { mRunMountPoint = fs::absolute(mConfig.runMountPoint, baseRunMountPointPath).string(); } - //LOGT("Creating Network Admin " << mConfig.networkConfig); - mNetworkAdmin.reset(new NetworkAdmin(mConfig)); - //LOGT("Creating Container Admin " << mConfig.config); mAdmin.reset(new ContainerAdmin(containersPath, lxcTemplatePrefix, mConfig)); } @@ -127,7 +119,6 @@ void Container::start() if (mConfig.enableDbusIntegration) { mConnectionTransport.reset(new ContainerConnectionTransport(mRunMountPoint)); } - mNetworkAdmin->start(); mAdmin->start(); if (mConfig.enableDbusIntegration) { connect(); @@ -167,7 +158,6 @@ void Container::stop() Lock lock(mReconnectMutex); disconnect(); mAdmin->stop(); - mNetworkAdmin->stop(); mConnectionTransport.reset(); } @@ -239,7 +229,6 @@ void Container::goBackground() void Container::setDetachOnExit() { Lock lock(mReconnectMutex); - mNetworkAdmin->setDetachOnExit(); mAdmin->setDetachOnExit(); if (mConnectionTransport) { mConnectionTransport->setDetachOnExit(); diff --git a/server/container.hpp b/server/container.hpp index 0fcd837..f4140b0 100644 --- a/server/container.hpp +++ b/server/container.hpp @@ -30,8 +30,6 @@ #include "container-admin.hpp" #include "container-connection.hpp" #include "container-connection-transport.hpp" -#include "network-admin.hpp" - #include #include @@ -218,7 +216,6 @@ private: std::vector mPermittedToSend; std::vector mPermittedToRecv; std::unique_ptr mConnectionTransport; - std::unique_ptr mNetworkAdmin; std::unique_ptr mAdmin; std::unique_ptr mConnection; std::thread mReconnectThread; diff --git a/server/network-admin.cpp b/server/network-admin.cpp deleted file mode 100644 index a649d48..0000000 --- a/server/network-admin.cpp +++ /dev/null @@ -1,152 +0,0 @@ -/* - * 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 Implementation of class for administrating single network - */ - -#include "config.hpp" - -#include "network-admin.hpp" -#include "exception.hpp" - -//#include "libvirt/helpers.hpp" -#include "logger/logger.hpp" -#include "utils/fs.hpp" - -#include - - -namespace security_containers { - -namespace { - -//std::string getNetworkName(virNetworkPtr net) -//{ -// assert(net); -// -// const char* name = virNetworkGetName(net); -// if (name == nullptr) { -// LOGE("Failed to get the network's id:\n" -// << libvirt::libvirtFormatError()); -// throw ContainerOperationException(); -// } -// -// return name; -//} - -} // namespace - - -NetworkAdmin::NetworkAdmin(const ContainerConfig& config) - : mConfig(config), - //mNWFilter(utils::readFileContent(mConfig.networkFilterConfig)), - //mNetwork(utils::readFileContent(mConfig.networkConfig)), - mId("TODO"),//mId(getNetworkName(mNetwork.get())), - mDetachOnExit(false) -{ - LOGD(mId << ": Instantiating NetworkAdmin object"); -} - - -NetworkAdmin::~NetworkAdmin() -{ - LOGD(mId << ": Destroying NetworkAdmin object..."); - // Try to stop - if (!mDetachOnExit) { - try { - stop(); - } catch (ServerException&) { - LOGE(mId << ": Failed to stop the network"); - } - } - - LOGD(mId << ": NetworkAdmin object destroyed"); -} - - -const std::string& NetworkAdmin::getId() const -{ - return mId; -} - - -void NetworkAdmin::start() -{ -// assert(mNetwork); -// -// LOGD(mId << ": Starting..."); -// if (isActive()) { -// LOGD(mId << ": Already running - nothing to do..."); -// return; -// } -// -// if (virNetworkCreate(mNetwork.get()) < 0) { -// LOGE(mId << ": Failed to start the network\n" -// << libvirt::libvirtFormatError()); -// throw ContainerOperationException(); -// } -// -// LOGD(mId << ": Started"); -} - - -void NetworkAdmin::stop() -{ -// assert(mNetwork); -// -// LOGD(mId << ": Stopping procedure started..."); -// if (!isActive()) { -// LOGD(mId << ": Already crashed/down/off - nothing to do"); -// return; -// } -// -// if (virNetworkDestroy(mNetwork.get()) < 0) { -// LOGE(mId << ": Failed to destroy the network\n" -// << libvirt::libvirtFormatError()); -// throw ContainerOperationException(); -// } -// -// LOGD(mId << ": Stopping procedure ended"); -} - - -bool NetworkAdmin::isActive() -{ -// assert(mNetwork); -// int ret = virNetworkIsActive(mNetwork.get()); -// if (ret < 0) { -// LOGE(mId << ": Failed to get network state\n" -// << libvirt::libvirtFormatError()); -// throw ContainerOperationException(); -// } -// return ret > 0; - return false; -} - - -void NetworkAdmin::setDetachOnExit() -{ -// mDetachOnExit = true; -// mNWFilter.setDetachOnExit(); -} - - -} // namespace security_containers diff --git a/server/network-admin.hpp b/server/network-admin.hpp deleted file mode 100644 index 66a6f75..0000000 --- a/server/network-admin.hpp +++ /dev/null @@ -1,83 +0,0 @@ -/* - * 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 Declaration of the class for administrating single network - */ - - -#ifndef SERVER_NETWORK_ADMIN_HPP -#define SERVER_NETWORK_ADMIN_HPP - -#include "container-config.hpp" - -//#include "libvirt/network-filter.hpp" -//#include "libvirt/network.hpp" - - -namespace security_containers { - - -class NetworkAdmin { - -public: - - NetworkAdmin(const ContainerConfig& config); - virtual ~NetworkAdmin(); - - /** - * Get the network id - */ - const std::string& getId() const; - - /** - * Start network. - */ - void start(); - - /** - * Stop network. - */ - void stop(); - - /** - * @return Is the network active? - */ - bool isActive(); - - /** - * Set whether container should be detached on exit. - */ - void setDetachOnExit(); - - -private: - const ContainerConfig& mConfig; - //libvirt::LibvirtNWFilter mNWFilter; - //libvirt::LibvirtNetwork mNetwork; - const std::string mId; - bool mDetachOnExit; -}; - - -} // namespace security_containers - - -#endif // SERVER_NETWORK_ADMIN_HPP diff --git a/tests/unit_tests/server/configs/CMakeLists.txt b/tests/unit_tests/server/configs/CMakeLists.txt index b3f8a70..17e10a2 100644 --- a/tests/unit_tests/server/configs/CMakeLists.txt +++ b/tests/unit_tests/server/configs/CMakeLists.txt @@ -31,8 +31,6 @@ FILE(GLOB container_container_CONF ut-container/containers/*.conf) FILE(GLOB admin_container_CONF ut-container-admin/containers/*.conf) -FILE(GLOB network_container_CONF ut-network-admin/containers/*.conf) - FILE(GLOB connection_CONF ut-container-connection/*.conf) @@ -43,12 +41,6 @@ CONFIGURE_FILE(ut-server/buggy-daemon.conf.in ${CMAKE_BINARY_DIR}/ut-server/buggy-daemon.conf @ONLY) FILE(GLOB server_manager_CONF_GEN ${CMAKE_BINARY_DIR}/ut-server/*.conf) -CONFIGURE_FILE(ut-network-admin/containers/test.conf.in - ${CMAKE_BINARY_DIR}/ut-network-admin/containers/test.conf @ONLY) -CONFIGURE_FILE(ut-network-admin/containers/buggy.conf.in - ${CMAKE_BINARY_DIR}/ut-network-admin/containers/buggy.conf @ONLY) -FILE(GLOB network_container_CONF_GEN ${CMAKE_BINARY_DIR}/ut-network-admin/containers/*.conf) - CONFIGURE_FILE(ut-container/containers/test-dbus.conf.in ${CMAKE_BINARY_DIR}/ut-container/containers/test-dbus.conf @ONLY) FILE(GLOB container_container_CONF_GEN ${CMAKE_BINARY_DIR}/ut-container/containers/*.conf) @@ -103,11 +95,6 @@ INSTALL(FILES ${container_container_CONF_GEN} INSTALL(FILES ${admin_container_CONF} DESTINATION ${SC_TEST_CONFIG_INSTALL_DIR}/server/ut-container-admin/containers) -INSTALL(FILES ${network_container_CONF} - DESTINATION ${SC_TEST_CONFIG_INSTALL_DIR}/server/ut-network-admin/containers) -INSTALL(FILES ${network_container_CONF_GEN} - DESTINATION ${SC_TEST_CONFIG_INSTALL_DIR}/server/ut-network-admin/containers) - INSTALL(FILES ${connection_CONF} DESTINATION ${SC_TEST_CONFIG_INSTALL_DIR}/server/ut-container-connection) diff --git a/tests/unit_tests/server/configs/ut-network-admin/containers/buggy.conf.in b/tests/unit_tests/server/configs/ut-network-admin/containers/buggy.conf.in deleted file mode 100644 index f4be18d..0000000 --- a/tests/unit_tests/server/configs/ut-network-admin/containers/buggy.conf.in +++ /dev/null @@ -1,11 +0,0 @@ -{ - "privilege" : 10, - "vt" : -1, - "switchToDefaultAfterTimeout" : true, - "enableDbusIntegration" : false, - "cpuQuotaForeground" : -1, - "cpuQuotaBackground" : 1000, - "runMountPoint" : "", - "permittedToSend" : [], - "permittedToRecv" : [] -} diff --git a/tests/unit_tests/server/configs/ut-network-admin/containers/missing.conf b/tests/unit_tests/server/configs/ut-network-admin/containers/missing.conf deleted file mode 100644 index f4be18d..0000000 --- a/tests/unit_tests/server/configs/ut-network-admin/containers/missing.conf +++ /dev/null @@ -1,11 +0,0 @@ -{ - "privilege" : 10, - "vt" : -1, - "switchToDefaultAfterTimeout" : true, - "enableDbusIntegration" : false, - "cpuQuotaForeground" : -1, - "cpuQuotaBackground" : 1000, - "runMountPoint" : "", - "permittedToSend" : [], - "permittedToRecv" : [] -} diff --git a/tests/unit_tests/server/configs/ut-network-admin/containers/test.conf.in b/tests/unit_tests/server/configs/ut-network-admin/containers/test.conf.in deleted file mode 100644 index f4be18d..0000000 --- a/tests/unit_tests/server/configs/ut-network-admin/containers/test.conf.in +++ /dev/null @@ -1,11 +0,0 @@ -{ - "privilege" : 10, - "vt" : -1, - "switchToDefaultAfterTimeout" : true, - "enableDbusIntegration" : false, - "cpuQuotaForeground" : -1, - "cpuQuotaBackground" : 1000, - "runMountPoint" : "", - "permittedToSend" : [], - "permittedToRecv" : [] -} diff --git a/tests/unit_tests/server/ut-network-admin.cpp b/tests/unit_tests/server/ut-network-admin.cpp deleted file mode 100644 index f120ac6..0000000 --- a/tests/unit_tests/server/ut-network-admin.cpp +++ /dev/null @@ -1,85 +0,0 @@ -/* - * 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 the NetworkAdmin class - */ - -//#include "config.hpp" -//#include "ut.hpp" -// -//#include "network-admin.hpp" -// -//#include "utils/exception.hpp" -////#include "libvirt/exception.hpp" -//#include "config/manager.hpp" -// -// -//using namespace security_containers; -// -//namespace { -// -//const std::string TEST_CONFIG_PATH = SC_TEST_CONFIG_INSTALL_DIR "/server/ut-network-admin/containers/test.conf"; -//const std::string BUGGY_CONFIG_PATH = SC_TEST_CONFIG_INSTALL_DIR "/server/ut-network-admin/containers/buggy.conf"; -//const std::string MISSING_CONFIG_PATH = SC_TEST_CONFIG_INSTALL_DIR "/server/ut-network-admin/containers/missing.conf"; -// -//} // namespace -// -// -//BOOST_AUTO_TEST_SUITE(NetworkAdminSuite) -// -//BOOST_AUTO_TEST_CASE(ConstructorDestructorTest) -//{ -// ContainerConfig config; -// config::loadFromFile(TEST_CONFIG_PATH, config); -// std::unique_ptr admin; -// BOOST_REQUIRE_NO_THROW(admin.reset(new NetworkAdmin(config))); -// BOOST_REQUIRE_NO_THROW(admin.reset()); -//} -// -////BOOST_AUTO_TEST_CASE(BuggyConfigTest) -////{ -//// ContainerConfig config; -//// config::loadFromFile(BUGGY_CONFIG_PATH, config); -//// BOOST_REQUIRE_THROW(NetworkAdmin na(config), LibvirtOperationException); -////} -// -//BOOST_AUTO_TEST_CASE(MissingConfigTest) -//{ -// ContainerConfig config; -// config::loadFromFile(MISSING_CONFIG_PATH, config); -// BOOST_REQUIRE_THROW(NetworkAdmin na(config), UtilsException); -//} -// -//BOOST_AUTO_TEST_CASE(StartStopTest) -//{ -// ContainerConfig config; -// config::loadFromFile(TEST_CONFIG_PATH, config); -// NetworkAdmin net(config); -// -// BOOST_CHECK(!net.isActive()); -// BOOST_CHECK_NO_THROW(net.start()); -// BOOST_CHECK(net.isActive()); -// BOOST_CHECK_NO_THROW(net.stop()); -// BOOST_CHECK(!net.isActive()); -//} -// -//BOOST_AUTO_TEST_SUITE_END() -- 2.7.4 From 1d05535e3b32d1b1451d90cd1090417e66e1d17c Mon Sep 17 00:00:00 2001 From: Mateusz Malicki Date: Fri, 14 Nov 2014 16:38:16 +0100 Subject: [PATCH 03/16] Implement lookup_domain_by_id function in server, client and cli [Bug/Feature] Implement lookup_domain_by_id function in server, client and cli [Cause] N/A [Solution] N/A [Verification] Build, run appropriate function (through cli) Change-Id: I2908e760613532caadcc9c58a1d522d4ac7767c4 --- cli/command-line-interface.cpp | 45 ++++++++++++++++++++ cli/command-line-interface.hpp | 8 ++++ cli/main.cpp | 8 ++++ client/security-containers-client-impl.cpp | 67 ++++++++++++++++++++++++++++-- client/security-containers-client.h | 2 +- server/common-dbus-definitions.hpp | 1 + server/container.cpp | 5 +++ server/container.hpp | 5 +++ server/containers-manager.cpp | 34 +++++++++++++++ server/containers-manager.hpp | 1 + server/host-connection.cpp | 15 +++++++ server/host-connection.hpp | 9 ++++ server/host-dbus-definitions.hpp | 5 +++ 13 files changed, 201 insertions(+), 4 deletions(-) diff --git a/cli/command-line-interface.cpp b/cli/command-line-interface.cpp index f3768d0..d027e63 100644 --- a/cli/command-line-interface.cpp +++ b/cli/command-line-interface.cpp @@ -30,6 +30,7 @@ #include #include #include +#include using namespace std; @@ -80,6 +81,37 @@ finish: } } +ostream& operator<<(ostream& out, const VsmDomainState& state) +{ + const char* name; + switch (state) { + case STOPPED: name = "STOPPED"; break; + case STARTING: name = "STARTING"; break; + case RUNNING: name = "RUNNING"; break; + case STOPPING: name = "STOPPING"; break; + case ABORTING: name = "ABORTING"; break; + case FREEZING: name = "FREEZING"; break; + case FROZEN: name = "FROZEN"; break; + case THAWED: name = "THAWED"; break; + case LOCKED: name = "LOCKED"; break; + case MAX_STATE: name = "MAX_STATE"; break; + case ACTIVATING: name = "ACTIVATING"; break; + default: name = "MAX_STATE (ERROR)"; + } + + out << name; + return out; +} + +ostream& operator<<(ostream& out, const VsmDomain& domain) +{ + out << "Name: " << domain->id + << "\nTerminal: " << domain->terminal + << "\nState: " << domain->state + << "\nRoot: " << domain->rootfs_path; + return out; +} + } // namespace void CommandLineInterface::printUsage(std::ostream& out) const @@ -122,5 +154,18 @@ void create_domain(int pos, int argc, const char** argv) one_shot(bind(vsm_create_domain, _1, argv[pos + 1], nullptr)); } +void lookup_domain_by_id(int pos, int argc, const char** argv) +{ + using namespace std::placeholders; + if (argc <= pos + 1) { + throw runtime_error("Not enough parameters"); + } + + VsmDomain domain; + one_shot(bind(vsm_lookup_domain_by_id, _1, argv[pos + 1], &domain)); + cout << domain << endl; + vsm_domain_free(domain); +} + } // namespace cli } // namespace security_containers diff --git a/cli/command-line-interface.hpp b/cli/command-line-interface.hpp index 27415c1..c254d84 100644 --- a/cli/command-line-interface.hpp +++ b/cli/command-line-interface.hpp @@ -110,6 +110,14 @@ void set_active_container(int pos, int argc, const char** argv); */ void create_domain(int pos, int argc, const char** argv); +/** + * Parses command line arguments and call vsm_lookup_domain_by_id + * + * @see vsm_lookup_domain_by_id + */ +void lookup_domain_by_id(int pos, int argc, const char** argv); + + } // namespace cli } // namespace security_containers diff --git a/cli/main.cpp b/cli/main.cpp index 135385d..d701475 100644 --- a/cli/main.cpp +++ b/cli/main.cpp @@ -48,6 +48,14 @@ std::map commands = { "Create and add container", {{"container_id", "id container name"}} } + }, + { + "lookup_domain_by_id", { + lookup_domain_by_id, + "lookup_domain_by_id container_id", + "Prints informations about domain", + {{"container_id", "id container name"}} + } } }; diff --git a/client/security-containers-client-impl.cpp b/client/security-containers-client-impl.cpp index 205a8b2..2fcc776 100644 --- a/client/security-containers-client-impl.cpp +++ b/client/security-containers-client-impl.cpp @@ -88,6 +88,50 @@ void toBasic(GVariant* in, char** str) *str = buf; } +VsmDomainState getDomainState(const char* state) +{ + if (strcmp(state, "STOPPED") == 0) { + return STOPPED; + } else if (strcmp(state, "STARTING") == 0) { + return STARTING; + } else if (strcmp(state, "RUNNING") == 0) { + return RUNNING; + } else if (strcmp(state, "STOPPING") == 0) { + return STOPPING; + } else if (strcmp(state, "ABORTING") == 0) { + return ABORTING; + } else if (strcmp(state, "FREEZING") == 0) { + return FREEZING; + } else if (strcmp(state, "FROZEN") == 0) { + return FROZEN; + } else if (strcmp(state, "THAWED") == 0) { + return THAWED; + } else if (strcmp(state, "LOCKED") == 0) { + return LOCKED; + } else if (strcmp(state, "MAX_STATE") == 0) { + return MAX_STATE; + } else if (strcmp(state, "ACTIVATING") == 0) { + return ACTIVATING; + } + assert(!"UNKNOWN STATE"); + return (VsmDomainState)-1; +} + +void toBasic(GVariant* in, VsmDomain* domain) +{ + const char* id; + const char* path; + const char* state; + int terminal; + VsmDomain vsmDomain = (VsmDomain)malloc(sizeof(*vsmDomain)); + g_variant_get(in, "(siss)", &id, &terminal, &state, &path); + vsmDomain->id = strdup(id); + vsmDomain->terminal = terminal; + vsmDomain->state = getDomainState(state); + vsmDomain->rootfs_path = strdup(path); + *domain = vsmDomain; +} + template void toArray(GVariant* in, T** scArray) { @@ -360,10 +404,27 @@ VsmStatus Client::vsm_lookup_domain_by_pid(int pid, VsmString* id) noexcept return vsm_get_status(); } -VsmStatus Client::vsm_lookup_domain_by_id(const char*, VsmDomain*) noexcept +VsmStatus Client::vsm_lookup_domain_by_id(const char* id, VsmDomain* domain) noexcept { - mStatus = Status(VSMCLIENT_OTHER_ERROR, "Not implemented"); - return vsm_get_status(); + assert(id); + assert(domain); + + GVariant* out; + GVariant* args_in = g_variant_new("(s)", id); + VsmStatus ret = callMethod(HOST_INTERFACE, + api::host::METHOD_GET_CONTAINER_INFO, + args_in, + "((siss))", + &out); + if (ret != VSMCLIENT_SUCCESS) { + return ret; + } + GVariant* unpacked; + g_variant_get(out, "(*)", &unpacked); + toBasic(unpacked, domain); + g_variant_unref(unpacked); + g_variant_unref(out); + return ret; } VsmStatus Client::vsm_lookup_domain_by_terminal_id(int, VsmString*) noexcept diff --git a/client/security-containers-client.h b/client/security-containers-client.h index 6419870..ee16e01 100644 --- a/client/security-containers-client.h +++ b/client/security-containers-client.h @@ -338,7 +338,7 @@ VsmStatus vsm_lookup_domain_by_pid(VsmClient client, int pid, VsmString* id); * @param[in] id domain name * @param[out] domain domain informations * @return status of this function call - * @remark Use @p vsm_doamin_free() to free memory occupied by @p domain + * @remark Use @p vsm_domain_free() to free memory occupied by @p domain */ VsmStatus vsm_lookup_domain_by_id(VsmClient client, const char* id, VsmDomain* domain); diff --git a/server/common-dbus-definitions.hpp b/server/common-dbus-definitions.hpp index 8821b34..14bb037 100644 --- a/server/common-dbus-definitions.hpp +++ b/server/common-dbus-definitions.hpp @@ -33,6 +33,7 @@ namespace api { const std::string ERROR_FORBIDDEN = "org.tizen.containers.Error.Forbidden"; const std::string ERROR_FORWARDED = "org.tizen.containers.Error.Forwarded"; const std::string ERROR_UNKNOWN_ID = "org.tizen.containers.Error.UnknownId"; +const std::string ERROR_INTERNAL = "org.tizen.containers.Error.Internal"; const std::string METHOD_PROXY_CALL = "ProxyCall"; diff --git a/server/container.cpp b/server/container.cpp index 59328db..f4ac2cc 100644 --- a/server/container.cpp +++ b/server/container.cpp @@ -211,6 +211,11 @@ std::string Container::getDbusAddress() return mDbusAddress; } +int Container::getVT() const +{ + return mConfig.vt; +} + bool Container::activateVT() { Lock lock(mReconnectMutex); diff --git a/server/container.hpp b/server/container.hpp index 6800f7a..46fafec 100644 --- a/server/container.hpp +++ b/server/container.hpp @@ -204,6 +204,11 @@ public: */ std::string getDbusAddress(); + /** + * Get id of VT + */ + int getVT() const; + private: ContainerConfig mConfig; std::vector mPermittedToSend; diff --git a/server/containers-manager.cpp b/server/containers-manager.cpp index d0d0a36..466a4e6 100644 --- a/server/containers-manager.cpp +++ b/server/containers-manager.cpp @@ -102,6 +102,9 @@ ContainersManager::ContainersManager(const std::string& managerConfigPath): mDet mHostConnection.setGetActiveContainerIdCallback(bind(&ContainersManager::handleGetActiveContainerIdCall, this, _1)); + mHostConnection.setGetContainerInfoCallback(bind(&ContainersManager::handleGetContainerInfoCall, + this, _1, _2)); + mHostConnection.setSetActiveContainerCallback(bind(&ContainersManager::handleSetActiveContainerCall, this, _1, _2)); @@ -497,6 +500,37 @@ void ContainersManager::handleGetActiveContainerIdCall(dbus::MethodResultBuilder } } +void ContainersManager::handleGetContainerInfoCall(const std::string& id, + dbus::MethodResultBuilder::Pointer result) +{ + LOGI("GetContainerInfo call"); + if (mContainers.count(id) == 0) { + LOGE("No container with id=" << id); + result->setError(api::ERROR_UNKNOWN_ID, "No such container id"); + return; + } + const auto& container = mContainers[id]; + const char* state; + //TODO: Use the lookup map. + if (container->isRunning()) { + state = "RUNNING"; + } else if (container->isStopped()) { + state = "STOPPED"; + } else if (container->isPaused()) { + state = "FROZEN"; + } else { + LOGE("Unrecognized state of container id=" << id); + result->setError(api::ERROR_INTERNAL, "Unrecognized state of container"); + return; + } + const std::string rootPath = boost::filesystem::absolute(id, mConfig.containersPath).string(); + result->set(g_variant_new("((siss))", + id.c_str(), + container->getVT(), + state, + rootPath.c_str())); +} + void ContainersManager::handleSetActiveContainerCall(const std::string& id, dbus::MethodResultBuilder::Pointer result) { diff --git a/server/containers-manager.hpp b/server/containers-manager.hpp index 3cbf833..d2efce1 100644 --- a/server/containers-manager.hpp +++ b/server/containers-manager.hpp @@ -124,6 +124,7 @@ private: void handleDbusStateChanged(const std::string& containerId, const std::string& dbusAddress); void handleGetContainerIdsCall(dbus::MethodResultBuilder::Pointer result); void handleGetActiveContainerIdCall(dbus::MethodResultBuilder::Pointer result); + void handleGetContainerInfoCall(const std::string& id, dbus::MethodResultBuilder::Pointer result); void handleSetActiveContainerCall(const std::string& id, dbus::MethodResultBuilder::Pointer result); void handleAddContainerCall(const std::string& id, diff --git a/server/host-connection.cpp b/server/host-connection.cpp index d25bee7..1b38a9c 100644 --- a/server/host-connection.cpp +++ b/server/host-connection.cpp @@ -125,6 +125,11 @@ void HostConnection::setGetActiveContainerIdCallback(const GetActiveContainerIdC mGetActiveContainerIdCallback = callback; } +void HostConnection::setGetContainerInfoCallback(const GetContainerInfoCallback& callback) +{ + mGetContainerInfoCallback = callback; +} + void HostConnection::setSetActiveContainerCallback(const SetActiveContainerCallback& callback) { mSetActiveContainerCallback = callback; @@ -205,6 +210,16 @@ void HostConnection::onMessageCall(const std::string& objectPath, return; } + if (methodName == api::host::METHOD_GET_CONTAINER_INFO){ + const gchar* id = NULL; + g_variant_get(parameters, "(&s)", &id); + + if (mGetContainerInfoCallback) { + mGetContainerInfoCallback(id, result); + } + return; + } + if (methodName == api::host::METHOD_ADD_CONTAINER) { const gchar* id = NULL; g_variant_get(parameters, "(&s)", &id); diff --git a/server/host-connection.hpp b/server/host-connection.hpp index c5d1bcc..1dacd11 100644 --- a/server/host-connection.hpp +++ b/server/host-connection.hpp @@ -59,6 +59,9 @@ public: )> GetActiveContainerIdCallback; typedef std::function GetContainerInfoCallback; + typedef std::function SetActiveContainerCallback; typedef std::function" " " " " + " " + " " + " " + " " " " " " " " -- 2.7.4 From 20e2b2163f5de0b032fa8bdecfce0a46bba34825 Mon Sep 17 00:00:00 2001 From: Piotr Bartosiewicz Date: Wed, 19 Nov 2014 13:37:16 +0100 Subject: [PATCH 04/16] Fix compilation problems [Bug/Feature] Code does not build using older gcc [Cause] N/A [Solution] N/A [Verification] Build Change-Id: Ifd7dd7bce080ac16983a23d30585ace08a522f05 --- common/lxc/domain.cpp | 2 +- tests/unit_tests/client/ut-client.cpp | 15 ++++++++++----- tests/unit_tests/lxc/ut-domain.cpp | 3 ++- tests/unit_tests/server/ut-container-admin.cpp | 6 +++++- tests/unit_tests/server/ut-container.cpp | 6 +++++- tests/unit_tests/server/ut-containers-manager.cpp | 15 +++++++++++---- tests/unit_tests/server/ut-server.cpp | 6 +++++- 7 files changed, 39 insertions(+), 14 deletions(-) diff --git a/common/lxc/domain.cpp b/common/lxc/domain.cpp index 57228c0..bc2e74b 100644 --- a/common/lxc/domain.cpp +++ b/common/lxc/domain.cpp @@ -195,7 +195,7 @@ bool LxcDomain::unfreeze() bool LxcDomain::setRunLevel(int runLevel) { - auto callback = [](void* param) { + auto callback = [](void* param) -> int { utils::RunLevel runLevel = *reinterpret_cast(param); return utils::setRunLevel(runLevel) ? 0 : 1; }; diff --git a/tests/unit_tests/client/ut-client.cpp b/tests/unit_tests/client/ut-client.cpp index 9847a92..6e0aab0 100644 --- a/tests/unit_tests/client/ut-client.cpp +++ b/tests/unit_tests/client/ut-client.cpp @@ -56,14 +56,19 @@ struct Loop { struct Fixture { Loop loop; - utils::ScopedDir mContainersPathGuard = CONTAINERS_PATH; - utils::ScopedDir mRun1Guard = utils::ScopedDir("/tmp/ut-run1"); - utils::ScopedDir mRun2Guard = utils::ScopedDir("/tmp/ut-run2"); - utils::ScopedDir mRun3Guard = utils::ScopedDir("/tmp/ut-run3"); + utils::ScopedDir mContainersPathGuard; + utils::ScopedDir mRun1Guard; + utils::ScopedDir mRun2Guard; + utils::ScopedDir mRun3Guard; ContainersManager cm; - Fixture(): cm(TEST_DBUS_CONFIG_PATH) + Fixture() + : mContainersPathGuard(CONTAINERS_PATH) + , mRun1Guard("/tmp/ut-run1") + , mRun2Guard("/tmp/ut-run2") + , mRun3Guard("/tmp/ut-run3") + , cm(TEST_DBUS_CONFIG_PATH) { cm.startAll(); } diff --git a/tests/unit_tests/lxc/ut-domain.cpp b/tests/unit_tests/lxc/ut-domain.cpp index 206d2c7..aa6c3a9 100644 --- a/tests/unit_tests/lxc/ut-domain.cpp +++ b/tests/unit_tests/lxc/ut-domain.cpp @@ -44,9 +44,10 @@ const std::string DOMAIN_NAME = "ut-domain"; const std::string TEMPLATE = SC_TEST_LXC_TEMPLATES_INSTALL_DIR "/minimal.sh"; struct Fixture { - utils::ScopedDir mLxcDirGuard = LXC_PATH; + utils::ScopedDir mLxcDirGuard; Fixture() + : mLxcDirGuard(LXC_PATH) { cleanup(); } diff --git a/tests/unit_tests/server/ut-container-admin.cpp b/tests/unit_tests/server/ut-container-admin.cpp index 6d2ba6c..ae00abf 100644 --- a/tests/unit_tests/server/ut-container-admin.cpp +++ b/tests/unit_tests/server/ut-container-admin.cpp @@ -46,10 +46,14 @@ const std::string LXC_TEMPLATES_PATH = SC_TEST_LXC_TEMPLATES_INSTALL_DIR; struct Fixture { utils::ScopedGlibLoop mLoop; - utils::ScopedDir mContainersPathGuard = CONTAINERS_PATH; + utils::ScopedDir mContainersPathGuard; ContainerConfig mConfig; + Fixture() + : mContainersPathGuard(CONTAINERS_PATH) + {} + std::unique_ptr create(const std::string& configPath) { config::loadFromFile(configPath, mConfig); diff --git a/tests/unit_tests/server/ut-container.cpp b/tests/unit_tests/server/ut-container.cpp index fe5aa64..c42b80d 100644 --- a/tests/unit_tests/server/ut-container.cpp +++ b/tests/unit_tests/server/ut-container.cpp @@ -54,9 +54,13 @@ const std::string LXC_TEMPLATES_PATH = SC_TEST_LXC_TEMPLATES_INSTALL_DIR; struct Fixture { utils::ScopedGlibLoop mLoop; - utils::ScopedDir mContainersPathGuard = CONTAINERS_PATH; + utils::ScopedDir mContainersPathGuard; utils::ScopedDir mRunGuard; + Fixture() + : mContainersPathGuard(CONTAINERS_PATH) + {} + std::unique_ptr create(const std::string& configPath) { return std::unique_ptr(new Container(CONTAINERS_PATH, diff --git a/tests/unit_tests/server/ut-containers-manager.cpp b/tests/unit_tests/server/ut-containers-manager.cpp index 317981d..399cd12 100644 --- a/tests/unit_tests/server/ut-containers-manager.cpp +++ b/tests/unit_tests/server/ut-containers-manager.cpp @@ -379,10 +379,17 @@ private: struct Fixture { security_containers::utils::ScopedGlibLoop mLoop; - utils::ScopedDir mContainersPathGuard = CONTAINERS_PATH; - utils::ScopedDir mRun1Guard = utils::ScopedDir("/tmp/ut-run1"); - utils::ScopedDir mRun2Guard = utils::ScopedDir("/tmp/ut-run2"); - utils::ScopedDir mRun3Guard = utils::ScopedDir("/tmp/ut-run3"); + utils::ScopedDir mContainersPathGuard; + utils::ScopedDir mRun1Guard; + utils::ScopedDir mRun2Guard; + utils::ScopedDir mRun3Guard; + + Fixture() + : mContainersPathGuard(CONTAINERS_PATH) + , mRun1Guard("/tmp/ut-run1") + , mRun2Guard("/tmp/ut-run2") + , mRun3Guard("/tmp/ut-run3") + {} }; } // namespace diff --git a/tests/unit_tests/server/ut-server.cpp b/tests/unit_tests/server/ut-server.cpp index 2dbfabc..1c3c46b 100644 --- a/tests/unit_tests/server/ut-server.cpp +++ b/tests/unit_tests/server/ut-server.cpp @@ -38,7 +38,11 @@ namespace { const std::string CONTAINERS_PATH = "/tmp/ut-containers"; // the same as in daemon.conf struct Fixture { - security_containers::utils::ScopedDir mContainersPathGuard = CONTAINERS_PATH; + security_containers::utils::ScopedDir mContainersPathGuard; + + Fixture() + : mContainersPathGuard(CONTAINERS_PATH) + {} }; } // namespace -- 2.7.4 From 47782bbb0c4b7bf91aa773718d0c9f681fba6eaf Mon Sep 17 00:00:00 2001 From: Jan Olszak Date: Wed, 19 Nov 2014 15:26:28 +0100 Subject: [PATCH 05/16] Fixing build brake for gcc 4.6 [Bug/Feature] N/A [Cause] N/A [Solution] N/A [Verification] Build with older gcc Change-Id: I0c889f7d79e10403a18b915c0c30e91400542de4 --- common/ipc/internals/processor.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/common/ipc/internals/processor.cpp b/common/ipc/internals/processor.cpp index 50327c0..5634880 100644 --- a/common/ipc/internals/processor.cpp +++ b/common/ipc/internals/processor.cpp @@ -109,7 +109,8 @@ Processor::PeerID Processor::addPeer(const std::shared_ptr& socketPtr) { Lock lock(mSocketsMutex); peerID = getNextPeerID(); - mNewSockets.emplace(peerID, std::move(socketPtr)); + SocketInfo socketInfo(peerID, std::move(socketPtr)); + mNewSockets.push(std::move(socketInfo)); } LOGI("New peer added. Id: " << peerID); mEventQueue.send(Event::NEW_PEER); @@ -392,7 +393,7 @@ bool Processor::handleEvent() return false; } - mSockets.emplace(socketInfo.peerID, std::move(socketInfo.socketPtr)); + mSockets[socketInfo.peerID] = std::move(socketInfo.socketPtr); } resetPolling(); if (mNewPeerCallback) { @@ -454,9 +455,11 @@ bool Processor::handleCall() if (mReturnCallbacks.count(messageID) != 0) { LOGE("There already was a return callback for messageID: " << messageID); } - mReturnCallbacks.emplace(messageID, ReturnCallbacks(call.peerID, - std::move(call.parse), - std::move(call.process))); + + // move insertion + mReturnCallbacks[messageID] = std::move(ReturnCallbacks(call.peerID, + std::move(call.parse), + std::move(call.process))); } try { -- 2.7.4 From e3d2a40e88a88c3319bf3d06378549b4e1ce1e5e Mon Sep 17 00:00:00 2001 From: Piotr Bartosiewicz Date: Thu, 20 Nov 2014 13:09:49 +0100 Subject: [PATCH 06/16] Fix lxc templates [Bug/Feature] Tests was failing on some images [Cause] N/A [Solution] N/A [Verification] Build, install, run tests Change-Id: I9b2029539a4c5567b6eaed178ef1df2b7dce3c44 --- tests/unit_tests/lxc/templates/minimal-dbus1.sh | 3 +++ tests/unit_tests/lxc/templates/minimal-dbus2.sh | 3 +++ tests/unit_tests/lxc/templates/minimal-dbus3.sh | 3 +++ tests/unit_tests/lxc/templates/minimal.sh | 8 +++++++- 4 files changed, 16 insertions(+), 1 deletion(-) diff --git a/tests/unit_tests/lxc/templates/minimal-dbus1.sh b/tests/unit_tests/lxc/templates/minimal-dbus1.sh index 35f816f..ff1f2de 100755 --- a/tests/unit_tests/lxc/templates/minimal-dbus1.sh +++ b/tests/unit_tests/lxc/templates/minimal-dbus1.sh @@ -23,6 +23,7 @@ done ROOTFS_DIRS="\ ${rootfs}/bin \ ${rootfs}/dev \ +${rootfs}/dev/pts \ ${rootfs}/etc \ ${rootfs}/home \ ${rootfs}/lib \ @@ -34,6 +35,7 @@ ${rootfs}/sbin \ ${rootfs}/sys \ ${rootfs}/tmp \ ${rootfs}/usr \ +${rootfs}/opt \ ${rootfs}/var \ ${rootfs}/var/run " @@ -62,6 +64,7 @@ lxc.mount.entry = /etc etc none ro,bind 0 0 lxc.mount.entry = /lib lib none ro,bind 0 0 lxc.mount.entry = /sbin sbin none ro,bind 0 0 lxc.mount.entry = /usr usr none ro,rbind 0 0 +lxc.mount.entry = /opt opt none ro,rbind 0 0 lxc.mount.entry = /tmp/ut-run1 var/run none rw,bind 0 0 EOF diff --git a/tests/unit_tests/lxc/templates/minimal-dbus2.sh b/tests/unit_tests/lxc/templates/minimal-dbus2.sh index f8f963e..86984b7 100755 --- a/tests/unit_tests/lxc/templates/minimal-dbus2.sh +++ b/tests/unit_tests/lxc/templates/minimal-dbus2.sh @@ -23,6 +23,7 @@ done ROOTFS_DIRS="\ ${rootfs}/bin \ ${rootfs}/dev \ +${rootfs}/dev/pts \ ${rootfs}/etc \ ${rootfs}/home \ ${rootfs}/lib \ @@ -34,6 +35,7 @@ ${rootfs}/sbin \ ${rootfs}/sys \ ${rootfs}/tmp \ ${rootfs}/usr \ +${rootfs}/opt \ ${rootfs}/var \ ${rootfs}/var/run " @@ -62,6 +64,7 @@ lxc.mount.entry = /etc etc none ro,bind 0 0 lxc.mount.entry = /lib lib none ro,bind 0 0 lxc.mount.entry = /sbin sbin none ro,bind 0 0 lxc.mount.entry = /usr usr none ro,rbind 0 0 +lxc.mount.entry = /opt opt none ro,rbind 0 0 lxc.mount.entry = /tmp/ut-run2 var/run none rw,bind 0 0 EOF diff --git a/tests/unit_tests/lxc/templates/minimal-dbus3.sh b/tests/unit_tests/lxc/templates/minimal-dbus3.sh index 68f4f11..034b4fd 100755 --- a/tests/unit_tests/lxc/templates/minimal-dbus3.sh +++ b/tests/unit_tests/lxc/templates/minimal-dbus3.sh @@ -23,6 +23,7 @@ done ROOTFS_DIRS="\ ${rootfs}/bin \ ${rootfs}/dev \ +${rootfs}/dev/pts \ ${rootfs}/etc \ ${rootfs}/home \ ${rootfs}/lib \ @@ -34,6 +35,7 @@ ${rootfs}/sbin \ ${rootfs}/sys \ ${rootfs}/tmp \ ${rootfs}/usr \ +${rootfs}/opt \ ${rootfs}/var \ ${rootfs}/var/run " @@ -62,6 +64,7 @@ lxc.mount.entry = /etc etc none ro,bind 0 0 lxc.mount.entry = /lib lib none ro,bind 0 0 lxc.mount.entry = /sbin sbin none ro,bind 0 0 lxc.mount.entry = /usr usr none ro,rbind 0 0 +lxc.mount.entry = /opt opt none ro,rbind 0 0 lxc.mount.entry = /tmp/ut-run3 var/run none rw,bind 0 0 EOF diff --git a/tests/unit_tests/lxc/templates/minimal.sh b/tests/unit_tests/lxc/templates/minimal.sh index 547661e..873277e 100755 --- a/tests/unit_tests/lxc/templates/minimal.sh +++ b/tests/unit_tests/lxc/templates/minimal.sh @@ -23,6 +23,7 @@ done ROOTFS_DIRS="\ ${rootfs}/bin \ ${rootfs}/dev \ +${rootfs}/dev/pts \ ${rootfs}/etc \ ${rootfs}/home \ ${rootfs}/lib \ @@ -33,7 +34,8 @@ ${rootfs}/run \ ${rootfs}/sbin \ ${rootfs}/sys \ ${rootfs}/tmp \ -${rootfs}/usr +${rootfs}/usr \ +${rootfs}/opt " /bin/mkdir ${ROOTFS_DIRS} @@ -52,6 +54,9 @@ lxc.haltsignal = SIGTERM lxc.pts = 256 lxc.tty = 0 +#lxc.loglevel = TRACE +#lxc.logfile = /tmp/${name}.log + lxc.cgroup.devices.deny = a lxc.mount.auto = proc sys cgroup @@ -60,6 +65,7 @@ lxc.mount.entry = /etc etc none ro,bind 0 0 lxc.mount.entry = /lib lib none ro,bind 0 0 lxc.mount.entry = /sbin sbin none ro,bind 0 0 lxc.mount.entry = /usr usr none ro,rbind 0 0 +lxc.mount.entry = /opt opt none ro,rbind 0 0 EOF if [ "$(uname -m)" = "x86_64" ]; then -- 2.7.4 From 7ea2c7bdeffd0ebc8514840af180574b835b6784 Mon Sep 17 00:00:00 2001 From: Jan Olszak Date: Mon, 17 Nov 2014 12:58:32 +0100 Subject: [PATCH 07/16] IPC: NONBLOCK sockets [Bug/Feature] All writes and reads have timeout Timeout in callSync removes the peer [Cause] N/A [Solution] N/A [Verification] Build, install, run tests Change-Id: I86213b04e435a48bc56ae6f995a0c364b712a4d0 --- common/ipc/internals/event-queue.hpp | 12 ++++ common/ipc/internals/eventfd.cpp | 2 +- common/ipc/internals/processor.cpp | 120 +++++++++++++++++++++++++++------- common/ipc/internals/processor.hpp | 92 +++++++++++++++++++------- common/ipc/internals/socket.cpp | 12 +++- common/ipc/internals/utils.cpp | 123 ++++++++++++++++++++++++++--------- common/ipc/internals/utils.hpp | 6 +- common/ipc/types.cpp | 4 ++ common/ipc/types.hpp | 2 + tests/unit_tests/ipc/ut-ipc.cpp | 60 +++++++++++++++++ 10 files changed, 349 insertions(+), 84 deletions(-) diff --git a/common/ipc/internals/event-queue.hpp b/common/ipc/internals/event-queue.hpp index 82cb2ff..b50f0c4 100644 --- a/common/ipc/internals/event-queue.hpp +++ b/common/ipc/internals/event-queue.hpp @@ -71,6 +71,11 @@ public: */ MessageType receive(); + /** + * @return is the queue empty + */ + bool isEmpty(); + private: typedef std::lock_guard Lock; @@ -106,6 +111,13 @@ MessageType EventQueue::receive() return mess; } +template +bool EventQueue::isEmpty() +{ + Lock lock(mCommunicationMutex); + return mMessages.empty(); +} + } // namespace ipc } // namespace security_containers diff --git a/common/ipc/internals/eventfd.cpp b/common/ipc/internals/eventfd.cpp index c8a17b6..37cf7dd 100644 --- a/common/ipc/internals/eventfd.cpp +++ b/common/ipc/internals/eventfd.cpp @@ -39,7 +39,7 @@ namespace ipc { EventFD::EventFD() { - mFD = ::eventfd(0, EFD_SEMAPHORE); + mFD = ::eventfd(0, EFD_SEMAPHORE | EFD_NONBLOCK); if (mFD == -1) { LOGE("Error in eventfd: " << std::string(strerror(errno))); throw IPCException("Error in eventfd: " + std::string(strerror(errno))); diff --git a/common/ipc/internals/processor.cpp b/common/ipc/internals/processor.cpp index 5634880..9677991 100644 --- a/common/ipc/internals/processor.cpp +++ b/common/ipc/internals/processor.cpp @@ -48,9 +48,6 @@ namespace ipc { LOGE("Callback threw an error: " << e.what()); \ } - - - const Processor::MethodID Processor::RETURN_METHOD_ID = std::numeric_limits::max(); Processor::Processor(const PeerCallback& newPeerCallback, @@ -118,9 +115,31 @@ Processor::PeerID Processor::addPeer(const std::shared_ptr& socketPtr) return peerID; } -void Processor::removePeer(const PeerID peerID, Status status) +void Processor::removePeer(const PeerID peerID) { - LOGW("Removing naughty peer. ID: " << peerID); + std::shared_ptr conditionPtr(new std::condition_variable()); + + { + Lock lock(mSocketsMutex); + RemovePeerRequest request(peerID, conditionPtr); + mPeersToDelete.push(std::move(request)); + } + + mEventQueue.send(Event::DELETE_PEER); + + auto isPeerDeleted = [&peerID, this] { + Lock lock(mSocketsMutex); + return mSockets.count(peerID) == 0; + }; + + std::mutex mutex; + std::unique_lock lock(mutex); + conditionPtr->wait(lock, isPeerDeleted); +} + +void Processor::removePeerInternal(const PeerID peerID, Status status) +{ + LOGW("Removing peer. ID: " << peerID); { Lock lock(mSocketsMutex); mSockets.erase(peerID); @@ -141,9 +160,49 @@ void Processor::removePeer(const PeerID peerID, Status status) } } + if (mRemovedPeerCallback) { + // Notify about the deletion + mRemovedPeerCallback(peerID); + } + resetPolling(); } +void Processor::cleanCommunication() +{ + while (!mEventQueue.isEmpty()) { + switch (mEventQueue.receive()) { + case Event::FINISH: { + LOGD("Event FINISH after FINISH"); + break; + } + case Event::CALL: { + LOGD("Event CALL after FINISH"); + Call call = getCall(); + IGNORE_EXCEPTIONS(call.process(Status::CLOSING, call.data)); + break; + } + + case Event::NEW_PEER: { + LOGD("Event NEW_PEER after FINISH"); + break; + } + + case Event::DELETE_PEER: { + LOGD("Event DELETE_PEER after FINISH"); + RemovePeerRequest request; + { + Lock lock(mSocketsMutex); + request = std::move(mPeersToDelete.front()); + mPeersToDelete.pop(); + } + request.conditionPtr->notify_all(); + break; + } + } + } +} + void Processor::resetPolling() { LOGI("Resetting polling"); @@ -198,6 +257,8 @@ void Processor::run() continue; } } + + cleanCommunication(); } @@ -215,11 +276,10 @@ bool Processor::handleLostConnections() peersToRemove.push_back(socketIt->first); } } - } for (const PeerID peerID : peersToRemove) { - removePeer(peerID, Status::PEER_DISCONNECTED); + removePeerInternal(peerID, Status::PEER_DISCONNECTED); } return !peersToRemove.empty(); @@ -280,7 +340,7 @@ bool Processor::onReturnValue(const PeerID peerID, mReturnCallbacks.erase(messageID); } catch (const std::out_of_range&) { LOGW("No return callback for messageID: " << messageID); - removePeer(peerID, Status::NAUGHTY_PEER); + removePeerInternal(peerID, Status::NAUGHTY_PEER); return true; } @@ -291,7 +351,7 @@ bool Processor::onReturnValue(const PeerID peerID, } catch (const std::exception& e) { LOGE("Exception during parsing: " << e.what()); IGNORE_EXCEPTIONS(returnCallbacks.process(Status::PARSING_ERROR, data)); - removePeer(peerID, Status::PARSING_ERROR); + removePeerInternal(peerID, Status::PARSING_ERROR); return true; } @@ -314,7 +374,7 @@ bool Processor::onRemoteCall(const PeerID peerID, methodCallbacks = mMethodsCallbacks.at(methodID); } catch (const std::out_of_range&) { LOGW("No method callback for methodID: " << methodID); - removePeer(peerID, Status::NAUGHTY_PEER); + removePeerInternal(peerID, Status::NAUGHTY_PEER); return true; } @@ -324,7 +384,7 @@ bool Processor::onRemoteCall(const PeerID peerID, data = methodCallbacks->parse(socket.getFD()); } catch (const std::exception& e) { LOGE("Exception during parsing: " << e.what()); - removePeer(peerID, Status::PARSING_ERROR); + removePeerInternal(peerID, Status::PARSING_ERROR); return true; } @@ -334,7 +394,7 @@ bool Processor::onRemoteCall(const PeerID peerID, returnData = methodCallbacks->method(data); } catch (const std::exception& e) { LOGE("Exception in method handler: " << e.what()); - removePeer(peerID, Status::NAUGHTY_PEER); + removePeerInternal(peerID, Status::NAUGHTY_PEER); return true; } @@ -347,7 +407,7 @@ bool Processor::onRemoteCall(const PeerID peerID, methodCallbacks->serialize(socket.getFD(), returnData); } catch (const std::exception& e) { LOGE("Exception during serialization: " << e.what()); - removePeer(peerID, Status::SERIALIZATION_ERROR); + removePeerInternal(peerID, Status::SERIALIZATION_ERROR); return true; } @@ -402,6 +462,20 @@ bool Processor::handleEvent() } return true; } + + case Event::DELETE_PEER: { + LOGD("Event DELETE_PEER"); + RemovePeerRequest request; + { + Lock lock(mSocketsMutex); + request = std::move(mPeersToDelete.front()); + mPeersToDelete.pop(); + } + + removePeerInternal(request.peerID, Status::REMOVED_PEER); + request.conditionPtr->notify_all(); + return true; + } } return false; @@ -447,39 +521,37 @@ bool Processor::handleCall() return false; } - MessageID messageID = getNextMessageID(); - { // Set what to do with the return message Lock lock(mReturnCallbacksMutex); - if (mReturnCallbacks.count(messageID) != 0) { - LOGE("There already was a return callback for messageID: " << messageID); + if (mReturnCallbacks.count(call.messageID) != 0) { + LOGE("There already was a return callback for messageID: " << call.messageID); } // move insertion - mReturnCallbacks[messageID] = std::move(ReturnCallbacks(call.peerID, - std::move(call.parse), - std::move(call.process))); + mReturnCallbacks[call.messageID] = std::move(ReturnCallbacks(call.peerID, + std::move(call.parse), + std::move(call.process))); } try { // Send the call with the socket Socket::Guard guard = socketPtr->getGuard(); socketPtr->write(&call.methodID, sizeof(call.methodID)); - socketPtr->write(&messageID, sizeof(messageID)); + socketPtr->write(&call.messageID, sizeof(call.messageID)); call.serialize(socketPtr->getFD(), call.data); } catch (const std::exception& e) { LOGE("Error during sending a message: " << e.what()); // Inform about the error - IGNORE_EXCEPTIONS(mReturnCallbacks[messageID].process(Status::SERIALIZATION_ERROR, call.data)); + IGNORE_EXCEPTIONS(mReturnCallbacks[call.messageID].process(Status::SERIALIZATION_ERROR, call.data)); { Lock lock(mReturnCallbacksMutex); - mReturnCallbacks.erase(messageID); + mReturnCallbacks.erase(call.messageID); } - removePeer(call.peerID, Status::SERIALIZATION_ERROR); + removePeerInternal(call.peerID, Status::SERIALIZATION_ERROR); return true; } diff --git a/common/ipc/internals/processor.hpp b/common/ipc/internals/processor.hpp index 3df3f9c..e34bdb6 100644 --- a/common/ipc/internals/processor.hpp +++ b/common/ipc/internals/processor.hpp @@ -65,7 +65,6 @@ const unsigned int DEFAULT_MAX_NUMBER_OF_PEERS = 500; * - Rest: The data written in a callback. One type per method.ReturnCallbacks * * TODO: -* - error codes passed to async callbacks * - remove ReturnCallbacks on peer disconnect * - on sync timeout erase the return callback * - don't throw timeout if the message is already processed @@ -73,12 +72,15 @@ const unsigned int DEFAULT_MAX_NUMBER_OF_PEERS = 500; * - removePeer API function * - error handling - special message type * - some mutexes may not be needed +* - make addPeer synchronous like removePeer */ class Processor { public: typedef std::function PeerCallback; typedef unsigned int PeerID; typedef unsigned int MethodID; + typedef unsigned int MessageID; + /** * Method ID. Used to indicate a message with the return value. @@ -122,6 +124,13 @@ public: PeerID addPeer(const std::shared_ptr& socketPtr); /** + * Request removing peer and wait + * + * @param peerID id of the peer + */ + void removePeer(const PeerID peerID); + + /** * Saves the callbacks connected to the method id. * When a message with the given method id is received, * the data will be passed to the serialization callback through file descriptor. @@ -171,17 +180,16 @@ public: * @tparam ReceivedDataType data type to receive */ template - void callAsync(const MethodID methodID, - const PeerID peerID, - const std::shared_ptr& data, - const typename ResultHandler::type& process); + MessageID callAsync(const MethodID methodID, + const PeerID peerID, + const std::shared_ptr& data, + const typename ResultHandler::type& process); private: typedef std::function& data)> SerializeCallback; typedef std::function(int fd)> ParseCallback; typedef std::lock_guard Lock; - typedef unsigned int MessageID; struct Call { Call(const Call& other) = delete; @@ -195,6 +203,7 @@ private: SerializeCallback serialize; ParseCallback parse; ResultHandler::type process; + MessageID messageID; }; struct MethodHandlers { @@ -238,10 +247,26 @@ private: std::shared_ptr socketPtr; }; + struct RemovePeerRequest { + RemovePeerRequest(const RemovePeerRequest& other) = delete; + RemovePeerRequest& operator=(const RemovePeerRequest&) = delete; + RemovePeerRequest() = default; + RemovePeerRequest(RemovePeerRequest&&) = default; + RemovePeerRequest& operator=(RemovePeerRequest &&) = default; + + RemovePeerRequest(const PeerID peerID, + const std::shared_ptr& conditionPtr) + : peerID(peerID), conditionPtr(conditionPtr) {} + + PeerID peerID; + std::shared_ptr conditionPtr; + }; + enum class Event : int { FINISH, // Shutdown request CALL, // New method call in the queue - NEW_PEER // New peer in the queue + NEW_PEER, // New peer in the queue + DELETE_PEER // Delete peer }; EventQueue mEventQueue; @@ -258,6 +283,7 @@ private: std::mutex mSocketsMutex; std::unordered_map > mSockets; std::queue mNewSockets; + std::queue mPeersToDelete; // Mutex for modifying the map with return callbacks std::mutex mReturnCallbacksMutex; @@ -292,8 +318,8 @@ private: MessageID getNextMessageID(); PeerID getNextPeerID(); Call getCall(); - void removePeer(const PeerID peerID, Status status); - + void removePeerInternal(const PeerID peerID, Status status); + void cleanCommunication(); }; template @@ -336,10 +362,10 @@ void Processor::addMethodHandler(const MethodID methodID, } template -void Processor::callAsync(const MethodID methodID, - const PeerID peerID, - const std::shared_ptr& data, - const typename ResultHandler::type& process) +Processor::MessageID Processor::callAsync(const MethodID methodID, + const PeerID peerID, + const std::shared_ptr& data, + const typename ResultHandler::type& process) { static_assert(config::isVisitable::value, "Use the libConfig library"); @@ -357,6 +383,7 @@ void Processor::callAsync(const MethodID methodID, call.peerID = peerID; call.methodID = methodID; call.data = data; + call.messageID = getNextMessageID(); call.parse = [](const int fd)->std::shared_ptr { std::shared_ptr data(new ReceivedDataType()); @@ -379,6 +406,8 @@ void Processor::callAsync(const MethodID methodID, } mEventQueue.send(Event::CALL); + + return call.messageID; } @@ -400,30 +429,43 @@ std::shared_ptr Processor::callSync(const MethodID methodID, std::shared_ptr result; - std::mutex mtx; - std::unique_lock lck(mtx); + std::mutex mutex; std::condition_variable cv; Status returnStatus = ipc::Status::UNDEFINED; - auto process = [&result, &cv, &returnStatus](Status status, std::shared_ptr returnedData) { + auto process = [&result, &mutex, &cv, &returnStatus](Status status, std::shared_ptr returnedData) { + std::unique_lock lock(mutex); returnStatus = status; result = returnedData; - cv.notify_one(); + cv.notify_all(); }; - callAsync(methodID, - peerID, - data, - process); + MessageID messageID = callAsync(methodID, + peerID, + data, + process); auto isResultInitialized = [&returnStatus]() { return returnStatus != ipc::Status::UNDEFINED; }; - if (!cv.wait_for(lck, std::chrono::milliseconds(timeoutMS), isResultInitialized)) { - LOGE("Function call timeout; methodID: " << methodID); - throw IPCTimeoutException("Function call timeout; methodID: " + std::to_string(methodID)); + std::unique_lock lock(mutex); + if (!cv.wait_for(lock, std::chrono::milliseconds(timeoutMS), isResultInitialized)) { + bool isTimeout = false; + { + Lock lock(mReturnCallbacksMutex); + if (1 == mReturnCallbacks.erase(messageID)) { + isTimeout = true; + } + } + if (isTimeout) { + removePeer(peerID); + LOGE("Function call timeout; methodID: " << methodID); + throw IPCTimeoutException("Function call timeout; methodID: " + std::to_string(methodID)); + } else { + // Timeout started during the return value processing, so wait for it to finish + cv.wait(lock, isResultInitialized); + } } throwOnError(returnStatus); diff --git a/common/ipc/internals/socket.cpp b/common/ipc/internals/socket.cpp index 002b9cf..4ac977a 100644 --- a/common/ipc/internals/socket.cpp +++ b/common/ipc/internals/socket.cpp @@ -102,8 +102,8 @@ int Socket::getSystemdSocket(const std::string& path) { int n = ::sd_listen_fds(-1 /*Block further calls to sd_listen_fds*/); if (n < 0) { - LOGE("sd_listen_fds fails with errno: " + n); - throw IPCException("sd_listen_fds fails with errno: " + n); + LOGE("sd_listen_fds fails with errno: " << n); + throw IPCException("sd_listen_fds fails with errno: " + std::to_string(n)); } for (int fd = SD_LISTEN_FDS_START; @@ -193,6 +193,14 @@ Socket Socket::connectSocket(const std::string& path) throw IPCException("Error in connect: " + std::string(strerror(errno))); } + // Nonblock socket + int flags = fcntl(fd, F_GETFL, 0); + if (-1 == fcntl(fd, F_SETFL, flags | O_NONBLOCK)) { + ::close(fd); + LOGE("Error in fcntl: " + std::string(strerror(errno))); + throw IPCException("Error in fcntl: " + std::string(strerror(errno))); + } + return Socket(fd); } diff --git a/common/ipc/internals/utils.cpp b/common/ipc/internals/utils.cpp index e98b60d..bb11c80 100644 --- a/common/ipc/internals/utils.cpp +++ b/common/ipc/internals/utils.cpp @@ -30,16 +30,63 @@ #include #include +#include #include - +#include #include #include namespace fs = boost::filesystem; +namespace chr = std::chrono; namespace security_containers { namespace ipc { +namespace { + +void waitForEvent(int fd, + short event, + const chr::high_resolution_clock::time_point deadline) +{ + // Wait for the rest of the data + struct pollfd fds[1]; + fds[0].fd = fd; + fds[0].events = event | POLLHUP; + + for (;;) { + chr::milliseconds timeoutMS = chr::duration_cast(deadline - chr::high_resolution_clock::now()); + if (timeoutMS.count() < 0) { + LOGE("Timeout in read"); + throw IPCException("Timeout in read"); + } + + int ret = ::poll(fds, 1 /*fds size*/, timeoutMS.count()); + + if (ret == -1) { + if (errno == EINTR) { + continue; + } + LOGE("Error in poll: " + std::string(strerror(errno))); + throw IPCException("Error in poll: " + std::string(strerror(errno))); + } + + if (ret == 0) { + LOGE("Timeout in read"); + throw IPCException("Timeout in read"); + } + + if (fds[0].revents & POLLHUP) { + LOGE("Peer disconnected"); + throw IPCException("Peer disconnected"); + } + + // Here Comes the Sun + break; + } +} + +} // namespace + void close(int fd) { if (fd < 0) { @@ -59,46 +106,62 @@ void close(int fd) } } -void write(int fd, const void* bufferPtr, const size_t size) +void write(int fd, const void* bufferPtr, const size_t size, int timeoutMS) { + chr::high_resolution_clock::time_point deadline = chr::high_resolution_clock::now() + + chr::milliseconds(timeoutMS); + size_t nTotal = 0; - int n; + for (;;) { + int n = ::write(fd, + reinterpret_cast(bufferPtr) + nTotal, + size - nTotal); + if (n > 0) { + nTotal += n; + } else if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) { + // Neglected errors + LOGD("Retrying write"); + } else { + LOGE("Error during reading: " + std::string(strerror(errno))); + throw IPCException("Error during reading: " + std::string(strerror(errno))); + } - do { - n = ::write(fd, - reinterpret_cast(bufferPtr) + nTotal, - size - nTotal); - if (n < 0) { - if (errno == EINTR) { - LOGD("Write interrupted by a signal, retrying"); - continue; - } - LOGE("Error during writing: " + std::string(strerror(errno))); - throw IPCException("Error during witting: " + std::string(strerror(errno))); + if (nTotal >= size) { + // All data is written, break loop + break; + } else { + waitForEvent(fd, POLLOUT, deadline); } - nTotal += n; - } while (nTotal < size); + } } -void read(int fd, void* bufferPtr, const size_t size) +void read(int fd, void* bufferPtr, const size_t size, int timeoutMS) { - size_t nTotal = 0; - int n; + chr::high_resolution_clock::time_point deadline = chr::high_resolution_clock::now() + + chr::milliseconds(timeoutMS); - do { - n = ::read(fd, - reinterpret_cast(bufferPtr) + nTotal, - size - nTotal); - if (n < 0) { - if (errno == EINTR) { - LOGD("Read interrupted by a signal, retrying"); - continue; - } + size_t nTotal = 0; + for (;;) { + int n = ::read(fd, + reinterpret_cast(bufferPtr) + nTotal, + size - nTotal); + if (n > 0) { + nTotal += n; + } else if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) { + // Neglected errors + LOGD("Retrying read"); + } else { LOGE("Error during reading: " + std::string(strerror(errno))); throw IPCException("Error during reading: " + std::string(strerror(errno))); } - nTotal += n; - } while (nTotal < size); + + if (nTotal >= size) { + // All data is read, break loop + break; + } else { + waitForEvent(fd, POLLIN, deadline); + } + } } unsigned int getMaxFDNumber() diff --git a/common/ipc/internals/utils.hpp b/common/ipc/internals/utils.hpp index 0b1815d..a9a79a0 100644 --- a/common/ipc/internals/utils.hpp +++ b/common/ipc/internals/utils.hpp @@ -42,8 +42,9 @@ void close(int fd); * @param fd file descriptor * @param bufferPtr pointer to the data buffer * @param size size of data to write + * @param timeoutMS timeout in milliseconds */ -void write(int fd, const void* bufferPtr, const size_t size); +void write(int fd, const void* bufferPtr, const size_t size, int timeoutMS = 500); /** * Read from a file descriptor, throw on error. @@ -51,8 +52,9 @@ void write(int fd, const void* bufferPtr, const size_t size); * @param fd file descriptor * @param bufferPtr pointer to the data buffer * @param size size of the data to read + * @param timeoutMS timeout in milliseconds */ -void read(int fd, void* bufferPtr, const size_t size); +void read(int fd, void* bufferPtr, const size_t size, int timeoutMS = 500); /** * @return the max number of file descriptors for this process. diff --git a/common/ipc/types.cpp b/common/ipc/types.cpp index e0ffc5b..bce862c 100644 --- a/common/ipc/types.cpp +++ b/common/ipc/types.cpp @@ -37,6 +37,8 @@ std::string toString(const Status status) case Status::SERIALIZATION_ERROR: return "Exception during writing/serializing data to the socket"; case Status::PEER_DISCONNECTED: return "No such peer. Might got disconnected."; case Status::NAUGHTY_PEER: return "Peer performed a forbidden action."; + case Status::REMOVED_PEER: return "Removing peer"; + case Status::CLOSING: return "Closing IPC"; case Status::UNDEFINED: return "Undefined state"; default: return "Unknown status"; } @@ -56,6 +58,8 @@ void throwOnError(const Status status) case Status::SERIALIZATION_ERROR: throw IPCSerializationException(message); case Status::PEER_DISCONNECTED: throw IPCPeerDisconnectedException(message); case Status::NAUGHTY_PEER: throw IPCNaughtyPeerException(message); + case Status::REMOVED_PEER: throw IPCException(message); + case Status::CLOSING: throw IPCException(message); case Status::UNDEFINED: throw IPCException(message); default: return throw IPCException(message); } diff --git a/common/ipc/types.hpp b/common/ipc/types.hpp index c07e504..1bfaa4e 100644 --- a/common/ipc/types.hpp +++ b/common/ipc/types.hpp @@ -40,6 +40,8 @@ enum class Status : int { SERIALIZATION_ERROR, PEER_DISCONNECTED, NAUGHTY_PEER, + REMOVED_PEER, + CLOSING, UNDEFINED }; diff --git a/tests/unit_tests/ipc/ut-ipc.cpp b/tests/unit_tests/ipc/ut-ipc.cpp index ea70b45..5d284bd 100644 --- a/tests/unit_tests/ipc/ut-ipc.cpp +++ b/tests/unit_tests/ipc/ut-ipc.cpp @@ -71,6 +71,27 @@ struct SendData { ) }; +struct LongSendData { + LongSendData(int i = 0, int waitTime = 1000): mSendData(i), mWaitTime(waitTime), intVal(i) {} + + template + void accept(Visitor visitor) + { + std::this_thread::sleep_for(std::chrono::milliseconds(mWaitTime)); + mSendData.accept(visitor); + } + template + void accept(Visitor visitor) const + { + std::this_thread::sleep_for(std::chrono::milliseconds(mWaitTime)); + mSendData.accept(visitor); + } + + SendData mSendData; + int mWaitTime; + int intVal; +}; + struct EmptyData { CONFIG_REGISTER_EMPTY }; @@ -441,6 +462,45 @@ BOOST_AUTO_TEST_CASE(DisconnectedPeerErrorTest) } +BOOST_AUTO_TEST_CASE(ReadTimeoutTest) +{ + Service s(socketPath); + auto longEchoCallback = [](std::shared_ptr& data) { + return std::shared_ptr(new LongSendData(data->intVal)); + }; + s.addMethodHandler(1, longEchoCallback); + s.start(); + + Client c(socketPath); + c.start(); + + // Test timeout on read + std::shared_ptr sentData(new SendData(334)); + BOOST_CHECK_THROW((c.callSync(1, sentData, 100)), IPCException); +} + + +BOOST_AUTO_TEST_CASE(WriteTimeoutTest) +{ + Service s(socketPath); + s.addMethodHandler(1, echoCallback); + s.start(); + + Client c(socketPath); + c.start(); + + // Test echo with a minimal timeout + std::shared_ptr sentDataA(new LongSendData(34, 10 /*ms*/)); + std::shared_ptr recvData = c.callSync(1, sentDataA, 100); + BOOST_CHECK_EQUAL(recvData->intVal, sentDataA->intVal); + + // Test timeout on write + std::shared_ptr sentDataB(new LongSendData(34, 1000 /*ms*/)); + BOOST_CHECK_THROW((c.callSync(1, sentDataB, 100)), IPCTimeoutException); +} + + + // BOOST_AUTO_TEST_CASE(ConnectionLimitTest) // { // unsigned oldLimit = ipc::getMaxFDNumber(); -- 2.7.4 From ee57290a9ebdb2d3d6bc5f99950c9962ae8096d2 Mon Sep 17 00:00:00 2001 From: Jan Olszak Date: Mon, 24 Nov 2014 17:30:27 +0100 Subject: [PATCH 08/16] IPC: Refactoring [Bug/Feature] N/A [Cause] N/A [Solution] N/A [Verification] Build, install, run tests Change-Id: I421b6af0c5da5b6f1d73d69e491e976f84894272 --- common/ipc/internals/processor.cpp | 158 ++++++++++++++++++++----------------- common/ipc/internals/processor.hpp | 16 ++-- 2 files changed, 89 insertions(+), 85 deletions(-) diff --git a/common/ipc/internals/processor.cpp b/common/ipc/internals/processor.cpp index 9677991..9aadfff 100644 --- a/common/ipc/internals/processor.cpp +++ b/common/ipc/internals/processor.cpp @@ -110,7 +110,7 @@ Processor::PeerID Processor::addPeer(const std::shared_ptr& socketPtr) mNewSockets.push(std::move(socketInfo)); } LOGI("New peer added. Id: " << peerID); - mEventQueue.send(Event::NEW_PEER); + mEventQueue.send(Event::ADD_PEER); return peerID; } @@ -125,7 +125,7 @@ void Processor::removePeer(const PeerID peerID) mPeersToDelete.push(std::move(request)); } - mEventQueue.send(Event::DELETE_PEER); + mEventQueue.send(Event::REMOVE_PEER); auto isPeerDeleted = [&peerID, this] { Lock lock(mSocketsMutex); @@ -168,41 +168,6 @@ void Processor::removePeerInternal(const PeerID peerID, Status status) resetPolling(); } -void Processor::cleanCommunication() -{ - while (!mEventQueue.isEmpty()) { - switch (mEventQueue.receive()) { - case Event::FINISH: { - LOGD("Event FINISH after FINISH"); - break; - } - case Event::CALL: { - LOGD("Event CALL after FINISH"); - Call call = getCall(); - IGNORE_EXCEPTIONS(call.process(Status::CLOSING, call.data)); - break; - } - - case Event::NEW_PEER: { - LOGD("Event NEW_PEER after FINISH"); - break; - } - - case Event::DELETE_PEER: { - LOGD("Event DELETE_PEER after FINISH"); - RemovePeerRequest request; - { - Lock lock(mSocketsMutex); - request = std::move(mPeersToDelete.front()); - mPeersToDelete.pop(); - } - request.conditionPtr->notify_all(); - break; - } - } - } -} - void Processor::resetPolling() { LOGI("Resetting polling"); @@ -432,53 +397,63 @@ bool Processor::handleEvent() case Event::CALL: { LOGD("Event CALL"); - return handleCall(); + return onCall(); } - case Event::NEW_PEER: { - LOGD("Event NEW_PEER"); - SocketInfo socketInfo; - { - Lock lock(mSocketsMutex); + case Event::ADD_PEER: { + LOGD("Event ADD_PEER"); + return onNewPeer(); + } - socketInfo = std::move(mNewSockets.front()); - mNewSockets.pop(); + case Event::REMOVE_PEER: { + LOGD("Event REMOVE_PEER"); + return onRemovePeer(); + } + } - if (mSockets.size() > mMaxNumberOfPeers) { - LOGE("There are too many peers. I don't accept the connection with " << socketInfo.peerID); - return false; - } - if (mSockets.count(socketInfo.peerID) != 0) { - LOGE("There already was a socket for peerID: " << socketInfo.peerID); - return false; - } + return false; +} - mSockets[socketInfo.peerID] = std::move(socketInfo.socketPtr); - } - resetPolling(); - if (mNewPeerCallback) { - // Notify about the new user. - mNewPeerCallback(socketInfo.peerID); - } - return true; - } +bool Processor::onNewPeer() +{ + SocketInfo socketInfo; + { + Lock lock(mSocketsMutex); - case Event::DELETE_PEER: { - LOGD("Event DELETE_PEER"); - RemovePeerRequest request; - { - Lock lock(mSocketsMutex); - request = std::move(mPeersToDelete.front()); - mPeersToDelete.pop(); + socketInfo = std::move(mNewSockets.front()); + mNewSockets.pop(); + + if (mSockets.size() > mMaxNumberOfPeers) { + LOGE("There are too many peers. I don't accept the connection with " << socketInfo.peerID); + return false; + } + if (mSockets.count(socketInfo.peerID) != 0) { + LOGE("There already was a socket for peerID: " << socketInfo.peerID); + return false; } - removePeerInternal(request.peerID, Status::REMOVED_PEER); - request.conditionPtr->notify_all(); - return true; + mSockets[socketInfo.peerID] = std::move(socketInfo.socketPtr); } + resetPolling(); + if (mNewPeerCallback) { + // Notify about the new user. + mNewPeerCallback(socketInfo.peerID); } + return true; +} - return false; +bool Processor::onRemovePeer() +{ + RemovePeerRequest request; + { + Lock lock(mSocketsMutex); + request = std::move(mPeersToDelete.front()); + mPeersToDelete.pop(); + } + + removePeerInternal(request.peerID, Status::REMOVED_PEER); + request.conditionPtr->notify_all(); + return true; } Processor::MessageID Processor::getNextMessageID() @@ -505,7 +480,7 @@ Processor::Call Processor::getCall() return call; } -bool Processor::handleCall() +bool Processor::onCall() { LOGT("Handle call (from another thread) to send a message."); Call call = getCall(); @@ -558,5 +533,40 @@ bool Processor::handleCall() return false; } +void Processor::cleanCommunication() +{ + while (!mEventQueue.isEmpty()) { + switch (mEventQueue.receive()) { + case Event::FINISH: { + LOGD("Event FINISH after FINISH"); + break; + } + case Event::CALL: { + LOGD("Event CALL after FINISH"); + Call call = getCall(); + IGNORE_EXCEPTIONS(call.process(Status::CLOSING, call.data)); + break; + } + + case Event::ADD_PEER: { + LOGD("Event ADD_PEER after FINISH"); + break; + } + + case Event::REMOVE_PEER: { + LOGD("Event REMOVE_PEER after FINISH"); + RemovePeerRequest request; + { + Lock lock(mSocketsMutex); + request = std::move(mPeersToDelete.front()); + mPeersToDelete.pop(); + } + request.conditionPtr->notify_all(); + break; + } + } + } +} + } // namespace ipc } // namespace security_containers diff --git a/common/ipc/internals/processor.hpp b/common/ipc/internals/processor.hpp index e34bdb6..7554eb8 100644 --- a/common/ipc/internals/processor.hpp +++ b/common/ipc/internals/processor.hpp @@ -65,14 +65,7 @@ const unsigned int DEFAULT_MAX_NUMBER_OF_PEERS = 500; * - Rest: The data written in a callback. One type per method.ReturnCallbacks * * TODO: -* - remove ReturnCallbacks on peer disconnect -* - on sync timeout erase the return callback -* - don't throw timeout if the message is already processed -* - naming convention or methods that just commissions the PROCESS thread to do something -* - removePeer API function -* - error handling - special message type * - some mutexes may not be needed -* - make addPeer synchronous like removePeer */ class Processor { public: @@ -81,7 +74,6 @@ public: typedef unsigned int MethodID; typedef unsigned int MessageID; - /** * Method ID. Used to indicate a message with the return value. */ @@ -265,8 +257,8 @@ private: enum class Event : int { FINISH, // Shutdown request CALL, // New method call in the queue - NEW_PEER, // New peer in the queue - DELETE_PEER // Delete peer + ADD_PEER, // New peer in the queue + REMOVE_PEER // Remove peer }; EventQueue mEventQueue; @@ -303,7 +295,9 @@ private: void run(); bool handleEvent(); - bool handleCall(); + bool onCall(); + bool onNewPeer(); + bool onRemovePeer(); bool handleLostConnections(); bool handleInputs(); bool handleInput(const PeerID peerID, const Socket& socket); -- 2.7.4 From 873c1046ffe0a34ab7ec0a4d48e59e1adae754d3 Mon Sep 17 00:00:00 2001 From: Piotr Bartosiewicz Date: Mon, 24 Nov 2014 17:10:29 +0100 Subject: [PATCH 09/16] Fix create container from template [Bug/Feature] Templates stops working after migration to lxc [Cause] N/A [Solution] N/A [Verification] Build, install, run tests Change-Id: Ifbc0db612391eb7460b757b3cd12dda79183178b --- common/utils/environment.cpp | 10 +++-- common/utils/environment.hpp | 2 +- server/configs/CMakeLists.txt | 4 ++ server/configs/lxc-templates/template.sh | 72 ++++++++++++++++++++++++++++++++ server/configs/templates/template.conf | 3 ++ server/containers-manager.cpp | 26 +++++------- 6 files changed, 98 insertions(+), 19 deletions(-) create mode 100755 server/configs/lxc-templates/template.sh diff --git a/common/utils/environment.cpp b/common/utils/environment.cpp index eaaef12..73c7057 100644 --- a/common/utils/environment.cpp +++ b/common/utils/environment.cpp @@ -84,8 +84,9 @@ bool dropRoot(uid_t uid, gid_t gid, const std::vector& caps) return true; } -bool launchAsRoot(const std::function& func) +bool launchAsRoot(const std::function& func) { + // TODO optimize if getuid() == 0 pid_t pid = fork(); if (pid < 0) { LOGE("Fork failed: " << strerror(errno)); @@ -99,8 +100,11 @@ bool launchAsRoot(const std::function& func) } try { - func(); - } catch (std::exception& e) { + if (!func()) { + LOGE("Failed to successfully execute func"); + ::exit(EXIT_FAILURE); + } + } catch (const std::exception& e) { LOGE("Failed to successfully execute func: " << e.what()); ::exit(EXIT_FAILURE); } diff --git a/common/utils/environment.hpp b/common/utils/environment.hpp index 120b6ac..07a767e 100644 --- a/common/utils/environment.hpp +++ b/common/utils/environment.hpp @@ -50,7 +50,7 @@ bool dropRoot(uid_t uid, gid_t gid, const std::vector& caps); * * This function forks, sets UID 0 to child process and calls func. */ -bool launchAsRoot(const std::function& func); +bool launchAsRoot(const std::function& func); } // namespace utils diff --git a/server/configs/CMakeLists.txt b/server/configs/CMakeLists.txt index ab4a94f..e4f9df9 100644 --- a/server/configs/CMakeLists.txt +++ b/server/configs/CMakeLists.txt @@ -21,6 +21,7 @@ MESSAGE(STATUS "Installing configs to " ${SC_CONFIG_INSTALL_DIR}) FILE(GLOB container_CONF containers/*.conf) FILE(GLOB admin_CONF lxc-templates/*.sh) +FILE(GLOB template_CONF templates/*.conf) ## Generate #################################################################### CONFIGURE_FILE(systemd/security-containers.service.in @@ -44,5 +45,8 @@ INSTALL(FILES ${container_CONF} INSTALL(PROGRAMS ${admin_CONF} DESTINATION ${SC_CONFIG_INSTALL_DIR}/lxc-templates) +INSTALL(PROGRAMS ${template_CONF} + DESTINATION ${SC_CONFIG_INSTALL_DIR}/templates) + INSTALL(FILES ${CMAKE_BINARY_DIR}/systemd/security-containers.service DESTINATION ${SYSTEMD_UNIT_DIR}) diff --git a/server/configs/lxc-templates/template.sh b/server/configs/lxc-templates/template.sh new file mode 100755 index 0000000..c03a050 --- /dev/null +++ b/server/configs/lxc-templates/template.sh @@ -0,0 +1,72 @@ +#!/bin/bash + +echo LXC template, args: $@ + +options=$(getopt -o p:n: -l rootfs:,path:,name: -- "$@") +if [ $? -ne 0 ]; then + exit 1 +fi +eval set -- "$options" + +while true +do + case "$1" in + -p|--path) path=$2; shift 2;; + --rootfs) rootfs=$2; shift 2;; + -n|--name) name=$2; shift 2;; + --) shift 1; break ;; + *) break ;; + esac +done + +br_name="virbr-${name}" +sub_net="103" # TODO from param + +# XXX assume rootfs if mounted from iso + +# Prepare container configuration file +> ${path}/config +cat <> ${path}/config +lxc.utsname = ${name} +lxc.rootfs = ${rootfs} + +# userns 1-to-1 mapping +#lxc.id_map = u 0 0 65536 +#lxc.id_map = g 0 0 65536 + +lxc.pts = 256 +lxc.tty = 0 + +lxc.mount.auto = proc sys cgroup +lxc.mount.entry = /var/run/containers/${name}/run var/run none rw,bind 0 0 + +lxc.network.type = veth +lxc.network.link = ${br_name} +lxc.network.flags = up +lxc.network.name = eth0 +lxc.network.veth.pair = veth-${name} +lxc.network.ipv4.gateway = 10.0.${sub_net}.1 +lxc.network.ipv4 = 10.0.${sub_net}.2/24 + +lxc.hook.pre-start = ${path}/pre-start.sh + +#lxc.loglevel = TRACE +#lxc.logfile = /tmp/${name}.log +EOF + +# prepare pre start hook +cat <> ${path}/pre-start.sh +if [ -z "\$(/usr/sbin/brctl show | /bin/grep -P "${br_name}\t")" ] +then + /usr/sbin/brctl addbr ${br_name} + /usr/sbin/brctl setfd ${br_name} 0 + /sbin/ifconfig ${br_name} 10.0.${sub_net}.1 netmask 255.255.255.0 up +fi +if [ -z "\$(/usr/sbin/iptables -t nat -S | /bin/grep MASQUERADE)" ] +then + /bin/echo 1 > /proc/sys/net/ipv4/ip_forward + /usr/sbin/iptables -t nat -A POSTROUTING -s 10.0.0.0/16 ! -d 10.0.0.0/16 -j MASQUERADE +fi +EOF + +chmod 755 ${path}/pre-start.sh diff --git a/server/configs/templates/template.conf b/server/configs/templates/template.conf index a8f47fc..f91bf5f 100644 --- a/server/configs/templates/template.conf +++ b/server/configs/templates/template.conf @@ -1,4 +1,7 @@ { + "name" : "~NAME~", + "lxcTemplate" : "template.sh", + "initWithArgs" : [], "cpuQuotaForeground" : -1, "cpuQuotaBackground" : 1000, "privilege" : 10, diff --git a/server/containers-manager.cpp b/server/containers-manager.cpp index 62dbc14..def04c2 100644 --- a/server/containers-manager.cpp +++ b/server/containers-manager.cpp @@ -41,9 +41,6 @@ #include #include -#include -#include -#include #include #include #include @@ -70,7 +67,6 @@ const std::string HOST_ID = "host"; const std::string CONTAINER_TEMPLATE_CONFIG_PATH = "template.conf"; const boost::regex CONTAINER_NAME_REGEX("~NAME~"); -const boost::regex CONTAINER_UUID_REGEX("~UUID~"); const boost::regex CONTAINER_IP_THIRD_OCTET_REGEX("~IP~"); const unsigned int CONTAINER_IP_BASE_THIRD_OCTET = 100; @@ -522,12 +518,15 @@ void ContainersManager::handleGetContainerInfoCall(const std::string& id, result->setError(api::ERROR_INTERNAL, "Unrecognized state of container"); return; } - const std::string rootPath = boost::filesystem::absolute(id, mConfig.containersPath).string(); + const auto containerPath = boost::filesystem::absolute(id, mConfig.containersPath); + const auto rootfsDir = boost::filesystem::path("rootfs"); + const auto rootfsPath = containerPath / rootfsDir; + result->set(g_variant_new("((siss))", id.c_str(), container->getVT(), state, - rootPath.c_str())); + rootfsPath.string().c_str())); } void ContainersManager::handleSetActiveContainerCall(const std::string& id, @@ -581,14 +580,10 @@ void ContainersManager::generateNewConfig(const std::string& id, std::string resultConfig = boost::regex_replace(config, CONTAINER_NAME_REGEX, id); - boost::uuids::uuid u = boost::uuids::random_generator()(); - std::string uuidStr = to_string(u); - LOGD("uuid: " << uuidStr); - resultConfig = boost::regex_replace(resultConfig, CONTAINER_UUID_REGEX, uuidStr); - // generate third IP octet for network config + // TODO change algorithm after implementing removeContainer std::string thirdOctetStr = std::to_string(CONTAINER_IP_BASE_THIRD_OCTET + mContainers.size() + 1); - LOGD("ip_third_octet: " << thirdOctetStr); + LOGD("IP third octet: " << thirdOctetStr); resultConfig = boost::regex_replace(resultConfig, CONTAINER_IP_THIRD_OCTET_REGEX, thirdOctetStr); if (!utils::saveFileContent(resultPath, resultConfig)) { @@ -630,7 +625,7 @@ void ContainersManager::handleAddContainerCall(const std::string& id, } // copy container image if config contains path to image - LOGT("image path: " << mConfig.containerImagePath); + LOGT("Image path: " << mConfig.containerImagePath); if (!mConfig.containerImagePath.empty()) { auto copyImageContentsWrapper = std::bind(&utils::copyImageContents, mConfig.containerImagePath, @@ -659,6 +654,7 @@ void ContainersManager::handleAddContainerCall(const std::string& id, } catch(const std::exception& e) { LOGW("Failed to remove data: " << boost::diagnostic_information(e)); } + return true; }; try { @@ -682,13 +678,13 @@ void ContainersManager::handleAddContainerCall(const std::string& id, return; } - auto resultCallback = [this, id, result, containerPathStr, removeAllWrapper](bool succeeded) { + auto resultCallback = [this, id, result](bool succeeded) { if (succeeded) { focus(id); result->setVoid(); } else { LOGE("Failed to start container."); - utils::launchAsRoot(std::bind(removeAllWrapper, containerPathStr)); + // TODO removeContainer result->setError(api::host::ERROR_CONTAINER_CREATE_FAILED, "Failed to start container."); } -- 2.7.4 From 42b000bb7378f7d1e686306008767ff0e7c8babe Mon Sep 17 00:00:00 2001 From: Dariusz Michaluk Date: Tue, 25 Nov 2014 14:47:46 +0100 Subject: [PATCH 10/16] Fix RPM build error [Bug/Feature] RPM build error [Cause] Installed but unpackaged file found [Solution] Add file [Verification] Build Change-Id: Ic4f8258386ae9a289c637cf28ac878effda0e197 Signed-off-by: Dariusz Michaluk --- packaging/security-containers.spec | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packaging/security-containers.spec b/packaging/security-containers.spec index ee5ee83..3425c51 100644 --- a/packaging/security-containers.spec +++ b/packaging/security-containers.spec @@ -43,9 +43,11 @@ between them. A process from inside a container can request a switch of context %dir /etc/security-containers %dir /etc/security-containers/containers %dir /etc/security-containers/lxc-templates +%dir /etc/security-containers/templates %config /etc/security-containers/daemon.conf %config /etc/security-containers/containers/*.conf %attr(755,root,root) /etc/security-containers/lxc-templates/*.sh +%config /etc/security-containers/templates/*.conf %{_unitdir}/security-containers.service %{_unitdir}/multi-user.target.wants/security-containers.service /etc/dbus-1/system.d/org.tizen.containers.host.conf -- 2.7.4 From e47bd19c9bb22feaebb71432ded3b2d609ac7e45 Mon Sep 17 00:00:00 2001 From: Piotr Bartosiewicz Date: Tue, 25 Nov 2014 14:47:58 +0100 Subject: [PATCH 11/16] Templated network configuration [Bug/Feature] N/A [Cause] N/A [Solution] N/A [Verification] Build, install, run test Change-Id: I46af4da7ad656b05193184fc972c1d7489eb25f6 --- common/lxc/domain.cpp | 7 ++- common/lxc/domain.hpp | 2 +- server/configs/containers/business.conf | 4 +- server/configs/containers/private.conf | 4 +- server/configs/lxc-templates/business.sh | 72 ---------------------- server/configs/lxc-templates/private.sh | 72 ---------------------- server/configs/lxc-templates/template.sh | 15 +++-- server/configs/templates/template.conf | 2 + server/container-admin.cpp | 11 +++- server/container-config.hpp | 12 ++++ .../ut-client/containers/console1-dbus.conf.in | 2 + .../ut-client/containers/console2-dbus.conf.in | 2 + .../ut-client/containers/console3-dbus.conf.in | 2 + tests/unit_tests/lxc/ut-domain.cpp | 17 ++--- .../ut-container-admin/containers/buggy.conf | 2 + .../ut-container-admin/containers/missing.conf | 2 + .../containers/test-no-shutdown.conf | 2 + .../ut-container-admin/containers/test.conf | 2 + .../configs/ut-container/containers/buggy.conf | 2 + .../ut-container/containers/test-dbus.conf.in | 2 + .../configs/ut-container/containers/test.conf | 2 + .../containers/console1-dbus.conf.in | 2 + .../ut-containers-manager/containers/console1.conf | 2 + .../containers/console2-dbus.conf.in | 2 + .../ut-containers-manager/containers/console2.conf | 2 + .../containers/console3-dbus.conf.in | 2 + .../ut-containers-manager/containers/console3.conf | 2 + .../configs/ut-server/containers/container1.conf | 2 + .../configs/ut-server/containers/container2.conf | 2 + .../configs/ut-server/containers/container3.conf | 2 + 30 files changed, 93 insertions(+), 163 deletions(-) delete mode 100755 server/configs/lxc-templates/business.sh delete mode 100755 server/configs/lxc-templates/private.sh diff --git a/common/lxc/domain.cpp b/common/lxc/domain.cpp index bc2e74b..191f405 100644 --- a/common/lxc/domain.cpp +++ b/common/lxc/domain.cpp @@ -93,9 +93,12 @@ LxcDomain::State LxcDomain::getState() return STATE_MAP.at(str); } -bool LxcDomain::create(const std::string& templatePath) +bool LxcDomain::create(const std::string& templatePath, const char* const* argv) { - if (!mContainer->create(mContainer, templatePath.c_str(), NULL, NULL, 0, NULL)) { + if (!mContainer->create(mContainer, + templatePath.c_str(), + NULL, NULL, 0, + const_cast(argv))) { LOGE("Could not create domain " + getName()); return false; } diff --git a/common/lxc/domain.hpp b/common/lxc/domain.hpp index 8421d6d..60ae5a4 100644 --- a/common/lxc/domain.hpp +++ b/common/lxc/domain.hpp @@ -64,7 +64,7 @@ public: State getState(); - bool create(const std::string& templatePath); + bool create(const std::string& templatePath, const char* const* argv); bool destroy(); bool start(const char* const* argv); diff --git a/server/configs/containers/business.conf b/server/configs/containers/business.conf index 92eb06d..b211ce3 100644 --- a/server/configs/containers/business.conf +++ b/server/configs/containers/business.conf @@ -1,7 +1,9 @@ { "name" : "business", - "lxcTemplate" : "business.sh", + "lxcTemplate" : "template.sh", "initWithArgs" : [], + "ipv4Gateway" : "10.0.102.1", + "ipv4" : "10.0.102.2", "cpuQuotaForeground" : -1, "cpuQuotaBackground" : 10000, "enableDbusIntegration" : true, diff --git a/server/configs/containers/private.conf b/server/configs/containers/private.conf index 074f4f3..b4fb56a 100644 --- a/server/configs/containers/private.conf +++ b/server/configs/containers/private.conf @@ -1,7 +1,9 @@ { "name" : "private", - "lxcTemplate" : "private.sh", + "lxcTemplate" : "template.sh", "initWithArgs" : [], + "ipv4Gateway" : "10.0.101.1", + "ipv4" : "10.0.101.2", "cpuQuotaForeground" : -1, "cpuQuotaBackground" : 10000, "enableDbusIntegration" : true, diff --git a/server/configs/lxc-templates/business.sh b/server/configs/lxc-templates/business.sh deleted file mode 100755 index 21f7d2e..0000000 --- a/server/configs/lxc-templates/business.sh +++ /dev/null @@ -1,72 +0,0 @@ -#!/bin/bash - -echo LXC template, args: $@ - -options=$(getopt -o p:n: -l rootfs:,path:,name: -- "$@") -if [ $? -ne 0 ]; then - exit 1 -fi -eval set -- "$options" - -while true -do - case "$1" in - -p|--path) path=$2; shift 2;; - --rootfs) rootfs=$2; shift 2;; - -n|--name) name=$2; shift 2;; - --) shift 1; break ;; - *) break ;; - esac -done - -br_name="virbr-${name}" -sub_net="101" # TODO from param - -# XXX assume rootfs if mounted from iso - -# Prepare container configuration file -> ${path}/config -cat <> ${path}/config -lxc.utsname = ${name} -lxc.rootfs = ${rootfs} - -# userns 1-to-1 mapping -#lxc.id_map = u 0 0 65536 -#lxc.id_map = g 0 0 65536 - -lxc.pts = 256 -lxc.tty = 0 - -lxc.mount.auto = proc sys cgroup -lxc.mount.entry = /var/run/containers/${name}/run var/run none rw,bind 0 0 - -lxc.network.type = veth -lxc.network.link = ${br_name} -lxc.network.flags = up -lxc.network.name = eth0 -lxc.network.veth.pair = veth-${name} -lxc.network.ipv4.gateway = 10.0.${sub_net}.1 -lxc.network.ipv4 = 10.0.${sub_net}.2/24 - -lxc.hook.pre-start = ${path}/pre-start.sh - -#lxc.loglevel = TRACE -#lxc.logfile = /tmp/${name}.log -EOF - -# prepare pre start hook -cat <> ${path}/pre-start.sh -if [ -z "\$(/usr/sbin/brctl show | /bin/grep -P "${br_name}\t")" ] -then - /usr/sbin/brctl addbr ${br_name} - /usr/sbin/brctl setfd ${br_name} 0 - /sbin/ifconfig ${br_name} 10.0.${sub_net}.1 netmask 255.255.255.0 up -fi -if [ -z "\$(/usr/sbin/iptables -t nat -S | /bin/grep MASQUERADE)" ] -then - /bin/echo 1 > /proc/sys/net/ipv4/ip_forward - /usr/sbin/iptables -t nat -A POSTROUTING -s 10.0.0.0/16 ! -d 10.0.0.0/16 -j MASQUERADE -fi -EOF - -chmod 755 ${path}/pre-start.sh diff --git a/server/configs/lxc-templates/private.sh b/server/configs/lxc-templates/private.sh deleted file mode 100755 index 542093a..0000000 --- a/server/configs/lxc-templates/private.sh +++ /dev/null @@ -1,72 +0,0 @@ -#!/bin/bash - -echo LXC template, args: $@ - -options=$(getopt -o p:n: -l rootfs:,path:,name: -- "$@") -if [ $? -ne 0 ]; then - exit 1 -fi -eval set -- "$options" - -while true -do - case "$1" in - -p|--path) path=$2; shift 2;; - --rootfs) rootfs=$2; shift 2;; - -n|--name) name=$2; shift 2;; - --) shift 1; break ;; - *) break ;; - esac -done - -br_name="virbr-${name}" -sub_net="102" # TODO from param - -# XXX assume rootfs if mounted from iso - -# Prepare container configuration file -> ${path}/config -cat <> ${path}/config -lxc.utsname = ${name} -lxc.rootfs = ${rootfs} - -# userns 1-to-1 mapping -#lxc.id_map = u 0 0 65536 -#lxc.id_map = g 0 0 65536 - -lxc.pts = 256 -lxc.tty = 0 - -lxc.mount.auto = proc sys cgroup -lxc.mount.entry = /var/run/containers/${name}/run var/run none rw,bind 0 0 - -lxc.network.type = veth -lxc.network.link = ${br_name} -lxc.network.flags = up -lxc.network.name = eth0 -lxc.network.veth.pair = veth-${name} -lxc.network.ipv4.gateway = 10.0.${sub_net}.1 -lxc.network.ipv4 = 10.0.${sub_net}.2/24 - -lxc.hook.pre-start = ${path}/pre-start.sh - -#lxc.loglevel = TRACE -#lxc.logfile = /tmp/${name}.log -EOF - -# prepare pre start hook -cat <> ${path}/pre-start.sh -if [ -z "\$(/usr/sbin/brctl show | /bin/grep -P "${br_name}\t")" ] -then - /usr/sbin/brctl addbr ${br_name} - /usr/sbin/brctl setfd ${br_name} 0 - /sbin/ifconfig ${br_name} 10.0.${sub_net}.1 netmask 255.255.255.0 up -fi -if [ -z "\$(/usr/sbin/iptables -t nat -S | /bin/grep MASQUERADE)" ] -then - /bin/echo 1 > /proc/sys/net/ipv4/ip_forward - /usr/sbin/iptables -t nat -A POSTROUTING -s 10.0.0.0/16 ! -d 10.0.0.0/16 -j MASQUERADE -fi -EOF - -chmod 755 ${path}/pre-start.sh diff --git a/server/configs/lxc-templates/template.sh b/server/configs/lxc-templates/template.sh index c03a050..531ec30 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: -- "$@") +options=$(getopt -o p:n: -l rootfs:,path:,name:,ipv4:,ipv4-gateway: -- "$@") if [ $? -ne 0 ]; then exit 1 fi @@ -14,13 +14,14 @@ do -p|--path) path=$2; shift 2;; --rootfs) rootfs=$2; shift 2;; -n|--name) name=$2; shift 2;; + --ipv4) ipv4=$2; shift 2;; + --ipv4-gateway) ipv4_gateway=$2; shift 2;; --) shift 1; break ;; *) break ;; esac done br_name="virbr-${name}" -sub_net="103" # TODO from param # XXX assume rootfs if mounted from iso @@ -40,13 +41,16 @@ lxc.tty = 0 lxc.mount.auto = proc sys cgroup lxc.mount.entry = /var/run/containers/${name}/run var/run none rw,bind 0 0 +# create a separate network per container +# - it forbids traffic sniffing (like macvlan in bridge mode) +# - it enables traffic controlling from host using iptables lxc.network.type = veth lxc.network.link = ${br_name} lxc.network.flags = up lxc.network.name = eth0 lxc.network.veth.pair = veth-${name} -lxc.network.ipv4.gateway = 10.0.${sub_net}.1 -lxc.network.ipv4 = 10.0.${sub_net}.2/24 +lxc.network.ipv4.gateway = ${ipv4_gateway} +lxc.network.ipv4 = ${ipv4}/24 lxc.hook.pre-start = ${path}/pre-start.sh @@ -55,12 +59,13 @@ lxc.hook.pre-start = ${path}/pre-start.sh EOF # prepare pre start hook +> ${path}/pre-start.sh cat <> ${path}/pre-start.sh if [ -z "\$(/usr/sbin/brctl show | /bin/grep -P "${br_name}\t")" ] then /usr/sbin/brctl addbr ${br_name} /usr/sbin/brctl setfd ${br_name} 0 - /sbin/ifconfig ${br_name} 10.0.${sub_net}.1 netmask 255.255.255.0 up + /sbin/ifconfig ${br_name} ${ipv4_gateway} netmask 255.255.255.0 up fi if [ -z "\$(/usr/sbin/iptables -t nat -S | /bin/grep MASQUERADE)" ] then diff --git a/server/configs/templates/template.conf b/server/configs/templates/template.conf index f91bf5f..1939d51 100644 --- a/server/configs/templates/template.conf +++ b/server/configs/templates/template.conf @@ -2,6 +2,8 @@ "name" : "~NAME~", "lxcTemplate" : "template.sh", "initWithArgs" : [], + "ipv4Gateway" : "10.0.~IP~.1", + "ipv4" : "10.0.~IP~.2", "cpuQuotaForeground" : -1, "cpuQuotaBackground" : 1000, "privilege" : 10, diff --git a/server/container-admin.cpp b/server/container-admin.cpp index 71185db..ef8ae0f 100644 --- a/server/container-admin.cpp +++ b/server/container-admin.cpp @@ -91,7 +91,16 @@ ContainerAdmin::ContainerAdmin(const std::string& containersPath, const std::string lxcTemplate = utils::getAbsolutePath(config.lxcTemplate, lxcTemplatePrefix); LOGI(mId << ": Creating domain from template: " << lxcTemplate); - if (!mDom.create(lxcTemplate)) { + std::vector args; + if (!config.ipv4Gateway.empty()) { + args.push_back("--ipv4-gateway"); + args.push_back(config.ipv4Gateway); + } + if (!config.ipv4.empty()) { + args.push_back("--ipv4"); + args.push_back(config.ipv4); + } + if (!mDom.create(lxcTemplate, Args(args).getAsCArray())) { throw ContainerOperationException("Could not create domain"); } } diff --git a/server/container-config.hpp b/server/container-config.hpp index 95030e0..779415d 100644 --- a/server/container-config.hpp +++ b/server/container-config.hpp @@ -53,6 +53,16 @@ struct ContainerConfig { std::vector initWithArgs; /** + * IP v4 gateway address + */ + std::string ipv4Gateway; + + /** + * IP v4 address + */ + std::string ipv4; + + /** * Privilege of the container. * The smaller the value the more important the container */ @@ -107,6 +117,8 @@ struct ContainerConfig { name, lxcTemplate, initWithArgs, + ipv4Gateway, + ipv4, privilege, vt, switchToDefaultAfterTimeout, diff --git a/tests/unit_tests/client/configs/ut-client/containers/console1-dbus.conf.in b/tests/unit_tests/client/configs/ut-client/containers/console1-dbus.conf.in index 6855a28..bf8d1fc 100644 --- a/tests/unit_tests/client/configs/ut-client/containers/console1-dbus.conf.in +++ b/tests/unit_tests/client/configs/ut-client/containers/console1-dbus.conf.in @@ -2,6 +2,8 @@ "name" : "ut-containers-manager-console1-dbus", "lxcTemplate" : "minimal-dbus1.sh", "initWithArgs" : ["/bin/sh", "-c", "trap exit SIGTERM; /usr/bin/dbus-daemon --config-file=@SC_TEST_CONFIG_INSTALL_DIR@/server/ut-containers-manager/ut-dbus.conf --fork; read"], + "ipv4Gateway" : "", + "ipv4" : "", "privilege" : 20, "vt" : -1, "switchToDefaultAfterTimeout" : true, diff --git a/tests/unit_tests/client/configs/ut-client/containers/console2-dbus.conf.in b/tests/unit_tests/client/configs/ut-client/containers/console2-dbus.conf.in index 728522a..73ff1b3 100644 --- a/tests/unit_tests/client/configs/ut-client/containers/console2-dbus.conf.in +++ b/tests/unit_tests/client/configs/ut-client/containers/console2-dbus.conf.in @@ -2,6 +2,8 @@ "name" : "ut-containers-manager-console2-dbus", "lxcTemplate" : "minimal-dbus2.sh", "initWithArgs" : ["/bin/sh", "-c", "trap exit SIGTERM; /usr/bin/dbus-daemon --config-file=@SC_TEST_CONFIG_INSTALL_DIR@/server/ut-containers-manager/ut-dbus.conf --fork; read"], + "ipv4Gateway" : "", + "ipv4" : "", "privilege" : 20, "vt" : -1, "switchToDefaultAfterTimeout" : false, diff --git a/tests/unit_tests/client/configs/ut-client/containers/console3-dbus.conf.in b/tests/unit_tests/client/configs/ut-client/containers/console3-dbus.conf.in index d273e57..c8a0a01 100644 --- a/tests/unit_tests/client/configs/ut-client/containers/console3-dbus.conf.in +++ b/tests/unit_tests/client/configs/ut-client/containers/console3-dbus.conf.in @@ -2,6 +2,8 @@ "name" : "ut-containers-manager-console3-dbus", "lxcTemplate" : "minimal-dbus3.sh", "initWithArgs" : ["/bin/sh", "-c", "trap exit SIGTERM; /usr/bin/dbus-daemon --config-file=@SC_TEST_CONFIG_INSTALL_DIR@/server/ut-containers-manager/ut-dbus.conf --fork; read"], + "ipv4Gateway" : "", + "ipv4" : "", "privilege" : 20, "vt" : -1, "switchToDefaultAfterTimeout" : true, diff --git a/tests/unit_tests/lxc/ut-domain.cpp b/tests/unit_tests/lxc/ut-domain.cpp index aa6c3a9..a529f9f 100644 --- a/tests/unit_tests/lxc/ut-domain.cpp +++ b/tests/unit_tests/lxc/ut-domain.cpp @@ -42,6 +42,7 @@ using namespace security_containers::lxc; const std::string LXC_PATH = "/tmp/ut-lxc/"; const std::string DOMAIN_NAME = "ut-domain"; const std::string TEMPLATE = SC_TEST_LXC_TEMPLATES_INSTALL_DIR "/minimal.sh"; +const char* TEMPLATE_ARGS[] = {NULL}; struct Fixture { utils::ScopedDir mLxcDirGuard; @@ -89,7 +90,7 @@ BOOST_AUTO_TEST_CASE(CreateDestroyTest) LxcDomain lxc(LXC_PATH, DOMAIN_NAME); BOOST_CHECK(!lxc.isDefined()); - BOOST_CHECK(lxc.create(TEMPLATE)); + BOOST_CHECK(lxc.create(TEMPLATE, TEMPLATE_ARGS)); BOOST_CHECK(lxc.isDefined()); BOOST_CHECK_EQUAL(lxc.getConfigItem("lxc.rootfs"), LXC_PATH + DOMAIN_NAME + "/rootfs"); @@ -104,7 +105,7 @@ BOOST_AUTO_TEST_CASE(StartShutdownTest) { { LxcDomain lxc(LXC_PATH, DOMAIN_NAME); - BOOST_CHECK(lxc.create(TEMPLATE)); + BOOST_CHECK(lxc.create(TEMPLATE, TEMPLATE_ARGS)); } LxcDomain lxc(LXC_PATH, DOMAIN_NAME); BOOST_CHECK(lxc.getState() == LxcDomain::State::STOPPED); @@ -127,7 +128,7 @@ BOOST_AUTO_TEST_CASE(StartStopTest) { { LxcDomain lxc(LXC_PATH, DOMAIN_NAME); - BOOST_CHECK(lxc.create(TEMPLATE)); + BOOST_CHECK(lxc.create(TEMPLATE, TEMPLATE_ARGS)); } LxcDomain lxc(LXC_PATH, DOMAIN_NAME); BOOST_CHECK(lxc.getState() == LxcDomain::State::STOPPED); @@ -149,7 +150,7 @@ BOOST_AUTO_TEST_CASE(StartHasStoppedTest) { { LxcDomain lxc(LXC_PATH, DOMAIN_NAME); - BOOST_CHECK(lxc.create(TEMPLATE)); + BOOST_CHECK(lxc.create(TEMPLATE, TEMPLATE_ARGS)); } LxcDomain lxc(LXC_PATH, DOMAIN_NAME); BOOST_CHECK(lxc.getState() == LxcDomain::State::STOPPED); @@ -170,7 +171,7 @@ BOOST_AUTO_TEST_CASE(StartHasStoppedTest) BOOST_AUTO_TEST_CASE(FreezeUnfreezeTest) { LxcDomain lxc(LXC_PATH, DOMAIN_NAME); - BOOST_CHECK(lxc.create(TEMPLATE)); + BOOST_CHECK(lxc.create(TEMPLATE, TEMPLATE_ARGS)); const char* argv[] = { "/bin/sh", "-c", @@ -193,7 +194,7 @@ BOOST_AUTO_TEST_CASE(FreezeUnfreezeTest) BOOST_AUTO_TEST_CASE(FreezeStopTest) { LxcDomain lxc(LXC_PATH, DOMAIN_NAME); - BOOST_CHECK(lxc.create(TEMPLATE)); + BOOST_CHECK(lxc.create(TEMPLATE, TEMPLATE_ARGS)); const char* argv[] = { "/bin/sh", "-c", @@ -216,8 +217,8 @@ BOOST_AUTO_TEST_CASE(FreezeStopTest) BOOST_AUTO_TEST_CASE(RepeatTest) { LxcDomain lxc(LXC_PATH, DOMAIN_NAME); - BOOST_CHECK(lxc.create(TEMPLATE)); - BOOST_CHECK(!lxc.create(TEMPLATE));// forbidden + BOOST_CHECK(lxc.create(TEMPLATE, TEMPLATE_ARGS)); + BOOST_CHECK(!lxc.create(TEMPLATE, TEMPLATE_ARGS));// forbidden const char* argv[] = { "/bin/sh", "-c", diff --git a/tests/unit_tests/server/configs/ut-container-admin/containers/buggy.conf b/tests/unit_tests/server/configs/ut-container-admin/containers/buggy.conf index 22f5810..ffa8652 100644 --- a/tests/unit_tests/server/configs/ut-container-admin/containers/buggy.conf +++ b/tests/unit_tests/server/configs/ut-container-admin/containers/buggy.conf @@ -2,6 +2,8 @@ "name" : "ut-container-admin-test", "lxcTemplate" : "minimal.sh", "initWithArgs" : ["/foo"], + "ipv4Gateway" : "", + "ipv4" : "", "privilege" : 10, "vt" : -1, "switchToDefaultAfterTimeout" : true, diff --git a/tests/unit_tests/server/configs/ut-container-admin/containers/missing.conf b/tests/unit_tests/server/configs/ut-container-admin/containers/missing.conf index 6ff36fe..686dde2 100644 --- a/tests/unit_tests/server/configs/ut-container-admin/containers/missing.conf +++ b/tests/unit_tests/server/configs/ut-container-admin/containers/missing.conf @@ -2,6 +2,8 @@ "name" : "ut-container-admin-test", "lxcTemplate" : "missing.sh", "initWithArgs" : ["/bin/sh", "-c", "trap exit SIGTERM; read"], + "ipv4Gateway" : "", + "ipv4" : "", "privilege" : 10, "vt" : -1, "switchToDefaultAfterTimeout" : true, diff --git a/tests/unit_tests/server/configs/ut-container-admin/containers/test-no-shutdown.conf b/tests/unit_tests/server/configs/ut-container-admin/containers/test-no-shutdown.conf index 7877f8e..307afd5 100644 --- a/tests/unit_tests/server/configs/ut-container-admin/containers/test-no-shutdown.conf +++ b/tests/unit_tests/server/configs/ut-container-admin/containers/test-no-shutdown.conf @@ -2,6 +2,8 @@ "name" : "ut-container-admin-test", "lxcTemplate" : "minimal.sh", "initWithArgs" : ["/bin/sh"], + "ipv4Gateway" : "", + "ipv4" : "", "privilege" : 10, "vt" : -1, "switchToDefaultAfterTimeout" : true, diff --git a/tests/unit_tests/server/configs/ut-container-admin/containers/test.conf b/tests/unit_tests/server/configs/ut-container-admin/containers/test.conf index a7cf00d..76f4a70 100644 --- a/tests/unit_tests/server/configs/ut-container-admin/containers/test.conf +++ b/tests/unit_tests/server/configs/ut-container-admin/containers/test.conf @@ -2,6 +2,8 @@ "name" : "ut-container-admin-test", "lxcTemplate" : "minimal.sh", "initWithArgs" : ["/bin/sh", "-c", "trap exit SIGTERM; read"], + "ipv4Gateway" : "", + "ipv4" : "", "privilege" : 10, "vt" : -1, "switchToDefaultAfterTimeout" : true, diff --git a/tests/unit_tests/server/configs/ut-container/containers/buggy.conf b/tests/unit_tests/server/configs/ut-container/containers/buggy.conf index c31efc5..2af10da 100644 --- a/tests/unit_tests/server/configs/ut-container/containers/buggy.conf +++ b/tests/unit_tests/server/configs/ut-container/containers/buggy.conf @@ -2,6 +2,8 @@ "name" : "ut-container-test", "lxcTemplate" : "/buggy/path", "initWithArgs" : ["/bin/sh", "-c", "trap exit SIGTERM; read"], + "ipv4Gateway" : "", + "ipv4" : "", "privilege" : 10, "vt" : -1, "switchToDefaultAfterTimeout" : true, diff --git a/tests/unit_tests/server/configs/ut-container/containers/test-dbus.conf.in b/tests/unit_tests/server/configs/ut-container/containers/test-dbus.conf.in index e3eaf9b..f8e60f6 100644 --- a/tests/unit_tests/server/configs/ut-container/containers/test-dbus.conf.in +++ b/tests/unit_tests/server/configs/ut-container/containers/test-dbus.conf.in @@ -2,6 +2,8 @@ "name" : "ut-container-test-dbus", "lxcTemplate" : "minimal-dbus1.sh", "initWithArgs" : ["/bin/sh", "-c", "trap exit SIGTERM; /usr/bin/dbus-daemon --config-file=@SC_TEST_CONFIG_INSTALL_DIR@/server/ut-container/ut-dbus.conf --fork; read"], + "ipv4Gateway" : "", + "ipv4" : "", "privilege" : 10, "vt" : -1, "switchToDefaultAfterTimeout" : true, diff --git a/tests/unit_tests/server/configs/ut-container/containers/test.conf b/tests/unit_tests/server/configs/ut-container/containers/test.conf index ec6bf54..1264669 100644 --- a/tests/unit_tests/server/configs/ut-container/containers/test.conf +++ b/tests/unit_tests/server/configs/ut-container/containers/test.conf @@ -2,6 +2,8 @@ "name" : "ut-container-test", "lxcTemplate" : "minimal.sh", "initWithArgs" : ["/bin/sh", "-c", "trap exit SIGTERM; read"], + "ipv4Gateway" : "", + "ipv4" : "", "privilege" : 10, "vt" : -1, "switchToDefaultAfterTimeout" : true, diff --git a/tests/unit_tests/server/configs/ut-containers-manager/containers/console1-dbus.conf.in b/tests/unit_tests/server/configs/ut-containers-manager/containers/console1-dbus.conf.in index 6855a28..bf8d1fc 100644 --- a/tests/unit_tests/server/configs/ut-containers-manager/containers/console1-dbus.conf.in +++ b/tests/unit_tests/server/configs/ut-containers-manager/containers/console1-dbus.conf.in @@ -2,6 +2,8 @@ "name" : "ut-containers-manager-console1-dbus", "lxcTemplate" : "minimal-dbus1.sh", "initWithArgs" : ["/bin/sh", "-c", "trap exit SIGTERM; /usr/bin/dbus-daemon --config-file=@SC_TEST_CONFIG_INSTALL_DIR@/server/ut-containers-manager/ut-dbus.conf --fork; read"], + "ipv4Gateway" : "", + "ipv4" : "", "privilege" : 20, "vt" : -1, "switchToDefaultAfterTimeout" : true, diff --git a/tests/unit_tests/server/configs/ut-containers-manager/containers/console1.conf b/tests/unit_tests/server/configs/ut-containers-manager/containers/console1.conf index 2bc7251..1ca1e5f 100644 --- a/tests/unit_tests/server/configs/ut-containers-manager/containers/console1.conf +++ b/tests/unit_tests/server/configs/ut-containers-manager/containers/console1.conf @@ -2,6 +2,8 @@ "name" : "ut-containers-manager-console1", "lxcTemplate" : "minimal.sh", "initWithArgs" : ["/bin/sh", "-c", "trap exit SIGTERM; read"], + "ipv4Gateway" : "", + "ipv4" : "", "privilege" : 20, "vt" : -1, "switchToDefaultAfterTimeout" : true, diff --git a/tests/unit_tests/server/configs/ut-containers-manager/containers/console2-dbus.conf.in b/tests/unit_tests/server/configs/ut-containers-manager/containers/console2-dbus.conf.in index 728522a..73ff1b3 100644 --- a/tests/unit_tests/server/configs/ut-containers-manager/containers/console2-dbus.conf.in +++ b/tests/unit_tests/server/configs/ut-containers-manager/containers/console2-dbus.conf.in @@ -2,6 +2,8 @@ "name" : "ut-containers-manager-console2-dbus", "lxcTemplate" : "minimal-dbus2.sh", "initWithArgs" : ["/bin/sh", "-c", "trap exit SIGTERM; /usr/bin/dbus-daemon --config-file=@SC_TEST_CONFIG_INSTALL_DIR@/server/ut-containers-manager/ut-dbus.conf --fork; read"], + "ipv4Gateway" : "", + "ipv4" : "", "privilege" : 20, "vt" : -1, "switchToDefaultAfterTimeout" : false, diff --git a/tests/unit_tests/server/configs/ut-containers-manager/containers/console2.conf b/tests/unit_tests/server/configs/ut-containers-manager/containers/console2.conf index 9a0134f..155910b 100644 --- a/tests/unit_tests/server/configs/ut-containers-manager/containers/console2.conf +++ b/tests/unit_tests/server/configs/ut-containers-manager/containers/console2.conf @@ -2,6 +2,8 @@ "name" : "ut-containers-manager-console2", "lxcTemplate" : "minimal.sh", "initWithArgs" : ["/bin/sh", "-c", "trap exit SIGTERM; read"], + "ipv4Gateway" : "", + "ipv4" : "", "privilege" : 10, "vt" : -1, "switchToDefaultAfterTimeout" : true, diff --git a/tests/unit_tests/server/configs/ut-containers-manager/containers/console3-dbus.conf.in b/tests/unit_tests/server/configs/ut-containers-manager/containers/console3-dbus.conf.in index d273e57..c8a0a01 100644 --- a/tests/unit_tests/server/configs/ut-containers-manager/containers/console3-dbus.conf.in +++ b/tests/unit_tests/server/configs/ut-containers-manager/containers/console3-dbus.conf.in @@ -2,6 +2,8 @@ "name" : "ut-containers-manager-console3-dbus", "lxcTemplate" : "minimal-dbus3.sh", "initWithArgs" : ["/bin/sh", "-c", "trap exit SIGTERM; /usr/bin/dbus-daemon --config-file=@SC_TEST_CONFIG_INSTALL_DIR@/server/ut-containers-manager/ut-dbus.conf --fork; read"], + "ipv4Gateway" : "", + "ipv4" : "", "privilege" : 20, "vt" : -1, "switchToDefaultAfterTimeout" : true, diff --git a/tests/unit_tests/server/configs/ut-containers-manager/containers/console3.conf b/tests/unit_tests/server/configs/ut-containers-manager/containers/console3.conf index 6ff8900..a144c79 100644 --- a/tests/unit_tests/server/configs/ut-containers-manager/containers/console3.conf +++ b/tests/unit_tests/server/configs/ut-containers-manager/containers/console3.conf @@ -2,6 +2,8 @@ "name" : "ut-containers-manager-console3", "lxcTemplate" : "minimal.sh", "initWithArgs" : ["/bin/sh", "-c", "trap exit SIGTERM; read"], + "ipv4Gateway" : "", + "ipv4" : "", "privilege" : 15, "vt" : -1, "switchToDefaultAfterTimeout" : true, diff --git a/tests/unit_tests/server/configs/ut-server/containers/container1.conf b/tests/unit_tests/server/configs/ut-server/containers/container1.conf index d232bc3..995efb4 100644 --- a/tests/unit_tests/server/configs/ut-server/containers/container1.conf +++ b/tests/unit_tests/server/configs/ut-server/containers/container1.conf @@ -2,6 +2,8 @@ "name" : "ut-server-container1", "lxcTemplate" : "minimal.sh", "initWithArgs" : ["/bin/sh", "-c", "trap exit SIGTERM; read"], + "ipv4Gateway" : "", + "ipv4" : "", "privilege" : 20, "vt" : -1, "switchToDefaultAfterTimeout" : true, diff --git a/tests/unit_tests/server/configs/ut-server/containers/container2.conf b/tests/unit_tests/server/configs/ut-server/containers/container2.conf index 650782f..1e9b3b7 100644 --- a/tests/unit_tests/server/configs/ut-server/containers/container2.conf +++ b/tests/unit_tests/server/configs/ut-server/containers/container2.conf @@ -2,6 +2,8 @@ "name" : "ut-server-container2", "lxcTemplate" : "minimal.sh", "initWithArgs" : ["/bin/sh", "-c", "trap exit SIGTERM; read"], + "ipv4Gateway" : "", + "ipv4" : "", "privilege" : 10, "vt" : -1, "switchToDefaultAfterTimeout" : true, diff --git a/tests/unit_tests/server/configs/ut-server/containers/container3.conf b/tests/unit_tests/server/configs/ut-server/containers/container3.conf index 0d9f947..47396f5 100644 --- a/tests/unit_tests/server/configs/ut-server/containers/container3.conf +++ b/tests/unit_tests/server/configs/ut-server/containers/container3.conf @@ -2,6 +2,8 @@ "name" : "ut-server-container3", "lxcTemplate" : "minimal.sh", "initWithArgs" : ["/bin/sh", "-c", "trap exit SIGTERM; read"], + "ipv4Gateway" : "", + "ipv4" : "", "privilege" : 15, "vt" : -1, "switchToDefaultAfterTimeout" : true, -- 2.7.4 From 734be9c06a56b1492ea90589ebfb6af0667d7841 Mon Sep 17 00:00:00 2001 From: Dariusz Michaluk Date: Tue, 25 Nov 2014 14:33:28 +0100 Subject: [PATCH 12/16] Add client stubs for domain lifetime management [Bug/Feature] Client stubs for domain lifetime management [Cause] N/A [Solution] N/A [Verification] Build Change-Id: I4b6b659e4de5e18d2b00851ecbdeacfd80e3d442 Signed-off-by: Dariusz Michaluk --- client/security-containers-client-impl.cpp | 12 ++++++++++++ client/security-containers-client-impl.hpp | 10 ++++++++++ client/security-containers-client.cpp | 10 ++++++++++ client/security-containers-client.h | 18 ++++++++++++++++++ 4 files changed, 50 insertions(+) diff --git a/client/security-containers-client-impl.cpp b/client/security-containers-client-impl.cpp index 2fcc776..0f08a02 100644 --- a/client/security-containers-client-impl.cpp +++ b/client/security-containers-client-impl.cpp @@ -471,6 +471,18 @@ VsmStatus Client::vsm_start_domain(const char*) noexcept return vsm_get_status(); } +VsmStatus Client::vsm_lock_domain(const char*) noexcept +{ + mStatus = Status(VSMCLIENT_OTHER_ERROR, "Not implemented"); + return vsm_get_status(); +} + +VsmStatus Client::vsm_unlock_domain(const char*) noexcept +{ + mStatus = Status(VSMCLIENT_OTHER_ERROR, "Not implemented"); + return vsm_get_status(); +} + VsmStatus Client::vsm_add_state_callback(VsmContainerDbusStateCallback containerDbusStateCallback, void* data, VsmSubscriptionId* subscriptionId) noexcept diff --git a/client/security-containers-client-impl.hpp b/client/security-containers-client-impl.hpp index 48e1c19..df07099 100644 --- a/client/security-containers-client-impl.hpp +++ b/client/security-containers-client-impl.hpp @@ -159,6 +159,16 @@ public: VsmStatus vsm_start_domain(const char* id) noexcept; /** + * @see ::vsm_lock_domain + */ + VsmStatus vsm_lock_domain(const char* id) noexcept; + + /** + * @see ::vsm_unlock_domain + */ + VsmStatus vsm_unlock_domain(const char* id) noexcept; + + /** * @see ::vsm_add_state_callback */ VsmStatus vsm_add_state_callback(VsmContainerDbusStateCallback containerDbusStateCallback, diff --git a/client/security-containers-client.cpp b/client/security-containers-client.cpp index e867a9e..b96718f 100644 --- a/client/security-containers-client.cpp +++ b/client/security-containers-client.cpp @@ -173,6 +173,16 @@ API VsmStatus vsm_start_domain(VsmClient client, const char* id) return getClient(client).vsm_start_domain(id); } +API VsmStatus vsm_lock_domain(VsmClient client, const char* id) +{ + return getClient(client).vsm_lock_domain(id); +} + +API VsmStatus vsm_unlock_domain(VsmClient client, const char* id) +{ + return getClient(client).vsm_unlock_domain(id); +} + API VsmStatus vsm_add_state_callback(VsmClient client, VsmContainerDbusStateCallback containerDbusStateCallback, void* data, diff --git a/client/security-containers-client.h b/client/security-containers-client.h index ee16e01..b796c16 100644 --- a/client/security-containers-client.h +++ b/client/security-containers-client.h @@ -401,6 +401,24 @@ VsmStatus vsm_shutdown_domain(VsmClient client, const char* id); VsmStatus vsm_start_domain(VsmClient client, const char* id); /** + * Lock domain + * + * @param[in] client security-containers-server's client + * @param[in] id domain name + * @return status of this function call + */ +VsmStatus vsm_lock_domain(VsmClient client, const char* id); + +/** + * Unlock domain + * + * @param[in] client security-containers-server's client + * @param[in] id domain name + * @return status of this function call + */ +VsmStatus vsm_unlock_domain(VsmClient client, const char* id); + +/** * Register dbus state change callback function. * * @note The callback function will be invoked on a different thread. -- 2.7.4 From 8bf786c5dbd0a19132e439fc0800a613aa4fa544 Mon Sep 17 00:00:00 2001 From: Dariusz Michaluk Date: Tue, 25 Nov 2014 16:13:41 +0100 Subject: [PATCH 13/16] Added lock_domain/unlock_domain to cli [Feature] Ability to lock/unlock domain through cli [Cause] The need for the ability to lock/unlock domains [Solution] Add lock_domain/unlock_domain cli function [Verification] Build, install, run Change-Id: I4a017b328f0b44913f0561bd444e913c68f67359 Signed-off-by: Dariusz Michaluk --- cli/command-line-interface.cpp | 22 ++++++++++++++++++++++ cli/command-line-interface.hpp | 14 ++++++++++++++ cli/main.cpp | 16 ++++++++++++++++ 3 files changed, 52 insertions(+) diff --git a/cli/command-line-interface.cpp b/cli/command-line-interface.cpp index d027e63..0549afd 100644 --- a/cli/command-line-interface.cpp +++ b/cli/command-line-interface.cpp @@ -154,6 +154,28 @@ void create_domain(int pos, int argc, const char** argv) one_shot(bind(vsm_create_domain, _1, argv[pos + 1], nullptr)); } +void lock_domain(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_lock_domain, _1, argv[pos + 1])); +} + +void unlock_domain(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_unlock_domain, _1, argv[pos + 1])); +} + void lookup_domain_by_id(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 c254d84..41f5048 100644 --- a/cli/command-line-interface.hpp +++ b/cli/command-line-interface.hpp @@ -111,6 +111,20 @@ void set_active_container(int pos, int argc, const char** argv); void create_domain(int pos, int argc, const char** argv); /** + * Parses command line arguments and call vsm_lock_domain + * + * @see vsm_lock_domain + */ +void lock_domain(int pos, int argc, const char** argv); + +/** + * Parses command line arguments and call vsm_unlock_domain + * + * @see vsm_unlock_domain + */ +void unlock_domain(int pos, int argc, const char** argv); + +/** * Parses command line arguments and call vsm_lookup_domain_by_id * * @see vsm_lookup_domain_by_id diff --git a/cli/main.cpp b/cli/main.cpp index d701475..774881b 100644 --- a/cli/main.cpp +++ b/cli/main.cpp @@ -50,6 +50,22 @@ std::map commands = { } }, { + "lock_domain", { + lock_domain, + "lock_domain container_id", + "Lock container", + {{"container_id", "id container name"}} + } + }, + { + "unlock_domain", { + unlock_domain, + "unlock_domain container_id", + "Unlock container", + {{"container_id", "id container name"}} + } + }, + { "lookup_domain_by_id", { lookup_domain_by_id, "lookup_domain_by_id container_id", -- 2.7.4 From 67c0aa5c4ea021326fd0d1565143543e29f468a2 Mon Sep 17 00:00:00 2001 From: Dariusz Michaluk Date: Mon, 24 Nov 2014 15:33:27 +0100 Subject: [PATCH 14/16] Add tizen common (with wayland) lxc template Change-Id: If6d1fe2641f9a36a7d3a8c16b9a658661491ff13 Signed-off-by: Dariusz Michaluk --- .../configs/lxc-templates/tizen-common-wayland.sh | 398 +++++++++++++++++++++ 1 file changed, 398 insertions(+) create mode 100755 server/configs/lxc-templates/tizen-common-wayland.sh diff --git a/server/configs/lxc-templates/tizen-common-wayland.sh b/server/configs/lxc-templates/tizen-common-wayland.sh new file mode 100755 index 0000000..002e1f7 --- /dev/null +++ b/server/configs/lxc-templates/tizen-common-wayland.sh @@ -0,0 +1,398 @@ +#!/bin/bash + +# lxc-tizen template script +# +# Copyright (c) 2000 - 2014 Samsung Electronics Co., Ltd All Rights Reserved +# +# Contact: Dariusz Michaluk +# +# 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. + +usage() +{ + cat < + [-p|--path=] [-r|--rootfs=] [-v|--vt=] + [--ipv4=] [--ipv4-gateway=] [-h|--help] +Mandatory args: + -n,--name container name +Optional args: + -p,--path path to container config files, defaults to /var/lib/lxc + --rootfs path to container rootfs, defaults to /var/lib/lxc/[NAME]/rootfs + -v,--vt container virtual terminal + --ipv4 container IP address + --ipv4-gateway container gateway + -h,--help print help +EOF + return 0 +} + +options=$(getopt -o hp:v:n: -l help,rootfs:,path:,vt:,name:,ipv4:,ipv4-gateway: -- "$@") +if [ $? -ne 0 ]; then + usage $(basename $0) + exit 1 +fi +eval set -- "$options" + +while true +do + case "$1" in + -h|--help) usage $0 && exit 0;; + --rootfs) rootfs=$2; shift 2;; + -p|--path) path=$2; shift 2;; + -v|--vt) vt=$2; shift 2;; + -n|--name) name=$2; shift 2;; + --ipv4) ipv4=$2; shift 2;; + --ipv4-gateway) ipv4_gateway=$2; shift 2;; + --) shift 1; break ;; + *) break ;; + esac +done + +if [ "$(id -u)" != "0" ]; then + echo "This script should be run as 'root'" + exit 1 +fi + +if [ -z $name ]; then + echo "Container name must be given" + exit 1 +fi + +if [ -z "$path" ]; then + echo "'path' parameter is required" + exit 1 +fi + +br_name="virbr-${name}" + +# Prepare container rootfs +ROOTFS_DIRS="\ +${rootfs}/bin \ +${rootfs}/dev \ +${rootfs}/etc \ +${rootfs}/home \ +${rootfs}/home/alice \ +${rootfs}/home/bob \ +${rootfs}/home/carol \ +${rootfs}/home/guest \ +${rootfs}/lib \ +${rootfs}/media \ +${rootfs}/mnt \ +${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 \ +${path}/hooks \ +${path}/scripts \ +${path}/systemd \ +${path}/systemd/system \ +${path}/systemd/user +" +/bin/mkdir ${ROOTFS_DIRS} +/bin/chown alice:users ${rootfs}/home/alice +/bin/chown bob:users ${rootfs}/home/bob +/bin/chown carol:users ${rootfs}/home/carol +/bin/chown guest:users ${rootfs}/home/guest + +/bin/ln -s /dev/null ${path}/systemd/system/bluetooth.service +/bin/ln -s /dev/null ${path}/systemd/system/sshd.service +/bin/ln -s /dev/null ${path}/systemd/system/sshd.socket +/bin/ln -s /dev/null ${path}/systemd/system/sshd@.service +/bin/ln -s /dev/null ${path}/systemd/system/systemd-udevd.service +/bin/ln -s /dev/null ${path}/systemd/system/user-session-launch@seat0-5100.service +/bin/ln -s /dev/null ${path}/systemd/system/vconf-setup.service + +cat <>${path}/systemd/system/display-manager-run.service +# Run weston with framebuffer backend on selected virtual terminal. +[Unit] +Description=Weston display daemon + +[Service] +User=display +WorkingDirectory=/run/%u +# FIXME: log files shouldn't be stored in tmpfs directories (can get quite big and have side effects) +#ExecStart=/bin/sh -c 'backend=drm ; [ -d /dev/dri ] || backend=fbdev ; exec /usr/bin/weston --backend=$backend-backend.so -i0 --log=/run/%u/weston.log' +ExecStart=/usr/bin/weston --backend=fbdev-backend.so -i0 --log=/tmp/weston.log --tty=${vt} +#StandardInput=tty +#TTYPath=/dev/tty7 +EnvironmentFile=/etc/systemd/system/weston +Restart=on-failure +RestartSec=10 + +#adding the capability to configure ttys +#may be needed if the user 'display' doesn't own the tty +CapabilityBoundingSet=CAP_SYS_TTY_CONFIG + +[Install] +WantedBy=graphical.target +EOF + +cat <>${path}/systemd/system/display-manager.path +# Wayland socket path is changed to /tmp directory. +[Unit] +Description=Wait for wayland socket +Requires=display-manager-run.service +After=display-manager-run.service + +[Path] +PathExists=/tmp/wayland-0 +EOF + +cat <>${path}/systemd/system/display-manager.service +# Wayland socket path is changed to /tmp directory. +[Unit] +Description=Display manager setup service +Requires=display-manager-run.service +After=display-manager-run.service + +[Service] +Type=oneshot +ExecStart=/usr/bin/chmod g+w /tmp/wayland-0 +#ExecStart=/usr/bin/chsmack -a User /tmp/wayland-0 + +[Install] +WantedBy=graphical.target +EOF + +cat <>${path}/systemd/system/weston +# path to display manager runtime dir +XDG_RUNTIME_DIR=/tmp +XDG_CONFIG_HOME=/etc/systemd/system +EOF + +cat <>${path}/systemd/system/weston.ini +# Weston config for container. +[core] +modules=desktop-shell.so + +[shell] +background-image=/usr/share/backgrounds/tizen/golfe-morbihan.jpg +background-color=0xff002244 +background-type=scale-crop +panel-color=0x95333333 +locking=true +panel=false +animation=zoom +#binding-modifier=ctrl +num-workspaces=4 +#cursor-theme=whiteglass +#cursor-size=24 +startup-animation=fade + +#lockscreen-icon=/usr/share/icons/gnome/256x256/actions/lock.png +#lockscreen=/usr/share/backgrounds/gnome/Garden.jpg +#homescreen=/usr/share/backgrounds/gnome/Blinds.jpg + +## weston + +[launcher] +icon=/usr/share/icons/tizen/32x32/terminal.png +path=/usr/bin/weston-terminal + +[screensaver] +# Uncomment path to disable screensaver +duration=600 + +[input-method] +path=/usr/libexec/weston-keyboard +#path=/bin/weekeyboard + +#[keyboard] +#keymap_layout=fr + +#[output] +#name=LVDS1 +#mode=1680x1050 +#transform=90 +#icc_profile=/usr/share/color/icc/colord/Bluish.icc + +#[output] +#name=VGA1 +#mode=173.00 1920 2048 2248 2576 1080 1083 1088 1120 -hsync +vsync +#transform=flipped + +#[output] +#name=X1 +#mode=1024x768 +#transform=flipped-270 + +#[touchpad] +#constant_accel_factor = 50 +#min_accel_factor = 0.16 +#max_accel_factor = 1.0 + +[output] +name=DP1 +default_output=1 +EOF + +cat <>${path}/systemd/user/weston-user.service +# Wayland socket path is changed to /tmp directory. +[Unit] +Description=Shared weston session + +[Service] +ExecStartPre=/usr/bin/ln -sf /tmp/wayland-0 /run/user/%U/ +ExecStart=/bin/sh -l -c "/usr/bin/tz-launcher -c /usr/share/applications/tizen/launcher.conf %h/.applications/desktop" +EnvironmentFile=/etc/sysconfig/weston-user + +[Install] +WantedBy=default.target +EOF + +# Prepare host configuration +cat <>/etc/udev/rules.d/99-tty.rules +SUBSYSTEM=="tty", KERNEL=="tty${vt}", OWNER="display", SECLABEL{smack}="^" +EOF + +cat </etc/systemd/system/display-manager-run.service +# Run weston with framebuffer backend on tty7. +[Unit] +Description=Weston display daemon + +[Service] +User=display +WorkingDirectory=/run/%u +# FIXME: log files shouldn't be stored in tmpfs directories (can get quite big and have side effects) +#ExecStart=/bin/sh -c 'backend=drm ; [ -d /dev/dri ] || backend=fbdev ; exec /usr/bin/weston --backend=$backend-backend.so -i0 --log=/run/%u/weston.log' +ExecStart=/usr/bin/weston --backend=fbdev-backend.so -i0 --log=/run/%u/weston.log +StandardInput=tty +TTYPath=/dev/tty7 +EnvironmentFile=/etc/sysconfig/weston +Restart=on-failure +RestartSec=10 + +#adding the capability to configure ttys +#may be needed if the user 'display' doesn't own the tty +#CapabilityBoundingSet=CAP_SYS_TTY_CONFIG + +[Install] +WantedBy=graphical.target +EOF + +# Prepare container configuration file +cat <>${path}/config +lxc.utsname = ${name} +lxc.rootfs = ${rootfs} + +#lxc.cap.drop = audit_control +#lxc.cap.drop = audit_write +#lxc.cap.drop = mac_admin +#lxc.cap.drop = mac_override +#lxc.cap.drop = mknod +#lxc.cap.drop = setfcap +#lxc.cap.drop = setpcap +#lxc.cap.drop = sys_admin +#lxc.cap.drop = sys_boot +#lxc.cap.drop = sys_chroot #required by SSH +#lxc.cap.drop = sys_module +#lxc.cap.drop = sys_nice +#lxc.cap.drop = sys_pacct +#lxc.cap.drop = sys_rawio +#lxc.cap.drop = sys_resource +#lxc.cap.drop = sys_time +#lxc.cap.drop = sys_tty_config #required by getty + +lxc.cgroup.devices.deny = a +lxc.cgroup.devices.allow = c 1:* rwm #/dev/null, /dev/zero, ... +lxc.cgroup.devices.allow = c 5:* rwm #/dev/console, /dev/ptmx, ... +lxc.cgroup.devices.allow = c 136:* rwm #/dev/pts/0 ... +lxc.cgroup.devices.allow = c 10:223 rwm #/dev/uinput +lxc.cgroup.devices.allow = c 13:64 rwm #/dev/input/event0 +lxc.cgroup.devices.allow = c 13:65 rwm #/dev/input/event1 +lxc.cgroup.devices.allow = c 13:66 rwm #/dev/input/event2 +lxc.cgroup.devices.allow = c 13:67 rwm #/dev/input/event3 +lxc.cgroup.devices.allow = c 13:68 rwm #/dev/input/event4 +lxc.cgroup.devices.allow = c 13:69 rwm #/dev/input/event5 +lxc.cgroup.devices.allow = c 13:63 rwm #/dev/input/mice +lxc.cgroup.devices.allow = c 13:32 rwm #/dev/input/mouse0 +lxc.cgroup.devices.allow = c 226:0 rwm #/dev/dri/card0 +lxc.cgroup.devices.allow = c 2:* rwm #/dev/pty + +lxc.pts = 256 +lxc.tty = 0 +#lxc.console=/dev/tty1 +lxc.kmsg = 0 + +#lxc.cgroup.cpu.shares = 1024 +#lxc.cgroup.cpuset.cpus = 0,1,2,3 +#lxc.cgroup.memory.limit_in_bytes = 512M +#lxc.cgroup.memory.memsw.limit_in_bytes = 1G +#lxc.cgroup.blkio.weight = 500 + +lxc.mount.auto = proc sys:rw cgroup +lxc.mount = ${path}/fstab + +# create a separate network per container +# - it forbids traffic sniffing (like macvlan in bridge mode) +# - it enables traffic controlling from host using iptables +lxc.network.type = veth +lxc.network.link = ${br_name} +lxc.network.flags = up +lxc.network.name = eth0 +lxc.network.veth.pair = veth-${name} +lxc.network.ipv4.gateway = ${ipv4_gateway} +lxc.network.ipv4 = ${ipv4}/24 + +lxc.hook.pre-start = ${path}/hooks/pre-start.sh +#lxc.hook.post-stop = ${path}/hooks/post-stop.sh +EOF + +# Prepare container hook files +cat <>${path}/hooks/pre-start.sh +if [ -z "\$(/usr/sbin/brctl show | /bin/grep -P "${br_name}\t")" ] +then + /usr/sbin/brctl addbr ${br_name} + /usr/sbin/brctl setfd ${br_name} 0 + /sbin/ifconfig ${br_name} ${ipv4_gateway} netmask 255.255.255.0 up +fi +if [ -z "\$(/usr/sbin/iptables -t nat -S | /bin/grep MASQUERADE)" ] +then + /bin/echo 1 > /proc/sys/net/ipv4/ip_forward + /usr/sbin/iptables -t nat -A POSTROUTING -s 10.0.0.0/16 ! -d 10.0.0.0/16 -j MASQUERADE +fi +EOF + +chmod 770 ${path}/hooks/pre-start.sh + +# Prepare container fstab file +cat <>${path}/fstab +/bin bin none ro,bind 0 0 +/etc etc none ro,bind 0 0 +${path}/systemd/system etc/systemd/system none ro,bind 0 0 +${path}/systemd/user etc/systemd/user none ro,bind 0 0 +/lib lib none ro,bind 0 0 +/media media none ro,bind 0 0 +/mnt mnt none ro,bind 0 0 +/sbin sbin none ro,bind 0 0 +/usr usr none ro,rbind 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 +#tmpfs run tmpfs rw,nosuid,nodev,mode=755 0 0 +EOF -- 2.7.4 From 24a8cab8c347623492ea89d32e8d250e9e1e1c03 Mon Sep 17 00:00:00 2001 From: Mateusz Malicki Date: Wed, 26 Nov 2014 15:24:00 +0100 Subject: [PATCH 15/16] Added testing union in config file [Bug/Feature] Testing union in config file [Cause] Added union to config [Solution] Added union field in testing structure [Verification] Build, install, run scs tests Change-Id: I538e6ea370ea1a57f647bbde0f040b1585a4fd0a --- tests/unit_tests/config/ut-configuration.cpp | 47 ++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/tests/unit_tests/config/ut-configuration.cpp b/tests/unit_tests/config/ut-configuration.cpp index 1d72bd8..7f9703b 100644 --- a/tests/unit_tests/config/ut-configuration.cpp +++ b/tests/unit_tests/config/ut-configuration.cpp @@ -26,6 +26,7 @@ #include "config.hpp" #include "ut.hpp" #include "config/fields.hpp" +#include "config/fields-union.hpp" #include "config/manager.hpp" #include #include @@ -62,6 +63,14 @@ struct TestConfig { ) }; + struct SubConfigOption { + CONFIG_DECLARE_UNION + ( + SubConfig, + int + ) + }; + int intVal; std::int64_t int64Val; std::string stringVal; @@ -76,6 +85,10 @@ struct TestConfig { SubConfig subObj; std::vector subVector; + SubConfigOption union1; + SubConfigOption union2; + std::vector unions; + CONFIG_REGISTER ( intVal, @@ -90,7 +103,11 @@ struct TestConfig { doubleVector, subObj, - subVector + subVector, + + union1, + union2, + unions ) }; @@ -111,7 +128,14 @@ const std::string jsonTestString = "\"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 } } ] }"; + "{ \"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; @@ -344,4 +368,23 @@ BOOST_AUTO_TEST_CASE(FromToFDTest) fs::remove(fifoPath); } +BOOST_AUTO_TEST_CASE(ConfigUnion) +{ + TestConfig testConfig; + BOOST_REQUIRE_NO_THROW(loadFromString(jsonTestString, testConfig)); + + BOOST_CHECK(testConfig.union1.is()); + BOOST_CHECK(!testConfig.union1.is()); + BOOST_CHECK_EQUAL(testConfig.union1.as(), 2); + BOOST_CHECK(!testConfig.union2.is()); + BOOST_CHECK(testConfig.union2.is()); + TestConfig::SubConfig& subConfig = testConfig.union2.as(); + BOOST_CHECK_EQUAL(subConfig.intVal, 54321); + BOOST_CHECK(testConfig.unions[0].is()); + BOOST_CHECK(testConfig.unions[1].is()); + + std::string out = saveToString(testConfig); + BOOST_CHECK_EQUAL(out, jsonTestString); +} + BOOST_AUTO_TEST_SUITE_END() -- 2.7.4 From 14ad168b8ffff24f1775e497e4762d1682c544f9 Mon Sep 17 00:00:00 2001 From: Piotr Bartosiewicz Date: Thu, 27 Nov 2014 16:39:33 +0100 Subject: [PATCH 16/16] Support for destroying container [Bug/Feature] N/A [Cause] N/A [Solution] N/A [Verification] Build, install, run tests Change-Id: I4303284b06fdebf452a040c9dfa2c1cddc0b8abf --- cli/command-line-interface.cpp | 11 +++ cli/command-line-interface.hpp | 7 ++ cli/main.cpp | 8 ++ client/security-containers-client-impl.cpp | 9 ++- client/security-containers-client.cpp | 2 +- client/security-containers-client.h | 2 +- server/common-dbus-definitions.hpp | 2 +- server/container-admin.cpp | 14 ++++ server/container-admin.hpp | 6 ++ server/container.cpp | 6 ++ server/container.hpp | 5 ++ server/containers-manager.cpp | 94 +++++++++++++++------- server/containers-manager.hpp | 17 +++- server/host-connection.cpp | 24 ++++-- server/host-connection.hpp | 15 +++- server/host-dbus-definitions.hpp | 9 ++- tests/unit_tests/client/ut-client.cpp | 2 +- tests/unit_tests/server/configs/CMakeLists.txt | 9 ++- .../empty-dbus-daemon.conf.in | 22 +++++ .../ut-containers-manager/templates/template.conf | 11 --- .../templates/template.conf.in | 16 ++++ tests/unit_tests/server/ut-containers-manager.cpp | 83 ++++++++++++------- 22 files changed, 279 insertions(+), 95 deletions(-) create mode 100644 tests/unit_tests/server/configs/ut-containers-manager/empty-dbus-daemon.conf.in delete mode 100644 tests/unit_tests/server/configs/ut-containers-manager/templates/template.conf create mode 100644 tests/unit_tests/server/configs/ut-containers-manager/templates/template.conf.in diff --git a/cli/command-line-interface.cpp b/cli/command-line-interface.cpp index 0549afd..b46871d 100644 --- a/cli/command-line-interface.cpp +++ b/cli/command-line-interface.cpp @@ -154,6 +154,17 @@ void create_domain(int pos, int argc, const char** argv) one_shot(bind(vsm_create_domain, _1, argv[pos + 1], nullptr)); } +void destroy_domain(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_destroy_domain, _1, argv[pos + 1], 1)); +} + void lock_domain(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 41f5048..360f46c 100644 --- a/cli/command-line-interface.hpp +++ b/cli/command-line-interface.hpp @@ -111,6 +111,13 @@ void set_active_container(int pos, int argc, const char** argv); void create_domain(int pos, int argc, const char** argv); /** + * Parses command line arguments and call vsm_destroy_domain + * + * @see vsm_destroy_domain + */ +void destroy_domain(int pos, int argc, const char** argv); + +/** * Parses command line arguments and call vsm_lock_domain * * @see vsm_lock_domain diff --git a/cli/main.cpp b/cli/main.cpp index 774881b..0ceb1e5 100644 --- a/cli/main.cpp +++ b/cli/main.cpp @@ -50,6 +50,14 @@ std::map commands = { } }, { + "destroy_domain", { + destroy_domain, + "destroy_domain container_id", + "Destroy container", + {{"container_id", "id container name"}} + } + }, + { "lock_domain", { lock_domain, "lock_domain container_id", diff --git a/client/security-containers-client-impl.cpp b/client/security-containers-client-impl.cpp index 0f08a02..1a6e999 100644 --- a/client/security-containers-client-impl.cpp +++ b/client/security-containers-client-impl.cpp @@ -450,13 +450,14 @@ VsmStatus Client::vsm_create_domain(const char* id, const char* tname) noexcept } GVariant* args_in = g_variant_new("(s)", id); - return callMethod(HOST_INTERFACE, api::host::METHOD_ADD_CONTAINER, args_in); + return callMethod(HOST_INTERFACE, api::host::METHOD_CREATE_CONTAINER, args_in); } -VsmStatus Client::vsm_destroy_domain(const char*) noexcept +VsmStatus Client::vsm_destroy_domain(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_DESTROY_CONTAINER, args_in); } VsmStatus Client::vsm_shutdown_domain(const char*) noexcept diff --git a/client/security-containers-client.cpp b/client/security-containers-client.cpp index b96718f..ef571c8 100644 --- a/client/security-containers-client.cpp +++ b/client/security-containers-client.cpp @@ -158,7 +158,7 @@ API VsmStatus vsm_create_domain(VsmClient client, const char* id, const char* tn return getClient(client).vsm_create_domain(id, tname); } -API VsmStatus vsm_destroy_domain(VsmClient client, const char* id) +API VsmStatus vsm_destroy_domain(VsmClient client, const char* id, int /*force*/) { return getClient(client).vsm_destroy_domain(id); } diff --git a/client/security-containers-client.h b/client/security-containers-client.h index b796c16..e7e738c 100644 --- a/client/security-containers-client.h +++ b/client/security-containers-client.h @@ -380,7 +380,7 @@ VsmStatus vsm_create_domain(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_domain(VsmStatus clent, const char* id, int force); +VsmStatus vsm_destroy_domain(VsmClient clent, const char* id, int force); /** * Shutdown domain diff --git a/server/common-dbus-definitions.hpp b/server/common-dbus-definitions.hpp index 14bb037..77d0949 100644 --- a/server/common-dbus-definitions.hpp +++ b/server/common-dbus-definitions.hpp @@ -32,7 +32,7 @@ namespace security_containers { namespace api { const std::string ERROR_FORBIDDEN = "org.tizen.containers.Error.Forbidden"; const std::string ERROR_FORWARDED = "org.tizen.containers.Error.Forwarded"; -const std::string ERROR_UNKNOWN_ID = "org.tizen.containers.Error.UnknownId"; +const std::string ERROR_INVALID_ID = "org.tizen.containers.Error.InvalidId"; const std::string ERROR_INTERNAL = "org.tizen.containers.Error.Internal"; const std::string METHOD_PROXY_CALL = "ProxyCall"; diff --git a/server/container-admin.cpp b/server/container-admin.cpp index ef8ae0f..65179c3 100644 --- a/server/container-admin.cpp +++ b/server/container-admin.cpp @@ -111,6 +111,15 @@ ContainerAdmin::~ContainerAdmin() { LOGD(mId << ": Destroying ContainerAdmin object..."); + if (mDestroyOnExit) { + if (!mDom.stop()) { + LOGE(mId << ": Failed to stop the container"); + } + if (!mDom.destroy()) { + LOGE(mId << ": Failed to destroy the container"); + } + } + if (!mDetachOnExit) { // Try to forcefully stop if (!mDom.stop()) { @@ -271,6 +280,11 @@ void ContainerAdmin::setDetachOnExit() mDetachOnExit = true; } +void ContainerAdmin::setDestroyOnExit() +{ + mDestroyOnExit = true; +} + std::int64_t ContainerAdmin::getSchedulerQuota() { // assert(mDom); diff --git a/server/container-admin.hpp b/server/container-admin.hpp index 79b0193..f3c9a4f 100644 --- a/server/container-admin.hpp +++ b/server/container-admin.hpp @@ -116,6 +116,11 @@ public: void setDetachOnExit(); /** + * Set if container should be destroyed on exit. + */ + void setDestroyOnExit(); + + /** * @return Scheduler CFS quota, * TODO: this function is only for UNIT TESTS */ @@ -126,6 +131,7 @@ private: lxc::LxcDomain mDom; const std::string mId; bool mDetachOnExit; + bool mDestroyOnExit; void setSchedulerParams(std::uint64_t cpuShares, std::uint64_t vcpuPeriod, std::int64_t vcpuQuota); }; diff --git a/server/container.cpp b/server/container.cpp index 80decd5..c371d8a 100644 --- a/server/container.cpp +++ b/server/container.cpp @@ -240,6 +240,12 @@ void Container::setDetachOnExit() } } +void Container::setDestroyOnExit() +{ + Lock lock(mReconnectMutex); + mAdmin->setDestroyOnExit(); +} + bool Container::isRunning() { Lock lock(mReconnectMutex); diff --git a/server/container.hpp b/server/container.hpp index 76e098a..bc1303c 100644 --- a/server/container.hpp +++ b/server/container.hpp @@ -135,6 +135,11 @@ public: void setDetachOnExit(); /** + * Set if container should be destroyed on exit. + */ + void setDestroyOnExit(); + + /** * @return Is the container running? */ bool isRunning(); diff --git a/server/containers-manager.cpp b/server/containers-manager.cpp index def04c2..5a6a32b 100644 --- a/server/containers-manager.cpp +++ b/server/containers-manager.cpp @@ -101,11 +101,14 @@ ContainersManager::ContainersManager(const std::string& managerConfigPath): mDet mHostConnection.setSetActiveContainerCallback(bind(&ContainersManager::handleSetActiveContainerCall, this, _1, _2)); - mHostConnection.setAddContainerCallback(bind(&ContainersManager::handleAddContainerCall, - this, _1, _2)); + mHostConnection.setCreateContainerCallback(bind(&ContainersManager::handleCreateContainerCall, + this, _1, _2)); + + mHostConnection.setDestroyContainerCallback(bind(&ContainersManager::handleDestroyContainerCall, + this, _1, _2)); for (auto& containerConfig : mConfig.containerConfigs) { - addContainer(containerConfig); + createContainer(containerConfig); } // check if default container exists, throw ContainerOperationException if not found @@ -143,7 +146,7 @@ ContainersManager::~ContainersManager() LOGD("ContainersManager object destroyed"); } -void ContainersManager::addContainer(const std::string& containerConfig) +void ContainersManager::createContainer(const std::string& containerConfig) { std::string baseConfigPath = utils::dirName(mConfigPath); std::string containerConfigPath = utils::getAbsolutePath(containerConfig, baseConfigPath); @@ -177,6 +180,20 @@ void ContainersManager::addContainer(const std::string& containerConfig) mContainers.insert(ContainerMap::value_type(id, std::move(c))); } +void ContainersManager::destroyContainer(const std::string& containerId) +{ + // TODO mutex for mContainers access + auto it = mContainers.find(containerId); + if (it == mContainers.end()) { + LOGE("Failed to destroy container " << containerId << ": no such container"); + throw ContainerOperationException("No such container"); + } + + // TODO give back the focus + it->second->setDestroyOnExit(); + mContainers.erase(it); +} + void ContainersManager::focus(const std::string& containerId) { /* try to access the object first to throw immediately if it doesn't exist */ @@ -440,7 +457,7 @@ void ContainersManager::handleProxyCall(const std::string& caller, ContainerMap::const_iterator targetIter = mContainers.find(target); if (targetIter == mContainers.end()) { LOGE("Target container '" << target << "' not found"); - result->setError(api::ERROR_UNKNOWN_ID, "Unknown proxy call target"); + result->setError(api::ERROR_INVALID_ID, "Unknown proxy call target"); return; } @@ -501,7 +518,7 @@ void ContainersManager::handleGetContainerInfoCall(const std::string& id, LOGI("GetContainerInfo call"); if (mContainers.count(id) == 0) { LOGE("No container with id=" << id); - result->setError(api::ERROR_UNKNOWN_ID, "No such container id"); + result->setError(api::ERROR_INVALID_ID, "No such container id"); return; } const auto& container = mContainers[id]; @@ -536,7 +553,7 @@ void ContainersManager::handleSetActiveContainerCall(const std::string& id, auto container = mContainers.find(id); if (container == mContainers.end()){ LOGE("No container with id=" << id ); - result->setError(api::ERROR_UNKNOWN_ID, "No such container id"); + result->setError(api::ERROR_INVALID_ID, "No such container id"); return; } @@ -597,33 +614,30 @@ void ContainersManager::generateNewConfig(const std::string& id, fs::perms::others_read); } -void ContainersManager::handleAddContainerCall(const std::string& id, - dbus::MethodResultBuilder::Pointer result) +void ContainersManager::handleCreateContainerCall(const std::string& id, + dbus::MethodResultBuilder::Pointer result) { if (id.empty()) { LOGE("Failed to add container - invalid name."); - result->setError(api::host::ERROR_CONTAINER_CREATE_FAILED, - "Failed to add container - invalid name."); + result->setError(api::ERROR_INVALID_ID, "Invalid name"); return; } - LOGI("Adding container " << id); + LOGI("Creating container " << id); // TODO: This solution is temporary. It utilizes direct access to config files when creating new // containers. Update this handler when config database will appear. namespace fs = boost::filesystem; - boost::system::error_code ec; - const std::string containerPathStr = utils::createFilePath(mConfig.containersPath, "/", id, "/"); - // check if container does not exist if (mContainers.find(id) != mContainers.end()) { LOGE("Cannot create " << id << " container - already exists!"); - result->setError(api::host::ERROR_CONTAINER_CREATE_FAILED, - "Cannot create " + id + " container - already exists!"); + result->setError(api::ERROR_INVALID_ID, "Already exists"); return; } + const std::string containerPathStr = utils::createFilePath(mConfig.containersPath, "/", id, "/"); + // copy container image if config contains path to image LOGT("Image path: " << mConfig.containerImagePath); if (!mConfig.containerImagePath.empty()) { @@ -633,8 +647,7 @@ void ContainersManager::handleAddContainerCall(const std::string& id, if (!utils::launchAsRoot(copyImageContentsWrapper)) { LOGE("Failed to copy container image."); - result->setError(api::host::ERROR_CONTAINER_CREATE_FAILED, - "Failed to copy container image."); + result->setError(api::ERROR_INTERNAL, "Failed to copy container image."); return; } } @@ -647,7 +660,7 @@ void ContainersManager::handleAddContainerCall(const std::string& id, std::string configPath = utils::createFilePath(templateDir, "/", CONTAINER_TEMPLATE_CONFIG_PATH); std::string newConfigPath = utils::createFilePath(configDir, "/containers/", id + ".conf"); - auto removeAllWrapper = [](const std::string& path) { + auto removeAllWrapper = [](const std::string& path) -> bool { try { LOGD("Removing copied data"); fs::remove_all(fs::path(path)); @@ -662,19 +675,19 @@ void ContainersManager::handleAddContainerCall(const std::string& id, generateNewConfig(id, configPath, newConfigPath); } catch (SecurityContainersException& e) { - LOGE(e.what()); + LOGE("Generate config failed: " << e.what()); utils::launchAsRoot(std::bind(removeAllWrapper, containerPathStr)); - result->setError(api::host::ERROR_CONTAINER_CREATE_FAILED, e.what()); + result->setError(api::ERROR_INTERNAL, "Failed to generate config"); return; } - LOGT("Adding new container"); + LOGT("Creating new container"); try { - addContainer(newConfigPath); + createContainer(newConfigPath); } catch (SecurityContainersException& e) { - LOGE(e.what()); + LOGE("Creating new container failed: " << e.what()); utils::launchAsRoot(std::bind(removeAllWrapper, containerPathStr)); - result->setError(api::host::ERROR_CONTAINER_CREATE_FAILED, e.what()); + result->setError(api::ERROR_INTERNAL, "Failed to create container"); return; } @@ -685,11 +698,36 @@ void ContainersManager::handleAddContainerCall(const std::string& id, } else { LOGE("Failed to start container."); // TODO removeContainer - result->setError(api::host::ERROR_CONTAINER_CREATE_FAILED, - "Failed to start container."); + result->setError(api::ERROR_INTERNAL, "Failed to start container"); } }; mContainers[id]->startAsync(resultCallback); } +void ContainersManager::handleDestroyContainerCall(const std::string& id, + dbus::MethodResultBuilder::Pointer result) +{ + if (mContainers.find(id) == mContainers.end()) { + LOGE("Failed to destroy container - no such container id: " << id); + result->setError(api::ERROR_INVALID_ID, "No such container id"); + return; + } + + LOGI("Destroying container " << id); + + auto destroyer = [id, result, this] { + try { + destroyContainer(id); + } catch (const SecurityContainersException& e) { + LOGE("Error during container destruction: " << e.what()); + result->setError(api::ERROR_INTERNAL, "Failed to destroy container"); + return; + } + result->setVoid(); + }; + + std::thread thread(destroyer); + thread.detach(); //TODO fix it +} + } // namespace security_containers diff --git a/server/containers-manager.hpp b/server/containers-manager.hpp index 16a8cf5..70637bc 100644 --- a/server/containers-manager.hpp +++ b/server/containers-manager.hpp @@ -47,11 +47,18 @@ public: ~ContainersManager(); /** - * Add new container. + * Create new container. * * @param containerConfig config of new container */ - void addContainer(const std::string& containerConfig); + void createContainer(const std::string& containerConfig); + + /** + * Destroy container. + * + * @param containerId id of the container + */ + void destroyContainer(const std::string& containerId); /** * Focus this container, put it to the foreground. @@ -126,8 +133,10 @@ private: void handleGetContainerInfoCall(const std::string& id, dbus::MethodResultBuilder::Pointer result); void handleSetActiveContainerCall(const std::string& id, dbus::MethodResultBuilder::Pointer result); - void handleAddContainerCall(const std::string& id, - dbus::MethodResultBuilder::Pointer result); + void handleCreateContainerCall(const std::string& id, + dbus::MethodResultBuilder::Pointer result); + void handleDestroyContainerCall(const std::string& id, + dbus::MethodResultBuilder::Pointer result); }; diff --git a/server/host-connection.cpp b/server/host-connection.cpp index 1b38a9c..b7d72b7 100644 --- a/server/host-connection.cpp +++ b/server/host-connection.cpp @@ -135,9 +135,14 @@ void HostConnection::setSetActiveContainerCallback(const SetActiveContainerCallb mSetActiveContainerCallback = callback; } -void HostConnection::setAddContainerCallback(const AddContainerCallback& callback) +void HostConnection::setCreateContainerCallback(const CreateContainerCallback& callback) { - mAddContainerCallback = callback; + mCreateContainerCallback = callback; +} + +void HostConnection::setDestroyContainerCallback(const DestroyContainerCallback& callback) +{ + mDestroyContainerCallback = callback; } void HostConnection::onMessageCall(const std::string& objectPath, @@ -220,12 +225,21 @@ void HostConnection::onMessageCall(const std::string& objectPath, return; } - if (methodName == api::host::METHOD_ADD_CONTAINER) { + if (methodName == api::host::METHOD_CREATE_CONTAINER) { + const gchar* id = NULL; + g_variant_get(parameters, "(&s)", &id); + + if (mCreateContainerCallback){ + mCreateContainerCallback(id, result); + } + } + + if (methodName == api::host::METHOD_DESTROY_CONTAINER) { const gchar* id = NULL; g_variant_get(parameters, "(&s)", &id); - if (mAddContainerCallback){ - mAddContainerCallback(id, result); + if (mDestroyContainerCallback){ + mDestroyContainerCallback(id, result); } } } diff --git a/server/host-connection.hpp b/server/host-connection.hpp index 1dacd11..8392578 100644 --- a/server/host-connection.hpp +++ b/server/host-connection.hpp @@ -65,7 +65,10 @@ public: )> SetActiveContainerCallback; typedef std::function AddContainerCallback; + )> CreateContainerCallback; + typedef std::function DestroyContainerCallback; /** * Register proxy call callback @@ -105,7 +108,12 @@ public: /** * Register a callback called to create new container */ - void setAddContainerCallback(const AddContainerCallback& callback); + void setCreateContainerCallback(const CreateContainerCallback& callback); + + /** + * Register a callback called to destroy container + */ + void setDestroyContainerCallback(const DestroyContainerCallback& callback); /** * Make a proxy call @@ -129,7 +137,8 @@ private: GetActiveContainerIdCallback mGetActiveContainerIdCallback; GetContainerInfoCallback mGetContainerInfoCallback; SetActiveContainerCallback mSetActiveContainerCallback; - AddContainerCallback mAddContainerCallback; + CreateContainerCallback mCreateContainerCallback; + DestroyContainerCallback mDestroyContainerCallback; void onNameAcquired(); void onNameLost(); diff --git a/server/host-dbus-definitions.hpp b/server/host-dbus-definitions.hpp index a722aee..b7b49c4 100644 --- a/server/host-dbus-definitions.hpp +++ b/server/host-dbus-definitions.hpp @@ -37,14 +37,14 @@ const std::string OBJECT_PATH = "/org/tizen/containers/host"; const std::string INTERFACE = "org.tizen.containers.host.manager"; const std::string ERROR_CONTAINER_STOPPED = "org.tizen.containers.host.Error.ContainersStopped"; -const std::string ERROR_CONTAINER_CREATE_FAILED = "org.tizen.containers.host.Error.ContainerCreateFailed"; const std::string METHOD_GET_CONTAINER_DBUSES = "GetContainerDbuses"; const std::string METHOD_GET_CONTAINER_ID_LIST = "GetContainerIds"; const std::string METHOD_GET_ACTIVE_CONTAINER_ID = "GetActiveContainerId"; const std::string METHOD_GET_CONTAINER_INFO = "GetContainerInfo"; const std::string METHOD_SET_ACTIVE_CONTAINER = "SetActiveContainer"; -const std::string METHOD_ADD_CONTAINER = "AddContainer"; +const std::string METHOD_CREATE_CONTAINER = "CreateContainer"; +const std::string METHOD_DESTROY_CONTAINER = "DestroyContainer"; const std::string SIGNAL_CONTAINER_DBUS_STATE = "ContainerDbusState"; @@ -77,7 +77,10 @@ const std::string DEFINITION = " " " " " " - " " + " " + " " + " " + " " " " " " " " diff --git a/tests/unit_tests/client/ut-client.cpp b/tests/unit_tests/client/ut-client.cpp index d6b3fbb..934a062 100644 --- a/tests/unit_tests/client/ut-client.cpp +++ b/tests/unit_tests/client/ut-client.cpp @@ -213,7 +213,7 @@ BOOST_AUTO_TEST_CASE(SetActiveContainerTest) vsm_client_free(client); } -BOOST_AUTO_TEST_CASE(AddContainerTest) +BOOST_AUTO_TEST_CASE(CreateContainerTest) { const std::string newActiveContainerId = ""; diff --git a/tests/unit_tests/server/configs/CMakeLists.txt b/tests/unit_tests/server/configs/CMakeLists.txt index 17e10a2..fc73fd4 100644 --- a/tests/unit_tests/server/configs/CMakeLists.txt +++ b/tests/unit_tests/server/configs/CMakeLists.txt @@ -24,7 +24,6 @@ FILE(GLOB server_container_CONF ut-server/containers/*.conf) FILE(GLOB manager_manager_CONF ut-containers-manager/*.conf) FILE(GLOB manager_container_CONF ut-containers-manager/containers/*.conf) -FILE(GLOB manager_admin_TEMPLATE ut-containers-manager/templates/*.conf) FILE(GLOB container_CONF ut-container/*.conf) FILE(GLOB container_container_CONF ut-container/containers/*.conf) @@ -55,6 +54,8 @@ CONFIGURE_FILE(ut-containers-manager/buggy-foreground-daemon.conf.in ${CMAKE_BINARY_DIR}/ut-containers-manager/buggy-foreground-daemon.conf @ONLY) CONFIGURE_FILE(ut-containers-manager/test-dbus-daemon.conf.in ${CMAKE_BINARY_DIR}/ut-containers-manager/test-dbus-daemon.conf @ONLY) +CONFIGURE_FILE(ut-containers-manager/empty-dbus-daemon.conf.in + ${CMAKE_BINARY_DIR}/ut-containers-manager/empty-dbus-daemon.conf @ONLY) FILE(GLOB manager_manager_CONF_GEN ${CMAKE_BINARY_DIR}/ut-containers-manager/*.conf) CONFIGURE_FILE(ut-containers-manager/containers/console1-dbus.conf.in @@ -65,6 +66,10 @@ CONFIGURE_FILE(ut-containers-manager/containers/console3-dbus.conf.in ${CMAKE_BINARY_DIR}/ut-containers-manager/containers/console3-dbus.conf @ONLY) FILE(GLOB manager_container_CONF_GEN ${CMAKE_BINARY_DIR}/ut-containers-manager/containers/*.conf) +CONFIGURE_FILE(ut-containers-manager/templates/template.conf.in + ${CMAKE_BINARY_DIR}/ut-containers-manager/templates/template.conf @ONLY) +FILE(GLOB manager_container_TEMPLATE_GEN ${CMAKE_BINARY_DIR}/ut-containers-manager/templates/*.conf) + ## Install ##################################################################### INSTALL(FILES ${server_manager_CONF} @@ -82,7 +87,7 @@ INSTALL(FILES ${manager_container_CONF} DESTINATION ${SC_TEST_CONFIG_INSTALL_DIR}/server/ut-containers-manager/containers) INSTALL(FILES ${manager_container_CONF_GEN} DESTINATION ${SC_TEST_CONFIG_INSTALL_DIR}/server/ut-containers-manager/containers) -INSTALL(FILES ${manager_admin_TEMPLATE} +INSTALL(FILES ${manager_container_TEMPLATE_GEN} DESTINATION ${SC_TEST_CONFIG_INSTALL_DIR}/server/ut-containers-manager/templates) INSTALL(FILES ${container_CONF} diff --git a/tests/unit_tests/server/configs/ut-containers-manager/empty-dbus-daemon.conf.in b/tests/unit_tests/server/configs/ut-containers-manager/empty-dbus-daemon.conf.in new file mode 100644 index 0000000..82f6c0e --- /dev/null +++ b/tests/unit_tests/server/configs/ut-containers-manager/empty-dbus-daemon.conf.in @@ -0,0 +1,22 @@ +{ + "containerConfigs" : [], + "foregroundId" : "", + "defaultId" : "", + "containersPath" : "/tmp/ut-containers", + "containerImagePath" : "", + "containerTemplatePath" : "templates", + "containerNewConfigPrefix" : "@SC_TEST_CONFIG_INSTALL_DIR@/server/ut-containers-manager/", + "runMountPointPrefix" : "", + "lxcTemplatePrefix" : "@SC_TEST_LXC_TEMPLATES_INSTALL_DIR@", + "inputConfig" : {"enabled" : false, + "device" : "/dev/doesnotexist", + "code" : 139, + "numberOfEvents" : 2, + "timeWindowMs" : 500}, + "proxyCallRules" : [{"caller" : "*", + "target" : "*", + "targetBusName" : "org.tizen.containers.tests", + "targetObjectPath" : "*", + "targetInterface" : "*", + "targetMethod" : "*"}] +} diff --git a/tests/unit_tests/server/configs/ut-containers-manager/templates/template.conf b/tests/unit_tests/server/configs/ut-containers-manager/templates/template.conf deleted file mode 100644 index 1229c12..0000000 --- a/tests/unit_tests/server/configs/ut-containers-manager/templates/template.conf +++ /dev/null @@ -1,11 +0,0 @@ -{ - "privilege" : 20, - "vt" : -1, - "switchToDefaultAfterTimeout" : true, - "cpuQuotaForeground" : -1, - "cpuQuotaBackground" : 1000, - "runMountPoint" : "/tmp/ut-containers-manager/~NAME~-dbus", - "enableDbusIntegration" : true, - "permittedToSend" : [ "/tmp/.*" ], - "permittedToRecv" : [ "/tmp/.*" ] -} diff --git a/tests/unit_tests/server/configs/ut-containers-manager/templates/template.conf.in b/tests/unit_tests/server/configs/ut-containers-manager/templates/template.conf.in new file mode 100644 index 0000000..f01cfff --- /dev/null +++ b/tests/unit_tests/server/configs/ut-containers-manager/templates/template.conf.in @@ -0,0 +1,16 @@ +{ + "name" : "~NAME~", + "lxcTemplate" : "minimal-dbus1.sh", + "initWithArgs" : ["/bin/sh", "-c", "trap exit SIGTERM; /usr/bin/dbus-daemon --config-file=@SC_TEST_CONFIG_INSTALL_DIR@/server/ut-containers-manager/ut-dbus.conf --fork; read"], + "ipv4Gateway" : "", + "ipv4" : "", + "privilege" : 20, + "vt" : -1, + "switchToDefaultAfterTimeout" : true, + "enableDbusIntegration" : true, + "cpuQuotaForeground" : -1, + "cpuQuotaBackground" : 1000, + "runMountPoint" : "/tmp/ut-run1", + "permittedToSend" : [ "/tmp/.*" ], + "permittedToRecv" : [ "/tmp/.*" ] +} diff --git a/tests/unit_tests/server/ut-containers-manager.cpp b/tests/unit_tests/server/ut-containers-manager.cpp index 399cd12..46f659f 100644 --- a/tests/unit_tests/server/ut-containers-manager.cpp +++ b/tests/unit_tests/server/ut-containers-manager.cpp @@ -62,6 +62,7 @@ namespace { const std::string TEST_CONFIG_PATH = SC_TEST_CONFIG_INSTALL_DIR "/server/ut-containers-manager/test-daemon.conf"; const std::string TEST_DBUS_CONFIG_PATH = SC_TEST_CONFIG_INSTALL_DIR "/server/ut-containers-manager/test-dbus-daemon.conf"; +const std::string EMPTY_DBUS_CONFIG_PATH = SC_TEST_CONFIG_INSTALL_DIR "/server/ut-containers-manager/empty-dbus-daemon.conf"; const std::string BUGGY_CONFIG_PATH = SC_TEST_CONFIG_INSTALL_DIR "/server/ut-containers-manager/buggy-daemon.conf"; const std::string BUGGY_FOREGROUND_CONFIG_PATH = SC_TEST_CONFIG_INSTALL_DIR "/server/ut-containers-manager/buggy-foreground-daemon.conf"; const std::string BUGGY_DEFAULTID_CONFIG_PATH = SC_TEST_CONFIG_INSTALL_DIR "/server/ut-containers-manager/buggy-default-daemon.conf"; @@ -85,7 +86,7 @@ public: typedef std::function TestApiMethodCallback; - typedef std::function AddContainerResultCallback; + typedef std::function VoidResultCallback; typedef std::map Dbuses; @@ -308,8 +309,8 @@ public: } - void callAsyncMethodAddContainer(const std::string& id, - const AddContainerResultCallback& result) + void callAsyncMethodCreateContainer(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)); @@ -321,7 +322,26 @@ public: mClient->callMethodAsync(api::host::BUS_NAME, api::host::OBJECT_PATH, api::host::INTERFACE, - api::host::METHOD_ADD_CONTAINER, + api::host::METHOD_CREATE_CONTAINER, + parameters, + "()", + asyncResult); + } + + void callAsyncMethodDestroyContainer(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_DESTROY_CONTAINER, parameters, "()", asyncResult); @@ -938,32 +958,33 @@ BOOST_AUTO_TEST_CASE(SetActiveContainerTest) DbusException); } -//TODO fix it -//BOOST_AUTO_TEST_CASE(AddContainerTest) -//{ -// const std::string newContainerId = "test1234"; -// const std::vector newContainerConfigs = { -// TEST_CONTAINER_CONF_PATH + newContainerId + ".conf", -// }; -// FileCleanerRAII cleaner(newContainerConfigs); -// -// ContainersManager cm(TEST_DBUS_CONFIG_PATH); -// cm.startAll(); -// -// Latch callDone; -// auto resultCallback = [&]() { -// callDone.set(); -// }; -// -// DbusAccessory dbus(DbusAccessory::HOST_ID); -// -// // create new container -// dbus.callAsyncMethodAddContainer(newContainerId, resultCallback); -// callDone.wait(EVENT_TIMEOUT); -// -// // focus new container -// cm.focus(newContainerId); -// BOOST_CHECK(cm.getRunningForegroundContainerId() == newContainerId); -//} +BOOST_AUTO_TEST_CASE(CreateDestroyContainerTest) +{ + const std::string newContainerId = "test1234"; + + ContainersManager cm(EMPTY_DBUS_CONFIG_PATH); + cm.startAll(); + + Latch callDone; + auto resultCallback = [&]() { + callDone.set(); + }; + + DbusAccessory dbus(DbusAccessory::HOST_ID); + + // create new container + dbus.callAsyncMethodCreateContainer(newContainerId, resultCallback); + BOOST_REQUIRE(callDone.wait(EVENT_TIMEOUT)); + + // focus new container + cm.focus(newContainerId); + BOOST_CHECK(cm.getRunningForegroundContainerId() == newContainerId); + + // destroy container + dbus.callAsyncMethodDestroyContainer(newContainerId, resultCallback); + BOOST_REQUIRE(callDone.wait(EVENT_TIMEOUT)); + + BOOST_CHECK(cm.getRunningForegroundContainerId() == ""); +} BOOST_AUTO_TEST_SUITE_END() -- 2.7.4