From 1f528c76ae924bfb4c9c8db253282db8b3d3d713 Mon Sep 17 00:00:00 2001 From: Tudor Bosman Date: Thu, 28 Mar 2013 16:48:51 -0700 Subject: [PATCH] Optionally, kill subprocess when parent dies Summary: Non-portable. Test Plan: test added Reviewed By: lucian@fb.com FB internal diff: D755528 --- folly/Subprocess.cpp | 9 ++ folly/Subprocess.h | 15 ++- folly/test/SubprocessTest.cpp | 32 +++++++ .../test/SubprocessTestParentDeathHelper.cpp | 95 +++++++++++++++++++ 4 files changed, 150 insertions(+), 1 deletion(-) create mode 100644 folly/test/SubprocessTestParentDeathHelper.cpp diff --git a/folly/Subprocess.cpp b/folly/Subprocess.cpp index e2d5f7bd..8421bd9e 100644 --- a/folly/Subprocess.cpp +++ b/folly/Subprocess.cpp @@ -16,6 +16,7 @@ #include "folly/Subprocess.h" +#include #include #include #include @@ -351,6 +352,14 @@ void Subprocess::runChild(const char* executable, } } + // Opt to receive signal on parent death, if requested + if (options.parentDeathSignal_ != 0) { + int r = prctl(PR_SET_PDEATHSIG, options.parentDeathSignal_, 0, 0, 0); + if (r == -1) { + abort(); + } + } + // Now, finally, exec. int r; if (options.usePath_) { diff --git a/folly/Subprocess.h b/folly/Subprocess.h index 47ec4260..5c53a319 100644 --- a/folly/Subprocess.h +++ b/folly/Subprocess.h @@ -179,7 +179,11 @@ class Subprocess : private boost::noncopyable { class Options : private boost::orable { friend class Subprocess; public: - Options() : closeOtherFds_(false), usePath_(false) { } + Options() + : closeOtherFds_(false), + usePath_(false), + parentDeathSignal_(0) { + } /** * Change action for file descriptor fd. @@ -233,6 +237,14 @@ class Subprocess : private boost::noncopyable { */ Options& usePath() { usePath_ = true; return *this; } + /** + * Child will receive a signal when the parent exits. + */ + Options& parentDeathSignal(int sig) { + parentDeathSignal_ = sig; + return *this; + } + /** * Helpful way to combine Options. */ @@ -243,6 +255,7 @@ class Subprocess : private boost::noncopyable { FdMap fdActions_; bool closeOtherFds_; bool usePath_; + int parentDeathSignal_; }; static Options pipeStdin() { return Options().stdin(PIPE); } diff --git a/folly/test/SubprocessTest.cpp b/folly/test/SubprocessTest.cpp index ef425068..5d3fd3a8 100644 --- a/folly/test/SubprocessTest.cpp +++ b/folly/test/SubprocessTest.cpp @@ -16,6 +16,8 @@ #include "folly/Subprocess.h" +#include + #include #include @@ -23,6 +25,7 @@ #include "folly/experimental/Gen.h" #include "folly/experimental/FileGen.h" #include "folly/experimental/StringGen.h" +#include "folly/experimental/io/FsUtil.h" using namespace folly; @@ -56,6 +59,35 @@ TEST(SimpleSubprocessTest, ShellExitsWithError) { EXPECT_EQ(1, proc.wait().exitStatus()); } +TEST(ParentDeathSubprocessTest, ParentDeathSignal) { + // Find out where we are. + static constexpr size_t pathLength = 2048; + char buf[pathLength]; + int r = readlink("/proc/self/exe", buf, pathLength); + CHECK_ERR(r >= 0); + buf[r] = '\0'; + + fs::path helper(buf); + helper.remove_filename(); + helper /= "subprocess_test_parent_death_helper"; + + fs::path tempFile(fs::temp_directory_path() / fs::unique_path()); + + std::vector args {helper.string(), tempFile.string()}; + Subprocess proc(args); + // The helper gets killed by its child, see details in + // SubprocessTestParentDeathHelper.cpp + ASSERT_EQ(SIGKILL, proc.wait().killSignal()); + + // Now wait for the file to be created, see details in + // SubprocessTestParentDeathHelper.cpp + while (!fs::exists(tempFile)) { + usleep(20000); // 20ms + } + + fs::remove(tempFile); +} + TEST(PopenSubprocessTest, PopenRead) { Subprocess proc("ls /", Subprocess::pipeStdout()); int found = 0; diff --git a/folly/test/SubprocessTestParentDeathHelper.cpp b/folly/test/SubprocessTestParentDeathHelper.cpp new file mode 100644 index 00000000..2eae7a3c --- /dev/null +++ b/folly/test/SubprocessTestParentDeathHelper.cpp @@ -0,0 +1,95 @@ +/* + * Copyright 2013 Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// This is a helper for the parentDeathSignal test in SubprocessTest.cpp. +// +// Basically, we create two processes, a parent and a child, and set the +// child to receive SIGUSR1 when the parent exits. We set the child to +// create a file when that happens. The child then kills the parent; the test +// will verify that the file actually gets created, which means that everything +// worked as intended. + +#include +#include +#include +#include +#include + +#include +#include + +#include "folly/Conv.h" +#include "folly/Subprocess.h" + +using folly::Subprocess; + +DEFINE_bool(child, false, ""); + +namespace { +constexpr int kSignal = SIGUSR1; +volatile bool caught = false; + +void signalHandler(int sig) { + if (sig != kSignal) { + abort(); + } + caught = true; +} + +} // namespace + +void runChild(const char* file) { + struct sigaction sa; + sa.sa_handler = signalHandler; + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + CHECK_ERR(sigaction(kSignal, &sa, nullptr)); + + // Kill the parent, wait for our signal. + CHECK_ERR(kill(getppid(), SIGKILL)); + + while (!caught) { + pause(); + } + + // Signal completion by creating the file + CHECK_ERR(creat(file, 0600)); +} + +void runParent(const char* file) { + std::vector args {"/proc/self/exe", "--child", file}; + Subprocess proc( + args, + Subprocess::Options().parentDeathSignal(kSignal)); + CHECK(proc.poll().running()); + + // The child will kill us. + for (;;) { + pause(); + } +} + +int main(int argc, char *argv[]) { + google::ParseCommandLineFlags(&argc, &argv, true); + CHECK_EQ(argc, 2); + if (FLAGS_child) { + runChild(argv[1]); + } else { + runParent(argv[1]); + } + return 0; +} + -- 2.34.1