1 // Copyright (c) 2013 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.
5 #include "base/process/kill.h"
10 #include "base/bind.h"
11 #include "base/bind_helpers.h"
12 #include "base/logging.h"
13 #include "base/message_loop/message_loop.h"
14 #include "base/process/process_iterator.h"
15 #include "base/win/object_watcher.h"
21 // Exit codes with special meanings on Windows.
22 const DWORD kNormalTerminationExitCode
= 0;
23 const DWORD kDebuggerInactiveExitCode
= 0xC0000354;
24 const DWORD kKeyboardInterruptExitCode
= 0xC000013A;
25 const DWORD kDebuggerTerminatedExitCode
= 0x40010004;
27 // This exit code is used by the Windows task manager when it kills a
28 // process. It's value is obviously not that unique, and it's
29 // surprising to me that the task manager uses this value, but it
30 // seems to be common practice on Windows to test for it as an
31 // indication that the task manager has killed something if the
33 const DWORD kProcessKilledExitCode
= 1;
35 // Maximum amount of time (in milliseconds) to wait for the process to exit.
36 static const int kWaitInterval
= 2000;
38 class TimerExpiredTask
: public win::ObjectWatcher::Delegate
{
40 explicit TimerExpiredTask(Process process
);
41 ~TimerExpiredTask() override
;
45 // MessageLoop::Watcher -----------------------------------------------------
46 void OnObjectSignaled(HANDLE object
) override
;
51 // The process that we are watching.
54 win::ObjectWatcher watcher_
;
56 DISALLOW_COPY_AND_ASSIGN(TimerExpiredTask
);
59 TimerExpiredTask::TimerExpiredTask(Process process
) : process_(process
.Pass()) {
60 watcher_
.StartWatching(process_
.Handle(), this);
63 TimerExpiredTask::~TimerExpiredTask() {
67 void TimerExpiredTask::TimedOut() {
68 if (process_
.IsValid())
72 void TimerExpiredTask::OnObjectSignaled(HANDLE object
) {
76 void TimerExpiredTask::KillProcess() {
77 // Stop watching the process handle since we're killing it.
78 watcher_
.StopWatching();
80 // OK, time to get frisky. We don't actually care when the process
81 // terminates. We just care that it eventually terminates, and that's what
82 // TerminateProcess should do for us. Don't check for the result code since
83 // it fails quite often. This should be investigated eventually.
84 process_
.Terminate(kProcessKilledExitCode
, false);
86 // Now, just cleanup as if the process exited normally.
87 OnObjectSignaled(process_
.Handle());
92 TerminationStatus
GetTerminationStatus(ProcessHandle handle
, int* exit_code
) {
93 DWORD tmp_exit_code
= 0;
95 if (!::GetExitCodeProcess(handle
, &tmp_exit_code
)) {
96 DPLOG(FATAL
) << "GetExitCodeProcess() failed";
98 // This really is a random number. We haven't received any
99 // information about the exit code, presumably because this
100 // process doesn't have permission to get the exit code, or
101 // because of some other cause for GetExitCodeProcess to fail
102 // (MSDN docs don't give the possible failure error codes for
103 // this function, so it could be anything). But we don't want
104 // to leave exit_code uninitialized, since that could cause
105 // random interpretations of the exit code. So we assume it
106 // terminated "normally" in this case.
107 *exit_code
= kNormalTerminationExitCode
;
109 // Assume the child has exited normally if we can't get the exit
111 return TERMINATION_STATUS_NORMAL_TERMINATION
;
113 if (tmp_exit_code
== STILL_ACTIVE
) {
114 DWORD wait_result
= WaitForSingleObject(handle
, 0);
115 if (wait_result
== WAIT_TIMEOUT
) {
117 *exit_code
= wait_result
;
118 return TERMINATION_STATUS_STILL_RUNNING
;
121 if (wait_result
== WAIT_FAILED
) {
122 DPLOG(ERROR
) << "WaitForSingleObject() failed";
124 DCHECK_EQ(WAIT_OBJECT_0
, wait_result
);
126 // Strange, the process used 0x103 (STILL_ACTIVE) as exit code.
130 return TERMINATION_STATUS_ABNORMAL_TERMINATION
;
134 *exit_code
= tmp_exit_code
;
136 switch (tmp_exit_code
) {
137 case kNormalTerminationExitCode
:
138 return TERMINATION_STATUS_NORMAL_TERMINATION
;
139 case kDebuggerInactiveExitCode
: // STATUS_DEBUGGER_INACTIVE.
140 case kKeyboardInterruptExitCode
: // Control-C/end session.
141 case kDebuggerTerminatedExitCode
: // Debugger terminated process.
142 case kProcessKilledExitCode
: // Task manager kill.
143 return TERMINATION_STATUS_PROCESS_WAS_KILLED
;
145 // All other exit codes indicate crashes.
146 return TERMINATION_STATUS_PROCESS_CRASHED
;
150 bool WaitForProcessesToExit(const FilePath::StringType
& executable_name
,
152 const ProcessFilter
* filter
) {
154 DWORD start_time
= GetTickCount();
156 NamedProcessIterator
iter(executable_name
, filter
);
157 for (const ProcessEntry
* entry
= iter
.NextProcessEntry(); entry
;
158 entry
= iter
.NextProcessEntry()) {
159 DWORD remaining_wait
= static_cast<DWORD
>(std::max(
160 static_cast<int64
>(0),
161 wait
.InMilliseconds() - (GetTickCount() - start_time
)));
162 HANDLE process
= OpenProcess(SYNCHRONIZE
,
164 entry
->th32ProcessID
);
165 DWORD wait_result
= WaitForSingleObject(process
, remaining_wait
);
166 CloseHandle(process
);
167 result
&= (wait_result
== WAIT_OBJECT_0
);
173 bool CleanupProcesses(const FilePath::StringType
& executable_name
,
176 const ProcessFilter
* filter
) {
177 if (WaitForProcessesToExit(executable_name
, wait
, filter
))
179 KillProcesses(executable_name
, exit_code
, filter
);
183 void EnsureProcessTerminated(Process process
) {
184 DCHECK(!process
.is_current());
186 // If already signaled, then we are done!
187 if (WaitForSingleObject(process
.Handle(), 0) == WAIT_OBJECT_0
) {
191 MessageLoop::current()->PostDelayedTask(
193 Bind(&TimerExpiredTask::TimedOut
,
194 Owned(new TimerExpiredTask(process
.Pass()))),
195 TimeDelta::FromMilliseconds(kWaitInterval
));