David van Moolenbroek 00b67f09dd Import NetBSD named(8)
Also known as ISC bind.  This import adds utilities such as host(1),
dig(1), and nslookup(1), as well as many other tools and libraries.

Change-Id: I035ca46e64f1965d57019e773f4ff0ef035e4aa3
2017-03-21 22:00:06 +00:00

677 lines
17 KiB
C

/* $NetBSD: process.c,v 1.3 2014/12/10 04:38:03 christos Exp $ */
/*
* Automated Testing Framework (atf)
*
* 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 <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/error.h"
#include "process.h"
#include "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 "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;
}