xref: /src/contrib/kyua/utils/process/executor.cpp (revision d61c8bca8648fa6dfa89bd5c7dc75726a2740c15)
108334c51SBrooks Davis // Copyright 2015 The Kyua Authors.
208334c51SBrooks Davis // All rights reserved.
308334c51SBrooks Davis //
408334c51SBrooks Davis // Redistribution and use in source and binary forms, with or without
508334c51SBrooks Davis // modification, are permitted provided that the following conditions are
608334c51SBrooks Davis // met:
708334c51SBrooks Davis //
808334c51SBrooks Davis // * Redistributions of source code must retain the above copyright
908334c51SBrooks Davis //   notice, this list of conditions and the following disclaimer.
1008334c51SBrooks Davis // * Redistributions in binary form must reproduce the above copyright
1108334c51SBrooks Davis //   notice, this list of conditions and the following disclaimer in the
1208334c51SBrooks Davis //   documentation and/or other materials provided with the distribution.
1308334c51SBrooks Davis // * Neither the name of Google Inc. nor the names of its contributors
1408334c51SBrooks Davis //   may be used to endorse or promote products derived from this software
1508334c51SBrooks Davis //   without specific prior written permission.
1608334c51SBrooks Davis //
1708334c51SBrooks Davis // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
1808334c51SBrooks Davis // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
1908334c51SBrooks Davis // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
2008334c51SBrooks Davis // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
2108334c51SBrooks Davis // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
2208334c51SBrooks Davis // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
2308334c51SBrooks Davis // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2408334c51SBrooks Davis // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2508334c51SBrooks Davis // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2608334c51SBrooks Davis // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
2708334c51SBrooks Davis // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2808334c51SBrooks Davis 
2908334c51SBrooks Davis #include "utils/process/executor.ipp"
3008334c51SBrooks Davis 
3108334c51SBrooks Davis #if defined(HAVE_CONFIG_H)
3208334c51SBrooks Davis #include "config.h"
3308334c51SBrooks Davis #endif
3408334c51SBrooks Davis 
3508334c51SBrooks Davis extern "C" {
3608334c51SBrooks Davis #include <sys/types.h>
3708334c51SBrooks Davis #include <sys/wait.h>
3808334c51SBrooks Davis 
3908334c51SBrooks Davis #include <signal.h>
4008334c51SBrooks Davis }
4108334c51SBrooks Davis 
425f174897SMuhammad Moinur Rahman #include <forward_list>
4308334c51SBrooks Davis #include <fstream>
4408334c51SBrooks Davis #include <map>
4508334c51SBrooks Davis #include <memory>
4608334c51SBrooks Davis #include <stdexcept>
475f174897SMuhammad Moinur Rahman #include <utility>
4808334c51SBrooks Davis 
4908334c51SBrooks Davis #include "utils/datetime.hpp"
5008334c51SBrooks Davis #include "utils/format/macros.hpp"
5108334c51SBrooks Davis #include "utils/fs/auto_cleaners.hpp"
5208334c51SBrooks Davis #include "utils/fs/exceptions.hpp"
5308334c51SBrooks Davis #include "utils/fs/operations.hpp"
5408334c51SBrooks Davis #include "utils/fs/path.hpp"
5508334c51SBrooks Davis #include "utils/logging/macros.hpp"
5608334c51SBrooks Davis #include "utils/logging/operations.hpp"
5708334c51SBrooks Davis #include "utils/noncopyable.hpp"
5808334c51SBrooks Davis #include "utils/optional.ipp"
5908334c51SBrooks Davis #include "utils/passwd.hpp"
6008334c51SBrooks Davis #include "utils/process/child.ipp"
6108334c51SBrooks Davis #include "utils/process/deadline_killer.hpp"
6208334c51SBrooks Davis #include "utils/process/isolation.hpp"
6308334c51SBrooks Davis #include "utils/process/operations.hpp"
6408334c51SBrooks Davis #include "utils/process/status.hpp"
6508334c51SBrooks Davis #include "utils/sanity.hpp"
6608334c51SBrooks Davis #include "utils/signals/interrupts.hpp"
6708334c51SBrooks Davis #include "utils/signals/timer.hpp"
6808334c51SBrooks Davis 
6908334c51SBrooks Davis namespace datetime = utils::datetime;
7008334c51SBrooks Davis namespace executor = utils::process::executor;
7108334c51SBrooks Davis namespace fs = utils::fs;
7208334c51SBrooks Davis namespace logging = utils::logging;
7308334c51SBrooks Davis namespace passwd = utils::passwd;
7408334c51SBrooks Davis namespace process = utils::process;
7508334c51SBrooks Davis namespace signals = utils::signals;
7608334c51SBrooks Davis 
7708334c51SBrooks Davis using utils::none;
7808334c51SBrooks Davis using utils::optional;
7908334c51SBrooks Davis 
8008334c51SBrooks Davis 
8108334c51SBrooks Davis namespace {
8208334c51SBrooks Davis 
8308334c51SBrooks Davis 
8408334c51SBrooks Davis /// Template for temporary directories created by the executor.
8508334c51SBrooks Davis static const char* work_directory_template = PACKAGE_TARNAME ".XXXXXX";
8608334c51SBrooks Davis 
8708334c51SBrooks Davis 
8808334c51SBrooks Davis /// Mapping of active subprocess PIDs to their execution data.
8908334c51SBrooks Davis typedef std::map< int, executor::exec_handle > exec_handles_map;
9008334c51SBrooks Davis 
9108334c51SBrooks Davis 
9208334c51SBrooks Davis }  // anonymous namespace
9308334c51SBrooks Davis 
9408334c51SBrooks Davis 
9508334c51SBrooks Davis /// Basename of the file containing the stdout of the subprocess.
9608334c51SBrooks Davis const char* utils::process::executor::detail::stdout_name = "stdout.txt";
9708334c51SBrooks Davis 
9808334c51SBrooks Davis 
9908334c51SBrooks Davis /// Basename of the file containing the stderr of the subprocess.
10008334c51SBrooks Davis const char* utils::process::executor::detail::stderr_name = "stderr.txt";
10108334c51SBrooks Davis 
10208334c51SBrooks Davis 
10308334c51SBrooks Davis /// Basename of the subdirectory in which the subprocess is actually executed.
10408334c51SBrooks Davis ///
10508334c51SBrooks Davis /// This is a subdirectory of the "unique work directory" generated for the
10608334c51SBrooks Davis /// subprocess so that our code can create control files on disk and not
10708334c51SBrooks Davis /// get them clobbered by the subprocess's activity.
10808334c51SBrooks Davis const char* utils::process::executor::detail::work_subdir = "work";
10908334c51SBrooks Davis 
11008334c51SBrooks Davis 
11108334c51SBrooks Davis /// Prepares a subprocess to run a user-provided hook in a controlled manner.
11208334c51SBrooks Davis ///
11308334c51SBrooks Davis /// \param unprivileged_user User to switch to if not none.
11408334c51SBrooks Davis /// \param control_directory Path to the subprocess-specific control directory.
11508334c51SBrooks Davis /// \param work_directory Path to the subprocess-specific work directory.
11608334c51SBrooks Davis void
setup_child(const optional<passwd::user> unprivileged_user,const fs::path & control_directory,const fs::path & work_directory)11708334c51SBrooks Davis utils::process::executor::detail::setup_child(
11808334c51SBrooks Davis     const optional< passwd::user > unprivileged_user,
11908334c51SBrooks Davis     const fs::path& control_directory,
12008334c51SBrooks Davis     const fs::path& work_directory)
12108334c51SBrooks Davis {
12208334c51SBrooks Davis     logging::set_inmemory();
12308334c51SBrooks Davis     process::isolate_path(unprivileged_user, control_directory);
12408334c51SBrooks Davis     process::isolate_child(unprivileged_user, work_directory);
12508334c51SBrooks Davis }
12608334c51SBrooks Davis 
12708334c51SBrooks Davis 
1285f174897SMuhammad Moinur Rahman /// Internal implementation for the exec_handle class.
12908334c51SBrooks Davis struct utils::process::executor::exec_handle::impl : utils::noncopyable {
13008334c51SBrooks Davis     /// PID of the process being run.
13108334c51SBrooks Davis     int pid;
13208334c51SBrooks Davis 
13308334c51SBrooks Davis     /// Path to the subprocess-specific work directory.
13408334c51SBrooks Davis     fs::path control_directory;
13508334c51SBrooks Davis 
13608334c51SBrooks Davis     /// Path to the subprocess's stdout file.
13708334c51SBrooks Davis     const fs::path stdout_file;
13808334c51SBrooks Davis 
13908334c51SBrooks Davis     /// Path to the subprocess's stderr file.
14008334c51SBrooks Davis     const fs::path stderr_file;
14108334c51SBrooks Davis 
14208334c51SBrooks Davis     /// Start time.
14308334c51SBrooks Davis     datetime::timestamp start_time;
14408334c51SBrooks Davis 
14508334c51SBrooks Davis     /// User the subprocess is running as if different than the current one.
14608334c51SBrooks Davis     const optional< passwd::user > unprivileged_user;
14708334c51SBrooks Davis 
14808334c51SBrooks Davis     /// Timer to kill the subprocess on activation.
14908334c51SBrooks Davis     process::deadline_killer timer;
15008334c51SBrooks Davis 
15108334c51SBrooks Davis     /// Number of owners of the on-disk state.
15208334c51SBrooks Davis     executor::detail::refcnt_t state_owners;
15308334c51SBrooks Davis 
15408334c51SBrooks Davis     /// Constructor.
15508334c51SBrooks Davis     ///
15608334c51SBrooks Davis     /// \param pid_ PID of the forked process.
15708334c51SBrooks Davis     /// \param control_directory_ Path to the subprocess-specific work
15808334c51SBrooks Davis     ///     directory.
15908334c51SBrooks Davis     /// \param stdout_file_ Path to the subprocess's stdout file.
16008334c51SBrooks Davis     /// \param stderr_file_ Path to the subprocess's stderr file.
16108334c51SBrooks Davis     /// \param start_time_ Timestamp of when this object was constructed.
16208334c51SBrooks Davis     /// \param timeout Maximum amount of time the subprocess can run for.
16308334c51SBrooks Davis     /// \param unprivileged_user_ User the subprocess is running as if
16408334c51SBrooks Davis     ///     different than the current one.
16508334c51SBrooks Davis     /// \param [in,out] state_owners_ Number of owners of the on-disk state.
16608334c51SBrooks Davis     ///     For first-time processes, this should be a new counter set to 0;
16708334c51SBrooks Davis     ///     for followup processes, this should point to the same counter used
16808334c51SBrooks Davis     ///     by the preceding process.
implutils::process::executor::exec_handle::impl16908334c51SBrooks Davis     impl(const int pid_,
17008334c51SBrooks Davis          const fs::path& control_directory_,
17108334c51SBrooks Davis          const fs::path& stdout_file_,
17208334c51SBrooks Davis          const fs::path& stderr_file_,
17308334c51SBrooks Davis          const datetime::timestamp& start_time_,
17408334c51SBrooks Davis          const datetime::delta& timeout,
17508334c51SBrooks Davis          const optional< passwd::user > unprivileged_user_,
17608334c51SBrooks Davis          executor::detail::refcnt_t state_owners_) :
17708334c51SBrooks Davis         pid(pid_),
17808334c51SBrooks Davis         control_directory(control_directory_),
17908334c51SBrooks Davis         stdout_file(stdout_file_),
18008334c51SBrooks Davis         stderr_file(stderr_file_),
18108334c51SBrooks Davis         start_time(start_time_),
18208334c51SBrooks Davis         unprivileged_user(unprivileged_user_),
18308334c51SBrooks Davis         timer(timeout, pid_),
18408334c51SBrooks Davis         state_owners(state_owners_)
18508334c51SBrooks Davis     {
18608334c51SBrooks Davis         (*state_owners)++;
18708334c51SBrooks Davis         POST(*state_owners > 0);
18808334c51SBrooks Davis     }
18908334c51SBrooks Davis };
19008334c51SBrooks Davis 
19108334c51SBrooks Davis 
19208334c51SBrooks Davis /// Constructor.
19308334c51SBrooks Davis ///
19408334c51SBrooks Davis /// \param pimpl Constructed internal implementation.
exec_handle(std::shared_ptr<impl> pimpl)19508334c51SBrooks Davis executor::exec_handle::exec_handle(std::shared_ptr< impl > pimpl) :
19608334c51SBrooks Davis     _pimpl(pimpl)
19708334c51SBrooks Davis {
19808334c51SBrooks Davis }
19908334c51SBrooks Davis 
20008334c51SBrooks Davis 
20108334c51SBrooks Davis /// Destructor.
~exec_handle(void)20208334c51SBrooks Davis executor::exec_handle::~exec_handle(void)
20308334c51SBrooks Davis {
20408334c51SBrooks Davis }
20508334c51SBrooks Davis 
20608334c51SBrooks Davis 
20708334c51SBrooks Davis /// Returns the PID of the process being run.
20808334c51SBrooks Davis ///
20908334c51SBrooks Davis /// \return A PID.
21008334c51SBrooks Davis int
pid(void) const21108334c51SBrooks Davis executor::exec_handle::pid(void) const
21208334c51SBrooks Davis {
21308334c51SBrooks Davis     return _pimpl->pid;
21408334c51SBrooks Davis }
21508334c51SBrooks Davis 
21608334c51SBrooks Davis 
21708334c51SBrooks Davis /// Returns the path to the subprocess-specific control directory.
21808334c51SBrooks Davis ///
21908334c51SBrooks Davis /// This is where the executor may store control files.
22008334c51SBrooks Davis ///
22108334c51SBrooks Davis /// \return The path to a directory that exists until cleanup() is called.
22208334c51SBrooks Davis fs::path
control_directory(void) const22308334c51SBrooks Davis executor::exec_handle::control_directory(void) const
22408334c51SBrooks Davis {
22508334c51SBrooks Davis     return _pimpl->control_directory;
22608334c51SBrooks Davis }
22708334c51SBrooks Davis 
22808334c51SBrooks Davis 
22908334c51SBrooks Davis /// Returns the path to the subprocess-specific work directory.
23008334c51SBrooks Davis ///
23108334c51SBrooks Davis /// This is guaranteed to be clear of files created by the executor.
23208334c51SBrooks Davis ///
23308334c51SBrooks Davis /// \return The path to a directory that exists until cleanup() is called.
23408334c51SBrooks Davis fs::path
work_directory(void) const23508334c51SBrooks Davis executor::exec_handle::work_directory(void) const
23608334c51SBrooks Davis {
23708334c51SBrooks Davis     return _pimpl->control_directory / detail::work_subdir;
23808334c51SBrooks Davis }
23908334c51SBrooks Davis 
24008334c51SBrooks Davis 
24108334c51SBrooks Davis /// Returns the path to the subprocess's stdout file.
24208334c51SBrooks Davis ///
24308334c51SBrooks Davis /// \return The path to a file that exists until cleanup() is called.
24408334c51SBrooks Davis const fs::path&
stdout_file(void) const24508334c51SBrooks Davis executor::exec_handle::stdout_file(void) const
24608334c51SBrooks Davis {
24708334c51SBrooks Davis     return _pimpl->stdout_file;
24808334c51SBrooks Davis }
24908334c51SBrooks Davis 
25008334c51SBrooks Davis 
25108334c51SBrooks Davis /// Returns the path to the subprocess's stderr file.
25208334c51SBrooks Davis ///
25308334c51SBrooks Davis /// \return The path to a file that exists until cleanup() is called.
25408334c51SBrooks Davis const fs::path&
stderr_file(void) const25508334c51SBrooks Davis executor::exec_handle::stderr_file(void) const
25608334c51SBrooks Davis {
25708334c51SBrooks Davis     return _pimpl->stderr_file;
25808334c51SBrooks Davis }
25908334c51SBrooks Davis 
26008334c51SBrooks Davis 
26108334c51SBrooks Davis /// Internal implementation for the exit_handle class.
26208334c51SBrooks Davis struct utils::process::executor::exit_handle::impl : utils::noncopyable {
26308334c51SBrooks Davis     /// Original PID of the terminated subprocess.
26408334c51SBrooks Davis     ///
26508334c51SBrooks Davis     /// Note that this PID is no longer valid and cannot be used on system
26608334c51SBrooks Davis     /// tables!
26708334c51SBrooks Davis     const int original_pid;
26808334c51SBrooks Davis 
26908334c51SBrooks Davis     /// Termination status of the subprocess, or none if it timed out.
27008334c51SBrooks Davis     const optional< process::status > status;
27108334c51SBrooks Davis 
27208334c51SBrooks Davis     /// The user the process ran as, if different than the current one.
27308334c51SBrooks Davis     const optional< passwd::user > unprivileged_user;
27408334c51SBrooks Davis 
27508334c51SBrooks Davis     /// Timestamp of when the subprocess was spawned.
27608334c51SBrooks Davis     const datetime::timestamp start_time;
27708334c51SBrooks Davis 
27808334c51SBrooks Davis     /// Timestamp of when wait() or wait_any() returned this object.
27908334c51SBrooks Davis     const datetime::timestamp end_time;
28008334c51SBrooks Davis 
28108334c51SBrooks Davis     /// Path to the subprocess-specific work directory.
28208334c51SBrooks Davis     const fs::path control_directory;
28308334c51SBrooks Davis 
28408334c51SBrooks Davis     /// Path to the subprocess's stdout file.
28508334c51SBrooks Davis     const fs::path stdout_file;
28608334c51SBrooks Davis 
28708334c51SBrooks Davis     /// Path to the subprocess's stderr file.
28808334c51SBrooks Davis     const fs::path stderr_file;
28908334c51SBrooks Davis 
29008334c51SBrooks Davis     /// Number of owners of the on-disk state.
29108334c51SBrooks Davis     ///
29208334c51SBrooks Davis     /// This will be 1 if this exit_handle is the last holder of the on-disk
29308334c51SBrooks Davis     /// state, in which case cleanup() invocations will wipe the disk state.
29408334c51SBrooks Davis     /// For all other cases, this will hold a higher value.
29508334c51SBrooks Davis     detail::refcnt_t state_owners;
29608334c51SBrooks Davis 
29708334c51SBrooks Davis     /// Mutable pointer to the corresponding executor state.
29808334c51SBrooks Davis     ///
29908334c51SBrooks Davis     /// This object references a member of the executor_handle that yielded this
30008334c51SBrooks Davis     /// exit_handle instance.  We need this direct access to clean up after
30108334c51SBrooks Davis     /// ourselves when the handle is destroyed.
30208334c51SBrooks Davis     exec_handles_map& all_exec_handles;
30308334c51SBrooks Davis 
30408334c51SBrooks Davis     /// Whether the subprocess state has been cleaned yet or not.
30508334c51SBrooks Davis     ///
30608334c51SBrooks Davis     /// Used to keep track of explicit calls to the public cleanup().
30708334c51SBrooks Davis     bool cleaned;
30808334c51SBrooks Davis 
30908334c51SBrooks Davis     /// Constructor.
31008334c51SBrooks Davis     ///
31108334c51SBrooks Davis     /// \param original_pid_ Original PID of the terminated subprocess.
31208334c51SBrooks Davis     /// \param status_ Termination status of the subprocess, or none if
31308334c51SBrooks Davis     ///     timed out.
31408334c51SBrooks Davis     /// \param unprivileged_user_ The user the process ran as, if different than
31508334c51SBrooks Davis     ///     the current one.
31608334c51SBrooks Davis     /// \param start_time_ Timestamp of when the subprocess was spawned.
31708334c51SBrooks Davis     /// \param end_time_ Timestamp of when wait() or wait_any() returned this
31808334c51SBrooks Davis     ///     object.
31908334c51SBrooks Davis     /// \param control_directory_ Path to the subprocess-specific work
32008334c51SBrooks Davis     ///     directory.
32108334c51SBrooks Davis     /// \param stdout_file_ Path to the subprocess's stdout file.
32208334c51SBrooks Davis     /// \param stderr_file_ Path to the subprocess's stderr file.
32308334c51SBrooks Davis     /// \param [in,out] state_owners_ Number of owners of the on-disk state.
32408334c51SBrooks Davis     /// \param [in,out] all_exec_handles_ Global object keeping track of all
32508334c51SBrooks Davis     ///     active executions for an executor.  This is a pointer to a member of
32608334c51SBrooks Davis     ///     the executor_handle object.
implutils::process::executor::exit_handle::impl32708334c51SBrooks Davis     impl(const int original_pid_,
32808334c51SBrooks Davis          const optional< process::status > status_,
32908334c51SBrooks Davis          const optional< passwd::user > unprivileged_user_,
33008334c51SBrooks Davis          const datetime::timestamp& start_time_,
33108334c51SBrooks Davis          const datetime::timestamp& end_time_,
33208334c51SBrooks Davis          const fs::path& control_directory_,
33308334c51SBrooks Davis          const fs::path& stdout_file_,
33408334c51SBrooks Davis          const fs::path& stderr_file_,
33508334c51SBrooks Davis          detail::refcnt_t state_owners_,
33608334c51SBrooks Davis          exec_handles_map& all_exec_handles_) :
33708334c51SBrooks Davis         original_pid(original_pid_), status(status_),
33808334c51SBrooks Davis         unprivileged_user(unprivileged_user_),
33908334c51SBrooks Davis         start_time(start_time_), end_time(end_time_),
34008334c51SBrooks Davis         control_directory(control_directory_),
34108334c51SBrooks Davis         stdout_file(stdout_file_), stderr_file(stderr_file_),
34208334c51SBrooks Davis         state_owners(state_owners_),
34308334c51SBrooks Davis         all_exec_handles(all_exec_handles_), cleaned(false)
34408334c51SBrooks Davis     {
34508334c51SBrooks Davis     }
34608334c51SBrooks Davis 
34708334c51SBrooks Davis     /// Destructor.
~implutils::process::executor::exit_handle::impl34808334c51SBrooks Davis     ~impl(void)
34908334c51SBrooks Davis     {
35008334c51SBrooks Davis         if (!cleaned) {
35108334c51SBrooks Davis             LW(F("Implicitly cleaning up exit_handle for exec_handle %s; "
35208334c51SBrooks Davis                  "ignoring errors!") % original_pid);
35308334c51SBrooks Davis             try {
35408334c51SBrooks Davis                 cleanup();
35508334c51SBrooks Davis             } catch (const std::runtime_error& error) {
35608334c51SBrooks Davis                 LE(F("Subprocess cleanup failed: %s") % error.what());
35708334c51SBrooks Davis             }
35808334c51SBrooks Davis         }
35908334c51SBrooks Davis     }
36008334c51SBrooks Davis 
36108334c51SBrooks Davis     /// Cleans up the subprocess on-disk state.
36208334c51SBrooks Davis     ///
36308334c51SBrooks Davis     /// \throw engine::error If the cleanup fails, especially due to the
36408334c51SBrooks Davis     ///     inability to remove the work directory.
36508334c51SBrooks Davis     void
cleanuputils::process::executor::exit_handle::impl36608334c51SBrooks Davis     cleanup(void)
36708334c51SBrooks Davis     {
36808334c51SBrooks Davis         PRE(*state_owners > 0);
36908334c51SBrooks Davis         if (*state_owners == 1) {
37008334c51SBrooks Davis             LI(F("Cleaning up exit_handle for exec_handle %s") % original_pid);
37108334c51SBrooks Davis             fs::rm_r(control_directory);
37208334c51SBrooks Davis         } else {
37308334c51SBrooks Davis             LI(F("Not cleaning up exit_handle for exec_handle %s; "
37408334c51SBrooks Davis                  "%s owners left") % original_pid % (*state_owners - 1));
37508334c51SBrooks Davis         }
37608334c51SBrooks Davis         // We must decrease our reference only after we have successfully
37708334c51SBrooks Davis         // cleaned up the control directory.  Otherwise, the rm_r call would
37808334c51SBrooks Davis         // throw an exception, which would in turn invoke the implicit cleanup
37908334c51SBrooks Davis         // from the destructor, which would make us crash due to an invalid
38008334c51SBrooks Davis         // reference count.
38108334c51SBrooks Davis         (*state_owners)--;
38208334c51SBrooks Davis         // Marking this object as clean here, even if we did not do actually the
38308334c51SBrooks Davis         // cleaning above, is fine (albeit a bit confusing).  Note that "another
38408334c51SBrooks Davis         // owner" refers to a handle for a different PID, so that handle will be
38508334c51SBrooks Davis         // the one issuing the cleanup.
38608334c51SBrooks Davis         all_exec_handles.erase(original_pid);
38708334c51SBrooks Davis         cleaned = true;
38808334c51SBrooks Davis     }
38908334c51SBrooks Davis };
39008334c51SBrooks Davis 
39108334c51SBrooks Davis 
39208334c51SBrooks Davis /// Constructor.
39308334c51SBrooks Davis ///
39408334c51SBrooks Davis /// \param pimpl Constructed internal implementation.
exit_handle(std::shared_ptr<impl> pimpl)39508334c51SBrooks Davis executor::exit_handle::exit_handle(std::shared_ptr< impl > pimpl) :
39608334c51SBrooks Davis     _pimpl(pimpl)
39708334c51SBrooks Davis {
39808334c51SBrooks Davis }
39908334c51SBrooks Davis 
40008334c51SBrooks Davis 
40108334c51SBrooks Davis /// Destructor.
~exit_handle(void)40208334c51SBrooks Davis executor::exit_handle::~exit_handle(void)
40308334c51SBrooks Davis {
40408334c51SBrooks Davis }
40508334c51SBrooks Davis 
40608334c51SBrooks Davis 
40708334c51SBrooks Davis /// Cleans up the subprocess status.
40808334c51SBrooks Davis ///
40908334c51SBrooks Davis /// This function should be called explicitly as it provides the means to
41008334c51SBrooks Davis /// control any exceptions raised during cleanup.  Do not rely on the destructor
41108334c51SBrooks Davis /// to clean things up.
41208334c51SBrooks Davis ///
41308334c51SBrooks Davis /// \throw engine::error If the cleanup fails, especially due to the inability
41408334c51SBrooks Davis ///     to remove the work directory.
41508334c51SBrooks Davis void
cleanup(void)41608334c51SBrooks Davis executor::exit_handle::cleanup(void)
41708334c51SBrooks Davis {
41808334c51SBrooks Davis     PRE(!_pimpl->cleaned);
41908334c51SBrooks Davis     _pimpl->cleanup();
42008334c51SBrooks Davis     POST(_pimpl->cleaned);
42108334c51SBrooks Davis }
42208334c51SBrooks Davis 
42308334c51SBrooks Davis 
42408334c51SBrooks Davis /// Gets the current number of owners of the on-disk data.
42508334c51SBrooks Davis ///
42608334c51SBrooks Davis /// \return A shared reference counter.  Even though this function is marked as
42708334c51SBrooks Davis /// const, the return value is intentionally mutable because we need to update
42808334c51SBrooks Davis /// reference counts from different but related processes.  This is why this
42908334c51SBrooks Davis /// method is not public.
43008334c51SBrooks Davis std::shared_ptr< std::size_t >
state_owners(void) const43108334c51SBrooks Davis executor::exit_handle::state_owners(void) const
43208334c51SBrooks Davis {
43308334c51SBrooks Davis     return _pimpl->state_owners;
43408334c51SBrooks Davis }
43508334c51SBrooks Davis 
43608334c51SBrooks Davis 
43708334c51SBrooks Davis /// Returns the original PID corresponding to the terminated subprocess.
43808334c51SBrooks Davis ///
43908334c51SBrooks Davis /// \return An exec_handle.
44008334c51SBrooks Davis int
original_pid(void) const44108334c51SBrooks Davis executor::exit_handle::original_pid(void) const
44208334c51SBrooks Davis {
44308334c51SBrooks Davis     return _pimpl->original_pid;
44408334c51SBrooks Davis }
44508334c51SBrooks Davis 
44608334c51SBrooks Davis 
44708334c51SBrooks Davis /// Returns the process termination status of the subprocess.
44808334c51SBrooks Davis ///
44908334c51SBrooks Davis /// \return A process termination status, or none if the subprocess timed out.
45008334c51SBrooks Davis const optional< process::status >&
status(void) const45108334c51SBrooks Davis executor::exit_handle::status(void) const
45208334c51SBrooks Davis {
45308334c51SBrooks Davis     return _pimpl->status;
45408334c51SBrooks Davis }
45508334c51SBrooks Davis 
45608334c51SBrooks Davis 
45708334c51SBrooks Davis /// Returns the user the process ran as if different than the current one.
45808334c51SBrooks Davis ///
45908334c51SBrooks Davis /// \return None if the credentials of the process were the same as the current
46008334c51SBrooks Davis /// one, or else a user.
46108334c51SBrooks Davis const optional< passwd::user >&
unprivileged_user(void) const46208334c51SBrooks Davis executor::exit_handle::unprivileged_user(void) const
46308334c51SBrooks Davis {
46408334c51SBrooks Davis     return _pimpl->unprivileged_user;
46508334c51SBrooks Davis }
46608334c51SBrooks Davis 
46708334c51SBrooks Davis 
46808334c51SBrooks Davis /// Returns the timestamp of when the subprocess was spawned.
46908334c51SBrooks Davis ///
47008334c51SBrooks Davis /// \return A timestamp.
47108334c51SBrooks Davis const datetime::timestamp&
start_time(void) const47208334c51SBrooks Davis executor::exit_handle::start_time(void) const
47308334c51SBrooks Davis {
47408334c51SBrooks Davis     return _pimpl->start_time;
47508334c51SBrooks Davis }
47608334c51SBrooks Davis 
47708334c51SBrooks Davis 
47808334c51SBrooks Davis /// Returns the timestamp of when wait() or wait_any() returned this object.
47908334c51SBrooks Davis ///
48008334c51SBrooks Davis /// \return A timestamp.
48108334c51SBrooks Davis const datetime::timestamp&
end_time(void) const48208334c51SBrooks Davis executor::exit_handle::end_time(void) const
48308334c51SBrooks Davis {
48408334c51SBrooks Davis     return _pimpl->end_time;
48508334c51SBrooks Davis }
48608334c51SBrooks Davis 
48708334c51SBrooks Davis 
48808334c51SBrooks Davis /// Returns the path to the subprocess-specific control directory.
48908334c51SBrooks Davis ///
49008334c51SBrooks Davis /// This is where the executor may store control files.
49108334c51SBrooks Davis ///
49208334c51SBrooks Davis /// \return The path to a directory that exists until cleanup() is called.
49308334c51SBrooks Davis fs::path
control_directory(void) const49408334c51SBrooks Davis executor::exit_handle::control_directory(void) const
49508334c51SBrooks Davis {
49608334c51SBrooks Davis     return _pimpl->control_directory;
49708334c51SBrooks Davis }
49808334c51SBrooks Davis 
49908334c51SBrooks Davis 
50008334c51SBrooks Davis /// Returns the path to the subprocess-specific work directory.
50108334c51SBrooks Davis ///
50208334c51SBrooks Davis /// This is guaranteed to be clear of files created by the executor.
50308334c51SBrooks Davis ///
50408334c51SBrooks Davis /// \return The path to a directory that exists until cleanup() is called.
50508334c51SBrooks Davis fs::path
work_directory(void) const50608334c51SBrooks Davis executor::exit_handle::work_directory(void) const
50708334c51SBrooks Davis {
50808334c51SBrooks Davis     return _pimpl->control_directory / detail::work_subdir;
50908334c51SBrooks Davis }
51008334c51SBrooks Davis 
51108334c51SBrooks Davis 
51208334c51SBrooks Davis /// Returns the path to the subprocess's stdout file.
51308334c51SBrooks Davis ///
51408334c51SBrooks Davis /// \return The path to a file that exists until cleanup() is called.
51508334c51SBrooks Davis const fs::path&
stdout_file(void) const51608334c51SBrooks Davis executor::exit_handle::stdout_file(void) const
51708334c51SBrooks Davis {
51808334c51SBrooks Davis     return _pimpl->stdout_file;
51908334c51SBrooks Davis }
52008334c51SBrooks Davis 
52108334c51SBrooks Davis 
52208334c51SBrooks Davis /// Returns the path to the subprocess's stderr file.
52308334c51SBrooks Davis ///
52408334c51SBrooks Davis /// \return The path to a file that exists until cleanup() is called.
52508334c51SBrooks Davis const fs::path&
stderr_file(void) const52608334c51SBrooks Davis executor::exit_handle::stderr_file(void) const
52708334c51SBrooks Davis {
52808334c51SBrooks Davis     return _pimpl->stderr_file;
52908334c51SBrooks Davis }
53008334c51SBrooks Davis 
53108334c51SBrooks Davis 
53208334c51SBrooks Davis /// Internal implementation for the executor_handle.
53308334c51SBrooks Davis ///
53408334c51SBrooks Davis /// Because the executor is a singleton, these essentially is a container for
53508334c51SBrooks Davis /// global variables.
53608334c51SBrooks Davis struct utils::process::executor::executor_handle::impl : utils::noncopyable {
53708334c51SBrooks Davis     /// Numeric counter of executed subprocesses.
53808334c51SBrooks Davis     ///
53908334c51SBrooks Davis     /// This is used to generate a unique identifier for each subprocess as an
54008334c51SBrooks Davis     /// easy mechanism to discern their unique work directories.
54108334c51SBrooks Davis     size_t last_subprocess;
54208334c51SBrooks Davis 
54308334c51SBrooks Davis     /// Interrupts handler.
544b392a90bSJohn Baldwin     std::unique_ptr< signals::interrupts_handler > interrupts_handler;
54508334c51SBrooks Davis 
54608334c51SBrooks Davis     /// Root work directory for all executed subprocesses.
547b392a90bSJohn Baldwin     std::unique_ptr< fs::auto_directory > root_work_directory;
54808334c51SBrooks Davis 
54908334c51SBrooks Davis     /// Mapping of PIDs to the data required at run time.
55008334c51SBrooks Davis     exec_handles_map all_exec_handles;
55108334c51SBrooks Davis 
5525f174897SMuhammad Moinur Rahman     /// Former members of all_exec_handles removed due to PID reuse.
5535f174897SMuhammad Moinur Rahman     std::forward_list<exec_handle> stale_exec_handles;
5545f174897SMuhammad Moinur Rahman 
55508334c51SBrooks Davis     /// Whether the executor state has been cleaned yet or not.
55608334c51SBrooks Davis     ///
55708334c51SBrooks Davis     /// Used to keep track of explicit calls to the public cleanup().
55808334c51SBrooks Davis     bool cleaned;
55908334c51SBrooks Davis 
56008334c51SBrooks Davis     /// Constructor.
implutils::process::executor::executor_handle::impl56108334c51SBrooks Davis     impl(void) :
56208334c51SBrooks Davis         last_subprocess(0),
56308334c51SBrooks Davis         interrupts_handler(new signals::interrupts_handler()),
56408334c51SBrooks Davis         root_work_directory(new fs::auto_directory(
56508334c51SBrooks Davis             fs::auto_directory::mkdtemp_public(work_directory_template))),
5665f174897SMuhammad Moinur Rahman         all_exec_handles(),
5675f174897SMuhammad Moinur Rahman         stale_exec_handles(),
56808334c51SBrooks Davis         cleaned(false)
56908334c51SBrooks Davis     {
57008334c51SBrooks Davis     }
57108334c51SBrooks Davis 
57208334c51SBrooks Davis     /// Destructor.
~implutils::process::executor::executor_handle::impl57308334c51SBrooks Davis     ~impl(void)
57408334c51SBrooks Davis     {
57508334c51SBrooks Davis         if (!cleaned) {
57608334c51SBrooks Davis             LW("Implicitly cleaning up executor; ignoring errors!");
57708334c51SBrooks Davis             try {
57808334c51SBrooks Davis                 cleanup();
57908334c51SBrooks Davis                 cleaned = true;
58008334c51SBrooks Davis             } catch (const std::runtime_error& error) {
58108334c51SBrooks Davis                 LE(F("Executor global cleanup failed: %s") % error.what());
58208334c51SBrooks Davis             }
58308334c51SBrooks Davis         }
58408334c51SBrooks Davis     }
58508334c51SBrooks Davis 
58608334c51SBrooks Davis     /// Cleans up the executor state.
58708334c51SBrooks Davis     void
cleanuputils::process::executor::executor_handle::impl58808334c51SBrooks Davis     cleanup(void)
58908334c51SBrooks Davis     {
59008334c51SBrooks Davis         PRE(!cleaned);
59108334c51SBrooks Davis 
59208334c51SBrooks Davis         for (exec_handles_map::const_iterator iter = all_exec_handles.begin();
59308334c51SBrooks Davis              iter != all_exec_handles.end(); ++iter) {
59408334c51SBrooks Davis             const int& pid = (*iter).first;
59508334c51SBrooks Davis             const exec_handle& data = (*iter).second;
59608334c51SBrooks Davis 
59708334c51SBrooks Davis             process::terminate_group(pid);
59808334c51SBrooks Davis             int status;
59908334c51SBrooks Davis             if (::waitpid(pid, &status, 0) == -1) {
60008334c51SBrooks Davis                 // Should not happen.
60108334c51SBrooks Davis                 LW(F("Failed to wait for PID %s") % pid);
60208334c51SBrooks Davis             }
60308334c51SBrooks Davis 
60408334c51SBrooks Davis             try {
60508334c51SBrooks Davis                 fs::rm_r(data.control_directory());
60608334c51SBrooks Davis             } catch (const fs::error& e) {
60708334c51SBrooks Davis                 LE(F("Failed to clean up subprocess work directory %s: %s") %
60808334c51SBrooks Davis                    data.control_directory() % e.what());
60908334c51SBrooks Davis             }
61008334c51SBrooks Davis         }
61108334c51SBrooks Davis         all_exec_handles.clear();
61208334c51SBrooks Davis 
6135f174897SMuhammad Moinur Rahman         for (auto iter : stale_exec_handles) {
6145f174897SMuhammad Moinur Rahman             // The process already exited, so no need to kill and wait.
6155f174897SMuhammad Moinur Rahman             try {
6165f174897SMuhammad Moinur Rahman                 fs::rm_r(iter.control_directory());
6175f174897SMuhammad Moinur Rahman             } catch (const fs::error& e) {
6185f174897SMuhammad Moinur Rahman                 LE(F("Failed to clean up stale subprocess work directory "
6195f174897SMuhammad Moinur Rahman                     "%s: %s") % iter.control_directory() % e.what());
6205f174897SMuhammad Moinur Rahman             }
6215f174897SMuhammad Moinur Rahman         }
6225f174897SMuhammad Moinur Rahman         stale_exec_handles.clear();
6235f174897SMuhammad Moinur Rahman 
62408334c51SBrooks Davis         try {
62508334c51SBrooks Davis             // The following only causes the work directory to be deleted, not
62608334c51SBrooks Davis             // any of its contents, so we expect this to always succeed.  This
62708334c51SBrooks Davis             // *should* be sufficient because, in the loop above, we have
62808334c51SBrooks Davis             // individually wiped the subdirectories of any still-unclean
62908334c51SBrooks Davis             // subprocesses.
63008334c51SBrooks Davis             root_work_directory->cleanup();
63108334c51SBrooks Davis         } catch (const fs::error& e) {
6325f174897SMuhammad Moinur Rahman             LE(F("Failed to clean up executor work directory %s: %s; "
6335f174897SMuhammad Moinur Rahman                 "this could be an internal error or a buggy test") %
6345f174897SMuhammad Moinur Rahman                 root_work_directory->directory() % e.what());
63508334c51SBrooks Davis         }
636d61c8bcaSJohn Baldwin         root_work_directory.reset();
63708334c51SBrooks Davis 
63808334c51SBrooks Davis         interrupts_handler->unprogram();
639d61c8bcaSJohn Baldwin         interrupts_handler.reset();
64008334c51SBrooks Davis     }
64108334c51SBrooks Davis 
64208334c51SBrooks Davis     /// Common code to run after any of the wait calls.
64308334c51SBrooks Davis     ///
64408334c51SBrooks Davis     /// \param original_pid The PID of the terminated subprocess.
64508334c51SBrooks Davis     /// \param status The exit status of the terminated subprocess.
64608334c51SBrooks Davis     ///
64708334c51SBrooks Davis     /// \return A pointer to an object describing the waited-for subprocess.
64808334c51SBrooks Davis     executor::exit_handle
post_waitutils::process::executor::executor_handle::impl64908334c51SBrooks Davis     post_wait(const int original_pid, const process::status& status)
65008334c51SBrooks Davis     {
65108334c51SBrooks Davis         PRE(original_pid == status.dead_pid());
65208334c51SBrooks Davis         LI(F("Waited for subprocess with exec_handle %s") % original_pid);
65308334c51SBrooks Davis 
65408334c51SBrooks Davis         process::terminate_group(status.dead_pid());
65508334c51SBrooks Davis 
65608334c51SBrooks Davis         const exec_handles_map::iterator iter = all_exec_handles.find(
65708334c51SBrooks Davis             original_pid);
65808334c51SBrooks Davis         exec_handle& data = (*iter).second;
65908334c51SBrooks Davis         data._pimpl->timer.unprogram();
66008334c51SBrooks Davis 
66108334c51SBrooks Davis         // It is tempting to assert here (and old code did) that, if the timer
66208334c51SBrooks Davis         // has fired, the process has been forcibly killed by us.  This is not
66308334c51SBrooks Davis         // always the case though: for short-lived processes and with very short
66408334c51SBrooks Davis         // timeouts (think 1ms), it is possible for scheduling decisions to
66508334c51SBrooks Davis         // allow the subprocess to finish while at the same time cause the timer
66608334c51SBrooks Davis         // to fire.  So we do not assert this any longer and just rely on the
66708334c51SBrooks Davis         // timer expiration to check if the process timed out or not.  If the
66808334c51SBrooks Davis         // process did finish but the timer expired... oh well, we do not detect
66908334c51SBrooks Davis         // this correctly but we don't care because this should not really
67008334c51SBrooks Davis         // happen.
67108334c51SBrooks Davis 
67208334c51SBrooks Davis         if (!fs::exists(data.stdout_file())) {
67308334c51SBrooks Davis             std::ofstream new_stdout(data.stdout_file().c_str());
67408334c51SBrooks Davis         }
67508334c51SBrooks Davis         if (!fs::exists(data.stderr_file())) {
67608334c51SBrooks Davis             std::ofstream new_stderr(data.stderr_file().c_str());
67708334c51SBrooks Davis         }
67808334c51SBrooks Davis 
67908334c51SBrooks Davis         return exit_handle(std::shared_ptr< exit_handle::impl >(
68008334c51SBrooks Davis             new exit_handle::impl(
68108334c51SBrooks Davis                 data.pid(),
68208334c51SBrooks Davis                 data._pimpl->timer.fired() ?
68308334c51SBrooks Davis                     none : utils::make_optional(status),
68408334c51SBrooks Davis                 data._pimpl->unprivileged_user,
68508334c51SBrooks Davis                 data._pimpl->start_time, datetime::timestamp::now(),
68608334c51SBrooks Davis                 data.control_directory(),
68708334c51SBrooks Davis                 data.stdout_file(),
68808334c51SBrooks Davis                 data.stderr_file(),
68908334c51SBrooks Davis                 data._pimpl->state_owners,
69008334c51SBrooks Davis                 all_exec_handles)));
69108334c51SBrooks Davis     }
692257e70f1SIgor Ostapenko 
693257e70f1SIgor Ostapenko     executor::exit_handle
reaputils::process::executor::executor_handle::impl694257e70f1SIgor Ostapenko     reap(const pid_t original_pid)
695257e70f1SIgor Ostapenko     {
696257e70f1SIgor Ostapenko         const exec_handles_map::iterator iter = all_exec_handles.find(
697257e70f1SIgor Ostapenko             original_pid);
698257e70f1SIgor Ostapenko         exec_handle& data = (*iter).second;
699257e70f1SIgor Ostapenko         data._pimpl->timer.unprogram();
700257e70f1SIgor Ostapenko 
701257e70f1SIgor Ostapenko         if (!fs::exists(data.stdout_file())) {
702257e70f1SIgor Ostapenko             std::ofstream new_stdout(data.stdout_file().c_str());
703257e70f1SIgor Ostapenko         }
704257e70f1SIgor Ostapenko         if (!fs::exists(data.stderr_file())) {
705257e70f1SIgor Ostapenko             std::ofstream new_stderr(data.stderr_file().c_str());
706257e70f1SIgor Ostapenko         }
707257e70f1SIgor Ostapenko 
708257e70f1SIgor Ostapenko         return exit_handle(std::shared_ptr< exit_handle::impl >(
709257e70f1SIgor Ostapenko             new exit_handle::impl(
710257e70f1SIgor Ostapenko                 data.pid(),
711257e70f1SIgor Ostapenko                 none,
712257e70f1SIgor Ostapenko                 data._pimpl->unprivileged_user,
713257e70f1SIgor Ostapenko                 data._pimpl->start_time, datetime::timestamp::now(),
714257e70f1SIgor Ostapenko                 data.control_directory(),
715257e70f1SIgor Ostapenko                 data.stdout_file(),
716257e70f1SIgor Ostapenko                 data.stderr_file(),
717257e70f1SIgor Ostapenko                 data._pimpl->state_owners,
718257e70f1SIgor Ostapenko                 all_exec_handles)));
719257e70f1SIgor Ostapenko     }
72008334c51SBrooks Davis };
72108334c51SBrooks Davis 
72208334c51SBrooks Davis 
72308334c51SBrooks Davis /// Constructor.
executor_handle(void)72408334c51SBrooks Davis executor::executor_handle::executor_handle(void) throw() : _pimpl(new impl())
72508334c51SBrooks Davis {
72608334c51SBrooks Davis }
72708334c51SBrooks Davis 
72808334c51SBrooks Davis 
72908334c51SBrooks Davis /// Destructor.
~executor_handle(void)73008334c51SBrooks Davis executor::executor_handle::~executor_handle(void)
73108334c51SBrooks Davis {
73208334c51SBrooks Davis }
73308334c51SBrooks Davis 
73408334c51SBrooks Davis 
73508334c51SBrooks Davis /// Queries the path to the root of the work directory for all subprocesses.
73608334c51SBrooks Davis ///
73708334c51SBrooks Davis /// \return A path.
73808334c51SBrooks Davis const fs::path&
root_work_directory(void) const73908334c51SBrooks Davis executor::executor_handle::root_work_directory(void) const
74008334c51SBrooks Davis {
74108334c51SBrooks Davis     return _pimpl->root_work_directory->directory();
74208334c51SBrooks Davis }
74308334c51SBrooks Davis 
74408334c51SBrooks Davis 
74508334c51SBrooks Davis /// Cleans up the executor state.
74608334c51SBrooks Davis ///
74708334c51SBrooks Davis /// This function should be called explicitly as it provides the means to
74808334c51SBrooks Davis /// control any exceptions raised during cleanup.  Do not rely on the destructor
74908334c51SBrooks Davis /// to clean things up.
75008334c51SBrooks Davis ///
75108334c51SBrooks Davis /// \throw engine::error If there are problems cleaning up the executor.
75208334c51SBrooks Davis void
cleanup(void)75308334c51SBrooks Davis executor::executor_handle::cleanup(void)
75408334c51SBrooks Davis {
75508334c51SBrooks Davis     PRE(!_pimpl->cleaned);
75608334c51SBrooks Davis     _pimpl->cleanup();
75708334c51SBrooks Davis     _pimpl->cleaned = true;
75808334c51SBrooks Davis }
75908334c51SBrooks Davis 
76008334c51SBrooks Davis 
76108334c51SBrooks Davis /// Initializes the executor.
76208334c51SBrooks Davis ///
76308334c51SBrooks Davis /// \pre This function can only be called if there is no other executor_handle
76408334c51SBrooks Davis /// object alive.
76508334c51SBrooks Davis ///
76608334c51SBrooks Davis /// \return A handle to the operations of the executor.
76708334c51SBrooks Davis executor::executor_handle
setup(void)76808334c51SBrooks Davis executor::setup(void)
76908334c51SBrooks Davis {
77008334c51SBrooks Davis     return executor_handle();
77108334c51SBrooks Davis }
77208334c51SBrooks Davis 
77308334c51SBrooks Davis 
77408334c51SBrooks Davis /// Pre-helper for the spawn() method.
77508334c51SBrooks Davis ///
77608334c51SBrooks Davis /// \return The created control directory for the subprocess.
77708334c51SBrooks Davis fs::path
spawn_pre(void)77808334c51SBrooks Davis executor::executor_handle::spawn_pre(void)
77908334c51SBrooks Davis {
78008334c51SBrooks Davis     signals::check_interrupt();
78108334c51SBrooks Davis 
78208334c51SBrooks Davis     ++_pimpl->last_subprocess;
78308334c51SBrooks Davis 
78408334c51SBrooks Davis     const fs::path control_directory =
78508334c51SBrooks Davis         _pimpl->root_work_directory->directory() /
78608334c51SBrooks Davis         (F("%s") % _pimpl->last_subprocess);
78708334c51SBrooks Davis     fs::mkdir_p(control_directory / detail::work_subdir, 0755);
78808334c51SBrooks Davis 
78908334c51SBrooks Davis     return control_directory;
79008334c51SBrooks Davis }
79108334c51SBrooks Davis 
79208334c51SBrooks Davis 
79308334c51SBrooks Davis /// Post-helper for the spawn() method.
79408334c51SBrooks Davis ///
79508334c51SBrooks Davis /// \param control_directory Control directory as returned by spawn_pre().
79608334c51SBrooks Davis /// \param stdout_file Path to the subprocess' stdout.
79708334c51SBrooks Davis /// \param stderr_file Path to the subprocess' stderr.
79808334c51SBrooks Davis /// \param timeout Maximum amount of time the subprocess can run for.
79908334c51SBrooks Davis /// \param unprivileged_user If not none, user to switch to before execution.
80008334c51SBrooks Davis /// \param child The process created by spawn().
80108334c51SBrooks Davis ///
80208334c51SBrooks Davis /// \return The execution handle of the started subprocess.
80308334c51SBrooks Davis executor::exec_handle
spawn_post(const fs::path & control_directory,const fs::path & stdout_file,const fs::path & stderr_file,const datetime::delta & timeout,const optional<passwd::user> unprivileged_user,std::unique_ptr<process::child> child)80408334c51SBrooks Davis executor::executor_handle::spawn_post(
80508334c51SBrooks Davis     const fs::path& control_directory,
80608334c51SBrooks Davis     const fs::path& stdout_file,
80708334c51SBrooks Davis     const fs::path& stderr_file,
80808334c51SBrooks Davis     const datetime::delta& timeout,
80908334c51SBrooks Davis     const optional< passwd::user > unprivileged_user,
810b392a90bSJohn Baldwin     std::unique_ptr< process::child > child)
81108334c51SBrooks Davis {
81208334c51SBrooks Davis     const exec_handle handle(std::shared_ptr< exec_handle::impl >(
81308334c51SBrooks Davis         new exec_handle::impl(
81408334c51SBrooks Davis             child->pid(),
81508334c51SBrooks Davis             control_directory,
81608334c51SBrooks Davis             stdout_file,
81708334c51SBrooks Davis             stderr_file,
81808334c51SBrooks Davis             datetime::timestamp::now(),
81908334c51SBrooks Davis             timeout,
82008334c51SBrooks Davis             unprivileged_user,
82108334c51SBrooks Davis             detail::refcnt_t(new detail::refcnt_t::element_type(0)))));
8225f174897SMuhammad Moinur Rahman     const auto value = exec_handles_map::value_type(handle.pid(), handle);
8235f174897SMuhammad Moinur Rahman     auto insert_pair = _pimpl->all_exec_handles.insert(value);
8245f174897SMuhammad Moinur Rahman     if (!insert_pair.second) {
8255f174897SMuhammad Moinur Rahman         LI(F("PID %s already in all_exec_handles") % handle.pid());
8265f174897SMuhammad Moinur Rahman         _pimpl->stale_exec_handles.push_front(insert_pair.first->second);
8275f174897SMuhammad Moinur Rahman         _pimpl->all_exec_handles.erase(insert_pair.first);
8285f174897SMuhammad Moinur Rahman         insert_pair = _pimpl->all_exec_handles.insert(value);
8295f174897SMuhammad Moinur Rahman         INV_MSG(insert_pair.second, F("PID %s still in all_exec_handles") %
8305f174897SMuhammad Moinur Rahman             handle.pid());
8315f174897SMuhammad Moinur Rahman     }
83208334c51SBrooks Davis     LI(F("Spawned subprocess with exec_handle %s") % handle.pid());
83308334c51SBrooks Davis     return handle;
83408334c51SBrooks Davis }
83508334c51SBrooks Davis 
83608334c51SBrooks Davis 
83708334c51SBrooks Davis /// Pre-helper for the spawn_followup() method.
83808334c51SBrooks Davis void
spawn_followup_pre(void)83908334c51SBrooks Davis executor::executor_handle::spawn_followup_pre(void)
84008334c51SBrooks Davis {
84108334c51SBrooks Davis     signals::check_interrupt();
84208334c51SBrooks Davis }
84308334c51SBrooks Davis 
84408334c51SBrooks Davis 
84508334c51SBrooks Davis /// Post-helper for the spawn_followup() method.
84608334c51SBrooks Davis ///
84708334c51SBrooks Davis /// \param base Exit handle of the subprocess to use as context.
84808334c51SBrooks Davis /// \param timeout Maximum amount of time the subprocess can run for.
84908334c51SBrooks Davis /// \param child The process created by spawn_followup().
85008334c51SBrooks Davis ///
85108334c51SBrooks Davis /// \return The execution handle of the started subprocess.
85208334c51SBrooks Davis executor::exec_handle
spawn_followup_post(const exit_handle & base,const datetime::delta & timeout,std::unique_ptr<process::child> child)85308334c51SBrooks Davis executor::executor_handle::spawn_followup_post(
85408334c51SBrooks Davis     const exit_handle& base,
85508334c51SBrooks Davis     const datetime::delta& timeout,
856b392a90bSJohn Baldwin     std::unique_ptr< process::child > child)
85708334c51SBrooks Davis {
85808334c51SBrooks Davis     INV(*base.state_owners() > 0);
85908334c51SBrooks Davis     const exec_handle handle(std::shared_ptr< exec_handle::impl >(
86008334c51SBrooks Davis         new exec_handle::impl(
86108334c51SBrooks Davis             child->pid(),
86208334c51SBrooks Davis             base.control_directory(),
86308334c51SBrooks Davis             base.stdout_file(),
86408334c51SBrooks Davis             base.stderr_file(),
86508334c51SBrooks Davis             datetime::timestamp::now(),
86608334c51SBrooks Davis             timeout,
86708334c51SBrooks Davis             base.unprivileged_user(),
86808334c51SBrooks Davis             base.state_owners())));
8695f174897SMuhammad Moinur Rahman     const auto value = exec_handles_map::value_type(handle.pid(), handle);
8705f174897SMuhammad Moinur Rahman     auto insert_pair = _pimpl->all_exec_handles.insert(value);
8715f174897SMuhammad Moinur Rahman     if (!insert_pair.second) {
8725f174897SMuhammad Moinur Rahman         LI(F("PID %s already in all_exec_handles") % handle.pid());
8735f174897SMuhammad Moinur Rahman         _pimpl->stale_exec_handles.push_front(insert_pair.first->second);
8745f174897SMuhammad Moinur Rahman         _pimpl->all_exec_handles.erase(insert_pair.first);
8755f174897SMuhammad Moinur Rahman         insert_pair = _pimpl->all_exec_handles.insert(value);
8765f174897SMuhammad Moinur Rahman         INV_MSG(insert_pair.second, F("PID %s still in all_exec_handles") %
8775f174897SMuhammad Moinur Rahman             handle.pid());
8785f174897SMuhammad Moinur Rahman     }
87908334c51SBrooks Davis     LI(F("Spawned subprocess with exec_handle %s") % handle.pid());
88008334c51SBrooks Davis     return handle;
88108334c51SBrooks Davis }
88208334c51SBrooks Davis 
88308334c51SBrooks Davis 
88408334c51SBrooks Davis /// Waits for completion of any forked process.
88508334c51SBrooks Davis ///
88608334c51SBrooks Davis /// \param exec_handle The handle of the process to wait for.
88708334c51SBrooks Davis ///
88808334c51SBrooks Davis /// \return A pointer to an object describing the waited-for subprocess.
88908334c51SBrooks Davis executor::exit_handle
wait(const exec_handle exec_handle)89008334c51SBrooks Davis executor::executor_handle::wait(const exec_handle exec_handle)
89108334c51SBrooks Davis {
89208334c51SBrooks Davis     signals::check_interrupt();
89308334c51SBrooks Davis     const process::status status = process::wait(exec_handle.pid());
89408334c51SBrooks Davis     return _pimpl->post_wait(exec_handle.pid(), status);
89508334c51SBrooks Davis }
89608334c51SBrooks Davis 
89708334c51SBrooks Davis 
89808334c51SBrooks Davis /// Waits for completion of any forked process.
89908334c51SBrooks Davis ///
90008334c51SBrooks Davis /// \return A pointer to an object describing the waited-for subprocess.
90108334c51SBrooks Davis executor::exit_handle
wait_any(void)90208334c51SBrooks Davis executor::executor_handle::wait_any(void)
90308334c51SBrooks Davis {
90408334c51SBrooks Davis     signals::check_interrupt();
90508334c51SBrooks Davis     const process::status status = process::wait_any();
90608334c51SBrooks Davis     return _pimpl->post_wait(status.dead_pid(), status);
90708334c51SBrooks Davis }
90808334c51SBrooks Davis 
90908334c51SBrooks Davis 
910257e70f1SIgor Ostapenko /// Forms exit_handle for the given PID subprocess.
911257e70f1SIgor Ostapenko ///
912257e70f1SIgor Ostapenko /// Can be used in the cases when we want to do cleanup(s) of a killed test
913257e70f1SIgor Ostapenko /// subprocess, but we do not have exit handle as we usually do after normal
914257e70f1SIgor Ostapenko /// wait mechanism.
915257e70f1SIgor Ostapenko ///
916257e70f1SIgor Ostapenko /// \return A pointer to an object describing the subprocess.
917257e70f1SIgor Ostapenko executor::exit_handle
reap(const int pid)918257e70f1SIgor Ostapenko executor::executor_handle::reap(const int pid)
919257e70f1SIgor Ostapenko {
920257e70f1SIgor Ostapenko     return _pimpl->reap(pid);
921257e70f1SIgor Ostapenko }
922257e70f1SIgor Ostapenko 
923257e70f1SIgor Ostapenko 
92408334c51SBrooks Davis /// Checks if an interrupt has fired.
92508334c51SBrooks Davis ///
92608334c51SBrooks Davis /// Calls to this function should be sprinkled in strategic places through the
92708334c51SBrooks Davis /// code protected by an interrupts_handler object.
92808334c51SBrooks Davis ///
92908334c51SBrooks Davis /// This is just a wrapper over signals::check_interrupt() to avoid leaking this
93008334c51SBrooks Davis /// dependency to the caller.
93108334c51SBrooks Davis ///
93208334c51SBrooks Davis /// \throw signals::interrupted_error If there has been an interrupt.
93308334c51SBrooks Davis void
check_interrupt(void) const93408334c51SBrooks Davis executor::executor_handle::check_interrupt(void) const
93508334c51SBrooks Davis {
93608334c51SBrooks Davis     signals::check_interrupt();
93708334c51SBrooks Davis }
938