1 /*
2 * Copyright (c) 2009-2022, Google LLC
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of Google LLC nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY DIRECT,
20 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28 // Shamelessly copied from the protobuf compiler's subprocess.cc
29 // except this version passes strings instead of Messages.
30
31 #include "upbc/subprocess.h"
32
33 #include <algorithm>
34 #include <cstring>
35 #include <iostream>
36
37 #ifndef _MSVC_LANG
38 #include <errno.h>
39 #include <signal.h>
40 #include <sys/select.h>
41 #include <sys/wait.h>
42 #endif
43
44 #include "absl/log/absl_log.h"
45 #include "absl/strings/substitute.h"
46
47 // Must be last.
48 #include "upb/port/def.inc"
49
50 namespace upbc {
51
52 namespace {
portable_strdup(const char * s)53 char* portable_strdup(const char* s) {
54 char* ns = (char*)malloc(strlen(s) + 1);
55 if (ns != nullptr) {
56 strcpy(ns, s);
57 }
58 return ns;
59 }
60 } // namespace
61
62 #ifdef _WIN32
63
CloseHandleOrDie(HANDLE handle)64 static void CloseHandleOrDie(HANDLE handle) {
65 if (!CloseHandle(handle)) {
66 ABSL_LOG(FATAL) << "CloseHandle: "
67 << Subprocess::Win32ErrorMessage(GetLastError());
68 }
69 }
70
Subprocess()71 Subprocess::Subprocess()
72 : process_start_error_(ERROR_SUCCESS),
73 child_handle_(nullptr),
74 child_stdin_(nullptr),
75 child_stdout_(nullptr) {}
76
~Subprocess()77 Subprocess::~Subprocess() {
78 if (child_stdin_ != nullptr) {
79 CloseHandleOrDie(child_stdin_);
80 }
81 if (child_stdout_ != nullptr) {
82 CloseHandleOrDie(child_stdout_);
83 }
84 }
85
Start(const std::string & program,SearchMode search_mode)86 void Subprocess::Start(const std::string& program, SearchMode search_mode) {
87 // Create the pipes.
88 HANDLE stdin_pipe_read;
89 HANDLE stdin_pipe_write;
90 HANDLE stdout_pipe_read;
91 HANDLE stdout_pipe_write;
92
93 if (!CreatePipe(&stdin_pipe_read, &stdin_pipe_write, nullptr, 0)) {
94 ABSL_LOG(FATAL) << "CreatePipe: " << Win32ErrorMessage(GetLastError());
95 }
96 if (!CreatePipe(&stdout_pipe_read, &stdout_pipe_write, nullptr, 0)) {
97 ABSL_LOG(FATAL) << "CreatePipe: " << Win32ErrorMessage(GetLastError());
98 }
99
100 // Make child side of the pipes inheritable.
101 if (!SetHandleInformation(stdin_pipe_read, HANDLE_FLAG_INHERIT,
102 HANDLE_FLAG_INHERIT)) {
103 ABSL_LOG(FATAL) << "SetHandleInformation: "
104 << Win32ErrorMessage(GetLastError());
105 }
106 if (!SetHandleInformation(stdout_pipe_write, HANDLE_FLAG_INHERIT,
107 HANDLE_FLAG_INHERIT)) {
108 ABSL_LOG(FATAL) << "SetHandleInformation: "
109 << Win32ErrorMessage(GetLastError());
110 }
111
112 // Setup STARTUPINFO to redirect handles.
113 STARTUPINFOA startup_info;
114 ZeroMemory(&startup_info, sizeof(startup_info));
115 startup_info.cb = sizeof(startup_info);
116 startup_info.dwFlags = STARTF_USESTDHANDLES;
117 startup_info.hStdInput = stdin_pipe_read;
118 startup_info.hStdOutput = stdout_pipe_write;
119 startup_info.hStdError = GetStdHandle(STD_ERROR_HANDLE);
120
121 if (startup_info.hStdError == INVALID_HANDLE_VALUE) {
122 ABSL_LOG(FATAL) << "GetStdHandle: " << Win32ErrorMessage(GetLastError());
123 }
124
125 // Invoking cmd.exe allows for '.bat' files from the path as well as '.exe'.
126 // Using a malloc'ed string because CreateProcess() can mutate its second
127 // parameter.
128 char* command_line =
129 portable_strdup(("cmd.exe /c \"" + program + "\"").c_str());
130
131 // Create the process.
132 PROCESS_INFORMATION process_info;
133
134 if (CreateProcessA((search_mode == SEARCH_PATH) ? nullptr : program.c_str(),
135 (search_mode == SEARCH_PATH) ? command_line : nullptr,
136 nullptr, // process security attributes
137 nullptr, // thread security attributes
138 TRUE, // inherit handles?
139 0, // obscure creation flags
140 nullptr, // environment (inherit from parent)
141 nullptr, // current directory (inherit from parent)
142 &startup_info, &process_info)) {
143 child_handle_ = process_info.hProcess;
144 CloseHandleOrDie(process_info.hThread);
145 child_stdin_ = stdin_pipe_write;
146 child_stdout_ = stdout_pipe_read;
147 } else {
148 process_start_error_ = GetLastError();
149 CloseHandleOrDie(stdin_pipe_write);
150 CloseHandleOrDie(stdout_pipe_read);
151 }
152
153 CloseHandleOrDie(stdin_pipe_read);
154 CloseHandleOrDie(stdout_pipe_write);
155 free(command_line);
156 }
157
Communicate(const std::string & input_data,std::string * output_data,std::string * error)158 bool Subprocess::Communicate(const std::string& input_data,
159 std::string* output_data, std::string* error) {
160 if (process_start_error_ != ERROR_SUCCESS) {
161 *error = Win32ErrorMessage(process_start_error_);
162 return false;
163 }
164
165 GOOGLE_CHECK(child_handle_ != nullptr) << "Must call Start() first.";
166
167 int input_pos = 0;
168
169 while (child_stdout_ != nullptr) {
170 HANDLE handles[2];
171 int handle_count = 0;
172
173 if (child_stdin_ != nullptr) {
174 handles[handle_count++] = child_stdin_;
175 }
176 if (child_stdout_ != nullptr) {
177 handles[handle_count++] = child_stdout_;
178 }
179
180 DWORD wait_result =
181 WaitForMultipleObjects(handle_count, handles, FALSE, INFINITE);
182
183 HANDLE signaled_handle = nullptr;
184 if (wait_result >= WAIT_OBJECT_0 &&
185 wait_result < WAIT_OBJECT_0 + handle_count) {
186 signaled_handle = handles[wait_result - WAIT_OBJECT_0];
187 } else if (wait_result == WAIT_FAILED) {
188 ABSL_LOG(FATAL) << "WaitForMultipleObjects: "
189 << Win32ErrorMessage(GetLastError());
190 } else {
191 ABSL_LOG(FATAL) << "WaitForMultipleObjects: Unexpected return code: "
192 << wait_result;
193 }
194
195 if (signaled_handle == child_stdin_) {
196 DWORD n;
197 if (!WriteFile(child_stdin_, input_data.data() + input_pos,
198 input_data.size() - input_pos, &n, nullptr)) {
199 // Child closed pipe. Presumably it will report an error later.
200 // Pretend we're done for now.
201 input_pos = input_data.size();
202 } else {
203 input_pos += n;
204 }
205
206 if (input_pos == input_data.size()) {
207 // We're done writing. Close.
208 CloseHandleOrDie(child_stdin_);
209 child_stdin_ = nullptr;
210 }
211 } else if (signaled_handle == child_stdout_) {
212 char buffer[4096];
213 DWORD n;
214
215 if (!ReadFile(child_stdout_, buffer, sizeof(buffer), &n, nullptr)) {
216 // We're done reading. Close.
217 CloseHandleOrDie(child_stdout_);
218 child_stdout_ = nullptr;
219 } else {
220 output_data->append(buffer, n);
221 }
222 }
223 }
224
225 if (child_stdin_ != nullptr) {
226 // Child did not finish reading input before it closed the output.
227 // Presumably it exited with an error.
228 CloseHandleOrDie(child_stdin_);
229 child_stdin_ = nullptr;
230 }
231
232 DWORD wait_result = WaitForSingleObject(child_handle_, INFINITE);
233
234 if (wait_result == WAIT_FAILED) {
235 ABSL_LOG(FATAL) << "WaitForSingleObject: "
236 << Win32ErrorMessage(GetLastError());
237 } else if (wait_result != WAIT_OBJECT_0) {
238 ABSL_LOG(FATAL) << "WaitForSingleObject: Unexpected return code: "
239 << wait_result;
240 }
241
242 DWORD exit_code;
243 if (!GetExitCodeProcess(child_handle_, &exit_code)) {
244 ABSL_LOG(FATAL) << "GetExitCodeProcess: "
245 << Win32ErrorMessage(GetLastError());
246 }
247
248 CloseHandleOrDie(child_handle_);
249 child_handle_ = nullptr;
250
251 if (exit_code != 0) {
252 *error = absl::Substitute("Plugin failed with status code $0.", exit_code);
253 return false;
254 }
255
256 return true;
257 }
258
Win32ErrorMessage(DWORD error_code)259 std::string Subprocess::Win32ErrorMessage(DWORD error_code) {
260 char* message;
261
262 // WTF?
263 FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
264 FORMAT_MESSAGE_IGNORE_INSERTS,
265 nullptr, error_code,
266 MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
267 (LPSTR)&message, // NOT A BUG!
268 0, nullptr);
269
270 std::string result = message;
271 LocalFree(message);
272 return result;
273 }
274
275 // ===================================================================
276
277 #else // _WIN32
278
Subprocess()279 Subprocess::Subprocess()
280 : child_pid_(-1), child_stdin_(-1), child_stdout_(-1) {}
281
~Subprocess()282 Subprocess::~Subprocess() {
283 if (child_stdin_ != -1) {
284 close(child_stdin_);
285 }
286 if (child_stdout_ != -1) {
287 close(child_stdout_);
288 }
289 }
290
Start(const std::string & program,SearchMode search_mode)291 void Subprocess::Start(const std::string& program, SearchMode search_mode) {
292 // Note that we assume that there are no other threads, thus we don't have to
293 // do crazy stuff like using socket pairs or avoiding libc locks.
294
295 // [0] is read end, [1] is write end.
296 int stdin_pipe[2];
297 int stdout_pipe[2];
298
299 int p0 = pipe(stdin_pipe);
300 int p1 = pipe(stdout_pipe);
301 UPB_ASSERT(p0 != -1);
302 UPB_ASSERT(p1 != -1);
303
304 char* argv[2] = {portable_strdup(program.c_str()), nullptr};
305
306 child_pid_ = fork();
307 if (child_pid_ == -1) {
308 std::cerr << "fork: " << strerror(errno);
309 } else if (child_pid_ == 0) {
310 // We are the child.
311 dup2(stdin_pipe[0], STDIN_FILENO);
312 dup2(stdout_pipe[1], STDOUT_FILENO);
313
314 close(stdin_pipe[0]);
315 close(stdin_pipe[1]);
316 close(stdout_pipe[0]);
317 close(stdout_pipe[1]);
318
319 switch (search_mode) {
320 case SEARCH_PATH:
321 execvp(argv[0], argv);
322 break;
323 case EXACT_NAME:
324 execv(argv[0], argv);
325 break;
326 }
327
328 // Write directly to STDERR_FILENO to avoid stdio code paths that may do
329 // stuff that is unsafe here.
330 int ignored;
331 ignored = write(STDERR_FILENO, argv[0], strlen(argv[0]));
332 const char* message =
333 ": program not found or is not executable\n"
334 "Please specify a program using absolute path or make sure "
335 "the program is available in your PATH system variable\n";
336 ignored = write(STDERR_FILENO, message, strlen(message));
337 (void)ignored;
338
339 // Must use _exit() rather than exit() to avoid flushing output buffers
340 // that will also be flushed by the parent.
341 _exit(1);
342 } else {
343 free(argv[0]);
344
345 close(stdin_pipe[0]);
346 close(stdout_pipe[1]);
347
348 child_stdin_ = stdin_pipe[1];
349 child_stdout_ = stdout_pipe[0];
350 }
351 }
352
Communicate(const std::string & input_data,std::string * output_data,std::string * error)353 bool Subprocess::Communicate(const std::string& input_data,
354 std::string* output_data, std::string* error) {
355 if (child_stdin_ == -1) {
356 std::cerr << "Must call Start() first." << std::endl;
357 UPB_ASSERT(child_stdin_ != -1);
358 }
359
360 // The "sighandler_t" typedef is GNU-specific, so define our own.
361 typedef void SignalHandler(int);
362
363 // Make sure SIGPIPE is disabled so that if the child dies it doesn't kill us.
364 SignalHandler* old_pipe_handler = signal(SIGPIPE, SIG_IGN);
365
366 int input_pos = 0;
367 int max_fd = std::max(child_stdin_, child_stdout_);
368
369 while (child_stdout_ != -1) {
370 fd_set read_fds;
371 fd_set write_fds;
372 FD_ZERO(&read_fds);
373 FD_ZERO(&write_fds);
374 if (child_stdout_ != -1) {
375 FD_SET(child_stdout_, &read_fds);
376 }
377 if (child_stdin_ != -1) {
378 FD_SET(child_stdin_, &write_fds);
379 }
380
381 if (select(max_fd + 1, &read_fds, &write_fds, nullptr, nullptr) < 0) {
382 if (errno == EINTR) {
383 // Interrupted by signal. Try again.
384 continue;
385 } else {
386 std::cerr << "select: " << strerror(errno) << std::endl;
387 UPB_ASSERT(0);
388 }
389 }
390
391 if (child_stdin_ != -1 && FD_ISSET(child_stdin_, &write_fds)) {
392 int n = write(child_stdin_, input_data.data() + input_pos,
393 input_data.size() - input_pos);
394 if (n < 0) {
395 // Child closed pipe. Presumably it will report an error later.
396 // Pretend we're done for now.
397 input_pos = input_data.size();
398 } else {
399 input_pos += n;
400 }
401
402 if (input_pos == (int)input_data.size()) {
403 // We're done writing. Close.
404 close(child_stdin_);
405 child_stdin_ = -1;
406 }
407 }
408
409 if (child_stdout_ != -1 && FD_ISSET(child_stdout_, &read_fds)) {
410 char buffer[4096];
411 int n = read(child_stdout_, buffer, sizeof(buffer));
412
413 if (n > 0) {
414 output_data->append(buffer, (size_t)n);
415 } else {
416 // We're done reading. Close.
417 close(child_stdout_);
418 child_stdout_ = -1;
419 }
420 }
421 }
422
423 if (child_stdin_ != -1) {
424 // Child did not finish reading input before it closed the output.
425 // Presumably it exited with an error.
426 close(child_stdin_);
427 child_stdin_ = -1;
428 }
429
430 int status;
431 while (waitpid(child_pid_, &status, 0) == -1) {
432 if (errno != EINTR) {
433 std::cerr << "waitpid: " << strerror(errno) << std::endl;
434 UPB_ASSERT(0);
435 }
436 }
437
438 // Restore SIGPIPE handling.
439 signal(SIGPIPE, old_pipe_handler);
440
441 if (WIFEXITED(status)) {
442 if (WEXITSTATUS(status) != 0) {
443 int error_code = WEXITSTATUS(status);
444 *error =
445 absl::Substitute("Plugin failed with status code $0.", error_code);
446 return false;
447 }
448 } else if (WIFSIGNALED(status)) {
449 int signal = WTERMSIG(status);
450 *error = absl::Substitute("Plugin killed by signal $0.", signal);
451 return false;
452 } else {
453 *error = "Neither WEXITSTATUS nor WTERMSIG is true?";
454 return false;
455 }
456
457 return true;
458 }
459
460 #endif // !_WIN32
461
462 } // namespace upbc
463