Index: base/process.cc |
diff --git a/base/process.cc b/base/process.cc |
deleted file mode 100644 |
index 0ebfb07542f6aeaf5629e984f018d78150ed6f59..0000000000000000000000000000000000000000 |
--- a/base/process.cc |
+++ /dev/null |
@@ -1,1650 +0,0 @@ |
-// Copyright 2004-2009 Google Inc. |
-// |
-// Licensed under the Apache License, Version 2.0 (the "License"); |
-// you may not use this file except in compliance with the License. |
-// You may obtain a copy of the License at |
-// |
-// http://www.apache.org/licenses/LICENSE-2.0 |
-// |
-// Unless required by applicable law or agreed to in writing, software |
-// distributed under the License is distributed on an "AS IS" BASIS, |
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
-// See the License for the specific language governing permissions and |
-// limitations under the License. |
-// ======================================================================== |
-// |
-// Defines class Process to incapsulate win32 |
-// functions for creation and some manipulations of |
-// processes. |
- |
-#include "omaha/base/process.h" |
- |
-#include <ntsecapi.h> |
-#include <psapi.h> |
-#include <stierr.h> |
-#include <tlhelp32.h> |
-#include <vector> |
- |
-#ifndef NT_SUCCESS |
-#define NT_SUCCESS(Status) ((NTSTATUS)(Status) >= 0) |
-#endif |
- |
-#include "omaha/base/debug.h" |
-#include "omaha/base/disk.h" |
-#include "omaha/base/error.h" |
-#include "omaha/base/logging.h" |
-#include "omaha/base/scoped_any.h" |
-#include "omaha/base/string.h" |
-#include "omaha/base/system.h" |
-#include "omaha/base/system_info.h" |
-#include "omaha/base/utils.h" |
-#include "omaha/base/user_info.h" |
-#include "omaha/base/window_utils.h" |
- |
-namespace omaha { |
- |
-const int kNumRetriesToFindProcess = 4; |
-const int kFindProcessRetryIntervalMs = 500; |
-const int kMaxCmdLineLengthBytes = 4096; |
- |
-// Constructor |
-Process::Process(const TCHAR* name, |
- const TCHAR* window_class_name) |
- : process_id_(0), |
- exit_code_(0), |
- number_of_restarts_(static_cast<uint32>(-1)), |
- name_(name), |
- shutdown_event_(NULL) { |
- ASSERT1(name); |
- command_line_ = name; |
- window_class_name_ = window_class_name; |
-} |
- |
-// Constructor |
-Process::Process(uint32 process_id) |
- : process_id_(process_id), |
- exit_code_(0), |
- number_of_restarts_(static_cast<uint32>(-1)), |
- name_(itostr(static_cast<uint32>(process_id))), |
- shutdown_event_(NULL) { |
- reset(process_, ::OpenProcess(PROCESS_QUERY_INFORMATION | SYNCHRONIZE, |
- false, |
- process_id)); |
- if (!valid(process_)) { |
- UTIL_LOG(LEVEL_ERROR, |
- (_T("[Process::Process - failed to open process][%u][0x%x]"), |
- process_id, HRESULTFromLastError())); |
- } |
-} |
- |
-// Destructor |
-Process::~Process() { |
-} |
- |
-// Start with command params |
-HRESULT Process::Start(const TCHAR* command_line_parameters, |
- HANDLE runas_token) { |
- if (command_line_parameters && *command_line_parameters) { |
- command_line_parameters_ = command_line_parameters; |
- } |
- |
- number_of_restarts_ = static_cast<uint32>(-1); |
- time_of_start_ = GetTickCount(); |
- |
- return Restart(runas_token); |
-} |
- |
-// Restart with the old command params |
-HRESULT Process::Restart(HANDLE runas_token) { |
- // Can't start the same process twice in the same containing object. |
- if (Running()) { |
- return E_FAIL; |
- } |
- |
- PROCESS_INFORMATION process_info = {0}; |
- HRESULT hr = runas_token ? |
- System::StartProcessAsUser(runas_token, |
- command_line_, |
- command_line_parameters_, |
- _T("WinSta0\\Default"), |
- &process_info) : |
- System::StartProcessWithArgsAndInfo(command_line_, |
- command_line_parameters_, |
- &process_info); |
- |
- if (SUCCEEDED(hr)) { |
- VERIFY1(::CloseHandle(process_info.hThread)); |
- |
- reset(process_, process_info.hProcess); |
- process_id_ = process_info.dwProcessId; |
- |
- ASSERT1(process_id_); |
- number_of_restarts_++; |
- } else { |
- UTIL_LOG(LE, (_T("[Process Restart failed][%s][0x%x]"), command_line_, hr)); |
- } |
- |
- return hr; |
-} |
- |
-// Check if the process is running. |
-bool Process::Running() const { |
- if (!get(process_)) { |
- return false; |
- } |
- |
- return (::WaitForSingleObject(get(process_), 0) == WAIT_TIMEOUT); |
-} |
- |
-// Create a job and assign the process to it |
-HANDLE Process::AssignToJob() { |
- // Make sure that the process handle is valid |
- if (!get(process_)) { |
- return false; |
- } |
- |
- // Create a job |
- scoped_job job(::CreateJobObject(NULL, NULL)); |
- if (!valid(job)) { |
- UTIL_LOG(LEVEL_ERROR, |
- (_T("[Process::AssignToJob - CreateJobObject failed][0x%x]"), |
- HRESULTFromLastError())); |
- return false; |
- } |
- |
- // Assign the process to the job |
- if (!::AssignProcessToJobObject(get(job), get(process_))) { |
- UTIL_LOG(LEVEL_ERROR, |
- (_T("[Process::AssignToJob-AssignProcessToJobObject fail][0x%x]"), |
- HRESULTFromLastError())); |
- return false; |
- } |
- |
- return release(job); |
-} |
- |
-// Wait till the process finishes |
-bool Process::WaitUntilDead(uint32 timeout_msec) { |
- ASSERT1(timeout_msec); |
- |
- if (!get(process_)) { |
- return false; |
- } |
- |
- uint32 ret = 0; |
- if (shutdown_event_) { |
- HANDLE wait_handles[2] = {0}; |
- wait_handles[0] = get(process_); |
- wait_handles[1] = shutdown_event_; |
- ret = ::WaitForMultipleObjectsEx(2, |
- wait_handles, |
- false, |
- timeout_msec, |
- true); |
- } else { |
- ret = ::WaitForSingleObjectEx(get(process_), timeout_msec, true); |
- } |
- if (ret == WAIT_OBJECT_0) { |
- UTIL_LOG(L2, (_T("[Process::WaitUntilDead - succeeded to wait process]") |
- _T("[%s]"), GetName())); |
- return true; |
- } else if (ret == WAIT_IO_COMPLETION) { |
- UTIL_LOG(LEVEL_ERROR, (_T("[Process::WaitUntilDead-recv APC][%s][%u][%u]"), |
- GetName(), process_id_)); |
- return false; |
- } else { |
- UTIL_LOG(LEVEL_ERROR, (_T("[Process::WaitUntilDead - fail to wait process,") |
- _T("possibly timeout][%s][%u][%u]"), |
- GetName(), process_id_, ret)); |
- return false; |
- } |
-} |
- |
-// Wait some time till the process and all its descendent processes finish |
-// |
-// Background: |
-// Some process might spawn another process and get itself terminated |
-// without waiting the descendant process to finish. |
-// |
-// Args: |
-// job: Job to which the process is assigned |
-// AssignToJob() will be called when NULL value is passed |
-// timeout_msec: Timeout value in msec |
-// path_to_exclude: Path of descendant process to excluded from waiting |
-// (this should be in long format) |
-// exit_code: To hold the exit code being returned |
-bool Process::WaitUntilAllDead(HANDLE job, |
- uint32 timeout_msec, |
- const TCHAR* path_to_exclude, |
- uint32* exit_code) { |
- ASSERT1(timeout_msec); |
- |
- UTIL_LOG(L2, (_T("[Process::WaitUntilAllDead][%u][%s]"), |
- timeout_msec, path_to_exclude)); |
- |
- if (exit_code) { |
- *exit_code = 0; |
- } |
- |
- scoped_job job_guard; |
- if (!job) { |
- reset(job_guard, AssignToJob()); |
- if (!valid(job_guard)) { |
- return false; |
- } |
- job = get(job_guard); |
- } |
- |
- return InternalWaitUntilAllDead(job, |
- timeout_msec, |
- path_to_exclude, |
- exit_code); |
-} |
- |
-// Helper function to wait till the process and all its descendent processes |
-// finish. |
-bool Process::InternalWaitUntilAllDead(HANDLE job, |
- uint32 timeout_msec, |
- const TCHAR* path_to_exclude, |
- uint32* exit_code) { |
- ASSERT1(job); |
- ASSERT1(timeout_msec); |
- |
- // Wait until current process finishes |
- if (!WaitUntilDead(timeout_msec)) { |
- return false; |
- } |
- |
- // Find descendant process |
- uint32 desc_process_id = GetDescendantProcess( |
- job, |
- false, // child_only |
- exit_code != NULL, // sole_descendent |
- NULL, // search_name |
- path_to_exclude); |
- |
- if (desc_process_id) { |
- // Open descendent process |
- Process desc_process(desc_process_id); |
- |
- // If descendant process dies too soon, do not need to wait for it |
- if (desc_process.Running()) { |
- // Release the parent process handle |
- // This to handle the scenario that Firefox uninstall code will wait till |
- // parent process handle becomes NULL |
- reset(process_); |
- |
- UTIL_LOG(L2, (_T("[Process::InternalWaitUntilAllDead]") |
- _T("[waiting descendant process][%u]"), desc_process_id)); |
- |
- // Propagate the shutdown event to descendent process |
- if (shutdown_event_) { |
- desc_process.SetShutdownEvent(shutdown_event_); |
- } |
- |
- // Wait till descendant process finishes |
- bool wait_ret = desc_process.InternalWaitUntilAllDead(job, |
- timeout_msec, |
- path_to_exclude, |
- exit_code); |
- |
- return wait_ret; |
- } |
- } |
- |
- // Use the exit code from parent process |
- if (exit_code) { |
- VERIFY1(GetExitCode(exit_code)); |
- } |
- |
- // Release the parent process handle |
- reset(process_); |
- |
- return true; |
-} |
- |
-// Wait until process is dead or a windows message arrives (for use in a message |
-// loop while waiting) |
-HRESULT Process::WaitUntilDeadOrInterrupt(uint32 msec) { |
- if (!get(process_)) { |
- return E_FAIL; |
- } |
- |
- HANDLE events[1] = { get(process_) }; |
- uint32 dw = ::MsgWaitForMultipleObjects(1, events, FALSE, msec, QS_ALLEVENTS); |
- switch (dw) { |
- case WAIT_OBJECT_0: |
- return CI_S_PROCESSWAIT_DEAD; |
- case WAIT_OBJECT_0 + 1: |
- return CI_S_PROCESSWAIT_MESSAGE; |
- case WAIT_TIMEOUT: |
- return CI_S_PROCESSWAIT_TIMEOUT; |
- case WAIT_FAILED: |
- default: |
- return E_FAIL; |
- } |
-} |
- |
-#if !SHIPPING |
-CString Process::GetDebugInfo() const { |
- return debug_info_; |
-} |
-#endif |
- |
-// Return the process ID |
-uint32 Process::GetId() const { |
- return process_id_; |
-} |
- |
-// Return the process name |
-const TCHAR *Process::GetName() const { |
- return name_; |
-} |
- |
-// Return win32 handle to the process. |
-HANDLE Process::GetHandle() const { |
- return get(process_); |
-} |
- |
-// Get process exit code. |
-bool Process::GetExitCode(uint32* exit_code) const { |
- ASSERT1(exit_code); |
- |
- if (!get(process_)) { |
- return false; |
- } |
- |
- if (!::GetExitCodeProcess(get(process_), |
- reinterpret_cast<DWORD*>(&exit_code_))) { |
- UTIL_LOG(LEVEL_ERROR, |
- (_T("[Process::GetExitCode - failed to get exit code][%u][0x%x]"), |
- process_id_, HRESULTFromLastError())); |
- return false; |
- } |
- if (exit_code_ == STILL_ACTIVE) { |
- return false; |
- } |
- |
- *exit_code = exit_code_; |
- return true; |
-} |
- |
-// default implementation allows termination |
-bool Process::IsTerminationAllowed() const { |
- return true; |
-} |
- |
-// Terminate the process. If wait_for_terminate_msec == 0 return value doesn't |
-// mean that the process actualy terminated. It becomes assync. operation. |
-// Check the status with Running accessor function in this case. |
-bool Process::Terminate(uint32 wait_for_terminate_msec) { |
- if (!Running()) { |
- return true; |
- } |
- |
- if (!IsTerminationAllowed()) { |
- return false; |
- } |
- |
- if (!::TerminateProcess(get(process_), 1)) { |
- return false; |
- } |
- |
- return wait_for_terminate_msec ? WaitUntilDead(wait_for_terminate_msec) : |
- true; |
-} |
- |
-// Default returns INFINITE means never restart. |
-// Return any number of msec if overwriting |
-uint32 Process::GetRestartInterval() const { |
- return INFINITE; |
-} |
- |
-// How many times the process can be restarted |
-// in case it crashes. When overriding return any |
-// number or INFINITE to restart forever. |
-uint32 Process::GetMaxNumberOfRestarts() const { |
- return 0; |
-} |
- |
-// what is the time window for number of crashes returned by |
-// GetMaxNumberOfRestarts(). If crashed more that this number of restarts |
-// in a specified time window - do not restart it anymore. |
-// Default implementation returns INFINITE which means that this is not time |
-// based at all, if the process crashed more than the value returned by |
-// GetMaxNumberOfRestarts it will not be restarted no matter how long it took. |
-uint32 Process::GetTimeWindowForCrashes() const { |
- return INFINITE; |
-} |
- |
-uint32 Process::GetMaxMemory() const { |
- return 0; |
-} |
- |
-// Have we exceeded the number of maximum restarting? |
-bool Process::AllowedToRestart() const { |
- uint32 max_number_of_restarts = GetMaxNumberOfRestarts(); |
- |
- if ((max_number_of_restarts == INFINITE) || |
- (number_of_restarts_ < max_number_of_restarts)) { |
- return true; |
- } |
- |
- // process crashed too many times. Let's look at the rate of crashes. |
- // Maybe we can "forgive" the process if it took some time for it to crash. |
- if ((::GetTickCount() - time_of_start_) < GetTimeWindowForCrashes()) { |
- return false; // not forgiven |
- } |
- |
- // Everything is forgiven. Give the process |
- // new start in life. |
- time_of_start_ = ::GetTickCount(); |
- number_of_restarts_ = static_cast<uint32>(-1); |
- |
- return true; |
-} |
- |
-// Set shutdown event using in signaling the process watch |
-void Process::SetShutdownEvent(HANDLE shutdown_event) { |
- ASSERT1(shutdown_event); |
- |
- shutdown_event_ = shutdown_event; |
-} |
- |
-// Set priority class to the process. |
-bool Process::SetPriority(uint32 priority_class) const { |
- if (!get(process_)) { |
- return false; |
- } |
- |
- VERIFY1(::SetPriorityClass(get(process_), priority_class)); |
- return true; |
-} |
- |
-HRESULT Process::GetParentProcessId(uint32* parent_pid) { |
- ASSERT1(parent_pid); |
- *parent_pid = 0; |
- |
- scoped_hfile process_snap(::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)); |
- if (!process_snap) { |
- HRESULT hr = HRESULTFromLastError(); |
- UTIL_LOG(LE, (_T("[GetParentProcessId][Failed snapshot][0x%x]"), hr)); |
- return hr; |
- } |
- |
- // Eumerate all processes in the snapshot |
- PROCESSENTRY32 pe32; |
- SetZero(pe32); |
- pe32.dwSize = sizeof(PROCESSENTRY32); |
- if (!::Process32First(get(process_snap), &pe32)) { |
- HRESULT hr = HRESULTFromLastError(); |
- UTIL_LOG(LE, (_T("[Process32First failed][0x%x]"), hr)); |
- return hr; |
- } |
- |
- do { |
- if (pe32.th32ProcessID != process_id_) { |
- continue; |
- } |
- |
- if (pe32.th32ParentProcessID) { |
- *parent_pid = pe32.th32ParentProcessID; |
- return S_OK; |
- } |
- } while (::Process32Next(get(process_snap), &pe32)); |
- |
- return E_FAIL; |
-} |
- |
-// Try to get a descendant process. Return process id if found. |
-uint32 Process::GetDescendantProcess(HANDLE job, |
- bool child_only, |
- bool sole_descedent, |
- const TCHAR* search_name, |
- const TCHAR* path_to_exclude) { |
- ASSERT1(job); |
- |
- // Find all descendent processes |
- std::vector<ProcessInfo> descendant_processes; |
- if (FAILED(GetAllDescendantProcesses(job, |
- child_only, |
- search_name, |
- path_to_exclude, |
- &descendant_processes))) { |
- return 0; |
- } |
- |
- // If more than one decendent processes is found, filter out those that are |
- // not direct children. This is because it might be the case that in a very |
- // short period of time, process A spawns B and B spawns C, and we capture |
- // both B and C. |
- std::vector<ProcessInfo> child_processes; |
- typedef std::vector<ProcessInfo>::const_iterator ProcessInfoConstIterator; |
- if (descendant_processes.size() > 1) { |
- for (ProcessInfoConstIterator it(descendant_processes.begin()); |
- it != descendant_processes.end(); ++it) { |
- if (it->parent_id == process_id_) { |
- child_processes.push_back(*it); |
- } |
- } |
- if (!child_processes.empty()) { |
- descendant_processes = child_processes; |
- } |
- } |
- |
- // Save the debugging information if needed |
-#if !SHIPPING |
- if (sole_descedent && descendant_processes.size() > 1) { |
- debug_info_ = _T("More than one descendent process is found for process "); |
- debug_info_ += itostr(process_id_); |
- debug_info_ += _T("\n"); |
- for (ProcessInfoConstIterator it(descendant_processes.begin()); |
- it != descendant_processes.end(); ++it) { |
- debug_info_.AppendFormat(_T("%u %u %s\n"), |
- it->process_id, |
- it->parent_id, |
- it->exe_file); |
- } |
- } |
-#else |
- sole_descedent; // unreferenced formal parameter |
-#endif |
- |
- return descendant_processes.empty() ? 0 : descendant_processes[0].process_id; |
-} |
- |
-BOOL Process::IsProcessInJob(HANDLE process_handle, |
- HANDLE job_handle, |
- PBOOL result) { |
- typedef BOOL (WINAPI *Fun)(HANDLE process_handle, |
- HANDLE job_handle, |
- PBOOL result); |
- |
- HINSTANCE kernel_instance = ::GetModuleHandle(_T("kernel32.dll")); |
- ASSERT1(kernel_instance); |
- Fun pfn = reinterpret_cast<Fun>(::GetProcAddress(kernel_instance, |
- "IsProcessInJob")); |
- ASSERT(pfn, (_T("IsProcessInJob export not found in kernel32.dll"))); |
- return pfn ? (*pfn)(process_handle, job_handle, result) : FALSE; |
-} |
- |
-// Try to get all matching descendant processes |
-HRESULT Process::GetAllDescendantProcesses( |
- HANDLE job, |
- bool child_only, |
- const TCHAR* search_name, |
- const TCHAR* path_to_exclude, |
- std::vector<ProcessInfo>* descendant_processes) { |
- ASSERT1(job); |
- ASSERT1(descendant_processes); |
- |
- // Take a snapshot |
- // Note that we do not have a seperate scoped_* type defined to wrap the |
- // handle returned by CreateToolhelp32Snapshot. So scoped_hfile with similar |
- // behavior is used. |
- scoped_hfile process_snap(::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)); |
- if (!process_snap) { |
- HRESULT hr = HRESULTFromLastError(); |
- UTIL_LOG(LEVEL_ERROR, |
- (_T("[Process::GetAllDescendantProcesses - fail to get snapshot]") |
- _T("[0x%x]"), hr)); |
- return hr; |
- } |
- |
- // Eumerate all processes in the snapshot |
- PROCESSENTRY32 pe32; |
- SetZero(pe32); |
- pe32.dwSize = sizeof(PROCESSENTRY32); |
- if (!::Process32First(get(process_snap), &pe32)) { |
- HRESULT hr = HRESULTFromLastError(); |
- UTIL_LOG(LEVEL_ERROR, (_T("[Process::GetAllDescendantProcesses - failed to") |
- _T("get first process][0x%x]"), hr)); |
- return hr; |
- } |
- |
- do { |
- // Skip process 0 and current process |
- if (pe32.th32ProcessID == 0 || pe32.th32ProcessID == process_id_) { |
- continue; |
- } |
- |
- // If searching for child only, perform the check |
- if (child_only && pe32.th32ParentProcessID != process_id_) { |
- continue; |
- } |
- |
- // Open the process |
- scoped_process process(::OpenProcess(PROCESS_QUERY_INFORMATION | |
- SYNCHRONIZE, |
- false, |
- pe32.th32ProcessID)); |
- if (!valid(process)) { |
- continue; |
- } |
- |
- // Determines whether the process is running in the specified job |
- BOOL result = FALSE; |
- if (!IsProcessInJob(get(process), job, &result) || !result) { |
- continue; |
- } |
- |
- // Check whether the process is still running |
- if (::WaitForSingleObject(get(process), 0) != WAIT_TIMEOUT) { |
- continue; |
- } |
- |
- // Compare the name if needed |
- if (search_name && *search_name) { |
- if (_tcsicmp(pe32.szExeFile, search_name) != 0) { |
- continue; |
- } |
- } |
- |
- // If we need to exclude certain path, check it now |
- if (path_to_exclude && *path_to_exclude) { |
- if (IsProcessRunningWithPath(pe32.th32ProcessID, path_to_exclude)) { |
- continue; |
- } |
- } |
- |
- // Add to the list |
- ProcessInfo proc_info; |
- proc_info.process_id = pe32.th32ProcessID; |
- proc_info.parent_id = pe32.th32ParentProcessID; |
-#if !SHIPPING |
- proc_info.exe_file = pe32.szExeFile; |
-#endif |
- descendant_processes->push_back(proc_info); |
- } while (::Process32Next(get(process_snap), &pe32)); |
- |
- return S_OK; |
-} |
- |
-HRESULT Process::FindProcesses(uint32 exclude_mask, |
- const TCHAR* search_name, |
- bool search_main_executable_only, |
- std::vector<uint32>* process_ids_found) { |
- ASSERT1(process_ids_found); |
- // Remove the only include processes owned by user mask from the exclude |
- // mask. This is needed as this is the behavior expected by the method, |
- // before the addition of the user_sid. |
- exclude_mask &= (~INCLUDE_ONLY_PROCESS_OWNED_BY_USER); |
- std::vector<CString> command_lines; |
- return FindProcesses(exclude_mask, search_name, search_main_executable_only, |
- _T(""), command_lines, process_ids_found); |
-} |
- |
-bool Process::IsStringPresentInList(const CString& process_command_line, |
- const std::vector<CString>& list) { |
- std::vector<CString>::const_iterator iter = list.begin(); |
- for (; iter != list.end(); ++iter) { |
- CString value_to_find = *iter; |
- |
- // If we are able to open the process command line, then we should |
- // ensure that it does not contain the value that we are looking for. |
- if (process_command_line.Find(value_to_find) != -1) { |
- // Found a match. |
- return true; |
- } |
- } |
- |
- return false; |
-} |
- |
-// TODO(omaha): Change the implementation of this method to take in a |
-// predicate that determines whether a process should be included in the |
-// result set. |
-HRESULT Process::FindProcesses(uint32 exclude_mask, |
- const TCHAR* search_name, |
- bool search_main_executable_only, |
- const CString& user_sid, |
- const std::vector<CString>& command_lines, |
- std::vector<uint32>* process_ids_found) { |
- ASSERT1(search_name && *search_name); |
- ASSERT1(process_ids_found); |
- ASSERT1(!((exclude_mask & EXCLUDE_PROCESS_COMMAND_LINE_CONTAINING_STRING) && |
- (exclude_mask & INCLUDE_PROCESS_COMMAND_LINE_CONTAINING_STRING))); |
- |
- const TCHAR* const kLocalSystemSid = _T("S-1-5-18"); |
- |
- // Clear the output queue |
- process_ids_found->clear(); |
- |
- // Get the list of process identifiers. |
- uint32 process_ids[kMaxProcesses] = {0}; |
- uint32 bytes_returned = 0; |
- if (!::EnumProcesses(reinterpret_cast<DWORD*>(process_ids), |
- sizeof(process_ids), |
- reinterpret_cast<DWORD*>(&bytes_returned))) { |
- HRESULT hr = HRESULTFromLastError(); |
- UTIL_LOG(LEVEL_ERROR, (_T("[Process::FindProcesses-fail to EnumProcesses]") |
- _T("[0x%x]"), hr)); |
- return hr; |
- } |
- |
- // Enumerate all processes |
- int num_processes = bytes_returned / sizeof(process_ids[0]); |
- // We have found an elevated number of crashes in 1.2.584.15114 on what |
- // we believe are Italian systems. The first step to solving this Italian job |
- // is to assert on the condition while we are further testing this. |
- ASSERT1(num_processes <= kMaxProcesses); |
- |
- // In Vista, SeDebugPrivilege is required to open the process not owned by |
- // current user. Also required for XP admins to open Local System processes |
- // with PROCESS_QUERY_INFORMATION access rights. |
- System::AdjustPrivilege(SE_DEBUG_NAME, true); |
- |
- const uint32 cur_process_id = ::GetCurrentProcessId(); |
- |
- uint32 parent_process_id = 0; |
- if (exclude_mask & EXCLUDE_PARENT_PROCESS) { |
- Process current_process(cur_process_id); |
- uint32 ppid = 0; |
- HRESULT hr = current_process.GetParentProcessId(&ppid); |
- parent_process_id = SUCCEEDED(hr) ? ppid : 0; |
- } |
- |
- // Get SID of current user |
- CString cur_user_sid; |
- HRESULT hr = omaha::user_info::GetProcessUser(NULL, NULL, &cur_user_sid); |
- if (FAILED(hr)) { |
- return hr; |
- } |
- |
- UTIL_LOG(L4, (_T("[Process::FindProcesses][processes=%d]"), num_processes)); |
- for (int i = 0; i < num_processes; ++i) { |
- // Skip the system idle process. |
- if (process_ids[i] == 0) { |
- continue; |
- } |
- |
- // Skip the current process if needed. |
- if ((exclude_mask & EXCLUDE_CURRENT_PROCESS) && |
- (process_ids[i] == cur_process_id)) { |
- UTIL_LOG(L4, (_T("[Excluding current process %d"), process_ids[i])); |
- continue; |
- } |
- |
- // Skip the parent process if needed. |
- if ((exclude_mask & EXCLUDE_PARENT_PROCESS) && |
- (process_ids[i] == parent_process_id)) { |
- UTIL_LOG(L4, (_T("[Excluding parent process(%d) of %d"), |
- process_ids[i], cur_process_id)); |
- continue; |
- } |
- |
- |
- // Get the owner sid. |
- // Note that we may fail to get the owner which is not current user. |
- // So if the owner_sid is empty, the process is sure not to be owned by the |
- // current user. |
- CString owner_sid; |
- Process::GetProcessOwner(process_ids[i], &owner_sid); |
- |
- if ((exclude_mask & INCLUDE_ONLY_PROCESS_OWNED_BY_USER) && |
- owner_sid != user_sid) { |
- UTIL_LOG(L4, |
- (_T("[Excluding process as not owned by user][%d]"), process_ids[i])); |
- continue; |
- } |
- |
- if ((exclude_mask & EXCLUDE_PROCESS_OWNED_BY_CURRENT_USER) && |
- owner_sid == cur_user_sid) { |
- UTIL_LOG(L4, |
- (_T("[Excluding process as owned by current user][%d]"), |
- process_ids[i])); |
- continue; |
- } |
- if ((exclude_mask & EXCLUDE_PROCESS_OWNED_BY_SYSTEM) && |
- owner_sid == kLocalSystemSid) { |
- UTIL_LOG(L4, |
- (_T("[Excluding process as owned by system][%d]"), process_ids[i])); |
- continue; |
- } |
- if (exclude_mask & EXCLUDE_PROCESS_COMMAND_LINE_CONTAINING_STRING || |
- exclude_mask & INCLUDE_PROCESS_COMMAND_LINE_CONTAINING_STRING) { |
- CString process_command_line; |
- HRESULT hr = GetCommandLine(process_ids[i], &process_command_line); |
- if (FAILED(hr)) { |
- UTIL_LOG(L4, |
- (_T("[Excluding process could not get command line][%d]"), |
- process_ids[i])); |
- continue; |
- } |
- |
- // If we are able to open the process command line, then we should |
- // ensure that it does not contain the value that we are looking for if |
- // we are excluding the command line or that it contains the command line |
- // that we are looking for in case the include switch is specified. |
- bool present = IsStringPresentInList(process_command_line, command_lines); |
- if ((present && |
- (exclude_mask & EXCLUDE_PROCESS_COMMAND_LINE_CONTAINING_STRING)) || |
- (!present && |
- (exclude_mask & INCLUDE_PROCESS_COMMAND_LINE_CONTAINING_STRING))) { |
- UTIL_LOG(L4, (_T("[Process command line matches criteria][%d]'[%s]'"), |
- process_ids[i], process_command_line)); |
- continue; |
- } |
- } |
- |
- // If search_name is provided, make sure it matches |
- if (Process::IsProcessUsingExeOrDll(process_ids[i], |
- search_name, |
- search_main_executable_only)) { |
- UTIL_LOG(L4, |
- (_T("[Including process][%d][%s]"), process_ids[i], search_name)); |
- process_ids_found->push_back(process_ids[i]); |
- } |
- } |
- |
- return S_OK; |
-} |
- |
-HRESULT Process::FindProcessesInSession( |
- DWORD session_id, |
- uint32 exclude_mask, |
- const TCHAR* search_name, |
- bool search_main_executable_only, |
- const CString& user_sid, |
- const std::vector<CString>& cmd_lines, |
- std::vector<uint32>* process_ids_found) { |
- HRESULT hr = FindProcesses(exclude_mask, |
- search_name, |
- search_main_executable_only, |
- user_sid, |
- cmd_lines, |
- process_ids_found); |
- if (FAILED(hr)) { |
- return hr; |
- } |
- |
- // Filter to processes running under session_id. |
- std::vector<uint32>::iterator iter = process_ids_found->begin(); |
- while (iter != process_ids_found->end()) { |
- uint32 process_pid = *iter; |
- DWORD process_session = 0; |
- hr = S_OK; |
- if (!::ProcessIdToSessionId(process_pid, &process_session)) { |
- hr = HRESULTFromLastError(); |
- UTIL_LOG(LE, (_T("[::ProcessIdToSessionId failed][0x%x]"), hr)); |
- } else if (process_session != session_id) { |
- UTIL_LOG(L4, (_T("[Excluding process, different session][%d][%d][%d]"), |
- process_pid, process_session, session_id)); |
- } |
- |
- if (FAILED(hr) || process_session != session_id) { |
- // Remove from list and continue. |
- iter = process_ids_found->erase(iter); |
- continue; |
- } |
- |
- ++iter; |
- } |
- |
- return S_OK; |
-} |
- |
-bool Process::IsModuleMatchingExeOrDll(const TCHAR* module_name, |
- const TCHAR* search_name, |
- bool is_fully_qualified_name) { |
- CString module_file_name; |
- if (is_fully_qualified_name) { |
- if (FAILED(GetLongPathName(module_name, &module_file_name))) { |
- return false; |
- } |
- } else { |
- module_file_name = ::PathFindFileName(module_name); |
- ASSERT1(!module_file_name.IsEmpty()); |
- if (module_file_name.IsEmpty()) { |
- return false; |
- } |
- } |
- |
- return (module_file_name.CompareNoCase(search_name) == 0); |
-} |
- |
-DWORD Process::GetProcessImageFileName(HANDLE proc_handle, |
- LPTSTR image_file, |
- DWORD file_size) { |
- typedef DWORD (WINAPI *Fun)(HANDLE proc_handle, |
- LPWSTR image_file, |
- DWORD file_size); |
- |
- HINSTANCE psapi_instance = ::GetModuleHandle(_T("Psapi.dll")); |
- ASSERT1(psapi_instance); |
- Fun pfn = reinterpret_cast<Fun>(::GetProcAddress(psapi_instance, |
- "GetProcessImageFileNameW")); |
- if (!pfn) { |
- UTIL_LOG(L1, (_T("::GetProcessImageFileNameW() not found in Psapi.dll"))); |
- return 0; |
- } |
- return (*pfn)(proc_handle, image_file, file_size); |
-} |
- |
-bool Process::IsProcImageMatch(HANDLE proc_handle, |
- const TCHAR* search_name, |
- bool is_fully_qualified_name) { |
- TCHAR image_name[MAX_PATH] = _T(""); |
- if (!GetProcessImageFileName(proc_handle, |
- image_name, |
- arraysize(image_name))) { |
- UTIL_LOG(L4, (_T("[GetProcessImageFileName fail[0x%x]"), |
- HRESULTFromLastError())); |
- return false; |
- } |
- |
- UTIL_LOG(L4, (_T("[GetProcessImageFileName][%s]"), image_name)); |
- CString dos_name; |
- HRESULT hr(DevicePathToDosPath(image_name, &dos_name)); |
- if (FAILED(hr)) { |
- UTIL_LOG(L4, (_T("[DevicePathToDosPath fail[0x%x]"), hr)); |
- return false; |
- } |
- |
- return IsModuleMatchingExeOrDll(dos_name, |
- search_name, |
- is_fully_qualified_name); |
-} |
- |
-// Is the process using the specified exe/dll? |
-bool Process::IsProcessUsingExeOrDll(uint32 process_id, |
- const TCHAR* search_name, |
- bool search_main_executable_only) { |
- ASSERT1(search_name); |
- |
- // Open the process |
- scoped_process process_handle(::OpenProcess( |
- PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, |
- FALSE, |
- process_id)); |
- if (!process_handle) { |
- UTIL_LOG(L4, (_T("[::OpenProcess failed][0x%x]"), HRESULTFromLastError())); |
- return false; |
- } |
- |
- // Does the name represent a fully qualified name? |
- // We only do a simple check here |
- bool is_fully_qualified_name = String_FindChar(search_name, _T('\\')) != -1; |
- CString long_search_name; |
- if (is_fully_qualified_name) { |
- HRESULT hr(GetLongPathName(search_name, &long_search_name)); |
- if (FAILED(hr)) { |
- UTIL_LOG(L4, (_T("[GetLongPathName fail][hr=x%x]"), hr)); |
- return false; |
- } |
- search_name = long_search_name; |
- } |
- |
- // Take a snapshot of all modules in the specified process |
- int num_modules_to_fetch = search_main_executable_only ? 1 : |
- kMaxProcessModules; |
- HMODULE module_handles[kMaxProcessModules]; |
- SetZero(module_handles); |
- uint32 bytes_needed = 0; |
- if (!::EnumProcessModules(get(process_handle), |
- module_handles, |
- num_modules_to_fetch * sizeof(HMODULE), |
- reinterpret_cast<DWORD*>(&bytes_needed))) { |
- HRESULT hr = HRESULTFromLastError(); |
- UTIL_LOG(LEVEL_ERROR, (_T("[EnumProcessModules failed][0x%x]"), hr)); |
- |
- if (IsWow64(::GetCurrentProcessId())) { |
- // ::EnumProcessModules from a WoW64 process fails for x64 processes. |
- // We try ::GetProcessImageFileName as a workaround here. |
- return search_main_executable_only ? |
- IsProcImageMatch(get(process_handle), |
- search_name, |
- is_fully_qualified_name) : |
- false; |
- } else { |
- return false; |
- } |
- } |
- |
- int num_modules = bytes_needed / sizeof(HMODULE); |
- if (num_modules > num_modules_to_fetch) { |
- num_modules = num_modules_to_fetch; |
- } |
- |
- for (int i = 0; i < num_modules; ++i) { |
- TCHAR module_name[MAX_PATH]; |
- SetZero(module_name); |
- if (!::GetModuleFileNameEx(get(process_handle), |
- module_handles[i], |
- module_name, |
- arraysize(module_name))) { |
- UTIL_LOG(LEVEL_ERROR, (_T("[GetModuleFileNameEx fail[x%x]"), |
- HRESULTFromLastError())); |
- continue; |
- } |
- |
- if (IsModuleMatchingExeOrDll(module_name, |
- search_name, |
- is_fully_qualified_name)) { |
- return true; |
- } |
- } |
- |
- return false; |
-} |
- |
-// Helper function to get long path name |
-HRESULT Process::GetLongPathName(const TCHAR* short_name, CString* long_name) { |
- ASSERT1(short_name); |
- ASSERT1(long_name); |
- |
- TCHAR temp_name[MAX_PATH]; |
- SetZero(temp_name); |
- |
- HRESULT hr = S_OK; |
- if (!::GetLongPathName(short_name, temp_name, arraysize(temp_name))) { |
- hr = HRESULTFromLastError(); |
- } else { |
- long_name->SetString(temp_name); |
- } |
- |
- return hr; |
-} |
- |
-// Retrieve the FQPN for the executable file for a process. (Note: Using |
-// GetModuleFileNameEx is slower than GetProcessImageFileName, but the former |
-// is available on Win2K, while the latter is only only on XP and up.) |
-HRESULT Process::GetExecutablePath(uint32 process_id, CString *exe_path) { |
- ASSERT1(process_id); |
- ASSERT1(exe_path); |
- |
- TCHAR temp_path[MAX_PATH]; |
- SetZero(temp_path); |
- |
- scoped_process process(::OpenProcess(PROCESS_QUERY_INFORMATION | |
- PROCESS_VM_READ, |
- FALSE, |
- process_id)); |
- if (!valid(process)) { |
- return HRESULTFromLastError(); |
- } |
- |
- if (0 == ::GetModuleFileNameEx(get(process), NULL, temp_path, MAX_PATH)) { |
- return HRESULTFromLastError(); |
- } |
- |
- exe_path->SetString(temp_path); |
- return S_OK; |
-} |
- |
-// Type definitions needed for GetCommandLine() and GetProcessIdFromHandle() |
-// From MSDN document on NtQueryInformationProcess() and other sources |
-typedef struct _PROCESS_BASIC_INFORMATION { |
- PVOID Reserved1; |
- BYTE *PebBaseAddress; |
- PVOID Reserved2[2]; |
- ULONG_PTR UniqueProcessId; |
- PVOID Reserved3; |
-} PROCESS_BASIC_INFORMATION; |
- |
-typedef enum _PROCESSINFOCLASS { |
- ProcessBasicInformation = 0, |
- ProcessWow64Information = 26 |
-} PROCESSINFOCLASS; |
- |
-typedef WINBASEAPI DWORD WINAPI |
-GetProcessIdFn( |
- HANDLE Process |
-); |
- |
-typedef LONG WINAPI |
-NtQueryInformationProcess( |
- IN HANDLE ProcessHandle, |
- IN PROCESSINFOCLASS ProcessInformationClass, |
- OUT PVOID ProcessInformation, |
- IN ULONG ProcessInformationLength, |
- OUT PULONG ReturnLength OPTIONAL |
-); |
- |
-typedef struct _RTL_DRIVE_LETTER_CURDIR { |
- USHORT Flags; |
- USHORT Length; |
- ULONG TimeStamp; |
- UNICODE_STRING DosPath; |
-} RTL_DRIVE_LETTER_CURDIR, *PRTL_DRIVE_LETTER_CURDIR; |
- |
-typedef struct _RTL_USER_PROCESS_PARAMETERS { |
- ULONG MaximumLength; |
- ULONG Length; |
- ULONG Flags; |
- ULONG DebugFlags; |
- PVOID ConsoleHandle; |
- ULONG ConsoleFlags; |
- HANDLE StdInputHandle; |
- HANDLE StdOutputHandle; |
- HANDLE StdErrorHandle; |
- UNICODE_STRING CurrentDirectoryPath; |
- HANDLE CurrentDirectoryHandle; |
- UNICODE_STRING DllPath; |
- UNICODE_STRING ImagePathName; |
- UNICODE_STRING CommandLine; |
- PVOID Environment; |
- ULONG StartingPositionLeft; |
- ULONG StartingPositionTop; |
- ULONG Width; |
- ULONG Height; |
- ULONG CharWidth; |
- ULONG CharHeight; |
- ULONG ConsoleTextAttributes; |
- ULONG WindowFlags; |
- ULONG ShowWindowFlags; |
- UNICODE_STRING WindowTitle; |
- UNICODE_STRING DesktopName; |
- UNICODE_STRING ShellInfo; |
- UNICODE_STRING RuntimeData; |
- RTL_DRIVE_LETTER_CURDIR DLCurrentDirectory[0x20]; |
-} RTL_USER_PROCESS_PARAMETERS, *PRTL_USER_PROCESS_PARAMETERS; |
- |
-// Get the function pointer to GetProcessId in KERNEL32.DLL |
-static HRESULT EnsureGPIFunction(GetProcessIdFn** gpi_func_ptr) { |
- static GetProcessIdFn* gpi_func = NULL; |
- if (!gpi_func) { |
- HMODULE kernel32_module = ::GetModuleHandle(_T("kernel32.dll")); |
- if (!kernel32_module) { |
- return HRESULTFromLastError(); |
- } |
- gpi_func = reinterpret_cast<GetProcessIdFn*>( |
- ::GetProcAddress(kernel32_module, "GetProcessId")); |
- if (!gpi_func) { |
- return HRESULTFromLastError(); |
- } |
- } |
- |
- *gpi_func_ptr = gpi_func; |
- return S_OK; |
-} |
- |
-// Get the function pointer to NtQueryInformationProcess in NTDLL.DLL |
-static HRESULT EnsureQIPFunction(NtQueryInformationProcess** qip_func_ptr) { |
- static NtQueryInformationProcess* qip_func = NULL; |
- if (!qip_func) { |
- HMODULE ntdll_module = ::GetModuleHandle(_T("ntdll.dll")); |
- if (!ntdll_module) { |
- return HRESULTFromLastError(); |
- } |
- qip_func = reinterpret_cast<NtQueryInformationProcess*>( |
- ::GetProcAddress(ntdll_module, "NtQueryInformationProcess")); |
- if (!qip_func) { |
- return HRESULTFromLastError(); |
- } |
- } |
- |
- *qip_func_ptr = qip_func; |
- return S_OK; |
-} |
- |
-// Obtain the process ID from a hProcess HANDLE |
-ULONG Process::GetProcessIdFromHandle(HANDLE hProcess) { |
- if (SystemInfo::IsRunningOnXPSP1OrLater()) { |
- // Thunk to the documented ::GetProcessId() API |
- GetProcessIdFn* gpi_func = NULL; |
- HRESULT hr = EnsureGPIFunction(&gpi_func); |
- if (FAILED(hr)) { |
- ASSERT(FALSE, |
- (_T("Process::GetProcessIdFromHandle - EnsureGPIFunction") |
- _T(" failed[0x%x]"), hr)); |
- return 0; |
- } |
- ASSERT1(gpi_func); |
- return gpi_func(hProcess); |
- } |
- |
- // For lower versions of Windows, we use undocumented |
- // function NtQueryInformationProcess to get at the PID |
- NtQueryInformationProcess* qip_func = NULL; |
- HRESULT hr = EnsureQIPFunction(&qip_func); |
- if (FAILED(hr)) { |
- ASSERT(FALSE, |
- (_T("Process::GetProcessIdFromHandle - EnsureQIPFunction") |
- _T(" failed[0x%x]"), hr)); |
- return 0; |
- } |
- ASSERT1(qip_func); |
- |
- PROCESS_BASIC_INFORMATION info; |
- SetZero(info); |
- if (!NT_SUCCESS(qip_func(hProcess, |
- ProcessBasicInformation, |
- &info, |
- sizeof(info), |
- NULL))) { |
- ASSERT(FALSE, (_T("Process::GetProcessIdFromHandle - ") |
- _T("NtQueryInformationProcess failed!"))); |
- return 0; |
- } |
- |
- return info.UniqueProcessId; |
-} |
- |
-// Get the command line of a process |
-HRESULT Process::GetCommandLine(uint32 process_id, CString* cmd_line) { |
- ASSERT1(process_id); |
- ASSERT1(cmd_line); |
- |
- // Open the process |
- scoped_process process_handle(::OpenProcess( |
- PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, |
- false, |
- process_id)); |
- if (!process_handle) { |
- return HRESULTFromLastError(); |
- } |
- |
- // Obtain Process Environment Block |
- // Note that NtQueryInformationProcess is not available in Windows 95/98/ME |
- NtQueryInformationProcess* qip_func = NULL; |
- HRESULT hr = EnsureQIPFunction(&qip_func); |
- |
- if (FAILED(hr)) { |
- return hr; |
- } |
- ASSERT1(qip_func); |
- |
- PROCESS_BASIC_INFORMATION info; |
- SetZero(info); |
- if (!NT_SUCCESS(qip_func(get(process_handle), |
- ProcessBasicInformation, |
- &info, |
- sizeof(info), |
- NULL))) { |
- return E_FAIL; |
- } |
- BYTE* peb = info.PebBaseAddress; |
- |
- // Read address of parameters (see some PEB reference) |
- // TODO(omaha): use offsetof(PEB, ProcessParameters) to replace 0x10 |
- // http://msdn.microsoft.com/en-us/library/aa813706.aspx |
- SIZE_T bytes_read = 0; |
- uint32 dw = 0; |
- if (!::ReadProcessMemory(get(process_handle), |
- peb + 0x10, |
- &dw, |
- sizeof(dw), |
- &bytes_read)) { |
- return HRESULTFromLastError(); |
- } |
- |
- // Read all the parameters |
- RTL_USER_PROCESS_PARAMETERS params; |
- SetZero(params); |
- if (!::ReadProcessMemory(get(process_handle), |
- reinterpret_cast<PVOID>(dw), |
- ¶ms, |
- sizeof(params), |
- &bytes_read)) { |
- return HRESULTFromLastError(); |
- } |
- |
- // Read the command line parameter |
- const int max_cmd_line_len = std::min( |
- static_cast<int>(params.CommandLine.MaximumLength), |
- kMaxCmdLineLengthBytes); |
- if (!::ReadProcessMemory(get(process_handle), |
- params.CommandLine.Buffer, |
- cmd_line->GetBufferSetLength(max_cmd_line_len), |
- max_cmd_line_len, |
- &bytes_read)) { |
- return HRESULTFromLastError(); |
- } |
- |
- cmd_line->ReleaseBuffer(); |
- |
- return S_OK; |
-} |
- |
-// Check if the process is running with a specified path |
-bool Process::IsProcessRunningWithPath(uint32 process_id, const TCHAR* path) { |
- ASSERT1(process_id); |
- ASSERT1(path && *path); |
- |
- const int kProcessWaitModuleFullyUpMs = 100; |
- const int kProcessWaitModuleRetries = 10; |
- |
- // Open the process |
- scoped_process process(::OpenProcess( |
- PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, |
- false, |
- process_id)); |
- if (!process) { |
- UTIL_LOG(LEVEL_ERROR, |
- (_T("[Process::IsProcessRunningWithPath - OpenProcess failed]") |
- _T("[%u][0x%x]"), |
- process_id, HRESULTFromLastError())); |
- return false; |
- } |
- |
- for (int i = 0; i < kProcessWaitModuleRetries; ++i) { |
- // Get the command line path of the main module |
- // Note that we are using psapi functions which is not supported in Windows |
- // 95/98/ME |
- // |
- // Sometimes it might be the case that the process is created but the main |
- // module is not fully loaded. If so, wait a while and then try again |
- TCHAR process_path[MAX_PATH]; |
- if (::GetModuleFileNameEx(get(process), |
- NULL, |
- process_path, |
- arraysize(process_path))) { |
- // Do the check |
- if (String_StartsWith(process_path, path, true)) { |
- return true; |
- } |
- |
- // Try again with short form |
- TCHAR short_path[MAX_PATH]; |
- if (::GetShortPathName(path, short_path, arraysize(short_path)) && |
- String_StartsWith(process_path, short_path, true)) { |
- return true; |
- } |
- |
- return false; |
- } |
- |
- UTIL_LOG(LEVEL_ERROR, |
- (_T("[Process::IsProcessRunningWithPath - GetModuleFileNameEx ") |
- _T("failed][%u][0x%x]"), |
- process_id, HRESULTFromLastError())); |
- |
- ::Sleep(kProcessWaitModuleFullyUpMs); |
- } |
- |
- UTIL_LOG(LEVEL_ERROR, |
- (_T("[Process::IsProcessRunningWithPath - failed to get process ") |
- _T("path][%u][0x%x]"), |
- process_id, HRESULTFromLastError())); |
- |
- return false; |
-} |
- |
-// Get the process owner |
-// Note that we may fail to get the owner which is not current user. |
-HRESULT Process::GetProcessOwner(uint32 pid, CString* owner_sid) { |
- ASSERT1(pid); |
- ASSERT1(owner_sid); |
- |
- scoped_process process(::OpenProcess(PROCESS_QUERY_INFORMATION, false, pid)); |
- if (!valid(process)) { |
- return HRESULTFromLastError(); |
- } |
- |
- CAccessToken token; |
- CSid sid; |
- if (!token.GetProcessToken(READ_CONTROL | TOKEN_QUERY, get(process)) || |
- !token.GetUser(&sid)) { |
- return HRESULTFromLastError(); |
- } |
- |
- *owner_sid = sid.Sid(); |
- return S_OK; |
-} |
- |
-// Creates an impersonation token for the user running process_id. |
-// The caller is responsible for closing the returned handle. |
-HRESULT Process::GetImpersonationToken(DWORD process_id, HANDLE* user_token) { |
- // Get a handle to the process. |
- scoped_process process(::OpenProcess( |
- PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION, |
- TRUE, |
- process_id)); |
- if (!valid(process)) { |
- HRESULT hr(HRESULTFromLastError()); |
- UTIL_LOG(LEVEL_ERROR, |
- (_T("[GetImpersonationToken - ::OpenProcess failed][0x%x]"), |
- hr)); |
- return hr; |
- } |
- |
- HRESULT result = S_OK; |
- scoped_handle process_token; |
- if (!::OpenProcessToken(get(process), TOKEN_DUPLICATE | TOKEN_QUERY, |
- address(process_token))) { |
- result = HRESULTFromLastError(); |
- } else { |
- if (!::DuplicateTokenEx(get(process_token), |
- TOKEN_IMPERSONATE | TOKEN_QUERY | |
- TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE, |
- NULL, |
- SecurityImpersonation, |
- TokenPrimary, |
- user_token)) { |
- result = HRESULTFromLastError(); |
- } |
- } |
- |
- ASSERT(SUCCEEDED(result), (_T("[GetImpersonationToken Failed][hr=0x%x]"), |
- result)); |
- return result; |
-} |
- |
-HRESULT Process::GetUsersOfProcesses(const TCHAR* task_name, |
- int maximum_users, |
- scoped_handle user_tokens[], |
- int* number_of_users) { |
- ASSERT1(task_name && *task_name); |
- ASSERT1(maximum_users); |
- ASSERT1(user_tokens); |
- ASSERT1(number_of_users); |
- |
- scoped_hfile th32cs_snapshot(::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, |
- 0)); |
- if (!valid(th32cs_snapshot)) { |
- HRESULT hr(HRESULTFromLastError()); |
- UTIL_LOG(LEVEL_ERROR, (_T("[::CreateToolhelp32Snapshot fail][0x%x]"), hr)); |
- return hr; |
- } |
- |
- HRESULT result = S_OK; |
- *number_of_users = 0; |
- // Walk the list of processes. |
- PROCESSENTRY32 process = {0}; |
- process.dwSize = sizeof(PROCESSENTRY32); |
- for (BOOL found = ::Process32First(get(th32cs_snapshot), &process); found; |
- found = ::Process32Next(get(th32cs_snapshot), &process)) { |
- // Check if it is one of the processes we are looking for. |
- if (_tcsicmp(task_name, process.szExeFile) == 0) { |
- // We match. Get the user's token. |
- scoped_handle user_token; |
- if (FAILED(GetImpersonationToken(process.th32ProcessID, |
- address(user_token)))) |
- continue; |
- |
- // Search through the existing list to see if it's a duplicate. |
- // It's O(n^2) but we should have very few logged on users. |
- int i = 0; |
- for (; i < *number_of_users; i++) { |
- if (get(user_tokens[i]) == get(user_token)) { |
- // It's a duplicate. |
- break; |
- } |
- } |
- if (i >= *number_of_users) { |
- // It's a new one. Add it if there's room. |
- ASSERT1(i < maximum_users); |
- if (i < maximum_users) { |
- // Release the user_token, we don't want it to be closed |
- // by the user_token destructor |
- reset(user_tokens[(*number_of_users)++], release(user_token)); |
- } |
- } |
- } |
- } |
- return result; |
-} |
- |
-HRESULT Process::GetImagePath(const CString& process_name, |
- const CString& user_sid, |
- CString* path) { |
- ASSERT1(path); |
- |
- // Search for running processes with process_name. |
- uint32 mask = INCLUDE_ONLY_PROCESS_OWNED_BY_USER; |
- std::vector<CString> command_line; |
- std::vector<uint32> process_ids; |
- HRESULT hr = FindProcesses(mask, |
- process_name, |
- true, |
- user_sid, |
- command_line, |
- &process_ids); |
- if (FAILED(hr)) { |
- UTIL_LOG(LEVEL_WARNING, (_T("[FindProcesses failed][0x%08x]"), hr)); |
- return hr; |
- } |
- |
- if (process_ids.empty()) { |
- return E_FAIL; |
- } |
- |
- uint32 process_id = process_ids[0]; |
- UTIL_LOG(L4, (_T("[GetImagePath][pid=%d]"), process_id)); |
- scoped_process process_handle(::OpenProcess(PROCESS_QUERY_INFORMATION | |
- PROCESS_VM_READ, |
- FALSE, |
- process_id)); |
- if (!process_handle) { |
- HRESULT hr = HRESULTFromLastError(); |
- UTIL_LOG(L4, (_T("[OpenProcess failed][0x%08x]"), hr)); |
- return hr; |
- } |
- |
- HMODULE module_handle = NULL; |
- DWORD bytes_needed = 0; |
- if (!::EnumProcessModules(get(process_handle), |
- &module_handle, |
- sizeof(HMODULE), |
- &bytes_needed)) { |
- HRESULT hr = HRESULTFromLastError(); |
- UTIL_LOG(LEVEL_WARNING, (_T("[EnumProcessModules failed][0x%08x]"), hr)); |
- // ::EnumProcessModules from a WoW64 process fails for x64 processes. We try |
- // ::GetProcessImageFileName as a workaround here. |
- TCHAR image_name[MAX_PATH] = {0}; |
- if (!GetProcessImageFileName(get(process_handle), |
- image_name, |
- arraysize(image_name))) { |
- HRESULT hr = HRESULTFromLastError(); |
- UTIL_LOG(LE, (_T("[GetProcessImageFileName failed][0x%08x]"), hr)); |
- return hr; |
- } else { |
- *path = image_name; |
- return S_OK; |
- } |
- } |
- |
- TCHAR module_name[MAX_PATH] = {0}; |
- if (!::GetModuleFileNameEx(get(process_handle), |
- module_handle, |
- module_name, |
- arraysize(module_name))) { |
- HRESULT hr = HRESULTFromLastError(); |
- UTIL_LOG(LEVEL_ERROR, (_T("[GetModuleFileNameEx failed][0x%08x]"), hr)); |
- return hr; |
- } |
- |
- *path = module_name; |
- return S_OK; |
-} |
- |
-bool Process::IsWow64(uint32 pid) { |
- typedef BOOL (WINAPI *IsWow64Process)(HANDLE, BOOL*); |
- scoped_process handle(::OpenProcess(PROCESS_QUERY_INFORMATION | SYNCHRONIZE, |
- false, |
- pid)); |
- if (!handle) { |
- return false; |
- } |
- |
- HINSTANCE kernel_instance = ::GetModuleHandle(_T("kernel32.dll")); |
- if (kernel_instance == NULL) { |
- ASSERT1(false); |
- HRESULT hr = HRESULTFromLastError(); |
- UTIL_LOG(LW, (_T("[::GetModuleHandle kernel32.dll failed][0x%08x]"), hr)); |
- return false; |
- } |
- |
- IsWow64Process pfn = reinterpret_cast<IsWow64Process>(::GetProcAddress( |
- kernel_instance, |
- "IsWow64Process")); |
- if (!pfn) { |
- UTIL_LOG(LW, (_T("[::IsWow64Process() not found in kernel32.dll]"))); |
- return false; |
- } |
- |
- BOOL wow64 = FALSE; |
- if (!(*pfn)(get(handle), &wow64)) { |
- HRESULT hr = HRESULTFromLastError(); |
- UTIL_LOG(LW, (_T("[::IsWow64Process() failed][0x%08x]"), hr)); |
- return false; |
- } |
- |
- return (wow64 != 0); |
-} |
- |
-HRESULT Process::MakeProcessWindowForeground(const CString& executable) { |
- UTIL_LOG(L3, (_T("[MakeProcessWindowForeground]"))); |
- |
- CString sid; |
- HRESULT hr = omaha::user_info::GetProcessUser(NULL, NULL, &sid); |
- if (FAILED(hr)) { |
- return hr; |
- } |
- |
- // This code does not handle two cases: |
- // 1. If a new process instance is starting up but there are other process |
- // instances running, then we will not wait for the new process instance. |
- // One way to fix this is to pass the number of expected processes to this |
- // method. |
- // 2. If we find multiple processes, and we are able to find the windows only |
- // for some of the processes (maybe because the rest are still starting up) |
- // then we will only set the windows of the one that we found to the |
- // foreground and ignore the rest. |
- bool found = false; |
- for (int retries = 0; retries < kNumRetriesToFindProcess && !found; |
- ++retries) { |
- std::vector<CString> command_lines; |
- std::vector<uint32> processes; |
- DWORD flags = EXCLUDE_CURRENT_PROCESS | INCLUDE_ONLY_PROCESS_OWNED_BY_USER; |
- hr = Process::FindProcesses(flags, |
- executable, |
- true, |
- sid, |
- command_lines, |
- &processes); |
- if (FAILED(hr)) { |
- UTIL_LOG(LW, (_T("[FindProcesses failed][0x%08x]"), hr)); |
- return hr; |
- } |
- |
- UTIL_LOG(L3, (_T("[Found %d processes]"), processes.size())); |
- for (size_t i = 0; i < processes.size(); ++i) { |
- CSimpleArray<HWND> windows; |
- if (!WindowUtils::FindProcessWindows(processes[i], 0, &windows)) { |
- UTIL_LOG(L3, (_T("[FindProcessWindows failed][0x%08x]"), hr)); |
- continue; |
- } |
- |
- for (int j = 0; j < windows.GetSize(); ++j) { |
- if (WindowUtils::IsMainWindow(windows[j])) { |
- UTIL_LOG(L4, (_T("[Found main window of process %d]"), processes[i])); |
- WindowUtils::MakeWindowForeground(windows[j]); |
- ::FlashWindow(windows[j], true); |
- found = true; |
- break; |
- } |
- } |
- } |
- |
- if (!found) { |
- ::Sleep(kFindProcessRetryIntervalMs); |
- } |
- } |
- |
- return S_OK; |
-} |
- |
-} // namespace omaha |
- |