std::string impl::check_requirements(const atf::tests::vars_map& metadata, const atf::tests::vars_map& config) { std::string failure_reason = ""; for (atf::tests::vars_map::const_iterator iter = metadata.begin(); failure_reason.empty() && iter != metadata.end(); iter++) { const std::string& name = (*iter).first; const std::string& value = (*iter).second; INV(!value.empty()); // Enforced by application/X-atf-tp parser. if (name == "require.arch") failure_reason = check_arch(value); else if (name == "require.config") failure_reason = check_config(value, config); else if (name == "require.machine") failure_reason = check_machine(value); else if (name == "require.progs") failure_reason = check_progs(value); else if (name == "require.user") failure_reason = check_user(value, config); else { // Unknown require.* properties are forbidden by the // application/X-atf-tp parser. INV(failure_reason.find("require.") != 0); } } return failure_reason; }
std::pair< int, int > impl::get_required_user(const atf::tests::vars_map& metadata, const atf::tests::vars_map& config) { const atf::tests::vars_map::const_iterator user = metadata.find( "require.user"); if (user == metadata.end()) return std::make_pair(-1, -1); if ((*user).second == "unprivileged") { if (impl::is_root()) { const atf::tests::vars_map::const_iterator iter = config.find( "unprivileged-user"); try { return impl::get_user_ids((*iter).second); } catch (const std::exception& e) { UNREACHABLE; // This has been validated by check_user. throw e; } } else { return std::make_pair(-1, -1); } } else return std::make_pair(-1, -1); }
std::pair< std::string, atf::process::status > impl::run_test_case(const atf::fs::path& executable, const std::string& test_case_name, const std::string& test_case_part, const atf::tests::vars_map& metadata, const atf::tests::vars_map& config, const atf::fs::path& resfile, const atf::fs::path& workdir, atf_tps_writer& writer) { // TODO: Capture termination signals and deliver them to the subprocess // instead. Or maybe do something else; think about it. test_case_params params(executable, test_case_name, test_case_part, metadata, config, resfile, workdir); atf::process::child child = atf::process::fork(run_test_case_child, atf::process::stream_capture(), atf::process::stream_capture(), static_cast< void * >(¶ms)); terminate_poll = false; const atf::tests::vars_map::const_iterator iter = metadata.find("timeout"); INV(iter != metadata.end()); const unsigned int timeout = atf::text::to_type< unsigned int >((*iter).second); const pid_t child_pid = child.pid(); // Get the input stream of stdout and stderr. impl::file_handle outfh = child.stdout_fd(); impl::file_handle errfh = child.stderr_fd(); bool timed_out = false; // Process the test case's output and multiplex it into our output // stream as we read it. int fds[2] = {outfh.get(), errfh.get()}; child_muxer mux(fds, 2, writer); try { child_timer timeout_timer(timeout, child_pid, terminate_poll); signal_programmer sigchld(SIGCHLD, sigchld_handler); mux.mux(terminate_poll); timed_out = timeout_timer.fired(); } catch (...) { UNREACHABLE; } ::killpg(child_pid, SIGKILL); mux.flush(); atf::process::status status = child.wait(); std::string reason; if (timed_out) { // Don't assume the child process has been signaled due to the timeout // expiration as older versions did. The child process may have exited // but we may have timed out due to a subchild process getting stuck. reason = "Test case timed out after " + atf::text::to_string(timeout) + " " + (timeout == 1 ? "second" : "seconds"); } return std::make_pair(reason, status); }