/*
* Automated Testing Framework (atf)
*
* Copyright (c) 2008 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
*
* 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/resource.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <atf-c.h>
#include "process.h"
#include "sanity.h"
#include "test_helpers.h"
/* ---------------------------------------------------------------------
* Auxiliary functions for testing of 'atf_process_fork'.
* --------------------------------------------------------------------- */
/*
* Testing of atf_process_fork is quite messy. We want to be able to test
* all the possible combinations of stdout and stderr behavior to ensure
* that the streams are manipulated correctly.
*
* To do this, the do_fork function is a wrapper for atf_process_fork that
* issues stream-specific hooks before fork, while the child is running and
* after the child terminates. We then provide test cases that just call
* do_fork with different hooks.
*
* The hooks are described by base_stream, and we then have one *_stream
* type for ever possible stream behavior.
*/
struct base_stream {
void (*init)(void *);
void (*fini)(void *);
/* m_sb is initialized by subclasses that need it, but all consumers
* must use m_sb_ptr, which may or may not point to m_sb. This allows
* us to test the interface with a NULL value, which triggers a
* default behavior. */
};
static
void
{
switch (type) {
case stdout_type:
break;
case stderr_type:
break;
default:
}
}
struct capture_stream {
char *m_msg;
};
type) }
static
void
capture_stream_init(void *v)
{
struct capture_stream *s = v;
}
static
void
{
struct capture_stream *s = v;
case stdout_type:
break;
case stderr_type:
break;
default:
}
}
static
void
capture_stream_fini(void *v)
{
struct capture_stream *s = v;
case stdout_type:
break;
case stderr_type:
break;
default:
}
}
struct connect_stream {
int m_fd;
};
NULL, \
type) }
static
void
connect_stream_init(void *v)
{
struct connect_stream *s = v;
int src_fd;
case stdout_type:
break;
case stderr_type:
break;
default:
src_fd = -1;
}
}
static
void
connect_stream_fini(void *v)
{
struct connect_stream *s = v;
}
struct inherit_stream {
int m_fd;
int m_old_fd;
};
NULL, \
type) }
static
void
inherit_stream_init(void *v)
{
struct inherit_stream *s = v;
const char *name;
case stdout_type:
s->m_fd = STDOUT_FILENO;
name = "stdout";
break;
case stderr_type:
s->m_fd = STDERR_FILENO;
name = "stderr";
break;
default:
}
s->m_fd);
}
static
void
inherit_stream_fini(void *v)
{
struct inherit_stream *s = v;
}
NULL, \
type) }
static
void
default_stream_init(void *v)
{
struct inherit_stream *s = v;
}
static
void
default_stream_fini(void *v)
{
}
struct redirect_fd_stream {
int m_fd;
};
NULL, \
type) }
static
void
redirect_fd_stream_init(void *v)
{
struct redirect_fd_stream *s = v;
case stdout_type:
break;
case stderr_type:
break;
default:
}
}
static
void
redirect_fd_stream_fini(void *v)
{
struct redirect_fd_stream *s = v;
}
struct redirect_path_stream {
};
NULL, \
type) }
static
void
redirect_path_stream_init(void *v)
{
struct redirect_path_stream *s = v;
case stdout_type:
break;
case stderr_type:
break;
default:
}
}
static
void
redirect_path_stream_fini(void *v)
{
struct redirect_path_stream *s = v;
atf_fs_path_fini(&s->m_path);
}
static void child_print(void *) ATF_DEFS_ATTRIBUTE_NORETURN;
struct child_print_data {
const char *m_msg;
};
static
void
child_print(void *v)
{
}
static
void
{
}
/* ---------------------------------------------------------------------
* Test cases for the "stream" type.
* --------------------------------------------------------------------- */
{
"atf_process_stream_init_capture function");
}
{
}
{
"atf_process_stream_init_connect function");
}
{
}
{
"atf_process_stream_init_inherit function");
}
{
}
{
"atf_process_stream_init_redirect_fd function");
}
{
}
{
"atf_process_stream_init_redirect_path function");
}
{
}
/* ---------------------------------------------------------------------
* Test cases for the "status" type.
* --------------------------------------------------------------------- */
static void child_exit_success(void) ATF_DEFS_ATTRIBUTE_NORETURN;
static void child_exit_failure(void) ATF_DEFS_ATTRIBUTE_NORETURN;
static void child_sigkill(void) ATF_DEFS_ATTRIBUTE_NORETURN;
static void child_sigquit(void) ATF_DEFS_ATTRIBUTE_NORETURN;
static void child_sigterm(void) ATF_DEFS_ATTRIBUTE_NORETURN;
void
child_exit_success(void)
{
}
void
child_exit_failure(void)
{
}
void
child_sigkill(void)
{
abort();
}
void
child_sigquit(void)
{
abort();
}
void
child_sigterm(void)
{
abort();
}
static
int
{
int status;
if (pid == 0) {
status = 0; /* Silence compiler warnings */
child_func();
} else {
}
return status;
}
{
"that exit cleanly");
}
{
{
}
{
}
}
{
"that end due to a signal");
}
{
{
}
{
}
}
{
"that crash");
}
{
atf_tc_skip("Cannot unlimit the core file size; check limits "
"manually");
}
/* ---------------------------------------------------------------------
* Test cases for the "child" type.
* --------------------------------------------------------------------- */
static void child_report_pid(void *) ATF_DEFS_ATTRIBUTE_NORETURN;
static
void
{
abort();
}
{
"stored in the child type");
}
{
sizeof(pid));
}
static
void
{
for (;;)
sleep(1);
}
static
void
{
}
static
void
{
do { \
if (atf_is_error(_aux_err)) { \
abort(); \
} \
} while (0)
{
}
abort();
printf("waiting\n");
if (!atf_is_error(err)) {
abort();
}
abort();
}
abort();
}
}
{
"method by an external signal, and the return of "
"an EINTR error");
}
{
{
}
{
/* 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. */
printf("Waiting for child to issue wait(2)\n");
sizeof(buf)) > 0);
sleep(1);
}
printf("Interrupting child's wait(2) call\n");
printf("Waiting for child's completion\n");
}
/* ---------------------------------------------------------------------
* Tests cases for the free functions.
* --------------------------------------------------------------------- */
static
void
void (*prehook)(void))
{
}
static
void
{
}
{
}
{
}
{
}
{
{
}
{
}
}
static void
exit_early(void)
{
exit(80);
}
{
}
{
}
{
}
{
}
static
void
child_cookie(void *v)
{
if (v == NULL)
else
}
{
"a null and non-null data cookie");
}
{
{
}
{
int dummy_int;
}
}
{ \
} \
{ \
}
/* ---------------------------------------------------------------------
* Main.
* --------------------------------------------------------------------- */
{
/* Add the tests for the "stream" type. */
/* Add the tests for the "status" type. */
/* Add the tests for the "child" type. */
/* Add the tests for the free functions. */
return atf_no_error();
}