lxcpp: Setting up environment variables 71/47271/7
authorJan Olszak <j.olszak@samsung.com>
Thu, 27 Aug 2015 15:06:30 +0000 (17:06 +0200)
committerDariusz Michaluk <d.michaluk@samsung.com>
Wed, 2 Sep 2015 15:54:45 +0000 (08:54 -0700)
[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
libs/lxcpp/commands/attach-manager.cpp [moved from libs/lxcpp/attach-manager.cpp with 66% similarity]
libs/lxcpp/commands/attach-manager.hpp [moved from libs/lxcpp/attach-manager.hpp with 64% similarity]
libs/lxcpp/container-impl.cpp
libs/lxcpp/container.hpp
libs/lxcpp/environment.cpp [new file with mode: 0644]
libs/lxcpp/environment.hpp [new file with mode: 0644]
libs/lxcpp/exception.hpp
tests/unit_tests/lxcpp/ut-environment.cpp [new file with mode: 0644]

index b04e437..c76de74 100644 (file)
@@ -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)
similarity index 66%
rename from libs/lxcpp/attach-manager.cpp
rename to libs/lxcpp/commands/attach-manager.cpp
index 3479673..1a274d3 100644 (file)
  * @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 <unistd.h>
 #include <sys/mount.h>
 
+#include <functional>
 
 namespace lxcpp {
 
@@ -64,6 +66,16 @@ void setupMountPoints()
     */
 }
 
+int execFunction(void* call)
+{
+    try {
+        return (*static_cast<AttachManager::Call*>(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<std::string>& envToKeep,
+                           const std::vector<std::pair<std::string, std::string>>& 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<std::string>& envToKeep,
+                         const std::vector<std::pair<std::string, std::string>>& envToSet)
 {
-    try {
-        // TODO Pass mask and options via data
-        dropCapsFromBoundingExcept(0);
-        setupMountPoints();
-        return (*static_cast<Container::AttachCall*>(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);
similarity index 64%
rename from libs/lxcpp/attach-manager.hpp
rename to libs/lxcpp/commands/attach-manager.hpp
index be37ade..f286564 100644 (file)
@@ -33,6 +33,8 @@ namespace lxcpp {
 
 class AttachManager final {
 public:
+    typedef std::function<int(void)> 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<std::string>& envToKeep,
+                const std::vector<std::pair<std::string, std::string>>& 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<std::string>& envToKeep,
+                     const std::vector<std::pair<std::string, std::string>>& 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);
 };
 
index 6de22dc..0d8d5b1 100644 (file)
@@ -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<Namespace>& 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);
index d07bc27..882aa87 100644 (file)
@@ -46,7 +46,7 @@ struct NetworkInterfaceInfo {
 
 class Container {
 public:
-    typedef std::function<int()> AttachCall;
+    typedef std::function<int(void)> AttachCall;
 
     virtual ~Container() {};
 
diff --git a/libs/lxcpp/environment.cpp b/libs/lxcpp/environment.cpp
new file mode 100644 (file)
index 0000000..489608b
--- /dev/null
@@ -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 <stdlib.h>
+
+namespace lxcpp {
+
+void clearenvExcept(const std::vector<std::string>& names)
+{
+    // Backup keeps pairs (name,value)
+    std::vector<std::pair< std::string,std::string>> 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<std::pair<std::string, std::string>>& 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 (file)
index 0000000..c9d8a20
--- /dev/null
@@ -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 <vector>
+#include <utility>
+#include <string>
+
+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<std::string>& names);
+
+std::string getenv(const std::string& name);
+
+void setenv(const std::string& name, const std::string& value);
+
+void setenv(const std::vector<std::pair<std::string, std::string>>& variables);
+
+} // namespace lxcpp
+
+#endif // LXCPP_ENVIRONMENT_HPP
index 51dc5ec..8c11131 100644 (file)
@@ -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 (file)
index 0000000..8d6d45d
--- /dev/null
@@ -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 <iostream>
+#include <sched.h>
+#include <stdlib.h>
+
+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()