#if __linux__
#include <sys/prctl.h>
+#include <sys/syscall.h>
+#include <unistd.h>
#endif
#include <fcntl.h>
// 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) {
// 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
#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 {
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.
*/
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); }
#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>
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());