xref: /freebsd/contrib/kyua/utils/process/executor.hpp (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 /// \file utils/process/executor.hpp
30b0d29bc4SBrooks Davis /// Multiprogrammed process executor with isolation guarantees.
31b0d29bc4SBrooks Davis ///
32b0d29bc4SBrooks Davis /// This module provides a mechanism to invoke more than one process
33b0d29bc4SBrooks Davis /// concurrently while at the same time ensuring that each process is run
34b0d29bc4SBrooks Davis /// in a clean container and in a "safe" work directory that gets cleaned
35b0d29bc4SBrooks Davis /// up automatically on termination.
36b0d29bc4SBrooks Davis ///
37b0d29bc4SBrooks Davis /// The intended workflow for using this module is the following:
38b0d29bc4SBrooks Davis ///
39b0d29bc4SBrooks Davis /// 1) Initialize the executor using setup().  Keep the returned object
40b0d29bc4SBrooks Davis ///    around through the lifetime of the next operations.  Only one
41b0d29bc4SBrooks Davis ///    instance of the executor can be alive at once.
42b0d29bc4SBrooks Davis /// 2) Spawn one or more processes with spawn().  On the caller side, keep
43b0d29bc4SBrooks Davis ///    track of any per-process data you may need using the returned
44b0d29bc4SBrooks Davis ///    exec_handle, which is unique among the set of active processes.
45b0d29bc4SBrooks Davis /// 3) Call wait() or wait_any() to wait for completion of a process started
46b0d29bc4SBrooks Davis ///    in the previous step.  Repeat as desired.
47b0d29bc4SBrooks Davis /// 4) Use the returned exit_handle object by wait() or wait_any() to query
48b0d29bc4SBrooks Davis ///    the status of the terminated process and/or to access any of its
49b0d29bc4SBrooks Davis ///    data files.
50b0d29bc4SBrooks Davis /// 5) Invoke cleanup() on the exit_handle to wipe any stale data.
51b0d29bc4SBrooks Davis /// 6) Invoke cleanup() on the object returned by setup().
52b0d29bc4SBrooks Davis ///
53b0d29bc4SBrooks Davis /// It is the responsibility of the caller to ensure that calls to
54b0d29bc4SBrooks Davis /// spawn() and spawn_followup() are balanced with wait() and wait_any() calls.
55b0d29bc4SBrooks Davis ///
56b0d29bc4SBrooks Davis /// Processes executed in this manner have access to two different "unique"
57b0d29bc4SBrooks Davis /// directories: the first is the "work directory", which is an empty directory
58b0d29bc4SBrooks Davis /// that acts as the subprocess' work directory; the second is the "control
59b0d29bc4SBrooks Davis /// directory", which is the location where the in-process code may place files
60b0d29bc4SBrooks Davis /// that are not clobbered by activities in the work directory.
61b0d29bc4SBrooks Davis 
62b0d29bc4SBrooks Davis #if !defined(UTILS_PROCESS_EXECUTOR_HPP)
63b0d29bc4SBrooks Davis #define UTILS_PROCESS_EXECUTOR_HPP
64b0d29bc4SBrooks Davis 
65b0d29bc4SBrooks Davis #include "utils/process/executor_fwd.hpp"
66b0d29bc4SBrooks Davis 
67b0d29bc4SBrooks Davis #include <cstddef>
68b0d29bc4SBrooks Davis #include <memory>
69b0d29bc4SBrooks Davis 
70b0d29bc4SBrooks Davis #include "utils/datetime_fwd.hpp"
71b0d29bc4SBrooks Davis #include "utils/fs/path_fwd.hpp"
72b0d29bc4SBrooks Davis #include "utils/optional.hpp"
73b0d29bc4SBrooks Davis #include "utils/passwd_fwd.hpp"
74b0d29bc4SBrooks Davis #include "utils/process/child_fwd.hpp"
75b0d29bc4SBrooks Davis #include "utils/process/status_fwd.hpp"
76b0d29bc4SBrooks Davis 
77b0d29bc4SBrooks Davis namespace utils {
78b0d29bc4SBrooks Davis namespace process {
79b0d29bc4SBrooks Davis namespace executor {
80b0d29bc4SBrooks Davis 
81b0d29bc4SBrooks Davis 
82b0d29bc4SBrooks Davis namespace detail {
83b0d29bc4SBrooks Davis 
84b0d29bc4SBrooks Davis 
85b0d29bc4SBrooks Davis extern const char* stdout_name;
86b0d29bc4SBrooks Davis extern const char* stderr_name;
87b0d29bc4SBrooks Davis extern const char* work_subdir;
88b0d29bc4SBrooks Davis 
89b0d29bc4SBrooks Davis 
90b0d29bc4SBrooks Davis /// Shared reference counter.
91b0d29bc4SBrooks Davis typedef std::shared_ptr< std::size_t > refcnt_t;
92b0d29bc4SBrooks Davis 
93b0d29bc4SBrooks Davis 
94b0d29bc4SBrooks Davis void setup_child(const utils::optional< utils::passwd::user >,
95b0d29bc4SBrooks Davis                  const utils::fs::path&, const utils::fs::path&);
96b0d29bc4SBrooks Davis 
97b0d29bc4SBrooks Davis 
98b0d29bc4SBrooks Davis }   // namespace detail
99b0d29bc4SBrooks Davis 
100b0d29bc4SBrooks Davis 
101b0d29bc4SBrooks Davis /// Maintenance data held while a subprocess is being executed.
102b0d29bc4SBrooks Davis ///
103b0d29bc4SBrooks Davis /// This data structure exists from the moment a subprocess is executed via
104b0d29bc4SBrooks Davis /// executor::spawn() to when it is cleaned up with exit_handle::cleanup().
105b0d29bc4SBrooks Davis ///
106b0d29bc4SBrooks Davis /// The caller NEED NOT maintain this object alive for the execution of the
107b0d29bc4SBrooks Davis /// subprocess.  However, the PID contained in here can be used to match
108b0d29bc4SBrooks Davis /// exec_handle objects with corresponding exit_handle objects via their
109b0d29bc4SBrooks Davis /// original_pid() method.
110b0d29bc4SBrooks Davis ///
111b0d29bc4SBrooks Davis /// Objects of this type can be copied around but their implementation is
112b0d29bc4SBrooks Davis /// shared.  The implication of this is that only the last copy of a given exit
113b0d29bc4SBrooks Davis /// handle will execute the automatic cleanup() on destruction.
114b0d29bc4SBrooks Davis class exec_handle {
115b0d29bc4SBrooks Davis     struct impl;
116b0d29bc4SBrooks Davis 
117b0d29bc4SBrooks Davis     /// Pointer to internal implementation.
118b0d29bc4SBrooks Davis     std::shared_ptr< impl > _pimpl;
119b0d29bc4SBrooks Davis 
120b0d29bc4SBrooks Davis     friend class executor_handle;
121b0d29bc4SBrooks Davis     exec_handle(std::shared_ptr< impl >);
122b0d29bc4SBrooks Davis 
123b0d29bc4SBrooks Davis public:
124b0d29bc4SBrooks Davis     ~exec_handle(void);
125b0d29bc4SBrooks Davis 
126b0d29bc4SBrooks Davis     int pid(void) const;
127b0d29bc4SBrooks Davis     utils::fs::path control_directory(void) const;
128b0d29bc4SBrooks Davis     utils::fs::path work_directory(void) const;
129b0d29bc4SBrooks Davis     const utils::fs::path& stdout_file(void) const;
130b0d29bc4SBrooks Davis     const utils::fs::path& stderr_file(void) const;
131b0d29bc4SBrooks Davis };
132b0d29bc4SBrooks Davis 
133b0d29bc4SBrooks Davis 
134b0d29bc4SBrooks Davis /// Container for the data of a process termination.
135b0d29bc4SBrooks Davis ///
136b0d29bc4SBrooks Davis /// This handle provides access to the details of the process that terminated
137b0d29bc4SBrooks Davis /// and serves as the owner of the remaining on-disk files.  The caller is
138b0d29bc4SBrooks Davis /// expected to call cleanup() before destruction to remove the on-disk state.
139b0d29bc4SBrooks Davis ///
140b0d29bc4SBrooks Davis /// Objects of this type can be copied around but their implementation is
141b0d29bc4SBrooks Davis /// shared.  The implication of this is that only the last copy of a given exit
142b0d29bc4SBrooks Davis /// handle will execute the automatic cleanup() on destruction.
143b0d29bc4SBrooks Davis class exit_handle {
144b0d29bc4SBrooks Davis     struct impl;
145b0d29bc4SBrooks Davis 
146b0d29bc4SBrooks Davis     /// Pointer to internal implementation.
147b0d29bc4SBrooks Davis     std::shared_ptr< impl > _pimpl;
148b0d29bc4SBrooks Davis 
149b0d29bc4SBrooks Davis     friend class executor_handle;
150b0d29bc4SBrooks Davis     exit_handle(std::shared_ptr< impl >);
151b0d29bc4SBrooks Davis 
152b0d29bc4SBrooks Davis     detail::refcnt_t state_owners(void) const;
153b0d29bc4SBrooks Davis 
154b0d29bc4SBrooks Davis public:
155b0d29bc4SBrooks Davis     ~exit_handle(void);
156b0d29bc4SBrooks Davis 
157b0d29bc4SBrooks Davis     void cleanup(void);
158b0d29bc4SBrooks Davis 
159b0d29bc4SBrooks Davis     int original_pid(void) const;
160b0d29bc4SBrooks Davis     const utils::optional< utils::process::status >& status(void) const;
161b0d29bc4SBrooks Davis     const utils::optional< utils::passwd::user >& unprivileged_user(void) const;
162b0d29bc4SBrooks Davis     const utils::datetime::timestamp& start_time() const;
163b0d29bc4SBrooks Davis     const utils::datetime::timestamp& end_time() const;
164b0d29bc4SBrooks Davis     utils::fs::path control_directory(void) const;
165b0d29bc4SBrooks Davis     utils::fs::path work_directory(void) const;
166b0d29bc4SBrooks Davis     const utils::fs::path& stdout_file(void) const;
167b0d29bc4SBrooks Davis     const utils::fs::path& stderr_file(void) const;
168b0d29bc4SBrooks Davis };
169b0d29bc4SBrooks Davis 
170b0d29bc4SBrooks Davis 
171b0d29bc4SBrooks Davis /// Handler for the livelihood of the executor.
172b0d29bc4SBrooks Davis ///
173b0d29bc4SBrooks Davis /// Objects of this type can be copied around (because we do not have move
174b0d29bc4SBrooks Davis /// semantics...) but their implementation is shared.  Only one instance of the
175b0d29bc4SBrooks Davis /// executor can exist at any point in time.
176b0d29bc4SBrooks Davis class executor_handle {
177b0d29bc4SBrooks Davis     struct impl;
178b0d29bc4SBrooks Davis     /// Pointer to internal implementation.
179b0d29bc4SBrooks Davis     std::shared_ptr< impl > _pimpl;
180b0d29bc4SBrooks Davis 
181b0d29bc4SBrooks Davis     friend executor_handle setup(void);
182b0d29bc4SBrooks Davis     executor_handle(void) throw();
183b0d29bc4SBrooks Davis 
184b0d29bc4SBrooks Davis     utils::fs::path spawn_pre(void);
185b0d29bc4SBrooks Davis     exec_handle spawn_post(const utils::fs::path&,
186b0d29bc4SBrooks Davis                            const utils::fs::path&,
187b0d29bc4SBrooks Davis                            const utils::fs::path&,
188b0d29bc4SBrooks Davis                            const utils::datetime::delta&,
189b0d29bc4SBrooks Davis                            const utils::optional< utils::passwd::user >,
190*b392a90bSJohn Baldwin                            std::unique_ptr< utils::process::child >);
191b0d29bc4SBrooks Davis 
192b0d29bc4SBrooks Davis     void spawn_followup_pre(void);
193b0d29bc4SBrooks Davis     exec_handle spawn_followup_post(const exit_handle&,
194b0d29bc4SBrooks Davis                                     const utils::datetime::delta&,
195*b392a90bSJohn Baldwin                                     std::unique_ptr< utils::process::child >);
196b0d29bc4SBrooks Davis 
197b0d29bc4SBrooks Davis public:
198b0d29bc4SBrooks Davis     ~executor_handle(void);
199b0d29bc4SBrooks Davis 
200b0d29bc4SBrooks Davis     const utils::fs::path& root_work_directory(void) const;
201b0d29bc4SBrooks Davis 
202b0d29bc4SBrooks Davis     void cleanup(void);
203b0d29bc4SBrooks Davis 
204b0d29bc4SBrooks Davis     template< class Hook >
205b0d29bc4SBrooks Davis     exec_handle spawn(Hook,
206b0d29bc4SBrooks Davis                       const datetime::delta&,
207b0d29bc4SBrooks Davis                       const utils::optional< utils::passwd::user >,
208b0d29bc4SBrooks Davis                       const utils::optional< utils::fs::path > = utils::none,
209b0d29bc4SBrooks Davis                       const utils::optional< utils::fs::path > = utils::none);
210b0d29bc4SBrooks Davis 
211b0d29bc4SBrooks Davis     template< class Hook >
212b0d29bc4SBrooks Davis     exec_handle spawn_followup(Hook,
213b0d29bc4SBrooks Davis                                const exit_handle&,
214b0d29bc4SBrooks Davis                                const datetime::delta&);
215b0d29bc4SBrooks Davis 
216b0d29bc4SBrooks Davis     exit_handle wait(const exec_handle);
217b0d29bc4SBrooks Davis     exit_handle wait_any(void);
218257e70f1SIgor Ostapenko     exit_handle reap(const pid_t);
219b0d29bc4SBrooks Davis 
220b0d29bc4SBrooks Davis     void check_interrupt(void) const;
221b0d29bc4SBrooks Davis };
222b0d29bc4SBrooks Davis 
223b0d29bc4SBrooks Davis 
224b0d29bc4SBrooks Davis executor_handle setup(void);
225b0d29bc4SBrooks Davis 
226b0d29bc4SBrooks Davis 
227b0d29bc4SBrooks Davis }  // namespace executor
228b0d29bc4SBrooks Davis }  // namespace process
229b0d29bc4SBrooks Davis }  // namespace utils
230b0d29bc4SBrooks Davis 
231b0d29bc4SBrooks Davis 
232b0d29bc4SBrooks Davis #endif  // !defined(UTILS_PROCESS_EXECUTOR_HPP)
233