1// 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>
36#include <cstring>
37#include <iostream>
38
39#ifndef _WIN32
40#include <errno.h>
41#include <signal.h>
42#include <sys/select.h>
43#include <sys/wait.h>
44#endif
45
46#include <google/protobuf/stubs/logging.h>
47#include <google/protobuf/stubs/common.h>
48#include <google/protobuf/stubs/substitute.h>
49#include <google/protobuf/message.h>
50
51namespace google {
52namespace protobuf {
53namespace compiler {
54
55namespace {
56char* portable_strdup(const char* s) {
57 char* ns = (char*)malloc(size: strlen(s: s) + 1);
58 if (ns != nullptr) {
59 strcpy(dest: ns, src: s);
60 }
61 return ns;
62}
63} // namespace
64
65#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_(nullptr),
77 child_stdin_(nullptr),
78 child_stdout_(nullptr) {}
79
80Subprocess::~Subprocess() {
81 if (child_stdin_ != nullptr) {
82 CloseHandleOrDie(child_stdin_);
83 }
84 if (child_stdout_ != nullptr) {
85 CloseHandleOrDie(child_stdout_);
86 }
87}
88
89void Subprocess::Start(const std::string& program, SearchMode search_mode) {
90 // Create the pipes.
91 HANDLE stdin_pipe_read;
92 HANDLE stdin_pipe_write;
93 HANDLE stdout_pipe_read;
94 HANDLE stdout_pipe_write;
95
96 if (!CreatePipe(&stdin_pipe_read, &stdin_pipe_write, nullptr, 0)) {
97 GOOGLE_LOG(FATAL) << "CreatePipe: " << Win32ErrorMessage(GetLastError());
98 }
99 if (!CreatePipe(&stdout_pipe_read, &stdout_pipe_write, nullptr, 0)) {
100 GOOGLE_LOG(FATAL) << "CreatePipe: " << Win32ErrorMessage(GetLastError());
101 }
102
103 // Make child side of the pipes inheritable.
104 if (!SetHandleInformation(stdin_pipe_read, HANDLE_FLAG_INHERIT,
105 HANDLE_FLAG_INHERIT)) {
106 GOOGLE_LOG(FATAL) << "SetHandleInformation: "
107 << Win32ErrorMessage(GetLastError());
108 }
109 if (!SetHandleInformation(stdout_pipe_write, HANDLE_FLAG_INHERIT,
110 HANDLE_FLAG_INHERIT)) {
111 GOOGLE_LOG(FATAL) << "SetHandleInformation: "
112 << Win32ErrorMessage(GetLastError());
113 }
114
115 // Setup STARTUPINFO to redirect handles.
116 STARTUPINFOA startup_info;
117 ZeroMemory(&startup_info, sizeof(startup_info));
118 startup_info.cb = sizeof(startup_info);
119 startup_info.dwFlags = STARTF_USESTDHANDLES;
120 startup_info.hStdInput = stdin_pipe_read;
121 startup_info.hStdOutput = stdout_pipe_write;
122 startup_info.hStdError = GetStdHandle(STD_ERROR_HANDLE);
123
124 if (startup_info.hStdError == INVALID_HANDLE_VALUE) {
125 GOOGLE_LOG(FATAL) << "GetStdHandle: " << Win32ErrorMessage(GetLastError());
126 }
127
128 // Invoking cmd.exe allows for '.bat' files from the path as well as '.exe'.
129 // Using a malloc'ed string because CreateProcess() can mutate its second
130 // parameter.
131 char* command_line =
132 portable_strdup(("cmd.exe /c \"" + program + "\"").c_str());
133
134 // Create the process.
135 PROCESS_INFORMATION process_info;
136
137 if (CreateProcessA((search_mode == SEARCH_PATH) ? nullptr : program.c_str(),
138 (search_mode == SEARCH_PATH) ? command_line : nullptr,
139 nullptr, // process security attributes
140 nullptr, // thread security attributes
141 TRUE, // inherit handles?
142 0, // obscure creation flags
143 nullptr, // environment (inherit from parent)
144 nullptr, // current directory (inherit from parent)
145 &startup_info, &process_info)) {
146 child_handle_ = process_info.hProcess;
147 CloseHandleOrDie(process_info.hThread);
148 child_stdin_ = stdin_pipe_write;
149 child_stdout_ = stdout_pipe_read;
150 } else {
151 process_start_error_ = GetLastError();
152 CloseHandleOrDie(stdin_pipe_write);
153 CloseHandleOrDie(stdout_pipe_read);
154 }
155
156 CloseHandleOrDie(stdin_pipe_read);
157 CloseHandleOrDie(stdout_pipe_write);
158 free(command_line);
159}
160
161bool Subprocess::Communicate(const Message& input, Message* output,
162 std::string* error) {
163 if (process_start_error_ != ERROR_SUCCESS) {
164 *error = Win32ErrorMessage(process_start_error_);
165 return false;
166 }
167
168 GOOGLE_CHECK(child_handle_ != nullptr) << "Must call Start() first.";
169
170 std::string input_data;
171 if (!input.SerializeToString(&input_data)) {
172 *error = "Failed to serialize request.";
173 return false;
174 }
175 std::string output_data;
176
177 int input_pos = 0;
178
179 while (child_stdout_ != nullptr) {
180 HANDLE handles[2];
181 int handle_count = 0;
182
183 if (child_stdin_ != nullptr) {
184 handles[handle_count++] = child_stdin_;
185 }
186 if (child_stdout_ != nullptr) {
187 handles[handle_count++] = child_stdout_;
188 }
189
190 DWORD wait_result =
191 WaitForMultipleObjects(handle_count, handles, FALSE, INFINITE);
192
193 HANDLE signaled_handle = nullptr;
194 if (wait_result >= WAIT_OBJECT_0 &&
195 wait_result < WAIT_OBJECT_0 + handle_count) {
196 signaled_handle = handles[wait_result - WAIT_OBJECT_0];
197 } else if (wait_result == WAIT_FAILED) {
198 GOOGLE_LOG(FATAL) << "WaitForMultipleObjects: "
199 << Win32ErrorMessage(GetLastError());
200 } else {
201 GOOGLE_LOG(FATAL) << "WaitForMultipleObjects: Unexpected return code: "
202 << wait_result;
203 }
204
205 if (signaled_handle == child_stdin_) {
206 DWORD n;
207 if (!WriteFile(child_stdin_, input_data.data() + input_pos,
208 input_data.size() - input_pos, &n, nullptr)) {
209 // Child closed pipe. Presumably it will report an error later.
210 // Pretend we're done for now.
211 input_pos = input_data.size();
212 } else {
213 input_pos += n;
214 }
215
216 if (input_pos == input_data.size()) {
217 // We're done writing. Close.
218 CloseHandleOrDie(child_stdin_);
219 child_stdin_ = nullptr;
220 }
221 } else if (signaled_handle == child_stdout_) {
222 char buffer[4096];
223 DWORD n;
224
225 if (!ReadFile(child_stdout_, buffer, sizeof(buffer), &n, nullptr)) {
226 // We're done reading. Close.
227 CloseHandleOrDie(child_stdout_);
228 child_stdout_ = nullptr;
229 } else {
230 output_data.append(buffer, n);
231 }
232 }
233 }
234
235 if (child_stdin_ != nullptr) {
236 // Child did not finish reading input before it closed the output.
237 // Presumably it exited with an error.
238 CloseHandleOrDie(child_stdin_);
239 child_stdin_ = nullptr;
240 }
241
242 DWORD wait_result = WaitForSingleObject(child_handle_, INFINITE);
243
244 if (wait_result == WAIT_FAILED) {
245 GOOGLE_LOG(FATAL) << "WaitForSingleObject: "
246 << Win32ErrorMessage(GetLastError());
247 } else if (wait_result != WAIT_OBJECT_0) {
248 GOOGLE_LOG(FATAL) << "WaitForSingleObject: Unexpected return code: "
249 << wait_result;
250 }
251
252 DWORD exit_code;
253 if (!GetExitCodeProcess(child_handle_, &exit_code)) {
254 GOOGLE_LOG(FATAL) << "GetExitCodeProcess: "
255 << Win32ErrorMessage(GetLastError());
256 }
257
258 CloseHandleOrDie(child_handle_);
259 child_handle_ = nullptr;
260
261 if (exit_code != 0) {
262 *error = strings::Substitute("Plugin failed with status code $0.", exit_code);
263 return false;
264 }
265
266 if (!output->ParseFromString(output_data)) {
267 *error = "Plugin output is unparseable: " + CEscape(output_data);
268 return false;
269 }
270
271 return true;
272}
273
274std::string Subprocess::Win32ErrorMessage(DWORD error_code) {
275 char* message;
276
277 // WTF?
278 FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
279 FORMAT_MESSAGE_IGNORE_INSERTS,
280 nullptr, error_code,
281 MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
282 (LPSTR)&message, // NOT A BUG!
283 0, nullptr);
284
285 std::string result = message;
286 LocalFree(message);
287 return result;
288}
289
290// ===================================================================
291
292#else // _WIN32
293
294Subprocess::Subprocess()
295 : child_pid_(-1), child_stdin_(-1), child_stdout_(-1) {}
296
297Subprocess::~Subprocess() {
298 if (child_stdin_ != -1) {
299 close(fd: child_stdin_);
300 }
301 if (child_stdout_ != -1) {
302 close(fd: child_stdout_);
303 }
304}
305
306void Subprocess::Start(const std::string& program, SearchMode search_mode) {
307 // Note that we assume that there are no other threads, thus we don't have to
308 // do crazy stuff like using socket pairs or avoiding libc locks.
309
310 // [0] is read end, [1] is write end.
311 int stdin_pipe[2];
312 int stdout_pipe[2];
313
314 GOOGLE_CHECK(pipe(stdin_pipe) != -1);
315 GOOGLE_CHECK(pipe(stdout_pipe) != -1);
316
317 char* argv[2] = {portable_strdup(s: program.c_str()), nullptr};
318
319 child_pid_ = fork();
320 if (child_pid_ == -1) {
321 GOOGLE_LOG(FATAL) << "fork: " << strerror(errno);
322 } else if (child_pid_ == 0) {
323 // We are the child.
324 dup2(fd: stdin_pipe[0], STDIN_FILENO);
325 dup2(fd: stdout_pipe[1], STDOUT_FILENO);
326
327 close(fd: stdin_pipe[0]);
328 close(fd: stdin_pipe[1]);
329 close(fd: stdout_pipe[0]);
330 close(fd: stdout_pipe[1]);
331
332 switch (search_mode) {
333 case SEARCH_PATH:
334 execvp(file: argv[0], argv: argv);
335 break;
336 case EXACT_NAME:
337 execv(path: argv[0], argv: argv);
338 break;
339 }
340
341 // Write directly to STDERR_FILENO to avoid stdio code paths that may do
342 // stuff that is unsafe here.
343 int ignored;
344 ignored = write(STDERR_FILENO, buf: argv[0], n: strlen(s: argv[0]));
345 const char* message =
346 ": program not found or is not executable\n"
347 "Please specify a program using absolute path or make sure "
348 "the program is available in your PATH system variable\n";
349 ignored = write(STDERR_FILENO, buf: message, n: strlen(s: message));
350 (void)ignored;
351
352 // Must use _exit() rather than exit() to avoid flushing output buffers
353 // that will also be flushed by the parent.
354 _exit(status: 1);
355 } else {
356 free(ptr: argv[0]);
357
358 close(fd: stdin_pipe[0]);
359 close(fd: stdout_pipe[1]);
360
361 child_stdin_ = stdin_pipe[1];
362 child_stdout_ = stdout_pipe[0];
363 }
364}
365
366bool Subprocess::Communicate(const Message& input, Message* output,
367 std::string* error) {
368 GOOGLE_CHECK_NE(child_stdin_, -1) << "Must call Start() first.";
369
370 // The "sighandler_t" typedef is GNU-specific, so define our own.
371 typedef void SignalHandler(int);
372
373 // Make sure SIGPIPE is disabled so that if the child dies it doesn't kill us.
374 SignalHandler* old_pipe_handler = signal(SIGPIPE, SIG_IGN);
375
376 std::string input_data;
377 if (!input.SerializeToString(output: &input_data)) {
378 *error = "Failed to serialize request.";
379 return false;
380 }
381 std::string output_data;
382
383 int input_pos = 0;
384 int max_fd = std::max(child_stdin_, child_stdout_);
385
386 while (child_stdout_ != -1) {
387 fd_set read_fds;
388 fd_set write_fds;
389 FD_ZERO(&read_fds);
390 FD_ZERO(&write_fds);
391 if (child_stdout_ != -1) {
392 FD_SET(child_stdout_, &read_fds);
393 }
394 if (child_stdin_ != -1) {
395 FD_SET(child_stdin_, &write_fds);
396 }
397
398 if (select(nfds: max_fd + 1, readfds: &read_fds, writefds: &write_fds, exceptfds: nullptr, timeout: nullptr) < 0) {
399 if (errno == EINTR) {
400 // Interrupted by signal. Try again.
401 continue;
402 } else {
403 GOOGLE_LOG(FATAL) << "select: " << strerror(errno);
404 }
405 }
406
407 if (child_stdin_ != -1 && FD_ISSET(child_stdin_, &write_fds)) {
408 int n = write(fd: child_stdin_, buf: input_data.data() + input_pos,
409 n: input_data.size() - input_pos);
410 if (n < 0) {
411 // Child closed pipe. Presumably it will report an error later.
412 // Pretend we're done for now.
413 input_pos = input_data.size();
414 } else {
415 input_pos += n;
416 }
417
418 if (input_pos == input_data.size()) {
419 // We're done writing. Close.
420 close(fd: child_stdin_);
421 child_stdin_ = -1;
422 }
423 }
424
425 if (child_stdout_ != -1 && FD_ISSET(child_stdout_, &read_fds)) {
426 char buffer[4096];
427 int n = read(fd: child_stdout_, buf: buffer, nbytes: sizeof(buffer));
428
429 if (n > 0) {
430 output_data.append(s: buffer, n: n);
431 } else {
432 // We're done reading. Close.
433 close(fd: child_stdout_);
434 child_stdout_ = -1;
435 }
436 }
437 }
438
439 if (child_stdin_ != -1) {
440 // Child did not finish reading input before it closed the output.
441 // Presumably it exited with an error.
442 close(fd: child_stdin_);
443 child_stdin_ = -1;
444 }
445
446 int status;
447 while (waitpid(pid: child_pid_, stat_loc: &status, options: 0) == -1) {
448 if (errno != EINTR) {
449 GOOGLE_LOG(FATAL) << "waitpid: " << strerror(errno);
450 }
451 }
452
453 // Restore SIGPIPE handling.
454 signal(SIGPIPE, handler: old_pipe_handler);
455
456 if (WIFEXITED(status)) {
457 if (WEXITSTATUS(status) != 0) {
458 int error_code = WEXITSTATUS(status);
459 *error =
460 strings::Substitute(format: "Plugin failed with status code $0.", arg0: error_code);
461 return false;
462 }
463 } else if (WIFSIGNALED(status)) {
464 int signal = WTERMSIG(status);
465 *error = strings::Substitute(format: "Plugin killed by signal $0.", arg0: signal);
466 return false;
467 } else {
468 *error = "Neither WEXITSTATUS nor WTERMSIG is true?";
469 return false;
470 }
471
472 if (!output->ParseFromString(data: output_data)) {
473 *error = "Plugin output is unparseable: " + CEscape(src: output_data);
474 return false;
475 }
476
477 return true;
478}
479
480#endif // !_WIN32
481
482} // namespace compiler
483} // namespace protobuf
484} // namespace google
485