From 633f086ac82e192354b061ffed1682f4fd1d2a1e Mon Sep 17 00:00:00 2001 From: Piotr Bartosiewicz Date: Mon, 23 Feb 2015 18:03:32 +0100 Subject: [PATCH 01/16] Epoll wrapper, epoll dispatchers (glib and threaded) [Bug/Feature] N/A [Cause] N/A [Solution] Epoll is the only way, using no additional threads, to aggregate descriptors to one epoll descriptor. [Verification] Run tests Change-Id: I3863129a8b947c467615b2e9d352fce3bd1cda9a --- common/ipc/client.cpp | 2 +- common/ipc/internals/acceptor.cpp | 2 +- common/ipc/internals/event-queue.hpp | 4 +- common/ipc/internals/processor.cpp | 10 +- common/ipc/internals/request-queue.hpp | 4 +- common/ipc/internals/socket.cpp | 18 +- common/ipc/service.cpp | 2 +- common/utils/event-poll.cpp | 151 +++++++++++++++ common/utils/event-poll.hpp | 68 +++++++ common/{ipc/internals => utils}/eventfd.cpp | 26 ++- common/{ipc/internals => utils}/eventfd.hpp | 11 +- .../internals/utils.cpp => utils/fd-utils.cpp} | 76 ++++---- .../internals/utils.hpp => utils/fd-utils.hpp} | 13 +- common/utils/glib-poll-dispatcher.cpp | 61 ++++++ common/utils/glib-poll-dispatcher.hpp | 53 ++++++ common/utils/thread-poll-dispatcher.cpp | 53 ++++++ common/utils/thread-poll-dispatcher.hpp | 52 ++++++ tests/unit_tests/ipc/ut-ipc.cpp | 2 - tests/unit_tests/utils/ut-event-poll.cpp | 205 +++++++++++++++++++++ 19 files changed, 726 insertions(+), 87 deletions(-) create mode 100644 common/utils/event-poll.cpp create mode 100644 common/utils/event-poll.hpp rename common/{ipc/internals => utils}/eventfd.cpp (67%) rename common/{ipc/internals => utils}/eventfd.hpp (88%) rename common/{ipc/internals/utils.cpp => utils/fd-utils.cpp} (68%) rename common/{ipc/internals/utils.hpp => utils/fd-utils.hpp} (90%) create mode 100644 common/utils/glib-poll-dispatcher.cpp create mode 100644 common/utils/glib-poll-dispatcher.hpp create mode 100644 common/utils/thread-poll-dispatcher.cpp create mode 100644 common/utils/thread-poll-dispatcher.hpp create mode 100644 tests/unit_tests/utils/ut-event-poll.cpp diff --git a/common/ipc/client.cpp b/common/ipc/client.cpp index 51e59f8..6669d8a 100644 --- a/common/ipc/client.cpp +++ b/common/ipc/client.cpp @@ -45,7 +45,7 @@ Client::~Client() LOGS("Client Destructor"); try { stop(); - } catch (IPCException& e) { + } catch (std::exception& e) { LOGE("Error in Client's destructor: " << e.what()); } } diff --git a/common/ipc/internals/acceptor.cpp b/common/ipc/internals/acceptor.cpp index 627e1fe..ecb2210 100644 --- a/common/ipc/internals/acceptor.cpp +++ b/common/ipc/internals/acceptor.cpp @@ -49,7 +49,7 @@ Acceptor::~Acceptor() LOGT("Destroying Acceptor"); try { stop(); - } catch (IPCException& e) { + } catch (std::exception& e) { LOGE("Error in destructor: " << e.what()); } LOGT("Destroyed Acceptor"); diff --git a/common/ipc/internals/event-queue.hpp b/common/ipc/internals/event-queue.hpp index 2c591f7..b4532fd 100644 --- a/common/ipc/internals/event-queue.hpp +++ b/common/ipc/internals/event-queue.hpp @@ -26,7 +26,7 @@ #define COMMON_IPC_INTERNALS_EVENT_QUEUE_HPP #include "ipc/exception.hpp" -#include "ipc/internals/eventfd.hpp" +#include "utils/eventfd.hpp" #include "logger/logger.hpp" #include @@ -82,7 +82,7 @@ private: std::mutex mCommunicationMutex; std::queue mMessages; - EventFD mEventFD; + utils::EventFD mEventFD; }; template diff --git a/common/ipc/internals/processor.cpp b/common/ipc/internals/processor.cpp index 79c1a22..93f721f 100644 --- a/common/ipc/internals/processor.cpp +++ b/common/ipc/internals/processor.cpp @@ -26,8 +26,8 @@ #include "ipc/exception.hpp" #include "ipc/internals/processor.hpp" -#include "ipc/internals/utils.hpp" #include "utils/signal.hpp" +#include "utils/exception.hpp" #include #include @@ -79,21 +79,21 @@ Processor::~Processor() LOGS(mLogPrefix + "Processor Destructor"); try { stop(); - } catch (IPCException& e) { + } catch (std::exception& e) { LOGE(mLogPrefix + "Error in Processor's destructor: " << e.what()); } } Processor::Peers::iterator Processor::getPeerInfoIterator(const FileDescriptor fd) { - return std::find_if(mPeerInfo.begin(), mPeerInfo.end(), [&fd](const PeerInfo & peerInfo) { + return std::find_if(mPeerInfo.begin(), mPeerInfo.end(), [fd](const PeerInfo & peerInfo) { return fd == peerInfo.socketPtr->getFD(); }); } Processor::Peers::iterator Processor::getPeerInfoIterator(const PeerID peerID) { - return std::find_if(mPeerInfo.begin(), mPeerInfo.end(), [&peerID](const PeerInfo & peerInfo) { + return std::find_if(mPeerInfo.begin(), mPeerInfo.end(), [peerID](const PeerInfo & peerInfo) { return peerID == peerInfo.peerID; }); } @@ -411,7 +411,7 @@ bool Processor::handleInput(const FileDescriptor fd) Socket::Guard guard = socket.getGuard(); socket.read(&methodID, sizeof(methodID)); socket.read(&messageID, sizeof(messageID)); - } catch (const IPCException& e) { + } catch (const UtilsException& e) { LOGE(mLogPrefix + "Error during reading the socket"); removePeerInternal(peerIt, std::make_exception_ptr(IPCNaughtyPeerException())); diff --git a/common/ipc/internals/request-queue.hpp b/common/ipc/internals/request-queue.hpp index e1ad46b..44306ee 100644 --- a/common/ipc/internals/request-queue.hpp +++ b/common/ipc/internals/request-queue.hpp @@ -26,7 +26,7 @@ #define COMMON_IPC_INTERNALS_MESSAGE_QUEUE_HPP #include "ipc/exception.hpp" -#include "ipc/internals/eventfd.hpp" +#include "utils/eventfd.hpp" #include "logger/logger.hpp" #include @@ -115,7 +115,7 @@ private: std::list mRequests; std::mutex mStateMutex; - EventFD mEventFD; + utils::EventFD mEventFD; }; template diff --git a/common/ipc/internals/socket.cpp b/common/ipc/internals/socket.cpp index 2469af6..a66456e 100644 --- a/common/ipc/internals/socket.cpp +++ b/common/ipc/internals/socket.cpp @@ -26,7 +26,7 @@ #include "ipc/exception.hpp" #include "ipc/internals/socket.hpp" -#include "ipc/internals/utils.hpp" +#include "utils/fd-utils.hpp" #include "logger/logger.hpp" #include @@ -60,8 +60,8 @@ Socket::Socket(Socket&& socket) noexcept Socket::~Socket() noexcept { try { - ipc::close(mFD); - } catch (IPCException& e) { + utils::close(mFD); + } catch (std::exception& e) { LOGE("Error in Socket's destructor: " << e.what()); } } @@ -89,13 +89,13 @@ std::shared_ptr Socket::accept() void Socket::write(const void* bufferPtr, const size_t size) const { Guard guard(mCommunicationMutex); - ipc::write(mFD, bufferPtr, size); + utils::write(mFD, bufferPtr, size); } void Socket::read(void* bufferPtr, const size_t size) const { Guard guard(mCommunicationMutex); - ipc::read(mFD, bufferPtr, size); + utils::read(mFD, bufferPtr, size); } int Socket::getSystemdSocket(const std::string& path) @@ -142,7 +142,7 @@ int Socket::createZoneSocket(const std::string& path) reinterpret_cast(&serverAddress), sizeof(struct sockaddr_un))) { std::string message = strerror(errno); - ::close(sockfd); + utils::close(sockfd); LOGE("Error in bind: " << message); IPCException("Error in bind: " + message); } @@ -150,7 +150,7 @@ int Socket::createZoneSocket(const std::string& path) if (-1 == ::listen(sockfd, MAX_QUEUE_LENGTH)) { std::string message = strerror(errno); - ::close(sockfd); + utils::close(sockfd); LOGE("Error in listen: " << message); IPCException("Error in listen: " + message); } @@ -188,7 +188,7 @@ Socket Socket::connectSocket(const std::string& path) if (-1 == connect(fd, reinterpret_cast(&serverAddress), sizeof(struct sockaddr_un))) { - ::close(fd); + utils::close(fd); LOGE("Error in connect: " + std::string(strerror(errno))); throw IPCException("Error in connect: " + std::string(strerror(errno))); } @@ -196,7 +196,7 @@ Socket Socket::connectSocket(const std::string& path) // Nonblock socket int flags = fcntl(fd, F_GETFL, 0); if (-1 == fcntl(fd, F_SETFL, flags | O_NONBLOCK)) { - ::close(fd); + utils::close(fd); LOGE("Error in fcntl: " + std::string(strerror(errno))); throw IPCException("Error in fcntl: " + std::string(strerror(errno))); } diff --git a/common/ipc/service.cpp b/common/ipc/service.cpp index d945f73..442d804 100644 --- a/common/ipc/service.cpp +++ b/common/ipc/service.cpp @@ -50,7 +50,7 @@ Service::~Service() LOGS("Service Destructor"); try { stop(); - } catch (IPCException& e) { + } catch (std::exception& e) { LOGE("Error in Service's destructor: " << e.what()); } } diff --git a/common/utils/event-poll.cpp b/common/utils/event-poll.cpp new file mode 100644 index 0000000..b37d727 --- /dev/null +++ b/common/utils/event-poll.cpp @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2015 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++ epoll wrapper + */ + +#include "config.hpp" +#include "utils/event-poll.hpp" +#include "utils/fd-utils.hpp" +#include "utils/exception.hpp" +#include "logger/logger.hpp" + +#include +#include +#include +#include + +namespace vasum { +namespace utils { + +EventPoll::EventPoll() + : mPollFD(::epoll_create1(EPOLL_CLOEXEC)) +{ + if (mPollFD == -1) { + LOGE("Failed to create epoll: " << getSystemErrorMessage()); + throw UtilsException("Could not create epoll"); + } +} + +EventPoll::~EventPoll() +{ + if (!mCallbacks.empty()) { + LOGW("Not removed callbacks: " << mCallbacks.size()); + assert(0 && "Not removed callbacks left"); + } + utils::close(mPollFD); +} + +int EventPoll::getPollFD() const +{ + return mPollFD; +} + +void EventPoll::addFD(const int fd, const Events events, Callback&& callback) +{ + std::lock_guard lock(mMutex); + + if (mCallbacks.find(fd) != mCallbacks.end()) { + LOGW("Already added fd: " << fd); + throw UtilsException("FD already added"); + } + + if (!addFDInternal(fd, events)) { + throw UtilsException("Could not add fd"); + } + + mCallbacks.insert({fd, std::make_shared(std::move(callback))}); + LOGT("Callback added for " << fd); +} + +void EventPoll::removeFD(const int fd) +{ + std::lock_guard lock(mMutex); + + auto iter = mCallbacks.find(fd); + if (iter == mCallbacks.end()) { + LOGW("Failed to remove nonexistent fd: " << fd); + throw UtilsException("FD does not exist"); + } + mCallbacks.erase(iter); + removeFDInternal(fd); + LOGT("Callback removed for " << fd); +} + +bool EventPoll::dispatchIteration(const int timeoutMs) +{ + for (;;) { + epoll_event event; + int num = epoll_wait(mPollFD, &event, 1, timeoutMs); + if (num == 0) { + return false; // timeout + } + if (num < 0) { + if (errno == EINTR) { + continue; + } + LOGE("Failed to wait on epoll: " << getSystemErrorMessage()); + throw UtilsException("Could not wait for event"); + } + + // callback could be removed in the meantime, so be careful, find it inside lock + std::lock_guard lock(mMutex); + auto iter = mCallbacks.find(event.data.fd); + if (iter == mCallbacks.end()) { + continue; + } + + // add ref because removeFD(self) can be called inside callback + std::shared_ptr callback(iter->second); + return (*callback)(event.data.fd, event.events); + } +} + +void EventPoll::dispatchLoop() +{ + while (dispatchIteration(-1)) {} +} + +bool EventPoll::addFDInternal(const int fd, const Events events) +{ + epoll_event event; + memset(&event, 0, sizeof(event)); + event.events = events; + event.data.fd = fd; + + if (epoll_ctl(mPollFD, EPOLL_CTL_ADD, fd, &event) == -1) { + LOGE("Failed to add fd to poll: " << getSystemErrorMessage()); + return false; + } + return true; +} + +void EventPoll::removeFDInternal(const int fd) +{ + if (epoll_ctl(mPollFD, EPOLL_CTL_DEL, fd, NULL) == -1) { + assert(errno != EBADF); // always removeFD before closing fd locally! + // this is important because linux will reuse this fd number + LOGE("Failed to remove fd from poll: " << getSystemErrorMessage()); + } +} + +} // namespace utils +} // namespace vasum diff --git a/common/utils/event-poll.hpp b/common/utils/event-poll.hpp new file mode 100644 index 0000000..1a7ae2c --- /dev/null +++ b/common/utils/event-poll.hpp @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2015 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++ epoll wrapper + */ + +#ifndef COMMON_UTILS_EVENT_POLL_HPP +#define COMMON_UTILS_EVENT_POLL_HPP + +#include +#include +#include +#include + +namespace vasum { +namespace utils { + +class EventPoll { +public: + + typedef unsigned int Events; + typedef std::function Callback; + + EventPoll(); + ~EventPoll(); + + int getPollFD() const; + + void addFD(const int fd, const Events events, Callback&& callback); + void removeFD(const int fd); + + bool dispatchIteration(const int timeoutMs); + void dispatchLoop(); + +private: + typedef std::recursive_mutex Mutex; + + const int mPollFD; + Mutex mMutex; + std::unordered_map> mCallbacks; + + bool addFDInternal(const int fd, const Events events); + void removeFDInternal(const int fd); +}; + + +} // namespace utils +} // namespace vasum + +#endif // COMMON_UTILS_EVENT_POLL_HPP diff --git a/common/ipc/internals/eventfd.cpp b/common/utils/eventfd.cpp similarity index 67% rename from common/ipc/internals/eventfd.cpp rename to common/utils/eventfd.cpp index 729243d..2fd8d62 100644 --- a/common/ipc/internals/eventfd.cpp +++ b/common/utils/eventfd.cpp @@ -24,9 +24,9 @@ #include "config.hpp" -#include "ipc/internals/eventfd.hpp" -#include "ipc/internals/utils.hpp" -#include "ipc/exception.hpp" +#include "utils/eventfd.hpp" +#include "utils/exception.hpp" +#include "utils/fd-utils.hpp" #include "logger/logger.hpp" #include @@ -35,24 +35,20 @@ #include namespace vasum { -namespace ipc { +namespace utils { EventFD::EventFD() { - mFD = ::eventfd(0, EFD_SEMAPHORE | EFD_NONBLOCK); + mFD = ::eventfd(0, EFD_SEMAPHORE | EFD_CLOEXEC); if (mFD == -1) { - LOGE("Error in eventfd: " << std::string(strerror(errno))); - throw IPCException("Error in eventfd: " + std::string(strerror(errno))); + LOGE("Error in eventfd: " << getSystemErrorMessage()); + throw UtilsException("Error in eventfd: " + getSystemErrorMessage()); } } EventFD::~EventFD() { - try { - ipc::close(mFD); - } catch (IPCException& e) { - LOGE("Error in Event's destructor: " << e.what()); - } + utils::close(mFD); } int EventFD::getFD() const @@ -63,15 +59,15 @@ int EventFD::getFD() const void EventFD::send() { const std::uint64_t toSend = 1; - ipc::write(mFD, &toSend, sizeof(toSend)); + utils::write(mFD, &toSend, sizeof(toSend)); } void EventFD::receive() { std::uint64_t readBuffer; - ipc::read(mFD, &readBuffer, sizeof(readBuffer)); + utils::read(mFD, &readBuffer, sizeof(readBuffer)); } -} // namespace ipc +} // namespace utils } // namespace vasum diff --git a/common/ipc/internals/eventfd.hpp b/common/utils/eventfd.hpp similarity index 88% rename from common/ipc/internals/eventfd.hpp rename to common/utils/eventfd.hpp index cf3094c..b3d5027 100644 --- a/common/ipc/internals/eventfd.hpp +++ b/common/utils/eventfd.hpp @@ -22,17 +22,18 @@ * @brief Eventfd wrapper */ -#ifndef COMMON_IPC_INTERNALS_EVENTFD_HPP -#define COMMON_IPC_INTERNALS_EVENTFD_HPP +#ifndef COMMON_UTILS_EVENTFD_HPP +#define COMMON_UTILS_EVENTFD_HPP namespace vasum { -namespace ipc { +namespace utils { class EventFD { public: EventFD(); ~EventFD(); + EventFD(const EventFD& eventfd) = delete; EventFD& operator=(const EventFD&) = delete; @@ -56,7 +57,7 @@ private: int mFD; }; -} // namespace ipc +} // namespace utils } // namespace vasum -#endif // COMMON_IPC_INTERNALS_EVENTFD_HPP +#endif // COMMON_UTILS_EVENTFD_HPP diff --git a/common/ipc/internals/utils.cpp b/common/utils/fd-utils.cpp similarity index 68% rename from common/ipc/internals/utils.cpp rename to common/utils/fd-utils.cpp index cb9c93c..59bad36 100644 --- a/common/ipc/internals/utils.cpp +++ b/common/utils/fd-utils.cpp @@ -19,13 +19,13 @@ /** * @file * @author Jan Olszak (j.olszak@samsung.com) - * @brief Utility functions + * @brief File descriptor utility functions */ #include "config.hpp" -#include "ipc/exception.hpp" -#include "ipc/internals/utils.hpp" +#include "utils/fd-utils.hpp" +#include "utils/exception.hpp" #include "logger/logger.hpp" #include @@ -40,7 +40,7 @@ namespace fs = boost::filesystem; namespace chr = std::chrono; namespace vasum { -namespace ipc { +namespace utils { namespace { @@ -51,13 +51,14 @@ void waitForEvent(int fd, // Wait for the rest of the data struct pollfd fds[1]; fds[0].fd = fd; - fds[0].events = event | POLLHUP; + fds[0].events = event; 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"); + LOGE("Timeout while waiting for event: " << std::hex << event << + " on fd: " << std::dec << fd); + throw UtilsException("Timeout"); } int ret = ::poll(fds, 1 /*fds size*/, timeoutMS.count()); @@ -66,13 +67,13 @@ void waitForEvent(int fd, if (errno == EINTR) { continue; } - LOGE("Error in poll: " + std::string(strerror(errno))); - throw IPCException("Error in poll: " + std::string(strerror(errno))); + LOGE("Error in poll: " + getSystemErrorMessage()); + throw UtilsException("Error in poll: " + getSystemErrorMessage()); } if (ret == 0) { LOGE("Timeout in read"); - throw IPCException("Timeout in read"); + throw UtilsException("Timeout in read"); } if (fds[0].revents & event) { @@ -82,7 +83,7 @@ void waitForEvent(int fd, if (fds[0].revents & POLLHUP) { LOGW("Peer disconnected"); - throw IPCException("Peer disconnected"); + throw UtilsException("Peer disconnected"); } } } @@ -98,11 +99,10 @@ void close(int fd) for (;;) { if (-1 == ::close(fd)) { if (errno == EINTR) { - LOGD("Close interrupted by a signal, retrying"); + LOGT("Close interrupted by a signal, retrying"); continue; } - LOGE("Error in close: " << std::string(strerror(errno))); - throw IPCException("Error in close: " + std::string(strerror(errno))); + LOGE("Error in close: " << getSystemErrorMessage()); } break; } @@ -118,22 +118,21 @@ void write(int fd, const void* bufferPtr, const size_t size, int timeoutMS) int n = ::write(fd, reinterpret_cast(bufferPtr) + nTotal, size - nTotal); - if (n > 0) { + if (n >= 0) { nTotal += n; + if (nTotal == size) { + // All data is written, break loop + break; + } } else if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) { // Neglected errors LOGD("Retrying write"); } else { - LOGE("Error during writing: " + std::string(strerror(errno))); - throw IPCException("Error during writing: " + std::string(strerror(errno))); + LOGE("Error during writing: " + getSystemErrorMessage()); + throw UtilsException("Error during writing: " + getSystemErrorMessage()); } - if (nTotal >= size) { - // All data is written, break loop - break; - } else { - waitForEvent(fd, POLLOUT, deadline); - } + waitForEvent(fd, POLLOUT, deadline); } } @@ -147,22 +146,25 @@ void read(int fd, void* bufferPtr, const size_t size, int timeoutMS) int n = ::read(fd, reinterpret_cast(bufferPtr) + nTotal, size - nTotal); - if (n > 0) { + if (n >= 0) { nTotal += n; + if (nTotal == size) { + // All data is read, break loop + break; + } + if (n == 0) { + LOGW("Peer disconnected"); + throw UtilsException("Peer disconnected"); + } } 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))); + LOGE("Error during reading: " + getSystemErrorMessage()); + throw UtilsException("Error during reading: " + getSystemErrorMessage()); } - if (nTotal >= size) { - // All data is read, break loop - break; - } else { - waitForEvent(fd, POLLIN, deadline); - } + waitForEvent(fd, POLLIN, deadline); } } @@ -170,8 +172,8 @@ unsigned int getMaxFDNumber() { struct rlimit rlim; if (-1 == getrlimit(RLIMIT_NOFILE, &rlim)) { - LOGE("Error during getrlimit: " + std::string(strerror(errno))); - throw IPCException("Error during getrlimit: " + std::string(strerror(errno))); + LOGE("Error during getrlimit: " + getSystemErrorMessage()); + throw UtilsException("Error during getrlimit: " + getSystemErrorMessage()); } return rlim.rlim_cur; } @@ -182,8 +184,8 @@ void setMaxFDNumber(unsigned int limit) rlim.rlim_cur = limit; rlim.rlim_max = limit; if (-1 == setrlimit(RLIMIT_NOFILE, &rlim)) { - LOGE("Error during setrlimit: " + std::string(strerror(errno))); - throw IPCException("Error during setrlimit: " + std::string(strerror(errno))); + LOGE("Error during setrlimit: " + getSystemErrorMessage()); + throw UtilsException("Error during setrlimit: " + getSystemErrorMessage()); } } @@ -194,6 +196,6 @@ unsigned int getFDNumber() fs::directory_iterator()); } -} // namespace ipc +} // namespace utils } // namespace vasum diff --git a/common/ipc/internals/utils.hpp b/common/utils/fd-utils.hpp similarity index 90% rename from common/ipc/internals/utils.hpp rename to common/utils/fd-utils.hpp index c3bcaf1..ac32b0a 100644 --- a/common/ipc/internals/utils.hpp +++ b/common/utils/fd-utils.hpp @@ -19,20 +19,19 @@ /** * @file * @author Jan Olszak (j.olszak@samsung.com) - * @brief Utility functions + * @brief File descriptor utility functions */ -#ifndef COMMON_IPC_INTERNALS_UTILS_HPP -#define COMMON_IPC_INTERNALS_UTILS_HPP +#ifndef COMMON_UTILS_FD_HPP +#define COMMON_UTILS_FD_HPP #include namespace vasum { -namespace ipc { +namespace utils { /** * Close the file descriptor. - * Repeat until */ void close(int fd); @@ -73,7 +72,7 @@ void setMaxFDNumber(unsigned int limit); */ unsigned int getFDNumber(); -} // namespace ipc +} // namespace utils } // namespace vasum -#endif // COMMON_IPC_INTERNALS_UTILS_HPP +#endif // COMMON_UTILS_FD_HPP diff --git a/common/utils/glib-poll-dispatcher.cpp b/common/utils/glib-poll-dispatcher.cpp new file mode 100644 index 0000000..5c66d16 --- /dev/null +++ b/common/utils/glib-poll-dispatcher.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2015 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 glib epoll dispatcher + */ + +#include "config.hpp" +#include "utils/glib-poll-dispatcher.hpp" +#include "utils/callback-wrapper.hpp" + +namespace vasum { +namespace utils { + +GlibPollDispatcher::GlibPollDispatcher(EventPoll& poll) +{ + mChannel = g_io_channel_unix_new(poll.getPollFD()); + + auto dispatchCallback = [&]() { + poll.dispatchIteration(0); + }; + + auto cCallback = [](GIOChannel*, GIOCondition, gpointer data) -> gboolean { + getCallbackFromPointer(data)(); + return TRUE; + }; + + mWatchId = g_io_add_watch_full(mChannel, + G_PRIORITY_DEFAULT, + G_IO_IN, + cCallback, + createCallbackWrapper(dispatchCallback, mGuard.spawn()), + &deleteCallbackWrapper); +} + +GlibPollDispatcher::~GlibPollDispatcher() +{ + g_source_remove(mWatchId); + g_io_channel_unref(mChannel); + // mGuard destructor will wait for full unregister of dispatchCallback +} + +} // namespace utils +} // namespace vasum diff --git a/common/utils/glib-poll-dispatcher.hpp b/common/utils/glib-poll-dispatcher.hpp new file mode 100644 index 0000000..07da0c3 --- /dev/null +++ b/common/utils/glib-poll-dispatcher.hpp @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2015 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 glib epoll dispatcher + */ + +#ifndef COMMON_UTILS_GLIB_POLL_DISPATCHER_HPP +#define COMMON_UTILS_GLIB_POLL_DISPATCHER_HPP + +#include "utils/event-poll.hpp" +#include "utils/callback-guard.hpp" + +#include + +namespace vasum { +namespace utils { + +/** + * Will dispatch poll events in glib thread + */ +class GlibPollDispatcher { +public: + GlibPollDispatcher(EventPoll& poll); + ~GlibPollDispatcher(); +private: + CallbackGuard mGuard; + GIOChannel* mChannel; + guint mWatchId; +}; + + +} // namespace utils +} // namespace vasum + +#endif // COMMON_UTILS_GLIB_POLL_DISPATCHER_HPP diff --git a/common/utils/thread-poll-dispatcher.cpp b/common/utils/thread-poll-dispatcher.cpp new file mode 100644 index 0000000..f350587 --- /dev/null +++ b/common/utils/thread-poll-dispatcher.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2015 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 Thread epoll dispatcher + */ + +#include "config.hpp" +#include "utils/thread-poll-dispatcher.hpp" + +#include + +namespace vasum { +namespace utils { + +ThreadPollDispatcher::ThreadPollDispatcher(EventPoll& poll) + : mPoll(poll) + , mThread([&]{ poll.dispatchLoop(); }) +{ + auto controlCallback = [this](int, EventPoll::Events) -> bool { + mStopEvent.receive(); + return false; // break the loop + }; + + poll.addFD(mStopEvent.getFD(), EPOLLIN, std::move(controlCallback)); +} + +ThreadPollDispatcher::~ThreadPollDispatcher() +{ + mStopEvent.send(); + mThread.join(); + mPoll.removeFD(mStopEvent.getFD()); +} + +} // namespace utils +} // namespace vasum diff --git a/common/utils/thread-poll-dispatcher.hpp b/common/utils/thread-poll-dispatcher.hpp new file mode 100644 index 0000000..e54cb4e --- /dev/null +++ b/common/utils/thread-poll-dispatcher.hpp @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2015 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 Thread epoll dispatcher + */ + +#ifndef COMMON_UTILS_THREAD_POLL_DISPATCHER_HPP +#define COMMON_UTILS_THREAD_POLL_DISPATCHER_HPP + +#include "utils/event-poll.hpp" +#include "utils/eventfd.hpp" + +#include + +namespace vasum { +namespace utils { + +/** + * Will dispatch poll events in a newly created thread + */ +class ThreadPollDispatcher { +public: + ThreadPollDispatcher(EventPoll& poll); + ~ThreadPollDispatcher(); +private: + EventPoll& mPoll; + EventFD mStopEvent; + std::thread mThread; +}; + +} // namespace utils +} // namespace vasum + +#endif // COMMON_UTILS_THREAD_POLL_DISPATCHER_HPP diff --git a/tests/unit_tests/ipc/ut-ipc.cpp b/tests/unit_tests/ipc/ut-ipc.cpp index 193eec9..fa0de22 100644 --- a/tests/unit_tests/ipc/ut-ipc.cpp +++ b/tests/unit_tests/ipc/ut-ipc.cpp @@ -592,7 +592,6 @@ BOOST_AUTO_TEST_CASE(ServiceGSource) l.set(); }; - IPCGSource::Pointer serviceGSource; Service s(SOCKET_PATH); s.setMethodHandler(1, echoCallback); @@ -622,7 +621,6 @@ BOOST_AUTO_TEST_CASE(ClientGSource) Service s(SOCKET_PATH); s.start(); - IPCGSource::Pointer clientGSource; Client c(SOCKET_PATH); c.setMethodHandler(1, echoCallback); c.setSignalHandler(2, signalHandler); diff --git a/tests/unit_tests/utils/ut-event-poll.cpp b/tests/unit_tests/utils/ut-event-poll.cpp new file mode 100644 index 0000000..e387393 --- /dev/null +++ b/tests/unit_tests/utils/ut-event-poll.cpp @@ -0,0 +1,205 @@ +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * Contact: Piotr Bartosiewicz + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + + +/** + * @file + * @author Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com) + * @brief Unit tests of event poll + */ + +#include "config.hpp" +#include "ut.hpp" + +#include "utils/event-poll.hpp" +#include "logger/logger.hpp" +#include "ipc/internals/socket.hpp" +#include "utils/latch.hpp" +#include "utils/glib-loop.hpp" +#include "utils/glib-poll-dispatcher.hpp" +#include "utils/thread-poll-dispatcher.hpp" + +#include +#include + +using namespace vasum::utils; +using namespace vasum::ipc; + +namespace { + +const int unsigned TIMEOUT = 1000; +#define ADD_EVENT(e) {EPOLL##e, #e} +const std::map EVENT_NAMES = { + ADD_EVENT(IN), + ADD_EVENT(OUT), + ADD_EVENT(ERR), + ADD_EVENT(HUP), + ADD_EVENT(RDHUP), +}; +#undef ADD_EVENT + +std::string strEvents(EventPoll::Events events) +{ + if (events == 0) { + return ""; + } + std::ostringstream ss; + for (const auto& p : EVENT_NAMES) { + if (events & p.first) { + ss << p.second << ", "; + events &= ~p.first; + } + } + if (events != 0) { + ss << std::hex << events; + return ss.str(); + } else { + std::string ret = ss.str(); + ret.resize(ret.size() - 2); + return ret; + } +} + +} // namespace + +BOOST_AUTO_TEST_SUITE(EventPollSuite) + +BOOST_AUTO_TEST_CASE(EmptyPoll) +{ + EventPoll poll; + BOOST_CHECK(!poll.dispatchIteration(0)); +} + +BOOST_AUTO_TEST_CASE(ThreadedPoll) +{ + EventPoll poll; + ThreadPollDispatcher dispatcher(poll); +} + +BOOST_AUTO_TEST_CASE(GlibPoll) +{ + ScopedGlibLoop loop; + + EventPoll poll; + GlibPollDispatcher dispatcher(poll); +} + +void doSocketTest(EventPoll& poll, Latch& goodMessage, Latch& remoteClosed) +{ + const std::string PATH = "/tmp/ut-poll.sock"; + const std::string MESSAGE = "This is a test message"; + + Socket listen = Socket::createSocket(PATH); + std::shared_ptr server; + + auto serverCallback = [&](int, EventPoll::Events events) -> bool { + LOGD("Server events: " << strEvents(events)); + + if (events & EPOLLOUT) { + server->write(MESSAGE.data(), MESSAGE.size()); + poll.removeFD(server->getFD()); + server.reset(); + } + return true; + }; + + auto listenCallback = [&](int, EventPoll::Events events) -> bool { + LOGD("Listen events: " << strEvents(events)); + if (events & EPOLLIN) { + server = listen.accept(); + poll.addFD(server->getFD(), EPOLLHUP | EPOLLRDHUP | EPOLLOUT, serverCallback); + } + return true; + }; + + poll.addFD(listen.getFD(), EPOLLIN, listenCallback); + + Socket client = Socket::connectSocket(PATH); + + auto clientCallback = [&](int, EventPoll::Events events) -> bool { + LOGD("Client events: " << strEvents(events)); + + if (events & EPOLLIN) { + std::string ret(MESSAGE.size(), 'x'); + client.read(&ret.front(), ret.size()); + if (ret == MESSAGE) { + goodMessage.set(); + } + } + if (events & EPOLLRDHUP) { + poll.removeFD(client.getFD()); + remoteClosed.set(); + } + return true; + }; + + poll.addFD(client.getFD(), EPOLLHUP | EPOLLRDHUP | EPOLLIN, clientCallback); + + BOOST_CHECK(goodMessage.wait(TIMEOUT)); + BOOST_CHECK(remoteClosed.wait(TIMEOUT)); + + poll.removeFD(listen.getFD()); +} + +BOOST_AUTO_TEST_CASE(ThreadedPollSocket) +{ + Latch goodMessage; + Latch remoteClosed; + + EventPoll poll; + ThreadPollDispatcher dispatcher(poll); + + doSocketTest(poll, goodMessage, remoteClosed); +} + +BOOST_AUTO_TEST_CASE(GlibPollSocket) +{ + Latch goodMessage; + Latch remoteClosed; + + ScopedGlibLoop loop; + + EventPoll poll; + GlibPollDispatcher dispatcher(poll); + + doSocketTest(poll, goodMessage, remoteClosed); +} + +BOOST_AUTO_TEST_CASE(PollStacking) +{ + Latch goodMessage; + Latch remoteClosed; + + EventPoll outer; + EventPoll inner; + + auto dispatchInner = [&](int, EventPoll::Events) -> bool { + inner.dispatchIteration(0); + return true; + }; + + outer.addFD(inner.getPollFD(), EPOLLIN, dispatchInner); + + ThreadPollDispatcher dispatcher(outer); + doSocketTest(inner, goodMessage, remoteClosed); + + outer.removeFD(inner.getPollFD()); +} + +BOOST_AUTO_TEST_SUITE_END() + -- 2.7.4 From a2bfdea7f5bb1e2126701e0cf7d691f326afa9c4 Mon Sep 17 00:00:00 2001 From: Mateusz Malicki Date: Thu, 12 Feb 2015 16:39:58 +0100 Subject: [PATCH 02/16] Creating netdev [Feature] Filled stubs for creating netdev (create_netdev_phys, create_netdev_macvlan, create_netdev_veth) [Cause] N/A [Solution] Manage netdev through netlink interface [Verification] Build, install, run vasum-server, create zone (vasum-cli create_zone zone1), start zone (vasum-cli start_zone zone1), move interface f.e. p2p1 to zone (vasum-cli create_netdev_phys zone1 p2p1) enter to zone (lxc-console -n zone1 --lxcpath=/usr/local/share/.zones -t 0) check interfaces (ifconfig -a) do analogous for create_netdev_macvlan and create_netdev_veth Change-Id: I299d3ebeb8f101a386f5b156dbe79d7779600ef6 --- common/lxc/zone.cpp | 5 + common/lxc/zone.hpp | 7 ++ common/netlink/netlink-message.cpp | 164 +++++++++++++++++++++++++ common/netlink/netlink-message.hpp | 116 ++++++++++++++++++ common/netlink/netlink.cpp | 155 ++++++++++++++++++++++++ common/netlink/netlink.hpp | 79 ++++++++++++ server/netdev.cpp | 242 +++++++++++++++++++++++++++++++++++++ server/netdev.hpp | 45 +++++++ server/zone-admin.cpp | 19 +-- 9 files changed, 823 insertions(+), 9 deletions(-) create mode 100644 common/netlink/netlink-message.cpp create mode 100644 common/netlink/netlink-message.hpp create mode 100644 common/netlink/netlink.cpp create mode 100644 common/netlink/netlink.hpp create mode 100644 server/netdev.cpp create mode 100644 server/netdev.hpp diff --git a/common/lxc/zone.cpp b/common/lxc/zone.cpp index dc148e4..bbb1e02 100644 --- a/common/lxc/zone.cpp +++ b/common/lxc/zone.cpp @@ -313,6 +313,11 @@ bool LxcZone::waitForState(State state, int timeout) return true; } +pid_t LxcZone::getInitPid() const +{ + return mLxcContainer->init_pid(mLxcContainer); +} + bool LxcZone::setRunLevel(int runLevel) { auto callback = [](void* param) -> int { diff --git a/common/lxc/zone.hpp b/common/lxc/zone.hpp index 1573c7d..0ed19a3 100644 --- a/common/lxc/zone.hpp +++ b/common/lxc/zone.hpp @@ -26,6 +26,7 @@ #define COMMON_LXC_ZONE_HPP #include +#include // fwd declaration of lxc internals struct lxc_container; @@ -136,6 +137,12 @@ public: * Unfreeze zone */ bool unfreeze(); + + /** + * Get pid of init process + */ + pid_t getInitPid() const; + private: lxc_container* mLxcContainer; diff --git a/common/netlink/netlink-message.cpp b/common/netlink/netlink-message.cpp new file mode 100644 index 0000000..72d3597 --- /dev/null +++ b/common/netlink/netlink-message.cpp @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * Contact: Mateusz Malicki + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +/** + * @file + * @author Mateusz Malicki (m.malicki2@samsung.com) + * @brief Netlink message class definition + */ + +#include "config.hpp" +#include "netlink-message.hpp" +#include "netlink.hpp" +#include "base-exception.hpp" + +#include + +#include +#include +#include + +#include +#include +#include +#include + +#ifndef PAGE_SIZE +#define PAGE_SIZE 4096 +#endif + +const int NLMSG_GOOD_SIZE = 2*PAGE_SIZE; +constexpr rtattr* NLMSG_TAIL(nlmsghdr* nmsg) +{ + return reinterpret_cast(reinterpret_cast(nmsg) + NLMSG_ALIGN(nmsg->nlmsg_len)); +} + +namespace vasum { +namespace netlink { + +NetlinkMessage::NetlinkMessage(uint16_t type, uint16_t flags) +{ + static uint32_t seq = 0; + mNlmsg.resize(NLMSG_GOOD_SIZE, 0); + hdr().nlmsg_len = NLMSG_HDRLEN; + hdr().nlmsg_flags = flags | NLM_F_ACK; + hdr().nlmsg_type = type; + hdr().nlmsg_seq = ++seq; + hdr().nlmsg_pid = getpid(); +} + +NetlinkMessage& NetlinkMessage::beginNested(int ifla) +{ + struct rtattr *nest = NLMSG_TAIL(&hdr()); + put(ifla, NULL, 0); + mNested.push(nest); + return *this; +} + +NetlinkMessage& NetlinkMessage::endNested() +{ + assert(!mNested.empty()); + rtattr *nest = reinterpret_cast(mNested.top()); + nest->rta_len = std::distance(reinterpret_cast(nest), + reinterpret_cast(NLMSG_TAIL(&hdr()))); + mNested.pop(); + return *this; +} + +NetlinkMessage& NetlinkMessage::put(int ifla, const std::string& value) +{ + return put(ifla, value.c_str(), value.size() + 1); +} + +NetlinkMessage& NetlinkMessage::put(int ifla, const void* data, int len) +{ + struct rtattr *rta; + size_t rtalen = RTA_LENGTH(len); + int newLen = NLMSG_ALIGN(hdr().nlmsg_len) + RTA_ALIGN(rtalen); + + setMinCapacity(newLen); + rta = NLMSG_TAIL(&hdr()); + rta->rta_type = ifla; + rta->rta_len = rtalen; + memcpy(RTA_DATA(rta), data, len); + hdr().nlmsg_len = newLen; + return *this; +} + +NetlinkMessage& NetlinkMessage::put(const void* data, int len) +{ + setMinCapacity(hdr().nlmsg_len + len); + memcpy((reinterpret_cast(&hdr()) + hdr().nlmsg_len), data, len); + hdr().nlmsg_len += len; + return *this; +} + +nlmsghdr& NetlinkMessage::hdr() { + return *reinterpret_cast(mNlmsg.data()); +} + +const nlmsghdr& NetlinkMessage::hdr() const { + return *reinterpret_cast(mNlmsg.data()); +} + +void NetlinkMessage::setMinCapacity(unsigned int size) +{ + if (mNlmsg.size() < size) { + mNlmsg.resize(size, 0); + } +} + +void send(const NetlinkMessage& msg) +{ + //TODO: Handle messages with responses + assert(msg.hdr().nlmsg_flags & NLM_F_ACK); + + const int answerLen = NLMSG_ALIGN(msg.hdr().nlmsg_len + sizeof(nlmsgerr)); + std::unique_ptr answerBuff(new char[answerLen]); + nlmsghdr* answer = reinterpret_cast(answerBuff.get()); + answer->nlmsg_len = answerLen; + + Netlink nl; + nl.open(); + try { + nl.send(&msg.hdr()); + //Receive ACK Netlink Message + do { + nl.rcv(answer); + } while (answer->nlmsg_type == NLMSG_NOOP); + } catch (const std::exception& ex) { + LOGE("Sending failed (" << ex.what() << ")"); + nl.close(); + throw; + } + nl.close(); + if (answer->nlmsg_type != NLMSG_ERROR) { + // It is not NACK/ACK message + throw VasumException("Sending failed ( unrecognized message type )"); + } + nlmsgerr *err = reinterpret_cast(NLMSG_DATA(answer)); + if (answer->nlmsg_seq != msg.hdr().nlmsg_seq) { + throw VasumException("Sending failed ( answer message was mismatched )"); + } + if (err->error) { + throw VasumException("Sending failed (" + getSystemErrorMessage(-err->error) + ")"); + } +} + +} // namespace netlink +} // namespace vasum diff --git a/common/netlink/netlink-message.hpp b/common/netlink/netlink-message.hpp new file mode 100644 index 0000000..e828feb --- /dev/null +++ b/common/netlink/netlink-message.hpp @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * Contact: Mateusz Malicki + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +/** + * @file + * @author Mateusz Malicki (m.malicki2@samsung.com) + * @brief Netlink message class declaration + */ + +#ifndef COMMON_NETLINK_NETLINK_MESSAGE_HPP +#define COMMON_NETLINK_NETLINK_MESSAGE_HPP + +#include +#include +#include +#include +#include +#include + +namespace vasum { +namespace netlink { + +/** + * NetlinkMessage is used to creatie a netlink messages + */ +class NetlinkMessage { +public: + /** + * Create netlink message + * + * @param type rtnetlink message type (see man 7 rtnetlink) + * @param flags nlmsg flags (see man 7 netlink) + */ + NetlinkMessage(std::uint16_t type, std::uint16_t flags); + + /** + * Add nested atribute type + * + * All future attributes will be nested in this attribute (till call to endNested) + * + * @param ifla attribute name + */ + NetlinkMessage& beginNested(int ifla); + + /** + * End nested atribute + */ + NetlinkMessage& endNested(); + + ///@{ + /*Add sample attribute */ + NetlinkMessage& put(int ifla, const std::string& value); + template + NetlinkMessage& put(int ifla, const T& value); + ///@} + + /** + * Add raw data + * + * Add raw data to end of netlink message + */ + template + NetlinkMessage& put(const T& value); + + /** + * Send netlink message + * + * It is not thread safe + */ + friend void send(const NetlinkMessage& msg); +private: + std::vector mNlmsg; + std::stack mNested; + + NetlinkMessage& put(int ifla, const void* data, int len); + NetlinkMessage& put(const void* data, int len); + nlmsghdr& hdr(); + const nlmsghdr& hdr() const; + void setMinCapacity(unsigned int size); + + +}; + +template +NetlinkMessage& NetlinkMessage::put(int ifla, const T& value) +{ + static_assert(std::is_pod::value, "Require trivial and standard-layout"); + return put(ifla, &value, sizeof(value)); +} + +template +NetlinkMessage& NetlinkMessage::put(const T& value) +{ + static_assert(std::is_pod::value, "Require trivial and standard-layout structure"); + return put(&value, sizeof(value)); +} + +} // namespace netlink +} // namespace vasum + +#endif // COMMON_NETLINK_NETLINK_MESSAGE_HPP diff --git a/common/netlink/netlink.cpp b/common/netlink/netlink.cpp new file mode 100644 index 0000000..fa86b45 --- /dev/null +++ b/common/netlink/netlink.cpp @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * Contact: Mateusz Malicki + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +/** + * @file + * @author Mateusz Malicki (m.malicki2@samsung.com) + * @brief Netlink class definition + */ + +#include "config.hpp" +#include "netlink.hpp" +#include "utils.hpp" +#include "base-exception.hpp" + +#include +#include +#include +#include +#include +#include + +namespace vasum { + +namespace { + +template +T make_clean() +{ + static_assert(std::is_pod::value, "make_clean require trivial and standard-layout"); + T value; + std::fill_n(reinterpret_cast(&value), sizeof(value), 0); + return value; +} + +} // namespace + +Netlink::Netlink() : mFd(-1) +{ +} + +Netlink::~Netlink() +{ + close(); +} + +void Netlink::open() +{ + assert(mFd == -1); + mFd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (mFd == -1) { + LOGE("Can't open socket (" << getSystemErrorMessage() << ")"); + throw VasumException("Can't open netlink connection"); + } + + sockaddr_nl local = make_clean(); + local.nl_family = AF_NETLINK; + + if (bind(mFd, (struct sockaddr *)&local, sizeof(local)) < 0) { + int err = errno; + close(); + LOGE("Can't bind to socket (" << getSystemErrorMessage(err) << ")"); + throw VasumException("Can't set up netlink connection"); + } +} + +void Netlink::close() +{ + if (mFd != -1) { + ::close(mFd); + mFd = -1; + } +} + +void Netlink::send(const nlmsghdr *nlmsg) +{ + msghdr msg = make_clean(); + sockaddr_nl nladdr = make_clean(); + iovec iov = make_clean(); + + iov.iov_base = (void *)nlmsg; + iov.iov_len = nlmsg->nlmsg_len; + msg.msg_name = &nladdr; + msg.msg_namelen = sizeof(nladdr); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + nladdr.nl_family = AF_NETLINK; + + int ret = sendmsg(mFd, &msg, 0); + if (ret < 0) { + LOGE("Can't send message (" << getSystemErrorMessage() << ")"); + throw VasumException("Can't send netlink message"); + } +} + +int Netlink::rcv(nlmsghdr *answer) +{ + //TODO: Handle too small buffer situation (buffer resizing) + msghdr msg = make_clean(); + sockaddr_nl nladdr = make_clean(); + iovec iov = make_clean(); + + iov.iov_base = answer; + iov.iov_len = answer->nlmsg_len; + msg.msg_name = &nladdr; + msg.msg_namelen = sizeof(nladdr); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + nladdr.nl_family = AF_NETLINK; + + int ret = recvmsg(mFd, &msg, 0); + if (ret < 0) { + LOGE("Can't receive message (" + getSystemErrorMessage() + ")"); + throw VasumException("Can't receive netlink message"); + } + if (ret == 0) { + LOGE("Peer has performed an orderly shutdown"); + throw VasumException("Can't receive netlink message"); + } + if (msg.msg_flags & MSG_TRUNC) { + LOGE("Can't receive message (" + getSystemErrorMessage(EMSGSIZE) + ")"); + throw VasumException("Can't receive netlink message"); + } + if (msg.msg_flags & MSG_ERRQUEUE) { + LOGE("No data was received but an extended error"); + throw VasumException("Can't receive netlink message"); + } + if (msg.msg_flags & MSG_OOB) { + LOGE("Internal error (expedited or out-of-band data were received)"); + throw VasumException("Can't receive netlink message"); + } + if (msg.msg_flags & (MSG_EOR | MSG_CTRUNC)) { + assert(!"This should not happen!"); + LOGE("Internal error (" << std::to_string(msg.msg_flags) << ")"); + throw VasumException("Internal error while recaiving netlink message"); + } + + return ret; +} + +} //namespace vasum diff --git a/common/netlink/netlink.hpp b/common/netlink/netlink.hpp new file mode 100644 index 0000000..2e73ce8 --- /dev/null +++ b/common/netlink/netlink.hpp @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * Contact: Mateusz Malicki + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +/** + * @file + * @author Mateusz Malicki (m.malicki2@samsung.com) + * @brief Netlink class declaration + */ + +#ifndef COMMON_NETLINK_NETLINK_HPP +#define COMMON_NETLINK_NETLINK_HPP + +#include + +namespace vasum { + +/** + * Netlink class is responsible for communicating + * with kernel through netlink interface + */ +class Netlink { +public: + Netlink(); + ~Netlink(); + + Netlink(const Netlink& other) = delete; + Netlink& operator=(const Netlink& other) = delete; + + /** + * Open connnection + */ + void open(); + + /** + * Close connection + */ + void close(); + + /** + * Send message + * + * It is not thread safe and even you shouldn't call this function on + * different instances at the same time + * + * @param nlmsg pointer to message + */ + void send(const nlmsghdr *nlmsg); + + /** + * Receive message + * + * It is not thread safe and even you shouldn't call this function on + * different instances at the same time + * + * @param answer pointer to answer buffer + */ + int rcv(nlmsghdr *answer); +private: + int mFd; +}; + +} // namespace vasum + +#endif /* COMMON_NETLINK_NETLINK_HPP */ diff --git a/server/netdev.cpp b/server/netdev.cpp new file mode 100644 index 0000000..b4a2d21 --- /dev/null +++ b/server/netdev.cpp @@ -0,0 +1,242 @@ +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * Contact: Mateusz Malicki + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +/** + * @file + * @author Mateusz Malicki (m.malicki2@samsung.com) + * @brief Network devices management functions definition + */ + +#include "config.hpp" +#include "netdev.hpp" +#include "netlink/netlink-message.hpp" +#include "utils.hpp" +#include "exception.hpp" + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace vasum; +using namespace vasum::netlink; + +namespace vasum { +namespace netdev { + +namespace { + +template +T make_clean() +{ + static_assert(std::is_pod::value, "make_clean require trivial and standard-layout"); + T value; + std::fill_n(reinterpret_cast(&value), sizeof(value), 0); + return value; +} + +string getUniqueVethName() +{ + auto find = [](const ifaddrs* ifaddr, const string& name) -> bool { + for (const ifaddrs* ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { + if (name == ifa->ifa_name) { + return true; + } + } + return false; + }; + + ifaddrs* ifaddr; + getifaddrs(&ifaddr); + string newName; + int i = 0; + do { + newName = "veth0" + to_string(++i); + } while (find(ifaddr, newName)); + + freeifaddrs(ifaddr); + return newName; +} + +uint32_t getInterfaceIndex(const string& name) { + uint32_t index = if_nametoindex(name.c_str()); + if (!index) { + LOGE("Can't get " << name << " interface index (" << getSystemErrorMessage() << ")"); + throw ZoneOperationException("Can't find interface"); + } + return index; +} + +void validateNetdevName(const string& name) +{ + if (name.size() <= 1 || name.size() >= IFNAMSIZ) { + throw ZoneOperationException("Invalid netdev name format"); + } +} + +void createPipedNetdev(const string& netdev1, const string& netdev2) +{ + validateNetdevName(netdev1); + validateNetdevName(netdev2); + + NetlinkMessage nlm(RTM_NEWLINK, NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK); + ifinfomsg infoPeer = make_clean(); + infoPeer.ifi_family = AF_UNSPEC; + infoPeer.ifi_change = 0xFFFFFFFF; + nlm.put(infoPeer) + .beginNested(IFLA_LINKINFO) + .put(IFLA_INFO_KIND, "veth") + .beginNested(IFLA_INFO_DATA) + .beginNested(VETH_INFO_PEER) + .put(infoPeer) + .put(IFLA_IFNAME, netdev2) + .endNested() + .endNested() + .endNested() + .put(IFLA_IFNAME, netdev1); + send(nlm); +} + +void attachToBridge(const string& bridge, const string& netdev) +{ + validateNetdevName(bridge); + validateNetdevName(netdev); + + uint32_t index = getInterfaceIndex(netdev); + int fd = socket(AF_LOCAL, SOCK_STREAM, 0); + if (fd < 0) { + LOGE("Can't open socket (" << getSystemErrorMessage() << ")"); + throw ZoneOperationException("Can't attach to bridge"); + } + + struct ifreq ifr = make_clean(); + strncpy(ifr.ifr_name, bridge.c_str(), IFNAMSIZ); + ifr.ifr_ifindex = index; + int err = ioctl(fd, SIOCBRADDIF, &ifr); + if (err < 0) { + int error = errno; + //TODO: Close can be interrupted. Move util functions from ipc + ::close(fd); + LOGE("Can't attach to bridge (" + getSystemErrorMessage(error) + ")"); + throw ZoneOperationException("Can't attach to bridge"); + } + close(fd); +} + +int setFlags(const string& name, uint32_t mask, uint32_t flags) +{ + uint32_t index = getInterfaceIndex(name); + NetlinkMessage nlm(RTM_NEWLINK, NLM_F_REQUEST | NLM_F_ACK); + ifinfomsg infoPeer = make_clean(); + infoPeer.ifi_family = AF_UNSPEC; + infoPeer.ifi_index = index; + infoPeer.ifi_flags = flags; + // since kernel v2.6.22 ifi_change is used to change only selected flags; + infoPeer.ifi_change = mask; + nlm.put(infoPeer); + send(nlm); + return 0; +} + +void up(const string& netdev) +{ + setFlags(netdev, IFF_UP, IFF_UP); +} + +void moveToNS(const string& netdev, pid_t pid) +{ + uint32_t index = getInterfaceIndex(netdev); + NetlinkMessage nlm(RTM_NEWLINK, NLM_F_REQUEST | NLM_F_ACK); + ifinfomsg infopeer = make_clean(); + infopeer.ifi_family = AF_UNSPEC; + infopeer.ifi_index = index; + nlm.put(infopeer) + .put(IFLA_NET_NS_PID, pid); + send(nlm); +} + +void createMacvlan(const string& master, const string& slave, const macvlan_mode& mode) +{ + validateNetdevName(master); + validateNetdevName(slave); + + uint32_t index = getInterfaceIndex(master); + NetlinkMessage nlm(RTM_NEWLINK, NLM_F_REQUEST|NLM_F_CREATE|NLM_F_EXCL|NLM_F_ACK); + ifinfomsg infopeer = make_clean(); + infopeer.ifi_family = AF_UNSPEC; + infopeer.ifi_change = 0xFFFFFFFF; + nlm.put(infopeer) + .beginNested(IFLA_LINKINFO) + .put(IFLA_INFO_KIND, "macvlan") + .beginNested(IFLA_INFO_DATA) + .put(IFLA_MACVLAN_MODE, static_cast(mode)) + .endNested() + .endNested() + .put(IFLA_LINK, index) + .put(IFLA_IFNAME, slave); + send(nlm); +} + +} // namespace + +void createVeth(const pid_t& nsPid, const string& nsDev, const string& hostDev) +{ + string hostVeth = getUniqueVethName(); + LOGT("Creating veth: bridge: " << hostDev << ", port: " << hostVeth << ", zone: " << nsDev); + createPipedNetdev(nsDev, hostVeth); + //TODO: clean up if following instructions fail + attachToBridge(hostDev, hostVeth); + up(hostVeth); + moveToNS(nsDev, nsPid); +} + +void createMacvlan(const pid_t& nsPid, + const string& nsDev, + const string& hostDev, + const macvlan_mode& mode) +{ + LOGT("Creating macvlan: host: " << hostDev << ", zone: " << nsDev << ", mode: " << mode); + createMacvlan(hostDev, nsDev, mode); + //TODO: clean up if following instructions fail + up(nsDev); + moveToNS(nsDev, nsPid); +} + +void movePhys(const pid_t& nsPid, const string& devId) +{ + LOGT("Creating phys: dev: " << devId); + moveToNS(devId, nsPid); +} + +} //namespace netdev +} //namespace vasum + diff --git a/server/netdev.hpp b/server/netdev.hpp new file mode 100644 index 0000000..de761ca --- /dev/null +++ b/server/netdev.hpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * Contact: Mateusz Malicki + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +/** + * @file + * @author Mateusz Malicki (m.malicki2@samsung.com) + * @brief Network devices management functions declaration + */ + +#ifndef SERVER_NETDEV_HPP +#define SERVER_NETDEV_HPP + +#include +#include +#include + +namespace vasum { +namespace netdev { + +void createVeth(const pid_t& nsPid, const std::string& nsDev, const std::string& hostDev); +void createMacvlan(const pid_t& nsPid, + const std::string& nsDev, + const std::string& hostDev, + const macvlan_mode& mode); +void movePhys(const pid_t& nsPid, const std::string& devId); + +} //namespace netdev +} //namespace vasum + +#endif // SERVER_NETDEV_HPP diff --git a/server/zone-admin.cpp b/server/zone-admin.cpp index c5e994d..5a4adfa 100644 --- a/server/zone-admin.cpp +++ b/server/zone-admin.cpp @@ -26,6 +26,7 @@ #include "zone-admin.hpp" #include "exception.hpp" +#include "netdev.hpp" #include "logger/logger.hpp" #include "utils/paths.hpp" @@ -270,22 +271,22 @@ std::int64_t ZoneAdmin::getSchedulerQuota() return std::stoll(ret); } -void ZoneAdmin::createNetdevVeth(const std::string& /* zoneDev */, - const std::string& /* hostDev */) +void ZoneAdmin::createNetdevVeth(const std::string& zoneDev, + const std::string& hostDev) { - throw ZoneOperationException("Not implemented"); + netdev::createVeth(mZone.getInitPid(), zoneDev, hostDev); } -void ZoneAdmin::createNetdevMacvlan(const std::string& /* zoneDev */, - const std::string& /* hostDev */, - const uint32_t& /* mode */) +void ZoneAdmin::createNetdevMacvlan(const std::string& zoneDev, + const std::string& hostDev, + const uint32_t& mode) { - throw ZoneOperationException("Not implemented"); + netdev::createMacvlan(mZone.getInitPid(), zoneDev, hostDev, static_cast(mode)); } -void ZoneAdmin::moveNetdev(const std::string& /* devId */) +void ZoneAdmin::moveNetdev(const std::string& devId) { - throw ZoneOperationException("Not implemented"); + netdev::movePhys(mZone.getInitPid(), devId); } } // namespace vasum -- 2.7.4 From d8256e79f51e16d2d35fbf3b4c12f714cc47e215 Mon Sep 17 00:00:00 2001 From: Mateusz Malicki Date: Thu, 19 Feb 2015 15:44:34 +0100 Subject: [PATCH 03/16] Stubs for get/set ipv4/ipv6, destroy netdev and list netdev [Feature] Stubs (cli, client, server) for get/set ipv4/ipv6, destroy netdev and list netdev netdev [Cause] N/A [Solution] N/A [Verification] Build, run cli commnads Change-Id: Ia1dc21e5fb252b2f83d617abec785bce7d6ffc9e --- cli/command-line-interface.cpp | 129 +++++++++++++++++++++++ cli/command-line-interface.hpp | 49 +++++++++ cli/main.cpp | 66 ++++++++++++ client/CMakeLists.txt | 4 +- client/vasum-client-impl.cpp | 205 ++++++++++++++++++++++++++++++++++--- client/vasum-client-impl.hpp | 10 ++ client/vasum-client.cpp | 14 +++ client/vasum-client.h | 25 +++++ common/netlink/netlink-message.cpp | 6 +- server/host-connection.cpp | 53 ++++++++++ server/host-connection.hpp | 36 ++++++- server/host-dbus-definitions.hpp | 17 +++ server/zone-admin.cpp | 15 +++ server/zone-admin.hpp | 16 +++ server/zone.cpp | 18 ++++ server/zone.hpp | 15 +++ server/zones-manager.cpp | 87 +++++++++++++++- server/zones-manager.hpp | 9 ++ 18 files changed, 754 insertions(+), 20 deletions(-) diff --git a/cli/command-line-interface.cpp b/cli/command-line-interface.cpp index a189a92..77e9dc5 100644 --- a/cli/command-line-interface.cpp +++ b/cli/command-line-interface.cpp @@ -38,6 +38,7 @@ #include #include #include +#include using namespace std; @@ -394,5 +395,133 @@ void create_netdev_phys(int pos, int argc, const char** argv) argv[pos + 2])); } +void zone_get_netdevs(int pos, int argc, const char** argv) +{ + using namespace std::placeholders; + + if (argc <= pos + 1) { + throw runtime_error("Not enough parameters"); + } + VsmArrayString ids; + one_shot(bind(vsm_zone_get_netdevs, + _1, + argv[pos + 1], + &ids)); + string delim; + for (VsmString* id = ids; *id; ++id) { + cout << delim << *id; + delim = ", "; + } + if (delim.empty()) { + cout << "There is no network device in zone"; + } + cout << endl; + vsm_array_string_free(ids); +} + +void netdev_get_ipv4_addr(int pos, int argc, const char** argv) +{ + using namespace std::placeholders; + + if (argc <= pos + 2) { + throw runtime_error("Not enough parameters"); + } + in_addr addr; + one_shot(bind(vsm_netdev_get_ipv4_addr, + _1, + argv[pos + 1], + argv[pos + 2], + &addr)); + char buf[INET_ADDRSTRLEN]; + if (inet_ntop(AF_INET, &addr, buf, INET_ADDRSTRLEN) == NULL) { + throw runtime_error("Server gave the wrong address format"); + } + cout << buf << endl; +} + +void netdev_get_ipv6_addr(int pos, int argc, const char** argv) +{ + using namespace std::placeholders; + + if (argc <= pos + 2) { + throw runtime_error("Not enough parameters"); + } + in6_addr addr; + one_shot(bind(vsm_netdev_get_ipv6_addr, + _1, + argv[pos + 1], + argv[pos + 2], + &addr)); + char buf[INET6_ADDRSTRLEN]; + if (inet_ntop(AF_INET6, &addr, buf, INET6_ADDRSTRLEN) == NULL) { + throw runtime_error("Server gave the wrong address format"); + } + cout << buf << endl; +} + +void netdev_set_ipv4_addr(int pos, int argc, const char** argv) +{ + using namespace std::placeholders; + + if (argc <= pos + 4) { + throw runtime_error("Not enough parameters"); + } + in_addr addr; + if (inet_pton(AF_INET, argv[pos + 3], &addr) != 1) { + throw runtime_error("Wrong address format"); + }; + one_shot(bind(vsm_netdev_set_ipv4_addr, + _1, + argv[pos + 1], + argv[pos + 2], + &addr, + stoi(argv[pos + 4]))); +} + +void netdev_set_ipv6_addr(int pos, int argc, const char** argv) +{ + using namespace std::placeholders; + + if (argc <= pos + 4) { + throw runtime_error("Not enough parameters"); + } + in6_addr addr; + if (inet_pton(AF_INET6, argv[pos + 3], &addr) != 1) { + throw runtime_error("Wrong address format"); + }; + one_shot(bind(vsm_netdev_set_ipv6_addr, + _1, + argv[pos + 1], + argv[pos + 2], + &addr, + stoi(argv[pos + 4]))); +} + +void netdev_up(int pos, int argc, const char** argv) +{ + using namespace std::placeholders; + + if (argc <= pos + 2) { + throw runtime_error("Not enough parameters"); + } + one_shot(bind(vsm_netdev_up, + _1, + argv[pos + 1], + argv[pos + 2])); +} + +void netdev_down(int pos, int argc, const char** argv) +{ + using namespace std::placeholders; + + if (argc <= pos + 2) { + throw runtime_error("Not enough parameters"); + } + one_shot(bind(vsm_netdev_down, + _1, + argv[pos + 1], + argv[pos + 2])); +} + } // namespace cli } // namespace vasum diff --git a/cli/command-line-interface.hpp b/cli/command-line-interface.hpp index 34ac013..eb33428 100644 --- a/cli/command-line-interface.hpp +++ b/cli/command-line-interface.hpp @@ -208,6 +208,55 @@ void create_netdev_macvlan(int pos, int argc, const char** argv); */ void create_netdev_phys(int pos, int argc, const char** argv); +/** + * Parses command line arguments and prints result of vsm_zone_get_netdevs + * + * @see vsm_zone_get_netdevs + */ +void zone_get_netdevs(int pos, int argc, const char** argv); + +/** + * Parses command line arguments and prints result of vsm_netdev_get_ipv4_addr + * + * @see vsm_netdev_get_ipv4_addr + */ +void netdev_get_ipv4_addr(int pos, int argc, const char** argv); + +/** + * Parses command line arguments and and prints result of vsm_netdev_get_ipv6_addr + * + * @see vsm_netdev_get_ipv6_addr + */ +void netdev_get_ipv6_addr(int pos, int argc, const char** argv); + +/** + * Parses command line arguments and call vsm_netdev_set_ipv4_addr + * + * @see vsm_netdev_set_ipv4_addr + */ +void netdev_set_ipv4_addr(int pos, int argc, const char** argv); + +/** + * Parses command line arguments and call vsm_netdev_set_ipv6_addr + * + * @see vsm_netdev_set_ipv6_addr + */ +void netdev_set_ipv6_addr(int pos, int argc, const char** argv); + +/** + * Parses command line arguments and call vsm_netdev_up + * + * @see vsm_netdev_up + */ +void netdev_up(int pos, int argc, const char** argv); + +/** + * Parses command line arguments and call vsm_netdev_down + * + * @see vsm_netdev_down + */ +void netdev_down(int pos, int argc, const char** argv); + } // namespace cli } // namespace vasum diff --git a/cli/main.cpp b/cli/main.cpp index 38a8ae5..5f073be 100644 --- a/cli/main.cpp +++ b/cli/main.cpp @@ -170,6 +170,72 @@ std::map commands = { {{"zone_id", "id zone name"}, {"devId", "network device id"}} } + }, + { + "zone_get_netdevs", { + zone_get_netdevs, + "zone_get_netdevs zone_id", + "List network devices in the zone", + {{"zone_id", "id zone name"}} + } + }, + { + "netdev_get_ipv4_addr", { + netdev_get_ipv4_addr, + "netdev_get_ipv4_addr zone_id netdev_id", + "Get ipv4 address", + {{"zone_id", "id zone name"}, + {"netdev_id", "network device name"}} + } + }, + { + "netdev_get_ipv6_addr", { + netdev_get_ipv6_addr, + "netdev_get_ipv6_addr zone_id netdev_id", + "Get ipv6 address", + {{"zone_id", "id zone name"}, + {"netdev_id", "network device name"}} + } + }, + { + "netdev_set_ipv4_addr", { + netdev_set_ipv4_addr, + "netdev_set_ipv4_addr zone_id netdev_id address prefix_len", + "Set ipv4 address", + {{"zone_id", "id zone name"}, + {"netdev_id", "network device name"}, + {"address", "ipv4 address"}, + {"prefix_len", "bit length of prefix"}} + } + }, + { + "netdev_set_ipv6_addr", { + netdev_set_ipv6_addr, + "netdev_set_ipv6_addr zone_id netdev_id address prefix_len", + "Set ipv6 address", + {{"zone_id", "id zone name"}, + {"netdev_id", "network device name"}, + {"address", "ipv6 address"}, + {"prefix_len", "bit length of prefix"}} + } + }, + { + "netdev_up", { + netdev_up, + "netdev_up zone_id netdev_id", + "Turn up a network device in the zone", + {{"zone_id", "id zone name"}, + {"netdev_id", "network device id"}} + } + }, + { + "zone_get_netdevs", { + zone_get_netdevs, + "zone_get_netdevs zone_id", + "Turn down a network device in the zone", + {{"zone_id", "id zone name"}, + {"netdev_id", "network device id"}} + } } }; diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 7060b7b..558f3a7 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -23,7 +23,9 @@ FILE(GLOB project_SRCS *.cpp *.hpp *.h) FILE(GLOB common_SRCS ${COMMON_FOLDER}/utils/callback-guard.hpp ${COMMON_FOLDER}/utils/callback-guard.cpp ${COMMON_FOLDER}/utils/glib-loop.cpp - ${COMMON_FOLDER}/utils/glib-loop.hpp) + ${COMMON_FOLDER}/utils/glib-loop.hpp + ${COMMON_FOLDER}/base-exception.hpp + ${COMMON_FOLDER}/base-exception.cpp) SET(_LIB_VERSION_ "0.0.1") SET(_LIB_SOVERSION_ "0") diff --git a/client/vasum-client-impl.cpp b/client/vasum-client-impl.cpp index d6f4217..d8ba562 100644 --- a/client/vasum-client-impl.cpp +++ b/client/vasum-client-impl.cpp @@ -31,10 +31,15 @@ #include #include #include +#include +#include +#include +#include #include #include #include +#include using namespace std; using namespace dbus; @@ -77,6 +82,21 @@ void toDict(GVariant* in, VsmArrayString* keys, VsmArrayString* values) *values = outv; } +vector> toDict(GVariant* in) +{ + assert(in); + + const gchar* key; + const gchar* value; + vector> dict; + GVariantIter iter; + g_variant_iter_init(&iter, in); + while (g_variant_iter_loop(&iter, "(&s&s)", &key, &value)) { + dict.push_back(make_tuple(key, value)); + } + return dict; +} + void toBasic(GVariant* in, char** str) { assert(in); @@ -152,6 +172,37 @@ void toArray(GVariant* in, T** scArray) *scArray = ids; } +string toString(const in_addr* addr) +{ + char buf[INET_ADDRSTRLEN]; + const char* ret = inet_ntop(AF_INET, addr, buf, INET_ADDRSTRLEN); + if (ret == NULL) { + throw runtime_error(getSystemErrorMessage()); + } + return ret; +} + +string toString(const in6_addr* addr) +{ + char buf[INET6_ADDRSTRLEN]; + const char* ret = inet_ntop(AF_INET6, addr, buf, INET6_ADDRSTRLEN); + if (ret == NULL) { + throw runtime_error(getSystemErrorMessage()); + } + return ret; +} + +GVariant* createTupleArray(const vector>& dict) +{ + GVariantBuilder builder; + g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY); + for (const auto entry : dict) { + g_variant_builder_add(&builder, "(ss)", get<0>(entry).c_str(), get<1>(entry).c_str()); + } + return g_variant_builder_end(&builder); +} + + VsmStatus toStatus(const exception& ex) { if (typeid(DbusCustomException) == typeid(ex)) { @@ -530,34 +581,162 @@ VsmStatus Client::vsm_revoke_device(const char* id, const char* device) noexcept return callMethod(HOST_INTERFACE, api::host::METHOD_REVOKE_DEVICE, args_in); } -VsmStatus Client::vsm_zone_get_netdevs(const char*, VsmArrayString*) noexcept +VsmStatus Client::vsm_zone_get_netdevs(const char* zone, VsmArrayString* netdevIds) noexcept { - mStatus = Status(VSMCLIENT_OTHER_ERROR, "Not implemented"); - return vsm_get_status(); + assert(zone); + assert(netdevIds); + + GVariant* out = NULL; + GVariant* args_in = g_variant_new("(s)", zone); + VsmStatus ret = callMethod(HOST_INTERFACE, + api::host::METHOD_GET_NETDEV_LIST, + args_in, + "(as)", + &out); + if (ret != VSMCLIENT_SUCCESS) { + return ret; + } + GVariant* unpacked; + g_variant_get(out, "(*)", &unpacked); + toArray(unpacked, netdevIds); + g_variant_unref(unpacked); + g_variant_unref(out); + return ret; } -VsmStatus Client::vsm_netdev_get_ipv4_addr(const char*, const char*, struct in_addr*) noexcept +VsmStatus Client::vsm_netdev_get_ipv4_addr(const char* zone, + const char* netdevId, + struct in_addr* addr) noexcept { - mStatus = Status(VSMCLIENT_OTHER_ERROR, "Not implemented"); + assert(zone); + assert(netdevId); + assert(addr); + + GVariant* out = NULL; + GVariant* args_in = g_variant_new("(ss)", zone, netdevId); + VsmStatus ret = callMethod(HOST_INTERFACE, + api::host::METHOD_GET_NETDEV_ATTRS, + args_in, + "(a(ss))", + &out); + if (ret != VSMCLIENT_SUCCESS) { + return ret; + } + GVariant* unpacked; + g_variant_get(out, "(*)", &unpacked); + vector> attrs = toDict(unpacked); + g_variant_unref(unpacked); + g_variant_unref(out); + + auto it = find_if(attrs.begin(), attrs.end(), [](const tuple& entry) { + return get<0>(entry) == "ipv4"; + }); + if (it != attrs.end()) { + if (inet_pton(AF_INET, get<1>(*it).c_str(), addr) != 1) { + mStatus = Status(VSMCLIENT_CUSTOM_ERROR, "Invalid response data"); + return vsm_get_status(); + } + mStatus = Status(); + return vsm_get_status(); + } + mStatus = Status(VSMCLIENT_CUSTOM_ERROR, "Address not found"); return vsm_get_status(); } -VsmStatus Client::vsm_netdev_get_ipv6_addr(const char*, const char*, struct in6_addr*) noexcept +VsmStatus Client::vsm_netdev_get_ipv6_addr(const char* zone, + const char* netdevId, + struct in6_addr* addr) noexcept { - mStatus = Status(VSMCLIENT_OTHER_ERROR, "Not implemented"); + assert(zone); + assert(netdevId); + assert(addr); + + GVariant* out = NULL; + GVariant* args_in = g_variant_new("(ss)", zone, netdevId); + VsmStatus ret = callMethod(HOST_INTERFACE, + api::host::METHOD_GET_NETDEV_ATTRS, + args_in, + "(a(ss))", + &out); + if (ret != VSMCLIENT_SUCCESS) { + return ret; + } + GVariant* unpacked; + g_variant_get(out, "(*)", &unpacked); + vector> attrs = toDict(unpacked); + g_variant_unref(unpacked); + g_variant_unref(out); + + //XXX: return only one address + auto it = find_if(attrs.begin(), attrs.end(), [](const tuple& entry) { + return get<0>(entry) == "ipv6"; + }); + if (it != attrs.end()) { + if (inet_pton(AF_INET6, get<1>(*it).c_str(), addr) != 1) { + mStatus = Status(VSMCLIENT_CUSTOM_ERROR, "Invalid response data"); + return vsm_get_status(); + } + mStatus = Status(); + return vsm_get_status(); + } + mStatus = Status(VSMCLIENT_CUSTOM_ERROR, "Address not found"); return vsm_get_status(); } -VsmStatus Client::vsm_netdev_set_ipv4_addr(const char*, const char*, struct in_addr*, int) noexcept +VsmStatus Client::vsm_netdev_set_ipv4_addr(const char* zone, + const char* netdevId, + struct in_addr* addr, + int mask) noexcept { - mStatus = Status(VSMCLIENT_OTHER_ERROR, "Not implemented"); - return vsm_get_status(); + try { + GVariant* dict = createTupleArray({make_tuple("ipv4", toString(addr)), + make_tuple("mask", to_string(mask))}); + GVariant* args_in = g_variant_new("(ss@a(ss))", zone, netdevId, dict); + return callMethod(HOST_INTERFACE, api::host::METHOD_SET_NETDEV_ATTRS, args_in); + } catch (exception& ex) { + mStatus = Status(VSMCLIENT_INVALID_ARGUMENT, ex.what()); + return vsm_get_status(); + } } -VsmStatus Client::vsm_netdev_set_ipv6_addr(const char*, const char*, struct in6_addr*, int) noexcept +VsmStatus Client::vsm_netdev_set_ipv6_addr(const char* zone, + const char* netdevId, + struct in6_addr* addr, + int mask) noexcept { - mStatus = Status(VSMCLIENT_OTHER_ERROR, "Not implemented"); - return vsm_get_status(); + try { + GVariant* dict = createTupleArray({make_tuple("ipv6", toString(addr)), + make_tuple("mask", to_string(mask))}); + GVariant* args_in = g_variant_new("(ss@a(ss))", zone, netdevId, dict); + return callMethod(HOST_INTERFACE, api::host::METHOD_SET_NETDEV_ATTRS, args_in); + } catch (exception& ex) { + mStatus = Status(VSMCLIENT_INVALID_ARGUMENT, ex.what()); + return vsm_get_status(); + } +} + +VsmStatus Client::vsm_netdev_up(const char* zone, const char* netdevId) noexcept +{ + try { + GVariant* dict = createTupleArray({make_tuple("up", "true")}); + GVariant* args_in = g_variant_new("(ss@a(ss))", zone, netdevId, dict); + return callMethod(HOST_INTERFACE, api::host::METHOD_SET_NETDEV_ATTRS, args_in); + } catch (exception& ex) { + mStatus = Status(VSMCLIENT_INVALID_ARGUMENT, ex.what()); + return vsm_get_status(); + } +} + +VsmStatus Client::vsm_netdev_down(const char* zone, const char* netdevId) noexcept +{ + try { + GVariant* dict = createTupleArray({make_tuple("up", "false")}); + GVariant* args_in = g_variant_new("(ss@a(ss))", zone, netdevId, dict); + return callMethod(HOST_INTERFACE, api::host::METHOD_SET_NETDEV_ATTRS, args_in); + } catch (exception& ex) { + mStatus = Status(VSMCLIENT_INVALID_ARGUMENT, ex.what()); + return vsm_get_status(); + } } VsmStatus Client::vsm_create_netdev_veth(const char* zone, diff --git a/client/vasum-client-impl.hpp b/client/vasum-client-impl.hpp index 193f140..9399839 100644 --- a/client/vasum-client-impl.hpp +++ b/client/vasum-client-impl.hpp @@ -229,6 +229,16 @@ public: int prefix) noexcept; /** + * @see ::vsm_netdev_up + */ + VsmStatus vsm_netdev_up(const char* zone, const char* netdevId) noexcept; + + /** + * @see ::vsm_netdev_down + */ + VsmStatus vsm_netdev_down(const char* zone, const char* netdevId) noexcept; + + /** * @see ::vsm_create_netdev_veth */ VsmStatus vsm_create_netdev_veth(const char* zone, diff --git a/client/vasum-client.cpp b/client/vasum-client.cpp index 1f011c1..9c0ca60 100644 --- a/client/vasum-client.cpp +++ b/client/vasum-client.cpp @@ -250,6 +250,20 @@ API VsmStatus vsm_netdev_set_ipv6_addr(VsmClient client, return getClient(client).vsm_netdev_set_ipv6_addr(zone, netdevId, addr, prefix); } +API VsmStatus vsm_netdev_up(VsmClient client, + const char* zone, + const char* netdevId) +{ + return getClient(client).vsm_netdev_up(zone, netdevId); +} + +API VsmStatus vsm_netdev_down(VsmClient client, + const char* zone, + const char* netdevId) +{ + return getClient(client).vsm_netdev_down(zone, netdevId); +} + API VsmStatus vsm_create_netdev_veth(VsmClient client, const char* zone, const char* zoneDev, diff --git a/client/vasum-client.h b/client/vasum-client.h index 1fb48b1..0bd40ad 100644 --- a/client/vasum-client.h +++ b/client/vasum-client.h @@ -552,6 +552,31 @@ VsmStatus vsm_netdev_set_ipv6_addr(VsmClient client, int prefix); /** + * Turn up a network device in the zone + * + * @param[in] client vasum-server's client + * @param[in] zone zone name + * @param[in] netdevId netdev id + * @return status of this function call + */ +VsmStatus vsm_netdev_up(VsmClient client, + const char* zone, + const char* netdevId); + +/** + * Turn down a network device in the zone + * + * @param[in] client vasum-server's client + * @param[in] zone zone name + * @param[in] netdevId netdev id + * @return status of this function call + */ +VsmStatus vsm_netdev_down(VsmClient client, + const char* zone, + const char* netdevId); + + +/** * Create veth netdev in zone * * @param[in] client vasum-server's client diff --git a/common/netlink/netlink-message.cpp b/common/netlink/netlink-message.cpp index 72d3597..9f33450 100644 --- a/common/netlink/netlink-message.cpp +++ b/common/netlink/netlink-message.cpp @@ -42,12 +42,16 @@ #define PAGE_SIZE 4096 #endif +namespace { + const int NLMSG_GOOD_SIZE = 2*PAGE_SIZE; -constexpr rtattr* NLMSG_TAIL(nlmsghdr* nmsg) +inline rtattr* NLMSG_TAIL(nlmsghdr* nmsg) { return reinterpret_cast(reinterpret_cast(nmsg) + NLMSG_ALIGN(nmsg->nlmsg_len)); } +} // namespace + namespace vasum { namespace netlink { diff --git a/server/host-connection.cpp b/server/host-connection.cpp index 75c4433..6790480 100644 --- a/server/host-connection.cpp +++ b/server/host-connection.cpp @@ -130,6 +130,21 @@ void HostConnection::setGetZoneInfoCallback(const GetZoneInfoCallback& callback) mGetZoneInfoCallback = callback; } +void HostConnection::setSetNetdevAttrsCallback(const SetNetdevAttrsCallback& callback) +{ + mSetNetdevAttrsCallback = callback; +} + +void HostConnection::setGetNetdevAttrsCallback(const GetNetdevAttrsCallback& callback) +{ + mGetNetdevAttrsCallback = callback; +} + +void HostConnection::setGetNetdevListCallback(const GetNetdevListCallback& callback) +{ + mGetNetdevListCallback = callback; +} + void HostConnection::setCreateNetdevVethCallback(const CreateNetdevVethCallback& callback) { mCreateNetdevVethCallback = callback; @@ -296,6 +311,44 @@ void HostConnection::onMessageCall(const std::string& objectPath, return; } + if (methodName == api::host::METHOD_SET_NETDEV_ATTRS){ + const gchar* zone = NULL; + const gchar* netdev = NULL; + GVariantIter* iter; + g_variant_get(parameters, "(&s&sa(ss))", &zone, &netdev, &iter); + gchar* key = NULL; + gchar* value = NULL; + std::vector> attrs; + while (g_variant_iter_loop(iter, "(&s&s)", &key, &value)) { + attrs.push_back(std::make_tuple(key, value)); + } + g_variant_iter_free(iter); + if (mSetNetdevAttrsCallback) { + mSetNetdevAttrsCallback(zone, netdev, attrs, result); + } + return; + } + + if (methodName == api::host::METHOD_GET_NETDEV_ATTRS){ + const gchar* zone = NULL; + const gchar* netdev = NULL; + g_variant_get(parameters, "(&s&s)", &zone, &netdev); + if (mGetNetdevAttrsCallback) { + mGetNetdevAttrsCallback(zone, netdev, result); + } + return; + } + + if (methodName == api::host::METHOD_GET_NETDEV_LIST){ + const gchar* zone = NULL; + g_variant_get(parameters, "(&s)", &zone); + if (mGetNetdevListCallback) { + mGetNetdevListCallback(zone, result); + } + return; + } + + if (methodName == api::host::METHOD_CREATE_NETDEV_VETH) { const gchar* id = NULL; const gchar* zoneDev = NULL; diff --git a/server/host-connection.hpp b/server/host-connection.hpp index 649ed15..569c68f 100644 --- a/server/host-connection.hpp +++ b/server/host-connection.hpp @@ -30,6 +30,8 @@ #include #include +#include +#include namespace vasum { @@ -60,8 +62,20 @@ public: typedef std::function GetZoneInfoCallback; - typedef std::function>& attrs, + dbus::MethodResultBuilder::Pointer result + )> SetNetdevAttrsCallback; + typedef std::function GetNetdevAttrsCallback; + typedef std::function GetNetdevListCallback; + typedef std::function CreateNetdevVethCallback; @@ -165,6 +179,21 @@ public: void setGetZoneInfoCallback(const GetZoneInfoCallback& callback); /** + * Register a callback called to set network device attributes + */ + void setSetNetdevAttrsCallback(const SetNetdevAttrsCallback& callback); + + /** + * Register a callback called to get network device attributes + */ + void setGetNetdevAttrsCallback(const GetNetdevAttrsCallback& callback); + + /** + * Register a callback called to get network device list + */ + void setGetNetdevListCallback(const GetNetdevListCallback& callback); + + /** * Register a callback called to create veth */ void setCreateNetdevVethCallback(const CreateNetdevVethCallback& callback); @@ -270,6 +299,9 @@ private: GetZoneIdsCallback mGetZoneIdsCallback; GetActiveZoneIdCallback mGetActiveZoneIdCallback; GetZoneInfoCallback mGetZoneInfoCallback; + SetNetdevAttrsCallback mSetNetdevAttrsCallback; + GetNetdevAttrsCallback mGetNetdevAttrsCallback; + GetNetdevListCallback mGetNetdevListCallback; CreateNetdevVethCallback mCreateNetdevVethCallback; CreateNetdevMacvlanCallback mCreateNetdevMacvlanCallback; CreateNetdevPhysCallback mCreateNetdevPhysCallback; diff --git a/server/host-dbus-definitions.hpp b/server/host-dbus-definitions.hpp index 6d25bb1..2c2a201 100644 --- a/server/host-dbus-definitions.hpp +++ b/server/host-dbus-definitions.hpp @@ -42,6 +42,9 @@ const std::string METHOD_GET_ZONE_DBUSES = "GetZoneDbuses"; const std::string METHOD_GET_ZONE_ID_LIST = "GetZoneIds"; const std::string METHOD_GET_ACTIVE_ZONE_ID = "GetActiveZoneId"; const std::string METHOD_GET_ZONE_INFO = "GetZoneInfo"; +const std::string METHOD_SET_NETDEV_ATTRS = "SetNetdevAttrs"; +const std::string METHOD_GET_NETDEV_ATTRS = "GetNetdevAttrs"; +const std::string METHOD_GET_NETDEV_LIST = "GetNetdevList"; const std::string METHOD_CREATE_NETDEV_VETH = "CreateNetdevVeth"; const std::string METHOD_CREATE_NETDEV_MACVLAN = "CreateNetdevMacvlan"; const std::string METHOD_CREATE_NETDEV_PHYS = "CreateNetdevPhys"; @@ -88,6 +91,20 @@ const std::string DEFINITION = " " " " " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " " " " " " " diff --git a/server/zone-admin.cpp b/server/zone-admin.cpp index 5a4adfa..519b172 100644 --- a/server/zone-admin.cpp +++ b/server/zone-admin.cpp @@ -289,4 +289,19 @@ void ZoneAdmin::moveNetdev(const std::string& devId) netdev::movePhys(mZone.getInitPid(), devId); } +void ZoneAdmin::setNetdevAttrs(const std::string& /* netdev */, const NetdevAttrs& /* attrs */) +{ + throw ZoneOperationException("Not implemented"); +} + +ZoneAdmin::NetdevAttrs ZoneAdmin::getNetdevAttrs(const std::string& /* netdev */) +{ + throw ZoneOperationException("Not implemented"); +} + +std::vector ZoneAdmin::getNetdevList() +{ + throw ZoneOperationException("Not implemented"); +} + } // namespace vasum diff --git a/server/zone-admin.hpp b/server/zone-admin.hpp index 9125a6d..44d2613 100644 --- a/server/zone-admin.hpp +++ b/server/zone-admin.hpp @@ -41,6 +41,7 @@ enum class SchedulerLevel { class ZoneAdmin { public: + typedef std::vector> NetdevAttrs; /** * ZoneAdmin constructor @@ -148,6 +149,21 @@ public: */ void moveNetdev(const std::string& devId); + /** + * Set network device attributes + */ + void setNetdevAttrs(const std::string& netdev, const NetdevAttrs& attrs); + + /** + * Get network device attributes + */ + NetdevAttrs getNetdevAttrs(const std::string& netdev); + + /** + * Get network device list + */ + std::vector getNetdevList(); + private: const ZoneConfig& mConfig; lxc::LxcZone mZone; diff --git a/server/zone.cpp b/server/zone.cpp index 0607fda..fed66fa 100644 --- a/server/zone.cpp +++ b/server/zone.cpp @@ -441,4 +441,22 @@ void Zone::removeDeclaration(const std::string& declarationId) mProvision->remove(declarationId); } +void Zone::setNetdevAttrs(const std::string& netdev, const ZoneAdmin::NetdevAttrs& attrs) +{ + Lock lock(mReconnectMutex); + mAdmin->setNetdevAttrs(netdev, attrs); +} + +ZoneAdmin::NetdevAttrs Zone::getNetdevAttrs(const std::string& netdev) +{ + Lock lock(mReconnectMutex); + return mAdmin->getNetdevAttrs(netdev); +} + +std::vector Zone::getNetdevList() +{ + Lock lock(mReconnectMutex); + return mAdmin->getNetdevList(); +} + } // namespace vasum diff --git a/server/zone.hpp b/server/zone.hpp index 759faa3..832fcec 100644 --- a/server/zone.hpp +++ b/server/zone.hpp @@ -285,6 +285,21 @@ public: */ void moveNetdev(const std::string& devId); + /** + * Set network device attributes + */ + void setNetdevAttrs(const std::string& netdev, const ZoneAdmin::NetdevAttrs& attrs); + + /** + * Get network device attributes + */ + ZoneAdmin::NetdevAttrs getNetdevAttrs(const std::string& netdev); + + /** + * Get network device list + */ + std::vector getNetdevList(); + private: utils::Worker::Pointer mWorker; ZoneConfig mConfig; diff --git a/server/zones-manager.cpp b/server/zones-manager.cpp index 5f0dca4..283f57f 100644 --- a/server/zones-manager.cpp +++ b/server/zones-manager.cpp @@ -141,6 +141,15 @@ ZonesManager::ZonesManager(const std::string& configPath) mHostConnection.setGetZoneInfoCallback(bind(&ZonesManager::handleGetZoneInfoCall, this, _1, _2)); + mHostConnection.setSetNetdevAttrsCallback(bind(&ZonesManager::handleSetNetdevAttrsCall, + this, _1, _2, _3, _4)); + + mHostConnection.setGetNetdevAttrsCallback(bind(&ZonesManager::handleGetNetdevAttrsCall, + this, _1, _2, _3)); + + mHostConnection.setGetNetdevListCallback(bind(&ZonesManager::handleGetNetdevListCall, + this, _1, _2)); + mHostConnection.setCreateNetdevVethCallback(bind(&ZonesManager::handleCreateNetdevVethCall, this, _1, _2, _3, _4)); @@ -803,6 +812,78 @@ void ZonesManager::handleGetZoneInfoCall(const std::string& id, zone.getRootPath().c_str())); } +void ZonesManager::handleSetNetdevAttrsCall(const std::string& zone, + const std::string& netdev, + const std::vector< + std::tuple>& attrs, + dbus::MethodResultBuilder::Pointer result) +{ + LOGI("SetNetdevAttrs call"); + try { + Lock lock(mMutex); + + getZone(zone).setNetdevAttrs(netdev, attrs); + result->setVoid(); + } catch (const InvalidZoneIdException&) { + LOGE("No zone with id=" << zone); + result->setError(api::ERROR_INVALID_ID, "No such zone id"); + } catch (const VasumException& ex) { + LOGE("Can't set attributes: " << ex.what()); + result->setError(api::ERROR_INTERNAL, ex.what()); + } +} + +void ZonesManager::handleGetNetdevAttrsCall(const std::string& zone, + const std::string& netdev, + dbus::MethodResultBuilder::Pointer result) +{ + LOGI("GetNetdevAttrs call"); + try { + Lock lock(mMutex); + + const auto attrs = getZone(zone).getNetdevAttrs(netdev); + + GVariantBuilder builder; + g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY); + for (const auto entry : attrs) { + g_variant_builder_add(&builder, + "(ss)", + std::get<0>(entry).c_str(), + std::get<1>(entry).c_str()); + } + result->set(g_variant_builder_end(&builder)); + } catch (const InvalidZoneIdException&) { + LOGE("No zone with id=" << zone); + result->setError(api::ERROR_INVALID_ID, "No such zone id"); + } catch (const VasumException& ex) { + LOGE("Can't set attributes: " << ex.what()); + result->setError(api::ERROR_INTERNAL, ex.what()); + } +} + +void ZonesManager::handleGetNetdevListCall(const std::string& zone, + dbus::MethodResultBuilder::Pointer result) +{ + LOGI("GetNetdevList call"); + try { + Lock lock(mMutex); + std::vector netdevs; + for(auto& netdev: getZone(zone).getNetdevList()){ + netdevs.push_back(g_variant_new_string(netdev.c_str())); + } + GVariant* array = g_variant_new_array(G_VARIANT_TYPE("s"), + netdevs.data(), + netdevs.size()); + result->set(g_variant_new("(@as)", array)); + } catch (const InvalidZoneIdException&) { + LOGE("No zone with id=" << zone); + result->setError(api::ERROR_INVALID_ID, "No such zone id"); + } catch (const VasumException& ex) { + LOGE("Can't set attributes: " << ex.what()); + result->setError(api::ERROR_INTERNAL, ex.what()); + } +} + void ZonesManager::handleCreateNetdevVethCall(const std::string& zone, const std::string& zoneDev, const std::string& hostDev, @@ -814,7 +895,7 @@ void ZonesManager::handleCreateNetdevVethCall(const std::string& zone, getZone(zone).createNetdevVeth(zoneDev, hostDev); result->setVoid(); - } catch (const std::out_of_range&) { + } catch (const InvalidZoneIdException&) { LOGE("No zone with id=" << zone); result->setError(api::ERROR_INVALID_ID, "No such zone id"); } catch (const VasumException& ex) { @@ -835,7 +916,7 @@ void ZonesManager::handleCreateNetdevMacvlanCall(const std::string& zone, getZone(zone).createNetdevMacvlan(zoneDev, hostDev, mode); result->setVoid(); - } catch (const std::out_of_range&) { + } catch (const InvalidZoneIdException&) { LOGE("No zone with id=" << zone); result->setError(api::ERROR_INVALID_ID, "No such zone id"); } catch (const VasumException& ex) { @@ -854,7 +935,7 @@ void ZonesManager::handleCreateNetdevPhysCall(const std::string& zone, getZone(zone).moveNetdev(devId); result->setVoid(); - } catch (const std::out_of_range&) { + } catch (const InvalidZoneIdException&) { LOGE("No zone with id=" << zone); result->setError(api::ERROR_INVALID_ID, "No such zone id"); } catch (const VasumException& ex) { diff --git a/server/zones-manager.hpp b/server/zones-manager.hpp index df5e12b..ee1606d 100644 --- a/server/zones-manager.hpp +++ b/server/zones-manager.hpp @@ -161,6 +161,15 @@ private: void handleGetZoneIdsCall(dbus::MethodResultBuilder::Pointer result); void handleGetActiveZoneIdCall(dbus::MethodResultBuilder::Pointer result); void handleGetZoneInfoCall(const std::string& id, dbus::MethodResultBuilder::Pointer result); + void handleSetNetdevAttrsCall(const std::string& zone, + const std::string& netdev, + const std::vector>& attrs, + dbus::MethodResultBuilder::Pointer result); + void handleGetNetdevAttrsCall(const std::string& zone, + const std::string& netdev, + dbus::MethodResultBuilder::Pointer result); + void handleGetNetdevListCall(const std::string& zone, + dbus::MethodResultBuilder::Pointer result); void handleCreateNetdevVethCall(const std::string& zone, const std::string& zoneDev, const std::string& hostDev, -- 2.7.4 From 0ca217568de95a695483c92db178355a739a77b9 Mon Sep 17 00:00:00 2001 From: Piotr Bartosiewicz Date: Mon, 2 Mar 2015 11:57:50 +0100 Subject: [PATCH 04/16] Persistent state of zone [Bug/Feature] Zone was always started after vasum restart regardless of the zone state before restart [Cause] N/A [Solution] N/A [Verification] Run tests. Run vasum, pause one container, restart vasum, check this container is still paused. The same test with stopped and started state. Change-Id: I0426e3e4f2af9b2bc0c110a7702b9c742a2747e6 --- server/configs/templates/default.conf | 1 + server/server.cpp | 4 +- server/zone-config.hpp | 6 ++ server/zone.cpp | 52 ++++++++- server/zone.hpp | 11 +- server/zones-manager.cpp | 37 +++---- server/zones-manager.hpp | 13 ++- .../ut-client/templates/console-dbus.conf.in | 1 + tests/unit_tests/client/ut-client.cpp | 4 +- .../configs/ut-server/templates/default.conf | 1 + .../configs/ut-zone-admin/templates/buggy.conf | 1 + .../configs/ut-zone-admin/templates/missing.conf | 1 + .../ut-zone-admin/templates/test-no-shutdown.conf | 1 + .../configs/ut-zone-admin/templates/test.conf | 1 + .../server/configs/ut-zone/templates/buggy.conf | 1 + .../configs/ut-zone/templates/test-dbus.conf.in | 1 + .../server/configs/ut-zone/templates/test.conf | 1 + .../templates/console-dbus.conf.in | 1 + .../ut-zones-manager/templates/console.conf | 1 + tests/unit_tests/server/ut-server.cpp | 2 +- tests/unit_tests/server/ut-zone-admin.cpp | 4 +- tests/unit_tests/server/ut-zone.cpp | 4 +- tests/unit_tests/server/ut-zones-manager.cpp | 119 ++++++++++++++++----- 23 files changed, 206 insertions(+), 62 deletions(-) diff --git a/server/configs/templates/default.conf b/server/configs/templates/default.conf index 2aa7a66..9eb0e08 100644 --- a/server/configs/templates/default.conf +++ b/server/configs/templates/default.conf @@ -1,6 +1,7 @@ { "lxcTemplate" : "tizen-common-wayland.sh", "initWithArgs" : [], + "requestedState" : "stopped", "ipv4Gateway" : "10.0.~IP~.1", "ipv4" : "10.0.~IP~.2", "cpuQuotaForeground" : -1, diff --git a/server/server.cpp b/server/server.cpp index b4e2344..c2ca778 100644 --- a/server/server.cpp +++ b/server/server.cpp @@ -107,7 +107,7 @@ void Server::run(bool asRoot) utils::ScopedGlibLoop loop; ZonesManager manager(mConfigPath); - manager.startAll(); + manager.restoreAll(); LOGI("Daemon started"); gSignalLatch.wait(); @@ -118,7 +118,7 @@ void Server::run(bool asRoot) } LOGI("Stopping daemon..."); - // manager.stopAll() will be called in destructor + // manager.shutdownAll() will be called in destructor } LOGI("Daemon stopped"); } diff --git a/server/zone-config.hpp b/server/zone-config.hpp index da4b570..f5d81e5 100644 --- a/server/zone-config.hpp +++ b/server/zone-config.hpp @@ -110,6 +110,11 @@ struct ZoneConfig { struct ZoneDynamicConfig { /** + * Requested zone state after restore + */ + std::string requestedState; + + /** * IP v4 gateway address */ std::string ipv4Gateway; @@ -131,6 +136,7 @@ struct ZoneDynamicConfig { CONFIG_REGISTER ( + requestedState, ipv4Gateway, ipv4, vt, diff --git a/server/zone.cpp b/server/zone.cpp index fed66fa..1bc2d09 100644 --- a/server/zone.cpp +++ b/server/zone.cpp @@ -50,6 +50,10 @@ typedef std::lock_guard Lock; const int RECONNECT_RETRIES = 15; const int RECONNECT_DELAY = 1 * 1000; +const std::string STATE_STOPPED = "stopped"; +const std::string STATE_RUNNING = "running"; +const std::string STATE_PAUSED = "paused"; + } // namespace Zone::Zone(const utils::Worker::Pointer& worker, @@ -60,6 +64,7 @@ Zone::Zone(const utils::Worker::Pointer& worker, const std::string& lxcTemplatePrefix, const std::string& baseRunMountPointPath) : mWorker(worker) + , mDbPath(dbPath) { const std::string dbPrefix = getZoneDbPrefix(zoneId); config::loadFromKVStoreWithJsonFile(dbPath, zoneTemplatePath, mConfig, dbPrefix); @@ -118,9 +123,46 @@ int Zone::getPrivilege() const return mConfig.privilege; } +void Zone::saveDynamicConfig() +{ + config::saveToKVStore(mDbPath, mDynamicConfig, getZoneDbPrefix(getId())); +} + +void Zone::updateRequestedState(const std::string& state) +{ + // assume mutex is locked + if (state != mDynamicConfig.requestedState) { + LOGT("Set requested state of " << getId() << " to " << state); + mDynamicConfig.requestedState = state; + saveDynamicConfig(); + } +} + +void Zone::restore() +{ + std::string requestedState; + { + Lock lock(mReconnectMutex); + requestedState = mDynamicConfig.requestedState; + LOGT("Requested state of " << getId() << ": " << requestedState); + } + + if (requestedState == STATE_RUNNING) { + start(); + } else if (requestedState == STATE_STOPPED) { + } else if (requestedState == STATE_PAUSED) { + start(); + suspend(); + } else { + LOGE("Invalid requested state: " << requestedState); + assert(0 && "invalid requested state"); + } +} + void Zone::start() { Lock lock(mReconnectMutex); + updateRequestedState(STATE_RUNNING); mProvision->start(); if (mConfig.enableDbusIntegration) { mConnectionTransport.reset(new ZoneConnectionTransport(mRunMountPoint)); @@ -135,9 +177,12 @@ void Zone::start() // refocus in ZonesManager will adjust cpu quota after all } -void Zone::stop() +void Zone::stop(bool saveState) { Lock lock(mReconnectMutex); + if (saveState) { + updateRequestedState(STATE_STOPPED); + } if (mAdmin->isRunning()) { // boost stopping goForeground(); @@ -192,6 +237,7 @@ std::string Zone::getDbusAddress() const int Zone::getVT() const { + Lock lock(mReconnectMutex); return mDynamicConfig.vt; } @@ -275,12 +321,14 @@ void Zone::suspend() { Lock lock(mReconnectMutex); mAdmin->suspend(); + updateRequestedState(STATE_PAUSED); } void Zone::resume() { Lock lock(mReconnectMutex); mAdmin->resume(); + updateRequestedState(STATE_RUNNING); } bool Zone::isPaused() @@ -329,7 +377,7 @@ void Zone::reconnectHandler() } LOGE(getId() << ": Reconnecting to the DBUS has failed, stopping the zone"); - stop(); + stop(false); } void Zone::setNotifyActiveZoneCallback(const NotifyActiveZoneCallback& callback) diff --git a/server/zone.hpp b/server/zone.hpp index 832fcec..69d0324 100644 --- a/server/zone.hpp +++ b/server/zone.hpp @@ -98,14 +98,20 @@ public: int getPrivilege() const; /** + * Restore zone to the previous state + */ + void restore(); + + /** * Boot the zone to the background. */ void start(); /** * Try to shutdown the zone, if failed, destroy it. + * @param saveState save zone's state */ - void stop(); + void stop(bool saveState); /** * Activate this zone's VT @@ -319,11 +325,14 @@ private: std::string mDbusAddress; std::string mRunMountPoint; std::string mRootPath; + std::string mDbPath; void onNameLostCallback(); void reconnectHandler(); void connect(); void disconnect(); + void saveDynamicConfig(); + void updateRequestedState(const std::string& state); }; diff --git a/server/zones-manager.cpp b/server/zones-manager.cpp index 283f57f..d90bab7 100644 --- a/server/zones-manager.cpp +++ b/server/zones-manager.cpp @@ -226,9 +226,9 @@ ZonesManager::~ZonesManager() if (!mDetachOnExit) { try { - stopAll(); + shutdownAll(); } catch (ServerException&) { - LOGE("Failed to stop all of the zones"); + LOGE("Failed to shutdown all of the zones"); } } // wait for all tasks to complete @@ -434,27 +434,27 @@ void ZonesManager::refocus() focusInternal(iter); } -void ZonesManager::startAll() +void ZonesManager::restoreAll() { - LOGI("Starting all zones"); + LOGI("Restoring all zones"); Lock lock(mMutex); for (auto& zone : mZones) { - zone->start(); + zone->restore(); } refocus(); } -void ZonesManager::stopAll() +void ZonesManager::shutdownAll() { LOGI("Stopping all zones"); Lock lock(mMutex); for (auto& zone : mZones) { - zone->stop(); + zone->stop(false); } refocus(); @@ -463,26 +463,19 @@ void ZonesManager::stopAll() bool ZonesManager::isPaused(const std::string& zoneId) { Lock lock(mMutex); - - auto iter = findZone(zoneId); - if (iter == mZones.end()) { - LOGE("No such zone id: " << zoneId); - throw InvalidZoneIdException("No such zone"); - } - - return get(iter).isPaused(); + return getZone(zoneId).isPaused(); } bool ZonesManager::isRunning(const std::string& zoneId) { Lock lock(mMutex); + return getZone(zoneId).isRunning(); +} - auto iter = findZone(zoneId); - if (iter == mZones.end()) { - LOGE("No such zone id: " << zoneId); - throw InvalidZoneIdException("No such zone"); - } - return get(iter).isRunning(); +bool ZonesManager::isStopped(const std::string& zoneId) +{ + Lock lock(mMutex); + return getZone(zoneId).isStopped(); } std::string ZonesManager::getRunningForegroundZoneId() @@ -1265,7 +1258,7 @@ void ZonesManager::handleShutdownZoneCall(const std::string& id, result->setError(api::ERROR_INVALID_ID, "No such zone id"); return; } - get(iter).stop(); + get(iter).stop(true); refocus(); result->setVoid(); } catch (ZoneOperationException& e) { diff --git a/server/zones-manager.hpp b/server/zones-manager.hpp index ee1606d..cb730e3 100644 --- a/server/zones-manager.hpp +++ b/server/zones-manager.hpp @@ -70,14 +70,14 @@ public: void focus(const std::string& zoneId); /** - * Start up all the configured zones + * Restore all the configured zones to the saved state */ - void startAll(); + void restoreAll(); /** - * Stop all managed zones + * Shutdown all managed zones without changing the saved state */ - void stopAll(); + void shutdownAll(); /** * @return Is the zone in a paused state? @@ -90,6 +90,11 @@ public: bool isRunning(const std::string& zoneId); /** + * @return Is the zone stopped? + */ + bool isStopped(const std::string& zoneId); + + /** * @return id of the currently focused/foreground zone */ std::string getRunningForegroundZoneId(); diff --git a/tests/unit_tests/client/configs/ut-client/templates/console-dbus.conf.in b/tests/unit_tests/client/configs/ut-client/templates/console-dbus.conf.in index 0bd0e03..7778208 100644 --- a/tests/unit_tests/client/configs/ut-client/templates/console-dbus.conf.in +++ b/tests/unit_tests/client/configs/ut-client/templates/console-dbus.conf.in @@ -1,6 +1,7 @@ { "lxcTemplate" : "minimal-dbus.sh", "initWithArgs" : ["/bin/sh", "-c", "trap exit SIGTERM; /usr/bin/dbus-daemon --config-file=@VSM_TEST_CONFIG_INSTALL_DIR@/server/ut-zones-manager/ut-dbus.conf --fork; read"], + "requestedState" : "running", "ipv4Gateway" : "", "ipv4" : "", "privilege" : 20, diff --git a/tests/unit_tests/client/ut-client.cpp b/tests/unit_tests/client/ut-client.cpp index 1c55668..ce31795 100644 --- a/tests/unit_tests/client/ut-client.cpp +++ b/tests/unit_tests/client/ut-client.cpp @@ -77,7 +77,7 @@ struct Fixture { cm.createZone("zone1", TEMPLATE_NAME); cm.createZone("zone2", TEMPLATE_NAME); cm.createZone("zone3", TEMPLATE_NAME); - cm.startAll(); + cm.restoreAll(); LOGI("------- setup complete --------"); } @@ -153,7 +153,7 @@ BOOST_FIXTURE_TEST_SUITE(ClientSuite, Fixture) BOOST_AUTO_TEST_CASE(NotRunningServerTest) { - cm.stopAll(); + cm.shutdownAll(); VsmClient client = vsm_client_create(); VsmStatus status = vsm_connect_custom(client, diff --git a/tests/unit_tests/server/configs/ut-server/templates/default.conf b/tests/unit_tests/server/configs/ut-server/templates/default.conf index 875cdc4..a2f703e 100644 --- a/tests/unit_tests/server/configs/ut-server/templates/default.conf +++ b/tests/unit_tests/server/configs/ut-server/templates/default.conf @@ -1,6 +1,7 @@ { "lxcTemplate" : "minimal.sh", "initWithArgs" : ["/bin/sh", "-c", "trap exit SIGTERM; read"], + "requestedState" : "running", "ipv4Gateway" : "", "ipv4" : "", "privilege" : 20, diff --git a/tests/unit_tests/server/configs/ut-zone-admin/templates/buggy.conf b/tests/unit_tests/server/configs/ut-zone-admin/templates/buggy.conf index 07569fe..f585ae8 100644 --- a/tests/unit_tests/server/configs/ut-zone-admin/templates/buggy.conf +++ b/tests/unit_tests/server/configs/ut-zone-admin/templates/buggy.conf @@ -1,6 +1,7 @@ { "lxcTemplate" : "minimal.sh", "initWithArgs" : ["/foo"], + "requestedState" : "running", "ipv4Gateway" : "", "ipv4" : "", "privilege" : 10, diff --git a/tests/unit_tests/server/configs/ut-zone-admin/templates/missing.conf b/tests/unit_tests/server/configs/ut-zone-admin/templates/missing.conf index e0b5e4a..a3f305b 100644 --- a/tests/unit_tests/server/configs/ut-zone-admin/templates/missing.conf +++ b/tests/unit_tests/server/configs/ut-zone-admin/templates/missing.conf @@ -1,6 +1,7 @@ { "lxcTemplate" : "missing.sh", "initWithArgs" : ["/bin/sh", "-c", "trap exit SIGTERM; read"], + "requestedState" : "running", "ipv4Gateway" : "", "ipv4" : "", "privilege" : 10, diff --git a/tests/unit_tests/server/configs/ut-zone-admin/templates/test-no-shutdown.conf b/tests/unit_tests/server/configs/ut-zone-admin/templates/test-no-shutdown.conf index 1579851..98e95ab 100644 --- a/tests/unit_tests/server/configs/ut-zone-admin/templates/test-no-shutdown.conf +++ b/tests/unit_tests/server/configs/ut-zone-admin/templates/test-no-shutdown.conf @@ -1,6 +1,7 @@ { "lxcTemplate" : "minimal.sh", "initWithArgs" : ["/bin/sh"], + "requestedState" : "running", "ipv4Gateway" : "", "ipv4" : "", "privilege" : 10, diff --git a/tests/unit_tests/server/configs/ut-zone-admin/templates/test.conf b/tests/unit_tests/server/configs/ut-zone-admin/templates/test.conf index d74f221..a16b3fa 100644 --- a/tests/unit_tests/server/configs/ut-zone-admin/templates/test.conf +++ b/tests/unit_tests/server/configs/ut-zone-admin/templates/test.conf @@ -1,6 +1,7 @@ { "lxcTemplate" : "minimal.sh", "initWithArgs" : ["/bin/sh", "-c", "trap exit SIGTERM; read"], + "requestedState" : "running", "ipv4Gateway" : "", "ipv4" : "", "privilege" : 10, diff --git a/tests/unit_tests/server/configs/ut-zone/templates/buggy.conf b/tests/unit_tests/server/configs/ut-zone/templates/buggy.conf index 1b449bc..79436c0 100644 --- a/tests/unit_tests/server/configs/ut-zone/templates/buggy.conf +++ b/tests/unit_tests/server/configs/ut-zone/templates/buggy.conf @@ -1,6 +1,7 @@ { "lxcTemplate" : "/buggy/path", "initWithArgs" : ["/bin/sh", "-c", "trap exit SIGTERM; read"], + "requestedState" : "running", "ipv4Gateway" : "", "ipv4" : "", "privilege" : 10, diff --git a/tests/unit_tests/server/configs/ut-zone/templates/test-dbus.conf.in b/tests/unit_tests/server/configs/ut-zone/templates/test-dbus.conf.in index 9e9a1f8..5d76429 100644 --- a/tests/unit_tests/server/configs/ut-zone/templates/test-dbus.conf.in +++ b/tests/unit_tests/server/configs/ut-zone/templates/test-dbus.conf.in @@ -1,6 +1,7 @@ { "lxcTemplate" : "minimal-dbus.sh", "initWithArgs" : ["/bin/sh", "-c", "trap exit SIGTERM; /usr/bin/dbus-daemon --config-file=@VSM_TEST_CONFIG_INSTALL_DIR@/server/ut-zone/ut-dbus.conf --fork; read"], + "requestedState" : "running", "ipv4Gateway" : "", "ipv4" : "", "privilege" : 10, diff --git a/tests/unit_tests/server/configs/ut-zone/templates/test.conf b/tests/unit_tests/server/configs/ut-zone/templates/test.conf index 3bc3277..168aa21 100644 --- a/tests/unit_tests/server/configs/ut-zone/templates/test.conf +++ b/tests/unit_tests/server/configs/ut-zone/templates/test.conf @@ -1,6 +1,7 @@ { "lxcTemplate" : "minimal.sh", "initWithArgs" : ["/bin/sh", "-c", "trap exit SIGTERM; read"], + "requestedState" : "running", "ipv4Gateway" : "", "ipv4" : "", "privilege" : 10, diff --git a/tests/unit_tests/server/configs/ut-zones-manager/templates/console-dbus.conf.in b/tests/unit_tests/server/configs/ut-zones-manager/templates/console-dbus.conf.in index 0bd0e03..7778208 100644 --- a/tests/unit_tests/server/configs/ut-zones-manager/templates/console-dbus.conf.in +++ b/tests/unit_tests/server/configs/ut-zones-manager/templates/console-dbus.conf.in @@ -1,6 +1,7 @@ { "lxcTemplate" : "minimal-dbus.sh", "initWithArgs" : ["/bin/sh", "-c", "trap exit SIGTERM; /usr/bin/dbus-daemon --config-file=@VSM_TEST_CONFIG_INSTALL_DIR@/server/ut-zones-manager/ut-dbus.conf --fork; read"], + "requestedState" : "running", "ipv4Gateway" : "", "ipv4" : "", "privilege" : 20, diff --git a/tests/unit_tests/server/configs/ut-zones-manager/templates/console.conf b/tests/unit_tests/server/configs/ut-zones-manager/templates/console.conf index 9b3013c..6e49c5b 100644 --- a/tests/unit_tests/server/configs/ut-zones-manager/templates/console.conf +++ b/tests/unit_tests/server/configs/ut-zones-manager/templates/console.conf @@ -1,6 +1,7 @@ { "lxcTemplate" : "minimal.sh", "initWithArgs" : ["/bin/sh", "-c", "trap exit SIGTERM; read"], + "requestedState" : "running", "ipv4Gateway" : "", "ipv4" : "", "privilege" : 20, diff --git a/tests/unit_tests/server/ut-server.cpp b/tests/unit_tests/server/ut-server.cpp index 4df5d89..eccea8a 100644 --- a/tests/unit_tests/server/ut-server.cpp +++ b/tests/unit_tests/server/ut-server.cpp @@ -63,7 +63,7 @@ struct Fixture { vasum::ZonesManager manager(TEST_CONFIG_PATH); manager.createZone("zone1", TEMPLATE_NAME); manager.createZone("zone2", TEMPLATE_NAME); - manager.startAll(); + manager.restoreAll(); } }; } // namespace diff --git a/tests/unit_tests/server/ut-zone-admin.cpp b/tests/unit_tests/server/ut-zone-admin.cpp index 4dd08db..c711169 100644 --- a/tests/unit_tests/server/ut-zone-admin.cpp +++ b/tests/unit_tests/server/ut-zone-admin.cpp @@ -158,10 +158,10 @@ BOOST_AUTO_TEST_CASE(SuspendResumeTest) BOOST_AUTO_TEST_CASE(SchedulerLevelTest) { - BOOST_REQUIRE(mConfig.cpuQuotaForeground != mConfig.cpuQuotaBackground); - auto admin = create(TEST_CONFIG_PATH); + BOOST_REQUIRE(mConfig.cpuQuotaForeground != mConfig.cpuQuotaBackground); + admin->start(); ensureStarted(); diff --git a/tests/unit_tests/server/ut-zone.cpp b/tests/unit_tests/server/ut-zone.cpp index 3cd6031..37f65be 100644 --- a/tests/unit_tests/server/ut-zone.cpp +++ b/tests/unit_tests/server/ut-zone.cpp @@ -111,7 +111,7 @@ BOOST_AUTO_TEST_CASE(StartStopTest) auto c = create(TEST_CONFIG_PATH); c->start(); ensureStarted(); - c->stop(); + c->stop(true); } BOOST_AUTO_TEST_CASE(DbusConnectionTest) @@ -121,7 +121,7 @@ BOOST_AUTO_TEST_CASE(DbusConnectionTest) auto c = create(TEST_DBUS_CONFIG_PATH); c->start(); ensureStarted(); - c->stop(); + c->stop(true); } // TODO: DbusReconnectionTest diff --git a/tests/unit_tests/server/ut-zones-manager.cpp b/tests/unit_tests/server/ut-zones-manager.cpp index e93ab84..0e40093 100644 --- a/tests/unit_tests/server/ut-zones-manager.cpp +++ b/tests/unit_tests/server/ut-zones-manager.cpp @@ -508,9 +508,9 @@ BOOST_AUTO_TEST_CASE(StartStopTest) cm.createZone("zone1", SIMPLE_TEMPLATE); cm.createZone("zone2", SIMPLE_TEMPLATE); - cm.startAll(); + cm.restoreAll(); BOOST_CHECK_EQUAL(cm.getRunningForegroundZoneId(), "zone1"); - cm.stopAll(); + cm.shutdownAll(); BOOST_CHECK_EQUAL(cm.getRunningForegroundZoneId(), ""); } @@ -520,13 +520,13 @@ BOOST_AUTO_TEST_CASE(DetachOnExitTest) ZonesManager cm(TEST_CONFIG_PATH); cm.createZone("zone1", SIMPLE_TEMPLATE); cm.createZone("zone2", SIMPLE_TEMPLATE); - cm.startAll(); + cm.restoreAll(); BOOST_CHECK_EQUAL(cm.getRunningForegroundZoneId(), "zone1"); cm.setZonesDetachOnExit(); } { ZonesManager cm(TEST_CONFIG_PATH); - cm.startAll(); + cm.restoreAll(); BOOST_CHECK_EQUAL(cm.getRunningForegroundZoneId(), "zone1"); } } @@ -537,7 +537,7 @@ BOOST_AUTO_TEST_CASE(FocusTest) cm.createZone("zone1", SIMPLE_TEMPLATE); cm.createZone("zone2", SIMPLE_TEMPLATE); cm.createZone("zone3", SIMPLE_TEMPLATE); - cm.startAll(); + cm.restoreAll(); BOOST_CHECK(cm.getRunningForegroundZoneId() == "zone1"); cm.focus("zone2"); @@ -554,7 +554,7 @@ BOOST_AUTO_TEST_CASE(NotifyActiveZoneTest) cm.createZone("zone1", DBUS_TEMPLATE); cm.createZone("zone2", DBUS_TEMPLATE); cm.createZone("zone3", DBUS_TEMPLATE); - cm.startAll(); + cm.restoreAll(); Latch signalReceivedLatch; std::map> signalReceivedSourcesMap; @@ -624,7 +624,7 @@ BOOST_AUTO_TEST_CASE(DisplayOffTest) cm.createZone("zone1", DBUS_TEMPLATE); cm.createZone("zone2", DBUS_TEMPLATE); cm.createZone("zone3", DBUS_TEMPLATE); - cm.startAll(); + cm.restoreAll(); std::vector> clients; for (int i = 1; i <= TEST_DBUS_CONNECTION_ZONES_COUNT; ++i) { @@ -661,7 +661,7 @@ BOOST_AUTO_TEST_CASE(MoveFileTest) cm.createZone("zone1", DBUS_TEMPLATE); cm.createZone("zone2", DBUS_TEMPLATE); cm.createZone("zone3", DBUS_TEMPLATE); - cm.startAll(); + cm.restoreAll(); Latch notificationLatch; std::string notificationSource; @@ -763,7 +763,7 @@ BOOST_AUTO_TEST_CASE(AllowSwitchToDefaultTest) cm.createZone("zone1", DBUS_TEMPLATE); cm.createZone("zone2", DBUS_TEMPLATE); cm.createZone("zone3", DBUS_TEMPLATE); - cm.startAll(); + cm.restoreAll(); std::vector> clients; for (int i = 1; i <= TEST_DBUS_CONNECTION_ZONES_COUNT; ++i) { @@ -812,7 +812,7 @@ BOOST_AUTO_TEST_CASE(ProxyCallTest) cm.createZone("zone1", DBUS_TEMPLATE); cm.createZone("zone2", DBUS_TEMPLATE); cm.createZone("zone3", DBUS_TEMPLATE); - cm.startAll(); + cm.restoreAll(); std::map> dbuses; for (int i = 0; i <= TEST_DBUS_CONNECTION_ZONES_COUNT; ++i) { @@ -904,9 +904,9 @@ BOOST_AUTO_TEST_CASE(GetZoneDbusesTest) cm.createZone("zone3", DBUS_TEMPLATE); BOOST_CHECK(EXPECTED_DBUSES_NONE == host.callMethodGetZoneDbuses()); - cm.startAll(); + cm.restoreAll(); BOOST_CHECK(EXPECTED_DBUSES_ALL == host.callMethodGetZoneDbuses()); - cm.stopAll(); + cm.shutdownAll(); BOOST_CHECK(EXPECTED_DBUSES_NONE == host.callMethodGetZoneDbuses()); } @@ -919,9 +919,9 @@ BOOST_AUTO_TEST_CASE(GetZoneDbusesNoDbusTest) cm.createZone("zone3", SIMPLE_TEMPLATE); BOOST_CHECK(EXPECTED_DBUSES_NONE == host.callMethodGetZoneDbuses()); - cm.startAll(); + cm.restoreAll(); BOOST_CHECK(EXPECTED_DBUSES_NONE == host.callMethodGetZoneDbuses()); - cm.stopAll(); + cm.shutdownAll(); BOOST_CHECK(EXPECTED_DBUSES_NONE == host.callMethodGetZoneDbuses()); } @@ -961,7 +961,7 @@ BOOST_AUTO_TEST_CASE(ZoneDbusesSignalsTest) BOOST_CHECK(signalLatch.empty()); BOOST_CHECK(collectedDbuses.empty()); - cm.startAll(); + cm.restoreAll(); BOOST_REQUIRE(signalLatch.waitForN(TEST_DBUS_CONNECTION_ZONES_COUNT, EVENT_TIMEOUT)); BOOST_CHECK(signalLatch.empty()); @@ -998,7 +998,7 @@ BOOST_AUTO_TEST_CASE(GetActiveZoneIdTest) cm.createZone("zone1", SIMPLE_TEMPLATE); cm.createZone("zone2", SIMPLE_TEMPLATE); cm.createZone("zone3", SIMPLE_TEMPLATE); - cm.startAll(); + cm.restoreAll(); DbusAccessory dbus(DbusAccessory::HOST_ID); @@ -1011,7 +1011,7 @@ BOOST_AUTO_TEST_CASE(GetActiveZoneIdTest) BOOST_CHECK(dbus.callMethodGetActiveZoneId() == zoneId); } - cm.stopAll(); + cm.shutdownAll(); BOOST_CHECK(dbus.callMethodGetActiveZoneId() == ""); } @@ -1021,7 +1021,7 @@ BOOST_AUTO_TEST_CASE(SetActiveZoneTest) cm.createZone("zone1", SIMPLE_TEMPLATE); cm.createZone("zone2", SIMPLE_TEMPLATE); cm.createZone("zone3", SIMPLE_TEMPLATE); - cm.startAll(); + cm.restoreAll(); DbusAccessory dbus(DbusAccessory::HOST_ID); @@ -1038,7 +1038,7 @@ BOOST_AUTO_TEST_CASE(SetActiveZoneTest) DbusException, WhatEquals("No such zone id")); - cm.stopAll(); + cm.shutdownAll(); BOOST_REQUIRE_EXCEPTION(dbus.callMethodSetActiveZone("zone1"), DbusException, WhatEquals("Could not activate stopped or paused zone")); @@ -1051,7 +1051,7 @@ BOOST_AUTO_TEST_CASE(CreateDestroyZoneTest) const std::string zone3 = "test3"; ZonesManager cm(TEST_CONFIG_PATH); - cm.startAll(); + cm.restoreAll(); BOOST_CHECK_EQUAL(cm.getRunningForegroundZoneId(), ""); @@ -1074,7 +1074,7 @@ BOOST_AUTO_TEST_CASE(CreateDestroyZoneTest) dbus.callAsyncMethodCreateZone(zone3, SIMPLE_TEMPLATE, resultCallback); BOOST_REQUIRE(callDone.wait(EVENT_TIMEOUT)); - cm.startAll(); + cm.restoreAll(); BOOST_CHECK_EQUAL(cm.getRunningForegroundZoneId(), zone1); cm.focus(zone3); @@ -1107,7 +1107,7 @@ BOOST_AUTO_TEST_CASE(CreateDestroyZonePersistenceTest) auto getZoneIds = []() -> std::vector { ZonesManager cm(TEST_CONFIG_PATH); - cm.startAll(); + cm.restoreAll(); DbusAccessory dbus(DbusAccessory::HOST_ID); return dbus.callMethodGetZoneIds(); @@ -1140,6 +1140,77 @@ BOOST_AUTO_TEST_CASE(CreateDestroyZonePersistenceTest) BOOST_CHECK(getZoneIds().empty()); } +BOOST_AUTO_TEST_CASE(ZoneStatePersistenceTest) +{ + const std::string zone1 = "zone1"; + const std::string zone2 = "zone2"; + const std::string zone3 = "zone3"; + const std::string zone4 = "zone4"; + const std::string zone5 = "zone5"; + + Latch callDone; + auto resultCallback = [&]() { + callDone.set(); + }; + + // firts run + { + ZonesManager cm(TEST_CONFIG_PATH); + DbusAccessory dbus(DbusAccessory::HOST_ID); + + // zone1 - created + dbus.callAsyncMethodCreateZone(zone1, SIMPLE_TEMPLATE, resultCallback); + BOOST_REQUIRE(callDone.wait(EVENT_TIMEOUT)); + + // zone2 - started + dbus.callAsyncMethodCreateZone(zone2, SIMPLE_TEMPLATE, resultCallback); + BOOST_REQUIRE(callDone.wait(EVENT_TIMEOUT)); + dbus.callAsyncMethodStartZone(zone2, resultCallback); + BOOST_REQUIRE(callDone.wait(EVENT_TIMEOUT)); + BOOST_CHECK(cm.isRunning(zone2)); + + // zone3 - started then paused + dbus.callAsyncMethodCreateZone(zone3, SIMPLE_TEMPLATE, resultCallback); + BOOST_REQUIRE(callDone.wait(EVENT_TIMEOUT)); + dbus.callAsyncMethodStartZone(zone3, resultCallback); + BOOST_REQUIRE(callDone.wait(EVENT_TIMEOUT)); + dbus.callMethodLockZone(zone3); + BOOST_CHECK(cm.isPaused(zone3)); + + // zone4 - started then stopped + dbus.callAsyncMethodCreateZone(zone4, SIMPLE_TEMPLATE, resultCallback); + BOOST_REQUIRE(callDone.wait(EVENT_TIMEOUT)); + dbus.callAsyncMethodStartZone(zone4, resultCallback); + BOOST_REQUIRE(callDone.wait(EVENT_TIMEOUT)); + dbus.callAsyncMethodShutdownZone(zone4, resultCallback); + BOOST_REQUIRE(callDone.wait(EVENT_TIMEOUT)); + BOOST_CHECK(cm.isStopped(zone4)); + + // zone5 - started then stopped then started + dbus.callAsyncMethodCreateZone(zone5, SIMPLE_TEMPLATE, resultCallback); + BOOST_REQUIRE(callDone.wait(EVENT_TIMEOUT)); + dbus.callAsyncMethodStartZone(zone5, resultCallback); + BOOST_REQUIRE(callDone.wait(EVENT_TIMEOUT)); + dbus.callAsyncMethodShutdownZone(zone5, resultCallback); + BOOST_REQUIRE(callDone.wait(EVENT_TIMEOUT)); + dbus.callAsyncMethodStartZone(zone5, resultCallback); + BOOST_REQUIRE(callDone.wait(EVENT_TIMEOUT)); + BOOST_CHECK(cm.isRunning(zone5)); + } + + // second run + { + ZonesManager cm(TEST_CONFIG_PATH); + cm.restoreAll(); + + BOOST_CHECK(cm.isRunning(zone1)); // because the default json value + BOOST_CHECK(cm.isRunning(zone2)); + BOOST_CHECK(cm.isPaused(zone3)); + BOOST_CHECK(cm.isStopped(zone4)); + BOOST_CHECK(cm.isRunning(zone5)); + } +} + BOOST_AUTO_TEST_CASE(StartShutdownZoneTest) { const std::string zone1 = "zone1"; @@ -1186,7 +1257,7 @@ BOOST_AUTO_TEST_CASE(LockUnlockZoneTest) cm.createZone("zone1", DBUS_TEMPLATE); cm.createZone("zone2", DBUS_TEMPLATE); cm.createZone("zone3", DBUS_TEMPLATE); - cm.startAll(); + cm.restoreAll(); DbusAccessory dbus(DbusAccessory::HOST_ID); @@ -1208,7 +1279,7 @@ BOOST_AUTO_TEST_CASE(LockUnlockZoneTest) DbusException, WhatEquals("No such zone id")); - cm.stopAll(); + cm.shutdownAll(); BOOST_REQUIRE_EXCEPTION(dbus.callMethodLockZone("zone1"), DbusException, WhatEquals("Zone is not running")); -- 2.7.4 From ff4f6693266208b5e9f5bfc69b256347ccca8d35 Mon Sep 17 00:00:00 2001 From: Jan Olszak Date: Wed, 4 Mar 2015 17:54:51 +0100 Subject: [PATCH 05/16] Coloring compiler's output [Feature] N/A [Cause] N/A [Solution] N/A [Verification] Build Change-Id: Ie417e1b7db193798b1f560bac33475be94f1ad4e --- CMakeLists.txt | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 67702a0..d5cb3e3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,12 +28,24 @@ INCLUDE(FindPkgConfig) ## default CMAKE_INSTALL_* variables ########################################### INCLUDE(GNUInstallDirs) +## Color output if it's possible: +IF (( "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" AND NOT (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.8.3)) + OR ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" )) + + ADD_DEFINITIONS("-fdiagnostics-color=always") +ENDIF() ## Compiler flags, depending on the build type ################################# IF(NOT CMAKE_BUILD_TYPE) SET(CMAKE_BUILD_TYPE "DEBUG") ENDIF(NOT CMAKE_BUILD_TYPE) -MESSAGE(STATUS "Build type: ${CMAKE_BUILD_TYPE}") + +## Print build information ##################################################### +MESSAGE(STATUS "-------------------------------------------------") +MESSAGE(STATUS "Compiler: " ${CMAKE_CXX_COMPILER_ID}) +MESSAGE(STATUS "Compiler version: " ${CMAKE_CXX_COMPILER_VERSION}) +MESSAGE(STATUS "Build type: " ${CMAKE_BUILD_TYPE}) +MESSAGE(STATUS "-------------------------------------------------") # special case for a GCC < 4.7, assume rest is fine IF("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.7) @@ -111,14 +123,12 @@ ADD_DEFINITIONS(-DDISK_GROUP="${DISK_GROUP}") ADD_DEFINITIONS(-DTTY_GROUP="${TTY_GROUP}") ## Python packages directory ################################################### - IF(NOT DEFINED PYTHON_SITELIB) EXECUTE_PROCESS(COMMAND python -c "from distutils.sysconfig import get_python_lib; import sys; sys.stdout.write(get_python_lib())" OUTPUT_VARIABLE PYTHON_SITELIB) ENDIF(NOT DEFINED PYTHON_SITELIB) - ## Subdirectories ############################################################## SET(COMMON_FOLDER ${PROJECT_SOURCE_DIR}/common) SET(CLIENT_FOLDER ${PROJECT_SOURCE_DIR}/client) -- 2.7.4 From 7396882735e82ef5f034c46286b0499d8f5437e7 Mon Sep 17 00:00:00 2001 From: Piotr Bartosiewicz Date: Mon, 9 Mar 2015 13:34:27 +0100 Subject: [PATCH 06/16] Epoll refactor [Bug/Feature] N/A [Cause] N/A [Solution] N/A [Verification] Run tests Change-Id: Iabb0557aa42ff09d0a2b3f9c36b451cf5fdad10f --- common/{utils => epoll}/event-poll.cpp | 6 +- common/{utils => epoll}/event-poll.hpp | 14 +-- common/epoll/events.cpp | 71 +++++++++++++++ common/epoll/events.hpp | 41 +++++++++ common/{utils => epoll}/glib-poll-dispatcher.cpp | 25 +++--- common/{utils => epoll}/glib-poll-dispatcher.hpp | 17 ++-- common/{utils => epoll}/thread-poll-dispatcher.cpp | 24 ++--- common/{utils => epoll}/thread-poll-dispatcher.hpp | 20 +++-- .../unit_tests/{utils => epoll}/ut-event-poll.cpp | 100 ++++++--------------- tests/unit_tests/ipc/ut-ipc.cpp | 1 - 10 files changed, 199 insertions(+), 120 deletions(-) rename common/{utils => epoll}/event-poll.cpp (98%) rename common/{utils => epoll}/event-poll.hpp (89%) create mode 100644 common/epoll/events.cpp create mode 100644 common/epoll/events.hpp rename common/{utils => epoll}/glib-poll-dispatcher.cpp (70%) rename common/{utils => epoll}/glib-poll-dispatcher.hpp (80%) rename common/{utils => epoll}/thread-poll-dispatcher.cpp (73%) rename common/{utils => epoll}/thread-poll-dispatcher.hpp (77%) rename tests/unit_tests/{utils => epoll}/ut-event-poll.cpp (58%) diff --git a/common/utils/event-poll.cpp b/common/epoll/event-poll.cpp similarity index 98% rename from common/utils/event-poll.cpp rename to common/epoll/event-poll.cpp index b37d727..dc1c1bc 100644 --- a/common/utils/event-poll.cpp +++ b/common/epoll/event-poll.cpp @@ -23,7 +23,7 @@ */ #include "config.hpp" -#include "utils/event-poll.hpp" +#include "epoll/event-poll.hpp" #include "utils/fd-utils.hpp" #include "utils/exception.hpp" #include "logger/logger.hpp" @@ -34,7 +34,7 @@ #include namespace vasum { -namespace utils { +namespace epoll { EventPoll::EventPoll() : mPollFD(::epoll_create1(EPOLL_CLOEXEC)) @@ -147,5 +147,5 @@ void EventPoll::removeFDInternal(const int fd) } } -} // namespace utils +} // namespace epoll } // namespace vasum diff --git a/common/utils/event-poll.hpp b/common/epoll/event-poll.hpp similarity index 89% rename from common/utils/event-poll.hpp rename to common/epoll/event-poll.hpp index 1a7ae2c..2d37aaa 100644 --- a/common/utils/event-poll.hpp +++ b/common/epoll/event-poll.hpp @@ -22,8 +22,10 @@ * @brief C++ epoll wrapper */ -#ifndef COMMON_UTILS_EVENT_POLL_HPP -#define COMMON_UTILS_EVENT_POLL_HPP +#ifndef COMMON_EPOLL_EVENT_POLL_HPP +#define COMMON_EPOLL_EVENT_POLL_HPP + +#include "epoll/events.hpp" #include #include @@ -31,12 +33,10 @@ #include namespace vasum { -namespace utils { +namespace epoll { class EventPoll { public: - - typedef unsigned int Events; typedef std::function Callback; EventPoll(); @@ -62,7 +62,7 @@ private: }; -} // namespace utils +} // namespace epoll } // namespace vasum -#endif // COMMON_UTILS_EVENT_POLL_HPP +#endif // COMMON_EPOLL_EVENT_POLL_HPP diff --git a/common/epoll/events.cpp b/common/epoll/events.cpp new file mode 100644 index 0000000..1ee27e0 --- /dev/null +++ b/common/epoll/events.cpp @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2015 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 Epoll events + */ + +#include "config.hpp" +#include "epoll/events.hpp" + +#include + +namespace vasum { +namespace epoll { + +namespace { + +std::string eventToString(Events event) +{ + switch (event) { + case EPOLLIN: return "IN"; + case EPOLLOUT: return "OUT"; + case EPOLLERR: return "ERR"; + case EPOLLHUP: return "HUP"; + case EPOLLRDHUP: return "RDHUP"; + default: + std::ostringstream ss; + ss << "0x" << std::hex << event; + return ss.str(); + } +} + +} // namespace + +std::string eventsToString(Events events) +{ + if (events == 0) { + return ""; + } + std::string ret; + for (unsigned int i = 0; i<32; ++i) { + Events event = 1u << i; + if (events & event) { + if (!ret.empty()) { + ret.append(", "); + } + ret.append(eventToString(event)); + } + } + return ret; +} + +} // namespace epoll +} // namespace vasum diff --git a/common/epoll/events.hpp b/common/epoll/events.hpp new file mode 100644 index 0000000..62eb00a --- /dev/null +++ b/common/epoll/events.hpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2015 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 Epoll events + */ + +#ifndef COMMON_EPOLL_EVENTS_HPP +#define COMMON_EPOLL_EVENTS_HPP + +#include +#include // for EPOLL* constatnts + +namespace vasum { +namespace epoll { + +typedef unsigned int Events; ///< bitmask of EPOLL* constants + +std::string eventsToString(Events events); + +} // namespace epoll +} // namespace vasum + +#endif // COMMON_EPOLL_EVENTS_HPP diff --git a/common/utils/glib-poll-dispatcher.cpp b/common/epoll/glib-poll-dispatcher.cpp similarity index 70% rename from common/utils/glib-poll-dispatcher.cpp rename to common/epoll/glib-poll-dispatcher.cpp index 5c66d16..cde535d 100644 --- a/common/utils/glib-poll-dispatcher.cpp +++ b/common/epoll/glib-poll-dispatcher.cpp @@ -23,22 +23,22 @@ */ #include "config.hpp" -#include "utils/glib-poll-dispatcher.hpp" +#include "epoll/glib-poll-dispatcher.hpp" #include "utils/callback-wrapper.hpp" namespace vasum { -namespace utils { +namespace epoll { -GlibPollDispatcher::GlibPollDispatcher(EventPoll& poll) +GlibPollDispatcher::GlibPollDispatcher() { - mChannel = g_io_channel_unix_new(poll.getPollFD()); + mChannel = g_io_channel_unix_new(mPoll.getPollFD()); - auto dispatchCallback = [&]() { - poll.dispatchIteration(0); + auto dispatchCallback = [this]() { + mPoll.dispatchIteration(0); }; auto cCallback = [](GIOChannel*, GIOCondition, gpointer data) -> gboolean { - getCallbackFromPointer(data)(); + utils::getCallbackFromPointer(data)(); return TRUE; }; @@ -46,8 +46,8 @@ GlibPollDispatcher::GlibPollDispatcher(EventPoll& poll) G_PRIORITY_DEFAULT, G_IO_IN, cCallback, - createCallbackWrapper(dispatchCallback, mGuard.spawn()), - &deleteCallbackWrapper); + utils::createCallbackWrapper(dispatchCallback, mGuard.spawn()), + &utils::deleteCallbackWrapper); } GlibPollDispatcher::~GlibPollDispatcher() @@ -57,5 +57,10 @@ GlibPollDispatcher::~GlibPollDispatcher() // mGuard destructor will wait for full unregister of dispatchCallback } -} // namespace utils +EventPoll& GlibPollDispatcher::getPoll() +{ + return mPoll; +} + +} // namespace epoll } // namespace vasum diff --git a/common/utils/glib-poll-dispatcher.hpp b/common/epoll/glib-poll-dispatcher.hpp similarity index 80% rename from common/utils/glib-poll-dispatcher.hpp rename to common/epoll/glib-poll-dispatcher.hpp index 07da0c3..cf300bb 100644 --- a/common/utils/glib-poll-dispatcher.hpp +++ b/common/epoll/glib-poll-dispatcher.hpp @@ -22,32 +22,35 @@ * @brief glib epoll dispatcher */ -#ifndef COMMON_UTILS_GLIB_POLL_DISPATCHER_HPP -#define COMMON_UTILS_GLIB_POLL_DISPATCHER_HPP +#ifndef COMMON_EPOLL_GLIB_POLL_DISPATCHER_HPP +#define COMMON_EPOLL_GLIB_POLL_DISPATCHER_HPP -#include "utils/event-poll.hpp" +#include "epoll/event-poll.hpp" #include "utils/callback-guard.hpp" #include namespace vasum { -namespace utils { +namespace epoll { /** * Will dispatch poll events in glib thread */ class GlibPollDispatcher { public: - GlibPollDispatcher(EventPoll& poll); + GlibPollDispatcher(); ~GlibPollDispatcher(); + + EventPoll& getPoll(); private: - CallbackGuard mGuard; + EventPoll mPoll; // before mGuard! + utils::CallbackGuard mGuard; GIOChannel* mChannel; guint mWatchId; }; -} // namespace utils +} // namespace epoll } // namespace vasum #endif // COMMON_UTILS_GLIB_POLL_DISPATCHER_HPP diff --git a/common/utils/thread-poll-dispatcher.cpp b/common/epoll/thread-poll-dispatcher.cpp similarity index 73% rename from common/utils/thread-poll-dispatcher.cpp rename to common/epoll/thread-poll-dispatcher.cpp index f350587..797a8c4 100644 --- a/common/utils/thread-poll-dispatcher.cpp +++ b/common/epoll/thread-poll-dispatcher.cpp @@ -23,23 +23,22 @@ */ #include "config.hpp" -#include "utils/thread-poll-dispatcher.hpp" - -#include +#include "epoll/thread-poll-dispatcher.hpp" namespace vasum { -namespace utils { +namespace epoll { -ThreadPollDispatcher::ThreadPollDispatcher(EventPoll& poll) - : mPoll(poll) - , mThread([&]{ poll.dispatchLoop(); }) +ThreadPollDispatcher::ThreadPollDispatcher() { - auto controlCallback = [this](int, EventPoll::Events) -> bool { + auto controlCallback = [this](int, Events) -> bool { mStopEvent.receive(); return false; // break the loop }; - poll.addFD(mStopEvent.getFD(), EPOLLIN, std::move(controlCallback)); + mPoll.addFD(mStopEvent.getFD(), EPOLLIN, std::move(controlCallback)); + mThread = std::thread([this] { + mPoll.dispatchLoop(); + }); } ThreadPollDispatcher::~ThreadPollDispatcher() @@ -49,5 +48,10 @@ ThreadPollDispatcher::~ThreadPollDispatcher() mPoll.removeFD(mStopEvent.getFD()); } -} // namespace utils +EventPoll& ThreadPollDispatcher::getPoll() +{ + return mPoll; +} + +} // namespace epoll } // namespace vasum diff --git a/common/utils/thread-poll-dispatcher.hpp b/common/epoll/thread-poll-dispatcher.hpp similarity index 77% rename from common/utils/thread-poll-dispatcher.hpp rename to common/epoll/thread-poll-dispatcher.hpp index e54cb4e..af6b278 100644 --- a/common/utils/thread-poll-dispatcher.hpp +++ b/common/epoll/thread-poll-dispatcher.hpp @@ -22,31 +22,33 @@ * @brief Thread epoll dispatcher */ -#ifndef COMMON_UTILS_THREAD_POLL_DISPATCHER_HPP -#define COMMON_UTILS_THREAD_POLL_DISPATCHER_HPP +#ifndef COMMON_EPOLL_THREAD_POLL_DISPATCHER_HPP +#define COMMON_EPOLL_THREAD_POLL_DISPATCHER_HPP -#include "utils/event-poll.hpp" +#include "epoll/event-poll.hpp" #include "utils/eventfd.hpp" #include namespace vasum { -namespace utils { +namespace epoll { /** * Will dispatch poll events in a newly created thread */ class ThreadPollDispatcher { public: - ThreadPollDispatcher(EventPoll& poll); + ThreadPollDispatcher(); ~ThreadPollDispatcher(); + + EventPoll& getPoll(); private: - EventPoll& mPoll; - EventFD mStopEvent; + EventPoll mPoll; + utils::EventFD mStopEvent; std::thread mThread; }; -} // namespace utils +} // namespace epoll } // namespace vasum -#endif // COMMON_UTILS_THREAD_POLL_DISPATCHER_HPP +#endif // COMMON_EPOLL_THREAD_POLL_DISPATCHER_HPP diff --git a/tests/unit_tests/utils/ut-event-poll.cpp b/tests/unit_tests/epoll/ut-event-poll.cpp similarity index 58% rename from tests/unit_tests/utils/ut-event-poll.cpp rename to tests/unit_tests/epoll/ut-event-poll.cpp index e387393..0bcbe81 100644 --- a/tests/unit_tests/utils/ut-event-poll.cpp +++ b/tests/unit_tests/epoll/ut-event-poll.cpp @@ -26,54 +26,21 @@ #include "config.hpp" #include "ut.hpp" -#include "utils/event-poll.hpp" +#include "epoll/event-poll.hpp" #include "logger/logger.hpp" #include "ipc/internals/socket.hpp" #include "utils/latch.hpp" #include "utils/glib-loop.hpp" -#include "utils/glib-poll-dispatcher.hpp" -#include "utils/thread-poll-dispatcher.hpp" - -#include -#include +#include "epoll/glib-poll-dispatcher.hpp" +#include "epoll/thread-poll-dispatcher.hpp" using namespace vasum::utils; +using namespace vasum::epoll; using namespace vasum::ipc; namespace { const int unsigned TIMEOUT = 1000; -#define ADD_EVENT(e) {EPOLL##e, #e} -const std::map EVENT_NAMES = { - ADD_EVENT(IN), - ADD_EVENT(OUT), - ADD_EVENT(ERR), - ADD_EVENT(HUP), - ADD_EVENT(RDHUP), -}; -#undef ADD_EVENT - -std::string strEvents(EventPoll::Events events) -{ - if (events == 0) { - return ""; - } - std::ostringstream ss; - for (const auto& p : EVENT_NAMES) { - if (events & p.first) { - ss << p.second << ", "; - events &= ~p.first; - } - } - if (events != 0) { - ss << std::hex << events; - return ss.str(); - } else { - std::string ret = ss.str(); - ret.resize(ret.size() - 2); - return ret; - } -} } // namespace @@ -87,28 +54,29 @@ BOOST_AUTO_TEST_CASE(EmptyPoll) BOOST_AUTO_TEST_CASE(ThreadedPoll) { - EventPoll poll; - ThreadPollDispatcher dispatcher(poll); + ThreadPollDispatcher dispatcher; } BOOST_AUTO_TEST_CASE(GlibPoll) { ScopedGlibLoop loop; - EventPoll poll; - GlibPollDispatcher dispatcher(poll); + GlibPollDispatcher dispatcher; } -void doSocketTest(EventPoll& poll, Latch& goodMessage, Latch& remoteClosed) +void doSocketTest(EventPoll& poll) { const std::string PATH = "/tmp/ut-poll.sock"; const std::string MESSAGE = "This is a test message"; + Latch goodMessage; + Latch remoteClosed; + Socket listen = Socket::createSocket(PATH); std::shared_ptr server; - auto serverCallback = [&](int, EventPoll::Events events) -> bool { - LOGD("Server events: " << strEvents(events)); + auto serverCallback = [&](int, Events events) -> bool { + LOGD("Server events: " << eventsToString(events)); if (events & EPOLLOUT) { server->write(MESSAGE.data(), MESSAGE.size()); @@ -118,8 +86,8 @@ void doSocketTest(EventPoll& poll, Latch& goodMessage, Latch& remoteClosed) return true; }; - auto listenCallback = [&](int, EventPoll::Events events) -> bool { - LOGD("Listen events: " << strEvents(events)); + auto listenCallback = [&](int, Events events) -> bool { + LOGD("Listen events: " << eventsToString(events)); if (events & EPOLLIN) { server = listen.accept(); poll.addFD(server->getFD(), EPOLLHUP | EPOLLRDHUP | EPOLLOUT, serverCallback); @@ -131,8 +99,8 @@ void doSocketTest(EventPoll& poll, Latch& goodMessage, Latch& remoteClosed) Socket client = Socket::connectSocket(PATH); - auto clientCallback = [&](int, EventPoll::Events events) -> bool { - LOGD("Client events: " << strEvents(events)); + auto clientCallback = [&](int, Events events) -> bool { + LOGD("Client events: " << eventsToString(events)); if (events & EPOLLIN) { std::string ret(MESSAGE.size(), 'x'); @@ -158,47 +126,33 @@ void doSocketTest(EventPoll& poll, Latch& goodMessage, Latch& remoteClosed) BOOST_AUTO_TEST_CASE(ThreadedPollSocket) { - Latch goodMessage; - Latch remoteClosed; + ThreadPollDispatcher dispatcher; - EventPoll poll; - ThreadPollDispatcher dispatcher(poll); - - doSocketTest(poll, goodMessage, remoteClosed); + doSocketTest(dispatcher.getPoll()); } BOOST_AUTO_TEST_CASE(GlibPollSocket) { - Latch goodMessage; - Latch remoteClosed; - ScopedGlibLoop loop; - EventPoll poll; - GlibPollDispatcher dispatcher(poll); + GlibPollDispatcher dispatcher; - doSocketTest(poll, goodMessage, remoteClosed); + doSocketTest(dispatcher.getPoll()); } BOOST_AUTO_TEST_CASE(PollStacking) { - Latch goodMessage; - Latch remoteClosed; + ThreadPollDispatcher dispatcher; - EventPoll outer; - EventPoll inner; + EventPoll innerPoll; - auto dispatchInner = [&](int, EventPoll::Events) -> bool { - inner.dispatchIteration(0); + auto dispatchInner = [&](int, Events) -> bool { + innerPoll.dispatchIteration(0); return true; }; - - outer.addFD(inner.getPollFD(), EPOLLIN, dispatchInner); - - ThreadPollDispatcher dispatcher(outer); - doSocketTest(inner, goodMessage, remoteClosed); - - outer.removeFD(inner.getPollFD()); + dispatcher.getPoll().addFD(innerPoll.getPollFD(), EPOLLIN, dispatchInner); + doSocketTest(innerPoll); + dispatcher.getPoll().removeFD(innerPoll.getPollFD()); } BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/unit_tests/ipc/ut-ipc.cpp b/tests/unit_tests/ipc/ut-ipc.cpp index fa0de22..088f576 100644 --- a/tests/unit_tests/ipc/ut-ipc.cpp +++ b/tests/unit_tests/ipc/ut-ipc.cpp @@ -70,7 +70,6 @@ const std::string SOCKET_PATH = TEST_DIR + "/test.socket"; struct Fixture { ScopedDir mTestPathGuard; - std::string SOCKET_PATH; Fixture() : mTestPathGuard(TEST_DIR) -- 2.7.4 From 02b066a8079eed2704dfc8afa1762bbc7faf29d4 Mon Sep 17 00:00:00 2001 From: Dariusz Michaluk Date: Tue, 3 Mar 2015 09:05:00 +0100 Subject: [PATCH 07/16] Disable dbus integration. [Bug/Feature] Disable dbus integration. [Cause] It is not used yet. Server and tests require a different dbus policy user, switching is inconvenient. [Solution] N/A [Verification] Build, run server, run tests Change-Id: I4e28b3b21d5eac6d23462ba2c4eb32aa7e32d4ed Signed-off-by: Dariusz Michaluk --- server/configs/lxc-templates/tizen-common-wayland.sh | 2 +- server/configs/templates/default.conf | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/server/configs/lxc-templates/tizen-common-wayland.sh b/server/configs/lxc-templates/tizen-common-wayland.sh index 9424dc4..5c572b7 100755 --- a/server/configs/lxc-templates/tizen-common-wayland.sh +++ b/server/configs/lxc-templates/tizen-common-wayland.sh @@ -297,6 +297,6 @@ ${path}/weston.ini etc/xdg/weston/weston.ini none ro,bind 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 /sys/fs/smackfs sys/fs/smackfs none rw,bind 0 0 -/var/run/zones/${name}/run var/run none rw,bind 0 0 +#/var/run/zones/${name}/run var/run none rw,bind 0 0 #tmpfs run tmpfs rw,nosuid,nodev,mode=755 0 0 EOF diff --git a/server/configs/templates/default.conf b/server/configs/templates/default.conf index 9eb0e08..42f587b 100644 --- a/server/configs/templates/default.conf +++ b/server/configs/templates/default.conf @@ -9,7 +9,7 @@ "privilege" : 10, "vt" : 0, "switchToDefaultAfterTimeout" : true, - "enableDbusIntegration" : true, + "enableDbusIntegration" : false, "runMountPoint" : "~NAME~/run", "provisions" : [], "permittedToSend" : [ "/tmp/.*" ], -- 2.7.4 From 8c8d3f3e21999353f84edb837b7a161dda71d9b3 Mon Sep 17 00:00:00 2001 From: Dariusz Michaluk Date: Wed, 4 Mar 2015 10:26:16 +0100 Subject: [PATCH 08/16] Add timeout after zone start. [Bug/Feature] Add timeout after zone start. [Cause] N/A [Solution] N/A [Verification] Build, run server, run tests Change-Id: Ie6adace3d36ad98756d8efc823a056356f41d02d Signed-off-by: Dariusz Michaluk --- server/zone-admin.cpp | 13 +++++++++++++ server/zone-admin.hpp | 1 + 2 files changed, 14 insertions(+) diff --git a/server/zone-admin.cpp b/server/zone-admin.cpp index 519b172..8005fb4 100644 --- a/server/zone-admin.cpp +++ b/server/zone-admin.cpp @@ -35,6 +35,8 @@ #include #include +#include +#include namespace vasum { @@ -55,6 +57,7 @@ ZoneAdmin::ZoneAdmin(const std::string& zoneId, const ZoneConfig& config, const ZoneDynamicConfig& dynamicConfig) : mConfig(config), + mDynamicConfig(dynamicConfig), mZone(zonesPath, zoneId), mId(zoneId), mDetachOnExit(false), @@ -138,6 +141,16 @@ void ZoneAdmin::start() throw ZoneOperationException("Could not start zone"); } + // Wait until the full platform launch with graphical stack. + // VT should be activated by a graphical stack. + // If we do it with 'zoneToFocus.activateVT' before starting the graphical stack, + // graphical stack initialization failed and we finally switch to the black screen. + // Skip waiting when graphical stack is not running (unit tests). + if (mDynamicConfig.vt > 0) { + // TODO, timeout is a temporary solution + std::this_thread::sleep_for(std::chrono::milliseconds(4000)); + } + LOGD(mId << ": Started"); } diff --git a/server/zone-admin.hpp b/server/zone-admin.hpp index 44d2613..ade4585 100644 --- a/server/zone-admin.hpp +++ b/server/zone-admin.hpp @@ -166,6 +166,7 @@ public: private: const ZoneConfig& mConfig; + const ZoneDynamicConfig& mDynamicConfig; lxc::LxcZone mZone; const std::string mId; bool mDetachOnExit; -- 2.7.4 From 0e37ce4957c03d1c4077ef1211f3e3799d50ad2e Mon Sep 17 00:00:00 2001 From: Dariusz Michaluk Date: Wed, 4 Mar 2015 14:28:11 +0100 Subject: [PATCH 09/16] Fix building rpm packages. [Bug/Feature] Not deleted configs after rpm packages erasing. [Cause] Configs are not marked in spec file. [Solution] Move configs to a single directory. Mark all configs in spec file. [Verification] Build, install, uninstall Change-Id: I9728eb67e756ceca1d5cad8078d0040e7018975b Signed-off-by: Dariusz Michaluk --- CMakeLists.txt | 1 - packaging/vasum.spec | 12 ++++++------ tests/unit_tests/CMakeLists.txt | 4 ++-- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d5cb3e3..743860b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -167,7 +167,6 @@ IF(NOT DEFINED DATA_DIR) ENDIF(NOT DEFINED DATA_DIR) SET(VSM_CONFIG_INSTALL_DIR ${SYSCONF_INSTALL_DIR}/vasum) -SET(VSM_DATA_INSTALL_DIR ${SHARE_INSTALL_PREFIX}/vasum) ADD_SUBDIRECTORY(${CLIENT_FOLDER}) ADD_SUBDIRECTORY(${SERVER_FOLDER}) diff --git a/packaging/vasum.spec b/packaging/vasum.spec index db8d6be..bd7e9f1 100644 --- a/packaging/vasum.spec +++ b/packaging/vasum.spec @@ -50,7 +50,7 @@ between them. A process from inside a zone can request a switch of context %{_unitdir}/vasum.service %{_unitdir}/vasum.socket %{_unitdir}/multi-user.target.wants/vasum.service -/etc/dbus-1/system.d/org.tizen.vasum.host.conf +%config /etc/dbus-1/system.d/org.tizen.vasum.host.conf %dir %{_datadir}/.zones %prep @@ -165,7 +165,7 @@ Zones support installed inside every zone. %files zone-support %manifest packaging/vasum-zone-support.manifest %defattr(644,root,root,755) -/etc/dbus-1/system.d/org.tizen.vasum.zone.conf +%config /etc/dbus-1/system.d/org.tizen.vasum.zone.conf ## Zone Daemon Package #################################################### @@ -182,7 +182,7 @@ Daemon running inside every zone. %manifest packaging/vasum-zone-daemon.manifest %defattr(644,root,root,755) %attr(755,root,root) %{_bindir}/vasum-zone-daemon -/etc/dbus-1/system.d/org.tizen.vasum.zone.daemon.conf +%config /etc/dbus-1/system.d/org.tizen.vasum.zone.daemon.conf ## Command Line Interface ###################################################### @@ -233,9 +233,9 @@ systemctl daemon-reload %attr(755,root,root) %{script_dir}/vsm_int_tests.py %attr(755,root,root) %{script_dir}/vsm_launch_test.py %{script_dir}/vsm_test_parser.py -%{_datadir}/vasum/tests -%attr(755,root,root) %{_datadir}/vasum/lxc-templates +%config /etc/vasum/tests +%attr(755,root,root) /etc/vasum/tests/lxc-templates %{python_sitelib}/vsm_integration_tests -/etc/dbus-1/system.d/org.tizen.vasum.tests.conf +%config /etc/dbus-1/system.d/org.tizen.vasum.tests.conf %{_unitdir}/vasum-socket-test.socket %{_unitdir}/vasum-socket-test.service diff --git a/tests/unit_tests/CMakeLists.txt b/tests/unit_tests/CMakeLists.txt index fc96586..2a561ed 100644 --- a/tests/unit_tests/CMakeLists.txt +++ b/tests/unit_tests/CMakeLists.txt @@ -67,8 +67,8 @@ TARGET_LINK_LIBRARIES(${UT_SERVER_CODENAME} ${UT_SERVER_DEPS_LIBRARIES} ${Boost_ TARGET_LINK_LIBRARIES(${SOCKET_TEST_CODENAME} ${UT_SERVER_DEPS_LIBRARIES} ${Boost_LIBRARIES}) ## Subdirectories ############################################################## -SET(VSM_TEST_CONFIG_INSTALL_DIR ${VSM_DATA_INSTALL_DIR}/tests) -SET(VSM_TEST_LXC_TEMPLATES_INSTALL_DIR ${VSM_DATA_INSTALL_DIR}/lxc-templates/tests) +SET(VSM_TEST_CONFIG_INSTALL_DIR ${VSM_CONFIG_INSTALL_DIR}/tests) +SET(VSM_TEST_LXC_TEMPLATES_INSTALL_DIR ${VSM_CONFIG_INSTALL_DIR}/tests/lxc-templates) ADD_DEFINITIONS(-DVSM_TEST_CONFIG_INSTALL_DIR="${VSM_TEST_CONFIG_INSTALL_DIR}") ADD_DEFINITIONS(-DVSM_TEST_LXC_TEMPLATES_INSTALL_DIR="${VSM_TEST_LXC_TEMPLATES_INSTALL_DIR}") -- 2.7.4 From 99314f1553605b796e6ae5b90466113f98e79360 Mon Sep 17 00:00:00 2001 From: Piotr Bartosiewicz Date: Tue, 10 Mar 2015 12:01:48 +0100 Subject: [PATCH 10/16] IPC works on epoll [Bug/Feature] N/A [Cause] N/A [Solution] N/A [Verification] Run tests Change-Id: I6f6b5a7a70cecbecbdf0c502d0f8618577892a48 --- common/epoll/event-poll.cpp | 15 +- ...lib-poll-dispatcher.cpp => glib-dispatcher.cpp} | 8 +- ...lib-poll-dispatcher.hpp => glib-dispatcher.hpp} | 12 +- ...d-poll-dispatcher.cpp => thread-dispatcher.cpp} | 8 +- ...d-poll-dispatcher.hpp => thread-dispatcher.hpp} | 12 +- common/ipc/client.cpp | 71 +++--- common/ipc/client.hpp | 35 +-- common/ipc/internals/acceptor.cpp | 92 +------- common/ipc/internals/acceptor.hpp | 34 --- common/ipc/internals/processor.cpp | 148 ++---------- common/ipc/internals/processor.hpp | 18 +- common/ipc/service.cpp | 97 +++----- common/ipc/service.hpp | 35 +-- tests/scripts/vsm_test_parser.py | 2 +- tests/unit_tests/epoll/ut-event-poll.cpp | 14 +- tests/unit_tests/ipc/ut-ipc.cpp | 250 ++++++++++----------- tests/unit_tests/ut.hpp | 14 ++ 17 files changed, 271 insertions(+), 594 deletions(-) rename common/epoll/{glib-poll-dispatcher.cpp => glib-dispatcher.cpp} (91%) rename common/epoll/{glib-poll-dispatcher.hpp => glib-dispatcher.hpp} (84%) rename common/epoll/{thread-poll-dispatcher.cpp => thread-dispatcher.cpp} (88%) rename common/epoll/{thread-poll-dispatcher.hpp => thread-dispatcher.hpp} (83%) diff --git a/common/epoll/event-poll.cpp b/common/epoll/event-poll.cpp index dc1c1bc..9eb9fa0 100644 --- a/common/epoll/event-poll.cpp +++ b/common/epoll/event-poll.cpp @@ -49,6 +49,9 @@ EventPoll::~EventPoll() { if (!mCallbacks.empty()) { LOGW("Not removed callbacks: " << mCallbacks.size()); + for (const auto& item : mCallbacks) { + LOGT("Not removed fd: " << item.first); + } assert(0 && "Not removed callbacks left"); } utils::close(mPollFD); @@ -73,7 +76,7 @@ void EventPoll::addFD(const int fd, const Events events, Callback&& callback) } mCallbacks.insert({fd, std::make_shared(std::move(callback))}); - LOGT("Callback added for " << fd); + LOGT("Callback added for fd: " << fd); } void EventPoll::removeFD(const int fd) @@ -87,7 +90,7 @@ void EventPoll::removeFD(const int fd) } mCallbacks.erase(iter); removeFDInternal(fd); - LOGT("Callback removed for " << fd); + LOGT("Callback removed for fd: " << fd); } bool EventPoll::dispatchIteration(const int timeoutMs) @@ -115,7 +118,13 @@ bool EventPoll::dispatchIteration(const int timeoutMs) // add ref because removeFD(self) can be called inside callback std::shared_ptr callback(iter->second); - return (*callback)(event.data.fd, event.events); + try { + LOGT("Dispatch fd: " << event.data.fd << ", events: " << eventsToString(event.events)); + return (*callback)(event.data.fd, event.events); + } catch (std::exception& e) { + LOGE("Got unexpected exception: " << e.what()); + assert(0 && "Callback should not throw any exceptions"); + } } } diff --git a/common/epoll/glib-poll-dispatcher.cpp b/common/epoll/glib-dispatcher.cpp similarity index 91% rename from common/epoll/glib-poll-dispatcher.cpp rename to common/epoll/glib-dispatcher.cpp index cde535d..9567abb 100644 --- a/common/epoll/glib-poll-dispatcher.cpp +++ b/common/epoll/glib-dispatcher.cpp @@ -23,13 +23,13 @@ */ #include "config.hpp" -#include "epoll/glib-poll-dispatcher.hpp" +#include "epoll/glib-dispatcher.hpp" #include "utils/callback-wrapper.hpp" namespace vasum { namespace epoll { -GlibPollDispatcher::GlibPollDispatcher() +GlibDispatcher::GlibDispatcher() { mChannel = g_io_channel_unix_new(mPoll.getPollFD()); @@ -50,14 +50,14 @@ GlibPollDispatcher::GlibPollDispatcher() &utils::deleteCallbackWrapper); } -GlibPollDispatcher::~GlibPollDispatcher() +GlibDispatcher::~GlibDispatcher() { g_source_remove(mWatchId); g_io_channel_unref(mChannel); // mGuard destructor will wait for full unregister of dispatchCallback } -EventPoll& GlibPollDispatcher::getPoll() +EventPoll& GlibDispatcher::getPoll() { return mPoll; } diff --git a/common/epoll/glib-poll-dispatcher.hpp b/common/epoll/glib-dispatcher.hpp similarity index 84% rename from common/epoll/glib-poll-dispatcher.hpp rename to common/epoll/glib-dispatcher.hpp index cf300bb..0fa7b3a 100644 --- a/common/epoll/glib-poll-dispatcher.hpp +++ b/common/epoll/glib-dispatcher.hpp @@ -22,8 +22,8 @@ * @brief glib epoll dispatcher */ -#ifndef COMMON_EPOLL_GLIB_POLL_DISPATCHER_HPP -#define COMMON_EPOLL_GLIB_POLL_DISPATCHER_HPP +#ifndef COMMON_EPOLL_GLIB_DISPATCHER_HPP +#define COMMON_EPOLL_GLIB_DISPATCHER_HPP #include "epoll/event-poll.hpp" #include "utils/callback-guard.hpp" @@ -36,10 +36,10 @@ namespace epoll { /** * Will dispatch poll events in glib thread */ -class GlibPollDispatcher { +class GlibDispatcher { public: - GlibPollDispatcher(); - ~GlibPollDispatcher(); + GlibDispatcher(); + ~GlibDispatcher(); EventPoll& getPoll(); private: @@ -53,4 +53,4 @@ private: } // namespace epoll } // namespace vasum -#endif // COMMON_UTILS_GLIB_POLL_DISPATCHER_HPP +#endif // COMMON_UTILS_GLIB_DISPATCHER_HPP diff --git a/common/epoll/thread-poll-dispatcher.cpp b/common/epoll/thread-dispatcher.cpp similarity index 88% rename from common/epoll/thread-poll-dispatcher.cpp rename to common/epoll/thread-dispatcher.cpp index 797a8c4..a82cad8 100644 --- a/common/epoll/thread-poll-dispatcher.cpp +++ b/common/epoll/thread-dispatcher.cpp @@ -23,12 +23,12 @@ */ #include "config.hpp" -#include "epoll/thread-poll-dispatcher.hpp" +#include "epoll/thread-dispatcher.hpp" namespace vasum { namespace epoll { -ThreadPollDispatcher::ThreadPollDispatcher() +ThreadDispatcher::ThreadDispatcher() { auto controlCallback = [this](int, Events) -> bool { mStopEvent.receive(); @@ -41,14 +41,14 @@ ThreadPollDispatcher::ThreadPollDispatcher() }); } -ThreadPollDispatcher::~ThreadPollDispatcher() +ThreadDispatcher::~ThreadDispatcher() { mStopEvent.send(); mThread.join(); mPoll.removeFD(mStopEvent.getFD()); } -EventPoll& ThreadPollDispatcher::getPoll() +EventPoll& ThreadDispatcher::getPoll() { return mPoll; } diff --git a/common/epoll/thread-poll-dispatcher.hpp b/common/epoll/thread-dispatcher.hpp similarity index 83% rename from common/epoll/thread-poll-dispatcher.hpp rename to common/epoll/thread-dispatcher.hpp index af6b278..7d0f30d 100644 --- a/common/epoll/thread-poll-dispatcher.hpp +++ b/common/epoll/thread-dispatcher.hpp @@ -22,8 +22,8 @@ * @brief Thread epoll dispatcher */ -#ifndef COMMON_EPOLL_THREAD_POLL_DISPATCHER_HPP -#define COMMON_EPOLL_THREAD_POLL_DISPATCHER_HPP +#ifndef COMMON_EPOLL_THREAD_DISPATCHER_HPP +#define COMMON_EPOLL_THREAD_DISPATCHER_HPP #include "epoll/event-poll.hpp" #include "utils/eventfd.hpp" @@ -36,10 +36,10 @@ namespace epoll { /** * Will dispatch poll events in a newly created thread */ -class ThreadPollDispatcher { +class ThreadDispatcher { public: - ThreadPollDispatcher(); - ~ThreadPollDispatcher(); + ThreadDispatcher(); + ~ThreadDispatcher(); EventPoll& getPoll(); private: @@ -51,4 +51,4 @@ private: } // namespace epoll } // namespace vasum -#endif // COMMON_EPOLL_THREAD_POLL_DISPATCHER_HPP +#endif // COMMON_EPOLL_THREAD_DISPATCHER_HPP diff --git a/common/ipc/client.cpp b/common/ipc/client.cpp index 6669d8a..abecb71 100644 --- a/common/ipc/client.cpp +++ b/common/ipc/client.cpp @@ -31,8 +31,9 @@ namespace vasum { namespace ipc { -Client::Client(const std::string& socketPath) - : mProcessor("[CLIENT] "), +Client::Client(epoll::EventPoll& eventPoll, const std::string& socketPath) + : mEventPoll(eventPoll), + mProcessor("[CLIENT] "), mSocketPath(socketPath) { LOGS("Client Constructor"); @@ -50,14 +51,19 @@ Client::~Client() } } -void Client::start(const bool usesExternalPolling) +void Client::start() { + if (mProcessor.isStarted()) { + return; + } LOGS("Client start"); // Initialize the connection with the server - if (usesExternalPolling) { - startPoll(); - } - mProcessor.start(usesExternalPolling); + auto handleEvent = [&](int, epoll::Events) -> bool { + mProcessor.handleEvent(); + return true; + }; + mEventPoll.addFD(mProcessor.getEventFD(), EPOLLIN, handleEvent); + mProcessor.start(); LOGD("Connecting to " + mSocketPath); auto socketPtr = std::make_shared(Socket::connectSocket(mSocketPath)); @@ -71,34 +77,18 @@ bool Client::isStarted() void Client::stop() { + if (!mProcessor.isStarted()) { + return; + } LOGS("Client stop"); mProcessor.stop(); - if (mIPCGSourcePtr) { - stopPoll(); - } -} - -void Client::startPoll() -{ - LOGS("Client startPoll"); - using namespace std::placeholders; - mIPCGSourcePtr = IPCGSource::create(std::bind(&Client::handle, this, _1, _2)); - mIPCGSourcePtr->addFD(mProcessor.getEventFD()); - mIPCGSourcePtr->attach(); + mEventPoll.removeFD(mProcessor.getEventFD()); } -void Client::stopPoll() -{ - LOGS("Client stopPoll"); - - mIPCGSourcePtr->removeFD(mProcessor.getEventFD()); - mIPCGSourcePtr->detach(); - mIPCGSourcePtr.reset(); -} - -void Client::handle(const FileDescriptor fd, const short pollEvent) +void Client::handle(const FileDescriptor fd, const epoll::Events pollEvents) { + //TODO remove handle method LOGS("Client handle"); if (!isStarted()) { @@ -106,17 +96,12 @@ void Client::handle(const FileDescriptor fd, const short pollEvent) return; } - if (fd == mProcessor.getEventFD() && (pollEvent & POLLIN)) { - mProcessor.handleEvent(); - return; - - } else if (pollEvent & POLLIN) { + if (pollEvents & EPOLLIN) { mProcessor.handleInput(fd); - return; + } - } else if (pollEvent & POLLHUP) { + if ((pollEvents & EPOLLHUP) || (pollEvents & EPOLLRDHUP)) { mProcessor.handleLostConnection(fd); - return; } } @@ -124,9 +109,11 @@ void Client::setNewPeerCallback(const PeerCallback& newPeerCallback) { LOGS("Client setNewPeerCallback"); auto callback = [newPeerCallback, this](PeerID peerID, FileDescriptor fd) { - if (mIPCGSourcePtr) { - mIPCGSourcePtr->addFD(fd); - } + auto handleFd = [&](FileDescriptor fd, epoll::Events events) -> bool { + handle(fd, events); + return true; + }; + mEventPoll.addFD(fd, EPOLLIN | EPOLLHUP | EPOLLRDHUP, handleFd); if (newPeerCallback) { newPeerCallback(peerID, fd); } @@ -138,9 +125,7 @@ void Client::setRemovedPeerCallback(const PeerCallback& removedPeerCallback) { LOGS("Client setRemovedPeerCallback"); auto callback = [removedPeerCallback, this](PeerID peerID, FileDescriptor fd) { - if (mIPCGSourcePtr) { - mIPCGSourcePtr->removeFD(fd); - } + mEventPoll.removeFD(fd); if (removedPeerCallback) { removedPeerCallback(peerID, fd); } diff --git a/common/ipc/client.hpp b/common/ipc/client.hpp index 321b4da..a3342b6 100644 --- a/common/ipc/client.hpp +++ b/common/ipc/client.hpp @@ -26,9 +26,9 @@ #define COMMON_IPC_CLIENT_HPP #include "ipc/internals/processor.hpp" -#include "ipc/ipc-gsource.hpp" #include "ipc/types.hpp" #include "ipc/result.hpp" +#include "epoll/event-poll.hpp" #include "logger/logger.hpp" #include @@ -40,28 +40,24 @@ namespace ipc { * This class wraps communication via UX sockets for client applications. * It uses serialization mechanism from libConfig. * - * There is one additional thread: - * - PROCESSOR is responsible for the communication and calling the callbacks - * * For message format @see ipc::Processor */ class Client { public: /** + * @param eventPoll event poll * @param serverPath path to the server's socket */ - Client(const std::string& serverPath); + Client(epoll::EventPoll& eventPoll, const std::string& serverPath); ~Client(); Client(const Client&) = delete; Client& operator=(const Client&) = delete; /** - * Starts the worker thread - * - * @param usesExternalPolling internal or external polling is used + * Starts processing */ - void start(const bool usesExternalPolling = false); + void start(); /** * @return is the communication thread running @@ -69,21 +65,11 @@ public: bool isStarted(); /** - * Stops all worker thread + * Stops processing */ void stop(); /** - * Used with an external polling loop. - * Handles one event from the file descriptor. - * - * @param fd file descriptor - * @param pollEvent event on the fd. Defined in poll.h - * - */ - void handle(const FileDescriptor fd, const short pollEvent); - - /** * Set the callback called for each new connection to a peer * * @param newPeerCallback the callback @@ -170,14 +156,13 @@ public: const std::shared_ptr& data); private: - - void startPoll(); - void stopPoll(); - + epoll::EventPoll& mEventPoll; PeerID mServiceID; Processor mProcessor; std::string mSocketPath; - IPCGSource::Pointer mIPCGSourcePtr; + + void handle(const FileDescriptor fd, const epoll::Events pollEvents); + }; template diff --git a/common/ipc/internals/acceptor.cpp b/common/ipc/internals/acceptor.cpp index ecb2210..15b9ad8 100644 --- a/common/ipc/internals/acceptor.cpp +++ b/common/ipc/internals/acceptor.cpp @@ -24,21 +24,14 @@ #include "config.hpp" -#include "ipc/exception.hpp" #include "ipc/internals/acceptor.hpp" #include "logger/logger.hpp" -#include -#include -#include -#include - namespace vasum { namespace ipc { Acceptor::Acceptor(const std::string& socketPath, const NewConnectionCallback& newConnectionCallback) - : mIsRunning(false), - mNewConnectionCallback(newConnectionCallback), + : mNewConnectionCallback(newConnectionCallback), mSocket(Socket::createSocket(socketPath)) { LOGT("Creating Acceptor for socket " << socketPath); @@ -46,98 +39,15 @@ Acceptor::Acceptor(const std::string& socketPath, const NewConnectionCallback& n Acceptor::~Acceptor() { - LOGT("Destroying Acceptor"); - try { - stop(); - } catch (std::exception& e) { - LOGE("Error in destructor: " << e.what()); - } LOGT("Destroyed Acceptor"); } -void Acceptor::start() -{ - LOGT("Starting Acceptor"); - if (!mThread.joinable()) { - mThread = std::thread(&Acceptor::run, this); - } - LOGT("Started Acceptor"); -} - -void Acceptor::stop() -{ - LOGT("Stopping Acceptor"); - - if (mThread.joinable()) { - mEventQueue.send(Event::FINISH); - LOGT("Waiting for Acceptor to finish"); - mThread.join(); - } - - LOGT("Stopped Acceptor"); -} - -void Acceptor::run() -{ - // Setup polling structure - std::vector fds(2); - - fds[0].fd = mEventQueue.getFD(); - fds[0].events = POLLIN; - - fds[1].fd = mSocket.getFD(); - fds[1].events = POLLIN; - - mIsRunning = true; - while (mIsRunning) { - LOGT("Waiting for new connections..."); - - int ret = ::poll(fds.data(), fds.size(), -1 /*blocking call*/); - - LOGT("...Incoming connection!"); - - if (ret == -1 || ret == 0) { - if (errno == EINTR) { - continue; - } - LOGE("Error in poll: " << std::string(strerror(errno))); - throw IPCException("Error in poll: " + std::string(strerror(errno))); - } - - // Check for incoming connections - if (fds[1].revents & POLLIN) { - fds[1].revents = 0; - handleConnection(); - } - - // Check for incoming events - if (fds[0].revents & POLLIN) { - fds[0].revents = 0; - handleEvent(); - } - } - LOGT("Exiting run"); -} - void Acceptor::handleConnection() { std::shared_ptr tmpSocket = mSocket.accept(); mNewConnectionCallback(tmpSocket); } -void Acceptor::handleEvent() -{ - if (mEventQueue.receive() == Event::FINISH) { - LOGD("Event FINISH"); - mIsRunning = false; - } -} - -FileDescriptor Acceptor::getEventFD() -{ - return mEventQueue.getFD(); -} - FileDescriptor Acceptor::getConnectionFD() { return mSocket.getFD(); diff --git a/common/ipc/internals/acceptor.hpp b/common/ipc/internals/acceptor.hpp index f87a0bb..a6e9ec8 100644 --- a/common/ipc/internals/acceptor.hpp +++ b/common/ipc/internals/acceptor.hpp @@ -28,11 +28,9 @@ #include "config.hpp" #include "ipc/internals/socket.hpp" -#include "ipc/internals/event-queue.hpp" #include "ipc/types.hpp" #include -#include namespace vasum { namespace ipc { @@ -59,51 +57,19 @@ public: Acceptor& operator=(const Acceptor&) = delete; /** - * Starts the thread accepting the new connections. - */ - void start(); - - /** - * Stops the accepting thread. - */ - void stop(); - - /** * Handle one incoming connection. * Used with external polling */ void handleConnection(); /** - * Handle one event from the internal event's queue - * Used with external polling - */ - void handleEvent(); - - /** - * @return file descriptor of internal event's queue - */ - FileDescriptor getEventFD(); - - /** * @return file descriptor for the connection socket */ FileDescriptor getConnectionFD(); private: - enum class Event : int { - FINISH // Shutdown request - }; - - bool mIsRunning; - NewConnectionCallback mNewConnectionCallback; Socket mSocket; - - EventQueue mEventQueue; - std::thread mThread; - - void run(); }; } // namespace ipc diff --git a/common/ipc/internals/processor.cpp b/common/ipc/internals/processor.cpp index 93f721f..587723c 100644 --- a/common/ipc/internals/processor.cpp +++ b/common/ipc/internals/processor.cpp @@ -26,7 +26,6 @@ #include "ipc/exception.hpp" #include "ipc/internals/processor.hpp" -#include "utils/signal.hpp" #include "utils/exception.hpp" #include @@ -65,8 +64,6 @@ Processor::Processor(const std::string& logName, { LOGS(mLogPrefix + "Processor Constructor"); - utils::signalBlock(SIGPIPE); - using namespace std::placeholders; setSignalHandlerInternal(REGISTER_SIGNAL_METHOD_ID, std::bind(&Processor::onNewSignals, this, _1, _2)); @@ -104,7 +101,7 @@ bool Processor::isStarted() return mIsRunning; } -void Processor::start(bool usesExternalPolling) +void Processor::start() { LOGS(mLogPrefix + "Processor start"); @@ -112,10 +109,6 @@ void Processor::start(bool usesExternalPolling) if (!mIsRunning) { LOGI(mLogPrefix + "Processor start"); mIsRunning = true; - mUsesExternalPolling = usesExternalPolling; - if (!usesExternalPolling) { - mThread = std::thread(&Processor::run, this); - } } } @@ -133,15 +126,12 @@ void Processor::stop() LOGD(mLogPrefix + "Waiting for the Processor to stop"); - if (mThread.joinable()) { - mThread.join(); - } else { - // Wait till the FINISH request is served - Lock lock(mStateMutex); - conditionPtr->wait(lock, [this]() { - return !mIsRunning; - }); - } + // Wait till the FINISH request is served + Lock lock(mStateMutex); + conditionPtr->wait(lock, [this]() { + return !mIsRunning; + }); + assert(mPeerInfo.empty()); } } @@ -204,7 +194,8 @@ PeerID Processor::addPeer(const std::shared_ptr& socketPtr) auto requestPtr = std::make_shared(socketPtr); mRequestQueue.pushBack(Event::ADD_PEER, requestPtr); - LOGI(mLogPrefix + "Add Peer Request. Id: " << requestPtr->peerID); + LOGI(mLogPrefix + "Add Peer Request. Id: " << requestPtr->peerID + << ", fd: " << socketPtr->getFD()); return requestPtr->peerID; } @@ -233,14 +224,14 @@ void Processor::removePeerSyncInternal(const PeerID peerID, Lock& lock) void Processor::removePeerInternal(Peers::iterator peerIt, const std::exception_ptr& exceptionPtr) { - LOGS(mLogPrefix + "Processor removePeerInternal peerID: " << peerIt->peerID); - LOGI(mLogPrefix + "Removing peer. peerID: " << peerIt->peerID); - if (peerIt == mPeerInfo.end()) { LOGW("Peer already removed"); return; } + LOGS(mLogPrefix + "Processor removePeerInternal peerID: " << peerIt->peerID); + LOGI(mLogPrefix + "Removing peer. peerID: " << peerIt->peerID); + // Remove from signal addressees for (auto it = mSignalsPeers.begin(); it != mSignalsPeers.end();) { it->second.remove(peerIt->peerID); @@ -270,100 +261,6 @@ void Processor::removePeerInternal(Peers::iterator peerIt, const std::exception_ mPeerInfo.erase(peerIt); } -void Processor::resetPolling() -{ - LOGS(mLogPrefix + "Processor resetPolling"); - - if (mUsesExternalPolling) { - return; - } - - // Setup polling on eventfd and sockets - mFDs.resize(mPeerInfo.size() + 1); - LOGI(mLogPrefix + "Reseting mFDS.size: " << mFDs.size()); - - mFDs[0].fd = mRequestQueue.getFD(); - mFDs[0].events = POLLIN; - - for (unsigned int i = 1; i < mFDs.size(); ++i) { - auto fd = mPeerInfo[i - 1].socketPtr->getFD(); - - LOGI(mLogPrefix + "Reseting fd: " << fd); - - mFDs[i].fd = fd; - mFDs[i].events = POLLIN | POLLHUP; // Listen for input events - // TODO: It's possible to block on writing to fd. Maybe listen for POLLOUT too? - } -} - -void Processor::run() -{ - LOGS(mLogPrefix + "Processor run"); - - { - Lock lock(mStateMutex); - resetPolling(); - } - - while (isStarted()) { - LOGT(mLogPrefix + "Waiting for communication..."); - int ret = poll(mFDs.data(), mFDs.size(), -1 /*blocking call*/); - LOGT(mLogPrefix + "... incoming communication!"); - if (ret == -1 || ret == 0) { - if (errno == EINTR) { - continue; - } - LOGE(mLogPrefix + "Error in poll: " << std::string(strerror(errno))); - throw IPCException("Error in poll: " + std::string(strerror(errno))); - } - - // Check for lost connections: - if (handleLostConnections()) { - // mFDs changed - resetPolling(); - continue; - } - - // Check for incoming data. - if (handleInputs()) { - // mFDs changed - resetPolling(); - continue; - } - - // Check for incoming events - if (mFDs[0].revents & POLLIN) { - mFDs[0].revents &= ~(POLLIN); - if (handleEvent()) { - // mFDs changed - resetPolling(); - continue; - } - } - - } -} - -bool Processor::handleLostConnections() -{ - Lock lock(mStateMutex); - - bool isPeerRemoved = false; - - for (unsigned int i = 1; i < mFDs.size(); ++i) { - if (mFDs[i].revents & POLLHUP) { - auto peerIt = getPeerInfoIterator(mFDs[i].fd); - LOGI(mLogPrefix + "Lost connection to peer: " << peerIt->peerID); - mFDs[i].revents &= ~(POLLHUP); - removePeerInternal(peerIt, - std::make_exception_ptr(IPCPeerDisconnectedException())); - isPeerRemoved = true; - } - } - - return isPeerRemoved; -} - bool Processor::handleLostConnection(const FileDescriptor fd) { Lock lock(mStateMutex); @@ -373,21 +270,6 @@ bool Processor::handleLostConnection(const FileDescriptor fd) return true; } -bool Processor::handleInputs() -{ - // Lock not needed, mFDs won't be changed by handleInput - - bool pollChanged = false; - for (unsigned int i = 1; i < mFDs.size(); ++i) { - if (mFDs[i].revents & POLLIN) { - mFDs[i].revents &= ~(POLLIN); - pollChanged = pollChanged || handleInput(mFDs[i].fd); - } - } - - return pollChanged; -} - bool Processor::handleInput(const FileDescriptor fd) { LOGS(mLogPrefix + "Processor handleInput fd: " << fd); @@ -795,6 +677,12 @@ bool Processor::onFinishRequest(FinishRequest& request) } } + // Close peers + while (!mPeerInfo.empty()) { + removePeerInternal(--mPeerInfo.end(), + std::make_exception_ptr(IPCClosingException())); + } + mIsRunning = false; request.conditionPtr->notify_all(); diff --git a/common/ipc/internals/processor.hpp b/common/ipc/internals/processor.hpp index 86a3f42..6e0ae81 100644 --- a/common/ipc/internals/processor.hpp +++ b/common/ipc/internals/processor.hpp @@ -43,7 +43,6 @@ #include "logger/logger-scope.hpp" #include -#include #include #include #include @@ -136,12 +135,9 @@ public: /** - * Start the processing thread. - * Quits immediately after starting the thread. - * - * @param usesExternalPolling internal or external polling is used + * Start processing. */ - void start(const bool usesExternalPolling); + void start(); /** * @return is processor running @@ -432,14 +428,12 @@ private: RequestQueue mRequestQueue; bool mIsRunning; - bool mUsesExternalPolling; std::unordered_map> mMethodsCallbacks; std::unordered_map> mSignalsCallbacks; std::unordered_map> mSignalsPeers; Peers mPeerInfo; - std::vector mFDs; std::unordered_map mReturnCallbacks; @@ -451,8 +445,6 @@ private: unsigned int mMaxNumberOfPeers; - std::thread mThread; - template void setMethodHandlerInternal(const MethodID methodID, const typename MethodHandler::type& process); @@ -466,8 +458,6 @@ private: const PeerID peerID, const std::shared_ptr& data); - void run(); - // Request handlers bool onMethodRequest(MethodRequest& request); bool onSignalRequest(SignalRequest& request); @@ -476,9 +466,6 @@ private: bool onSendResultRequest(SendResultRequest& request); bool onFinishRequest(FinishRequest& request); - bool handleLostConnections(); - bool handleInputs(); - bool onReturnValue(Peers::iterator& peerIt, const MessageID messageID); bool onRemoteMethod(Peers::iterator& peerIt, @@ -489,7 +476,6 @@ private: const MethodID methodID, const MessageID messageID, std::shared_ptr signalCallbacks); - void resetPolling(); void removePeerInternal(Peers::iterator peerIt, const std::exception_ptr& exceptionPtr); diff --git a/common/ipc/service.cpp b/common/ipc/service.cpp index 442d804..c436726 100644 --- a/common/ipc/service.cpp +++ b/common/ipc/service.cpp @@ -33,10 +33,12 @@ using namespace std::placeholders; namespace vasum { namespace ipc { -Service::Service(const std::string& socketPath, +Service::Service(epoll::EventPoll& eventPoll, + const std::string& socketPath, const PeerCallback& addPeerCallback, const PeerCallback& removePeerCallback) - : mProcessor("[SERVICE] "), + : mEventPoll(eventPoll), + mProcessor("[SERVICE] "), mAcceptor(socketPath, std::bind(&Processor::addPeer, &mProcessor, _1)) { @@ -55,19 +57,23 @@ Service::~Service() } } -void Service::start(const bool usesExternalPolling) +void Service::start() { - LOGS("Service start"); - if (usesExternalPolling) { - startPoll(); - } - mProcessor.start(usesExternalPolling); - - // There can be an incoming connection from mAcceptor before mProcessor is listening, - // but it's OK. It will handle the connection when ready. So no need to wait for mProcessor. - if (!usesExternalPolling) { - mAcceptor.start(); + if (mProcessor.isStarted()) { + return; } + LOGS("Service start"); + auto handleConnection = [&](int, epoll::Events) -> bool { + mAcceptor.handleConnection(); + return true; + }; + auto handleProcessorEvent = [&](int, epoll::Events) -> bool { + mProcessor.handleEvent(); + return true; + }; + mEventPoll.addFD(mAcceptor.getConnectionFD(), EPOLLIN, handleConnection); + mEventPoll.addFD(mProcessor.getEventFD(), EPOLLIN, handleProcessorEvent); + mProcessor.start(); } bool Service::isStarted() @@ -77,39 +83,19 @@ bool Service::isStarted() void Service::stop() { + if (!mProcessor.isStarted()) { + return; + } LOGS("Service stop"); - mAcceptor.stop(); mProcessor.stop(); - if (mIPCGSourcePtr) { - stopPoll(); - } -} - -void Service::startPoll() -{ - LOGS("Service startPoll"); - - mIPCGSourcePtr = IPCGSource::create(std::bind(&Service::handle, this, _1, _2)); - mIPCGSourcePtr->addFD(mAcceptor.getEventFD()); - mIPCGSourcePtr->addFD(mAcceptor.getConnectionFD()); - mIPCGSourcePtr->addFD(mProcessor.getEventFD()); - mIPCGSourcePtr->attach(); + mEventPoll.removeFD(mAcceptor.getConnectionFD()); + mEventPoll.removeFD(mProcessor.getEventFD()); } -void Service::stopPoll() -{ - LOGS("Service stopPoll"); - - mIPCGSourcePtr->removeFD(mAcceptor.getEventFD()); - mIPCGSourcePtr->removeFD(mAcceptor.getConnectionFD()); - mIPCGSourcePtr->removeFD(mProcessor.getEventFD()); - mIPCGSourcePtr->detach(); - mIPCGSourcePtr.reset(); -} - -void Service::handle(const FileDescriptor fd, const short pollEvent) +void Service::handle(const FileDescriptor fd, const epoll::Events pollEvents) { + //TODO remove handle method LOGS("Service handle"); if (!isStarted()) { @@ -117,25 +103,12 @@ void Service::handle(const FileDescriptor fd, const short pollEvent) return; } - if (fd == mProcessor.getEventFD() && (pollEvent & POLLIN)) { - mProcessor.handleEvent(); - return; - - } else if (fd == mAcceptor.getConnectionFD() && (pollEvent & POLLIN)) { - mAcceptor.handleConnection(); - return; - - } else if (fd == mAcceptor.getEventFD() && (pollEvent & POLLIN)) { - mAcceptor.handleEvent(); - return; - - } else if (pollEvent & POLLIN) { + if (pollEvents & EPOLLIN) { mProcessor.handleInput(fd); - return; + } - } else if (pollEvent & POLLHUP) { + if ((pollEvents & EPOLLHUP) || (pollEvents & EPOLLRDHUP)) { mProcessor.handleLostConnection(fd); - return; } } @@ -143,9 +116,11 @@ void Service::setNewPeerCallback(const PeerCallback& newPeerCallback) { LOGS("Service setNewPeerCallback"); auto callback = [newPeerCallback, this](PeerID peerID, FileDescriptor fd) { - if (mIPCGSourcePtr) { - mIPCGSourcePtr->addFD(fd); - } + auto handleFd = [&](FileDescriptor fd, epoll::Events events) -> bool { + handle(fd, events); + return true; + }; + mEventPoll.addFD(fd, EPOLLIN | EPOLLHUP | EPOLLRDHUP, handleFd); if (newPeerCallback) { newPeerCallback(peerID, fd); } @@ -157,9 +132,7 @@ void Service::setRemovedPeerCallback(const PeerCallback& removedPeerCallback) { LOGS("Service setRemovedPeerCallback"); auto callback = [removedPeerCallback, this](PeerID peerID, FileDescriptor fd) { - if (mIPCGSourcePtr) { - mIPCGSourcePtr->removeFD(fd); - } + mEventPoll.removeFD(fd); if (removedPeerCallback) { removedPeerCallback(peerID, fd); } diff --git a/common/ipc/service.hpp b/common/ipc/service.hpp index 022c9c9..880c137 100644 --- a/common/ipc/service.hpp +++ b/common/ipc/service.hpp @@ -27,9 +27,9 @@ #include "ipc/internals/processor.hpp" #include "ipc/internals/acceptor.hpp" -#include "ipc/ipc-gsource.hpp" #include "ipc/types.hpp" #include "ipc/result.hpp" +#include "epoll/event-poll.hpp" #include "logger/logger.hpp" #include @@ -42,18 +42,16 @@ namespace ipc { * This class wraps communication via UX sockets. * It uses serialization mechanism from libConfig. * - * There are two working threads: - * - ACCEPTOR accepts incoming connections and passes them to PROCESSOR - * - PROCESSOR is responsible for the communication and calling the callbacks - * * For message format @see ipc::Processor */ class Service { public: /** + * @param eventPoll event poll * @param path path to the socket */ - Service(const std::string& path, + Service(epoll::EventPoll& eventPoll, + const std::string& path, const PeerCallback& addPeerCallback = nullptr, const PeerCallback& removePeerCallback = nullptr); ~Service(); @@ -62,11 +60,9 @@ public: Service& operator=(const Service&) = delete; /** - * Starts the worker and acceptor threads - * - * @param usesExternalPolling internal or external polling is used + * Starts processing */ - void start(const bool usesExternalPolling = false); + void start(); /** * @return is the communication thread running @@ -79,16 +75,6 @@ public: void stop(); /** - * Used with an external polling loop. - * Handles one event from the file descriptor. - * - * @param fd file descriptor - * @param pollEvent event on the fd. Defined in poll.h - * - */ - void handle(const FileDescriptor fd, const short pollEvent); - - /** * Set the callback called for each new connection to a peer * * @param newPeerCallback the callback @@ -175,14 +161,11 @@ public: void signal(const MethodID methodID, const std::shared_ptr& data); private: - - void startPoll(); - void stopPoll(); - - typedef std::lock_guard Lock; + epoll::EventPoll& mEventPoll; Processor mProcessor; Acceptor mAcceptor; - IPCGSource::Pointer mIPCGSourcePtr; + + void handle(const FileDescriptor fd, const epoll::Events pollEvents); }; diff --git a/tests/scripts/vsm_test_parser.py b/tests/scripts/vsm_test_parser.py index 41dd495..d0d4fa7 100644 --- a/tests/scripts/vsm_test_parser.py +++ b/tests/scripts/vsm_test_parser.py @@ -35,7 +35,7 @@ class Logger(object): __indentChar = " " def testCaseSummary(self, testSuite, testName, testResult, recLevel): - msg = self.__indentChar * recLevel + BOLD + "{:<50}".format(testName) + msg = self.__indentChar * recLevel + BOLD + "{:<50} ".format(testName) if testResult == "passed": msg += GREEN diff --git a/tests/unit_tests/epoll/ut-event-poll.cpp b/tests/unit_tests/epoll/ut-event-poll.cpp index 0bcbe81..00cb385 100644 --- a/tests/unit_tests/epoll/ut-event-poll.cpp +++ b/tests/unit_tests/epoll/ut-event-poll.cpp @@ -31,8 +31,8 @@ #include "ipc/internals/socket.hpp" #include "utils/latch.hpp" #include "utils/glib-loop.hpp" -#include "epoll/glib-poll-dispatcher.hpp" -#include "epoll/thread-poll-dispatcher.hpp" +#include "epoll/glib-dispatcher.hpp" +#include "epoll/thread-dispatcher.hpp" using namespace vasum::utils; using namespace vasum::epoll; @@ -54,14 +54,14 @@ BOOST_AUTO_TEST_CASE(EmptyPoll) BOOST_AUTO_TEST_CASE(ThreadedPoll) { - ThreadPollDispatcher dispatcher; + ThreadDispatcher dispatcher; } BOOST_AUTO_TEST_CASE(GlibPoll) { ScopedGlibLoop loop; - GlibPollDispatcher dispatcher; + GlibDispatcher dispatcher; } void doSocketTest(EventPoll& poll) @@ -126,7 +126,7 @@ void doSocketTest(EventPoll& poll) BOOST_AUTO_TEST_CASE(ThreadedPollSocket) { - ThreadPollDispatcher dispatcher; + ThreadDispatcher dispatcher; doSocketTest(dispatcher.getPoll()); } @@ -135,14 +135,14 @@ BOOST_AUTO_TEST_CASE(GlibPollSocket) { ScopedGlibLoop loop; - GlibPollDispatcher dispatcher; + GlibDispatcher dispatcher; doSocketTest(dispatcher.getPoll()); } BOOST_AUTO_TEST_CASE(PollStacking) { - ThreadPollDispatcher dispatcher; + ThreadDispatcher dispatcher; EventPoll innerPoll; diff --git a/tests/unit_tests/ipc/ut-ipc.cpp b/tests/unit_tests/ipc/ut-ipc.cpp index 088f576..8f47a65 100644 --- a/tests/unit_tests/ipc/ut-ipc.cpp +++ b/tests/unit_tests/ipc/ut-ipc.cpp @@ -34,6 +34,8 @@ #include "ipc/client.hpp" #include "ipc/types.hpp" #include "ipc/result.hpp" +#include "epoll/thread-dispatcher.hpp" +#include "epoll/glib-dispatcher.hpp" #include "utils/glib-loop.hpp" #include "utils/latch.hpp" #include "utils/value-latch.hpp" @@ -51,11 +53,10 @@ using namespace vasum; using namespace vasum::ipc; +using namespace vasum::epoll; using namespace vasum::utils; using namespace std::placeholders; -namespace { - // Timeout for sending one message const int TIMEOUT = 1000 /*ms*/; @@ -68,15 +69,28 @@ const int LONG_OPERATION_TIME = 1000 + TIMEOUT; const std::string TEST_DIR = "/tmp/ut-ipc"; const std::string SOCKET_PATH = TEST_DIR + "/test.socket"; -struct Fixture { +struct FixtureBase { ScopedDir mTestPathGuard; - Fixture() + FixtureBase() : mTestPathGuard(TEST_DIR) { } }; +struct ThreadedFixture : FixtureBase { + ThreadDispatcher dispatcher; + + EventPoll& getPoll() { return dispatcher.getPoll(); } +}; + +struct GlibFixture : FixtureBase { + ScopedGlibLoop glibLoop; + GlibDispatcher dispatcher; + + EventPoll& getPoll() { return dispatcher.getPoll(); } +}; + struct SendData { int intVal; SendData(int i): intVal(i) {} @@ -167,7 +181,7 @@ void longEchoCallback(const PeerID, methodResult->set(returnData); } -PeerID connect(Service& s, Client& c, bool isServiceGlib = false, bool isClientGlib = false) +PeerID connect(Service& s, Client& c) { // Connects the Client to the Service and returns Clients PeerID ValueLatch peerIDLatch; @@ -178,10 +192,10 @@ PeerID connect(Service& s, Client& c, bool isServiceGlib = false, bool isClientG s.setNewPeerCallback(newPeerCallback); if (!s.isStarted()) { - s.start(isServiceGlib); + s.start(); } - c.start(isClientGlib); + c.start(); PeerID peerID = peerIDLatch.get(TIMEOUT); s.setNewPeerCallback(nullptr); @@ -189,16 +203,6 @@ PeerID connect(Service& s, Client& c, bool isServiceGlib = false, bool isClientG return peerID; } -PeerID connectServiceGSource(Service& s, Client& c) -{ - return connect(s, c, true, false); -} - -PeerID connectClientGSource(Service& s, Client& c) -{ - return connect(s, c, false, true); -} - void testEcho(Client& c, const MethodID methodID) { std::shared_ptr sentData(new SendData(34)); @@ -215,20 +219,17 @@ void testEcho(Service& s, const MethodID methodID, const PeerID peerID) BOOST_CHECK_EQUAL(recvData->intVal, sentData->intVal); } -} // namespace - - -BOOST_FIXTURE_TEST_SUITE(IPCSuite, Fixture) +BOOST_AUTO_TEST_SUITE(IPCSuite) -BOOST_AUTO_TEST_CASE(ConstructorDestructor) +MULTI_FIXTURE_TEST_CASE(ConstructorDestructor, F, ThreadedFixture, GlibFixture) { - Service s(SOCKET_PATH); - Client c(SOCKET_PATH); + Service s(F::getPoll(), SOCKET_PATH); + Client c(F::getPoll(), SOCKET_PATH); } -BOOST_AUTO_TEST_CASE(ServiceAddRemoveMethod) +MULTI_FIXTURE_TEST_CASE(ServiceAddRemoveMethod, F, ThreadedFixture, GlibFixture) { - Service s(SOCKET_PATH); + Service s(F::getPoll(), SOCKET_PATH); s.setMethodHandler(1, returnEmptyCallback); s.setMethodHandler(1, returnDataCallback); @@ -237,7 +238,7 @@ BOOST_AUTO_TEST_CASE(ServiceAddRemoveMethod) s.setMethodHandler(1, echoCallback); s.setMethodHandler(2, returnDataCallback); - Client c(SOCKET_PATH); + Client c(F::getPoll(), SOCKET_PATH); connect(s, c); testEcho(c, 1); @@ -247,10 +248,10 @@ BOOST_AUTO_TEST_CASE(ServiceAddRemoveMethod) BOOST_CHECK_THROW(testEcho(c, 2), IPCException); } -BOOST_AUTO_TEST_CASE(ClientAddRemoveMethod) +MULTI_FIXTURE_TEST_CASE(ClientAddRemoveMethod, F, ThreadedFixture, GlibFixture) { - Service s(SOCKET_PATH); - Client c(SOCKET_PATH); + Service s(F::getPoll(), SOCKET_PATH); + Client c(F::getPoll(), SOCKET_PATH); c.setMethodHandler(1, returnEmptyCallback); c.setMethodHandler(1, returnDataCallback); @@ -267,9 +268,9 @@ BOOST_AUTO_TEST_CASE(ClientAddRemoveMethod) BOOST_CHECK_THROW(testEcho(s, 1, peerID), IPCException); } -BOOST_AUTO_TEST_CASE(ServiceStartStop) +MULTI_FIXTURE_TEST_CASE(ServiceStartStop, F, ThreadedFixture, GlibFixture) { - Service s(SOCKET_PATH); + Service s(F::getPoll(), SOCKET_PATH); s.setMethodHandler(1, returnDataCallback); @@ -282,10 +283,10 @@ BOOST_AUTO_TEST_CASE(ServiceStartStop) s.start(); } -BOOST_AUTO_TEST_CASE(ClientStartStop) +MULTI_FIXTURE_TEST_CASE(ClientStartStop, F, ThreadedFixture, GlibFixture) { - Service s(SOCKET_PATH); - Client c(SOCKET_PATH); + Service s(F::getPoll(), SOCKET_PATH); + Client c(F::getPoll(), SOCKET_PATH); c.setMethodHandler(1, returnDataCallback); c.start(); @@ -300,27 +301,27 @@ BOOST_AUTO_TEST_CASE(ClientStartStop) c.stop(); } -BOOST_AUTO_TEST_CASE(SyncClientToServiceEcho) +MULTI_FIXTURE_TEST_CASE(SyncClientToServiceEcho, F, ThreadedFixture, GlibFixture) { - Service s(SOCKET_PATH); + Service s(F::getPoll(), SOCKET_PATH); s.setMethodHandler(1, echoCallback); s.setMethodHandler(2, echoCallback); - Client c(SOCKET_PATH); + Client c(F::getPoll(), SOCKET_PATH); connect(s, c); testEcho(c, 1); testEcho(c, 2); } -BOOST_AUTO_TEST_CASE(Restart) +MULTI_FIXTURE_TEST_CASE(Restart, F, ThreadedFixture, GlibFixture) { - Service s(SOCKET_PATH); + Service s(F::getPoll(), SOCKET_PATH); s.setMethodHandler(1, echoCallback); s.start(); s.setMethodHandler(2, echoCallback); - Client c(SOCKET_PATH); + Client c(F::getPoll(), SOCKET_PATH); c.start(); testEcho(c, 1); testEcho(c, 2); @@ -334,14 +335,19 @@ BOOST_AUTO_TEST_CASE(Restart) s.stop(); s.start(); + BOOST_CHECK_THROW(testEcho(c, 2), IPCException); + + c.stop(); + c.start(); + testEcho(c, 1); testEcho(c, 2); } -BOOST_AUTO_TEST_CASE(SyncServiceToClientEcho) +MULTI_FIXTURE_TEST_CASE(SyncServiceToClientEcho, F, ThreadedFixture, GlibFixture) { - Service s(SOCKET_PATH); - Client c(SOCKET_PATH); + Service s(F::getPoll(), SOCKET_PATH); + Client c(F::getPoll(), SOCKET_PATH); c.setMethodHandler(1, echoCallback); PeerID peerID = connect(s, c); @@ -351,16 +357,16 @@ BOOST_AUTO_TEST_CASE(SyncServiceToClientEcho) BOOST_CHECK_EQUAL(recvData->intVal, sentData->intVal); } -BOOST_AUTO_TEST_CASE(AsyncClientToServiceEcho) +MULTI_FIXTURE_TEST_CASE(AsyncClientToServiceEcho, F, ThreadedFixture, GlibFixture) { std::shared_ptr sentData(new SendData(34)); ValueLatch> recvDataLatch; // Setup Service and Client - Service s(SOCKET_PATH); + Service s(F::getPoll(), SOCKET_PATH); s.setMethodHandler(1, echoCallback); s.start(); - Client c(SOCKET_PATH); + Client c(F::getPoll(), SOCKET_PATH); c.start(); //Async call @@ -374,13 +380,13 @@ BOOST_AUTO_TEST_CASE(AsyncClientToServiceEcho) BOOST_CHECK_EQUAL(recvData->intVal, sentData->intVal); } -BOOST_AUTO_TEST_CASE(AsyncServiceToClientEcho) +MULTI_FIXTURE_TEST_CASE(AsyncServiceToClientEcho, F, ThreadedFixture, GlibFixture) { std::shared_ptr sentData(new SendData(56)); ValueLatch> recvDataLatch; - Service s(SOCKET_PATH); - Client c(SOCKET_PATH); + Service s(F::getPoll(), SOCKET_PATH); + Client c(F::getPoll(), SOCKET_PATH); c.setMethodHandler(1, echoCallback); PeerID peerID = connect(s, c); @@ -397,24 +403,24 @@ BOOST_AUTO_TEST_CASE(AsyncServiceToClientEcho) } -BOOST_AUTO_TEST_CASE(SyncTimeout) +MULTI_FIXTURE_TEST_CASE(SyncTimeout, F, ThreadedFixture, GlibFixture) { - Service s(SOCKET_PATH); + Service s(F::getPoll(), SOCKET_PATH); s.setMethodHandler(1, longEchoCallback); - Client c(SOCKET_PATH); + Client c(F::getPoll(), SOCKET_PATH); connect(s, c); std::shared_ptr sentData(new SendData(78)); BOOST_REQUIRE_THROW((c.callSync(1, sentData, TIMEOUT)), IPCException); } -BOOST_AUTO_TEST_CASE(SerializationError) +MULTI_FIXTURE_TEST_CASE(SerializationError, F, ThreadedFixture, GlibFixture) { - Service s(SOCKET_PATH); + Service s(F::getPoll(), SOCKET_PATH); s.setMethodHandler(1, echoCallback); - Client c(SOCKET_PATH); + Client c(F::getPoll(), SOCKET_PATH); connect(s, c); std::shared_ptr throwingData(new ThrowOnAcceptData()); @@ -423,23 +429,24 @@ BOOST_AUTO_TEST_CASE(SerializationError) } -BOOST_AUTO_TEST_CASE(ParseError) +MULTI_FIXTURE_TEST_CASE(ParseError, F, ThreadedFixture, GlibFixture) { - Service s(SOCKET_PATH); + Service s(F::getPoll(), SOCKET_PATH); s.setMethodHandler(1, echoCallback); s.start(); - Client c(SOCKET_PATH); + Client c(F::getPoll(), SOCKET_PATH); c.start(); std::shared_ptr sentData(new SendData(78)); BOOST_CHECK_THROW((c.callSync(1, sentData, 10000)), IPCParsingException); } -BOOST_AUTO_TEST_CASE(DisconnectedPeerError) +MULTI_FIXTURE_TEST_CASE(DisconnectedPeerError, F, ThreadedFixture, GlibFixture) { ValueLatch> retStatusLatch; - Service s(SOCKET_PATH); + + Service s(F::getPoll(), SOCKET_PATH); auto method = [](const PeerID, std::shared_ptr&, MethodResult::Pointer methodResult) { auto resultData = std::make_shared(1); @@ -450,7 +457,7 @@ BOOST_AUTO_TEST_CASE(DisconnectedPeerError) s.setMethodHandler(1, method); s.start(); - Client c(SOCKET_PATH); + Client c(F::getPoll(), SOCKET_PATH); c.start(); auto dataBack = [&retStatusLatch](Result && r) { @@ -470,16 +477,16 @@ BOOST_AUTO_TEST_CASE(DisconnectedPeerError) } -BOOST_AUTO_TEST_CASE(ReadTimeout) +MULTI_FIXTURE_TEST_CASE(ReadTimeout, F, ThreadedFixture, GlibFixture) { - Service s(SOCKET_PATH); + Service s(F::getPoll(), SOCKET_PATH); auto longEchoCallback = [](const PeerID, std::shared_ptr& data, MethodResult::Pointer methodResult) { auto resultData = std::make_shared(data->intVal, LONG_OPERATION_TIME); methodResult->set(resultData); }; s.setMethodHandler(1, longEchoCallback); - Client c(SOCKET_PATH); + Client c(F::getPoll(), SOCKET_PATH); connect(s, c); // Test timeout on read @@ -488,13 +495,13 @@ BOOST_AUTO_TEST_CASE(ReadTimeout) } -BOOST_AUTO_TEST_CASE(WriteTimeout) +MULTI_FIXTURE_TEST_CASE(WriteTimeout, F, ThreadedFixture, GlibFixture) { - Service s(SOCKET_PATH); + Service s(F::getPoll(), SOCKET_PATH); s.setMethodHandler(1, echoCallback); s.start(); - Client c(SOCKET_PATH); + Client c(F::getPoll(), SOCKET_PATH); c.start(); // Test echo with a minimal timeout @@ -509,13 +516,13 @@ BOOST_AUTO_TEST_CASE(WriteTimeout) } -BOOST_AUTO_TEST_CASE(AddSignalInRuntime) +MULTI_FIXTURE_TEST_CASE(AddSignalInRuntime, F, ThreadedFixture, GlibFixture) { ValueLatch> recvDataLatchA; ValueLatch> recvDataLatchB; - Service s(SOCKET_PATH); - Client c(SOCKET_PATH); + Service s(F::getPoll(), SOCKET_PATH); + Client c(F::getPoll(), SOCKET_PATH); connect(s, c); auto handlerA = [&recvDataLatchA](const PeerID, std::shared_ptr& data) { @@ -545,13 +552,13 @@ BOOST_AUTO_TEST_CASE(AddSignalInRuntime) } -BOOST_AUTO_TEST_CASE(AddSignalOffline) +MULTI_FIXTURE_TEST_CASE(AddSignalOffline, F, ThreadedFixture, GlibFixture) { ValueLatch> recvDataLatchA; ValueLatch> recvDataLatchB; - Service s(SOCKET_PATH); - Client c(SOCKET_PATH); + Service s(F::getPoll(), SOCKET_PATH); + Client c(F::getPoll(), SOCKET_PATH); auto handlerA = [&recvDataLatchA](const PeerID, std::shared_ptr& data) { recvDataLatchA.set(data); @@ -581,66 +588,13 @@ BOOST_AUTO_TEST_CASE(AddSignalOffline) BOOST_CHECK_EQUAL(recvDataB->intVal, sendDataB->intVal); } - -BOOST_AUTO_TEST_CASE(ServiceGSource) -{ - utils::Latch l; - ScopedGlibLoop loop; - - auto signalHandler = [&l](const PeerID, std::shared_ptr&) { - l.set(); - }; - - Service s(SOCKET_PATH); - s.setMethodHandler(1, echoCallback); - - Client c(SOCKET_PATH); - s.setSignalHandler(2, signalHandler); - - connectServiceGSource(s, c); - - testEcho(c, 1); - - auto data = std::make_shared(1); - c.signal(2, data); - - BOOST_CHECK(l.wait(TIMEOUT)); -} - - -BOOST_AUTO_TEST_CASE(ClientGSource) -{ - utils::Latch l; - ScopedGlibLoop loop; - - auto signalHandler = [&l](const PeerID, std::shared_ptr&) { - l.set(); - }; - - Service s(SOCKET_PATH); - s.start(); - - Client c(SOCKET_PATH); - c.setMethodHandler(1, echoCallback); - c.setSignalHandler(2, signalHandler); - - PeerID peerID = connectClientGSource(s, c); - - testEcho(s, 1, peerID); - - auto data = std::make_shared(1); - s.signal(2, data); - - BOOST_CHECK(l.wait(TIMEOUT)); -} - -BOOST_AUTO_TEST_CASE(UsersError) +MULTI_FIXTURE_TEST_CASE(UsersError, F, ThreadedFixture, GlibFixture) { const int TEST_ERROR_CODE = -234; const std::string TEST_ERROR_MESSAGE = "Ay, caramba!"; - Service s(SOCKET_PATH); - Client c(SOCKET_PATH); + Service s(F::getPoll(), SOCKET_PATH); + Client c(F::getPoll(), SOCKET_PATH); auto clientID = connect(s, c); auto throwingMethodHandler = [&](const PeerID, std::shared_ptr&, MethodResult::Pointer) { @@ -668,13 +622,13 @@ BOOST_AUTO_TEST_CASE(UsersError) BOOST_CHECK_EXCEPTION((s.callSync(2, clientID, sentData, TIMEOUT)), IPCUserException, hasProperData); } -BOOST_AUTO_TEST_CASE(AsyncResult) +MULTI_FIXTURE_TEST_CASE(AsyncResult, F, ThreadedFixture, GlibFixture) { const int TEST_ERROR_CODE = -567; const std::string TEST_ERROR_MESSAGE = "Ooo jooo!"; - Service s(SOCKET_PATH); - Client c(SOCKET_PATH); + Service s(F::getPoll(), SOCKET_PATH); + Client c(F::getPoll(), SOCKET_PATH); auto clientID = connect(s, c); auto errorMethodHandler = [&](const PeerID, std::shared_ptr&, MethodResult::Pointer methodResult) { @@ -724,20 +678,44 @@ BOOST_AUTO_TEST_CASE(AsyncResult) BOOST_CHECK_EQUAL(recvData->intVal, sentData->intVal); } -// BOOST_AUTO_TEST_CASE(ConnectionLimitTest) +MULTI_FIXTURE_TEST_CASE(MixOperations, F, ThreadedFixture, GlibFixture) +{ + utils::Latch l; + + auto signalHandler = [&l](const PeerID, std::shared_ptr&) { + l.set(); + }; + + Service s(F::getPoll(), SOCKET_PATH); + s.setMethodHandler(1, echoCallback); + + Client c(F::getPoll(), SOCKET_PATH); + s.setSignalHandler(2, signalHandler); + + connect(s, c); + + testEcho(c, 1); + + auto data = std::make_shared(1); + c.signal(2, data); + + BOOST_CHECK(l.wait(TIMEOUT)); +} + +// MULTI_FIXTURE_TEST_CASE(ConnectionLimitTest, F, ThreadedFixture, GlibFixture) // { // unsigned oldLimit = ipc::getMaxFDNumber(); // ipc::setMaxFDNumber(50); // // Setup Service and many Clients -// Service s(SOCKET_PATH); +// Service s(F::getPoll(), SOCKET_PATH); // s.setMethodHandler(1, echoCallback); // s.start(); // std::list clients; // for (int i = 0; i < 100; ++i) { // try { -// clients.push_back(Client(SOCKET_PATH)); +// clients.push_back(Client(F::getPoll(), SOCKET_PATH)); // clients.back().start(); // } catch (...) {} // } diff --git a/tests/unit_tests/ut.hpp b/tests/unit_tests/ut.hpp index d8b4b94..e96d363 100644 --- a/tests/unit_tests/ut.hpp +++ b/tests/unit_tests/ut.hpp @@ -28,9 +28,23 @@ #define BOOST_TEST_DYN_LINK #include +#include + #include /** + * Usage example: + * + * MULTI_FIXTURE_TEST_CASE(Test, T, Fixture1, Fixture2, Fixture3) { + * std::cout << T::i << "\n"; + * } + */ +#define MULTI_FIXTURE_TEST_CASE(NAME, TPARAM, ...) \ + typedef boost::mpl::vector<__VA_ARGS__> NAME##_fixtures; \ + BOOST_FIXTURE_TEST_CASE_TEMPLATE(NAME, TPARAM, NAME##_fixtures, TPARAM) + + +/** * An exception message checker * * Usage example: -- 2.7.4 From a894b6301c6c798912f0963011936df271c2495e Mon Sep 17 00:00:00 2001 From: Mateusz Malicki Date: Wed, 11 Mar 2015 12:57:02 +0100 Subject: [PATCH 11/16] Complementing cli commands in bash [Bug/Feature] Complementing cli commands in bash [Cause] N/A [Solution] N/A [Verification] Check completion (you must first enable bash completion or execute: source /etc/bash_completion.d/vasum-cli-completion.sh) Change-Id: I256ed064a128f03874b2e4655ccb818058a13897 --- cli/CMakeLists.txt | 5 +++++ cli/command-line-interface.cpp | 10 ++++++---- cli/main.cpp | 9 ++++++++- cli/support/vasum-cli-completion.sh.in | 15 +++++++++++++++ packaging/vasum.spec | 11 +++++++++++ 5 files changed, 45 insertions(+), 5 deletions(-) create mode 100755 cli/support/vasum-cli-completion.sh.in diff --git a/cli/CMakeLists.txt b/cli/CMakeLists.txt index f28e65b..2ffdcc4 100644 --- a/cli/CMakeLists.txt +++ b/cli/CMakeLists.txt @@ -33,6 +33,11 @@ INCLUDE_DIRECTORIES(${CLIENT_FOLDER}) INCLUDE_DIRECTORIES(${COMMON_FOLDER}) TARGET_LINK_LIBRARIES(${CLI_CODENAME} ${LIB_DEPS_LIBRARIES} ${PROJECT_NAME}) +CONFIGURE_FILE(support/vasum-cli-completion.sh.in + ${CMAKE_BINARY_DIR}/vasum-cli-completion.sh + @ONLY) ## Install ##################################################################### INSTALL(TARGETS ${CLI_CODENAME} DESTINATION bin) +INSTALL(FILES ${CMAKE_BINARY_DIR}/vasum-cli-completion.sh + DESTINATION ${SYSCONF_INSTALL_DIR}/bash_completion.d) diff --git a/cli/command-line-interface.cpp b/cli/command-line-interface.cpp index 77e9dc5..ecf6b9c 100644 --- a/cli/command-line-interface.cpp +++ b/cli/command-line-interface.cpp @@ -174,10 +174,12 @@ void CommandLineInterface::printUsage(std::ostream& out) const { out << mUsage << "\n\n" << "\tDescription\n" - << "\t\t" << mUsageInfo << "\n\n" - << "\tOptions\n"; - for (const auto& args : mArgsSpec) { - out << "\t\t" << args.first << " -- " << args.second << "\n"; + << "\t\t" << mUsageInfo << "\n"; + if (!mArgsSpec.empty()) { + out << "\n\tOptions\n"; + for (const auto& args : mArgsSpec) { + out << "\t\t" << args.first << " -- " << args.second << "\n"; + } } out << "\n"; } diff --git a/cli/main.cpp b/cli/main.cpp index 5f073be..a30146e 100644 --- a/cli/main.cpp +++ b/cli/main.cpp @@ -29,6 +29,7 @@ #include #include #include +#include using namespace vasum::cli; @@ -241,7 +242,7 @@ std::map commands = { void printUsage(std::ostream& out, const std::string& name) { - out << "Usage: " << name << " [command [args]]\n\n" + out << "Usage: " << name << " [command [-h|args]]\n\n" << "command can be one of the following:\n"; for (const auto& command : commands) { @@ -263,6 +264,12 @@ int main(const int argc, const char** argv) } CommandLineInterface& command = commands[argv[1]]; + auto it = std::find(argv, argv+argc, std::string("-h")); + if (it != argv + argc) { + command.printUsage(std::cout); + return EXIT_SUCCESS; + } + try { command.execute(1, argc, argv); } catch (const std::runtime_error& ex) { diff --git a/cli/support/vasum-cli-completion.sh.in b/cli/support/vasum-cli-completion.sh.in new file mode 100755 index 0000000..79a5dad --- /dev/null +++ b/cli/support/vasum-cli-completion.sh.in @@ -0,0 +1,15 @@ +# Check for bash +[ -z "$BASH_VERSION" ] && return + +__@PROJECT_NAME@_cli() { + local cur="${COMP_WORDS[COMP_CWORD]}" + + COMPREPLY=() + if [ "$COMP_CWORD" == "1" ]; then + COMPREPLY=($(compgen -W "$(@CLI_CODENAME@ | grep -e '^\S' | tail -n +3 | cut -f1 -d' ')" -- $cur)) + elif [ "$COMP_CWORD" == "2" ]; then + COMPREPLY=($(compgen -W "-h" -- $cur)) + fi +} + +complete -F __@PROJECT_NAME@_cli @CLI_CODENAME@ diff --git a/packaging/vasum.spec b/packaging/vasum.spec index bd7e9f1..6cea869 100644 --- a/packaging/vasum.spec +++ b/packaging/vasum.spec @@ -198,6 +198,17 @@ Command Line Interface for vasum. %defattr(644,root,root,755) %attr(755,root,root) %{_bindir}/vasum-cli +%package cli-completion +Summary: Vasum Command Line Interface bash completion +Group: Security/Other +Requires: vasum-cli = %{version}-%{release} +#Requires: bash-completion + +%description cli-completion +Command Line Interface bash completion. + +%files cli-completion +%attr(755,root,root) %{_sysconfdir}/bash_completion.d/vasum-cli-completion.sh ## Test Package ################################################################ %package tests -- 2.7.4 From 855c585bf6a39817a301e72e61620f6f9776210d Mon Sep 17 00:00:00 2001 From: Jan Olszak Date: Tue, 10 Mar 2015 13:35:55 +0100 Subject: [PATCH 12/16] IPC agnostic MethodResultBuilder [Feature] ZoneManager and Zone doesn't know which IPC mechanism is used. [Cause] N/A [Solution] N/A [Verification] Build, run tests, run tests with valgrind Change-Id: I852fa5ed3bcd105096cb7ca740f9e98fb8b6adba --- client/vasum-client-impl.cpp | 4 +- common/api/dbus-method-result-builder.hpp | 88 ++++++ common/api/ipc-method-result-builder.cpp | 54 ++++ common/api/ipc-method-result-builder.hpp | 56 ++++ common/api/messages.hpp | 404 +++++++++++++++++++++++++ common/api/method-result-builder.hpp | 62 ++++ server/host-connection.cpp | 108 ++++--- server/host-connection.hpp | 51 ++-- server/host-dbus-definitions.hpp | 2 +- server/zone-connection.cpp | 18 +- server/zone-connection.hpp | 15 +- server/zone.cpp | 6 +- server/zone.hpp | 6 +- server/zones-manager.cpp | 252 +++++++-------- server/zones-manager.hpp | 66 ++-- tests/unit_tests/server/ut-zone-connection.cpp | 17 +- tests/unit_tests/server/ut-zones-manager.cpp | 4 +- 17 files changed, 947 insertions(+), 266 deletions(-) create mode 100644 common/api/dbus-method-result-builder.hpp create mode 100644 common/api/ipc-method-result-builder.cpp create mode 100644 common/api/ipc-method-result-builder.hpp create mode 100644 common/api/messages.hpp create mode 100644 common/api/method-result-builder.hpp diff --git a/client/vasum-client-impl.cpp b/client/vasum-client-impl.cpp index d8ba562..1ac4c3c 100644 --- a/client/vasum-client-impl.cpp +++ b/client/vasum-client-impl.cpp @@ -74,7 +74,7 @@ void toDict(GVariant* in, VsmArrayString* keys, VsmArrayString* values) value_type* outv = (value_type*)calloc(size + 1, sizeof(value_type)); g_variant_iter_init(&iter, in); - for (int i = 0; g_variant_iter_loop(&iter, "{ss}", &key, &value); i++) { + for (int i = 0; g_variant_iter_loop(&iter, "(ss)", &key, &value); i++) { outk[i] = strdup(key); outv[i] = strdup(value); } @@ -377,7 +377,7 @@ VsmStatus Client::vsm_get_zone_dbuses(VsmArrayString* keys, VsmArrayString* valu VsmStatus ret = callMethod(HOST_INTERFACE, api::host::METHOD_GET_ZONE_DBUSES, NULL, - "(a{ss})", + "(a(ss))", &out); if (ret != VSMCLIENT_SUCCESS) { return ret; diff --git a/common/api/dbus-method-result-builder.hpp b/common/api/dbus-method-result-builder.hpp new file mode 100644 index 0000000..99f3bc0 --- /dev/null +++ b/common/api/dbus-method-result-builder.hpp @@ -0,0 +1,88 @@ +/* +* Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved +* +* Contact: Jan Olszak (j.olszak@samsung.com) +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License +*/ + +/** + * @file + * @author Jan Olszak (j.olszak@samsung.com) + * @brief Interface for result builders + + */ + +#ifndef COMMON_RESULT_DBUS_METHOD_RESULT_BUILDER_HPP +#define COMMON_RESULT_DBUS_METHOD_RESULT_BUILDER_HPP + +#include "config.hpp" + +#include "api/method-result-builder.hpp" + +#include "dbus/connection.hpp" +#include "config/manager.hpp" + +#include +#include + +namespace vasum { +namespace api { + +template +class DbusMethodResultBuilder: public MethodResultBuilder { +public: + DbusMethodResultBuilder(const dbus::MethodResultBuilder::Pointer& methodResultBuilderPtr); + ~DbusMethodResultBuilder() {} + +private: + void setImpl(const std::shared_ptr& data) override; + void setVoid() override; + void setError(const std::string& name, const std::string& message) override; + + dbus::MethodResultBuilder::Pointer mMethodResultBuilderPtr; + std::function)> mSerialize; +}; + +template +DbusMethodResultBuilder::DbusMethodResultBuilder(const dbus::MethodResultBuilder::Pointer& methodResultBuilderPtr) + : mMethodResultBuilderPtr(methodResultBuilderPtr) +{ + mSerialize = [](const std::shared_ptr data)->GVariant* { + return config::saveToGVariant(*std::static_pointer_cast(data)); + }; +} + +template +void DbusMethodResultBuilder::setImpl(const std::shared_ptr& data) +{ + GVariant* parameters = mSerialize(data); + mMethodResultBuilderPtr->set(parameters); +} + +template +void DbusMethodResultBuilder::setVoid() +{ + mMethodResultBuilderPtr->setVoid(); +} + +template +void DbusMethodResultBuilder::setError(const std::string& name, const std::string& message) +{ + mMethodResultBuilderPtr->setError(name, message); +} + +} // namespace result +} // namespace vasum + +#endif // COMMON_RESULT_DBUS_METHOD_RESULT_BUILDER_HPP diff --git a/common/api/ipc-method-result-builder.cpp b/common/api/ipc-method-result-builder.cpp new file mode 100644 index 0000000..4ea2845 --- /dev/null +++ b/common/api/ipc-method-result-builder.cpp @@ -0,0 +1,54 @@ +/* +* Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved +* +* Contact: Jan Olszak (j.olszak@samsung.com) +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License +*/ + +/** + * @file + * @author Jan Olszak (j.olszak@samsung.com) + * @brief Class for sending the result of a method + */ + +#include "api/ipc-method-result-builder.hpp" + +namespace vasum { +namespace api { + +IPCMethodResultBuilder::IPCMethodResultBuilder(const ipc::MethodResult::Pointer& methodResultPtr) + : mMethodResultPtr(methodResultPtr) +{ +} + +void IPCMethodResultBuilder::setImpl(const std::shared_ptr& data) +{ + mMethodResultPtr->set(data); +} + +void IPCMethodResultBuilder::setVoid() +{ + mMethodResultPtr->setVoid(); +} + +void IPCMethodResultBuilder::setError(const std::string& , const std::string& message) +{ + // TODO: Change int codes to string names in IPC MethodResult + mMethodResultPtr->setError(1, message); +} + +} // namespace result +} // namespace vasum + + diff --git a/common/api/ipc-method-result-builder.hpp b/common/api/ipc-method-result-builder.hpp new file mode 100644 index 0000000..7b03ece --- /dev/null +++ b/common/api/ipc-method-result-builder.hpp @@ -0,0 +1,56 @@ +/* +* Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved +* +* Contact: Jan Olszak (j.olszak@samsung.com) +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License +*/ + +/** + * @file + * @author Jan Olszak (j.olszak@samsung.com) + * @brief Interface for result builders + + */ + +#ifndef COMMON_RESULT_IPC_METHOD_RESULT_BUILDER_HPP +#define COMMON_RESULT_IPC_METHOD_RESULT_BUILDER_HPP + +#include "config.hpp" + +#include "api/method-result-builder.hpp" +#include "ipc/method-result.hpp" + +#include + +namespace vasum { +namespace api { + +class IPCMethodResultBuilder: public MethodResultBuilder { +public: + IPCMethodResultBuilder(const ipc::MethodResult::Pointer& methodResult); + ~IPCMethodResultBuilder() {} + +private: + void setImpl(const std::shared_ptr& data) override; + void setVoid() override; + void setError(const std::string& name, const std::string& message) override; + + ipc::MethodResult::Pointer mMethodResultPtr; +}; + + +} // namespace result +} // namespace vasum + +#endif // COMMON_RESULT_IPC_METHOD_RESULT_BUILDER_HPP diff --git a/common/api/messages.hpp b/common/api/messages.hpp new file mode 100644 index 0000000..6c6d4da --- /dev/null +++ b/common/api/messages.hpp @@ -0,0 +1,404 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * + * Contact: Jan Olszak (j.olszak@samsung.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +/** + * @file + * @author Jan Olszak (j.olszak@samsung.com) + * @brief Host's internal IPC messages declaration + */ + + +#ifndef COMMON_API_MESSAGES +#define COMMON_API_MESSAGES + +#include "config/fields.hpp" +#include +#include + +namespace vasum { +namespace api { + +struct Void { + CONFIG_REGISTER_EMPTY +}; + +struct String { + std::string value; + + CONFIG_REGISTER + ( + value + ) +}; + +struct StringPair { + std::string first; + std::string second; + + CONFIG_REGISTER + ( + first, + second + ) +}; + +struct VectorOfStrings { + std::vector values; + + CONFIG_REGISTER + ( + values + ) +}; + +struct VectorOfStringPairs { + std::vector values; + + CONFIG_REGISTER + ( + values + ) +}; + +typedef api::String ZoneId; +typedef api::String Declaration; +typedef api::String FileMoveRequestStatus; +typedef api::VectorOfStrings ZoneIds; +typedef api::VectorOfStrings Declarations; +typedef api::VectorOfStrings NetDevList; +typedef api::VectorOfStringPairs Dbuses; +typedef api::VectorOfStringPairs NetDevAttrs; + +struct ZoneInfo { + std::string id; + int vt; + std::string state; + std::string rootPath; + + CONFIG_REGISTER + ( + id, + vt, + state, + rootPath + ) +}; + +// struct MethodSetActiveZoneConfig { +// std::string id; + +// CONFIG_REGISTER +// ( +// id +// ) +// }; + +// struct MethodGetZoneDbusesConfig { +// CONFIG_REGISTER_EMPTY +// }; + +// struct MethodGetZoneIdListConfig { +// CONFIG_REGISTER_EMPTY +// }; + + +// struct MethodGetActiveZoneIdConfig { +// CONFIG_REGISTER_EMPTY +// }; + + +// struct MethodGetZoneInfoConfig { +// std::string id; + +// CONFIG_REGISTER +// ( +// id +// ) +// }; + +// struct MethodSetNetDevAttrsConfig { +// std::string zone; +// std::string netdev; + +// struct Store { +// std::string key; +// std::string value; + +// CONFIG_REGISTER +// ( +// key, +// value +// ) +// }; + +// std::vector attrs; + +// CONFIG_REGISTER +// ( +// zone, +// netdev, +// attrs +// ) +// }; + +// struct MethodGetNetDevAttrsConfig { +// std::string zone; +// std::string netdev; + +// CONFIG_REGISTER +// ( +// zone, +// netdev +// ) +// }; + +// struct MethodGetNetDevListConfig { +// std::string zone; + +// CONFIG_REGISTER +// ( +// zone +// ) +// }; + +// struct MethodCreateNetDevVethConfig { +// std::string id; +// std::string zoneDev; +// std::string hostDev; + +// CONFIG_REGISTER +// ( +// id, +// zoneDev, +// hostDev +// ) +// }; + +// struct MethodCreateNetDevMacvlanConfig { +// std::string id; +// std::string zoneDev; +// std::string hostDev; + +// CONFIG_REGISTER +// ( +// id, +// zoneDev, +// hostDev +// ) +// }; + +// struct MethodCreateNetDevPhysConfig { +// std::string id; +// std::string devId; + +// CONFIG_REGISTER +// ( +// id, +// devId +// ) +// }; + +// struct MethodGetDeclareFileConfig { +// std::string zone; +// int32_t type; +// std::string path; +// int32_t flags; +// int32_t mode; + +// CONFIG_REGISTER +// ( +// zone, +// type, +// path, +// flags, +// mode +// ) +// }; + +// struct MethodGetDeclareMountConfig { +// std::string source; +// std::string zone; +// std::string target; +// uint64_t flags; +// std::string data; + +// CONFIG_REGISTER +// ( +// source, +// zone, +// target, +// flags, +// data +// ) +// }; + +// struct MethodGetDeclareLinkConfig { +// std::string source; +// std::string zone; +// std::string target; + +// CONFIG_REGISTER +// ( +// source, +// zone, +// target +// ) +// }; + +// struct MethodGetDeclarationConfig { +// std::string zone; +// std::string declarationId; + +// CONFIG_REGISTER +// ( +// zone, +// declarationId +// ) +// }; + +// struct MethodRemoveDeclarationConfig { +// std::string id; +// std::string declarationId; + +// CONFIG_REGISTER +// ( +// id, +// declarationId +// ) +// }; + +// struct MethodCreateZoneConfig { +// std::string id; +// std::string templateName; + +// CONFIG_REGISTER +// ( +// id, +// templateName +// ) +// }; + +// struct MethodDestroyZoneConfig { +// std::string id; + +// CONFIG_REGISTER +// ( +// id +// ) +// }; + + +// struct MethodShutdownZoneConfig { +// std::string id; + +// CONFIG_REGISTER +// ( +// id +// ) +// }; + +// struct MethodStartZoneConfig { +// std::string id; + +// CONFIG_REGISTER +// ( +// id +// ) +// }; + +// struct MethodLockZoneConfig { +// std::string id; + +// CONFIG_REGISTER +// ( +// id +// ) +// }; + +// struct MethodUnlockZoneConfig { +// std::string id; + +// CONFIG_REGISTER +// ( +// id +// ) +// }; + +// struct MethodGrantDeviceConfig { +// std::string id; +// std::string device; +// uint32_t flags; + +// CONFIG_REGISTER +// ( +// id, +// device, +// flags +// ) +// }; + +// struct MethodRevokeDeviceConfig { +// std::string id; +// std::string device; + +// CONFIG_REGISTER +// ( +// id, +// device +// ) +// }; + +// TODO: Agregate configs if it makes sense. For example: MethodLockZoneConfig and MethodUnlockZoneConfig + + +// Zone: +// struct MethodNotifyActiveZoneConfig { +// std::string application; +// std::string message; + +// CONFIG_REGISTER +// ( +// application, +// message +// ) +// }; + +// struct MethodFileMoveRequest { +// std::string destination; +// std::string path; + +// CONFIG_REGISTER +// ( +// destination, +// path +// ) +// }; + +// struct MethodFileMoveRequestResult { +// std::string result; + +// CONFIG_REGISTER +// ( +// result +// ) +// }; + +} // namespace api +} // namespace vasum + +#endif // COMMON_API_MESSAGES diff --git a/common/api/method-result-builder.hpp b/common/api/method-result-builder.hpp new file mode 100644 index 0000000..04ede40 --- /dev/null +++ b/common/api/method-result-builder.hpp @@ -0,0 +1,62 @@ +/* +* Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved +* +* Contact: Jan Olszak (j.olszak@samsung.com) +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License +*/ + +/** + * @file + * @author Jan Olszak (j.olszak@samsung.com) + * @brief Interface for result builders + + */ + +#ifndef COMMON_RESULT_METHOD_RESULT_BUILDER_HPP +#define COMMON_RESULT_METHOD_RESULT_BUILDER_HPP + +#include + +#include "config/is-union.hpp" + +namespace vasum { +namespace api { + +/** + * An interface used to set a result to a method call. + */ +class MethodResultBuilder { +public: + typedef std::shared_ptr Pointer; + + virtual ~MethodResultBuilder() {} + virtual void setVoid() = 0; + virtual void setError(const std::string& name, const std::string& message) = 0; + + template + void set(const std::shared_ptr& data) + { + static_assert(config::isVisitable::value, "Use only libConfig's structures"); + setImpl(data); + } + +private: + virtual void setImpl(const std::shared_ptr& data) =0; + +}; + +} // namespace api +} // namespace vasum + +#endif // COMMON_RESULT_METHOD_RESULT_BUILDER_HPP diff --git a/server/host-connection.cpp b/server/host-connection.cpp index 6790480..a1a7448 100644 --- a/server/host-connection.cpp +++ b/server/host-connection.cpp @@ -27,6 +27,8 @@ #include "host-connection.hpp" #include "host-dbus-definitions.hpp" #include "exception.hpp" +#include "api/dbus-method-result-builder.hpp" +#include "api/messages.hpp" #include "logger/logger.hpp" @@ -65,7 +67,7 @@ HostConnection::HostConnection() mDbusConnection->registerObject(api::host::OBJECT_PATH, api::host::DEFINITION, std::bind(&HostConnection::onMessageCall, - this, _1, _2, _3, _4, _5)); + this, _1, _2, _3, _4, _5)); LOGD("Connected"); } @@ -246,14 +248,16 @@ void HostConnection::onMessageCall(const std::string& objectPath, g_variant_get(parameters, "(&s)", &id); if (mSetActiveZoneCallback) { - mSetActiveZoneCallback(id, result); + auto rb = std::make_shared>(result); + mSetActiveZoneCallback(id, rb); } return; } if (methodName == api::host::METHOD_GET_ZONE_DBUSES) { if (mGetZoneDbusesCallback) { - mGetZoneDbusesCallback(result); + auto rb = std::make_shared>(result); + mGetZoneDbusesCallback(rb); } return; } @@ -287,31 +291,34 @@ void HostConnection::onMessageCall(const std::string& objectPath, return; } - if (methodName == api::host::METHOD_GET_ZONE_ID_LIST){ - if (mGetZoneIdsCallback){ - mGetZoneIdsCallback(result); + if (methodName == api::host::METHOD_GET_ZONE_ID_LIST) { + if (mGetZoneIdsCallback) { + auto rb = std::make_shared>(result); + mGetZoneIdsCallback(rb); } return; } - if (methodName == api::host::METHOD_GET_ACTIVE_ZONE_ID){ - if (mGetActiveZoneIdCallback){ - mGetActiveZoneIdCallback(result); + if (methodName == api::host::METHOD_GET_ACTIVE_ZONE_ID) { + if (mGetActiveZoneIdCallback) { + auto rb = std::make_shared>(result); + mGetActiveZoneIdCallback(rb); } return; } - if (methodName == api::host::METHOD_GET_ZONE_INFO){ + if (methodName == api::host::METHOD_GET_ZONE_INFO) { const gchar* id = NULL; g_variant_get(parameters, "(&s)", &id); if (mGetZoneInfoCallback) { - mGetZoneInfoCallback(id, result); + auto rb = std::make_shared>(result); + mGetZoneInfoCallback(id, rb); } return; } - if (methodName == api::host::METHOD_SET_NETDEV_ATTRS){ + if (methodName == api::host::METHOD_SET_NETDEV_ATTRS) { const gchar* zone = NULL; const gchar* netdev = NULL; GVariantIter* iter; @@ -324,26 +331,29 @@ void HostConnection::onMessageCall(const std::string& objectPath, } g_variant_iter_free(iter); if (mSetNetdevAttrsCallback) { - mSetNetdevAttrsCallback(zone, netdev, attrs, result); + auto rb = std::make_shared>(result); + mSetNetdevAttrsCallback(zone, netdev, attrs, rb); } return; } - if (methodName == api::host::METHOD_GET_NETDEV_ATTRS){ + if (methodName == api::host::METHOD_GET_NETDEV_ATTRS) { const gchar* zone = NULL; const gchar* netdev = NULL; g_variant_get(parameters, "(&s&s)", &zone, &netdev); if (mGetNetdevAttrsCallback) { - mGetNetdevAttrsCallback(zone, netdev, result); + auto rb = std::make_shared>(result); + mGetNetdevAttrsCallback(zone, netdev, rb); } return; } - if (methodName == api::host::METHOD_GET_NETDEV_LIST){ + if (methodName == api::host::METHOD_GET_NETDEV_LIST) { const gchar* zone = NULL; g_variant_get(parameters, "(&s)", &zone); if (mGetNetdevListCallback) { - mGetNetdevListCallback(zone, result); + auto rb = std::make_shared>(result); + mGetNetdevListCallback(zone, rb); } return; } @@ -355,7 +365,8 @@ void HostConnection::onMessageCall(const std::string& objectPath, const gchar* hostDev = NULL; g_variant_get(parameters, "(&s&s&s)", &id, &zoneDev, &hostDev); if (mCreateNetdevVethCallback) { - mCreateNetdevVethCallback(id, zoneDev, hostDev, result); + auto rb = std::make_shared>(result); + mCreateNetdevVethCallback(id, zoneDev, hostDev, rb); } return; } @@ -367,7 +378,8 @@ void HostConnection::onMessageCall(const std::string& objectPath, guint32 mode; g_variant_get(parameters, "(&s&s&su)", &id, &zoneDev, &hostDev, &mode); if (mCreateNetdevMacvlanCallback) { - mCreateNetdevMacvlanCallback(id, zoneDev, hostDev, mode, result); + auto rb = std::make_shared>(result); + mCreateNetdevMacvlanCallback(id, zoneDev, hostDev, mode, rb); } } @@ -376,7 +388,8 @@ void HostConnection::onMessageCall(const std::string& objectPath, const gchar* devId = NULL; g_variant_get(parameters, "(&s&s)", &id, &devId); if (mCreateNetdevPhysCallback) { - mCreateNetdevPhysCallback(id, devId, result); + auto rb = std::make_shared>(result); + mCreateNetdevPhysCallback(id, devId, rb); } } @@ -389,7 +402,8 @@ void HostConnection::onMessageCall(const std::string& objectPath, g_variant_get(parameters, "(&si&sii)", &zone, &type, &path, &flags, &mode); if (mDeclareFileCallback) { - mDeclareFileCallback(zone, type, path, flags, mode, result); + auto rb = std::make_shared>(result); + mDeclareFileCallback(zone, type, path, flags, mode, rb); } return; } @@ -411,7 +425,8 @@ void HostConnection::onMessageCall(const std::string& objectPath, &data); if (mDeclareMountCallback) { - mDeclareMountCallback(source, zone, target, type, flags, data, result); + auto rb = std::make_shared>(result); + mDeclareMountCallback(source, zone, target, type, flags, data, rb); } return; } @@ -423,7 +438,8 @@ void HostConnection::onMessageCall(const std::string& objectPath, g_variant_get(parameters, "(&s&s&s)", &source, &zone, &target); if (mDeclareLinkCallback) { - mDeclareLinkCallback(source, zone, target, result); + auto rb = std::make_shared>(result); + mDeclareLinkCallback(source, zone, target, rb); } return; } @@ -433,7 +449,8 @@ void HostConnection::onMessageCall(const std::string& objectPath, g_variant_get(parameters, "(&s)", &zone); if (mGetDeclarationsCallback) { - mGetDeclarationsCallback(zone, result); + auto rb = std::make_shared>(result); + mGetDeclarationsCallback(zone, rb); } return; } @@ -444,7 +461,8 @@ void HostConnection::onMessageCall(const std::string& objectPath, g_variant_get(parameters, "(&s&s)", &zone, &declarationId); if (mRemoveDeclarationCallback) { - mRemoveDeclarationCallback(zone, declarationId, result); + auto rb = std::make_shared>(result); + mRemoveDeclarationCallback(zone, declarationId, rb); } return; } @@ -454,8 +472,9 @@ void HostConnection::onMessageCall(const std::string& objectPath, const gchar* templateName = NULL; g_variant_get(parameters, "(&s&s)", &id, &templateName); - if (mCreateZoneCallback){ - mCreateZoneCallback(id, templateName, result); + if (mCreateZoneCallback) { + auto rb = std::make_shared>(result); + mCreateZoneCallback(id, templateName, rb); } return; } @@ -464,8 +483,9 @@ void HostConnection::onMessageCall(const std::string& objectPath, const gchar* id = NULL; g_variant_get(parameters, "(&s)", &id); - if (mDestroyZoneCallback){ - mDestroyZoneCallback(id, result); + if (mDestroyZoneCallback) { + auto rb = std::make_shared>(result); + mDestroyZoneCallback(id, rb); } return; } @@ -474,8 +494,9 @@ void HostConnection::onMessageCall(const std::string& objectPath, const gchar* id = NULL; g_variant_get(parameters, "(&s)", &id); - if (mShutdownZoneCallback){ - mShutdownZoneCallback(id, result); + if (mShutdownZoneCallback) { + auto rb = std::make_shared>(result); + mShutdownZoneCallback(id, rb); } } @@ -483,8 +504,9 @@ void HostConnection::onMessageCall(const std::string& objectPath, const gchar* id = NULL; g_variant_get(parameters, "(&s)", &id); - if (mStartZoneCallback){ - mStartZoneCallback(id, result); + if (mStartZoneCallback) { + auto rb = std::make_shared>(result); + mStartZoneCallback(id, rb); } } @@ -492,8 +514,9 @@ void HostConnection::onMessageCall(const std::string& objectPath, const gchar* id = NULL; g_variant_get(parameters, "(&s)", &id); - if (mLockZoneCallback){ - mLockZoneCallback(id, result); + if (mLockZoneCallback) { + auto rb = std::make_shared>(result); + mLockZoneCallback(id, rb); } return; } @@ -502,8 +525,9 @@ void HostConnection::onMessageCall(const std::string& objectPath, const gchar* id = NULL; g_variant_get(parameters, "(&s)", &id); - if (mUnlockZoneCallback){ - mUnlockZoneCallback(id, result); + if (mUnlockZoneCallback) { + auto rb = std::make_shared>(result); + mUnlockZoneCallback(id, rb); } return; } @@ -514,8 +538,9 @@ void HostConnection::onMessageCall(const std::string& objectPath, uint32_t flags; g_variant_get(parameters, "(&s&su)", &id, &device, &flags); - if (mGrantDeviceCallback){ - mGrantDeviceCallback(id, device, flags, result); + if (mGrantDeviceCallback) { + auto rb = std::make_shared>(result); + mGrantDeviceCallback(id, device, flags, rb); } return; } @@ -525,8 +550,9 @@ void HostConnection::onMessageCall(const std::string& objectPath, const gchar* device = NULL; g_variant_get(parameters, "(&s&s)", &id, &device); - if (mRevokeDeviceCallback){ - mRevokeDeviceCallback(id, device, result); + if (mRevokeDeviceCallback) { + auto rb = std::make_shared>(result); + mRevokeDeviceCallback(id, device, rb); } return; } diff --git a/server/host-connection.hpp b/server/host-connection.hpp index 569c68f..3ef8cdf 100644 --- a/server/host-connection.hpp +++ b/server/host-connection.hpp @@ -27,6 +27,7 @@ #define SERVER_HOST_CONNECTION_HPP #include "dbus/connection.hpp" +#include "api/method-result-builder.hpp" #include #include @@ -53,48 +54,48 @@ public: GVariant* parameters, dbus::MethodResultBuilder::Pointer result )> ProxyCallCallback; - typedef std::function GetZoneDbusesCallback; - typedef std::function GetZoneIdsCallback; - typedef std::function GetActiveZoneIdCallback; typedef std::function GetZoneInfoCallback; typedef std::function>& attrs, - dbus::MethodResultBuilder::Pointer result + api::MethodResultBuilder::Pointer result )> SetNetdevAttrsCallback; typedef std::function GetNetdevAttrsCallback; typedef std::function GetNetdevListCallback; typedef std::function CreateNetdevVethCallback; typedef std::function CreateNetdevMacvlanCallback; typedef std::function CreateNetdevPhysCallback; typedef std::function DeclareFileCallback; typedef std::function DeclareMountCallback; typedef std::function DeclareLinkCallback; typedef std::function GetDeclarationsCallback; typedef std::function RemoveDeclarationCallback; typedef std::function SetActiveZoneCallback; typedef std::function CreateZoneCallback; typedef std::function DestroyZoneCallback; typedef std::function ShutdownZoneCallback; typedef std::function StartZoneCallback; typedef std::function LockZoneCallback; typedef std::function UnlockZoneCallback; typedef std::function GrantDeviceCallback; typedef std::function RevokeDeviceCallback; /** @@ -171,7 +172,7 @@ public: /** * Register a callback called to get the active zone id */ - void setGetActiveZoneIdCallback(const GetZoneIdsCallback& callback); + void setGetActiveZoneIdCallback(const GetActiveZoneIdCallback& callback); /** * Register a callback called to get the zone informations diff --git a/server/host-dbus-definitions.hpp b/server/host-dbus-definitions.hpp index 2c2a201..34d64eb 100644 --- a/server/host-dbus-definitions.hpp +++ b/server/host-dbus-definitions.hpp @@ -79,7 +79,7 @@ const std::string DEFINITION = " " " " " " - " " + " " " " " " " " diff --git a/server/zone-connection.cpp b/server/zone-connection.cpp index 35341b3..a86886c 100644 --- a/server/zone-connection.cpp +++ b/server/zone-connection.cpp @@ -30,6 +30,9 @@ // TODO: Switch to real power-manager dbus defs when they will be implemented in power-manager #include "fake-power-manager-dbus-definitions.hpp" +#include "api/dbus-method-result-builder.hpp" +#include "api/messages.hpp" + #include "logger/logger.hpp" @@ -139,10 +142,10 @@ void ZoneConnection::setDisplayOffCallback(const DisplayOffCallback& callback) mDisplayOffCallback = callback; } -void ZoneConnection::setFileMoveRequestCallback( - const FileMoveRequestCallback& callback) +void ZoneConnection::setFileMoveCallback( + const FileMoveCallback& callback) { - mFileMoveRequestCallback = callback; + mFileMoveCallback = callback; } void ZoneConnection::setProxyCallCallback(const ProxyCallCallback& callback) @@ -165,8 +168,8 @@ void ZoneConnection::onMessageCall(const std::string& objectPath, const gchar* message = NULL; g_variant_get(parameters, "(&s&s)", &application, &message); if (mNotifyActiveZoneCallback) { - mNotifyActiveZoneCallback(application, message); - result->setVoid(); + auto rb = std::make_shared>(result); + mNotifyActiveZoneCallback(application, message, rb); } } @@ -174,8 +177,9 @@ void ZoneConnection::onMessageCall(const std::string& objectPath, const gchar* destination = NULL; const gchar* path = NULL; g_variant_get(parameters, "(&s&s)", &destination, &path); - if (mFileMoveRequestCallback) { - mFileMoveRequestCallback(destination, path, result); + if (mFileMoveCallback) { + auto rb = std::make_shared>(result); + mFileMoveCallback(destination, path, rb); } } diff --git a/server/zone-connection.hpp b/server/zone-connection.hpp index 78b6e56..8f025b0 100644 --- a/server/zone-connection.hpp +++ b/server/zone-connection.hpp @@ -27,11 +27,11 @@ #define SERVER_ZONE_CONNECTION_HPP #include "dbus/connection.hpp" +#include "api/method-result-builder.hpp" #include #include - namespace vasum { @@ -47,13 +47,14 @@ public: // ------------- API -------------- typedef std::function NotifyActiveZoneCallback; typedef std::function FileMoveRequestCallback; + api::MethodResultBuilder::Pointer result + )> FileMoveCallback; typedef std::function ProxyCallCallback; + )> ProxyCallCallback; /** * Register notification request callback @@ -77,7 +78,7 @@ public: /* * Register file move request callback */ - void setFileMoveRequestCallback(const FileMoveRequestCallback& callback); + void setFileMoveCallback(const FileMoveCallback& callback); /** * Register proxy call callback @@ -110,7 +111,7 @@ private: OnNameLostCallback mOnNameLostCallback; NotifyActiveZoneCallback mNotifyActiveZoneCallback; DisplayOffCallback mDisplayOffCallback; - FileMoveRequestCallback mFileMoveRequestCallback; + FileMoveCallback mFileMoveCallback; ProxyCallCallback mProxyCallCallback; void onNameAcquired(); diff --git a/server/zone.cpp b/server/zone.cpp index 1bc2d09..181cc9e 100644 --- a/server/zone.cpp +++ b/server/zone.cpp @@ -206,7 +206,7 @@ void Zone::connect() mConnection->setDisplayOffCallback(mDisplayOffCallback); } if (mFileMoveCallback) { - mConnection->setFileMoveRequestCallback(mFileMoveCallback); + mConnection->setFileMoveCallback(mFileMoveCallback); } if (mProxyCallCallback) { mConnection->setProxyCallCallback(mProxyCallCallback); @@ -411,13 +411,13 @@ void Zone::setDisplayOffCallback(const DisplayOffCallback& callback) } } -void Zone::setFileMoveRequestCallback(const FileMoveRequestCallback& callback) +void Zone::setFileMoveCallback(const FileMoveCallback& callback) { Lock lock(mReconnectMutex); mFileMoveCallback = callback; if (mConnection) { - mConnection->setFileMoveRequestCallback(callback); + mConnection->setFileMoveCallback(callback); } } diff --git a/server/zone.hpp b/server/zone.hpp index 69d0324..45bacbd 100644 --- a/server/zone.hpp +++ b/server/zone.hpp @@ -67,7 +67,7 @@ public: typedef ZoneConnection::NotifyActiveZoneCallback NotifyActiveZoneCallback; typedef ZoneConnection::DisplayOffCallback DisplayOffCallback; - typedef ZoneConnection::FileMoveRequestCallback FileMoveRequestCallback; + typedef ZoneConnection::FileMoveCallback FileMoveCallback; typedef ZoneConnection::ProxyCallCallback ProxyCallCallback; typedef std::function DbusStateChangedCallback; @@ -210,7 +210,7 @@ public: /** * Register file move request callback */ - void setFileMoveRequestCallback(const FileMoveRequestCallback& callback); + void setFileMoveCallback(const FileMoveCallback& callback); /** * Register dbus state changed callback @@ -319,7 +319,7 @@ private: mutable std::recursive_mutex mReconnectMutex; NotifyActiveZoneCallback mNotifyCallback; DisplayOffCallback mDisplayOffCallback; - FileMoveRequestCallback mFileMoveCallback; + FileMoveCallback mFileMoveCallback; ProxyCallCallback mProxyCallCallback; DbusStateChangedCallback mDbusStateChangedCallback; std::string mDbusAddress; diff --git a/server/zones-manager.cpp b/server/zones-manager.cpp index d90bab7..2dcfaa0 100644 --- a/server/zones-manager.cpp +++ b/server/zones-manager.cpp @@ -40,6 +40,7 @@ #include "utils/fs.hpp" #include "utils/img.hpp" #include "utils/environment.hpp" +#include "api/messages.hpp" #include #include @@ -56,7 +57,7 @@ namespace { bool regexMatchVector(const std::string& str, const std::vector& v) { - for (const boost::regex& toMatch: v) { + for (const boost::regex& toMatch : v) { if (boost::regex_match(str, toMatch)) { return true; } @@ -129,7 +130,7 @@ ZonesManager::ZonesManager(const std::string& configPath) mHostConnection.setProxyCallCallback(bind(&ZonesManager::handleProxyCall, this, HOST_ID, _1, _2, _3, _4, _5, _6, _7)); - mHostConnection.setGetZoneDbusesCallback(bind(&ZonesManager::handleGetZoneDbuses, + mHostConnection.setGetZoneDbusesCallback(bind(&ZonesManager::handleGetZoneDbusesCall, this, _1)); mHostConnection.setGetZoneIdsCallback(bind(&ZonesManager::handleGetZoneIdsCall, @@ -142,22 +143,22 @@ ZonesManager::ZonesManager(const std::string& configPath) this, _1, _2)); mHostConnection.setSetNetdevAttrsCallback(bind(&ZonesManager::handleSetNetdevAttrsCall, - this, _1, _2, _3, _4)); + this, _1, _2, _3, _4)); mHostConnection.setGetNetdevAttrsCallback(bind(&ZonesManager::handleGetNetdevAttrsCall, - this, _1, _2, _3)); + this, _1, _2, _3)); mHostConnection.setGetNetdevListCallback(bind(&ZonesManager::handleGetNetdevListCall, - this, _1, _2)); + this, _1, _2)); mHostConnection.setCreateNetdevVethCallback(bind(&ZonesManager::handleCreateNetdevVethCall, - this, _1, _2, _3, _4)); + this, _1, _2, _3, _4)); mHostConnection.setCreateNetdevMacvlanCallback(bind(&ZonesManager::handleCreateNetdevMacvlanCall, - this, _1, _2, _3, _4, _5)); + this, _1, _2, _3, _4, _5)); mHostConnection.setCreateNetdevPhysCallback(bind(&ZonesManager::handleCreateNetdevPhysCall, - this, _1, _2, _3)); + this, _1, _2, _3)); mHostConnection.setDeclareFileCallback(bind(&ZonesManager::handleDeclareFileCall, this, _1, _2, _3, _4, _5, _6)); @@ -172,7 +173,7 @@ ZonesManager::ZonesManager(const std::string& configPath) this, _1, _2)); mHostConnection.setRemoveDeclarationCallback(bind(&ZonesManager::handleRemoveDeclarationCall, - this, _1, _2, _3)); + this, _1, _2, _3)); mHostConnection.setSetActiveZoneCallback(bind(&ZonesManager::handleSetActiveZoneCall, this, _1, _2)); @@ -212,9 +213,9 @@ ZonesManager::ZonesManager(const std::string& configPath) if (mConfig.inputConfig.enabled) { LOGI("Registering input monitor [" << mConfig.inputConfig.device.c_str() << "]"); mSwitchingSequenceMonitor.reset( - new InputMonitor(mConfig.inputConfig, - std::bind(&ZonesManager::switchingSequenceMonitorNotify, - this))); + new InputMonitor(mConfig.inputConfig, + std::bind(&ZonesManager::switchingSequenceMonitorNotify, + this))); } @@ -307,13 +308,13 @@ void ZonesManager::insertZone(const std::string& zoneId, const std::string& zone mConfig.runMountPointPrefix)); using namespace std::placeholders; - zone->setNotifyActiveZoneCallback(bind(&ZonesManager::notifyActiveZoneHandler, - this, zoneId, _1, _2)); + zone->setNotifyActiveZoneCallback(bind(&ZonesManager::handleNotifyActiveZoneCall, + this, zoneId, _1, _2, _3)); - zone->setDisplayOffCallback(bind(&ZonesManager::displayOffHandler, + zone->setDisplayOffCallback(bind(&ZonesManager::handleDisplayOffCall, this, zoneId)); - zone->setFileMoveRequestCallback(bind(&ZonesManager::handleZoneMoveFileRequest, + zone->setFileMoveCallback(bind(&ZonesManager::handleFileMoveCall, this, zoneId, _1, _2, _3)); zone->setProxyCallCallback(bind(&ZonesManager::handleProxyCall, @@ -547,11 +548,12 @@ void ZonesManager::setZonesDetachOnExit() } } -void ZonesManager::notifyActiveZoneHandler(const std::string& caller, - const std::string& application, - const std::string& message) +void ZonesManager::handleNotifyActiveZoneCall(const std::string& caller, + const std::string& application, + const std::string& message, + api::MethodResultBuilder::Pointer result) { - LOGI("notifyActiveZoneHandler(" << caller << ", " << application << ", " << message + LOGI("handleNotifyActiveZoneCall(" << caller << ", " << application << ", " << message << ") called"); Lock lock(mMutex); @@ -561,12 +563,14 @@ void ZonesManager::notifyActiveZoneHandler(const std::string& caller, if (iter != mZones.end() && caller != get(iter).getId()) { get(iter).sendNotification(caller, application, message); } - } catch(const VasumException&) { + result->setVoid(); + } catch (const VasumException&) { LOGE("Notification from " << caller << " hasn't been sent"); + result->setError(api::ERROR_INTERNAL, "Notification hasn't been sent"); } } -void ZonesManager::displayOffHandler(const std::string& /*caller*/) +void ZonesManager::handleDisplayOffCall(const std::string& /*caller*/) { // get config of currently set zone and switch if switchToDefaultAfterTimeout is true Lock lock(mMutex); @@ -584,10 +588,10 @@ void ZonesManager::displayOffHandler(const std::string& /*caller*/) } } -void ZonesManager::handleZoneMoveFileRequest(const std::string& srcZoneId, - const std::string& dstZoneId, - const std::string& path, - dbus::MethodResultBuilder::Pointer result) +void ZonesManager::handleFileMoveCall(const std::string& srcZoneId, + const std::string& dstZoneId, + const std::string& path, + api::MethodResultBuilder::Pointer result) { // TODO: this implementation is only a placeholder. // There are too many unanswered questions and security concerns: @@ -622,29 +626,35 @@ void ZonesManager::handleZoneMoveFileRequest(const std::string& srcZoneId, } Zone& srcZone = get(srcIter); + auto status = std::make_shared(); + auto dstIter = findZone(dstZoneId); if (dstIter == mZones.end()) { LOGE("Destination zone '" << dstZoneId << "' not found"); - result->set(g_variant_new("(s)", api::zone::FILE_MOVE_DESTINATION_NOT_FOUND.c_str())); + status->value = api::zone::FILE_MOVE_DESTINATION_NOT_FOUND; + result->set(status); return; } Zone& dstContanier = get(dstIter); if (srcZoneId == dstZoneId) { LOGE("Cannot send a file to yourself"); - result->set(g_variant_new("(s)", api::zone::FILE_MOVE_WRONG_DESTINATION.c_str())); + status->value = api::zone::FILE_MOVE_WRONG_DESTINATION; + result->set(status); return; } if (!regexMatchVector(path, srcZone.getPermittedToSend())) { LOGE("Source zone has no permissions to send the file: " << path); - result->set(g_variant_new("(s)", api::zone::FILE_MOVE_NO_PERMISSIONS_SEND.c_str())); + status->value = api::zone::FILE_MOVE_NO_PERMISSIONS_SEND; + result->set(status); return; } if (!regexMatchVector(path, dstContanier.getPermittedToRecv())) { LOGE("Destination zone has no permissions to receive the file: " << path); - result->set(g_variant_new("(s)", api::zone::FILE_MOVE_NO_PERMISSIONS_RECEIVE.c_str())); + status->value = api::zone::FILE_MOVE_NO_PERMISSIONS_RECEIVE; + result->set(status); return; } @@ -654,9 +664,11 @@ void ZonesManager::handleZoneMoveFileRequest(const std::string& srcZoneId, if (!utils::moveFile(srcPath, dstPath)) { LOGE("Failed to move the file: " << path); - result->set(g_variant_new("(s)", api::zone::FILE_MOVE_FAILED.c_str())); + status->value = api::zone::FILE_MOVE_FAILED; + result->set(status); } else { - result->set(g_variant_new("(s)", api::zone::FILE_MOVE_SUCCEEDED.c_str())); + status->value = api::zone::FILE_MOVE_SUCCEEDED; + result->set(status); try { dstContanier.sendNotification(srcZoneId, path, api::zone::FILE_MOVE_SUCCEEDED); } catch (ServerException&) { @@ -681,15 +693,15 @@ void ZonesManager::handleProxyCall(const std::string& caller, targetInterface, targetMethod)) { LOGW("Forbidden proxy call; " << caller << " -> " << target << "; " << targetBusName - << "; " << targetObjectPath << "; " << targetInterface << "; " << targetMethod); + << "; " << targetObjectPath << "; " << targetInterface << "; " << targetMethod); result->setError(api::ERROR_FORBIDDEN, "Proxy call forbidden"); return; } LOGI("Proxy call; " << caller << " -> " << target << "; " << targetBusName - << "; " << targetObjectPath << "; " << targetInterface << "; " << targetMethod); + << "; " << targetObjectPath << "; " << targetInterface << "; " << targetMethod); - auto asyncResultCallback = [result](dbus::AsyncMethodCallResult& asyncMethodCallResult) { + auto asyncResultCallback = [result](dbus::AsyncMethodCallResult & asyncMethodCallResult) { try { GVariant* targetResult = asyncMethodCallResult.get(); result->set(g_variant_new("(v)", targetResult)); @@ -719,26 +731,22 @@ void ZonesManager::handleProxyCall(const std::string& caller, Zone& targetZone = get(targetIter); targetZone.proxyCallAsync(targetBusName, - targetObjectPath, - targetInterface, - targetMethod, - parameters, - asyncResultCallback); + targetObjectPath, + targetInterface, + targetMethod, + parameters, + asyncResultCallback); } -void ZonesManager::handleGetZoneDbuses(dbus::MethodResultBuilder::Pointer result) +void ZonesManager::handleGetZoneDbusesCall(api::MethodResultBuilder::Pointer result) { Lock lock(mMutex); - std::vector entries; + auto dbuses = std::make_shared(); for (auto& zone : mZones) { - GVariant* zoneId = g_variant_new_string(zone->getId().c_str()); - GVariant* dbusAddress = g_variant_new_string(zone->getDbusAddress().c_str()); - GVariant* entry = g_variant_new_dict_entry(zoneId, dbusAddress); - entries.push_back(entry); + dbuses->values.push_back({zone->getId(), zone->getDbusAddress()}); } - GVariant* dict = g_variant_new_array(G_VARIANT_TYPE("{ss}"), entries.data(), entries.size()); - result->set(g_variant_new("(@a{ss})", dict)); + result->set(dbuses); } void ZonesManager::handleDbusStateChanged(const std::string& zoneId, @@ -747,31 +755,28 @@ void ZonesManager::handleDbusStateChanged(const std::string& zoneId, mHostConnection.signalZoneDbusState(zoneId, dbusAddress); } -void ZonesManager::handleGetZoneIdsCall(dbus::MethodResultBuilder::Pointer result) +void ZonesManager::handleGetZoneIdsCall(api::MethodResultBuilder::Pointer result) { Lock lock(mMutex); - std::vector zoneIds; - for(auto& zone: mZones){ - zoneIds.push_back(g_variant_new_string(zone->getId().c_str())); + auto zoneIds = std::make_shared(); + for (const auto& zone : mZones) { + zoneIds->values.push_back(zone->getId()); } - GVariant* array = g_variant_new_array(G_VARIANT_TYPE("s"), - zoneIds.data(), - zoneIds.size()); - result->set(g_variant_new("(@as)", array)); + result->set(zoneIds); } -void ZonesManager::handleGetActiveZoneIdCall(dbus::MethodResultBuilder::Pointer result) +void ZonesManager::handleGetActiveZoneIdCall(api::MethodResultBuilder::Pointer result) { LOGI("GetActiveZoneId call"); - - std::string id = getRunningForegroundZoneId(); - result->set(g_variant_new("(s)", id.c_str())); + auto zoneId = std::make_shared(); + zoneId->value = getRunningForegroundZoneId(); + result->set(zoneId); } void ZonesManager::handleGetZoneInfoCall(const std::string& id, - dbus::MethodResultBuilder::Pointer result) + api::MethodResultBuilder::Pointer result) { LOGI("GetZoneInfo call"); @@ -783,38 +788,33 @@ void ZonesManager::handleGetZoneInfoCall(const std::string& id, result->setError(api::ERROR_INVALID_ID, "No such zone id"); return; } + Zone& zone = get(iter); - const char* state; + auto zoneInfo = std::make_shared(); if (zone.isRunning()) { - state = "RUNNING"; + zoneInfo->state = "RUNNING"; } else if (zone.isStopped()) { - state = "STOPPED"; + zoneInfo->state = "STOPPED"; } else if (zone.isPaused()) { - state = "FROZEN"; + zoneInfo->state = "FROZEN"; } else { LOGE("Unrecognized state of zone id=" << id); result->setError(api::ERROR_INTERNAL, "Unrecognized state of zone"); return; } - result->set(g_variant_new("((siss))", - id.c_str(), - zone.getVT(), - state, - zone.getRootPath().c_str())); + result->set(zoneInfo); } void ZonesManager::handleSetNetdevAttrsCall(const std::string& zone, const std::string& netdev, - const std::vector< - std::tuple>& attrs, - dbus::MethodResultBuilder::Pointer result) + const std::vector>& attrs, + api::MethodResultBuilder::Pointer result) { LOGI("SetNetdevAttrs call"); try { Lock lock(mMutex); - getZone(zone).setNetdevAttrs(netdev, attrs); result->setVoid(); } catch (const InvalidZoneIdException&) { @@ -828,23 +828,18 @@ void ZonesManager::handleSetNetdevAttrsCall(const std::string& zone, void ZonesManager::handleGetNetdevAttrsCall(const std::string& zone, const std::string& netdev, - dbus::MethodResultBuilder::Pointer result) + api::MethodResultBuilder::Pointer result) { LOGI("GetNetdevAttrs call"); try { Lock lock(mMutex); - + auto netDevAttrs = std::make_shared(); const auto attrs = getZone(zone).getNetdevAttrs(netdev); - GVariantBuilder builder; - g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY); - for (const auto entry : attrs) { - g_variant_builder_add(&builder, - "(ss)", - std::get<0>(entry).c_str(), - std::get<1>(entry).c_str()); + for (size_t i = 0; i < attrs.size(); ++i) { + netDevAttrs->values.push_back({std::get<0>(attrs[i]), std::get<1>(attrs[i])}); } - result->set(g_variant_builder_end(&builder)); + result->set(netDevAttrs); } catch (const InvalidZoneIdException&) { LOGE("No zone with id=" << zone); result->setError(api::ERROR_INVALID_ID, "No such zone id"); @@ -855,19 +850,14 @@ void ZonesManager::handleGetNetdevAttrsCall(const std::string& zone, } void ZonesManager::handleGetNetdevListCall(const std::string& zone, - dbus::MethodResultBuilder::Pointer result) + api::MethodResultBuilder::Pointer result) { LOGI("GetNetdevList call"); try { Lock lock(mMutex); - std::vector netdevs; - for(auto& netdev: getZone(zone).getNetdevList()){ - netdevs.push_back(g_variant_new_string(netdev.c_str())); - } - GVariant* array = g_variant_new_array(G_VARIANT_TYPE("s"), - netdevs.data(), - netdevs.size()); - result->set(g_variant_new("(@as)", array)); + auto netDevList = std::make_shared(); + netDevList->values = getZone(zone).getNetdevList(); + result->set(netDevList); } catch (const InvalidZoneIdException&) { LOGE("No zone with id=" << zone); result->setError(api::ERROR_INVALID_ID, "No such zone id"); @@ -880,7 +870,7 @@ void ZonesManager::handleGetNetdevListCall(const std::string& zone, void ZonesManager::handleCreateNetdevVethCall(const std::string& zone, const std::string& zoneDev, const std::string& hostDev, - dbus::MethodResultBuilder::Pointer result) + api::MethodResultBuilder::Pointer result) { LOGI("CreateNetdevVeth call"); try { @@ -901,7 +891,7 @@ void ZonesManager::handleCreateNetdevMacvlanCall(const std::string& zone, const std::string& zoneDev, const std::string& hostDev, const uint32_t& mode, - dbus::MethodResultBuilder::Pointer result) + api::MethodResultBuilder::Pointer result) { LOGI("CreateNetdevMacvlan call"); try { @@ -920,7 +910,7 @@ void ZonesManager::handleCreateNetdevMacvlanCall(const std::string& zone, void ZonesManager::handleCreateNetdevPhysCall(const std::string& zone, const std::string& devId, - dbus::MethodResultBuilder::Pointer result) + api::MethodResultBuilder::Pointer result) { LOGI("CreateNetdevPhys call"); try { @@ -942,15 +932,15 @@ void ZonesManager::handleDeclareFileCall(const std::string& zone, const std::string& path, const int32_t& flags, const int32_t& mode, - dbus::MethodResultBuilder::Pointer result) + api::MethodResultBuilder::Pointer result) { LOGI("DeclareFile call"); try { Lock lock(mMutex); - - const std::string id = getZone(zone).declareFile(type, path, flags, mode); - result->set(g_variant_new("(s)", id.c_str())); + auto declaration = std::make_shared(); + declaration->value = getZone(zone).declareFile(type, path, flags, mode); + result->set(declaration); } catch (const InvalidZoneIdException&) { LOGE("No zone with id=" << zone); result->setError(api::ERROR_INVALID_ID, "No such zone id"); @@ -966,15 +956,15 @@ void ZonesManager::handleDeclareMountCall(const std::string& source, const std::string& type, const uint64_t& flags, const std::string& data, - dbus::MethodResultBuilder::Pointer result) + api::MethodResultBuilder::Pointer result) { LOGI("DeclareMount call"); try { Lock lock(mMutex); - - const std::string id = getZone(zone).declareMount(source, target, type, flags, data); - result->set(g_variant_new("(s)", id.c_str())); + auto declaration = std::make_shared(); + declaration->value = getZone(zone).declareMount(source, target, type, flags, data); + result->set(declaration); } catch (const InvalidZoneIdException&) { LOGE("No zone with id=" << zone); result->setError(api::ERROR_INVALID_ID, "No such zone id"); @@ -987,14 +977,14 @@ void ZonesManager::handleDeclareMountCall(const std::string& source, void ZonesManager::handleDeclareLinkCall(const std::string& source, const std::string& zone, const std::string& target, - dbus::MethodResultBuilder::Pointer result) + api::MethodResultBuilder::Pointer result) { LOGI("DeclareLink call"); try { Lock lock(mMutex); - - const std::string id = getZone(zone).declareLink(source, target); - result->set(g_variant_new("(s)", id.c_str())); + auto declaration = std::make_shared(); + declaration->value = getZone(zone).declareLink(source, target); + result->set(declaration); } catch (const InvalidZoneIdException&) { LOGE("No zone with id=" << zone); result->setError(api::ERROR_INVALID_ID, "No such zone id"); @@ -1005,23 +995,14 @@ void ZonesManager::handleDeclareLinkCall(const std::string& source, } void ZonesManager::handleGetDeclarationsCall(const std::string& zone, - dbus::MethodResultBuilder::Pointer result) + api::MethodResultBuilder::Pointer result) { LOGI("GetDeclarations call Id=" << zone); try { Lock lock(mMutex); - - std::vector declarations = getZone(zone).getDeclarations(); - - std::vector out; - for (auto declaration : declarations) { - out.push_back(g_variant_new_string(declaration.c_str())); - } - - GVariant* array = g_variant_new_array(G_VARIANT_TYPE("s"), - out.data(), - out.size()); - result->set(g_variant_new("(@as)", array)); + auto declarations = std::make_shared(); + declarations->values = getZone(zone).getDeclarations(); + result->set(declarations); } catch (const InvalidZoneIdException&) { LOGE("No zone with id=" << zone); result->setError(api::ERROR_INVALID_ID, "No such zone id"); @@ -1029,12 +1010,11 @@ void ZonesManager::handleGetDeclarationsCall(const std::string& zone, LOGE(ex.what()); result->setError(api::ERROR_INTERNAL, ex.what()); } - } void ZonesManager::handleRemoveDeclarationCall(const std::string& zone, const std::string& declarationId, - dbus::MethodResultBuilder::Pointer result) + api::MethodResultBuilder::Pointer result) { LOGI("RemoveDeclaration call Id=" << zone); try { @@ -1053,20 +1033,20 @@ void ZonesManager::handleRemoveDeclarationCall(const std::string& zone, } void ZonesManager::handleSetActiveZoneCall(const std::string& id, - dbus::MethodResultBuilder::Pointer result) + api::MethodResultBuilder::Pointer result) { LOGI("SetActiveZone call; Id=" << id ); Lock lock(mMutex); auto iter = findZone(id); - if (iter == mZones.end()){ + if (iter == mZones.end()) { LOGE("No zone with id=" << id ); result->setError(api::ERROR_INVALID_ID, "No such zone id"); return; } - if (!get(iter).isRunning()){ + if (!get(iter).isRunning()) { LOGE("Could not activate stopped or paused zone"); result->setError(api::host::ERROR_ZONE_NOT_RUNNING, "Could not activate stopped or paused zone"); @@ -1170,11 +1150,11 @@ void ZonesManager::createZone(const std::string& id, } } - auto removeAllWrapper = [](const std::string& path) -> bool { + auto removeAllWrapper = [](const std::string & path) -> bool { try { LOGD("Removing copied data"); fs::remove_all(fs::path(path)); - } catch(const std::exception& e) { + } catch (const std::exception& e) { LOGW("Failed to remove data: " << boost::diagnostic_information(e)); } return true; @@ -1208,7 +1188,7 @@ void ZonesManager::createZone(const std::string& id, void ZonesManager::handleCreateZoneCall(const std::string& id, const std::string& templateName, - dbus::MethodResultBuilder::Pointer result) + api::MethodResultBuilder::Pointer result) { try { createZone(id, templateName); @@ -1221,7 +1201,7 @@ void ZonesManager::handleCreateZoneCall(const std::string& id, } void ZonesManager::handleDestroyZoneCall(const std::string& id, - dbus::MethodResultBuilder::Pointer result) + api::MethodResultBuilder::Pointer result) { auto destroyer = [id, result, this] { try { @@ -1243,7 +1223,7 @@ void ZonesManager::handleDestroyZoneCall(const std::string& id, } void ZonesManager::handleShutdownZoneCall(const std::string& id, - dbus::MethodResultBuilder::Pointer result) + api::MethodResultBuilder::Pointer result) { LOGI("ShutdownZone call; Id=" << id ); @@ -1272,7 +1252,7 @@ void ZonesManager::handleShutdownZoneCall(const std::string& id, } void ZonesManager::handleStartZoneCall(const std::string& id, - dbus::MethodResultBuilder::Pointer result) + api::MethodResultBuilder::Pointer result) { LOGI("StartZone call; Id=" << id ); @@ -1299,7 +1279,7 @@ void ZonesManager::handleStartZoneCall(const std::string& id, } void ZonesManager::handleLockZoneCall(const std::string& id, - dbus::MethodResultBuilder::Pointer result) + api::MethodResultBuilder::Pointer result) { LOGI("LockZone call; Id=" << id ); @@ -1334,7 +1314,7 @@ void ZonesManager::handleLockZoneCall(const std::string& id, } void ZonesManager::handleUnlockZoneCall(const std::string& id, - dbus::MethodResultBuilder::Pointer result) + api::MethodResultBuilder::Pointer result) { LOGI("UnlockZone call; Id=" << id ); @@ -1369,7 +1349,7 @@ void ZonesManager::handleUnlockZoneCall(const std::string& id, void ZonesManager::handleGrantDeviceCall(const std::string& id, const std::string& device, uint32_t flags, - dbus::MethodResultBuilder::Pointer result) + api::MethodResultBuilder::Pointer result) { LOGI("GrantDevice call; id=" << id << "; dev=" << device); @@ -1409,7 +1389,7 @@ void ZonesManager::handleGrantDeviceCall(const std::string& id, void ZonesManager::handleRevokeDeviceCall(const std::string& id, const std::string& device, - dbus::MethodResultBuilder::Pointer result) + api::MethodResultBuilder::Pointer result) { LOGI("RevokeDevice call; id=" << id << "; dev=" << device); diff --git a/server/zones-manager.hpp b/server/zones-manager.hpp index cb730e3..825b1c8 100644 --- a/server/zones-manager.hpp +++ b/server/zones-manager.hpp @@ -32,6 +32,7 @@ #include "input-monitor.hpp" #include "proxy-call-policy.hpp" #include "utils/worker.hpp" +#include "api/method-result-builder.hpp" #include #include @@ -145,14 +146,15 @@ private: int getVTForNewZone(); void insertZone(const std::string& zoneId, const std::string& templatePath); - void notifyActiveZoneHandler(const std::string& caller, - const std::string& appliaction, - const std::string& message); - void displayOffHandler(const std::string& caller); - void handleZoneMoveFileRequest(const std::string& srcZoneId, - const std::string& dstZoneId, - const std::string& path, - dbus::MethodResultBuilder::Pointer result); + void handleNotifyActiveZoneCall(const std::string& caller, + const std::string& appliaction, + const std::string& message, + api::MethodResultBuilder::Pointer result); + void handleDisplayOffCall(const std::string& caller); + void handleFileMoveCall(const std::string& srcZoneId, + const std::string& dstZoneId, + const std::string& path, + api::MethodResultBuilder::Pointer result); void handleProxyCall(const std::string& caller, const std::string& target, const std::string& targetBusName, @@ -161,76 +163,76 @@ private: const std::string& targetMethod, GVariant* parameters, dbus::MethodResultBuilder::Pointer result); - void handleGetZoneDbuses(dbus::MethodResultBuilder::Pointer result); + void handleGetZoneDbusesCall(api::MethodResultBuilder::Pointer result); void handleDbusStateChanged(const std::string& zoneId, const std::string& dbusAddress); - void handleGetZoneIdsCall(dbus::MethodResultBuilder::Pointer result); - void handleGetActiveZoneIdCall(dbus::MethodResultBuilder::Pointer result); - void handleGetZoneInfoCall(const std::string& id, dbus::MethodResultBuilder::Pointer result); + void handleGetZoneIdsCall(api::MethodResultBuilder::Pointer result); + void handleGetActiveZoneIdCall(api::MethodResultBuilder::Pointer result); + void handleGetZoneInfoCall(const std::string& id, api::MethodResultBuilder::Pointer result); void handleSetNetdevAttrsCall(const std::string& zone, const std::string& netdev, const std::vector>& attrs, - dbus::MethodResultBuilder::Pointer result); + api::MethodResultBuilder::Pointer result); void handleGetNetdevAttrsCall(const std::string& zone, const std::string& netdev, - dbus::MethodResultBuilder::Pointer result); + api::MethodResultBuilder::Pointer result); void handleGetNetdevListCall(const std::string& zone, - dbus::MethodResultBuilder::Pointer result); + api::MethodResultBuilder::Pointer result); void handleCreateNetdevVethCall(const std::string& zone, const std::string& zoneDev, const std::string& hostDev, - dbus::MethodResultBuilder::Pointer result); + api::MethodResultBuilder::Pointer result); void handleCreateNetdevMacvlanCall(const std::string& zone, const std::string& zoneDev, const std::string& hostDev, const uint32_t& mode, - dbus::MethodResultBuilder::Pointer result); + api::MethodResultBuilder::Pointer result); void handleCreateNetdevPhysCall(const std::string& zone, const std::string& devId, - dbus::MethodResultBuilder::Pointer result); + api::MethodResultBuilder::Pointer result); void handleDeclareFileCall(const std::string& zone, const int32_t& type, const std::string& path, const int32_t& flags, const int32_t& mode, - dbus::MethodResultBuilder::Pointer result); + api::MethodResultBuilder::Pointer result); void handleDeclareMountCall(const std::string& source, const std::string& zone, const std::string& target, const std::string& type, const uint64_t& flags, const std::string& data, - dbus::MethodResultBuilder::Pointer result); + api::MethodResultBuilder::Pointer result); void handleDeclareLinkCall(const std::string& source, const std::string& zone, const std::string& target, - dbus::MethodResultBuilder::Pointer result); + api::MethodResultBuilder::Pointer result); void handleGetDeclarationsCall(const std::string& zone, - dbus::MethodResultBuilder::Pointer result); + api::MethodResultBuilder::Pointer result); void handleRemoveDeclarationCall(const std::string& zone, const std::string& declarationId, - dbus::MethodResultBuilder::Pointer result); + api::MethodResultBuilder::Pointer result); void handleSetActiveZoneCall(const std::string& id, - dbus::MethodResultBuilder::Pointer result); + api::MethodResultBuilder::Pointer result); void handleCreateZoneCall(const std::string& id, const std::string& templateName, - dbus::MethodResultBuilder::Pointer result); + api::MethodResultBuilder::Pointer result); void handleDestroyZoneCall(const std::string& id, - dbus::MethodResultBuilder::Pointer result); + api::MethodResultBuilder::Pointer result); void handleShutdownZoneCall(const std::string& id, - dbus::MethodResultBuilder::Pointer result); + api::MethodResultBuilder::Pointer result); void handleStartZoneCall(const std::string& id, - dbus::MethodResultBuilder::Pointer result); + api::MethodResultBuilder::Pointer result); void handleLockZoneCall(const std::string& id, - dbus::MethodResultBuilder::Pointer result); + api::MethodResultBuilder::Pointer result); void handleUnlockZoneCall(const std::string& id, - dbus::MethodResultBuilder::Pointer result); + api::MethodResultBuilder::Pointer result); void handleGrantDeviceCall(const std::string& id, const std::string& device, uint32_t flags, - dbus::MethodResultBuilder::Pointer result); + api::MethodResultBuilder::Pointer result); void handleRevokeDeviceCall(const std::string& id, const std::string& device, - dbus::MethodResultBuilder::Pointer result); + api::MethodResultBuilder::Pointer result); }; diff --git a/tests/unit_tests/server/ut-zone-connection.cpp b/tests/unit_tests/server/ut-zone-connection.cpp index fb6df88..2a72b87 100644 --- a/tests/unit_tests/server/ut-zone-connection.cpp +++ b/tests/unit_tests/server/ut-zone-connection.cpp @@ -41,6 +41,8 @@ #include "utils/fs.hpp" #include "utils/scoped-dir.hpp" +#include "api/method-result-builder.hpp" + using namespace vasum; using namespace vasum::utils; @@ -141,10 +143,11 @@ BOOST_AUTO_TEST_CASE(NotifyActiveZoneApiTest) Latch notifyCalled; ZoneConnection connection(acquireAddress(), nullptr); - auto callback = [&](const std::string& application, const std::string& message) { + auto callback = [&](const std::string& application, const std::string& message, api::MethodResultBuilder::Pointer result) { if (application == "testapp" && message == "testmessage") { notifyCalled.set(); } + result->setVoid(); }; connection.setNotifyActiveZoneCallback(callback); @@ -169,19 +172,19 @@ BOOST_AUTO_TEST_CASE(SignalNotificationApiTest) const std::string& objectPath, const std::string& interface, const std::string& signalName, - GVariant* parameters) { + GVariant* parameters) { if (objectPath == api::zone::OBJECT_PATH && - interface == api::zone::INTERFACE && - signalName == api::zone::SIGNAL_NOTIFICATION && - g_variant_is_of_type(parameters, G_VARIANT_TYPE("(sss)"))) { + interface == api::zone::INTERFACE && + signalName == api::zone::SIGNAL_NOTIFICATION && + g_variant_is_of_type(parameters, G_VARIANT_TYPE("(sss)"))) { const gchar* zone = NULL; const gchar* application = NULL; const gchar* message = NULL; g_variant_get(parameters, "(&s&s&s)", &zone, &application, &message); if (zone == std::string("testzone") && - application == std::string("testapp") && - message == std::string("testmessage")) { + application == std::string("testapp") && + message == std::string("testmessage")) { signalEmitted.set(); } } diff --git a/tests/unit_tests/server/ut-zones-manager.cpp b/tests/unit_tests/server/ut-zones-manager.cpp index 0e40093..ea858e2 100644 --- a/tests/unit_tests/server/ut-zones-manager.cpp +++ b/tests/unit_tests/server/ut-zones-manager.cpp @@ -256,7 +256,7 @@ public: api::host::INTERFACE, api::host::METHOD_GET_ZONE_DBUSES, NULL, - "(a{ss})"); + "(a(ss))"); GVariant* array = NULL; g_variant_get(result.get(), "(*)", &array); dbus::GVariantPtr autounref(array, g_variant_unref); @@ -264,7 +264,7 @@ public: for (size_t n = 0; n < count; ++n) { const char* zoneId = NULL; const char* dbusAddress = NULL; - g_variant_get_child(array, n, "{&s&s}", &zoneId, &dbusAddress); + g_variant_get_child(array, n, "(&s&s)", &zoneId, &dbusAddress); dbuses.insert(Dbuses::value_type(zoneId, dbusAddress)); } return dbuses; -- 2.7.4 From 51dee2857db927c2be55c5eb6a2be8031f4b9082 Mon Sep 17 00:00:00 2001 From: Mateusz Malicki Date: Thu, 12 Mar 2015 15:42:08 +0100 Subject: [PATCH 13/16] Added functions to file descriptor passing and joining to namespace [Feature] Added functions to file descriptor passing and joining to namespace [Cause] N/A [Solution] N/A [Verification] Build, run tests Change-Id: I72ec4f65e469125d5add10cb87a6d044b4ff9b05 --- common/utils/environment.cpp | 167 +++++++++++++++++++++++++++++++++++++------ common/utils/environment.hpp | 9 +++ common/utils/execute.cpp | 64 +++++++++++++---- common/utils/execute.hpp | 18 +++++ common/utils/make-clean.hpp | 53 ++++++++++++++ 5 files changed, 274 insertions(+), 37 deletions(-) create mode 100644 common/utils/make-clean.hpp diff --git a/common/utils/environment.cpp b/common/utils/environment.cpp index 9d3f75a..fc45321 100644 --- a/common/utils/environment.cpp +++ b/common/utils/environment.cpp @@ -26,6 +26,8 @@ #include "utils/environment.hpp" #include "utils/execute.hpp" +#include "utils/make-clean.hpp" +#include "base-exception.hpp" #include "logger/logger.hpp" #include @@ -34,7 +36,95 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include + +#if !__GLIBC_PREREQ(2, 14) + +#include + +#ifdef __NR_setns +static inline int setns(int fd, int nstype) +{ + // setns system call are available since v2.6.39-6479-g7b21fdd + return syscall(__NR_setns, fd, nstype); +} +#else +#error "setns syscall isn't available" +#endif + +#endif + +using namespace vasum::utils; + +namespace { + +const std::map NAMESPACES = { + {CLONE_NEWIPC, "ipc"}, + {CLONE_NEWNET, "net"}, + {CLONE_NEWNS, "mnt"}, + {CLONE_NEWPID, "pid"}, + {CLONE_NEWUSER, "user"}, + {CLONE_NEWUTS, "uts"}}; + +int fdRecv(int socket) +{ + msghdr msg = make_clean(); + iovec iov = make_clean(); + char cmsgBuff[CMSG_SPACE(sizeof(int))]; + + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + msg.msg_control = cmsgBuff; + msg.msg_controllen = sizeof(cmsgBuff); + + int ret = recvmsg(socket, &msg, MSG_CMSG_CLOEXEC); + if (ret != 0 || msg.msg_flags & (MSG_TRUNC | MSG_ERRQUEUE | MSG_OOB | MSG_CTRUNC | MSG_EOR)) { + LOGE("Can't receive fd: ret: " << ret << ", flags: " << msg.msg_flags); + return -1; + } + + cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); + assert(cmsg->cmsg_level == SOL_SOCKET); + assert(cmsg->cmsg_type == SCM_RIGHTS); + assert(CMSG_NXTHDR(&msg, cmsg) == NULL); + return *reinterpret_cast(CMSG_DATA(cmsg)); +} + +bool fdSend(int socket, int fd) +{ + msghdr msg = make_clean(); + struct iovec iov = make_clean(); + struct cmsghdr *cmsg = NULL; + char cmsgBuff[CMSG_SPACE(sizeof(int))]; + + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + msg.msg_control = cmsgBuff; + msg.msg_controllen = sizeof(cmsgBuff); + + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(sizeof(int)); + *reinterpret_cast(CMSG_DATA(cmsg)) = fd; + + int ret = sendmsg(socket, &msg, 0); + if (ret < 0) { + LOGE("Can't send fd: ret: " << ret); + return false; + } + return true; +} +} // namespace namespace vasum { namespace utils { @@ -88,43 +178,76 @@ bool dropRoot(uid_t uid, gid_t gid, const std::vector& caps) bool launchAsRoot(const std::function& func) { // TODO optimize if getuid() == 0 - pid_t pid = fork(); - if (pid < 0) { - LOGE("Fork failed: " << strerror(errno)); - return false; - } - - if (pid == 0) { + return executeAndWait([&func]() { if (::setuid(0) < 0) { - LOGW("Failed to become root: " << strerror(errno)); + LOGW("Failed to become root: " << getSystemErrorMessage()); _exit(EXIT_FAILURE); } - try { - if (!func()) { - LOGE("Failed to successfully execute func"); - _exit(EXIT_FAILURE); - } - } catch (const std::exception& e) { - LOGE("Failed to successfully execute func: " << e.what()); + if (!func()) { + LOGE("Failed to successfully execute func"); _exit(EXIT_FAILURE); } + }); +} - _exit(EXIT_SUCCESS); +bool joinToNs(int nsPid, int ns) +{ + auto ins = NAMESPACES.find(ns); + if (ins == NAMESPACES.end()) { + LOGE("Namespace isn't supported: " << ns); + return false; } - - int status; - if (!waitPid(pid, status)) { + std::string nsPath = "/proc/" + std::to_string(nsPid) + "/ns/" + ins->second; + int nsFd = ::open(nsPath.c_str(), O_RDONLY); + if (nsFd == -1) { + LOGE("Can't open namesace: " + getSystemErrorMessage()); return false; } - if (status != 0) { - LOGE("Function launched as root exited with status " << status); + int ret = setns(nsFd, ins->first); + if (ret != 0) { + LOGE("Can't set namesace: " + getSystemErrorMessage()); + close(nsFd); return false; } - + close(nsFd); return true; } +int passNemaspacedFd(int nsPid, int ns, const std::function& fdFactory) +{ + int fds[2]; + int ret = socketpair(PF_LOCAL, SOCK_RAW, 0, fds); + if (ret == -1) { + LOGE("Can't create socket pair: " << vasum::getSystemErrorMessage()); + return -1; + } + bool success = executeAndWait([&, fds, nsPid, ns]() { + close(fds[0]); + + int fd = -1; + if (joinToNs(nsPid, ns)) { + fd = fdFactory(); + } + if (fd == -1) { + close(fds[1]); + _exit(EXIT_FAILURE); + } + LOGT("FD pass, send: " << fd); + fdSend(fds[1], fd); + close(fds[1]); + close(fd); + }); + + close(fds[1]); + int fd = -1; + if (success) { + fd = fdRecv(fds[0]); + } + close(fds[0]); + LOGT("FD pass, rcv: " << fd); + return fd; +} } // namespace utils } // namespace vasum diff --git a/common/utils/environment.hpp b/common/utils/environment.hpp index 2060e19..d63ac16 100644 --- a/common/utils/environment.hpp +++ b/common/utils/environment.hpp @@ -52,6 +52,15 @@ bool dropRoot(uid_t uid, gid_t gid, const std::vector& caps); */ bool launchAsRoot(const std::function& func); +/** + * Join to namespace + */ +bool joinToNs(int nsPid, int ns); + +/** + * Pass file descriptor from namespace of some process + */ +int passNemaspacedFd(int nsPid, int ns, const std::function& fdFactory); } // namespace utils } // namespace vasum diff --git a/common/utils/execute.cpp b/common/utils/execute.cpp index 5c9d1ce..4e8eebf 100644 --- a/common/utils/execute.cpp +++ b/common/utils/execute.cpp @@ -23,6 +23,7 @@ */ #include "config.hpp" +#include "base-exception.hpp" #include "utils/execute.hpp" #include "logger/logger.hpp" @@ -46,22 +47,65 @@ std::ostream& operator<< (std::ostream& out, const char* const* argv) return out; } +bool isExecutionSuccessful(int status) +{ + if (!WIFEXITED(status)) { + if (WIFSIGNALED(status)) { + LOGE("Child terminated by signal, signal: " << WTERMSIG(status)); + } else if (WIFSTOPPED(status)) { + LOGW("Child was stopped by signal " << WSTOPSIG(status)); + } else { + LOGE("Child exited abnormally, status: " << status); + } + return false; + } + if (WEXITSTATUS(status) != EXIT_SUCCESS) { + LOGE("Child exit status: " << WEXITSTATUS(status)); + return false; + } + return true; +} + } // namespace -bool executeAndWait(const char* fname, const char* const* argv, int& status) +bool executeAndWait(const std::function& func, int& status) { - LOGD("Execute " << fname << argv); + LOGD("Execute child process"); pid_t pid = fork(); if (pid == -1) { - LOGE("Fork failed"); + LOGE("Fork failed: " << vasum::getSystemErrorMessage()); return false; } if (pid == 0) { + func(); + _exit(EXIT_SUCCESS); + } + return waitPid(pid, status); +} + +bool executeAndWait(const std::function& func) +{ + int status; + if (!executeAndWait(func, status)) { + return false; + } + return isExecutionSuccessful(status); +} + + +bool executeAndWait(const char* fname, const char* const* argv, int& status) +{ + LOGD("Execute " << fname << argv); + + bool success = executeAndWait([=]() { execv(fname, const_cast(argv)); _exit(EXIT_FAILURE); + }, status); + if (!success) { + LOGW("Process " << fname << " has exited abnormally"); } - return waitPid(pid, status); + return success; } bool executeAndWait(const char* fname, const char* const* argv) @@ -70,17 +114,7 @@ bool executeAndWait(const char* fname, const char* const* argv) 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; + return isExecutionSuccessful(status); } bool waitPid(pid_t pid, int& status) diff --git a/common/utils/execute.hpp b/common/utils/execute.hpp index 47db58a..8f7fd56 100644 --- a/common/utils/execute.hpp +++ b/common/utils/execute.hpp @@ -26,14 +26,32 @@ #define COMMON_UTILS_EXECUTE_HPP #include +#include namespace vasum { namespace utils { +/** + * Execute binary + */ +///@{ bool executeAndWait(const char* fname, const char* const* argv); bool executeAndWait(const char* fname, const char* const* argv, int& status); +///@} + +/** + * Execute function in child process + */ +///@{ +bool executeAndWait(const std::function& func, int& status); + +bool executeAndWait(const std::function& func); +///@} +/** + * Wait until child processes ends + */ bool waitPid(pid_t pid, int& status); } // namespace utils diff --git a/common/utils/make-clean.hpp b/common/utils/make-clean.hpp new file mode 100644 index 0000000..1d551ef --- /dev/null +++ b/common/utils/make-clean.hpp @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * + * Contact: Mateusz Malicki + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +/** + * @file + * @author Mateusz Malicki (m.malicki2@samsung.com) + * @brief Function used to initialize C structures + */ + +#ifndef COMMON_UTILS_MAKE_CLEAN_HPP +#define COMMON_UTILS_MAKE_CLEAN_HPP + +#include +#include + +namespace vasum { +namespace utils { + +template +void make_clean(T& value) +{ + static_assert(std::is_pod::value, "make_clean require trivial and standard-layout"); + std::fill_n(reinterpret_cast(&value), sizeof(value), 0); +} + +template +T make_clean() +{ + T value; + make_clean(value); + return value; +} + +} // namespace utils +} // namespace vasum + + +#endif // COMMON_UTILS_MAKE_CLEAN_HPP -- 2.7.4 From 476935e75959c057dcd506804cc873e29d30a425 Mon Sep 17 00:00:00 2001 From: Mateusz Malicki Date: Mon, 2 Mar 2015 12:44:42 +0100 Subject: [PATCH 14/16] Ability to fetch information from netlink (list netdev) [Feature] Ability to fetch information from netlink, list netdevs [Cause] N/A [Solution] N/A [Verification] Build, run test, run cli command (zone_get_netdevs) Change-Id: I404f6c2c47b0d2882b94649c134981a09a978a52 --- common/netlink/netlink-message.cpp | 223 +++++++++++++++++++++++++++++------- common/netlink/netlink-message.hpp | 132 ++++++++++++++++++++- common/netlink/netlink.cpp | 161 ++++++++++++++++---------- common/netlink/netlink.hpp | 17 ++- server/netdev.cpp | 38 +++--- server/netdev.hpp | 2 + server/zone-admin.cpp | 2 +- tests/unit_tests/server/ut-zone.cpp | 19 ++- 8 files changed, 473 insertions(+), 121 deletions(-) diff --git a/common/netlink/netlink-message.cpp b/common/netlink/netlink-message.cpp index 9f33450..ac42c58 100644 --- a/common/netlink/netlink-message.cpp +++ b/common/netlink/netlink-message.cpp @@ -29,8 +29,10 @@ #include +#include #include #include +#include #include #include @@ -38,16 +40,15 @@ #include #include -#ifndef PAGE_SIZE -#define PAGE_SIZE 4096 -#endif - namespace { -const int NLMSG_GOOD_SIZE = 2*PAGE_SIZE; -inline rtattr* NLMSG_TAIL(nlmsghdr* nmsg) +inline const rtattr* asAttr(const void* data) { return reinterpret_cast(data); } +inline const nlmsghdr* asHdr(const void* data) { return reinterpret_cast(data); } +inline rtattr* asAttr(void* data) { return reinterpret_cast(data); } +inline nlmsghdr* asHdr(void* data) { return reinterpret_cast(data); } +inline char* NLMSG_TAIL(nlmsghdr* nmsg) { - return reinterpret_cast(reinterpret_cast(nmsg) + NLMSG_ALIGN(nmsg->nlmsg_len)); + return reinterpret_cast(nmsg) + NLMSG_ALIGN(nmsg->nlmsg_len); } } // namespace @@ -55,10 +56,15 @@ inline rtattr* NLMSG_TAIL(nlmsghdr* nmsg) namespace vasum { namespace netlink { +NetlinkResponse send(const NetlinkMessage& msg) +{ + return send(msg, 0); +} + NetlinkMessage::NetlinkMessage(uint16_t type, uint16_t flags) { - static uint32_t seq = 0; - mNlmsg.resize(NLMSG_GOOD_SIZE, 0); + static std::atomic seq(0); + mNlmsg.resize(NLMSG_HDRLEN, 0); hdr().nlmsg_len = NLMSG_HDRLEN; hdr().nlmsg_flags = flags | NLM_F_ACK; hdr().nlmsg_type = type; @@ -68,18 +74,17 @@ NetlinkMessage::NetlinkMessage(uint16_t type, uint16_t flags) NetlinkMessage& NetlinkMessage::beginNested(int ifla) { - struct rtattr *nest = NLMSG_TAIL(&hdr()); + auto offset = std::distance(reinterpret_cast(&hdr()), NLMSG_TAIL(&hdr())); put(ifla, NULL, 0); - mNested.push(nest); + mNested.push(offset); return *this; } NetlinkMessage& NetlinkMessage::endNested() { assert(!mNested.empty()); - rtattr *nest = reinterpret_cast(mNested.top()); - nest->rta_len = std::distance(reinterpret_cast(nest), - reinterpret_cast(NLMSG_TAIL(&hdr()))); + rtattr* nest = asAttr(reinterpret_cast(&hdr()) + mNested.top()); + nest->rta_len = std::distance(reinterpret_cast(nest), NLMSG_TAIL(&hdr())); mNested.pop(); return *this; } @@ -96,7 +101,7 @@ NetlinkMessage& NetlinkMessage::put(int ifla, const void* data, int len) int newLen = NLMSG_ALIGN(hdr().nlmsg_len) + RTA_ALIGN(rtalen); setMinCapacity(newLen); - rta = NLMSG_TAIL(&hdr()); + rta = asAttr(NLMSG_TAIL(&hdr())); rta->rta_type = ifla; rta->rta_len = rtalen; memcpy(RTA_DATA(rta), data, len); @@ -113,11 +118,11 @@ NetlinkMessage& NetlinkMessage::put(const void* data, int len) } nlmsghdr& NetlinkMessage::hdr() { - return *reinterpret_cast(mNlmsg.data()); + return *asHdr(mNlmsg.data()); } const nlmsghdr& NetlinkMessage::hdr() const { - return *reinterpret_cast(mNlmsg.data()); + return *asHdr(mNlmsg.data()); } void NetlinkMessage::setMinCapacity(unsigned int size) @@ -127,41 +132,179 @@ void NetlinkMessage::setMinCapacity(unsigned int size) } } -void send(const NetlinkMessage& msg) +NetlinkResponse::NetlinkResponse(std::unique_ptr>&& message) + : mNlmsg(std::move(message)) + , mNlmsgHdr(asHdr(mNlmsg.get()->data())) + , mPosition(NLMSG_HDRLEN) { - //TODO: Handle messages with responses - assert(msg.hdr().nlmsg_flags & NLM_F_ACK); +} + +bool NetlinkResponse::hasMessage() const +{ + unsigned int tail = size() - getHdrPosition(); + bool hasHeader = NLMSG_OK(mNlmsgHdr, tail); + if (!hasHeader) { + return false; + } + //Check if isn't ACK message + return NLMSG_PAYLOAD(mNlmsgHdr,0) > sizeof(uint32_t); +} + +int NetlinkResponse::getMessageType() const +{ + return mNlmsgHdr->nlmsg_type; +} + +void NetlinkResponse::fetchNextMessage() +{ + if (mNlmsgHdr->nlmsg_type == NLMSG_DONE) { + throw VasumException("There is no next message"); + } + int tail = size() - mPosition; + mNlmsgHdr = NLMSG_NEXT(mNlmsgHdr, tail); + mPosition = getHdrPosition() + NLMSG_HDRLEN; +} + +bool NetlinkResponse::hasAttribute() const +{ + assert(mPosition >= getHdrPosition()); + int tail = mNlmsgHdr->nlmsg_len - (mPosition - getHdrPosition()); + return RTA_OK(asAttr(get(0)), tail); +} - const int answerLen = NLMSG_ALIGN(msg.hdr().nlmsg_len + sizeof(nlmsgerr)); - std::unique_ptr answerBuff(new char[answerLen]); - nlmsghdr* answer = reinterpret_cast(answerBuff.get()); - answer->nlmsg_len = answerLen; +bool NetlinkResponse::isNestedAttribute() const +{ + return asAttr(get(RTA_LENGTH(0)))->rta_len == RTA_LENGTH(0); +} + +void NetlinkResponse::skipAttribute() +{ + const rtattr *rta = asAttr(get(RTA_LENGTH(0))); + if (size() < mPosition + RTA_ALIGN(rta->rta_len)) { + LOGE("Skipping out of buffer:" + << " to: " << mPosition + RTA_ALIGN(rta->rta_len) + << ", buf size: " << size()); + throw VasumException("Skipping out of buffer"); + } + seek(RTA_ALIGN(rta->rta_len)); +} + +NetlinkResponse& NetlinkResponse::openNested(int ifla) +{ + const rtattr *rta = asAttr(get(RTA_LENGTH(0))); + if (rta->rta_type == ifla) { + LOGE("Wrong attribute type, expected: " << ifla << ", got: " << rta->rta_type); + throw VasumException("Wrong attribute type"); + } + int pos = mPosition; + seek(RTA_LENGTH(0)); + mNested.push(pos); + return *this; +} + +NetlinkResponse& NetlinkResponse::closeNested() +{ + assert(!mNested.empty()); + int pos = mNested.top(); + const rtattr *rta = asAttr(mNlmsg->data() + pos); + if (rta->rta_len != mPosition - pos) { + LOGE("There is no nested attribute end. Did you read all attributes (read: " + << mPosition - pos << ", length: " << rta->rta_len); + throw VasumException("There is no nested attribute end"); + } + mNested.pop(); + mPosition = pos; + return *this; +} +NetlinkResponse& NetlinkResponse::fetch(int ifla, std::string& value, int maxSize) +{ + value = std::string(get(ifla, maxSize)); + skipAttribute(); + return *this; +} + +const char* NetlinkResponse::get(int ifla, int len) const +{ + const rtattr *rta = asAttr(get(RTA_LENGTH(len < 0 ? 0 : len))); + if (rta->rta_type != ifla) { + LOGE("Wrong attribute type, expected:" << ifla << ", got: " << rta->rta_type); + throw VasumException("Wrong attribute type"); + } + if (len >= 0 && rta->rta_len != RTA_LENGTH(len)) { + LOGE("Wrong attribute length, expected: " << rta->rta_len + ", got " << len); + throw VasumException("Wrong attribute length"); + } + return reinterpret_cast(RTA_DATA(get(rta->rta_len))); +} + +const char* NetlinkResponse::get(int len) const +{ + if (size() < mPosition + len) { + LOGE("Read out of buffer:" + << " from: " << mPosition + len + << ", buf size: " << size()); + throw VasumException("Read out of buffer"); + } + return mNlmsg->data() + mPosition; +} + +NetlinkResponse& NetlinkResponse::fetch(int ifla, char* data, int len) +{ + std::copy_n(get(ifla, len), len, data); + skipAttribute(); + return *this; +} + +NetlinkResponse& NetlinkResponse::fetch(char* data, int len) +{ + std::copy_n(get(len), len, data); + seek(len); + return *this; +} + +int NetlinkResponse::getAttributeType() const +{ + return asAttr(get(RTA_LENGTH(0)))->rta_type; +} + +NetlinkResponse& NetlinkResponse::seek(int len) +{ + if (size() < mPosition + len) { + throw VasumException("Skipping out of buffer"); + } + mPosition += len; + return *this; +} + +int NetlinkResponse::size() const +{ + return mNlmsg->size(); +} + +inline int NetlinkResponse::getHdrPosition() const +{ + return std::distance(reinterpret_cast(mNlmsg->data()), + reinterpret_cast(mNlmsgHdr)); +} + +NetlinkResponse send(const NetlinkMessage& msg, int pid) +{ + assert(msg.hdr().nlmsg_flags & NLM_F_ACK); + + std::unique_ptr> data; Netlink nl; - nl.open(); + nl.open(pid); try { nl.send(&msg.hdr()); - //Receive ACK Netlink Message - do { - nl.rcv(answer); - } while (answer->nlmsg_type == NLMSG_NOOP); + data = nl.rcv(msg.hdr().nlmsg_seq); } catch (const std::exception& ex) { LOGE("Sending failed (" << ex.what() << ")"); nl.close(); throw; } nl.close(); - if (answer->nlmsg_type != NLMSG_ERROR) { - // It is not NACK/ACK message - throw VasumException("Sending failed ( unrecognized message type )"); - } - nlmsgerr *err = reinterpret_cast(NLMSG_DATA(answer)); - if (answer->nlmsg_seq != msg.hdr().nlmsg_seq) { - throw VasumException("Sending failed ( answer message was mismatched )"); - } - if (err->error) { - throw VasumException("Sending failed (" + getSystemErrorMessage(-err->error) + ")"); - } + return NetlinkResponse(std::move(data)); } } // namespace netlink diff --git a/common/netlink/netlink-message.hpp b/common/netlink/netlink-message.hpp index e828feb..6b84de4 100644 --- a/common/netlink/netlink-message.hpp +++ b/common/netlink/netlink-message.hpp @@ -25,6 +25,7 @@ #ifndef COMMON_NETLINK_NETLINK_MESSAGE_HPP #define COMMON_NETLINK_NETLINK_MESSAGE_HPP +#include #include #include #include @@ -35,6 +36,9 @@ namespace vasum { namespace netlink { +class NetlinkResponse; +class NetlinkMessage; + /** * NetlinkMessage is used to creatie a netlink messages */ @@ -81,21 +85,124 @@ public: * Send netlink message * * It is not thread safe + * @param msg Netlink message + * @param pid Process id which describes network namespace */ - friend void send(const NetlinkMessage& msg); + friend NetlinkResponse send(const NetlinkMessage& msg, int pid); private: std::vector mNlmsg; - std::stack mNested; + std::stack mNested; NetlinkMessage& put(int ifla, const void* data, int len); NetlinkMessage& put(const void* data, int len); nlmsghdr& hdr(); const nlmsghdr& hdr() const; void setMinCapacity(unsigned int size); +}; + +/** + * NetlinkResponse is used to read netlink messages + */ +class NetlinkResponse { +public: + /** + * Check if theres is next message in netlink response + */ + bool hasMessage() const; + + /** + * Fetch next message + */ + void fetchNextMessage(); + /** + * Get message type + */ + int getMessageType() const; + /** + * Check if there is any attribute in message + */ + bool hasAttribute() const; + + /** + * Check if current attribute is nested + */ + bool isNestedAttribute() const; + + /** + * Skip attribute + */ + void skipAttribute(); + + /** + * Start reading nested attribute + */ + NetlinkResponse& openNested(int ifla); + + /** + * End reading nested attribute + */ + NetlinkResponse& closeNested(); + + ///@{ + /** + * Fetch attribute + */ + NetlinkResponse& fetch(int ifla, std::string& value, int maxSize = std::string::npos); + template + NetlinkResponse& fetch(int ifla, T& value); + ///@} + + /** + * Get attributie type + **/ + int getAttributeType() const; + + /** + * Fetch data of type T + */ + template + NetlinkResponse& fetch(T& value); + + /** + * Skip data of type T + */ + template + NetlinkResponse& skip(); + + /** + * Send netlink message + * + * It is not thread safe + * @param msg Netlink message + * @param pid Process id which describes network namespace + */ + friend NetlinkResponse send(const NetlinkMessage& msg, int pid); +private: + NetlinkResponse(std::unique_ptr>&& message); + + std::unique_ptr> mNlmsg; + std::stack mNested; + nlmsghdr* mNlmsgHdr; + int mPosition; + + const char* get(int ifla, int iflasize) const; + const char* get(int size = 0) const; + NetlinkResponse& fetch(int ifla, char* data, int len); + NetlinkResponse& fetch(char* data, int len); + NetlinkResponse& seek(int len); + int size() const; + int getHdrPosition() const; }; +/** + * Send netlink message + * + * It is not thread safe + */ +NetlinkResponse send(const NetlinkMessage& msg); + template NetlinkMessage& NetlinkMessage::put(int ifla, const T& value) { @@ -110,6 +217,27 @@ NetlinkMessage& NetlinkMessage::put(const T& value) return put(&value, sizeof(value)); } +template +NetlinkResponse& NetlinkResponse::fetch(int ifla, T& value) +{ + static_assert(std::is_pod::value, "Require trivial and standard-layout"); + return fetch(ifla, reinterpret_cast(&value), sizeof(value)); +} + +template +NetlinkResponse& NetlinkResponse::fetch(T& value) +{ + static_assert(std::is_pod::value, "Require trivial and standard-layout structure"); + return fetch(reinterpret_cast(&value), sizeof(value)); +} + +template +NetlinkResponse& NetlinkResponse::skip() +{ + static_assert(std::is_pod::value, "Require trivial and standard-layout structure"); + return seek(sizeof(T)); +} + } // namespace netlink } // namespace vasum diff --git a/common/netlink/netlink.cpp b/common/netlink/netlink.cpp index fa86b45..e5cf8a7 100644 --- a/common/netlink/netlink.cpp +++ b/common/netlink/netlink.cpp @@ -26,6 +26,8 @@ #include "netlink.hpp" #include "utils.hpp" #include "base-exception.hpp" +#include "utils/make-clean.hpp" +#include "utils/environment.hpp" #include #include @@ -34,21 +36,54 @@ #include #include -namespace vasum { +#ifndef PAGE_SIZE +#define PAGE_SIZE 4096 +#endif + +using namespace vasum; namespace { -template -T make_clean() +const int NLMSG_RCV_GOOD_SIZE = 2*PAGE_SIZE; + +int vsm_recvmsg(int fd, struct msghdr *msg, int flags) { - static_assert(std::is_pod::value, "make_clean require trivial and standard-layout"); - T value; - std::fill_n(reinterpret_cast(&value), sizeof(value), 0); - return value; + int ret = recvmsg(fd, msg, flags); + if (ret < 0) { + LOGE("Can't receive message: " + getSystemErrorMessage()); + } else if (ret == 0 && msg->msg_iov && msg->msg_iov->iov_len > 0) { + LOGE("Peer has performed an orderly shutdown"); + } else if (msg->msg_flags & MSG_TRUNC) { + LOGE("Can't receive message: " + getSystemErrorMessage(EMSGSIZE)); + } else if (msg->msg_flags & MSG_ERRQUEUE) { + LOGE("No data was received but an extended error"); + } else if (msg->msg_flags & MSG_OOB) { + LOGE("Internal error (expedited or out-of-band data were received)"); + } else if (msg->msg_flags & MSG_CTRUNC) { + LOGE("Some control data were discarded"); + } else if (msg->msg_flags & MSG_EOR) { + LOGE("End-of-record"); + } else { + // All ok + return ret; + } + throw VasumException("Can't receive netlink message"); +} + +void vsm_sendmsg(int fd, const struct msghdr *msg, int flags) +{ + int ret = sendmsg(fd, msg, flags); + if (ret < 0) { + LOGE("Can't send message: " << getSystemErrorMessage()); + throw VasumException("Can't send netlink message"); + } } } // namespace +namespace vasum { +namespace netlink { + Netlink::Netlink() : mFd(-1) { } @@ -58,22 +93,30 @@ Netlink::~Netlink() close(); } -void Netlink::open() +void Netlink::open(int netNsPid) { + auto fdFactory = []{ return socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); }; + assert(mFd == -1); - mFd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (netNsPid == 0 || netNsPid == getpid()) { + mFd = fdFactory(); + if (mFd == -1) { + LOGE("Can't open socket: " << getSystemErrorMessage()); + } + } else { + mFd = utils::passNemaspacedFd(netNsPid, CLONE_NEWNET, fdFactory); + } if (mFd == -1) { - LOGE("Can't open socket (" << getSystemErrorMessage() << ")"); throw VasumException("Can't open netlink connection"); } - sockaddr_nl local = make_clean(); + sockaddr_nl local = utils::make_clean(); local.nl_family = AF_NETLINK; if (bind(mFd, (struct sockaddr *)&local, sizeof(local)) < 0) { int err = errno; close(); - LOGE("Can't bind to socket (" << getSystemErrorMessage(err) << ")"); + LOGE("Can't bind to socket: " << getSystemErrorMessage(err)); throw VasumException("Can't set up netlink connection"); } } @@ -86,70 +129,72 @@ void Netlink::close() } } -void Netlink::send(const nlmsghdr *nlmsg) +unsigned int Netlink::send(const void *nlmsg) { - msghdr msg = make_clean(); - sockaddr_nl nladdr = make_clean(); - iovec iov = make_clean(); + msghdr msg = utils::make_clean(); + sockaddr_nl nladdr = utils::make_clean(); + iovec iov = utils::make_clean(); - iov.iov_base = (void *)nlmsg; - iov.iov_len = nlmsg->nlmsg_len; + iov.iov_base = const_cast(nlmsg); + iov.iov_len = reinterpret_cast(nlmsg)->nlmsg_len; msg.msg_name = &nladdr; msg.msg_namelen = sizeof(nladdr); msg.msg_iov = &iov; msg.msg_iovlen = 1; nladdr.nl_family = AF_NETLINK; - int ret = sendmsg(mFd, &msg, 0); - if (ret < 0) { - LOGE("Can't send message (" << getSystemErrorMessage() << ")"); - throw VasumException("Can't send netlink message"); - } + vsm_sendmsg(mFd, &msg, 0); + return reinterpret_cast(nlmsg)->nlmsg_seq; } -int Netlink::rcv(nlmsghdr *answer) +std::unique_ptr> Netlink::rcv(unsigned int nlmsgSeq) { - //TODO: Handle too small buffer situation (buffer resizing) - msghdr msg = make_clean(); - sockaddr_nl nladdr = make_clean(); - iovec iov = make_clean(); + std::unique_ptr> buf(new std::vector()); + + msghdr msg = utils::make_clean(); + sockaddr_nl nladdr = utils::make_clean(); + iovec iov = utils::make_clean(); - iov.iov_base = answer; - iov.iov_len = answer->nlmsg_len; msg.msg_name = &nladdr; msg.msg_namelen = sizeof(nladdr); msg.msg_iov = &iov; msg.msg_iovlen = 1; nladdr.nl_family = AF_NETLINK; - int ret = recvmsg(mFd, &msg, 0); - if (ret < 0) { - LOGE("Can't receive message (" + getSystemErrorMessage() + ")"); - throw VasumException("Can't receive netlink message"); - } - if (ret == 0) { - LOGE("Peer has performed an orderly shutdown"); - throw VasumException("Can't receive netlink message"); - } - if (msg.msg_flags & MSG_TRUNC) { - LOGE("Can't receive message (" + getSystemErrorMessage(EMSGSIZE) + ")"); - throw VasumException("Can't receive netlink message"); - } - if (msg.msg_flags & MSG_ERRQUEUE) { - LOGE("No data was received but an extended error"); - throw VasumException("Can't receive netlink message"); - } - if (msg.msg_flags & MSG_OOB) { - LOGE("Internal error (expedited or out-of-band data were received)"); - throw VasumException("Can't receive netlink message"); - } - if (msg.msg_flags & (MSG_EOR | MSG_CTRUNC)) { - assert(!"This should not happen!"); - LOGE("Internal error (" << std::to_string(msg.msg_flags) << ")"); - throw VasumException("Internal error while recaiving netlink message"); - } - - return ret; + nlmsghdr* answer; + nlmsghdr* lastOk = NULL; + size_t offset = 0; + do { + buf->resize(offset + NLMSG_RCV_GOOD_SIZE); + answer = reinterpret_cast(buf->data() + offset); + iov.iov_base = answer; + iov.iov_len = buf->size() - offset; + unsigned int ret = vsm_recvmsg(mFd, &msg, 0); + for (unsigned int len = ret; NLMSG_OK(answer, len); answer = NLMSG_NEXT(answer, len)) { + lastOk = answer; + if (answer->nlmsg_type == NLMSG_ERROR) { + // It is NACK/ACK message + nlmsgerr *err = reinterpret_cast(NLMSG_DATA(answer)); + if (answer->nlmsg_seq != nlmsgSeq) { + throw VasumException("Sending failed: answer message was mismatched"); + } + if (err->error) { + throw VasumException("Sending failed: " + getSystemErrorMessage(-err->error)); + } + } else if (answer->nlmsg_type == NLMSG_OVERRUN) { + throw VasumException("Sending failed: data lost"); + } + } + if (lastOk == NULL) { + LOGE("Something went terribly wrong. Check vsm_recvmsg function"); + throw VasumException("Can't receive data from system"); + } + offset += NLMSG_ALIGN(ret); + } while (lastOk->nlmsg_type != NLMSG_DONE && lastOk->nlmsg_flags & NLM_F_MULTI); + + buf->resize(offset); + return buf; } +} //namespace netlink } //namespace vasum diff --git a/common/netlink/netlink.hpp b/common/netlink/netlink.hpp index 2e73ce8..8d33957 100644 --- a/common/netlink/netlink.hpp +++ b/common/netlink/netlink.hpp @@ -25,9 +25,11 @@ #ifndef COMMON_NETLINK_NETLINK_HPP #define COMMON_NETLINK_NETLINK_HPP -#include +#include +#include namespace vasum { +namespace netlink { /** * Netlink class is responsible for communicating @@ -43,8 +45,10 @@ public: /** * Open connnection + * + * @param netNsPid pid which defines net namespace */ - void open(); + void open(int netNsPid = 0); /** * Close connection @@ -58,8 +62,9 @@ public: * different instances at the same time * * @param nlmsg pointer to message + * @return sequence number */ - void send(const nlmsghdr *nlmsg); + unsigned int send(const void* nlmsg); /** * Receive message @@ -67,13 +72,15 @@ public: * It is not thread safe and even you shouldn't call this function on * different instances at the same time * - * @param answer pointer to answer buffer + * @param nlmsgSeq sequence number + * @return received data */ - int rcv(nlmsghdr *answer); + std::unique_ptr> rcv(unsigned int nlmsgSeq); private: int mFd; }; +} // namesapce netlink } // namespace vasum #endif /* COMMON_NETLINK_NETLINK_HPP */ diff --git a/server/netdev.cpp b/server/netdev.cpp index b4a2d21..f8f9264 100644 --- a/server/netdev.cpp +++ b/server/netdev.cpp @@ -25,6 +25,7 @@ #include "config.hpp" #include "netdev.hpp" #include "netlink/netlink-message.hpp" +#include "utils/make-clean.hpp" #include "utils.hpp" #include "exception.hpp" @@ -55,15 +56,6 @@ namespace netdev { namespace { -template -T make_clean() -{ - static_assert(std::is_pod::value, "make_clean require trivial and standard-layout"); - T value; - std::fill_n(reinterpret_cast(&value), sizeof(value), 0); - return value; -} - string getUniqueVethName() { auto find = [](const ifaddrs* ifaddr, const string& name) -> bool { @@ -109,7 +101,7 @@ void createPipedNetdev(const string& netdev1, const string& netdev2) validateNetdevName(netdev2); NetlinkMessage nlm(RTM_NEWLINK, NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK); - ifinfomsg infoPeer = make_clean(); + ifinfomsg infoPeer = utils::make_clean(); infoPeer.ifi_family = AF_UNSPEC; infoPeer.ifi_change = 0xFFFFFFFF; nlm.put(infoPeer) @@ -138,7 +130,7 @@ void attachToBridge(const string& bridge, const string& netdev) throw ZoneOperationException("Can't attach to bridge"); } - struct ifreq ifr = make_clean(); + struct ifreq ifr = utils::make_clean(); strncpy(ifr.ifr_name, bridge.c_str(), IFNAMSIZ); ifr.ifr_ifindex = index; int err = ioctl(fd, SIOCBRADDIF, &ifr); @@ -156,7 +148,7 @@ int setFlags(const string& name, uint32_t mask, uint32_t flags) { uint32_t index = getInterfaceIndex(name); NetlinkMessage nlm(RTM_NEWLINK, NLM_F_REQUEST | NLM_F_ACK); - ifinfomsg infoPeer = make_clean(); + ifinfomsg infoPeer = utils::make_clean(); infoPeer.ifi_family = AF_UNSPEC; infoPeer.ifi_index = index; infoPeer.ifi_flags = flags; @@ -176,7 +168,7 @@ void moveToNS(const string& netdev, pid_t pid) { uint32_t index = getInterfaceIndex(netdev); NetlinkMessage nlm(RTM_NEWLINK, NLM_F_REQUEST | NLM_F_ACK); - ifinfomsg infopeer = make_clean(); + ifinfomsg infopeer = utils::make_clean(); infopeer.ifi_family = AF_UNSPEC; infopeer.ifi_index = index; nlm.put(infopeer) @@ -191,7 +183,7 @@ void createMacvlan(const string& master, const string& slave, const macvlan_mode uint32_t index = getInterfaceIndex(master); NetlinkMessage nlm(RTM_NEWLINK, NLM_F_REQUEST|NLM_F_CREATE|NLM_F_EXCL|NLM_F_ACK); - ifinfomsg infopeer = make_clean(); + ifinfomsg infopeer = utils::make_clean(); infopeer.ifi_family = AF_UNSPEC; infopeer.ifi_change = 0xFFFFFFFF; nlm.put(infopeer) @@ -237,6 +229,24 @@ void movePhys(const pid_t& nsPid, const string& devId) moveToNS(devId, nsPid); } +std::vector listNetdev(const pid_t& nsPid) +{ + NetlinkMessage nlm(RTM_GETLINK, NLM_F_REQUEST|NLM_F_DUMP|NLM_F_ROOT); + ifinfomsg info = utils::make_clean(); + info.ifi_family = AF_PACKET; + nlm.put(info); + NetlinkResponse response = send(nlm, nsPid); + std::vector interfaces; + while (response.hasMessage()) { + std::string ifName; + response.skip(); + response.fetch(IFLA_IFNAME, ifName); + interfaces.push_back(ifName); + response.fetchNextMessage(); + } + return interfaces; +} + } //namespace netdev } //namespace vasum diff --git a/server/netdev.hpp b/server/netdev.hpp index de761ca..b3d658a 100644 --- a/server/netdev.hpp +++ b/server/netdev.hpp @@ -26,6 +26,7 @@ #define SERVER_NETDEV_HPP #include +#include #include #include @@ -38,6 +39,7 @@ void createMacvlan(const pid_t& nsPid, const std::string& hostDev, const macvlan_mode& mode); void movePhys(const pid_t& nsPid, const std::string& devId); +std::vector listNetdev(const pid_t& nsPid); } //namespace netdev } //namespace vasum diff --git a/server/zone-admin.cpp b/server/zone-admin.cpp index 8005fb4..64092a8 100644 --- a/server/zone-admin.cpp +++ b/server/zone-admin.cpp @@ -314,7 +314,7 @@ ZoneAdmin::NetdevAttrs ZoneAdmin::getNetdevAttrs(const std::string& /* netdev */ std::vector ZoneAdmin::getNetdevList() { - throw ZoneOperationException("Not implemented"); + return netdev::listNetdev(mZone.getInitPid()); } } // namespace vasum diff --git a/tests/unit_tests/server/ut-zone.cpp b/tests/unit_tests/server/ut-zone.cpp index 37f65be..a8b608f 100644 --- a/tests/unit_tests/server/ut-zone.cpp +++ b/tests/unit_tests/server/ut-zone.cpp @@ -33,13 +33,13 @@ #include "utils/glib-loop.hpp" #include "utils/scoped-dir.hpp" #include "config/exception.hpp" +#include "netdev.hpp" #include #include #include #include - using namespace vasum; using namespace config; @@ -126,5 +126,22 @@ BOOST_AUTO_TEST_CASE(DbusConnectionTest) // TODO: DbusReconnectionTest +BOOST_AUTO_TEST_CASE(ListNetdevTest) +{ + typedef std::vector NetdevList; + + auto c = create(TEST_CONFIG_PATH); + c->start(); + ensureStarted(); + // Depending on the kernel configuration there can be lots of interfaces (f.e. sit0, ip6tnl0) + NetdevList netdevs = c->getNetdevList(); + // Check if there is mandatory loopback interface + BOOST_CHECK(find(netdevs.begin(), netdevs.end(), "lo") != netdevs.end()); + NetdevList hostNetdevs = netdev::listNetdev(0); + // Check if we get interfaces from zone net namespace + BOOST_CHECK(hostNetdevs != netdevs); + + c->stop(false); +} BOOST_AUTO_TEST_SUITE_END() -- 2.7.4 From 5a16448719fc2b94c103becb4dc3d523dda5372a Mon Sep 17 00:00:00 2001 From: Mateusz Malicki Date: Tue, 3 Mar 2015 16:01:44 +0100 Subject: [PATCH 15/16] Add destroyNetdev, createBridge and tests for netdev [Feature] Add destroyNetdev, createBridge, CreateNetdevVethTest, CreateNetdevMacvlanTest [Cause] N/A [Solution] Netlink interface [Verification] Build, run test Change-Id: Iba17d864158d35d71d2fae83742ef13d729d5a7f --- cli/command-line-interface.cpp | 13 +++++++ cli/command-line-interface.hpp | 7 ++++ cli/main.cpp | 9 +++++ client/vasum-client-impl.cpp | 6 +-- server/host-connection.cpp | 15 +++++++ server/host-connection.hpp | 10 +++++ server/host-dbus-definitions.hpp | 5 +++ server/netdev.cpp | 78 +++++++++++++++++++++++++++++++++---- server/netdev.hpp | 12 +++++- server/zone-admin.cpp | 5 +++ server/zone-admin.hpp | 5 +++ server/zone.cpp | 6 +++ server/zone.hpp | 5 +++ server/zones-manager.cpp | 22 +++++++++++ server/zones-manager.hpp | 3 ++ tests/unit_tests/server/ut-zone.cpp | 65 +++++++++++++++++++++++++++++++ 16 files changed, 255 insertions(+), 11 deletions(-) diff --git a/cli/command-line-interface.cpp b/cli/command-line-interface.cpp index ecf6b9c..100aaa1 100644 --- a/cli/command-line-interface.cpp +++ b/cli/command-line-interface.cpp @@ -397,6 +397,19 @@ void create_netdev_phys(int pos, int argc, const char** argv) argv[pos + 2])); } +void destroy_netdev(int pos, int argc, const char** argv) +{ + using namespace std::placeholders; + + if (argc <= pos + 2) { + throw runtime_error("Not enough parameters"); + } + one_shot(bind(vsm_destroy_netdev, + _1, + argv[pos + 1], + argv[pos + 2])); +} + void zone_get_netdevs(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 eb33428..006396c 100644 --- a/cli/command-line-interface.hpp +++ b/cli/command-line-interface.hpp @@ -209,6 +209,13 @@ void create_netdev_macvlan(int pos, int argc, const char** argv); void create_netdev_phys(int pos, int argc, const char** argv); /** + * Parses command line arguments and call vsm_destroy_netdev + * + * @see vsm_destroy_netdev + */ +void destroy_netdev(int pos, int argc, const char** argv); + +/** * Parses command line arguments and prints result of vsm_zone_get_netdevs * * @see vsm_zone_get_netdevs diff --git a/cli/main.cpp b/cli/main.cpp index a30146e..c915415 100644 --- a/cli/main.cpp +++ b/cli/main.cpp @@ -173,6 +173,15 @@ std::map commands = { } }, { + "destroy_netdev", { + destroy_netdev, + "destroy_netdev zone_id devId", + "Destroy netdev in zone", + {{"zone_id", "id zone name"}, + {"devId", "network device id"}} + } + }, + { "zone_get_netdevs", { zone_get_netdevs, "zone_get_netdevs zone_id", diff --git a/client/vasum-client-impl.cpp b/client/vasum-client-impl.cpp index 1ac4c3c..fb1b2ec 100644 --- a/client/vasum-client-impl.cpp +++ b/client/vasum-client-impl.cpp @@ -768,10 +768,10 @@ VsmStatus Client::vsm_lookup_netdev_by_name(const char*, const char*, VsmNetdev* return vsm_get_status(); } -VsmStatus Client::vsm_destroy_netdev(const char*, const char*) noexcept +VsmStatus Client::vsm_destroy_netdev(const char* zone, const char* devId) noexcept { - mStatus = Status(VSMCLIENT_OTHER_ERROR, "Not implemented"); - return vsm_get_status(); + GVariant* args_in = g_variant_new("(ss)", zone, devId); + return callMethod(HOST_INTERFACE, api::host::METHOD_DESTROY_NETDEV, args_in); } VsmStatus Client::vsm_declare_file(const char* zone, diff --git a/server/host-connection.cpp b/server/host-connection.cpp index a1a7448..d0b612a 100644 --- a/server/host-connection.cpp +++ b/server/host-connection.cpp @@ -162,6 +162,11 @@ void HostConnection::setCreateNetdevPhysCallback(const CreateNetdevPhysCallback& mCreateNetdevPhysCallback = callback; } +void HostConnection::setDestroyNetdevCallback(const DestroyNetdevCallback& callback) +{ + mDestroyNetdevCallback = callback; +} + void HostConnection::setDeclareFileCallback(const DeclareFileCallback& callback) { mDeclareFileCallback = callback; @@ -393,6 +398,16 @@ void HostConnection::onMessageCall(const std::string& objectPath, } } + if (methodName == api::host::METHOD_DESTROY_NETDEV) { + const gchar* id = NULL; + const gchar* devId = NULL; + g_variant_get(parameters, "(&s&s)", &id, &devId); + if (mDestroyNetdevCallback) { + auto rb = std::make_shared>(result); + mDestroyNetdevCallback(id, devId, rb); + } + } + if (methodName == api::host::METHOD_DECLARE_FILE) { const gchar* zone; int32_t type; diff --git a/server/host-connection.hpp b/server/host-connection.hpp index 3ef8cdf..0ef9585 100644 --- a/server/host-connection.hpp +++ b/server/host-connection.hpp @@ -90,6 +90,10 @@ public: const std::string& devId, api::MethodResultBuilder::Pointer result )> CreateNetdevPhysCallback; + typedef std::function DestroyNetdevCallback; typedef std::function" " " + " " + " " + " " + " " " " " " " " diff --git a/server/netdev.cpp b/server/netdev.cpp index f8f9264..e17ecda 100644 --- a/server/netdev.cpp +++ b/server/netdev.cpp @@ -46,6 +46,17 @@ #include #include #include +#include +#include + +//IFLA_BRIDGE_FLAGS and BRIDGE_FLAGS_MASTER +//should be defined in linux/if_bridge.h since kernel v3.7 +#ifndef IFLA_BRIDGE_FLAGS +#define IFLA_BRIDGE_FLAGS 0 +#endif +#ifndef BRIDGE_FLAGS_MASTER +#define BRIDGE_FLAGS_MASTER 1 +#endif using namespace std; using namespace vasum; @@ -205,10 +216,18 @@ void createVeth(const pid_t& nsPid, const string& nsDev, const string& hostDev) string hostVeth = getUniqueVethName(); LOGT("Creating veth: bridge: " << hostDev << ", port: " << hostVeth << ", zone: " << nsDev); createPipedNetdev(nsDev, hostVeth); - //TODO: clean up if following instructions fail - attachToBridge(hostDev, hostVeth); - up(hostVeth); - moveToNS(nsDev, nsPid); + try { + attachToBridge(hostDev, hostVeth); + up(hostVeth); + moveToNS(nsDev, nsPid); + } catch(const exception& ex) { + try { + destroyNetdev(hostVeth); + } catch (const exception& ex) { + LOGE("Can't destroy netdev pipe: " << hostVeth << ", " << nsDev); + } + throw; + } } void createMacvlan(const pid_t& nsPid, @@ -218,9 +237,17 @@ void createMacvlan(const pid_t& nsPid, { LOGT("Creating macvlan: host: " << hostDev << ", zone: " << nsDev << ", mode: " << mode); createMacvlan(hostDev, nsDev, mode); - //TODO: clean up if following instructions fail - up(nsDev); - moveToNS(nsDev, nsPid); + try { + up(nsDev); + moveToNS(nsDev, nsPid); + } catch(const exception& ex) { + try { + destroyNetdev(nsDev); + } catch (const exception& ex) { + LOGE("Can't destroy netdev: " << nsDev); + } + throw; + } } void movePhys(const pid_t& nsPid, const string& devId) @@ -247,6 +274,43 @@ std::vector listNetdev(const pid_t& nsPid) return interfaces; } +void destroyNetdev(const string& netdev, const pid_t pid) +{ + LOGT("Destroying netdev: " << netdev); + validateNetdevName(netdev); + + NetlinkMessage nlm(RTM_DELLINK, NLM_F_REQUEST|NLM_F_ACK); + ifinfomsg infopeer = utils::make_clean(); + infopeer.ifi_family = AF_UNSPEC; + infopeer.ifi_change = 0xFFFFFFFF; + nlm.put(infopeer) + .put(IFLA_IFNAME, netdev); + send(nlm, pid); +} + +void createBridge(const string& netdev) +{ + LOGT("Creating bridge: " << netdev); + validateNetdevName(netdev); + + NetlinkMessage nlm(RTM_NEWLINK, NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK); + ifinfomsg infoPeer = utils::make_clean(); + infoPeer.ifi_family = AF_UNSPEC; + infoPeer.ifi_change = 0xFFFFFFFF; + nlm.put(infoPeer) + .beginNested(IFLA_LINKINFO) + .put(IFLA_INFO_KIND, "bridge") + .beginNested(IFLA_INFO_DATA) + .beginNested(IFLA_AF_SPEC) + .put(IFLA_BRIDGE_FLAGS, BRIDGE_FLAGS_MASTER) + .endNested() + .endNested() + .endNested() + .put(IFLA_IFNAME, netdev); + send(nlm); +} + + } //namespace netdev } //namespace vasum diff --git a/server/netdev.hpp b/server/netdev.hpp index b3d658a..0e9bfde 100644 --- a/server/netdev.hpp +++ b/server/netdev.hpp @@ -39,7 +39,17 @@ void createMacvlan(const pid_t& nsPid, const std::string& hostDev, const macvlan_mode& mode); void movePhys(const pid_t& nsPid, const std::string& devId); -std::vector listNetdev(const pid_t& nsPid); +std::vector listNetdev(const pid_t& nsPid = 0); +void destroyNetdev(const std::string& netdev, const pid_t pid = 0); + +/** + * Create bridge + * + * Bridge are in BRIDGE_MODE_VEB (loopback) mode and it is software bridge (BRIDGE_FLAGS_MASTER) + * + * @param netdev bridge name + */ +void createBridge(const std::string& netdev); } //namespace netdev } //namespace vasum diff --git a/server/zone-admin.cpp b/server/zone-admin.cpp index 64092a8..29577f8 100644 --- a/server/zone-admin.cpp +++ b/server/zone-admin.cpp @@ -302,6 +302,11 @@ void ZoneAdmin::moveNetdev(const std::string& devId) netdev::movePhys(mZone.getInitPid(), devId); } +void ZoneAdmin::destroyNetdev(const std::string& devId) +{ + netdev::destroyNetdev(devId, mZone.getInitPid()); +} + void ZoneAdmin::setNetdevAttrs(const std::string& /* netdev */, const NetdevAttrs& /* attrs */) { throw ZoneOperationException("Not implemented"); diff --git a/server/zone-admin.hpp b/server/zone-admin.hpp index ade4585..c6d26f0 100644 --- a/server/zone-admin.hpp +++ b/server/zone-admin.hpp @@ -150,6 +150,11 @@ public: void moveNetdev(const std::string& devId); /** + * Destroy network device in zone + */ + void destroyNetdev(const std::string& devId); + + /** * Set network device attributes */ void setNetdevAttrs(const std::string& netdev, const NetdevAttrs& attrs); diff --git a/server/zone.cpp b/server/zone.cpp index 181cc9e..b0c3c28 100644 --- a/server/zone.cpp +++ b/server/zone.cpp @@ -278,6 +278,12 @@ void Zone::moveNetdev(const std::string& devId) mAdmin->moveNetdev(devId); } +void Zone::destroyNetdev(const std::string& devId) +{ + Lock lock(mReconnectMutex); + mAdmin->destroyNetdev(devId); +} + void Zone::goForeground() { Lock lock(mReconnectMutex); diff --git a/server/zone.hpp b/server/zone.hpp index 45bacbd..b981e9a 100644 --- a/server/zone.hpp +++ b/server/zone.hpp @@ -292,6 +292,11 @@ public: void moveNetdev(const std::string& devId); /** + * Destroy network device in zone + */ + void destroyNetdev(const std::string& devId); + + /** * Set network device attributes */ void setNetdevAttrs(const std::string& netdev, const ZoneAdmin::NetdevAttrs& attrs); diff --git a/server/zones-manager.cpp b/server/zones-manager.cpp index 2dcfaa0..4266820 100644 --- a/server/zones-manager.cpp +++ b/server/zones-manager.cpp @@ -160,6 +160,9 @@ ZonesManager::ZonesManager(const std::string& configPath) mHostConnection.setCreateNetdevPhysCallback(bind(&ZonesManager::handleCreateNetdevPhysCall, this, _1, _2, _3)); + mHostConnection.setDestroyNetdevCallback(bind(&ZonesManager::handleDestroyNetdevCall, + this, _1, _2, _3)); + mHostConnection.setDeclareFileCallback(bind(&ZonesManager::handleDeclareFileCall, this, _1, _2, _3, _4, _5, _6)); @@ -927,6 +930,25 @@ void ZonesManager::handleCreateNetdevPhysCall(const std::string& zone, } } +void ZonesManager::handleDestroyNetdevCall(const std::string& zone, + const std::string& devId, + api::MethodResultBuilder::Pointer result) +{ + LOGI("DestroyNetdev call"); + try { + Lock lock(mMutex); + + getZone(zone).destroyNetdev(devId); + result->setVoid(); + } catch (const InvalidZoneIdException&) { + LOGE("No zone with id=" << zone); + result->setError(api::ERROR_INVALID_ID, "No such zone id"); + } catch (const VasumException& ex) { + LOGE("Can't create netdev: " << ex.what()); + result->setError(api::ERROR_INTERNAL, ex.what()); + } +} + void ZonesManager::handleDeclareFileCall(const std::string& zone, const int32_t& type, const std::string& path, diff --git a/server/zones-manager.hpp b/server/zones-manager.hpp index 825b1c8..deb9b9d 100644 --- a/server/zones-manager.hpp +++ b/server/zones-manager.hpp @@ -189,6 +189,9 @@ private: void handleCreateNetdevPhysCall(const std::string& zone, const std::string& devId, api::MethodResultBuilder::Pointer result); + void handleDestroyNetdevCall(const std::string& zone, + const std::string& devId, + api::MethodResultBuilder::Pointer result); void handleDeclareFileCall(const std::string& zone, const int32_t& type, const std::string& path, diff --git a/tests/unit_tests/server/ut-zone.cpp b/tests/unit_tests/server/ut-zone.cpp index a8b608f..91d0540 100644 --- a/tests/unit_tests/server/ut-zone.cpp +++ b/tests/unit_tests/server/ut-zone.cpp @@ -27,6 +27,7 @@ #include "ut.hpp" #include "zone.hpp" +#include "netdev.hpp" #include "exception.hpp" #include "utils/exception.hpp" @@ -39,8 +40,11 @@ #include #include #include +#include +#include using namespace vasum; +using namespace vasum::netdev; using namespace config; namespace { @@ -53,15 +57,28 @@ const std::string MISSING_CONFIG_PATH = TEMPLATES_DIR + "/missing.conf"; const std::string ZONES_PATH = "/tmp/ut-zones"; const std::string LXC_TEMPLATES_PATH = VSM_TEST_LXC_TEMPLATES_INSTALL_DIR; const std::string DB_PATH = ZONES_PATH + "/vasum.db"; +const std::string BRIDGE_NAME = "brtest01"; +const std::string ZONE_NETDEV = "netdevtest01"; struct Fixture { utils::ScopedGlibLoop mLoop; utils::ScopedDir mZonesPathGuard; utils::ScopedDir mRunGuard; + std::string mBridgeName; Fixture() : mZonesPathGuard(ZONES_PATH) {} + ~Fixture() + { + if (!mBridgeName.empty()) { + try { + destroyNetdev(mBridgeName); + } catch (std::exception& ex) { + BOOST_MESSAGE("Can't destroy bridge: " + std::string(ex.what())); + } + } + } std::unique_ptr create(const std::string& configPath) { @@ -74,11 +91,23 @@ struct Fixture { "")); } + void setupBridge(const std::string& name) + { + createBridge(name); + mBridgeName = name; + } + + void ensureStarted() { // wait for zones init to fully start std::this_thread::sleep_for(std::chrono::milliseconds(200)); } + void ensureStop() + { + // wait for fully stop + std::this_thread::sleep_for(std::chrono::milliseconds(200)); + } }; } // namespace @@ -144,4 +173,40 @@ BOOST_AUTO_TEST_CASE(ListNetdevTest) c->stop(false); } +BOOST_AUTO_TEST_CASE(CreateNetdevVethTest) +{ + typedef std::vector NetdevList; + + setupBridge(BRIDGE_NAME); + auto c = create(TEST_CONFIG_PATH); + c->start(); + ensureStarted(); + c->createNetdevVeth(ZONE_NETDEV, BRIDGE_NAME); + NetdevList netdevs = c->getNetdevList(); + BOOST_CHECK(find(netdevs.begin(), netdevs.end(), ZONE_NETDEV) != netdevs.end()); + c->stop(false); + ensureStop(); + + //Check clean up + NetdevList hostNetdevsInit = listNetdev(); + BOOST_REQUIRE_THROW(c->createNetdevVeth(ZONE_NETDEV, BRIDGE_NAME), VasumException); + NetdevList hostNetdevsThrow = listNetdev(); + BOOST_CHECK_EQUAL_COLLECTIONS(hostNetdevsInit.begin(), hostNetdevsInit.end(), + hostNetdevsThrow.begin(), hostNetdevsThrow.end()); +} + +BOOST_AUTO_TEST_CASE(CreateNetdevMacvlanTest) +{ + typedef std::vector NetdevList; + + setupBridge(BRIDGE_NAME); + auto c = create(TEST_CONFIG_PATH); + c->start(); + ensureStarted(); + c->createNetdevVeth(ZONE_NETDEV, BRIDGE_NAME); + NetdevList netdevs = c->getNetdevList(); + BOOST_CHECK(find(netdevs.begin(), netdevs.end(), ZONE_NETDEV) != netdevs.end()); + c->stop(false); +} + BOOST_AUTO_TEST_SUITE_END() -- 2.7.4 From 84a5119de0fdfa01e9a70a37682c29d25119f25e Mon Sep 17 00:00:00 2001 From: Piotr Bartosiewicz Date: Mon, 16 Mar 2015 16:14:54 +0100 Subject: [PATCH 16/16] Epoll modifyFD method added. Some refactor. [Bug/Feature] N/A [Cause] N/A [Solution] N/A [Verification] Run tests Change-Id: Idcba92d12a618a8095420f3c941a12d61aef1761 --- common/epoll/event-poll.cpp | 36 +++++-- common/epoll/event-poll.hpp | 11 ++- common/epoll/thread-dispatcher.cpp | 9 +- common/epoll/thread-dispatcher.hpp | 2 + common/ipc/client.cpp | 7 +- common/ipc/internals/processor.hpp | 3 - common/ipc/service.cpp | 15 ++- tests/unit_tests/dbus/ut-connection.cpp | 5 - tests/unit_tests/epoll/ut-event-poll.cpp | 162 ++++++++++++++++++++++++------- tests/unit_tests/utils/ut-glib-loop.cpp | 21 ++-- 10 files changed, 192 insertions(+), 79 deletions(-) diff --git a/common/epoll/event-poll.cpp b/common/epoll/event-poll.cpp index 9eb9fa0..dfb0385 100644 --- a/common/epoll/event-poll.cpp +++ b/common/epoll/event-poll.cpp @@ -79,14 +79,22 @@ void EventPoll::addFD(const int fd, const Events events, Callback&& callback) LOGT("Callback added for fd: " << fd); } +void EventPoll::modifyFD(const int fd, const Events events) +{ + // No need to lock and check mCallbacks map + if (!modifyFDInternal(fd, events)) { + throw UtilsException("Could not modify fd"); + } +} + void EventPoll::removeFD(const int fd) { std::lock_guard lock(mMutex); auto iter = mCallbacks.find(fd); if (iter == mCallbacks.end()) { - LOGW("Failed to remove nonexistent fd: " << fd); - throw UtilsException("FD does not exist"); + LOGT("Callback not found, probably already removed fd: " << fd); + return; } mCallbacks.erase(iter); removeFDInternal(fd); @@ -94,6 +102,7 @@ void EventPoll::removeFD(const int fd) } bool EventPoll::dispatchIteration(const int timeoutMs) + { for (;;) { epoll_event event; @@ -120,19 +129,16 @@ bool EventPoll::dispatchIteration(const int timeoutMs) std::shared_ptr callback(iter->second); try { LOGT("Dispatch fd: " << event.data.fd << ", events: " << eventsToString(event.events)); - return (*callback)(event.data.fd, event.events); + (*callback)(event.data.fd, event.events); + return true; } catch (std::exception& e) { LOGE("Got unexpected exception: " << e.what()); assert(0 && "Callback should not throw any exceptions"); + return true; } } } -void EventPoll::dispatchLoop() -{ - while (dispatchIteration(-1)) {} -} - bool EventPoll::addFDInternal(const int fd, const Events events) { epoll_event event; @@ -147,6 +153,20 @@ bool EventPoll::addFDInternal(const int fd, const Events events) return true; } +bool EventPoll::modifyFDInternal(const int fd, const Events events) +{ + epoll_event event; + memset(&event, 0, sizeof(event)); + event.events = events; + event.data.fd = fd; + + if (epoll_ctl(mPollFD, EPOLL_CTL_MOD, fd, &event) == -1) { + LOGE("Failed to modify fd in poll: " << getSystemErrorMessage()); + return false; + } + return true; +} + void EventPoll::removeFDInternal(const int fd) { if (epoll_ctl(mPollFD, EPOLL_CTL_DEL, fd, NULL) == -1) { diff --git a/common/epoll/event-poll.hpp b/common/epoll/event-poll.hpp index 2d37aaa..67614ab 100644 --- a/common/epoll/event-poll.hpp +++ b/common/epoll/event-poll.hpp @@ -37,7 +37,7 @@ namespace epoll { class EventPoll { public: - typedef std::function Callback; + typedef std::function Callback; EventPoll(); ~EventPoll(); @@ -45,10 +45,16 @@ public: int getPollFD() const; void addFD(const int fd, const Events events, Callback&& callback); + void modifyFD(const int fd, const Events events); void removeFD(const int fd); + /** + * Dispatch at most one signalled FD + * @param timeoutMs how long should wait in case of no pending events + * (0 - return immediately, -1 - wait forever) + * @return false on timeout + */ bool dispatchIteration(const int timeoutMs); - void dispatchLoop(); private: typedef std::recursive_mutex Mutex; @@ -58,6 +64,7 @@ private: std::unordered_map> mCallbacks; bool addFDInternal(const int fd, const Events events); + bool modifyFDInternal(const int fd, const Events events); void removeFDInternal(const int fd); }; diff --git a/common/epoll/thread-dispatcher.cpp b/common/epoll/thread-dispatcher.cpp index a82cad8..d286c19 100644 --- a/common/epoll/thread-dispatcher.cpp +++ b/common/epoll/thread-dispatcher.cpp @@ -29,15 +29,18 @@ namespace vasum { namespace epoll { ThreadDispatcher::ThreadDispatcher() + : mStopped(false) { - auto controlCallback = [this](int, Events) -> bool { + auto controlCallback = [this](int, Events) { mStopEvent.receive(); - return false; // break the loop + mStopped.store(true, std::memory_order_release); }; mPoll.addFD(mStopEvent.getFD(), EPOLLIN, std::move(controlCallback)); mThread = std::thread([this] { - mPoll.dispatchLoop(); + while (!mStopped.load(std::memory_order_acquire)) { + mPoll.dispatchIteration(-1); + } }); } diff --git a/common/epoll/thread-dispatcher.hpp b/common/epoll/thread-dispatcher.hpp index 7d0f30d..5c6c145 100644 --- a/common/epoll/thread-dispatcher.hpp +++ b/common/epoll/thread-dispatcher.hpp @@ -29,6 +29,7 @@ #include "utils/eventfd.hpp" #include +#include namespace vasum { namespace epoll { @@ -45,6 +46,7 @@ public: private: EventPoll mPoll; utils::EventFD mStopEvent; + std::atomic_bool mStopped; std::thread mThread; }; diff --git a/common/ipc/client.cpp b/common/ipc/client.cpp index abecb71..75a62ad 100644 --- a/common/ipc/client.cpp +++ b/common/ipc/client.cpp @@ -58,9 +58,8 @@ void Client::start() } LOGS("Client start"); // Initialize the connection with the server - auto handleEvent = [&](int, epoll::Events) -> bool { + auto handleEvent = [&](int, epoll::Events) { mProcessor.handleEvent(); - return true; }; mEventPoll.addFD(mProcessor.getEventFD(), EPOLLIN, handleEvent); mProcessor.start(); @@ -98,6 +97,7 @@ void Client::handle(const FileDescriptor fd, const epoll::Events pollEvents) if (pollEvents & EPOLLIN) { mProcessor.handleInput(fd); + return; // because handleInput will handle RDHUP } if ((pollEvents & EPOLLHUP) || (pollEvents & EPOLLRDHUP)) { @@ -109,9 +109,8 @@ void Client::setNewPeerCallback(const PeerCallback& newPeerCallback) { LOGS("Client setNewPeerCallback"); auto callback = [newPeerCallback, this](PeerID peerID, FileDescriptor fd) { - auto handleFd = [&](FileDescriptor fd, epoll::Events events) -> bool { + auto handleFd = [&](FileDescriptor fd, epoll::Events events) { handle(fd, events); - return true; }; mEventPoll.addFD(fd, EPOLLIN | EPOLLHUP | EPOLLRDHUP, handleFd); if (newPeerCallback) { diff --git a/common/ipc/internals/processor.hpp b/common/ipc/internals/processor.hpp index 6e0ae81..dec16e8 100644 --- a/common/ipc/internals/processor.hpp +++ b/common/ipc/internals/processor.hpp @@ -80,11 +80,8 @@ const unsigned int DEFAULT_MAX_NUMBER_OF_PEERS = 500; * - new way to generate UIDs * - callbacks for serialization/parsing * - store Sockets in a vector, maybe SocketStore? -* - poll loop outside. * - waiting till the EventQueue is empty before leaving stop() * - no new events added after stop() called -* - when using IPCGSource: addFD and removeFD can be called from addPeer removePeer callbacks, but -* there is no mechanism to ensure the IPCSource exists.. therefore SIGSEGV :) * */ class Processor { diff --git a/common/ipc/service.cpp b/common/ipc/service.cpp index c436726..849ce3c 100644 --- a/common/ipc/service.cpp +++ b/common/ipc/service.cpp @@ -63,17 +63,15 @@ void Service::start() return; } LOGS("Service start"); - auto handleConnection = [&](int, epoll::Events) -> bool { + auto handleConnection = [&](int, epoll::Events) { mAcceptor.handleConnection(); - return true; }; - auto handleProcessorEvent = [&](int, epoll::Events) -> bool { + auto handleProcessorEvent = [&](int, epoll::Events) { mProcessor.handleEvent(); - return true; }; - mEventPoll.addFD(mAcceptor.getConnectionFD(), EPOLLIN, handleConnection); mEventPoll.addFD(mProcessor.getEventFD(), EPOLLIN, handleProcessorEvent); mProcessor.start(); + mEventPoll.addFD(mAcceptor.getConnectionFD(), EPOLLIN, handleConnection); } bool Service::isStarted() @@ -87,9 +85,8 @@ void Service::stop() return; } LOGS("Service stop"); - mProcessor.stop(); - mEventPoll.removeFD(mAcceptor.getConnectionFD()); + mProcessor.stop(); mEventPoll.removeFD(mProcessor.getEventFD()); } @@ -105,6 +102,7 @@ void Service::handle(const FileDescriptor fd, const epoll::Events pollEvents) if (pollEvents & EPOLLIN) { mProcessor.handleInput(fd); + return; // because handleInput will handle RDHUP } if ((pollEvents & EPOLLHUP) || (pollEvents & EPOLLRDHUP)) { @@ -116,9 +114,8 @@ void Service::setNewPeerCallback(const PeerCallback& newPeerCallback) { LOGS("Service setNewPeerCallback"); auto callback = [newPeerCallback, this](PeerID peerID, FileDescriptor fd) { - auto handleFd = [&](FileDescriptor fd, epoll::Events events) -> bool { + auto handleFd = [&](FileDescriptor fd, epoll::Events events) { handle(fd, events); - return true; }; mEventPoll.addFD(fd, EPOLLIN | EPOLLHUP | EPOLLRDHUP, handleFd); if (newPeerCallback) { diff --git a/tests/unit_tests/dbus/ut-connection.cpp b/tests/unit_tests/dbus/ut-connection.cpp index 4948cd7..e52778e 100644 --- a/tests/unit_tests/dbus/ut-connection.cpp +++ b/tests/unit_tests/dbus/ut-connection.cpp @@ -96,11 +96,6 @@ std::string getInterfaceFromIntrospectionXML(const std::string& xml, const std:: } // namespace -BOOST_AUTO_TEST_CASE(GlibLoopTest) -{ - ScopedGlibLoop loop; -} - BOOST_AUTO_TEST_CASE(DbusDaemonTest) { ScopedDbusDaemon daemon; diff --git a/tests/unit_tests/epoll/ut-event-poll.cpp b/tests/unit_tests/epoll/ut-event-poll.cpp index 00cb385..3a26b5e 100644 --- a/tests/unit_tests/epoll/ut-event-poll.cpp +++ b/tests/unit_tests/epoll/ut-event-poll.cpp @@ -29,11 +29,12 @@ #include "epoll/event-poll.hpp" #include "logger/logger.hpp" #include "ipc/internals/socket.hpp" -#include "utils/latch.hpp" +#include "utils/value-latch.hpp" #include "utils/glib-loop.hpp" #include "epoll/glib-dispatcher.hpp" #include "epoll/thread-dispatcher.hpp" +using namespace vasum; using namespace vasum::utils; using namespace vasum::epoll; using namespace vasum::ipc; @@ -66,62 +67,152 @@ BOOST_AUTO_TEST_CASE(GlibPoll) void doSocketTest(EventPoll& poll) { - const std::string PATH = "/tmp/ut-poll.sock"; - const std::string MESSAGE = "This is a test message"; - - Latch goodMessage; - Latch remoteClosed; - - Socket listen = Socket::createSocket(PATH); - std::shared_ptr server; + using namespace std::placeholders; - auto serverCallback = [&](int, Events events) -> bool { + //TODO don't use ipc socket + const std::string PATH = "/tmp/ut-poll.sock"; + const size_t REQUEST_LEN = 5; + const std::string REQUEST_GOOD = "GET 1"; + const std::string REQUEST_BAD = "GET 7"; + const std::string RESPONSE = "This is a response message"; + + // Scenario 1: + // client connects to server listening socket + // client ---good-request---> server + // server ---response---> client + // client disconnects + // + // Scenario 2: + // client connects to server listening socket + // client ---bad-request----> server + // server disconnects + + // { server setup + + auto serverCallback = [&](int /*fd*/, + Events events, + std::shared_ptr socket, + CallbackGuard::Tracker) { LOGD("Server events: " << eventsToString(events)); + if (events & EPOLLIN) { + std::string request(REQUEST_LEN, 'x'); + socket->read(&request.front(), request.size()); + if (request == REQUEST_GOOD) { + poll.modifyFD(socket->getFD(), EPOLLRDHUP | EPOLLOUT); + } else { + // disconnect (socket is kept in callback) + poll.removeFD(socket->getFD()); + } + } + if (events & EPOLLOUT) { - server->write(MESSAGE.data(), MESSAGE.size()); - poll.removeFD(server->getFD()); - server.reset(); + socket->write(RESPONSE.data(), RESPONSE.size()); + poll.modifyFD(socket->getFD(), EPOLLRDHUP); + } + + if (events & EPOLLRDHUP) { + // client has disconnected + poll.removeFD(socket->getFD()); } - return true; }; - auto listenCallback = [&](int, Events events) -> bool { + Socket listenSocket = Socket::createSocket(PATH); + CallbackGuard serverSocketsGuard; + + auto listenCallback = [&](int /*fd*/, Events events) { LOGD("Listen events: " << eventsToString(events)); if (events & EPOLLIN) { - server = listen.accept(); - poll.addFD(server->getFD(), EPOLLHUP | EPOLLRDHUP | EPOLLOUT, serverCallback); + // accept new server connection + std::shared_ptr socket = listenSocket.accept(); + poll.addFD(socket->getFD(), + EPOLLRDHUP | EPOLLIN, + std::bind(serverCallback, _1, _2, socket, serverSocketsGuard.spawn())); } - return true; }; - poll.addFD(listen.getFD(), EPOLLIN, listenCallback); + poll.addFD(listenSocket.getFD(), EPOLLIN, listenCallback); + + // } server setup - Socket client = Socket::connectSocket(PATH); + // { client setup - auto clientCallback = [&](int, Events events) -> bool { + auto clientCallback = [&](int /*fd*/, + Events events, + Socket& socket, + const std::string& request, + ValueLatch& response) { LOGD("Client events: " << eventsToString(events)); + if (events & EPOLLOUT) { + socket.write(request.data(), request.size()); + poll.modifyFD(socket.getFD(), EPOLLRDHUP | EPOLLIN); + } + if (events & EPOLLIN) { - std::string ret(MESSAGE.size(), 'x'); - client.read(&ret.front(), ret.size()); - if (ret == MESSAGE) { - goodMessage.set(); + try { + std::string msg(RESPONSE.size(), 'x'); + socket.read(&msg.front(), msg.size()); + response.set(msg); + } catch (UtilsException&) { + response.set(std::string()); } + poll.modifyFD(socket.getFD(), EPOLLRDHUP); } + if (events & EPOLLRDHUP) { - poll.removeFD(client.getFD()); - remoteClosed.set(); + LOGD("Server has disconnected"); + poll.removeFD(socket.getFD()); //prevent active loop } - return true; }; - poll.addFD(client.getFD(), EPOLLHUP | EPOLLRDHUP | EPOLLIN, clientCallback); - - BOOST_CHECK(goodMessage.wait(TIMEOUT)); - BOOST_CHECK(remoteClosed.wait(TIMEOUT)); - - poll.removeFD(listen.getFD()); + // } client setup + + // Scenario 1 + LOGD("Scerario 1"); + { + Socket client = Socket::connectSocket(PATH); + ValueLatch response; + + poll.addFD(client.getFD(), + EPOLLRDHUP | EPOLLOUT, + std::bind(clientCallback, + _1, + _2, + std::ref(client), + REQUEST_GOOD, + std::ref(response))); + + BOOST_CHECK(response.get(TIMEOUT) == RESPONSE); + + poll.removeFD(client.getFD()); + } + + // Scenario 2 + LOGD("Scerario 2"); + { + Socket client = Socket::connectSocket(PATH); + ValueLatch response; + + poll.addFD(client.getFD(), + EPOLLRDHUP | EPOLLOUT, + std::bind(clientCallback, + _1, + _2, + std::ref(client), + REQUEST_BAD, + std::ref(response))); + + BOOST_CHECK(response.get(TIMEOUT) == std::string()); + + poll.removeFD(client.getFD()); + } + LOGD("Done"); + + poll.removeFD(listenSocket.getFD()); + + // wait for all server sockets (ensure all EPOLLRDHUP are processed) + BOOST_REQUIRE(serverSocketsGuard.waitForTrackers(TIMEOUT)); } BOOST_AUTO_TEST_CASE(ThreadedPollSocket) @@ -146,9 +237,8 @@ BOOST_AUTO_TEST_CASE(PollStacking) EventPoll innerPoll; - auto dispatchInner = [&](int, Events) -> bool { + auto dispatchInner = [&](int, Events) { innerPoll.dispatchIteration(0); - return true; }; dispatcher.getPoll().addFD(innerPoll.getPollFD(), EPOLLIN, dispatchInner); doSocketTest(innerPoll); diff --git a/tests/unit_tests/utils/ut-glib-loop.cpp b/tests/unit_tests/utils/ut-glib-loop.cpp index 30d5342..7c5e318 100644 --- a/tests/unit_tests/utils/ut-glib-loop.cpp +++ b/tests/unit_tests/utils/ut-glib-loop.cpp @@ -26,12 +26,11 @@ #include "config.hpp" #include "ut.hpp" -#include "utils/latch.hpp" #include "utils/glib-loop.hpp" #include -BOOST_AUTO_TEST_SUITE(UtilsGlibLoopSuite) +BOOST_AUTO_TEST_SUITE(GlibLoopSuite) using namespace vasum; using namespace vasum::utils; @@ -39,22 +38,25 @@ using namespace vasum::utils; namespace { -const unsigned int TIMER_INTERVAL_MS = 10; -const unsigned int TIMER_NUMBER = 5; +const unsigned int TIMER_INTERVAL_MS = 100; +const unsigned int TIMER_NUMBER = 4; const unsigned int TIMER_WAIT_FOR = 2 * TIMER_NUMBER * TIMER_INTERVAL_MS; } // namespace +BOOST_AUTO_TEST_CASE(GlibLoopTest) +{ + ScopedGlibLoop loop; +} + BOOST_AUTO_TEST_CASE(GlibTimerEventTest) { ScopedGlibLoop loop; - Latch latch; std::atomic_uint counter(0); CallbackGuard guard; - Glib::OnTimerEventCallback callback = [&]()->bool { - latch.set(); + auto callback = [&]()-> bool { if (++counter >= TIMER_NUMBER) { return false; } @@ -63,8 +65,9 @@ BOOST_AUTO_TEST_CASE(GlibTimerEventTest) Glib::addTimerEvent(TIMER_INTERVAL_MS, callback, guard); - BOOST_REQUIRE(latch.waitForN(TIMER_NUMBER, TIMER_WAIT_FOR)); - BOOST_REQUIRE(latch.wait(TIMER_WAIT_FOR) == false); + BOOST_CHECK(counter < TIMER_NUMBER); + BOOST_CHECK(guard.waitForTrackers(TIMER_WAIT_FOR)); + BOOST_CHECK_EQUAL(counter, TIMER_NUMBER); } BOOST_AUTO_TEST_SUITE_END() -- 2.7.4