#include <sys/resource.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <pwd.h>
#include <err.h>
#include <libprop/proplib.h>
#include "parser.h"
#include "testcase.h"
#include "runlist.h"
#include "config.h"
#include <dfregress.h>
prop_dictionary_t
testcase_from_struct(struct testcase *testcase)
{
int i, r;
prop_dictionary_t dict, testcase_dict;
prop_array_t a;
char *s;
testcase_dict = prop_dictionary_create();
if (testcase_dict == NULL)
err(1, "could not create testcase dict");
r = prop_dictionary_set_cstring(testcase_dict, "name", testcase->name);
if (r == 0)
err(1, "prop_dictionary operation failed");
r = prop_dictionary_set_cstring(testcase_dict, "type", testcase->type_str);
if (r == 0)
err(1, "prop_dictionary operation failed");
r = prop_dictionary_set_int32(testcase_dict, "argc",
(int32_t)testcase->argc);
if (r == 0)
err(1, "prop_dictionary operation failed");
a = prop_array_create_with_capacity(testcase->argc+1);
if (a == NULL)
err(1, "prop_array_create for argv failed");
s = strrchr(testcase->name, '/');
r = prop_array_set_cstring(a, 0, (s == NULL) ? testcase->name : s+1);
if (r == 0)
err(1, "prop_array_set_cstring operation failed");
for (i = 1; i <= testcase->argc; i++) {
r = prop_array_set_cstring(a, i, testcase->argv[i-1]);
if (r == 0)
err(1, "prop_array_set_cstring operation failed");
}
r = prop_dictionary_set(testcase_dict, "args", a);
if (r == 0)
err(1, "prop_dictionary_set \"args\" failed");
dict = prop_dictionary_create();
if (dict == NULL)
err(1, "could not create dict");
r = prop_dictionary_set_int32(dict, "timeout_in_secs",
(int32_t)testcase->opts.timeout_in_secs);
if (r == 0)
err(1, "prop_dictionary operation failed");
r = prop_dictionary_set_int32(dict, "rc",
(int32_t)testcase->opts.rc);
if (r == 0)
err(1, "prop_dictionary operation failed");
r = prop_dictionary_set_uint32(dict, "flags", testcase->opts.flags);
if (r == 0)
err(1, "prop_dictionary operation failed");
if (testcase->opts.pre_cmd != NULL) {
r = prop_dictionary_set_cstring(dict, "pre_cmd",
testcase->opts.pre_cmd);
if (r == 0)
err(1, "prop_dictionary operation failed");
}
if (testcase->opts.post_cmd != NULL) {
r = prop_dictionary_set_cstring(dict, "post_cmd",
testcase->opts.post_cmd);
if (r == 0)
err(1, "prop_dictionary operation failed");
}
if (testcase->opts.interpreter != NULL) {
r = prop_dictionary_set_cstring(dict, "interpreter",
testcase->opts.interpreter);
if (r == 0)
err(1, "prop_dictionary operation failed");
}
r = prop_dictionary_set_uint32(dict, "runas_uid",
(uint32_t)testcase->opts.runas_uid);
if (r == 0)
err(1, "prop_dictionary operation failed");
r = prop_dictionary_set_cstring(dict, "make_cmd",
(testcase->opts.make_cmd != NULL) ? testcase->opts.make_cmd : "make");
if (r == 0)
err(1, "prop_dictionary operation failed");
r = prop_dictionary_set(testcase_dict, "opts", dict);
if (r == 0)
err(1, "prop_dictionary operation failed");
return testcase_dict;
}
struct timeval *
testcase_get_timeout(prop_dictionary_t testcase)
{
static struct timeval tv;
int32_t val;
int r;
r = prop_dictionary_get_int32(prop_dictionary_get(testcase, "opts"),
"timeout_in_secs", &val);
if (r == 0)
err(1, "prop_dictionary operation failed");
tv.tv_usec = 0;
tv.tv_sec = (long)val;
return &tv;
}
int
testcase_get_type(prop_dictionary_t testcase)
{
const char *type;
int r;
r = prop_dictionary_get_cstring_nocopy(testcase, "type", &type);
if (r == 0)
err(1, "prop_dictionary operation failed");
if (strcmp(type, "userland") == 0)
return TESTCASE_TYPE_USERLAND;
else if (strcmp(type, "kernel") == 0)
return TESTCASE_TYPE_KERNEL;
else if (strcmp(type, "buildonly") == 0)
return TESTCASE_TYPE_BUILDONLY;
return 0;
}
const char *
testcase_get_type_desc(prop_dictionary_t testcase)
{
const char *str;
int r;
r = prop_dictionary_get_cstring_nocopy(testcase, "type", &str);
if (r == 0)
err(1, "prop_dictionary operation failed");
return str;
}
const char *
testcase_get_name(prop_dictionary_t testcase)
{
const char *str;
int r;
r = prop_dictionary_get_cstring_nocopy(testcase, "name", &str);
if (r == 0)
err(1, "prop_dictionary operation failed");
return str;
}
int
testcase_get_argc(prop_dictionary_t testcase)
{
int32_t argc;
int r;
r = prop_dictionary_get_int32(testcase, "argc", &argc);
if (r == 0)
err(1, "prop_dictionary operation failed for argc");
return argc;
}
const char **
testcase_get_args(prop_dictionary_t testcase)
{
static const char *argv[64];
unsigned int i, count;
prop_array_t a;
int r;
a = prop_dictionary_get(testcase, "args");
if (a == NULL)
err(1, "testcase_get_args NULL array");
count = prop_array_count(a);
for (i = 0; i < count; i++) {
r = prop_array_get_cstring_nocopy(a, i, &argv[i]);
if (r == 0)
err(1, "error building argv");
}
argv[i] = NULL;
return argv;
}
uint32_t
testcase_get_flags(prop_dictionary_t testcase)
{
uint32_t flags;
int r;
r = prop_dictionary_get_uint32(prop_dictionary_get(testcase, "opts"),
"flags", &flags);
if (r == 0)
err(1, "prop_dictionary operation failed");
return flags;
}
int
testcase_get_precmd_type(prop_dictionary_t testcase)
{
uint32_t flags = testcase_get_flags(testcase);
return (flags & (TESTCASE_INT_PRE | TESTCASE_CUSTOM_PRE));
}
int
testcase_get_rc(prop_dictionary_t testcase)
{
int32_t rc;
int r;
r = prop_dictionary_get_int32(prop_dictionary_get(testcase, "opts"),
"rc", &rc);
if (r == 0)
err(1, "prop_dictionary operation failed for rc");
return rc;
}
int
testcase_get_postcmd_type(prop_dictionary_t testcase)
{
uint32_t flags = testcase_get_flags(testcase);
return (flags & (TESTCASE_INT_POST | TESTCASE_CUSTOM_POST));
}
int
testcase_needs_setuid(prop_dictionary_t testcase)
{
uint32_t flags = testcase_get_flags(testcase);
return (flags & TESTCASE_RUN_AS);
}
uid_t
testcase_get_runas_uid(prop_dictionary_t testcase)
{
uint32_t uid = 0;
int r;
r = prop_dictionary_get_uint32(
prop_dictionary_get(testcase, "opts"), "runas_uid", &uid);
if (r == 0)
err(1, "prop_dictionary operation failed");
return (uid_t)uid;
}
const char *
testcase_get_custom_precmd(prop_dictionary_t testcase)
{
const char *str;
int r;
r = prop_dictionary_get_cstring_nocopy(
prop_dictionary_get(testcase, "opts"), "pre_cmd", &str);
if (r == 0)
err(1, "prop_dictionary operation failed");
return str;
}
const char *
testcase_get_custom_postcmd(prop_dictionary_t testcase)
{
const char *str;
int r;
r = prop_dictionary_get_cstring_nocopy(
prop_dictionary_get(testcase, "opts"), "pre_cmd", &str);
if (r == 0)
err(1, "prop_dictionary operation failed");
return str;
}
static const char *
_testcase_get_interpreter(prop_dictionary_t testcase, bool fatal)
{
const char *str;
int r;
r = prop_dictionary_get_cstring_nocopy(
prop_dictionary_get(testcase, "opts"), "interpreter", &str);
if (r == 0) {
if (fatal)
err(1, "prop_dictionary operation failed for interpreter");
else
return NULL;
}
return str;
}
const char *
testcase_get_interpreter(prop_dictionary_t testcase)
{
return _testcase_get_interpreter(testcase, true);
}
const char *
testcase_get_interpreter_noexit(prop_dictionary_t testcase)
{
return _testcase_get_interpreter(testcase, false);
}
const char *
testcase_get_make_cmd(prop_dictionary_t testcase)
{
const char *str;
int r;
r = prop_dictionary_get_cstring_nocopy(
prop_dictionary_get(testcase, "opts"), "make_cmd", &str);
if (r == 0)
err(1, "prop_dictionary operation failed");
return str;
}
prop_dictionary_t
testcase_get_result_dict(prop_dictionary_t testcase)
{
prop_dictionary_t result_dict;
int r;
result_dict = prop_dictionary_get(testcase, "result");
if (result_dict == NULL) {
result_dict = prop_dictionary_create();
if (result_dict == NULL)
err(1, "could not allocate new result dict");
r = prop_dictionary_set(testcase, "result", result_dict);
if (r == 0)
err(1, "prop_dictionary operation failed");
}
return result_dict;
}
int
testcase_set_build_buf(prop_dictionary_t testcase, const char *buf)
{
prop_dictionary_t dict = testcase_get_result_dict(testcase);
return !prop_dictionary_set_cstring(dict, "build_buf", buf);
}
int
testcase_set_cleanup_buf(prop_dictionary_t testcase, const char *buf)
{
prop_dictionary_t dict = testcase_get_result_dict(testcase);
return !prop_dictionary_set_cstring(dict, "cleanup_buf", buf);
}
int
testcase_set_sys_buf(prop_dictionary_t testcase, const char *buf)
{
prop_dictionary_t dict = testcase_get_result_dict(testcase);
return !prop_dictionary_set_cstring(dict, "sys_buf", buf);
}
int
testcase_set_precmd_buf(prop_dictionary_t testcase, const char *buf)
{
prop_dictionary_t dict = testcase_get_result_dict(testcase);
return !prop_dictionary_set_cstring(dict, "precmd_buf", buf);
}
int
testcase_set_postcmd_buf(prop_dictionary_t testcase, const char *buf)
{
prop_dictionary_t dict = testcase_get_result_dict(testcase);
return !prop_dictionary_set_cstring(dict, "postcmd_buf", buf);
}
int
testcase_set_stdout_buf(prop_dictionary_t testcase, const char *buf)
{
prop_dictionary_t dict = testcase_get_result_dict(testcase);
return !prop_dictionary_set_cstring(dict, "stdout_buf", buf);
}
int
testcase_set_stderr_buf(prop_dictionary_t testcase, const char *buf)
{
prop_dictionary_t dict = testcase_get_result_dict(testcase);
return !prop_dictionary_set_cstring(dict, "stderr_buf", buf);
}
int
testcase_set_result(prop_dictionary_t testcase, int result)
{
prop_dictionary_t dict = testcase_get_result_dict(testcase);
return !prop_dictionary_set_int32(dict, "result", result);
}
int
testcase_set_exit_value(prop_dictionary_t testcase, int exitval)
{
prop_dictionary_t dict = testcase_get_result_dict(testcase);
return !prop_dictionary_set_int32(dict, "exit_value", exitval);
}
int
testcase_set_signal(prop_dictionary_t testcase, int sig)
{
prop_dictionary_t dict = testcase_get_result_dict(testcase);
return !prop_dictionary_set_int32(dict, "signal", sig);
}
const char *
testcase_get_build_buf(prop_dictionary_t testcase)
{
const char *str = "";
prop_dictionary_t dict = testcase_get_result_dict(testcase);
prop_dictionary_get_cstring_nocopy(dict, "build_buf", &str);
return str;
}
const char *
testcase_get_cleanup_buf(prop_dictionary_t testcase)
{
const char *str = "";
prop_dictionary_t dict = testcase_get_result_dict(testcase);
prop_dictionary_get_cstring_nocopy(dict, "cleanup_buf", &str);
return str;
}
const char *
testcase_get_sys_buf(prop_dictionary_t testcase)
{
const char *str = "";
prop_dictionary_t dict = testcase_get_result_dict(testcase);
prop_dictionary_get_cstring_nocopy(dict, "sys_buf", &str);
return str;
}
const char *
testcase_get_precmd_buf(prop_dictionary_t testcase)
{
const char *str = "";
prop_dictionary_t dict = testcase_get_result_dict(testcase);
prop_dictionary_get_cstring_nocopy(dict, "precmd_buf", &str);
return str;
}
const char *
testcase_get_postcmd_buf(prop_dictionary_t testcase)
{
const char *str = "";
prop_dictionary_t dict = testcase_get_result_dict(testcase);
prop_dictionary_get_cstring_nocopy(dict, "postcmd_buf", &str);
return str;
}
const char *
testcase_get_stdout_buf(prop_dictionary_t testcase)
{
const char *str = "";
prop_dictionary_t dict = testcase_get_result_dict(testcase);
prop_dictionary_get_cstring_nocopy(dict, "stdout_buf", &str);
return str;
}
const char *
testcase_get_stderr_buf(prop_dictionary_t testcase)
{
const char *str = "";
prop_dictionary_t dict = testcase_get_result_dict(testcase);
prop_dictionary_get_cstring_nocopy(dict, "stderr_buf", &str);
return str;
}
int
testcase_get_result(prop_dictionary_t testcase)
{
int32_t result = RESULT_NOTRUN;
prop_dictionary_t dict = testcase_get_result_dict(testcase);
prop_dictionary_get_int32(dict, "result", &result);
return (int)result;
}
const char *
testcase_get_result_desc(prop_dictionary_t testcase)
{
int result = testcase_get_result(testcase);
switch(result) {
case RESULT_TIMEOUT: return "TIMEOUT";
case RESULT_SIGNALLED: return "SIGNALLED";
case RESULT_NOTRUN: return "NOT RUN";
case RESULT_FAIL: return "FAIL";
case RESULT_PASS: return "PASS";
case RESULT_PREFAIL: return "PREFAIL";
case RESULT_POSTFAIL: return "POSTFAIL";
case RESULT_BUILDFAIL: return "BUILDFAIL";
default: return "UNKNOWN";
}
}
int
testcase_get_exit_value(prop_dictionary_t testcase)
{
int32_t exitval;
int r;
prop_dictionary_t dict = testcase_get_result_dict(testcase);
r = prop_dictionary_get_int32(dict, "exit_value", &exitval);
if (r == 0)
err(1, "prop_dictionary operation failed");
return (int)exitval;
}
int
testcase_get_signal(prop_dictionary_t testcase)
{
int32_t sig;
int r;
prop_dictionary_t dict = testcase_get_result_dict(testcase);
r = prop_dictionary_get_int32(dict, "signal", &sig);
if (r == 0)
err(1, "prop_dictionary operation failed");
return (int)sig;
}
int
parse_testcase_option(struct testcase_options *opts, char *option)
{
struct passwd *pwd;
char *parameter, *endptr;
long lval;
int noparam = 0;
parameter = strchr(option, '=');
noparam = (parameter == NULL);
if (!noparam)
{
*parameter = '\0';
++parameter;
}
if (strcmp(option, "timeout") == 0) {
if (noparam)
syntax_error("The option 'timeout' needs a parameter");
lval = strtol(parameter, &endptr, 10);
if (*endptr != '\0')
syntax_error("The option 'timeout' expects an integer "
"parameter, not '%s'", parameter);
opts->timeout_in_secs = (long int)lval;
} else if (strcmp(option, "rc") == 0) {
if (noparam)
syntax_error("The option 'timeout' needs a parameter");
lval = strtol(parameter, &endptr, 10);
if (*endptr != '\0')
syntax_error("The option 'timeout' expects an integer "
"parameter, not '%s'", parameter);
opts->rc = (int)lval;
} else if (strcmp(option, "intpre") == 0) {
opts->flags |= TESTCASE_INT_PRE;
} else if (strcmp(option, "intpost") == 0) {
opts->flags |= TESTCASE_INT_POST;
} else if (strcmp(option, "pre") == 0) {
if (noparam)
syntax_error("The option 'pre' needs a parameter");
opts->flags |= TESTCASE_CUSTOM_PRE;
opts->pre_cmd = strdup(parameter);
} else if (strcmp(option, "post") == 0) {
if (noparam)
syntax_error("The option 'post' needs a parameter");
opts->flags |= TESTCASE_CUSTOM_POST;
opts->post_cmd = strdup(parameter);
} else if (strcmp(option, "runas") == 0) {
if (noparam)
syntax_error("The option 'runas' needs a parameter");
if ((pwd = getpwnam(parameter))) {
opts->runas_uid = pwd->pw_uid;
opts->flags |= TESTCASE_RUN_AS;
} else {
syntax_error("invalid user name for 'runas': %s",
parameter);
}
} else if (strcmp(option, "nobuild") == 0) {
opts->flags |= TESTCASE_NOBUILD;
} else if (strcmp(option, "interpreter") == 0) {
if (noparam)
syntax_error("The option 'interpreter' needs a parameter");
opts->interpreter = strdup(parameter);
} else if (strcmp(option, "make") == 0) {
if (noparam)
syntax_error("The option 'make' needs a parameter");
opts->make_cmd = strdup(parameter);
} else if (strcmp(option, "defaults") == 0) {
} else {
syntax_error("Unknown option: %s", option);
}
return 0;
}
void
testcase_entry_parser(void *arg, char **tokens)
{
prop_array_t runlist;
prop_dictionary_t testcase_dict;
struct testcase *testcase;
char *options[256];
int i, r, nopts;
runlist = (prop_array_t)arg;
testcase = malloc(sizeof(struct testcase));
if (testcase == NULL)
err(1, "could not malloc testcase memory");
bzero(testcase, sizeof(struct testcase));
entry_check_num_args(tokens, 3);
testcase->argv = &tokens[3];
for (testcase->argc = 0; testcase->argv[testcase->argc] != NULL;
testcase->argc++)
;
nopts = parse_options(tokens[2], options);
testcase->name = tokens[0];
if (strcmp(tokens[1], "userland") == 0) {
testcase->type = TESTCASE_TYPE_USERLAND;
} else if (strcmp(tokens[1], "kernel") == 0) {
testcase->type = TESTCASE_TYPE_KERNEL;
} else if (strcmp(tokens[1], "buildonly") == 0) {
testcase->type = TESTCASE_TYPE_BUILDONLY;
} else {
syntax_error("Unknown type: %s", tokens[1]);
}
testcase->type_str = tokens[1];
config_get_defaults(&testcase->opts);
for (i = 0; i < nopts; i++)
parse_testcase_option(&testcase->opts, options[i]);
if ((testcase->type != TESTCASE_TYPE_USERLAND) &&
(testcase->opts.flags & (TESTCASE_INT_PRE | TESTCASE_INT_POST)))
syntax_error("'intpre' and 'intpost' options are only valid "
"with testcase type 'userland'");
if ((testcase->type == TESTCASE_TYPE_BUILDONLY) &&
(testcase->opts.flags & TESTCASE_NOBUILD))
syntax_error("'nobuild' option is incompatible with type "
"'buildonly'");
testcase_dict = testcase_from_struct(testcase);
if (testcase->opts.pre_cmd != NULL)
free(testcase->opts.pre_cmd);
if (testcase->opts.post_cmd != NULL)
free(testcase->opts.post_cmd);
if (testcase->opts.interpreter != NULL)
free(testcase->opts.interpreter);
if (testcase->opts.make_cmd != NULL)
free(testcase->opts.make_cmd);
free(testcase);
r = prop_array_add(runlist, testcase_dict);
if (r == 0)
err(1, "prop_array_add failed");
}