xref: /freebsd/contrib/kyua/utils/process/executor.hpp (revision b0d29bc47dba79f6f38e67eabadfb4b32ffd9390)
1*b0d29bc4SBrooks Davis // Copyright 2015 The Kyua Authors.
2*b0d29bc4SBrooks Davis // All rights reserved.
3*b0d29bc4SBrooks Davis //
4*b0d29bc4SBrooks Davis // Redistribution and use in source and binary forms, with or without
5*b0d29bc4SBrooks Davis // modification, are permitted provided that the following conditions are
6*b0d29bc4SBrooks Davis // met:
7*b0d29bc4SBrooks Davis //
8*b0d29bc4SBrooks Davis // * Redistributions of source code must retain the above copyright
9*b0d29bc4SBrooks Davis //   notice, this list of conditions and the following disclaimer.
10*b0d29bc4SBrooks Davis // * Redistributions in binary form must reproduce the above copyright
11*b0d29bc4SBrooks Davis //   notice, this list of conditions and the following disclaimer in the
12*b0d29bc4SBrooks Davis //   documentation and/or other materials provided with the distribution.
13*b0d29bc4SBrooks Davis // * Neither the name of Google Inc. nor the names of its contributors
14*b0d29bc4SBrooks Davis //   may be used to endorse or promote products derived from this software
15*b0d29bc4SBrooks Davis //   without specific prior written permission.
16*b0d29bc4SBrooks Davis //
17*b0d29bc4SBrooks Davis // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18*b0d29bc4SBrooks Davis // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19*b0d29bc4SBrooks Davis // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20*b0d29bc4SBrooks Davis // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21*b0d29bc4SBrooks Davis // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22*b0d29bc4SBrooks Davis // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23*b0d29bc4SBrooks Davis // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24*b0d29bc4SBrooks Davis // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25*b0d29bc4SBrooks Davis // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26*b0d29bc4SBrooks Davis // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27*b0d29bc4SBrooks Davis // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28*b0d29bc4SBrooks Davis 
29*b0d29bc4SBrooks Davis /// \file utils/process/executor.hpp
30*b0d29bc4SBrooks Davis /// Multiprogrammed process executor with isolation guarantees.
31*b0d29bc4SBrooks Davis ///
32*b0d29bc4SBrooks Davis /// This module provides a mechanism to invoke more than one process
33*b0d29bc4SBrooks Davis /// concurrently while at the same time ensuring that each process is run
34*b0d29bc4SBrooks Davis /// in a clean container and in a "safe" work directory that gets cleaned
35*b0d29bc4SBrooks Davis /// up automatically on termination.
36*b0d29bc4SBrooks Davis ///
37*b0d29bc4SBrooks Davis /// The intended workflow for using this module is the following:
38*b0d29bc4SBrooks Davis ///
39*b0d29bc4SBrooks Davis /// 1) Initialize the executor using setup().  Keep the returned object
40*b0d29bc4SBrooks Davis ///    around through the lifetime of the next operations.  Only one
41*b0d29bc4SBrooks Davis ///    instance of the executor can be alive at once.
42*b0d29bc4SBrooks Davis /// 2) Spawn one or more processes with spawn().  On the caller side, keep
43*b0d29bc4SBrooks Davis ///    track of any per-process data you may need using the returned
44*b0d29bc4SBrooks Davis ///    exec_handle, which is unique among the set of active processes.
45*b0d29bc4SBrooks Davis /// 3) Call wait() or wait_any() to wait for completion of a process started
46*b0d29bc4SBrooks Davis ///    in the previous step.  Repeat as desired.
47*b0d29bc4SBrooks Davis /// 4) Use the returned exit_handle object by wait() or wait_any() to query
48*b0d29bc4SBrooks Davis ///    the status of the terminated process and/or to access any of its
49*b0d29bc4SBrooks Davis ///    data files.
50*b0d29bc4SBrooks Davis /// 5) Invoke cleanup() on the exit_handle to wipe any stale data.
51*b0d29bc4SBrooks Davis /// 6) Invoke cleanup() on the object returned by setup().
52*b0d29bc4SBrooks Davis ///
53*b0d29bc4SBrooks Davis /// It is the responsibility of the caller to ensure that calls to
54*b0d29bc4SBrooks Davis /// spawn() and spawn_followup() are balanced with wait() and wait_any() calls.
55*b0d29bc4SBrooks Davis ///
56*b0d29bc4SBrooks Davis /// Processes executed in this manner have access to two different "unique"
57*b0d29bc4SBrooks Davis /// directories: the first is the "work directory", which is an empty directory
58*b0d29bc4SBrooks Davis /// that acts as the subprocess' work directory; the second is the "control
59*b0d29bc4SBrooks Davis /// directory", which is the location where the in-process code may place files
60*b0d29bc4SBrooks Davis /// that are not clobbered by activities in the work directory.
61*b0d29bc4SBrooks Davis 
62*b0d29bc4SBrooks Davis #if !defined(UTILS_PROCESS_EXECUTOR_HPP)
63*b0d29bc4SBrooks Davis #define UTILS_PROCESS_EXECUTOR_HPP
64*b0d29bc4SBrooks Davis 
65*b0d29bc4SBrooks Davis #include "utils/process/executor_fwd.hpp"
66*b0d29bc4SBrooks Davis 
67*b0d29bc4SBrooks Davis #include <cstddef>
68*b0d29bc4SBrooks Davis #include <memory>
69*b0d29bc4SBrooks Davis 
70*b0d29bc4SBrooks Davis #include "utils/datetime_fwd.hpp"
71*b0d29bc4SBrooks Davis #include "utils/fs/path_fwd.hpp"
72*b0d29bc4SBrooks Davis #include "utils/optional.hpp"
73*b0d29bc4SBrooks Davis #include "utils/passwd_fwd.hpp"
74*b0d29bc4SBrooks Davis #include "utils/process/child_fwd.hpp"
75*b0d29bc4SBrooks Davis #include "utils/process/status_fwd.hpp"
76*b0d29bc4SBrooks Davis 
77*b0d29bc4SBrooks Davis namespace utils {
78*b0d29bc4SBrooks Davis namespace process {
79*b0d29bc4SBrooks Davis namespace executor {
80*b0d29bc4SBrooks Davis 
81*b0d29bc4SBrooks Davis 
82*b0d29bc4SBrooks Davis namespace detail {
83*b0d29bc4SBrooks Davis 
84*b0d29bc4SBrooks Davis 
85*b0d29bc4SBrooks Davis extern const char* stdout_name;
86*b0d29bc4SBrooks Davis extern const char* stderr_name;
87*b0d29bc4SBrooks Davis extern const char* work_subdir;
88*b0d29bc4SBrooks Davis 
89*b0d29bc4SBrooks Davis 
90*b0d29bc4SBrooks Davis /// Shared reference counter.
91*b0d29bc4SBrooks Davis typedef std::shared_ptr< std::size_t > refcnt_t;
92*b0d29bc4SBrooks Davis 
93*b0d29bc4SBrooks Davis 
94*b0d29bc4SBrooks Davis void setup_child(const utils::optional< utils::passwd::user >,
95*b0d29bc4SBrooks Davis                  const utils::fs::path&, const utils::fs::path&);
96*b0d29bc4SBrooks Davis 
97*b0d29bc4SBrooks Davis 
98*b0d29bc4SBrooks Davis }   // namespace detail
99*b0d29bc4SBrooks Davis 
100*b0d29bc4SBrooks Davis 
101*b0d29bc4SBrooks Davis /// Maintenance data held while a subprocess is being executed.
102*b0d29bc4SBrooks Davis ///
103*b0d29bc4SBrooks Davis /// This data structure exists from the moment a subprocess is executed via
104*b0d29bc4SBrooks Davis /// executor::spawn() to when it is cleaned up with exit_handle::cleanup().
105*b0d29bc4SBrooks Davis ///
106*b0d29bc4SBrooks Davis /// The caller NEED NOT maintain this object alive for the execution of the
107*b0d29bc4SBrooks Davis /// subprocess.  However, the PID contained in here can be used to match
108*b0d29bc4SBrooks Davis /// exec_handle objects with corresponding exit_handle objects via their
109*b0d29bc4SBrooks Davis /// original_pid() method.
110*b0d29bc4SBrooks Davis ///
111*b0d29bc4SBrooks Davis /// Objects of this type can be copied around but their implementation is
112*b0d29bc4SBrooks Davis /// shared.  The implication of this is that only the last copy of a given exit
113*b0d29bc4SBrooks Davis /// handle will execute the automatic cleanup() on destruction.
114*b0d29bc4SBrooks Davis class exec_handle {
115*b0d29bc4SBrooks Davis     struct impl;
116*b0d29bc4SBrooks Davis 
117*b0d29bc4SBrooks Davis     /// Pointer to internal implementation.
118*b0d29bc4SBrooks Davis     std::shared_ptr< impl > _pimpl;
119*b0d29bc4SBrooks Davis 
120*b0d29bc4SBrooks Davis     friend class executor_handle;
121*b0d29bc4SBrooks Davis     exec_handle(std::shared_ptr< impl >);
122*b0d29bc4SBrooks Davis 
123*b0d29bc4SBrooks Davis public:
124*b0d29bc4SBrooks Davis     ~exec_handle(void);
125*b0d29bc4SBrooks Davis 
126*b0d29bc4SBrooks Davis     int pid(void) const;
127*b0d29bc4SBrooks Davis     utils::fs::path control_directory(void) const;
128*b0d29bc4SBrooks Davis     utils::fs::path work_directory(void) const;
129*b0d29bc4SBrooks Davis     const utils::fs::path& stdout_file(void) const;
130*b0d29bc4SBrooks Davis     const utils::fs::path& stderr_file(void) const;
131*b0d29bc4SBrooks Davis };
132*b0d29bc4SBrooks Davis 
133*b0d29bc4SBrooks Davis 
134*b0d29bc4SBrooks Davis /// Container for the data of a process termination.
135*b0d29bc4SBrooks Davis ///
136*b0d29bc4SBrooks Davis /// This handle provides access to the details of the process that terminated
137*b0d29bc4SBrooks Davis /// and serves as the owner of the remaining on-disk files.  The caller is
138*b0d29bc4SBrooks Davis /// expected to call cleanup() before destruction to remove the on-disk state.
139*b0d29bc4SBrooks Davis ///
140*b0d29bc4SBrooks Davis /// Objects of this type can be copied around but their implementation is
141*b0d29bc4SBrooks Davis /// shared.  The implication of this is that only the last copy of a given exit
142*b0d29bc4SBrooks Davis /// handle will execute the automatic cleanup() on destruction.
143*b0d29bc4SBrooks Davis class exit_handle {
144*b0d29bc4SBrooks Davis     struct impl;
145*b0d29bc4SBrooks Davis 
146*b0d29bc4SBrooks Davis     /// Pointer to internal implementation.
147*b0d29bc4SBrooks Davis     std::shared_ptr< impl > _pimpl;
148*b0d29bc4SBrooks Davis 
149*b0d29bc4SBrooks Davis     friend class executor_handle;
150*b0d29bc4SBrooks Davis     exit_handle(std::shared_ptr< impl >);
151*b0d29bc4SBrooks Davis 
152*b0d29bc4SBrooks Davis     detail::refcnt_t state_owners(void) const;
153*b0d29bc4SBrooks Davis 
154*b0d29bc4SBrooks Davis public:
155*b0d29bc4SBrooks Davis     ~exit_handle(void);
156*b0d29bc4SBrooks Davis 
157*b0d29bc4SBrooks Davis     void cleanup(void);
158*b0d29bc4SBrooks Davis 
159*b0d29bc4SBrooks Davis     int original_pid(void) const;
160*b0d29bc4SBrooks Davis     const utils::optional< utils::process::status >& status(void) const;
161*b0d29bc4SBrooks Davis     const utils::optional< utils::passwd::user >& unprivileged_user(void) const;
162*b0d29bc4SBrooks Davis     const utils::datetime::timestamp& start_time() const;
163*b0d29bc4SBrooks Davis     const utils::datetime::timestamp& end_time() const;
164*b0d29bc4SBrooks Davis     utils::fs::path control_directory(void) const;
165*b0d29bc4SBrooks Davis     utils::fs::path work_directory(void) const;
166*b0d29bc4SBrooks Davis     const utils::fs::path& stdout_file(void) const;
167*b0d29bc4SBrooks Davis     const utils::fs::path& stderr_file(void) const;
168*b0d29bc4SBrooks Davis };
169*b0d29bc4SBrooks Davis 
170*b0d29bc4SBrooks Davis 
171*b0d29bc4SBrooks Davis /// Handler for the livelihood of the executor.
172*b0d29bc4SBrooks Davis ///
173*b0d29bc4SBrooks Davis /// Objects of this type can be copied around (because we do not have move
174*b0d29bc4SBrooks Davis /// semantics...) but their implementation is shared.  Only one instance of the
175*b0d29bc4SBrooks Davis /// executor can exist at any point in time.
176*b0d29bc4SBrooks Davis class executor_handle {
177*b0d29bc4SBrooks Davis     struct impl;
178*b0d29bc4SBrooks Davis     /// Pointer to internal implementation.
179*b0d29bc4SBrooks Davis     std::shared_ptr< impl > _pimpl;
180*b0d29bc4SBrooks Davis 
181*b0d29bc4SBrooks Davis     friend executor_handle setup(void);
182*b0d29bc4SBrooks Davis     executor_handle(void) throw();
183*b0d29bc4SBrooks Davis 
184*b0d29bc4SBrooks Davis     utils::fs::path spawn_pre(void);
185*b0d29bc4SBrooks Davis     exec_handle spawn_post(const utils::fs::path&,
186*b0d29bc4SBrooks Davis                            const utils::fs::path&,
187*b0d29bc4SBrooks Davis                            const utils::fs::path&,
188*b0d29bc4SBrooks Davis                            const utils::datetime::delta&,
189*b0d29bc4SBrooks Davis                            const utils::optional< utils::passwd::user >,
190*b0d29bc4SBrooks Davis                            std::auto_ptr< utils::process::child >);
191*b0d29bc4SBrooks Davis 
192*b0d29bc4SBrooks Davis     void spawn_followup_pre(void);
193*b0d29bc4SBrooks Davis     exec_handle spawn_followup_post(const exit_handle&,
194*b0d29bc4SBrooks Davis                                     const utils::datetime::delta&,
195*b0d29bc4SBrooks Davis                                     std::auto_ptr< utils::process::child >);
196*b0d29bc4SBrooks Davis 
197*b0d29bc4SBrooks Davis public:
198*b0d29bc4SBrooks Davis     ~executor_handle(void);
199*b0d29bc4SBrooks Davis 
200*b0d29bc4SBrooks Davis     const utils::fs::path& root_work_directory(void) const;
201*b0d29bc4SBrooks Davis 
202*b0d29bc4SBrooks Davis     void cleanup(void);
203*b0d29bc4SBrooks Davis 
204*b0d29bc4SBrooks Davis     template< class Hook >
205*b0d29bc4SBrooks Davis     exec_handle spawn(Hook,
206*b0d29bc4SBrooks Davis                       const datetime::delta&,
207*b0d29bc4SBrooks Davis                       const utils::optional< utils::passwd::user >,
208*b0d29bc4SBrooks Davis                       const utils::optional< utils::fs::path > = utils::none,
209*b0d29bc4SBrooks Davis                       const utils::optional< utils::fs::path > = utils::none);
210*b0d29bc4SBrooks Davis 
211*b0d29bc4SBrooks Davis     template< class Hook >
212*b0d29bc4SBrooks Davis     exec_handle spawn_followup(Hook,
213*b0d29bc4SBrooks Davis                                const exit_handle&,
214*b0d29bc4SBrooks Davis                                const datetime::delta&);
215*b0d29bc4SBrooks Davis 
216*b0d29bc4SBrooks Davis     exit_handle wait(const exec_handle);
217*b0d29bc4SBrooks Davis     exit_handle wait_any(void);
218*b0d29bc4SBrooks Davis 
219*b0d29bc4SBrooks Davis     void check_interrupt(void) const;
220*b0d29bc4SBrooks Davis };
221*b0d29bc4SBrooks Davis 
222*b0d29bc4SBrooks Davis 
223*b0d29bc4SBrooks Davis executor_handle setup(void);
224*b0d29bc4SBrooks Davis 
225*b0d29bc4SBrooks Davis 
226*b0d29bc4SBrooks Davis }  // namespace executor
227*b0d29bc4SBrooks Davis }  // namespace process
228*b0d29bc4SBrooks Davis }  // namespace utils
229*b0d29bc4SBrooks Davis 
230*b0d29bc4SBrooks Davis 
231*b0d29bc4SBrooks Davis #endif  // !defined(UTILS_PROCESS_EXECUTOR_HPP)
232