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