PageRenderTime 47ms CodeModel.GetById 15ms app.highlight 27ms RepoModel.GetById 2ms app.codeStats 0ms

/thirdparty/breakpad/third_party/protobuf/protobuf/src/google/protobuf/compiler/subprocess.cc

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