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