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