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