1 // Copyright 2014 The Chromium 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 "gn/exec_process.h"
6
7 #include <stddef.h>
8
9 #include <memory>
10
11 #include "base/command_line.h"
12 #include "base/files/file_util.h"
13 #include "base/logging.h"
14 #include "util/build_config.h"
15
16 #if defined(OS_WIN)
17 #include <windows.h>
18
19 #include "base/win/scoped_handle.h"
20 #include "base/win/scoped_process_information.h"
21 #include "base/win/win_util.h"
22 #else
23 #include <errno.h>
24 #include <fcntl.h>
25 #include <signal.h>
26 #include <sys/wait.h>
27 #include <unistd.h>
28
29 #include "base/posix/eintr_wrapper.h"
30 #include "base/posix/file_descriptor_shuffle.h"
31 #endif
32
33 namespace internal {
34
35 #if defined(OS_WIN)
ExecProcess(const base::CommandLine & cmdline,const base::FilePath & startup_dir,std::string * std_out,std::string * std_err,int * exit_code)36 bool ExecProcess(const base::CommandLine& cmdline,
37 const base::FilePath& startup_dir,
38 std::string* std_out,
39 std::string* std_err,
40 int* exit_code) {
41 return ExecProcess(cmdline.GetCommandLineString(), startup_dir, std_out,
42 std_err, exit_code);
43 }
44
ExecProcess(const std::u16string & cmdline_str,const base::FilePath & startup_dir,std::string * std_out,std::string * std_err,int * exit_code)45 bool ExecProcess(const std::u16string& cmdline_str,
46 const base::FilePath& startup_dir,
47 std::string* std_out,
48 std::string* std_err,
49 int* exit_code) {
50 SECURITY_ATTRIBUTES sa_attr;
51 // Set the bInheritHandle flag so pipe handles are inherited.
52 sa_attr.nLength = sizeof(SECURITY_ATTRIBUTES);
53 sa_attr.bInheritHandle = TRUE;
54 sa_attr.lpSecurityDescriptor = nullptr;
55
56 // Create the pipe for the child process's STDOUT.
57 HANDLE out_read = nullptr;
58 HANDLE out_write = nullptr;
59 if (!CreatePipe(&out_read, &out_write, &sa_attr, 0)) {
60 NOTREACHED() << "Failed to create pipe";
61 return false;
62 }
63 base::win::ScopedHandle scoped_out_read(out_read);
64 base::win::ScopedHandle scoped_out_write(out_write);
65
66 // Create the pipe for the child process's STDERR.
67 HANDLE err_read = nullptr;
68 HANDLE err_write = nullptr;
69 if (!CreatePipe(&err_read, &err_write, &sa_attr, 0)) {
70 NOTREACHED() << "Failed to create pipe";
71 return false;
72 }
73 base::win::ScopedHandle scoped_err_read(err_read);
74 base::win::ScopedHandle scoped_err_write(err_write);
75
76 // Ensure the read handle to the pipe for STDOUT/STDERR is not inherited.
77 if (!SetHandleInformation(out_read, HANDLE_FLAG_INHERIT, 0)) {
78 NOTREACHED() << "Failed to disable pipe inheritance";
79 return false;
80 }
81 if (!SetHandleInformation(err_read, HANDLE_FLAG_INHERIT, 0)) {
82 NOTREACHED() << "Failed to disable pipe inheritance";
83 return false;
84 }
85
86 STARTUPINFO start_info = {};
87
88 start_info.cb = sizeof(STARTUPINFO);
89 start_info.hStdOutput = out_write;
90 // Keep the normal stdin.
91 start_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
92 // FIXME(brettw) set stderr here when we actually read it below.
93 // start_info.hStdError = err_write;
94 start_info.hStdError = GetStdHandle(STD_ERROR_HANDLE);
95 start_info.dwFlags |= STARTF_USESTDHANDLES;
96
97 std::u16string cmdline_writable = cmdline_str;
98
99 // Create the child process.
100 PROCESS_INFORMATION temp_process_info = {};
101 if (!CreateProcess(
102 nullptr, base::ToWCharT(&cmdline_writable[0]), nullptr, nullptr,
103 TRUE, // Handles are inherited.
104 NORMAL_PRIORITY_CLASS, nullptr, base::ToWCharT(&startup_dir.value()),
105 &start_info, &temp_process_info)) {
106 return false;
107 }
108 base::win::ScopedProcessInformation proc_info(temp_process_info);
109
110 // Close our writing end of pipes now. Otherwise later read would not be
111 // able to detect end of child's output.
112 scoped_out_write.Close();
113 scoped_err_write.Close();
114
115 // Read output from the child process's pipe for STDOUT
116 const int kBufferSize = 1024;
117 char buffer[kBufferSize];
118
119 // FIXME(brettw) read from stderr here! This is complicated because we want
120 // to read both of them at the same time, probably need overlapped I/O.
121 // Also uncomment start_info code above.
122 for (;;) {
123 DWORD bytes_read = 0;
124 BOOL success =
125 ReadFile(out_read, buffer, kBufferSize, &bytes_read, nullptr);
126 if (!success || bytes_read == 0)
127 break;
128 std_out->append(buffer, bytes_read);
129 }
130
131 // Let's wait for the process to finish.
132 WaitForSingleObject(proc_info.process_handle(), INFINITE);
133
134 DWORD dw_exit_code;
135 GetExitCodeProcess(proc_info.process_handle(), &dw_exit_code);
136 *exit_code = static_cast<int>(dw_exit_code);
137
138 return true;
139 }
140 #else
141 // Reads from the provided file descriptor and appends to output. Returns false
142 // if the fd is closed or there is an unexpected error (not
143 // EINTR/EAGAIN/EWOULDBLOCK).
144 bool ReadFromPipe(int fd, std::string* output) {
145 char buffer[256];
146 int bytes_read = HANDLE_EINTR(read(fd, buffer, sizeof(buffer)));
147 if (bytes_read == -1) {
148 return errno == EAGAIN || errno == EWOULDBLOCK;
149 } else if (bytes_read <= 0) {
150 return false;
151 }
152 output->append(buffer, bytes_read);
153 return true;
154 }
155
156 bool WaitForExit(int pid, int* exit_code) {
157 int status;
158 if (waitpid(pid, &status, 0) < 0) {
159 PLOG(ERROR) << "waitpid";
160 return false;
161 }
162
163 if (WIFEXITED(status)) {
164 *exit_code = WEXITSTATUS(status);
165 return true;
166 } else if (WIFSIGNALED(status)) {
167 if (WTERMSIG(status) == SIGINT || WTERMSIG(status) == SIGTERM ||
168 WTERMSIG(status) == SIGHUP)
169 return false;
170 }
171 return false;
172 }
173
174 bool ExecProcess(const base::CommandLine& cmdline,
175 const base::FilePath& startup_dir,
176 std::string* std_out,
177 std::string* std_err,
178 int* exit_code) {
179 *exit_code = EXIT_FAILURE;
180
181 std::vector<std::string> argv = cmdline.argv();
182
183 int out_fd[2], err_fd[2];
184 pid_t pid;
185 base::InjectiveMultimap fd_shuffle1, fd_shuffle2;
186 std::unique_ptr<char*[]> argv_cstr(new char*[argv.size() + 1]);
187
188 fd_shuffle1.reserve(3);
189 fd_shuffle2.reserve(3);
190
191 if (pipe(out_fd) < 0)
192 return false;
193 base::ScopedFD out_read(out_fd[0]), out_write(out_fd[1]);
194
195 if (pipe(err_fd) < 0)
196 return false;
197 base::ScopedFD err_read(err_fd[0]), err_write(err_fd[1]);
198
199 if (out_read.get() >= FD_SETSIZE || err_read.get() >= FD_SETSIZE)
200 return false;
201
202 switch (pid = fork()) {
203 case -1: // error
204 return false;
205 case 0: // child
206 {
207 #if defined(OS_MAC)
208 // When debugging the app under Xcode, the child will receive a SIGTRAP
209 // signal which will terminate the child process. Ignore the signal to
210 // allow debugging under macOS.
211 sigignore(SIGTRAP);
212 #endif
213
214 // DANGER: no calls to malloc are allowed from now on:
215 // http://crbug.com/36678
216 //
217 // STL iterators are also not allowed (including those implied
218 // by range-based for loops), since debug iterators use locks.
219
220 // Obscure fork() rule: in the child, if you don't end up doing exec*(),
221 // you call _exit() instead of exit(). This is because _exit() does not
222 // call any previously-registered (in the parent) exit handlers, which
223 // might do things like block waiting for threads that don't even exist
224 // in the child.
225 int dev_null = open("/dev/null", O_WRONLY);
226 if (dev_null < 0)
227 _exit(127);
228
229 fd_shuffle1.push_back(
230 base::InjectionArc(out_write.get(), STDOUT_FILENO, true));
231 fd_shuffle1.push_back(
232 base::InjectionArc(err_write.get(), STDERR_FILENO, true));
233 fd_shuffle1.push_back(base::InjectionArc(dev_null, STDIN_FILENO, true));
234 // Adding another element here? Remeber to increase the argument to
235 // reserve(), above.
236
237 // DANGER: Do NOT convert to range-based for loop!
238 for (size_t i = 0; i < fd_shuffle1.size(); ++i)
239 fd_shuffle2.push_back(fd_shuffle1[i]);
240
241 if (!ShuffleFileDescriptors(&fd_shuffle1))
242 _exit(127);
243
244 base::SetCurrentDirectory(startup_dir);
245
246 // TODO(brettw) the base version GetAppOutput does a
247 // CloseSuperfluousFds call here. Do we need this?
248
249 // DANGER: Do NOT convert to range-based for loop!
250 for (size_t i = 0; i < argv.size(); i++)
251 argv_cstr[i] = const_cast<char*>(argv[i].c_str());
252 argv_cstr[argv.size()] = nullptr;
253 execvp(argv_cstr[0], argv_cstr.get());
254 _exit(127);
255 }
256 default: // parent
257 {
258 // Close our writing end of pipe now. Otherwise later read would not
259 // be able to detect end of child's output (in theory we could still
260 // write to the pipe).
261 out_write.reset();
262 err_write.reset();
263
264 bool out_open = true, err_open = true;
265 while (out_open || err_open) {
266 fd_set read_fds;
267 FD_ZERO(&read_fds);
268 FD_SET(out_read.get(), &read_fds);
269 FD_SET(err_read.get(), &read_fds);
270 int res =
271 HANDLE_EINTR(select(std::max(out_read.get(), err_read.get()) + 1,
272 &read_fds, nullptr, nullptr, nullptr));
273 if (res <= 0)
274 break;
275 if (FD_ISSET(out_read.get(), &read_fds))
276 out_open = ReadFromPipe(out_read.get(), std_out);
277 if (FD_ISSET(err_read.get(), &read_fds))
278 err_open = ReadFromPipe(err_read.get(), std_err);
279 }
280
281 return WaitForExit(pid, exit_code);
282 }
283 }
284
285 return false;
286 }
287 #endif
288
289 } // namespace internal
290