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