132 lines
2.3 KiB
C
132 lines
2.3 KiB
C
|
/*
|
||
|
* Copyright 2013, Michael Ellerman, IBM Corp.
|
||
|
* Licensed under GPLv2.
|
||
|
*/
|
||
|
|
||
|
#include <errno.h>
|
||
|
#include <signal.h>
|
||
|
#include <stdbool.h>
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <sys/types.h>
|
||
|
#include <sys/wait.h>
|
||
|
#include <unistd.h>
|
||
|
#include <elf.h>
|
||
|
#include <fcntl.h>
|
||
|
#include <link.h>
|
||
|
#include <sys/stat.h>
|
||
|
|
||
|
#include "subunit.h"
|
||
|
#include "utils.h"
|
||
|
|
||
|
#define KILL_TIMEOUT 5
|
||
|
|
||
|
static uint64_t timeout = 120;
|
||
|
|
||
|
int run_test(int (test_function)(void), char *name)
|
||
|
{
|
||
|
bool terminated;
|
||
|
int rc, status;
|
||
|
pid_t pid;
|
||
|
|
||
|
/* Make sure output is flushed before forking */
|
||
|
fflush(stdout);
|
||
|
|
||
|
pid = fork();
|
||
|
if (pid == 0) {
|
||
|
setpgid(0, 0);
|
||
|
exit(test_function());
|
||
|
} else if (pid == -1) {
|
||
|
perror("fork");
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
setpgid(pid, pid);
|
||
|
|
||
|
/* Wake us up in timeout seconds */
|
||
|
alarm(timeout);
|
||
|
terminated = false;
|
||
|
|
||
|
wait:
|
||
|
rc = waitpid(pid, &status, 0);
|
||
|
if (rc == -1) {
|
||
|
if (errno != EINTR) {
|
||
|
printf("unknown error from waitpid\n");
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
if (terminated) {
|
||
|
printf("!! force killing %s\n", name);
|
||
|
kill(-pid, SIGKILL);
|
||
|
return 1;
|
||
|
} else {
|
||
|
printf("!! killing %s\n", name);
|
||
|
kill(-pid, SIGTERM);
|
||
|
terminated = true;
|
||
|
alarm(KILL_TIMEOUT);
|
||
|
goto wait;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Kill anything else in the process group that is still running */
|
||
|
kill(-pid, SIGTERM);
|
||
|
|
||
|
if (WIFEXITED(status))
|
||
|
status = WEXITSTATUS(status);
|
||
|
else {
|
||
|
if (WIFSIGNALED(status))
|
||
|
printf("!! child died by signal %d\n", WTERMSIG(status));
|
||
|
else
|
||
|
printf("!! child died by unknown cause\n");
|
||
|
|
||
|
status = 1; /* Signal or other */
|
||
|
}
|
||
|
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
static void sig_handler(int signum)
|
||
|
{
|
||
|
/* Just wake us up from waitpid */
|
||
|
}
|
||
|
|
||
|
static struct sigaction sig_action = {
|
||
|
.sa_handler = sig_handler,
|
||
|
};
|
||
|
|
||
|
void test_harness_set_timeout(uint64_t time)
|
||
|
{
|
||
|
timeout = time;
|
||
|
}
|
||
|
|
||
|
int test_harness(int (test_function)(void), char *name)
|
||
|
{
|
||
|
int rc;
|
||
|
|
||
|
test_start(name);
|
||
|
test_set_git_version(GIT_VERSION);
|
||
|
|
||
|
if (sigaction(SIGINT, &sig_action, NULL)) {
|
||
|
perror("sigaction (sigint)");
|
||
|
test_error(name);
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
if (sigaction(SIGALRM, &sig_action, NULL)) {
|
||
|
perror("sigaction (sigalrm)");
|
||
|
test_error(name);
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
rc = run_test(test_function, name);
|
||
|
|
||
|
if (rc == MAGIC_SKIP_RETURN_VALUE) {
|
||
|
test_skip(name);
|
||
|
/* so that skipped test is not marked as failed */
|
||
|
rc = 0;
|
||
|
} else
|
||
|
test_finish(name, rc);
|
||
|
|
||
|
return rc;
|
||
|
}
|