xref: /freebsd/contrib/kyua/utils/process/executor.cpp (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 #include "utils/process/executor.ipp"
30*b0d29bc4SBrooks Davis 
31*b0d29bc4SBrooks Davis #if defined(HAVE_CONFIG_H)
32*b0d29bc4SBrooks Davis #include "config.h"
33*b0d29bc4SBrooks Davis #endif
34*b0d29bc4SBrooks Davis 
35*b0d29bc4SBrooks Davis extern "C" {
36*b0d29bc4SBrooks Davis #include <sys/types.h>
37*b0d29bc4SBrooks Davis #include <sys/wait.h>
38*b0d29bc4SBrooks Davis 
39*b0d29bc4SBrooks Davis #include <signal.h>
40*b0d29bc4SBrooks Davis }
41*b0d29bc4SBrooks Davis 
42*b0d29bc4SBrooks Davis #include <fstream>
43*b0d29bc4SBrooks Davis #include <map>
44*b0d29bc4SBrooks Davis #include <memory>
45*b0d29bc4SBrooks Davis #include <stdexcept>
46*b0d29bc4SBrooks Davis 
47*b0d29bc4SBrooks Davis #include "utils/datetime.hpp"
48*b0d29bc4SBrooks Davis #include "utils/format/macros.hpp"
49*b0d29bc4SBrooks Davis #include "utils/fs/auto_cleaners.hpp"
50*b0d29bc4SBrooks Davis #include "utils/fs/exceptions.hpp"
51*b0d29bc4SBrooks Davis #include "utils/fs/operations.hpp"
52*b0d29bc4SBrooks Davis #include "utils/fs/path.hpp"
53*b0d29bc4SBrooks Davis #include "utils/logging/macros.hpp"
54*b0d29bc4SBrooks Davis #include "utils/logging/operations.hpp"
55*b0d29bc4SBrooks Davis #include "utils/noncopyable.hpp"
56*b0d29bc4SBrooks Davis #include "utils/optional.ipp"
57*b0d29bc4SBrooks Davis #include "utils/passwd.hpp"
58*b0d29bc4SBrooks Davis #include "utils/process/child.ipp"
59*b0d29bc4SBrooks Davis #include "utils/process/deadline_killer.hpp"
60*b0d29bc4SBrooks Davis #include "utils/process/isolation.hpp"
61*b0d29bc4SBrooks Davis #include "utils/process/operations.hpp"
62*b0d29bc4SBrooks Davis #include "utils/process/status.hpp"
63*b0d29bc4SBrooks Davis #include "utils/sanity.hpp"
64*b0d29bc4SBrooks Davis #include "utils/signals/interrupts.hpp"
65*b0d29bc4SBrooks Davis #include "utils/signals/timer.hpp"
66*b0d29bc4SBrooks Davis 
67*b0d29bc4SBrooks Davis namespace datetime = utils::datetime;
68*b0d29bc4SBrooks Davis namespace executor = utils::process::executor;
69*b0d29bc4SBrooks Davis namespace fs = utils::fs;
70*b0d29bc4SBrooks Davis namespace logging = utils::logging;
71*b0d29bc4SBrooks Davis namespace passwd = utils::passwd;
72*b0d29bc4SBrooks Davis namespace process = utils::process;
73*b0d29bc4SBrooks Davis namespace signals = utils::signals;
74*b0d29bc4SBrooks Davis 
75*b0d29bc4SBrooks Davis using utils::none;
76*b0d29bc4SBrooks Davis using utils::optional;
77*b0d29bc4SBrooks Davis 
78*b0d29bc4SBrooks Davis 
79*b0d29bc4SBrooks Davis namespace {
80*b0d29bc4SBrooks Davis 
81*b0d29bc4SBrooks Davis 
82*b0d29bc4SBrooks Davis /// Template for temporary directories created by the executor.
83*b0d29bc4SBrooks Davis static const char* work_directory_template = PACKAGE_TARNAME ".XXXXXX";
84*b0d29bc4SBrooks Davis 
85*b0d29bc4SBrooks Davis 
86*b0d29bc4SBrooks Davis /// Mapping of active subprocess PIDs to their execution data.
87*b0d29bc4SBrooks Davis typedef std::map< int, executor::exec_handle > exec_handles_map;
88*b0d29bc4SBrooks Davis 
89*b0d29bc4SBrooks Davis 
90*b0d29bc4SBrooks Davis }  // anonymous namespace
91*b0d29bc4SBrooks Davis 
92*b0d29bc4SBrooks Davis 
93*b0d29bc4SBrooks Davis /// Basename of the file containing the stdout of the subprocess.
94*b0d29bc4SBrooks Davis const char* utils::process::executor::detail::stdout_name = "stdout.txt";
95*b0d29bc4SBrooks Davis 
96*b0d29bc4SBrooks Davis 
97*b0d29bc4SBrooks Davis /// Basename of the file containing the stderr of the subprocess.
98*b0d29bc4SBrooks Davis const char* utils::process::executor::detail::stderr_name = "stderr.txt";
99*b0d29bc4SBrooks Davis 
100*b0d29bc4SBrooks Davis 
101*b0d29bc4SBrooks Davis /// Basename of the subdirectory in which the subprocess is actually executed.
102*b0d29bc4SBrooks Davis ///
103*b0d29bc4SBrooks Davis /// This is a subdirectory of the "unique work directory" generated for the
104*b0d29bc4SBrooks Davis /// subprocess so that our code can create control files on disk and not
105*b0d29bc4SBrooks Davis /// get them clobbered by the subprocess's activity.
106*b0d29bc4SBrooks Davis const char* utils::process::executor::detail::work_subdir = "work";
107*b0d29bc4SBrooks Davis 
108*b0d29bc4SBrooks Davis 
109*b0d29bc4SBrooks Davis /// Prepares a subprocess to run a user-provided hook in a controlled manner.
110*b0d29bc4SBrooks Davis ///
111*b0d29bc4SBrooks Davis /// \param unprivileged_user User to switch to if not none.
112*b0d29bc4SBrooks Davis /// \param control_directory Path to the subprocess-specific control directory.
113*b0d29bc4SBrooks Davis /// \param work_directory Path to the subprocess-specific work directory.
114*b0d29bc4SBrooks Davis void
115*b0d29bc4SBrooks Davis utils::process::executor::detail::setup_child(
116*b0d29bc4SBrooks Davis     const optional< passwd::user > unprivileged_user,
117*b0d29bc4SBrooks Davis     const fs::path& control_directory,
118*b0d29bc4SBrooks Davis     const fs::path& work_directory)
119*b0d29bc4SBrooks Davis {
120*b0d29bc4SBrooks Davis     logging::set_inmemory();
121*b0d29bc4SBrooks Davis     process::isolate_path(unprivileged_user, control_directory);
122*b0d29bc4SBrooks Davis     process::isolate_child(unprivileged_user, work_directory);
123*b0d29bc4SBrooks Davis }
124*b0d29bc4SBrooks Davis 
125*b0d29bc4SBrooks Davis 
126*b0d29bc4SBrooks Davis /// Internal implementation for the exit_handle class.
127*b0d29bc4SBrooks Davis struct utils::process::executor::exec_handle::impl : utils::noncopyable {
128*b0d29bc4SBrooks Davis     /// PID of the process being run.
129*b0d29bc4SBrooks Davis     int pid;
130*b0d29bc4SBrooks Davis 
131*b0d29bc4SBrooks Davis     /// Path to the subprocess-specific work directory.
132*b0d29bc4SBrooks Davis     fs::path control_directory;
133*b0d29bc4SBrooks Davis 
134*b0d29bc4SBrooks Davis     /// Path to the subprocess's stdout file.
135*b0d29bc4SBrooks Davis     const fs::path stdout_file;
136*b0d29bc4SBrooks Davis 
137*b0d29bc4SBrooks Davis     /// Path to the subprocess's stderr file.
138*b0d29bc4SBrooks Davis     const fs::path stderr_file;
139*b0d29bc4SBrooks Davis 
140*b0d29bc4SBrooks Davis     /// Start time.
141*b0d29bc4SBrooks Davis     datetime::timestamp start_time;
142*b0d29bc4SBrooks Davis 
143*b0d29bc4SBrooks Davis     /// User the subprocess is running as if different than the current one.
144*b0d29bc4SBrooks Davis     const optional< passwd::user > unprivileged_user;
145*b0d29bc4SBrooks Davis 
146*b0d29bc4SBrooks Davis     /// Timer to kill the subprocess on activation.
147*b0d29bc4SBrooks Davis     process::deadline_killer timer;
148*b0d29bc4SBrooks Davis 
149*b0d29bc4SBrooks Davis     /// Number of owners of the on-disk state.
150*b0d29bc4SBrooks Davis     executor::detail::refcnt_t state_owners;
151*b0d29bc4SBrooks Davis 
152*b0d29bc4SBrooks Davis     /// Constructor.
153*b0d29bc4SBrooks Davis     ///
154*b0d29bc4SBrooks Davis     /// \param pid_ PID of the forked process.
155*b0d29bc4SBrooks Davis     /// \param control_directory_ Path to the subprocess-specific work
156*b0d29bc4SBrooks Davis     ///     directory.
157*b0d29bc4SBrooks Davis     /// \param stdout_file_ Path to the subprocess's stdout file.
158*b0d29bc4SBrooks Davis     /// \param stderr_file_ Path to the subprocess's stderr file.
159*b0d29bc4SBrooks Davis     /// \param start_time_ Timestamp of when this object was constructed.
160*b0d29bc4SBrooks Davis     /// \param timeout Maximum amount of time the subprocess can run for.
161*b0d29bc4SBrooks Davis     /// \param unprivileged_user_ User the subprocess is running as if
162*b0d29bc4SBrooks Davis     ///     different than the current one.
163*b0d29bc4SBrooks Davis     /// \param [in,out] state_owners_ Number of owners of the on-disk state.
164*b0d29bc4SBrooks Davis     ///     For first-time processes, this should be a new counter set to 0;
165*b0d29bc4SBrooks Davis     ///     for followup processes, this should point to the same counter used
166*b0d29bc4SBrooks Davis     ///     by the preceding process.
167*b0d29bc4SBrooks Davis     impl(const int pid_,
168*b0d29bc4SBrooks Davis          const fs::path& control_directory_,
169*b0d29bc4SBrooks Davis          const fs::path& stdout_file_,
170*b0d29bc4SBrooks Davis          const fs::path& stderr_file_,
171*b0d29bc4SBrooks Davis          const datetime::timestamp& start_time_,
172*b0d29bc4SBrooks Davis          const datetime::delta& timeout,
173*b0d29bc4SBrooks Davis          const optional< passwd::user > unprivileged_user_,
174*b0d29bc4SBrooks Davis          executor::detail::refcnt_t state_owners_) :
175*b0d29bc4SBrooks Davis         pid(pid_),
176*b0d29bc4SBrooks Davis         control_directory(control_directory_),
177*b0d29bc4SBrooks Davis         stdout_file(stdout_file_),
178*b0d29bc4SBrooks Davis         stderr_file(stderr_file_),
179*b0d29bc4SBrooks Davis         start_time(start_time_),
180*b0d29bc4SBrooks Davis         unprivileged_user(unprivileged_user_),
181*b0d29bc4SBrooks Davis         timer(timeout, pid_),
182*b0d29bc4SBrooks Davis         state_owners(state_owners_)
183*b0d29bc4SBrooks Davis     {
184*b0d29bc4SBrooks Davis         (*state_owners)++;
185*b0d29bc4SBrooks Davis         POST(*state_owners > 0);
186*b0d29bc4SBrooks Davis     }
187*b0d29bc4SBrooks Davis };
188*b0d29bc4SBrooks Davis 
189*b0d29bc4SBrooks Davis 
190*b0d29bc4SBrooks Davis /// Constructor.
191*b0d29bc4SBrooks Davis ///
192*b0d29bc4SBrooks Davis /// \param pimpl Constructed internal implementation.
193*b0d29bc4SBrooks Davis executor::exec_handle::exec_handle(std::shared_ptr< impl > pimpl) :
194*b0d29bc4SBrooks Davis     _pimpl(pimpl)
195*b0d29bc4SBrooks Davis {
196*b0d29bc4SBrooks Davis }
197*b0d29bc4SBrooks Davis 
198*b0d29bc4SBrooks Davis 
199*b0d29bc4SBrooks Davis /// Destructor.
200*b0d29bc4SBrooks Davis executor::exec_handle::~exec_handle(void)
201*b0d29bc4SBrooks Davis {
202*b0d29bc4SBrooks Davis }
203*b0d29bc4SBrooks Davis 
204*b0d29bc4SBrooks Davis 
205*b0d29bc4SBrooks Davis /// Returns the PID of the process being run.
206*b0d29bc4SBrooks Davis ///
207*b0d29bc4SBrooks Davis /// \return A PID.
208*b0d29bc4SBrooks Davis int
209*b0d29bc4SBrooks Davis executor::exec_handle::pid(void) const
210*b0d29bc4SBrooks Davis {
211*b0d29bc4SBrooks Davis     return _pimpl->pid;
212*b0d29bc4SBrooks Davis }
213*b0d29bc4SBrooks Davis 
214*b0d29bc4SBrooks Davis 
215*b0d29bc4SBrooks Davis /// Returns the path to the subprocess-specific control directory.
216*b0d29bc4SBrooks Davis ///
217*b0d29bc4SBrooks Davis /// This is where the executor may store control files.
218*b0d29bc4SBrooks Davis ///
219*b0d29bc4SBrooks Davis /// \return The path to a directory that exists until cleanup() is called.
220*b0d29bc4SBrooks Davis fs::path
221*b0d29bc4SBrooks Davis executor::exec_handle::control_directory(void) const
222*b0d29bc4SBrooks Davis {
223*b0d29bc4SBrooks Davis     return _pimpl->control_directory;
224*b0d29bc4SBrooks Davis }
225*b0d29bc4SBrooks Davis 
226*b0d29bc4SBrooks Davis 
227*b0d29bc4SBrooks Davis /// Returns the path to the subprocess-specific work directory.
228*b0d29bc4SBrooks Davis ///
229*b0d29bc4SBrooks Davis /// This is guaranteed to be clear of files created by the executor.
230*b0d29bc4SBrooks Davis ///
231*b0d29bc4SBrooks Davis /// \return The path to a directory that exists until cleanup() is called.
232*b0d29bc4SBrooks Davis fs::path
233*b0d29bc4SBrooks Davis executor::exec_handle::work_directory(void) const
234*b0d29bc4SBrooks Davis {
235*b0d29bc4SBrooks Davis     return _pimpl->control_directory / detail::work_subdir;
236*b0d29bc4SBrooks Davis }
237*b0d29bc4SBrooks Davis 
238*b0d29bc4SBrooks Davis 
239*b0d29bc4SBrooks Davis /// Returns the path to the subprocess's stdout file.
240*b0d29bc4SBrooks Davis ///
241*b0d29bc4SBrooks Davis /// \return The path to a file that exists until cleanup() is called.
242*b0d29bc4SBrooks Davis const fs::path&
243*b0d29bc4SBrooks Davis executor::exec_handle::stdout_file(void) const
244*b0d29bc4SBrooks Davis {
245*b0d29bc4SBrooks Davis     return _pimpl->stdout_file;
246*b0d29bc4SBrooks Davis }
247*b0d29bc4SBrooks Davis 
248*b0d29bc4SBrooks Davis 
249*b0d29bc4SBrooks Davis /// Returns the path to the subprocess's stderr file.
250*b0d29bc4SBrooks Davis ///
251*b0d29bc4SBrooks Davis /// \return The path to a file that exists until cleanup() is called.
252*b0d29bc4SBrooks Davis const fs::path&
253*b0d29bc4SBrooks Davis executor::exec_handle::stderr_file(void) const
254*b0d29bc4SBrooks Davis {
255*b0d29bc4SBrooks Davis     return _pimpl->stderr_file;
256*b0d29bc4SBrooks Davis }
257*b0d29bc4SBrooks Davis 
258*b0d29bc4SBrooks Davis 
259*b0d29bc4SBrooks Davis /// Internal implementation for the exit_handle class.
260*b0d29bc4SBrooks Davis struct utils::process::executor::exit_handle::impl : utils::noncopyable {
261*b0d29bc4SBrooks Davis     /// Original PID of the terminated subprocess.
262*b0d29bc4SBrooks Davis     ///
263*b0d29bc4SBrooks Davis     /// Note that this PID is no longer valid and cannot be used on system
264*b0d29bc4SBrooks Davis     /// tables!
265*b0d29bc4SBrooks Davis     const int original_pid;
266*b0d29bc4SBrooks Davis 
267*b0d29bc4SBrooks Davis     /// Termination status of the subprocess, or none if it timed out.
268*b0d29bc4SBrooks Davis     const optional< process::status > status;
269*b0d29bc4SBrooks Davis 
270*b0d29bc4SBrooks Davis     /// The user the process ran as, if different than the current one.
271*b0d29bc4SBrooks Davis     const optional< passwd::user > unprivileged_user;
272*b0d29bc4SBrooks Davis 
273*b0d29bc4SBrooks Davis     /// Timestamp of when the subprocess was spawned.
274*b0d29bc4SBrooks Davis     const datetime::timestamp start_time;
275*b0d29bc4SBrooks Davis 
276*b0d29bc4SBrooks Davis     /// Timestamp of when wait() or wait_any() returned this object.
277*b0d29bc4SBrooks Davis     const datetime::timestamp end_time;
278*b0d29bc4SBrooks Davis 
279*b0d29bc4SBrooks Davis     /// Path to the subprocess-specific work directory.
280*b0d29bc4SBrooks Davis     const fs::path control_directory;
281*b0d29bc4SBrooks Davis 
282*b0d29bc4SBrooks Davis     /// Path to the subprocess's stdout file.
283*b0d29bc4SBrooks Davis     const fs::path stdout_file;
284*b0d29bc4SBrooks Davis 
285*b0d29bc4SBrooks Davis     /// Path to the subprocess's stderr file.
286*b0d29bc4SBrooks Davis     const fs::path stderr_file;
287*b0d29bc4SBrooks Davis 
288*b0d29bc4SBrooks Davis     /// Number of owners of the on-disk state.
289*b0d29bc4SBrooks Davis     ///
290*b0d29bc4SBrooks Davis     /// This will be 1 if this exit_handle is the last holder of the on-disk
291*b0d29bc4SBrooks Davis     /// state, in which case cleanup() invocations will wipe the disk state.
292*b0d29bc4SBrooks Davis     /// For all other cases, this will hold a higher value.
293*b0d29bc4SBrooks Davis     detail::refcnt_t state_owners;
294*b0d29bc4SBrooks Davis 
295*b0d29bc4SBrooks Davis     /// Mutable pointer to the corresponding executor state.
296*b0d29bc4SBrooks Davis     ///
297*b0d29bc4SBrooks Davis     /// This object references a member of the executor_handle that yielded this
298*b0d29bc4SBrooks Davis     /// exit_handle instance.  We need this direct access to clean up after
299*b0d29bc4SBrooks Davis     /// ourselves when the handle is destroyed.
300*b0d29bc4SBrooks Davis     exec_handles_map& all_exec_handles;
301*b0d29bc4SBrooks Davis 
302*b0d29bc4SBrooks Davis     /// Whether the subprocess state has been cleaned yet or not.
303*b0d29bc4SBrooks Davis     ///
304*b0d29bc4SBrooks Davis     /// Used to keep track of explicit calls to the public cleanup().
305*b0d29bc4SBrooks Davis     bool cleaned;
306*b0d29bc4SBrooks Davis 
307*b0d29bc4SBrooks Davis     /// Constructor.
308*b0d29bc4SBrooks Davis     ///
309*b0d29bc4SBrooks Davis     /// \param original_pid_ Original PID of the terminated subprocess.
310*b0d29bc4SBrooks Davis     /// \param status_ Termination status of the subprocess, or none if
311*b0d29bc4SBrooks Davis     ///     timed out.
312*b0d29bc4SBrooks Davis     /// \param unprivileged_user_ The user the process ran as, if different than
313*b0d29bc4SBrooks Davis     ///     the current one.
314*b0d29bc4SBrooks Davis     /// \param start_time_ Timestamp of when the subprocess was spawned.
315*b0d29bc4SBrooks Davis     /// \param end_time_ Timestamp of when wait() or wait_any() returned this
316*b0d29bc4SBrooks Davis     ///     object.
317*b0d29bc4SBrooks Davis     /// \param control_directory_ Path to the subprocess-specific work
318*b0d29bc4SBrooks Davis     ///     directory.
319*b0d29bc4SBrooks Davis     /// \param stdout_file_ Path to the subprocess's stdout file.
320*b0d29bc4SBrooks Davis     /// \param stderr_file_ Path to the subprocess's stderr file.
321*b0d29bc4SBrooks Davis     /// \param [in,out] state_owners_ Number of owners of the on-disk state.
322*b0d29bc4SBrooks Davis     /// \param [in,out] all_exec_handles_ Global object keeping track of all
323*b0d29bc4SBrooks Davis     ///     active executions for an executor.  This is a pointer to a member of
324*b0d29bc4SBrooks Davis     ///     the executor_handle object.
325*b0d29bc4SBrooks Davis     impl(const int original_pid_,
326*b0d29bc4SBrooks Davis          const optional< process::status > status_,
327*b0d29bc4SBrooks Davis          const optional< passwd::user > unprivileged_user_,
328*b0d29bc4SBrooks Davis          const datetime::timestamp& start_time_,
329*b0d29bc4SBrooks Davis          const datetime::timestamp& end_time_,
330*b0d29bc4SBrooks Davis          const fs::path& control_directory_,
331*b0d29bc4SBrooks Davis          const fs::path& stdout_file_,
332*b0d29bc4SBrooks Davis          const fs::path& stderr_file_,
333*b0d29bc4SBrooks Davis          detail::refcnt_t state_owners_,
334*b0d29bc4SBrooks Davis          exec_handles_map& all_exec_handles_) :
335*b0d29bc4SBrooks Davis         original_pid(original_pid_), status(status_),
336*b0d29bc4SBrooks Davis         unprivileged_user(unprivileged_user_),
337*b0d29bc4SBrooks Davis         start_time(start_time_), end_time(end_time_),
338*b0d29bc4SBrooks Davis         control_directory(control_directory_),
339*b0d29bc4SBrooks Davis         stdout_file(stdout_file_), stderr_file(stderr_file_),
340*b0d29bc4SBrooks Davis         state_owners(state_owners_),
341*b0d29bc4SBrooks Davis         all_exec_handles(all_exec_handles_), cleaned(false)
342*b0d29bc4SBrooks Davis     {
343*b0d29bc4SBrooks Davis     }
344*b0d29bc4SBrooks Davis 
345*b0d29bc4SBrooks Davis     /// Destructor.
346*b0d29bc4SBrooks Davis     ~impl(void)
347*b0d29bc4SBrooks Davis     {
348*b0d29bc4SBrooks Davis         if (!cleaned) {
349*b0d29bc4SBrooks Davis             LW(F("Implicitly cleaning up exit_handle for exec_handle %s; "
350*b0d29bc4SBrooks Davis                  "ignoring errors!") % original_pid);
351*b0d29bc4SBrooks Davis             try {
352*b0d29bc4SBrooks Davis                 cleanup();
353*b0d29bc4SBrooks Davis             } catch (const std::runtime_error& error) {
354*b0d29bc4SBrooks Davis                 LE(F("Subprocess cleanup failed: %s") % error.what());
355*b0d29bc4SBrooks Davis             }
356*b0d29bc4SBrooks Davis         }
357*b0d29bc4SBrooks Davis     }
358*b0d29bc4SBrooks Davis 
359*b0d29bc4SBrooks Davis     /// Cleans up the subprocess on-disk state.
360*b0d29bc4SBrooks Davis     ///
361*b0d29bc4SBrooks Davis     /// \throw engine::error If the cleanup fails, especially due to the
362*b0d29bc4SBrooks Davis     ///     inability to remove the work directory.
363*b0d29bc4SBrooks Davis     void
364*b0d29bc4SBrooks Davis     cleanup(void)
365*b0d29bc4SBrooks Davis     {
366*b0d29bc4SBrooks Davis         PRE(*state_owners > 0);
367*b0d29bc4SBrooks Davis         if (*state_owners == 1) {
368*b0d29bc4SBrooks Davis             LI(F("Cleaning up exit_handle for exec_handle %s") % original_pid);
369*b0d29bc4SBrooks Davis             fs::rm_r(control_directory);
370*b0d29bc4SBrooks Davis         } else {
371*b0d29bc4SBrooks Davis             LI(F("Not cleaning up exit_handle for exec_handle %s; "
372*b0d29bc4SBrooks Davis                  "%s owners left") % original_pid % (*state_owners - 1));
373*b0d29bc4SBrooks Davis         }
374*b0d29bc4SBrooks Davis         // We must decrease our reference only after we have successfully
375*b0d29bc4SBrooks Davis         // cleaned up the control directory.  Otherwise, the rm_r call would
376*b0d29bc4SBrooks Davis         // throw an exception, which would in turn invoke the implicit cleanup
377*b0d29bc4SBrooks Davis         // from the destructor, which would make us crash due to an invalid
378*b0d29bc4SBrooks Davis         // reference count.
379*b0d29bc4SBrooks Davis         (*state_owners)--;
380*b0d29bc4SBrooks Davis         // Marking this object as clean here, even if we did not do actually the
381*b0d29bc4SBrooks Davis         // cleaning above, is fine (albeit a bit confusing).  Note that "another
382*b0d29bc4SBrooks Davis         // owner" refers to a handle for a different PID, so that handle will be
383*b0d29bc4SBrooks Davis         // the one issuing the cleanup.
384*b0d29bc4SBrooks Davis         all_exec_handles.erase(original_pid);
385*b0d29bc4SBrooks Davis         cleaned = true;
386*b0d29bc4SBrooks Davis     }
387*b0d29bc4SBrooks Davis };
388*b0d29bc4SBrooks Davis 
389*b0d29bc4SBrooks Davis 
390*b0d29bc4SBrooks Davis /// Constructor.
391*b0d29bc4SBrooks Davis ///
392*b0d29bc4SBrooks Davis /// \param pimpl Constructed internal implementation.
393*b0d29bc4SBrooks Davis executor::exit_handle::exit_handle(std::shared_ptr< impl > pimpl) :
394*b0d29bc4SBrooks Davis     _pimpl(pimpl)
395*b0d29bc4SBrooks Davis {
396*b0d29bc4SBrooks Davis }
397*b0d29bc4SBrooks Davis 
398*b0d29bc4SBrooks Davis 
399*b0d29bc4SBrooks Davis /// Destructor.
400*b0d29bc4SBrooks Davis executor::exit_handle::~exit_handle(void)
401*b0d29bc4SBrooks Davis {
402*b0d29bc4SBrooks Davis }
403*b0d29bc4SBrooks Davis 
404*b0d29bc4SBrooks Davis 
405*b0d29bc4SBrooks Davis /// Cleans up the subprocess status.
406*b0d29bc4SBrooks Davis ///
407*b0d29bc4SBrooks Davis /// This function should be called explicitly as it provides the means to
408*b0d29bc4SBrooks Davis /// control any exceptions raised during cleanup.  Do not rely on the destructor
409*b0d29bc4SBrooks Davis /// to clean things up.
410*b0d29bc4SBrooks Davis ///
411*b0d29bc4SBrooks Davis /// \throw engine::error If the cleanup fails, especially due to the inability
412*b0d29bc4SBrooks Davis ///     to remove the work directory.
413*b0d29bc4SBrooks Davis void
414*b0d29bc4SBrooks Davis executor::exit_handle::cleanup(void)
415*b0d29bc4SBrooks Davis {
416*b0d29bc4SBrooks Davis     PRE(!_pimpl->cleaned);
417*b0d29bc4SBrooks Davis     _pimpl->cleanup();
418*b0d29bc4SBrooks Davis     POST(_pimpl->cleaned);
419*b0d29bc4SBrooks Davis }
420*b0d29bc4SBrooks Davis 
421*b0d29bc4SBrooks Davis 
422*b0d29bc4SBrooks Davis /// Gets the current number of owners of the on-disk data.
423*b0d29bc4SBrooks Davis ///
424*b0d29bc4SBrooks Davis /// \return A shared reference counter.  Even though this function is marked as
425*b0d29bc4SBrooks Davis /// const, the return value is intentionally mutable because we need to update
426*b0d29bc4SBrooks Davis /// reference counts from different but related processes.  This is why this
427*b0d29bc4SBrooks Davis /// method is not public.
428*b0d29bc4SBrooks Davis std::shared_ptr< std::size_t >
429*b0d29bc4SBrooks Davis executor::exit_handle::state_owners(void) const
430*b0d29bc4SBrooks Davis {
431*b0d29bc4SBrooks Davis     return _pimpl->state_owners;
432*b0d29bc4SBrooks Davis }
433*b0d29bc4SBrooks Davis 
434*b0d29bc4SBrooks Davis 
435*b0d29bc4SBrooks Davis /// Returns the original PID corresponding to the terminated subprocess.
436*b0d29bc4SBrooks Davis ///
437*b0d29bc4SBrooks Davis /// \return An exec_handle.
438*b0d29bc4SBrooks Davis int
439*b0d29bc4SBrooks Davis executor::exit_handle::original_pid(void) const
440*b0d29bc4SBrooks Davis {
441*b0d29bc4SBrooks Davis     return _pimpl->original_pid;
442*b0d29bc4SBrooks Davis }
443*b0d29bc4SBrooks Davis 
444*b0d29bc4SBrooks Davis 
445*b0d29bc4SBrooks Davis /// Returns the process termination status of the subprocess.
446*b0d29bc4SBrooks Davis ///
447*b0d29bc4SBrooks Davis /// \return A process termination status, or none if the subprocess timed out.
448*b0d29bc4SBrooks Davis const optional< process::status >&
449*b0d29bc4SBrooks Davis executor::exit_handle::status(void) const
450*b0d29bc4SBrooks Davis {
451*b0d29bc4SBrooks Davis     return _pimpl->status;
452*b0d29bc4SBrooks Davis }
453*b0d29bc4SBrooks Davis 
454*b0d29bc4SBrooks Davis 
455*b0d29bc4SBrooks Davis /// Returns the user the process ran as if different than the current one.
456*b0d29bc4SBrooks Davis ///
457*b0d29bc4SBrooks Davis /// \return None if the credentials of the process were the same as the current
458*b0d29bc4SBrooks Davis /// one, or else a user.
459*b0d29bc4SBrooks Davis const optional< passwd::user >&
460*b0d29bc4SBrooks Davis executor::exit_handle::unprivileged_user(void) const
461*b0d29bc4SBrooks Davis {
462*b0d29bc4SBrooks Davis     return _pimpl->unprivileged_user;
463*b0d29bc4SBrooks Davis }
464*b0d29bc4SBrooks Davis 
465*b0d29bc4SBrooks Davis 
466*b0d29bc4SBrooks Davis /// Returns the timestamp of when the subprocess was spawned.
467*b0d29bc4SBrooks Davis ///
468*b0d29bc4SBrooks Davis /// \return A timestamp.
469*b0d29bc4SBrooks Davis const datetime::timestamp&
470*b0d29bc4SBrooks Davis executor::exit_handle::start_time(void) const
471*b0d29bc4SBrooks Davis {
472*b0d29bc4SBrooks Davis     return _pimpl->start_time;
473*b0d29bc4SBrooks Davis }
474*b0d29bc4SBrooks Davis 
475*b0d29bc4SBrooks Davis 
476*b0d29bc4SBrooks Davis /// Returns the timestamp of when wait() or wait_any() returned this object.
477*b0d29bc4SBrooks Davis ///
478*b0d29bc4SBrooks Davis /// \return A timestamp.
479*b0d29bc4SBrooks Davis const datetime::timestamp&
480*b0d29bc4SBrooks Davis executor::exit_handle::end_time(void) const
481*b0d29bc4SBrooks Davis {
482*b0d29bc4SBrooks Davis     return _pimpl->end_time;
483*b0d29bc4SBrooks Davis }
484*b0d29bc4SBrooks Davis 
485*b0d29bc4SBrooks Davis 
486*b0d29bc4SBrooks Davis /// Returns the path to the subprocess-specific control directory.
487*b0d29bc4SBrooks Davis ///
488*b0d29bc4SBrooks Davis /// This is where the executor may store control files.
489*b0d29bc4SBrooks Davis ///
490*b0d29bc4SBrooks Davis /// \return The path to a directory that exists until cleanup() is called.
491*b0d29bc4SBrooks Davis fs::path
492*b0d29bc4SBrooks Davis executor::exit_handle::control_directory(void) const
493*b0d29bc4SBrooks Davis {
494*b0d29bc4SBrooks Davis     return _pimpl->control_directory;
495*b0d29bc4SBrooks Davis }
496*b0d29bc4SBrooks Davis 
497*b0d29bc4SBrooks Davis 
498*b0d29bc4SBrooks Davis /// Returns the path to the subprocess-specific work directory.
499*b0d29bc4SBrooks Davis ///
500*b0d29bc4SBrooks Davis /// This is guaranteed to be clear of files created by the executor.
501*b0d29bc4SBrooks Davis ///
502*b0d29bc4SBrooks Davis /// \return The path to a directory that exists until cleanup() is called.
503*b0d29bc4SBrooks Davis fs::path
504*b0d29bc4SBrooks Davis executor::exit_handle::work_directory(void) const
505*b0d29bc4SBrooks Davis {
506*b0d29bc4SBrooks Davis     return _pimpl->control_directory / detail::work_subdir;
507*b0d29bc4SBrooks Davis }
508*b0d29bc4SBrooks Davis 
509*b0d29bc4SBrooks Davis 
510*b0d29bc4SBrooks Davis /// Returns the path to the subprocess's stdout file.
511*b0d29bc4SBrooks Davis ///
512*b0d29bc4SBrooks Davis /// \return The path to a file that exists until cleanup() is called.
513*b0d29bc4SBrooks Davis const fs::path&
514*b0d29bc4SBrooks Davis executor::exit_handle::stdout_file(void) const
515*b0d29bc4SBrooks Davis {
516*b0d29bc4SBrooks Davis     return _pimpl->stdout_file;
517*b0d29bc4SBrooks Davis }
518*b0d29bc4SBrooks Davis 
519*b0d29bc4SBrooks Davis 
520*b0d29bc4SBrooks Davis /// Returns the path to the subprocess's stderr file.
521*b0d29bc4SBrooks Davis ///
522*b0d29bc4SBrooks Davis /// \return The path to a file that exists until cleanup() is called.
523*b0d29bc4SBrooks Davis const fs::path&
524*b0d29bc4SBrooks Davis executor::exit_handle::stderr_file(void) const
525*b0d29bc4SBrooks Davis {
526*b0d29bc4SBrooks Davis     return _pimpl->stderr_file;
527*b0d29bc4SBrooks Davis }
528*b0d29bc4SBrooks Davis 
529*b0d29bc4SBrooks Davis 
530*b0d29bc4SBrooks Davis /// Internal implementation for the executor_handle.
531*b0d29bc4SBrooks Davis ///
532*b0d29bc4SBrooks Davis /// Because the executor is a singleton, these essentially is a container for
533*b0d29bc4SBrooks Davis /// global variables.
534*b0d29bc4SBrooks Davis struct utils::process::executor::executor_handle::impl : utils::noncopyable {
535*b0d29bc4SBrooks Davis     /// Numeric counter of executed subprocesses.
536*b0d29bc4SBrooks Davis     ///
537*b0d29bc4SBrooks Davis     /// This is used to generate a unique identifier for each subprocess as an
538*b0d29bc4SBrooks Davis     /// easy mechanism to discern their unique work directories.
539*b0d29bc4SBrooks Davis     size_t last_subprocess;
540*b0d29bc4SBrooks Davis 
541*b0d29bc4SBrooks Davis     /// Interrupts handler.
542*b0d29bc4SBrooks Davis     std::auto_ptr< signals::interrupts_handler > interrupts_handler;
543*b0d29bc4SBrooks Davis 
544*b0d29bc4SBrooks Davis     /// Root work directory for all executed subprocesses.
545*b0d29bc4SBrooks Davis     std::auto_ptr< fs::auto_directory > root_work_directory;
546*b0d29bc4SBrooks Davis 
547*b0d29bc4SBrooks Davis     /// Mapping of PIDs to the data required at run time.
548*b0d29bc4SBrooks Davis     exec_handles_map all_exec_handles;
549*b0d29bc4SBrooks Davis 
550*b0d29bc4SBrooks Davis     /// Whether the executor state has been cleaned yet or not.
551*b0d29bc4SBrooks Davis     ///
552*b0d29bc4SBrooks Davis     /// Used to keep track of explicit calls to the public cleanup().
553*b0d29bc4SBrooks Davis     bool cleaned;
554*b0d29bc4SBrooks Davis 
555*b0d29bc4SBrooks Davis     /// Constructor.
556*b0d29bc4SBrooks Davis     impl(void) :
557*b0d29bc4SBrooks Davis         last_subprocess(0),
558*b0d29bc4SBrooks Davis         interrupts_handler(new signals::interrupts_handler()),
559*b0d29bc4SBrooks Davis         root_work_directory(new fs::auto_directory(
560*b0d29bc4SBrooks Davis             fs::auto_directory::mkdtemp_public(work_directory_template))),
561*b0d29bc4SBrooks Davis         cleaned(false)
562*b0d29bc4SBrooks Davis     {
563*b0d29bc4SBrooks Davis     }
564*b0d29bc4SBrooks Davis 
565*b0d29bc4SBrooks Davis     /// Destructor.
566*b0d29bc4SBrooks Davis     ~impl(void)
567*b0d29bc4SBrooks Davis     {
568*b0d29bc4SBrooks Davis         if (!cleaned) {
569*b0d29bc4SBrooks Davis             LW("Implicitly cleaning up executor; ignoring errors!");
570*b0d29bc4SBrooks Davis             try {
571*b0d29bc4SBrooks Davis                 cleanup();
572*b0d29bc4SBrooks Davis                 cleaned = true;
573*b0d29bc4SBrooks Davis             } catch (const std::runtime_error& error) {
574*b0d29bc4SBrooks Davis                 LE(F("Executor global cleanup failed: %s") % error.what());
575*b0d29bc4SBrooks Davis             }
576*b0d29bc4SBrooks Davis         }
577*b0d29bc4SBrooks Davis     }
578*b0d29bc4SBrooks Davis 
579*b0d29bc4SBrooks Davis     /// Cleans up the executor state.
580*b0d29bc4SBrooks Davis     void
581*b0d29bc4SBrooks Davis     cleanup(void)
582*b0d29bc4SBrooks Davis     {
583*b0d29bc4SBrooks Davis         PRE(!cleaned);
584*b0d29bc4SBrooks Davis 
585*b0d29bc4SBrooks Davis         for (exec_handles_map::const_iterator iter = all_exec_handles.begin();
586*b0d29bc4SBrooks Davis              iter != all_exec_handles.end(); ++iter) {
587*b0d29bc4SBrooks Davis             const int& pid = (*iter).first;
588*b0d29bc4SBrooks Davis             const exec_handle& data = (*iter).second;
589*b0d29bc4SBrooks Davis 
590*b0d29bc4SBrooks Davis             process::terminate_group(pid);
591*b0d29bc4SBrooks Davis             int status;
592*b0d29bc4SBrooks Davis             if (::waitpid(pid, &status, 0) == -1) {
593*b0d29bc4SBrooks Davis                 // Should not happen.
594*b0d29bc4SBrooks Davis                 LW(F("Failed to wait for PID %s") % pid);
595*b0d29bc4SBrooks Davis             }
596*b0d29bc4SBrooks Davis 
597*b0d29bc4SBrooks Davis             try {
598*b0d29bc4SBrooks Davis                 fs::rm_r(data.control_directory());
599*b0d29bc4SBrooks Davis             } catch (const fs::error& e) {
600*b0d29bc4SBrooks Davis                 LE(F("Failed to clean up subprocess work directory %s: %s") %
601*b0d29bc4SBrooks Davis                    data.control_directory() % e.what());
602*b0d29bc4SBrooks Davis             }
603*b0d29bc4SBrooks Davis         }
604*b0d29bc4SBrooks Davis         all_exec_handles.clear();
605*b0d29bc4SBrooks Davis 
606*b0d29bc4SBrooks Davis         try {
607*b0d29bc4SBrooks Davis             // The following only causes the work directory to be deleted, not
608*b0d29bc4SBrooks Davis             // any of its contents, so we expect this to always succeed.  This
609*b0d29bc4SBrooks Davis             // *should* be sufficient because, in the loop above, we have
610*b0d29bc4SBrooks Davis             // individually wiped the subdirectories of any still-unclean
611*b0d29bc4SBrooks Davis             // subprocesses.
612*b0d29bc4SBrooks Davis             root_work_directory->cleanup();
613*b0d29bc4SBrooks Davis         } catch (const fs::error& e) {
614*b0d29bc4SBrooks Davis             LE(F("Failed to clean up executor work directory %s: %s; this is "
615*b0d29bc4SBrooks Davis                  "an internal error") % root_work_directory->directory()
616*b0d29bc4SBrooks Davis                % e.what());
617*b0d29bc4SBrooks Davis         }
618*b0d29bc4SBrooks Davis         root_work_directory.reset(NULL);
619*b0d29bc4SBrooks Davis 
620*b0d29bc4SBrooks Davis         interrupts_handler->unprogram();
621*b0d29bc4SBrooks Davis         interrupts_handler.reset(NULL);
622*b0d29bc4SBrooks Davis     }
623*b0d29bc4SBrooks Davis 
624*b0d29bc4SBrooks Davis     /// Common code to run after any of the wait calls.
625*b0d29bc4SBrooks Davis     ///
626*b0d29bc4SBrooks Davis     /// \param original_pid The PID of the terminated subprocess.
627*b0d29bc4SBrooks Davis     /// \param status The exit status of the terminated subprocess.
628*b0d29bc4SBrooks Davis     ///
629*b0d29bc4SBrooks Davis     /// \return A pointer to an object describing the waited-for subprocess.
630*b0d29bc4SBrooks Davis     executor::exit_handle
631*b0d29bc4SBrooks Davis     post_wait(const int original_pid, const process::status& status)
632*b0d29bc4SBrooks Davis     {
633*b0d29bc4SBrooks Davis         PRE(original_pid == status.dead_pid());
634*b0d29bc4SBrooks Davis         LI(F("Waited for subprocess with exec_handle %s") % original_pid);
635*b0d29bc4SBrooks Davis 
636*b0d29bc4SBrooks Davis         process::terminate_group(status.dead_pid());
637*b0d29bc4SBrooks Davis 
638*b0d29bc4SBrooks Davis         const exec_handles_map::iterator iter = all_exec_handles.find(
639*b0d29bc4SBrooks Davis             original_pid);
640*b0d29bc4SBrooks Davis         exec_handle& data = (*iter).second;
641*b0d29bc4SBrooks Davis         data._pimpl->timer.unprogram();
642*b0d29bc4SBrooks Davis 
643*b0d29bc4SBrooks Davis         // It is tempting to assert here (and old code did) that, if the timer
644*b0d29bc4SBrooks Davis         // has fired, the process has been forcibly killed by us.  This is not
645*b0d29bc4SBrooks Davis         // always the case though: for short-lived processes and with very short
646*b0d29bc4SBrooks Davis         // timeouts (think 1ms), it is possible for scheduling decisions to
647*b0d29bc4SBrooks Davis         // allow the subprocess to finish while at the same time cause the timer
648*b0d29bc4SBrooks Davis         // to fire.  So we do not assert this any longer and just rely on the
649*b0d29bc4SBrooks Davis         // timer expiration to check if the process timed out or not.  If the
650*b0d29bc4SBrooks Davis         // process did finish but the timer expired... oh well, we do not detect
651*b0d29bc4SBrooks Davis         // this correctly but we don't care because this should not really
652*b0d29bc4SBrooks Davis         // happen.
653*b0d29bc4SBrooks Davis 
654*b0d29bc4SBrooks Davis         if (!fs::exists(data.stdout_file())) {
655*b0d29bc4SBrooks Davis             std::ofstream new_stdout(data.stdout_file().c_str());
656*b0d29bc4SBrooks Davis         }
657*b0d29bc4SBrooks Davis         if (!fs::exists(data.stderr_file())) {
658*b0d29bc4SBrooks Davis             std::ofstream new_stderr(data.stderr_file().c_str());
659*b0d29bc4SBrooks Davis         }
660*b0d29bc4SBrooks Davis 
661*b0d29bc4SBrooks Davis         return exit_handle(std::shared_ptr< exit_handle::impl >(
662*b0d29bc4SBrooks Davis             new exit_handle::impl(
663*b0d29bc4SBrooks Davis                 data.pid(),
664*b0d29bc4SBrooks Davis                 data._pimpl->timer.fired() ?
665*b0d29bc4SBrooks Davis                     none : utils::make_optional(status),
666*b0d29bc4SBrooks Davis                 data._pimpl->unprivileged_user,
667*b0d29bc4SBrooks Davis                 data._pimpl->start_time, datetime::timestamp::now(),
668*b0d29bc4SBrooks Davis                 data.control_directory(),
669*b0d29bc4SBrooks Davis                 data.stdout_file(),
670*b0d29bc4SBrooks Davis                 data.stderr_file(),
671*b0d29bc4SBrooks Davis                 data._pimpl->state_owners,
672*b0d29bc4SBrooks Davis                 all_exec_handles)));
673*b0d29bc4SBrooks Davis     }
674*b0d29bc4SBrooks Davis };
675*b0d29bc4SBrooks Davis 
676*b0d29bc4SBrooks Davis 
677*b0d29bc4SBrooks Davis /// Constructor.
678*b0d29bc4SBrooks Davis executor::executor_handle::executor_handle(void) throw() : _pimpl(new impl())
679*b0d29bc4SBrooks Davis {
680*b0d29bc4SBrooks Davis }
681*b0d29bc4SBrooks Davis 
682*b0d29bc4SBrooks Davis 
683*b0d29bc4SBrooks Davis /// Destructor.
684*b0d29bc4SBrooks Davis executor::executor_handle::~executor_handle(void)
685*b0d29bc4SBrooks Davis {
686*b0d29bc4SBrooks Davis }
687*b0d29bc4SBrooks Davis 
688*b0d29bc4SBrooks Davis 
689*b0d29bc4SBrooks Davis /// Queries the path to the root of the work directory for all subprocesses.
690*b0d29bc4SBrooks Davis ///
691*b0d29bc4SBrooks Davis /// \return A path.
692*b0d29bc4SBrooks Davis const fs::path&
693*b0d29bc4SBrooks Davis executor::executor_handle::root_work_directory(void) const
694*b0d29bc4SBrooks Davis {
695*b0d29bc4SBrooks Davis     return _pimpl->root_work_directory->directory();
696*b0d29bc4SBrooks Davis }
697*b0d29bc4SBrooks Davis 
698*b0d29bc4SBrooks Davis 
699*b0d29bc4SBrooks Davis /// Cleans up the executor state.
700*b0d29bc4SBrooks Davis ///
701*b0d29bc4SBrooks Davis /// This function should be called explicitly as it provides the means to
702*b0d29bc4SBrooks Davis /// control any exceptions raised during cleanup.  Do not rely on the destructor
703*b0d29bc4SBrooks Davis /// to clean things up.
704*b0d29bc4SBrooks Davis ///
705*b0d29bc4SBrooks Davis /// \throw engine::error If there are problems cleaning up the executor.
706*b0d29bc4SBrooks Davis void
707*b0d29bc4SBrooks Davis executor::executor_handle::cleanup(void)
708*b0d29bc4SBrooks Davis {
709*b0d29bc4SBrooks Davis     PRE(!_pimpl->cleaned);
710*b0d29bc4SBrooks Davis     _pimpl->cleanup();
711*b0d29bc4SBrooks Davis     _pimpl->cleaned = true;
712*b0d29bc4SBrooks Davis }
713*b0d29bc4SBrooks Davis 
714*b0d29bc4SBrooks Davis 
715*b0d29bc4SBrooks Davis /// Initializes the executor.
716*b0d29bc4SBrooks Davis ///
717*b0d29bc4SBrooks Davis /// \pre This function can only be called if there is no other executor_handle
718*b0d29bc4SBrooks Davis /// object alive.
719*b0d29bc4SBrooks Davis ///
720*b0d29bc4SBrooks Davis /// \return A handle to the operations of the executor.
721*b0d29bc4SBrooks Davis executor::executor_handle
722*b0d29bc4SBrooks Davis executor::setup(void)
723*b0d29bc4SBrooks Davis {
724*b0d29bc4SBrooks Davis     return executor_handle();
725*b0d29bc4SBrooks Davis }
726*b0d29bc4SBrooks Davis 
727*b0d29bc4SBrooks Davis 
728*b0d29bc4SBrooks Davis /// Pre-helper for the spawn() method.
729*b0d29bc4SBrooks Davis ///
730*b0d29bc4SBrooks Davis /// \return The created control directory for the subprocess.
731*b0d29bc4SBrooks Davis fs::path
732*b0d29bc4SBrooks Davis executor::executor_handle::spawn_pre(void)
733*b0d29bc4SBrooks Davis {
734*b0d29bc4SBrooks Davis     signals::check_interrupt();
735*b0d29bc4SBrooks Davis 
736*b0d29bc4SBrooks Davis     ++_pimpl->last_subprocess;
737*b0d29bc4SBrooks Davis 
738*b0d29bc4SBrooks Davis     const fs::path control_directory =
739*b0d29bc4SBrooks Davis         _pimpl->root_work_directory->directory() /
740*b0d29bc4SBrooks Davis         (F("%s") % _pimpl->last_subprocess);
741*b0d29bc4SBrooks Davis     fs::mkdir_p(control_directory / detail::work_subdir, 0755);
742*b0d29bc4SBrooks Davis 
743*b0d29bc4SBrooks Davis     return control_directory;
744*b0d29bc4SBrooks Davis }
745*b0d29bc4SBrooks Davis 
746*b0d29bc4SBrooks Davis 
747*b0d29bc4SBrooks Davis /// Post-helper for the spawn() method.
748*b0d29bc4SBrooks Davis ///
749*b0d29bc4SBrooks Davis /// \param control_directory Control directory as returned by spawn_pre().
750*b0d29bc4SBrooks Davis /// \param stdout_file Path to the subprocess' stdout.
751*b0d29bc4SBrooks Davis /// \param stderr_file Path to the subprocess' stderr.
752*b0d29bc4SBrooks Davis /// \param timeout Maximum amount of time the subprocess can run for.
753*b0d29bc4SBrooks Davis /// \param unprivileged_user If not none, user to switch to before execution.
754*b0d29bc4SBrooks Davis /// \param child The process created by spawn().
755*b0d29bc4SBrooks Davis ///
756*b0d29bc4SBrooks Davis /// \return The execution handle of the started subprocess.
757*b0d29bc4SBrooks Davis executor::exec_handle
758*b0d29bc4SBrooks Davis executor::executor_handle::spawn_post(
759*b0d29bc4SBrooks Davis     const fs::path& control_directory,
760*b0d29bc4SBrooks Davis     const fs::path& stdout_file,
761*b0d29bc4SBrooks Davis     const fs::path& stderr_file,
762*b0d29bc4SBrooks Davis     const datetime::delta& timeout,
763*b0d29bc4SBrooks Davis     const optional< passwd::user > unprivileged_user,
764*b0d29bc4SBrooks Davis     std::auto_ptr< process::child > child)
765*b0d29bc4SBrooks Davis {
766*b0d29bc4SBrooks Davis     const exec_handle handle(std::shared_ptr< exec_handle::impl >(
767*b0d29bc4SBrooks Davis         new exec_handle::impl(
768*b0d29bc4SBrooks Davis             child->pid(),
769*b0d29bc4SBrooks Davis             control_directory,
770*b0d29bc4SBrooks Davis             stdout_file,
771*b0d29bc4SBrooks Davis             stderr_file,
772*b0d29bc4SBrooks Davis             datetime::timestamp::now(),
773*b0d29bc4SBrooks Davis             timeout,
774*b0d29bc4SBrooks Davis             unprivileged_user,
775*b0d29bc4SBrooks Davis             detail::refcnt_t(new detail::refcnt_t::element_type(0)))));
776*b0d29bc4SBrooks Davis     INV_MSG(_pimpl->all_exec_handles.find(handle.pid()) ==
777*b0d29bc4SBrooks Davis             _pimpl->all_exec_handles.end(),
778*b0d29bc4SBrooks Davis             F("PID %s already in all_exec_handles; not properly cleaned "
779*b0d29bc4SBrooks Davis               "up or reused too fast") % handle.pid());;
780*b0d29bc4SBrooks Davis     _pimpl->all_exec_handles.insert(exec_handles_map::value_type(
781*b0d29bc4SBrooks Davis         handle.pid(), handle));
782*b0d29bc4SBrooks Davis     LI(F("Spawned subprocess with exec_handle %s") % handle.pid());
783*b0d29bc4SBrooks Davis     return handle;
784*b0d29bc4SBrooks Davis }
785*b0d29bc4SBrooks Davis 
786*b0d29bc4SBrooks Davis 
787*b0d29bc4SBrooks Davis /// Pre-helper for the spawn_followup() method.
788*b0d29bc4SBrooks Davis void
789*b0d29bc4SBrooks Davis executor::executor_handle::spawn_followup_pre(void)
790*b0d29bc4SBrooks Davis {
791*b0d29bc4SBrooks Davis     signals::check_interrupt();
792*b0d29bc4SBrooks Davis }
793*b0d29bc4SBrooks Davis 
794*b0d29bc4SBrooks Davis 
795*b0d29bc4SBrooks Davis /// Post-helper for the spawn_followup() method.
796*b0d29bc4SBrooks Davis ///
797*b0d29bc4SBrooks Davis /// \param base Exit handle of the subprocess to use as context.
798*b0d29bc4SBrooks Davis /// \param timeout Maximum amount of time the subprocess can run for.
799*b0d29bc4SBrooks Davis /// \param child The process created by spawn_followup().
800*b0d29bc4SBrooks Davis ///
801*b0d29bc4SBrooks Davis /// \return The execution handle of the started subprocess.
802*b0d29bc4SBrooks Davis executor::exec_handle
803*b0d29bc4SBrooks Davis executor::executor_handle::spawn_followup_post(
804*b0d29bc4SBrooks Davis     const exit_handle& base,
805*b0d29bc4SBrooks Davis     const datetime::delta& timeout,
806*b0d29bc4SBrooks Davis     std::auto_ptr< process::child > child)
807*b0d29bc4SBrooks Davis {
808*b0d29bc4SBrooks Davis     INV(*base.state_owners() > 0);
809*b0d29bc4SBrooks Davis     const exec_handle handle(std::shared_ptr< exec_handle::impl >(
810*b0d29bc4SBrooks Davis         new exec_handle::impl(
811*b0d29bc4SBrooks Davis             child->pid(),
812*b0d29bc4SBrooks Davis             base.control_directory(),
813*b0d29bc4SBrooks Davis             base.stdout_file(),
814*b0d29bc4SBrooks Davis             base.stderr_file(),
815*b0d29bc4SBrooks Davis             datetime::timestamp::now(),
816*b0d29bc4SBrooks Davis             timeout,
817*b0d29bc4SBrooks Davis             base.unprivileged_user(),
818*b0d29bc4SBrooks Davis             base.state_owners())));
819*b0d29bc4SBrooks Davis     INV_MSG(_pimpl->all_exec_handles.find(handle.pid()) ==
820*b0d29bc4SBrooks Davis             _pimpl->all_exec_handles.end(),
821*b0d29bc4SBrooks Davis             F("PID %s already in all_exec_handles; not properly cleaned "
822*b0d29bc4SBrooks Davis               "up or reused too fast") % handle.pid());;
823*b0d29bc4SBrooks Davis     _pimpl->all_exec_handles.insert(exec_handles_map::value_type(
824*b0d29bc4SBrooks Davis         handle.pid(), handle));
825*b0d29bc4SBrooks Davis     LI(F("Spawned subprocess with exec_handle %s") % handle.pid());
826*b0d29bc4SBrooks Davis     return handle;
827*b0d29bc4SBrooks Davis }
828*b0d29bc4SBrooks Davis 
829*b0d29bc4SBrooks Davis 
830*b0d29bc4SBrooks Davis /// Waits for completion of any forked process.
831*b0d29bc4SBrooks Davis ///
832*b0d29bc4SBrooks Davis /// \param exec_handle The handle of the process to wait for.
833*b0d29bc4SBrooks Davis ///
834*b0d29bc4SBrooks Davis /// \return A pointer to an object describing the waited-for subprocess.
835*b0d29bc4SBrooks Davis executor::exit_handle
836*b0d29bc4SBrooks Davis executor::executor_handle::wait(const exec_handle exec_handle)
837*b0d29bc4SBrooks Davis {
838*b0d29bc4SBrooks Davis     signals::check_interrupt();
839*b0d29bc4SBrooks Davis     const process::status status = process::wait(exec_handle.pid());
840*b0d29bc4SBrooks Davis     return _pimpl->post_wait(exec_handle.pid(), status);
841*b0d29bc4SBrooks Davis }
842*b0d29bc4SBrooks Davis 
843*b0d29bc4SBrooks Davis 
844*b0d29bc4SBrooks Davis /// Waits for completion of any forked process.
845*b0d29bc4SBrooks Davis ///
846*b0d29bc4SBrooks Davis /// \return A pointer to an object describing the waited-for subprocess.
847*b0d29bc4SBrooks Davis executor::exit_handle
848*b0d29bc4SBrooks Davis executor::executor_handle::wait_any(void)
849*b0d29bc4SBrooks Davis {
850*b0d29bc4SBrooks Davis     signals::check_interrupt();
851*b0d29bc4SBrooks Davis     const process::status status = process::wait_any();
852*b0d29bc4SBrooks Davis     return _pimpl->post_wait(status.dead_pid(), status);
853*b0d29bc4SBrooks Davis }
854*b0d29bc4SBrooks Davis 
855*b0d29bc4SBrooks Davis 
856*b0d29bc4SBrooks Davis /// Checks if an interrupt has fired.
857*b0d29bc4SBrooks Davis ///
858*b0d29bc4SBrooks Davis /// Calls to this function should be sprinkled in strategic places through the
859*b0d29bc4SBrooks Davis /// code protected by an interrupts_handler object.
860*b0d29bc4SBrooks Davis ///
861*b0d29bc4SBrooks Davis /// This is just a wrapper over signals::check_interrupt() to avoid leaking this
862*b0d29bc4SBrooks Davis /// dependency to the caller.
863*b0d29bc4SBrooks Davis ///
864*b0d29bc4SBrooks Davis /// \throw signals::interrupted_error If there has been an interrupt.
865*b0d29bc4SBrooks Davis void
866*b0d29bc4SBrooks Davis executor::executor_handle::check_interrupt(void) const
867*b0d29bc4SBrooks Davis {
868*b0d29bc4SBrooks Davis     signals::check_interrupt();
869*b0d29bc4SBrooks Davis }
870