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