| // Copyright 2014 The Chromium Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include "tools/gn/exec_process.h" | 
 |  | 
 | #include <stddef.h> | 
 |  | 
 | #include <memory> | 
 |  | 
 | #include "base/command_line.h" | 
 | #include "base/files/file_util.h" | 
 | #include "base/logging.h" | 
 | #include "util/build_config.h" | 
 |  | 
 | #if defined(OS_WIN) | 
 | #include <windows.h> | 
 |  | 
 | #include "base/win/scoped_handle.h" | 
 | #include "base/win/scoped_process_information.h" | 
 | #else | 
 | #include <errno.h> | 
 | #include <fcntl.h> | 
 | #include <sys/signal.h> | 
 | #include <sys/wait.h> | 
 | #include <unistd.h> | 
 |  | 
 | #include "base/posix/eintr_wrapper.h" | 
 | #include "base/posix/file_descriptor_shuffle.h" | 
 | #endif | 
 |  | 
 | namespace internal { | 
 |  | 
 | #if defined(OS_WIN) | 
 | bool ExecProcess(const base::CommandLine& cmdline, | 
 |                  const base::FilePath& startup_dir, | 
 |                  std::string* std_out, | 
 |                  std::string* std_err, | 
 |                  int* exit_code) { | 
 |   return ExecProcess(cmdline.GetCommandLineString(), startup_dir, std_out, | 
 |                      std_err, exit_code); | 
 | } | 
 |  | 
 | bool ExecProcess(const base::string16& cmdline_str, | 
 |                  const base::FilePath& startup_dir, | 
 |                  std::string* std_out, | 
 |                  std::string* std_err, | 
 |                  int* exit_code) { | 
 |   SECURITY_ATTRIBUTES sa_attr; | 
 |   // Set the bInheritHandle flag so pipe handles are inherited. | 
 |   sa_attr.nLength = sizeof(SECURITY_ATTRIBUTES); | 
 |   sa_attr.bInheritHandle = TRUE; | 
 |   sa_attr.lpSecurityDescriptor = nullptr; | 
 |  | 
 |   // Create the pipe for the child process's STDOUT. | 
 |   HANDLE out_read = nullptr; | 
 |   HANDLE out_write = nullptr; | 
 |   if (!CreatePipe(&out_read, &out_write, &sa_attr, 0)) { | 
 |     NOTREACHED() << "Failed to create pipe"; | 
 |     return false; | 
 |   } | 
 |   base::win::ScopedHandle scoped_out_read(out_read); | 
 |   base::win::ScopedHandle scoped_out_write(out_write); | 
 |  | 
 |   // Create the pipe for the child process's STDERR. | 
 |   HANDLE err_read = nullptr; | 
 |   HANDLE err_write = nullptr; | 
 |   if (!CreatePipe(&err_read, &err_write, &sa_attr, 0)) { | 
 |     NOTREACHED() << "Failed to create pipe"; | 
 |     return false; | 
 |   } | 
 |   base::win::ScopedHandle scoped_err_read(err_read); | 
 |   base::win::ScopedHandle scoped_err_write(err_write); | 
 |  | 
 |   // Ensure the read handle to the pipe for STDOUT/STDERR is not inherited. | 
 |   if (!SetHandleInformation(out_read, HANDLE_FLAG_INHERIT, 0)) { | 
 |     NOTREACHED() << "Failed to disable pipe inheritance"; | 
 |     return false; | 
 |   } | 
 |   if (!SetHandleInformation(err_read, HANDLE_FLAG_INHERIT, 0)) { | 
 |     NOTREACHED() << "Failed to disable pipe inheritance"; | 
 |     return false; | 
 |   } | 
 |  | 
 |   STARTUPINFO start_info = {}; | 
 |  | 
 |   start_info.cb = sizeof(STARTUPINFO); | 
 |   start_info.hStdOutput = out_write; | 
 |   // Keep the normal stdin. | 
 |   start_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE); | 
 |   // FIXME(brettw) set stderr here when we actually read it below. | 
 |   // start_info.hStdError = err_write; | 
 |   start_info.hStdError = GetStdHandle(STD_ERROR_HANDLE); | 
 |   start_info.dwFlags |= STARTF_USESTDHANDLES; | 
 |  | 
 |   base::string16 cmdline_writable = cmdline_str; | 
 |  | 
 |   // Create the child process. | 
 |   PROCESS_INFORMATION temp_process_info = {}; | 
 |   if (!CreateProcess(nullptr, &cmdline_writable[0], nullptr, nullptr, | 
 |                      TRUE,  // Handles are inherited. | 
 |                      NORMAL_PRIORITY_CLASS, nullptr, | 
 |                      startup_dir.value().c_str(), &start_info, | 
 |                      &temp_process_info)) { | 
 |     return false; | 
 |   } | 
 |   base::win::ScopedProcessInformation proc_info(temp_process_info); | 
 |  | 
 |   // Close our writing end of pipes now. Otherwise later read would not be | 
 |   // able to detect end of child's output. | 
 |   scoped_out_write.Close(); | 
 |   scoped_err_write.Close(); | 
 |  | 
 |   // Read output from the child process's pipe for STDOUT | 
 |   const int kBufferSize = 1024; | 
 |   char buffer[kBufferSize]; | 
 |  | 
 |   // FIXME(brettw) read from stderr here! This is complicated because we want | 
 |   // to read both of them at the same time, probably need overlapped I/O. | 
 |   // Also uncomment start_info code above. | 
 |   for (;;) { | 
 |     DWORD bytes_read = 0; | 
 |     BOOL success = | 
 |         ReadFile(out_read, buffer, kBufferSize, &bytes_read, nullptr); | 
 |     if (!success || bytes_read == 0) | 
 |       break; | 
 |     std_out->append(buffer, bytes_read); | 
 |   } | 
 |  | 
 |   // Let's wait for the process to finish. | 
 |   WaitForSingleObject(proc_info.process_handle(), INFINITE); | 
 |  | 
 |   DWORD dw_exit_code; | 
 |   GetExitCodeProcess(proc_info.process_handle(), &dw_exit_code); | 
 |   *exit_code = static_cast<int>(dw_exit_code); | 
 |  | 
 |   return true; | 
 | } | 
 | #else | 
 | // Reads from the provided file descriptor and appends to output. Returns false | 
 | // if the fd is closed or there is an unexpected error (not | 
 | // EINTR/EAGAIN/EWOULDBLOCK). | 
 | bool ReadFromPipe(int fd, std::string* output) { | 
 |   char buffer[256]; | 
 |   int bytes_read = HANDLE_EINTR(read(fd, buffer, sizeof(buffer))); | 
 |   if (bytes_read == -1) { | 
 |     return errno == EAGAIN || errno == EWOULDBLOCK; | 
 |   } else if (bytes_read <= 0) { | 
 |     return false; | 
 |   } | 
 |   output->append(buffer, bytes_read); | 
 |   return true; | 
 | } | 
 |  | 
 | bool WaitForExit(int pid, int* exit_code) { | 
 |   int status; | 
 |   if (waitpid(pid, &status, 0) < 0) { | 
 |     PLOG(ERROR) << "waitpid"; | 
 |     return false; | 
 |   } | 
 |  | 
 |   if (WIFEXITED(status)) { | 
 |     *exit_code = WEXITSTATUS(status); | 
 |     return true; | 
 |   } else if (WIFSIGNALED(status)) { | 
 |     if (WTERMSIG(status) == SIGINT || WTERMSIG(status) == SIGTERM || | 
 |         WTERMSIG(status) == SIGHUP) | 
 |       return false; | 
 |   } | 
 |   return false; | 
 | } | 
 |  | 
 | bool ExecProcess(const base::CommandLine& cmdline, | 
 |                  const base::FilePath& startup_dir, | 
 |                  std::string* std_out, | 
 |                  std::string* std_err, | 
 |                  int* exit_code) { | 
 |   *exit_code = EXIT_FAILURE; | 
 |  | 
 |   std::vector<std::string> argv = cmdline.argv(); | 
 |  | 
 |   int out_fd[2], err_fd[2]; | 
 |   pid_t pid; | 
 |   base::InjectiveMultimap fd_shuffle1, fd_shuffle2; | 
 |   std::unique_ptr<char*[]> argv_cstr(new char*[argv.size() + 1]); | 
 |  | 
 |   fd_shuffle1.reserve(3); | 
 |   fd_shuffle2.reserve(3); | 
 |  | 
 |   if (pipe(out_fd) < 0) | 
 |     return false; | 
 |   base::ScopedFD out_read(out_fd[0]), out_write(out_fd[1]); | 
 |  | 
 |   if (pipe(err_fd) < 0) | 
 |     return false; | 
 |   base::ScopedFD err_read(err_fd[0]), err_write(err_fd[1]); | 
 |  | 
 |   if (out_read.get() >= FD_SETSIZE || err_read.get() >= FD_SETSIZE) | 
 |     return false; | 
 |  | 
 |   switch (pid = fork()) { | 
 |     case -1:  // error | 
 |       return false; | 
 |     case 0:  // child | 
 |     { | 
 |       // DANGER: no calls to malloc are allowed from now on: | 
 |       // http://crbug.com/36678 | 
 |       // | 
 |       // STL iterators are also not allowed (including those implied | 
 |       // by range-based for loops), since debug iterators use locks. | 
 |  | 
 |       // Obscure fork() rule: in the child, if you don't end up doing exec*(), | 
 |       // you call _exit() instead of exit(). This is because _exit() does not | 
 |       // call any previously-registered (in the parent) exit handlers, which | 
 |       // might do things like block waiting for threads that don't even exist | 
 |       // in the child. | 
 |       int dev_null = open("/dev/null", O_WRONLY); | 
 |       if (dev_null < 0) | 
 |         _exit(127); | 
 |  | 
 |       fd_shuffle1.push_back( | 
 |           base::InjectionArc(out_write.get(), STDOUT_FILENO, true)); | 
 |       fd_shuffle1.push_back( | 
 |           base::InjectionArc(err_write.get(), STDERR_FILENO, true)); | 
 |       fd_shuffle1.push_back(base::InjectionArc(dev_null, STDIN_FILENO, true)); | 
 |       // Adding another element here? Remeber to increase the argument to | 
 |       // reserve(), above. | 
 |  | 
 |       // DANGER: Do NOT convert to range-based for loop! | 
 |       for (size_t i = 0; i < fd_shuffle1.size(); ++i) | 
 |         fd_shuffle2.push_back(fd_shuffle1[i]); | 
 |  | 
 |       if (!ShuffleFileDescriptors(&fd_shuffle1)) | 
 |         _exit(127); | 
 |  | 
 |       base::SetCurrentDirectory(startup_dir); | 
 |  | 
 |       // TODO(brettw) the base version GetAppOutput does a | 
 |       // CloseSuperfluousFds call here. Do we need this? | 
 |  | 
 |       // DANGER: Do NOT convert to range-based for loop! | 
 |       for (size_t i = 0; i < argv.size(); i++) | 
 |         argv_cstr[i] = const_cast<char*>(argv[i].c_str()); | 
 |       argv_cstr[argv.size()] = nullptr; | 
 |       execvp(argv_cstr[0], argv_cstr.get()); | 
 |       _exit(127); | 
 |     } | 
 |     default:  // parent | 
 |     { | 
 |       // Close our writing end of pipe now. Otherwise later read would not | 
 |       // be able to detect end of child's output (in theory we could still | 
 |       // write to the pipe). | 
 |       out_write.reset(); | 
 |       err_write.reset(); | 
 |  | 
 |       bool out_open = true, err_open = true; | 
 |       while (out_open || err_open) { | 
 |         fd_set read_fds; | 
 |         FD_ZERO(&read_fds); | 
 |         FD_SET(out_read.get(), &read_fds); | 
 |         FD_SET(err_read.get(), &read_fds); | 
 |         int res = | 
 |             HANDLE_EINTR(select(std::max(out_read.get(), err_read.get()) + 1, | 
 |                                 &read_fds, nullptr, nullptr, nullptr)); | 
 |         if (res <= 0) | 
 |           break; | 
 |         if (FD_ISSET(out_read.get(), &read_fds)) | 
 |           out_open = ReadFromPipe(out_read.get(), std_out); | 
 |         if (FD_ISSET(err_read.get(), &read_fds)) | 
 |           err_open = ReadFromPipe(err_read.get(), std_err); | 
 |       } | 
 |  | 
 |       return WaitForExit(pid, exit_code); | 
 |     } | 
 |   } | 
 |  | 
 |   return false; | 
 | } | 
 | #endif | 
 |  | 
 | }  // namespace internal |