1 /*
2  * Copyright 2013, Michael Ellerman, IBM Corp.
3  * Licensed under GPLv2.
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 
15 #include "subunit.h"
16 #include "utils.h"
17 
18 #define TIMEOUT		120
19 #define KILL_TIMEOUT	5
20 
21 
22 int run_test(int (test_function)(void), char *name)
23 {
24 	bool terminated;
25 	int rc, status;
26 	pid_t pid;
27 
28 	/* Make sure output is flushed before forking */
29 	fflush(stdout);
30 
31 	pid = fork();
32 	if (pid == 0) {
33 		setpgid(0, 0);
34 		exit(test_function());
35 	} else if (pid == -1) {
36 		perror("fork");
37 		return 1;
38 	}
39 
40 	setpgid(pid, pid);
41 
42 	/* Wake us up in timeout seconds */
43 	alarm(TIMEOUT);
44 	terminated = false;
45 
46 wait:
47 	rc = waitpid(pid, &status, 0);
48 	if (rc == -1) {
49 		if (errno != EINTR) {
50 			printf("unknown error from waitpid\n");
51 			return 1;
52 		}
53 
54 		if (terminated) {
55 			printf("!! force killing %s\n", name);
56 			kill(-pid, SIGKILL);
57 			return 1;
58 		} else {
59 			printf("!! killing %s\n", name);
60 			kill(-pid, SIGTERM);
61 			terminated = true;
62 			alarm(KILL_TIMEOUT);
63 			goto wait;
64 		}
65 	}
66 
67 	/* Kill anything else in the process group that is still running */
68 	kill(-pid, SIGTERM);
69 
70 	if (WIFEXITED(status))
71 		status = WEXITSTATUS(status);
72 	else {
73 		if (WIFSIGNALED(status))
74 			printf("!! child died by signal %d\n", WTERMSIG(status));
75 		else
76 			printf("!! child died by unknown cause\n");
77 
78 		status = 1; /* Signal or other */
79 	}
80 
81 	return status;
82 }
83 
84 static void alarm_handler(int signum)
85 {
86 	/* Jut wake us up from waitpid */
87 }
88 
89 static struct sigaction alarm_action = {
90 	.sa_handler = alarm_handler,
91 };
92 
93 int test_harness(int (test_function)(void), char *name)
94 {
95 	int rc;
96 
97 	test_start(name);
98 	test_set_git_version(GIT_VERSION);
99 
100 	if (sigaction(SIGALRM, &alarm_action, NULL)) {
101 		perror("sigaction");
102 		test_error(name);
103 		return 1;
104 	}
105 
106 	rc = run_test(test_function, name);
107 
108 	if (rc == MAGIC_SKIP_RETURN_VALUE)
109 		test_skip(name);
110 	else
111 		test_finish(name, rc);
112 
113 	return rc;
114 }
115