162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright 2013, Michael Ellerman, IBM Corp.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <errno.h>
762306a36Sopenharmony_ci#include <signal.h>
862306a36Sopenharmony_ci#include <stdbool.h>
962306a36Sopenharmony_ci#include <stdio.h>
1062306a36Sopenharmony_ci#include <stdlib.h>
1162306a36Sopenharmony_ci#include <sys/types.h>
1262306a36Sopenharmony_ci#include <sys/wait.h>
1362306a36Sopenharmony_ci#include <unistd.h>
1462306a36Sopenharmony_ci#include <elf.h>
1562306a36Sopenharmony_ci#include <fcntl.h>
1662306a36Sopenharmony_ci#include <link.h>
1762306a36Sopenharmony_ci#include <sys/stat.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include "subunit.h"
2062306a36Sopenharmony_ci#include "utils.h"
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#define KILL_TIMEOUT	5
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci/* Setting timeout to -1 disables the alarm */
2562306a36Sopenharmony_cistatic uint64_t timeout = 120;
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ciint run_test(int (test_function)(void), const char *name)
2862306a36Sopenharmony_ci{
2962306a36Sopenharmony_ci	bool terminated;
3062306a36Sopenharmony_ci	int rc, status;
3162306a36Sopenharmony_ci	pid_t pid;
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci	/* Make sure output is flushed before forking */
3462306a36Sopenharmony_ci	fflush(stdout);
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	pid = fork();
3762306a36Sopenharmony_ci	if (pid == 0) {
3862306a36Sopenharmony_ci		setpgid(0, 0);
3962306a36Sopenharmony_ci		exit(test_function());
4062306a36Sopenharmony_ci	} else if (pid == -1) {
4162306a36Sopenharmony_ci		perror("fork");
4262306a36Sopenharmony_ci		return 1;
4362306a36Sopenharmony_ci	}
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	setpgid(pid, pid);
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	if (timeout != -1)
4862306a36Sopenharmony_ci		/* Wake us up in timeout seconds */
4962306a36Sopenharmony_ci		alarm(timeout);
5062306a36Sopenharmony_ci	terminated = false;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ciwait:
5362306a36Sopenharmony_ci	rc = waitpid(pid, &status, 0);
5462306a36Sopenharmony_ci	if (rc == -1) {
5562306a36Sopenharmony_ci		if (errno != EINTR) {
5662306a36Sopenharmony_ci			printf("unknown error from waitpid\n");
5762306a36Sopenharmony_ci			return 1;
5862306a36Sopenharmony_ci		}
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci		if (terminated) {
6162306a36Sopenharmony_ci			printf("!! force killing %s\n", name);
6262306a36Sopenharmony_ci			kill(-pid, SIGKILL);
6362306a36Sopenharmony_ci			return 1;
6462306a36Sopenharmony_ci		} else {
6562306a36Sopenharmony_ci			printf("!! killing %s\n", name);
6662306a36Sopenharmony_ci			kill(-pid, SIGTERM);
6762306a36Sopenharmony_ci			terminated = true;
6862306a36Sopenharmony_ci			alarm(KILL_TIMEOUT);
6962306a36Sopenharmony_ci			goto wait;
7062306a36Sopenharmony_ci		}
7162306a36Sopenharmony_ci	}
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	/* Kill anything else in the process group that is still running */
7462306a36Sopenharmony_ci	kill(-pid, SIGTERM);
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	if (WIFEXITED(status))
7762306a36Sopenharmony_ci		status = WEXITSTATUS(status);
7862306a36Sopenharmony_ci	else {
7962306a36Sopenharmony_ci		if (WIFSIGNALED(status))
8062306a36Sopenharmony_ci			printf("!! child died by signal %d\n", WTERMSIG(status));
8162306a36Sopenharmony_ci		else
8262306a36Sopenharmony_ci			printf("!! child died by unknown cause\n");
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci		status = 1; /* Signal or other */
8562306a36Sopenharmony_ci	}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	return status;
8862306a36Sopenharmony_ci}
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_cistatic void sig_handler(int signum)
9162306a36Sopenharmony_ci{
9262306a36Sopenharmony_ci	/* Just wake us up from waitpid */
9362306a36Sopenharmony_ci}
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_cistatic struct sigaction sig_action = {
9662306a36Sopenharmony_ci	.sa_handler = sig_handler,
9762306a36Sopenharmony_ci};
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_civoid test_harness_set_timeout(uint64_t time)
10062306a36Sopenharmony_ci{
10162306a36Sopenharmony_ci	timeout = time;
10262306a36Sopenharmony_ci}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ciint test_harness(int (test_function)(void), const char *name)
10562306a36Sopenharmony_ci{
10662306a36Sopenharmony_ci	int rc;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	test_start(name);
10962306a36Sopenharmony_ci	test_set_git_version(GIT_VERSION);
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	if (sigaction(SIGINT, &sig_action, NULL)) {
11262306a36Sopenharmony_ci		perror("sigaction (sigint)");
11362306a36Sopenharmony_ci		test_error(name);
11462306a36Sopenharmony_ci		return 1;
11562306a36Sopenharmony_ci	}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	if (sigaction(SIGALRM, &sig_action, NULL)) {
11862306a36Sopenharmony_ci		perror("sigaction (sigalrm)");
11962306a36Sopenharmony_ci		test_error(name);
12062306a36Sopenharmony_ci		return 1;
12162306a36Sopenharmony_ci	}
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	rc = run_test(test_function, name);
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	if (rc == MAGIC_SKIP_RETURN_VALUE) {
12662306a36Sopenharmony_ci		test_skip(name);
12762306a36Sopenharmony_ci		/* so that skipped test is not marked as failed */
12862306a36Sopenharmony_ci		rc = 0;
12962306a36Sopenharmony_ci	} else
13062306a36Sopenharmony_ci		test_finish(name, rc);
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	return rc;
13362306a36Sopenharmony_ci}
134