summaryrefslogtreecommitdiffstats
path: root/unit/atf-src/atf-c/detail/process.c
diff options
context:
space:
mode:
Diffstat (limited to 'unit/atf-src/atf-c/detail/process.c')
-rw-r--r--unit/atf-src/atf-c/detail/process.c670
1 files changed, 670 insertions, 0 deletions
diff --git a/unit/atf-src/atf-c/detail/process.c b/unit/atf-src/atf-c/detail/process.c
new file mode 100644
index 0000000..8e08b6c
--- /dev/null
+++ b/unit/atf-src/atf-c/detail/process.c
@@ -0,0 +1,670 @@
+/* Copyright (c) 2007 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
+
+#include "atf-c/detail/process.h"
+
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "atf-c/defs.h"
+#include "atf-c/detail/sanity.h"
+#include "atf-c/error.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 "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_connect = 2;
+const int atf_process_stream_type_inherit = 3;
+const int atf_process_stream_type_redirect_fd = 4;
+const int atf_process_stream_type_redirect_path = 5;
+
+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_connect) ||
+ (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)
+{
+ sb->m_type = atf_process_stream_type_capture;
+
+ POST(stream_is_valid(sb));
+ return atf_no_error();
+}
+
+atf_error_t
+atf_process_stream_init_connect(atf_process_stream_t *sb,
+ const int src_fd, const int tgt_fd)
+{
+ PRE(src_fd >= 0);
+ PRE(tgt_fd >= 0);
+ PRE(src_fd != tgt_fd);
+
+ sb->m_type = atf_process_stream_type_connect;
+ sb->m_src_fd = src_fd;
+ sb->m_tgt_fd = tgt_fd;
+
+ POST(stream_is_valid(sb));
+ return atf_no_error();
+}
+
+atf_error_t
+atf_process_stream_init_inherit(atf_process_stream_t *sb)
+{
+ 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)
+{
+ 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)
+{
+ 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));
+}
+
+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)
+{
+ s->m_status = status;
+
+ return atf_no_error();
+}
+
+void
+atf_process_status_fini(atf_process_status_t *s ATF_DEFS_ATTRIBUTE_UNUSED)
+{
+}
+
+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)
+{
+ 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)
+{
+ if (c->m_stdout != -1)
+ close(c->m_stdout);
+ if (c->m_stderr != -1)
+ close(c->m_stderr);
+}
+
+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;
+}
+
+pid_t
+atf_process_child_pid(const atf_process_child_t *c)
+{
+ return c->m_pid;
+}
+
+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
+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_connect) {
+ if (dup2(sp->m_sb->m_tgt_fd, sp->m_sb->m_src_fd) == -1)
+ err = atf_libc_error(errno, "Cannot connect descriptor %d to %d",
+ sp->m_sb->m_tgt_fd, sp->m_sb->m_src_fd);
+ else
+ err = atf_no_error();
+ } 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_connect) {
+ /* Do nothing. */
+ } 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(void (*)(void *),
+ void *,
+ const stream_prepare_t *,
+ const stream_prepare_t *) ATF_DEFS_ATTRIBUTE_NORETURN;
+
+static
+void
+do_child(void (*start)(void *),
+ void *v,
+ const stream_prepare_t *outsp,
+ const stream_prepare_t *errsp)
+{
+ atf_error_t err;
+
+ 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;
+
+ start(v);
+ UNREACHABLE;
+
+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);
+}
+
+static
+atf_error_t
+fork_with_streams(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;
+ 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 err_errpipe;
+ }
+
+ 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;
+}
+
+static
+atf_error_t
+init_stream_w_default(const atf_process_stream_t *usersb,
+ atf_process_stream_t *inheritsb,
+ const atf_process_stream_t **realsb)
+{
+ atf_error_t err;
+
+ if (usersb == NULL) {
+ err = atf_process_stream_init_inherit(inheritsb);
+ if (!atf_is_error(err))
+ *realsb = inheritsb;
+ } else {
+ err = atf_no_error();
+ *realsb = usersb;
+ }
+
+ return err;
+}
+
+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;
+}
+
+static
+int
+const_execvp(const char *file, const char *const *argv)
+{
+#define UNCONST(a) ((void *)(unsigned long)(const void *)(a))
+ return execvp(file, UNCONST(argv));
+#undef UNCONST
+}
+
+static
+atf_error_t
+list_to_array(const atf_list_t *l, const char ***ap)
+{
+ atf_error_t err;
+ const char **a;
+
+ a = (const char **)malloc((atf_list_size(l) + 1) * sizeof(const char *));
+ if (a == NULL)
+ err = atf_no_memory_error();
+ else {
+ const char **aiter;
+ atf_list_citer_t liter;
+
+ aiter = a;
+ atf_list_for_each_c(liter, l) {
+ *aiter = (const char *)atf_list_citer_data(liter);
+ aiter++;
+ }
+ *aiter = NULL;
+
+ err = atf_no_error();
+ *ap = a;
+ }
+
+ return err;
+}
+
+struct exec_args {
+ const atf_fs_path_t *m_prog;
+ const char *const *m_argv;
+ void (*m_prehook)(void);
+};
+
+static
+void
+do_exec(void *v)
+{
+ struct exec_args *ea = v;
+
+ if (ea->m_prehook != NULL)
+ ea->m_prehook();
+
+ const int ret = const_execvp(atf_fs_path_cstring(ea->m_prog), ea->m_argv);
+ const int errnocopy = errno;
+ INV(ret == -1);
+ fprintf(stderr, "exec(%s) failed: %s\n",
+ atf_fs_path_cstring(ea->m_prog), strerror(errnocopy));
+ exit(EXIT_FAILURE);
+}
+
+atf_error_t
+atf_process_exec_array(atf_process_status_t *s,
+ const atf_fs_path_t *prog,
+ const char *const *argv,
+ const atf_process_stream_t *outsb,
+ const atf_process_stream_t *errsb,
+ void (*prehook)(void))
+{
+ atf_error_t err;
+ atf_process_child_t c;
+ struct exec_args ea = { prog, argv, prehook };
+
+ PRE(outsb == NULL ||
+ atf_process_stream_type(outsb) != atf_process_stream_type_capture);
+ PRE(errsb == NULL ||
+ atf_process_stream_type(errsb) != atf_process_stream_type_capture);
+
+ err = atf_process_fork(&c, do_exec, outsb, errsb, &ea);
+ if (atf_is_error(err))
+ goto out;
+
+again:
+ err = atf_process_child_wait(&c, s);
+ if (atf_is_error(err)) {
+ INV(atf_error_is(err, "libc") && atf_libc_error_code(err) == EINTR);
+ atf_error_free(err);
+ goto again;
+ }
+
+out:
+ return err;
+}
+
+atf_error_t
+atf_process_exec_list(atf_process_status_t *s,
+ const atf_fs_path_t *prog,
+ const atf_list_t *argv,
+ const atf_process_stream_t *outsb,
+ const atf_process_stream_t *errsb,
+ void (*prehook)(void))
+{
+ atf_error_t err;
+ const char **argv2;
+
+ PRE(outsb == NULL ||
+ atf_process_stream_type(outsb) != atf_process_stream_type_capture);
+ PRE(errsb == NULL ||
+ atf_process_stream_type(errsb) != atf_process_stream_type_capture);
+
+ argv2 = NULL; /* Silence GCC warning. */
+ err = list_to_array(argv, &argv2);
+ if (atf_is_error(err))
+ goto out;
+
+ err = atf_process_exec_array(s, prog, argv2, outsb, errsb, prehook);
+
+ free(argv2);
+out:
+ return err;
+}