Apache Mesos
fork.hpp
Go to the documentation of this file.
1 // Licensed under the Apache License, Version 2.0 (the "License");
2 // you may not use this file except in compliance with the License.
3 // You may obtain a copy of the License at
4 //
5 // http://www.apache.org/licenses/LICENSE-2.0
6 //
7 // Unless required by applicable law or agreed to in writing, software
8 // distributed under the License is distributed on an "AS IS" BASIS,
9 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 // See the License for the specific language governing permissions and
11 // limitations under the License.
12 
13 #ifndef __STOUT_OS_POSIX_FORK_HPP__
14 #define __STOUT_OS_POSIX_FORK_HPP__
15 
16 #include <fcntl.h>
17 #include <unistd.h>
18 
19 #include <sys/mman.h>
20 #ifdef __FreeBSD__
21 #include <sys/stat.h>
22 #endif // __FreeBSD__
23 #include <sys/types.h>
24 #include <sys/wait.h>
25 
26 #include <atomic>
27 #include <list>
28 #include <memory>
29 #include <set>
30 #include <string>
31 
32 #include <stout/abort.hpp>
33 #include <stout/check.hpp>
34 #include <stout/error.hpp>
35 #include <stout/exit.hpp>
36 #include <stout/foreach.hpp>
37 #include <stout/os/strerror.hpp>
38 #include <stout/stringify.hpp>
39 #include <stout/try.hpp>
40 
41 #include <stout/os/close.hpp>
42 #include <stout/os/ftruncate.hpp>
43 #include <stout/os/process.hpp>
44 
45 
46 // Abstractions around forking process trees. You can declare a
47 // process tree "template" using 'Fork', 'Exec', and 'Wait'. For
48 // example, to describe a simple "fork/exec" you can do:
49 //
50 // Fork f = Fork(Exec("sleep 10));
51 //
52 // The command passed to an 'Exec' is run via 'sh -c'. You can
53 // construct more complicated templates via nesting, for example:
54 //
55 // Fork f =
56 // Fork(None(),
57 // Fork(Exec("echo 'grandchild 1'")),
58 // Fork(None(),
59 // Fork(Exec("echo 'great-grandchild'")),
60 // Exec("echo 'grandchild 2'"))
61 // Exec("echo 'child'"));
62 //
63 // Note that the first argument to 'Fork' here is an optional function
64 // that can be invoked before forking any more children or executing a
65 // command. THIS FUNCTION SHOULD BE ASYNC SIGNAL SAFE.
66 //
67 // To wait for children, you can use 'Wait' instead of 'Exec', for
68 // example:
69 //
70 // Fork f =
71 // Fork(None(),
72 // Fork(Exec("echo 'grandchild 1'")),
73 // Fork(Exec("echo 'grandchild 2'")),
74 // Wait());
75 //
76 // You can also omit either an 'Exec' or a 'Wait' and the forked
77 // process will just 'exit(0)'. For example, the following will cause
78 // to processes to get reparented by 'init'.
79 //
80 // Fork f =
81 // Fork(None(),
82 // Fork(Exec("echo 'grandchild 1'")),
83 // Fork(Exec("echo 'grandchild 2'")));
84 //
85 // A template can be instantiated by invoking the 'Fork' as a
86 // functor. For example, using any of the templates above we can do:
87 //
88 // Try<ProcessTree> tree = f();
89 //
90 // It's important to note that the process tree returned represents
91 // the instant in time after the forking has completed but before
92 // 'Exec', 'Wait' or 'exit(0)' has occurred (i.e., the process tree
93 // will be complete).
94 
95 namespace os {
96 
97 // Forward declaration.
99 
100 
101 struct Exec
102 {
103  Exec(const std::string& _command)
104  : command(_command) {}
105 
106  const std::string command;
107 };
108 
109 
110 struct Wait {};
111 
112 
113 struct Fork
114 {
115  // -+- parent.
116  Fork(const Option<void(*)()>& _function,
117  const Exec& _exec)
118  : function(_function),
119  exec(_exec) {}
120 
121  Fork(const Exec& _exec) : exec(_exec) {}
122 
123  // -+- parent
124  // \--- child.
125  Fork(const Option<void(*)()>& _function,
126  const Fork& fork1)
127  : function(_function)
128  {
129  children.push_back(fork1);
130  }
131 
132  Fork(const Option<void(*)()>& _function,
133  const Fork& fork1,
134  const Exec& _exec)
135  : function(_function),
136  exec(_exec)
137  {
138  children.push_back(fork1);
139  }
140 
141  Fork(const Option<void(*)()>& _function,
142  const Fork& fork1,
143  const Wait& _wait)
144  : function(_function),
145  wait(_wait)
146  {
147  children.push_back(fork1);
148  }
149 
150 
151  // -+- parent
152  // |--- child
153  // \--- child.
154  Fork(const Option<void(*)()>& _function,
155  const Fork& fork1,
156  const Fork& fork2)
157  : function(_function)
158  {
159  children.push_back(fork1);
160  children.push_back(fork2);
161  }
162 
163  Fork(const Option<void(*)()>& _function,
164  const Fork& fork1,
165  const Fork& fork2,
166  const Exec& _exec)
167  : function(_function),
168  exec(_exec)
169  {
170  children.push_back(fork1);
171  children.push_back(fork2);
172  }
173 
174  Fork(const Option<void(*)()>& _function,
175  const Fork& fork1,
176  const Fork& fork2,
177  const Wait& _wait)
178  : function(_function),
179  wait(_wait)
180  {
181  children.push_back(fork1);
182  children.push_back(fork2);
183  }
184 
185 
186  // -+- parent
187  // |--- child
188  // |--- child
189  // \--- child.
190  Fork(const Option<void(*)()>& _function,
191  const Fork& fork1,
192  const Fork& fork2,
193  const Fork& fork3)
194  : function(_function)
195  {
196  children.push_back(fork1);
197  children.push_back(fork2);
198  children.push_back(fork3);
199  }
200 
201  Fork(const Option<void(*)()>& _function,
202  const Fork& fork1,
203  const Fork& fork2,
204  const Fork& fork3,
205  const Exec& _exec)
206  : function(_function),
207  exec(_exec)
208  {
209  children.push_back(fork1);
210  children.push_back(fork2);
211  children.push_back(fork3);
212  }
213 
214  Fork(const Option<void(*)()>& _function,
215  const Fork& fork1,
216  const Fork& fork2,
217  const Fork& fork3,
218  const Wait& _wait)
219  : function(_function),
220  wait(_wait)
221  {
222  children.push_back(fork1);
223  children.push_back(fork2);
224  children.push_back(fork3);
225  }
226 
227 private:
228  // Represents the "tree" of descendants where each node has a
229  // pointer (into shared memory) from which we can read the
230  // descendants process information as well as a vector of children.
231  struct Tree
232  {
233  // NOTE: This struct is stored in shared memory and thus cannot
234  // hold any pointers to heap allocated memory.
235  struct Memory {
240 
241  std::atomic_bool set; // Has this been initialized?
242  };
243 
244  std::shared_ptr<Memory> memory;
245  std::vector<Tree> children;
246  };
247 
248  // We use shared memory to "share" the pids of forked descendants.
249  // The benefit of shared memory over pipes is that each forked
250  // process can read its descendants' pids leading to a simpler
251  // implementation (with pipes, only one reader can ever read the
252  // value from the pipe, forcing much more complicated coordination).
253  //
254  // Shared memory works like a file (in memory) that gets deleted by
255  // "unlinking" it, but it won't get completely deleted until all
256  // open file descriptors referencing it have been closed. Each
257  // forked process has the shared memory mapped into it as well as an
258  // open file descriptor, both of which should get cleaned up
259  // automagically when the process exits, but we use a special
260  // "deleter" (in combination with shared_ptr) in order to clean this
261  // stuff up when we are actually finished using the shared memory.
262  struct SharedMemoryDeleter
263  {
264  SharedMemoryDeleter(int _fd) : fd(_fd) {}
265 
266  void operator()(Tree::Memory* process) const
267  {
268  if (munmap(process, sizeof(Tree::Memory)) == -1) {
269  ABORT(std::string("Failed to unmap memory: ") + os::strerror(errno));
270  }
271  if (::close(fd) == -1) {
272  ABORT(std::string("Failed to close shared memory file descriptor: ") +
273  os::strerror(errno));
274  }
275  }
276 
277  const int fd;
278  };
279 
280  // Constructs a Tree (see above) from this fork template.
281  Try<Tree> prepare() const
282  {
283  static std::atomic_int forks(0);
284 
285  // Each "instance" of an instantiated Fork needs a unique name for
286  // creating shared memory.
287  int instance = forks.fetch_add(1);
288 
289  std::string name =
290  "/stout-forks-" + stringify(getpid()) + stringify(instance);
291 
292  int fd = shm_open(name.c_str(), O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
293 
294  if (fd == -1) {
295  return ErrnoError("Failed to open a shared memory object");
296  }
297 
298  Try<Nothing> truncated = ftruncate(fd, sizeof(Tree::Memory));
299  if (truncated.isError()) {
300  return Error(
301  "Failed to set size of shared memory object: " + truncated.error());
302  }
303 
304  void* memory = mmap(
305  nullptr,
306  sizeof(Tree::Memory),
307  PROT_READ | PROT_WRITE, MAP_SHARED,
308  fd,
309  0);
310 
311  if (memory == MAP_FAILED) {
312  return ErrnoError("Failed to map shared memory object");
313  }
314 
315  if (shm_unlink(name.c_str()) == -1) {
316  return ErrnoError("Failed to unlink shared memory object");
317  }
318 
319  SharedMemoryDeleter deleter(fd);
320 
321  Tree tree;
322  tree.memory = std::shared_ptr<Tree::Memory>((Tree::Memory*)memory, deleter);
323  tree.memory->set.store(false);
324 
325  for (size_t i = 0; i < children.size(); i++) {
326  Try<Tree> tree_ = children[i].prepare();
327  if (tree_.isError()) {
328  return Error(tree_.error());
329  }
330  tree.children.push_back(tree_.get());
331  }
332 
333  return tree;
334  }
335 
336  // Performs the fork, executes the function, recursively
337  // instantiates any children, and then executes/waits/exits.
338  pid_t instantiate(const Tree& tree) const
339  {
340  pid_t pid = ::fork();
341  if (pid > 0) {
342  return pid;
343  }
344 
345  // Set the basic process information into shared memory.
346  tree.memory->pid = getpid();
347  tree.memory->parent = getppid();
348  tree.memory->group = getpgid(0);
349  tree.memory->session = getsid(0);
350  tree.memory->set.store(true);
351 
352  // Execute the function, if any.
353  if (function.isSome()) {
354  function.get()();
355  }
356 
357  // Fork the children, if any.
358  CHECK(children.size() == tree.children.size());
359  std::set<pid_t> pids;
360  for (size_t i = 0; i < children.size(); i++) {
361  pids.insert(children[i].instantiate(tree.children[i]));
362  }
363 
364  // Execute or wait.
365  if (exec.isSome()) {
366  // Execute the command (via '/bin/sh -c command').
367  const char* command = exec->command.c_str();
368  execlp("sh", "sh", "-c", command, (char*) nullptr);
369  EXIT(EXIT_FAILURE)
370  << "Failed to execute '" << command << "': " << os::strerror(errno);
371  } else if (wait.isSome()) {
372  foreach (pid_t pid, pids) {
373  // TODO(benh): Check for signal interruption or other errors.
374  waitpid(pid, nullptr, 0);
375  }
376  }
377 
378  exit(0);
379  return -1;
380  }
381 
382  // Waits for all of the descendant processes in the tree to update
383  // their pids and constructs a ProcessTree using the Tree::Memory
384  // information from shared memory.
385  static Try<ProcessTree> coordinate(const Tree& tree)
386  {
387  // Wait for the forked process.
388  // TODO(benh): Don't wait forever?
389  while (!tree.memory->set.load());
390 
391  // All processes in the returned ProcessTree will have the
392  // command-line of the top level process, since we construct the
393  // tree using post-fork pre-exec information. So, we'll grab the
394  // command of the current process here.
395  Result<Process> self = os::process(getpid());
396 
398  tree.memory->pid,
399  tree.memory->parent,
400  tree.memory->group,
401  tree.memory->session,
402  None(),
403  None(),
404  None(),
405  self.isSome() ? self->command : "",
406  false);
407 
408  std::list<ProcessTree> children;
409  for (size_t i = 0; i < tree.children.size(); i++) {
410  Try<ProcessTree> child = coordinate(tree.children[i]);
411  if (child.isError()) {
412  return Error(child.error());
413  }
414  children.push_back(child.get());
415  }
416 
417  return ProcessTree(process, children);
418  }
419 
420 public:
421  // Prepares and instantiates the process tree.
423  {
424  Try<Tree> tree = prepare();
425 
426  if (tree.isError()) {
427  return Error(tree.error());
428  }
429 
430  Try<pid_t> pid = instantiate(tree.get());
431 
432  if (pid.isError()) {
433  return Error(pid.error());
434  }
435 
436  return coordinate(tree.get());
437  }
438 
439 private:
440  Option<void(*)()> function;
441  Option<const Exec> exec;
443  std::vector<Fork> children;
444 };
445 
446 } // namespace os {
447 
448 #endif // __STOUT_OS_POSIX_FORK_HPP__
Fork(const Option< void(*)()> &_function, const Fork &fork1, const Fork &fork2)
Definition: fork.hpp:154
std::string strerror(int errno_)
A thread-safe version of strerror.
Definition: strerror.hpp:30
Definition: fork.hpp:113
Definition: errorbase.hpp:36
Definition: option.hpp:29
#define ABORT(...)
Definition: abort.hpp:40
Fork(const Option< void(*)()> &_function, const Fork &fork1, const Fork &fork2, const Exec &_exec)
Definition: fork.hpp:163
T & get()&
Definition: try.hpp:80
Definition: check.hpp:33
const mode_t S_IWUSR
Definition: windows.hpp:306
pid_t session
Definition: fork.hpp:239
#define EXIT(status)
Definition: exit.hpp:31
Definition: errorbase.hpp:50
Definition: posix_signalhandler.hpp:23
const mode_t S_IRUSR
Definition: windows.hpp:305
Definition: fork.hpp:235
Fork(const Option< void(*)()> &_function, const Fork &fork1)
Definition: fork.hpp:125
Definition: check.hpp:30
Fork(const Option< void(*)()> &_function, const Exec &_exec)
Definition: fork.hpp:116
Fork(const Option< void(*)()> &_function, const Fork &fork1, const Fork &fork2, const Fork &fork3)
Definition: fork.hpp:190
Fork(const Option< void(*)()> &_function, const Fork &fork1, const Fork &fork2, const Fork &fork3, const Exec &_exec)
Definition: fork.hpp:201
Fork(const Exec &_exec)
Definition: fork.hpp:121
DWORD pid_t
Definition: windows.hpp:181
Definition: process.hpp:32
Try< ProcessTree > operator()() const
Definition: fork.hpp:422
Try< Nothing > close(int fd)
Definition: close.hpp:24
Definition: fork.hpp:101
Fork(const Option< void(*)()> &_function, const Fork &fork1, const Fork &fork2, const Fork &fork3, const Wait &_wait)
Definition: fork.hpp:214
Result< pid_t > waitpid(pid_t pid, int *status, int options)
Definition: os.hpp:141
static Try error(const E &e)
Definition: try.hpp:43
bool wait(const UPID &pid, const Duration &duration=Seconds(-1))
Wait for the process to exit for no more than the specified seconds.
Definition: fork.hpp:110
pid_t pid
Definition: fork.hpp:236
pid_t group
Definition: fork.hpp:238
Result< Process > process(pid_t pid)
Definition: freebsd.hpp:30
Definition: none.hpp:27
bool isError() const
Definition: try.hpp:78
std::set< pid_t > children(pid_t, const std::list< Process > &, bool)
Definition: os.hpp:217
Definition: executor.hpp:48
const std::string command
Definition: fork.hpp:106
pid_t parent
Definition: fork.hpp:237
Fork(const Option< void(*)()> &_function, const Fork &fork1, const Fork &fork2, const Wait &_wait)
Definition: fork.hpp:174
Try< std::string > prepare(const std::string &baseHierarchy, const std::string &subsystem, const std::string &cgroup)
std::string stringify(int flags)
Try< Memory > memory()
Definition: freebsd.hpp:78
Fork(const Option< void(*)()> &_function, const Fork &fork1, const Exec &_exec)
Definition: fork.hpp:132
Definition: process.hpp:74
Try< std::set< pid_t > > pids()
Definition: freebsd.hpp:62
Exec(const std::string &_command)
Definition: fork.hpp:103
Fork(const Option< void(*)()> &_function, const Fork &fork1, const Wait &_wait)
Definition: fork.hpp:141
constexpr const char * name
Definition: shell.hpp:41
Try< Nothing > ftruncate(int fd, off_t length)
Definition: ftruncate.hpp:26