Home | History | Annotate | Download | only in process
      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 
     59 TimerExpiredTask::TimerExpiredTask(ProcessHandle process) : process_(process) {
     60   watcher_.StartWatching(process_, this);
     61 }
     62 
     63 TimerExpiredTask::~TimerExpiredTask() {
     64   TimedOut();
     65   DCHECK(!process_) << "Make sure to close the handle.";
     66 }
     67 
     68 void TimerExpiredTask::TimedOut() {
     69   if (process_)
     70     KillProcess();
     71 }
     72 
     73 void TimerExpiredTask::OnObjectSignaled(HANDLE object) {
     74   CloseHandle(process_);
     75   process_ = NULL;
     76 }
     77 
     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 
     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.
    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 
    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 
    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 
    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 
    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 
    222 bool WaitForSingleProcess(ProcessHandle handle, base::TimeDelta wait) {
    223   int exit_code;
    224   return WaitForExitCodeWithTimeout(handle, &exit_code, wait) && exit_code == 0;
    225 }
    226 
    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 
    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