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