From 47782bbb0c4b7bf91aa773718d0c9f681fba6eaf Mon Sep 17 00:00:00 2001 From: Jan Olszak Date: Wed, 19 Nov 2014 15:26:28 +0100 Subject: [PATCH 01/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 02/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 03/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 04/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 05/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 06/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 07/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 08/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 09/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 10/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 11/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 12/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 From 7f7ca51f217acca99c35d06c6144ca6ace55e9c2 Mon Sep 17 00:00:00 2001 From: Jan Olszak Date: Thu, 27 Nov 2014 14:02:11 +0100 Subject: [PATCH 13/16] IPC: Signals [Bug/Feature] Signals in the IPC mechanism [Cause] N/A [Solution] N/A [Verification] Build, install, run tests Change-Id: Ie84b88ffa2956e744216d9ff7e9c227631f78e50 --- common/ipc/client.cpp | 15 ++ common/ipc/client.hpp | 67 +++++++- common/ipc/internals/processor.cpp | 185 +++++++++++++++++---- common/ipc/internals/processor.hpp | 318 ++++++++++++++++++++++++++++++------- common/ipc/service.cpp | 15 ++ common/ipc/service.hpp | 65 +++++++- common/ipc/types.hpp | 11 +- tests/unit_tests/ipc/ut-ipc.cpp | 197 ++++++++++++++--------- 8 files changed, 707 insertions(+), 166 deletions(-) diff --git a/common/ipc/client.cpp b/common/ipc/client.cpp index c1651f3..c806e7b 100644 --- a/common/ipc/client.cpp +++ b/common/ipc/client.cpp @@ -63,6 +63,11 @@ void Client::start() LOGD("Started client"); } +bool Client::isStarted() +{ + return mProcessor.isStarted(); +} + void Client::stop() { LOGD("Stopping client..."); @@ -70,6 +75,16 @@ void Client::stop() LOGD("Stopped"); } +void Client::setNewPeerCallback(const PeerCallback& newPeerCallback) +{ + mProcessor.setNewPeerCallback(newPeerCallback); +} + +void Client::setRemovedPeerCallback(const PeerCallback& removedPeerCallback) +{ + mProcessor.setRemovedPeerCallback(removedPeerCallback); +} + void Client::removeMethod(const MethodID methodID) { LOGD("Removing method id: " << methodID); diff --git a/common/ipc/client.hpp b/common/ipc/client.hpp index 7429b24..3178474 100644 --- a/common/ipc/client.hpp +++ b/common/ipc/client.hpp @@ -45,8 +45,6 @@ namespace ipc { */ class Client { public: - typedef Processor::MethodID MethodID; - /** * @param serverPath path to the server's socket */ @@ -62,11 +60,30 @@ public: void start(); /** + * @return is the communication thread running + */ + bool isStarted(); + + /** * Stops all worker thread */ void stop(); /** + * Set the callback called for each new connection to a peer + * + * @param newPeerCallback the callback + */ + void setNewPeerCallback(const PeerCallback& newPeerCallback); + + /** + * Set the callback called when connection to a peer is lost + * + * @param removedPeerCallback the callback + */ + void setRemovedPeerCallback(const PeerCallback& removedPeerCallback); + + /** * Saves the callback connected to the method id. * When a message with the given method id is received * the data will be parsed and passed to this callback. @@ -79,6 +96,19 @@ public: const typename MethodHandler::type& method); /** + * Saves the callback connected to the method id. + * When a message with the given method id is received + * the data will be parsed and passed to this callback. + * + * @param methodID API dependent id of the method + * @param SignalHandler signal handling implementation + * @tparam ReceivedDataType data type to serialize + */ + template + void addSignalHandler(const MethodID methodID, + const typename SignalHandler::type& signal); + + /** * Removes the callback * * @param methodID API dependent id of the method @@ -112,8 +142,21 @@ public: const std::shared_ptr& data, const typename ResultHandler::type& resultCallback); + /** + * Send a signal to the peer. + * There is no return value from the peer + * Sends any data only if a peer registered this a signal + * + * @param methodID API dependent id of the method + * @param data data to sent + * @tparam SentDataType data type to send + */ + template + void signal(const MethodID methodID, + const std::shared_ptr& data); + private: - Processor::PeerID mServiceID; + PeerID mServiceID; Processor mProcessor; std::string mSocketPath; }; @@ -127,6 +170,15 @@ void Client::addMethodHandler(const MethodID methodID, LOGD("Added method with id " << methodID); } +template +void Client::addSignalHandler(const MethodID methodID, + const typename SignalHandler::type& handler) +{ + LOGD("Adding signal with id " << methodID); + mProcessor.addSignalHandler(methodID, handler); + LOGD("Added signal with id " << methodID); +} + template std::shared_ptr Client::callSync(const MethodID methodID, const std::shared_ptr& data, @@ -150,6 +202,15 @@ void Client::callAsync(const MethodID methodID, LOGD("Async called method: " << methodID); } +template +void Client::signal(const MethodID methodID, + const std::shared_ptr& data) +{ + LOGD("Signaling: " << methodID); + mProcessor.signal(methodID, data); + LOGD("Signaled: " << methodID); +} + } // namespace ipc } // namespace security_containers diff --git a/common/ipc/internals/processor.cpp b/common/ipc/internals/processor.cpp index 9aadfff..dc7df42 100644 --- a/common/ipc/internals/processor.cpp +++ b/common/ipc/internals/processor.cpp @@ -28,7 +28,6 @@ #include "ipc/internals/processor.hpp" #include "ipc/internals/utils.hpp" -#include #include #include #include @@ -48,7 +47,8 @@ namespace ipc { LOGE("Callback threw an error: " << e.what()); \ } -const Processor::MethodID Processor::RETURN_METHOD_ID = std::numeric_limits::max(); +const MethodID Processor::RETURN_METHOD_ID = std::numeric_limits::max(); +const MethodID Processor::REGISTER_SIGNAL_METHOD_ID = std::numeric_limits::max() - 1; Processor::Processor(const PeerCallback& newPeerCallback, const PeerCallback& removedPeerCallback, @@ -60,6 +60,11 @@ Processor::Processor(const PeerCallback& newPeerCallback, mPeerIDCounter(0) { LOGT("Creating Processor"); + using namespace std::placeholders; + + addMethodHandlerInternal(REGISTER_SIGNAL_METHOD_ID, + std::bind(&Processor::onNewSignals, this, _1, _2)); + } Processor::~Processor() @@ -73,10 +78,15 @@ Processor::~Processor() LOGT("Destroyed Processor"); } +bool Processor::isStarted() +{ + return mThread.joinable(); +} + void Processor::start() { LOGT("Starting Processor"); - if (!mThread.joinable()) { + if (!isStarted()) { mThread = std::thread(&Processor::run, this); } LOGT("Started Processor"); @@ -85,13 +95,25 @@ void Processor::start() void Processor::stop() { LOGT("Stopping Processor"); - if (mThread.joinable()) { + if (isStarted()) { mEventQueue.send(Event::FINISH); mThread.join(); } LOGT("Stopped Processor"); } +void Processor::setNewPeerCallback(const PeerCallback& newPeerCallback) +{ + Lock lock(mCallbacksMutex); + mNewPeerCallback = newPeerCallback; +} + +void Processor::setRemovedPeerCallback(const PeerCallback& removedPeerCallback) +{ + Lock lock(mCallbacksMutex); + mRemovedPeerCallback = removedPeerCallback; +} + void Processor::removeMethod(const MethodID methodID) { LOGT("Removing method " << methodID); @@ -99,7 +121,7 @@ void Processor::removeMethod(const MethodID methodID) mMethodsCallbacks.erase(methodID); } -Processor::PeerID Processor::addPeer(const std::shared_ptr& socketPtr) +PeerID Processor::addPeer(const std::shared_ptr& socketPtr) { LOGT("Adding socket"); PeerID peerID; @@ -127,7 +149,7 @@ void Processor::removePeer(const PeerID peerID) mEventQueue.send(Event::REMOVE_PEER); - auto isPeerDeleted = [&peerID, this] { + auto isPeerDeleted = [&peerID, this]()->bool { Lock lock(mSocketsMutex); return mSockets.count(peerID) == 0; }; @@ -143,6 +165,16 @@ void Processor::removePeerInternal(const PeerID peerID, Status status) { Lock lock(mSocketsMutex); mSockets.erase(peerID); + + // Remove from signal addressees + for (auto it = mSignalsPeers.begin(); it != mSignalsPeers.end();) { + it->second.remove(peerID); + if (it->second.empty()) { + it = mSignalsPeers.erase(it); + } else { + ++it; + } + } } { @@ -160,9 +192,13 @@ void Processor::removePeerInternal(const PeerID peerID, Status status) } } - if (mRemovedPeerCallback) { - // Notify about the deletion - mRemovedPeerCallback(peerID); + + { + Lock lock(mCallbacksMutex); + if (mRemovedPeerCallback) { + // Notify about the deletion + mRemovedPeerCallback(peerID); + } } resetPolling(); @@ -284,14 +320,46 @@ bool Processor::handleInput(const PeerID peerID, const Socket& socket) if (methodID == RETURN_METHOD_ID) { return onReturnValue(peerID, socket, messageID); + } else { - return onRemoteCall(peerID, socket, methodID, messageID); + Lock lock(mCallsMutex); + if (mMethodsCallbacks.count(methodID)) { + // Method + std::shared_ptr methodCallbacks = mMethodsCallbacks.at(methodID); + mCallsMutex.unlock(); + return onRemoteCall(peerID, socket, methodID, messageID, methodCallbacks); + + } else if (mSignalsCallbacks.count(methodID)) { + // Signal + std::shared_ptr signalCallbacks = mSignalsCallbacks.at(methodID); + mCallsMutex.unlock(); + return onRemoteSignal(peerID, socket, methodID, messageID, signalCallbacks); + + } else { + // Nothing + mCallsMutex.unlock(); + LOGW("No method or signal callback for methodID: " << methodID); + removePeerInternal(peerID, Status::NAUGHTY_PEER); + return true; + } } } return false; } +std::shared_ptr Processor::onNewSignals(const PeerID peerID, + std::shared_ptr& data) +{ + LOGD("New signals for peer: " << peerID); + Lock lock(mSocketsMutex); + for (MethodID methodID : data->ids) { + mSignalsPeers[methodID].push_back(peerID); + } + + return std::make_shared(); +} + bool Processor::onReturnValue(const PeerID peerID, const Socket& socket, const MessageID messageID) @@ -326,23 +394,44 @@ bool Processor::onReturnValue(const PeerID peerID, return false; } -bool Processor::onRemoteCall(const PeerID peerID, - const Socket& socket, - const MethodID methodID, - const MessageID messageID) +bool Processor::onRemoteSignal(const PeerID peerID, + const Socket& socket, + const MethodID methodID, + const MessageID messageID, + std::shared_ptr signalCallbacks) { - LOGI("Remote call; methodID: " << methodID << " messageID: " << messageID); + LOGI("Remote signal; methodID: " << methodID << " messageID: " << messageID); - std::shared_ptr methodCallbacks; + std::shared_ptr data; try { - Lock lock(mCallsMutex); - methodCallbacks = mMethodsCallbacks.at(methodID); - } catch (const std::out_of_range&) { - LOGW("No method callback for methodID: " << methodID); + LOGT("Parsing incoming data"); + data = signalCallbacks->parse(socket.getFD()); + } catch (const std::exception& e) { + LOGE("Exception during parsing: " << e.what()); + removePeerInternal(peerID, Status::PARSING_ERROR); + return true; + } + + LOGT("Signal callback for methodID: " << methodID << "; messageID: " << messageID); + try { + signalCallbacks->signal(peerID, data); + } catch (const std::exception& e) { + LOGE("Exception in method handler: " << e.what()); removePeerInternal(peerID, Status::NAUGHTY_PEER); return true; } + return false; +} + +bool Processor::onRemoteCall(const PeerID peerID, + const Socket& socket, + const MethodID methodID, + const MessageID messageID, + std::shared_ptr methodCallbacks) +{ + LOGI("Remote call; methodID: " << methodID << " messageID: " << messageID); + std::shared_ptr data; try { LOGT("Parsing incoming data"); @@ -356,7 +445,7 @@ bool Processor::onRemoteCall(const PeerID peerID, LOGT("Process callback for methodID: " << methodID << "; messageID: " << messageID); std::shared_ptr returnData; try { - returnData = methodCallbacks->method(data); + returnData = methodCallbacks->method(peerID, data); } catch (const std::exception& e) { LOGE("Exception in method handler: " << e.what()); removePeerInternal(peerID, Status::NAUGHTY_PEER); @@ -434,10 +523,44 @@ bool Processor::onNewPeer() mSockets[socketInfo.peerID] = std::move(socketInfo.socketPtr); } + + + // Broadcast the new signal to peers + LOGW("Sending handled signals"); + std::list peersIDs; + { + Lock lock(mSocketsMutex); + for (const auto kv : mSockets) { + peersIDs.push_back(kv.first); + } + } + + std::vector ids; + { + Lock lock(mSocketsMutex); + for (const auto kv : mSignalsCallbacks) { + ids.push_back(kv.first); + } + } + auto data = std::make_shared(ids); + + for (const PeerID peerID : peersIDs) { + callInternal(REGISTER_SIGNAL_METHOD_ID, + peerID, + data, + discardResultHandler); + } + LOGW("Sent handled signals"); + + resetPolling(); - if (mNewPeerCallback) { - // Notify about the new user. - mNewPeerCallback(socketInfo.peerID); + + { + Lock lock(mCallbacksMutex); + if (mNewPeerCallback) { + // Notify about the new user. + mNewPeerCallback(socketInfo.peerID); + } } return true; } @@ -456,13 +579,13 @@ bool Processor::onRemovePeer() return true; } -Processor::MessageID Processor::getNextMessageID() +MessageID Processor::getNextMessageID() { // TODO: This method of generating UIDs is buggy. To be changed. return ++mMessageIDCounter; } -Processor::PeerID Processor::getNextPeerID() +PeerID Processor::getNextPeerID() { // TODO: This method of generating UIDs is buggy. To be changed. return ++mPeerIDCounter; @@ -496,14 +619,12 @@ bool Processor::onCall() return false; } - { - // Set what to do with the return message + if (call.parse && call.process) { + // Set what to do with the return message, but only if needed Lock lock(mReturnCallbacksMutex); if (mReturnCallbacks.count(call.messageID) != 0) { LOGE("There already was a return callback for messageID: " << call.messageID); } - - // move insertion mReturnCallbacks[call.messageID] = std::move(ReturnCallbacks(call.peerID, std::move(call.parse), std::move(call.process))); @@ -544,7 +665,9 @@ void Processor::cleanCommunication() case Event::CALL: { LOGD("Event CALL after FINISH"); Call call = getCall(); - IGNORE_EXCEPTIONS(call.process(Status::CLOSING, call.data)); + if (call.process) { + IGNORE_EXCEPTIONS(call.process(Status::CLOSING, call.data)); + } break; } diff --git a/common/ipc/internals/processor.hpp b/common/ipc/internals/processor.hpp index 7554eb8..e43fe62 100644 --- a/common/ipc/internals/processor.hpp +++ b/common/ipc/internals/processor.hpp @@ -31,6 +31,7 @@ #include "ipc/types.hpp" #include "config/manager.hpp" #include "config/is-visitable.hpp" +#include "config/fields.hpp" #include "logger/logger.hpp" #include @@ -43,14 +44,16 @@ #include #include #include +#include #include #include namespace security_containers { namespace ipc { -namespace { + const unsigned int DEFAULT_MAX_NUMBER_OF_PEERS = 500; -} +const unsigned int DEFAULT_METHOD_TIMEOUT = 1000; + /** * This class wraps communication via UX sockets * @@ -66,18 +69,25 @@ const unsigned int DEFAULT_MAX_NUMBER_OF_PEERS = 500; * * TODO: * - some mutexes may not be needed +* - synchronous call to many peers +* - implement CallQueue class +* - implement HandlerStore class for storing both signals and methods +* - API for removing signals +* - implement CallbackStore - thread safe calling/setting callbacks +* - helper function for removing from unordered map */ 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. + * Used to indicate a message with the return value. */ static const MethodID RETURN_METHOD_ID; + + /** + * Indicates an Processor's internal request/broadcast to register a Signal + */ + static const MethodID REGISTER_SIGNAL_METHOD_ID; + /** * Constructs the Processor, but doesn't start it. * The object is ready to add methods. @@ -101,12 +111,31 @@ public: void start(); /** + * @return is processor running + */ + bool isStarted(); + + /** * Stops the processing thread. * No incoming data will be handled after. */ void stop(); /** + * Set the callback called for each new connection to a peer + * + * @param newPeerCallback the callback + */ + void setNewPeerCallback(const PeerCallback& newPeerCallback); + + /** + * Set the callback called when connection to a peer is lost + * + * @param removedPeerCallback the callback + */ + void setRemovedPeerCallback(const PeerCallback& removedPeerCallback); + + /** * From now on socket is owned by the Processor object. * Calls the newPeerCallback. * @@ -139,6 +168,24 @@ public: const typename MethodHandler::type& process); /** + * 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. + * + * Then the process callback will be called with the parsed data. + * There is no return data to send back. + * + * Adding signal sends a registering message to all peers + * + * @param methodID API dependent id of the method + * @param process data processing callback + * @tparam ReceivedDataType data type to receive + */ + template + void addSignalHandler(const MethodID methodID, + const typename SignalHandler::type& process); + + /** * Removes the callback * * @param methodID API dependent id of the method @@ -178,11 +225,42 @@ public: const typename ResultHandler::type& process); + /** + * Send a signal to the peer. + * There is no return value from the peer + * Sends any data only if a peer registered this a signal + * + * @param methodID API dependent id of the method + * @param data data to sent + * @tparam SentDataType data type to send + */ + template + void signal(const MethodID methodID, + const std::shared_ptr& data); + + private: typedef std::function& data)> SerializeCallback; typedef std::function(int fd)> ParseCallback; typedef std::lock_guard Lock; + struct EmptyData { + CONFIG_REGISTER_EMPTY + }; + + struct RegisterSignalsMessage { + RegisterSignalsMessage() = default; + RegisterSignalsMessage(const std::vector ids) + : ids(ids) {} + + std::vector ids; + + CONFIG_REGISTER + ( + ids + ) + }; + struct Call { Call(const Call& other) = delete; Call& operator=(const Call&) = delete; @@ -210,6 +288,17 @@ private: MethodHandler::type method; }; + struct SignalHandlers { + SignalHandlers(const SignalHandlers& other) = delete; + SignalHandlers& operator=(const SignalHandlers&) = delete; + SignalHandlers() = default; + SignalHandlers(SignalHandlers&&) = default; + SignalHandlers& operator=(SignalHandlers &&) = default; + + ParseCallback parse; + SignalHandler::type signal; + }; + struct ReturnCallbacks { ReturnCallbacks(const ReturnCallbacks& other) = delete; ReturnCallbacks& operator=(const ReturnCallbacks&) = delete; @@ -269,6 +358,8 @@ private: std::mutex mCallsMutex; std::queue mCalls; std::unordered_map> mMethodsCallbacks; + std::unordered_map> mSignalsCallbacks; + std::unordered_map> mSignalsPeers; // Mutex for changing mSockets map. // Shouldn't be locked on any read/write, that could block. Just copy the ptr. @@ -281,7 +372,8 @@ private: std::mutex mReturnCallbacksMutex; std::unordered_map mReturnCallbacks; - + // Mutex for setting callbacks + std::mutex mCallbacksMutex; PeerCallback mNewPeerCallback; PeerCallback mRemovedPeerCallback; @@ -293,6 +385,24 @@ private: std::atomic mMessageIDCounter; std::atomic mPeerIDCounter; + template + void addMethodHandlerInternal(const MethodID methodID, + const typename MethodHandler::type& process); + + template + MessageID callInternal(const MethodID methodID, + const PeerID peerID, + const std::shared_ptr& data); + + template + MessageID callInternal(const MethodID methodID, + const PeerID peerID, + const std::shared_ptr& data, + const typename ResultHandler::type& process); + + template + static void discardResultHandler(Status, std::shared_ptr&) {} + void run(); bool handleEvent(); bool onCall(); @@ -307,31 +417,30 @@ private: bool onRemoteCall(const PeerID peerID, const Socket& socket, const MethodID methodID, - const MessageID messageID); + const MessageID messageID, + std::shared_ptr methodCallbacks); + bool onRemoteSignal(const PeerID peerID, + const Socket& socket, + const MethodID methodID, + const MessageID messageID, + std::shared_ptr signalCallbacks); void resetPolling(); MessageID getNextMessageID(); PeerID getNextPeerID(); Call getCall(); void removePeerInternal(const PeerID peerID, Status status); + + std::shared_ptr onNewSignals(const PeerID peerID, + std::shared_ptr& data); + + void cleanCommunication(); }; template -void Processor::addMethodHandler(const MethodID methodID, - const typename MethodHandler::type& method) +void Processor::addMethodHandlerInternal(const MethodID methodID, + const typename MethodHandler::type& method) { - static_assert(config::isVisitable::value, - "Use the libConfig library"); - static_assert(config::isVisitable::value, - "Use the libConfig library"); - - if (methodID == RETURN_METHOD_ID) { - LOGE("Forbidden methodID: " << methodID); - throw IPCException("Forbidden methodID: " + std::to_string(methodID)); - } - - using namespace std::placeholders; - MethodHandlers methodCall; methodCall.parse = [](const int fd)->std::shared_ptr { @@ -344,9 +453,9 @@ void Processor::addMethodHandler(const MethodID methodID, config::saveToFD(fd, *std::static_pointer_cast(data)); }; - methodCall.method = [method](std::shared_ptr& data)->std::shared_ptr { + methodCall.method = [method](const PeerID peerID, std::shared_ptr& data)->std::shared_ptr { std::shared_ptr tmpData = std::static_pointer_cast(data); - return method(tmpData); + return method(peerID, tmpData); }; { @@ -356,39 +465,104 @@ void Processor::addMethodHandler(const MethodID methodID, } template -Processor::MessageID Processor::callAsync(const MethodID methodID, - const PeerID peerID, - const std::shared_ptr& data, - const typename ResultHandler::type& process) +void Processor::addMethodHandler(const MethodID methodID, + const typename MethodHandler::type& method) { - static_assert(config::isVisitable::value, - "Use the libConfig library"); - static_assert(config::isVisitable::value, - "Use the libConfig library"); + if (methodID == RETURN_METHOD_ID || methodID == REGISTER_SIGNAL_METHOD_ID) { + LOGE("Forbidden methodID: " << methodID); + throw IPCException("Forbidden methodID: " + std::to_string(methodID)); + } - if (!mThread.joinable()) { - LOGE("The Processor thread is not started. Can't send any data."); - throw IPCException("The Processor thread is not started. Can't send any data."); + { + Lock lock(mCallsMutex); + if (mSignalsCallbacks.count(methodID)) { + LOGE("MethodID used by a signal: " << methodID); + throw IPCException("MethodID used by a signal: " + std::to_string(methodID)); + } } - using namespace std::placeholders; + addMethodHandlerInternal(methodID, method); +} + +template +void Processor::addSignalHandler(const MethodID methodID, + const typename SignalHandler::type& handler) +{ + if (methodID == RETURN_METHOD_ID || methodID == REGISTER_SIGNAL_METHOD_ID) { + LOGE("Forbidden methodID: " << methodID); + throw IPCException("Forbidden methodID: " + std::to_string(methodID)); + } + { + Lock lock(mCallsMutex); + if (mMethodsCallbacks.count(methodID)) { + LOGE("MethodID used by a method: " << methodID); + throw IPCException("MethodID used by a method: " + std::to_string(methodID)); + } + } + + SignalHandlers signalCall; + + signalCall.parse = [](const int fd)->std::shared_ptr { + std::shared_ptr data(new ReceivedDataType()); + config::loadFromFD(fd, *data); + return data; + }; + + signalCall.signal = [handler](const PeerID peerID, std::shared_ptr& data) { + std::shared_ptr tmpData = std::static_pointer_cast(data); + handler(peerID, tmpData); + }; + + { + Lock lock(mCallsMutex); + mSignalsCallbacks[methodID] = std::make_shared(std::move(signalCall)); + } + + if (isStarted()) { + // Broadcast the new signal to peers + std::vector ids {methodID}; + auto data = std::make_shared(ids); + + std::list peersIDs; + { + Lock lock(mSocketsMutex); + for (const auto kv : mSockets) { + peersIDs.push_back(kv.first); + } + } + + for (const PeerID peerID : peersIDs) { + callSync(REGISTER_SIGNAL_METHOD_ID, + peerID, + data, + DEFAULT_METHOD_TIMEOUT); + } + } +} + +template +MessageID Processor::callInternal(const MethodID methodID, + const PeerID peerID, + const std::shared_ptr& data, + const typename ResultHandler::type& process) +{ Call call; call.peerID = peerID; call.methodID = methodID; call.data = data; call.messageID = getNextMessageID(); + call.serialize = [](const int fd, std::shared_ptr& data)->void { + config::saveToFD(fd, *std::static_pointer_cast(data)); + }; + call.parse = [](const int fd)->std::shared_ptr { std::shared_ptr data(new ReceivedDataType()); config::loadFromFD(fd, *data); return data; }; - call.serialize = [](const int fd, std::shared_ptr& data)->void { - config::saveToFD(fd, *std::static_pointer_cast(data)); - }; - call.process = [process](Status status, std::shared_ptr& data)->void { std::shared_ptr tmpData = std::static_pointer_cast(data); return process(status, tmpData); @@ -397,13 +571,26 @@ Processor::MessageID Processor::callAsync(const MethodID methodID, { Lock lock(mCallsMutex); mCalls.push(std::move(call)); + mEventQueue.send(Event::CALL); } - mEventQueue.send(Event::CALL); - return call.messageID; } +template +MessageID Processor::callAsync(const MethodID methodID, + const PeerID peerID, + const std::shared_ptr& data, + const typename ResultHandler::type& process) +{ + if (!isStarted()) { + LOGE("The Processor thread is not started. Can't send any data."); + throw IPCException("The Processor thread is not started. Can't send any data."); + } + + return callInternal(methodID, peerID, data, process); +} + template std::shared_ptr Processor::callSync(const MethodID methodID, @@ -411,16 +598,6 @@ std::shared_ptr Processor::callSync(const MethodID methodID, const std::shared_ptr& data, unsigned int timeoutMS) { - static_assert(config::isVisitable::value, - "Use the libConfig library"); - static_assert(config::isVisitable::value, - "Use the libConfig library"); - - if (!mThread.joinable()) { - LOGE("The Processor thread is not started. Can't send any data."); - throw IPCException("The Processor thread is not started. Can't send any data."); - } - std::shared_ptr result; std::mutex mutex; @@ -467,6 +644,39 @@ std::shared_ptr Processor::callSync(const MethodID methodID, return result; } +template +void Processor::signal(const MethodID methodID, + const std::shared_ptr& data) +{ + if (!isStarted()) { + LOGE("The Processor thread is not started. Can't send any data."); + throw IPCException("The Processor thread is not started. Can't send any data."); + } + + std::list peersIDs; + { + Lock lock(mSocketsMutex); + peersIDs = mSignalsPeers[methodID]; + } + + for (const PeerID peerID : peersIDs) { + Call call; + call.peerID = peerID; + call.methodID = methodID; + call.data = data; + call.messageID = getNextMessageID(); + + call.serialize = [](const int fd, std::shared_ptr& data)->void { + config::saveToFD(fd, *std::static_pointer_cast(data)); + }; + + { + Lock lock(mCallsMutex); + mCalls.push(std::move(call)); + mEventQueue.send(Event::CALL); + } + } +} } // namespace ipc } // namespace security_containers diff --git a/common/ipc/service.cpp b/common/ipc/service.cpp index d2590d6..e845fe6 100644 --- a/common/ipc/service.cpp +++ b/common/ipc/service.cpp @@ -66,6 +66,11 @@ void Service::start() LOGD("Started server"); } +bool Service::isStarted() +{ + return mProcessor.isStarted(); +} + void Service::stop() { LOGD("Stopping server.."); @@ -74,6 +79,16 @@ void Service::stop() LOGD("Stopped"); } +void Service::setNewPeerCallback(const PeerCallback& newPeerCallback) +{ + mProcessor.setNewPeerCallback(newPeerCallback); +} + +void Service::setRemovedPeerCallback(const PeerCallback& removedPeerCallback) +{ + mProcessor.setRemovedPeerCallback(removedPeerCallback); +} + void Service::removeMethod(const MethodID methodID) { LOGD("Removing method " << methodID); diff --git a/common/ipc/service.hpp b/common/ipc/service.hpp index 873673d..ac22eb2 100644 --- a/common/ipc/service.hpp +++ b/common/ipc/service.hpp @@ -48,10 +48,6 @@ namespace ipc { */ class Service { public: - typedef Processor::PeerCallback PeerCallback; - typedef Processor::PeerID PeerID; - typedef Processor::MethodID MethodID; - /** * @param path path to the socket */ @@ -69,11 +65,30 @@ public: void start(); /** + * @return is the communication thread running + */ + bool isStarted(); + + /** * Stops all working threads */ void stop(); /** + * Set the callback called for each new connection to a peer + * + * @param newPeerCallback the callback + */ + void setNewPeerCallback(const PeerCallback& newPeerCallback); + + /** + * Set the callback called when connection to a peer is lost + * + * @param removedPeerCallback the callback + */ + void setRemovedPeerCallback(const PeerCallback& removedPeerCallback); + + /** * Saves the callback connected to the method id. * When a message with the given method id is received * the data will be parsed and passed to this callback. @@ -86,6 +101,19 @@ public: const typename MethodHandler::type& method); /** + * Saves the callback connected to the method id. + * When a message with the given method id is received + * the data will be parsed and passed to this callback. + * + * @param methodID API dependent id of the method + * @param handler handling implementation + * @tparam ReceivedDataType data type to serialize + */ + template + void addSignalHandler(const MethodID methodID, + const typename SignalHandler::type& handler); + + /** * Removes the callback * * @param methodID API dependent id of the method @@ -120,6 +148,18 @@ public: const std::shared_ptr& data, const typename ResultHandler::type& resultCallback); + /** + * Send a signal to the peer. + * There is no return value from the peer + * Sends any data only if a peer registered this a signal + * + * @param methodID API dependent id of the method + * @param data data to sent + * @tparam SentDataType data type to send + */ + template + void signal(const MethodID methodID, + const std::shared_ptr& data); private: typedef std::lock_guard Lock; Processor mProcessor; @@ -136,6 +176,15 @@ void Service::addMethodHandler(const MethodID methodID, LOGD("Added method with id " << methodID); } +template +void Service::addSignalHandler(const MethodID methodID, + const typename SignalHandler::type& handler) +{ + LOGD("Adding signal with id " << methodID); + mProcessor.addSignalHandler(methodID, handler); + LOGD("Added signal with id " << methodID); +} + template std::shared_ptr Service::callSync(const MethodID methodID, const PeerID peerID, @@ -161,6 +210,14 @@ void Service::callAsync(const MethodID methodID, LOGD("Async called method: " << methodID << "for user: " << peerID); } +template +void Service::signal(const MethodID methodID, + const std::shared_ptr& data) +{ + LOGD("Signaling: " << methodID); + mProcessor.signal(methodID, data); + LOGD("Signaled: " << methodID); +} } // namespace ipc } // namespace security_containers diff --git a/common/ipc/types.hpp b/common/ipc/types.hpp index 1bfaa4e..6588fb0 100644 --- a/common/ipc/types.hpp +++ b/common/ipc/types.hpp @@ -34,6 +34,11 @@ namespace security_containers { namespace ipc { +typedef std::function PeerCallback; +typedef unsigned int PeerID; +typedef unsigned int MethodID; +typedef unsigned int MessageID; + enum class Status : int { OK = 0, PARSING_ERROR, @@ -50,9 +55,13 @@ void throwOnError(const Status status); template struct MethodHandler { - typedef std::function(std::shared_ptr&)> type; + typedef std::function(PeerID, std::shared_ptr&)> type; }; +template +struct SignalHandler { + typedef std::function&)> type; +}; template struct ResultHandler { diff --git a/tests/unit_tests/ipc/ut-ipc.cpp b/tests/unit_tests/ipc/ut-ipc.cpp index 5d284bd..88696fe 100644 --- a/tests/unit_tests/ipc/ut-ipc.cpp +++ b/tests/unit_tests/ipc/ut-ipc.cpp @@ -25,6 +25,7 @@ // TODO: Test connection limit // TODO: Refactor tests - function for setting up env +// TODO: Callback wrapper that waits till the callback is called #include "config.hpp" @@ -37,6 +38,7 @@ #include "config/fields.hpp" #include "logger/logger.hpp" +#include #include #include #include @@ -111,35 +113,64 @@ struct ThrowOnAcceptData { } }; -std::shared_ptr returnEmptyCallback(std::shared_ptr&) +std::shared_ptr returnEmptyCallback(const PeerID, std::shared_ptr&) { return std::shared_ptr(new EmptyData()); } -std::shared_ptr returnDataCallback(std::shared_ptr&) +std::shared_ptr returnDataCallback(const PeerID, std::shared_ptr&) { return std::shared_ptr(new SendData(1)); } -std::shared_ptr echoCallback(std::shared_ptr& data) +std::shared_ptr echoCallback(const PeerID, std::shared_ptr& data) { return data; } -std::shared_ptr longEchoCallback(std::shared_ptr& data) +std::shared_ptr longEchoCallback(const PeerID, std::shared_ptr& data) { std::this_thread::sleep_for(std::chrono::seconds(1)); return data; } -void testEcho(Client& c, const Client::MethodID methodID) +PeerID connect(Service& s, Client& c) +{ + // Connects the Client to the Service and returns Clients PeerID + + std::mutex mutex; + std::unique_lock lock(mutex); + std::condition_variable cv; + + unsigned int peerID = 0; + auto newPeerCallback = [&cv, &peerID](unsigned int newPeerID) { + peerID = newPeerID; + cv.notify_one(); + }; + + s.setNewPeerCallback(newPeerCallback); + + if (!s.isStarted()) { + s.start(); + } + + c.start(); + + BOOST_CHECK(cv.wait_for(lock, std::chrono::milliseconds(1000), [&peerID]() { + return peerID != 0; + })); + + return peerID; +} + +void testEcho(Client& c, const MethodID methodID) { std::shared_ptr sentData(new SendData(34)); std::shared_ptr recvData = c.callSync(methodID, sentData); BOOST_CHECK_EQUAL(recvData->intVal, sentData->intVal); } -void testEcho(Service& s, const Client::MethodID methodID, const Service::PeerID peerID) +void testEcho(Service& s, const MethodID methodID, const PeerID peerID) { std::shared_ptr sentData(new SendData(56)); std::shared_ptr recvData = s.callSync(methodID, peerID, sentData); @@ -151,13 +182,13 @@ void testEcho(Service& s, const Client::MethodID methodID, const Service::PeerID BOOST_FIXTURE_TEST_SUITE(IPCSuite, Fixture) -BOOST_AUTO_TEST_CASE(ConstructorDestructorTest) +BOOST_AUTO_TEST_CASE(ConstructorDestructor) { Service s(socketPath); Client c(socketPath); } -BOOST_AUTO_TEST_CASE(ServiceAddRemoveMethodTest) +BOOST_AUTO_TEST_CASE(ServiceAddRemoveMethod) { Service s(socketPath); @@ -179,32 +210,18 @@ BOOST_AUTO_TEST_CASE(ServiceAddRemoveMethodTest) BOOST_CHECK_THROW(testEcho(c, 2), IPCException); } -BOOST_AUTO_TEST_CASE(ClientAddRemoveMethodTest) +BOOST_AUTO_TEST_CASE(ClientAddRemoveMethod) { - std::mutex mtx; - std::unique_lock lck(mtx); - std::condition_variable cv; - unsigned int peerID = 0; - auto newPeerCallback = [&cv, &peerID](unsigned int newPeerID) { - peerID = newPeerID; - cv.notify_one(); - }; - Service s(socketPath, newPeerCallback); - s.start(); + Service s(socketPath); Client c(socketPath); - c.addMethodHandler(1, returnEmptyCallback); c.addMethodHandler(1, returnDataCallback); - c.start(); + PeerID peerID = connect(s, c); c.addMethodHandler(1, echoCallback); c.addMethodHandler(2, returnDataCallback); - BOOST_CHECK(cv.wait_for(lck, std::chrono::milliseconds(1000), [&peerID]() { - return peerID != 0; - })); - testEcho(s, 1, peerID); c.removeMethod(1); @@ -213,7 +230,7 @@ BOOST_AUTO_TEST_CASE(ClientAddRemoveMethodTest) BOOST_CHECK_THROW(testEcho(s, 1, peerID), IPCException); } -BOOST_AUTO_TEST_CASE(ServiceStartStopTest) +BOOST_AUTO_TEST_CASE(ServiceStartStop) { Service s(socketPath); @@ -228,7 +245,7 @@ BOOST_AUTO_TEST_CASE(ServiceStartStopTest) s.start(); } -BOOST_AUTO_TEST_CASE(ClientStartStopTest) +BOOST_AUTO_TEST_CASE(ClientStartStop) { Service s(socketPath); Client c(socketPath); @@ -246,7 +263,7 @@ BOOST_AUTO_TEST_CASE(ClientStartStopTest) c.stop(); } -BOOST_AUTO_TEST_CASE(SyncClientToServiceEchoTest) +BOOST_AUTO_TEST_CASE(SyncClientToServiceEcho) { Service s(socketPath); s.addMethodHandler(1, echoCallback); @@ -259,7 +276,7 @@ BOOST_AUTO_TEST_CASE(SyncClientToServiceEchoTest) testEcho(c, 2); } -BOOST_AUTO_TEST_CASE(RestartTest) +BOOST_AUTO_TEST_CASE(Restart) { Service s(socketPath); s.addMethodHandler(1, echoCallback); @@ -284,32 +301,19 @@ BOOST_AUTO_TEST_CASE(RestartTest) testEcho(c, 2); } -BOOST_AUTO_TEST_CASE(SyncServiceToClientEchoTest) +BOOST_AUTO_TEST_CASE(SyncServiceToClientEcho) { - std::mutex mtx; - std::unique_lock lck(mtx); - std::condition_variable cv; - unsigned int peerID = 0; - auto newPeerCallback = [&cv, &peerID](unsigned int newPeerID) { - peerID = newPeerID; - cv.notify_one(); - }; - Service s(socketPath, newPeerCallback); - s.start(); + Service s(socketPath); Client c(socketPath); c.addMethodHandler(1, echoCallback); - c.start(); - - BOOST_CHECK(cv.wait_for(lck, std::chrono::milliseconds(1000), [&peerID]() { - return peerID != 0; - })); + PeerID peerID = connect(s, c); std::shared_ptr sentData(new SendData(56)); std::shared_ptr recvData = s.callSync(1, peerID, sentData); BOOST_CHECK_EQUAL(recvData->intVal, sentData->intVal); } -BOOST_AUTO_TEST_CASE(AsyncClientToServiceEchoTest) +BOOST_AUTO_TEST_CASE(AsyncClientToServiceEcho) { // Setup Service and Client Service s(socketPath); @@ -340,33 +344,20 @@ BOOST_AUTO_TEST_CASE(AsyncClientToServiceEchoTest) BOOST_CHECK_EQUAL(recvData->intVal, sentData->intVal); } -BOOST_AUTO_TEST_CASE(AsyncServiceToClientEchoTest) +BOOST_AUTO_TEST_CASE(AsyncServiceToClientEcho) { - std::mutex mtx; - std::unique_lock lck(mtx); - std::condition_variable cv; - - // Setup Service and Client - unsigned int peerID = 0; - auto newPeerCallback = [&cv, &peerID](unsigned int newPeerID) { - peerID = newPeerID; - cv.notify_one(); - }; - Service s(socketPath, newPeerCallback); - s.start(); + Service s(socketPath); Client c(socketPath); c.addMethodHandler(1, echoCallback); - c.start(); - - // Wait for the connection - BOOST_CHECK(cv.wait_for(lck, std::chrono::milliseconds(1000), [&peerID]() { - return peerID != 0; - })); + PeerID peerID = connect(s, c); // Async call std::shared_ptr sentData(new SendData(56)); std::shared_ptr recvData; + std::mutex mtx; + std::unique_lock lck(mtx); + std::condition_variable cv; auto dataBack = [&cv, &recvData](ipc::Status status, std::shared_ptr& data) { BOOST_CHECK(status == ipc::Status::OK); recvData = data; @@ -384,7 +375,7 @@ BOOST_AUTO_TEST_CASE(AsyncServiceToClientEchoTest) } -BOOST_AUTO_TEST_CASE(SyncTimeoutTest) +BOOST_AUTO_TEST_CASE(SyncTimeout) { Service s(socketPath); s.addMethodHandler(1, longEchoCallback); @@ -398,7 +389,7 @@ BOOST_AUTO_TEST_CASE(SyncTimeoutTest) BOOST_CHECK_THROW((c.callSync(1, sentData, 10)), IPCException); } -BOOST_AUTO_TEST_CASE(SerializationErrorTest) +BOOST_AUTO_TEST_CASE(SerializationError) { Service s(socketPath); s.addMethodHandler(1, echoCallback); @@ -413,7 +404,7 @@ BOOST_AUTO_TEST_CASE(SerializationErrorTest) } -BOOST_AUTO_TEST_CASE(ParseErrorTest) +BOOST_AUTO_TEST_CASE(ParseError) { Service s(socketPath); s.addMethodHandler(1, echoCallback); @@ -426,11 +417,11 @@ BOOST_AUTO_TEST_CASE(ParseErrorTest) BOOST_CHECK_THROW((c.callSync(1, sentData, 10000)), IPCParsingException); } -BOOST_AUTO_TEST_CASE(DisconnectedPeerErrorTest) +BOOST_AUTO_TEST_CASE(DisconnectedPeerError) { Service s(socketPath); - auto method = [](std::shared_ptr&) { + auto method = [](const PeerID, std::shared_ptr&) { return std::shared_ptr(new SendData(1)); }; @@ -462,10 +453,10 @@ BOOST_AUTO_TEST_CASE(DisconnectedPeerErrorTest) } -BOOST_AUTO_TEST_CASE(ReadTimeoutTest) +BOOST_AUTO_TEST_CASE(ReadTimeout) { Service s(socketPath); - auto longEchoCallback = [](std::shared_ptr& data) { + auto longEchoCallback = [](const PeerID, std::shared_ptr& data) { return std::shared_ptr(new LongSendData(data->intVal)); }; s.addMethodHandler(1, longEchoCallback); @@ -480,7 +471,7 @@ BOOST_AUTO_TEST_CASE(ReadTimeoutTest) } -BOOST_AUTO_TEST_CASE(WriteTimeoutTest) +BOOST_AUTO_TEST_CASE(WriteTimeout) { Service s(socketPath); s.addMethodHandler(1, echoCallback); @@ -500,6 +491,66 @@ BOOST_AUTO_TEST_CASE(WriteTimeoutTest) } +BOOST_AUTO_TEST_CASE(AddSignalInRuntime) +{ + Service s(socketPath); + Client c(socketPath); + connect(s, c); + + std::atomic_bool isHandlerACalled(false); + auto handlerA = [&isHandlerACalled](const PeerID, std::shared_ptr&) { + isHandlerACalled = true; + }; + + std::atomic_bool isHandlerBCalled(false); + auto handlerB = [&isHandlerBCalled](const PeerID, std::shared_ptr&) { + isHandlerBCalled = true; + }; + + c.addSignalHandler(1, handlerA); + c.addSignalHandler(2, handlerB); + + auto data = std::make_shared(1); + s.signal(2, data); + s.signal(1, data); + + // Wait for the signals to arrive + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + BOOST_CHECK(isHandlerACalled && isHandlerBCalled); +} + + +BOOST_AUTO_TEST_CASE(AddSignalOffline) +{ + Service s(socketPath); + Client c(socketPath); + + std::atomic_bool isHandlerACalled(false); + auto handlerA = [&isHandlerACalled](const PeerID, std::shared_ptr&) { + isHandlerACalled = true; + }; + + std::atomic_bool isHandlerBCalled(false); + auto handlerB = [&isHandlerBCalled](const PeerID, std::shared_ptr&) { + isHandlerBCalled = true; + }; + + c.addSignalHandler(1, handlerA); + c.addSignalHandler(2, handlerB); + + connect(s, c); + + // Wait for the information about the signals to propagate + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + auto data = std::make_shared(1); + s.signal(2, data); + s.signal(1, data); + + // Wait for the signals to arrive + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + BOOST_CHECK(isHandlerACalled && isHandlerBCalled); +} + // BOOST_AUTO_TEST_CASE(ConnectionLimitTest) // { -- 2.7.4 From a41c5f342fb73d6abd12298c103182bfabc979cc Mon Sep 17 00:00:00 2001 From: Piotr Bartosiewicz Date: Tue, 2 Dec 2014 16:27:25 +0100 Subject: [PATCH 14/16] Fix running tests under valgrind [Bug/Feature] Running LXC commands via exec, new building flag USE_EXEC [Cause] N/A [Solution] N/A [Verification] Build with and without USE_EXEC, run tests Change-Id: I42a2745d81d309922704bd213a72727b4c613c40 --- common/lxc/domain.cpp | 160 ++++++++++++++++++++++++++++++++----- common/lxc/domain.hpp | 63 +++++++++++++++ common/utils/c-array.hpp | 73 +++++++++++++++++ common/utils/execute.cpp | 92 +++++++++++++++++++++ common/utils/execute.hpp | 40 ++++++++++ server/container-admin.cpp | 60 ++++---------- tests/unit_tests/lxc/ut-domain.cpp | 1 - 7 files changed, 422 insertions(+), 67 deletions(-) create mode 100644 common/utils/c-array.hpp create mode 100644 common/utils/execute.cpp create mode 100644 common/utils/execute.hpp diff --git a/common/lxc/domain.cpp b/common/lxc/domain.cpp index 191f405..089799d 100644 --- a/common/lxc/domain.cpp +++ b/common/lxc/domain.cpp @@ -22,10 +22,22 @@ * @brief Lxc domain */ +// Define macro USE_EXEC if you are goind to use valgrind +// TODO Consider always using exec to control lxc. Advantages: +// - 'ps' output is more descriptive with lxc-start +// - lxc library is not tested well with multithread programs +// (there are still some issues like using umask etc.) +// - it could be possible not to be uid 0 in this process +// - fork + unshare does not work with traced process (e.g. under valgrind) + #include "config.hpp" #include "logger/logger.hpp" #include "lxc/domain.hpp" #include "lxc/exception.hpp" +#ifdef USE_EXEC +#include "utils/execute.hpp" +#include "utils/c-array.hpp" +#endif #include #include @@ -39,18 +51,36 @@ namespace security_containers { namespace lxc { namespace { +#define ITEM(X) {#X, LxcDomain::State::X}, const std::map STATE_MAP = { - {"STOPPED", LxcDomain::State::STOPPED}, - {"STARTING", LxcDomain::State::STARTING}, - {"RUNNING", LxcDomain::State::RUNNING}, - {"STOPPING", LxcDomain::State::STOPPING}, - {"ABORTING", LxcDomain::State::ABORTING}, - {"FREEZING", LxcDomain::State::FREEZING}, - {"FROZEN", LxcDomain::State::FROZEN}, - {"THAWED", LxcDomain::State::THAWED} + ITEM(STOPPED) + ITEM(STARTING) + ITEM(RUNNING) + ITEM(STOPPING) + ITEM(ABORTING) + ITEM(FREEZING) + ITEM(FROZEN) + ITEM(THAWED) }; +#undef ITEM } // namespace +std::string LxcDomain::toString(State state) +{ +#define CASE(X) case LxcDomain::State::X: return #X; + switch (state) { + CASE(STOPPED) + CASE(STARTING) + CASE(RUNNING) + CASE(STOPPING) + CASE(ABORTING) + CASE(FREEZING) + CASE(FROZEN) + CASE(THAWED) + } +#undef CASE +} + LxcDomain::LxcDomain(const std::string& lxcPath, const std::string& domainName) : mContainer(nullptr) { @@ -76,7 +106,7 @@ std::string LxcDomain::getConfigItem(const std::string& key) char buffer[1024]; int len = mContainer->get_config_item(mContainer, key.c_str(), buffer, sizeof(buffer)); if (len < 0) { - LOGE("Key '" + key + "' not found in domain " + getName()); + LOGE("Key '" << key << "' not found in domain " << getName()); throw LxcException("Key not found"); } return buffer; @@ -95,20 +125,40 @@ LxcDomain::State LxcDomain::getState() bool LxcDomain::create(const std::string& templatePath, const char* const* argv) { +#ifdef USE_EXEC + utils::CStringArrayBuilder args; + args.add("lxc-create") + .add("-n").add(mContainer->name) + .add("-t").add(templatePath.c_str()) + .add("-P").add(mContainer->config_path); + + while (*argv) { + args.add(*argv++); + } + + if (!utils::executeAndWait("/usr/bin/lxc-create", args.c_array())) { + LOGE("Could not create domain " << getName()); + return false; + } + + refresh(); + return true; +#else if (!mContainer->create(mContainer, templatePath.c_str(), NULL, NULL, 0, const_cast(argv))) { - LOGE("Could not create domain " + getName()); + LOGE("Could not create domain " << getName()); return false; } return true; +#endif } bool LxcDomain::destroy() { if (!mContainer->destroy(mContainer)) { - LOGE("Could not destroy domain " + getName()); + LOGE("Could not destroy domain " << getName()); return false; } return true; @@ -116,25 +166,57 @@ bool LxcDomain::destroy() bool LxcDomain::start(const char* const* argv) { +#ifdef USE_EXEC + if (mContainer->is_running(mContainer)) { + LOGE("Already started " << getName()); + return false; + } + + utils::CStringArrayBuilder args; + args.add("lxc-start") + .add("-d") + .add("-n").add(mContainer->name) + .add("-P").add(mContainer->config_path) + .add("--"); + + while (*argv) { + args.add(*argv++); + } + + if (!utils::executeAndWait("/usr/bin/lxc-start", args.c_array())) { + LOGE("Could not start domain " << getName()); + return false; + } + + refresh(); + + // we have to check status because lxc-start runs in daemonized mode + if (!mContainer->is_running(mContainer)) { + LOGE("Could not start init in domain " << getName()); + return false; + } + return true; +#else if (mContainer->is_running(mContainer)) { - LOGE("Already started " + getName()); + LOGE("Already started " << getName()); return false; } if (!mContainer->want_daemonize(mContainer, true)) { - LOGE("Could not configure domain " + getName()); + LOGE("Could not configure domain " << getName()); return false; } if (!mContainer->start(mContainer, false, const_cast(argv))) { - LOGE("Could not start domain " + getName()); + LOGE("Could not start domain " << getName()); return false; } return true; +#endif } bool LxcDomain::stop() { if (!mContainer->stop(mContainer)) { - LOGE("Could not stop domain " + getName()); + LOGE("Could not stop domain " << getName()); return false; } return true; @@ -143,7 +225,7 @@ bool LxcDomain::stop() bool LxcDomain::reboot() { if (!mContainer->reboot(mContainer)) { - LOGE("Could not reboot domain " + getName()); + LOGE("Could not reboot domain " << getName()); return false; } return true; @@ -160,10 +242,27 @@ bool LxcDomain::shutdown(int timeout) return false; } +#ifdef USE_EXEC + utils::CStringArrayBuilder args; + std::string timeoutStr = std::to_string(timeout); + args.add("lxc-stop") + .add("-n").add(mContainer->name) + .add("-P").add(mContainer->config_path) + .add("-t").add(timeoutStr.c_str()) + .add("--nokill"); + + if (!utils::executeAndWait("/usr/bin/lxc-stop", args.c_array())) { + LOGE("Could not gracefully shutdown domain " << getName() << " in " << timeout << "s"); + return false; + } + + refresh(); + return true; +#else // try shutdown by sending poweroff to init if (setRunLevel(utils::RUNLEVEL_POWEROFF)) { - if (!mContainer->wait(mContainer, "STOPPED", timeout)) { - LOGE("Could not gracefully shutdown domain " + getName() + " in " << timeout << "s"); + if (!waitForState(State::STOPPED, timeout)) { + LOGE("Could not gracefully shutdown domain " << getName() << " in " << timeout << "s"); return false; } return true; @@ -172,16 +271,17 @@ bool LxcDomain::shutdown(int timeout) // fallback for other inits like bash: lxc sends 'lxc.haltsignal' signal to init if (!mContainer->shutdown(mContainer, timeout)) { - LOGE("Could not gracefully shutdown domain " + getName() + " in " << timeout << "s"); + LOGE("Could not gracefully shutdown domain " << getName() << " in " << timeout << "s"); return false; } return true; +#endif } bool LxcDomain::freeze() { if (!mContainer->freeze(mContainer)) { - LOGE("Could not freeze domain " + getName()); + LOGE("Could not freeze domain " << getName()); return false; } return true; @@ -190,7 +290,16 @@ bool LxcDomain::freeze() bool LxcDomain::unfreeze() { if (!mContainer->unfreeze(mContainer)) { - LOGE("Could not unfreeze domain " + getName()); + LOGE("Could not unfreeze domain " << getName()); + return false; + } + return true; +} + +bool LxcDomain::waitForState(State state, int timeout) +{ + if (!mContainer->wait(mContainer, toString(state).c_str(), timeout)) { + LOGD("Timeout while waiting for state " << toString(state) << " of domain " << getName()); return false; } return true; @@ -216,6 +325,15 @@ bool LxcDomain::setRunLevel(int runLevel) return status == 0; } +void LxcDomain::refresh() +{ + //TODO Consider make LxcDomain state-less + std::string domainName = mContainer->name; + std::string lxcPath = mContainer->config_path; + lxc_container_put(mContainer); + mContainer = lxc_container_new(domainName.c_str(), lxcPath.c_str()); +} + } // namespace lxc } // namespace security_containers diff --git a/common/lxc/domain.hpp b/common/lxc/domain.hpp index 60ae5a4..3c45b85 100644 --- a/common/lxc/domain.hpp +++ b/common/lxc/domain.hpp @@ -50,34 +50,97 @@ public: THAWED }; + /** + * LxcDomain constructor + * @param lxcPath path where containers lives + * @param domainName name of domain + */ LxcDomain(const std::string& lxcPath, const std::string& domainName); ~LxcDomain(); LxcDomain(const LxcDomain&) = delete; LxcDomain& operator=(const LxcDomain&) = delete; + /** + * Get domain name + */ std::string getName() const; + /** + * Get item from lxc config file + * @throw LxcException if key not found + */ std::string getConfigItem(const std::string& key); + /** + * Is domain defined (created)? + */ bool isDefined(); + /** + * String representation of state + */ + static std::string toString(State state); + + /** + * Get domain state + */ State getState(); + /** + * Wait till domain is in specified state + * @return false on timeout + */ + bool waitForState(State state, int timeout); + + /** + * Create domain + * @param templatePath template from which domain will be created + * @param argv additional template arguments + */ bool create(const std::string& templatePath, const char* const* argv); + + /** + * Destroy domain + */ bool destroy(); + /** + * Start domain + * @param argv init process with arguments + */ bool start(const char* const* argv); + + /** + * Immediate stop the domain + * It kills all processes within this domain + */ bool stop(); + + /** + * Reboot domain + */ bool reboot(); + + /** + * Gracefully shutdown domain. + */ bool shutdown(int timeout); + /** + * Freeze (pause/lock) domain + */ bool freeze(); + + /** + * Unfreeze domain + */ bool unfreeze(); private: lxc_container* mContainer; bool setRunLevel(int runLevel); + void refresh(); }; diff --git a/common/utils/c-array.hpp b/common/utils/c-array.hpp new file mode 100644 index 0000000..73b037b --- /dev/null +++ b/common/utils/c-array.hpp @@ -0,0 +1,73 @@ +/* + * 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 C array helper + */ + +#ifndef COMMON_UTILS_C_ARRAY_HPP +#define COMMON_UTILS_C_ARRAY_HPP + +#include + +namespace security_containers { +namespace utils { + +template +class CArrayBuilder { +public: + CArrayBuilder(): mArray(1, nullptr) {} + + CArrayBuilder& add(const T& v) { + mArray.back() = v; + mArray.push_back(nullptr); + return *this; + } + + const T* c_array() const { + return mArray.data(); + } + + size_t size() const { + return mArray.size() - 1; + } + + bool empty() const { + return size() == 0; + } + + const T* begin() const { + return &*mArray.begin(); + } + + const T* end() const { + return &*(mArray.end() - 1); + } +private: + std::vector mArray; +}; + +typedef CArrayBuilder CStringArrayBuilder; + +} // namespace utils +} // namespace security_containers + + +#endif // COMMON_UTILS_C_ARRAY_HPP diff --git a/common/utils/execute.cpp b/common/utils/execute.cpp new file mode 100644 index 0000000..2f2e927 --- /dev/null +++ b/common/utils/execute.cpp @@ -0,0 +1,92 @@ +/* + * 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 Execute utils + */ + +#include "config.hpp" +#include "utils/execute.hpp" +#include "logger/logger.hpp" + +#include +#include +#include + +namespace security_containers { +namespace utils { + +namespace { + +std::ostream& operator<< (std::ostream& out, const char* const* argv) +{ + if (*argv) { + argv++; //skip + } + while (*argv) { + out << " '" << *argv++ << "'"; + } + return out; +} + +} // namespace + +bool executeAndWait(const char* fname, const char* const* argv, int& status) +{ + LOGD("Execute " << fname << argv); + + pid_t pid = fork(); + if (pid == -1) { + LOGE("Fork failed"); + return false; + } + if (pid == 0) { + execv(fname, const_cast(argv)); + _exit(EXIT_FAILURE); + } + int ret = waitpid(pid, &status, 0); + if (ret != pid) { + LOGE("Waitpid failed"); + return false; + } + return true; +} + +bool executeAndWait(const char* fname, const char* const* argv) +{ + int status; + if (!executeAndWait(fname, argv, status)) { + return false; + } + if (status != EXIT_SUCCESS) { + if (WIFEXITED(status)) { + LOGW("Process " << fname << " has exited with status " << WEXITSTATUS(status)); + } else if (WIFSIGNALED(status)) { + LOGW("Process " << fname << " was killed by signal " << WTERMSIG(status)); + } else if (WIFSTOPPED(status)) { + LOGW("Process " << fname << " was stopped by signal " << WSTOPSIG(status)); + } + return false; + } + return true; +} + +} // namespace utils +} // namespace security_containers diff --git a/common/utils/execute.hpp b/common/utils/execute.hpp new file mode 100644 index 0000000..1fc7b29 --- /dev/null +++ b/common/utils/execute.hpp @@ -0,0 +1,40 @@ +/* + * 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 Execute utils + */ + +#ifndef COMMON_UTILS_EXECUTE_HPP +#define COMMON_UTILS_EXECUTE_HPP + + +namespace security_containers { +namespace utils { + +bool executeAndWait(const char* fname, const char* const* argv); + +bool executeAndWait(const char* fname, const char* const* argv, int& status); + +} // namespace utils +} // namespace security_containers + + +#endif // COMMON_UTILS_EXECUTE_HPP diff --git a/server/container-admin.cpp b/server/container-admin.cpp index 65179c3..1d8cdee 100644 --- a/server/container-admin.cpp +++ b/server/container-admin.cpp @@ -29,6 +29,7 @@ #include "logger/logger.hpp" #include "utils/paths.hpp" +#include "utils/c-array.hpp" #include @@ -40,37 +41,6 @@ namespace { // TODO: this should be in container's configuration file const int SHUTDOWN_WAIT = 10; -class Args { -public: - Args(const std::vector& args) - { - mArgs.reserve(args.size() + 1); - for (const std::string& arg : args) { - mArgs.push_back(arg.c_str()); - } - mArgs.push_back(NULL); - } - bool empty() const - { - return mArgs.size() == 1; - } - const char* const* getAsCArray() const - { - return mArgs.data(); - } - friend std::ostream& operator<<(std::ostream& os, const Args& a) - { - for (const char* arg : a.mArgs) { - if (arg != NULL) { - os << "'" << arg << "' "; - } - } - return os; - } -private: - std::vector mArgs; -}; - } // namespace const std::uint64_t DEFAULT_CPU_SHARES = 1024; @@ -82,7 +52,8 @@ ContainerAdmin::ContainerAdmin(const std::string& containersPath, : mConfig(config), mDom(containersPath, config.name), mId(mDom.getName()), - mDetachOnExit(false) + mDetachOnExit(false), + mDestroyOnExit(false) { LOGD(mId << ": Instantiating ContainerAdmin object"); @@ -91,16 +62,16 @@ ContainerAdmin::ContainerAdmin(const std::string& containersPath, const std::string lxcTemplate = utils::getAbsolutePath(config.lxcTemplate, lxcTemplatePrefix); LOGI(mId << ": Creating domain from template: " << lxcTemplate); - std::vector args; + utils::CStringArrayBuilder args; if (!config.ipv4Gateway.empty()) { - args.push_back("--ipv4-gateway"); - args.push_back(config.ipv4Gateway); + args.add("--ipv4-gateway"); + args.add(config.ipv4Gateway.c_str()); } if (!config.ipv4.empty()) { - args.push_back("--ipv4"); - args.push_back(config.ipv4); + args.add("--ipv4"); + args.add(config.ipv4.c_str()); } - if (!mDom.create(lxcTemplate, Args(args).getAsCArray())) { + if (!mDom.create(lxcTemplate, args.c_array())) { throw ContainerOperationException("Could not create domain"); } } @@ -145,16 +116,15 @@ void ContainerAdmin::start() return; } - const Args args(mConfig.initWithArgs); - bool result; + utils::CStringArrayBuilder args; + for (const std::string& arg : mConfig.initWithArgs) { + args.add(arg.c_str()); + } if (args.empty()) { - result = mDom.start(NULL); - } else { - LOGD(mId << ": Init: " << args); - result = mDom.start(args.getAsCArray()); + args.add("/sbin/init"); } - if (!result) { + if (!mDom.start(args.c_array())) { throw ContainerOperationException("Could not start container"); } diff --git a/tests/unit_tests/lxc/ut-domain.cpp b/tests/unit_tests/lxc/ut-domain.cpp index a529f9f..a9942b8 100644 --- a/tests/unit_tests/lxc/ut-domain.cpp +++ b/tests/unit_tests/lxc/ut-domain.cpp @@ -161,7 +161,6 @@ BOOST_AUTO_TEST_CASE(StartHasStoppedTest) NULL }; BOOST_CHECK(lxc.start(argv)); - BOOST_CHECK(lxc.getState() == LxcDomain::State::RUNNING); waitForInit(); BOOST_CHECK(lxc.getState() == LxcDomain::State::STOPPED); -- 2.7.4 From 161b6dc2bb991737ee46df3bbe1714f1e190144c Mon Sep 17 00:00:00 2001 From: Jan Olszak Date: Tue, 2 Dec 2014 17:27:17 +0100 Subject: [PATCH 15/16] IPC: CallQueue [Bug/Feature] CallQueue to wrap the Calls' queue [Cause] N/A [Solution] N/A [Verification] Build, install, run tests Change-Id: I9519114e1c6f2e4040e956ae54f5093646f2dfaf --- common/ipc/internals/call-queue.cpp | 66 ++++++++++++++++ common/ipc/internals/call-queue.hpp | 150 ++++++++++++++++++++++++++++++++++++ common/ipc/internals/processor.cpp | 21 +---- common/ipc/internals/processor.hpp | 81 ++++--------------- 4 files changed, 233 insertions(+), 85 deletions(-) create mode 100644 common/ipc/internals/call-queue.cpp create mode 100644 common/ipc/internals/call-queue.hpp diff --git a/common/ipc/internals/call-queue.cpp b/common/ipc/internals/call-queue.cpp new file mode 100644 index 0000000..9be7e53 --- /dev/null +++ b/common/ipc/internals/call-queue.cpp @@ -0,0 +1,66 @@ +/* +* 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 Managing the queue with calls + */ + +#include "config.hpp" + +#include "ipc/internals/call-queue.hpp" +#include "ipc/exception.hpp" +#include "logger/logger.hpp" + +namespace security_containers { +namespace ipc { + +CallQueue::CallQueue() + : mMessageIDCounter(0) +{ +} + +CallQueue::~CallQueue() +{ +} + +bool CallQueue::isEmpty() const +{ + return mCalls.empty(); +} + +MessageID CallQueue::getNextMessageID() +{ + // TODO: This method of generating UIDs is buggy. To be changed. + return ++mMessageIDCounter; +} + +CallQueue::Call CallQueue::pop() +{ + if (isEmpty()) { + LOGE("CallQueue is empty"); + throw IPCException("CallQueue is empty"); + } + Call call = std::move(mCalls.front()); + mCalls.pop(); + return call; +} + +} // namespace ipc +} // namespace security_containers diff --git a/common/ipc/internals/call-queue.hpp b/common/ipc/internals/call-queue.hpp new file mode 100644 index 0000000..4d1ecf6 --- /dev/null +++ b/common/ipc/internals/call-queue.hpp @@ -0,0 +1,150 @@ +/* +* Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved +* +* Contact: Jan Olszak +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License +*/ + +/** + * @file + * @author Jan Olszak (j.olszak@samsung.com) + * @brief Managing the queue with calls + */ + +#ifndef COMMON_IPC_INTERNALS_CALL_QUEUE_HPP +#define COMMON_IPC_INTERNALS_CALL_QUEUE_HPP + +#include "ipc/types.hpp" +#include "config/manager.hpp" + +#include +#include + +namespace security_containers { +namespace ipc { + +/** +* Class for managing a queue of calls in the Processor +*/ +class CallQueue { +public: + typedef std::function& data)> SerializeCallback; + typedef std::function(int fd)> ParseCallback; + + struct Call { + Call(const Call& other) = delete; + Call& operator=(const Call&) = delete; + Call() = default; + Call(Call&&) = default; + + PeerID peerID; + MethodID methodID; + MessageID messageID; + std::shared_ptr data; + SerializeCallback serialize; + ParseCallback parse; + ResultHandler::type process; + }; + + CallQueue(); + ~CallQueue(); + + CallQueue(const CallQueue&) = delete; + CallQueue(CallQueue&&) = delete; + CallQueue& operator=(const CallQueue&) = delete; + + template + MessageID push(const MethodID methodID, + const PeerID peerID, + const std::shared_ptr& data, + const typename ResultHandler::type& process); + + + template + MessageID push(const MethodID methodID, + const PeerID peerID, + const std::shared_ptr& data); + + Call pop(); + + bool isEmpty() const; + +private: + std::queue mCalls; + std::atomic mMessageIDCounter; + + MessageID getNextMessageID(); +}; + + +template +MessageID CallQueue::push(const MethodID methodID, + const PeerID peerID, + const std::shared_ptr& data, + const typename ResultHandler::type& process) +{ + Call call; + call.methodID = methodID; + call.peerID = peerID; + call.data = data; + + MessageID messageID = getNextMessageID(); + call.messageID = messageID; + + call.serialize = [](const int fd, std::shared_ptr& data)->void { + config::saveToFD(fd, *std::static_pointer_cast(data)); + }; + + call.parse = [](const int fd)->std::shared_ptr { + std::shared_ptr data(new ReceivedDataType()); + config::loadFromFD(fd, *data); + return data; + }; + + call.process = [process](Status status, std::shared_ptr& data)->void { + std::shared_ptr tmpData = std::static_pointer_cast(data); + return process(status, tmpData); + }; + + mCalls.push(std::move(call)); + + return messageID; +} + +template +MessageID CallQueue::push(const MethodID methodID, + const PeerID peerID, + const std::shared_ptr& data) +{ + Call call; + call.methodID = methodID; + call.peerID = peerID; + call.data = data; + + MessageID messageID = getNextMessageID(); + call.messageID = messageID; + + call.serialize = [](const int fd, std::shared_ptr& data)->void { + config::saveToFD(fd, *std::static_pointer_cast(data)); + }; + + mCalls.push(std::move(call)); + + return messageID; +} + +} // namespace ipc +} // namespace security_containers + +#endif // COMMON_IPC_INTERNALS_CALL_QUEUE_HPP diff --git a/common/ipc/internals/processor.cpp b/common/ipc/internals/processor.cpp index dc7df42..5565124 100644 --- a/common/ipc/internals/processor.cpp +++ b/common/ipc/internals/processor.cpp @@ -56,7 +56,6 @@ Processor::Processor(const PeerCallback& newPeerCallback, : mNewPeerCallback(newPeerCallback), mRemovedPeerCallback(removedPeerCallback), mMaxNumberOfPeers(maxNumberOfPeers), - mMessageIDCounter(0), mPeerIDCounter(0) { LOGT("Creating Processor"); @@ -579,34 +578,22 @@ bool Processor::onRemovePeer() return true; } -MessageID Processor::getNextMessageID() -{ - // TODO: This method of generating UIDs is buggy. To be changed. - return ++mMessageIDCounter; -} - PeerID Processor::getNextPeerID() { // TODO: This method of generating UIDs is buggy. To be changed. return ++mPeerIDCounter; } -Processor::Call Processor::getCall() +CallQueue::Call Processor::getCall() { Lock lock(mCallsMutex); - if (mCalls.empty()) { - LOGE("Calls queue empty"); - throw IPCException("Calls queue empty"); - } - Call call = std::move(mCalls.front()); - mCalls.pop(); - return call; + return mCalls.pop(); } bool Processor::onCall() { LOGT("Handle call (from another thread) to send a message."); - Call call = getCall(); + CallQueue::Call call = getCall(); std::shared_ptr socketPtr; try { @@ -664,7 +651,7 @@ void Processor::cleanCommunication() } case Event::CALL: { LOGD("Event CALL after FINISH"); - Call call = getCall(); + CallQueue::Call call = getCall(); if (call.process) { IGNORE_EXCEPTIONS(call.process(Status::CLOSING, call.data)); } diff --git a/common/ipc/internals/processor.hpp b/common/ipc/internals/processor.hpp index e43fe62..8fc17fb 100644 --- a/common/ipc/internals/processor.hpp +++ b/common/ipc/internals/processor.hpp @@ -27,10 +27,10 @@ #include "ipc/internals/socket.hpp" #include "ipc/internals/event-queue.hpp" +#include "ipc/internals/call-queue.hpp" #include "ipc/exception.hpp" #include "ipc/types.hpp" #include "config/manager.hpp" -#include "config/is-visitable.hpp" #include "config/fields.hpp" #include "logger/logger.hpp" @@ -70,11 +70,12 @@ const unsigned int DEFAULT_METHOD_TIMEOUT = 1000; * TODO: * - some mutexes may not be needed * - synchronous call to many peers -* - implement CallQueue class * - implement HandlerStore class for storing both signals and methods * - API for removing signals * - implement CallbackStore - thread safe calling/setting callbacks * - helper function for removing from unordered map +* - new way to generate UIDs +* - callbacks for serialization/parsing */ class Processor { public: @@ -261,21 +262,6 @@ private: ) }; - struct Call { - Call(const Call& other) = delete; - Call& operator=(const Call&) = delete; - Call() = default; - Call(Call&&) = default; - - PeerID peerID; - MethodID methodID; - std::shared_ptr data; - SerializeCallback serialize; - ParseCallback parse; - ResultHandler::type process; - MessageID messageID; - }; - struct MethodHandlers { MethodHandlers(const MethodHandlers& other) = delete; MethodHandlers& operator=(const MethodHandlers&) = delete; @@ -356,7 +342,7 @@ private: // Mutex for the Calls queue and the map of methods. std::mutex mCallsMutex; - std::queue mCalls; + CallQueue mCalls; std::unordered_map> mMethodsCallbacks; std::unordered_map> mSignalsCallbacks; std::unordered_map> mSignalsPeers; @@ -382,18 +368,12 @@ private: std::thread mThread; std::vector mFDs; - std::atomic mMessageIDCounter; std::atomic mPeerIDCounter; template void addMethodHandlerInternal(const MethodID methodID, const typename MethodHandler::type& process); - template - MessageID callInternal(const MethodID methodID, - const PeerID peerID, - const std::shared_ptr& data); - template MessageID callInternal(const MethodID methodID, const PeerID peerID, @@ -425,9 +405,8 @@ private: const MessageID messageID, std::shared_ptr signalCallbacks); void resetPolling(); - MessageID getNextMessageID(); PeerID getNextPeerID(); - Call getCall(); + CallQueue::Call getCall(); void removePeerInternal(const PeerID peerID, Status status); std::shared_ptr onNewSignals(const PeerID peerID, @@ -547,34 +526,11 @@ MessageID Processor::callInternal(const MethodID methodID, const std::shared_ptr& data, const typename ResultHandler::type& process) { - Call call; - call.peerID = peerID; - call.methodID = methodID; - call.data = data; - call.messageID = getNextMessageID(); - - call.serialize = [](const int fd, std::shared_ptr& data)->void { - config::saveToFD(fd, *std::static_pointer_cast(data)); - }; - - call.parse = [](const int fd)->std::shared_ptr { - std::shared_ptr data(new ReceivedDataType()); - config::loadFromFD(fd, *data); - return data; - }; - - call.process = [process](Status status, std::shared_ptr& data)->void { - std::shared_ptr tmpData = std::static_pointer_cast(data); - return process(status, tmpData); - }; + Lock lock(mCallsMutex); + MessageID messageID = mCalls.push(methodID, peerID, data, process); + mEventQueue.send(Event::CALL); - { - Lock lock(mCallsMutex); - mCalls.push(std::move(call)); - mEventQueue.send(Event::CALL); - } - - return call.messageID; + return messageID; } template @@ -660,24 +616,13 @@ void Processor::signal(const MethodID methodID, } for (const PeerID peerID : peersIDs) { - Call call; - call.peerID = peerID; - call.methodID = methodID; - call.data = data; - call.messageID = getNextMessageID(); - - call.serialize = [](const int fd, std::shared_ptr& data)->void { - config::saveToFD(fd, *std::static_pointer_cast(data)); - }; - - { - Lock lock(mCallsMutex); - mCalls.push(std::move(call)); - mEventQueue.send(Event::CALL); - } + Lock lock(mCallsMutex); + mCalls.push(methodID, peerID, data); + mEventQueue.send(Event::CALL); } } + } // namespace ipc } // namespace security_containers -- 2.7.4 From 1b846e5c49695251d97aaf3c1e2af4af12bb404d Mon Sep 17 00:00:00 2001 From: Piotr Bartosiewicz Date: Wed, 3 Dec 2014 17:03:43 +0100 Subject: [PATCH 16/16] Fix compilation issue [Bug/Feature] Code does not compile under gcc 4.6 [Cause] N/A [Solution] N/A [Verification] Build Change-Id: I883ae5c04de5dafd690e4ac2c1956780f8d20725 --- common/lxc/domain.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/common/lxc/domain.cpp b/common/lxc/domain.cpp index 089799d..24453a6 100644 --- a/common/lxc/domain.cpp +++ b/common/lxc/domain.cpp @@ -79,6 +79,7 @@ std::string LxcDomain::toString(State state) CASE(THAWED) } #undef CASE + throw LxcException("Invalid state"); } LxcDomain::LxcDomain(const std::string& lxcPath, const std::string& domainName) -- 2.7.4