third_party/protobuf/src/google/protobuf/compiler/subprocess.cc
Go to the documentation of this file.
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/message.h>
49 #include <google/protobuf/stubs/substitute.h>
50 
51 namespace google {
52 namespace protobuf {
53 namespace compiler {
54 
55 namespace {
56 char* 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 
65 #ifdef _WIN32
66 
67 static void CloseHandleOrDie(HANDLE handle) {
68  if (!CloseHandle(handle)) {
69  GOOGLE_LOG(FATAL) << "CloseHandle: "
70  << Subprocess::Win32ErrorMessage(GetLastError());
71  }
72 }
73 
75  : process_start_error_(ERROR_SUCCESS),
76  child_handle_(NULL),
77  child_stdin_(NULL),
78  child_stdout_(NULL) {}
79 
81  if (child_stdin_ != NULL) {
82  CloseHandleOrDie(child_stdin_);
83  }
84  if (child_stdout_ != NULL) {
85  CloseHandleOrDie(child_stdout_);
86  }
87 }
88 
89 void 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, NULL, 0)) {
97  GOOGLE_LOG(FATAL) << "CreatePipe: " << Win32ErrorMessage(GetLastError());
98  }
99  if (!CreatePipe(&stdout_pipe_read, &stdout_pipe_write, NULL, 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) ? NULL : program.c_str(),
138  (search_mode == SEARCH_PATH) ? command_line : NULL,
139  NULL, // process security attributes
140  NULL, // thread security attributes
141  TRUE, // inherit handles?
142  0, // obscure creation flags
143  NULL, // environment (inherit from parent)
144  NULL, // 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 
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_ != NULL) << "Must call Start() first.";
169 
170  std::string input_data = input.SerializeAsString();
171  std::string output_data;
172 
173  int input_pos = 0;
174 
175  while (child_stdout_ != NULL) {
176  HANDLE handles[2];
177  int handle_count = 0;
178 
179  if (child_stdin_ != NULL) {
180  handles[handle_count++] = child_stdin_;
181  }
182  if (child_stdout_ != NULL) {
183  handles[handle_count++] = child_stdout_;
184  }
185 
186  DWORD wait_result =
187  WaitForMultipleObjects(handle_count, handles, FALSE, INFINITE);
188 
189  HANDLE signaled_handle = NULL;
190  if (wait_result >= WAIT_OBJECT_0 &&
191  wait_result < WAIT_OBJECT_0 + handle_count) {
192  signaled_handle = handles[wait_result - WAIT_OBJECT_0];
193  } else if (wait_result == WAIT_FAILED) {
194  GOOGLE_LOG(FATAL) << "WaitForMultipleObjects: "
195  << Win32ErrorMessage(GetLastError());
196  } else {
197  GOOGLE_LOG(FATAL) << "WaitForMultipleObjects: Unexpected return code: "
198  << wait_result;
199  }
200 
201  if (signaled_handle == child_stdin_) {
202  DWORD n;
203  if (!WriteFile(child_stdin_, input_data.data() + input_pos,
204  input_data.size() - input_pos, &n, NULL)) {
205  // Child closed pipe. Presumably it will report an error later.
206  // Pretend we're done for now.
207  input_pos = input_data.size();
208  } else {
209  input_pos += n;
210  }
211 
212  if (input_pos == input_data.size()) {
213  // We're done writing. Close.
214  CloseHandleOrDie(child_stdin_);
215  child_stdin_ = NULL;
216  }
217  } else if (signaled_handle == child_stdout_) {
218  char buffer[4096];
219  DWORD n;
220 
221  if (!ReadFile(child_stdout_, buffer, sizeof(buffer), &n, NULL)) {
222  // We're done reading. Close.
223  CloseHandleOrDie(child_stdout_);
224  child_stdout_ = NULL;
225  } else {
226  output_data.append(buffer, n);
227  }
228  }
229  }
230 
231  if (child_stdin_ != NULL) {
232  // Child did not finish reading input before it closed the output.
233  // Presumably it exited with an error.
234  CloseHandleOrDie(child_stdin_);
235  child_stdin_ = NULL;
236  }
237 
238  DWORD wait_result = WaitForSingleObject(child_handle_, INFINITE);
239 
240  if (wait_result == WAIT_FAILED) {
241  GOOGLE_LOG(FATAL) << "WaitForSingleObject: "
242  << Win32ErrorMessage(GetLastError());
243  } else if (wait_result != WAIT_OBJECT_0) {
244  GOOGLE_LOG(FATAL) << "WaitForSingleObject: Unexpected return code: "
245  << wait_result;
246  }
247 
248  DWORD exit_code;
249  if (!GetExitCodeProcess(child_handle_, &exit_code)) {
250  GOOGLE_LOG(FATAL) << "GetExitCodeProcess: "
251  << Win32ErrorMessage(GetLastError());
252  }
253 
254  CloseHandleOrDie(child_handle_);
255  child_handle_ = NULL;
256 
257  if (exit_code != 0) {
258  *error = strings::Substitute("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 
270 std::string Subprocess::Win32ErrorMessage(DWORD error_code) {
271  char* message;
272 
273  // WTF?
274  FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
275  FORMAT_MESSAGE_IGNORE_INSERTS,
276  NULL, error_code, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
277  (LPSTR)&message, // NOT A BUG!
278  0, NULL);
279 
281  LocalFree(message);
282  return result;
283 }
284 
285 // ===================================================================
286 
287 #else // _WIN32
288 
290  : child_pid_(-1), child_stdin_(-1), child_stdout_(-1) {}
291 
293  if (child_stdin_ != -1) {
295  }
296  if (child_stdout_ != -1) {
298  }
299 }
300 
301 void Subprocess::Start(const std::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 
312  char* argv[2] = {portable_strdup(program.c_str()), NULL};
313 
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 =
341  ": program not found or is not executable\n"
342  "Please specify a program using absolute path or make sure "
343  "the program is available in your PATH system variable\n";
344  ignored = write(STDERR_FILENO, message, strlen(message));
345  (void)ignored;
346 
347  // Must use _exit() rather than exit() to avoid flushing output buffers
348  // that will also be flushed by the parent.
349  _exit(1);
350  } else {
351  free(argv[0]);
352 
353  close(stdin_pipe[0]);
354  close(stdout_pipe[1]);
355 
358  }
359 }
360 
362  std::string* error) {
363  GOOGLE_CHECK_NE(child_stdin_, -1) << "Must call Start() first.";
364 
365  // The "sighandler_t" typedef is GNU-specific, so define our own.
366  typedef void SignalHandler(int);
367 
368  // Make sure SIGPIPE is disabled so that if the child dies it doesn't kill us.
369  SignalHandler* old_pipe_handler = signal(SIGPIPE, SIG_IGN);
370 
371  std::string input_data = input.SerializeAsString();
372  std::string output_data;
373 
374  int input_pos = 0;
375  int max_fd = std::max(child_stdin_, child_stdout_);
376 
377  while (child_stdout_ != -1) {
378  fd_set read_fds;
379  fd_set write_fds;
380  FD_ZERO(&read_fds);
381  FD_ZERO(&write_fds);
382  if (child_stdout_ != -1) {
383  FD_SET(child_stdout_, &read_fds);
384  }
385  if (child_stdin_ != -1) {
386  FD_SET(child_stdin_, &write_fds);
387  }
388 
389  if (select(max_fd + 1, &read_fds, &write_fds, NULL, NULL) < 0) {
390  if (errno == EINTR) {
391  // Interrupted by signal. Try again.
392  continue;
393  } else {
394  GOOGLE_LOG(FATAL) << "select: " << strerror(errno);
395  }
396  }
397 
398  if (child_stdin_ != -1 && FD_ISSET(child_stdin_, &write_fds)) {
399  int n = write(child_stdin_, input_data.data() + input_pos,
400  input_data.size() - input_pos);
401  if (n < 0) {
402  // Child closed pipe. Presumably it will report an error later.
403  // Pretend we're done for now.
404  input_pos = input_data.size();
405  } else {
406  input_pos += n;
407  }
408 
409  if (input_pos == input_data.size()) {
410  // We're done writing. Close.
412  child_stdin_ = -1;
413  }
414  }
415 
416  if (child_stdout_ != -1 && FD_ISSET(child_stdout_, &read_fds)) {
417  char buffer[4096];
418  int n = read(child_stdout_, buffer, sizeof(buffer));
419 
420  if (n > 0) {
421  output_data.append(buffer, n);
422  } else {
423  // We're done reading. Close.
425  child_stdout_ = -1;
426  }
427  }
428  }
429 
430  if (child_stdin_ != -1) {
431  // Child did not finish reading input before it closed the output.
432  // Presumably it exited with an error.
434  child_stdin_ = -1;
435  }
436 
437  int status;
438  while (waitpid(child_pid_, &status, 0) == -1) {
439  if (errno != EINTR) {
440  GOOGLE_LOG(FATAL) << "waitpid: " << strerror(errno);
441  }
442  }
443 
444  // Restore SIGPIPE handling.
445  signal(SIGPIPE, old_pipe_handler);
446 
447  if (WIFEXITED(status)) {
448  if (WEXITSTATUS(status) != 0) {
449  int error_code = WEXITSTATUS(status);
450  *error =
451  strings::Substitute("Plugin failed with status code $0.", error_code);
452  return false;
453  }
454  } else if (WIFSIGNALED(status)) {
455  int signal = WTERMSIG(status);
456  *error = strings::Substitute("Plugin killed by signal $0.", signal);
457  return false;
458  } else {
459  *error = "Neither WEXITSTATUS nor WTERMSIG is true?";
460  return false;
461  }
462 
463  if (!output->ParseFromString(output_data)) {
464  *error = "Plugin output is unparseable: " + CEscape(output_data);
465  return false;
466  }
467 
468  return true;
469 }
470 
471 #endif // !_WIN32
472 
473 } // namespace compiler
474 } // namespace protobuf
475 } // namespace google
_gevent_test_main.result
result
Definition: _gevent_test_main.py:96
stdin_pipe
uv_pipe_t stdin_pipe
Definition: libuv/docs/code/uvtee/main.c:15
write
#define write
Definition: test-fs.c:47
cpp.utils.ReadFile
def ReadFile(filename, print_error=True)
Definition: bloaty/third_party/googletest/googlemock/scripts/generator/cpp/utils.py:30
handles
Definition: test-ipc-send-recv.c:35
testing::internal::string
::std::string string
Definition: bloaty/third_party/protobuf/third_party/googletest/googletest/include/gtest/internal/gtest-port.h:881
error
grpc_error_handle error
Definition: retry_filter.cc:499
google::protobuf::CEscape
string CEscape(const string &src)
Definition: bloaty/third_party/protobuf/src/google/protobuf/stubs/strutil.cc:615
google::protobuf
Definition: bloaty/third_party/protobuf/benchmarks/util/data_proto2_to_proto3_util.h:12
status
absl::Status status
Definition: rls.cc:251
message
char * message
Definition: libuv/docs/code/tty-gravity/main.c:12
gen_stats_data.c_str
def c_str(s, encoding='ascii')
Definition: gen_stats_data.py:38
max
int max
Definition: bloaty/third_party/zlib/examples/enough.c:170
signal
static void signal(notification *n)
Definition: alts_tsi_handshaker_test.cc:107
gmock_output_test.output
output
Definition: bloaty/third_party/googletest/googlemock/test/gmock_output_test.py:175
google::protobuf::compiler::Subprocess::child_stdout_
int child_stdout_
Definition: third_party/bloaty/third_party/protobuf/src/google/protobuf/compiler/subprocess.h:100
close
#define close
Definition: test-fs.c:48
google::protobuf::strings::Substitute
string Substitute(const char *format, const SubstituteArg &arg0, const SubstituteArg &arg1, const SubstituteArg &arg2, const SubstituteArg &arg3, const SubstituteArg &arg4, const SubstituteArg &arg5, const SubstituteArg &arg6, const SubstituteArg &arg7, const SubstituteArg &arg8, const SubstituteArg &arg9)
Definition: bloaty/third_party/protobuf/src/google/protobuf/stubs/substitute.cc:55
buffer
char buffer[1024]
Definition: libuv/docs/code/idle-compute/main.c:8
google::protobuf::compiler::Subprocess::EXACT_NAME
@ EXACT_NAME
Definition: third_party/bloaty/third_party/protobuf/src/google/protobuf/compiler/subprocess.h:64
n
int n
Definition: abseil-cpp/absl/container/btree_test.cc:1080
FATAL
#define FATAL(msg)
Definition: task.h:88
stdout_pipe
uv_pipe_t stdout_pipe
Definition: libuv/docs/code/uvtee/main.c:16
read
int read(izstream &zs, T *x, Items items)
Definition: bloaty/third_party/zlib/contrib/iostream2/zstream.h:115
google::protobuf::compiler::Subprocess::SEARCH_PATH
@ SEARCH_PATH
Definition: third_party/bloaty/third_party/protobuf/src/google/protobuf/compiler/subprocess.h:63
google::protobuf::compiler::Subprocess::~Subprocess
~Subprocess()
Definition: third_party/bloaty/third_party/protobuf/src/google/protobuf/compiler/subprocess.cc:293
google::protobuf::compiler::Subprocess::Communicate
bool Communicate(const Message &input, Message *output, std::string *error)
Definition: third_party/bloaty/third_party/protobuf/src/google/protobuf/compiler/subprocess.cc:362
INVALID_HANDLE_VALUE
#define INVALID_HANDLE_VALUE
Definition: bloaty/third_party/zlib/contrib/minizip/iowin32.c:21
GOOGLE_CHECK
#define GOOGLE_CHECK(EXPRESSION)
Definition: bloaty/third_party/protobuf/src/google/protobuf/stubs/logging.h:153
google::protobuf::compiler::Subprocess::Start
void Start(const std::string &program, SearchMode search_mode)
Definition: third_party/bloaty/third_party/protobuf/src/google/protobuf/compiler/subprocess.cc:302
google::protobuf::compiler::Subprocess::child_stdin_
int child_stdin_
Definition: third_party/bloaty/third_party/protobuf/src/google/protobuf/compiler/subprocess.h:99
input
std::string input
Definition: bloaty/third_party/protobuf/src/google/protobuf/io/tokenizer_unittest.cc:197
ns
static int64_t ns
Definition: bloaty/third_party/re2/util/benchmark.cc:43
handle
static csh handle
Definition: test_arm_regression.c:16
GOOGLE_CHECK_NE
#define GOOGLE_CHECK_NE(A, B)
Definition: bloaty/third_party/protobuf/src/google/protobuf/stubs/logging.h:157
google::protobuf::compiler::Subprocess::Subprocess
Subprocess()
Definition: third_party/bloaty/third_party/protobuf/src/google/protobuf/compiler/subprocess.cc:290
GOOGLE_LOG
#define GOOGLE_LOG(LEVEL)
Definition: bloaty/third_party/protobuf/src/google/protobuf/stubs/logging.h:146
google::protobuf::compiler::Subprocess::child_pid_
pid_t child_pid_
Definition: third_party/bloaty/third_party/protobuf/src/google/protobuf/compiler/subprocess.h:95
run_tests.exit_code
int exit_code
Definition: run_tests.py:1701
compiler
Definition: bloaty/third_party/protobuf/src/google/protobuf/compiler/plugin.pb.cc:21
google
Definition: bloaty/third_party/protobuf/benchmarks/util/data_proto2_to_proto3_util.h:11
errno.h
Message
Definition: protobuf/php/ext/google/protobuf/message.c:53


grpc
Author(s):
autogenerated on Fri May 16 2025 03:00:23