/// Waits for completion of any forked process. /// /// \return A pointer to an object describing the waited-for subprocess. executor::exit_handle executor::executor_handle::wait_any(void) { signals::check_interrupt(); const process::status status = process::wait_any(); return _pimpl->post_wait(status.dead_pid(), status); }
/// Blocks to wait for completion of any subprocess. /// /// \return The termination status of the child process that terminated. /// /// \throw process::system_error If the call to wait(2) fails. process::status process::wait_any(void) { const process::status status = safe_wait(); { signals::interrupts_inhibiter inhibiter; signals::remove_pid_to_kill(status.dead_pid()); } return status; }
/// Tests an exec function with no arguments. /// /// \param tc The calling test case. /// \param do_exec The exec function to test. static void check_exec_no_args(const atf::tests::tc* tc, const exec_function do_exec) { std::auto_ptr< process::child > child = process::child::fork_files( child_exec(do_exec, get_helpers(tc), process::args_vector()), fs::path("stdout"), fs::path("stderr")); const process::status status = child->wait(); ATF_REQUIRE(status.exited()); ATF_REQUIRE_EQ(EXIT_FAILURE, status.exitstatus()); ATF_REQUIRE(atf::utils::grep_file("Must provide a helper name", "stderr")); }
/// Generates a core dump, if possible. /// /// \post If this fails to generate a core file, the test case is marked as /// skipped. The caller can rely on this when attempting further checks on the /// core dump by assuming that the core dump exists somewhere. /// /// \param test_case Pointer to the caller test case, needed to obtain the path /// to the source directory. /// \param base_name Name of the binary to execute, which will be a copy of a /// helper binary that always crashes. This name should later be part of /// the core filename. /// /// \return The status of the crashed binary. static process::status generate_core(const atf::tests::tc* test_case, const char* base_name) { utils::unlimit_core_size(); const fs::path helper = fs::path(test_case->get_config_var("srcdir")) / "stacktrace_helper"; const process::status status = process::child::fork_files( crash_me(helper, base_name), fs::path("unused.out"), fs::path("unused.err"))->wait(); ATF_REQUIRE(status.signaled()); if (!status.coredump()) ATF_SKIP("Test failed to generate core dump"); return status; }
/// Tests an exec function with some arguments. /// /// \param tc The calling test case. /// \param do_exec The exec function to test. static void check_exec_some_args(const atf::tests::tc* tc, const exec_function do_exec) { process::args_vector args; args.push_back("print-args"); args.push_back("foo"); args.push_back("bar"); std::auto_ptr< process::child > child = process::child::fork_files( child_exec(do_exec, get_helpers(tc), args), fs::path("stdout"), fs::path("stderr")); const process::status status = child->wait(); ATF_REQUIRE(status.exited()); ATF_REQUIRE_EQ(EXIT_SUCCESS, status.exitstatus()); ATF_REQUIRE(atf::utils::grep_file("argv\\[1\\] = print-args", "stdout")); ATF_REQUIRE(atf::utils::grep_file("argv\\[2\\] = foo", "stdout")); ATF_REQUIRE(atf::utils::grep_file("argv\\[3\\] = bar", "stdout")); }
/// Common code to run after any of the wait calls. /// /// \param handle The exec_handle of the terminated subprocess. /// \param status The exit status of the terminated subprocess. /// /// \return A pointer to an object describing the waited-for subprocess. executor::exit_handle post_wait(const executor::exec_handle handle, const process::status& status) { PRE(handle == status.dead_pid()); LI(F("Waited for subprocess with exec_handle %s") % handle); process::terminate_group(status.dead_pid()); const exec_data_map::iterator iter = all_exec_data.find(handle); exec_data_ptr& data = (*iter).second; data->timer.unprogram(); // It is tempting to assert here (and old code did) that, if the timer // has fired, the process has been forcibly killed by us. This is not // always the case though: for short-lived processes and with very short // timeouts (think 1ms), it is possible for scheduling decisions to // allow the subprocess to finish while at the same time cause the timer // to fire. So we do not assert this any longer and just rely on the // timer expiration to check if the process timed out or not. If the // process did finish but the timer expired... oh well, we do not detect // this correctly but we don't care because this should not really // happen. if (!fs::exists(data->stdout_file)) { std::ofstream new_stdout(data->stdout_file.c_str()); } if (!fs::exists(data->stderr_file)) { std::ofstream new_stderr(data->stderr_file.c_str()); } return exit_handle(std::shared_ptr< exit_handle::impl >( new exit_handle::impl( handle, data->timer.fired() ? none : utils::make_optional(status), data->unprivileged_user, data->start_time, datetime::timestamp::now(), data->control_directory, data->stdout_file, data->stderr_file, data->state_owners, all_exec_data))); }