OLD | NEW |
| (Empty) |
1 // Copyright 2004-2010 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 #include "omaha/base/system.h" | |
17 | |
18 #include <objidl.h> | |
19 #include <psapi.h> | |
20 #include <winioctl.h> | |
21 #include <wtsapi32.h> | |
22 #include "omaha/base/commands.h" | |
23 #include "omaha/base/commontypes.h" | |
24 #include "omaha/base/const_config.h" | |
25 #include "omaha/base/debug.h" | |
26 #include "omaha/base/disk.h" | |
27 #include "omaha/base/dynamic_link_kernel32.h" | |
28 #include "omaha/base/error.h" | |
29 #include "omaha/base/file.h" | |
30 #include "omaha/base/logging.h" | |
31 #include "omaha/base/module_utils.h" | |
32 #include "omaha/base/path.h" | |
33 #include "omaha/base/scope_guard.h" | |
34 #include "omaha/base/scoped_any.h" | |
35 #include "omaha/base/string.h" | |
36 #include "omaha/base/system_info.h" | |
37 #include "omaha/base/utils.h" | |
38 #include "omaha/base/vistautil.h" | |
39 | |
40 namespace omaha { | |
41 | |
42 // Constant | |
43 const TCHAR kNeedRebootHiddenFileSuffix[] = _T(".needreboot"); | |
44 | |
45 HRESULT System::WaitForDiskActivity(const uint32 max_delay_milliseconds, | |
46 const uint32 sleep_time_ms, | |
47 uint32 *time_waited) { | |
48 ASSERT(time_waited, (L"")); | |
49 uint32 sleep_time = sleep_time_ms; | |
50 if (sleep_time < 20) { sleep_time = 20; } | |
51 else if (sleep_time > 1000) { sleep_time = 1000; } | |
52 HRESULT r; | |
53 *time_waited = 0; | |
54 uint64 writes = 0; | |
55 uint64 new_writes = 0; | |
56 // get current counters | |
57 if (FAILED(r=GetDiskActivityCounters(NULL, &writes, NULL, NULL))) { | |
58 return r; | |
59 } | |
60 | |
61 // wait until a write - reads may be cached | |
62 while (1) { | |
63 if (FAILED(r=GetDiskActivityCounters(NULL, &new_writes, NULL, NULL))) { | |
64 return r; | |
65 } | |
66 if (new_writes > writes) { return S_OK; } | |
67 if (*time_waited > max_delay_milliseconds) { return E_FAIL; } | |
68 SleepEx(sleep_time, TRUE); | |
69 *time_waited += sleep_time; | |
70 } | |
71 } | |
72 | |
73 HRESULT System::GetDiskActivityCounters(uint64* reads, | |
74 uint64* writes, | |
75 uint64* bytes_read, | |
76 uint64* bytes_written) { | |
77 if (reads) { | |
78 *reads = 0; | |
79 } | |
80 | |
81 if (writes) { | |
82 *writes = 0; | |
83 } | |
84 | |
85 if (bytes_read) { | |
86 *bytes_read = 0; | |
87 } | |
88 | |
89 if (bytes_written) { | |
90 *bytes_written = 0; | |
91 } | |
92 | |
93 // Don't want to risk displaying UI errors here | |
94 DisableThreadErrorUI disable_error_dialog_box; | |
95 | |
96 // for all drives | |
97 for (int drive = 0; ; drive++) { | |
98 struct _DISK_PERFORMANCE perf_data; | |
99 const int max_device_len = 50; | |
100 | |
101 // check whether we can access this device | |
102 CString device_name; | |
103 device_name.Format(_T("\\\\.\\PhysicalDrive%d"), drive); | |
104 scoped_handle device(::CreateFile(device_name, 0, | |
105 FILE_SHARE_READ | FILE_SHARE_WRITE, | |
106 NULL, OPEN_EXISTING, 0, NULL)); | |
107 | |
108 if (get(device) == INVALID_HANDLE_VALUE) { | |
109 if (!drive) { | |
110 UTIL_LOG(LEVEL_ERROR, (_T("[Failed to access drive %i][0x%x]"), | |
111 drive, | |
112 HRESULTFromLastError())); | |
113 } | |
114 break; | |
115 } | |
116 | |
117 // disk performance counters must be on (diskperf -y on older machines; | |
118 // defaults to on on newer windows) | |
119 DWORD size = 0; | |
120 if (::DeviceIoControl(get(device), | |
121 IOCTL_DISK_PERFORMANCE, | |
122 NULL, | |
123 0, | |
124 &perf_data, | |
125 sizeof(_DISK_PERFORMANCE), | |
126 &size, | |
127 NULL)) { | |
128 if (reads) { | |
129 *reads += perf_data.ReadCount; | |
130 } | |
131 | |
132 if (writes) { | |
133 *writes += perf_data.WriteCount; | |
134 } | |
135 | |
136 if (bytes_read) { | |
137 *bytes_read += perf_data.BytesRead.QuadPart; | |
138 } | |
139 | |
140 if (bytes_written) { | |
141 *bytes_written += perf_data.BytesWritten.QuadPart; | |
142 } | |
143 } else { | |
144 HRESULT hr = HRESULTFromLastError(); | |
145 UTIL_LOG(LEVEL_ERROR, | |
146 (_T("[System::GetDiskActivityCounters - failed to ") | |
147 _T("DeviceIoControl][0x%x]"), hr)); | |
148 return hr; | |
149 } | |
150 } | |
151 | |
152 return S_OK; | |
153 } | |
154 | |
155 HRESULT System::GetDiskStatistics(const TCHAR* path, | |
156 uint64 *free_bytes_current_user, | |
157 uint64 *total_bytes_current_user, | |
158 uint64 *free_bytes_all_users) { | |
159 ASSERT1(path); | |
160 ASSERT1(free_bytes_current_user); | |
161 ASSERT1(total_bytes_current_user); | |
162 ASSERT1(free_bytes_all_users); | |
163 ASSERT1(sizeof(LARGE_INTEGER) == sizeof(uint64)); // NOLINT | |
164 | |
165 DisableThreadErrorUI disable_error_dialog_box; | |
166 | |
167 if (!::GetDiskFreeSpaceEx( | |
168 path, | |
169 reinterpret_cast<PULARGE_INTEGER>(free_bytes_current_user), | |
170 reinterpret_cast<PULARGE_INTEGER>(total_bytes_current_user), | |
171 reinterpret_cast<PULARGE_INTEGER>(free_bytes_all_users))) { | |
172 HRESULT hr = HRESULTFromLastError(); | |
173 UTIL_LOG(LEVEL_ERROR, | |
174 (_T("[Failed to GetDiskFreeSpaceEx][%s][0x%x]"), path, hr)); | |
175 return hr; | |
176 } | |
177 | |
178 return S_OK; | |
179 } | |
180 | |
181 HRESULT System::GetProcessMemoryStatistics(uint64 *current_working_set, | |
182 uint64 *peak_working_set, | |
183 uint64 *min_working_set_size, | |
184 uint64 *max_working_set_size) { | |
185 HANDLE process_handle = GetCurrentProcess(); | |
186 HRESULT hr = S_OK; | |
187 | |
188 DWORD min_size(0), max_size(0); | |
189 if (GetProcessWorkingSetSize(process_handle, &min_size, &max_size)) { | |
190 UTIL_LOG(L2, (_T("[working set][min: %lu][max: %lu]"), min_size, max_size)); | |
191 if (min_working_set_size) { | |
192 *min_working_set_size = min_size; | |
193 } | |
194 if (max_working_set_size) { | |
195 *max_working_set_size = max_size; | |
196 } | |
197 } else { | |
198 if (min_working_set_size) { | |
199 *min_working_set_size = 0; | |
200 } | |
201 if (max_working_set_size) { | |
202 *max_working_set_size = 0; | |
203 } | |
204 hr = E_FAIL; | |
205 } | |
206 | |
207 if (current_working_set) { *current_working_set = 0; } | |
208 if (peak_working_set) { *peak_working_set = 0; } | |
209 | |
210 // including this call (w/psapi.lib) adds 24k to the process memory | |
211 // according to task manager in one test, memory usage according to task | |
212 // manager increased by 4k after calling this | |
213 PROCESS_MEMORY_COUNTERS counters = { sizeof(counters), 0 }; | |
214 if (GetProcessMemoryInfo(process_handle, | |
215 &counters, | |
216 sizeof(PROCESS_MEMORY_COUNTERS))) { | |
217 if (current_working_set) { | |
218 *current_working_set = counters.WorkingSetSize; | |
219 } | |
220 if (peak_working_set) { | |
221 *peak_working_set = counters.PeakWorkingSetSize; | |
222 } | |
223 UTIL_LOG(L2, (_T("[working set][current: %s][peak: %s]"), | |
224 String_Int64ToString(*current_working_set, 10), | |
225 String_Int64ToString(*peak_working_set, 10))); | |
226 } else { | |
227 if (current_working_set) { | |
228 *current_working_set = 0; | |
229 } | |
230 if (peak_working_set) { | |
231 *peak_working_set = 0; | |
232 } | |
233 hr = E_FAIL; | |
234 } | |
235 | |
236 return hr; | |
237 } | |
238 | |
239 HRESULT System::MaxPhysicalMemoryAvailable(uint64* max_bytes) { | |
240 ASSERT1(max_bytes); | |
241 | |
242 *max_bytes = 0; | |
243 | |
244 uint32 memory_load_percentage = 0; | |
245 uint64 free_physical_memory = 0; | |
246 | |
247 RET_IF_FAILED(System::GetGlobalMemoryStatistics(&memory_load_percentage, | |
248 &free_physical_memory, NULL, NULL, NULL, NULL, NULL)); | |
249 | |
250 UTIL_LOG(L4, (_T("mem load %u max physical memory available %s"), | |
251 memory_load_percentage, | |
252 String_Int64ToString(free_physical_memory, 10))); | |
253 | |
254 *max_bytes = free_physical_memory; | |
255 | |
256 return S_OK; | |
257 } | |
258 | |
259 HRESULT System::GetGlobalMemoryStatistics(uint32 *memory_load_percentage, | |
260 uint64 *free_physical_memory, | |
261 uint64 *total_physical_memory, | |
262 uint64 *free_paged_memory, | |
263 uint64 *total_paged_memory, | |
264 uint64 *process_free_virtual_memory, | |
265 uint64 *process_total_virtual_mem) { | |
266 MEMORYSTATUSEX status; | |
267 status.dwLength = sizeof(status); | |
268 if (!GlobalMemoryStatusEx(&status)) { | |
269 UTIL_LOG(LEVEL_ERROR, (_T("memory status error %u"), GetLastError())); | |
270 return E_FAIL; | |
271 } | |
272 if (memory_load_percentage) { *memory_load_percentage = status.dwMemoryLoad; } | |
273 if (free_physical_memory) { *free_physical_memory = status.ullAvailPhys; } | |
274 if (total_physical_memory) { *total_physical_memory = status.ullTotalPhys; } | |
275 if (free_paged_memory) { *free_paged_memory = status.ullAvailPageFile; } | |
276 if (total_paged_memory) { *total_paged_memory = status.ullTotalPageFile; } | |
277 if (process_free_virtual_memory) { | |
278 *process_free_virtual_memory = status.ullAvailVirtual; | |
279 } | |
280 if (process_total_virtual_mem) { | |
281 *process_total_virtual_mem = status.ullTotalVirtual; | |
282 } | |
283 // GetPerformanceInfo; | |
284 return S_OK; | |
285 } | |
286 | |
287 void System::FreeProcessWorkingSet() { | |
288 // -1,-1 is a special signal to the OS to temporarily trim the working set | |
289 // size to 0. See MSDN for further information. | |
290 ::SetProcessWorkingSetSize(::GetCurrentProcess(), (SIZE_T)-1, (SIZE_T)-1); | |
291 } | |
292 | |
293 HRESULT System::SetThreadPriority(enum Priority priority) { | |
294 int pri; | |
295 | |
296 switch (priority) { | |
297 case LOW: pri = THREAD_PRIORITY_BELOW_NORMAL; break; | |
298 case HIGH: pri = THREAD_PRIORITY_HIGHEST; break; | |
299 case NORMAL: pri = THREAD_PRIORITY_NORMAL; break; | |
300 case IDLE: pri = THREAD_PRIORITY_IDLE; break; | |
301 default: return E_FAIL; | |
302 } | |
303 | |
304 if (::SetThreadPriority(GetCurrentThread(), pri)) { | |
305 return S_OK; | |
306 } else { | |
307 return E_FAIL; | |
308 } | |
309 } | |
310 | |
311 HRESULT System::SetProcessPriority(enum Priority priority) { | |
312 DWORD pri = 0; | |
313 switch (priority) { | |
314 case LOW: pri = BELOW_NORMAL_PRIORITY_CLASS; break; | |
315 case HIGH: pri = ABOVE_NORMAL_PRIORITY_CLASS; break; | |
316 case NORMAL: pri = NORMAL_PRIORITY_CLASS; break; | |
317 case IDLE: return E_INVALIDARG; | |
318 default: return E_INVALIDARG; | |
319 } | |
320 | |
321 DWORD pid = ::GetCurrentProcessId(); | |
322 | |
323 scoped_handle handle(::OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid)); | |
324 if (!valid(handle)) { | |
325 HRESULT hr = HRESULTFromLastError(); | |
326 UTIL_LOG(LE, (_T("[::OpenProcess failed][%u][0x%x]"), pid, hr)); | |
327 return hr; | |
328 } | |
329 | |
330 if (!::SetPriorityClass(get(handle), pri)) { | |
331 HRESULT hr = HRESULTFromLastError(); | |
332 UTIL_LOG(LE, (_T("[::SetPriorityClass failed][%u][0x%x]"), pid, hr)); | |
333 return hr; | |
334 } | |
335 | |
336 return S_OK; | |
337 } | |
338 | |
339 // start another process painlessly via ::CreateProcess. Use the | |
340 // ShellExecuteProcessXXX variants instead of these methods where possible, | |
341 // since ::ShellExecuteEx has better behavior on Windows Vista. | |
342 // When using this method, avoid using process_name - see | |
343 // http://blogs.msdn.com/oldnewthing/archive/2006/05/15/597984.aspx. | |
344 HRESULT System::StartProcess(const TCHAR* process_name, | |
345 TCHAR* command_line, | |
346 PROCESS_INFORMATION* pi) { | |
347 ASSERT1(pi); | |
348 ASSERT1(command_line || process_name); | |
349 ASSERT(!process_name, (_T("Avoid using process_name. See method comment."))); | |
350 | |
351 STARTUPINFO si = {sizeof(si), 0}; | |
352 | |
353 // Feedback cursor is off while the process is starting. | |
354 si.dwFlags = STARTF_FORCEOFFFEEDBACK; | |
355 | |
356 UTIL_LOG(L3, (_T("[System::StartProcess][process %s][cmd %s]"), | |
357 process_name, command_line)); | |
358 | |
359 BOOL success = ::CreateProcess( | |
360 process_name, // Module name | |
361 command_line, // Command line | |
362 NULL, // Process handle not inheritable | |
363 NULL, // Thread handle not inheritable | |
364 FALSE, // Set handle inheritance to FALSE | |
365 0, // No creation flags | |
366 NULL, // Use parent's environment block | |
367 NULL, // Use parent's starting directory | |
368 &si, // Pointer to STARTUPINFO structure | |
369 pi); // Pointer to PROCESS_INFORMATION structure | |
370 | |
371 if (!success) { | |
372 HRESULT hr = HRESULTFromLastError(); | |
373 UTIL_LOG(LEVEL_ERROR, | |
374 (_T("[System::StartProcess][::CreateProcess failed][0x%x]"), hr)); | |
375 return hr; | |
376 } | |
377 | |
378 OPT_LOG(L1, (_T("[Started process][%u]"), pi->dwProcessId)); | |
379 | |
380 return S_OK; | |
381 } | |
382 | |
383 // start another process painlessly via ::CreateProcess. Use the | |
384 // ShellExecuteProcessXXX variants instead of these methods where possible, | |
385 // since ::ShellExecuteEx has better behavior on Windows Vista. | |
386 HRESULT System::StartProcessWithArgsAndInfo(const TCHAR *process_name, | |
387 const TCHAR *cmd_line_arguments, | |
388 PROCESS_INFORMATION *pi) { | |
389 ASSERT1(process_name && cmd_line_arguments && pi); | |
390 | |
391 CString command_line(process_name); | |
392 EnclosePath(&command_line); | |
393 command_line.AppendChar(_T(' ')); | |
394 command_line.Append(cmd_line_arguments); | |
395 return System::StartProcess(NULL, CStrBuf(command_line), pi); | |
396 } | |
397 | |
398 // start another process painlessly via ::CreateProcess. Use the | |
399 // ShellExecuteProcessXXX variants instead of these methods where possible, | |
400 // since ::ShellExecuteEx has better behavior on Windows Vista. | |
401 HRESULT System::StartProcessWithArgs(const TCHAR *process_name, | |
402 const TCHAR *cmd_line_arguments) { | |
403 ASSERT1(process_name && cmd_line_arguments); | |
404 PROCESS_INFORMATION pi = {0}; | |
405 HRESULT hr = System::StartProcessWithArgsAndInfo(process_name, | |
406 cmd_line_arguments, | |
407 &pi); | |
408 if (SUCCEEDED(hr)) { | |
409 ::CloseHandle(pi.hProcess); | |
410 ::CloseHandle(pi.hThread); | |
411 } | |
412 return hr; | |
413 } | |
414 | |
415 HRESULT System::StartCommandLine(const TCHAR* command_line_to_execute) { | |
416 ASSERT1(command_line_to_execute); | |
417 | |
418 CString command_line(command_line_to_execute); | |
419 PROCESS_INFORMATION pi = {0}; | |
420 HRESULT hr = System::StartProcess(NULL, CStrBuf(command_line), &pi); | |
421 if (SUCCEEDED(hr)) { | |
422 ::CloseHandle(pi.hProcess); | |
423 ::CloseHandle(pi.hThread); | |
424 } | |
425 return hr; | |
426 } | |
427 | |
428 // TODO(omaha3): Unit test this method. | |
429 HRESULT System::StartProcessAsUser(HANDLE user_token, | |
430 const CString& executable_path, | |
431 const CString& parameters, | |
432 LPWSTR desktop, | |
433 PROCESS_INFORMATION* pi) { | |
434 UTIL_LOG(L3, (_T("[StartProcessAsUser][%s][%s][%s]"), | |
435 executable_path, parameters, desktop)); | |
436 ASSERT1(pi); | |
437 | |
438 CString cmd(executable_path); | |
439 EnclosePath(&cmd); | |
440 cmd.AppendChar(_T(' ')); | |
441 cmd.Append(parameters); | |
442 | |
443 STARTUPINFO startup_info = { sizeof(startup_info) }; | |
444 startup_info.lpDesktop = desktop; | |
445 DWORD creation_flags(0); | |
446 | |
447 void* environment_block(NULL); | |
448 if (!::CreateEnvironmentBlock(&environment_block, user_token, TRUE)) { | |
449 HRESULT hr = HRESULTFromLastError(); | |
450 ASSERT(false, (_T("[::CreateEnvironmentBlock failed][0x%x]"), hr)); | |
451 return hr; | |
452 } | |
453 | |
454 ON_SCOPE_EXIT(::DestroyEnvironmentBlock, environment_block); | |
455 | |
456 creation_flags |= CREATE_UNICODE_ENVIRONMENT; | |
457 BOOL success = ::CreateProcessAsUser(user_token, | |
458 0, | |
459 CStrBuf(cmd, MAX_PATH), | |
460 0, | |
461 0, | |
462 false, | |
463 creation_flags, | |
464 environment_block, | |
465 0, | |
466 &startup_info, | |
467 pi); | |
468 | |
469 if (!success) { | |
470 HRESULT hr(HRESULTFromLastError()); | |
471 UTIL_LOG(LE, (_T("[::CreateProcessAsUser failed][%s][0x%x]"), cmd, hr)); | |
472 return hr; | |
473 } | |
474 | |
475 return S_OK; | |
476 } | |
477 | |
478 // start another process painlessly via ::ShellExecuteEx. Use this method | |
479 // instead of the StartProcessXXX methods that use ::CreateProcess where | |
480 // possible, since ::ShellExecuteEx has better behavior on Windows Vista. | |
481 // | |
482 // ShellExecuteExEnsureParent displays the PID of the started process if it is | |
483 // returned. It is only returned if the mask includes SEE_MASK_NOCLOSEPROCESS. | |
484 // Therefore, we always set this flag and pass a handle. If the caller did not | |
485 // request the handle, we close it. | |
486 HRESULT System::ShellExecuteProcess(const TCHAR* file_name_to_execute, | |
487 const TCHAR* command_line_parameters, | |
488 HWND hwnd, | |
489 HANDLE* process_handle) { | |
490 ASSERT1(file_name_to_execute); | |
491 | |
492 UTIL_LOG(L3, (_T("[System::ShellExecuteProcess]") | |
493 _T("[file_name_to_execute '%s' command_line_parameters '%s']"), | |
494 file_name_to_execute, command_line_parameters)); | |
495 | |
496 SHELLEXECUTEINFO sei = {0}; | |
497 sei.cbSize = sizeof(sei); | |
498 // SEE_MASK_NOZONECHECKS is set below to work around a problem in systems that | |
499 // had Internet Explorer 7 Beta installed. See http://b/804674. | |
500 // This only works for Windows XP SP1 and later. | |
501 sei.fMask = SEE_MASK_NOCLOSEPROCESS | // Set hProcess to process handle. | |
502 SEE_MASK_FLAG_NO_UI | // Do not display an error message box. | |
503 SEE_MASK_NOZONECHECKS | // Do not perform a zone check. | |
504 SEE_MASK_NOASYNC; // Wait to complete before returning. | |
505 sei.lpVerb = _T("open"); | |
506 sei.lpFile = file_name_to_execute; | |
507 sei.lpParameters = command_line_parameters; | |
508 sei.nShow = SW_SHOWNORMAL; | |
509 sei.hwnd = hwnd; | |
510 | |
511 // Use ShellExecuteExEnsureParent to ensure that we always have a parent | |
512 // window. We need to use the HWND property to be acknowledged as a foreground | |
513 // application on Windows Vista. Otherwise, the elevation prompt will appear | |
514 // minimized on the taskbar. | |
515 if (!ShellExecuteExEnsureParent(&sei)) { | |
516 HRESULT hr(HRESULTFromLastError()); | |
517 OPT_LOG(LEVEL_ERROR, (_T("[Failed to ::ShellExecuteEx][%s][%s][0x%08x]"), | |
518 file_name_to_execute, command_line_parameters, hr)); | |
519 return hr; | |
520 } | |
521 | |
522 if (process_handle) { | |
523 *process_handle = sei.hProcess; | |
524 } else { | |
525 ::CloseHandle(sei.hProcess); | |
526 } | |
527 | |
528 return S_OK; | |
529 } | |
530 | |
531 // start another process painlessly via ::ShellExecuteEx. Use this method | |
532 // instead of the StartProcessXXX methods that use ::CreateProcess where | |
533 // possible, since ::ShellExecuteEx has better behavior on Windows Vista. | |
534 HRESULT System::ShellExecuteCommandLine(const TCHAR* command_line_to_execute, | |
535 HWND hwnd, | |
536 HANDLE* process_handle) { | |
537 ASSERT1(command_line_to_execute); | |
538 | |
539 CString exe; | |
540 CString args; | |
541 | |
542 HRESULT hr = CommandParsingSimple::SplitExeAndArgs(command_line_to_execute, | |
543 &exe, | |
544 &args); | |
545 | |
546 if (SUCCEEDED(hr)) { | |
547 hr = System::ShellExecuteProcess(exe, args, hwnd, process_handle); | |
548 if (FAILED(hr)) { | |
549 UTIL_LOG(LEVEL_ERROR, (_T("[System::ShellExecuteProcess failed]") | |
550 _T("[%s][%s][0x%08x]"), exe, args, hr)); | |
551 } | |
552 } | |
553 | |
554 return hr; | |
555 } | |
556 | |
557 // returns the number of ms the system has had no user input | |
558 int System::GetUserIdleTime() { | |
559 LASTINPUTINFO last_input_info; | |
560 last_input_info.cbSize = sizeof(LASTINPUTINFO); | |
561 // get time in windows ticks since system start of last activity | |
562 BOOL b = GetLastInputInfo(&last_input_info); | |
563 if (b == TRUE) { | |
564 return (GetTickCount()-last_input_info.dwTime); // compute idle time | |
565 } | |
566 return 0; | |
567 } | |
568 | |
569 bool System::IsUserIdle() { | |
570 // Only notify when the user has been idle less than this time | |
571 static int user_idle_threshold_ms = kUserIdleThresholdMs; | |
572 | |
573 bool is_user_idle = (GetUserIdleTime() > user_idle_threshold_ms); | |
574 UTIL_LOG(L2, (_T("System::IsUserIdle() %s; user_idle_threshold_ms = %d"), | |
575 is_user_idle ? _T("TRUE") : _T("FALSE"), | |
576 user_idle_threshold_ms)); | |
577 return is_user_idle; | |
578 } | |
579 | |
580 bool System::IsUserBusy() { | |
581 // The user is busy typing or interacting with another application | |
582 // if the user is below the minimum threshold: | |
583 static int user_idle_min_threshold_ms = kUserIdleMinThresholdMs; | |
584 // The user is probably not paying attention | |
585 // if the user is above the maximum threshold: | |
586 static int user_idle_max_threshold_ms = kUserIdleMaxThresholdMs; | |
587 | |
588 int user_idle_time = GetUserIdleTime(); | |
589 bool is_user_busy = user_idle_time < user_idle_min_threshold_ms || | |
590 user_idle_time > user_idle_max_threshold_ms; | |
591 UTIL_LOG(L2, (_T("[System::IsUserBusy() %s][user_idle_time = %d]") | |
592 _T("[user_idle_min_threshold_ms = %d]") | |
593 _T("[user_idle_max_threshold_ms = %d]"), | |
594 is_user_busy? _T("TRUE") : _T("FALSE"), | |
595 user_idle_time, | |
596 user_idle_min_threshold_ms, | |
597 user_idle_max_threshold_ms)); | |
598 return is_user_busy; | |
599 } | |
600 | |
601 bool System::IsScreensaverRunning() { | |
602 // NT 4.0 and below require testing OpenDesktop("screen-saver") | |
603 // We require W2K or better so we have an easier way | |
604 DWORD result = 0; | |
605 ::SystemParametersInfo(SPI_GETSCREENSAVERRUNNING, 0, &result, 0); | |
606 bool is_screensaver_running = (result != FALSE); | |
607 UTIL_LOG(L2, (_T("System::IsScreensaverRunning() %s"), | |
608 is_screensaver_running? _T("TRUE") : _T("FALSE"))); | |
609 return is_screensaver_running; | |
610 } | |
611 | |
612 bool System::IsWorkstationLocked() { | |
613 bool is_workstation_locked = true; | |
614 HDESK inputdesk = ::OpenInputDesktop(0, 0, GENERIC_READ); | |
615 if (NULL != inputdesk) { | |
616 TCHAR name[256]; | |
617 DWORD needed = arraysize(name); | |
618 BOOL ok = ::GetUserObjectInformation(inputdesk, | |
619 UOI_NAME, | |
620 name, | |
621 sizeof(name), | |
622 &needed); | |
623 ::CloseDesktop(inputdesk); | |
624 if (ok) { | |
625 is_workstation_locked = (0 != lstrcmpi(name, NOTRANSL(_T("default")))); | |
626 } | |
627 } | |
628 | |
629 UTIL_LOG(L2, (_T("System::IsWorkstationLocked() %s"), | |
630 is_workstation_locked? _T("TRUE") : _T("FALSE"))); | |
631 return is_workstation_locked; | |
632 } | |
633 | |
634 bool System::IsUserAway() { | |
635 return IsScreensaverRunning() || IsWorkstationLocked(); | |
636 } | |
637 | |
638 uint32 System::GetProcessHandleCount() { | |
639 typedef LONG (CALLBACK *Fun)(HANDLE, int32, PVOID, ULONG, PULONG); | |
640 | |
641 // This new version of getting the number of open handles works on win2k. | |
642 HMODULE h = GetModuleHandle(_T("ntdll.dll")); | |
643 Fun NtQueryInformationProcess = | |
644 reinterpret_cast<Fun>(::GetProcAddress(h, "NtQueryInformationProcess")); | |
645 | |
646 if (!NtQueryInformationProcess) { | |
647 UTIL_LOG(LEVEL_ERROR, (_T("[NtQueryInformationProcess failed][0x%x]"), | |
648 HRESULTFromLastError())); | |
649 return 0; | |
650 } | |
651 | |
652 DWORD count = 0; | |
653 VERIFY(NtQueryInformationProcess(GetCurrentProcess(), | |
654 kProcessHandleCount, | |
655 &count, | |
656 sizeof(count), | |
657 NULL) >= 0, (L"")); | |
658 | |
659 return count; | |
660 } | |
661 | |
662 uint32 System::GetProcessHandleCountOld() { | |
663 typedef BOOL (CALLBACK * Fun)(HANDLE, PDWORD); | |
664 | |
665 // GetProcessHandleCount not available on win2k | |
666 HMODULE handle = GetModuleHandle(_T("kernel32")); | |
667 Fun f = reinterpret_cast<Fun>(GetProcAddress(handle, | |
668 "GetProcessHandleCount")); | |
669 | |
670 if (!f) return 0; | |
671 | |
672 DWORD count = 0; | |
673 VERIFY((*f)(GetCurrentProcess(), &count), (L"")); | |
674 return count; | |
675 | |
676 // DWORD GetGuiResources (HANDLE hProcess, DWORD uiFlags); | |
677 // Parameters, hProcess | |
678 // [in] Handle to the process. The handle must have the | |
679 // PROCESS_QUERY_INFORMATION access right. For more information, see Process | |
680 // Security and Access Rights. | |
681 // uiFlags | |
682 // [in] GUI object type. This parameter can be one of the following values. | |
683 // Value Meaning | |
684 // GR_GDIOBJECTS Return the count of GDI objects. | |
685 // GR_USEROBJECTS Return the count of USER objects. | |
686 } | |
687 | |
688 void System::GetGuiObjectCount(uint32 *gdi, uint32 *user) { | |
689 if (gdi) { | |
690 *gdi = GetGuiResources(GetCurrentProcess(), GR_GDIOBJECTS); | |
691 } | |
692 if (user) { | |
693 *user = GetGuiResources(GetCurrentProcess(), GR_USEROBJECTS); | |
694 } | |
695 } | |
696 | |
697 HRESULT System::GetRebootCheckDummyFileName(const TCHAR* base_file, | |
698 CString* dummy_file) { | |
699 ASSERT1(dummy_file); | |
700 | |
701 if (base_file && *base_file) { | |
702 ASSERT1(File::Exists(base_file)); | |
703 dummy_file->SetString(base_file); | |
704 } else { | |
705 RET_IF_FAILED(GetModuleFileName(NULL, dummy_file)); | |
706 } | |
707 dummy_file->Append(_T(".needreboot")); | |
708 return S_OK; | |
709 } | |
710 | |
711 // Is the system being rebooted? | |
712 bool System::IsRebooted(const TCHAR* base_file) { | |
713 CString dummy_file; | |
714 if (SUCCEEDED(GetRebootCheckDummyFileName(base_file, &dummy_file))) { | |
715 if (File::Exists(dummy_file)) { | |
716 // If the file exists but it is not found in the | |
717 // PendingFileRenameOperations, (probably becaused that this key is messed | |
718 // up and thus the system restart fails to delete the file), re-add it | |
719 if (!File::AreMovesPendingReboot(dummy_file, true)) { | |
720 File::MoveAfterReboot(dummy_file, NULL); | |
721 } | |
722 return false; | |
723 } else { | |
724 return true; | |
725 } | |
726 } | |
727 return false; | |
728 } | |
729 | |
730 // Mark the system as reboot required | |
731 HRESULT System::MarkAsRebootRequired(const TCHAR* base_file) { | |
732 // Create a dummy file if needed | |
733 CString dummy_file; | |
734 RET_IF_FAILED(GetRebootCheckDummyFileName(base_file, &dummy_file)); | |
735 if (File::Exists(dummy_file)) { | |
736 return S_OK; | |
737 } | |
738 | |
739 File file; | |
740 RET_IF_FAILED(file.Open(dummy_file, true, false)); | |
741 RET_IF_FAILED(file.Close()); | |
742 | |
743 // Hide it | |
744 DWORD file_attr = ::GetFileAttributes(dummy_file); | |
745 if (file_attr == INVALID_FILE_ATTRIBUTES || | |
746 !::SetFileAttributes(dummy_file, file_attr | FILE_ATTRIBUTE_HIDDEN)) { | |
747 return HRESULTFromLastError(); | |
748 } | |
749 | |
750 // Mark it as being deleted after reboot | |
751 return File::MoveAfterReboot(dummy_file, NULL); | |
752 } | |
753 | |
754 // Unmark the system as reboot required | |
755 HRESULT System::UnmarkAsRebootRequired(const TCHAR* base_file) { | |
756 CString dummy_file; | |
757 RET_IF_FAILED(GetRebootCheckDummyFileName(base_file, &dummy_file)); | |
758 | |
759 return File::RemoveFromMovesPendingReboot(dummy_file, false); | |
760 } | |
761 | |
762 // Restart the computer | |
763 HRESULT System::RestartComputer() { | |
764 RET_IF_FAILED(AdjustPrivilege(SE_SHUTDOWN_NAME, true)); | |
765 | |
766 if (!::ExitWindowsEx(EWX_REBOOT, SHTDN_REASON_MAJOR_APPLICATION | | |
767 SHTDN_REASON_MINOR_INSTALLATION | | |
768 SHTDN_REASON_FLAG_PLANNED)) { | |
769 HRESULT hr = HRESULTFromLastError(); | |
770 UTIL_LOG(LEVEL_ERROR, (_T("[System::RestartComputer - failed to") | |
771 _T(" ExitWindowsEx][0x%x]"), hr)); | |
772 return hr; | |
773 } | |
774 | |
775 return S_OK; | |
776 } | |
777 | |
778 // The implementation works on all Windows versions. On NT and XP the screen | |
779 // saver is actually stored in registry at | |
780 // HKEY_CURRENT_USER\Control Panel\Desktop\SCRNSAVE.EXE but the | |
781 // GetPrivateProfileString call is automatically mapped to the registry | |
782 HRESULT System::GetCurrentScreenSaver(CString* fileName) { | |
783 if (!fileName) return E_POINTER; | |
784 | |
785 DWORD nChars = ::GetPrivateProfileString(_T("boot"), | |
786 _T("SCRNSAVE.EXE"), | |
787 _T(""), | |
788 fileName->GetBuffer(MAX_PATH), | |
789 MAX_PATH, | |
790 _T("system.ini")); | |
791 fileName->ReleaseBufferSetLength(nChars); | |
792 | |
793 return S_OK; | |
794 } | |
795 | |
796 HRESULT System::CoCreateInstanceAsAdmin(HWND hwnd, | |
797 REFCLSID rclsid, | |
798 REFIID riid, | |
799 void** ppv) { | |
800 UTIL_LOG(L6, (_T("[CoCreateInstanceAsAdmin][%d][%s][%s]"), | |
801 hwnd, GuidToString(rclsid), GuidToString(riid))); | |
802 | |
803 if (vista_util::IsUserAdmin()) { | |
804 return ::CoCreateInstance(rclsid, NULL, CLSCTX_LOCAL_SERVER, riid, ppv); | |
805 } | |
806 | |
807 if (!SystemInfo::IsRunningOnVistaOrLater()) { | |
808 return E_ACCESSDENIED; | |
809 } | |
810 | |
811 CString moniker_name(_T("Elevation:Administrator!new:")); | |
812 moniker_name += GuidToString(rclsid); | |
813 BIND_OPTS3 bo; | |
814 SetZero(bo); | |
815 bo.cbStruct = sizeof(bo); | |
816 bo.hwnd = hwnd; | |
817 bo.dwClassContext = CLSCTX_LOCAL_SERVER; | |
818 | |
819 return ::CoGetObject(moniker_name, &bo, riid, ppv); | |
820 } | |
821 | |
822 HRESULT System::IsPrivilegeEnabled(const TCHAR* privilege, bool* present) { | |
823 ASSERT1(privilege); | |
824 ASSERT1(present); | |
825 | |
826 *present = false; | |
827 | |
828 scoped_handle token; | |
829 if (!::OpenProcessToken(::GetCurrentProcess(), | |
830 TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, | |
831 address(token))) { | |
832 HRESULT hr = HRESULTFromLastError(); | |
833 UTIL_LOG(LEVEL_ERROR, (_T("[System::IsPrivilegeEnabled - failed to ") | |
834 _T("OpenProcessToken][0x%x]"), hr)); | |
835 return hr; | |
836 } | |
837 | |
838 LUID luid = {0}; | |
839 if (!::LookupPrivilegeValue(NULL, privilege, &luid)) { | |
840 HRESULT hr = HRESULTFromLastError(); | |
841 UTIL_LOG(LEVEL_ERROR, (_T("[System::IsPrivilegeEnabled - failed to") | |
842 _T("LookupPrivilegeValue][0x%x]"), hr)); | |
843 return hr; | |
844 } | |
845 | |
846 PRIVILEGE_SET required_privilege = {0}; | |
847 required_privilege.PrivilegeCount = 1; | |
848 required_privilege.Control = PRIVILEGE_SET_ALL_NECESSARY; | |
849 required_privilege.Privilege[0].Luid = luid; | |
850 | |
851 BOOL result = FALSE; | |
852 if (!::PrivilegeCheck(get(token), &required_privilege, &result)) { | |
853 HRESULT hr = HRESULTFromLastError(); | |
854 UTIL_LOG(LEVEL_ERROR, (_T("[System::IsPrivilegeEnabled - failed to") | |
855 _T("PrivilegeCheck][0x%x]"), hr)); | |
856 return hr; | |
857 } | |
858 | |
859 if (required_privilege.Privilege[0].Attributes & | |
860 SE_PRIVILEGE_USED_FOR_ACCESS) { | |
861 *present = true; | |
862 } | |
863 | |
864 return S_OK; | |
865 } | |
866 | |
867 // Attempts to adjust current process privileges. | |
868 // Only process running with administrator privileges will succeed. | |
869 HRESULT System::AdjustPrivilege(const TCHAR* privilege, bool enable) { | |
870 ASSERT1(privilege); | |
871 | |
872 scoped_handle token; | |
873 if (!::OpenProcessToken(::GetCurrentProcess(), | |
874 TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, | |
875 address(token))) { | |
876 HRESULT hr = HRESULTFromLastError(); | |
877 UTIL_LOG(LEVEL_ERROR, (_T("[System::AdjustPrivilege - failed to ") | |
878 _T("OpenProcessToken][0x%x]"), hr)); | |
879 return hr; | |
880 } | |
881 | |
882 LUID luid = {0}; | |
883 if (!::LookupPrivilegeValue(NULL, privilege, &luid)) { | |
884 HRESULT hr = HRESULTFromLastError(); | |
885 UTIL_LOG(LEVEL_ERROR, (_T("[System::AdjustPrivilege - failed to") | |
886 _T("LookupPrivilegeValue][0x%x]"), hr)); | |
887 return hr; | |
888 } | |
889 | |
890 TOKEN_PRIVILEGES privs; | |
891 privs.PrivilegeCount = 1; | |
892 privs.Privileges[0].Luid = luid; | |
893 privs.Privileges[0].Attributes = enable ? SE_PRIVILEGE_ENABLED : 0; | |
894 | |
895 if (!::AdjustTokenPrivileges(get(token), FALSE, &privs, 0, NULL, 0)) { | |
896 HRESULT hr = HRESULTFromLastError(); | |
897 UTIL_LOG(LEVEL_ERROR, (_T("[System::AdjustPrivilege - failed to ") | |
898 _T("AdjustTokenPrivileges][0x%x]"), hr)); | |
899 return hr; | |
900 } | |
901 | |
902 return S_OK; | |
903 } | |
904 | |
905 DWORD System::WTSGetActiveConsoleSessionId() { | |
906 typedef DWORD (* Fun)(); | |
907 | |
908 HINSTANCE hInst = ::GetModuleHandle(_T("kernel32.dll")); | |
909 ASSERT1(hInst); | |
910 Fun pfn = reinterpret_cast<Fun>(::GetProcAddress( | |
911 hInst, | |
912 "WTSGetActiveConsoleSessionId")); | |
913 return !pfn ? kInvalidSessionId : (*pfn)(); | |
914 } | |
915 | |
916 // Get the session the current process is running under | |
917 DWORD System::GetCurrentSessionId() { | |
918 DWORD session_id = kInvalidSessionId; | |
919 DWORD* session_id_ptr = NULL; | |
920 DWORD bytes_returned = 0; | |
921 | |
922 if (::WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE, | |
923 WTS_CURRENT_SESSION, | |
924 WTSSessionId, | |
925 reinterpret_cast<LPTSTR*>(&session_id_ptr), | |
926 &bytes_returned)) { | |
927 ASSERT1(bytes_returned == sizeof(*session_id_ptr)); | |
928 session_id = *session_id_ptr; | |
929 ::WTSFreeMemory(session_id_ptr); | |
930 UTIL_LOG(L6, (_T("[System::GetCurrentSessionId]") | |
931 _T("[session_id from ::WTSQuerySessionInformation][%d]"), | |
932 session_id)); | |
933 return session_id; | |
934 } | |
935 | |
936 // ::WTSQuerySessionInformation can fail if we are not running | |
937 // in a Terminal Services scenario, in which case, we use | |
938 // ::ProcessIdToSessionId() | |
939 if (::ProcessIdToSessionId(::GetCurrentProcessId(), &session_id)) { | |
940 UTIL_LOG(L6, (_T("[System::GetCurrentSessionId]") | |
941 _T("[session_id from ::ProcessIdToSessionId][%d]"), | |
942 session_id)); | |
943 return session_id; | |
944 } | |
945 | |
946 UTIL_LOG(LEVEL_ERROR, | |
947 (_T("[System::GetCurrentSessionId - both") | |
948 _T("::WTSQuerySessionInformation and ") | |
949 _T("::ProcessIdToSessionId failed][0x%x]"), | |
950 ::GetLastError())); | |
951 | |
952 return kInvalidSessionId; | |
953 } | |
954 | |
955 // Get the best guess as to the currently active session, or kInvalidSessionId | |
956 // if there is no active session. | |
957 DWORD System::GetActiveSessionId() { | |
958 // WTSGetActiveConsoleSessionId retrieves the Terminal Services session | |
959 // currently attached to the physical console. | |
960 DWORD active_session_id = WTSGetActiveConsoleSessionId(); | |
961 | |
962 if (IsSessionActive(active_session_id)) { | |
963 UTIL_LOG(L6, (_T("[System::GetActiveSessionId]") | |
964 _T("[Active session id from ::WTSGetActiveConsoleSessionId]") | |
965 _T("[%d]"), active_session_id)); | |
966 | |
967 return active_session_id; | |
968 } | |
969 | |
970 // WTSGetActiveConsoleSessionId works for FUS, but it does not work for TS | |
971 // servers where the current active session is always the console. We then use | |
972 // a different method as below. We get all the sessions that are present on | |
973 // the system, to see if we can find an active session. | |
974 active_session_id = kInvalidSessionId; | |
975 WTS_SESSION_INFO* session_info = NULL; | |
976 DWORD num_sessions = 0; | |
977 if (::WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, | |
978 &session_info, &num_sessions)) { | |
979 // Pick the first active session we can find | |
980 for (DWORD i = 0 ; i < num_sessions; ++i) { | |
981 if (session_info[i].State == WTSActive) { | |
982 // There is a user logged on to the WinStation associated with the | |
983 // session. | |
984 active_session_id = session_info[i].SessionId; | |
985 break; | |
986 } | |
987 } | |
988 | |
989 ::WTSFreeMemory(session_info); | |
990 UTIL_LOG(L6, (_T("[System::GetActiveSessionId]") | |
991 _T("[Active session id from ::WTSEnumerateSessions][0x%x]"), | |
992 active_session_id)); | |
993 | |
994 return active_session_id; | |
995 } | |
996 | |
997 UTIL_LOG(LEVEL_ERROR, | |
998 (_T("[System::GetActiveSessionId - ") | |
999 _T("Both ::WTSGetActiveConsoleSessionId and ::WTSEnumerateSessions ") | |
1000 _T("failed][0x%x]"), | |
1001 ::GetLastError())); | |
1002 | |
1003 return kInvalidSessionId; | |
1004 } | |
1005 | |
1006 // Is there a user logged on and active in the specified session? | |
1007 bool System::IsSessionActive(DWORD session_id) { | |
1008 if (kInvalidSessionId == session_id) { | |
1009 return false; | |
1010 } | |
1011 | |
1012 WTS_CONNECTSTATE_CLASS wts_connect_state = WTSDisconnected; | |
1013 WTS_CONNECTSTATE_CLASS* ptr_wts_connect_state = NULL; | |
1014 DWORD bytes_returned = 0; | |
1015 if (::WTSQuerySessionInformation( | |
1016 WTS_CURRENT_SERVER_HANDLE, | |
1017 session_id, | |
1018 WTSConnectState, | |
1019 reinterpret_cast<LPTSTR*>(&ptr_wts_connect_state), | |
1020 &bytes_returned)) { | |
1021 ASSERT1(bytes_returned == sizeof(*ptr_wts_connect_state)); | |
1022 wts_connect_state = *ptr_wts_connect_state; | |
1023 ::WTSFreeMemory(ptr_wts_connect_state); | |
1024 | |
1025 UTIL_LOG(L6, (_T("[System::IsSessionActive]") | |
1026 _T("[wts_connect_state %d]"), wts_connect_state)); | |
1027 return WTSActive == wts_connect_state; | |
1028 } | |
1029 | |
1030 UTIL_LOG(LE, (_T("[WTSQuerySessionInformation failed][0x%x]"), | |
1031 ::GetLastError())); | |
1032 return false; | |
1033 } | |
1034 | |
1035 // Is the current process running under WinSta0 | |
1036 bool System::IsCurrentProcessInteractive() { | |
1037 // Use a non-scoped handle, since a handle retrieved via | |
1038 // ::GetProcessWindowStation() should not be closed. | |
1039 HWINSTA handle_window_station(::GetProcessWindowStation()); | |
1040 DWORD len = 0; | |
1041 CString str_window_station; | |
1042 | |
1043 if (!handle_window_station || handle_window_station == INVALID_HANDLE_VALUE) { | |
1044 UTIL_LOG(LEVEL_ERROR, | |
1045 (_T("[System::IsCurrentProcessInteractive - ") | |
1046 _T("::GetProcessWindowStation() failed (%d)]"), | |
1047 ::GetLastError())); | |
1048 return false; | |
1049 } | |
1050 | |
1051 if (!::GetUserObjectInformation(handle_window_station, | |
1052 UOI_NAME, | |
1053 CStrBuf(str_window_station, MAX_PATH), | |
1054 MAX_PATH, | |
1055 &len)) { | |
1056 UTIL_LOG(LEVEL_ERROR, | |
1057 (_T("[System::IsCurrentProcessInteractive - ") | |
1058 _T("::GetUserObjectInfoformation(hWinSta) failed (%d)]"), | |
1059 ::GetLastError())); | |
1060 return false; | |
1061 } | |
1062 | |
1063 UTIL_LOG(L6, (_T("[System::IsCurrentProcessInteractive]") | |
1064 _T("[WindowStation name][%s]"), | |
1065 str_window_station)); | |
1066 return (str_window_station == _T("WinSta0")); | |
1067 } | |
1068 | |
1069 // is the current process running under WinSta0 for the currently active session | |
1070 bool System::IsCurrentProcessActiveAndInteractive() { | |
1071 return IsSessionActive(GetCurrentSessionId()) && | |
1072 IsCurrentProcessInteractive(); | |
1073 } | |
1074 | |
1075 bool System::IsRunningOnBatteries() { | |
1076 SYSTEM_POWER_STATUS system_power_status = {0}; | |
1077 if (::GetSystemPowerStatus(&system_power_status)) { | |
1078 bool has_battery = !(system_power_status.BatteryFlag & 128); | |
1079 bool ac_status_offline = system_power_status.ACLineStatus == 0; | |
1080 return ac_status_offline && has_battery; | |
1081 } | |
1082 return false; | |
1083 } | |
1084 | |
1085 } // namespace omaha | |
1086 | |
OLD | NEW |