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.
4
5#include "base/process/kill.h"
6
7#include <io.h>
8#include <windows.h>
9
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"
16
17namespace base {
18
19namespace {
20
21// Exit codes with special meanings on Windows.
22const DWORD kNormalTerminationExitCode = 0;
23const DWORD kDebuggerInactiveExitCode = 0xC0000354;
24const DWORD kKeyboardInterruptExitCode = 0xC000013A;
25const DWORD kDebuggerTerminatedExitCode = 0x40010004;
26
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
32// process goes away.
33const DWORD kProcessKilledExitCode = 1;
34
35// Maximum amount of time (in milliseconds) to wait for the process to exit.
36static const int kWaitInterval = 2000;
37
38class TimerExpiredTask : public win::ObjectWatcher::Delegate {
39 public:
40  explicit TimerExpiredTask(ProcessHandle process);
41  ~TimerExpiredTask();
42
43  void TimedOut();
44
45  // MessageLoop::Watcher -----------------------------------------------------
46  virtual void OnObjectSignaled(HANDLE object);
47
48 private:
49  void KillProcess();
50
51  // The process that we are watching.
52  ProcessHandle process_;
53
54  win::ObjectWatcher watcher_;
55
56  DISALLOW_COPY_AND_ASSIGN(TimerExpiredTask);
57};
58
59TimerExpiredTask::TimerExpiredTask(ProcessHandle process) : process_(process) {
60  watcher_.StartWatching(process_, this);
61}
62
63TimerExpiredTask::~TimerExpiredTask() {
64  TimedOut();
65  DCHECK(!process_) << "Make sure to close the handle.";
66}
67
68void TimerExpiredTask::TimedOut() {
69  if (process_)
70    KillProcess();
71}
72
73void TimerExpiredTask::OnObjectSignaled(HANDLE object) {
74  CloseHandle(process_);
75  process_ = NULL;
76}
77
78void TimerExpiredTask::KillProcess() {
79  // Stop watching the process handle since we're killing it.
80  watcher_.StopWatching();
81
82  // OK, time to get frisky.  We don't actually care when the process
83  // terminates.  We just care that it eventually terminates, and that's what
84  // TerminateProcess should do for us. Don't check for the result code since
85  // it fails quite often. This should be investigated eventually.
86  base::KillProcess(process_, kProcessKilledExitCode, false);
87
88  // Now, just cleanup as if the process exited normally.
89  OnObjectSignaled(process_);
90}
91
92}  // namespace
93
94bool KillProcess(ProcessHandle process, int exit_code, bool wait) {
95  bool result = (TerminateProcess(process, exit_code) != FALSE);
96  if (result && wait) {
97    // The process may not end immediately due to pending I/O
98    if (WAIT_OBJECT_0 != WaitForSingleObject(process, 60 * 1000))
99      DPLOG(ERROR) << "Error waiting for process exit";
100  } else if (!result) {
101    DPLOG(ERROR) << "Unable to terminate process";
102  }
103  return result;
104}
105
106// Attempts to kill the process identified by the given process
107// entry structure, giving it the specified exit code.
108// Returns true if this is successful, false otherwise.
109bool KillProcessById(ProcessId process_id, int exit_code, bool wait) {
110  HANDLE process = OpenProcess(PROCESS_TERMINATE | SYNCHRONIZE,
111                               FALSE,  // Don't inherit handle
112                               process_id);
113  if (!process) {
114    DPLOG(ERROR) << "Unable to open process " << process_id;
115    return false;
116  }
117  bool ret = KillProcess(process, exit_code, wait);
118  CloseHandle(process);
119  return ret;
120}
121
122TerminationStatus GetTerminationStatus(ProcessHandle handle, int* exit_code) {
123  DWORD tmp_exit_code = 0;
124
125  if (!::GetExitCodeProcess(handle, &tmp_exit_code)) {
126    DPLOG(FATAL) << "GetExitCodeProcess() failed";
127    if (exit_code) {
128      // This really is a random number.  We haven't received any
129      // information about the exit code, presumably because this
130      // process doesn't have permission to get the exit code, or
131      // because of some other cause for GetExitCodeProcess to fail
132      // (MSDN docs don't give the possible failure error codes for
133      // this function, so it could be anything).  But we don't want
134      // to leave exit_code uninitialized, since that could cause
135      // random interpretations of the exit code.  So we assume it
136      // terminated "normally" in this case.
137      *exit_code = kNormalTerminationExitCode;
138    }
139    // Assume the child has exited normally if we can't get the exit
140    // code.
141    return TERMINATION_STATUS_NORMAL_TERMINATION;
142  }
143  if (tmp_exit_code == STILL_ACTIVE) {
144    DWORD wait_result = WaitForSingleObject(handle, 0);
145    if (wait_result == WAIT_TIMEOUT) {
146      if (exit_code)
147        *exit_code = wait_result;
148      return TERMINATION_STATUS_STILL_RUNNING;
149    }
150
151    if (wait_result == WAIT_FAILED) {
152      DPLOG(ERROR) << "WaitForSingleObject() failed";
153    } else {
154      DCHECK_EQ(WAIT_OBJECT_0, wait_result);
155
156      // Strange, the process used 0x103 (STILL_ACTIVE) as exit code.
157      NOTREACHED();
158    }
159
160    return TERMINATION_STATUS_ABNORMAL_TERMINATION;
161  }
162
163  if (exit_code)
164    *exit_code = tmp_exit_code;
165
166  switch (tmp_exit_code) {
167    case kNormalTerminationExitCode:
168      return TERMINATION_STATUS_NORMAL_TERMINATION;
169    case kDebuggerInactiveExitCode:  // STATUS_DEBUGGER_INACTIVE.
170    case kKeyboardInterruptExitCode:  // Control-C/end session.
171    case kDebuggerTerminatedExitCode:  // Debugger terminated process.
172    case kProcessKilledExitCode:  // Task manager kill.
173      return TERMINATION_STATUS_PROCESS_WAS_KILLED;
174    default:
175      // All other exit codes indicate crashes.
176      return TERMINATION_STATUS_PROCESS_CRASHED;
177  }
178}
179
180bool WaitForExitCode(ProcessHandle handle, int* exit_code) {
181  bool success = WaitForExitCodeWithTimeout(
182      handle, exit_code, base::TimeDelta::FromMilliseconds(INFINITE));
183  CloseProcessHandle(handle);
184  return success;
185}
186
187bool WaitForExitCodeWithTimeout(ProcessHandle handle,
188                                int* exit_code,
189                                base::TimeDelta timeout) {
190  if (::WaitForSingleObject(handle, timeout.InMilliseconds()) != WAIT_OBJECT_0)
191    return false;
192  DWORD temp_code;  // Don't clobber out-parameters in case of failure.
193  if (!::GetExitCodeProcess(handle, &temp_code))
194    return false;
195
196  *exit_code = temp_code;
197  return true;
198}
199
200bool WaitForProcessesToExit(const FilePath::StringType& executable_name,
201                            base::TimeDelta wait,
202                            const ProcessFilter* filter) {
203  bool result = true;
204  DWORD start_time = GetTickCount();
205
206  NamedProcessIterator iter(executable_name, filter);
207  for (const ProcessEntry* entry = iter.NextProcessEntry(); entry;
208       entry = iter.NextProcessEntry()) {
209    DWORD remaining_wait = std::max<int64>(
210        0, wait.InMilliseconds() - (GetTickCount() - start_time));
211    HANDLE process = OpenProcess(SYNCHRONIZE,
212                                 FALSE,
213                                 entry->th32ProcessID);
214    DWORD wait_result = WaitForSingleObject(process, remaining_wait);
215    CloseHandle(process);
216    result &= (wait_result == WAIT_OBJECT_0);
217  }
218
219  return result;
220}
221
222bool WaitForSingleProcess(ProcessHandle handle, base::TimeDelta wait) {
223  int exit_code;
224  return WaitForExitCodeWithTimeout(handle, &exit_code, wait) && exit_code == 0;
225}
226
227bool CleanupProcesses(const FilePath::StringType& executable_name,
228                      base::TimeDelta wait,
229                      int exit_code,
230                      const ProcessFilter* filter) {
231  if (WaitForProcessesToExit(executable_name, wait, filter))
232    return true;
233  KillProcesses(executable_name, exit_code, filter);
234  return false;
235}
236
237void EnsureProcessTerminated(ProcessHandle process) {
238  DCHECK(process != GetCurrentProcess());
239
240  // If already signaled, then we are done!
241  if (WaitForSingleObject(process, 0) == WAIT_OBJECT_0) {
242    CloseHandle(process);
243    return;
244  }
245
246  MessageLoop::current()->PostDelayedTask(
247      FROM_HERE,
248      base::Bind(&TimerExpiredTask::TimedOut,
249                 base::Owned(new TimerExpiredTask(process))),
250      base::TimeDelta::FromMilliseconds(kWaitInterval));
251}
252
253}  // namespace base
254