| // 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 "base/process/kill.h" | 
 | #include "base/process/launch.h" | 
 | #include "base/process/process.h" | 
 | #include "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 <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) { | 
 |   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 disabled pipe inheritance"; | 
 |     return false; | 
 |   } | 
 |   if (!SetHandleInformation(err_read, HANDLE_FLAG_INHERIT, 0)) { | 
 |     NOTREACHED() << "Failed to disabled pipe inheritance"; | 
 |     return false; | 
 |   } | 
 |  | 
 |   base::FilePath::StringType cmdline_str(cmdline.GetCommandLineString()); | 
 |  | 
 |   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; | 
 |  | 
 |   // Create the child process. | 
 |   PROCESS_INFORMATION temp_process_info = {}; | 
 |   if (!CreateProcess(nullptr, | 
 |                      &cmdline_str[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 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); | 
 |         } | 
 |  | 
 |         base::Process process(pid); | 
 |         return process.WaitForExit(exit_code); | 
 |       } | 
 |   } | 
 |  | 
 |   return false; | 
 | } | 
 | #endif | 
 |  | 
 | }  // namespace internal | 
 |  |