Add getns() for namespaces. Review: https://reviews.apache.org/r/27127
Project: http://git-wip-us.apache.org/repos/asf/mesos/repo Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/36505730 Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/36505730 Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/36505730 Branch: refs/heads/master Commit: 36505730ac59d5255cd616618ce78e2ee88fca3d Parents: 57447a7 Author: Ian Downes <[email protected]> Authored: Fri Oct 24 11:49:18 2014 -0700 Committer: Ian Downes <[email protected]> Committed: Tue Oct 28 12:04:16 2014 -0700 ---------------------------------------------------------------------- src/linux/ns.hpp | 25 +++++++++++++++++++ src/tests/ns_tests.cpp | 60 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/mesos/blob/36505730/src/linux/ns.hpp ---------------------------------------------------------------------- diff --git a/src/linux/ns.hpp b/src/linux/ns.hpp index 53c95a4..60adaa4 100644 --- a/src/linux/ns.hpp +++ b/src/linux/ns.hpp @@ -198,6 +198,31 @@ inline Try<Nothing> setns(pid_t pid, const std::string& ns) return ns::setns(path, ns); } + +// Get the inode number of the specified namespace for the specified +// pid. The inode number identifies the namespace and can be used for +// comparisons, i.e., two processes with the same inode for a given +// namespace type are in the same namespace. +inline Try<ino_t> getns(pid_t pid, const std::string& ns) +{ + if (!os::exists(pid)) { + return Error("Pid " + stringify(pid) + " does not exist"); + } + + if (ns::namespaces().count(ns) < 1) { + return Error("Namespace '" + ns + "' is not supported"); + } + + std::string path = path::join("/proc", stringify(pid), "ns", ns); + struct stat s; + if (::stat(path.c_str(), &s) < 0) { + return ErrnoError("Failed to stat " + ns + " namespace handle" + " for pid " + stringify(pid)); + } + + return s.st_ino; +} + } // namespace ns { #endif // __LINUX_NS_HPP__ http://git-wip-us.apache.org/repos/asf/mesos/blob/36505730/src/tests/ns_tests.cpp ---------------------------------------------------------------------- diff --git a/src/tests/ns_tests.cpp b/src/tests/ns_tests.cpp index c4cf9ab..30218cf 100644 --- a/src/tests/ns_tests.cpp +++ b/src/tests/ns_tests.cpp @@ -154,3 +154,63 @@ TEST(NsTest, ROOT_setnsMultipleThreads) EXPECT_EQ(0, pthread_cancel(pthread)); EXPECT_EQ(0, pthread_join(pthread, NULL)); } + + +// Use a different child function for clone because it requires +// int(*)(void*). +static int childGetns(void* arg) +{ + // Sleep until killed. + while (true) { sleep(1); } + + ABORT("Error, child should be killed before reaching here"); +} + + +// Test that we can get the namespace inodes for a forked child. +TEST(NsTest, ROOT_getns) +{ + set<string> namespaces = ns::namespaces(); + + // ns::setns() does not support "pid". + namespaces.erase("pid"); + + // Use the first other namespace available. + ASSERT_FALSE(namespaces.empty()); + string ns = *(namespaces.begin()); + + ASSERT_SOME(ns::getns(::getpid(), ns)); + + Try<int> nstype = ns::nstype(ns); + ASSERT_SOME(nstype); + + // 8 MiB stack for child. + static unsigned long long stack[(8*1024*1024)/sizeof(unsigned long long)]; + + pid_t pid = clone( + childGetns, + &stack[sizeof(stack)/sizeof(stack[0]) - 1], // Stack grows down. + SIGCHLD | nstype.get(), + NULL); + + ASSERT_NE(-1, pid); + + // Continue in parent. + Try<ino_t> nsParent = ns::getns(::getpid(), ns); + ASSERT_SOME(nsParent); + + Try<ino_t> nsChild = ns::getns(pid, ns); + ASSERT_SOME(nsChild); + + // Child should be in a different namespace. + EXPECT_NE(nsParent.get(), nsChild.get()); + + // Kill the child process. + ASSERT_NE(-1, ::kill(pid, SIGKILL)); + + // Wait for the child process. + int status; + EXPECT_NE(-1, ::waitpid((pid_t) -1, &status, 0)); + ASSERT_TRUE(WIFSIGNALED(status)); + EXPECT_EQ(SIGKILL, WTERMSIG(status)); +}
