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