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