1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * Copyright 2013, Michael Ellerman, IBM Corp.
4 */
5
6#include <errno.h>
7#include <signal.h>
8#include <stdbool.h>
9#include <stdio.h>
10#include <stdlib.h>
11#include <sys/types.h>
12#include <sys/wait.h>
13#include <unistd.h>
14#include <elf.h>
15#include <fcntl.h>
16#include <link.h>
17#include <sys/stat.h>
18
19#include "subunit.h"
20#include "utils.h"
21
22#define KILL_TIMEOUT	5
23
24/* Setting timeout to -1 disables the alarm */
25static uint64_t timeout = 120;
26
27int run_test(int (test_function)(void), const char *name)
28{
29	bool terminated;
30	int rc, status;
31	pid_t pid;
32
33	/* Make sure output is flushed before forking */
34	fflush(stdout);
35
36	pid = fork();
37	if (pid == 0) {
38		setpgid(0, 0);
39		exit(test_function());
40	} else if (pid == -1) {
41		perror("fork");
42		return 1;
43	}
44
45	setpgid(pid, pid);
46
47	if (timeout != -1)
48		/* Wake us up in timeout seconds */
49		alarm(timeout);
50	terminated = false;
51
52wait:
53	rc = waitpid(pid, &status, 0);
54	if (rc == -1) {
55		if (errno != EINTR) {
56			printf("unknown error from waitpid\n");
57			return 1;
58		}
59
60		if (terminated) {
61			printf("!! force killing %s\n", name);
62			kill(-pid, SIGKILL);
63			return 1;
64		} else {
65			printf("!! killing %s\n", name);
66			kill(-pid, SIGTERM);
67			terminated = true;
68			alarm(KILL_TIMEOUT);
69			goto wait;
70		}
71	}
72
73	/* Kill anything else in the process group that is still running */
74	kill(-pid, SIGTERM);
75
76	if (WIFEXITED(status))
77		status = WEXITSTATUS(status);
78	else {
79		if (WIFSIGNALED(status))
80			printf("!! child died by signal %d\n", WTERMSIG(status));
81		else
82			printf("!! child died by unknown cause\n");
83
84		status = 1; /* Signal or other */
85	}
86
87	return status;
88}
89
90static void sig_handler(int signum)
91{
92	/* Just wake us up from waitpid */
93}
94
95static struct sigaction sig_action = {
96	.sa_handler = sig_handler,
97};
98
99void test_harness_set_timeout(uint64_t time)
100{
101	timeout = time;
102}
103
104int test_harness(int (test_function)(void), const char *name)
105{
106	int rc;
107
108	test_start(name);
109	test_set_git_version(GIT_VERSION);
110
111	if (sigaction(SIGINT, &sig_action, NULL)) {
112		perror("sigaction (sigint)");
113		test_error(name);
114		return 1;
115	}
116
117	if (sigaction(SIGALRM, &sig_action, NULL)) {
118		perror("sigaction (sigalrm)");
119		test_error(name);
120		return 1;
121	}
122
123	rc = run_test(test_function, name);
124
125	if (rc == MAGIC_SKIP_RETURN_VALUE) {
126		test_skip(name);
127		/* so that skipped test is not marked as failed */
128		rc = 0;
129	} else
130		test_finish(name, rc);
131
132	return rc;
133}
134