The unified diff between revisions [f48a12ad..] and [3ba2af4a..] is displayed below. It can also be downloaded as a raw diff.

This diff has been restricted to the following files: 'atf-c/process.c'

#
#
# patch "atf-c/process.c"
#  from [951565dc23d58942899eb2c68a9e9b9d1b48928e]
#    to [0abd2229aaa6a55aa99fb4ac23fd7f285862e12e]
#
============================================================
--- atf-c/process.c	951565dc23d58942899eb2c68a9e9b9d1b48928e
+++ atf-c/process.c	0abd2229aaa6a55aa99fb4ac23fd7f285862e12e
@@ -31,14 +31,20 @@
 #include <sys/wait.h>

 #include <errno.h>
+#include <fcntl.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>

+#include "atf-c/defs.h"
 #include "atf-c/error.h"
 #include "atf-c/process.h"
 #include "atf-c/sanity.h"

+/* This prototype is not in the header file because this is a private
+ * function; however, we need to access it during testing. */
+atf_error_t atf_process_status_init(atf_process_status_t *, int);
+
 /* ---------------------------------------------------------------------
  * The "child" error type.
  * --------------------------------------------------------------------- */
@@ -78,24 +84,440 @@ child_error(const char *cmd, int state)
 }

 /* ---------------------------------------------------------------------
+ * The "stream_prepare" auxiliary type.
+ * --------------------------------------------------------------------- */
+
+struct stream_prepare {
+    const atf_process_stream_t *m_sb;
+
+    bool m_pipefds_ok;
+    int m_pipefds[2];
+};
+typedef struct stream_prepare stream_prepare_t;
+
+static
+atf_error_t
+stream_prepare_init(stream_prepare_t *sp, const atf_process_stream_t *sb)
+{
+    atf_error_t err;
+
+    const int type = atf_process_stream_type(sb);
+
+    sp->m_sb = sb;
+    sp->m_pipefds_ok = false;
+
+    if (type == atf_process_stream_type_capture) {
+        if (pipe(sp->m_pipefds) == -1)
+            err = atf_libc_error(errno, "Failed to create pipe");
+        else {
+            err = atf_no_error();
+            sp->m_pipefds_ok = true;
+        }
+    } else
+        err = atf_no_error();
+
+    return err;
+}
+
+static
+void
+stream_prepare_fini(stream_prepare_t *sp)
+{
+    if (sp->m_pipefds_ok) {
+        close(sp->m_pipefds[0]);
+        close(sp->m_pipefds[1]);
+    }
+}
+
+/* ---------------------------------------------------------------------
+ * The "atf_process_stream" type.
+ * --------------------------------------------------------------------- */
+
+const int atf_process_stream_type_capture = 1;
+const int atf_process_stream_type_inherit = 2;
+const int atf_process_stream_type_redirect_fd = 3;
+const int atf_process_stream_type_redirect_path = 4;
+
+static
+bool
+stream_is_valid(const atf_process_stream_t *sb)
+{
+    return (sb->m_type == atf_process_stream_type_capture) ||
+           (sb->m_type == atf_process_stream_type_inherit) ||
+           (sb->m_type == atf_process_stream_type_redirect_fd) ||
+           (sb->m_type == atf_process_stream_type_redirect_path);
+}
+
+atf_error_t
+atf_process_stream_init_capture(atf_process_stream_t *sb)
+{
+    atf_object_init(&sb->m_object);
+
+    sb->m_type = atf_process_stream_type_capture;
+
+    POST(stream_is_valid(sb));
+    return atf_no_error();
+}
+
+atf_error_t
+atf_process_stream_init_inherit(atf_process_stream_t *sb)
+{
+    atf_object_init(&sb->m_object);
+
+    sb->m_type = atf_process_stream_type_inherit;
+
+    POST(stream_is_valid(sb));
+    return atf_no_error();
+}
+
+atf_error_t
+atf_process_stream_init_redirect_fd(atf_process_stream_t *sb,
+                                    const int fd)
+{
+    atf_object_init(&sb->m_object);
+
+    sb->m_type = atf_process_stream_type_redirect_fd;
+    sb->m_fd = fd;
+
+    POST(stream_is_valid(sb));
+    return atf_no_error();
+}
+
+atf_error_t
+atf_process_stream_init_redirect_path(atf_process_stream_t *sb,
+                                      const atf_fs_path_t *path)
+{
+    atf_object_init(&sb->m_object);
+
+    sb->m_type = atf_process_stream_type_redirect_path;
+    sb->m_path = path;
+
+    POST(stream_is_valid(sb));
+    return atf_no_error();
+}
+
+void
+atf_process_stream_fini(atf_process_stream_t *sb)
+{
+    PRE(stream_is_valid(sb));
+
+    atf_object_fini(&sb->m_object);
+}
+
+int
+atf_process_stream_type(const atf_process_stream_t *sb)
+{
+    PRE(stream_is_valid(sb));
+
+    return sb->m_type;
+}
+
+/* ---------------------------------------------------------------------
+ * The "atf_process_status" type.
+ * --------------------------------------------------------------------- */
+
+atf_error_t
+atf_process_status_init(atf_process_status_t *s, int status)
+{
+    atf_object_init(&s->m_object);
+
+    s->m_status = status;
+
+    return atf_no_error();
+}
+
+void
+atf_process_status_fini(atf_process_status_t *s)
+{
+    atf_object_fini(&s->m_object);
+}
+
+bool
+atf_process_status_exited(const atf_process_status_t *s)
+{
+    int mutable_status = s->m_status;
+    return WIFEXITED(mutable_status);
+}
+
+int
+atf_process_status_exitstatus(const atf_process_status_t *s)
+{
+    PRE(atf_process_status_exited(s));
+    int mutable_status = s->m_status;
+    return WEXITSTATUS(mutable_status);
+}
+
+bool
+atf_process_status_signaled(const atf_process_status_t *s)
+{
+    int mutable_status = s->m_status;
+    return WIFSIGNALED(mutable_status);
+}
+
+int
+atf_process_status_termsig(const atf_process_status_t *s)
+{
+    PRE(atf_process_status_signaled(s));
+    int mutable_status = s->m_status;
+    return WTERMSIG(mutable_status);
+}
+
+bool
+atf_process_status_coredump(const atf_process_status_t *s)
+{
+    PRE(atf_process_status_signaled(s));
+#if defined(WCOREDUMP)
+    int mutable_status = s->m_status;
+    return WCOREDUMP(mutable_status);
+#else
+    return false;
+#endif
+}
+
+/* ---------------------------------------------------------------------
+ * The "atf_process_child" type.
+ * --------------------------------------------------------------------- */
+
+static
+atf_error_t
+atf_process_child_init(atf_process_child_t *c)
+{
+    atf_object_init(&c->m_object);
+
+    c->m_pid = 0;
+    c->m_stdout = -1;
+    c->m_stderr = -1;
+
+    return atf_no_error();
+}
+
+static
+void
+atf_process_child_fini(atf_process_child_t *c)
+{
+    atf_object_fini(&c->m_object);
+}
+
+atf_error_t
+atf_process_child_wait(atf_process_child_t *c, atf_process_status_t *s)
+{
+    atf_error_t err;
+    int status;
+
+    if (waitpid(c->m_pid, &status, 0) == -1)
+        err = atf_libc_error(errno, "Failed waiting for process %d",
+                             c->m_pid);
+    else {
+        atf_process_child_fini(c);
+        err = atf_process_status_init(s, status);
+    }
+
+    return err;
+}
+
+int
+atf_process_child_stdout(atf_process_child_t *c)
+{
+    PRE(c->m_stdout != -1);
+    return c->m_stdout;
+}
+
+int
+atf_process_child_stderr(atf_process_child_t *c)
+{
+    PRE(c->m_stderr != -1);
+    return c->m_stderr;
+}
+
+/* ---------------------------------------------------------------------
  * Free functions.
  * --------------------------------------------------------------------- */

+static
 atf_error_t
-atf_process_fork(pid_t *outpid)
+safe_dup(const int oldfd, const int newfd)
 {
     atf_error_t err;
+
+    if (oldfd != newfd) {
+        if (dup2(oldfd, newfd) == -1) {
+            err = atf_libc_error(errno, "Could not allocate file descriptor");
+        } else {
+            close(oldfd);
+            err = atf_no_error();
+        }
+    } else
+        err = atf_no_error();
+
+    return err;
+}
+
+static
+atf_error_t
+child_connect(const stream_prepare_t *sp, int procfd)
+{
+    atf_error_t err;
+    const int type = atf_process_stream_type(sp->m_sb);
+
+    if (type == atf_process_stream_type_capture) {
+        close(sp->m_pipefds[0]);
+        err = safe_dup(sp->m_pipefds[1], procfd);
+    } else if (type == atf_process_stream_type_inherit) {
+        err = atf_no_error();
+    } else if (type == atf_process_stream_type_redirect_fd) {
+        err = safe_dup(sp->m_sb->m_fd, procfd);
+    } else if (type == atf_process_stream_type_redirect_path) {
+        int aux = open(atf_fs_path_cstring(sp->m_sb->m_path),
+                       O_WRONLY | O_CREAT | O_TRUNC, 0644);
+        if (aux == -1)
+            err = atf_libc_error(errno, "Could not create %s",
+                                 atf_fs_path_cstring(sp->m_sb->m_path));
+        else {
+            err = safe_dup(aux, procfd);
+            if (atf_is_error(err))
+                close(aux);
+        }
+    } else {
+        UNREACHABLE;
+        err = atf_no_error();
+    }
+
+    return err;
+}
+
+static
+void
+parent_connect(const stream_prepare_t *sp, int *fd)
+{
+    const int type = atf_process_stream_type(sp->m_sb);
+
+    if (type == atf_process_stream_type_capture) {
+        close(sp->m_pipefds[1]);
+        *fd = sp->m_pipefds[0];
+    } else if (type == atf_process_stream_type_inherit) {
+        /* Do nothing. */
+    } else if (type == atf_process_stream_type_redirect_fd) {
+        /* Do nothing. */
+    } else if (type == atf_process_stream_type_redirect_path) {
+        /* Do nothing. */
+    } else {
+        UNREACHABLE;
+    }
+}
+
+static
+atf_error_t
+do_parent(atf_process_child_t *c,
+          const pid_t pid,
+          const stream_prepare_t *outsp,
+          const stream_prepare_t *errsp)
+{
+    atf_error_t err;
+
+    err = atf_process_child_init(c);
+    if (atf_is_error(err))
+        goto out;
+
+    c->m_pid = pid;
+
+    parent_connect(outsp, &c->m_stdout);
+    parent_connect(errsp, &c->m_stderr);
+
+out:
+    return err;
+}
+
+static
+void
+do_child(atf_error_t (*)(const void *),
+         const void *,
+         const stream_prepare_t *,
+         const stream_prepare_t *) ATF_DEFS_ATTRIBUTE_NORETURN;
+
+static
+void
+do_child(atf_error_t (*start)(const void *),
+         const void *v,
+         const stream_prepare_t *outsp,
+         const stream_prepare_t *errsp)
+{
+    atf_error_t err;
+
+    atf_reset_exit_checks();
+    err = child_connect(outsp, STDOUT_FILENO);
+    if (atf_is_error(err))
+        goto out;
+
+    err = child_connect(errsp, STDERR_FILENO);
+    if (atf_is_error(err))
+        goto out;
+
+    err = start(v);
+
+out:
+    if (atf_is_error(err)) {
+        char buf[1024];
+
+        atf_error_format(err, buf, sizeof(buf));
+        fprintf(stderr, "Unhandled error: %s\n", buf);
+        atf_error_free(err);
+
+        exit(EXIT_FAILURE);
+    } else
+        exit(EXIT_SUCCESS);
+}
+
+atf_error_t
+atf_process_oldfork(pid_t *pid)
+{
+    *pid = fork();
+    return atf_no_error();
+}
+
+atf_error_t
+atf_process_fork(atf_process_child_t *c,
+                 atf_error_t (*start)(const void *),
+                 const atf_process_stream_t *outsb,
+                 const atf_process_stream_t *errsb,
+                 const void *v)
+{
+    atf_error_t err;
+    stream_prepare_t outsp;
+    stream_prepare_t errsp;
     pid_t pid;

+    err = stream_prepare_init(&outsp, outsb);
+    if (atf_is_error(err))
+        goto out;
+
+    err = stream_prepare_init(&errsp, errsb);
+    if (atf_is_error(err))
+        goto err_outpipe;
+
     pid = fork();
     if (pid == -1) {
         err = atf_libc_error(errno, "Failed to fork");
-        goto out;
+        goto err_errpipe;
     }

-    *outpid = pid;
-    err = atf_no_error();
+    if (pid == 0) {
+        do_child(start, v, &outsp, &errsp);
+        UNREACHABLE;
+        abort();
+        err = atf_no_error();
+    } else {
+        err = do_parent(c, pid, &outsp, &errsp);
+        if (atf_is_error(err))
+            goto err_errpipe;
+    }

+    goto out;
+
+err_errpipe:
+    stream_prepare_fini(&errsp);
+err_outpipe:
+    stream_prepare_fini(&outsp);
+
 out:
     return err;
 }