lxcpp: setns wrapper 34/45034/6
authorJan Olszak <j.olszak@samsung.com>
Wed, 29 Jul 2015 13:06:02 +0000 (15:06 +0200)
committerJan Olszak <j.olszak@samsung.com>
Fri, 31 Jul 2015 13:38:09 +0000 (15:38 +0200)
[Feature]       Added setns wrapper
                Added Namespace type
[Cause]         N/A
[Solution]      N/A
[Verification]  N/A

Change-Id: Ib07374bb07183dba604053139d624bdf2a850268

common/utils/execute.cpp
libs/lxcpp/CMakeLists.txt
libs/lxcpp/exception.hpp
libs/lxcpp/namespace.cpp [new file with mode: 0644]
libs/lxcpp/namespace.hpp [new file with mode: 0644]
libs/lxcpp/process.cpp
libs/lxcpp/process.hpp
tests/unit_tests/lxcpp/ut-container.cpp
tests/unit_tests/lxcpp/ut-namespace.cpp [new file with mode: 0644]
tests/unit_tests/lxcpp/ut-process.cpp [new file with mode: 0644]

index 4b6d59d..55fc912 100644 (file)
@@ -120,6 +120,7 @@ bool waitPid(pid_t pid, int& status)
 {
     while (waitpid(pid, &status, 0) == -1) {
         if (errno != EINTR) {
+            LOGE("waitpid() failed: " << getSystemErrorMessage());
             return false;
         }
     }
index 0d49df9..65bd2fc 100644 (file)
@@ -37,6 +37,7 @@ SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES
 
 ## Link libraries ##############################################################
 INCLUDE_DIRECTORIES(${LIBS_FOLDER})
+INCLUDE_DIRECTORIES(${COMMON_FOLDER})
 TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${pkgs_LDFLAGS} Logger)
 
 ## Generate the pc file ########################################################
index 9d59101..12ae299 100644 (file)
@@ -45,6 +45,12 @@ struct ProcessSetupException: public Exception {
     ProcessSetupException(const std::string& message = "Error during setting up a process")
         : Exception(message) {}
 };
+
+struct BadArgument: public Exception {
+    BadArgument(const std::string& message = "Bad argument passed")
+        : Exception(message) {}
+};
+
 } // namespace lxcpp
 
 #endif // LXCPP_EXCEPTION_HPP
diff --git a/libs/lxcpp/namespace.cpp b/libs/lxcpp/namespace.cpp
new file mode 100644 (file)
index 0000000..35bc140
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ *  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   lxcpp container factory definition
+ */
+
+#include "lxcpp/namespace.hpp"
+#include "lxcpp/exception.hpp"
+#include "logger/logger.hpp"
+
+#include <numeric>
+#include <functional>
+
+namespace lxcpp {
+
+Namespace operator|(const Namespace a, const Namespace b)
+{
+    return static_cast<Namespace>(static_cast<std::underlying_type<Namespace>::type>(a) |
+                                  static_cast<std::underlying_type<Namespace>::type>(b));
+}
+
+std::string toString(const Namespace ns)
+{
+    switch(ns) {
+    case Namespace::USER:
+        return "user";
+    case Namespace::MNT:
+        return "mnt";
+    case Namespace::PID:
+        return "pid";
+    case Namespace::UTS:
+        return "uts";
+    case Namespace::IPC:
+        return "ipc";
+    case Namespace::NET:
+        return "net";
+    default:
+        LOGE("Bad namespace passed to the function");
+        throw BadArgument("Bad namespace passed to the function");
+    }
+}
+
+int toFlag(const std::vector<Namespace>& namespaces)
+{
+    Namespace flag = std::accumulate(namespaces.begin(),
+                                     namespaces.end(),
+                                     static_cast<Namespace>(0),
+                                     std::bit_or<Namespace>());
+    return static_cast<int>(flag);
+}
+
+int toFlag(const Namespace ns)
+{
+    return static_cast<int>(ns);
+}
+
+std::string getNsPath(const pid_t pid)
+{
+    return "/proc/" + std::to_string(pid) + "/ns";
+}
+
+std::string getPath(const pid_t pid, const Namespace ns)
+{
+    return getNsPath(pid) + "/" + toString(ns);
+}
+
+} // namespace lxcpp
diff --git a/libs/lxcpp/namespace.hpp b/libs/lxcpp/namespace.hpp
new file mode 100644 (file)
index 0000000..be6d033
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ *  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   process handling routines
+ */
+
+#ifndef LXCPP_NAMESPACE_HPP
+#define LXCPP_NAMESPACE_HPP
+
+#include <sched.h>
+#include <string>
+#include <vector>
+
+namespace lxcpp {
+
+enum class Namespace : int {
+    USER = CLONE_NEWUSER,
+    MNT = CLONE_NEWNS,
+    PID = CLONE_NEWPID,
+    UTS = CLONE_NEWUTS,
+    IPC = CLONE_NEWIPC,
+    NET = CLONE_NEWNET
+};
+
+Namespace operator |(const Namespace a, const Namespace b);
+
+std::string toString(const Namespace ns);
+
+std::string getNsPath(const pid_t pid);
+
+std::string getPath(const pid_t pid, const Namespace ns);
+
+int toFlag(const Namespace ns);
+
+int toFlag(const std::vector<Namespace>& namespaces);
+
+} // namespace lxcpp
+
+#endif // LXCPP_NAMESPACE_HPP
\ No newline at end of file
index 200d12e..68fd62c 100644 (file)
 
 #include "lxcpp/process.hpp"
 #include "lxcpp/exception.hpp"
+
 #include "logger/logger.hpp"
+#include "utils/fd-utils.hpp"
+#include "utils/exception.hpp"
 
 #include <alloca.h>
 #include <sched.h>
 #include <unistd.h>
 #include <sys/wait.h>
+#include <fcntl.h>
 
 namespace lxcpp {
 
-pid_t clone(int (*function)(void *), int flags, void *args) {
+pid_t clone(int (*function)(void *),
+            void *args,
+            const std::vector<Namespace>& namespaces,
+            const int additionalFlags)
+{
     // Won't fail, well known resource name
     size_t stackSize = ::sysconf(_SC_PAGESIZE);
 
     // PAGESIZE is enough, it'll exec after this
     char *stack = static_cast<char*>(::alloca(stackSize));
 
-    pid_t ret;
-    ret = ::clone(function, stack  + stackSize, flags | SIGCHLD, args);
-    if (ret < 0) {
-        LOGE("clone() failed");
-        throw ProcessSetupException("clone() failed");
+    pid_t pid = ::clone(function, stack  + stackSize, toFlag(namespaces) | additionalFlags | SIGCHLD, args);
+    if (pid < 0) {
+        const std::string msg = utils::getSystemErrorMessage();
+        LOGE("clone() failed: " << msg);
+        throw ProcessSetupException("clone() failed " + msg);
+    }
+
+    return pid;
+}
+
+void setns(const std::vector<Namespace>& namespaces)
+{
+    pid_t pid = ::getpid();
+
+    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);
+    }
+
+    // Open FDs connected with the requested namespaces
+    std::vector<int> 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);
+        if(fds[i] < 0) {
+            const std::string msg = 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);
+        }
+    }
+
+    // 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();
+
+            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);
+        }
+        utils::close(fds[i]);
     }
 
-    return ret;
+    utils::close(dirFD);
 }
 
 } // namespace lxcpp
\ No newline at end of file
index 8ca8aaf..1255589 100644 (file)
 #ifndef LXCPP_PROCESS_HPP
 #define LXCPP_PROCESS_HPP
 
+#include "lxcpp/namespace.hpp"
+
 #include <sys/types.h>
+#include <vector>
 
 namespace lxcpp {
 
-pid_t clone(int (*function)(void *), int flags, void *args);
+pid_t clone(int (*function)(void *),
+            void *args,
+            const std::vector<Namespace>& namespaces,
+            const int additionalFlags = 0);
+
+void setns(const std::vector<Namespace>& namespaces);
 
 } // namespace lxcpp
 
index 74ebbfe..6177502 100644 (file)
@@ -1,7 +1,7 @@
 /*
- *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
  *
- *  Contact: Piotr Bartosiewicz <p.bartosiewi@partner.samsung.com>
+ *  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.
@@ -29,8 +29,6 @@
 #include "lxcpp/lxcpp.hpp"
 #include "lxcpp/exception.hpp"
 
-#include <memory>
-
 namespace {
 
 struct Fixture {
diff --git a/tests/unit_tests/lxcpp/ut-namespace.cpp b/tests/unit_tests/lxcpp/ut-namespace.cpp
new file mode 100644 (file)
index 0000000..9e10e81
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ *  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/namespace.hpp"
+#include <iostream>
+#include <sched.h>
+
+namespace {
+
+struct Fixture {
+    Fixture() {}
+    ~Fixture() {}
+};
+
+} // namespace
+
+BOOST_FIXTURE_TEST_SUITE(LxcppNamespaceSuite, Fixture)
+
+using namespace lxcpp;
+
+const std::array<Namespace, 6> NAMESPACES  {{
+        Namespace::USER,
+        Namespace::MNT,
+        Namespace::PID,
+        Namespace::UTS,
+        Namespace::IPC,
+        Namespace::NET
+    }};
+
+BOOST_AUTO_TEST_CASE(OR)
+{
+    Namespace a = Namespace::USER;
+    Namespace b = Namespace::MNT;
+    BOOST_CHECK_EQUAL(CLONE_NEWUSER | CLONE_NEWNS, static_cast<int>(a | b));
+}
+
+BOOST_AUTO_TEST_CASE(GetPath)
+{
+    for(const auto ns: NAMESPACES) {
+        getPath(0, ns);
+    }
+}
+
+BOOST_AUTO_TEST_CASE(ToString)
+{
+    for(const auto ns: NAMESPACES) {
+        toString(ns);
+    }
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/tests/unit_tests/lxcpp/ut-process.cpp b/tests/unit_tests/lxcpp/ut-process.cpp
new file mode 100644 (file)
index 0000000..df9f33d
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ *  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 process helpers
+ */
+
+#include "config.hpp"
+#include "ut.hpp"
+
+#include "lxcpp/process.hpp"
+#include "lxcpp/exception.hpp"
+#include "utils/exception.hpp"
+#include "utils/execute.hpp"
+
+#include <iostream>
+#include <sched.h>
+#include <sys/wait.h>
+
+namespace {
+
+struct Fixture {
+    Fixture() {}
+    ~Fixture() {}
+};
+
+int clonefn(void* /*args*/) {
+    return 0;
+}
+
+} // namespace
+
+BOOST_FIXTURE_TEST_SUITE(LxcppProcessSuite, Fixture)
+
+using namespace lxcpp;
+
+const std::vector<Namespace> NAMESPACES  {{
+        Namespace::USER,
+        Namespace::MNT,
+        Namespace::PID,
+        Namespace::UTS,
+        Namespace::IPC,
+        Namespace::NET
+    }};
+
+BOOST_AUTO_TEST_CASE(Clone)
+{
+    BOOST_CHECK_NO_THROW(clone(clonefn, nullptr, NAMESPACES));
+    BOOST_CHECK_NO_THROW(clone(clonefn, nullptr, {Namespace::MNT}));
+}
+
+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) {
+        try {
+            setns({Namespace::MNT,
+                   Namespace::PID,
+                   Namespace::UTS,
+                   Namespace::IPC,
+                   Namespace::NET
+                  });
+            exit(TEST_PASSED);
+        } 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(SetnsUserNamespace)
+{
+    const int TEST_PASSED = 0;
+    const int ERROR = -1;
+
+    pid_t pid = fork();
+    if (pid==-1) {
+        BOOST_REQUIRE(false);
+    } else if(pid ==0) {
+        try {
+            setns({Namespace::USER});
+            exit(ERROR);
+        } catch(ProcessSetupException) {
+            exit(TEST_PASSED);
+        } catch(...) {
+            exit(ERROR);
+        }
+    } else if(pid>0) {
+        int status;
+        BOOST_REQUIRE(utils::waitPid(pid, status));
+        BOOST_REQUIRE(status == TEST_PASSED);
+    }
+}
+
+BOOST_AUTO_TEST_SUITE_END()