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