• 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 <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 
CloseHandleOrDie(HANDLE handle)57 static void CloseHandleOrDie(HANDLE handle) {
58   if (!CloseHandle(handle)) {
59     GOOGLE_LOG(FATAL) << "CloseHandle: "
60                       << Subprocess::Win32ErrorMessage(GetLastError());
61   }
62 }
63 
Subprocess()64 Subprocess::Subprocess()
65     : process_start_error_(ERROR_SUCCESS),
66       child_handle_(NULL), child_stdin_(NULL), child_stdout_(NULL) {}
67 
~Subprocess()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 
Start(const string & program,SearchMode search_mode)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 
Communicate(const Message & input,Message * output,string * error)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 
Win32ErrorMessage(DWORD error_code)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