• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 namespace base {
18 
19 namespace {
20 
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;
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.
33 const DWORD kProcessKilledExitCode = 1;
34 
35 // Maximum amount of time (in milliseconds) to wait for the process to exit.
36 static const int kWaitInterval = 2000;
37 
38 class 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 
TimerExpiredTask(ProcessHandle process)59 TimerExpiredTask::TimerExpiredTask(ProcessHandle process) : process_(process) {
60   watcher_.StartWatching(process_, this);
61 }
62 
~TimerExpiredTask()63 TimerExpiredTask::~TimerExpiredTask() {
64   TimedOut();
65   DCHECK(!process_) << "Make sure to close the handle.";
66 }
67 
TimedOut()68 void TimerExpiredTask::TimedOut() {
69   if (process_)
70     KillProcess();
71 }
72 
OnObjectSignaled(HANDLE object)73 void TimerExpiredTask::OnObjectSignaled(HANDLE object) {
74   CloseHandle(process_);
75   process_ = NULL;
76 }
77 
KillProcess()78 void 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 
KillProcess(ProcessHandle process,int exit_code,bool wait)94 bool 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.
KillProcessById(ProcessId process_id,int exit_code,bool wait)109 bool 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 
GetTerminationStatus(ProcessHandle handle,int * exit_code)122 TerminationStatus 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 
WaitForExitCode(ProcessHandle handle,int * exit_code)180 bool 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 
WaitForExitCodeWithTimeout(ProcessHandle handle,int * exit_code,base::TimeDelta timeout)187 bool 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 
WaitForProcessesToExit(const FilePath::StringType & executable_name,base::TimeDelta wait,const ProcessFilter * filter)200 bool 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 
WaitForSingleProcess(ProcessHandle handle,base::TimeDelta wait)222 bool WaitForSingleProcess(ProcessHandle handle, base::TimeDelta wait) {
223   int exit_code;
224   return WaitForExitCodeWithTimeout(handle, &exit_code, wait) && exit_code == 0;
225 }
226 
CleanupProcesses(const FilePath::StringType & executable_name,base::TimeDelta wait,int exit_code,const ProcessFilter * filter)227 bool 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 
EnsureProcessTerminated(ProcessHandle process)237 void 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