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