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