You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mesos.apache.org by ji...@apache.org on 2014/06/06 19:07:01 UTC

git commit: Added setns utilities to stout.

Repository: mesos
Updated Branches:
  refs/heads/master 1920efdd8 -> 5cbdbf2a5


Added setns utilities to stout.

Review: https://reviews.apache.org/r/22160


Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/5cbdbf2a
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/5cbdbf2a
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/5cbdbf2a

Branch: refs/heads/master
Commit: 5cbdbf2a5923a04beadf0e812131ca05602c59c3
Parents: 1920efd
Author: Jie Yu <yu...@gmail.com>
Authored: Mon Jun 2 13:43:29 2014 -0700
Committer: Jie Yu <yu...@gmail.com>
Committed: Fri Jun 6 10:06:37 2014 -0700

----------------------------------------------------------------------
 3rdparty/libprocess/3rdparty/Makefile.am        |   1 +
 3rdparty/libprocess/3rdparty/stout/Makefile.am  |   2 +
 .../3rdparty/stout/include/stout/os/setns.hpp   | 200 +++++++++++++++++++
 .../3rdparty/stout/tests/os/setns_tests.cpp     |  53 +++++
 4 files changed, 256 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/5cbdbf2a/3rdparty/libprocess/3rdparty/Makefile.am
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/3rdparty/Makefile.am b/3rdparty/libprocess/3rdparty/Makefile.am
index 1474bbc..3359907 100644
--- a/3rdparty/libprocess/3rdparty/Makefile.am
+++ b/3rdparty/libprocess/3rdparty/Makefile.am
@@ -161,6 +161,7 @@ stout_tests_SOURCES =				\
 
 if OS_LINUX
   stout_tests_SOURCES += $(STOUT)/tests/proc_tests.cpp
+  stout_tests_SOURCES += $(STOUT)/tests/os/setns_tests.cpp
 endif
 
 stout_tests_CPPFLAGS =				\

http://git-wip-us.apache.org/repos/asf/mesos/blob/5cbdbf2a/3rdparty/libprocess/3rdparty/stout/Makefile.am
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/3rdparty/stout/Makefile.am b/3rdparty/libprocess/3rdparty/stout/Makefile.am
index 8f32a66..00d8c61 100644
--- a/3rdparty/libprocess/3rdparty/stout/Makefile.am
+++ b/3rdparty/libprocess/3rdparty/stout/Makefile.am
@@ -52,6 +52,7 @@ EXTRA_DIST =					\
   include/stout/os/process.hpp			\
   include/stout/os/read.hpp			\
   include/stout/os/sendfile.hpp			\
+  include/stout/os/setns.hpp			\
   include/stout/os/shell.hpp			\
   include/stout/os/signals.hpp			\
   include/stout/os/permissions.hpp		\
@@ -93,6 +94,7 @@ EXTRA_DIST =					\
   tests/option_tests.cpp			\
   tests/os_tests.cpp				\
   tests/os/sendfile_tests.cpp			\
+  tests/os/setns_tests.cpp			\
   tests/os/signals_tests.cpp			\
   tests/proc_tests.cpp				\
   tests/protobuf_tests.cpp			\

http://git-wip-us.apache.org/repos/asf/mesos/blob/5cbdbf2a/3rdparty/libprocess/3rdparty/stout/include/stout/os/setns.hpp
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/3rdparty/stout/include/stout/os/setns.hpp b/3rdparty/libprocess/3rdparty/stout/include/stout/os/setns.hpp
new file mode 100644
index 0000000..5ec2602
--- /dev/null
+++ b/3rdparty/libprocess/3rdparty/stout/include/stout/os/setns.hpp
@@ -0,0 +1,200 @@
+/**
+ * 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.
+ */
+
+#ifndef __STOUT_OS_SETNS_HPP__
+#define __STOUT_OS_SETNS_HPP__
+
+// This file contains Linux-only OS utilities.
+#ifndef __linux__
+#error "stout/os/setns.hpp is only available on Linux systems."
+#endif
+
+#include <sched.h>
+#include <unistd.h>
+
+#include <sys/syscall.h>
+
+#include <set>
+#include <string>
+
+#include <stout/error.hpp>
+#include <stout/hashmap.hpp>
+#include <stout/nothing.hpp>
+#include <stout/os.hpp>
+#include <stout/path.hpp>
+#include <stout/proc.hpp>
+#include <stout/stringify.hpp>
+#include <stout/try.hpp>
+
+#include <stout/os/exists.hpp>
+#include <stout/os/ls.hpp>
+
+namespace os {
+
+// Returns all the supported namespaces by the kernel.
+inline std::set<std::string> namespaces()
+{
+  std::set<std::string> result;
+  foreach (const std::string& ns, os::ls("/proc/self/ns")) {
+    result.insert(ns);
+  }
+  return result;
+}
+
+
+// Returns the nstype (e.g., CLONE_NEWNET, CLONE_NEWNS, etc.) for the
+// given namespace which will be used when calling ::setns.
+inline Try<int> nstype(const std::string& ns)
+{
+  hashmap<std::string, int> nstypes;
+
+#ifdef CLONE_NEWNS
+  nstypes["mnt"] = CLONE_NEWNS;
+#else
+  nstypes["mnt"] = 0x00020000;
+#endif
+
+#ifdef CLONE_NEWUTS
+  nstypes["uts"] = CLONE_NEWUTS;
+#else
+  nstypes["uts"] = 0x04000000;
+#endif
+
+#ifdef CLONE_NEWIPC
+  nstypes["ipc"] = CLONE_NEWIPC;
+#else
+  nstypes["ipc"] = 0x08000000;
+#endif
+
+#ifdef CLONE_NEWNET
+  nstypes["net"] = CLONE_NEWNET;
+#else
+  nstypes["net"] = 0x40000000;
+#endif
+
+#ifdef CLONE_NEWUSER
+  nstypes["user"] = CLONE_NEWUSER;
+#else
+  nstypes["user"] = 0x10000000;
+#endif
+
+#ifndef CLONE_NEWPID
+  nstypes["pid"] = CLONE_NEWPID;
+#else
+  nstypes["pid"] = 0x20000000;
+#endif
+
+  if (!nstypes.contains(ns)) {
+    return Error("Unknown namespace '" + ns + "'");
+  }
+
+  return nstypes[ns];
+}
+
+
+// Re-associate the calling process with the specified namespace. The
+// path refers to one of the corresponding namespace entries in the
+// /proc/[pid]/ns/ directory (or bind mounted elsewhere). We do not
+// allow a process with multiple threads to call this function because
+// it will lead to some weird situations where different threads of a
+// process are in different namespaces.
+inline Try<Nothing> setns(const std::string& path, const std::string& ns)
+{
+  // Return error if there're multiple threads in the calling process.
+  Try<std::set<pid_t> > threads = proc::threads(::getpid());
+  if (threads.isError()) {
+    return Error(
+        "Failed to get the threads of the current process: " +
+        threads.error());
+  } else if (threads.get().size() > 1) {
+    return Error("Multiple threads exist in the current process");
+  }
+
+  if (os::namespaces().count(ns) == 0) {
+    return Error("Namespace '" + ns + "' is not supported");
+  }
+
+  // Currently, we don't support pid namespace as its semantics is
+  // different from other namespaces (instead of re-associating the
+  // calling thread, it re-associates the *children* of the calling
+  // thread with the specified namespace).
+  if (ns == "pid") {
+    return Error("Pid namespace is not supported");
+  }
+
+#ifdef O_CLOEXEC
+  Try<int> fd = os::open(path, O_RDONLY | O_CLOEXEC);
+#else
+  Try<int> fd = os::open(path, O_RDONLY);
+#endif
+
+  if (fd.isError()) {
+    return Error("Failed to open '" + path + "': " + fd.error());
+  }
+
+#ifndef O_CLOEXEC
+  Try<Nothing> cloexec = os::cloexec(fd.get());
+  if (cloexec.isError()) {
+    os::close(fd.get());
+    return Error("Failed to cloexec: " + cloexec.error());
+  }
+#endif
+
+  Try<int> nstype = os::nstype(ns);
+  if (nstype.isError()) {
+    return Error(nstype.error());
+  }
+
+#ifdef SYS_setns
+  int ret = ::syscall(SYS_setns, fd.get(), nstype.get());
+#elif __x86_64__
+  // A workaround for those hosts that have an old glibc (older than
+  // 2.14) but have a new kernel. The magic number '308' here is the
+  // syscall number for 'setns' on x86_64 architecture.
+  int ret = ::syscall(308, fd.get(), nstype.get());
+#else
+#error "setns is not available"
+#endif
+
+  if (ret == -1) {
+    // Save the errno as it might be overwritten by 'os::close' below.
+    ErrnoError error;
+    os::close(fd.get());
+    return error;
+  }
+
+  os::close(fd.get());
+  return Nothing();
+}
+
+
+// Re-associate the calling process with the specified namespace. The
+// pid specifies the process whose namespace we will associate.
+inline Try<Nothing> setns(pid_t pid, const std::string& ns)
+{
+  if (!os::exists(pid)) {
+    return Error("Pid " + stringify(pid) + " does not exist");
+  }
+
+  std::string path = path::join("/proc", stringify(pid), "ns", ns);
+  if (!os::exists(path)) {
+    return Error("Namespace '" + ns + "' is not supported");
+  }
+
+  return os::setns(path, ns);
+}
+
+} // namespace os {
+
+#endif // __STOUT_OS_SETNS_HPP__

http://git-wip-us.apache.org/repos/asf/mesos/blob/5cbdbf2a/3rdparty/libprocess/3rdparty/stout/tests/os/setns_tests.cpp
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/3rdparty/stout/tests/os/setns_tests.cpp b/3rdparty/libprocess/3rdparty/stout/tests/os/setns_tests.cpp
new file mode 100644
index 0000000..f6d79b1
--- /dev/null
+++ b/3rdparty/libprocess/3rdparty/stout/tests/os/setns_tests.cpp
@@ -0,0 +1,53 @@
+#include <pthread.h>
+#include <unistd.h>
+
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include <stout/gtest.hpp>
+#include <stout/os.hpp>
+
+#include <stout/os/setns.hpp>
+
+using std::set;
+using std::string;
+
+
+static void* child(void* arg)
+{
+  // Newly created threads have PTHREAD_CANCEL_ENABLE and
+  // PTHREAD_CANCEL_DEFERRED so they can be cancelled.
+  while (true) { os::sleep(Seconds(1)); }
+}
+
+
+TEST(OsSetnsTest, setns)
+{
+  if (os::user() != "root") {
+    return;
+  }
+
+  // Get all the available namespaces.
+  set<string> namespaces = os::namespaces();
+
+  foreach (const string& ns, namespaces) {
+    if (ns == "pid") {
+      EXPECT_ERROR(os::setns(::getpid(), ns));
+    } else {
+      EXPECT_SOME(os::setns(::getpid(), ns));
+    }
+  }
+
+  // Do not allow multi-threaded environment.
+  pthread_t pthread;
+  ASSERT_EQ(0, pthread_create(&pthread, NULL, child, NULL));
+
+  foreach (const string& ns, namespaces) {
+    EXPECT_ERROR(os::setns(::getpid(), ns));
+  }
+
+  // Terminate the threads.
+  EXPECT_EQ(0, pthread_cancel(pthread));
+  EXPECT_EQ(0, pthread_join(pthread, NULL));
+}