launch_win.cc revision ca12bfac764ba476d6cd062bf1dde12cc64c3f40
1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "base/process/launch.h"
6
7#include <fcntl.h>
8#include <io.h>
9#include <windows.h>
10#include <userenv.h>
11#include <psapi.h>
12
13#include <ios>
14
15#include "base/bind.h"
16#include "base/bind_helpers.h"
17#include "base/command_line.h"
18#include "base/debug/stack_trace.h"
19#include "base/logging.h"
20#include "base/memory/scoped_ptr.h"
21#include "base/message_loop/message_loop.h"
22#include "base/metrics/histogram.h"
23#include "base/process/kill.h"
24#include "base/sys_info.h"
25#include "base/win/object_watcher.h"
26#include "base/win/scoped_handle.h"
27#include "base/win/scoped_process_information.h"
28#include "base/win/windows_version.h"
29
30// userenv.dll is required for CreateEnvironmentBlock().
31#pragma comment(lib, "userenv.lib")
32
33namespace base {
34
35namespace {
36
37// This exit code is used by the Windows task manager when it kills a
38// process.  It's value is obviously not that unique, and it's
39// surprising to me that the task manager uses this value, but it
40// seems to be common practice on Windows to test for it as an
41// indication that the task manager has killed something if the
42// process goes away.
43const DWORD kProcessKilledExitCode = 1;
44
45}  // namespace
46
47void RouteStdioToConsole() {
48  // Don't change anything if stdout or stderr already point to a
49  // valid stream.
50  //
51  // If we are running under Buildbot or under Cygwin's default
52  // terminal (mintty), stderr and stderr will be pipe handles.  In
53  // that case, we don't want to open CONOUT$, because its output
54  // likely does not go anywhere.
55  //
56  // We don't use GetStdHandle() to check stdout/stderr here because
57  // it can return dangling IDs of handles that were never inherited
58  // by this process.  These IDs could have been reused by the time
59  // this function is called.  The CRT checks the validity of
60  // stdout/stderr on startup (before the handle IDs can be reused).
61  // _fileno(stdout) will return -2 (_NO_CONSOLE_FILENO) if stdout was
62  // invalid.
63  if (_fileno(stdout) >= 0 || _fileno(stderr) >= 0)
64    return;
65
66  if (!AttachConsole(ATTACH_PARENT_PROCESS)) {
67    unsigned int result = GetLastError();
68    // Was probably already attached.
69    if (result == ERROR_ACCESS_DENIED)
70      return;
71    // Don't bother creating a new console for each child process if the
72    // parent process is invalid (eg: crashed).
73    if (result == ERROR_GEN_FAILURE)
74      return;
75    // Make a new console if attaching to parent fails with any other error.
76    // It should be ERROR_INVALID_HANDLE at this point, which means the browser
77    // was likely not started from a console.
78    AllocConsole();
79  }
80
81  // Arbitrary byte count to use when buffering output lines.  More
82  // means potential waste, less means more risk of interleaved
83  // log-lines in output.
84  enum { kOutputBufferSize = 64 * 1024 };
85
86  if (freopen("CONOUT$", "w", stdout)) {
87    setvbuf(stdout, NULL, _IOLBF, kOutputBufferSize);
88    // Overwrite FD 1 for the benefit of any code that uses this FD
89    // directly.  This is safe because the CRT allocates FDs 0, 1 and
90    // 2 at startup even if they don't have valid underlying Windows
91    // handles.  This means we won't be overwriting an FD created by
92    // _open() after startup.
93    _dup2(_fileno(stdout), 1);
94  }
95  if (freopen("CONOUT$", "w", stderr)) {
96    setvbuf(stderr, NULL, _IOLBF, kOutputBufferSize);
97    _dup2(_fileno(stderr), 2);
98  }
99
100  // Fix all cout, wcout, cin, wcin, cerr, wcerr, clog and wclog.
101  std::ios::sync_with_stdio();
102}
103
104bool LaunchProcess(const string16& cmdline,
105                   const LaunchOptions& options,
106                   ProcessHandle* process_handle) {
107  STARTUPINFO startup_info = {};
108  startup_info.cb = sizeof(startup_info);
109  if (options.empty_desktop_name)
110    startup_info.lpDesktop = L"";
111  startup_info.dwFlags = STARTF_USESHOWWINDOW;
112  startup_info.wShowWindow = options.start_hidden ? SW_HIDE : SW_SHOW;
113
114  if (options.stdin_handle || options.stdout_handle || options.stderr_handle) {
115    DCHECK(options.inherit_handles);
116    DCHECK(options.stdin_handle);
117    DCHECK(options.stdout_handle);
118    DCHECK(options.stderr_handle);
119    startup_info.dwFlags |= STARTF_USESTDHANDLES;
120    startup_info.hStdInput = options.stdin_handle;
121    startup_info.hStdOutput = options.stdout_handle;
122    startup_info.hStdError = options.stderr_handle;
123  }
124
125  DWORD flags = 0;
126
127  if (options.job_handle) {
128    flags |= CREATE_SUSPENDED;
129
130    // If this code is run under a debugger, the launched process is
131    // automatically associated with a job object created by the debugger.
132    // The CREATE_BREAKAWAY_FROM_JOB flag is used to prevent this.
133    flags |= CREATE_BREAKAWAY_FROM_JOB;
134  }
135
136  if (options.force_breakaway_from_job_)
137    flags |= CREATE_BREAKAWAY_FROM_JOB;
138
139  base::win::ScopedProcessInformation process_info;
140
141  if (options.as_user) {
142    flags |= CREATE_UNICODE_ENVIRONMENT;
143    void* enviroment_block = NULL;
144
145    if (!CreateEnvironmentBlock(&enviroment_block, options.as_user, FALSE)) {
146      DPLOG(ERROR);
147      return false;
148    }
149
150    BOOL launched =
151        CreateProcessAsUser(options.as_user, NULL,
152                            const_cast<wchar_t*>(cmdline.c_str()),
153                            NULL, NULL, options.inherit_handles, flags,
154                            enviroment_block, NULL, &startup_info,
155                            process_info.Receive());
156    DestroyEnvironmentBlock(enviroment_block);
157    if (!launched) {
158      DPLOG(ERROR);
159      return false;
160    }
161  } else {
162    if (!CreateProcess(NULL,
163                       const_cast<wchar_t*>(cmdline.c_str()), NULL, NULL,
164                       options.inherit_handles, flags, NULL, NULL,
165                       &startup_info, process_info.Receive())) {
166      DPLOG(ERROR);
167      return false;
168    }
169  }
170
171  if (options.job_handle) {
172    if (0 == AssignProcessToJobObject(options.job_handle,
173                                      process_info.process_handle())) {
174      DLOG(ERROR) << "Could not AssignProcessToObject.";
175      KillProcess(process_info.process_handle(), kProcessKilledExitCode, true);
176      return false;
177    }
178
179    ResumeThread(process_info.thread_handle());
180  }
181
182  if (options.wait)
183    WaitForSingleObject(process_info.process_handle(), INFINITE);
184
185  // If the caller wants the process handle, we won't close it.
186  if (process_handle)
187    *process_handle = process_info.TakeProcessHandle();
188
189  return true;
190}
191
192bool LaunchProcess(const CommandLine& cmdline,
193                   const LaunchOptions& options,
194                   ProcessHandle* process_handle) {
195  return LaunchProcess(cmdline.GetCommandLineString(), options, process_handle);
196}
197
198bool SetJobObjectAsKillOnJobClose(HANDLE job_object) {
199  JOBOBJECT_EXTENDED_LIMIT_INFORMATION limit_info = {0};
200  limit_info.BasicLimitInformation.LimitFlags =
201      JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
202  return 0 != SetInformationJobObject(
203      job_object,
204      JobObjectExtendedLimitInformation,
205      &limit_info,
206      sizeof(limit_info));
207}
208
209bool GetAppOutput(const CommandLine& cl, std::string* output) {
210  HANDLE out_read = NULL;
211  HANDLE out_write = NULL;
212
213  SECURITY_ATTRIBUTES sa_attr;
214  // Set the bInheritHandle flag so pipe handles are inherited.
215  sa_attr.nLength = sizeof(SECURITY_ATTRIBUTES);
216  sa_attr.bInheritHandle = TRUE;
217  sa_attr.lpSecurityDescriptor = NULL;
218
219  // Create the pipe for the child process's STDOUT.
220  if (!CreatePipe(&out_read, &out_write, &sa_attr, 0)) {
221    NOTREACHED() << "Failed to create pipe";
222    return false;
223  }
224
225  // Ensure we don't leak the handles.
226  win::ScopedHandle scoped_out_read(out_read);
227  win::ScopedHandle scoped_out_write(out_write);
228
229  // Ensure the read handle to the pipe for STDOUT is not inherited.
230  if (!SetHandleInformation(out_read, HANDLE_FLAG_INHERIT, 0)) {
231    NOTREACHED() << "Failed to disabled pipe inheritance";
232    return false;
233  }
234
235  FilePath::StringType writable_command_line_string(cl.GetCommandLineString());
236
237  base::win::ScopedProcessInformation proc_info;
238  STARTUPINFO start_info = { 0 };
239
240  start_info.cb = sizeof(STARTUPINFO);
241  start_info.hStdOutput = out_write;
242  // Keep the normal stdin and stderr.
243  start_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
244  start_info.hStdError = GetStdHandle(STD_ERROR_HANDLE);
245  start_info.dwFlags |= STARTF_USESTDHANDLES;
246
247  // Create the child process.
248  if (!CreateProcess(NULL,
249                     &writable_command_line_string[0],
250                     NULL, NULL,
251                     TRUE,  // Handles are inherited.
252                     0, NULL, NULL, &start_info, proc_info.Receive())) {
253    NOTREACHED() << "Failed to start process";
254    return false;
255  }
256
257  // Close our writing end of pipe now. Otherwise later read would not be able
258  // to detect end of child's output.
259  scoped_out_write.Close();
260
261  // Read output from the child process's pipe for STDOUT
262  const int kBufferSize = 1024;
263  char buffer[kBufferSize];
264
265  for (;;) {
266    DWORD bytes_read = 0;
267    BOOL success = ReadFile(out_read, buffer, kBufferSize, &bytes_read, NULL);
268    if (!success || bytes_read == 0)
269      break;
270    output->append(buffer, bytes_read);
271  }
272
273  // Let's wait for the process to finish.
274  WaitForSingleObject(proc_info.process_handle(), INFINITE);
275
276  return true;
277}
278
279void RaiseProcessToHighPriority() {
280  SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
281}
282
283}  // namespace base
284