OLD | NEW |
| (Empty) |
1 // Copyright 2004-2009 Google Inc. | |
2 // | |
3 // Licensed under the Apache License, Version 2.0 (the "License"); | |
4 // you may not use this file except in compliance with the License. | |
5 // You may obtain a copy of the License at | |
6 // | |
7 // http://www.apache.org/licenses/LICENSE-2.0 | |
8 // | |
9 // Unless required by applicable law or agreed to in writing, software | |
10 // distributed under the License is distributed on an "AS IS" BASIS, | |
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
12 // See the License for the specific language governing permissions and | |
13 // limitations under the License. | |
14 // ======================================================================== | |
15 // | |
16 // Defines class Process to incapsulate win32 | |
17 // functions for creation and some manipulations of | |
18 // processes. | |
19 | |
20 #include "omaha/base/process.h" | |
21 | |
22 #include <ntsecapi.h> | |
23 #include <psapi.h> | |
24 #include <stierr.h> | |
25 #include <tlhelp32.h> | |
26 #include <vector> | |
27 | |
28 #ifndef NT_SUCCESS | |
29 #define NT_SUCCESS(Status) ((NTSTATUS)(Status) >= 0) | |
30 #endif | |
31 | |
32 #include "omaha/base/debug.h" | |
33 #include "omaha/base/disk.h" | |
34 #include "omaha/base/error.h" | |
35 #include "omaha/base/logging.h" | |
36 #include "omaha/base/scoped_any.h" | |
37 #include "omaha/base/string.h" | |
38 #include "omaha/base/system.h" | |
39 #include "omaha/base/system_info.h" | |
40 #include "omaha/base/utils.h" | |
41 #include "omaha/base/user_info.h" | |
42 #include "omaha/base/window_utils.h" | |
43 | |
44 namespace omaha { | |
45 | |
46 const int kNumRetriesToFindProcess = 4; | |
47 const int kFindProcessRetryIntervalMs = 500; | |
48 const int kMaxCmdLineLengthBytes = 4096; | |
49 | |
50 // Constructor | |
51 Process::Process(const TCHAR* name, | |
52 const TCHAR* window_class_name) | |
53 : process_id_(0), | |
54 exit_code_(0), | |
55 number_of_restarts_(static_cast<uint32>(-1)), | |
56 name_(name), | |
57 shutdown_event_(NULL) { | |
58 ASSERT1(name); | |
59 command_line_ = name; | |
60 window_class_name_ = window_class_name; | |
61 } | |
62 | |
63 // Constructor | |
64 Process::Process(uint32 process_id) | |
65 : process_id_(process_id), | |
66 exit_code_(0), | |
67 number_of_restarts_(static_cast<uint32>(-1)), | |
68 name_(itostr(static_cast<uint32>(process_id))), | |
69 shutdown_event_(NULL) { | |
70 reset(process_, ::OpenProcess(PROCESS_QUERY_INFORMATION | SYNCHRONIZE, | |
71 false, | |
72 process_id)); | |
73 if (!valid(process_)) { | |
74 UTIL_LOG(LEVEL_ERROR, | |
75 (_T("[Process::Process - failed to open process][%u][0x%x]"), | |
76 process_id, HRESULTFromLastError())); | |
77 } | |
78 } | |
79 | |
80 // Destructor | |
81 Process::~Process() { | |
82 } | |
83 | |
84 // Start with command params | |
85 HRESULT Process::Start(const TCHAR* command_line_parameters, | |
86 HANDLE runas_token) { | |
87 if (command_line_parameters && *command_line_parameters) { | |
88 command_line_parameters_ = command_line_parameters; | |
89 } | |
90 | |
91 number_of_restarts_ = static_cast<uint32>(-1); | |
92 time_of_start_ = GetTickCount(); | |
93 | |
94 return Restart(runas_token); | |
95 } | |
96 | |
97 // Restart with the old command params | |
98 HRESULT Process::Restart(HANDLE runas_token) { | |
99 // Can't start the same process twice in the same containing object. | |
100 if (Running()) { | |
101 return E_FAIL; | |
102 } | |
103 | |
104 PROCESS_INFORMATION process_info = {0}; | |
105 HRESULT hr = runas_token ? | |
106 System::StartProcessAsUser(runas_token, | |
107 command_line_, | |
108 command_line_parameters_, | |
109 _T("WinSta0\\Default"), | |
110 &process_info) : | |
111 System::StartProcessWithArgsAndInfo(command_line_, | |
112 command_line_parameters_, | |
113 &process_info); | |
114 | |
115 if (SUCCEEDED(hr)) { | |
116 VERIFY1(::CloseHandle(process_info.hThread)); | |
117 | |
118 reset(process_, process_info.hProcess); | |
119 process_id_ = process_info.dwProcessId; | |
120 | |
121 ASSERT1(process_id_); | |
122 number_of_restarts_++; | |
123 } else { | |
124 UTIL_LOG(LE, (_T("[Process Restart failed][%s][0x%x]"), command_line_, hr)); | |
125 } | |
126 | |
127 return hr; | |
128 } | |
129 | |
130 // Check if the process is running. | |
131 bool Process::Running() const { | |
132 if (!get(process_)) { | |
133 return false; | |
134 } | |
135 | |
136 return (::WaitForSingleObject(get(process_), 0) == WAIT_TIMEOUT); | |
137 } | |
138 | |
139 // Create a job and assign the process to it | |
140 HANDLE Process::AssignToJob() { | |
141 // Make sure that the process handle is valid | |
142 if (!get(process_)) { | |
143 return false; | |
144 } | |
145 | |
146 // Create a job | |
147 scoped_job job(::CreateJobObject(NULL, NULL)); | |
148 if (!valid(job)) { | |
149 UTIL_LOG(LEVEL_ERROR, | |
150 (_T("[Process::AssignToJob - CreateJobObject failed][0x%x]"), | |
151 HRESULTFromLastError())); | |
152 return false; | |
153 } | |
154 | |
155 // Assign the process to the job | |
156 if (!::AssignProcessToJobObject(get(job), get(process_))) { | |
157 UTIL_LOG(LEVEL_ERROR, | |
158 (_T("[Process::AssignToJob-AssignProcessToJobObject fail][0x%x]"), | |
159 HRESULTFromLastError())); | |
160 return false; | |
161 } | |
162 | |
163 return release(job); | |
164 } | |
165 | |
166 // Wait till the process finishes | |
167 bool Process::WaitUntilDead(uint32 timeout_msec) { | |
168 ASSERT1(timeout_msec); | |
169 | |
170 if (!get(process_)) { | |
171 return false; | |
172 } | |
173 | |
174 uint32 ret = 0; | |
175 if (shutdown_event_) { | |
176 HANDLE wait_handles[2] = {0}; | |
177 wait_handles[0] = get(process_); | |
178 wait_handles[1] = shutdown_event_; | |
179 ret = ::WaitForMultipleObjectsEx(2, | |
180 wait_handles, | |
181 false, | |
182 timeout_msec, | |
183 true); | |
184 } else { | |
185 ret = ::WaitForSingleObjectEx(get(process_), timeout_msec, true); | |
186 } | |
187 if (ret == WAIT_OBJECT_0) { | |
188 UTIL_LOG(L2, (_T("[Process::WaitUntilDead - succeeded to wait process]") | |
189 _T("[%s]"), GetName())); | |
190 return true; | |
191 } else if (ret == WAIT_IO_COMPLETION) { | |
192 UTIL_LOG(LEVEL_ERROR, (_T("[Process::WaitUntilDead-recv APC][%s][%u][%u]"), | |
193 GetName(), process_id_)); | |
194 return false; | |
195 } else { | |
196 UTIL_LOG(LEVEL_ERROR, (_T("[Process::WaitUntilDead - fail to wait process,") | |
197 _T("possibly timeout][%s][%u][%u]"), | |
198 GetName(), process_id_, ret)); | |
199 return false; | |
200 } | |
201 } | |
202 | |
203 // Wait some time till the process and all its descendent processes finish | |
204 // | |
205 // Background: | |
206 // Some process might spawn another process and get itself terminated | |
207 // without waiting the descendant process to finish. | |
208 // | |
209 // Args: | |
210 // job: Job to which the process is assigned | |
211 // AssignToJob() will be called when NULL value is passed | |
212 // timeout_msec: Timeout value in msec | |
213 // path_to_exclude: Path of descendant process to excluded from waiting | |
214 // (this should be in long format) | |
215 // exit_code: To hold the exit code being returned | |
216 bool Process::WaitUntilAllDead(HANDLE job, | |
217 uint32 timeout_msec, | |
218 const TCHAR* path_to_exclude, | |
219 uint32* exit_code) { | |
220 ASSERT1(timeout_msec); | |
221 | |
222 UTIL_LOG(L2, (_T("[Process::WaitUntilAllDead][%u][%s]"), | |
223 timeout_msec, path_to_exclude)); | |
224 | |
225 if (exit_code) { | |
226 *exit_code = 0; | |
227 } | |
228 | |
229 scoped_job job_guard; | |
230 if (!job) { | |
231 reset(job_guard, AssignToJob()); | |
232 if (!valid(job_guard)) { | |
233 return false; | |
234 } | |
235 job = get(job_guard); | |
236 } | |
237 | |
238 return InternalWaitUntilAllDead(job, | |
239 timeout_msec, | |
240 path_to_exclude, | |
241 exit_code); | |
242 } | |
243 | |
244 // Helper function to wait till the process and all its descendent processes | |
245 // finish. | |
246 bool Process::InternalWaitUntilAllDead(HANDLE job, | |
247 uint32 timeout_msec, | |
248 const TCHAR* path_to_exclude, | |
249 uint32* exit_code) { | |
250 ASSERT1(job); | |
251 ASSERT1(timeout_msec); | |
252 | |
253 // Wait until current process finishes | |
254 if (!WaitUntilDead(timeout_msec)) { | |
255 return false; | |
256 } | |
257 | |
258 // Find descendant process | |
259 uint32 desc_process_id = GetDescendantProcess( | |
260 job, | |
261 false, // child_only | |
262 exit_code != NULL, // sole_descendent | |
263 NULL, // search_name | |
264 path_to_exclude); | |
265 | |
266 if (desc_process_id) { | |
267 // Open descendent process | |
268 Process desc_process(desc_process_id); | |
269 | |
270 // If descendant process dies too soon, do not need to wait for it | |
271 if (desc_process.Running()) { | |
272 // Release the parent process handle | |
273 // This to handle the scenario that Firefox uninstall code will wait till | |
274 // parent process handle becomes NULL | |
275 reset(process_); | |
276 | |
277 UTIL_LOG(L2, (_T("[Process::InternalWaitUntilAllDead]") | |
278 _T("[waiting descendant process][%u]"), desc_process_id)); | |
279 | |
280 // Propagate the shutdown event to descendent process | |
281 if (shutdown_event_) { | |
282 desc_process.SetShutdownEvent(shutdown_event_); | |
283 } | |
284 | |
285 // Wait till descendant process finishes | |
286 bool wait_ret = desc_process.InternalWaitUntilAllDead(job, | |
287 timeout_msec, | |
288 path_to_exclude, | |
289 exit_code); | |
290 | |
291 return wait_ret; | |
292 } | |
293 } | |
294 | |
295 // Use the exit code from parent process | |
296 if (exit_code) { | |
297 VERIFY1(GetExitCode(exit_code)); | |
298 } | |
299 | |
300 // Release the parent process handle | |
301 reset(process_); | |
302 | |
303 return true; | |
304 } | |
305 | |
306 // Wait until process is dead or a windows message arrives (for use in a message | |
307 // loop while waiting) | |
308 HRESULT Process::WaitUntilDeadOrInterrupt(uint32 msec) { | |
309 if (!get(process_)) { | |
310 return E_FAIL; | |
311 } | |
312 | |
313 HANDLE events[1] = { get(process_) }; | |
314 uint32 dw = ::MsgWaitForMultipleObjects(1, events, FALSE, msec, QS_ALLEVENTS); | |
315 switch (dw) { | |
316 case WAIT_OBJECT_0: | |
317 return CI_S_PROCESSWAIT_DEAD; | |
318 case WAIT_OBJECT_0 + 1: | |
319 return CI_S_PROCESSWAIT_MESSAGE; | |
320 case WAIT_TIMEOUT: | |
321 return CI_S_PROCESSWAIT_TIMEOUT; | |
322 case WAIT_FAILED: | |
323 default: | |
324 return E_FAIL; | |
325 } | |
326 } | |
327 | |
328 #if !SHIPPING | |
329 CString Process::GetDebugInfo() const { | |
330 return debug_info_; | |
331 } | |
332 #endif | |
333 | |
334 // Return the process ID | |
335 uint32 Process::GetId() const { | |
336 return process_id_; | |
337 } | |
338 | |
339 // Return the process name | |
340 const TCHAR *Process::GetName() const { | |
341 return name_; | |
342 } | |
343 | |
344 // Return win32 handle to the process. | |
345 HANDLE Process::GetHandle() const { | |
346 return get(process_); | |
347 } | |
348 | |
349 // Get process exit code. | |
350 bool Process::GetExitCode(uint32* exit_code) const { | |
351 ASSERT1(exit_code); | |
352 | |
353 if (!get(process_)) { | |
354 return false; | |
355 } | |
356 | |
357 if (!::GetExitCodeProcess(get(process_), | |
358 reinterpret_cast<DWORD*>(&exit_code_))) { | |
359 UTIL_LOG(LEVEL_ERROR, | |
360 (_T("[Process::GetExitCode - failed to get exit code][%u][0x%x]"), | |
361 process_id_, HRESULTFromLastError())); | |
362 return false; | |
363 } | |
364 if (exit_code_ == STILL_ACTIVE) { | |
365 return false; | |
366 } | |
367 | |
368 *exit_code = exit_code_; | |
369 return true; | |
370 } | |
371 | |
372 // default implementation allows termination | |
373 bool Process::IsTerminationAllowed() const { | |
374 return true; | |
375 } | |
376 | |
377 // Terminate the process. If wait_for_terminate_msec == 0 return value doesn't | |
378 // mean that the process actualy terminated. It becomes assync. operation. | |
379 // Check the status with Running accessor function in this case. | |
380 bool Process::Terminate(uint32 wait_for_terminate_msec) { | |
381 if (!Running()) { | |
382 return true; | |
383 } | |
384 | |
385 if (!IsTerminationAllowed()) { | |
386 return false; | |
387 } | |
388 | |
389 if (!::TerminateProcess(get(process_), 1)) { | |
390 return false; | |
391 } | |
392 | |
393 return wait_for_terminate_msec ? WaitUntilDead(wait_for_terminate_msec) : | |
394 true; | |
395 } | |
396 | |
397 // Default returns INFINITE means never restart. | |
398 // Return any number of msec if overwriting | |
399 uint32 Process::GetRestartInterval() const { | |
400 return INFINITE; | |
401 } | |
402 | |
403 // How many times the process can be restarted | |
404 // in case it crashes. When overriding return any | |
405 // number or INFINITE to restart forever. | |
406 uint32 Process::GetMaxNumberOfRestarts() const { | |
407 return 0; | |
408 } | |
409 | |
410 // what is the time window for number of crashes returned by | |
411 // GetMaxNumberOfRestarts(). If crashed more that this number of restarts | |
412 // in a specified time window - do not restart it anymore. | |
413 // Default implementation returns INFINITE which means that this is not time | |
414 // based at all, if the process crashed more than the value returned by | |
415 // GetMaxNumberOfRestarts it will not be restarted no matter how long it took. | |
416 uint32 Process::GetTimeWindowForCrashes() const { | |
417 return INFINITE; | |
418 } | |
419 | |
420 uint32 Process::GetMaxMemory() const { | |
421 return 0; | |
422 } | |
423 | |
424 // Have we exceeded the number of maximum restarting? | |
425 bool Process::AllowedToRestart() const { | |
426 uint32 max_number_of_restarts = GetMaxNumberOfRestarts(); | |
427 | |
428 if ((max_number_of_restarts == INFINITE) || | |
429 (number_of_restarts_ < max_number_of_restarts)) { | |
430 return true; | |
431 } | |
432 | |
433 // process crashed too many times. Let's look at the rate of crashes. | |
434 // Maybe we can "forgive" the process if it took some time for it to crash. | |
435 if ((::GetTickCount() - time_of_start_) < GetTimeWindowForCrashes()) { | |
436 return false; // not forgiven | |
437 } | |
438 | |
439 // Everything is forgiven. Give the process | |
440 // new start in life. | |
441 time_of_start_ = ::GetTickCount(); | |
442 number_of_restarts_ = static_cast<uint32>(-1); | |
443 | |
444 return true; | |
445 } | |
446 | |
447 // Set shutdown event using in signaling the process watch | |
448 void Process::SetShutdownEvent(HANDLE shutdown_event) { | |
449 ASSERT1(shutdown_event); | |
450 | |
451 shutdown_event_ = shutdown_event; | |
452 } | |
453 | |
454 // Set priority class to the process. | |
455 bool Process::SetPriority(uint32 priority_class) const { | |
456 if (!get(process_)) { | |
457 return false; | |
458 } | |
459 | |
460 VERIFY1(::SetPriorityClass(get(process_), priority_class)); | |
461 return true; | |
462 } | |
463 | |
464 HRESULT Process::GetParentProcessId(uint32* parent_pid) { | |
465 ASSERT1(parent_pid); | |
466 *parent_pid = 0; | |
467 | |
468 scoped_hfile process_snap(::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)); | |
469 if (!process_snap) { | |
470 HRESULT hr = HRESULTFromLastError(); | |
471 UTIL_LOG(LE, (_T("[GetParentProcessId][Failed snapshot][0x%x]"), hr)); | |
472 return hr; | |
473 } | |
474 | |
475 // Eumerate all processes in the snapshot | |
476 PROCESSENTRY32 pe32; | |
477 SetZero(pe32); | |
478 pe32.dwSize = sizeof(PROCESSENTRY32); | |
479 if (!::Process32First(get(process_snap), &pe32)) { | |
480 HRESULT hr = HRESULTFromLastError(); | |
481 UTIL_LOG(LE, (_T("[Process32First failed][0x%x]"), hr)); | |
482 return hr; | |
483 } | |
484 | |
485 do { | |
486 if (pe32.th32ProcessID != process_id_) { | |
487 continue; | |
488 } | |
489 | |
490 if (pe32.th32ParentProcessID) { | |
491 *parent_pid = pe32.th32ParentProcessID; | |
492 return S_OK; | |
493 } | |
494 } while (::Process32Next(get(process_snap), &pe32)); | |
495 | |
496 return E_FAIL; | |
497 } | |
498 | |
499 // Try to get a descendant process. Return process id if found. | |
500 uint32 Process::GetDescendantProcess(HANDLE job, | |
501 bool child_only, | |
502 bool sole_descedent, | |
503 const TCHAR* search_name, | |
504 const TCHAR* path_to_exclude) { | |
505 ASSERT1(job); | |
506 | |
507 // Find all descendent processes | |
508 std::vector<ProcessInfo> descendant_processes; | |
509 if (FAILED(GetAllDescendantProcesses(job, | |
510 child_only, | |
511 search_name, | |
512 path_to_exclude, | |
513 &descendant_processes))) { | |
514 return 0; | |
515 } | |
516 | |
517 // If more than one decendent processes is found, filter out those that are | |
518 // not direct children. This is because it might be the case that in a very | |
519 // short period of time, process A spawns B and B spawns C, and we capture | |
520 // both B and C. | |
521 std::vector<ProcessInfo> child_processes; | |
522 typedef std::vector<ProcessInfo>::const_iterator ProcessInfoConstIterator; | |
523 if (descendant_processes.size() > 1) { | |
524 for (ProcessInfoConstIterator it(descendant_processes.begin()); | |
525 it != descendant_processes.end(); ++it) { | |
526 if (it->parent_id == process_id_) { | |
527 child_processes.push_back(*it); | |
528 } | |
529 } | |
530 if (!child_processes.empty()) { | |
531 descendant_processes = child_processes; | |
532 } | |
533 } | |
534 | |
535 // Save the debugging information if needed | |
536 #if !SHIPPING | |
537 if (sole_descedent && descendant_processes.size() > 1) { | |
538 debug_info_ = _T("More than one descendent process is found for process "); | |
539 debug_info_ += itostr(process_id_); | |
540 debug_info_ += _T("\n"); | |
541 for (ProcessInfoConstIterator it(descendant_processes.begin()); | |
542 it != descendant_processes.end(); ++it) { | |
543 debug_info_.AppendFormat(_T("%u %u %s\n"), | |
544 it->process_id, | |
545 it->parent_id, | |
546 it->exe_file); | |
547 } | |
548 } | |
549 #else | |
550 sole_descedent; // unreferenced formal parameter | |
551 #endif | |
552 | |
553 return descendant_processes.empty() ? 0 : descendant_processes[0].process_id; | |
554 } | |
555 | |
556 BOOL Process::IsProcessInJob(HANDLE process_handle, | |
557 HANDLE job_handle, | |
558 PBOOL result) { | |
559 typedef BOOL (WINAPI *Fun)(HANDLE process_handle, | |
560 HANDLE job_handle, | |
561 PBOOL result); | |
562 | |
563 HINSTANCE kernel_instance = ::GetModuleHandle(_T("kernel32.dll")); | |
564 ASSERT1(kernel_instance); | |
565 Fun pfn = reinterpret_cast<Fun>(::GetProcAddress(kernel_instance, | |
566 "IsProcessInJob")); | |
567 ASSERT(pfn, (_T("IsProcessInJob export not found in kernel32.dll"))); | |
568 return pfn ? (*pfn)(process_handle, job_handle, result) : FALSE; | |
569 } | |
570 | |
571 // Try to get all matching descendant processes | |
572 HRESULT Process::GetAllDescendantProcesses( | |
573 HANDLE job, | |
574 bool child_only, | |
575 const TCHAR* search_name, | |
576 const TCHAR* path_to_exclude, | |
577 std::vector<ProcessInfo>* descendant_processes) { | |
578 ASSERT1(job); | |
579 ASSERT1(descendant_processes); | |
580 | |
581 // Take a snapshot | |
582 // Note that we do not have a seperate scoped_* type defined to wrap the | |
583 // handle returned by CreateToolhelp32Snapshot. So scoped_hfile with similar | |
584 // behavior is used. | |
585 scoped_hfile process_snap(::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)); | |
586 if (!process_snap) { | |
587 HRESULT hr = HRESULTFromLastError(); | |
588 UTIL_LOG(LEVEL_ERROR, | |
589 (_T("[Process::GetAllDescendantProcesses - fail to get snapshot]") | |
590 _T("[0x%x]"), hr)); | |
591 return hr; | |
592 } | |
593 | |
594 // Eumerate all processes in the snapshot | |
595 PROCESSENTRY32 pe32; | |
596 SetZero(pe32); | |
597 pe32.dwSize = sizeof(PROCESSENTRY32); | |
598 if (!::Process32First(get(process_snap), &pe32)) { | |
599 HRESULT hr = HRESULTFromLastError(); | |
600 UTIL_LOG(LEVEL_ERROR, (_T("[Process::GetAllDescendantProcesses - failed to") | |
601 _T("get first process][0x%x]"), hr)); | |
602 return hr; | |
603 } | |
604 | |
605 do { | |
606 // Skip process 0 and current process | |
607 if (pe32.th32ProcessID == 0 || pe32.th32ProcessID == process_id_) { | |
608 continue; | |
609 } | |
610 | |
611 // If searching for child only, perform the check | |
612 if (child_only && pe32.th32ParentProcessID != process_id_) { | |
613 continue; | |
614 } | |
615 | |
616 // Open the process | |
617 scoped_process process(::OpenProcess(PROCESS_QUERY_INFORMATION | | |
618 SYNCHRONIZE, | |
619 false, | |
620 pe32.th32ProcessID)); | |
621 if (!valid(process)) { | |
622 continue; | |
623 } | |
624 | |
625 // Determines whether the process is running in the specified job | |
626 BOOL result = FALSE; | |
627 if (!IsProcessInJob(get(process), job, &result) || !result) { | |
628 continue; | |
629 } | |
630 | |
631 // Check whether the process is still running | |
632 if (::WaitForSingleObject(get(process), 0) != WAIT_TIMEOUT) { | |
633 continue; | |
634 } | |
635 | |
636 // Compare the name if needed | |
637 if (search_name && *search_name) { | |
638 if (_tcsicmp(pe32.szExeFile, search_name) != 0) { | |
639 continue; | |
640 } | |
641 } | |
642 | |
643 // If we need to exclude certain path, check it now | |
644 if (path_to_exclude && *path_to_exclude) { | |
645 if (IsProcessRunningWithPath(pe32.th32ProcessID, path_to_exclude)) { | |
646 continue; | |
647 } | |
648 } | |
649 | |
650 // Add to the list | |
651 ProcessInfo proc_info; | |
652 proc_info.process_id = pe32.th32ProcessID; | |
653 proc_info.parent_id = pe32.th32ParentProcessID; | |
654 #if !SHIPPING | |
655 proc_info.exe_file = pe32.szExeFile; | |
656 #endif | |
657 descendant_processes->push_back(proc_info); | |
658 } while (::Process32Next(get(process_snap), &pe32)); | |
659 | |
660 return S_OK; | |
661 } | |
662 | |
663 HRESULT Process::FindProcesses(uint32 exclude_mask, | |
664 const TCHAR* search_name, | |
665 bool search_main_executable_only, | |
666 std::vector<uint32>* process_ids_found) { | |
667 ASSERT1(process_ids_found); | |
668 // Remove the only include processes owned by user mask from the exclude | |
669 // mask. This is needed as this is the behavior expected by the method, | |
670 // before the addition of the user_sid. | |
671 exclude_mask &= (~INCLUDE_ONLY_PROCESS_OWNED_BY_USER); | |
672 std::vector<CString> command_lines; | |
673 return FindProcesses(exclude_mask, search_name, search_main_executable_only, | |
674 _T(""), command_lines, process_ids_found); | |
675 } | |
676 | |
677 bool Process::IsStringPresentInList(const CString& process_command_line, | |
678 const std::vector<CString>& list) { | |
679 std::vector<CString>::const_iterator iter = list.begin(); | |
680 for (; iter != list.end(); ++iter) { | |
681 CString value_to_find = *iter; | |
682 | |
683 // If we are able to open the process command line, then we should | |
684 // ensure that it does not contain the value that we are looking for. | |
685 if (process_command_line.Find(value_to_find) != -1) { | |
686 // Found a match. | |
687 return true; | |
688 } | |
689 } | |
690 | |
691 return false; | |
692 } | |
693 | |
694 // TODO(omaha): Change the implementation of this method to take in a | |
695 // predicate that determines whether a process should be included in the | |
696 // result set. | |
697 HRESULT Process::FindProcesses(uint32 exclude_mask, | |
698 const TCHAR* search_name, | |
699 bool search_main_executable_only, | |
700 const CString& user_sid, | |
701 const std::vector<CString>& command_lines, | |
702 std::vector<uint32>* process_ids_found) { | |
703 ASSERT1(search_name && *search_name); | |
704 ASSERT1(process_ids_found); | |
705 ASSERT1(!((exclude_mask & EXCLUDE_PROCESS_COMMAND_LINE_CONTAINING_STRING) && | |
706 (exclude_mask & INCLUDE_PROCESS_COMMAND_LINE_CONTAINING_STRING))); | |
707 | |
708 const TCHAR* const kLocalSystemSid = _T("S-1-5-18"); | |
709 | |
710 // Clear the output queue | |
711 process_ids_found->clear(); | |
712 | |
713 // Get the list of process identifiers. | |
714 uint32 process_ids[kMaxProcesses] = {0}; | |
715 uint32 bytes_returned = 0; | |
716 if (!::EnumProcesses(reinterpret_cast<DWORD*>(process_ids), | |
717 sizeof(process_ids), | |
718 reinterpret_cast<DWORD*>(&bytes_returned))) { | |
719 HRESULT hr = HRESULTFromLastError(); | |
720 UTIL_LOG(LEVEL_ERROR, (_T("[Process::FindProcesses-fail to EnumProcesses]") | |
721 _T("[0x%x]"), hr)); | |
722 return hr; | |
723 } | |
724 | |
725 // Enumerate all processes | |
726 int num_processes = bytes_returned / sizeof(process_ids[0]); | |
727 // We have found an elevated number of crashes in 1.2.584.15114 on what | |
728 // we believe are Italian systems. The first step to solving this Italian job | |
729 // is to assert on the condition while we are further testing this. | |
730 ASSERT1(num_processes <= kMaxProcesses); | |
731 | |
732 // In Vista, SeDebugPrivilege is required to open the process not owned by | |
733 // current user. Also required for XP admins to open Local System processes | |
734 // with PROCESS_QUERY_INFORMATION access rights. | |
735 System::AdjustPrivilege(SE_DEBUG_NAME, true); | |
736 | |
737 const uint32 cur_process_id = ::GetCurrentProcessId(); | |
738 | |
739 uint32 parent_process_id = 0; | |
740 if (exclude_mask & EXCLUDE_PARENT_PROCESS) { | |
741 Process current_process(cur_process_id); | |
742 uint32 ppid = 0; | |
743 HRESULT hr = current_process.GetParentProcessId(&ppid); | |
744 parent_process_id = SUCCEEDED(hr) ? ppid : 0; | |
745 } | |
746 | |
747 // Get SID of current user | |
748 CString cur_user_sid; | |
749 HRESULT hr = omaha::user_info::GetProcessUser(NULL, NULL, &cur_user_sid); | |
750 if (FAILED(hr)) { | |
751 return hr; | |
752 } | |
753 | |
754 UTIL_LOG(L4, (_T("[Process::FindProcesses][processes=%d]"), num_processes)); | |
755 for (int i = 0; i < num_processes; ++i) { | |
756 // Skip the system idle process. | |
757 if (process_ids[i] == 0) { | |
758 continue; | |
759 } | |
760 | |
761 // Skip the current process if needed. | |
762 if ((exclude_mask & EXCLUDE_CURRENT_PROCESS) && | |
763 (process_ids[i] == cur_process_id)) { | |
764 UTIL_LOG(L4, (_T("[Excluding current process %d"), process_ids[i])); | |
765 continue; | |
766 } | |
767 | |
768 // Skip the parent process if needed. | |
769 if ((exclude_mask & EXCLUDE_PARENT_PROCESS) && | |
770 (process_ids[i] == parent_process_id)) { | |
771 UTIL_LOG(L4, (_T("[Excluding parent process(%d) of %d"), | |
772 process_ids[i], cur_process_id)); | |
773 continue; | |
774 } | |
775 | |
776 | |
777 // Get the owner sid. | |
778 // Note that we may fail to get the owner which is not current user. | |
779 // So if the owner_sid is empty, the process is sure not to be owned by the | |
780 // current user. | |
781 CString owner_sid; | |
782 Process::GetProcessOwner(process_ids[i], &owner_sid); | |
783 | |
784 if ((exclude_mask & INCLUDE_ONLY_PROCESS_OWNED_BY_USER) && | |
785 owner_sid != user_sid) { | |
786 UTIL_LOG(L4, | |
787 (_T("[Excluding process as not owned by user][%d]"), process_ids[i])); | |
788 continue; | |
789 } | |
790 | |
791 if ((exclude_mask & EXCLUDE_PROCESS_OWNED_BY_CURRENT_USER) && | |
792 owner_sid == cur_user_sid) { | |
793 UTIL_LOG(L4, | |
794 (_T("[Excluding process as owned by current user][%d]"), | |
795 process_ids[i])); | |
796 continue; | |
797 } | |
798 if ((exclude_mask & EXCLUDE_PROCESS_OWNED_BY_SYSTEM) && | |
799 owner_sid == kLocalSystemSid) { | |
800 UTIL_LOG(L4, | |
801 (_T("[Excluding process as owned by system][%d]"), process_ids[i])); | |
802 continue; | |
803 } | |
804 if (exclude_mask & EXCLUDE_PROCESS_COMMAND_LINE_CONTAINING_STRING || | |
805 exclude_mask & INCLUDE_PROCESS_COMMAND_LINE_CONTAINING_STRING) { | |
806 CString process_command_line; | |
807 HRESULT hr = GetCommandLine(process_ids[i], &process_command_line); | |
808 if (FAILED(hr)) { | |
809 UTIL_LOG(L4, | |
810 (_T("[Excluding process could not get command line][%d]"), | |
811 process_ids[i])); | |
812 continue; | |
813 } | |
814 | |
815 // If we are able to open the process command line, then we should | |
816 // ensure that it does not contain the value that we are looking for if | |
817 // we are excluding the command line or that it contains the command line | |
818 // that we are looking for in case the include switch is specified. | |
819 bool present = IsStringPresentInList(process_command_line, command_lines); | |
820 if ((present && | |
821 (exclude_mask & EXCLUDE_PROCESS_COMMAND_LINE_CONTAINING_STRING)) || | |
822 (!present && | |
823 (exclude_mask & INCLUDE_PROCESS_COMMAND_LINE_CONTAINING_STRING))) { | |
824 UTIL_LOG(L4, (_T("[Process command line matches criteria][%d]'[%s]'"), | |
825 process_ids[i], process_command_line)); | |
826 continue; | |
827 } | |
828 } | |
829 | |
830 // If search_name is provided, make sure it matches | |
831 if (Process::IsProcessUsingExeOrDll(process_ids[i], | |
832 search_name, | |
833 search_main_executable_only)) { | |
834 UTIL_LOG(L4, | |
835 (_T("[Including process][%d][%s]"), process_ids[i], search_name)); | |
836 process_ids_found->push_back(process_ids[i]); | |
837 } | |
838 } | |
839 | |
840 return S_OK; | |
841 } | |
842 | |
843 HRESULT Process::FindProcessesInSession( | |
844 DWORD session_id, | |
845 uint32 exclude_mask, | |
846 const TCHAR* search_name, | |
847 bool search_main_executable_only, | |
848 const CString& user_sid, | |
849 const std::vector<CString>& cmd_lines, | |
850 std::vector<uint32>* process_ids_found) { | |
851 HRESULT hr = FindProcesses(exclude_mask, | |
852 search_name, | |
853 search_main_executable_only, | |
854 user_sid, | |
855 cmd_lines, | |
856 process_ids_found); | |
857 if (FAILED(hr)) { | |
858 return hr; | |
859 } | |
860 | |
861 // Filter to processes running under session_id. | |
862 std::vector<uint32>::iterator iter = process_ids_found->begin(); | |
863 while (iter != process_ids_found->end()) { | |
864 uint32 process_pid = *iter; | |
865 DWORD process_session = 0; | |
866 hr = S_OK; | |
867 if (!::ProcessIdToSessionId(process_pid, &process_session)) { | |
868 hr = HRESULTFromLastError(); | |
869 UTIL_LOG(LE, (_T("[::ProcessIdToSessionId failed][0x%x]"), hr)); | |
870 } else if (process_session != session_id) { | |
871 UTIL_LOG(L4, (_T("[Excluding process, different session][%d][%d][%d]"), | |
872 process_pid, process_session, session_id)); | |
873 } | |
874 | |
875 if (FAILED(hr) || process_session != session_id) { | |
876 // Remove from list and continue. | |
877 iter = process_ids_found->erase(iter); | |
878 continue; | |
879 } | |
880 | |
881 ++iter; | |
882 } | |
883 | |
884 return S_OK; | |
885 } | |
886 | |
887 bool Process::IsModuleMatchingExeOrDll(const TCHAR* module_name, | |
888 const TCHAR* search_name, | |
889 bool is_fully_qualified_name) { | |
890 CString module_file_name; | |
891 if (is_fully_qualified_name) { | |
892 if (FAILED(GetLongPathName(module_name, &module_file_name))) { | |
893 return false; | |
894 } | |
895 } else { | |
896 module_file_name = ::PathFindFileName(module_name); | |
897 ASSERT1(!module_file_name.IsEmpty()); | |
898 if (module_file_name.IsEmpty()) { | |
899 return false; | |
900 } | |
901 } | |
902 | |
903 return (module_file_name.CompareNoCase(search_name) == 0); | |
904 } | |
905 | |
906 DWORD Process::GetProcessImageFileName(HANDLE proc_handle, | |
907 LPTSTR image_file, | |
908 DWORD file_size) { | |
909 typedef DWORD (WINAPI *Fun)(HANDLE proc_handle, | |
910 LPWSTR image_file, | |
911 DWORD file_size); | |
912 | |
913 HINSTANCE psapi_instance = ::GetModuleHandle(_T("Psapi.dll")); | |
914 ASSERT1(psapi_instance); | |
915 Fun pfn = reinterpret_cast<Fun>(::GetProcAddress(psapi_instance, | |
916 "GetProcessImageFileNameW")); | |
917 if (!pfn) { | |
918 UTIL_LOG(L1, (_T("::GetProcessImageFileNameW() not found in Psapi.dll"))); | |
919 return 0; | |
920 } | |
921 return (*pfn)(proc_handle, image_file, file_size); | |
922 } | |
923 | |
924 bool Process::IsProcImageMatch(HANDLE proc_handle, | |
925 const TCHAR* search_name, | |
926 bool is_fully_qualified_name) { | |
927 TCHAR image_name[MAX_PATH] = _T(""); | |
928 if (!GetProcessImageFileName(proc_handle, | |
929 image_name, | |
930 arraysize(image_name))) { | |
931 UTIL_LOG(L4, (_T("[GetProcessImageFileName fail[0x%x]"), | |
932 HRESULTFromLastError())); | |
933 return false; | |
934 } | |
935 | |
936 UTIL_LOG(L4, (_T("[GetProcessImageFileName][%s]"), image_name)); | |
937 CString dos_name; | |
938 HRESULT hr(DevicePathToDosPath(image_name, &dos_name)); | |
939 if (FAILED(hr)) { | |
940 UTIL_LOG(L4, (_T("[DevicePathToDosPath fail[0x%x]"), hr)); | |
941 return false; | |
942 } | |
943 | |
944 return IsModuleMatchingExeOrDll(dos_name, | |
945 search_name, | |
946 is_fully_qualified_name); | |
947 } | |
948 | |
949 // Is the process using the specified exe/dll? | |
950 bool Process::IsProcessUsingExeOrDll(uint32 process_id, | |
951 const TCHAR* search_name, | |
952 bool search_main_executable_only) { | |
953 ASSERT1(search_name); | |
954 | |
955 // Open the process | |
956 scoped_process process_handle(::OpenProcess( | |
957 PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, | |
958 FALSE, | |
959 process_id)); | |
960 if (!process_handle) { | |
961 UTIL_LOG(L4, (_T("[::OpenProcess failed][0x%x]"), HRESULTFromLastError())); | |
962 return false; | |
963 } | |
964 | |
965 // Does the name represent a fully qualified name? | |
966 // We only do a simple check here | |
967 bool is_fully_qualified_name = String_FindChar(search_name, _T('\\')) != -1; | |
968 CString long_search_name; | |
969 if (is_fully_qualified_name) { | |
970 HRESULT hr(GetLongPathName(search_name, &long_search_name)); | |
971 if (FAILED(hr)) { | |
972 UTIL_LOG(L4, (_T("[GetLongPathName fail][hr=x%x]"), hr)); | |
973 return false; | |
974 } | |
975 search_name = long_search_name; | |
976 } | |
977 | |
978 // Take a snapshot of all modules in the specified process | |
979 int num_modules_to_fetch = search_main_executable_only ? 1 : | |
980 kMaxProcessModules; | |
981 HMODULE module_handles[kMaxProcessModules]; | |
982 SetZero(module_handles); | |
983 uint32 bytes_needed = 0; | |
984 if (!::EnumProcessModules(get(process_handle), | |
985 module_handles, | |
986 num_modules_to_fetch * sizeof(HMODULE), | |
987 reinterpret_cast<DWORD*>(&bytes_needed))) { | |
988 HRESULT hr = HRESULTFromLastError(); | |
989 UTIL_LOG(LEVEL_ERROR, (_T("[EnumProcessModules failed][0x%x]"), hr)); | |
990 | |
991 if (IsWow64(::GetCurrentProcessId())) { | |
992 // ::EnumProcessModules from a WoW64 process fails for x64 processes. | |
993 // We try ::GetProcessImageFileName as a workaround here. | |
994 return search_main_executable_only ? | |
995 IsProcImageMatch(get(process_handle), | |
996 search_name, | |
997 is_fully_qualified_name) : | |
998 false; | |
999 } else { | |
1000 return false; | |
1001 } | |
1002 } | |
1003 | |
1004 int num_modules = bytes_needed / sizeof(HMODULE); | |
1005 if (num_modules > num_modules_to_fetch) { | |
1006 num_modules = num_modules_to_fetch; | |
1007 } | |
1008 | |
1009 for (int i = 0; i < num_modules; ++i) { | |
1010 TCHAR module_name[MAX_PATH]; | |
1011 SetZero(module_name); | |
1012 if (!::GetModuleFileNameEx(get(process_handle), | |
1013 module_handles[i], | |
1014 module_name, | |
1015 arraysize(module_name))) { | |
1016 UTIL_LOG(LEVEL_ERROR, (_T("[GetModuleFileNameEx fail[x%x]"), | |
1017 HRESULTFromLastError())); | |
1018 continue; | |
1019 } | |
1020 | |
1021 if (IsModuleMatchingExeOrDll(module_name, | |
1022 search_name, | |
1023 is_fully_qualified_name)) { | |
1024 return true; | |
1025 } | |
1026 } | |
1027 | |
1028 return false; | |
1029 } | |
1030 | |
1031 // Helper function to get long path name | |
1032 HRESULT Process::GetLongPathName(const TCHAR* short_name, CString* long_name) { | |
1033 ASSERT1(short_name); | |
1034 ASSERT1(long_name); | |
1035 | |
1036 TCHAR temp_name[MAX_PATH]; | |
1037 SetZero(temp_name); | |
1038 | |
1039 HRESULT hr = S_OK; | |
1040 if (!::GetLongPathName(short_name, temp_name, arraysize(temp_name))) { | |
1041 hr = HRESULTFromLastError(); | |
1042 } else { | |
1043 long_name->SetString(temp_name); | |
1044 } | |
1045 | |
1046 return hr; | |
1047 } | |
1048 | |
1049 // Retrieve the FQPN for the executable file for a process. (Note: Using | |
1050 // GetModuleFileNameEx is slower than GetProcessImageFileName, but the former | |
1051 // is available on Win2K, while the latter is only only on XP and up.) | |
1052 HRESULT Process::GetExecutablePath(uint32 process_id, CString *exe_path) { | |
1053 ASSERT1(process_id); | |
1054 ASSERT1(exe_path); | |
1055 | |
1056 TCHAR temp_path[MAX_PATH]; | |
1057 SetZero(temp_path); | |
1058 | |
1059 scoped_process process(::OpenProcess(PROCESS_QUERY_INFORMATION | | |
1060 PROCESS_VM_READ, | |
1061 FALSE, | |
1062 process_id)); | |
1063 if (!valid(process)) { | |
1064 return HRESULTFromLastError(); | |
1065 } | |
1066 | |
1067 if (0 == ::GetModuleFileNameEx(get(process), NULL, temp_path, MAX_PATH)) { | |
1068 return HRESULTFromLastError(); | |
1069 } | |
1070 | |
1071 exe_path->SetString(temp_path); | |
1072 return S_OK; | |
1073 } | |
1074 | |
1075 // Type definitions needed for GetCommandLine() and GetProcessIdFromHandle() | |
1076 // From MSDN document on NtQueryInformationProcess() and other sources | |
1077 typedef struct _PROCESS_BASIC_INFORMATION { | |
1078 PVOID Reserved1; | |
1079 BYTE *PebBaseAddress; | |
1080 PVOID Reserved2[2]; | |
1081 ULONG_PTR UniqueProcessId; | |
1082 PVOID Reserved3; | |
1083 } PROCESS_BASIC_INFORMATION; | |
1084 | |
1085 typedef enum _PROCESSINFOCLASS { | |
1086 ProcessBasicInformation = 0, | |
1087 ProcessWow64Information = 26 | |
1088 } PROCESSINFOCLASS; | |
1089 | |
1090 typedef WINBASEAPI DWORD WINAPI | |
1091 GetProcessIdFn( | |
1092 HANDLE Process | |
1093 ); | |
1094 | |
1095 typedef LONG WINAPI | |
1096 NtQueryInformationProcess( | |
1097 IN HANDLE ProcessHandle, | |
1098 IN PROCESSINFOCLASS ProcessInformationClass, | |
1099 OUT PVOID ProcessInformation, | |
1100 IN ULONG ProcessInformationLength, | |
1101 OUT PULONG ReturnLength OPTIONAL | |
1102 ); | |
1103 | |
1104 typedef struct _RTL_DRIVE_LETTER_CURDIR { | |
1105 USHORT Flags; | |
1106 USHORT Length; | |
1107 ULONG TimeStamp; | |
1108 UNICODE_STRING DosPath; | |
1109 } RTL_DRIVE_LETTER_CURDIR, *PRTL_DRIVE_LETTER_CURDIR; | |
1110 | |
1111 typedef struct _RTL_USER_PROCESS_PARAMETERS { | |
1112 ULONG MaximumLength; | |
1113 ULONG Length; | |
1114 ULONG Flags; | |
1115 ULONG DebugFlags; | |
1116 PVOID ConsoleHandle; | |
1117 ULONG ConsoleFlags; | |
1118 HANDLE StdInputHandle; | |
1119 HANDLE StdOutputHandle; | |
1120 HANDLE StdErrorHandle; | |
1121 UNICODE_STRING CurrentDirectoryPath; | |
1122 HANDLE CurrentDirectoryHandle; | |
1123 UNICODE_STRING DllPath; | |
1124 UNICODE_STRING ImagePathName; | |
1125 UNICODE_STRING CommandLine; | |
1126 PVOID Environment; | |
1127 ULONG StartingPositionLeft; | |
1128 ULONG StartingPositionTop; | |
1129 ULONG Width; | |
1130 ULONG Height; | |
1131 ULONG CharWidth; | |
1132 ULONG CharHeight; | |
1133 ULONG ConsoleTextAttributes; | |
1134 ULONG WindowFlags; | |
1135 ULONG ShowWindowFlags; | |
1136 UNICODE_STRING WindowTitle; | |
1137 UNICODE_STRING DesktopName; | |
1138 UNICODE_STRING ShellInfo; | |
1139 UNICODE_STRING RuntimeData; | |
1140 RTL_DRIVE_LETTER_CURDIR DLCurrentDirectory[0x20]; | |
1141 } RTL_USER_PROCESS_PARAMETERS, *PRTL_USER_PROCESS_PARAMETERS; | |
1142 | |
1143 // Get the function pointer to GetProcessId in KERNEL32.DLL | |
1144 static HRESULT EnsureGPIFunction(GetProcessIdFn** gpi_func_ptr) { | |
1145 static GetProcessIdFn* gpi_func = NULL; | |
1146 if (!gpi_func) { | |
1147 HMODULE kernel32_module = ::GetModuleHandle(_T("kernel32.dll")); | |
1148 if (!kernel32_module) { | |
1149 return HRESULTFromLastError(); | |
1150 } | |
1151 gpi_func = reinterpret_cast<GetProcessIdFn*>( | |
1152 ::GetProcAddress(kernel32_module, "GetProcessId")); | |
1153 if (!gpi_func) { | |
1154 return HRESULTFromLastError(); | |
1155 } | |
1156 } | |
1157 | |
1158 *gpi_func_ptr = gpi_func; | |
1159 return S_OK; | |
1160 } | |
1161 | |
1162 // Get the function pointer to NtQueryInformationProcess in NTDLL.DLL | |
1163 static HRESULT EnsureQIPFunction(NtQueryInformationProcess** qip_func_ptr) { | |
1164 static NtQueryInformationProcess* qip_func = NULL; | |
1165 if (!qip_func) { | |
1166 HMODULE ntdll_module = ::GetModuleHandle(_T("ntdll.dll")); | |
1167 if (!ntdll_module) { | |
1168 return HRESULTFromLastError(); | |
1169 } | |
1170 qip_func = reinterpret_cast<NtQueryInformationProcess*>( | |
1171 ::GetProcAddress(ntdll_module, "NtQueryInformationProcess")); | |
1172 if (!qip_func) { | |
1173 return HRESULTFromLastError(); | |
1174 } | |
1175 } | |
1176 | |
1177 *qip_func_ptr = qip_func; | |
1178 return S_OK; | |
1179 } | |
1180 | |
1181 // Obtain the process ID from a hProcess HANDLE | |
1182 ULONG Process::GetProcessIdFromHandle(HANDLE hProcess) { | |
1183 if (SystemInfo::IsRunningOnXPSP1OrLater()) { | |
1184 // Thunk to the documented ::GetProcessId() API | |
1185 GetProcessIdFn* gpi_func = NULL; | |
1186 HRESULT hr = EnsureGPIFunction(&gpi_func); | |
1187 if (FAILED(hr)) { | |
1188 ASSERT(FALSE, | |
1189 (_T("Process::GetProcessIdFromHandle - EnsureGPIFunction") | |
1190 _T(" failed[0x%x]"), hr)); | |
1191 return 0; | |
1192 } | |
1193 ASSERT1(gpi_func); | |
1194 return gpi_func(hProcess); | |
1195 } | |
1196 | |
1197 // For lower versions of Windows, we use undocumented | |
1198 // function NtQueryInformationProcess to get at the PID | |
1199 NtQueryInformationProcess* qip_func = NULL; | |
1200 HRESULT hr = EnsureQIPFunction(&qip_func); | |
1201 if (FAILED(hr)) { | |
1202 ASSERT(FALSE, | |
1203 (_T("Process::GetProcessIdFromHandle - EnsureQIPFunction") | |
1204 _T(" failed[0x%x]"), hr)); | |
1205 return 0; | |
1206 } | |
1207 ASSERT1(qip_func); | |
1208 | |
1209 PROCESS_BASIC_INFORMATION info; | |
1210 SetZero(info); | |
1211 if (!NT_SUCCESS(qip_func(hProcess, | |
1212 ProcessBasicInformation, | |
1213 &info, | |
1214 sizeof(info), | |
1215 NULL))) { | |
1216 ASSERT(FALSE, (_T("Process::GetProcessIdFromHandle - ") | |
1217 _T("NtQueryInformationProcess failed!"))); | |
1218 return 0; | |
1219 } | |
1220 | |
1221 return info.UniqueProcessId; | |
1222 } | |
1223 | |
1224 // Get the command line of a process | |
1225 HRESULT Process::GetCommandLine(uint32 process_id, CString* cmd_line) { | |
1226 ASSERT1(process_id); | |
1227 ASSERT1(cmd_line); | |
1228 | |
1229 // Open the process | |
1230 scoped_process process_handle(::OpenProcess( | |
1231 PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, | |
1232 false, | |
1233 process_id)); | |
1234 if (!process_handle) { | |
1235 return HRESULTFromLastError(); | |
1236 } | |
1237 | |
1238 // Obtain Process Environment Block | |
1239 // Note that NtQueryInformationProcess is not available in Windows 95/98/ME | |
1240 NtQueryInformationProcess* qip_func = NULL; | |
1241 HRESULT hr = EnsureQIPFunction(&qip_func); | |
1242 | |
1243 if (FAILED(hr)) { | |
1244 return hr; | |
1245 } | |
1246 ASSERT1(qip_func); | |
1247 | |
1248 PROCESS_BASIC_INFORMATION info; | |
1249 SetZero(info); | |
1250 if (!NT_SUCCESS(qip_func(get(process_handle), | |
1251 ProcessBasicInformation, | |
1252 &info, | |
1253 sizeof(info), | |
1254 NULL))) { | |
1255 return E_FAIL; | |
1256 } | |
1257 BYTE* peb = info.PebBaseAddress; | |
1258 | |
1259 // Read address of parameters (see some PEB reference) | |
1260 // TODO(omaha): use offsetof(PEB, ProcessParameters) to replace 0x10 | |
1261 // http://msdn.microsoft.com/en-us/library/aa813706.aspx | |
1262 SIZE_T bytes_read = 0; | |
1263 uint32 dw = 0; | |
1264 if (!::ReadProcessMemory(get(process_handle), | |
1265 peb + 0x10, | |
1266 &dw, | |
1267 sizeof(dw), | |
1268 &bytes_read)) { | |
1269 return HRESULTFromLastError(); | |
1270 } | |
1271 | |
1272 // Read all the parameters | |
1273 RTL_USER_PROCESS_PARAMETERS params; | |
1274 SetZero(params); | |
1275 if (!::ReadProcessMemory(get(process_handle), | |
1276 reinterpret_cast<PVOID>(dw), | |
1277 ¶ms, | |
1278 sizeof(params), | |
1279 &bytes_read)) { | |
1280 return HRESULTFromLastError(); | |
1281 } | |
1282 | |
1283 // Read the command line parameter | |
1284 const int max_cmd_line_len = std::min( | |
1285 static_cast<int>(params.CommandLine.MaximumLength), | |
1286 kMaxCmdLineLengthBytes); | |
1287 if (!::ReadProcessMemory(get(process_handle), | |
1288 params.CommandLine.Buffer, | |
1289 cmd_line->GetBufferSetLength(max_cmd_line_len), | |
1290 max_cmd_line_len, | |
1291 &bytes_read)) { | |
1292 return HRESULTFromLastError(); | |
1293 } | |
1294 | |
1295 cmd_line->ReleaseBuffer(); | |
1296 | |
1297 return S_OK; | |
1298 } | |
1299 | |
1300 // Check if the process is running with a specified path | |
1301 bool Process::IsProcessRunningWithPath(uint32 process_id, const TCHAR* path) { | |
1302 ASSERT1(process_id); | |
1303 ASSERT1(path && *path); | |
1304 | |
1305 const int kProcessWaitModuleFullyUpMs = 100; | |
1306 const int kProcessWaitModuleRetries = 10; | |
1307 | |
1308 // Open the process | |
1309 scoped_process process(::OpenProcess( | |
1310 PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, | |
1311 false, | |
1312 process_id)); | |
1313 if (!process) { | |
1314 UTIL_LOG(LEVEL_ERROR, | |
1315 (_T("[Process::IsProcessRunningWithPath - OpenProcess failed]") | |
1316 _T("[%u][0x%x]"), | |
1317 process_id, HRESULTFromLastError())); | |
1318 return false; | |
1319 } | |
1320 | |
1321 for (int i = 0; i < kProcessWaitModuleRetries; ++i) { | |
1322 // Get the command line path of the main module | |
1323 // Note that we are using psapi functions which is not supported in Windows | |
1324 // 95/98/ME | |
1325 // | |
1326 // Sometimes it might be the case that the process is created but the main | |
1327 // module is not fully loaded. If so, wait a while and then try again | |
1328 TCHAR process_path[MAX_PATH]; | |
1329 if (::GetModuleFileNameEx(get(process), | |
1330 NULL, | |
1331 process_path, | |
1332 arraysize(process_path))) { | |
1333 // Do the check | |
1334 if (String_StartsWith(process_path, path, true)) { | |
1335 return true; | |
1336 } | |
1337 | |
1338 // Try again with short form | |
1339 TCHAR short_path[MAX_PATH]; | |
1340 if (::GetShortPathName(path, short_path, arraysize(short_path)) && | |
1341 String_StartsWith(process_path, short_path, true)) { | |
1342 return true; | |
1343 } | |
1344 | |
1345 return false; | |
1346 } | |
1347 | |
1348 UTIL_LOG(LEVEL_ERROR, | |
1349 (_T("[Process::IsProcessRunningWithPath - GetModuleFileNameEx ") | |
1350 _T("failed][%u][0x%x]"), | |
1351 process_id, HRESULTFromLastError())); | |
1352 | |
1353 ::Sleep(kProcessWaitModuleFullyUpMs); | |
1354 } | |
1355 | |
1356 UTIL_LOG(LEVEL_ERROR, | |
1357 (_T("[Process::IsProcessRunningWithPath - failed to get process ") | |
1358 _T("path][%u][0x%x]"), | |
1359 process_id, HRESULTFromLastError())); | |
1360 | |
1361 return false; | |
1362 } | |
1363 | |
1364 // Get the process owner | |
1365 // Note that we may fail to get the owner which is not current user. | |
1366 HRESULT Process::GetProcessOwner(uint32 pid, CString* owner_sid) { | |
1367 ASSERT1(pid); | |
1368 ASSERT1(owner_sid); | |
1369 | |
1370 scoped_process process(::OpenProcess(PROCESS_QUERY_INFORMATION, false, pid)); | |
1371 if (!valid(process)) { | |
1372 return HRESULTFromLastError(); | |
1373 } | |
1374 | |
1375 CAccessToken token; | |
1376 CSid sid; | |
1377 if (!token.GetProcessToken(READ_CONTROL | TOKEN_QUERY, get(process)) || | |
1378 !token.GetUser(&sid)) { | |
1379 return HRESULTFromLastError(); | |
1380 } | |
1381 | |
1382 *owner_sid = sid.Sid(); | |
1383 return S_OK; | |
1384 } | |
1385 | |
1386 // Creates an impersonation token for the user running process_id. | |
1387 // The caller is responsible for closing the returned handle. | |
1388 HRESULT Process::GetImpersonationToken(DWORD process_id, HANDLE* user_token) { | |
1389 // Get a handle to the process. | |
1390 scoped_process process(::OpenProcess( | |
1391 PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION, | |
1392 TRUE, | |
1393 process_id)); | |
1394 if (!valid(process)) { | |
1395 HRESULT hr(HRESULTFromLastError()); | |
1396 UTIL_LOG(LEVEL_ERROR, | |
1397 (_T("[GetImpersonationToken - ::OpenProcess failed][0x%x]"), | |
1398 hr)); | |
1399 return hr; | |
1400 } | |
1401 | |
1402 HRESULT result = S_OK; | |
1403 scoped_handle process_token; | |
1404 if (!::OpenProcessToken(get(process), TOKEN_DUPLICATE | TOKEN_QUERY, | |
1405 address(process_token))) { | |
1406 result = HRESULTFromLastError(); | |
1407 } else { | |
1408 if (!::DuplicateTokenEx(get(process_token), | |
1409 TOKEN_IMPERSONATE | TOKEN_QUERY | | |
1410 TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE, | |
1411 NULL, | |
1412 SecurityImpersonation, | |
1413 TokenPrimary, | |
1414 user_token)) { | |
1415 result = HRESULTFromLastError(); | |
1416 } | |
1417 } | |
1418 | |
1419 ASSERT(SUCCEEDED(result), (_T("[GetImpersonationToken Failed][hr=0x%x]"), | |
1420 result)); | |
1421 return result; | |
1422 } | |
1423 | |
1424 HRESULT Process::GetUsersOfProcesses(const TCHAR* task_name, | |
1425 int maximum_users, | |
1426 scoped_handle user_tokens[], | |
1427 int* number_of_users) { | |
1428 ASSERT1(task_name && *task_name); | |
1429 ASSERT1(maximum_users); | |
1430 ASSERT1(user_tokens); | |
1431 ASSERT1(number_of_users); | |
1432 | |
1433 scoped_hfile th32cs_snapshot(::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, | |
1434 0)); | |
1435 if (!valid(th32cs_snapshot)) { | |
1436 HRESULT hr(HRESULTFromLastError()); | |
1437 UTIL_LOG(LEVEL_ERROR, (_T("[::CreateToolhelp32Snapshot fail][0x%x]"), hr)); | |
1438 return hr; | |
1439 } | |
1440 | |
1441 HRESULT result = S_OK; | |
1442 *number_of_users = 0; | |
1443 // Walk the list of processes. | |
1444 PROCESSENTRY32 process = {0}; | |
1445 process.dwSize = sizeof(PROCESSENTRY32); | |
1446 for (BOOL found = ::Process32First(get(th32cs_snapshot), &process); found; | |
1447 found = ::Process32Next(get(th32cs_snapshot), &process)) { | |
1448 // Check if it is one of the processes we are looking for. | |
1449 if (_tcsicmp(task_name, process.szExeFile) == 0) { | |
1450 // We match. Get the user's token. | |
1451 scoped_handle user_token; | |
1452 if (FAILED(GetImpersonationToken(process.th32ProcessID, | |
1453 address(user_token)))) | |
1454 continue; | |
1455 | |
1456 // Search through the existing list to see if it's a duplicate. | |
1457 // It's O(n^2) but we should have very few logged on users. | |
1458 int i = 0; | |
1459 for (; i < *number_of_users; i++) { | |
1460 if (get(user_tokens[i]) == get(user_token)) { | |
1461 // It's a duplicate. | |
1462 break; | |
1463 } | |
1464 } | |
1465 if (i >= *number_of_users) { | |
1466 // It's a new one. Add it if there's room. | |
1467 ASSERT1(i < maximum_users); | |
1468 if (i < maximum_users) { | |
1469 // Release the user_token, we don't want it to be closed | |
1470 // by the user_token destructor | |
1471 reset(user_tokens[(*number_of_users)++], release(user_token)); | |
1472 } | |
1473 } | |
1474 } | |
1475 } | |
1476 return result; | |
1477 } | |
1478 | |
1479 HRESULT Process::GetImagePath(const CString& process_name, | |
1480 const CString& user_sid, | |
1481 CString* path) { | |
1482 ASSERT1(path); | |
1483 | |
1484 // Search for running processes with process_name. | |
1485 uint32 mask = INCLUDE_ONLY_PROCESS_OWNED_BY_USER; | |
1486 std::vector<CString> command_line; | |
1487 std::vector<uint32> process_ids; | |
1488 HRESULT hr = FindProcesses(mask, | |
1489 process_name, | |
1490 true, | |
1491 user_sid, | |
1492 command_line, | |
1493 &process_ids); | |
1494 if (FAILED(hr)) { | |
1495 UTIL_LOG(LEVEL_WARNING, (_T("[FindProcesses failed][0x%08x]"), hr)); | |
1496 return hr; | |
1497 } | |
1498 | |
1499 if (process_ids.empty()) { | |
1500 return E_FAIL; | |
1501 } | |
1502 | |
1503 uint32 process_id = process_ids[0]; | |
1504 UTIL_LOG(L4, (_T("[GetImagePath][pid=%d]"), process_id)); | |
1505 scoped_process process_handle(::OpenProcess(PROCESS_QUERY_INFORMATION | | |
1506 PROCESS_VM_READ, | |
1507 FALSE, | |
1508 process_id)); | |
1509 if (!process_handle) { | |
1510 HRESULT hr = HRESULTFromLastError(); | |
1511 UTIL_LOG(L4, (_T("[OpenProcess failed][0x%08x]"), hr)); | |
1512 return hr; | |
1513 } | |
1514 | |
1515 HMODULE module_handle = NULL; | |
1516 DWORD bytes_needed = 0; | |
1517 if (!::EnumProcessModules(get(process_handle), | |
1518 &module_handle, | |
1519 sizeof(HMODULE), | |
1520 &bytes_needed)) { | |
1521 HRESULT hr = HRESULTFromLastError(); | |
1522 UTIL_LOG(LEVEL_WARNING, (_T("[EnumProcessModules failed][0x%08x]"), hr)); | |
1523 // ::EnumProcessModules from a WoW64 process fails for x64 processes. We try | |
1524 // ::GetProcessImageFileName as a workaround here. | |
1525 TCHAR image_name[MAX_PATH] = {0}; | |
1526 if (!GetProcessImageFileName(get(process_handle), | |
1527 image_name, | |
1528 arraysize(image_name))) { | |
1529 HRESULT hr = HRESULTFromLastError(); | |
1530 UTIL_LOG(LE, (_T("[GetProcessImageFileName failed][0x%08x]"), hr)); | |
1531 return hr; | |
1532 } else { | |
1533 *path = image_name; | |
1534 return S_OK; | |
1535 } | |
1536 } | |
1537 | |
1538 TCHAR module_name[MAX_PATH] = {0}; | |
1539 if (!::GetModuleFileNameEx(get(process_handle), | |
1540 module_handle, | |
1541 module_name, | |
1542 arraysize(module_name))) { | |
1543 HRESULT hr = HRESULTFromLastError(); | |
1544 UTIL_LOG(LEVEL_ERROR, (_T("[GetModuleFileNameEx failed][0x%08x]"), hr)); | |
1545 return hr; | |
1546 } | |
1547 | |
1548 *path = module_name; | |
1549 return S_OK; | |
1550 } | |
1551 | |
1552 bool Process::IsWow64(uint32 pid) { | |
1553 typedef BOOL (WINAPI *IsWow64Process)(HANDLE, BOOL*); | |
1554 scoped_process handle(::OpenProcess(PROCESS_QUERY_INFORMATION | SYNCHRONIZE, | |
1555 false, | |
1556 pid)); | |
1557 if (!handle) { | |
1558 return false; | |
1559 } | |
1560 | |
1561 HINSTANCE kernel_instance = ::GetModuleHandle(_T("kernel32.dll")); | |
1562 if (kernel_instance == NULL) { | |
1563 ASSERT1(false); | |
1564 HRESULT hr = HRESULTFromLastError(); | |
1565 UTIL_LOG(LW, (_T("[::GetModuleHandle kernel32.dll failed][0x%08x]"), hr)); | |
1566 return false; | |
1567 } | |
1568 | |
1569 IsWow64Process pfn = reinterpret_cast<IsWow64Process>(::GetProcAddress( | |
1570 kernel_instance, | |
1571 "IsWow64Process")); | |
1572 if (!pfn) { | |
1573 UTIL_LOG(LW, (_T("[::IsWow64Process() not found in kernel32.dll]"))); | |
1574 return false; | |
1575 } | |
1576 | |
1577 BOOL wow64 = FALSE; | |
1578 if (!(*pfn)(get(handle), &wow64)) { | |
1579 HRESULT hr = HRESULTFromLastError(); | |
1580 UTIL_LOG(LW, (_T("[::IsWow64Process() failed][0x%08x]"), hr)); | |
1581 return false; | |
1582 } | |
1583 | |
1584 return (wow64 != 0); | |
1585 } | |
1586 | |
1587 HRESULT Process::MakeProcessWindowForeground(const CString& executable) { | |
1588 UTIL_LOG(L3, (_T("[MakeProcessWindowForeground]"))); | |
1589 | |
1590 CString sid; | |
1591 HRESULT hr = omaha::user_info::GetProcessUser(NULL, NULL, &sid); | |
1592 if (FAILED(hr)) { | |
1593 return hr; | |
1594 } | |
1595 | |
1596 // This code does not handle two cases: | |
1597 // 1. If a new process instance is starting up but there are other process | |
1598 // instances running, then we will not wait for the new process instance. | |
1599 // One way to fix this is to pass the number of expected processes to this | |
1600 // method. | |
1601 // 2. If we find multiple processes, and we are able to find the windows only | |
1602 // for some of the processes (maybe because the rest are still starting up) | |
1603 // then we will only set the windows of the one that we found to the | |
1604 // foreground and ignore the rest. | |
1605 bool found = false; | |
1606 for (int retries = 0; retries < kNumRetriesToFindProcess && !found; | |
1607 ++retries) { | |
1608 std::vector<CString> command_lines; | |
1609 std::vector<uint32> processes; | |
1610 DWORD flags = EXCLUDE_CURRENT_PROCESS | INCLUDE_ONLY_PROCESS_OWNED_BY_USER; | |
1611 hr = Process::FindProcesses(flags, | |
1612 executable, | |
1613 true, | |
1614 sid, | |
1615 command_lines, | |
1616 &processes); | |
1617 if (FAILED(hr)) { | |
1618 UTIL_LOG(LW, (_T("[FindProcesses failed][0x%08x]"), hr)); | |
1619 return hr; | |
1620 } | |
1621 | |
1622 UTIL_LOG(L3, (_T("[Found %d processes]"), processes.size())); | |
1623 for (size_t i = 0; i < processes.size(); ++i) { | |
1624 CSimpleArray<HWND> windows; | |
1625 if (!WindowUtils::FindProcessWindows(processes[i], 0, &windows)) { | |
1626 UTIL_LOG(L3, (_T("[FindProcessWindows failed][0x%08x]"), hr)); | |
1627 continue; | |
1628 } | |
1629 | |
1630 for (int j = 0; j < windows.GetSize(); ++j) { | |
1631 if (WindowUtils::IsMainWindow(windows[j])) { | |
1632 UTIL_LOG(L4, (_T("[Found main window of process %d]"), processes[i])); | |
1633 WindowUtils::MakeWindowForeground(windows[j]); | |
1634 ::FlashWindow(windows[j], true); | |
1635 found = true; | |
1636 break; | |
1637 } | |
1638 } | |
1639 } | |
1640 | |
1641 if (!found) { | |
1642 ::Sleep(kFindProcessRetryIntervalMs); | |
1643 } | |
1644 } | |
1645 | |
1646 return S_OK; | |
1647 } | |
1648 | |
1649 } // namespace omaha | |
1650 | |
OLD | NEW |