Apache Mesos
ns.hpp
Go to the documentation of this file.
1 // Licensed to the Apache Software Foundation (ASF) under one
2 // or more contributor license agreements. See the NOTICE file
3 // distributed with this work for additional information
4 // regarding copyright ownership. The ASF licenses this file
5 // to you under the Apache License, Version 2.0 (the
6 // "License"); you may not use this file except in compliance
7 // with the License. You may obtain a copy of the License at
8 //
9 // http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 
17 #ifndef __LINUX_NS_HPP__
18 #define __LINUX_NS_HPP__
19 
20 // This file contains Linux-only OS utilities.
21 #ifndef __linux__
22 #error "linux/ns.hpp is only available on Linux systems."
23 #endif
24 
25 #include <sched.h>
26 
27 #include <sys/syscall.h>
28 
29 #include <queue>
30 #include <set>
31 #include <string>
32 #include <thread>
33 
34 #include <process/future.hpp>
35 
36 #include <stout/lambda.hpp>
37 #include <stout/nothing.hpp>
38 #include <stout/option.hpp>
39 #include <stout/result.hpp>
40 #include <stout/try.hpp>
41 
42 #ifndef CLONE_NEWNS
43 #define CLONE_NEWNS 0x00020000
44 #endif
45 
46 #ifndef CLONE_NEWUTS
47 #define CLONE_NEWUTS 0x04000000
48 #endif
49 
50 #ifndef CLONE_NEWIPC
51 #define CLONE_NEWIPC 0x08000000
52 #endif
53 
54 #ifndef CLONE_NEWPID
55 #define CLONE_NEWPID 0x20000000
56 #endif
57 
58 #ifndef CLONE_NEWNET
59 #define CLONE_NEWNET 0x40000000
60 #endif
61 
62 #ifndef CLONE_NEWUSER
63 #define CLONE_NEWUSER 0x10000000
64 #endif
65 
66 #ifndef CLONE_NEWCGROUP
67 #define CLONE_NEWCGROUP 0x02000000
68 #endif
69 
70 // Define a 'setns' for compilation environments that don't already
71 // have one.
72 inline int setns(int fd, int nstype)
73 {
74 #ifdef SYS_setns
75  return ::syscall(SYS_setns, fd, nstype);
76 #elif defined(__x86_64__)
77  // A workaround for those hosts that have an old glibc (older than
78  // 2.14) but have a new kernel. The magic number '308' here is the
79  // syscall number for 'setns' on x86_64 architecture.
80  return ::syscall(308, fd, nstype);
81 #else
82 #error "setns is not available"
83 #endif
84 }
85 
86 namespace ns {
87 
88 // Returns the nstype (e.g., CLONE_NEWNET, CLONE_NEWNS, etc.) for the
89 // given namespace which can be used when calling ::setns.
90 Try<int> nstype(const std::string& ns);
91 
92 
93 // Given a single CLONE_NEW* constant, return the corresponding namespace
94 // name. This is the inverse of ns::nstype().
95 Try<std::string> nsname(int nsType);
96 
97 
98 // Returns all the configured kernel namespaces.
99 std::set<int> nstypes();
100 
101 
102 // Returns true if all the given CLONE_NEW* constants are supported
103 // in the running kernel. If CLONE_NEWUSER is specified, the kernel
104 // version must be at least 3.12.0 since prior to that version, major
105 // kernel subsystems (e.g. XFS) did not implement user namespace
106 // support. See also user_namespaces(7).
107 Try<bool> supported(int nsTypes);
108 
109 
110 // Re-associate the calling process with the specified namespace. The
111 // path refers to one of the corresponding namespace entries in the
112 // /proc/[pid]/ns/ directory (or bind mounted elsewhere). We do not
113 // allow a process with multiple threads to call this function because
114 // it will lead to some weird situations where different threads of a
115 // process are in different namespaces.
117  const std::string& path,
118  const std::string& ns,
119  bool checkMultithreaded = true);
120 
121 
122 // Re-associate the calling process with the specified namespace. The
123 // pid specifies the process whose namespace we will associate.
125  pid_t pid,
126  const std::string& ns,
127  bool checkMultithreaded = true);
128 
129 
130 // Get the inode number of the specified namespace for the specified
131 // pid. The inode number identifies the namespace and can be used for
132 // comparisons, i.e., two processes with the same inode for a given
133 // namespace type are in the same namespace.
134 Result<ino_t> getns(pid_t pid, const std::string& ns);
135 
136 
176  pid_t target,
177  int nstypes,
178  const lambda::function<int()>& f,
179  int flags);
180 
181 
182 // Returns the namespace flags in the string form of bitwise-ORing the
183 // flags, e.g., CLONE_NEWNS | CLONE_NEWNET.
184 std::string stringify(int flags);
185 
186 
187 // The NamespaceRunner runs any function in a specified namespace.
188 // To do that it manages a separate thread which would be re-associated
189 // with that namespace.
191 {
192 public:
194  {
195  // Start the looper thread.
196  thread.reset(new std::thread(&NamespaceRunner::loop, this));
197  }
198 
200  {
201  // Shutdown the queue.
202  queue.shutdown();
203  // Wait for the thread to complete.
204  thread->join();
205  thread.reset();
206  }
207 
208  // Run any function in a specified namespace.
209  template <typename T>
211  const std::string& path,
212  const std::string& ns,
213  const lambda::function<Try<T>()>& func)
214  {
215  std::shared_ptr<process::Promise<T>> promise(
216  new process::Promise<T>);
217  process::Future<T> future = promise->future();
218 
219  // Put a function to the queue, the function will be called
220  // in the thread. The thread will be re-associated with the
221  // specified namespace.
222  queue.put([=]{
223  Try<Nothing> setns = ::ns::setns(path, ns, false);
224  if (setns.isError()) {
225  promise->fail(setns.error());
226  } else {
227  promise->set(func());
228  }
229  });
230 
231  return future;
232  }
233 
234 private:
235  typedef lambda::function<void()> Func;
236 
237  // The thread loop.
238  void loop()
239  {
240  for (;;) {
241  // Get a function from the queue.
242  Option<Func> func = queue.get();
243 
244  // Stop the thread if the queue is shutdowned.
245  if (func.isNone()) {
246  break;
247  }
248 
249  // Call the function, it re-associates the thread with the
250  // specified namespace and calls the initial user function.
251  func.get()();
252  }
253  }
254 
255  // It's not safe to use process::Queue when not all of its callers are
256  // managed by libprocess. Calling Future::await() in looper thread
257  // might cause the looper thread to be donated to a libprocess Process.
258  // If that Process is very busy (e.g., master or agent Process), it's
259  // possible that the looper thread will never re-gain control.
260  //
261  // ProcessingQueue uses mutex and condition variable to solve this
262  // problem. ProcessingQueue::get() can block the thread. The main
263  // use cases for the class are thread workers and thread pools.
264  template <typename T>
265  class ProcessingQueue
266  {
267  public:
268  ProcessingQueue() : finished(false) {}
269 
270  ~ProcessingQueue() = default;
271 
272  // Add an element to the queue and notify one client.
273  void put(T&& t)
274  {
275  synchronized (mutex) {
276  queue.push(std::forward<T>(t));
277  cond.notify_one();
278  }
279  }
280 
281  // NOTE: This function blocks the thread. It returns the oldest
282  // element from the queue and returns None() if the queue is
283  // shutdowned.
284  Option<T> get()
285  {
286  synchronized (mutex) {
287  // Wait for either a new queue element or queue shutdown.
288  while (queue.empty() && !finished) {
289  synchronized_wait(&cond, &mutex);
290  }
291 
292  if (finished) {
293  // The queue is shutdowned.
294  return None();
295  }
296 
297  // Return the oldest element from the queue.
298  T t = std::move(queue.front());
299  queue.pop();
300  return Some(std::move(t));
301  }
302  }
303 
304  // Shutdown the queue and notify all clients.
305  void shutdown() {
306  synchronized (mutex) {
307  finished = true;
308  std::queue<T>().swap(queue);
309  cond.notify_all();
310  }
311  }
312 
313  private:
314  std::mutex mutex;
315  std::condition_variable cond;
316  std::queue<T> queue;
317  bool finished;
318  };
319 
320  ProcessingQueue<Func> queue;
321  std::unique_ptr<std::thread> thread;
322 };
323 
324 } // namespace ns {
325 
326 #endif // __LINUX_NS_HPP__
process::Future< T > run(const std::string &path, const std::string &ns, const lambda::function< Try< T >()> &func)
Definition: ns.hpp:210
Definition: path.hpp:26
Definition: option.hpp:28
Try< pid_t > clone(pid_t target, int nstypes, const lambda::function< int()> &f, int flags)
Performs an os::clone after entering a set of namespaces for the specified target process...
Try< bool > supported(int nsTypes)
F && f
Definition: defer.hpp:270
Definition: check.hpp:33
int setns(int fd, int nstype)
Definition: ns.hpp:72
Try< std::string > nsname(int nsType)
Try< Nothing > setns(const std::string &path, const std::string &ns, bool checkMultithreaded=true)
Definition: check.hpp:30
void synchronized_wait(CV *cv, Lock *lock)
Waits on the condition variable associated with &#39;lock&#39; which has already been synchronized.
std::set< int > nstypes()
DWORD pid_t
Definition: windows.hpp:181
NamespaceRunner()
Definition: ns.hpp:193
~NamespaceRunner()
Definition: ns.hpp:199
Definition: future.hpp:74
const T & get() const &
Definition: option.hpp:118
Protocol< PromiseRequest, PromiseResponse > promise
static Try error(const E &e)
Definition: try.hpp:42
_Some< typename std::decay< T >::type > Some(T &&t)
Definition: some.hpp:42
Definition: none.hpp:27
bool isError() const
Definition: try.hpp:71
Result< ino_t > getns(pid_t pid, const std::string &ns)
Definition: ns.hpp:86
Definition: ns.hpp:190
Try< int > nstype(const std::string &ns)
bool isNone() const
Definition: option.hpp:116
std::string stringify(int flags)
Definition: parse.hpp:33
Definition: future.hpp:58