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 
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 
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 
80 Subprocess::~Subprocess() {
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 
161 bool 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_ != 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 =
259  strings::Substitute("Plugin failed with status code $0.", exit_code);
260  return false;
261  }
262 
263  if (!output->ParseFromString(output_data)) {
264  *error = "Plugin output is unparseable: " + CEscape(output_data);
265  return false;
266  }
267 
268  return true;
269 }
270 
271 std::string Subprocess::Win32ErrorMessage(DWORD error_code) {
272  char* message;
273 
274  // WTF?
275  FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
276  FORMAT_MESSAGE_IGNORE_INSERTS,
277  NULL, error_code, 0,
278  (LPSTR)&message, // NOT A BUG!
279  0, NULL);
280 
281  std::string result = message;
282  LocalFree(message);
283  return result;
284 }
285 
286 // ===================================================================
287 
288 #else // _WIN32
289 
291  : child_pid_(-1), child_stdin_(-1), child_stdout_(-1) {}
292 
294  if (child_stdin_ != -1) {
295  close(child_stdin_);
296  }
297  if (child_stdout_ != -1) {
298  close(child_stdout_);
299  }
300 }
301 
302 void Subprocess::Start(const std::string& program, SearchMode search_mode) {
303  // Note that we assume that there are no other threads, thus we don't have to
304  // do crazy stuff like using socket pairs or avoiding libc locks.
305 
306  // [0] is read end, [1] is write end.
307  int stdin_pipe[2];
308  int stdout_pipe[2];
309 
310  GOOGLE_CHECK(pipe(stdin_pipe) != -1);
311  GOOGLE_CHECK(pipe(stdout_pipe) != -1);
312 
313  char* argv[2] = {portable_strdup(program.c_str()), NULL};
314 
315  child_pid_ = fork();
316  if (child_pid_ == -1) {
317  GOOGLE_LOG(FATAL) << "fork: " << strerror(errno);
318  } else if (child_pid_ == 0) {
319  // We are the child.
320  dup2(stdin_pipe[0], STDIN_FILENO);
321  dup2(stdout_pipe[1], STDOUT_FILENO);
322 
323  close(stdin_pipe[0]);
324  close(stdin_pipe[1]);
325  close(stdout_pipe[0]);
326  close(stdout_pipe[1]);
327 
328  switch (search_mode) {
329  case SEARCH_PATH:
330  execvp(argv[0], argv);
331  break;
332  case EXACT_NAME:
333  execv(argv[0], argv);
334  break;
335  }
336 
337  // Write directly to STDERR_FILENO to avoid stdio code paths that may do
338  // stuff that is unsafe here.
339  int ignored;
340  ignored = write(STDERR_FILENO, argv[0], strlen(argv[0]));
341  const char* message =
342  ": program not found or is not executable\n"
343  "Please specify a program using absolute path or make sure "
344  "the program is available in your PATH system variable\n";
345  ignored = write(STDERR_FILENO, message, strlen(message));
346  (void)ignored;
347 
348  // Must use _exit() rather than exit() to avoid flushing output buffers
349  // that will also be flushed by the parent.
350  _exit(1);
351  } else {
352  free(argv[0]);
353 
354  close(stdin_pipe[0]);
355  close(stdout_pipe[1]);
356 
357  child_stdin_ = stdin_pipe[1];
358  child_stdout_ = stdout_pipe[0];
359  }
360 }
361 
363  std::string* error) {
364  GOOGLE_CHECK_NE(child_stdin_, -1) << "Must call Start() first.";
365 
366  // The "sighandler_t" typedef is GNU-specific, so define our own.
367  typedef void SignalHandler(int);
368 
369  // Make sure SIGPIPE is disabled so that if the child dies it doesn't kill us.
370  SignalHandler* old_pipe_handler = signal(SIGPIPE, SIG_IGN);
371 
372  std::string input_data = input.SerializeAsString();
373  std::string output_data;
374 
375  int input_pos = 0;
376  int max_fd = std::max(child_stdin_, child_stdout_);
377 
378  while (child_stdout_ != -1) {
379  fd_set read_fds;
380  fd_set write_fds;
381  FD_ZERO(&read_fds);
382  FD_ZERO(&write_fds);
383  if (child_stdout_ != -1) {
384  FD_SET(child_stdout_, &read_fds);
385  }
386  if (child_stdin_ != -1) {
387  FD_SET(child_stdin_, &write_fds);
388  }
389 
390  if (select(max_fd + 1, &read_fds, &write_fds, NULL, NULL) < 0) {
391  if (errno == EINTR) {
392  // Interrupted by signal. Try again.
393  continue;
394  } else {
395  GOOGLE_LOG(FATAL) << "select: " << strerror(errno);
396  }
397  }
398 
399  if (child_stdin_ != -1 && FD_ISSET(child_stdin_, &write_fds)) {
400  int n = write(child_stdin_, input_data.data() + input_pos,
401  input_data.size() - input_pos);
402  if (n < 0) {
403  // Child closed pipe. Presumably it will report an error later.
404  // Pretend we're done for now.
405  input_pos = input_data.size();
406  } else {
407  input_pos += n;
408  }
409 
410  if (input_pos == input_data.size()) {
411  // We're done writing. Close.
412  close(child_stdin_);
413  child_stdin_ = -1;
414  }
415  }
416 
417  if (child_stdout_ != -1 && FD_ISSET(child_stdout_, &read_fds)) {
418  char buffer[4096];
419  int n = read(child_stdout_, buffer, sizeof(buffer));
420 
421  if (n > 0) {
422  output_data.append(buffer, n);
423  } else {
424  // We're done reading. Close.
425  close(child_stdout_);
426  child_stdout_ = -1;
427  }
428  }
429  }
430 
431  if (child_stdin_ != -1) {
432  // Child did not finish reading input before it closed the output.
433  // Presumably it exited with an error.
434  close(child_stdin_);
435  child_stdin_ = -1;
436  }
437 
438  int status;
439  while (waitpid(child_pid_, &status, 0) == -1) {
440  if (errno != EINTR) {
441  GOOGLE_LOG(FATAL) << "waitpid: " << strerror(errno);
442  }
443  }
444 
445  // Restore SIGPIPE handling.
446  signal(SIGPIPE, old_pipe_handler);
447 
448  if (WIFEXITED(status)) {
449  if (WEXITSTATUS(status) != 0) {
450  int error_code = WEXITSTATUS(status);
451  *error =
452  strings::Substitute("Plugin failed with status code $0.", error_code);
453  return false;
454  }
455  } else if (WIFSIGNALED(status)) {
456  int signal = WTERMSIG(status);
457  *error = strings::Substitute("Plugin killed by signal $0.", signal);
458  return false;
459  } else {
460  *error = "Neither WEXITSTATUS nor WTERMSIG is true?";
461  return false;
462  }
463 
464  if (!output->ParseFromString(output_data)) {
465  *error = "Plugin output is unparseable: " + CEscape(output_data);
466  return false;
467  }
468 
469  return true;
470 }
471 
472 #endif // !_WIN32
473 
474 } // namespace compiler
475 } // namespace protobuf
476 } // namespace google
google::protobuf::compiler::Subprocess::Start
void Start(const std::string &program, SearchMode search_mode)
Definition: subprocess.cc:302
google::protobuf::compiler::Subprocess::SEARCH_PATH
@ SEARCH_PATH
Definition: subprocess.h:63
NULL
NULL
Definition: test_security_zap.cpp:405
EINTR
#define EINTR
Definition: errno.hpp:7
input
std::string input
Definition: tokenizer_unittest.cc:197
FATAL
const int FATAL
Definition: log_severity.h:60
google::protobuf::CEscape
string CEscape(const string &src)
Definition: strutil.cc:615
string
GLsizei const GLchar *const * string
Definition: glcorearb.h:3083
errno
int errno
google::protobuf::compiler::Subprocess::child_pid_
pid_t child_pid_
Definition: subprocess.h:95
ReadFile
std::string ReadFile(const std::string &name)
Definition: cpp_benchmark.cc:194
error
Definition: cJSON.c:88
subprocess.h
gmock_test_utils.Subprocess
Subprocess
Definition: gmock_test_utils.py:96
google::protobuf::compiler::Subprocess::SearchMode
SearchMode
Definition: subprocess.h:62
google::protobuf::compiler::Subprocess::child_stdin_
int child_stdin_
Definition: subprocess.h:99
message.h
google::protobuf::compiler::Subprocess::Subprocess
Subprocess()
Definition: subprocess.cc:290
GOOGLE_LOG
#define GOOGLE_LOG(LEVEL)
Definition: logging.h:146
google::protobuf::compiler::Subprocess::EXACT_NAME
@ EXACT_NAME
Definition: subprocess.h:64
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: substitute.cc:55
buffer
Definition: buffer_processor.h:43
GOOGLE_CHECK_NE
#define GOOGLE_CHECK_NE(A, B)
Definition: logging.h:157
GOOGLE_CHECK
#define GOOGLE_CHECK(EXPRESSION)
Definition: logging.h:153
void
typedef void(APIENTRY *GLDEBUGPROCARB)(GLenum source
n
GLdouble n
Definition: glcorearb.h:4153
google::protobuf::compiler::Subprocess::~Subprocess
~Subprocess()
Definition: subprocess.cc:293
google::protobuf::Message
Definition: src/google/protobuf/message.h:205
common.h
google::protobuf::compiler::Subprocess::Communicate
bool Communicate(const Message &input, Message *output, std::string *error)
Definition: subprocess.cc:362
logging.h
HANDLE
void * HANDLE
Definition: wepoll.c:70
strerror
char * strerror(int errno)
substitute.h
program
GLbitfield GLuint program
Definition: glcorearb.h:4044
google::protobuf::compiler::Subprocess::child_stdout_
int child_stdout_
Definition: subprocess.h:100
output
const upb_json_parsermethod const upb_symtab upb_sink * output
Definition: ruby/ext/google/protobuf_c/upb.h:10503
google::protobuf.text_encoding.CEscape
def CEscape(text, as_utf8)
Definition: text_encoding.py:58
compiler
Definition: plugin.pb.cc:22
google
Definition: data_proto2_to_proto3_util.h:11
message
GLenum GLuint GLenum GLsizei const GLchar * message
Definition: glcorearb.h:2695


libaditof
Author(s):
autogenerated on Wed May 21 2025 02:06:59