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