launch_win.cc revision f2477e01787aa58f445919b809d89e252beef54f
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                   win::ScopedHandle* 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  PROCESS_INFORMATION temp_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                            &temp_process_info);
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, &temp_process_info)) {
166      DPLOG(ERROR);
167      return false;
168    }
169  }
170  base::win::ScopedProcessInformation process_info(temp_process_info);
171
172  if (options.job_handle) {
173    if (0 == AssignProcessToJobObject(options.job_handle,
174                                      process_info.process_handle())) {
175      DLOG(ERROR) << "Could not AssignProcessToObject.";
176      KillProcess(process_info.process_handle(), kProcessKilledExitCode, true);
177      return false;
178    }
179
180    ResumeThread(process_info.thread_handle());
181  }
182
183  if (options.wait)
184    WaitForSingleObject(process_info.process_handle(), INFINITE);
185
186  // If the caller wants the process handle, we won't close it.
187  if (process_handle)
188    process_handle->Set(process_info.TakeProcessHandle());
189
190  return true;
191}
192
193bool LaunchProcess(const CommandLine& cmdline,
194                   const LaunchOptions& options,
195                   ProcessHandle* process_handle) {
196  if (!process_handle)
197    return LaunchProcess(cmdline.GetCommandLineString(), options, NULL);
198
199  win::ScopedHandle process;
200  bool rv = LaunchProcess(cmdline.GetCommandLineString(), options, &process);
201  *process_handle = process.Take();
202  return rv;
203}
204
205bool SetJobObjectLimitFlags(HANDLE job_object, DWORD limit_flags) {
206  JOBOBJECT_EXTENDED_LIMIT_INFORMATION limit_info = {0};
207  limit_info.BasicLimitInformation.LimitFlags = limit_flags;
208  return 0 != SetInformationJobObject(
209      job_object,
210      JobObjectExtendedLimitInformation,
211      &limit_info,
212      sizeof(limit_info));
213}
214
215bool GetAppOutput(const CommandLine& cl, std::string* output) {
216  HANDLE out_read = NULL;
217  HANDLE out_write = NULL;
218
219  SECURITY_ATTRIBUTES sa_attr;
220  // Set the bInheritHandle flag so pipe handles are inherited.
221  sa_attr.nLength = sizeof(SECURITY_ATTRIBUTES);
222  sa_attr.bInheritHandle = TRUE;
223  sa_attr.lpSecurityDescriptor = NULL;
224
225  // Create the pipe for the child process's STDOUT.
226  if (!CreatePipe(&out_read, &out_write, &sa_attr, 0)) {
227    NOTREACHED() << "Failed to create pipe";
228    return false;
229  }
230
231  // Ensure we don't leak the handles.
232  win::ScopedHandle scoped_out_read(out_read);
233  win::ScopedHandle scoped_out_write(out_write);
234
235  // Ensure the read handle to the pipe for STDOUT is not inherited.
236  if (!SetHandleInformation(out_read, HANDLE_FLAG_INHERIT, 0)) {
237    NOTREACHED() << "Failed to disabled pipe inheritance";
238    return false;
239  }
240
241  FilePath::StringType writable_command_line_string(cl.GetCommandLineString());
242
243  STARTUPINFO start_info = {};
244
245  start_info.cb = sizeof(STARTUPINFO);
246  start_info.hStdOutput = out_write;
247  // Keep the normal stdin and stderr.
248  start_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
249  start_info.hStdError = GetStdHandle(STD_ERROR_HANDLE);
250  start_info.dwFlags |= STARTF_USESTDHANDLES;
251
252  // Create the child process.
253  PROCESS_INFORMATION temp_process_info = {};
254  if (!CreateProcess(NULL,
255                     &writable_command_line_string[0],
256                     NULL, NULL,
257                     TRUE,  // Handles are inherited.
258                     0, NULL, NULL, &start_info, &temp_process_info)) {
259    NOTREACHED() << "Failed to start process";
260    return false;
261  }
262  base::win::ScopedProcessInformation proc_info(temp_process_info);
263
264  // Close our writing end of pipe now. Otherwise later read would not be able
265  // to detect end of child's output.
266  scoped_out_write.Close();
267
268  // Read output from the child process's pipe for STDOUT
269  const int kBufferSize = 1024;
270  char buffer[kBufferSize];
271
272  for (;;) {
273    DWORD bytes_read = 0;
274    BOOL success = ReadFile(out_read, buffer, kBufferSize, &bytes_read, NULL);
275    if (!success || bytes_read == 0)
276      break;
277    output->append(buffer, bytes_read);
278  }
279
280  // Let's wait for the process to finish.
281  WaitForSingleObject(proc_info.process_handle(), INFINITE);
282
283  return true;
284}
285
286void RaiseProcessToHighPriority() {
287  SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
288}
289
290}  // namespace base
291