LCOV - code coverage report
Current view: top level - third_party/protobuf/src/google/protobuf/compiler - subprocess.cc (source / functions) Hit Total Coverage
Test: tmp.zDYK9MVh93 Lines: 54 89 60.7 %
Date: 2015-10-10 Functions: 6 6 100.0 %

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

Generated by: LCOV version 1.10