Repository: mesos
Updated Branches:
  refs/heads/master e48e9288b -> 3c730dc42


Refactored subprocess options [1/2].

Previously the subprocess interface supported a several options for the
child process such as setsid. In order to make the interface more
flexible we refactored such options into a vector of ChildHooks.
In order not to allow arbitrary code inside a ChildHook it has to be
constructed via pre-defined factory methods.

Review: https://reviews.apache.org/r/45491/


Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/5ce0e46a
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/5ce0e46a
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/5ce0e46a

Branch: refs/heads/master
Commit: 5ce0e46aeb083de1af09d53364ac7260441e9e94
Parents: e48e928
Author: Joerg Schad <jo...@mesosphere.io>
Authored: Wed Sep 21 09:50:15 2016 -0700
Committer: Jie Yu <yujie....@gmail.com>
Committed: Wed Sep 21 09:50:15 2016 -0700

----------------------------------------------------------------------
 .../include/process/posix/subprocess.hpp        | 29 +++----
 .../libprocess/include/process/ssl/gtest.hpp    |  1 -
 .../include/process/subprocess_base.hpp         | 81 +++++++++++++-------
 3rdparty/libprocess/src/subprocess.cpp          | 38 ++++++++-
 .../libprocess/src/tests/subprocess_tests.cpp   |  7 --
 5 files changed, 95 insertions(+), 61 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/5ce0e46a/3rdparty/libprocess/include/process/posix/subprocess.hpp
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/include/process/posix/subprocess.hpp 
b/3rdparty/libprocess/include/process/posix/subprocess.hpp
index 1e9a7b9..d82db3c 100644
--- a/3rdparty/libprocess/include/process/posix/subprocess.hpp
+++ b/3rdparty/libprocess/include/process/posix/subprocess.hpp
@@ -206,13 +206,12 @@ inline int childMain(
     const string& path,
     char** argv,
     char** envp,
-    const Setsid set_sid,
     const InputFileDescriptors& stdinfds,
     const OutputFileDescriptors& stdoutfds,
     const OutputFileDescriptors& stderrfds,
     bool blocking,
     int pipes[2],
-    const Option<string>& working_directory,
+    const vector<Subprocess::ChildHook>& child_hooks,
     const Watchdog watchdog)
 {
   // Close parent's end of the pipes.
@@ -274,21 +273,13 @@ inline int childMain(
     ::close(pipes[0]);
   }
 
-  // Move to a different session (and new process group) so we're
-  // independent from the caller's session (otherwise children will
-  // receive SIGHUP if the slave exits).
-  if (set_sid == SETSID) {
-    // POSIX guarantees a forked child's pid does not match any existing
-    // process group id so only a single `setsid()` is required and the
-    // session id will be the pid.
-    if (::setsid() == -1) {
-      ABORT("Failed to put child in a new session");
-    }
-  }
+  // Run the child hooks.
+  foreach (const Subprocess::ChildHook& hook, child_hooks) {
+    Try<Nothing> callback = hook();
 
-  if (working_directory.isSome()) {
-    if (::chdir(working_directory->c_str()) == -1) {
-      ABORT("Failed to change directory");
+    // If the callback failed, we should abort execution.
+    if (callback.isError()) {
+      ABORT("Failed to execute Subprocess::ChildHook: " + callback.error());
     }
   }
 
@@ -311,12 +302,11 @@ inline int childMain(
 inline Try<pid_t> cloneChild(
     const string& path,
     vector<string> argv,
-    const Setsid set_sid,
     const Option<map<string, string>>& environment,
     const Option<lambda::function<
         pid_t(const lambda::function<int()>&)>>& _clone,
     const vector<Subprocess::Hook>& parent_hooks,
-    const Option<string>& working_directory,
+    const vector<Subprocess::ChildHook>& child_hooks,
     const Watchdog watchdog,
     const InputFileDescriptors stdinfds,
     const OutputFileDescriptors stdoutfds,
@@ -373,13 +363,12 @@ inline Try<pid_t> cloneChild(
       path,
       _argv,
       envp,
-      set_sid,
       stdinfds,
       stdoutfds,
       stderrfds,
       blocking,
       pipes,
-      working_directory,
+      child_hooks,
       watchdog));
 
   delete[] _argv;

http://git-wip-us.apache.org/repos/asf/mesos/blob/5ce0e46a/3rdparty/libprocess/include/process/ssl/gtest.hpp
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/include/process/ssl/gtest.hpp 
b/3rdparty/libprocess/include/process/ssl/gtest.hpp
index 2ad623a..21a0fc4 100644
--- a/3rdparty/libprocess/include/process/ssl/gtest.hpp
+++ b/3rdparty/libprocess/include/process/ssl/gtest.hpp
@@ -361,7 +361,6 @@ protected:
         process::Subprocess::PIPE(),
         process::Subprocess::PIPE(),
         process::Subprocess::FD(STDERR_FILENO),
-        process::NO_SETSID,
         nullptr,
         environment);
   }

http://git-wip-us.apache.org/repos/asf/mesos/blob/5ce0e46a/3rdparty/libprocess/include/process/subprocess_base.hpp
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/include/process/subprocess_base.hpp 
b/3rdparty/libprocess/include/process/subprocess_base.hpp
index 23bc14a..0502f96 100644
--- a/3rdparty/libprocess/include/process/subprocess_base.hpp
+++ b/3rdparty/libprocess/include/process/subprocess_base.hpp
@@ -34,14 +34,7 @@
 
 namespace process {
 
-// Flag describing whether a new process should generate a new sid.
-enum Setsid
-{
-  SETSID,
-  NO_SETSID,
-};
-
-// Flag describing whether a new process should be monitored by a seperate
+// Flag describing whether a new process should be monitored by a separate
 // watch process and be killed in case the parent process dies.
 enum Watchdog {
   MONITOR,
@@ -61,6 +54,7 @@ class Subprocess
 public:
   // Forward declarations.
   struct Hook;
+  class ChildHook;
 
   /**
    * Describes how the I/O is redirected for stdin/stdout/stderr.
@@ -97,7 +91,7 @@ public:
     };
 
     /**
-     * For output file descriptors a child write to the `write` file
+     * For output file descriptors a child writes to the `write` file
      * descriptor and a parent may read from the `read` file
      * descriptor if one is present.
      *
@@ -145,13 +139,12 @@ public:
         const Subprocess::IO& in,
         const Subprocess::IO& out,
         const Subprocess::IO& err,
-        const Setsid set_sid,
         const flags::FlagsBase* flags,
         const Option<std::map<std::string, std::string>>& environment,
         const Option<lambda::function<
             pid_t(const lambda::function<int()>&)>>& clone,
         const std::vector<Subprocess::Hook>& parent_hooks,
-        const Option<std::string>& working_directory,
+        const std::vector<Subprocess::ChildHook>& child_hooks,
         const Watchdog watchdog);
 
     IO(const lambda::function<Try<InputFileDescriptors>()>& _input,
@@ -185,8 +178,8 @@ public:
     Hook(const lambda::function<Try<Nothing>(pid_t)>& _parent_callback);
 
     /**
-     * The callback that must be sepcified for execution after the
-     * child has been cloned, but before it start executing the new
+     * The callback that must be specified for execution after the
+     * child has been cloned, but before it starts executing the new
      * process. This provides access to the child pid after its
      * initialization to add tracking or modify execution state of
      * the child before it executes the new process.
@@ -196,6 +189,42 @@ public:
     friend class Subprocess;
   };
 
+  /**
+   * A `ChildHook` can be passed to a `subprocess` call. It provides a way to
+   * inject predefined behavior between the clone and exec calls in the
+   * child process.
+   * As such `ChildHooks` have to fulfill certain criteria (especially
+   * being async safe) the class does not offer a public constructor.
+   * Instead instances can be created via factory methods.
+   * NOTE: Returning an error from a childHook causes the child process to
+   * abort.
+   */
+  class ChildHook
+  {
+  public:
+    /**
+     * `ChildHook` for changing the working directory.
+     */
+    static ChildHook CHDIR(const std::string& working_directory);
+
+    /**
+     * `ChildHook` for generating a new session id.
+     */
+    static ChildHook SETSID();
+
+    /**
+     * Returns an empty list of `ChildHooks`.
+     */
+    static std::vector<ChildHook> None() { return std::vector<ChildHook>(); }
+
+    Try<Nothing> operator()() const { return child_setup(); }
+
+  private:
+    ChildHook(const lambda::function<Try<Nothing>()>& _child_setup);
+
+    const lambda::function<Try<Nothing>()> child_setup;
+  };
+
   // Some syntactic sugar to create an IO::PIPE redirector.
   static IO PIPE();
   static IO PATH(const std::string& path);
@@ -269,13 +298,12 @@ private:
       const Subprocess::IO& in,
       const Subprocess::IO& out,
       const Subprocess::IO& err,
-      const Setsid setsid,
       const flags::FlagsBase* flags,
       const Option<std::map<std::string, std::string>>& environment,
       const Option<lambda::function<
           pid_t(const lambda::function<int()>&)>>& clone,
       const std::vector<Subprocess::Hook>& parent_hooks,
-      const Option<std::string>& working_directory,
+      const std::vector<Subprocess::ChildHook>& child_hooks,
       const Watchdog watchdog);
 
   struct Data
@@ -331,8 +359,6 @@ private:
  * @param in Redirection specification for stdin.
  * @param out Redirection specification for stdout.
  * @param err Redirection specification for stderr.
- * @param set_sid Indicator whether the process should be placed in
- *     a new session after the 'parent_hooks' have been executed.
  * @param flags Flags to be stringified and appended to 'argv'.
  * @param environment Environment variables to use for the new
  *     subprocess or if None (the default) then the new subprocess
@@ -341,8 +367,8 @@ private:
  *     subprocess.
  * @param parent_hooks Hooks that will be executed in the parent
  *     before the child execs.
- * @param working_directory Directory in which the process should
- *     chdir before exec after the 'parent_hooks' have been executed.
+ * @param child_hooks Hooks that will be executed in the child
+ *     before the child execs but after parent_hooks have executed.
  * @param watchdog Indicator whether the new process should be monitored
  *     and killed if the parent process terminates.
  * @return The subprocess or an error if one occurred.
@@ -355,14 +381,14 @@ Try<Subprocess> subprocess(
     const Subprocess::IO& in = Subprocess::FD(STDIN_FILENO),
     const Subprocess::IO& out = Subprocess::FD(STDOUT_FILENO),
     const Subprocess::IO& err = Subprocess::FD(STDERR_FILENO),
-    const Setsid set_sid = NO_SETSID,
     const flags::FlagsBase* flags = nullptr,
     const Option<std::map<std::string, std::string>>& environment = None(),
     const Option<lambda::function<
         pid_t(const lambda::function<int()>&)>>& clone = None(),
     const std::vector<Subprocess::Hook>& parent_hooks =
       Subprocess::Hook::None(),
-    const Option<std::string>& working_directory = None(),
+    const std::vector<Subprocess::ChildHook>& child_hooks =
+      Subprocess::ChildHook::None(),
     const Watchdog watchdog = NO_MONITOR);
 
 
@@ -377,8 +403,6 @@ Try<Subprocess> subprocess(
  * @param in Redirection specification for stdin.
  * @param out Redirection specification for stdout.
  * @param err Redirection specification for stderr.
- * @param set_sid Indicator whether the process should be placed in
- *     a new session after the 'parent_hooks' have been executed.
  * @param environment Environment variables to use for the new
  *     subprocess or if None (the default) then the new subprocess
  *     will inherit the environment of the current process.
@@ -386,8 +410,8 @@ Try<Subprocess> subprocess(
  *     subprocess.
  * @param parent_hooks Hooks that will be executed in the parent
  *     before the child execs.
- * @param working_directory Directory in which the process should
- *     chdir before exec after the 'parent_hooks' have been executed.
+ * @param child_hooks Hooks that will be executed in the child
+ *     before the child execs but after parent_hooks have executed.
  * @param watchdog Indicator whether the new process should be monitored
  *     and killed if the parent process terminates.
  * @return The subprocess or an error if one occurred.
@@ -399,13 +423,13 @@ inline Try<Subprocess> subprocess(
     const Subprocess::IO& in = Subprocess::FD(STDIN_FILENO),
     const Subprocess::IO& out = Subprocess::FD(STDOUT_FILENO),
     const Subprocess::IO& err = Subprocess::FD(STDERR_FILENO),
-    const Setsid set_sid = NO_SETSID,
     const Option<std::map<std::string, std::string>>& environment = None(),
     const Option<lambda::function<
         pid_t(const lambda::function<int()>&)>>& clone = None(),
     const std::vector<Subprocess::Hook>& parent_hooks =
       Subprocess::Hook::None(),
-    const Option<std::string>& working_directory = None(),
+    const std::vector<Subprocess::ChildHook>& child_hooks =
+      Subprocess::ChildHook::None(),
     const Watchdog watchdog = NO_MONITOR)
 {
   std::vector<std::string> argv = {os::Shell::arg0, os::Shell::arg1, command};
@@ -416,12 +440,11 @@ inline Try<Subprocess> subprocess(
       in,
       out,
       err,
-      set_sid,
       nullptr,
       environment,
       clone,
       parent_hooks,
-      working_directory,
+      child_hooks,
       watchdog);
 }
 

http://git-wip-us.apache.org/repos/asf/mesos/blob/5ce0e46a/3rdparty/libprocess/src/subprocess.cpp
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/src/subprocess.cpp 
b/3rdparty/libprocess/src/subprocess.cpp
index 8ca61f7..ce5c88f 100644
--- a/3rdparty/libprocess/src/subprocess.cpp
+++ b/3rdparty/libprocess/src/subprocess.cpp
@@ -53,6 +53,38 @@ Subprocess::Hook::Hook(
     const lambda::function<Try<Nothing>(pid_t)>& _parent_callback)
   : parent_callback(_parent_callback) {}
 
+
+Subprocess::ChildHook::ChildHook(
+    const lambda::function<Try<Nothing>()>& _child_setup)
+  : child_setup(_child_setup) {}
+
+
+Subprocess::ChildHook Subprocess::ChildHook::CHDIR(
+    const std::string& working_directory)
+{
+  return Subprocess::ChildHook([working_directory]() -> Try<Nothing> {
+    if (::chdir(working_directory.c_str()) == -1) {
+      return Error("Could not chdir");
+    }
+
+    return Nothing();
+  });
+}
+
+
+Subprocess::ChildHook Subprocess::ChildHook::SETSID()
+{
+  return Subprocess::ChildHook([]() -> Try<Nothing> {
+    // Put child into its own process session to prevent slave suicide
+    // on child process SIGKILL/SIGTERM.
+    if (::setsid() == -1) {
+      return Error("Could not setsid");
+    }
+
+    return Nothing();
+  });
+}
+
 namespace internal {
 
 static void cleanup(
@@ -92,13 +124,12 @@ Try<Subprocess> subprocess(
     const Subprocess::IO& in,
     const Subprocess::IO& out,
     const Subprocess::IO& err,
-    const Setsid set_sid,
     const flags::FlagsBase* flags,
     const Option<map<string, string>>& environment,
     const Option<lambda::function<
         pid_t(const lambda::function<int()>&)>>& _clone,
     const vector<Subprocess::Hook>& parent_hooks,
-    const Option<string>& working_directory,
+    const vector<Subprocess::ChildHook>& child_hooks,
     const Watchdog watchdog)
 {
   // File descriptors for redirecting stdin/stdout/stderr.
@@ -172,11 +203,10 @@ Try<Subprocess> subprocess(
     Try<pid_t> pid = internal::cloneChild(
         path,
         argv,
-        set_sid,
         environment,
         _clone,
         parent_hooks,
-        working_directory,
+        child_hooks,
         watchdog,
         stdinfds,
         stdoutfds,

http://git-wip-us.apache.org/repos/asf/mesos/blob/5ce0e46a/3rdparty/libprocess/src/tests/subprocess_tests.cpp
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/src/tests/subprocess_tests.cpp 
b/3rdparty/libprocess/src/tests/subprocess_tests.cpp
index 0ff3945..e32f3e5 100644
--- a/3rdparty/libprocess/src/tests/subprocess_tests.cpp
+++ b/3rdparty/libprocess/src/tests/subprocess_tests.cpp
@@ -222,7 +222,6 @@ TEST_F(SubprocessTest, EnvironmentEcho)
             Subprocess::FD(STDIN_FILENO),
             Subprocess::PATH(outfile),
             Subprocess::FD(STDERR_FILENO),
-            process::Setsid::NO_SETSID,
             environment);
       });
 
@@ -766,7 +765,6 @@ TEST_F(SubprocessTest, Flags)
       Subprocess::FD(STDIN_FILENO),
       Subprocess::PATH(out),
       Subprocess::FD(STDERR_FILENO),
-      process::NO_SETSID,
       &flags);
 
   ASSERT_SOME(s);
@@ -833,7 +831,6 @@ TEST_F(SubprocessTest, Environment)
       Subprocess::FD(STDIN_FILENO),
       Subprocess::PIPE(),
       Subprocess::FD(STDERR_FILENO),
-      process::NO_SETSID,
       environment);
 
   ASSERT_SOME(s);
@@ -865,7 +862,6 @@ TEST_F(SubprocessTest, Environment)
       Subprocess::FD(STDIN_FILENO),
       Subprocess::PIPE(),
       Subprocess::FD(STDERR_FILENO),
-      process::NO_SETSID,
       environment);
 
   ASSERT_SOME(s);
@@ -900,7 +896,6 @@ TEST_F(SubprocessTest, EnvironmentWithSpaces)
       Subprocess::FD(STDIN_FILENO),
       Subprocess::PIPE(),
       Subprocess::FD(STDERR_FILENO),
-      process::NO_SETSID,
       environment);
 
   ASSERT_SOME(s);
@@ -935,7 +930,6 @@ TEST_F(SubprocessTest, EnvironmentWithSpacesAndQuotes)
       Subprocess::FD(STDIN_FILENO),
       Subprocess::PIPE(),
       Subprocess::FD(STDERR_FILENO),
-      process::NO_SETSID,
       environment);
 
   ASSERT_SOME(s);
@@ -973,7 +967,6 @@ TEST_F(SubprocessTest, EnvironmentOverride)
       Subprocess::FD(STDIN_FILENO),
       Subprocess::PIPE(),
       Subprocess::FD(STDERR_FILENO),
-      process::NO_SETSID,
       environment);
 
   ASSERT_SOME(s);

Reply via email to