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/stubs/substitute.h> |
49 | #include <google/protobuf/message.h> |
50 | |
51 | namespace google { |
52 | namespace protobuf { |
53 | namespace compiler { |
54 | |
55 | namespace { |
56 | char* portable_strdup(const char* s) { |
57 | char* ns = (char*)malloc(size: strlen(s: s) + 1); |
58 | if (ns != nullptr) { |
59 | strcpy(dest: ns, src: s); |
60 | } |
61 | return ns; |
62 | } |
63 | } // namespace |
64 | |
65 | #ifdef _WIN32 |
66 | |
67 | static void CloseHandleOrDie(HANDLE handle) { |
68 | if (!CloseHandle(handle)) { |
69 | GOOGLE_LOG(FATAL) << "CloseHandle: " |
70 | << Subprocess::Win32ErrorMessage(GetLastError()); |
71 | } |
72 | } |
73 | |
74 | Subprocess::Subprocess() |
75 | : process_start_error_(ERROR_SUCCESS), |
76 | child_handle_(nullptr), |
77 | child_stdin_(nullptr), |
78 | child_stdout_(nullptr) {} |
79 | |
80 | Subprocess::~Subprocess() { |
81 | if (child_stdin_ != nullptr) { |
82 | CloseHandleOrDie(child_stdin_); |
83 | } |
84 | if (child_stdout_ != nullptr) { |
85 | CloseHandleOrDie(child_stdout_); |
86 | } |
87 | } |
88 | |
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, nullptr, 0)) { |
97 | GOOGLE_LOG(FATAL) << "CreatePipe: " << Win32ErrorMessage(GetLastError()); |
98 | } |
99 | if (!CreatePipe(&stdout_pipe_read, &stdout_pipe_write, nullptr, 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) ? nullptr : program.c_str(), |
138 | (search_mode == SEARCH_PATH) ? command_line : nullptr, |
139 | nullptr, // process security attributes |
140 | nullptr, // thread security attributes |
141 | TRUE, // inherit handles? |
142 | 0, // obscure creation flags |
143 | nullptr, // environment (inherit from parent) |
144 | nullptr, // 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 | |
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_ != nullptr) << "Must call Start() first." ; |
169 | |
170 | std::string input_data; |
171 | if (!input.SerializeToString(&input_data)) { |
172 | *error = "Failed to serialize request." ; |
173 | return false; |
174 | } |
175 | std::string output_data; |
176 | |
177 | int input_pos = 0; |
178 | |
179 | while (child_stdout_ != nullptr) { |
180 | HANDLE handles[2]; |
181 | int handle_count = 0; |
182 | |
183 | if (child_stdin_ != nullptr) { |
184 | handles[handle_count++] = child_stdin_; |
185 | } |
186 | if (child_stdout_ != nullptr) { |
187 | handles[handle_count++] = child_stdout_; |
188 | } |
189 | |
190 | DWORD wait_result = |
191 | WaitForMultipleObjects(handle_count, handles, FALSE, INFINITE); |
192 | |
193 | HANDLE signaled_handle = nullptr; |
194 | if (wait_result >= WAIT_OBJECT_0 && |
195 | wait_result < WAIT_OBJECT_0 + handle_count) { |
196 | signaled_handle = handles[wait_result - WAIT_OBJECT_0]; |
197 | } else if (wait_result == WAIT_FAILED) { |
198 | GOOGLE_LOG(FATAL) << "WaitForMultipleObjects: " |
199 | << Win32ErrorMessage(GetLastError()); |
200 | } else { |
201 | GOOGLE_LOG(FATAL) << "WaitForMultipleObjects: Unexpected return code: " |
202 | << wait_result; |
203 | } |
204 | |
205 | if (signaled_handle == child_stdin_) { |
206 | DWORD n; |
207 | if (!WriteFile(child_stdin_, input_data.data() + input_pos, |
208 | input_data.size() - input_pos, &n, nullptr)) { |
209 | // Child closed pipe. Presumably it will report an error later. |
210 | // Pretend we're done for now. |
211 | input_pos = input_data.size(); |
212 | } else { |
213 | input_pos += n; |
214 | } |
215 | |
216 | if (input_pos == input_data.size()) { |
217 | // We're done writing. Close. |
218 | CloseHandleOrDie(child_stdin_); |
219 | child_stdin_ = nullptr; |
220 | } |
221 | } else if (signaled_handle == child_stdout_) { |
222 | char buffer[4096]; |
223 | DWORD n; |
224 | |
225 | if (!ReadFile(child_stdout_, buffer, sizeof(buffer), &n, nullptr)) { |
226 | // We're done reading. Close. |
227 | CloseHandleOrDie(child_stdout_); |
228 | child_stdout_ = nullptr; |
229 | } else { |
230 | output_data.append(buffer, n); |
231 | } |
232 | } |
233 | } |
234 | |
235 | if (child_stdin_ != nullptr) { |
236 | // Child did not finish reading input before it closed the output. |
237 | // Presumably it exited with an error. |
238 | CloseHandleOrDie(child_stdin_); |
239 | child_stdin_ = nullptr; |
240 | } |
241 | |
242 | DWORD wait_result = WaitForSingleObject(child_handle_, INFINITE); |
243 | |
244 | if (wait_result == WAIT_FAILED) { |
245 | GOOGLE_LOG(FATAL) << "WaitForSingleObject: " |
246 | << Win32ErrorMessage(GetLastError()); |
247 | } else if (wait_result != WAIT_OBJECT_0) { |
248 | GOOGLE_LOG(FATAL) << "WaitForSingleObject: Unexpected return code: " |
249 | << wait_result; |
250 | } |
251 | |
252 | DWORD exit_code; |
253 | if (!GetExitCodeProcess(child_handle_, &exit_code)) { |
254 | GOOGLE_LOG(FATAL) << "GetExitCodeProcess: " |
255 | << Win32ErrorMessage(GetLastError()); |
256 | } |
257 | |
258 | CloseHandleOrDie(child_handle_); |
259 | child_handle_ = nullptr; |
260 | |
261 | if (exit_code != 0) { |
262 | *error = strings::Substitute("Plugin failed with status code $0." , exit_code); |
263 | return false; |
264 | } |
265 | |
266 | if (!output->ParseFromString(output_data)) { |
267 | *error = "Plugin output is unparseable: " + CEscape(output_data); |
268 | return false; |
269 | } |
270 | |
271 | return true; |
272 | } |
273 | |
274 | std::string Subprocess::Win32ErrorMessage(DWORD error_code) { |
275 | char* message; |
276 | |
277 | // WTF? |
278 | FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | |
279 | FORMAT_MESSAGE_IGNORE_INSERTS, |
280 | nullptr, error_code, |
281 | MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), |
282 | (LPSTR)&message, // NOT A BUG! |
283 | 0, nullptr); |
284 | |
285 | std::string result = message; |
286 | LocalFree(message); |
287 | return result; |
288 | } |
289 | |
290 | // =================================================================== |
291 | |
292 | #else // _WIN32 |
293 | |
294 | Subprocess::Subprocess() |
295 | : child_pid_(-1), child_stdin_(-1), child_stdout_(-1) {} |
296 | |
297 | Subprocess::~Subprocess() { |
298 | if (child_stdin_ != -1) { |
299 | close(fd: child_stdin_); |
300 | } |
301 | if (child_stdout_ != -1) { |
302 | close(fd: child_stdout_); |
303 | } |
304 | } |
305 | |
306 | void Subprocess::Start(const std::string& program, SearchMode search_mode) { |
307 | // Note that we assume that there are no other threads, thus we don't have to |
308 | // do crazy stuff like using socket pairs or avoiding libc locks. |
309 | |
310 | // [0] is read end, [1] is write end. |
311 | int stdin_pipe[2]; |
312 | int stdout_pipe[2]; |
313 | |
314 | GOOGLE_CHECK(pipe(stdin_pipe) != -1); |
315 | GOOGLE_CHECK(pipe(stdout_pipe) != -1); |
316 | |
317 | char* argv[2] = {portable_strdup(s: program.c_str()), nullptr}; |
318 | |
319 | child_pid_ = fork(); |
320 | if (child_pid_ == -1) { |
321 | GOOGLE_LOG(FATAL) << "fork: " << strerror(errno); |
322 | } else if (child_pid_ == 0) { |
323 | // We are the child. |
324 | dup2(fd: stdin_pipe[0], STDIN_FILENO); |
325 | dup2(fd: stdout_pipe[1], STDOUT_FILENO); |
326 | |
327 | close(fd: stdin_pipe[0]); |
328 | close(fd: stdin_pipe[1]); |
329 | close(fd: stdout_pipe[0]); |
330 | close(fd: stdout_pipe[1]); |
331 | |
332 | switch (search_mode) { |
333 | case SEARCH_PATH: |
334 | execvp(file: argv[0], argv: argv); |
335 | break; |
336 | case EXACT_NAME: |
337 | execv(path: argv[0], argv: argv); |
338 | break; |
339 | } |
340 | |
341 | // Write directly to STDERR_FILENO to avoid stdio code paths that may do |
342 | // stuff that is unsafe here. |
343 | int ignored; |
344 | ignored = write(STDERR_FILENO, buf: argv[0], n: strlen(s: argv[0])); |
345 | const char* message = |
346 | ": program not found or is not executable\n" |
347 | "Please specify a program using absolute path or make sure " |
348 | "the program is available in your PATH system variable\n" ; |
349 | ignored = write(STDERR_FILENO, buf: message, n: strlen(s: message)); |
350 | (void)ignored; |
351 | |
352 | // Must use _exit() rather than exit() to avoid flushing output buffers |
353 | // that will also be flushed by the parent. |
354 | _exit(status: 1); |
355 | } else { |
356 | free(ptr: argv[0]); |
357 | |
358 | close(fd: stdin_pipe[0]); |
359 | close(fd: stdout_pipe[1]); |
360 | |
361 | child_stdin_ = stdin_pipe[1]; |
362 | child_stdout_ = stdout_pipe[0]; |
363 | } |
364 | } |
365 | |
366 | bool Subprocess::Communicate(const Message& input, Message* output, |
367 | std::string* error) { |
368 | GOOGLE_CHECK_NE(child_stdin_, -1) << "Must call Start() first." ; |
369 | |
370 | // The "sighandler_t" typedef is GNU-specific, so define our own. |
371 | typedef void SignalHandler(int); |
372 | |
373 | // Make sure SIGPIPE is disabled so that if the child dies it doesn't kill us. |
374 | SignalHandler* old_pipe_handler = signal(SIGPIPE, SIG_IGN); |
375 | |
376 | std::string input_data; |
377 | if (!input.SerializeToString(output: &input_data)) { |
378 | *error = "Failed to serialize request." ; |
379 | return false; |
380 | } |
381 | std::string output_data; |
382 | |
383 | int input_pos = 0; |
384 | int max_fd = std::max(child_stdin_, child_stdout_); |
385 | |
386 | while (child_stdout_ != -1) { |
387 | fd_set read_fds; |
388 | fd_set write_fds; |
389 | FD_ZERO(&read_fds); |
390 | FD_ZERO(&write_fds); |
391 | if (child_stdout_ != -1) { |
392 | FD_SET(child_stdout_, &read_fds); |
393 | } |
394 | if (child_stdin_ != -1) { |
395 | FD_SET(child_stdin_, &write_fds); |
396 | } |
397 | |
398 | if (select(nfds: max_fd + 1, readfds: &read_fds, writefds: &write_fds, exceptfds: nullptr, timeout: nullptr) < 0) { |
399 | if (errno == EINTR) { |
400 | // Interrupted by signal. Try again. |
401 | continue; |
402 | } else { |
403 | GOOGLE_LOG(FATAL) << "select: " << strerror(errno); |
404 | } |
405 | } |
406 | |
407 | if (child_stdin_ != -1 && FD_ISSET(child_stdin_, &write_fds)) { |
408 | int n = write(fd: child_stdin_, buf: input_data.data() + input_pos, |
409 | n: input_data.size() - input_pos); |
410 | if (n < 0) { |
411 | // Child closed pipe. Presumably it will report an error later. |
412 | // Pretend we're done for now. |
413 | input_pos = input_data.size(); |
414 | } else { |
415 | input_pos += n; |
416 | } |
417 | |
418 | if (input_pos == input_data.size()) { |
419 | // We're done writing. Close. |
420 | close(fd: child_stdin_); |
421 | child_stdin_ = -1; |
422 | } |
423 | } |
424 | |
425 | if (child_stdout_ != -1 && FD_ISSET(child_stdout_, &read_fds)) { |
426 | char buffer[4096]; |
427 | int n = read(fd: child_stdout_, buf: buffer, nbytes: sizeof(buffer)); |
428 | |
429 | if (n > 0) { |
430 | output_data.append(s: buffer, n: n); |
431 | } else { |
432 | // We're done reading. Close. |
433 | close(fd: child_stdout_); |
434 | child_stdout_ = -1; |
435 | } |
436 | } |
437 | } |
438 | |
439 | if (child_stdin_ != -1) { |
440 | // Child did not finish reading input before it closed the output. |
441 | // Presumably it exited with an error. |
442 | close(fd: child_stdin_); |
443 | child_stdin_ = -1; |
444 | } |
445 | |
446 | int status; |
447 | while (waitpid(pid: child_pid_, stat_loc: &status, options: 0) == -1) { |
448 | if (errno != EINTR) { |
449 | GOOGLE_LOG(FATAL) << "waitpid: " << strerror(errno); |
450 | } |
451 | } |
452 | |
453 | // Restore SIGPIPE handling. |
454 | signal(SIGPIPE, handler: old_pipe_handler); |
455 | |
456 | if (WIFEXITED(status)) { |
457 | if (WEXITSTATUS(status) != 0) { |
458 | int error_code = WEXITSTATUS(status); |
459 | *error = |
460 | strings::Substitute(format: "Plugin failed with status code $0." , arg0: error_code); |
461 | return false; |
462 | } |
463 | } else if (WIFSIGNALED(status)) { |
464 | int signal = WTERMSIG(status); |
465 | *error = strings::Substitute(format: "Plugin killed by signal $0." , arg0: signal); |
466 | return false; |
467 | } else { |
468 | *error = "Neither WEXITSTATUS nor WTERMSIG is true?" ; |
469 | return false; |
470 | } |
471 | |
472 | if (!output->ParseFromString(data: output_data)) { |
473 | *error = "Plugin output is unparseable: " + CEscape(src: output_data); |
474 | return false; |
475 | } |
476 | |
477 | return true; |
478 | } |
479 | |
480 | #endif // !_WIN32 |
481 | |
482 | } // namespace compiler |
483 | } // namespace protobuf |
484 | } // namespace google |
485 | |