Commit 4e9783b9 authored by Aravind Anbudurai's avatar Aravind Anbudurai Committed by Facebook Github Bot

Support linux namespace clone flags

Summary:
This diffs adds supports on folly::Subprocess to be able to take clone flags and
use them to call clone(2) instead of the default vfork()

I checked that all tests pass when I replace vfork with fork on trunk. So there
isn't anything built assuming the parent is paused for an execve. Correct me if
I am wrong here.

(Note: this ignores all push blocking failures!)

Reviewed By: snarkmaster

Differential Revision: D4853169

fbshipit-source-id: 7e5851df3a49996a4a5dc1945457686dd042e1f4
parent b41eb6f0
......@@ -22,6 +22,8 @@
#if __linux__
#include <sys/prctl.h>
#include <sys/syscall.h>
#include <unistd.h>
#endif
#include <fcntl.h>
......@@ -389,7 +391,19 @@ void Subprocess::spawnInternal(
// Call c_str() here, as it's not necessarily safe after fork.
const char* childDir =
options.childDir_.empty() ? nullptr : options.childDir_.c_str();
pid_t pid = vfork();
pid_t pid;
#ifdef __linux__
if (options.cloneFlags_) {
pid = syscall(SYS_clone, *options.cloneFlags_, 0, nullptr, nullptr);
checkUnixError(pid, errno, "clone");
} else {
#endif
pid = vfork();
checkUnixError(pid, errno, "vfork");
#ifdef __linux__
}
#endif
if (pid == 0) {
int errnoValue = prepareChild(options, &oldSignals, childDir);
if (errnoValue != 0) {
......@@ -400,8 +414,6 @@ void Subprocess::spawnInternal(
// If we get here, exec() failed.
childError(errFd, kExecFailure, errnoValue);
}
// In parent. Make sure vfork() succeeded.
checkUnixError(pid, errno, "vfork");
// Child is alive. We have to be very careful about throwing after this
// point. We are inside the constructor, so if we throw the Subprocess
......
......@@ -112,11 +112,12 @@
#include <folly/File.h>
#include <folly/FileUtil.h>
#include <folly/Function.h>
#include <folly/gen/String.h>
#include <folly/io/IOBufQueue.h>
#include <folly/MapUtil.h>
#include <folly/Optional.h>
#include <folly/Portability.h>
#include <folly/Range.h>
#include <folly/gen/String.h>
#include <folly/io/IOBufQueue.h>
namespace folly {
......@@ -397,6 +398,29 @@ class Subprocess {
return *this;
}
#if __linux__
/**
* This is an experimental feature, it is best you don't use it at this
* point of time.
* Although folly would support cloning with custom flags in some form, this
* API might change in the near future. So use the following assuming it is
* experimental. (Apr 11, 2017)
*
* This unlocks Subprocess to support clone flags, many of them need
* CAP_SYS_ADMIN permissions. It might also require you to go through the
* implementation to understand what happens before, between and after the
* fork-and-exec.
*
* `man 2 clone` would be a starting point for knowing about the available
* flags.
*/
using clone_flags_t = uint64_t;
Options& useCloneWithFlags(clone_flags_t cloneFlags) noexcept {
cloneFlags_ = cloneFlags;
return *this;
}
#endif
/**
* Helpful way to combine Options.
*/
......@@ -414,6 +438,11 @@ class Subprocess {
bool processGroupLeader_{false};
DangerousPostForkPreExecCallback*
dangerousPostForkPreExecCallback_{nullptr};
#if __linux__
// none means `vfork()` instead of a custom `clone()`
// Optional<> is used because value of '0' means do clone without any flags.
Optional<clone_flags_t> cloneFlags_;
#endif
};
static Options pipeStdin() { return Options().stdinFd(PIPE); }
......
......@@ -22,14 +22,14 @@
#include <glog/logging.h>
#include <folly/Exception.h>
#include <folly/Format.h>
#include <folly/FileUtil.h>
#include <folly/Format.h>
#include <folly/String.h>
#include <folly/experimental/TestUtil.h>
#include <folly/experimental/io/FsUtil.h>
#include <folly/gen/Base.h>
#include <folly/gen/File.h>
#include <folly/gen/String.h>
#include <folly/experimental/TestUtil.h>
#include <folly/experimental/io/FsUtil.h>
#include <folly/portability/GTest.h>
#include <folly/portability/Unistd.h>
......@@ -47,6 +47,29 @@ TEST(SimpleSubprocessTest, ExitsSuccessfullyChecked) {
proc.waitChecked();
}
TEST(SimpleSubprocessTest, CloneFlagsWithVfork) {
Subprocess proc(
std::vector<std::string>{"/bin/true"},
Subprocess::Options().useCloneWithFlags(SIGCHLD | CLONE_VFORK));
EXPECT_EQ(0, proc.wait().exitStatus());
}
TEST(SimpleSubprocessTest, CloneFlagsWithFork) {
Subprocess proc(
std::vector<std::string>{"/bin/true"},
Subprocess::Options().useCloneWithFlags(SIGCHLD));
EXPECT_EQ(0, proc.wait().exitStatus());
}
TEST(SimpleSubprocessTest, CloneFlagsSubprocessCtorExitsAfterExec) {
Subprocess proc(
std::vector<std::string>{"/bin/sleep", "3600"},
Subprocess::Options().useCloneWithFlags(SIGCHLD));
checkUnixError(::kill(proc.pid(), SIGKILL), "kill");
auto retCode = proc.wait();
EXPECT_TRUE(retCode.killed());
}
TEST(SimpleSubprocessTest, ExitsWithError) {
Subprocess proc(std::vector<std::string>{ "/bin/false" });
EXPECT_EQ(1, proc.wait().exitStatus());
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment