• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 {
portable_strdup(const char * s)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 
CloseHandleOrDie(HANDLE handle)67 static void CloseHandleOrDie(HANDLE handle) {
68   if (!CloseHandle(handle)) {
69     GOOGLE_LOG(FATAL) << "CloseHandle: "
70                       << Subprocess::Win32ErrorMessage(GetLastError());
71   }
72 }
73 
Subprocess()74 Subprocess::Subprocess()
75     : process_start_error_(ERROR_SUCCESS),
76       child_handle_(NULL),
77       child_stdin_(NULL),
78       child_stdout_(NULL) {}
79 
~Subprocess()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 
Start(const std::string & program,SearchMode search_mode)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 
Communicate(const Message & input,Message * output,std::string * error)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 = 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 
Win32ErrorMessage(DWORD error_code)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 
280   std::string result = message;
281   LocalFree(message);
282   return result;
283 }
284 
285 // ===================================================================
286 
287 #else  // _WIN32
288 
Subprocess()289 Subprocess::Subprocess()
290     : child_pid_(-1), child_stdin_(-1), child_stdout_(-1) {}
291 
~Subprocess()292 Subprocess::~Subprocess() {
293   if (child_stdin_ != -1) {
294     close(child_stdin_);
295   }
296   if (child_stdout_ != -1) {
297     close(child_stdout_);
298   }
299 }
300 
Start(const std::string & program,SearchMode search_mode)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 
356     child_stdin_ = stdin_pipe[1];
357     child_stdout_ = stdout_pipe[0];
358   }
359 }
360 
Communicate(const Message & input,Message * output,std::string * error)361 bool Subprocess::Communicate(const Message& input, Message* output,
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.
411         close(child_stdin_);
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.
424         close(child_stdout_);
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.
433     close(child_stdin_);
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
476