From 6c71adb3430b415b16392a0d00772225d394e395 Mon Sep 17 00:00:00 2001 From: Dariusz Michaluk Date: Mon, 3 Aug 2015 15:52:58 +0200 Subject: [PATCH 01/16] Fix lxcpp tests [Bug] N/A [Cause] N/A [Solution] N/A [Verification] N/A Change-Id: Idf5939f96ffb6af7380cfc5fe216361b53402801 --- tests/unit_tests/lxcpp/ut-process.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/unit_tests/lxcpp/ut-process.cpp b/tests/unit_tests/lxcpp/ut-process.cpp index df9f33d..cae893a 100644 --- a/tests/unit_tests/lxcpp/ut-process.cpp +++ b/tests/unit_tests/lxcpp/ut-process.cpp @@ -83,9 +83,9 @@ BOOST_AUTO_TEST_CASE(Setns) Namespace::IPC, Namespace::NET }); - exit(TEST_PASSED); + _exit(TEST_PASSED); } catch(...) { - exit(ERROR); + _exit(ERROR); } } else if(pid>0) { int status = -1; @@ -105,11 +105,11 @@ BOOST_AUTO_TEST_CASE(SetnsUserNamespace) } else if(pid ==0) { try { setns({Namespace::USER}); - exit(ERROR); + _exit(ERROR); } catch(ProcessSetupException) { - exit(TEST_PASSED); + _exit(TEST_PASSED); } catch(...) { - exit(ERROR); + _exit(ERROR); } } else if(pid>0) { int status; -- 2.7.4 From 3ba639b76de913af1a80d35c94cc2106dd6b1376 Mon Sep 17 00:00:00 2001 From: Jan Olszak Date: Fri, 31 Jul 2015 17:11:16 +0200 Subject: [PATCH 02/16] lxcpp: Added utils sources [Feature] N/A [Cause] N/A [Solution] N/A [Verification] Link only against liblxcpp Change-Id: I7e9d32fc502a068dd9c50e5e45ab3ed07756f815 --- libs/lxcpp/CMakeLists.txt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/libs/lxcpp/CMakeLists.txt b/libs/lxcpp/CMakeLists.txt index 65bd2fc..4ea53ef 100644 --- a/libs/lxcpp/CMakeLists.txt +++ b/libs/lxcpp/CMakeLists.txt @@ -22,14 +22,18 @@ PROJECT(lxcpp) MESSAGE(STATUS "") MESSAGE(STATUS "Generating makefile for the liblxcpp...") FILE(GLOB HEADERS *.hpp) +FILE(GLOB HEADERS_UTILS ${COMMON_FOLDER}/utils/fd-utils.hpp + ${COMMON_FOLDER}/utils/exception.hpp) FILE(GLOB SRCS *.cpp *.hpp) +FILE(GLOB SRCS_UTILS ${COMMON_FOLDER}/utils/fd-utils.cpp + ${COMMON_FOLDER}/utils/exception.cpp) SET(_LIB_VERSION_ "${VERSION}") SET(_LIB_SOVERSION_ "0") SET(PC_FILE "lib${PROJECT_NAME}.pc") ## Setup target ################################################################ -ADD_LIBRARY(${PROJECT_NAME} SHARED ${SRCS}) +ADD_LIBRARY(${PROJECT_NAME} SHARED ${SRCS} ${SRCS_UTILS}) SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES SOVERSION ${_LIB_SOVERSION_} VERSION ${_LIB_VERSION_} @@ -53,3 +57,5 @@ INSTALL(TARGETS ${PROJECT_NAME} INSTALL(FILES ${HEADERS} DESTINATION ${INCLUDE_INSTALL_DIR}/lxcpp) +INSTALL(FILES ${HEADERS_UTILS} + DESTINATION ${INCLUDE_INSTALL_DIR}/lxcpp/utils) -- 2.7.4 From aa482a5e599af2f33ce893dbef2c73a2e6804e3b Mon Sep 17 00:00:00 2001 From: Dariusz Michaluk Date: Tue, 28 Jul 2015 15:55:25 +0200 Subject: [PATCH 03/16] Add daemonize function to work in the background. [Feature] Add daemonize function to work in the background. [Cause] N/A [Solution] N/A [Verification] Build, run tests, run server as daemon. Change-Id: Ie8987ac4d23ac350ab6036c476ade47436afbe69 --- common/utils/daemon.cpp | 93 +++++++++++++++++++++++++++++++++++++++++++++++++ common/utils/daemon.hpp | 34 ++++++++++++++++++ server/main.cpp | 11 +++++- zone-daemon/main.cpp | 11 +++++- 4 files changed, 147 insertions(+), 2 deletions(-) create mode 100644 common/utils/daemon.cpp create mode 100644 common/utils/daemon.hpp diff --git a/common/utils/daemon.cpp b/common/utils/daemon.cpp new file mode 100644 index 0000000..8ee84c5 --- /dev/null +++ b/common/utils/daemon.cpp @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * Contact: Dariusz Michaluk + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +/** + * @file + * @author Dariusz Michaluk + * @brief Run a process as a daemon + */ + +#include "fd-utils.hpp" + +#include +#include +#include +#include +#include + +namespace utils { + +bool daemonize() +{ + pid_t pid = ::fork(); + + if (pid == -1) { + std::cerr << "Fork failed" << std::endl; + return false; + } else if (pid != 0) { + exit (0); + } + + if (::setsid() == -1) { + return false; + } + + pid = ::fork(); + + /* + * Fork a second child and exit immediately to prevent zombies. + * This causes the second child process to be orphaned, making the init + * process responsible for its cleanup. And, since the first child is + * a session leader without a controlling terminal, it's possible for + * it to acquire one by opening a terminal in the future (System V + * based systems). This second fork guarantees that the child is no + * longer a session leader, preventing the daemon from ever acquiring + * a controlling terminal. + */ + + if (pid == -1) { + std::cerr << "Fork failed" << std::endl; + return false; + } else if (pid != 0) { + exit(0); + } + + if (::chdir("/") == -1) { + return false; + } + + ::umask(0); + + /** Close all open standard file descriptors */ + int fd = ::open("/dev/null", O_RDWR); + if (fd == -1) { + return false; + } + + for (int i = 0; i <= 2; i++) { + if (::dup2(fd, i) == -1) { + utils::close(fd); + return false; + } + } + utils::close(fd); + + return true; +} + +} // namespace utils diff --git a/common/utils/daemon.hpp b/common/utils/daemon.hpp new file mode 100644 index 0000000..4b5924a --- /dev/null +++ b/common/utils/daemon.hpp @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * Contact: Dariusz Michaluk + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +/** + * @file + * @author Dariusz Michaluk + * @brief Run a process as a daemon + */ + +#ifndef COMMON_UTILS_DAEMON_HPP +#define COMMON_UTILS_DAEMON_HPP + +namespace utils { + +bool daemonize(); + +} // namespace utils + +#endif // COMMON_UTILS_DAEMON_HPP diff --git a/server/main.cpp b/server/main.cpp index e9440cb..7ed4ef1 100644 --- a/server/main.cpp +++ b/server/main.cpp @@ -34,6 +34,7 @@ #include "logger/backend-syslog.hpp" #include "utils/typeinfo.hpp" #include "utils/signal.hpp" +#include "utils/daemon.hpp" #include #include @@ -64,9 +65,10 @@ int main(int argc, char* argv[]) desc.add_options() ("help,h", "print this help") ("root,r", "Don't drop root privileges at startup") - ("version,v", "show application version") + ("daemon,d", "Run server as daemon") ("log-level,l", po::value()->default_value("DEBUG"), "set log level") ("check,c", "check runtime environment and exit") + ("version,v", "show application version") ; po::variables_map vm; @@ -103,6 +105,13 @@ int main(int argc, char* argv[]) return Server::checkEnvironment() ? 0 : 1; } + bool runAsDaemon = vm.count("daemon") > 0; + + if (runAsDaemon && !utils::daemonize()) { + std::cerr << "Failed to daemonize" << std::endl; + return 1; + } + Logger::setLogLevel(vm["log-level"].as()); #if !defined(NDEBUG) Logger::setLogBackend(new StderrBackend()); diff --git a/zone-daemon/main.cpp b/zone-daemon/main.cpp index 58fad9b..1bb3091 100644 --- a/zone-daemon/main.cpp +++ b/zone-daemon/main.cpp @@ -33,6 +33,7 @@ #include "logger/backend-journal.hpp" #include "logger/backend-syslog.hpp" #include "utils/typeinfo.hpp" +#include "utils/daemon.hpp" #include #include @@ -58,8 +59,9 @@ int main(int argc, char* argv[]) desc.add_options() ("help,h", "print this help") - ("version,v", "show application version") + ("daemon,d", "Run server as daemon") ("log-level,l", po::value()->default_value("DEBUG"), "set log level") + ("version,v", "show application version") ; po::variables_map vm; @@ -93,6 +95,13 @@ int main(int argc, char* argv[]) return 0; } + bool runAsDaemon = vm.count("daemon") > 0; + + if (runAsDaemon && !utils::daemonize()) { + std::cerr << "Failed to daemonize" << std::endl; + return 1; + } + Logger::setLogLevel(vm["log-level"].as()); #if !defined(NDEBUG) Logger::setLogBackend(new StderrBackend()); -- 2.7.4 From a18c42df8f27093a407ce428d98698c14b28fe73 Mon Sep 17 00:00:00 2001 From: Mateusz Malicki Date: Mon, 27 Jul 2015 17:28:22 +0200 Subject: [PATCH 04/16] InputMonitor connected to epoll dispatcher [Feature] InputMonitor connected to epoll dispatcher [Cause] N/A [Solution] N/A [Verification] Build, run tests Change-Id: If2bdcd8c8493bc22357aa93b43b5c6e530c954d4 --- server/input-monitor.cpp | 139 +++++++++++---------------- server/input-monitor.hpp | 36 +++---- server/zones-manager.cpp | 16 ++- server/zones-manager.hpp | 3 +- tests/unit_tests/server/ut-input-monitor.cpp | 125 ++++++++++++++++++------ 5 files changed, 179 insertions(+), 140 deletions(-) diff --git a/server/input-monitor.cpp b/server/input-monitor.cpp index e99906f..bb65b90 100644 --- a/server/input-monitor.cpp +++ b/server/input-monitor.cpp @@ -26,19 +26,17 @@ #include "input-monitor-config.hpp" #include "input-monitor.hpp" +#include "zones-manager.hpp" #include "exception.hpp" #include "logger/logger.hpp" #include "utils/exception.hpp" #include "utils/fs.hpp" -#include "utils/callback-wrapper.hpp" -#include "utils/scoped-gerror.hpp" +#include "utils/fd-utils.hpp" #include #include #include -#include -#include #include #include #include @@ -64,10 +62,13 @@ const std::string DEVICE_DIR = "/dev/input/"; } // namespace -InputMonitor::InputMonitor(const InputConfig& inputConfig, - const NotifyCallback& notifyCallback) - : mConfig(inputConfig), - mNotifyCallback(notifyCallback) +InputMonitor::InputMonitor(ipc::epoll::EventPoll& eventPoll, + const InputConfig& inputConfig, + ZonesManager* zonesManager) + : mConfig(inputConfig) + , mZonesManager(zonesManager) + , mFd(-1) + , mEventPoll(eventPoll) { if (mConfig.timeWindowMs > MAX_TIME_WINDOW_SEC * 1000L) { LOGE("Time window exceeds maximum: " << MAX_TIME_WINDOW_SEC); @@ -79,37 +80,39 @@ InputMonitor::InputMonitor(const InputConfig& inputConfig, throw InputMonitorException("Number of events exceeds maximum"); } - std::string devicePath = getDevicePath(); + mDevicePath = getDevicePath(); LOGT("Input monitor configuration: \n" << "\tenabled: " << mConfig.enabled << "\n" << "\tdevice: " << mConfig.device << "\n" - << "\tpath: " << devicePath << "\n" + << "\tpath: " << mDevicePath << "\n" << "\ttype: " << EV_KEY << "\n" << "\tcode: " << mConfig.code << "\n" << "\tvalue: " << KEY_PRESSED << "\n" << "\tnumberOfEvents: " << mConfig.numberOfEvents << "\n" << "\ttimeWindowMs: " << mConfig.timeWindowMs); - - createGIOChannel(devicePath); } InputMonitor::~InputMonitor() { + std::unique_lock mMutex; LOGD("Destroying InputMonitor"); - ScopedGError error; - g_io_channel_unref(mChannelPtr); - if (g_io_channel_shutdown(mChannelPtr, FALSE, &error) - != G_IO_STATUS_NORMAL) { - LOGE("Error during shutting down GIOChannel: " << error->message); - } + stop(); +} - if (!g_source_remove(mSourceId)) { - LOGE("Error during removing the source"); - } +void InputMonitor::start() +{ + std::unique_lock mMutex; + setHandler(mDevicePath); +} + +void InputMonitor::stop() +{ + leaveDevice(); } namespace { + bool isDeviceWithName(const boost::regex& deviceNameRegex, const fs::directory_entry& directoryEntry) { @@ -119,7 +122,7 @@ bool isDeviceWithName(const boost::regex& deviceNameRegex, return false; } - int fd = open(path.c_str(), O_RDONLY); + int fd = ::open(path.c_str(), O_RDONLY); if (fd < 0) { LOGD("Failed to open " << path); return false; @@ -128,13 +131,13 @@ bool isDeviceWithName(const boost::regex& deviceNameRegex, char name[DEVICE_NAME_LENGTH]; if (ioctl(fd, EVIOCGNAME(sizeof(name)), name) < 0) { LOGD("Failed to get the device name of: " << path); - if (close(fd) < 0) { + if (::close(fd) < 0) { LOGE("Error during closing file " << path); } return false; } - if (close(fd) < 0) { + if (::close(fd) < 0) { LOGE("Error during closing file " << path); } @@ -146,6 +149,7 @@ bool isDeviceWithName(const boost::regex& deviceNameRegex, return false; } + } // namespace std::string InputMonitor::getDevicePath() const @@ -174,80 +178,47 @@ std::string InputMonitor::getDevicePath() const return it->path().string(); } - - -void InputMonitor::createGIOChannel(const std::string& devicePath) +void InputMonitor::setHandler(const std::string& devicePath) { + using namespace std::placeholders; + // We need NONBLOCK for FIFOs in the tests - int fd = open(devicePath.c_str(), O_RDONLY | O_NONBLOCK); - if (fd < 0) { + mFd = ::open(devicePath.c_str(), O_RDONLY | O_NONBLOCK | O_CLOEXEC); + if (mFd < 0) { LOGE("Cannot create input monitor channel. Device file: " << devicePath << " doesn't exist"); throw InputMonitorException("Device does not exist"); } - - mChannelPtr = g_io_channel_unix_new(fd); - - // Read binary data - if (g_io_channel_set_encoding(mChannelPtr, - NULL, - NULL) != G_IO_STATUS_NORMAL) { - LOGE("Cannot set encoding for input monitor channel "); - throw InputMonitorException("Cannot set encoding"); - } - - using namespace std::placeholders; - ReadDeviceCallback callback = std::bind(&InputMonitor::readDevice, this, _1); - // Add the callback - mSourceId = g_io_add_watch_full(mChannelPtr, - G_PRIORITY_DEFAULT, - G_IO_IN, - readDeviceCallback, - utils::createCallbackWrapper(callback, mGuard.spawn()), - &utils::deleteCallbackWrapper); - if (!mSourceId) { - LOGE("Cannot add watch on device input file"); - throw InputMonitorException("Cannot add watch"); - } + mEventPoll.addFD(mFd, EPOLLIN, std::bind(&InputMonitor::handleInternal, this, _1, _2)); } -gboolean InputMonitor::readDeviceCallback(GIOChannel* gio, - GIOCondition /*condition*/, - gpointer data) -{ - const ReadDeviceCallback& callback = utils::getCallbackFromPointer(data); - callback(gio); - return TRUE; -} - -void InputMonitor::readDevice(GIOChannel* gio) +void InputMonitor::handleInternal(int /* fd */, ipc::epoll::Events events) { struct input_event ie; - gsize nBytesReadTotal = 0; - gsize nBytesRequested = sizeof(struct input_event); - ScopedGError error; - - do { - gsize nBytesRead = 0; - GIOStatus readStatus = g_io_channel_read_chars(gio, - &reinterpret_cast(&ie)[nBytesReadTotal], - nBytesRequested, - &nBytesRead, - &error); - - if (readStatus == G_IO_STATUS_ERROR) { - LOGE("Read from input monitor channel failed: " << error->message); + try { + if (events == EPOLLHUP) { + stop(); return; } - - nBytesRequested -= nBytesRead; - nBytesReadTotal += nBytesRead; - } while (nBytesRequested > 0); - - + utils::read(mFd, &ie, sizeof(struct input_event)); + } catch (const std::exception& ex) { + LOGE("Read from input monitor channel failed: " << ex.what()); + return; + } if (isExpectedEventSequence(ie)) { LOGI("Input monitor detected pattern."); - mNotifyCallback(); + if (mZonesManager->isRunning()) { + mZonesManager->switchingSequenceMonitorNotify(); + } + } +} + +void InputMonitor::leaveDevice() +{ + if (mFd != -1) { + mEventPoll.removeFD(mFd); + utils::close(mFd); + mFd = -1; } } diff --git a/server/input-monitor.hpp b/server/input-monitor.hpp index 2b0ed45..7ab5344 100644 --- a/server/input-monitor.hpp +++ b/server/input-monitor.hpp @@ -26,45 +26,45 @@ #define SERVER_INPUT_MONITOR_HPP #include "input-monitor-config.hpp" -#include "utils/callback-guard.hpp" +#include "ipc/epoll/event-poll.hpp" #include #include -#include -#include #include #include +#include namespace vasum { +class ZonesManager; + class InputMonitor { public: - typedef std::function NotifyCallback; - - InputMonitor(const InputConfig& inputConfig, - const NotifyCallback& notifyCallback); + InputMonitor(ipc::epoll::EventPoll& eventPoll, + const InputConfig& inputConfig, + ZonesManager* zonesManager); ~InputMonitor(); + void start(); + void stop(); private: - typedef std::function ReadDeviceCallback; + typedef std::mutex Mutex; InputConfig mConfig; - NotifyCallback mNotifyCallback; - + ZonesManager* mZonesManager; + int mFd; + ipc::epoll::EventPoll& mEventPoll; std::list mEventTimes; - GIOChannel* mChannelPtr; + std::string mDevicePath; + Mutex mMutex; std::string getDevicePath() const; - void createGIOChannel(const std::string& devicePath); - - // Internal callback to be registered at glib g_io_add_watch() - static gboolean readDeviceCallback(GIOChannel*, GIOCondition, gpointer); + void setHandler(const std::string& devicePath); + void handleInternal(int fd, ipc::epoll::Events events); + void leaveDevice(); bool isExpectedEventSequence(const struct input_event&); - void readDevice(GIOChannel*); - utils::CallbackGuard mGuard; - guint mSourceId; }; } // namespace vasum diff --git a/server/zones-manager.cpp b/server/zones-manager.cpp index 484b3e9..0d88d24 100644 --- a/server/zones-manager.cpp +++ b/server/zones-manager.cpp @@ -173,6 +173,11 @@ ZonesManager::ZonesManager(ipc::epoll::EventPoll& eventPoll, const std::string& configPath, mDynamicConfig, getVasumDbPrefix()); + + if (mConfig.inputConfig.enabled) { + LOGI("Registering input monitor [" << mConfig.inputConfig.device.c_str() << "]"); + mSwitchingSequenceMonitor.reset(new InputMonitor(eventPoll, mConfig.inputConfig, this)); + } } ZonesManager::~ZonesManager() @@ -207,11 +212,8 @@ void ZonesManager::start() LOGD("ZonesManager object instantiated"); if (mConfig.inputConfig.enabled) { - LOGI("Registering input monitor [" << mConfig.inputConfig.device.c_str() << "]"); - mSwitchingSequenceMonitor.reset( - new InputMonitor(mConfig.inputConfig, - std::bind(&ZonesManager::switchingSequenceMonitorNotify, - this))); + LOGI("Starting input monitor "); + mSwitchingSequenceMonitor->start(); } // After everything's initialized start to respond to clients' requests @@ -238,6 +240,10 @@ void ZonesManager::stop(bool wait) // wait for all tasks to complete mWorker.reset(); mHostIPCConnection.stop(wait); + if (mConfig.inputConfig.enabled) { + LOGI("Stopping input monitor "); + mSwitchingSequenceMonitor->stop(); + } mIsRunning = false; } diff --git a/server/zones-manager.hpp b/server/zones-manager.hpp index de6663d..c6f6918 100644 --- a/server/zones-manager.hpp +++ b/server/zones-manager.hpp @@ -198,6 +198,8 @@ public: api::MethodResultBuilder::Pointer result); void handleCleanUpZonesRootCall(api::MethodResultBuilder::Pointer result); + void switchingSequenceMonitorNotify(); + private: typedef std::recursive_mutex Mutex; typedef std::unique_lock Lock; @@ -227,7 +229,6 @@ private: void saveDynamicConfig(); void updateDefaultId(); void refocus(); - void switchingSequenceMonitorNotify(); void generateNewConfig(const std::string& id, const std::string& templatePath); std::string getTemplatePathForExistingZone(const std::string& id); diff --git a/tests/unit_tests/server/ut-input-monitor.cpp b/tests/unit_tests/server/ut-input-monitor.cpp index 22fba47..75079a2 100644 --- a/tests/unit_tests/server/ut-input-monitor.cpp +++ b/tests/unit_tests/server/ut-input-monitor.cpp @@ -29,10 +29,12 @@ #include "input-monitor.hpp" #include "input-monitor-config.hpp" #include "exception.hpp" +#include "zones-manager.hpp" #include "utils/glib-loop.hpp" #include "utils/latch.hpp" #include "utils/scoped-dir.hpp" +#include "ipc/epoll/thread-dispatcher.hpp" #include #include @@ -44,7 +46,6 @@ #include #include - using namespace vasum; using namespace utils; @@ -58,16 +59,24 @@ const int EVENT_CODE = 139; const int EVENT_BUTTON_RELEASED = 0; const int EVENT_BUTTON_PRESSED = 1; -const int SINGLE_EVENT_TIMEOUT = 1000; +const int EVENT_TIMEOUT = 1000; + +const std::string CONFIG_DIR = VSM_TEST_CONFIG_INSTALL_DIR; +const std::string TEST_CONFIG_PATH = CONFIG_DIR + "/test-daemon.conf"; +const std::string SIMPLE_TEMPLATE = "console-ipc"; +const std::string ZONES_PATH = "/tmp/ut-zones"; // the same as in daemon.conf struct Fixture { utils::ScopedGlibLoop mLoop; ScopedDir mTestPathGuard; + ScopedDir mZonesPathGuard; InputConfig inputConfig; struct input_event ie; + ipc::epoll::ThreadDispatcher dispatcher; Fixture() : mTestPathGuard(TEST_DIR) + , mZonesPathGuard(ZONES_PATH) { inputConfig.numberOfEvents = 2; inputConfig.device = TEST_INPUT_DEVICE; @@ -84,46 +93,65 @@ struct Fixture { BOOST_CHECK(::mkfifo(TEST_INPUT_DEVICE.c_str(), S_IWUSR | S_IRUSR) >= 0); } + + ipc::epoll::EventPoll& getPoll() { + return dispatcher.getPoll(); + } }; + +template +bool spinWaitFor(int timeoutMs, Predicate pred) +{ + auto until = std::chrono::steady_clock::now() + std::chrono::milliseconds(timeoutMs); + while (!pred()) { + if (std::chrono::steady_clock::now() >= until) { + return false; + } + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + return true; +} + } // namespace BOOST_FIXTURE_TEST_SUITE(InputMonitorSuite, Fixture) BOOST_AUTO_TEST_CASE(ConfigOK) { - InputMonitor inputMonitor(inputConfig, InputMonitor::NotifyCallback()); + ZonesManager cm(getPoll(), TEST_CONFIG_PATH); + InputMonitor inputMonitor(getPoll(), inputConfig, &cm); } BOOST_AUTO_TEST_CASE(ConfigTimeWindowMsTooHigh) { + ZonesManager cm(getPoll(), TEST_CONFIG_PATH); inputConfig.timeWindowMs = 50000; - BOOST_REQUIRE_EXCEPTION(InputMonitor inputMonitor(inputConfig, InputMonitor::NotifyCallback()), + BOOST_REQUIRE_EXCEPTION(InputMonitor inputMonitor(getPoll(), inputConfig, &cm), InputMonitorException, WhatEquals("Time window exceeds maximum")); } BOOST_AUTO_TEST_CASE(ConfigDeviceFilePathNotExisting) { + ZonesManager cm(getPoll(), TEST_CONFIG_PATH); inputConfig.device = TEST_INPUT_DEVICE + "notExisting"; - BOOST_REQUIRE_EXCEPTION(InputMonitor inputMonitor(inputConfig, InputMonitor::NotifyCallback()), + BOOST_REQUIRE_EXCEPTION(InputMonitor inputMonitor(getPoll(), inputConfig, &cm), InputMonitorException, WhatEquals("Cannot find a device")); } namespace { -void sendNEvents(Fixture& f, unsigned int noOfEventsToSend) +void sendEvent(Fixture& f, ZonesManager& cm) { - Latch eventLatch; - - InputMonitor inputMonitor(f.inputConfig, [&] {eventLatch.set();}); - + InputMonitor inputMonitor(f.getPoll(), f.inputConfig, &cm); + inputMonitor.start(); int fd = ::open(TEST_INPUT_DEVICE.c_str(), O_WRONLY); BOOST_REQUIRE(fd >= 0); - for (unsigned int i = 0; i < noOfEventsToSend * f.inputConfig.numberOfEvents; ++i) { + for (int i = 0; i < f.inputConfig.numberOfEvents; ++i) { // button pressed event f.ie.value = EVENT_BUTTON_PRESSED; f.ie.time.tv_usec += 5; @@ -140,36 +168,52 @@ void sendNEvents(Fixture& f, unsigned int noOfEventsToSend) } BOOST_CHECK(::close(fd) >= 0); - BOOST_CHECK(eventLatch.waitForN(noOfEventsToSend, SINGLE_EVENT_TIMEOUT * noOfEventsToSend)); - - // Check if no more events are waiting - BOOST_CHECK(!eventLatch.wait(10)); } } // namespace -BOOST_AUTO_TEST_CASE(EventOneAtATime) +BOOST_AUTO_TEST_CASE(SingleEvent) { - sendNEvents(*this, 1); + ZonesManager cm(getPoll(), TEST_CONFIG_PATH); + cm.start(); + cm.createZone("zone1", SIMPLE_TEMPLATE); + cm.createZone("zone2", SIMPLE_TEMPLATE); + cm.createZone("zone3", SIMPLE_TEMPLATE); + cm.restoreAll(); + sendEvent(*this, cm); + BOOST_CHECK(spinWaitFor(EVENT_TIMEOUT, [&] { + return cm.getRunningForegroundZoneId() == "zone2"; + })); } -BOOST_AUTO_TEST_CASE(EventTenAtATime) +BOOST_AUTO_TEST_CASE(MultipleEvent) { - sendNEvents(*this, 10); + ZonesManager cm(getPoll(), TEST_CONFIG_PATH); + cm.start(); + cm.createZone("zone1", SIMPLE_TEMPLATE); + cm.createZone("zone2", SIMPLE_TEMPLATE); + cm.createZone("zone3", SIMPLE_TEMPLATE); + cm.restoreAll(); + for (int i = 1; i < 10; ++i) { + sendEvent(*this, cm); + std::string zoneId = "zone" + std::to_string(i % 3 + 1); + BOOST_CHECK(spinWaitFor(EVENT_TIMEOUT, [&] { + return cm.getRunningForegroundZoneId() == zoneId; + })); + } } namespace { -void sendNEventsWithPauses(Fixture& f, unsigned int noOfEventsToSend) +void sendEventWithPauses(Fixture& f, ZonesManager& cm) { - Latch eventLatch; - - InputMonitor inputMonitor(f.inputConfig, [&] {eventLatch.set();}); + InputMonitor inputMonitor(f.getPoll(), f.inputConfig, &cm); + inputMonitor.start(); int fd = ::open(TEST_INPUT_DEVICE.c_str(), O_WRONLY); BOOST_REQUIRE(fd >= 0); - for (unsigned int i = 0; i < noOfEventsToSend * f.inputConfig.numberOfEvents; ++i) { + for (int i = 0; i < f.inputConfig.numberOfEvents; ++i) { // Send first two bytes of the button pressed event f.ie.value = EVENT_BUTTON_PRESSED; f.ie.time.tv_usec += 5; @@ -191,22 +235,39 @@ void sendNEventsWithPauses(Fixture& f, unsigned int noOfEventsToSend) BOOST_CHECK(ret > 0); } BOOST_CHECK(::close(fd) >= 0); - BOOST_CHECK(eventLatch.waitForN(noOfEventsToSend, SINGLE_EVENT_TIMEOUT * noOfEventsToSend)); - - // Check if no more events are waiting - BOOST_CHECK(!eventLatch.wait(10)); } } // namespace -BOOST_AUTO_TEST_CASE(EventOneAtATimeWithPauses) +BOOST_AUTO_TEST_CASE(SingleEventWithPauses) { - sendNEventsWithPauses(*this, 1); + ZonesManager cm(getPoll(), TEST_CONFIG_PATH); + cm.start(); + cm.createZone("zone1", SIMPLE_TEMPLATE); + cm.createZone("zone2", SIMPLE_TEMPLATE); + cm.createZone("zone3", SIMPLE_TEMPLATE); + cm.restoreAll(); + sendEventWithPauses(*this, cm); + BOOST_CHECK(spinWaitFor(EVENT_TIMEOUT, [&] { + return cm.getRunningForegroundZoneId() == "zone2"; + })); } -BOOST_AUTO_TEST_CASE(EventTenAtATimeWithPauses) +BOOST_AUTO_TEST_CASE(MultipleEventWithPauses) { - sendNEventsWithPauses(*this, 10); + ZonesManager cm(getPoll(), TEST_CONFIG_PATH); + cm.start(); + cm.createZone("zone1", SIMPLE_TEMPLATE); + cm.createZone("zone2", SIMPLE_TEMPLATE); + cm.createZone("zone3", SIMPLE_TEMPLATE); + cm.restoreAll(); + for (int i = 1; i < 10; ++i) { + sendEventWithPauses(*this, cm); + std::string zoneId = "zone" + std::to_string(i % 3 + 1); + BOOST_CHECK(spinWaitFor(EVENT_TIMEOUT, [&] { + return cm.getRunningForegroundZoneId() == zoneId; + })); + } } -- 2.7.4 From 7d2ce1f9cfb60d4de3336fc3e8d7296f183daace Mon Sep 17 00:00:00 2001 From: Lukasz Pawelczyk Date: Tue, 4 Aug 2015 12:17:23 +0200 Subject: [PATCH 05/16] lxcpp: fix linking of 3rd party binaries with lxcpp [Feature] N/A [Cause] To be finally able to link with lxcpp of 3rd party code [Solution] N/A [Verification] Link non-vasum program against liblxcpp Change-Id: I218756ace9d9217db87d3e19b820d35653a22153 --- libs/lxcpp/CMakeLists.txt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/libs/lxcpp/CMakeLists.txt b/libs/lxcpp/CMakeLists.txt index 4ea53ef..c7bd926 100644 --- a/libs/lxcpp/CMakeLists.txt +++ b/libs/lxcpp/CMakeLists.txt @@ -40,9 +40,10 @@ SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES ) ## Link libraries ############################################################## -INCLUDE_DIRECTORIES(${LIBS_FOLDER}) -INCLUDE_DIRECTORIES(${COMMON_FOLDER}) -TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${pkgs_LDFLAGS} Logger) +FIND_PACKAGE(Boost COMPONENTS system filesystem) +INCLUDE_DIRECTORIES(${LIBS_FOLDER} ${COMMON_FOLDER}) +INCLUDE_DIRECTORIES(SYSTEM ${Boost_INCLUDE_DIRS}) +TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${pkgs_LDFLAGS} ${Boost_LIBRARIES} Logger) ## Generate the pc file ######################################################## CONFIGURE_FILE(${PC_FILE}.in ${CMAKE_CURRENT_BINARY_DIR}/${PC_FILE} @ONLY) -- 2.7.4 From 991d45a32263a07df6d1e1af42fba048f2b06bf6 Mon Sep 17 00:00:00 2001 From: Krzysztof Dynowski Date: Fri, 31 Jul 2015 15:01:38 +0200 Subject: [PATCH 06/16] Cleanup cppcheck statistics [Feature] N/A [Cause] Cppcheck results reported on jenkins contains error/warning messages [Solution] Make code correction to satisfy cppcheck, add cppcheck configuration to suppress some messages (e.g. API funcs) [Verification] Have installed cppcheck >= v1.66 Perform ./tests/cppcheck/cppcheck.sh script before commiting to review Change-Id: Ice10bbc118ab921feb9cb7261d4a2bdc22282353 --- common/netlink/netlink-message.cpp | 7 ++++--- common/utils/same-thread-guard.hpp | 4 ++-- common/utils/worker.cpp | 2 +- libs/config/fields.hpp | 5 +---- libs/config/sqlite3/connection.cpp | 4 +--- libs/config/types.hpp | 2 +- libs/ipc/client.cpp | 1 + libs/ipc/internals/processor.hpp | 4 +--- libs/ipc/internals/socket.cpp | 8 ++++++-- server/host-dbus-connection.cpp | 3 ++- server/host-dbus-connection.hpp | 2 +- server/netdev.cpp | 2 +- server/zone-provision.cpp | 8 ++++---- server/zone-provision.hpp | 8 ++++---- server/zones-manager.cpp | 2 +- tests/cppcheck/cppcheck.sh | 15 +++++++++++++++ tests/cppcheck/cppcheck.suppress | 7 +++++++ tests/unit_tests/config/testconfig-example.hpp | 13 +++++++------ tests/unit_tests/ipc/ut-ipc.cpp | 7 +------ tests/unit_tests/lxc/ut-zone.cpp | 4 ++-- tests/unit_tests/server/ut-server.cpp | 2 +- tests/unit_tests/server/ut-zone-provision.cpp | 6 +++--- tests/unit_tests/server/ut-zone.cpp | 4 ++-- wrapper/wrapper-compatibility.cpp | 21 ++++++++++----------- zone-daemon/daemon.cpp | 10 ++++++---- zone-daemon/daemon.hpp | 6 +++--- zone-daemon/runner.hpp | 4 ++-- 27 files changed, 90 insertions(+), 71 deletions(-) create mode 100755 tests/cppcheck/cppcheck.sh create mode 100644 tests/cppcheck/cppcheck.suppress diff --git a/common/netlink/netlink-message.cpp b/common/netlink/netlink-message.cpp index 29b079a..58aa1a2 100644 --- a/common/netlink/netlink-message.cpp +++ b/common/netlink/netlink-message.cpp @@ -289,14 +289,15 @@ inline int NetlinkResponse::getHdrPosition() const NetlinkResponse send(const NetlinkMessage& msg, int pid) { - assert(msg.hdr().nlmsg_flags & NLM_F_ACK); + const auto &hdr = msg.hdr(); + assert(hdr.nlmsg_flags & NLM_F_ACK); std::unique_ptr> data; Netlink nl; nl.open(pid); try { - nl.send(&msg.hdr()); - data = nl.rcv(msg.hdr().nlmsg_seq); + nl.send(&hdr); + data = nl.rcv(hdr.nlmsg_seq); } catch (const std::exception& ex) { LOGE("Sending failed (" << ex.what() << ")"); nl.close(); diff --git a/common/utils/same-thread-guard.hpp b/common/utils/same-thread-guard.hpp index 3a173b5..ec62b70 100644 --- a/common/utils/same-thread-guard.hpp +++ b/common/utils/same-thread-guard.hpp @@ -67,8 +67,8 @@ private: #else // ENABLE_SAME_THREAD_GUARD # define ASSERT_SAME_THREAD(g) - bool check() {return true;} - void reset() {} + static bool check() {return true;} + static void reset() {} #endif // ENABLE_SAME_THREAD_GUARD }; diff --git a/common/utils/worker.cpp b/common/utils/worker.cpp index 09069e8..eeac593 100644 --- a/common/utils/worker.cpp +++ b/common/utils/worker.cpp @@ -145,7 +145,7 @@ private: LOGT("Worker thread exited"); } - void execute(const TaskInfo& taskInfo) + static void execute(const TaskInfo& taskInfo) { try { LOGT("Executing task from subgroup " << taskInfo.groupID); diff --git a/libs/config/fields.hpp b/libs/config/fields.hpp index 8169cb4..d1fc4be 100644 --- a/libs/config/fields.hpp +++ b/libs/config/fields.hpp @@ -60,10 +60,7 @@ #define CONFIG_REGISTER_EMPTY \ template \ - void accept(Visitor ) { \ - } \ - template \ - void accept(Visitor ) const { \ + static void accept(Visitor ) { \ } \ #define CONFIG_REGISTER(...) \ diff --git a/libs/config/sqlite3/connection.cpp b/libs/config/sqlite3/connection.cpp index cb25694..3887352 100644 --- a/libs/config/sqlite3/connection.cpp +++ b/libs/config/sqlite3/connection.cpp @@ -50,9 +50,7 @@ Connection::Connection(const std::string& path) Connection::~Connection() { - if (::sqlite3_close(mDbPtr) != SQLITE_OK) { - throw ConfigException("Error during closing the database. Error: " + getErrorMessage()); - } + ::sqlite3_close(mDbPtr); } void Connection::exec(const std::string& query) diff --git a/libs/config/types.hpp b/libs/config/types.hpp index 3355f8f..f8b788d 100644 --- a/libs/config/types.hpp +++ b/libs/config/types.hpp @@ -41,4 +41,4 @@ struct FileDescriptor { }// config -#endif //COMMON_CONFIG_TYPES_HPP \ No newline at end of file +#endif //COMMON_CONFIG_TYPES_HPP diff --git a/libs/ipc/client.cpp b/libs/ipc/client.cpp index ad2eb47..245faee 100644 --- a/libs/ipc/client.cpp +++ b/libs/ipc/client.cpp @@ -32,6 +32,7 @@ namespace ipc { Client::Client(epoll::EventPoll& eventPoll, const std::string& socketPath) : mEventPoll(eventPoll), + mServiceID(-1), mProcessor(eventPoll, "[CLIENT] "), mSocketPath(socketPath) { diff --git a/libs/ipc/internals/processor.hpp b/libs/ipc/internals/processor.hpp index 4d0089f..7f7cd63 100644 --- a/libs/ipc/internals/processor.hpp +++ b/libs/ipc/internals/processor.hpp @@ -329,8 +329,6 @@ public: FileDescriptor getEventFD(); private: - typedef std::function& data)> SerializeCallback; - typedef std::function(int fd)> ParseCallback; typedef std::unique_lock Lock; typedef RequestQueue::Request Request; @@ -340,7 +338,7 @@ private: struct RegisterSignalsProtocolMessage { RegisterSignalsProtocolMessage() = default; - RegisterSignalsProtocolMessage(const std::vector ids) + RegisterSignalsProtocolMessage(const std::vector& ids) : ids(ids) {} std::vector ids; diff --git a/libs/ipc/internals/socket.cpp b/libs/ipc/internals/socket.cpp index 1961a52..c58809d 100644 --- a/libs/ipc/internals/socket.cpp +++ b/libs/ipc/internals/socket.cpp @@ -182,11 +182,15 @@ int Socket::createSocketInternal(const std::string& path) Socket Socket::createSocket(const std::string& path) { // Initialize a socket - int fd = -1; + int fd; #ifdef HAVE_SYSTEMD fd = getSystemdSocketInternal(path); + if (fd == -1) { + fd = createSocketInternal(path); + } +#else + fd = createSocketInternal(path); #endif // HAVE_SYSTEMD - fd = fd != -1 ? fd : createSocketInternal(path); return Socket(fd); } diff --git a/server/host-dbus-connection.cpp b/server/host-dbus-connection.cpp index 1229acb..ce415c8 100644 --- a/server/host-dbus-connection.cpp +++ b/server/host-dbus-connection.cpp @@ -424,8 +424,9 @@ void HostDbusConnection::onSignalCall(const std::string& /* senderBusName */, const std::string& objectPath, const std::string& interface, const std::string& /* signalName */, - GVariant* /* parameters */) + GVariant* /* parameters */) const { + (void)this; // satisfy cpp-check if (objectPath != api::dbus::OBJECT_PATH || interface != api::dbus::INTERFACE) { return; } diff --git a/server/host-dbus-connection.hpp b/server/host-dbus-connection.hpp index bb59afc..1a703e7 100644 --- a/server/host-dbus-connection.hpp +++ b/server/host-dbus-connection.hpp @@ -97,7 +97,7 @@ private: const std::string& objectPath, const std::string& interface, const std::string& signalName, - GVariant* parameters); + GVariant* parameters) const; }; diff --git a/server/netdev.cpp b/server/netdev.cpp index bf2576c..3086e7f 100644 --- a/server/netdev.cpp +++ b/server/netdev.cpp @@ -595,7 +595,7 @@ void setAttrs(const pid_t nsPid, const std::string& netdev, const Attrs& attrs) size_t pos = addrAttr.find(":"); if (pos == string::npos || pos == addrAttr.length()) { LOGE("Wrong input data format: ill formed address attribute: " << addrAttr); - VasumException("Wrong input data format: ill formed address attribute"); + throw VasumException("Wrong input data format: ill formed address attribute"); } attrs.push_back(make_tuple(addrAttr.substr(0, pos), addrAttr.substr(pos + 1))); } diff --git a/server/zone-provision.cpp b/server/zone-provision.cpp index 70c3711..4e99f83 100644 --- a/server/zone-provision.cpp +++ b/server/zone-provision.cpp @@ -255,7 +255,7 @@ void ZoneProvision::link(const ZoneProvisioningConfig::Link& config) throw UtilsException("Failed to hard link: path prefix is not valid"); } -std::string ZoneProvision::getId(const ZoneProvisioningConfig::File& file) const +std::string ZoneProvision::getId(const ZoneProvisioningConfig::File& file) { //TODO output of type,flags and mode should be more user friendly return "file " + @@ -265,7 +265,7 @@ std::string ZoneProvision::getId(const ZoneProvisioningConfig::File& file) const std::to_string(file.mode); } -std::string ZoneProvision::getId(const ZoneProvisioningConfig::Mount& mount) const +std::string ZoneProvision::getId(const ZoneProvisioningConfig::Mount& mount) { //TODO output of flags should be more user friendly return "mount " + @@ -276,12 +276,12 @@ std::string ZoneProvision::getId(const ZoneProvisioningConfig::Mount& mount) con mount.data; } -std::string ZoneProvision::getId(const ZoneProvisioningConfig::Link& link) const +std::string ZoneProvision::getId(const ZoneProvisioningConfig::Link& link) { return "link " + link.source + " " + link.target; } -std::string ZoneProvision::getId(const ZoneProvisioningConfig::Provision& provision) const +std::string ZoneProvision::getId(const ZoneProvisioningConfig::Provision& provision) { using namespace vasum; if (provision.is()) { diff --git a/server/zone-provision.hpp b/server/zone-provision.hpp index 2f123ae..cf3d5c6 100644 --- a/server/zone-provision.hpp +++ b/server/zone-provision.hpp @@ -113,10 +113,10 @@ private: void file(const ZoneProvisioningConfig::File& config); void link(const ZoneProvisioningConfig::Link& config); - std::string getId(const ZoneProvisioningConfig::File& file) const; - std::string getId(const ZoneProvisioningConfig::Mount& mount) const; - std::string getId(const ZoneProvisioningConfig::Link& link) const; - std::string getId(const ZoneProvisioningConfig::Provision& provision) const; + static std::string getId(const ZoneProvisioningConfig::File& file); + static std::string getId(const ZoneProvisioningConfig::Mount& mount); + static std::string getId(const ZoneProvisioningConfig::Link& link); + static std::string getId(const ZoneProvisioningConfig::Provision& provision); }; } // namespace vasum diff --git a/server/zones-manager.cpp b/server/zones-manager.cpp index 0d88d24..5280d21 100644 --- a/server/zones-manager.cpp +++ b/server/zones-manager.cpp @@ -399,7 +399,7 @@ void ZonesManager::focusInternal(Zones::iterator iter) } Zone& zoneToFocus = get(iter); - const std::string idToFocus = zoneToFocus.getId(); + const std::string& idToFocus = zoneToFocus.getId(); if (idToFocus == mActiveZoneId) { return; diff --git a/tests/cppcheck/cppcheck.sh b/tests/cppcheck/cppcheck.sh new file mode 100755 index 0000000..b8bb637 --- /dev/null +++ b/tests/cppcheck/cppcheck.sh @@ -0,0 +1,15 @@ +#!/bin/bash +OPT="" +OPT+=" --enable=all" +OPT+=" --std=c++11" +OPT+=" --inconclusive" +OPT+=" --suppressions tests/cppcheck/cppcheck.suppress" +OPT+=" --suppress=missingIncludeSystem" +OPT+=" --inline-suppr" +OPT+=" -iCMakeFiles" +OPT+=" -I common" +OPT+=" -I libs" +OPT+=" -I client" +OPT+=" -I server" +OPT+=" -U__NR_capset" +cppcheck ${OPT} --template=gcc ./ 1>/dev/null diff --git a/tests/cppcheck/cppcheck.suppress b/tests/cppcheck/cppcheck.suppress new file mode 100644 index 0000000..d3b098f --- /dev/null +++ b/tests/cppcheck/cppcheck.suppress @@ -0,0 +1,7 @@ +// public API functions (or write tests to use them all) +unusedFunction:wrapper/wrapper-compatibility.cpp +unusedFunction:wrapper/wrapper.cpp + +// lambda not recognized to catch exception (v1.68) +exceptThrowInNoexecptFunction:client/vasum-client-impl.cpp + diff --git a/tests/unit_tests/config/testconfig-example.hpp b/tests/unit_tests/config/testconfig-example.hpp index ff5c2ef..549985b 100644 --- a/tests/unit_tests/config/testconfig-example.hpp +++ b/tests/unit_tests/config/testconfig-example.hpp @@ -34,13 +34,7 @@ struct TestConfig { struct SubConfig { struct SubSubConfig { - int intVal; - bool moved; - CONFIG_REGISTER - ( - intVal - ) SubSubConfig() : intVal(), moved(false) {} SubSubConfig(const SubSubConfig& config) : intVal(config.intVal), moved(false) {} SubSubConfig(SubSubConfig&& config) : intVal(std::move(config.intVal)), moved(false) { @@ -60,6 +54,13 @@ struct TestConfig { bool isMoved() const { return moved; } + + int intVal; + bool moved; + CONFIG_REGISTER + ( + intVal + ) }; int intVal; diff --git a/tests/unit_tests/ipc/ut-ipc.cpp b/tests/unit_tests/ipc/ut-ipc.cpp index ab920fe..8bb829e 100644 --- a/tests/unit_tests/ipc/ut-ipc.cpp +++ b/tests/unit_tests/ipc/ut-ipc.cpp @@ -159,15 +159,10 @@ struct EmptyData { struct ThrowOnAcceptData { template - void accept(Visitor) + static void accept(Visitor) { throw std::runtime_error("intentional failure in accept"); } - template - void accept(Visitor) const - { - throw std::runtime_error("intentional failure in accept const"); - } }; void returnEmptyCallback(const PeerID, diff --git a/tests/unit_tests/lxc/ut-zone.cpp b/tests/unit_tests/lxc/ut-zone.cpp index 6702773..80c003f 100644 --- a/tests/unit_tests/lxc/ut-zone.cpp +++ b/tests/unit_tests/lxc/ut-zone.cpp @@ -60,7 +60,7 @@ struct Fixture { cleanup(); } - void cleanup() + static void cleanup() { LxcZone lxc(ZONE_PATH, ZONE_NAME); if (lxc.isDefined()) { @@ -71,7 +71,7 @@ struct Fixture { } } - void waitForInit() + static void waitForInit() { // wait for init fully started (wait for bash to be able to trap SIGTERM) std::this_thread::sleep_for(std::chrono::milliseconds(200)); diff --git a/tests/unit_tests/server/ut-server.cpp b/tests/unit_tests/server/ut-server.cpp index e4d1ce4..79a7d72 100644 --- a/tests/unit_tests/server/ut-server.cpp +++ b/tests/unit_tests/server/ut-server.cpp @@ -63,7 +63,7 @@ struct Fixture { LOGI("------------ setup complete -----------"); } - void prepare() + static void prepare() { ScopedGlibLoop loop; ipc::epoll::ThreadDispatcher mDispatcher; diff --git a/tests/unit_tests/server/ut-zone-provision.cpp b/tests/unit_tests/server/ut-zone-provision.cpp index 45a74d7..034552e 100644 --- a/tests/unit_tests/server/ut-zone-provision.cpp +++ b/tests/unit_tests/server/ut-zone-provision.cpp @@ -69,7 +69,7 @@ struct Fixture { BOOST_REQUIRE(utils::saveFileContent(SOME_FILE_PATH.string(), "text")); } - ZoneProvision create(const std::vector& validLinkPrefixes) + static ZoneProvision create(const std::vector& validLinkPrefixes) { return ZoneProvision(ROOTFS_PATH.string(), TEST_CONFIG_PATH, @@ -78,12 +78,12 @@ struct Fixture { validLinkPrefixes); } - void load(ZoneProvisioningConfig& config) + static void load(ZoneProvisioningConfig& config) { config::loadFromKVStoreWithJsonFile(DB_PATH.string(), TEST_CONFIG_PATH, config, DB_PREFIX); } - void save(const ZoneProvisioningConfig& config) + static void save(const ZoneProvisioningConfig& config) { config::saveToKVStore(DB_PATH.string(), config, DB_PREFIX); } diff --git a/tests/unit_tests/server/ut-zone.cpp b/tests/unit_tests/server/ut-zone.cpp index e492c37..0072d74 100644 --- a/tests/unit_tests/server/ut-zone.cpp +++ b/tests/unit_tests/server/ut-zone.cpp @@ -102,12 +102,12 @@ struct Fixture { } - void ensureStarted() + static void ensureStarted() { // wait for zones init to fully start std::this_thread::sleep_for(std::chrono::milliseconds(200)); } - void ensureStop() + static void ensureStop() { // wait for fully stop std::this_thread::sleep_for(std::chrono::milliseconds(200)); diff --git a/wrapper/wrapper-compatibility.cpp b/wrapper/wrapper-compatibility.cpp index 634c187..04c043d 100644 --- a/wrapper/wrapper-compatibility.cpp +++ b/wrapper/wrapper-compatibility.cpp @@ -342,10 +342,9 @@ API int wait_for_pid_status(pid_t pid) API vsm_fso_type_t fso_string_to_type(char *str) { LOGS(""); - int len; int i; for (i = 0; i <= VSM_FSO_MAX_TYPE; i++) { - len = strlen(fso_type_strtab[i]); + int len = strlen(fso_type_strtab[i]); if (strncmp(str, fso_type_strtab[i], len) == 0) return static_cast(i); } @@ -358,12 +357,11 @@ API int mkdir_p(const char *dir, mode_t mode) LOGS(""); const char *tmp = dir; const char *orig = dir; - char *makeme; do { dir = tmp + strspn(tmp, "/"); tmp = dir + strcspn(dir, "/"); - makeme = strndup(orig, dir - orig); + char *makeme = strndup(orig, dir - orig); if (*makeme) { if (mkdir(makeme, mode) && errno != EEXIST) { free(makeme); @@ -421,8 +419,6 @@ API int remove_file(char *path) { LOGS(""); struct stat path_stat; - DIR *dp; - struct dirent *d; int status = 0; if (lstat(path, &path_stat) < 0) { @@ -433,6 +429,8 @@ API int remove_file(char *path) } if (S_ISDIR(path_stat.st_mode)) { + struct dirent *d; + DIR *dp; if ((dp = opendir(path)) == NULL) { ERROR("Unable to opendir %s", path); return -1; @@ -475,7 +473,6 @@ API int copy_file(const char *source, const char *dest, int /*flags*/) LOGS(""); int ret; FILE *sfp, *dfp; - size_t nread, nwritten, size = BUF_SIZE; char buffer[BUF_SIZE]; if ((sfp = fopen(source, "r")) == NULL) { @@ -490,6 +487,7 @@ API int copy_file(const char *source, const char *dest, int /*flags*/) } while (1) { + size_t nread, nwritten, size = BUF_SIZE; nread = fread(buffer, 1, size, sfp); if (nread != size && ferror(sfp)) { @@ -840,7 +838,7 @@ API pid_t get_init_pid(const char *name) fp = fopen(filename, "r"); if (fp != NULL) { - if (fscanf(fp, "%d", &ret) < 0) { + if (fscanf(fp, "%7d", &ret) < 0) { ERROR("Failed to read %s\n", filename); ret = -2; } @@ -894,7 +892,7 @@ API pid_t get_zone_pid(const char *name, const char *target) FILE *cmdfp; char cmdpath[PATH_MAX]; - res = sscanf(line, "%d", &pid); + res = sscanf(line, "%7d", &pid); if (res != 1) { ERROR("Failed to read %s\n", path); res = -1; @@ -915,7 +913,7 @@ API pid_t get_zone_pid(const char *name, const char *target) continue; } - if (fscanf(cmdfp, "%s", cmd) < 0) { + if (fscanf(cmdfp, "%1023s", cmd) < 0) { ERROR("Failed to read cmdline - pid : %d\n", pid); continue; } @@ -1271,11 +1269,12 @@ static int parse_statement(struct parser_context *ctx, int argc, char **argv, { struct parser_state state; char *args[PARSER_MAXARGS]; - int i, nargs, done, rc; + int i; int ret = 0; UNUSED(ctx); for (i = 0; i < argc; i++) { + int nargs, done, rc; done = nargs = 0; parser_init_state(&state, argv[i]); diff --git a/zone-daemon/daemon.cpp b/zone-daemon/daemon.cpp index 0ded40a..c4979e0 100644 --- a/zone-daemon/daemon.cpp +++ b/zone-daemon/daemon.cpp @@ -45,21 +45,23 @@ Daemon::~Daemon() { } -void Daemon::onNameLostCallback() +void Daemon::onNameLostCallback() const { + (void)this; // satisfy cpp-check //TODO: Try to reconnect or close the daemon. LOGE("Dbus name lost"); } -void Daemon::onGainFocusCallback() +void Daemon::onGainFocusCallback() const { + (void)this; // satisfy cpp-check LOGD("Gained Focus"); } -void Daemon::onLoseFocusCallback() +void Daemon::onLoseFocusCallback() const { + (void)this; // satisfy cpp-check LOGD("Lost Focus"); - } } // namespace zone_daemon diff --git a/zone-daemon/daemon.hpp b/zone-daemon/daemon.hpp index 92dcccf..ab52e69 100644 --- a/zone-daemon/daemon.hpp +++ b/zone-daemon/daemon.hpp @@ -40,9 +40,9 @@ public: virtual ~Daemon(); private: - void onNameLostCallback(); - void onGainFocusCallback(); - void onLoseFocusCallback(); + void onNameLostCallback() const; + void onGainFocusCallback() const; + void onLoseFocusCallback() const; std::unique_ptr mConnectionPtr; }; diff --git a/zone-daemon/runner.hpp b/zone-daemon/runner.hpp index 1a88530..1827553 100644 --- a/zone-daemon/runner.hpp +++ b/zone-daemon/runner.hpp @@ -39,13 +39,13 @@ public: /** * Starts all the daemon and blocks until SIGINT or SIGTERM */ - void run(); + static void run(); /** * Terminates the daemon. * Equivalent of sending SIGINT or SIGTERM signal */ - void terminate(); + static void terminate(); }; -- 2.7.4 From 36dfc7551f4fca721d364638b1a6191908ce34dd Mon Sep 17 00:00:00 2001 From: Krzysztof Dynowski Date: Thu, 30 Jul 2015 14:04:25 +0200 Subject: [PATCH 07/16] Support getting list of ip/mask for one interface, change netdev_set_ip* to netdev_add_ip* [Feature] Get list of configured ip/mask for zone interface [Cause] Many ip/mask entries can be assigned to one interface [Solution] Implement generic method getting list of network addresses [Verification] Build, install, use vsm net-add-ip to add ips vsm net-list to get configured ips Change-Id: I8d4c1b59e03800aa513811992cc13e71df8d599e --- cli/command-line-interface.cpp | 32 ++++++++------------ client/vasum-client-impl.cpp | 68 ++++++++++++++++++++++++++++-------------- client/vasum-client-impl.hpp | 29 +++++++++++++----- client/vasum-client.cpp | 49 +++++++++++++++++++++++++++--- client/vasum-client.h | 61 ++++++++++++++++++++++++++++++++++--- 5 files changed, 181 insertions(+), 58 deletions(-) diff --git a/cli/command-line-interface.cpp b/cli/command-line-interface.cpp index e8922ee..86bc808 100644 --- a/cli/command-line-interface.cpp +++ b/cli/command-line-interface.cpp @@ -562,8 +562,6 @@ void netdev_list(const Args& argv) } else { VsmNetdev netdev = NULL; - in_addr ipv4; - in6_addr ipv6; char buf[INET_ADDRSTRLEN|INET6_ADDRSTRLEN]; CommandLineInterface::executeCallback(bind(vsm_lookup_netdev_by_name, _1, @@ -573,25 +571,21 @@ void netdev_list(const Args& argv) cout << netdevToString(netdev) << endl; vsm_netdev_free(netdev); - CommandLineInterface::executeCallback(bind(vsm_netdev_get_ipv4_addr, + VsmAddrList addrs = NULL; + CommandLineInterface::executeCallback(bind(vsm_netdev_get_ip_addr, _1, argv[1].c_str(), argv[2].c_str(), - &ipv4)); - if (inet_ntop(AF_INET, &ipv4, buf, INET_ADDRSTRLEN) == NULL) { - throw runtime_error("Wrong address received"); + &addrs)); + unsigned listsize = vsm_addrlist_size(addrs); + for (unsigned i=0; i < listsize; ++i) { + int type=vsm_addrlist_get_type(addrs, i); + if (inet_ntop(type, vsm_addrlist_get_addr(addrs, i), buf, INET6_ADDRSTRLEN) == NULL) { + throw runtime_error("Wrong address received ["+std::to_string(i)+"] type="+std::to_string(type)); + } + cout << buf << "/" << vsm_addrlist_get_prefix(addrs, i) << endl; } - cout << buf << endl; - - CommandLineInterface::executeCallback(bind(vsm_netdev_get_ipv6_addr, - _1, - argv[1].c_str(), - argv[2].c_str(), - &ipv6)); - if (inet_ntop(AF_INET6, &ipv6, buf, INET6_ADDRSTRLEN) == NULL) { - throw runtime_error("Wrong address received"); - } - cout << buf << endl; + vsm_addrlist_free(addrs); } } @@ -606,7 +600,7 @@ void netdev_add_ip_addr(const Args& argv) if (inet_pton(AF_INET, argv[3].c_str(), &addr) != 1) { throw runtime_error("Wrong address format"); }; - CommandLineInterface::executeCallback(bind(vsm_netdev_set_ipv4_addr, + CommandLineInterface::executeCallback(bind(vsm_netdev_add_ipv4_addr, _1, argv[1].c_str(), argv[2].c_str(), @@ -618,7 +612,7 @@ void netdev_add_ip_addr(const Args& argv) if (inet_pton(AF_INET6, argv[3].c_str(), &addr) != 1) { throw runtime_error("Wrong address format"); }; - CommandLineInterface::executeCallback(bind(vsm_netdev_set_ipv6_addr, + CommandLineInterface::executeCallback(bind(vsm_netdev_add_ipv6_addr, _1, argv[1].c_str(), argv[2].c_str(), diff --git a/client/vasum-client-impl.cpp b/client/vasum-client-impl.cpp index 15cccf6..e3aca1d 100644 --- a/client/vasum-client-impl.cpp +++ b/client/vasum-client-impl.cpp @@ -525,42 +525,48 @@ VsmStatus Client::vsm_zone_get_netdevs(const char* id, VsmArrayString* netdevIds VsmStatus Client::vsm_netdev_get_ip_addr(const char* id, const char* netdevId, - int type, - void* addr) noexcept + std::vector& addrs) noexcept { using namespace boost::algorithm; return coverException([&] { IS_SET(id); IS_SET(netdevId); - IS_SET(addr); + + addrs.clear(); api::GetNetDevAttrs attrs = *mClient->callSync( api::ipc::METHOD_GET_NETDEV_ATTRS, std::make_shared(api::GetNetDevAttrsIn{ id, netdevId })); - auto it = find_if(attrs.values.begin(), - attrs.values.end(), - [type](const api::StringPair& entry) { - return entry.first == (type == AF_INET ? "ipv4" : "ipv6"); - }); + for (const auto &attr : attrs.values) { + InetAddr addr; + if (attr.first == "ipv4") { + addr.type = AF_INET; + } + else if (attr.first == "ipv6") { + addr.type = AF_INET6; + } + else continue; - if (it != attrs.values.end()) { - vector addrAttrs; - for(auto addrAttr : split(addrAttrs, it->second, is_any_of(","))) { + std::vector addrAttrs; + for(const auto& addrAttr : split(addrAttrs, attr.second, is_any_of(","))) { size_t pos = addrAttr.find(":"); - if (addrAttr.substr(0, pos) == "ip") { - if (pos != string::npos && pos < addrAttr.length() && - inet_pton(type, addrAttr.substr(pos + 1).c_str(), addr) == 1) { - //XXX: return only one address - return; - } else { - throw InvalidResponseException("Wrong address format returned"); + if (pos == string::npos) continue; + + if (addrAttr.substr(0, pos) == "prefixlen") { + addr.prefix = atoi(addrAttr.substr(pos + 1).c_str()); + } + else if (addrAttr.substr(0, pos) == "ip") { + if (inet_pton(addr.type, addrAttr.substr(pos + 1).c_str(), &addr.addr) != 1) { + addr.type = -1; + break; } } } + if (addr.type >= 0) + addrs.push_back(addr); } - throw OperationFailedException("Address not found"); }); } @@ -568,17 +574,33 @@ VsmStatus Client::vsm_netdev_get_ipv4_addr(const char* id, const char* netdevId, struct in_addr* addr) noexcept { - return vsm_netdev_get_ip_addr(id, netdevId, AF_INET, addr); + std::vector addrs; + VsmStatus st=vsm_netdev_get_ip_addr(id, netdevId, addrs); + for (const auto& a : addrs) { + if (a.type == AF_INET) { + memcpy(addr, &a.addr, sizeof(*addr)); + break; + } + } + return st; } VsmStatus Client::vsm_netdev_get_ipv6_addr(const char* id, const char* netdevId, struct in6_addr* addr) noexcept { - return vsm_netdev_get_ip_addr(id, netdevId, AF_INET6, addr); + std::vector addrs; + VsmStatus st=vsm_netdev_get_ip_addr(id, netdevId, addrs); + for (const auto& a : addrs) { + if (a.type == AF_INET6) { + memcpy(addr, &a.addr, sizeof(*addr)); + break; + } + } + return st; } -VsmStatus Client::vsm_netdev_set_ipv4_addr(const char* id, +VsmStatus Client::vsm_netdev_add_ipv4_addr(const char* id, const char* netdevId, struct in_addr* addr, int prefix) noexcept @@ -596,7 +618,7 @@ VsmStatus Client::vsm_netdev_set_ipv4_addr(const char* id, }); } -VsmStatus Client::vsm_netdev_set_ipv6_addr(const char* id, +VsmStatus Client::vsm_netdev_add_ipv6_addr(const char* id, const char* netdevId, struct in6_addr* addr, int prefix) noexcept diff --git a/client/vasum-client-impl.hpp b/client/vasum-client-impl.hpp index 4297c95..dbc9001 100644 --- a/client/vasum-client-impl.hpp +++ b/client/vasum-client-impl.hpp @@ -64,6 +64,19 @@ typedef struct NetdevStructure { } *Netdev; /** + * Network interface information structure + */ +typedef struct { + int type; + int prefix; + union { + struct in_addr ipv4; + struct in6_addr ipv6; + } addr; +} InetAddr; + + +/** * vasum's client definition. * * Client uses dbus API. @@ -227,6 +240,10 @@ public: */ VsmStatus vsm_zone_get_netdevs(const char* zone, VsmArrayString* netdevIds) noexcept; + VsmStatus vsm_netdev_get_ip_addr(const char* zone, + const char* netdevId, + std::vector& addrs) noexcept; + /** * @see ::vsm_netdev_get_ipv4_addr */ @@ -242,17 +259,17 @@ public: struct in6_addr *addr) noexcept; /** - * @see ::vsm_netdev_set_ipv4_addr + * @see ::vsm_netdev_add_ipv4_addr */ - VsmStatus vsm_netdev_set_ipv4_addr(const char* zone, + VsmStatus vsm_netdev_add_ipv4_addr(const char* zone, const char* netdevId, struct in_addr *addr, int prefix) noexcept; /** - * @see ::vsm_netdev_set_ipv6_addr + * @see ::vsm_netdev_add_ipv6_addr */ - VsmStatus vsm_netdev_set_ipv6_addr(const char* zone, + VsmStatus vsm_netdev_add_ipv6_addr(const char* zone, const char* netdevId, struct in6_addr *addr, int prefix) noexcept; @@ -376,10 +393,6 @@ private: bool isInternalDispatcherEnabled() const; ipc::epoll::EventPoll& getEventPoll() const; VsmStatus coverException(const std::function& worker) noexcept; - VsmStatus vsm_netdev_get_ip_addr(const char* zone, - const char* netdevId, - int type, - void* addr) noexcept; }; #endif /* VASUM_CLIENT_IMPL_HPP */ diff --git a/client/vasum-client.cpp b/client/vasum-client.cpp index ee3e6d4..9cf15b3 100644 --- a/client/vasum-client.cpp +++ b/client/vasum-client.cpp @@ -279,6 +279,21 @@ API VsmStatus vsm_zone_get_netdevs(VsmClient client, return getClient(client).vsm_zone_get_netdevs(zone, netdevIds); } +API VsmStatus vsm_netdev_get_ip_addr(VsmClient client, + const char* zone, + const char* netdevId, + VsmAddrList *addrs) +{ + std::vector addrlist; + VsmStatus status = getClient(client).vsm_netdev_get_ip_addr(zone, netdevId, addrlist); + int n = addrlist.size(); + InetAddr *a = (InetAddr *)malloc((n+1)*sizeof(InetAddr)); + std::copy(addrlist.begin(), addrlist.end(), a); + a[n].type=-1; + *addrs = a; + return status; +} + API VsmStatus vsm_netdev_get_ipv4_addr(VsmClient client, const char* zone, const char* netdevId, @@ -295,22 +310,22 @@ API VsmStatus vsm_netdev_get_ipv6_addr(VsmClient client, return getClient(client).vsm_netdev_get_ipv6_addr(zone, netdevId, addr); } -API VsmStatus vsm_netdev_set_ipv4_addr(VsmClient client, +API VsmStatus vsm_netdev_add_ipv4_addr(VsmClient client, const char* zone, const char* netdevId, struct in_addr *addr, int prefix) { - return getClient(client).vsm_netdev_set_ipv4_addr(zone, netdevId, addr, prefix); + return getClient(client).vsm_netdev_add_ipv4_addr(zone, netdevId, addr, prefix); } -API VsmStatus vsm_netdev_set_ipv6_addr(VsmClient client, +API VsmStatus vsm_netdev_add_ipv6_addr(VsmClient client, const char* zone, const char* netdevId, struct in6_addr *addr, int prefix) { - return getClient(client).vsm_netdev_set_ipv6_addr(zone, netdevId, addr, prefix); + return getClient(client).vsm_netdev_add_ipv6_addr(zone, netdevId, addr, prefix); } API VsmStatus vsm_netdev_del_ipv4_addr(VsmClient client, @@ -430,3 +445,29 @@ API VsmStatus vsm_clean_up_zones_root(VsmClient client) return getClient(client).vsm_clean_up_zones_root(); } +API unsigned int vsm_addrlist_size(VsmAddrList addrs) +{ + InetAddr *a = static_cast(addrs); + unsigned int i; + for (i = 0; a[i].type >= 0; ++i) ; + return i; +} + +API int vsm_addrlist_get_type(VsmAddrList addrs, unsigned int i) +{ + return static_cast(addrs)[i].type; +} + +API const void *vsm_addrlist_get_addr(VsmAddrList addrs, unsigned int i) +{ + return &static_cast(addrs)[i].addr; +} + +API unsigned int vsm_addrlist_get_prefix(VsmAddrList addrs, unsigned int i) +{ + return static_cast(addrs)[i].prefix; +} + +API void vsm_addrlist_free(VsmAddrList addrs) { + free(addrs); +} diff --git a/client/vasum-client.h b/client/vasum-client.h index b696dcf..253306d 100644 --- a/client/vasum-client.h +++ b/client/vasum-client.h @@ -186,6 +186,7 @@ typedef char* VsmString; */ typedef VsmString* VsmArrayString; +typedef void *VsmAddrList; /** * Completion status of libvasum-client's functions */ @@ -652,6 +653,29 @@ VsmStatus vsm_revoke_device(VsmClient client, const char* zone, const char* devi */ VsmStatus vsm_zone_get_netdevs(VsmClient client, const char* zone, VsmArrayString* netdevIds); + +/** + * Get ipv4 address for given netdevId + * + * @param[in] client vasum-server's client + * @param[in] zone zone name + * @param[in] netdevId netdev id + * @param[out] addrs ip address array + * @return status of this function call + * @remark Use vsm_netdev_addr_free() to free memory occupied by address array. + */ +VsmStatus vsm_netdev_get_ip_addr(VsmClient client, + const char* zone, + const char* netdevId, + VsmAddrList *addrs); + +/** + * Release VsmAddrList + * + * @param addrs VsmAddrList + */ +void vsm_addrlist_free(VsmAddrList addrs); + /** * Get ipv4 address for given netdevId * @@ -681,7 +705,7 @@ VsmStatus vsm_netdev_get_ipv6_addr(VsmClient client, struct in6_addr *addr); /** - * Set ipv4 address for given netdevId + * Add ipv4 address for given netdevId * * @param[in] client vasum-server's client * @param[in] zone zone name @@ -690,14 +714,14 @@ VsmStatus vsm_netdev_get_ipv6_addr(VsmClient client, * @param[in] prefix bit-length of the network prefix * @return status of this function call */ -VsmStatus vsm_netdev_set_ipv4_addr(VsmClient client, +VsmStatus vsm_netdev_add_ipv4_addr(VsmClient client, const char* zone, const char* netdevId, struct in_addr *addr, int prefix); /** - * Set ipv6 address for given netdevId + * Add ipv6 address for given netdevId * * @param[in] client vasum-server's client * @param[in] zone zone name @@ -706,7 +730,7 @@ VsmStatus vsm_netdev_set_ipv4_addr(VsmClient client, * @param[in] prefix bit-length of the network prefix * @return status of this function call */ -VsmStatus vsm_netdev_set_ipv6_addr(VsmClient client, +VsmStatus vsm_netdev_add_ipv6_addr(VsmClient client, const char* zone, const char* netdevId, struct in6_addr *addr, @@ -933,6 +957,35 @@ VsmStatus vsm_remove_declaration(VsmClient client, */ VsmStatus vsm_clean_up_zones_root(VsmClient client); +/** + * Retrieve array size + * + * @return array size + */ +unsigned int vsm_addrlist_size(VsmAddrList addrs); + +/** + * Get address type for i'th entry + * + * @return network type (AF_INET or AF_INET6) + */ +int vsm_addrlist_get_type(VsmAddrList addrs, unsigned int i); + +/** + * Get pointer to in_addr property for i'th entry + * see inet_ntop man pages + * + * @return poiner of in_addr + */ +const void *vsm_addrlist_get_addr(VsmAddrList addrs, unsigned int i); + +/** + * Get address prefix for i'th entry + * + * @return adress prefix (mask bits count) + */ +unsigned int vsm_addrlist_get_prefix(VsmAddrList addrs, unsigned int i); + #endif /* __VASUM_WRAPPER_SOURCE__ */ #ifdef __cplusplus -- 2.7.4 From 2e5e4e040a25202b5fdc868b24f0c2e0ba183662 Mon Sep 17 00:00:00 2001 From: Krzysztof Dynowski Date: Fri, 7 Aug 2015 11:05:14 +0200 Subject: [PATCH 08/16] vsm: persistant history of entered commands in interactive mode [Feature] Keep history of entered commands (in ~/.vsm_history file) [Cause] No history after starting vsm [Solution] use read_history/write_history from readline library [Verification] Build, install, use vsm command Change-Id: I107ba49ac01d52825312416a11fbb094735401af --- cli/main.cpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/cli/main.cpp b/cli/main.cpp index 9bec3e7..48a71f2 100644 --- a/cli/main.cpp +++ b/cli/main.cpp @@ -36,12 +36,15 @@ #include #include #include +#include #include #include using namespace vasum::cli; +namespace fs = boost::filesystem; + namespace { static int interactiveMode = 0; @@ -480,6 +483,12 @@ int cliMode(const int argc, const char** argv) return rc; } +fs::path getHomePath() { + const char *h = ::getenv("HOME"); + return fs::path(h ? h : ""); +} + + } // namespace @@ -508,13 +517,24 @@ int main(const int argc, const char *argv[]) } } else { + fs::path historyfile(".vsm_history"); if (isatty(0) == 1) { + fs::path home = getHomePath(); + if (!home.empty()) { + historyfile = home / historyfile; + } + ::read_history(historyfile.c_str()); + interactiveMode = 1; ::rl_attempted_completion_function = completion; } rc = processStream(std::cin); + + if (interactiveMode) { + ::write_history(historyfile.c_str()); + } } disconnect(); -- 2.7.4 From ec1876ec7d820ad1cd34952f5e63e1592c17cf65 Mon Sep 17 00:00:00 2001 From: Krzysztof Dynowski Date: Wed, 19 Aug 2015 12:51:22 +0200 Subject: [PATCH 09/16] vsm: make vsm> prompt, finish prompt line on exit in interactive mode [Feature] 'vsm>' prompt, new line when exit [Cause] prompt gives clear info what is the interpreter [Solution] N/A [Verification] Build, install, use vsm command Change-Id: Ic5b6751dbebc38d204565311380d6459448d25a0 --- cli/main.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cli/main.cpp b/cli/main.cpp index 48a71f2..739d293 100644 --- a/cli/main.cpp +++ b/cli/main.cpp @@ -390,7 +390,7 @@ static int processStream(std::istream& stream) int rc = EXIT_FAILURE; std::string ln; - while (readline_from(">>> ", stream, ln)) { + while (readline_from("vsm> ", stream, ln)) { if (ln.empty() || ln[0] == '#') { //skip empty line or comment continue; } @@ -534,6 +534,7 @@ int main(const int argc, const char *argv[]) if (interactiveMode) { ::write_history(historyfile.c_str()); + std::cout << std::endl; // finish prompt line } } -- 2.7.4 From e17105e146dd179c4f2cb756022cc995ad4cddec Mon Sep 17 00:00:00 2001 From: Krzysztof Dynowski Date: Thu, 20 Aug 2015 16:10:52 +0200 Subject: [PATCH 10/16] vsm: update network related help, formating changes [Feature] clarify help for net-create command [Cause] poor help on how to create virtual network [Solution] extend description format, inerpretation [Verification] Build, install, run vsm net-create help Change-Id: I09b877d090607fb9f8d3c5b367f6f6b2d1a7ed85 --- cli/command-line-interface.cpp | 37 +++++++++++++++++++++++++++--------- cli/main.cpp | 43 +++++++++++++++++++++++++++--------------- 2 files changed, 56 insertions(+), 24 deletions(-) diff --git a/cli/command-line-interface.cpp b/cli/command-line-interface.cpp index 86bc808..93201c3 100644 --- a/cli/command-line-interface.cpp +++ b/cli/command-line-interface.cpp @@ -143,6 +143,18 @@ void buildZoneList(std::vector& list) vsm_array_string_free(ids); } +void buildNetdevList(const std::string& zone,std::vector& list) +{ + using namespace std::placeholders; + VsmArrayString ids; + + CommandLineInterface::executeCallback(bind(vsm_zone_get_netdevs, _1, zone.c_str(), &ids)); + for (VsmString* id = ids; *id; ++id) { + list.push_back(*id); + } + vsm_array_string_free(ids); +} + } // namespace const std::vector CommandLineInterface::buildCompletionList(const Args& a) const @@ -165,9 +177,7 @@ const std::vector CommandLineInterface::buildCompletionList(const A buildZoneList(v); } else if (ss == "{NETDEV}") { - //TODO: get list of available interfaces - v.push_back("lo"); - v.push_back("eth0"); + buildNetdevList(a[a.size()-2],v); // zone name must precede netdev } else if (ss.length() > 0) { v.push_back(ss); @@ -242,19 +252,28 @@ const std::string& CommandLineInterface::getDescription() const void CommandLineInterface::printUsage(std::ostream& out) const { - out << mName; + out << "Syntax\n"; + out << "\t" << mName; for (const auto& args : mArgsSpec) { out << " " << args.name; } out << "\n\n" - << "\tDescription\n" - << "\t\t" << mDescription << "\n"; + "Description\n" + "\t" << mDescription << "\n"; if (!mArgsSpec.empty()) { - out << "\n\tOptions\n"; + out << "\n" + "Options\n"; for (const auto& args : mArgsSpec) { - out << "\t\t" << args.name << " -- " << args.description << "\n"; + out << "\t" << args.name << " -- "; + const std::string& d=args.description; + std::stringstream ss(d); + std::string item; + std::getline(ss, item); + out << item << std::endl; + while (std::getline(ss, item)) + out << "\t\t" << item << std::endl; } } out << "\n"; @@ -484,7 +503,7 @@ void create_netdev(const Args& argv) { using namespace std::placeholders; - if (argv.size() < 2) { + if (argv.size() < 3) { throw runtime_error("Not enough parameters"); } diff --git a/cli/main.cpp b/cli/main.cpp index 739d293..528c4c8 100644 --- a/cli/main.cpp +++ b/cli/main.cpp @@ -153,14 +153,24 @@ std::vector commands = { { create_netdev, "net-create", - "Create network interface in zone", + "Create network virtualization for the zone", MODE_COMMAND_LINE | MODE_INTERACTIVE, { {"zone_id", "zone name", "{ZONE}"}, - {"netdevtype", "interface type", "macvlan|phys|veth"}, + {"netdevtype", "interface type (veth, macvlan, phys)\n" + " veth - create new zone iface and bridge to host\n" + "macvlan - create new zone slave iface briged to master with specified mode\n" + " phys - move existing iface from host to zone (no way to move it back)", + "veth|macvlan|phys" + }, {"zone_netdev", "interface name (eth0)", "eth0|eth1"}, {"host_netdev", "bridge name (virbr0)", "virbr0|virbr1"}, - {"mode", "macvlan mode (private, vepa, bridge, passthru)", "private|vepa|bridge|passthru"}} + {"mode", "macvlan mode (private, vepa, bridge, passthru)\n" + " private - bridge but no comunicate with otheri vlan\n" + " vepa - ethernet switch\n" + " bridge - light weight to other vlan\n" + "passthru - only one vlan device", + "private|vepa|bridge|passthru"}} }, { destroy_netdev, @@ -242,14 +252,14 @@ void printUsage(std::ostream& out, const std::string& name, unsigned int mode) n = name + " "; } + out << "Usage: " << n << "[-h|help|-f |[ [-h|help|]]]\n\n"; if (mode == MODE_COMMAND_LINE) { - out << "Usage: " << n << "[-h|-f |[ [-h|]]\n" - << "Called without parameters enters interactive mode.\n" + out << "Description:\n" + << "\tCommand line tool to manage vasum containers.\n" + << "\tCalled without parameters enters interactive mode.\n" << "Options:\n" - << "-h print help\n" - << "-f read and execute commands from file\n\n"; - } else { - out << "Usage: [-h| [-h|]]\n\n"; + << "\t-h,help print this help\n" + << "\t-f read and execute commands from file\n\n"; } out << "command can be one of the following:\n"; @@ -258,12 +268,16 @@ void printUsage(std::ostream& out, const std::string& name, unsigned int mode) if (std::find(addLineBefore.begin(), addLineBefore.end(), command.getName()) != addLineBefore.end()) { out << std::endl; } - out << " " << std::setw(25) << std::left << command.getName() - << command.getDescription() << std::endl; + out << " " << std::setw(20) << std::left << command.getName(); + const std::string& d = command.getDescription(); + std::stringstream ss(d); + std::string item; + std::getline(ss, item); + out << item << std::endl; } } - out << "\nSee " << n << "command -h to read about a specific one.\n"; + out << "\nType '" << n << "command help' to read about a specific one.\n"; } int connect() @@ -303,8 +317,7 @@ int executeCommand(const Args& argv, int mode) return EXIT_FAILURE; } - auto it = std::find(argv.begin(), argv.end(), std::string("-h")); - if (it != argv.end()) { + if (argv.size() > 1 && (argv[1] == "-h" || argv[1] == "help")) { command.printUsage(std::cout); return EXIT_SUCCESS; } @@ -466,7 +479,7 @@ int bashComplMode(int argc, const char *argv[]) int cliMode(const int argc, const char** argv) { - if (std::string(argv[1]) == "-h") { + if (std::string(argv[1]) == "-h" || std::string(argv[1]) == "help") { printUsage(std::cout, argv[0], MODE_COMMAND_LINE); return EXIT_SUCCESS; } -- 2.7.4 From f220ce5658986db5cb0a7e67631b28ac41a678fb Mon Sep 17 00:00:00 2001 From: Jan Olszak Date: Tue, 18 Aug 2015 12:02:12 +0200 Subject: [PATCH 11/16] lxcpp: Simple attach implementation [Feature] Running code in the container's context Socketpair wrapper - Channel [Cause] N/A [Solution] N/A [Verification] Build, install, run tests Change-Id: Ib5b1011c5f8578ab9e258bcbea3cd7aa3bc233a3 --- common/utils/channel.cpp | 83 +++++++++++++++++++++++++++ common/utils/channel.hpp | 103 ++++++++++++++++++++++++++++++++++ common/utils/execute.cpp | 2 +- common/utils/fd-utils.cpp | 13 +++++ common/utils/fd-utils.hpp | 5 ++ libs/lxcpp/container-impl.cpp | 58 +++++++++++++++++++ libs/lxcpp/container-impl.hpp | 16 ++++++ libs/lxcpp/container.hpp | 6 ++ libs/lxcpp/process.cpp | 86 ++++++++++++++++++++++------ libs/lxcpp/process.hpp | 11 +++- tests/unit_tests/lxcpp/ut-process.cpp | 44 +++++++-------- tests/unit_tests/utils/ut-channel.cpp | 76 +++++++++++++++++++++++++ 12 files changed, 459 insertions(+), 44 deletions(-) create mode 100644 common/utils/channel.cpp create mode 100644 common/utils/channel.hpp create mode 100644 tests/unit_tests/utils/ut-channel.cpp diff --git a/common/utils/channel.cpp b/common/utils/channel.cpp new file mode 100644 index 0000000..fbb110d --- /dev/null +++ b/common/utils/channel.cpp @@ -0,0 +1,83 @@ +/* +* Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved +* +* Contact: Jan Olszak +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License +*/ + +/** + * @file + * @author Jan Olszak (j.olszak@samsung.com) + * @brief IPC implementation for related processes + */ + +#include "utils/channel.hpp" +#include "utils/exception.hpp" + +#include "logger/logger.hpp" + +#include + +namespace { +const int LEFT = 0; +const int RIGHT = 1; +} + +namespace utils { + +Channel::Channel() + : mSocketIndex(-1) +{ + if (::socketpair(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0, mSockets) < 0) { + const std::string msg = "socketpair() failed: " + + utils::getSystemErrorMessage(); + LOGE(msg); + throw UtilsException(msg); + } +} + +Channel::~Channel() +{ + closeSocket(LEFT); + closeSocket(RIGHT); +} + +void Channel::setLeft() +{ + mSocketIndex = LEFT; + utils::close(mSockets[RIGHT]); + mSockets[RIGHT] = -1; +} + +void Channel::setRight() +{ + mSocketIndex = RIGHT; + utils::close(mSockets[LEFT]); + mSockets[LEFT] = -1; +} + +void Channel::shutdown() +{ + assert(mSocketIndex != -1 && "Channel's end isn't set"); + closeSocket(mSocketIndex); +} + +void Channel::closeSocket(int socketIndex) +{ + utils::shutdown(mSockets[socketIndex]); + utils::close(mSockets[socketIndex]); + mSockets[socketIndex] = -1; +} + +} // namespace utils diff --git a/common/utils/channel.hpp b/common/utils/channel.hpp new file mode 100644 index 0000000..f537121 --- /dev/null +++ b/common/utils/channel.hpp @@ -0,0 +1,103 @@ +/* +* Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved +* +* Contact: Jan Olszak +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License +*/ + +/** + * @file + * @author Jan Olszak (j.olszak@samsung.com) + * @brief IPC implementation for related processes + */ + +#ifndef COMMON_UTILS_CHANNEL_HPP +#define COMMON_UTILS_CHANNEL_HPP + +#include "utils/fd-utils.hpp" +#include + +namespace utils { + +/** + * Channel is implemented with a pair of anonymous sockets + */ +class Channel { +public: + Channel(); + ~Channel(); + + Channel(const Channel&) = delete; + Channel& operator=(const Channel&) = delete; + + /** + * Use the "left" end of the channel + * Closes the "right" end + */ + void setLeft(); + + /** + * Use the "right" end of the channel + * Closes the "left" end + */ + void setRight(); + + /** + * Gracefully shutdown the used end of the channel + */ + void shutdown(); + + /** + * Send the data to the other end of the channel + * + * @param data data to send + */ + template + void write(const Data& data); + + /** + * Receive data of a given type (size) + */ + template + Data read(); + +private: + + void closeSocket(int socketIndex); + + int mSocketIndex; + int mSockets[2]; +}; + +template +void Channel::write(const Data& data) +{ + assert(mSocketIndex != -1 && "Channel's end isn't set"); + + utils::write(mSockets[mSocketIndex], &data, sizeof(Data)); +} + +template +Data Channel::read() +{ + assert(mSocketIndex != -1 && "Channel's end isn't set"); + + Data data; + utils::read(mSockets[mSocketIndex], &data, sizeof(Data)); + return data; +} + +} // namespace utils + +#endif // COMMON_UTILS_CHANNEL_HPP diff --git a/common/utils/execute.cpp b/common/utils/execute.cpp index 55fc912..d047a2e 100644 --- a/common/utils/execute.cpp +++ b/common/utils/execute.cpp @@ -118,7 +118,7 @@ bool executeAndWait(const char* fname, const char* const* argv) bool waitPid(pid_t pid, int& status) { - while (waitpid(pid, &status, 0) == -1) { + while (::waitpid(pid, &status, 0) == -1) { if (errno != EINTR) { LOGE("waitpid() failed: " << getSystemErrorMessage()); return false; diff --git a/common/utils/fd-utils.cpp b/common/utils/fd-utils.cpp index 4fe9a17..030d1fc 100644 --- a/common/utils/fd-utils.cpp +++ b/common/utils/fd-utils.cpp @@ -111,6 +111,19 @@ void close(int fd) } } +void shutdown(int fd) +{ + if (fd < 0) { + return; + } + + if (-1 == ::shutdown(fd, SHUT_RDWR)) { + std::string msg = "shutdown() failed: " + getSystemErrorMessage(); + LOGE(msg); + throw UtilsException(msg); + } +} + void write(int fd, const void* bufferPtr, const size_t size, int timeoutMS) { chr::high_resolution_clock::time_point deadline = chr::high_resolution_clock::now() + diff --git a/common/utils/fd-utils.hpp b/common/utils/fd-utils.hpp index d6a4032..56e3f41 100644 --- a/common/utils/fd-utils.hpp +++ b/common/utils/fd-utils.hpp @@ -35,6 +35,11 @@ namespace utils { void close(int fd); /** + * Shut down part of a full-duplex connection + */ +void shutdown(int fd); + +/** * Write to a file descriptor, throw on error. * * @param fd file descriptor diff --git a/libs/lxcpp/container-impl.cpp b/libs/lxcpp/container-impl.cpp index e4f290d..6fd9775 100644 --- a/libs/lxcpp/container-impl.cpp +++ b/libs/lxcpp/container-impl.cpp @@ -23,6 +23,11 @@ #include "lxcpp/container-impl.hpp" #include "lxcpp/exception.hpp" +#include "lxcpp/process.hpp" + +#include "utils/exception.hpp" + +#include namespace lxcpp { @@ -94,4 +99,57 @@ std::string ContainerImpl::getRootPath() throw NotImplementedException(); } + +int ContainerImpl::attachChild(void* data) { + try { + return (*static_cast(data))(); + } catch(...) { + return -1; // Non-zero on failure + } + return 0; // Success +} + +void ContainerImpl::attachParent(utils::Channel& channel, const pid_t interPid) +{ + // TODO: Setup cgroups etc + pid_t childPid = channel.read(); + + // Wait for the Intermediate process + lxcpp::waitpid(interPid); + + // Wait for the Child process + lxcpp::waitpid(childPid); +} + +void ContainerImpl::attachIntermediate(utils::Channel& channel, Container::AttachCall& call) +{ + lxcpp::setns(mInitPid, mNamespaces); + + // PID namespace won't affect the returned pid + // CLONE_PARENT: Child's PPID == Caller's PID + const pid_t pid = lxcpp::clone(&ContainerImpl::attachChild, + &call, + CLONE_PARENT); + channel.write(pid); +} + +void ContainerImpl::attach(Container::AttachCall& call) +{ + utils::Channel channel; + + const pid_t interPid = lxcpp::fork(); + if (interPid > 0) { + channel.setLeft(); + attachParent(channel, interPid); + channel.shutdown(); + } else { + channel.setRight(); + attachIntermediate(channel, call); + channel.shutdown(); + ::_exit(0); + } +} + + + } // namespace lxcpp diff --git a/libs/lxcpp/container-impl.hpp b/libs/lxcpp/container-impl.hpp index f5d9547..e236932 100644 --- a/libs/lxcpp/container-impl.hpp +++ b/libs/lxcpp/container-impl.hpp @@ -25,6 +25,9 @@ #define LXCPP_CONTAINER_IMPL_HPP #include "lxcpp/container.hpp" +#include "lxcpp/namespace.hpp" + +#include "utils/channel.hpp" namespace lxcpp { @@ -49,6 +52,19 @@ public: void destroy(); void setRootPath(const std::string& path); std::string getRootPath(); + + // Other + void attach(Container::AttachCall& attachCall); + +private: + + // Methods for different stages of setting up the attachment + void attachParent(utils::Channel& channel, const pid_t pid); + void attachIntermediate(utils::Channel& channel, Container::AttachCall& call); + static int attachChild(void* data); + + pid_t mInitPid; + std::vector mNamespaces; }; } // namespace lxcpp diff --git a/libs/lxcpp/container.hpp b/libs/lxcpp/container.hpp index 74caa51..4f2d697 100644 --- a/libs/lxcpp/container.hpp +++ b/libs/lxcpp/container.hpp @@ -25,11 +25,14 @@ #define LXCPP_CONTAINER_HPP #include +#include namespace lxcpp { class Container { public: + typedef std::function AttachCall; + virtual ~Container() {}; virtual std::string getName() = 0; @@ -48,6 +51,9 @@ public: virtual void destroy() = 0; virtual void setRootPath(const std::string& path) = 0; virtual std::string getRootPath() = 0; + + // Other + virtual void attach(AttachCall& attachCall) = 0; }; } // namespace lxcpp diff --git a/libs/lxcpp/process.cpp b/libs/lxcpp/process.cpp index 68fd62c..b707353 100644 --- a/libs/lxcpp/process.cpp +++ b/libs/lxcpp/process.cpp @@ -36,10 +36,21 @@ namespace lxcpp { +pid_t fork() +{ + pid_t pid = ::fork(); + if (pid < 0) { + const std::string msg = "fork() failed: " + + utils::getSystemErrorMessage(); + LOGE(msg); + throw ProcessSetupException(msg); + } + return pid; +} + pid_t clone(int (*function)(void *), void *args, - const std::vector& namespaces, - const int additionalFlags) + const int flags) { // Won't fail, well known resource name size_t stackSize = ::sysconf(_SC_PAGESIZE); @@ -47,56 +58,66 @@ pid_t clone(int (*function)(void *), // PAGESIZE is enough, it'll exec after this char *stack = static_cast(::alloca(stackSize)); - pid_t pid = ::clone(function, stack + stackSize, toFlag(namespaces) | additionalFlags | SIGCHLD, args); + pid_t pid = ::clone(function, stack + stackSize, flags | SIGCHLD, args); if (pid < 0) { - const std::string msg = utils::getSystemErrorMessage(); - LOGE("clone() failed: " << msg); - throw ProcessSetupException("clone() failed " + msg); + const std::string msg = "clone() failed: " + + utils::getSystemErrorMessage(); + LOGE(msg); + throw ProcessSetupException(msg); } return pid; } -void setns(const std::vector& namespaces) +pid_t clone(int (*function)(void *), + void *args, + const std::vector& namespaces, + const int additionalFlags) { - pid_t pid = ::getpid(); + return clone(function, args, toFlag(namespaces) | additionalFlags); +} +void setns(const pid_t pid, const std::vector& namespaces) +{ int dirFD = ::open(getNsPath(pid).c_str(), O_DIRECTORY | O_CLOEXEC); if(dirFD < 0) { - const std::string msg = utils::getSystemErrorMessage(); - LOGE("open() failed: " << msg); - throw ProcessSetupException("open() failed: " + msg); + const std::string msg = "open() failed: " + + utils::getSystemErrorMessage(); + LOGE(msg); + throw ProcessSetupException(msg); } // Open FDs connected with the requested namespaces std::vector fds(namespaces.size(), -1); for(size_t i = 0; i < namespaces.size(); ++i) { - fds[i] = ::openat(dirFD, toString(namespaces[i]).c_str(), O_RDONLY | O_CLOEXEC); + fds[i] = ::openat(dirFD, + toString(namespaces[i]).c_str(), + O_RDONLY | O_CLOEXEC); if(fds[i] < 0) { - const std::string msg = utils::getSystemErrorMessage(); + const std::string msg = "openat() failed: " + utils::getSystemErrorMessage(); for (size_t j = 0; j < i; ++j) { utils::close(fds[j]); } utils::close(dirFD); - LOGE("openat() failed: " << msg); - throw ProcessSetupException("openat() failed: " + msg); + LOGE(msg); + throw ProcessSetupException(msg); } } // Setns for every namespace for(size_t i = 0; i < fds.size(); ++i) { if(-1 == ::setns(fds[i], toFlag(namespaces[i]))) { - const std::string msg = utils::getSystemErrorMessage(); + const std::string msg = "setns() failed: " + utils::getSystemErrorMessage(); for (size_t j = i; j < fds.size(); ++j) { utils::close(fds[j]); } utils::close(dirFD); - LOGE("setns() failed: " << msg); - throw ProcessSetupException("setns() failed: " + msg); + LOGE(msg); + throw ProcessSetupException(msg); } utils::close(fds[i]); } @@ -104,4 +125,33 @@ void setns(const std::vector& namespaces) utils::close(dirFD); } +int waitpid(const pid_t pid) +{ + int status; + while (-1 == ::waitpid(pid, &status, 0)) { + if (errno == EINTR) { + LOGT("waitpid() interrupted, retrying"); + continue; + } + const std::string msg = "waitpid() failed: " + utils::getSystemErrorMessage(); + LOGE(msg); + throw ProcessSetupException(msg); + } + + // Return child's return status if everything is OK + if (WIFEXITED(status)) { + return WEXITSTATUS(status); + } + + // Something went wrong in the child + std::string msg; + if (WIFSIGNALED(status)) { + msg = "Child killed by signal " + std::to_string(WTERMSIG(status)); + } else { + msg = "Unknown eror in child process"; + } + LOGE(msg); + throw ProcessSetupException(msg); +} + } // namespace lxcpp \ No newline at end of file diff --git a/libs/lxcpp/process.hpp b/libs/lxcpp/process.hpp index 1255589..a640e1a 100644 --- a/libs/lxcpp/process.hpp +++ b/libs/lxcpp/process.hpp @@ -31,12 +31,21 @@ namespace lxcpp { +pid_t fork(); + +pid_t clone(int (*function)(void *), + void *args, + const int flags); + pid_t clone(int (*function)(void *), void *args, const std::vector& namespaces, const int additionalFlags = 0); -void setns(const std::vector& namespaces); +void setns(const pid_t pid, + const std::vector& namespaces); + +int waitpid(const pid_t pid); } // namespace lxcpp diff --git a/tests/unit_tests/lxcpp/ut-process.cpp b/tests/unit_tests/lxcpp/ut-process.cpp index cae893a..662dd82 100644 --- a/tests/unit_tests/lxcpp/ut-process.cpp +++ b/tests/unit_tests/lxcpp/ut-process.cpp @@ -63,8 +63,8 @@ const std::vector NAMESPACES {{ BOOST_AUTO_TEST_CASE(Clone) { - BOOST_CHECK_NO_THROW(clone(clonefn, nullptr, NAMESPACES)); - BOOST_CHECK_NO_THROW(clone(clonefn, nullptr, {Namespace::MNT})); + BOOST_CHECK_NO_THROW(lxcpp::clone(clonefn, nullptr, NAMESPACES)); + BOOST_CHECK_NO_THROW(lxcpp::clone(clonefn, nullptr, {Namespace::MNT})); } BOOST_AUTO_TEST_CASE(Setns) @@ -72,22 +72,20 @@ BOOST_AUTO_TEST_CASE(Setns) const int TEST_PASSED = 0; const int ERROR = 1; - pid_t pid = fork(); - if (pid==-1) { - BOOST_REQUIRE(false); - } else if(pid ==0) { + pid_t pid = lxcpp::fork(); + if (pid == 0) { try { - setns({Namespace::MNT, - Namespace::PID, - Namespace::UTS, - Namespace::IPC, - Namespace::NET - }); - _exit(TEST_PASSED); + lxcpp::setns(::getpid(), {Namespace::MNT, + Namespace::PID, + Namespace::UTS, + Namespace::IPC, + Namespace::NET + }); + ::_exit(TEST_PASSED); } catch(...) { - _exit(ERROR); + ::_exit(ERROR); } - } else if(pid>0) { + } else if (pid > 0) { int status = -1; BOOST_REQUIRE(utils::waitPid(pid, status)); BOOST_REQUIRE(status == TEST_PASSED); @@ -99,19 +97,17 @@ BOOST_AUTO_TEST_CASE(SetnsUserNamespace) const int TEST_PASSED = 0; const int ERROR = -1; - pid_t pid = fork(); - if (pid==-1) { - BOOST_REQUIRE(false); - } else if(pid ==0) { + pid_t pid = lxcpp::fork(); + if (pid == 0) { try { - setns({Namespace::USER}); - _exit(ERROR); + lxcpp::setns(::getpid(), {Namespace::USER}); + ::_exit(ERROR); } catch(ProcessSetupException) { - _exit(TEST_PASSED); + ::_exit(TEST_PASSED); } catch(...) { - _exit(ERROR); + ::_exit(ERROR); } - } else if(pid>0) { + } else if (pid > 0) { int status; BOOST_REQUIRE(utils::waitPid(pid, status)); BOOST_REQUIRE(status == TEST_PASSED); diff --git a/tests/unit_tests/utils/ut-channel.cpp b/tests/unit_tests/utils/ut-channel.cpp new file mode 100644 index 0000000..8c469ee --- /dev/null +++ b/tests/unit_tests/utils/ut-channel.cpp @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * 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 Unit tests of the channel class + */ + +#include "config.hpp" +#include "ut.hpp" + +#include "utils/channel.hpp" +#include "utils/execute.hpp" + +BOOST_AUTO_TEST_SUITE(ChannelSuite) + +using namespace utils; + +BOOST_AUTO_TEST_CASE(ConstructorDestructor) +{ + Channel c; +} + +BOOST_AUTO_TEST_CASE(SetLeftRight) +{ + const int TEST_PASSED = 0; + const int ERROR = 1; + const int DATA = 1234; + + Channel c; + + pid_t pid = ::fork(); + if (pid == -1) { + BOOST_REQUIRE(false); + } + + if (pid == 0) { + try { + c.setLeft(); + c.write(DATA); + c.shutdown(); + ::_exit(TEST_PASSED); + } catch(...) { + ::_exit(ERROR); + } + } + + c.setRight(); + + int recData = c.read(); + + BOOST_REQUIRE(recData == DATA); + + int status = -1; + BOOST_REQUIRE(utils::waitPid(pid, status)); + BOOST_REQUIRE(status == TEST_PASSED); + c.shutdown(); + +} + +BOOST_AUTO_TEST_SUITE_END() -- 2.7.4 From 690e0f29a5c8ede2c6edce6aaa37c4a4d87b0a02 Mon Sep 17 00:00:00 2001 From: Jan Olszak Date: Mon, 24 Aug 2015 11:13:05 +0200 Subject: [PATCH 12/16] lxcpp: Remounting /proc and /sys on attach [Feature] Remounting /proc /sys Filesystem handling functions [Cause] N/A [Solution] N/A [Verification] Build, install, run tests Change-Id: I8015b7a66c1fabab9133ad360e9e7a45c069082c --- libs/lxcpp/container-impl.cpp | 32 +++++++++++ libs/lxcpp/exception.hpp | 5 ++ libs/lxcpp/filesystem.cpp | 122 ++++++++++++++++++++++++++++++++++++++++++ libs/lxcpp/filesystem.hpp | 52 ++++++++++++++++++ libs/lxcpp/process.cpp | 8 +++ libs/lxcpp/process.hpp | 2 + 6 files changed, 221 insertions(+) create mode 100644 libs/lxcpp/filesystem.cpp create mode 100644 libs/lxcpp/filesystem.hpp diff --git a/libs/lxcpp/container-impl.cpp b/libs/lxcpp/container-impl.cpp index 6fd9775..2ac232f 100644 --- a/libs/lxcpp/container-impl.cpp +++ b/libs/lxcpp/container-impl.cpp @@ -24,10 +24,14 @@ #include "lxcpp/container-impl.hpp" #include "lxcpp/exception.hpp" #include "lxcpp/process.hpp" +#include "lxcpp/filesystem.hpp" +#include "lxcpp/namespace.hpp" #include "utils/exception.hpp" #include +#include + namespace lxcpp { @@ -99,9 +103,37 @@ std::string ContainerImpl::getRootPath() throw NotImplementedException(); } +namespace { +void setupMountPoints() +{ + /* TODO: Uncomment when preparing the final attach() version + + // TODO: This unshare should be optional only if we attach to PID/NET namespace, but not MNT. + // Otherwise container already has remounted /proc /sys + lxcpp::unshare(Namespace::MNT); + + if (isMountPointShared("/")) { + // TODO: Handle case when the container rootfs or mount location is MS_SHARED, but not '/' + lxcpp::mount(nullptr, "/", nullptr, MS_SLAVE | MS_REC, nullptr); + } + + if(isMountPoint("/proc")) { + lxcpp::umount("/proc", MNT_DETACH); + lxcpp::mount("none", "/proc", "proc", 0, nullptr); + } + + if(isMountPoint("/sys")) { + lxcpp::umount("/sys", MNT_DETACH); + lxcpp::mount("none", "/sys", "sysfs", 0, nullptr); + } + + */ +} +} // namespace int ContainerImpl::attachChild(void* data) { try { + setupMountPoints(); return (*static_cast(data))(); } catch(...) { return -1; // Non-zero on failure diff --git a/libs/lxcpp/exception.hpp b/libs/lxcpp/exception.hpp index 12ae299..bdb56f7 100644 --- a/libs/lxcpp/exception.hpp +++ b/libs/lxcpp/exception.hpp @@ -46,6 +46,11 @@ struct ProcessSetupException: public Exception { : Exception(message) {} }; +struct FileSystemSetupException: public Exception { + FileSystemSetupException(const std::string& message = "Error during a file system operation") + : Exception(message) {} +}; + struct BadArgument: public Exception { BadArgument(const std::string& message = "Bad argument passed") : Exception(message) {} diff --git a/libs/lxcpp/filesystem.cpp b/libs/lxcpp/filesystem.cpp new file mode 100644 index 0000000..c085861 --- /dev/null +++ b/libs/lxcpp/filesystem.cpp @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * @author Jan Olszak (j.olszak@samsung.com) + * @brief file system handling routines + */ + +#include "lxcpp/filesystem.hpp" +#include "lxcpp/exception.hpp" +#include "lxcpp/process.hpp" + +#include "utils/paths.hpp" +#include "utils/exception.hpp" +#include "logger/logger.hpp" + +#include +#include +#include +#include +#include + + +namespace lxcpp { + +void mount(const std::string& source, + const std::string& target, + const std::string& filesystemtype, + unsigned long mountflags, + const std::string& data) +{ + if (-1 == ::mount(source.c_str(), + target.c_str(), + filesystemtype.c_str(), + mountflags, + data.c_str())) { + const std::string msg = "mount() failed: src:" + source + + ", tgt: " + target + + ", filesystemtype: " + filesystemtype + + ", mountflags: " + std::to_string(mountflags) + + ", data: " + data + + ", msg: " + utils::getSystemErrorMessage(); + LOGE(msg); + throw FileSystemSetupException(msg); + } +} + +void umount(const std::string& path, const int flags) +{ + if (-1 == ::umount2(path.c_str(), flags)) { + const std::string msg = "umount() failed: '" + path + "': " + utils::getSystemErrorMessage(); + LOGD(msg); + throw FileSystemSetupException(msg); + } +} + +bool isMountPoint(const std::string& path) +{ + std::string parentPath = utils::dirName(path); + + struct stat s1, s2; + if (-1 == ::stat(path.c_str(), &s1)) { + const std::string msg = "stat() failed: " + path + ": " + utils::getSystemErrorMessage(); + LOGE(msg); + throw FileSystemSetupException(msg); + } + + if (-1 == ::stat(parentPath.c_str(), &s2)) { + const std::string msg = "stat() failed: " + parentPath + ": " + utils::getSystemErrorMessage(); + LOGE(msg); + throw FileSystemSetupException(msg); + } + + return s1.st_dev != s2.st_dev; +} + +bool isMountPointShared(const std::string& path) +{ + std::ifstream fileStream("/proc/self/mountinfo"); + if (!fileStream.good()) { + const std::string msg = "Failed to open /proc/self/mountinfo"; + LOGE(msg); + throw FileSystemSetupException(msg); + } + + // Find the line corresponding to the path + std::string line; + while (std::getline(fileStream, line).good()) { + std::istringstream iss(line); + auto it = std::istream_iterator(iss); + std::advance(it, 4); + + if (it->compare(path)) { + // Wrong line, different path + continue; + } + + // Right line, check if mount point shared + std::advance(it, 2); + return it->find("shared:") != std::string::npos; + } + + // Path not found + return false; +} + +} // namespace lxcpp \ No newline at end of file diff --git a/libs/lxcpp/filesystem.hpp b/libs/lxcpp/filesystem.hpp new file mode 100644 index 0000000..6833ffc --- /dev/null +++ b/libs/lxcpp/filesystem.hpp @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * @author Jan Olszak (j.olszak@samsung.com) + * @brief file system handling routines + */ + +#ifndef LXCPP_FILESYSTEM_HPP +#define LXCPP_FILESYSTEM_HPP + +#include + +namespace lxcpp { + +void mount(const std::string& source, + const std::string& target, + const std::string& filesystemtype, + unsigned long mountflags, + const std::string& data); + +void umount(const std::string& path, const int flags); + +bool isMountPoint(const std::string& path); + +/** + * Detect whether path is mounted as MS_SHARED. + * Parses /proc/self/mountinfo + * + * @param path mount point + * @return is the mount point shared + */ +bool isMountPointShared(const std::string& path); + +} // namespace lxcpp + +#endif // LXCPP_FILESYSTEM_HPP \ No newline at end of file diff --git a/libs/lxcpp/process.cpp b/libs/lxcpp/process.cpp index b707353..96475d5 100644 --- a/libs/lxcpp/process.cpp +++ b/libs/lxcpp/process.cpp @@ -154,4 +154,12 @@ int waitpid(const pid_t pid) throw ProcessSetupException(msg); } +void unshare(const Namespace ns) +{ + if(-1 == ::unshare(toFlag(ns))) { + const std::string msg = "unshare() failed: " + utils::getSystemErrorMessage(); + LOGE(msg); + throw ProcessSetupException(msg); + } +} } // namespace lxcpp \ No newline at end of file diff --git a/libs/lxcpp/process.hpp b/libs/lxcpp/process.hpp index a640e1a..869e3d6 100644 --- a/libs/lxcpp/process.hpp +++ b/libs/lxcpp/process.hpp @@ -47,6 +47,8 @@ void setns(const pid_t pid, int waitpid(const pid_t pid); +void unshare(const Namespace ns); + } // namespace lxcpp #endif // LXCPP_PROCESS_HPP \ No newline at end of file -- 2.7.4 From 2dd74d74bf3c90da8e225ac75c6a7eb739cf076c Mon Sep 17 00:00:00 2001 From: Lukasz Pawelczyk Date: Fri, 21 Aug 2015 15:27:15 +0200 Subject: [PATCH 13/16] Add ascii-art logo to the readme [Feature] AA logo [Cause] We love it [Verification] Look and admire Change-Id: I1bb6181e708a9a8643d77bd860619880e4ca81a2 --- README.md | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/README.md b/README.md index 9ccd43c..5428a92 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,42 @@ + + .lOX0c0k; ,o00k;' + 'o0WMMW0o;. ,doX0. + .cxk',MMMMMMMMMWNNWWNX0x,:MMMk' + ':loxxxxdoodkNMMMMN oMMMMMMMMMMMMMMMMMMlkMMMMXd' . + ,OMMMMMMMMMMMMMMMMMMMM'.MMMMMMMMMMMMMMMMMW.OMMMMMMMWOo, O0c + .0W:kMMMMMMMMMMMMMMMMMMMl XMMMMMMMMMMMMMMMK.dMMMMMMMMMMMMW;l0;K: + ::;c:lkWMMMMMMMMMMMMMMMMX oMMMMMMMMMMMMMMo.OMMMMMMMMMMMMM0,NMMMM. + .kMWOl,;OMMMMMMMMMMMMMMMo.WMMMMMMMMMMMW,'NMMMMMMMMMMMM0,:WMMMMM' + lWMMMMMMNd:xWMMMMMMMMMMMMM.:MMMMMMMMMMM;cMMMMMMMMMMMWx.;KMMMMMMM. + .lNMMMMMMMMMMMO:lXMMMMMMMMMMM0 oMO;WMMMMModW;OMMMMMMM0c,oNMMMMMMMMMx . + 'oKMMMMMMMMMMMMMMMMK:,xWMMMNWMMMMx .cl;''',c;;':xKXX0ddoxXMMMMMMMMMMMMM; Xd + 'KMMMMMMMMMMMMMMMMMMMMMWd':OWckMX, 'c.:c;,,;ccdoK' kMk;WMMMMMMMMMMMMW. 'NdO + .WMMMMMMMMMMMMMMMMMMMMMMMMMKl:lko ':;xNMMMMMMd0MMKxl.O: ,OK0kdl::cdkkkxdo:xWMMM, + 'N;0MMMWWWMMMMMMMMMMMMMMMMMMMMl .OWKocxWMMMMlMMMMN,dOo lWoXMMWNXXXNMMMMMMMW + KXd,. .dlc:::cxKWMMMMMMMKKMMd .:dKMMMMMMKloKk0cOxXk'kMMWo ':lx0WMMMMMMMMMMMMc + lMMMMMMWKkooolokKNkkMd ;kMMMMMMMMMMMMNodc ,.'d,x0Ox' xldl' .:dKMMMMMMMk + OMMMMMMMMMMMMMNOxolo Wl:.,kkxxxxkkko', cMNMMMM0o;..;dKWNo .lc + .MMMMMMMMMMMMMMMMMMMM. ; oMMMMMMMMMNKd .'lWMMMMMMMMMN0kdlld0XdO + 0MMMMMMMMMMMMMMMMMMMMN. :WMMMMMMMMMKld. dNXdK,cXMMMMMMMMMMMMMMMMX' + lMMMMMMMMMMMMMMMMMMMMMMN. lMMMMMMXkoooxNMMKl:.xd0:,MMMMMMd.:XMMMMMMMMMMMWk, + 'MMMMMMMMMMMMMMMMMMMMMk;Nk KMMM0xdxKWMMMMMMWd,XMNN: WMMMMMMNc :XMMMMMMMNc + kKOMMMWKkdlc:;,''''''''''. l0kd.XMMMMMMMMM0,lWMMMMl 0MMMMMMMMWd.:XMMMMk + o0x0:. cO xMMMMMMW:'XMMMMMMk OMMMMMMMMMMMKloxk; + Od cMMMMMo KMMMMMMMW dMMMMMMMMMMMMMMXlOOl + lMMMM0.,d0WMMMMM0.xOkdlccloxkOOOd::. + ,kWMMKc: ,dXMMMWd;. + .;:cxk; .,co,ckk; + ,clc, cc:' + lMMMMK oMMMMo + XMMMM, .WMMMW. ,lxOOOOxc. .cdkOOkd; :xxo. cxxl. xxd: ,dOOOxc .cxOOkd, + :MMMMk OMMMM; .NMMMX0NMMMMo OMMMKOXMMMl XMMMK NMMMx 'MMMMWWKXMMMMXWN0WMMMM; + KMMMW ;MMMMl .kXXl NMMM0 'MMMX, xxc MMMMx .MMMMo cMMMMK. XMMMMc 'MMMM: + 'MMMMc NMMMo ;dOKNNWMMMMd cXMMMMNOl. .MMMM: :MMMM; xMMMM. WMMMO ;MMMM' + kMMMXkMMMx ;WMMMc. oMMMMc .'c0MMMW. ;MMMM; .NMMMM. 0MMMX .MMMMc dMMMM + .WMMMMMMd xMMMMX0NWMMMMMO.NMNOkkXMMMX .WMMMMKKMWMMMW XMMMk :MMMM, kMMMX + .oxxxx: cxO0Od; :k0Od. 'lxkO0Oko; .ckO0kl..lxxo 'dxx, lxxx .oxxc + + # Vasum [Vasum](https://wiki.tizen.org/wiki/Security:Vasum) is a Linux daemon and a set of utilities used for managing para-virtualization. It uses Linux Containers to create separate, graphical environments called *zones*. One can concurrently run several zones on one physical device. Vasum exports a rich C/Dbus API that the application frameworks can use to interact with zones. -- 2.7.4 From f7e1587986414bed7bdbff7c9df70d62a9a36da9 Mon Sep 17 00:00:00 2001 From: Jan Olszak Date: Mon, 24 Aug 2015 17:07:57 +0200 Subject: [PATCH 14/16] lxcpp: Dropping capabilities from the bounding set [Feature] N/A [Cause] N/A [Solution] N/A [Verification] Build, install, run tests Change-Id: Id9e351d1993b43850e6a4d2a59b9eac2b4c5d354 --- libs/lxcpp/capability.cpp | 76 +++++++++++++++++++++++++++++++++++++++++++ libs/lxcpp/capability.hpp | 33 +++++++++++++++++++ libs/lxcpp/container-impl.cpp | 3 ++ libs/lxcpp/exception.hpp | 5 +++ 4 files changed, 117 insertions(+) create mode 100644 libs/lxcpp/capability.cpp create mode 100644 libs/lxcpp/capability.hpp diff --git a/libs/lxcpp/capability.cpp b/libs/lxcpp/capability.cpp new file mode 100644 index 0000000..075f4a7 --- /dev/null +++ b/libs/lxcpp/capability.cpp @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * @author Jan Olszak (j.olszak@samsung.com) + * @brief Linux capabilities handling routines + */ + +#include "lxcpp/capability.hpp" +#include "lxcpp/exception.hpp" + +#include "logger/logger.hpp" +#include "utils/exception.hpp" + +#include +#include +#include +#include + +#include + +namespace lxcpp { + +namespace { + +int getLastCap() +{ + std::ifstream ifs("/proc/sys/kernel/cap_last_cap"); + if (!ifs.good()) { + const std::string msg = "Failed to open /proc/sys/kernel/cap_last_cap"; + LOGE(msg); + throw CapabilitySetupException(msg); + } + + int lastCap; + ifs >> lastCap; + + return lastCap; +} + +} // namespace + +void dropCapsFromBoundingExcept(unsigned long long mask) +{ + // This is thread safe in C++11 + static int lastCap = getLastCap(); + + // Drop caps except those in the mask + for (int cap = 0; cap <= lastCap; ++cap) { + if (mask & (1LL << cap)) + continue; + + if (::prctl(PR_CAPBSET_DROP, cap, 0, 0, 0)) { + const std::string msg = "Failed to remove capability id: " + std::to_string(cap) + + ", error: " + utils::getSystemErrorMessage(); + LOGE(msg); + throw ProcessSetupException(msg); + } + } +} +} // namespace lxcpp diff --git a/libs/lxcpp/capability.hpp b/libs/lxcpp/capability.hpp new file mode 100644 index 0000000..10105e1 --- /dev/null +++ b/libs/lxcpp/capability.hpp @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * @author Jan Olszak (j.olszak@samsung.com) + * @brief Linux capabilities handling routines + */ + +#ifndef LXCPP_CAPABILITY_HPP +#define LXCPP_CAPABILITY_HPP + +namespace lxcpp { + +void dropCapsFromBoundingExcept(unsigned long long mask); + +} // namespace lxcpp + +#endif // LXCPP_CAPABILITY_HPP diff --git a/libs/lxcpp/container-impl.cpp b/libs/lxcpp/container-impl.cpp index 2ac232f..9af272f 100644 --- a/libs/lxcpp/container-impl.cpp +++ b/libs/lxcpp/container-impl.cpp @@ -26,6 +26,7 @@ #include "lxcpp/process.hpp" #include "lxcpp/filesystem.hpp" #include "lxcpp/namespace.hpp" +#include "lxcpp/capability.hpp" #include "utils/exception.hpp" @@ -133,6 +134,8 @@ void setupMountPoints() int ContainerImpl::attachChild(void* data) { try { + // TODO Pass mask and options via data + dropCapsFromBoundingExcept(0); setupMountPoints(); return (*static_cast(data))(); } catch(...) { diff --git a/libs/lxcpp/exception.hpp b/libs/lxcpp/exception.hpp index bdb56f7..9d8b2cd 100644 --- a/libs/lxcpp/exception.hpp +++ b/libs/lxcpp/exception.hpp @@ -51,6 +51,11 @@ struct FileSystemSetupException: public Exception { : Exception(message) {} }; +struct CapabilitySetupException: public Exception { + CapabilitySetupException(const std::string& message = "Error during a capability operation") + : Exception(message) {} +}; + struct BadArgument: public Exception { BadArgument(const std::string& message = "Bad argument passed") : Exception(message) {} -- 2.7.4 From eb055c7913b512c5e3bda7f90efb750d8f82e393 Mon Sep 17 00:00:00 2001 From: Pawel Kubik Date: Wed, 19 Aug 2015 17:28:08 +0200 Subject: [PATCH 15/16] Add Doxygen libConfig documentation. [Feature] libConfig documentation [Cause] Explain the library usage [Verification] Built HTML using generate_documentation.sh and rendered it in browser Change-Id: I459756566055023f357fb2037b3409410129f53e --- libs/config/fields-union.hpp | 46 ++++++++++++++++------------- libs/config/fields.hpp | 26 ++++++++++------ libs/config/manager.hpp | 70 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 113 insertions(+), 29 deletions(-) diff --git a/libs/config/fields-union.hpp b/libs/config/fields-union.hpp index 67720e7..edc2b80 100644 --- a/libs/config/fields-union.hpp +++ b/libs/config/fields-union.hpp @@ -31,10 +31,26 @@ #include #include +class DisableMoveAnyWrapper : public boost::any +{ + public: + DisableMoveAnyWrapper() {} + DisableMoveAnyWrapper(const DisableMoveAnyWrapper& any) + : boost::any(static_cast(any)) {}; + DisableMoveAnyWrapper& operator=(DisableMoveAnyWrapper&& any) = delete; + DisableMoveAnyWrapper& operator=(const DisableMoveAnyWrapper& any) { + static_cast(*this) = static_cast(any); + return *this; + } +}; + /** - * Use this macro to declare and register config fields + * @ingroup libConfig * - * Example: + * Use this macro to declare and register config fields. + * + * Example of fields registration: + * @code * struct Foo { * std::string bar; * @@ -52,8 +68,10 @@ * int * ) * }; + * @endcode * - * Example of valid configuration: + * Example of valid configuration: + * @code * 1. { * "type": "Foo", * "value": { "bar": "some string" } @@ -62,9 +80,10 @@ * "type": "int", * "value": 1 * } + * @endcode * - * - * Usage: + * Usage of existing bindings: + * @code * Config config; * // ... * if (config.isSet()) { @@ -83,24 +102,11 @@ * config.set(std::move(foo)); //< copy sic! * config.set(Foo({"some string"})); * } + * @endcode */ - -class DisbaleMoveAnyWrapper : public boost::any -{ - public: - DisbaleMoveAnyWrapper() {} - DisbaleMoveAnyWrapper(const DisbaleMoveAnyWrapper& any) - : boost::any(static_cast(any)) {}; - DisbaleMoveAnyWrapper& operator=(DisbaleMoveAnyWrapper&& any) = delete; - DisbaleMoveAnyWrapper& operator=(const DisbaleMoveAnyWrapper& any) { - static_cast(*this) = static_cast(any); - return *this; - } -}; - #define CONFIG_DECLARE_UNION(...) \ private: \ - DisbaleMoveAnyWrapper mConfigDeclareField; \ + DisableMoveAnyWrapper mConfigDeclareField; \ \ template \ void visitOption(Visitor& v, const std::string& name) { \ diff --git a/libs/config/fields.hpp b/libs/config/fields.hpp index d1fc4be..7a43bf6 100644 --- a/libs/config/fields.hpp +++ b/libs/config/fields.hpp @@ -18,8 +18,9 @@ /** * @file - * @author Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com) - * @brief Macros for registering configuration fields + * @author Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com) + * @addtogroup libConfig libConfig + * @brief C++ library for handling configurations */ #ifndef COMMON_CONFIG_FIELDS_HPP @@ -35,9 +36,21 @@ #endif /** - * Use this macro to register config fields. + * @ingroup libConfig + * Register empty config class + */ +#define CONFIG_REGISTER_EMPTY \ + template \ + static void accept(Visitor ) { \ + } \ + +/** + * @ingroup libConfig + * + * Registers config fields within class. * * Example: + * @code * struct Foo * { * std::string bar; @@ -56,13 +69,8 @@ * sub_a * ) * }; + * @endcode */ - -#define CONFIG_REGISTER_EMPTY \ - template \ - static void accept(Visitor ) { \ - } \ - #define CONFIG_REGISTER(...) \ template \ void accept(Visitor v) { \ diff --git a/libs/config/manager.hpp b/libs/config/manager.hpp index f3bac43..e2877df 100644 --- a/libs/config/manager.hpp +++ b/libs/config/manager.hpp @@ -19,7 +19,73 @@ /** * @file * @author Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com) + * @defgroup manager Manager + * @ingroup libConfig * @brief Configuration management functions + * + * Example of various data formats operations: + * + * @code + * #include "config/fields.hpp" + * #include "config/manager.hpp" + * #include + * #include + * + * struct Foo + * { + * std::string bar = "plain-text"; + * std::vector tab = std::vector{1, 2, 4, 8}; + * double number = 3.14; + * + * CONFIG_REGISTER + * ( + * bar, + * tab, + * number + * ) + * }; + * + * int main() + * { + * Foo foo; + * + * const std::string jsonString = config::saveToJsonString(foo); + * config::loadFromJsonString(jsonString, foo); + * + * const GVariant* gVariantPointer = config::saveToGVariant(foo); + * config::loadFromGVariant(gVariantPointer, foo); + * g_variant_unref(gVariantPointer); + * + * constexpr std::string jsonFile = "foo.json"; + * config::saveToJsonFile(jsonFile, foo); + * config::loadFromJsonFile(jsonFile, foo); + * + * constexpr std::string kvDBPath = "kvdb"; + * constexpr std::string key = "foo"; + * config::saveToKVStore(kvDBPath, foo, key); + * config::loadFromKVStore(kvDBPath, foo, key); + * + * config::loadFromKVStoreWithJson(kvDBPath, jsonString, foo, key); + * config::loadFromKVStoreWithJsonFile(kvDBPath, jsonFile, foo, key); + * + * FILE* file = fopen("blob", "wb"); + * if (!file) + * { + * return EXIT_FAILURE; + * } + * const int fd = ::fileno(file); + * config::saveToFD(fd, foo); + * ::fclose(file); + * file = ::fopen("blob", "rb"); + * if(!file) { + * return EXIT_FAILURE; + * } + * config::loadFromFD(fd, foo); + * ::fclose(file); + * + * return 0; + * } + * @endcode */ #ifndef COMMON_CONFIG_MANAGER_HPP @@ -39,6 +105,8 @@ namespace config { +/*@{*/ + /** * Fills the configuration with data stored in the GVariant * @@ -254,4 +322,6 @@ void saveToFD(const int fd, const Config& config) } // namespace config +/*@}*/ + #endif // COMMON_CONFIG_MANAGER_HPP -- 2.7.4 From 9c3710942aac6095d0f28587286868783ec1ef6a Mon Sep 17 00:00:00 2001 From: Krzysztof Dynowski Date: Fri, 21 Aug 2015 17:08:22 +0200 Subject: [PATCH 16/16] lxcpp: network interface [Feature] Network interface for lxcpp [Cause] N/A [Solution] N/A [Verification] Build, install, run tests Change-Id: Ifcf03b46662168c198a8e36c268b6856639ff52e --- libs/lxcpp/container-impl.cpp | 56 +++++++++++++++++- libs/lxcpp/container-impl.hpp | 22 +++++++ libs/lxcpp/container.hpp | 34 +++++++++++ libs/lxcpp/exception.hpp | 5 ++ libs/lxcpp/network-config.cpp | 47 +++++++++++++++ libs/lxcpp/network-config.hpp | 116 ++++++++++++++++++++++++++++++++++++ libs/lxcpp/network.cpp | 134 ++++++++++++++++++++++++++++++++++++++++++ libs/lxcpp/network.hpp | 73 +++++++++++++++++++++++ 8 files changed, 486 insertions(+), 1 deletion(-) create mode 100644 libs/lxcpp/network-config.cpp create mode 100644 libs/lxcpp/network-config.hpp create mode 100644 libs/lxcpp/network.cpp create mode 100644 libs/lxcpp/network.hpp diff --git a/libs/lxcpp/container-impl.cpp b/libs/lxcpp/container-impl.cpp index 9af272f..cba0691 100644 --- a/libs/lxcpp/container-impl.cpp +++ b/libs/lxcpp/container-impl.cpp @@ -33,7 +33,6 @@ #include #include - namespace lxcpp { ContainerImpl::ContainerImpl() @@ -185,6 +184,61 @@ void ContainerImpl::attach(Container::AttachCall& call) } } +void ContainerImpl::addInterfaceConfig(const std::string& hostif, + const std::string& zoneif, + InterfaceType type, + MacVLanMode mode) +{ + mInterfaceConfig.push_back(NetworkInterfaceConfig(hostif,zoneif,type,mode)); +} + +void ContainerImpl::addAddrConfig(const std::string& /*ifname*/, const InetAddr& /*addr*/) +{ + throw NotImplementedException(); +} + +std::vector ContainerImpl::getInterfaces() +{ + return NetworkInterface::getInterfaces(getInitPid()); +} + +NetworkInterfaceInfo ContainerImpl::getInterfaceInfo(const std::string& /*ifname*/) +{ + throw NotImplementedException(); +} + +void ContainerImpl::createInterface(const std::string& hostif, + const std::string& zoneif, + InterfaceType type, + MacVLanMode mode) +{ + NetworkInterface ni(*this, zoneif); + ni.create(hostif, type, mode); +} + +void ContainerImpl::destroyInterface(const std::string& /*ifname*/) +{ + throw NotImplementedException(); +} + +void ContainerImpl::setUp(const std::string& /*ifname*/) +{ + throw NotImplementedException(); +} + +void ContainerImpl::setDown(const std::string& /*ifname*/) +{ + throw NotImplementedException(); +} +void ContainerImpl::addAddr(const std::string& /*ifname*/, const InetAddr& /*addr*/) +{ + throw NotImplementedException(); +} + +void ContainerImpl::delAddr(const std::string& /*ifname*/, const InetAddr& /*addr*/) +{ + throw NotImplementedException(); +} } // namespace lxcpp diff --git a/libs/lxcpp/container-impl.hpp b/libs/lxcpp/container-impl.hpp index e236932..a57c340 100644 --- a/libs/lxcpp/container-impl.hpp +++ b/libs/lxcpp/container-impl.hpp @@ -26,6 +26,7 @@ #include "lxcpp/container.hpp" #include "lxcpp/namespace.hpp" +#include "lxcpp/network.hpp" #include "utils/channel.hpp" @@ -56,6 +57,26 @@ public: // Other void attach(Container::AttachCall& attachCall); + // Network interfaces setup/config + void addInterfaceConfig(const std::string& hostif, + const std::string& zoneif, + InterfaceType type, + MacVLanMode mode); + void addAddrConfig(const std::string& ifname, const InetAddr& addr); + + // Network interfaces (runtime) + std::vector getInterfaces(); + NetworkInterfaceInfo getInterfaceInfo(const std::string& ifname); + void createInterface(const std::string& hostif, + const std::string& zoneif, + InterfaceType type, + MacVLanMode mode); + void destroyInterface(const std::string& ifname); + void setUp(const std::string& ifname); + void setDown(const std::string& ifname); + void addAddr(const std::string& ifname, const InetAddr& addr); + void delAddr(const std::string& ifname, const InetAddr& addr); + private: // Methods for different stages of setting up the attachment @@ -65,6 +86,7 @@ private: pid_t mInitPid; std::vector mNamespaces; + std::vector mInterfaceConfig; }; } // namespace lxcpp diff --git a/libs/lxcpp/container.hpp b/libs/lxcpp/container.hpp index 4f2d697..44a3567 100644 --- a/libs/lxcpp/container.hpp +++ b/libs/lxcpp/container.hpp @@ -24,11 +24,25 @@ #ifndef LXCPP_CONTAINER_HPP #define LXCPP_CONTAINER_HPP +#include "lxcpp/network-config.hpp" + #include #include +#include namespace lxcpp { +enum class NetStatus { + DOWN, + UP +}; + +struct NetworkInterfaceInfo { + const std::string ifname; + const NetStatus status; + const std::vector addrs; +}; + class Container { public: typedef std::function AttachCall; @@ -54,6 +68,26 @@ public: // Other virtual void attach(AttachCall& attachCall) = 0; + + // Network interfaces setup/config + virtual void addInterfaceConfig(const std::string& hostif, + const std::string& zoneif, + InterfaceType type, + MacVLanMode mode) = 0; + virtual void addAddrConfig(const std::string& ifname, const InetAddr& addr) = 0; + + // Network interfaces (runtime) + virtual std::vector getInterfaces() = 0; + virtual NetworkInterfaceInfo getInterfaceInfo(const std::string& ifname) = 0; + virtual void createInterface(const std::string& hostif, + const std::string& zoneif, + InterfaceType type, + MacVLanMode mode) = 0; + virtual void destroyInterface(const std::string& ifname) = 0; + virtual void setUp(const std::string& ifname) = 0; + virtual void setDown(const std::string& ifname) = 0; + virtual void addAddr(const std::string& ifname, const InetAddr& addr) = 0; + virtual void delAddr(const std::string& ifname, const InetAddr& addr) = 0; }; } // namespace lxcpp diff --git a/libs/lxcpp/exception.hpp b/libs/lxcpp/exception.hpp index 9d8b2cd..51dc5ec 100644 --- a/libs/lxcpp/exception.hpp +++ b/libs/lxcpp/exception.hpp @@ -61,6 +61,11 @@ struct BadArgument: public Exception { : Exception(message) {} }; +struct NetworkException : public Exception { + NetworkException (const std::string& message = "Error during setting up a network") + : Exception(message) {} +}; + } // namespace lxcpp #endif // LXCPP_EXCEPTION_HPP diff --git a/libs/lxcpp/network-config.cpp b/libs/lxcpp/network-config.cpp new file mode 100644 index 0000000..77e220a --- /dev/null +++ b/libs/lxcpp/network-config.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +/** + * @file + * @author Krzysztof Dynowski (k.dynowski@samsumg.com) + * @brief Network configuration classes + */ + +#include "lxcpp/network-config.hpp" +#include "lxcpp/exception.hpp" +#include + +namespace lxcpp { + +void NetworkInterfaceConfig::addNetAddr(const InetAddr& addr) +{ + std::vector::iterator exists = std::find(mIpAddrList.begin(), mIpAddrList.end(), addr); + if (exists != mIpAddrList.end()) { + std::string msg("Address alredy assigned"); + throw NetworkException(msg); + } + mIpAddrList.push_back(addr); +} + +void NetworkInterfaceConfig::delNetAddr(const InetAddr& addr) +{ + std::vector::iterator exists = std::find(mIpAddrList.begin(), mIpAddrList.end(), addr); + mIpAddrList.erase(exists); +} + +} //namespace lxcpp diff --git a/libs/lxcpp/network-config.hpp b/libs/lxcpp/network-config.hpp new file mode 100644 index 0000000..b490aa0 --- /dev/null +++ b/libs/lxcpp/network-config.hpp @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * @author Krzysztof Dynowski (k.dynowski@samsumg.com) + * @brief Network configuration classes + */ + +#ifndef LXCPP_NETWORK_CONFIG_HPP +#define LXCPP_NETWORK_CONFIG_HPP + +#include +#include + +#include +#include + +namespace lxcpp { + +/** + * Created interface type + */ +enum class InterfaceType { + VETH, + BRIDGE, + MACVLAN, + MOVE +}; + +/** + * Suported MacVLan modes + */ +enum class MacVLanMode { + PRIVATE, + VEPA, + BRIDGE, + PASSTHRU +}; + +/** + * Suported address types + */ +enum class InetAddrType { + IPV4, + IPV6 +}; + +/** + * Unified ip address + */ +struct InetAddr { + InetAddrType type; + int prefix; + union { + struct in_addr ipv4; + struct in6_addr ipv6; + } addr; +}; + +static inline bool operator==(const InetAddr& a, const InetAddr& b) { + if (a.type == b.type && a.prefix == b.prefix) { + if (a.type == InetAddrType::IPV4) { + return ::memcmp(&a.addr.ipv4, &b.addr.ipv4, sizeof(a.addr.ipv4)) == 0; + } + if (a.type == InetAddrType::IPV6) { + return ::memcmp(&a.addr.ipv6, &b.addr.ipv6, sizeof(a.addr.ipv6)) == 0; + } + } + return false; +} + + +/** + * Network interface configuration + */ +class NetworkInterfaceConfig { +public: + NetworkInterfaceConfig(const std::string& hostif, + const std::string& zoneif, + InterfaceType type, + MacVLanMode mode = MacVLanMode::PRIVATE) : + mHostIf(hostif), + mZoneIf(zoneif), + mType(type), + mMode(mode) + { + } + void addNetAddr(const InetAddr&); + void delNetAddr(const InetAddr&); + +private: + const std::string mHostIf; + const std::string mZoneIf; + const InterfaceType mType; + const MacVLanMode mMode; + std::vector mIpAddrList; +}; + +} //namespace lxcpp + +#endif // LXCPP_NETWORK_CONFIG_HPP diff --git a/libs/lxcpp/network.cpp b/libs/lxcpp/network.cpp new file mode 100644 index 0000000..ad01f6f --- /dev/null +++ b/libs/lxcpp/network.cpp @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * @author Krzysztof Dynowski (k.dynowski@samsumg.com) + * @brief Actions on network interace in the container + */ + + +#include "lxcpp/network.hpp" +#include "lxcpp/exception.hpp" +#include "netlink/netlink-message.hpp" +#include "utils/make-clean.hpp" + +#include + +using namespace vasum::netlink; + +namespace lxcpp { + +void NetworkInterface::create(const std::string& hostif, + InterfaceType type, + MacVLanMode mode) +{ + switch (type) { + case InterfaceType::VETH: + createVeth(hostif); + break; + case InterfaceType::BRIDGE: + createBridge(hostif); + break; + case InterfaceType::MACVLAN: + createMacVLan(hostif, mode); + break; + case InterfaceType::MOVE: + move(hostif); + break; + default: + throw NetworkException("Unsuported interface type"); + } +} + +void NetworkInterface::createVeth(const std::string& /*hostif*/) +{ + throw NotImplementedException(); +} + +void NetworkInterface::createBridge(const std::string& /*hostif*/) +{ + throw NotImplementedException(); +} + +void NetworkInterface::createMacVLan(const std::string& /*hostif*/, MacVLanMode /*mode*/) +{ + throw NotImplementedException(); +} + +void NetworkInterface::move(const std::string& /*hostif*/) +{ + throw NotImplementedException(); +} + +void NetworkInterface::destroy() +{ + throw NotImplementedException(); +} + +NetStatus NetworkInterface::status() +{ + throw NotImplementedException(); + /* + //TODO get container status, if stopped return CONFIGURED + if (mContainer.getInitPid()<=0) return CONFIGURED; + // read netlink + return DOWN;*/ +} + + +void NetworkInterface::up() +{ + throw NotImplementedException(); +} + +void NetworkInterface::down() +{ + throw NotImplementedException(); +} + +void NetworkInterface::setAttrs(const Attrs& /*attrs*/) +{ + throw NotImplementedException(); +} + +const Attrs NetworkInterface::getAttrs() const +{ + throw NotImplementedException(); +} + +std::vector NetworkInterface::getInterfaces(pid_t initpid) +{ + // get interfaces seen by netlink + 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, initpid); + + std::vector iflist; + while (response.hasMessage()) { + std::string ifName; + response.skip(); + response.fetch(IFLA_IFNAME, ifName); + iflist.push_back(ifName); + response.fetchNextMessage(); + } + return iflist; +} + +} // namespace lxcpp diff --git a/libs/lxcpp/network.hpp b/libs/lxcpp/network.hpp new file mode 100644 index 0000000..44ab268 --- /dev/null +++ b/libs/lxcpp/network.hpp @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * @author Krzysztof Dynowski (k.dynowski@samsumg.com) + * @brief Actions on network interace in the container + */ + +#ifndef LXCPP_NETWORK_HPP +#define LXCPP_NETWORK_HPP + +#include "lxcpp/container.hpp" +#include + +namespace lxcpp { + +struct Attr { + std::string name; + std::string value; +}; + +typedef std::vector Attrs; + + +/// Network operations to be performed on given container and interface +/// operates on netlink device +class NetworkInterface { +public: + NetworkInterface(Container& c, const std::string& ifname) : + mContainer(c), + mIfname(ifname) + { } + + //Network actions on Container + void create(const std::string& hostif, InterfaceType type, MacVLanMode mode=MacVLanMode::PRIVATE); + void destroy(); + + NetStatus status(); + void up(); + void down(); + void setAttrs(const Attrs& attrs); + const Attrs getAttrs() const; + + static std::vector getInterfaces(pid_t initpid); + +private: + void createVeth(const std::string& hostif); + void createBridge(const std::string& hostif); + void createMacVLan(const std::string& hostif, MacVLanMode mode); + void move(const std::string& hostif); + + Container& mContainer; ///< Container to operate on + const std::string mIfname; ///< network interface name inside zone +}; + +} // namespace lxcpp + +#endif // LXCPP_NETWORK_HPP -- 2.7.4