From 42745f132f330133017084e7d10749a7ccbf48e3 Mon Sep 17 00:00:00 2001 From: Jan Olszak Date: Thu, 27 Aug 2015 17:06:30 +0200 Subject: [PATCH] lxcpp: Setting up environment variables [Feature] Setting up environement in attach Created the commands directory [Cause] N/A [Solution] N/A [Verification] Build, install, run tests Change-Id: I7526f04ca48e931be75a99cdbf774b4c5fbb5c35 --- libs/lxcpp/CMakeLists.txt | 17 ++-- libs/lxcpp/{ => commands}/attach-manager.cpp | 65 +++++++++---- libs/lxcpp/{ => commands}/attach-manager.hpp | 20 +++- libs/lxcpp/container-impl.cpp | 15 ++- libs/lxcpp/container.hpp | 2 +- libs/lxcpp/environment.cpp | 92 ++++++++++++++++++ libs/lxcpp/environment.hpp | 52 ++++++++++ libs/lxcpp/exception.hpp | 10 ++ tests/unit_tests/lxcpp/ut-environment.cpp | 140 +++++++++++++++++++++++++++ 9 files changed, 378 insertions(+), 35 deletions(-) rename libs/lxcpp/{ => commands}/attach-manager.cpp (66%) rename libs/lxcpp/{ => commands}/attach-manager.hpp (64%) create mode 100644 libs/lxcpp/environment.cpp create mode 100644 libs/lxcpp/environment.hpp create mode 100644 tests/unit_tests/lxcpp/ut-environment.cpp diff --git a/libs/lxcpp/CMakeLists.txt b/libs/lxcpp/CMakeLists.txt index b04e437..c76de74 100644 --- a/libs/lxcpp/CMakeLists.txt +++ b/libs/lxcpp/CMakeLists.txt @@ -22,12 +22,14 @@ 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 - ${COMMON_FOLDER}/utils/channel.hpp - ${COMMON_FOLDER}/utils/environment.hpp - ${COMMON_FOLDER}/utils/execute.hpp) +FILE(GLOB HEADERS_UTILS ${COMMON_FOLDER}/utils/fd-utils.hpp + ${COMMON_FOLDER}/utils/exception.hpp + ${COMMON_FOLDER}/utils/channel.hpp + ${COMMON_FOLDER}/utils/environment.hpp + ${COMMON_FOLDER}/utils/execute.hpp) FILE(GLOB HEADERS_NETLINK ${COMMON_FOLDER}/netlink/*.hpp) +FILE(GLOB HEADERS_COMMANDS commands/*.hpp) + FILE(GLOB SRCS *.cpp *.hpp) FILE(GLOB SRCS_UTILS ${COMMON_FOLDER}/utils/fd-utils.cpp ${COMMON_FOLDER}/utils/exception.cpp @@ -35,13 +37,14 @@ FILE(GLOB SRCS_UTILS ${COMMON_FOLDER}/utils/fd-utils.cpp ${COMMON_FOLDER}/utils/environment.cpp ${COMMON_FOLDER}/utils/execute.cpp) FILE(GLOB SRCS_NETLINK ${COMMON_FOLDER}/netlink/*.cpp) +FILE(GLOB SRCS_COMMANDS commands/*.cpp) SET(_LIB_VERSION_ "${VERSION}") SET(_LIB_SOVERSION_ "0") SET(PC_FILE "lib${PROJECT_NAME}.pc") ## Setup target ################################################################ -ADD_LIBRARY(${PROJECT_NAME} SHARED ${SRCS} ${SRCS_UTILS} ${SRCS_NETLINK}) +ADD_LIBRARY(${PROJECT_NAME} SHARED ${SRCS} ${SRCS_UTILS} ${SRCS_NETLINK} ${SRCS_COMMANDS}) SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES SOVERSION ${_LIB_SOVERSION_} VERSION ${_LIB_VERSION_} @@ -70,3 +73,5 @@ INSTALL(FILES ${HEADERS_UTILS} DESTINATION ${INCLUDE_INSTALL_DIR}/lxcpp/utils) INSTALL(FILES ${HEADERS_NETLINK} DESTINATION ${INCLUDE_INSTALL_DIR}/lxcpp/netlink) +INSTALL(FILES ${HEADERS_COMMANDS} + DESTINATION ${INCLUDE_INSTALL_DIR}/lxcpp/commands) diff --git a/libs/lxcpp/attach-manager.cpp b/libs/lxcpp/commands/attach-manager.cpp similarity index 66% rename from libs/lxcpp/attach-manager.cpp rename to libs/lxcpp/commands/attach-manager.cpp index 3479673..1a274d3 100644 --- a/libs/lxcpp/attach-manager.cpp +++ b/libs/lxcpp/commands/attach-manager.cpp @@ -21,18 +21,20 @@ * @brief Implementation of attaching to a container */ -#include "lxcpp/attach-manager.hpp" +#include "lxcpp/commands/attach-manager.hpp" #include "lxcpp/exception.hpp" #include "lxcpp/process.hpp" #include "lxcpp/filesystem.hpp" #include "lxcpp/namespace.hpp" #include "lxcpp/capability.hpp" +#include "lxcpp/environment.hpp" #include "utils/exception.hpp" #include #include +#include namespace lxcpp { @@ -64,6 +66,16 @@ void setupMountPoints() */ } +int execFunction(void* call) +{ + try { + return (*static_cast(call))(); + } catch(...) { + return -1; // Non-zero on failure + } + return 0; // Success +} + } // namespace AttachManager::AttachManager(lxcpp::ContainerImpl& container) @@ -75,12 +87,21 @@ AttachManager::~AttachManager() { } -void AttachManager::attach(Container::AttachCall& call, - const std::string& wdInContainer) +void AttachManager::attach(Container::AttachCall& userCall, + const int capsToKeep, + const std::string& workDirInContainer, + const std::vector& envToKeep, + const std::vector>& envToSet) { // Channels for setup synchronization utils::Channel intermChannel; + Call call = std::bind(&AttachManager::child, + std::move(userCall), + capsToKeep, + std::move(envToKeep), + std::move(envToSet)); + const pid_t interPid = lxcpp::fork(); if (interPid > 0) { intermChannel.setLeft(); @@ -88,23 +109,29 @@ void AttachManager::attach(Container::AttachCall& call, intermChannel.shutdown(); } else { intermChannel.setRight(); - interm(intermChannel, wdInContainer, call); + interm(intermChannel, workDirInContainer, call); intermChannel.shutdown(); ::_exit(0); } } -int AttachManager::child(void* data) +int AttachManager::child(const Container::AttachCall& call, + const int capsToKeep, + const std::vector& envToKeep, + const std::vector>& envToSet) { - try { - // TODO Pass mask and options via data - dropCapsFromBoundingExcept(0); - setupMountPoints(); - return (*static_cast(data))(); - } catch(...) { - return -1; // Non-zero on failure - } - return 0; // Success + // Setup capabilities + dropCapsFromBoundingExcept(capsToKeep); + + // Setup /proc /sys mount + setupMountPoints(); + + // Setup environment variables + clearenvExcept(envToKeep); + setenv(envToSet); + + // Run user's code + return call(); } void AttachManager::parent(utils::Channel& intermChannel, const pid_t interPid) @@ -118,18 +145,18 @@ void AttachManager::parent(utils::Channel& intermChannel, const pid_t interPid) } void AttachManager::interm(utils::Channel& intermChannel, - const std::string& wdInContainer, - Container::AttachCall& call) + const std::string& workDirInContainer, + Call& call) { lxcpp::setns(mContainer.getInitPid(), mContainer.getNamespaces()); // Change the current work directory - // wdInContainer is a path relative to the container's root - lxcpp::chdir(wdInContainer); + // workDirInContainer is a path relative to the container's root + lxcpp::chdir(workDirInContainer); // PID namespace won't affect the returned pid // CLONE_PARENT: Child's PPID == Caller's PID - const pid_t childPid = lxcpp::clone(&AttachManager::child, + const pid_t childPid = lxcpp::clone(execFunction, &call, CLONE_PARENT); intermChannel.write(childPid); diff --git a/libs/lxcpp/attach-manager.hpp b/libs/lxcpp/commands/attach-manager.hpp similarity index 64% rename from libs/lxcpp/attach-manager.hpp rename to libs/lxcpp/commands/attach-manager.hpp index be37ade..f286564 100644 --- a/libs/lxcpp/attach-manager.hpp +++ b/libs/lxcpp/commands/attach-manager.hpp @@ -33,6 +33,8 @@ namespace lxcpp { class AttachManager final { public: + typedef std::function Call; + AttachManager(lxcpp::ContainerImpl& container); ~AttachManager(); @@ -40,22 +42,32 @@ public: * Runs the call in the container's context * * @param call function to run inside container - * @param wdInContainer Current Work Directory. Path relative to container's root + * @param capsToKeep mask of the capabilities that shouldn't be dropped + * @param workDirInContainer Current Work Directory. Path relative to container's root + * @param envToKeep environment variables to keep in container + * @param envToSet environment variables to add/modify in container */ void attach(Container::AttachCall& call, - const std::string& wdInContainer); + const int capsToKeep, + const std::string& workDirInContainer, + const std::vector& envToKeep, + const std::vector>& envToSet); private: + const lxcpp::ContainerImpl& mContainer; // Methods for different stages of setting up the attachment - static int child(void* data); + static int child(const Container::AttachCall& call, + const int capsToKeep, + const std::vector& envToKeep, + const std::vector>& envToSet); void parent(utils::Channel& intermChannel, const pid_t pid); void interm(utils::Channel& intermChannel, - const std::string& wdInContainer, + const std::string& workDirInContainer, Container::AttachCall& call); }; diff --git a/libs/lxcpp/container-impl.cpp b/libs/lxcpp/container-impl.cpp index 6de22dc..0d8d5b1 100644 --- a/libs/lxcpp/container-impl.cpp +++ b/libs/lxcpp/container-impl.cpp @@ -27,7 +27,7 @@ #include "lxcpp/filesystem.hpp" #include "lxcpp/namespace.hpp" #include "lxcpp/capability.hpp" -#include "lxcpp/attach-manager.hpp" +#include "lxcpp/commands/attach-manager.hpp" #include "utils/exception.hpp" @@ -108,7 +108,12 @@ void ContainerImpl::attach(Container::AttachCall& call, const std::string& cwdInContainer) { AttachManager attachManager(*this); - attachManager.attach(call, cwdInContainer); + // TODO: Env variables should agree with the ones already in the container + attachManager.attach(call, + /*capsToKeep*/ 0, + cwdInContainer, + /*envToKeep*/ {}, + /*envInContainer*/{{"container","lxcpp"}} ); } const std::vector& ContainerImpl::getNamespaces() const @@ -141,9 +146,9 @@ NetworkInterfaceInfo ContainerImpl::getInterfaceInfo(const std::string& /*ifname } void ContainerImpl::createInterface(const std::string& hostif, - const std::string& zoneif, - InterfaceType type, - MacVLanMode mode) + const std::string& zoneif, + InterfaceType type, + MacVLanMode mode) { NetworkInterface ni(*this, zoneif); ni.create(hostif, type, mode); diff --git a/libs/lxcpp/container.hpp b/libs/lxcpp/container.hpp index d07bc27..882aa87 100644 --- a/libs/lxcpp/container.hpp +++ b/libs/lxcpp/container.hpp @@ -46,7 +46,7 @@ struct NetworkInterfaceInfo { class Container { public: - typedef std::function AttachCall; + typedef std::function AttachCall; virtual ~Container() {}; diff --git a/libs/lxcpp/environment.cpp b/libs/lxcpp/environment.cpp new file mode 100644 index 0000000..489608b --- /dev/null +++ b/libs/lxcpp/environment.cpp @@ -0,0 +1,92 @@ +/* + * 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 Handling environment variables + */ + +#include "lxcpp/environment.hpp" +#include "lxcpp/exception.hpp" + +#include "logger/logger.hpp" +#include "utils/exception.hpp" + +#include + +namespace lxcpp { + +void clearenvExcept(const std::vector& names) +{ + // Backup keeps pairs (name,value) + std::vector> backup; + for(const auto& name: names) { + try { + backup.push_back({name, lxcpp::getenv(name)}); + } catch(const NoSuchValue&) { + // Continue if there's no such variable + } + } + + lxcpp::clearenv(); + + // Restore backup + lxcpp::setenv(backup); +} + +void clearenv() +{ + if(::clearenv()) { + const std::string msg = "clearenv() failed: " + + utils::getSystemErrorMessage(); + LOGE(msg); + throw EnvironmentSetupException(msg); + } +} + +std::string getenv(const std::string& name) +{ + const char* value = ::getenv(name.c_str()); + if (!value) { + const std::string msg = "getenv() failed: No such name"; + LOGW(msg); + throw NoSuchValue(msg); + } + return value; +} + +void setenv(const std::string& name, const std::string& value) +{ + if (-1 == ::setenv(name.c_str(), + value.c_str(), + 1 /*write if exists*/)) { + const std::string msg = "setenv() failed. Not all env set. " + + utils::getSystemErrorMessage(); + LOGE(msg); + throw EnvironmentSetupException(msg); + } +} + +void setenv(const std::vector>& variables) +{ + for(const auto& variable: variables) { + lxcpp::setenv(variable.first, variable.second); + } +} + +} // namespace lxcpp diff --git a/libs/lxcpp/environment.hpp b/libs/lxcpp/environment.hpp new file mode 100644 index 0000000..c9d8a20 --- /dev/null +++ b/libs/lxcpp/environment.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 Handling environment variables + */ + +#ifndef LXCPP_ENVIRONMENT_HPP +#define LXCPP_ENVIRONMENT_HPP + +#include +#include +#include + +namespace lxcpp { + +void clearenv(); + +/** + * Clears the env variables except those listed. + * There's a race condition - a moment when listed variables aren't set + * Function should be used only for setting up a new process. + * + * @param names names of the variables to keep + */ +void clearenvExcept(const std::vector& names); + +std::string getenv(const std::string& name); + +void setenv(const std::string& name, const std::string& value); + +void setenv(const std::vector>& variables); + +} // namespace lxcpp + +#endif // LXCPP_ENVIRONMENT_HPP diff --git a/libs/lxcpp/exception.hpp b/libs/lxcpp/exception.hpp index 51dc5ec..8c11131 100644 --- a/libs/lxcpp/exception.hpp +++ b/libs/lxcpp/exception.hpp @@ -51,6 +51,11 @@ struct FileSystemSetupException: public Exception { : Exception(message) {} }; +struct EnvironmentSetupException: public Exception { + EnvironmentSetupException(const std::string& message = "Error during handling environment variables") + : Exception(message) {} +}; + struct CapabilitySetupException: public Exception { CapabilitySetupException(const std::string& message = "Error during a capability operation") : Exception(message) {} @@ -61,6 +66,11 @@ struct BadArgument: public Exception { : Exception(message) {} }; +struct NoSuchValue: public Exception { + NoSuchValue(const std::string& message = "Value not found") + : Exception(message) {} +}; + struct NetworkException : public Exception { NetworkException (const std::string& message = "Error during setting up a network") : Exception(message) {} diff --git a/tests/unit_tests/lxcpp/ut-environment.cpp b/tests/unit_tests/lxcpp/ut-environment.cpp new file mode 100644 index 0000000..8d6d45d --- /dev/null +++ b/tests/unit_tests/lxcpp/ut-environment.cpp @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * Contact: Jan Olszak(j.olszak@samsung.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + + +/** + * @file + * @author Jan Olszak(j.olszak@samsung.com) + * @brief Unit tests of lxcpp namespace helpers + */ + +#include "config.hpp" +#include "ut.hpp" + +#include "lxcpp/environment.hpp" +#include "lxcpp/exception.hpp" +#include "lxcpp/namespace.hpp" +#include "lxcpp/process.hpp" + +#include "utils/execute.hpp" + +#include +#include +#include + +namespace { + +struct Fixture { + Fixture() {} + ~Fixture() {} +}; + +const int TEST_PASSED = 0; +const int ERROR = 1; + +const std::string TEST_NAME = "TEST_NAME"; +const std::string TEST_VALUE = "TEST_VALUE"; + +const std::string TEST_NAME_REMOVED = "TEST_NAME_REMOVED"; +const std::string TEST_VALUE_REMOVED = "TEST_VALUE_REMOVED"; + +} // namespace + +BOOST_FIXTURE_TEST_SUITE(LxcppEnvironmentSuite, Fixture) + +using namespace lxcpp; + +BOOST_AUTO_TEST_CASE(SetGetEnv) +{ + pid_t pid = lxcpp::fork(); + if (pid == 0) { + try { + lxcpp::setenv(TEST_NAME, TEST_VALUE); + + if(lxcpp::getenv(TEST_NAME) == TEST_VALUE) { + ::_exit(TEST_PASSED); + } + ::_exit(ERROR); + } catch(...) { + ::_exit(ERROR); + } + } else if (pid > 0) { + int status = -1; + BOOST_REQUIRE(utils::waitPid(pid, status)); + BOOST_REQUIRE(status == TEST_PASSED); + } +} + +BOOST_AUTO_TEST_CASE(ClearEnvExcept) +{ + pid_t pid = lxcpp::fork(); + if (pid == 0) { + try { + lxcpp::setenv(TEST_NAME, TEST_VALUE); + lxcpp::setenv(TEST_NAME_REMOVED, TEST_VALUE_REMOVED); + + clearenvExcept({TEST_NAME}); + + try { + lxcpp::getenv(TEST_NAME_REMOVED); + } catch(const lxcpp::NoSuchValue&) { + if(lxcpp::getenv(TEST_NAME) == TEST_VALUE) { + ::_exit(TEST_PASSED); + } + } + ::_exit(ERROR); + } catch(...) { + ::_exit(ERROR); + } + } else if (pid > 0) { + int status = -1; + BOOST_REQUIRE(utils::waitPid(pid, status)); + BOOST_REQUIRE(status == TEST_PASSED); + } +} + +BOOST_AUTO_TEST_CASE(ClearEnv) +{ + pid_t pid = lxcpp::fork(); + if (pid == 0) { + try { + lxcpp::setenv(TEST_NAME_REMOVED, TEST_VALUE_REMOVED); + lxcpp::clearenv(); + + // clearenv should clear environ + if (::environ) { + ::_exit(ERROR); + } + + try { + lxcpp::getenv(TEST_NAME_REMOVED); + } catch(const lxcpp::NoSuchValue&) { + ::_exit(TEST_PASSED); + } + ::_exit(ERROR); + } catch(...) { + ::_exit(ERROR); + } + } else if (pid > 0) { + int status = -1; + BOOST_REQUIRE(utils::waitPid(pid, status)); + BOOST_REQUIRE(status == TEST_PASSED); + } +} + +BOOST_AUTO_TEST_SUITE_END() -- 2.7.4