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