• 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 =
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 
Win32ErrorMessage(DWORD error_code)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 
Subprocess()290 Subprocess::Subprocess()
291     : child_pid_(-1), child_stdin_(-1), child_stdout_(-1) {}
292 
~Subprocess()293 Subprocess::~Subprocess() {
294   if (child_stdin_ != -1) {
295     close(child_stdin_);
296   }
297   if (child_stdout_ != -1) {
298     close(child_stdout_);
299   }
300 }
301 
Start(const std::string & program,SearchMode search_mode)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 
Communicate(const Message & input,Message * output,std::string * error)362 bool Subprocess::Communicate(const Message& input, Message* output,
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
477