/* TODO: Investigate if it's worth to add this functionality as part of * the public API. I.e. a function to easily run a test case body in a * subprocess. */ void run_h_tc(atf_tc_t *tc, const char *outname, const char *errname, const char *resname) { atf_fs_path_t outpath, errpath; atf_process_stream_t outb, errb; atf_process_child_t child; atf_process_status_t status; RE(atf_fs_path_init_fmt(&outpath, outname)); RE(atf_fs_path_init_fmt(&errpath, errname)); struct run_h_tc_data data = { tc, resname }; RE(atf_process_stream_init_redirect_path(&outb, &outpath)); RE(atf_process_stream_init_redirect_path(&errb, &errpath)); RE(atf_process_fork(&child, run_h_tc_child, &outb, &errb, &data)); atf_process_stream_fini(&errb); atf_process_stream_fini(&outb); RE(atf_process_child_wait(&child, &status)); ATF_CHECK(atf_process_status_exited(&status)); atf_process_status_fini(&status); atf_fs_path_fini(&errpath); atf_fs_path_fini(&outpath); }
atf_error_t atf_process_fork(atf_process_child_t *c, void (*start)(void *), const atf_process_stream_t *outsb, const atf_process_stream_t *errsb, void *v) { atf_error_t err; atf_process_stream_t inherit_outsb, inherit_errsb; const atf_process_stream_t *real_outsb, *real_errsb; real_outsb = NULL; /* Shut up GCC warning. */ err = init_stream_w_default(outsb, &inherit_outsb, &real_outsb); if (atf_is_error(err)) goto out; real_errsb = NULL; /* Shut up GCC warning. */ err = init_stream_w_default(errsb, &inherit_errsb, &real_errsb); if (atf_is_error(err)) goto out_out; err = fork_with_streams(c, start, real_outsb, real_errsb, v); if (errsb == NULL) atf_process_stream_fini(&inherit_errsb); out_out: if (outsb == NULL) atf_process_stream_fini(&inherit_outsb); out: return err; }
ATF_TC_BODY (child_pid, tc) { atf_process_stream_t outsb, errsb; atf_process_child_t child; atf_process_status_t status; pid_t pid; RE (atf_process_stream_init_capture (&outsb)); RE (atf_process_stream_init_inherit (&errsb)); RE (atf_process_fork (&child, child_report_pid, &outsb, &errsb, NULL)); ATF_CHECK_EQ (read (atf_process_child_stdout (&child), &pid, sizeof (pid)), sizeof (pid)); printf ("Expected PID: %d\n", (int) atf_process_child_pid (&child)); printf ("Actual PID: %d\n", (int) pid); ATF_CHECK_EQ (atf_process_child_pid (&child), pid); RE (atf_process_child_wait (&child, &status)); atf_process_status_fini (&status); atf_process_stream_fini (&outsb); atf_process_stream_fini (&errsb); }
ATF_TC_BODY (stream_init_inherit, tc) { atf_process_stream_t sb; RE (atf_process_stream_init_inherit (&sb)); ATF_CHECK_EQ (atf_process_stream_type (&sb), atf_process_stream_type_inherit); atf_process_stream_fini (&sb); }
ATF_TC_BODY (stream_init_capture, tc) { atf_process_stream_t sb; RE (atf_process_stream_init_capture (&sb)); ATF_CHECK_EQ (atf_process_stream_type (&sb), atf_process_stream_type_capture); atf_process_stream_fini (&sb); }
ATF_TC_BODY (stream_init_redirect_fd, tc) { atf_process_stream_t sb; RE (atf_process_stream_init_redirect_fd (&sb, 1)); ATF_CHECK_EQ (atf_process_stream_type (&sb), atf_process_stream_type_redirect_fd); atf_process_stream_fini (&sb); }
ATF_TC_BODY (stream_init_connect, tc) { atf_process_stream_t sb; RE (atf_process_stream_init_connect (&sb, 1, 2)); ATF_CHECK_EQ (atf_process_stream_type (&sb), atf_process_stream_type_connect); atf_process_stream_fini (&sb); }
static void redirect_path_stream_fini (void *v) { struct redirect_path_stream *s = v; atf_process_stream_fini (&s->m_base.m_sb); atf_fs_path_fini (&s->m_path); check_file (s->m_base.m_type); }
static void redirect_fd_stream_fini (void *v) { struct redirect_fd_stream *s = v; ATF_REQUIRE (close (s->m_fd) != -1); atf_process_stream_fini (&s->m_base.m_sb); check_file (s->m_base.m_type); }
static void inherit_stream_fini (void *v) { struct inherit_stream *s = v; ATF_REQUIRE (dup2 (s->m_old_fd, s->m_fd) != -1); ATF_REQUIRE (close (s->m_old_fd) != -1); atf_process_stream_fini (&s->m_base.m_sb); check_file (s->m_base.m_type); }
ATF_TC_BODY (fork_cookie, tc) { atf_process_stream_t outsb, errsb; RE (atf_process_stream_init_inherit (&outsb)); RE (atf_process_stream_init_inherit (&errsb)); { atf_process_child_t child; atf_process_status_t status; RE (atf_process_fork (&child, child_cookie, &outsb, &errsb, NULL)); RE (atf_process_child_wait (&child, &status)); ATF_CHECK (atf_process_status_exited (&status)); ATF_CHECK_EQ (atf_process_status_exitstatus (&status), exit_v_null); atf_process_status_fini (&status); } { atf_process_child_t child; atf_process_status_t status; int dummy_int; RE (atf_process_fork (&child, child_cookie, &outsb, &errsb, &dummy_int)); RE (atf_process_child_wait (&child, &status)); ATF_CHECK (atf_process_status_exited (&status)); ATF_CHECK_EQ (atf_process_status_exitstatus (&status), exit_v_notnull); atf_process_status_fini (&status); } atf_process_stream_fini (&errsb); atf_process_stream_fini (&outsb); }
ATF_TC_BODY (stream_init_redirect_path, tc) { atf_process_stream_t sb; atf_fs_path_t path; RE (atf_fs_path_init_fmt (&path, "foo")); RE (atf_process_stream_init_redirect_path (&sb, &path)); ATF_CHECK_EQ (atf_process_stream_type (&sb), atf_process_stream_type_redirect_path); atf_process_stream_fini (&sb); atf_fs_path_fini (&path); }
ATF_TC_BODY (child_wait_eintr, tc) { atf_process_child_t child; atf_process_status_t status; { atf_process_stream_t outsb, errsb; RE (atf_process_stream_init_capture (&outsb)); RE (atf_process_stream_init_inherit (&errsb)); RE (atf_process_fork (&child, child_spawn_loop_and_wait_eintr, &outsb, &errsb, NULL)); atf_process_stream_fini (&outsb); atf_process_stream_fini (&errsb); } { /* Wait until the child process performs the wait call. This is * racy, because the message we get from it is sent *before* * doing the real system call... but I can't figure any other way * to do this. */ char buf[16]; printf ("Waiting for child to issue wait(2)\n"); ATF_REQUIRE (read (atf_process_child_stdout (&child), buf, sizeof (buf)) > 0); sleep (1); } printf ("Interrupting child's wait(2) call\n"); kill (atf_process_child_pid (&child), SIGHUP); printf ("Waiting for child's completion\n"); RE (atf_process_child_wait (&child, &status)); ATF_REQUIRE (atf_process_status_exited (&status)); ATF_REQUIRE_EQ (atf_process_status_exitstatus (&status), EXIT_SUCCESS); atf_process_status_fini (&status); }
static atf_error_t init_sbs(const atf_fs_path_t *outfile, atf_process_stream_t *outsb, const atf_fs_path_t *errfile, atf_process_stream_t *errsb) { atf_error_t err; err = init_sb(outfile, outsb); if (atf_is_error(err)) goto out; err = init_sb(errfile, errsb); if (atf_is_error(err)) { atf_process_stream_fini(outsb); goto out; } out: return err; }
ATF_TC_BODY (exec_list, tc) { atf_fs_path_t process_helpers; atf_list_t argv; atf_process_status_t status; RE (atf_list_init (&argv)); get_process_helpers_path (tc, true, &process_helpers); atf_list_append (&argv, strdup (atf_fs_path_cstring (&process_helpers)), true); atf_list_append (&argv, strdup ("echo"), true); atf_list_append (&argv, strdup ("test-message"), true); { atf_fs_path_t outpath; atf_process_stream_t outsb; RE (atf_fs_path_init_fmt (&outpath, "stdout")); RE (atf_process_stream_init_redirect_path (&outsb, &outpath)); RE (atf_process_exec_list (&status, &process_helpers, &argv, &outsb, NULL)); atf_process_stream_fini (&outsb); atf_fs_path_fini (&outpath); } atf_list_fini (&argv); ATF_CHECK (atf_process_status_exited (&status)); ATF_CHECK_EQ (atf_process_status_exitstatus (&status), EXIT_SUCCESS); { int fd = open ("stdout", O_RDONLY); ATF_CHECK (fd != -1); check_line (fd, "test-message"); close (fd); } atf_process_status_fini (&status); atf_fs_path_fini (&process_helpers); }
static void capture_stream_fini (void *v) { struct capture_stream *s = v; switch (s->m_base.m_type) { case stdout_type: ATF_CHECK (grep_string (&s->m_msg, "stdout: msg")); ATF_CHECK (!grep_string (&s->m_msg, "stderr: msg")); break; case stderr_type: ATF_CHECK (!grep_string (&s->m_msg, "stdout: msg")); ATF_CHECK (grep_string (&s->m_msg, "stderr: msg")); break; default: UNREACHABLE; } atf_dynstr_fini (&s->m_msg); atf_process_stream_fini (&s->m_base.m_sb); }
static void child_spawn_loop_and_wait_eintr (void *v) { atf_process_child_t child; atf_process_status_t status; struct sigaction sighup, old_sighup; #define RE_ABORT(expr) \ do { \ atf_error_t _aux_err = expr; \ if (atf_is_error(_aux_err)) { \ atf_error_free(_aux_err); \ abort(); \ } \ } while (0) { atf_process_stream_t outsb, errsb; RE_ABORT (atf_process_stream_init_capture (&outsb)); RE_ABORT (atf_process_stream_init_inherit (&errsb)); RE_ABORT (atf_process_fork (&child, child_loop, &outsb, &errsb, NULL)); atf_process_stream_fini (&outsb); atf_process_stream_fini (&errsb); } sighup.sa_handler = nop_signal; sigemptyset (&sighup.sa_mask); sighup.sa_flags = 0; if (sigaction (SIGHUP, &sighup, &old_sighup) == -1) abort (); printf ("waiting\n"); fflush (stdout); fprintf (stderr, "Child entering wait(2)\n"); atf_error_t err = atf_process_child_wait (&child, &status); fprintf (stderr, "Child's wait(2) terminated\n"); if (!atf_is_error (err)) { fprintf (stderr, "wait completed successfully (not interrupted)\n"); abort (); } if (!atf_error_is (err, "libc")) { fprintf (stderr, "wait did not raise libc_error\n"); abort (); } if (atf_libc_error_code (err) != EINTR) { fprintf (stderr, "libc_error is not EINTR\n"); abort (); } atf_error_free (err); sigaction (SIGHUP, &old_sighup, NULL); fprintf (stderr, "Child is killing subchild\n"); kill (atf_process_child_pid (&child), SIGTERM); RE_ABORT (atf_process_child_wait (&child, &status)); atf_process_status_fini (&status); #undef RE_ABORT exit (EXIT_SUCCESS); }
impl::basic_stream::~basic_stream(void) { if (m_inited) atf_process_stream_fini(&m_sb); }