1 // Copyright 2018 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <runtests-utils/posix-run-test.h>
6
7 #include <errno.h>
8 #include <fcntl.h>
9 #include <spawn.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string>
13 #include <sys/stat.h>
14 #include <sys/wait.h>
15 #include <unistd.h>
16
17 #include <fbl/algorithm.h>
18 #include <fbl/auto_call.h>
19 #include <unittest/unittest.h>
20
21 namespace runtests {
22 namespace {
23
24 // A whitelist of the names of environment variables names that we pass into the
25 // spawned test subprocess.
26 constexpr const char* const kEnvironmentWhitelist[] = {
27 "TMPDIR",
28 // Paths to the symbolizer for various sanitizers.
29 "ASAN_SYMBOLIZER_PATH",
30 "LSAN_SYMBOLIZER_PATH",
31 "MSAN_SYMBOLIZER_PATH",
32 "UBSAN_SYMBOLIZER_PATH",
33 // From unittest.h. Set by RunAllTests().
34 TEST_ENV_NAME,
35 };
36
37 } // namespace
38
PosixRunTest(const char * argv[],const char *,const char * output_filename)39 fbl::unique_ptr<Result> PosixRunTest(const char* argv[],
40 const char*, // output_dir
41 const char* output_filename) {
42 int status;
43 const char* path = argv[0];
44 FILE* output_file = nullptr;
45
46 // Initialize |file_actions|, which dictate what I/O will be performed in the
47 // launched process, and ensure its destruction on function exit.
48 posix_spawn_file_actions_t file_actions;
49 if ((status = posix_spawn_file_actions_init(&file_actions))) {
50 fprintf(stderr, "FAILURE: posix_spawn_file_actions_init failed: %s\n",
51 strerror(status));
52 return fbl::make_unique<Result>(path, FAILED_TO_LAUNCH, 0);
53 }
54
55 auto auto_tidy = fbl::MakeAutoCall([&] {
56 posix_spawn_file_actions_destroy(&file_actions);
57 if (output_file != nullptr) {
58 fclose(output_file);
59 }
60 });
61
62 // Construct the array of whitelisted environment variable strings of the
63 // form "<name>=<value>". The env_strings array just keeps the underlying
64 // std::string objects alive so the envp pointers remain valid.
65 std::string env_strings[fbl::count_of(kEnvironmentWhitelist)];
66 const char* envp[fbl::count_of(env_strings) + 1]; // +1 for null terminator.
67 size_t i = 0;
68 for (const char* var : kEnvironmentWhitelist) {
69 const char* val = getenv(var);
70 if (val) {
71 env_strings[i] = std::string(var) + "=" + val;
72 envp[i] = env_strings[i].c_str();
73 ++i;
74 }
75 }
76 envp[i] = nullptr;
77
78 // Tee output.
79 if (output_filename != nullptr) {
80 output_file = fopen(output_filename, "w");
81 if (output_file == nullptr) {
82 return fbl::make_unique<Result>(path, FAILED_DURING_IO, 0);
83 }
84 if ((status = posix_spawn_file_actions_addopen(
85 &file_actions, STDOUT_FILENO, output_filename,
86 O_WRONLY | O_CREAT | O_TRUNC, 0644))) {
87 fprintf(stderr, "FAILURE: posix_spawn_file_actions_addopen failed: %s\n",
88 strerror(status));
89 return fbl::make_unique<Result>(path, FAILED_TO_LAUNCH, 0);
90 }
91 if ((status = posix_spawn_file_actions_adddup2(&file_actions, STDOUT_FILENO,
92 STDERR_FILENO))) {
93 fprintf(stderr, "FAILURE: posix_spawn_file_actions_adddup2 failed: %s\n",
94 strerror(status));
95 return fbl::make_unique<Result>(path, FAILED_TO_LAUNCH, 0);
96 }
97 }
98
99 // Launch the test subprocess.
100 pid_t test_pid;
101 if ((status = posix_spawn(&test_pid, path, &file_actions, nullptr,
102 const_cast<char**>(argv),
103 const_cast<char**>(envp)))) {
104 fprintf(stderr, "FAILURE: posix_spawn failed: %s\n", strerror(status));
105 return fbl::make_unique<Result>(path, FAILED_TO_LAUNCH, 0);
106 }
107
108 if (waitpid(test_pid, &status, WUNTRACED | WCONTINUED) == -1) {
109 fprintf(stderr, "FAILURE: waitpid failed: %s\n", strerror(errno));
110 return fbl::make_unique<Result>(path, FAILED_TO_WAIT, 0);
111 }
112 if (WIFEXITED(status)) {
113 int return_code = WEXITSTATUS(status);
114 LaunchStatus launch_status =
115 return_code ? FAILED_NONZERO_RETURN_CODE : SUCCESS;
116 return fbl::make_unique<Result>(path, launch_status, return_code);
117 }
118 if (WIFSIGNALED(status)) {
119 fprintf(stderr, "FAILURE: test process killed by signal %d\n", WTERMSIG(status));
120 return fbl::make_unique<Result>(path, FAILED_NONZERO_RETURN_CODE, 1);
121 }
122 if (WIFSTOPPED(status)) {
123 fprintf(stderr, "FAILURE: test process stopped by signal %d\n", WSTOPSIG(status));
124 return fbl::make_unique<Result>(path, FAILED_NONZERO_RETURN_CODE, 1);
125 }
126
127 fprintf(stderr, "FAILURE: test process with unexpected status: %#x", status);
128 return fbl::make_unique<Result>(path, FAILED_UNKNOWN, 0);
129 }
130
131 } // namespace runtests
132