summaryrefslogtreecommitdiffstats
path: root/base/process
diff options
context:
space:
mode:
authorrsesek@chromium.org <rsesek@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-07-17 18:12:40 +0000
committerrsesek@chromium.org <rsesek@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-07-17 18:12:40 +0000
commit300c386b2aa297d9fe887f4c63f840d9b60db3ca (patch)
tree03f16ab42b2560f15603dbf3818bcddb99df0c5a /base/process
parent1f523dc9b34f3f6e2ca73d52e1c060368a268c2a (diff)
downloadchromium_src-300c386b2aa297d9fe887f4c63f840d9b60db3ca.zip
chromium_src-300c386b2aa297d9fe887f4c63f840d9b60db3ca.tar.gz
chromium_src-300c386b2aa297d9fe887f4c63f840d9b60db3ca.tar.bz2
Finish splitting base/process_util.h by moving the remaining routines to base/process/launch.h
This leaves a forwarding header at base/process_util.h so that include paths can be cleaned up. BUG=242290 R=brettw@chromium.org Review URL: https://codereview.chromium.org/19240006 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@212091 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'base/process')
-rw-r--r--base/process/launch.h258
-rw-r--r--base/process/launch_ios.cc13
-rw-r--r--base/process/launch_mac.cc28
-rw-r--r--base/process/launch_posix.cc728
-rw-r--r--base/process/launch_win.cc283
5 files changed, 1310 insertions, 0 deletions
diff --git a/base/process/launch.h b/base/process/launch.h
new file mode 100644
index 0000000..45b1053
--- /dev/null
+++ b/base/process/launch.h
@@ -0,0 +1,258 @@
+// Copyright (c) 2013 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.
+
+// This file contains functions for launching subprocesses.
+
+#ifndef BASE_PROCESS_LAUNCH_H_
+#define BASE_PROCESS_LAUNCH_H_
+
+#include <set>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+#include "base/process/process_handle.h"
+
+#if defined(OS_POSIX)
+#include "base/posix/file_descriptor_shuffle.h"
+#elif defined(OS_WIN)
+#include <windows.h>
+#endif
+
+class CommandLine;
+
+namespace base {
+
+typedef std::vector<std::pair<std::string, std::string> > EnvironmentVector;
+typedef std::vector<std::pair<int, int> > FileHandleMappingVector;
+
+// Options for launching a subprocess that are passed to LaunchProcess().
+// The default constructor constructs the object with default options.
+struct LaunchOptions {
+ LaunchOptions()
+ : wait(false),
+#if defined(OS_WIN)
+ start_hidden(false),
+ inherit_handles(false),
+ as_user(NULL),
+ empty_desktop_name(false),
+ job_handle(NULL),
+ stdin_handle(NULL),
+ stdout_handle(NULL),
+ stderr_handle(NULL),
+ force_breakaway_from_job_(false)
+#else
+ environ(NULL),
+ fds_to_remap(NULL),
+ maximize_rlimits(NULL),
+ new_process_group(false)
+#if defined(OS_LINUX)
+ , clone_flags(0)
+#endif // OS_LINUX
+#if defined(OS_CHROMEOS)
+ , ctrl_terminal_fd(-1)
+#endif // OS_CHROMEOS
+#endif // !defined(OS_WIN)
+ {}
+
+ // If true, wait for the process to complete.
+ bool wait;
+
+#if defined(OS_WIN)
+ bool start_hidden;
+
+ // If true, the new process inherits handles from the parent. In production
+ // code this flag should be used only when running short-lived, trusted
+ // binaries, because open handles from other libraries and subsystems will
+ // leak to the child process, causing errors such as open socket hangs.
+ bool inherit_handles;
+
+ // If non-NULL, runs as if the user represented by the token had launched it.
+ // Whether the application is visible on the interactive desktop depends on
+ // the token belonging to an interactive logon session.
+ //
+ // To avoid hard to diagnose problems, when specified this loads the
+ // environment variables associated with the user and if this operation fails
+ // the entire call fails as well.
+ UserTokenHandle as_user;
+
+ // If true, use an empty string for the desktop name.
+ bool empty_desktop_name;
+
+ // If non-NULL, launches the application in that job object. The process will
+ // be terminated immediately and LaunchProcess() will fail if assignment to
+ // the job object fails.
+ HANDLE job_handle;
+
+ // Handles for the redirection of stdin, stdout and stderr. The handles must
+ // be inheritable. Caller should either set all three of them or none (i.e.
+ // there is no way to redirect stderr without redirecting stdin). The
+ // |inherit_handles| flag must be set to true when redirecting stdio stream.
+ HANDLE stdin_handle;
+ HANDLE stdout_handle;
+ HANDLE stderr_handle;
+
+ // If set to true, ensures that the child process is launched with the
+ // CREATE_BREAKAWAY_FROM_JOB flag which allows it to breakout of the parent
+ // job if any.
+ bool force_breakaway_from_job_;
+#else
+ // If non-NULL, set/unset environment variables.
+ // See documentation of AlterEnvironment().
+ // This pointer is owned by the caller and must live through the
+ // call to LaunchProcess().
+ const EnvironmentVector* environ;
+
+ // If non-NULL, remap file descriptors according to the mapping of
+ // src fd->dest fd to propagate FDs into the child process.
+ // This pointer is owned by the caller and must live through the
+ // call to LaunchProcess().
+ const FileHandleMappingVector* fds_to_remap;
+
+ // Each element is an RLIMIT_* constant that should be raised to its
+ // rlim_max. This pointer is owned by the caller and must live through
+ // the call to LaunchProcess().
+ const std::set<int>* maximize_rlimits;
+
+ // If true, start the process in a new process group, instead of
+ // inheriting the parent's process group. The pgid of the child process
+ // will be the same as its pid.
+ bool new_process_group;
+
+#if defined(OS_LINUX)
+ // If non-zero, start the process using clone(), using flags as provided.
+ int clone_flags;
+#endif // defined(OS_LINUX)
+
+#if defined(OS_CHROMEOS)
+ // If non-negative, the specified file descriptor will be set as the launched
+ // process' controlling terminal.
+ int ctrl_terminal_fd;
+#endif // defined(OS_CHROMEOS)
+
+#endif // !defined(OS_WIN)
+};
+
+// Launch a process via the command line |cmdline|.
+// See the documentation of LaunchOptions for details on |options|.
+//
+// Returns true upon success.
+//
+// Upon success, if |process_handle| is non-NULL, it will be filled in with the
+// handle of the launched process. NOTE: In this case, the caller is
+// responsible for closing the handle so that it doesn't leak!
+// Otherwise, the process handle will be implicitly closed.
+//
+// Unix-specific notes:
+// - All file descriptors open in the parent process will be closed in the
+// child process except for any preserved by options::fds_to_remap, and
+// stdin, stdout, and stderr. If not remapped by options::fds_to_remap,
+// stdin is reopened as /dev/null, and the child is allowed to inherit its
+// parent's stdout and stderr.
+// - If the first argument on the command line does not contain a slash,
+// PATH will be searched. (See man execvp.)
+BASE_EXPORT bool LaunchProcess(const CommandLine& cmdline,
+ const LaunchOptions& options,
+ ProcessHandle* process_handle);
+
+#if defined(OS_WIN)
+// Windows-specific LaunchProcess that takes the command line as a
+// string. Useful for situations where you need to control the
+// command line arguments directly, but prefer the CommandLine version
+// if launching Chrome itself.
+//
+// The first command line argument should be the path to the process,
+// and don't forget to quote it.
+//
+// Example (including literal quotes)
+// cmdline = "c:\windows\explorer.exe" -foo "c:\bar\"
+BASE_EXPORT bool LaunchProcess(const string16& cmdline,
+ const LaunchOptions& options,
+ ProcessHandle* process_handle);
+
+#elif defined(OS_POSIX)
+// A POSIX-specific version of LaunchProcess that takes an argv array
+// instead of a CommandLine. Useful for situations where you need to
+// control the command line arguments directly, but prefer the
+// CommandLine version if launching Chrome itself.
+BASE_EXPORT bool LaunchProcess(const std::vector<std::string>& argv,
+ const LaunchOptions& options,
+ ProcessHandle* process_handle);
+
+// AlterEnvironment returns a modified environment vector, constructed from the
+// given environment and the list of changes given in |changes|. Each key in
+// the environment is matched against the first element of the pairs. In the
+// event of a match, the value is replaced by the second of the pair, unless
+// the second is empty, in which case the key-value is removed.
+//
+// The returned array is allocated using new[] and must be freed by the caller.
+BASE_EXPORT char** AlterEnvironment(const EnvironmentVector& changes,
+ const char* const* const env);
+
+// Close all file descriptors, except those which are a destination in the
+// given multimap. Only call this function in a child process where you know
+// that there aren't any other threads.
+BASE_EXPORT void CloseSuperfluousFds(const InjectiveMultimap& saved_map);
+#endif // defined(OS_POSIX)
+
+#if defined(OS_WIN)
+// Set JOBOBJECT_EXTENDED_LIMIT_INFORMATION to JobObject |job_object|.
+// As its limit_info.BasicLimitInformation.LimitFlags has
+// JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE.
+// When the provide JobObject |job_object| is closed, the binded process will
+// be terminated.
+BASE_EXPORT bool SetJobObjectAsKillOnJobClose(HANDLE job_object);
+
+// Output multi-process printf, cout, cerr, etc to the cmd.exe console that ran
+// chrome. This is not thread-safe: only call from main thread.
+BASE_EXPORT void RouteStdioToConsole();
+#endif // defined(OS_WIN)
+
+// Executes the application specified by |cl| and wait for it to exit. Stores
+// the output (stdout) in |output|. Redirects stderr to /dev/null. Returns true
+// on success (application launched and exited cleanly, with exit code
+// indicating success).
+BASE_EXPORT bool GetAppOutput(const CommandLine& cl, std::string* output);
+
+#if defined(OS_POSIX)
+// A POSIX-specific version of GetAppOutput that takes an argv array
+// instead of a CommandLine. Useful for situations where you need to
+// control the command line arguments directly.
+BASE_EXPORT bool GetAppOutput(const std::vector<std::string>& argv,
+ std::string* output);
+
+// A restricted version of |GetAppOutput()| which (a) clears the environment,
+// and (b) stores at most |max_output| bytes; also, it doesn't search the path
+// for the command.
+BASE_EXPORT bool GetAppOutputRestricted(const CommandLine& cl,
+ std::string* output, size_t max_output);
+
+// A version of |GetAppOutput()| which also returns the exit code of the
+// executed command. Returns true if the application runs and exits cleanly. If
+// this is the case the exit code of the application is available in
+// |*exit_code|.
+BASE_EXPORT bool GetAppOutputWithExitCode(const CommandLine& cl,
+ std::string* output, int* exit_code);
+#endif // defined(OS_POSIX)
+
+// If supported on the platform, and the user has sufficent rights, increase
+// the current process's scheduling priority to a high priority.
+BASE_EXPORT void RaiseProcessToHighPriority();
+
+#if defined(OS_MACOSX)
+// Restore the default exception handler, setting it to Apple Crash Reporter
+// (ReportCrash). When forking and execing a new process, the child will
+// inherit the parent's exception ports, which may be set to the Breakpad
+// instance running inside the parent. The parent's Breakpad instance should
+// not handle the child's exceptions. Calling RestoreDefaultExceptionHandler
+// in the child after forking will restore the standard exception handler.
+// See http://crbug.com/20371/ for more details.
+void RestoreDefaultExceptionHandler();
+#endif // defined(OS_MACOSX)
+
+} // namespace base
+
+#endif // BASE_PROCESS_LAUNCH_H_
diff --git a/base/process/launch_ios.cc b/base/process/launch_ios.cc
new file mode 100644
index 0000000..3c700f8
--- /dev/null
+++ b/base/process/launch_ios.cc
@@ -0,0 +1,13 @@
+// Copyright (c) 2012 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 "base/process/launch.h"
+
+namespace base {
+
+void RaiseProcessToHighPriority() {
+ // Impossible on iOS. Do nothing.
+}
+
+} // namespace base
diff --git a/base/process/launch_mac.cc b/base/process/launch_mac.cc
new file mode 100644
index 0000000..176edca
--- /dev/null
+++ b/base/process/launch_mac.cc
@@ -0,0 +1,28 @@
+// Copyright (c) 2012 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 "base/process/launch.h"
+
+#include <mach/mach.h>
+
+namespace base {
+
+void RestoreDefaultExceptionHandler() {
+ // This function is tailored to remove the Breakpad exception handler.
+ // exception_mask matches s_exception_mask in
+ // breakpad/src/client/mac/handler/exception_handler.cc
+ const exception_mask_t exception_mask = EXC_MASK_BAD_ACCESS |
+ EXC_MASK_BAD_INSTRUCTION |
+ EXC_MASK_ARITHMETIC |
+ EXC_MASK_BREAKPOINT;
+
+ // Setting the exception port to MACH_PORT_NULL may not be entirely
+ // kosher to restore the default exception handler, but in practice,
+ // it results in the exception port being set to Apple Crash Reporter,
+ // the desired behavior.
+ task_set_exception_ports(mach_task_self(), exception_mask, MACH_PORT_NULL,
+ EXCEPTION_DEFAULT, THREAD_STATE_NONE);
+}
+
+} // namespace base
diff --git a/base/process/launch_posix.cc b/base/process/launch_posix.cc
new file mode 100644
index 0000000..336633c
--- /dev/null
+++ b/base/process/launch_posix.cc
@@ -0,0 +1,728 @@
+// Copyright (c) 2012 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 "base/process/launch.h"
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <sys/resource.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <iterator>
+#include <limits>
+#include <set>
+
+#include "base/allocator/type_profiler_control.h"
+#include "base/command_line.h"
+#include "base/compiler_specific.h"
+#include "base/debug/debugger.h"
+#include "base/debug/stack_trace.h"
+#include "base/file_util.h"
+#include "base/files/dir_reader_posix.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/process_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/third_party/dynamic_annotations/dynamic_annotations.h"
+#include "base/threading/platform_thread.h"
+#include "base/threading/thread_restrictions.h"
+
+#if defined(OS_CHROMEOS)
+#include <sys/ioctl.h>
+#endif
+
+#if defined(OS_FREEBSD)
+#include <sys/event.h>
+#include <sys/ucontext.h>
+#endif
+
+#if defined(OS_MACOSX)
+#include <crt_externs.h>
+#include <sys/event.h>
+#else
+extern char** environ;
+#endif
+
+namespace base {
+
+namespace {
+
+// Get the process's "environment" (i.e. the thing that setenv/getenv
+// work with).
+char** GetEnvironment() {
+#if defined(OS_MACOSX)
+ return *_NSGetEnviron();
+#else
+ return environ;
+#endif
+}
+
+// Set the process's "environment" (i.e. the thing that setenv/getenv
+// work with).
+void SetEnvironment(char** env) {
+#if defined(OS_MACOSX)
+ *_NSGetEnviron() = env;
+#else
+ environ = env;
+#endif
+}
+
+#if !defined(OS_LINUX) || \
+ (!defined(__i386__) && !defined(__x86_64__) && !defined(__arm__))
+void ResetChildSignalHandlersToDefaults() {
+ // The previous signal handlers are likely to be meaningless in the child's
+ // context so we reset them to the defaults for now. http://crbug.com/44953
+ // These signal handlers are set up at least in browser_main_posix.cc:
+ // BrowserMainPartsPosix::PreEarlyInitialization and stack_trace_posix.cc:
+ // EnableInProcessStackDumping.
+ signal(SIGHUP, SIG_DFL);
+ signal(SIGINT, SIG_DFL);
+ signal(SIGILL, SIG_DFL);
+ signal(SIGABRT, SIG_DFL);
+ signal(SIGFPE, SIG_DFL);
+ signal(SIGBUS, SIG_DFL);
+ signal(SIGSEGV, SIG_DFL);
+ signal(SIGSYS, SIG_DFL);
+ signal(SIGTERM, SIG_DFL);
+}
+
+#else
+
+// TODO(jln): remove the Linux special case once kernels are fixed.
+
+// Internally the kernel makes sigset_t an array of long large enough to have
+// one bit per signal.
+typedef uint64_t kernel_sigset_t;
+
+// This is what struct sigaction looks like to the kernel at least on X86 and
+// ARM. MIPS, for instance, is very different.
+struct kernel_sigaction {
+ void* k_sa_handler; // For this usage it only needs to be a generic pointer.
+ unsigned long k_sa_flags;
+ void* k_sa_restorer; // For this usage it only needs to be a generic pointer.
+ kernel_sigset_t k_sa_mask;
+};
+
+// glibc's sigaction() will prevent access to sa_restorer, so we need to roll
+// our own.
+int sys_rt_sigaction(int sig, const struct kernel_sigaction* act,
+ struct kernel_sigaction* oact) {
+ return syscall(SYS_rt_sigaction, sig, act, oact, sizeof(kernel_sigset_t));
+}
+
+// This function is intended to be used in between fork() and execve() and will
+// reset all signal handlers to the default.
+// The motivation for going through all of them is that sa_restorer can leak
+// from parents and help defeat ASLR on buggy kernels. We reset it to NULL.
+// See crbug.com/177956.
+void ResetChildSignalHandlersToDefaults(void) {
+ for (int signum = 1; ; ++signum) {
+ struct kernel_sigaction act = {0};
+ int sigaction_get_ret = sys_rt_sigaction(signum, NULL, &act);
+ if (sigaction_get_ret && errno == EINVAL) {
+#if !defined(NDEBUG)
+ // Linux supports 32 real-time signals from 33 to 64.
+ // If the number of signals in the Linux kernel changes, someone should
+ // look at this code.
+ const int kNumberOfSignals = 64;
+ RAW_CHECK(signum == kNumberOfSignals + 1);
+#endif // !defined(NDEBUG)
+ break;
+ }
+ // All other failures are fatal.
+ if (sigaction_get_ret) {
+ RAW_LOG(FATAL, "sigaction (get) failed.");
+ }
+
+ // The kernel won't allow to re-set SIGKILL or SIGSTOP.
+ if (signum != SIGSTOP && signum != SIGKILL) {
+ act.k_sa_handler = reinterpret_cast<void*>(SIG_DFL);
+ act.k_sa_restorer = NULL;
+ if (sys_rt_sigaction(signum, &act, NULL)) {
+ RAW_LOG(FATAL, "sigaction (set) failed.");
+ }
+ }
+#if !defined(NDEBUG)
+ // Now ask the kernel again and check that no restorer will leak.
+ if (sys_rt_sigaction(signum, NULL, &act) || act.k_sa_restorer) {
+ RAW_LOG(FATAL, "Cound not fix sa_restorer.");
+ }
+#endif // !defined(NDEBUG)
+ }
+}
+#endif // !defined(OS_LINUX) ||
+ // (!defined(__i386__) && !defined(__x86_64__) && !defined(__arm__))
+
+} // anonymous namespace
+
+// A class to handle auto-closing of DIR*'s.
+class ScopedDIRClose {
+ public:
+ inline void operator()(DIR* x) const {
+ if (x) {
+ closedir(x);
+ }
+ }
+};
+typedef scoped_ptr_malloc<DIR, ScopedDIRClose> ScopedDIR;
+
+#if defined(OS_LINUX)
+static const char kFDDir[] = "/proc/self/fd";
+#elif defined(OS_MACOSX)
+static const char kFDDir[] = "/dev/fd";
+#elif defined(OS_SOLARIS)
+static const char kFDDir[] = "/dev/fd";
+#elif defined(OS_FREEBSD)
+static const char kFDDir[] = "/dev/fd";
+#elif defined(OS_OPENBSD)
+static const char kFDDir[] = "/dev/fd";
+#elif defined(OS_ANDROID)
+static const char kFDDir[] = "/proc/self/fd";
+#endif
+
+void CloseSuperfluousFds(const base::InjectiveMultimap& saved_mapping) {
+ // DANGER: no calls to malloc are allowed from now on:
+ // http://crbug.com/36678
+
+ // Get the maximum number of FDs possible.
+ size_t max_fds = GetMaxFds();
+
+ DirReaderPosix fd_dir(kFDDir);
+ if (!fd_dir.IsValid()) {
+ // Fallback case: Try every possible fd.
+ for (size_t i = 0; i < max_fds; ++i) {
+ const int fd = static_cast<int>(i);
+ if (fd == STDIN_FILENO || fd == STDOUT_FILENO || fd == STDERR_FILENO)
+ continue;
+ InjectiveMultimap::const_iterator j;
+ for (j = saved_mapping.begin(); j != saved_mapping.end(); j++) {
+ if (fd == j->dest)
+ break;
+ }
+ if (j != saved_mapping.end())
+ continue;
+
+ // Since we're just trying to close anything we can find,
+ // ignore any error return values of close().
+ ignore_result(HANDLE_EINTR(close(fd)));
+ }
+ return;
+ }
+
+ const int dir_fd = fd_dir.fd();
+
+ for ( ; fd_dir.Next(); ) {
+ // Skip . and .. entries.
+ if (fd_dir.name()[0] == '.')
+ continue;
+
+ char *endptr;
+ errno = 0;
+ const long int fd = strtol(fd_dir.name(), &endptr, 10);
+ if (fd_dir.name()[0] == 0 || *endptr || fd < 0 || errno)
+ continue;
+ if (fd == STDIN_FILENO || fd == STDOUT_FILENO || fd == STDERR_FILENO)
+ continue;
+ InjectiveMultimap::const_iterator i;
+ for (i = saved_mapping.begin(); i != saved_mapping.end(); i++) {
+ if (fd == i->dest)
+ break;
+ }
+ if (i != saved_mapping.end())
+ continue;
+ if (fd == dir_fd)
+ continue;
+
+ // When running under Valgrind, Valgrind opens several FDs for its
+ // own use and will complain if we try to close them. All of
+ // these FDs are >= |max_fds|, so we can check against that here
+ // before closing. See https://bugs.kde.org/show_bug.cgi?id=191758
+ if (fd < static_cast<int>(max_fds)) {
+ int ret = HANDLE_EINTR(close(fd));
+ DPCHECK(ret == 0);
+ }
+ }
+}
+
+char** AlterEnvironment(const EnvironmentVector& changes,
+ const char* const* const env) {
+ unsigned count = 0;
+ unsigned size = 0;
+
+ // First assume that all of the current environment will be included.
+ for (unsigned i = 0; env[i]; i++) {
+ const char *const pair = env[i];
+ count++;
+ size += strlen(pair) + 1 /* terminating NUL */;
+ }
+
+ for (EnvironmentVector::const_iterator j = changes.begin();
+ j != changes.end();
+ ++j) {
+ bool found = false;
+ const char *pair;
+
+ for (unsigned i = 0; env[i]; i++) {
+ pair = env[i];
+ const char *const equals = strchr(pair, '=');
+ if (!equals)
+ continue;
+ const unsigned keylen = equals - pair;
+ if (keylen == j->first.size() &&
+ memcmp(pair, j->first.data(), keylen) == 0) {
+ found = true;
+ break;
+ }
+ }
+
+ // if found, we'll either be deleting or replacing this element.
+ if (found) {
+ count--;
+ size -= strlen(pair) + 1;
+ if (j->second.size())
+ found = false;
+ }
+
+ // if !found, then we have a new element to add.
+ if (!found && !j->second.empty()) {
+ count++;
+ size += j->first.size() + 1 /* '=' */ + j->second.size() + 1 /* NUL */;
+ }
+ }
+
+ count++; // for the final NULL
+ uint8_t *buffer = new uint8_t[sizeof(char*) * count + size];
+ char **const ret = reinterpret_cast<char**>(buffer);
+ unsigned k = 0;
+ char *scratch = reinterpret_cast<char*>(buffer + sizeof(char*) * count);
+
+ for (unsigned i = 0; env[i]; i++) {
+ const char *const pair = env[i];
+ const char *const equals = strchr(pair, '=');
+ if (!equals) {
+ const unsigned len = strlen(pair);
+ ret[k++] = scratch;
+ memcpy(scratch, pair, len + 1);
+ scratch += len + 1;
+ continue;
+ }
+ const unsigned keylen = equals - pair;
+ bool handled = false;
+ for (EnvironmentVector::const_iterator
+ j = changes.begin(); j != changes.end(); j++) {
+ if (j->first.size() == keylen &&
+ memcmp(j->first.data(), pair, keylen) == 0) {
+ if (!j->second.empty()) {
+ ret[k++] = scratch;
+ memcpy(scratch, pair, keylen + 1);
+ scratch += keylen + 1;
+ memcpy(scratch, j->second.c_str(), j->second.size() + 1);
+ scratch += j->second.size() + 1;
+ }
+ handled = true;
+ break;
+ }
+ }
+
+ if (!handled) {
+ const unsigned len = strlen(pair);
+ ret[k++] = scratch;
+ memcpy(scratch, pair, len + 1);
+ scratch += len + 1;
+ }
+ }
+
+ // Now handle new elements
+ for (EnvironmentVector::const_iterator
+ j = changes.begin(); j != changes.end(); j++) {
+ if (j->second.empty())
+ continue;
+
+ bool found = false;
+ for (unsigned i = 0; env[i]; i++) {
+ const char *const pair = env[i];
+ const char *const equals = strchr(pair, '=');
+ if (!equals)
+ continue;
+ const unsigned keylen = equals - pair;
+ if (keylen == j->first.size() &&
+ memcmp(pair, j->first.data(), keylen) == 0) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ ret[k++] = scratch;
+ memcpy(scratch, j->first.data(), j->first.size());
+ scratch += j->first.size();
+ *scratch++ = '=';
+ memcpy(scratch, j->second.c_str(), j->second.size() + 1);
+ scratch += j->second.size() + 1;
+ }
+ }
+
+ ret[k] = NULL;
+ return ret;
+}
+
+bool LaunchProcess(const std::vector<std::string>& argv,
+ const LaunchOptions& options,
+ ProcessHandle* process_handle) {
+ size_t fd_shuffle_size = 0;
+ if (options.fds_to_remap) {
+ fd_shuffle_size = options.fds_to_remap->size();
+ }
+
+ InjectiveMultimap fd_shuffle1;
+ InjectiveMultimap fd_shuffle2;
+ fd_shuffle1.reserve(fd_shuffle_size);
+ fd_shuffle2.reserve(fd_shuffle_size);
+
+ scoped_ptr<char*[]> argv_cstr(new char*[argv.size() + 1]);
+ scoped_ptr<char*[]> new_environ;
+ if (options.environ)
+ new_environ.reset(AlterEnvironment(*options.environ, GetEnvironment()));
+
+ pid_t pid;
+#if defined(OS_LINUX)
+ if (options.clone_flags) {
+ pid = syscall(__NR_clone, options.clone_flags, 0, 0, 0);
+ } else
+#endif
+ {
+ pid = fork();
+ }
+
+ if (pid < 0) {
+ DPLOG(ERROR) << "fork";
+ return false;
+ } else if (pid == 0) {
+ // Child process
+
+ // DANGER: 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.
+
+ // If a child process uses the readline library, the process block forever.
+ // In BSD like OSes including OS X it is safe to assign /dev/null as stdin.
+ // See http://crbug.com/56596.
+ int null_fd = HANDLE_EINTR(open("/dev/null", O_RDONLY));
+ if (null_fd < 0) {
+ RAW_LOG(ERROR, "Failed to open /dev/null");
+ _exit(127);
+ }
+
+ file_util::ScopedFD null_fd_closer(&null_fd);
+ int new_fd = HANDLE_EINTR(dup2(null_fd, STDIN_FILENO));
+ if (new_fd != STDIN_FILENO) {
+ RAW_LOG(ERROR, "Failed to dup /dev/null for stdin");
+ _exit(127);
+ }
+
+ if (options.new_process_group) {
+ // Instead of inheriting the process group ID of the parent, the child
+ // starts off a new process group with pgid equal to its process ID.
+ if (setpgid(0, 0) < 0) {
+ RAW_LOG(ERROR, "setpgid failed");
+ _exit(127);
+ }
+ }
+
+ // Stop type-profiler.
+ // The profiler should be stopped between fork and exec since it inserts
+ // locks at new/delete expressions. See http://crbug.com/36678.
+ base::type_profiler::Controller::Stop();
+
+ if (options.maximize_rlimits) {
+ // Some resource limits need to be maximal in this child.
+ std::set<int>::const_iterator resource;
+ for (resource = options.maximize_rlimits->begin();
+ resource != options.maximize_rlimits->end();
+ ++resource) {
+ struct rlimit limit;
+ if (getrlimit(*resource, &limit) < 0) {
+ RAW_LOG(WARNING, "getrlimit failed");
+ } else if (limit.rlim_cur < limit.rlim_max) {
+ limit.rlim_cur = limit.rlim_max;
+ if (setrlimit(*resource, &limit) < 0) {
+ RAW_LOG(WARNING, "setrlimit failed");
+ }
+ }
+ }
+ }
+
+#if defined(OS_MACOSX)
+ RestoreDefaultExceptionHandler();
+#endif // defined(OS_MACOSX)
+
+ ResetChildSignalHandlersToDefaults();
+
+#if 0
+ // When debugging it can be helpful to check that we really aren't making
+ // any hidden calls to malloc.
+ void *malloc_thunk =
+ reinterpret_cast<void*>(reinterpret_cast<intptr_t>(malloc) & ~4095);
+ mprotect(malloc_thunk, 4096, PROT_READ | PROT_WRITE | PROT_EXEC);
+ memset(reinterpret_cast<void*>(malloc), 0xff, 8);
+#endif // 0
+
+ // DANGER: no calls to malloc are allowed from now on:
+ // http://crbug.com/36678
+
+#if defined(OS_CHROMEOS)
+ if (options.ctrl_terminal_fd >= 0) {
+ // Set process' controlling terminal.
+ if (HANDLE_EINTR(setsid()) != -1) {
+ if (HANDLE_EINTR(
+ ioctl(options.ctrl_terminal_fd, TIOCSCTTY, NULL)) == -1) {
+ RAW_LOG(WARNING, "ioctl(TIOCSCTTY), ctrl terminal not set");
+ }
+ } else {
+ RAW_LOG(WARNING, "setsid failed, ctrl terminal not set");
+ }
+ }
+#endif // defined(OS_CHROMEOS)
+
+ if (options.fds_to_remap) {
+ for (FileHandleMappingVector::const_iterator
+ it = options.fds_to_remap->begin();
+ it != options.fds_to_remap->end(); ++it) {
+ fd_shuffle1.push_back(InjectionArc(it->first, it->second, false));
+ fd_shuffle2.push_back(InjectionArc(it->first, it->second, false));
+ }
+ }
+
+ if (options.environ)
+ SetEnvironment(new_environ.get());
+
+ // fd_shuffle1 is mutated by this call because it cannot malloc.
+ if (!ShuffleFileDescriptors(&fd_shuffle1))
+ _exit(127);
+
+ CloseSuperfluousFds(fd_shuffle2);
+
+ for (size_t i = 0; i < argv.size(); i++)
+ argv_cstr[i] = const_cast<char*>(argv[i].c_str());
+ argv_cstr[argv.size()] = NULL;
+ execvp(argv_cstr[0], argv_cstr.get());
+
+ RAW_LOG(ERROR, "LaunchProcess: failed to execvp:");
+ RAW_LOG(ERROR, argv_cstr[0]);
+ _exit(127);
+ } else {
+ // Parent process
+ if (options.wait) {
+ // While this isn't strictly disk IO, waiting for another process to
+ // finish is the sort of thing ThreadRestrictions is trying to prevent.
+ base::ThreadRestrictions::AssertIOAllowed();
+ pid_t ret = HANDLE_EINTR(waitpid(pid, 0, 0));
+ DPCHECK(ret > 0);
+ }
+
+ if (process_handle)
+ *process_handle = pid;
+ }
+
+ return true;
+}
+
+
+bool LaunchProcess(const CommandLine& cmdline,
+ const LaunchOptions& options,
+ ProcessHandle* process_handle) {
+ return LaunchProcess(cmdline.argv(), options, process_handle);
+}
+
+void RaiseProcessToHighPriority() {
+ // On POSIX, we don't actually do anything here. We could try to nice() or
+ // setpriority() or sched_getscheduler, but these all require extra rights.
+}
+
+// Return value used by GetAppOutputInternal to encapsulate the various exit
+// scenarios from the function.
+enum GetAppOutputInternalResult {
+ EXECUTE_FAILURE,
+ EXECUTE_SUCCESS,
+ GOT_MAX_OUTPUT,
+};
+
+// Executes the application specified by |argv| and wait for it to exit. Stores
+// the output (stdout) in |output|. If |do_search_path| is set, it searches the
+// path for the application; in that case, |envp| must be null, and it will use
+// the current environment. If |do_search_path| is false, |argv[0]| should fully
+// specify the path of the application, and |envp| will be used as the
+// environment. Redirects stderr to /dev/null.
+// If we successfully start the application and get all requested output, we
+// return GOT_MAX_OUTPUT, or if there is a problem starting or exiting
+// the application we return RUN_FAILURE. Otherwise we return EXECUTE_SUCCESS.
+// The GOT_MAX_OUTPUT return value exists so a caller that asks for limited
+// output can treat this as a success, despite having an exit code of SIG_PIPE
+// due to us closing the output pipe.
+// In the case of EXECUTE_SUCCESS, the application exit code will be returned
+// in |*exit_code|, which should be checked to determine if the application
+// ran successfully.
+static GetAppOutputInternalResult GetAppOutputInternal(
+ const std::vector<std::string>& argv,
+ char* const envp[],
+ std::string* output,
+ size_t max_output,
+ bool do_search_path,
+ int* exit_code) {
+ // Doing a blocking wait for another command to finish counts as IO.
+ base::ThreadRestrictions::AssertIOAllowed();
+ // exit_code must be supplied so calling function can determine success.
+ DCHECK(exit_code);
+ *exit_code = EXIT_FAILURE;
+
+ int pipe_fd[2];
+ pid_t pid;
+ InjectiveMultimap fd_shuffle1, fd_shuffle2;
+ scoped_ptr<char*[]> argv_cstr(new char*[argv.size() + 1]);
+
+ fd_shuffle1.reserve(3);
+ fd_shuffle2.reserve(3);
+
+ // Either |do_search_path| should be false or |envp| should be null, but not
+ // both.
+ DCHECK(!do_search_path ^ !envp);
+
+ if (pipe(pipe_fd) < 0)
+ return EXECUTE_FAILURE;
+
+ switch (pid = fork()) {
+ case -1: // error
+ close(pipe_fd[0]);
+ close(pipe_fd[1]);
+ return EXECUTE_FAILURE;
+ case 0: // child
+ {
+#if defined(OS_MACOSX)
+ RestoreDefaultExceptionHandler();
+#endif
+ // DANGER: no calls to malloc are allowed from now on:
+ // http://crbug.com/36678
+
+ // 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);
+
+ // Stop type-profiler.
+ // The profiler should be stopped between fork and exec since it inserts
+ // locks at new/delete expressions. See http://crbug.com/36678.
+ base::type_profiler::Controller::Stop();
+
+ fd_shuffle1.push_back(InjectionArc(pipe_fd[1], STDOUT_FILENO, true));
+ fd_shuffle1.push_back(InjectionArc(dev_null, STDERR_FILENO, true));
+ fd_shuffle1.push_back(InjectionArc(dev_null, STDIN_FILENO, true));
+ // Adding another element here? Remeber to increase the argument to
+ // reserve(), above.
+
+ std::copy(fd_shuffle1.begin(), fd_shuffle1.end(),
+ std::back_inserter(fd_shuffle2));
+
+ if (!ShuffleFileDescriptors(&fd_shuffle1))
+ _exit(127);
+
+ CloseSuperfluousFds(fd_shuffle2);
+
+ for (size_t i = 0; i < argv.size(); i++)
+ argv_cstr[i] = const_cast<char*>(argv[i].c_str());
+ argv_cstr[argv.size()] = NULL;
+ if (do_search_path)
+ execvp(argv_cstr[0], argv_cstr.get());
+ else
+ execve(argv_cstr[0], argv_cstr.get(), envp);
+ _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).
+ close(pipe_fd[1]);
+
+ output->clear();
+ char buffer[256];
+ size_t output_buf_left = max_output;
+ ssize_t bytes_read = 1; // A lie to properly handle |max_output == 0|
+ // case in the logic below.
+
+ while (output_buf_left > 0) {
+ bytes_read = HANDLE_EINTR(read(pipe_fd[0], buffer,
+ std::min(output_buf_left, sizeof(buffer))));
+ if (bytes_read <= 0)
+ break;
+ output->append(buffer, bytes_read);
+ output_buf_left -= static_cast<size_t>(bytes_read);
+ }
+ close(pipe_fd[0]);
+
+ // Always wait for exit code (even if we know we'll declare
+ // GOT_MAX_OUTPUT).
+ bool success = WaitForExitCode(pid, exit_code);
+
+ // If we stopped because we read as much as we wanted, we return
+ // GOT_MAX_OUTPUT (because the child may exit due to |SIGPIPE|).
+ if (!output_buf_left && bytes_read > 0)
+ return GOT_MAX_OUTPUT;
+ else if (success)
+ return EXECUTE_SUCCESS;
+ return EXECUTE_FAILURE;
+ }
+ }
+}
+
+bool GetAppOutput(const CommandLine& cl, std::string* output) {
+ return GetAppOutput(cl.argv(), output);
+}
+
+bool GetAppOutput(const std::vector<std::string>& argv, std::string* output) {
+ // Run |execve()| with the current environment and store "unlimited" data.
+ int exit_code;
+ GetAppOutputInternalResult result = GetAppOutputInternal(
+ argv, NULL, output, std::numeric_limits<std::size_t>::max(), true,
+ &exit_code);
+ return result == EXECUTE_SUCCESS && exit_code == EXIT_SUCCESS;
+}
+
+// TODO(viettrungluu): Conceivably, we should have a timeout as well, so we
+// don't hang if what we're calling hangs.
+bool GetAppOutputRestricted(const CommandLine& cl,
+ std::string* output, size_t max_output) {
+ // Run |execve()| with the empty environment.
+ char* const empty_environ = NULL;
+ int exit_code;
+ GetAppOutputInternalResult result = GetAppOutputInternal(
+ cl.argv(), &empty_environ, output, max_output, false, &exit_code);
+ return result == GOT_MAX_OUTPUT || (result == EXECUTE_SUCCESS &&
+ exit_code == EXIT_SUCCESS);
+}
+
+bool GetAppOutputWithExitCode(const CommandLine& cl,
+ std::string* output,
+ int* exit_code) {
+ // Run |execve()| with the current environment and store "unlimited" data.
+ GetAppOutputInternalResult result = GetAppOutputInternal(
+ cl.argv(), NULL, output, std::numeric_limits<std::size_t>::max(), true,
+ exit_code);
+ return result == EXECUTE_SUCCESS;
+}
+
+} // namespace base
diff --git a/base/process/launch_win.cc b/base/process/launch_win.cc
new file mode 100644
index 0000000..41477f7
--- /dev/null
+++ b/base/process/launch_win.cc
@@ -0,0 +1,283 @@
+// Copyright (c) 2012 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 "base/process/launch.h"
+
+#include <fcntl.h>
+#include <io.h>
+#include <windows.h>
+#include <userenv.h>
+#include <psapi.h>
+
+#include <ios>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/command_line.h"
+#include "base/debug/stack_trace.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop.h"
+#include "base/metrics/histogram.h"
+#include "base/process/kill.h"
+#include "base/sys_info.h"
+#include "base/win/object_watcher.h"
+#include "base/win/scoped_handle.h"
+#include "base/win/scoped_process_information.h"
+#include "base/win/windows_version.h"
+
+// userenv.dll is required for CreateEnvironmentBlock().
+#pragma comment(lib, "userenv.lib")
+
+namespace base {
+
+namespace {
+
+// This exit code is used by the Windows task manager when it kills a
+// process. It's value is obviously not that unique, and it's
+// surprising to me that the task manager uses this value, but it
+// seems to be common practice on Windows to test for it as an
+// indication that the task manager has killed something if the
+// process goes away.
+const DWORD kProcessKilledExitCode = 1;
+
+} // namespace
+
+void RouteStdioToConsole() {
+ // Don't change anything if stdout or stderr already point to a
+ // valid stream.
+ //
+ // If we are running under Buildbot or under Cygwin's default
+ // terminal (mintty), stderr and stderr will be pipe handles. In
+ // that case, we don't want to open CONOUT$, because its output
+ // likely does not go anywhere.
+ //
+ // We don't use GetStdHandle() to check stdout/stderr here because
+ // it can return dangling IDs of handles that were never inherited
+ // by this process. These IDs could have been reused by the time
+ // this function is called. The CRT checks the validity of
+ // stdout/stderr on startup (before the handle IDs can be reused).
+ // _fileno(stdout) will return -2 (_NO_CONSOLE_FILENO) if stdout was
+ // invalid.
+ if (_fileno(stdout) >= 0 || _fileno(stderr) >= 0)
+ return;
+
+ if (!AttachConsole(ATTACH_PARENT_PROCESS)) {
+ unsigned int result = GetLastError();
+ // Was probably already attached.
+ if (result == ERROR_ACCESS_DENIED)
+ return;
+ // Don't bother creating a new console for each child process if the
+ // parent process is invalid (eg: crashed).
+ if (result == ERROR_GEN_FAILURE)
+ return;
+ // Make a new console if attaching to parent fails with any other error.
+ // It should be ERROR_INVALID_HANDLE at this point, which means the browser
+ // was likely not started from a console.
+ AllocConsole();
+ }
+
+ // Arbitrary byte count to use when buffering output lines. More
+ // means potential waste, less means more risk of interleaved
+ // log-lines in output.
+ enum { kOutputBufferSize = 64 * 1024 };
+
+ if (freopen("CONOUT$", "w", stdout)) {
+ setvbuf(stdout, NULL, _IOLBF, kOutputBufferSize);
+ // Overwrite FD 1 for the benefit of any code that uses this FD
+ // directly. This is safe because the CRT allocates FDs 0, 1 and
+ // 2 at startup even if they don't have valid underlying Windows
+ // handles. This means we won't be overwriting an FD created by
+ // _open() after startup.
+ _dup2(_fileno(stdout), 1);
+ }
+ if (freopen("CONOUT$", "w", stderr)) {
+ setvbuf(stderr, NULL, _IOLBF, kOutputBufferSize);
+ _dup2(_fileno(stderr), 2);
+ }
+
+ // Fix all cout, wcout, cin, wcin, cerr, wcerr, clog and wclog.
+ std::ios::sync_with_stdio();
+}
+
+bool LaunchProcess(const string16& cmdline,
+ const LaunchOptions& options,
+ ProcessHandle* process_handle) {
+ STARTUPINFO startup_info = {};
+ startup_info.cb = sizeof(startup_info);
+ if (options.empty_desktop_name)
+ startup_info.lpDesktop = L"";
+ startup_info.dwFlags = STARTF_USESHOWWINDOW;
+ startup_info.wShowWindow = options.start_hidden ? SW_HIDE : SW_SHOW;
+
+ if (options.stdin_handle || options.stdout_handle || options.stderr_handle) {
+ DCHECK(options.inherit_handles);
+ DCHECK(options.stdin_handle);
+ DCHECK(options.stdout_handle);
+ DCHECK(options.stderr_handle);
+ startup_info.dwFlags |= STARTF_USESTDHANDLES;
+ startup_info.hStdInput = options.stdin_handle;
+ startup_info.hStdOutput = options.stdout_handle;
+ startup_info.hStdError = options.stderr_handle;
+ }
+
+ DWORD flags = 0;
+
+ if (options.job_handle) {
+ flags |= CREATE_SUSPENDED;
+
+ // If this code is run under a debugger, the launched process is
+ // automatically associated with a job object created by the debugger.
+ // The CREATE_BREAKAWAY_FROM_JOB flag is used to prevent this.
+ flags |= CREATE_BREAKAWAY_FROM_JOB;
+ }
+
+ if (options.force_breakaway_from_job_)
+ flags |= CREATE_BREAKAWAY_FROM_JOB;
+
+ base::win::ScopedProcessInformation process_info;
+
+ if (options.as_user) {
+ flags |= CREATE_UNICODE_ENVIRONMENT;
+ void* enviroment_block = NULL;
+
+ if (!CreateEnvironmentBlock(&enviroment_block, options.as_user, FALSE)) {
+ DPLOG(ERROR);
+ return false;
+ }
+
+ BOOL launched =
+ CreateProcessAsUser(options.as_user, NULL,
+ const_cast<wchar_t*>(cmdline.c_str()),
+ NULL, NULL, options.inherit_handles, flags,
+ enviroment_block, NULL, &startup_info,
+ process_info.Receive());
+ DestroyEnvironmentBlock(enviroment_block);
+ if (!launched) {
+ DPLOG(ERROR);
+ return false;
+ }
+ } else {
+ if (!CreateProcess(NULL,
+ const_cast<wchar_t*>(cmdline.c_str()), NULL, NULL,
+ options.inherit_handles, flags, NULL, NULL,
+ &startup_info, process_info.Receive())) {
+ DPLOG(ERROR);
+ return false;
+ }
+ }
+
+ if (options.job_handle) {
+ if (0 == AssignProcessToJobObject(options.job_handle,
+ process_info.process_handle())) {
+ DLOG(ERROR) << "Could not AssignProcessToObject.";
+ KillProcess(process_info.process_handle(), kProcessKilledExitCode, true);
+ return false;
+ }
+
+ ResumeThread(process_info.thread_handle());
+ }
+
+ if (options.wait)
+ WaitForSingleObject(process_info.process_handle(), INFINITE);
+
+ // If the caller wants the process handle, we won't close it.
+ if (process_handle)
+ *process_handle = process_info.TakeProcessHandle();
+
+ return true;
+}
+
+bool LaunchProcess(const CommandLine& cmdline,
+ const LaunchOptions& options,
+ ProcessHandle* process_handle) {
+ return LaunchProcess(cmdline.GetCommandLineString(), options, process_handle);
+}
+
+bool SetJobObjectAsKillOnJobClose(HANDLE job_object) {
+ JOBOBJECT_EXTENDED_LIMIT_INFORMATION limit_info = {0};
+ limit_info.BasicLimitInformation.LimitFlags =
+ JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
+ return 0 != SetInformationJobObject(
+ job_object,
+ JobObjectExtendedLimitInformation,
+ &limit_info,
+ sizeof(limit_info));
+}
+
+bool GetAppOutput(const CommandLine& cl, std::string* output) {
+ HANDLE out_read = NULL;
+ HANDLE out_write = NULL;
+
+ 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 = NULL;
+
+ // Create the pipe for the child process's STDOUT.
+ if (!CreatePipe(&out_read, &out_write, &sa_attr, 0)) {
+ NOTREACHED() << "Failed to create pipe";
+ return false;
+ }
+
+ // Ensure we don't leak the handles.
+ win::ScopedHandle scoped_out_read(out_read);
+ win::ScopedHandle scoped_out_write(out_write);
+
+ // Ensure the read handle to the pipe for STDOUT is not inherited.
+ if (!SetHandleInformation(out_read, HANDLE_FLAG_INHERIT, 0)) {
+ NOTREACHED() << "Failed to disabled pipe inheritance";
+ return false;
+ }
+
+ FilePath::StringType writable_command_line_string(cl.GetCommandLineString());
+
+ base::win::ScopedProcessInformation proc_info;
+ STARTUPINFO start_info = { 0 };
+
+ start_info.cb = sizeof(STARTUPINFO);
+ start_info.hStdOutput = out_write;
+ // Keep the normal stdin and stderr.
+ start_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
+ start_info.hStdError = GetStdHandle(STD_ERROR_HANDLE);
+ start_info.dwFlags |= STARTF_USESTDHANDLES;
+
+ // Create the child process.
+ if (!CreateProcess(NULL,
+ &writable_command_line_string[0],
+ NULL, NULL,
+ TRUE, // Handles are inherited.
+ 0, NULL, NULL, &start_info, proc_info.Receive())) {
+ NOTREACHED() << "Failed to start process";
+ return false;
+ }
+
+ // Close our writing end of pipe now. Otherwise later read would not be able
+ // to detect end of child's output.
+ scoped_out_write.Close();
+
+ // Read output from the child process's pipe for STDOUT
+ const int kBufferSize = 1024;
+ char buffer[kBufferSize];
+
+ for (;;) {
+ DWORD bytes_read = 0;
+ BOOL success = ReadFile(out_read, buffer, kBufferSize, &bytes_read, NULL);
+ if (!success || bytes_read == 0)
+ break;
+ output->append(buffer, bytes_read);
+ }
+
+ // Let's wait for the process to finish.
+ WaitForSingleObject(proc_info.process_handle(), INFINITE);
+
+ return true;
+}
+
+void RaiseProcessToHighPriority() {
+ SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
+}
+
+} // namespace base