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