You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
715 lines
18 KiB
715 lines
18 KiB
#include "settings.h"
|
|
|
|
#include <ctype.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <getopt.h>
|
|
#include <libgen.h>
|
|
#include <limits.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
|
|
enum {
|
|
OPT_ABORT_ON_ERROR,
|
|
OPT_TEST_LIST,
|
|
OPT_IGNORE_MISSING,
|
|
OPT_PIGLIT_DMESG,
|
|
OPT_DMESG_WARN_LEVEL,
|
|
OPT_OVERALL_TIMEOUT,
|
|
OPT_HELP = 'h',
|
|
OPT_NAME = 'n',
|
|
OPT_DRY_RUN = 'd',
|
|
OPT_INCLUDE = 't',
|
|
OPT_EXCLUDE = 'x',
|
|
OPT_SYNC = 's',
|
|
OPT_LOG_LEVEL = 'l',
|
|
OPT_OVERWRITE = 'o',
|
|
OPT_MULTIPLE = 'm',
|
|
OPT_TIMEOUT = 'c',
|
|
OPT_WATCHDOG = 'g',
|
|
OPT_BLACKLIST = 'b',
|
|
OPT_LIST_ALL = 'L',
|
|
};
|
|
|
|
static struct {
|
|
int level;
|
|
const char *name;
|
|
} log_levels[] = {
|
|
{ LOG_LEVEL_NORMAL, "normal" },
|
|
{ LOG_LEVEL_QUIET, "quiet" },
|
|
{ LOG_LEVEL_VERBOSE, "verbose" },
|
|
{ 0, 0 },
|
|
};
|
|
|
|
static struct {
|
|
int value;
|
|
const char *name;
|
|
} abort_conditions[] = {
|
|
{ ABORT_TAINT, "taint" },
|
|
{ ABORT_LOCKDEP, "lockdep" },
|
|
{ ABORT_ALL, "all" },
|
|
{ 0, 0 },
|
|
};
|
|
|
|
static bool set_log_level(struct settings* settings, const char *level)
|
|
{
|
|
typeof(*log_levels) *it;
|
|
|
|
for (it = log_levels; it->name; it++) {
|
|
if (!strcmp(level, it->name)) {
|
|
settings->log_level = it->level;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static bool set_abort_condition(struct settings* settings, const char *cond)
|
|
{
|
|
typeof(*abort_conditions) *it;
|
|
|
|
if (!cond) {
|
|
settings->abort_mask = ABORT_ALL;
|
|
return true;
|
|
}
|
|
|
|
if (strlen(cond) == 0) {
|
|
settings->abort_mask = 0;
|
|
return true;
|
|
}
|
|
|
|
for (it = abort_conditions; it->name; it++) {
|
|
if (!strcmp(cond, it->name)) {
|
|
settings->abort_mask |= it->value;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static bool parse_abort_conditions(struct settings *settings, const char *optarg)
|
|
{
|
|
char *dup, *origdup, *p;
|
|
if (!optarg)
|
|
return set_abort_condition(settings, NULL);
|
|
|
|
origdup = dup = strdup(optarg);
|
|
while (dup) {
|
|
if ((p = strchr(dup, ',')) != NULL) {
|
|
*p = '\0';
|
|
p++;
|
|
}
|
|
|
|
if (!set_abort_condition(settings, dup)) {
|
|
free(origdup);
|
|
return false;
|
|
}
|
|
|
|
dup = p;
|
|
}
|
|
|
|
free(origdup);
|
|
|
|
return true;
|
|
}
|
|
|
|
static const char *usage_str =
|
|
"usage: runner [options] [test_root] results-path\n"
|
|
" or: runner --list-all [options] [test_root]\n\n"
|
|
"Options:\n"
|
|
" Piglit compatible:\n"
|
|
" -h, --help Show this help message and exit\n"
|
|
" -n <test name>, --name <test name>\n"
|
|
" Name of this test run\n"
|
|
" -d, --dry-run Do not execute the tests\n"
|
|
" -t <regex>, --include-tests <regex>\n"
|
|
" Run only matching tests (can be used more than once)\n"
|
|
" -x <regex>, --exclude-tests <regex>\n"
|
|
" Exclude matching tests (can be used more than once)\n"
|
|
" --abort-on-monitored-error[=list]\n"
|
|
" Abort execution when a fatal condition is detected.\n"
|
|
" A comma-separated list of conditions to check can be\n"
|
|
" given. If not given, all conditions are checked. An\n"
|
|
" empty string as a condition disables aborting\n"
|
|
" Possible conditions:\n"
|
|
" lockdep - abort when kernel lockdep has been angered.\n"
|
|
" taint - abort when kernel becomes fatally tainted.\n"
|
|
" all - abort for all of the above.\n"
|
|
" -s, --sync Sync results to disk after every test\n"
|
|
" -l {quiet,verbose,dummy}, --log-level {quiet,verbose,dummy}\n"
|
|
" Set the logger verbosity level\n"
|
|
" --test-list TEST_LIST\n"
|
|
" A file containing a list of tests to run\n"
|
|
" -o, --overwrite If the results-path already exists, delete it\n"
|
|
" --ignore-missing Ignored but accepted, for piglit compatibility\n"
|
|
"\n"
|
|
" Incompatible options:\n"
|
|
" -m, --multiple-mode Run multiple subtests in the same binary execution.\n"
|
|
" If a testlist file is given, consecutive subtests are\n"
|
|
" run in the same execution if they are from the same\n"
|
|
" binary. Note that in that case relative ordering of the\n"
|
|
" subtest execution is dictated by the test binary, not\n"
|
|
" the testlist\n"
|
|
" --inactivity-timeout <seconds>\n"
|
|
" Kill the running test after <seconds> of inactivity in\n"
|
|
" the test's stdout, stderr, or dmesg\n"
|
|
" --overall-timeout <seconds>\n"
|
|
" Don't execute more tests after <seconds> has elapsed\n"
|
|
" --use-watchdog Use hardware watchdog for lethal enforcement of the\n"
|
|
" above timeout. Killing the test process is still\n"
|
|
" attempted at timeout trigger.\n"
|
|
" --dmesg-warn-level <level>\n"
|
|
" Messages with log level equal or lower (more serious)\n"
|
|
" to the given one will override the test result to\n"
|
|
" dmesg-warn/dmesg-fail, assuming they go through filtering.\n"
|
|
" Defaults to 4 (KERN_WARNING).\n"
|
|
" --piglit-style-dmesg Filter dmesg like piglit does. Piglit considers matches\n"
|
|
" against a short filter list to mean the test result\n"
|
|
" should be changed to dmesg-warn/dmesg-fail. Without\n"
|
|
" this option everything except matches against a\n"
|
|
" (longer) filter list means the test result should\n"
|
|
" change. KERN_NOTICE dmesg level is treated as warn,\n"
|
|
" unless overridden with --dmesg-warn-level.\n"
|
|
" -b, --blacklist FILENAME\n"
|
|
" Exclude all test matching to regexes from FILENAME\n"
|
|
" (can be used more than once)\n"
|
|
" -L, --list-all List all matching subtests instead of running\n"
|
|
" [test_root] Directory that contains the IGT tests. The environment\n"
|
|
" variable IGT_TEST_ROOT will be used if set, overriding\n"
|
|
" this option if given.\n"
|
|
;
|
|
|
|
static void usage(const char *extra_message, FILE *f)
|
|
{
|
|
if (extra_message)
|
|
fprintf(f, "%s\n\n", extra_message);
|
|
|
|
fputs(usage_str, f);
|
|
}
|
|
|
|
static bool add_regex(struct regex_list *list, char *new)
|
|
{
|
|
GRegex *regex;
|
|
GError *error = NULL;
|
|
|
|
regex = g_regex_new(new, G_REGEX_OPTIMIZE, 0, &error);
|
|
if (error) {
|
|
char *buf = malloc(snprintf(NULL, 0, "Invalid regex '%s': %s", new, error->message) + 1);
|
|
|
|
sprintf(buf, "Invalid regex '%s': %s", new, error->message);
|
|
usage(buf, stderr);
|
|
|
|
free(buf);
|
|
g_error_free(error);
|
|
return false;
|
|
}
|
|
|
|
list->regexes = realloc(list->regexes,
|
|
(list->size + 1) * sizeof(*list->regexes));
|
|
list->regex_strings = realloc(list->regex_strings,
|
|
(list->size + 1) * sizeof(*list->regex_strings));
|
|
list->regexes[list->size] = regex;
|
|
list->regex_strings[list->size] = new;
|
|
list->size++;
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool parse_blacklist(struct regex_list *exclude_regexes,
|
|
char *blacklist_filename)
|
|
{
|
|
FILE *f;
|
|
char *line = NULL;
|
|
size_t line_len = 0;
|
|
bool status = false;
|
|
|
|
if ((f = fopen(blacklist_filename, "r")) == NULL) {
|
|
fprintf(stderr, "Cannot open blacklist file %s\n", blacklist_filename);
|
|
return false;
|
|
}
|
|
while (1) {
|
|
size_t str_size = 0, idx = 0;
|
|
|
|
if (getline(&line, &line_len, f) == -1) {
|
|
if (errno == EINTR)
|
|
continue;
|
|
else
|
|
break;
|
|
}
|
|
|
|
while (true) {
|
|
if (line[idx] == '\n' ||
|
|
line[idx] == '\0' ||
|
|
line[idx] == '#') /* # starts a comment */
|
|
break;
|
|
if (!isspace(line[idx]))
|
|
str_size = idx + 1;
|
|
idx++;
|
|
}
|
|
if (str_size > 0) {
|
|
char *test_regex = strndup(line, str_size);
|
|
|
|
status = add_regex(exclude_regexes, test_regex);
|
|
if (!status)
|
|
break;
|
|
}
|
|
}
|
|
|
|
free(line);
|
|
fclose(f);
|
|
return status;
|
|
}
|
|
|
|
static void free_regexes(struct regex_list *regexes)
|
|
{
|
|
size_t i;
|
|
|
|
for (i = 0; i < regexes->size; i++) {
|
|
free(regexes->regex_strings[i]);
|
|
g_regex_unref(regexes->regexes[i]);
|
|
}
|
|
free(regexes->regex_strings);
|
|
free(regexes->regexes);
|
|
}
|
|
|
|
static bool readable_file(char *filename)
|
|
{
|
|
return !access(filename, R_OK);
|
|
}
|
|
|
|
void init_settings(struct settings *settings)
|
|
{
|
|
memset(settings, 0, sizeof(*settings));
|
|
}
|
|
|
|
void free_settings(struct settings *settings)
|
|
{
|
|
free(settings->test_list);
|
|
free(settings->name);
|
|
free(settings->test_root);
|
|
free(settings->results_path);
|
|
|
|
free_regexes(&settings->include_regexes);
|
|
free_regexes(&settings->exclude_regexes);
|
|
|
|
init_settings(settings);
|
|
}
|
|
|
|
bool parse_options(int argc, char **argv,
|
|
struct settings *settings)
|
|
{
|
|
int c;
|
|
char *env_test_root;
|
|
|
|
static struct option long_options[] = {
|
|
{"help", no_argument, NULL, OPT_HELP},
|
|
{"name", required_argument, NULL, OPT_NAME},
|
|
{"dry-run", no_argument, NULL, OPT_DRY_RUN},
|
|
{"include-tests", required_argument, NULL, OPT_INCLUDE},
|
|
{"exclude-tests", required_argument, NULL, OPT_EXCLUDE},
|
|
{"abort-on-monitored-error", optional_argument, NULL, OPT_ABORT_ON_ERROR},
|
|
{"sync", no_argument, NULL, OPT_SYNC},
|
|
{"log-level", required_argument, NULL, OPT_LOG_LEVEL},
|
|
{"test-list", required_argument, NULL, OPT_TEST_LIST},
|
|
{"overwrite", no_argument, NULL, OPT_OVERWRITE},
|
|
{"ignore-missing", no_argument, NULL, OPT_IGNORE_MISSING},
|
|
{"multiple-mode", no_argument, NULL, OPT_MULTIPLE},
|
|
{"inactivity-timeout", required_argument, NULL, OPT_TIMEOUT},
|
|
{"overall-timeout", required_argument, NULL, OPT_OVERALL_TIMEOUT},
|
|
{"use-watchdog", no_argument, NULL, OPT_WATCHDOG},
|
|
{"piglit-style-dmesg", no_argument, NULL, OPT_PIGLIT_DMESG},
|
|
{"dmesg-warn-level", required_argument, NULL, OPT_DMESG_WARN_LEVEL},
|
|
{"blacklist", required_argument, NULL, OPT_BLACKLIST},
|
|
{"list-all", no_argument, NULL, OPT_LIST_ALL},
|
|
{ 0, 0, 0, 0},
|
|
};
|
|
|
|
free_settings(settings);
|
|
|
|
optind = 1;
|
|
|
|
settings->dmesg_warn_level = -1;
|
|
|
|
while ((c = getopt_long(argc, argv, "hn:dt:x:sl:omb:L",
|
|
long_options, NULL)) != -1) {
|
|
switch (c) {
|
|
case OPT_HELP:
|
|
usage(NULL, stdout);
|
|
goto error;
|
|
case OPT_NAME:
|
|
settings->name = strdup(optarg);
|
|
break;
|
|
case OPT_DRY_RUN:
|
|
settings->dry_run = true;
|
|
break;
|
|
case OPT_INCLUDE:
|
|
if (!add_regex(&settings->include_regexes, strdup(optarg)))
|
|
goto error;
|
|
break;
|
|
case OPT_EXCLUDE:
|
|
if (!add_regex(&settings->exclude_regexes, strdup(optarg)))
|
|
goto error;
|
|
break;
|
|
case OPT_ABORT_ON_ERROR:
|
|
if (!parse_abort_conditions(settings, optarg))
|
|
goto error;
|
|
break;
|
|
case OPT_SYNC:
|
|
settings->sync = true;
|
|
break;
|
|
case OPT_LOG_LEVEL:
|
|
if (!set_log_level(settings, optarg)) {
|
|
usage("Cannot parse log level", stderr);
|
|
goto error;
|
|
}
|
|
break;
|
|
case OPT_TEST_LIST:
|
|
settings->test_list = absolute_path(optarg);
|
|
break;
|
|
case OPT_OVERWRITE:
|
|
settings->overwrite = true;
|
|
break;
|
|
case OPT_IGNORE_MISSING:
|
|
/* Ignored, piglit compatibility */
|
|
break;
|
|
case OPT_MULTIPLE:
|
|
settings->multiple_mode = true;
|
|
break;
|
|
case OPT_TIMEOUT:
|
|
settings->inactivity_timeout = atoi(optarg);
|
|
break;
|
|
case OPT_OVERALL_TIMEOUT:
|
|
settings->overall_timeout = atoi(optarg);
|
|
break;
|
|
case OPT_WATCHDOG:
|
|
settings->use_watchdog = true;
|
|
break;
|
|
case OPT_PIGLIT_DMESG:
|
|
settings->piglit_style_dmesg = true;
|
|
if (settings->dmesg_warn_level < 0)
|
|
settings->dmesg_warn_level = 5; /* KERN_NOTICE */
|
|
break;
|
|
case OPT_DMESG_WARN_LEVEL:
|
|
settings->dmesg_warn_level = atoi(optarg);
|
|
break;
|
|
case OPT_BLACKLIST:
|
|
if (!parse_blacklist(&settings->exclude_regexes,
|
|
absolute_path(optarg)))
|
|
goto error;
|
|
break;
|
|
case OPT_LIST_ALL:
|
|
settings->list_all = true;
|
|
break;
|
|
case '?':
|
|
usage(NULL, stderr);
|
|
goto error;
|
|
default:
|
|
usage("Cannot parse options", stderr);
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
if (settings->dmesg_warn_level < 0)
|
|
settings->dmesg_warn_level = 4; /* KERN_WARN */
|
|
|
|
if (settings->list_all) { /* --list-all doesn't require results path */
|
|
switch (argc - optind) {
|
|
case 1:
|
|
settings->test_root = absolute_path(argv[optind]);
|
|
++optind;
|
|
/* fallthrough */
|
|
case 0:
|
|
break;
|
|
default:
|
|
usage("Too many arguments for --list-all", stderr);
|
|
goto error;
|
|
}
|
|
} else {
|
|
switch (argc - optind) {
|
|
case 2:
|
|
settings->test_root = absolute_path(argv[optind]);
|
|
++optind;
|
|
/* fallthrough */
|
|
case 1:
|
|
settings->results_path = absolute_path(argv[optind]);
|
|
break;
|
|
case 0:
|
|
usage("Results-path missing", stderr);
|
|
goto error;
|
|
default:
|
|
usage("Extra arguments after results-path", stderr);
|
|
goto error;
|
|
}
|
|
if (!settings->name) {
|
|
char *name = strdup(settings->results_path);
|
|
|
|
settings->name = strdup(basename(name));
|
|
free(name);
|
|
}
|
|
}
|
|
|
|
if ((env_test_root = getenv("IGT_TEST_ROOT")) != NULL) {
|
|
free(settings->test_root);
|
|
settings->test_root = absolute_path(env_test_root);
|
|
}
|
|
|
|
if (!settings->test_root) {
|
|
usage("Test root not set", stderr);
|
|
goto error;
|
|
}
|
|
|
|
|
|
return true;
|
|
|
|
error:
|
|
free_settings(settings);
|
|
return false;
|
|
}
|
|
|
|
bool validate_settings(struct settings *settings)
|
|
{
|
|
int dirfd, fd;
|
|
|
|
if (settings->test_list && !readable_file(settings->test_list)) {
|
|
usage("Cannot open test-list file", stderr);
|
|
return false;
|
|
}
|
|
|
|
if (!settings->results_path) {
|
|
usage("No results-path set; this shouldn't happen", stderr);
|
|
return false;
|
|
}
|
|
|
|
if (!settings->test_root) {
|
|
usage("No test root set; this shouldn't happen", stderr);
|
|
return false;
|
|
}
|
|
|
|
dirfd = open(settings->test_root, O_DIRECTORY | O_RDONLY);
|
|
if (dirfd < 0) {
|
|
fprintf(stderr, "Test directory %s cannot be opened\n", settings->test_root);
|
|
return false;
|
|
}
|
|
|
|
fd = openat(dirfd, "test-list.txt", O_RDONLY);
|
|
if (fd < 0) {
|
|
fprintf(stderr, "Cannot open %s/test-list.txt\n", settings->test_root);
|
|
close(dirfd);
|
|
return false;
|
|
}
|
|
|
|
close(fd);
|
|
close(dirfd);
|
|
|
|
return true;
|
|
}
|
|
|
|
static char *_dirname(const char *path)
|
|
{
|
|
char *tmppath = strdup(path);
|
|
char *tmpname = dirname(tmppath);
|
|
tmpname = strdup(tmpname);
|
|
free(tmppath);
|
|
return tmpname;
|
|
}
|
|
|
|
static char *_basename(const char *path)
|
|
{
|
|
char *tmppath = strdup(path);
|
|
char *tmpname = basename(tmppath);
|
|
tmpname = strdup(tmpname);
|
|
free(tmppath);
|
|
return tmpname;
|
|
}
|
|
|
|
char *absolute_path(char *path)
|
|
{
|
|
char *result = NULL;
|
|
char *base, *dir;
|
|
char *ret;
|
|
|
|
result = realpath(path, NULL);
|
|
if (result != NULL)
|
|
return result;
|
|
|
|
dir = _dirname(path);
|
|
ret = absolute_path(dir);
|
|
free(dir);
|
|
|
|
base = _basename(path);
|
|
asprintf(&result, "%s/%s", ret, base);
|
|
free(base);
|
|
free(ret);
|
|
|
|
return result;
|
|
}
|
|
|
|
static char settings_filename[] = "metadata.txt";
|
|
bool serialize_settings(struct settings *settings)
|
|
{
|
|
#define SERIALIZE_LINE(f, s, name, format) fprintf(f, "%s : " format "\n", #name, s->name)
|
|
|
|
int dirfd, fd;
|
|
FILE *f;
|
|
|
|
if (!settings->results_path) {
|
|
usage("No results-path set; this shouldn't happen", stderr);
|
|
return false;
|
|
}
|
|
|
|
if ((dirfd = open(settings->results_path, O_DIRECTORY | O_RDONLY)) < 0) {
|
|
mkdir(settings->results_path, 0755);
|
|
if ((dirfd = open(settings->results_path, O_DIRECTORY | O_RDONLY)) < 0) {
|
|
usage("Creating results-path failed", stderr);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (!settings->overwrite &&
|
|
faccessat(dirfd, settings_filename, F_OK, 0) == 0) {
|
|
usage("Settings metadata already exists and not overwriting", stderr);
|
|
return false;
|
|
}
|
|
|
|
if (settings->overwrite &&
|
|
unlinkat(dirfd, settings_filename, 0) != 0 &&
|
|
errno != ENOENT) {
|
|
usage("Error removing old settings metadata", stderr);
|
|
return false;
|
|
}
|
|
|
|
if ((fd = openat(dirfd, settings_filename, O_CREAT | O_EXCL | O_WRONLY, 0666)) < 0) {
|
|
char *msg;
|
|
|
|
asprintf(&msg, "Creating settings serialization file failed: %s", strerror(errno));
|
|
usage(msg, stderr);
|
|
|
|
free(msg);
|
|
close(dirfd);
|
|
return false;
|
|
}
|
|
|
|
f = fdopen(fd, "w");
|
|
if (!f) {
|
|
close(fd);
|
|
close(dirfd);
|
|
return false;
|
|
}
|
|
|
|
SERIALIZE_LINE(f, settings, abort_mask, "%d");
|
|
if (settings->test_list)
|
|
SERIALIZE_LINE(f, settings, test_list, "%s");
|
|
if (settings->name)
|
|
SERIALIZE_LINE(f, settings, name, "%s");
|
|
SERIALIZE_LINE(f, settings, dry_run, "%d");
|
|
SERIALIZE_LINE(f, settings, sync, "%d");
|
|
SERIALIZE_LINE(f, settings, log_level, "%d");
|
|
SERIALIZE_LINE(f, settings, overwrite, "%d");
|
|
SERIALIZE_LINE(f, settings, multiple_mode, "%d");
|
|
SERIALIZE_LINE(f, settings, inactivity_timeout, "%d");
|
|
SERIALIZE_LINE(f, settings, overall_timeout, "%d");
|
|
SERIALIZE_LINE(f, settings, use_watchdog, "%d");
|
|
SERIALIZE_LINE(f, settings, piglit_style_dmesg, "%d");
|
|
SERIALIZE_LINE(f, settings, dmesg_warn_level, "%d");
|
|
SERIALIZE_LINE(f, settings, test_root, "%s");
|
|
SERIALIZE_LINE(f, settings, results_path, "%s");
|
|
|
|
if (settings->sync) {
|
|
fsync(fd);
|
|
fsync(dirfd);
|
|
}
|
|
|
|
fclose(f);
|
|
close(dirfd);
|
|
return true;
|
|
|
|
#undef SERIALIZE_LINE
|
|
}
|
|
|
|
bool read_settings_from_file(struct settings *settings, FILE *f)
|
|
{
|
|
#define PARSE_LINE(s, name, val, field, write) \
|
|
if (!strcmp(name, #field)) { \
|
|
s->field = write; \
|
|
free(name); \
|
|
free(val); \
|
|
name = val = NULL; \
|
|
continue; \
|
|
}
|
|
|
|
char *name = NULL, *val = NULL;
|
|
|
|
settings->dmesg_warn_level = -1;
|
|
|
|
while (fscanf(f, "%ms : %ms", &name, &val) == 2) {
|
|
int numval = atoi(val);
|
|
PARSE_LINE(settings, name, val, abort_mask, numval);
|
|
PARSE_LINE(settings, name, val, test_list, val ? strdup(val) : NULL);
|
|
PARSE_LINE(settings, name, val, name, val ? strdup(val) : NULL);
|
|
PARSE_LINE(settings, name, val, dry_run, numval);
|
|
PARSE_LINE(settings, name, val, sync, numval);
|
|
PARSE_LINE(settings, name, val, log_level, numval);
|
|
PARSE_LINE(settings, name, val, overwrite, numval);
|
|
PARSE_LINE(settings, name, val, multiple_mode, numval);
|
|
PARSE_LINE(settings, name, val, inactivity_timeout, numval);
|
|
PARSE_LINE(settings, name, val, overall_timeout, numval);
|
|
PARSE_LINE(settings, name, val, use_watchdog, numval);
|
|
PARSE_LINE(settings, name, val, piglit_style_dmesg, numval);
|
|
PARSE_LINE(settings, name, val, dmesg_warn_level, numval);
|
|
PARSE_LINE(settings, name, val, test_root, val ? strdup(val) : NULL);
|
|
PARSE_LINE(settings, name, val, results_path, val ? strdup(val) : NULL);
|
|
|
|
printf("Warning: Unknown field in settings file: %s = %s\n",
|
|
name, val);
|
|
free(name);
|
|
free(val);
|
|
name = val = NULL;
|
|
}
|
|
|
|
if (settings->dmesg_warn_level < 0) {
|
|
if (settings->piglit_style_dmesg)
|
|
settings->dmesg_warn_level = 5;
|
|
else
|
|
settings->dmesg_warn_level = 4;
|
|
}
|
|
|
|
free(name);
|
|
free(val);
|
|
|
|
return true;
|
|
|
|
#undef PARSE_LINE
|
|
}
|
|
|
|
bool read_settings_from_dir(struct settings *settings, int dirfd)
|
|
{
|
|
int fd;
|
|
FILE *f;
|
|
|
|
free_settings(settings);
|
|
|
|
if ((fd = openat(dirfd, settings_filename, O_RDONLY)) < 0)
|
|
return false;
|
|
|
|
f = fdopen(fd, "r");
|
|
if (!f) {
|
|
close(fd);
|
|
return false;
|
|
}
|
|
|
|
if (!read_settings_from_file(settings, f)) {
|
|
fclose(f);
|
|
return false;
|
|
}
|
|
|
|
fclose(f);
|
|
|
|
return true;
|
|
}
|