| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "base/process_util.h" | 5 #include "base/process_util.h" |
| 6 | 6 |
| 7 #include <fcntl.h> | 7 #include <fcntl.h> |
| 8 #include <io.h> | 8 #include <io.h> |
| 9 #include <windows.h> | 9 #include <windows.h> |
| 10 #include <userenv.h> | 10 #include <userenv.h> |
| (...skipping 15 matching lines...) Expand all Loading... |
| 26 #include "base/win/scoped_process_information.h" | 26 #include "base/win/scoped_process_information.h" |
| 27 #include "base/win/windows_version.h" | 27 #include "base/win/windows_version.h" |
| 28 | 28 |
| 29 // userenv.dll is required for CreateEnvironmentBlock(). | 29 // userenv.dll is required for CreateEnvironmentBlock(). |
| 30 #pragma comment(lib, "userenv.lib") | 30 #pragma comment(lib, "userenv.lib") |
| 31 | 31 |
| 32 namespace base { | 32 namespace base { |
| 33 | 33 |
| 34 namespace { | 34 namespace { |
| 35 | 35 |
| 36 // Exit codes with special meanings on Windows. | |
| 37 const DWORD kNormalTerminationExitCode = 0; | |
| 38 const DWORD kDebuggerInactiveExitCode = 0xC0000354; | |
| 39 const DWORD kKeyboardInterruptExitCode = 0xC000013A; | |
| 40 const DWORD kDebuggerTerminatedExitCode = 0x40010004; | |
| 41 | |
| 42 // Maximum amount of time (in milliseconds) to wait for the process to exit. | |
| 43 static const int kWaitInterval = 2000; | |
| 44 | |
| 45 // This exit code is used by the Windows task manager when it kills a | 36 // This exit code is used by the Windows task manager when it kills a |
| 46 // process. It's value is obviously not that unique, and it's | 37 // process. It's value is obviously not that unique, and it's |
| 47 // surprising to me that the task manager uses this value, but it | 38 // surprising to me that the task manager uses this value, but it |
| 48 // seems to be common practice on Windows to test for it as an | 39 // seems to be common practice on Windows to test for it as an |
| 49 // indication that the task manager has killed something if the | 40 // indication that the task manager has killed something if the |
| 50 // process goes away. | 41 // process goes away. |
| 51 const DWORD kProcessKilledExitCode = 1; | 42 const DWORD kProcessKilledExitCode = 1; |
| 52 | 43 |
| 53 class TimerExpiredTask : public win::ObjectWatcher::Delegate { | |
| 54 public: | |
| 55 explicit TimerExpiredTask(ProcessHandle process); | |
| 56 ~TimerExpiredTask(); | |
| 57 | |
| 58 void TimedOut(); | |
| 59 | |
| 60 // MessageLoop::Watcher ----------------------------------------------------- | |
| 61 virtual void OnObjectSignaled(HANDLE object); | |
| 62 | |
| 63 private: | |
| 64 void KillProcess(); | |
| 65 | |
| 66 // The process that we are watching. | |
| 67 ProcessHandle process_; | |
| 68 | |
| 69 win::ObjectWatcher watcher_; | |
| 70 | |
| 71 DISALLOW_COPY_AND_ASSIGN(TimerExpiredTask); | |
| 72 }; | |
| 73 | |
| 74 TimerExpiredTask::TimerExpiredTask(ProcessHandle process) : process_(process) { | |
| 75 watcher_.StartWatching(process_, this); | |
| 76 } | |
| 77 | |
| 78 TimerExpiredTask::~TimerExpiredTask() { | |
| 79 TimedOut(); | |
| 80 DCHECK(!process_) << "Make sure to close the handle."; | |
| 81 } | |
| 82 | |
| 83 void TimerExpiredTask::TimedOut() { | |
| 84 if (process_) | |
| 85 KillProcess(); | |
| 86 } | |
| 87 | |
| 88 void TimerExpiredTask::OnObjectSignaled(HANDLE object) { | |
| 89 CloseHandle(process_); | |
| 90 process_ = NULL; | |
| 91 } | |
| 92 | |
| 93 void TimerExpiredTask::KillProcess() { | |
| 94 // Stop watching the process handle since we're killing it. | |
| 95 watcher_.StopWatching(); | |
| 96 | |
| 97 // OK, time to get frisky. We don't actually care when the process | |
| 98 // terminates. We just care that it eventually terminates, and that's what | |
| 99 // TerminateProcess should do for us. Don't check for the result code since | |
| 100 // it fails quite often. This should be investigated eventually. | |
| 101 base::KillProcess(process_, kProcessKilledExitCode, false); | |
| 102 | |
| 103 // Now, just cleanup as if the process exited normally. | |
| 104 OnObjectSignaled(process_); | |
| 105 } | |
| 106 | |
| 107 } // namespace | 44 } // namespace |
| 108 | 45 |
| 109 void RouteStdioToConsole() { | 46 void RouteStdioToConsole() { |
| 110 // Don't change anything if stdout or stderr already point to a | 47 // Don't change anything if stdout or stderr already point to a |
| 111 // valid stream. | 48 // valid stream. |
| 112 // | 49 // |
| 113 // If we are running under Buildbot or under Cygwin's default | 50 // If we are running under Buildbot or under Cygwin's default |
| 114 // terminal (mintty), stderr and stderr will be pipe handles. In | 51 // terminal (mintty), stderr and stderr will be pipe handles. In |
| 115 // that case, we don't want to open CONOUT$, because its output | 52 // that case, we don't want to open CONOUT$, because its output |
| 116 // likely does not go anywhere. | 53 // likely does not go anywhere. |
| (...skipping 255 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 372 JOBOBJECT_EXTENDED_LIMIT_INFORMATION limit_info = {0}; | 309 JOBOBJECT_EXTENDED_LIMIT_INFORMATION limit_info = {0}; |
| 373 limit_info.BasicLimitInformation.LimitFlags = | 310 limit_info.BasicLimitInformation.LimitFlags = |
| 374 JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE; | 311 JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE; |
| 375 return 0 != SetInformationJobObject( | 312 return 0 != SetInformationJobObject( |
| 376 job_object, | 313 job_object, |
| 377 JobObjectExtendedLimitInformation, | 314 JobObjectExtendedLimitInformation, |
| 378 &limit_info, | 315 &limit_info, |
| 379 sizeof(limit_info)); | 316 sizeof(limit_info)); |
| 380 } | 317 } |
| 381 | 318 |
| 382 // Attempts to kill the process identified by the given process | |
| 383 // entry structure, giving it the specified exit code. | |
| 384 // Returns true if this is successful, false otherwise. | |
| 385 bool KillProcessById(ProcessId process_id, int exit_code, bool wait) { | |
| 386 HANDLE process = OpenProcess(PROCESS_TERMINATE | SYNCHRONIZE, | |
| 387 FALSE, // Don't inherit handle | |
| 388 process_id); | |
| 389 if (!process) { | |
| 390 DLOG_GETLASTERROR(ERROR) << "Unable to open process " << process_id; | |
| 391 return false; | |
| 392 } | |
| 393 bool ret = KillProcess(process, exit_code, wait); | |
| 394 CloseHandle(process); | |
| 395 return ret; | |
| 396 } | |
| 397 | |
| 398 bool GetAppOutput(const CommandLine& cl, std::string* output) { | 319 bool GetAppOutput(const CommandLine& cl, std::string* output) { |
| 399 HANDLE out_read = NULL; | 320 HANDLE out_read = NULL; |
| 400 HANDLE out_write = NULL; | 321 HANDLE out_write = NULL; |
| 401 | 322 |
| 402 SECURITY_ATTRIBUTES sa_attr; | 323 SECURITY_ATTRIBUTES sa_attr; |
| 403 // Set the bInheritHandle flag so pipe handles are inherited. | 324 // Set the bInheritHandle flag so pipe handles are inherited. |
| 404 sa_attr.nLength = sizeof(SECURITY_ATTRIBUTES); | 325 sa_attr.nLength = sizeof(SECURITY_ATTRIBUTES); |
| 405 sa_attr.bInheritHandle = TRUE; | 326 sa_attr.bInheritHandle = TRUE; |
| 406 sa_attr.lpSecurityDescriptor = NULL; | 327 sa_attr.lpSecurityDescriptor = NULL; |
| 407 | 328 |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 458 break; | 379 break; |
| 459 output->append(buffer, bytes_read); | 380 output->append(buffer, bytes_read); |
| 460 } | 381 } |
| 461 | 382 |
| 462 // Let's wait for the process to finish. | 383 // Let's wait for the process to finish. |
| 463 WaitForSingleObject(proc_info.process_handle(), INFINITE); | 384 WaitForSingleObject(proc_info.process_handle(), INFINITE); |
| 464 | 385 |
| 465 return true; | 386 return true; |
| 466 } | 387 } |
| 467 | 388 |
| 468 bool KillProcess(ProcessHandle process, int exit_code, bool wait) { | |
| 469 bool result = (TerminateProcess(process, exit_code) != FALSE); | |
| 470 if (result && wait) { | |
| 471 // The process may not end immediately due to pending I/O | |
| 472 if (WAIT_OBJECT_0 != WaitForSingleObject(process, 60 * 1000)) | |
| 473 DLOG_GETLASTERROR(ERROR) << "Error waiting for process exit"; | |
| 474 } else if (!result) { | |
| 475 DLOG_GETLASTERROR(ERROR) << "Unable to terminate process"; | |
| 476 } | |
| 477 return result; | |
| 478 } | |
| 479 | |
| 480 TerminationStatus GetTerminationStatus(ProcessHandle handle, int* exit_code) { | |
| 481 DWORD tmp_exit_code = 0; | |
| 482 | |
| 483 if (!::GetExitCodeProcess(handle, &tmp_exit_code)) { | |
| 484 DLOG_GETLASTERROR(FATAL) << "GetExitCodeProcess() failed"; | |
| 485 if (exit_code) { | |
| 486 // This really is a random number. We haven't received any | |
| 487 // information about the exit code, presumably because this | |
| 488 // process doesn't have permission to get the exit code, or | |
| 489 // because of some other cause for GetExitCodeProcess to fail | |
| 490 // (MSDN docs don't give the possible failure error codes for | |
| 491 // this function, so it could be anything). But we don't want | |
| 492 // to leave exit_code uninitialized, since that could cause | |
| 493 // random interpretations of the exit code. So we assume it | |
| 494 // terminated "normally" in this case. | |
| 495 *exit_code = kNormalTerminationExitCode; | |
| 496 } | |
| 497 // Assume the child has exited normally if we can't get the exit | |
| 498 // code. | |
| 499 return TERMINATION_STATUS_NORMAL_TERMINATION; | |
| 500 } | |
| 501 if (tmp_exit_code == STILL_ACTIVE) { | |
| 502 DWORD wait_result = WaitForSingleObject(handle, 0); | |
| 503 if (wait_result == WAIT_TIMEOUT) { | |
| 504 if (exit_code) | |
| 505 *exit_code = wait_result; | |
| 506 return TERMINATION_STATUS_STILL_RUNNING; | |
| 507 } | |
| 508 | |
| 509 if (wait_result == WAIT_FAILED) { | |
| 510 DLOG_GETLASTERROR(ERROR) << "WaitForSingleObject() failed"; | |
| 511 } else { | |
| 512 DCHECK_EQ(WAIT_OBJECT_0, wait_result); | |
| 513 | |
| 514 // Strange, the process used 0x103 (STILL_ACTIVE) as exit code. | |
| 515 NOTREACHED(); | |
| 516 } | |
| 517 | |
| 518 return TERMINATION_STATUS_ABNORMAL_TERMINATION; | |
| 519 } | |
| 520 | |
| 521 if (exit_code) | |
| 522 *exit_code = tmp_exit_code; | |
| 523 | |
| 524 switch (tmp_exit_code) { | |
| 525 case kNormalTerminationExitCode: | |
| 526 return TERMINATION_STATUS_NORMAL_TERMINATION; | |
| 527 case kDebuggerInactiveExitCode: // STATUS_DEBUGGER_INACTIVE. | |
| 528 case kKeyboardInterruptExitCode: // Control-C/end session. | |
| 529 case kDebuggerTerminatedExitCode: // Debugger terminated process. | |
| 530 case kProcessKilledExitCode: // Task manager kill. | |
| 531 return TERMINATION_STATUS_PROCESS_WAS_KILLED; | |
| 532 default: | |
| 533 // All other exit codes indicate crashes. | |
| 534 return TERMINATION_STATUS_PROCESS_CRASHED; | |
| 535 } | |
| 536 } | |
| 537 | |
| 538 bool WaitForExitCode(ProcessHandle handle, int* exit_code) { | |
| 539 bool success = WaitForExitCodeWithTimeout( | |
| 540 handle, exit_code, base::TimeDelta::FromMilliseconds(INFINITE)); | |
| 541 CloseProcessHandle(handle); | |
| 542 return success; | |
| 543 } | |
| 544 | |
| 545 bool WaitForExitCodeWithTimeout(ProcessHandle handle, int* exit_code, | |
| 546 base::TimeDelta timeout) { | |
| 547 if (::WaitForSingleObject(handle, timeout.InMilliseconds()) != WAIT_OBJECT_0) | |
| 548 return false; | |
| 549 DWORD temp_code; // Don't clobber out-parameters in case of failure. | |
| 550 if (!::GetExitCodeProcess(handle, &temp_code)) | |
| 551 return false; | |
| 552 | |
| 553 *exit_code = temp_code; | |
| 554 return true; | |
| 555 } | |
| 556 | |
| 557 bool WaitForProcessesToExit(const FilePath::StringType& executable_name, | |
| 558 base::TimeDelta wait, | |
| 559 const ProcessFilter* filter) { | |
| 560 const ProcessEntry* entry; | |
| 561 bool result = true; | |
| 562 DWORD start_time = GetTickCount(); | |
| 563 | |
| 564 NamedProcessIterator iter(executable_name, filter); | |
| 565 while ((entry = iter.NextProcessEntry())) { | |
| 566 DWORD remaining_wait = std::max<int64>( | |
| 567 0, wait.InMilliseconds() - (GetTickCount() - start_time)); | |
| 568 HANDLE process = OpenProcess(SYNCHRONIZE, | |
| 569 FALSE, | |
| 570 entry->th32ProcessID); | |
| 571 DWORD wait_result = WaitForSingleObject(process, remaining_wait); | |
| 572 CloseHandle(process); | |
| 573 result = result && (wait_result == WAIT_OBJECT_0); | |
| 574 } | |
| 575 | |
| 576 return result; | |
| 577 } | |
| 578 | |
| 579 bool WaitForSingleProcess(ProcessHandle handle, base::TimeDelta wait) { | |
| 580 int exit_code; | |
| 581 if (!WaitForExitCodeWithTimeout(handle, &exit_code, wait)) | |
| 582 return false; | |
| 583 return exit_code == 0; | |
| 584 } | |
| 585 | |
| 586 bool CleanupProcesses(const FilePath::StringType& executable_name, | |
| 587 base::TimeDelta wait, | |
| 588 int exit_code, | |
| 589 const ProcessFilter* filter) { | |
| 590 bool exited_cleanly = WaitForProcessesToExit(executable_name, wait, filter); | |
| 591 if (!exited_cleanly) | |
| 592 KillProcesses(executable_name, exit_code, filter); | |
| 593 return exited_cleanly; | |
| 594 } | |
| 595 | |
| 596 void EnsureProcessTerminated(ProcessHandle process) { | |
| 597 DCHECK(process != GetCurrentProcess()); | |
| 598 | |
| 599 // If already signaled, then we are done! | |
| 600 if (WaitForSingleObject(process, 0) == WAIT_OBJECT_0) { | |
| 601 CloseHandle(process); | |
| 602 return; | |
| 603 } | |
| 604 | |
| 605 MessageLoop::current()->PostDelayedTask( | |
| 606 FROM_HERE, | |
| 607 base::Bind(&TimerExpiredTask::TimedOut, | |
| 608 base::Owned(new TimerExpiredTask(process))), | |
| 609 base::TimeDelta::FromMilliseconds(kWaitInterval)); | |
| 610 } | |
| 611 | |
| 612 void RaiseProcessToHighPriority() { | 389 void RaiseProcessToHighPriority() { |
| 613 SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS); | 390 SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS); |
| 614 } | 391 } |
| 615 | 392 |
| 616 } // namespace base | 393 } // namespace base |
| OLD | NEW |