xref: /freebsd/contrib/kyua/utils/process/executor.ipp (revision b392a90ba4e5ea07d8a88a834fd102191d1967bf)
1b0d29bc4SBrooks Davis// Copyright 2015 The Kyua Authors.
2b0d29bc4SBrooks Davis// All rights reserved.
3b0d29bc4SBrooks Davis//
4b0d29bc4SBrooks Davis// Redistribution and use in source and binary forms, with or without
5b0d29bc4SBrooks Davis// modification, are permitted provided that the following conditions are
6b0d29bc4SBrooks Davis// met:
7b0d29bc4SBrooks Davis//
8b0d29bc4SBrooks Davis// * Redistributions of source code must retain the above copyright
9b0d29bc4SBrooks Davis//   notice, this list of conditions and the following disclaimer.
10b0d29bc4SBrooks Davis// * Redistributions in binary form must reproduce the above copyright
11b0d29bc4SBrooks Davis//   notice, this list of conditions and the following disclaimer in the
12b0d29bc4SBrooks Davis//   documentation and/or other materials provided with the distribution.
13b0d29bc4SBrooks Davis// * Neither the name of Google Inc. nor the names of its contributors
14b0d29bc4SBrooks Davis//   may be used to endorse or promote products derived from this software
15b0d29bc4SBrooks Davis//   without specific prior written permission.
16b0d29bc4SBrooks Davis//
17b0d29bc4SBrooks Davis// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18b0d29bc4SBrooks Davis// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19b0d29bc4SBrooks Davis// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20b0d29bc4SBrooks Davis// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21b0d29bc4SBrooks Davis// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22b0d29bc4SBrooks Davis// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23b0d29bc4SBrooks Davis// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24b0d29bc4SBrooks Davis// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25b0d29bc4SBrooks Davis// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26b0d29bc4SBrooks Davis// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27b0d29bc4SBrooks Davis// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28b0d29bc4SBrooks Davis
29b0d29bc4SBrooks Davis#if !defined(UTILS_PROCESS_EXECUTOR_IPP)
30b0d29bc4SBrooks Davis#define UTILS_PROCESS_EXECUTOR_IPP
31b0d29bc4SBrooks Davis
32b0d29bc4SBrooks Davis#include "utils/process/executor.hpp"
33b0d29bc4SBrooks Davis
34b0d29bc4SBrooks Davis#include "utils/fs/path.hpp"
35b0d29bc4SBrooks Davis#include "utils/optional.ipp"
36b0d29bc4SBrooks Davis#include "utils/passwd.hpp"
37b0d29bc4SBrooks Davis#include "utils/process/child.ipp"
38b0d29bc4SBrooks Davis
39b0d29bc4SBrooks Davisnamespace utils {
40b0d29bc4SBrooks Davisnamespace process {
41b0d29bc4SBrooks Davis
42b0d29bc4SBrooks Davis
43b0d29bc4SBrooks Davisnamespace executor {
44b0d29bc4SBrooks Davisnamespace detail {
45b0d29bc4SBrooks Davis
46b0d29bc4SBrooks Davis/// Functor to execute a hook in a child process.
47b0d29bc4SBrooks Davis///
48b0d29bc4SBrooks Davis/// The hook is executed after the process has been isolated per the logic in
49b0d29bc4SBrooks Davis/// utils::process::isolation based on the input parameters at construction
50b0d29bc4SBrooks Davis/// time.
51b0d29bc4SBrooks Davistemplate< class Hook >
52b0d29bc4SBrooks Davisclass run_child {
53b0d29bc4SBrooks Davis    /// Function or functor to invoke in the child.
54b0d29bc4SBrooks Davis    Hook _hook;
55b0d29bc4SBrooks Davis
56b0d29bc4SBrooks Davis    /// Directory where the hook may place control files.
57b0d29bc4SBrooks Davis    const fs::path& _control_directory;
58b0d29bc4SBrooks Davis
59b0d29bc4SBrooks Davis    /// Directory to enter when running the subprocess.
60b0d29bc4SBrooks Davis    ///
61b0d29bc4SBrooks Davis    /// This is a subdirectory of _control_directory but is separate so that
62b0d29bc4SBrooks Davis    /// subprocess operations do not inadvertently affect our files.
63b0d29bc4SBrooks Davis    const fs::path& _work_directory;
64b0d29bc4SBrooks Davis
65b0d29bc4SBrooks Davis    /// User to switch to when running the subprocess.
66b0d29bc4SBrooks Davis    ///
67b0d29bc4SBrooks Davis    /// If not none, the subprocess will be executed as the provided user and
68b0d29bc4SBrooks Davis    /// the control and work directories will be writable by this user.
69b0d29bc4SBrooks Davis    const optional< passwd::user > _unprivileged_user;
70b0d29bc4SBrooks Davis
71b0d29bc4SBrooks Davispublic:
72b0d29bc4SBrooks Davis    /// Constructor.
73b0d29bc4SBrooks Davis    ///
74b0d29bc4SBrooks Davis    /// \param hook Function or functor to invoke in the child.
75b0d29bc4SBrooks Davis    /// \param control_directory Directory where control files can be placed.
76b0d29bc4SBrooks Davis    /// \param work_directory Directory to enter when running the subprocess.
77b0d29bc4SBrooks Davis    /// \param unprivileged_user If set, user to switch to before execution.
78b0d29bc4SBrooks Davis    run_child(Hook hook,
79b0d29bc4SBrooks Davis              const fs::path& control_directory,
80b0d29bc4SBrooks Davis              const fs::path& work_directory,
81b0d29bc4SBrooks Davis              const optional< passwd::user > unprivileged_user) :
82b0d29bc4SBrooks Davis        _hook(hook),
83b0d29bc4SBrooks Davis        _control_directory(control_directory),
84b0d29bc4SBrooks Davis        _work_directory(work_directory),
85b0d29bc4SBrooks Davis        _unprivileged_user(unprivileged_user)
86b0d29bc4SBrooks Davis    {
87b0d29bc4SBrooks Davis    }
88b0d29bc4SBrooks Davis
89b0d29bc4SBrooks Davis    /// Body of the subprocess.
90b0d29bc4SBrooks Davis    void
91b0d29bc4SBrooks Davis    operator()(void)
92b0d29bc4SBrooks Davis    {
93b0d29bc4SBrooks Davis        executor::detail::setup_child(_unprivileged_user,
94b0d29bc4SBrooks Davis                                      _control_directory, _work_directory);
95b0d29bc4SBrooks Davis        _hook(_control_directory);
96b0d29bc4SBrooks Davis    }
97b0d29bc4SBrooks Davis};
98b0d29bc4SBrooks Davis
99b0d29bc4SBrooks Davis}  // namespace detail
100b0d29bc4SBrooks Davis}  // namespace executor
101b0d29bc4SBrooks Davis
102b0d29bc4SBrooks Davis
103b0d29bc4SBrooks Davis/// Forks and executes a subprocess asynchronously.
104b0d29bc4SBrooks Davis///
105b0d29bc4SBrooks Davis/// \tparam Hook Type of the hook.
106b0d29bc4SBrooks Davis/// \param hook Function or functor to run in the subprocess.
107b0d29bc4SBrooks Davis/// \param timeout Maximum amount of time the subprocess can run for.
108b0d29bc4SBrooks Davis/// \param unprivileged_user If not none, user to switch to before execution.
109b0d29bc4SBrooks Davis/// \param stdout_target If not none, file to which to write the stdout of the
110b0d29bc4SBrooks Davis///     test case.
111b0d29bc4SBrooks Davis/// \param stderr_target If not none, file to which to write the stderr of the
112b0d29bc4SBrooks Davis///     test case.
113b0d29bc4SBrooks Davis///
114b0d29bc4SBrooks Davis/// \return A handle for the background operation.  Used to match the result of
115b0d29bc4SBrooks Davis/// the execution returned by wait_any() with this invocation.
116b0d29bc4SBrooks Davistemplate< class Hook >
117b0d29bc4SBrooks Davisexecutor::exec_handle
118b0d29bc4SBrooks Davisexecutor::executor_handle::spawn(
119b0d29bc4SBrooks Davis    Hook hook,
120b0d29bc4SBrooks Davis    const datetime::delta& timeout,
121b0d29bc4SBrooks Davis    const optional< passwd::user > unprivileged_user,
122b0d29bc4SBrooks Davis    const optional< fs::path > stdout_target,
123b0d29bc4SBrooks Davis    const optional< fs::path > stderr_target)
124b0d29bc4SBrooks Davis{
125b0d29bc4SBrooks Davis    const fs::path unique_work_directory = spawn_pre();
126b0d29bc4SBrooks Davis
127b0d29bc4SBrooks Davis    const fs::path stdout_path = stdout_target ?
128b0d29bc4SBrooks Davis        stdout_target.get() : (unique_work_directory / detail::stdout_name);
129b0d29bc4SBrooks Davis    const fs::path stderr_path = stderr_target ?
130b0d29bc4SBrooks Davis        stderr_target.get() : (unique_work_directory / detail::stderr_name);
131b0d29bc4SBrooks Davis
132*b392a90bSJohn Baldwin    std::unique_ptr< process::child > child = process::child::fork_files(
133b0d29bc4SBrooks Davis        detail::run_child< Hook >(hook,
134b0d29bc4SBrooks Davis                                  unique_work_directory,
135b0d29bc4SBrooks Davis                                  unique_work_directory / detail::work_subdir,
136b0d29bc4SBrooks Davis                                  unprivileged_user),
137b0d29bc4SBrooks Davis        stdout_path, stderr_path);
138b0d29bc4SBrooks Davis
139b0d29bc4SBrooks Davis    return spawn_post(unique_work_directory, stdout_path, stderr_path,
140*b392a90bSJohn Baldwin                      timeout, unprivileged_user, std::move(child));
141b0d29bc4SBrooks Davis}
142b0d29bc4SBrooks Davis
143b0d29bc4SBrooks Davis
144b0d29bc4SBrooks Davis/// Forks and executes a subprocess asynchronously in the context of another.
145b0d29bc4SBrooks Davis///
146b0d29bc4SBrooks Davis/// By context we understand the on-disk state of a previously-executed process,
147b0d29bc4SBrooks Davis/// thus the new subprocess spawned by this function will run with the same
148b0d29bc4SBrooks Davis/// control and work directories as another process.
149b0d29bc4SBrooks Davis///
150b0d29bc4SBrooks Davis/// \tparam Hook Type of the hook.
151b0d29bc4SBrooks Davis/// \param hook Function or functor to run in the subprocess.
152b0d29bc4SBrooks Davis/// \param base Context of the subprocess in which to run this one.  The
153b0d29bc4SBrooks Davis///     exit_handle provided here must remain alive throughout the existence of
154b0d29bc4SBrooks Davis///     this other object because the original exit_handle is the one that owns
155b0d29bc4SBrooks Davis///     the on-disk state.
156b0d29bc4SBrooks Davis/// \param timeout Maximum amount of time the subprocess can run for.
157b0d29bc4SBrooks Davis///
158b0d29bc4SBrooks Davis/// \return A handle for the background operation.  Used to match the result of
159b0d29bc4SBrooks Davis/// the execution returned by wait_any() with this invocation.
160b0d29bc4SBrooks Davistemplate< class Hook >
161b0d29bc4SBrooks Davisexecutor::exec_handle
162b0d29bc4SBrooks Davisexecutor::executor_handle::spawn_followup(Hook hook,
163b0d29bc4SBrooks Davis                                          const exit_handle& base,
164b0d29bc4SBrooks Davis                                          const datetime::delta& timeout)
165b0d29bc4SBrooks Davis{
166b0d29bc4SBrooks Davis    spawn_followup_pre();
167b0d29bc4SBrooks Davis
168*b392a90bSJohn Baldwin    std::unique_ptr< process::child > child = process::child::fork_files(
169b0d29bc4SBrooks Davis        detail::run_child< Hook >(hook,
170b0d29bc4SBrooks Davis                                  base.control_directory(),
171b0d29bc4SBrooks Davis                                  base.work_directory(),
172b0d29bc4SBrooks Davis                                  base.unprivileged_user()),
173b0d29bc4SBrooks Davis        base.stdout_file(), base.stderr_file());
174b0d29bc4SBrooks Davis
175*b392a90bSJohn Baldwin    return spawn_followup_post(base, timeout, std::move(child));
176b0d29bc4SBrooks Davis}
177b0d29bc4SBrooks Davis
178b0d29bc4SBrooks Davis
179b0d29bc4SBrooks Davis}  // namespace process
180b0d29bc4SBrooks Davis}  // namespace utils
181b0d29bc4SBrooks Davis
182b0d29bc4SBrooks Davis#endif  // !defined(UTILS_PROCESS_EXECUTOR_IPP)
183