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 |