From 4e9783b98883ed7b5fbef915556c0736ffdfe545 Mon Sep 17 00:00:00 2001 From: Aravind Anbudurai Date: Wed, 12 Apr 2017 01:36:29 -0700 Subject: [PATCH] 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 --- folly/Subprocess.cpp | 18 +++++++++++++++--- folly/Subprocess.h | 33 +++++++++++++++++++++++++++++++-- folly/test/SubprocessTest.cpp | 29 ++++++++++++++++++++++++++--- 3 files changed, 72 insertions(+), 8 deletions(-) diff --git a/folly/Subprocess.cpp b/folly/Subprocess.cpp index 6c04e91b..b8070c07 100644 --- a/folly/Subprocess.cpp +++ b/folly/Subprocess.cpp @@ -22,6 +22,8 @@ #if __linux__ #include +#include +#include #endif #include @@ -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 diff --git a/folly/Subprocess.h b/folly/Subprocess.h index 44e3fad6..86ca6bfe 100644 --- a/folly/Subprocess.h +++ b/folly/Subprocess.h @@ -112,11 +112,12 @@ #include #include #include -#include -#include #include +#include #include #include +#include +#include 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 cloneFlags_; +#endif }; static Options pipeStdin() { return Options().stdinFd(PIPE); } diff --git a/folly/test/SubprocessTest.cpp b/folly/test/SubprocessTest.cpp index 07ede20a..8d9f100d 100644 --- a/folly/test/SubprocessTest.cpp +++ b/folly/test/SubprocessTest.cpp @@ -22,14 +22,14 @@ #include #include -#include #include +#include #include +#include +#include #include #include #include -#include -#include #include #include @@ -47,6 +47,29 @@ TEST(SimpleSubprocessTest, ExitsSuccessfullyChecked) { proc.waitChecked(); } +TEST(SimpleSubprocessTest, CloneFlagsWithVfork) { + Subprocess proc( + std::vector{"/bin/true"}, + Subprocess::Options().useCloneWithFlags(SIGCHLD | CLONE_VFORK)); + EXPECT_EQ(0, proc.wait().exitStatus()); +} + +TEST(SimpleSubprocessTest, CloneFlagsWithFork) { + Subprocess proc( + std::vector{"/bin/true"}, + Subprocess::Options().useCloneWithFlags(SIGCHLD)); + EXPECT_EQ(0, proc.wait().exitStatus()); +} + +TEST(SimpleSubprocessTest, CloneFlagsSubprocessCtorExitsAfterExec) { + Subprocess proc( + std::vector{"/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{ "/bin/false" }); EXPECT_EQ(1, proc.wait().exitStatus()); -- 2.34.1