VirtualBox

source: vbox/trunk/src/VBox/Additions/common/VBoxService/VBoxServiceVMInfo-win.cpp@ 57372

Last change on this file since 57372 was 57372, checked in by vboxsync, 9 years ago

scm: fixes in previous cleanup run.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 52.0 KB
Line 
1/* $Id: VBoxServiceVMInfo-win.cpp 57372 2015-08-14 22:01:25Z vboxsync $ */
2/** @file
3 * VBoxService - Virtual Machine Information for the Host, Windows specifics.
4 */
5
6/*
7 * Copyright (C) 2009-2015 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0502
23# undef _WIN32_WINNT
24# define _WIN32_WINNT 0x0502 /* CachedRemoteInteractive in recent SDKs. */
25#endif
26#include <Windows.h>
27#include <wtsapi32.h> /* For WTS* calls. */
28#include <psapi.h> /* EnumProcesses. */
29#include <Ntsecapi.h> /* Needed for process security information. */
30
31#include <iprt/assert.h>
32#include <iprt/ldr.h>
33#include <iprt/localipc.h>
34#include <iprt/mem.h>
35#include <iprt/thread.h>
36#include <iprt/string.h>
37#include <iprt/semaphore.h>
38#include <iprt/system.h>
39#include <iprt/time.h>
40#include <VBox/VBoxGuestLib.h>
41#include "VBoxServiceInternal.h"
42#include "VBoxServiceUtils.h"
43#include "VBoxServiceVMInfo.h"
44#include "../../WINNT/VBoxTray/VBoxTrayMsg.h" /* For IPC. */
45
46static uint32_t s_uDebugGuestPropClientID = 0;
47static uint32_t s_uDebugIter = 0;
48/** Whether to skip the logged-in user detection over RDP or not.
49 * See notes in this section why we might want to skip this. */
50static bool s_fSkipRDPDetection = false;
51
52
53/*********************************************************************************************************************************
54* Structures and Typedefs *
55*********************************************************************************************************************************/
56/** Structure for storing the looked up user information. */
57typedef struct VBOXSERVICEVMINFOUSER
58{
59 WCHAR wszUser[_MAX_PATH];
60 WCHAR wszAuthenticationPackage[_MAX_PATH];
61 WCHAR wszLogonDomain[_MAX_PATH];
62 /** Number of assigned user processes. */
63 ULONG ulNumProcs;
64 /** Last (highest) session ID. This
65 * is needed for distinguishing old session
66 * process counts from new (current) session
67 * ones. */
68 ULONG ulLastSession;
69} VBOXSERVICEVMINFOUSER, *PVBOXSERVICEVMINFOUSER;
70
71/** Structure for the file information lookup. */
72typedef struct VBOXSERVICEVMINFOFILE
73{
74 char *pszFilePath;
75 char *pszFileName;
76} VBOXSERVICEVMINFOFILE, *PVBOXSERVICEVMINFOFILE;
77
78/** Structure for process information lookup. */
79typedef struct VBOXSERVICEVMINFOPROC
80{
81 /** The PID. */
82 DWORD id;
83 /** The SID. */
84 PSID pSid;
85 /** The LUID. */
86 LUID luid;
87 /** Interactive process. */
88 bool fInteractive;
89} VBOXSERVICEVMINFOPROC, *PVBOXSERVICEVMINFOPROC;
90
91
92/*******************************************************************************
93* Prototypes
94*******************************************************************************/
95uint32_t VBoxServiceVMInfoWinSessionHasProcesses(PLUID pSession, PVBOXSERVICEVMINFOPROC const paProcs, DWORD cProcs);
96bool VBoxServiceVMInfoWinIsLoggedIn(PVBOXSERVICEVMINFOUSER a_pUserInfo, PLUID a_pSession);
97int VBoxServiceVMInfoWinProcessesEnumerate(PVBOXSERVICEVMINFOPROC *ppProc, DWORD *pdwCount);
98void VBoxServiceVMInfoWinProcessesFree(DWORD cProcs, PVBOXSERVICEVMINFOPROC paProcs);
99int vboxServiceVMInfoWinWriteLastInput(PVBOXSERVICEVEPROPCACHE pCache, const char *pszUser, const char *pszDomain);
100
101typedef BOOL WINAPI FNQUERYFULLPROCESSIMAGENAME(HANDLE, DWORD, LPWSTR, PDWORD);
102typedef FNQUERYFULLPROCESSIMAGENAME *PFNQUERYFULLPROCESSIMAGENAME;
103
104
105#ifndef TARGET_NT4
106
107static bool vboxServiceVMInfoSession0Separation(void)
108{
109 /** @todo Only do this once. Later. */
110 OSVERSIONINFOEX OSInfoEx;
111 RT_ZERO(OSInfoEx);
112 OSInfoEx.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
113 if ( !GetVersionEx((LPOSVERSIONINFO) &OSInfoEx)
114 || OSInfoEx.dwPlatformId != VER_PLATFORM_WIN32_NT)
115 {
116 /* Platform other than NT (e.g. Win9x) not supported. */
117 return false;
118 }
119
120 if ( OSInfoEx.dwMajorVersion >= 6
121 && OSInfoEx.dwMinorVersion >= 0)
122 {
123 return true;
124 }
125
126 return false;
127}
128
129/**
130 * Retrieves the module name of a given process.
131 *
132 * @return IPRT status code.
133 */
134static int VBoxServiceVMInfoWinProcessesGetModuleNameA(PVBOXSERVICEVMINFOPROC const pProc,
135 PRTUTF16 *ppszName)
136{
137 AssertPtrReturn(pProc, VERR_INVALID_POINTER);
138 AssertPtrReturn(ppszName, VERR_INVALID_POINTER);
139
140 /** @todo Only do this once. Later. */
141 OSVERSIONINFOEX OSInfoEx;
142 RT_ZERO(OSInfoEx);
143 OSInfoEx.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
144 if ( !GetVersionEx((LPOSVERSIONINFO) &OSInfoEx)
145 || OSInfoEx.dwPlatformId != VER_PLATFORM_WIN32_NT)
146 {
147 /* Platform other than NT (e.g. Win9x) not supported. */
148 return VERR_NOT_SUPPORTED;
149 }
150
151 int rc = VINF_SUCCESS;
152
153 DWORD dwFlags = PROCESS_QUERY_INFORMATION | PROCESS_VM_READ;
154 if (OSInfoEx.dwMajorVersion >= 6 /* Vista or later */)
155 dwFlags = 0x1000; /* = PROCESS_QUERY_LIMITED_INFORMATION; less privileges needed. */
156
157 HANDLE h = OpenProcess(dwFlags, FALSE, pProc->id);
158 if (h == NULL)
159 {
160 DWORD dwErr = GetLastError();
161 if (g_cVerbosity)
162 VBoxServiceError("Unable to open process with PID=%ld, error=%ld\n",
163 pProc->id, dwErr);
164 rc = RTErrConvertFromWin32(dwErr);
165 }
166 else
167 {
168 /* Since GetModuleFileNameEx has trouble with cross-bitness stuff (32-bit apps cannot query 64-bit
169 apps and vice verse) we have to use a different code path for Vista and up. */
170 WCHAR wszName[_1K];
171 DWORD dwLen = sizeof(wszName);
172
173 /* Note: For 2000 + NT4 we might just use GetModuleFileNameW() instead. */
174 if (OSInfoEx.dwMajorVersion >= 6 /* Vista or later */)
175 {
176 /* Loading the module and getting the symbol for each and every process is expensive
177 * -- since this function (at the moment) only is used for debugging purposes it's okay. */
178 PFNQUERYFULLPROCESSIMAGENAME pfnQueryFullProcessImageName;
179 pfnQueryFullProcessImageName = (PFNQUERYFULLPROCESSIMAGENAME)
180 RTLdrGetSystemSymbol("kernel32.dll", "QueryFullProcessImageNameW");
181 if (pfnQueryFullProcessImageName)
182 {
183 if (!pfnQueryFullProcessImageName(h, 0 /*PROCESS_NAME_NATIVE*/, wszName, &dwLen))
184 rc = VERR_ACCESS_DENIED;
185 }
186 }
187 else
188 {
189 if (!GetModuleFileNameExW(h, NULL /* Get main executable */, wszName, dwLen))
190 rc = VERR_ACCESS_DENIED;
191 }
192
193 if ( RT_FAILURE(rc)
194 && g_cVerbosity > 3)
195 {
196 VBoxServiceError("Unable to retrieve process name for PID=%ld, error=%ld\n",
197 pProc->id, GetLastError());
198 }
199 else
200 {
201 PRTUTF16 pszName = RTUtf16Dup(wszName);
202 if (pszName)
203 *ppszName = pszName;
204 else
205 rc = VERR_NO_MEMORY;
206 }
207
208 CloseHandle(h);
209 }
210
211 return rc;
212}
213
214
215/**
216 * Fills in more data for a process.
217 *
218 * @returns VBox status code.
219 * @param pProc The process structure to fill data into.
220 * @param tkClass The kind of token information to get.
221 */
222static int VBoxServiceVMInfoWinProcessesGetTokenInfo(PVBOXSERVICEVMINFOPROC pProc,
223 TOKEN_INFORMATION_CLASS tkClass)
224{
225 AssertPtrReturn(pProc, VERR_INVALID_POINTER);
226
227 DWORD dwErr = ERROR_SUCCESS;
228 HANDLE h = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pProc->id);
229 if (h == NULL)
230 {
231 dwErr = GetLastError();
232 if (g_cVerbosity > 4)
233 VBoxServiceError("Unable to open process with PID=%ld, error=%ld\n",
234 pProc->id, dwErr);
235 return RTErrConvertFromWin32(dwErr);
236 }
237
238 int rc = VINF_SUCCESS;
239 HANDLE hToken;
240 if (OpenProcessToken(h, TOKEN_QUERY, &hToken))
241 {
242 void *pvTokenInfo = NULL;
243 DWORD dwTokenInfoSize;
244 switch (tkClass)
245 {
246 case TokenStatistics:
247 dwTokenInfoSize = sizeof(TOKEN_STATISTICS);
248 pvTokenInfo = HeapAlloc(GetProcessHeap(),
249 HEAP_ZERO_MEMORY, dwTokenInfoSize);
250 AssertPtr(pvTokenInfo);
251 break;
252
253 case TokenGroups:
254 dwTokenInfoSize = 0;
255 /* Allocation will follow in a second step. */
256 break;
257
258 case TokenUser:
259 dwTokenInfoSize = 0;
260 /* Allocation will follow in a second step. */
261 break;
262
263 default:
264 VBoxServiceError("Token class not implemented: %ld\n", tkClass);
265 rc = VERR_NOT_IMPLEMENTED;
266 break;
267 }
268
269 if (RT_SUCCESS(rc))
270 {
271 DWORD dwRetLength;
272 if (!GetTokenInformation(hToken, tkClass, pvTokenInfo, dwTokenInfoSize, &dwRetLength))
273 {
274 dwErr = GetLastError();
275 if (dwErr == ERROR_INSUFFICIENT_BUFFER)
276 {
277 dwErr = ERROR_SUCCESS;
278
279 switch (tkClass)
280 {
281 case TokenGroups:
282 pvTokenInfo = (PTOKEN_GROUPS)HeapAlloc(GetProcessHeap(),
283 HEAP_ZERO_MEMORY, dwRetLength);
284 if (!pvTokenInfo)
285 dwErr = GetLastError();
286 dwTokenInfoSize = dwRetLength;
287 break;
288
289 case TokenUser:
290 pvTokenInfo = (PTOKEN_USER)HeapAlloc(GetProcessHeap(),
291 HEAP_ZERO_MEMORY, dwRetLength);
292 if (!pvTokenInfo)
293 dwErr = GetLastError();
294 dwTokenInfoSize = dwRetLength;
295 break;
296
297 default:
298 AssertMsgFailed(("Re-allocating of token information for token class not implemented\n"));
299 break;
300 }
301
302 if (dwErr == ERROR_SUCCESS)
303 {
304 if (!GetTokenInformation(hToken, tkClass, pvTokenInfo, dwTokenInfoSize, &dwRetLength))
305 dwErr = GetLastError();
306 }
307 }
308 }
309
310 if (dwErr == ERROR_SUCCESS)
311 {
312 rc = VINF_SUCCESS;
313
314 switch (tkClass)
315 {
316 case TokenStatistics:
317 {
318 PTOKEN_STATISTICS pStats = (PTOKEN_STATISTICS)pvTokenInfo;
319 AssertPtr(pStats);
320 memcpy(&pProc->luid, &pStats->AuthenticationId, sizeof(LUID));
321 /** @todo Add more information of TOKEN_STATISTICS as needed. */
322 break;
323 }
324
325 case TokenGroups:
326 {
327 pProc->fInteractive = false;
328
329 SID_IDENTIFIER_AUTHORITY sidAuthNT = SECURITY_NT_AUTHORITY;
330 PSID pSidInteractive = NULL; /* S-1-5-4 */
331 if (!AllocateAndInitializeSid(&sidAuthNT, 1, 4, 0, 0, 0, 0, 0, 0, 0, &pSidInteractive))
332 dwErr = GetLastError();
333
334 PSID pSidLocal = NULL; /* S-1-2-0 */
335 if (dwErr == ERROR_SUCCESS)
336 {
337 SID_IDENTIFIER_AUTHORITY sidAuthLocal = SECURITY_LOCAL_SID_AUTHORITY;
338 if (!AllocateAndInitializeSid(&sidAuthLocal, 1, 0, 0, 0, 0, 0, 0, 0, 0, &pSidLocal))
339 dwErr = GetLastError();
340 }
341
342 if (dwErr == ERROR_SUCCESS)
343 {
344 PTOKEN_GROUPS pGroups = (PTOKEN_GROUPS)pvTokenInfo;
345 AssertPtr(pGroups);
346 for (DWORD i = 0; i < pGroups->GroupCount; i++)
347 {
348 if ( EqualSid(pGroups->Groups[i].Sid, pSidInteractive)
349 || EqualSid(pGroups->Groups[i].Sid, pSidLocal)
350 || pGroups->Groups[i].Attributes & SE_GROUP_LOGON_ID)
351 {
352 pProc->fInteractive = true;
353 break;
354 }
355 }
356 }
357
358 if (pSidInteractive)
359 FreeSid(pSidInteractive);
360 if (pSidLocal)
361 FreeSid(pSidLocal);
362 break;
363 }
364
365 case TokenUser:
366 {
367 PTOKEN_USER pUser = (PTOKEN_USER)pvTokenInfo;
368 AssertPtr(pUser);
369
370 DWORD dwLength = GetLengthSid(pUser->User.Sid);
371 Assert(dwLength);
372 if (dwLength)
373 {
374 pProc->pSid = (PSID)HeapAlloc(GetProcessHeap(),
375 HEAP_ZERO_MEMORY, dwLength);
376 AssertPtr(pProc->pSid);
377 if (CopySid(dwLength, pProc->pSid, pUser->User.Sid))
378 {
379 if (!IsValidSid(pProc->pSid))
380 dwErr = ERROR_INVALID_NAME;
381 }
382 else
383 dwErr = GetLastError();
384 }
385 else
386 dwErr = ERROR_NO_DATA;
387
388 if (dwErr != ERROR_SUCCESS)
389 {
390 VBoxServiceError("Error retrieving SID of process PID=%ld: %ld\n",
391 pProc->id, dwErr);
392 if (pProc->pSid)
393 {
394 HeapFree(GetProcessHeap(), 0 /* Flags */, pProc->pSid);
395 pProc->pSid = NULL;
396 }
397 }
398 break;
399 }
400
401 default:
402 AssertMsgFailed(("Unhandled token information class\n"));
403 break;
404 }
405 }
406
407 if (pvTokenInfo)
408 HeapFree(GetProcessHeap(), 0 /* Flags */, pvTokenInfo);
409 }
410 CloseHandle(hToken);
411 }
412 else
413 dwErr = GetLastError();
414
415 if (dwErr != ERROR_SUCCESS)
416 {
417 if (g_cVerbosity)
418 VBoxServiceError("Unable to query token information for PID=%ld, error=%ld\n",
419 pProc->id, dwErr);
420 rc = RTErrConvertFromWin32(dwErr);
421 }
422
423 CloseHandle(h);
424 return rc;
425}
426
427
428/**
429 * Enumerate all the processes in the system and get the logon user IDs for
430 * them.
431 *
432 * @returns VBox status code.
433 * @param ppaProcs Where to return the process snapshot. This must be
434 * freed by calling VBoxServiceVMInfoWinProcessesFree.
435 *
436 * @param pcProcs Where to store the returned process count.
437 */
438int VBoxServiceVMInfoWinProcessesEnumerate(PVBOXSERVICEVMINFOPROC *ppaProcs, PDWORD pcProcs)
439{
440 AssertPtr(ppaProcs);
441 AssertPtr(pcProcs);
442
443 /*
444 * Call EnumProcesses with an increasingly larger buffer until it all fits
445 * or we think something is screwed up.
446 */
447 DWORD cProcesses = 64;
448 PDWORD paPID = NULL;
449 int rc = VINF_SUCCESS;
450 do
451 {
452 /* Allocate / grow the buffer first. */
453 cProcesses *= 2;
454 void *pvNew = RTMemRealloc(paPID, cProcesses * sizeof(DWORD));
455 if (!pvNew)
456 {
457 rc = VERR_NO_MEMORY;
458 break;
459 }
460 paPID = (PDWORD)pvNew;
461
462 /* Query the processes. Not the cbRet == buffer size means there could be more work to be done. */
463 DWORD cbRet;
464 if (!EnumProcesses(paPID, cProcesses * sizeof(DWORD), &cbRet))
465 {
466 rc = RTErrConvertFromWin32(GetLastError());
467 break;
468 }
469 if (cbRet < cProcesses * sizeof(DWORD))
470 {
471 cProcesses = cbRet / sizeof(DWORD);
472 break;
473 }
474 } while (cProcesses <= _32K); /* Should be enough; see: http://blogs.technet.com/markrussinovich/archive/2009/07/08/3261309.aspx */
475
476 if (RT_SUCCESS(rc))
477 {
478 /*
479 * Allocate out process structures and fill data into them.
480 * We currently only try lookup their LUID's.
481 */
482 PVBOXSERVICEVMINFOPROC paProcs;
483 paProcs = (PVBOXSERVICEVMINFOPROC)RTMemAllocZ(cProcesses * sizeof(VBOXSERVICEVMINFOPROC));
484 if (paProcs)
485 {
486 for (DWORD i = 0; i < cProcesses; i++)
487 {
488 paProcs[i].id = paPID[i];
489 paProcs[i].pSid = NULL;
490
491 int rc2 = VBoxServiceVMInfoWinProcessesGetTokenInfo(&paProcs[i], TokenUser);
492 if (RT_FAILURE(rc2) && g_cVerbosity)
493 VBoxServiceError("Get token class \"user\" for process %ld failed, rc=%Rrc\n",
494 paProcs[i].id, rc2);
495
496 rc2 = VBoxServiceVMInfoWinProcessesGetTokenInfo(&paProcs[i], TokenGroups);
497 if (RT_FAILURE(rc2) && g_cVerbosity)
498 VBoxServiceError("Get token class \"groups\" for process %ld failed, rc=%Rrc\n",
499 paProcs[i].id, rc2);
500
501 rc2 = VBoxServiceVMInfoWinProcessesGetTokenInfo(&paProcs[i], TokenStatistics);
502 if (RT_FAILURE(rc2) && g_cVerbosity)
503 VBoxServiceError("Get token class \"statistics\" for process %ld failed, rc=%Rrc\n",
504 paProcs[i].id, rc2);
505 }
506
507 /* Save number of processes */
508 if (RT_SUCCESS(rc))
509 {
510 *pcProcs = cProcesses;
511 *ppaProcs = paProcs;
512 }
513 else
514 VBoxServiceVMInfoWinProcessesFree(cProcesses, paProcs);
515 }
516 else
517 rc = VERR_NO_MEMORY;
518 }
519
520 RTMemFree(paPID);
521 return rc;
522}
523
524/**
525 * Frees the process structures returned by
526 * VBoxServiceVMInfoWinProcessesEnumerate() before.
527 *
528 * @param paProcs What
529 */
530void VBoxServiceVMInfoWinProcessesFree(DWORD cProcs, PVBOXSERVICEVMINFOPROC paProcs)
531{
532 for (DWORD i = 0; i < cProcs; i++)
533 {
534 if (paProcs[i].pSid)
535 {
536 HeapFree(GetProcessHeap(), 0 /* Flags */, paProcs[i].pSid);
537 paProcs[i].pSid = NULL;
538 }
539
540 }
541 RTMemFree(paProcs);
542}
543
544/**
545 * Determines whether the specified session has processes on the system.
546 *
547 * @returns Number of processes found for a specified session.
548 * @param pSession The current user's SID.
549 * @param paProcs The process snapshot.
550 * @param cProcs The number of processes in the snaphot.
551 * @param puSession Looked up session number. Optional.
552 */
553uint32_t VBoxServiceVMInfoWinSessionHasProcesses(PLUID pSession,
554 PVBOXSERVICEVMINFOPROC const paProcs, DWORD cProcs,
555 PULONG puTerminalSession)
556{
557 if (!pSession)
558 {
559 VBoxServiceVerbose(1, "Session became invalid while enumerating!\n");
560 return 0;
561 }
562
563 PSECURITY_LOGON_SESSION_DATA pSessionData = NULL;
564 NTSTATUS rcNt = LsaGetLogonSessionData(pSession, &pSessionData);
565 if (rcNt != STATUS_SUCCESS)
566 {
567 VBoxServiceError("Could not get logon session data! rcNt=%#x\n", rcNt);
568 return 0;
569 }
570
571 if (!IsValidSid(pSessionData->Sid))
572 {
573 VBoxServiceError("User SID=%p is not valid\n", pSessionData->Sid);
574 if (pSessionData)
575 LsaFreeReturnBuffer(pSessionData);
576 return 0;
577 }
578
579 int rc = VINF_SUCCESS;
580
581 /*
582 * Even if a user seems to be logged in, it could be a stale/orphaned logon
583 * session. So check if we have some processes bound to it by comparing the
584 * session <-> process LUIDs.
585 */
586 uint32_t cNumProcs = 0;
587
588 for (DWORD i = 0; i < cProcs; i++)
589 {
590 PSID pProcSID = paProcs[i].pSid;
591 if ( RT_SUCCESS(rc)
592 && pProcSID
593 && IsValidSid(pProcSID))
594 {
595 if (EqualSid(pSessionData->Sid, paProcs[i].pSid))
596 {
597 if (g_cVerbosity)
598 {
599 PRTUTF16 pszName;
600 int rc2 = VBoxServiceVMInfoWinProcessesGetModuleNameA(&paProcs[i], &pszName);
601 VBoxServiceVerbose(4, "Session %RU32: PID=%ld (fInt=%RTbool): %ls\n",
602 pSessionData->Session, paProcs[i].id, paProcs[i].fInteractive,
603 RT_SUCCESS(rc2) ? pszName : L"<Unknown>");
604 if (RT_SUCCESS(rc2))
605 RTUtf16Free(pszName);
606 }
607
608 if (paProcs[i].fInteractive)
609 {
610 cNumProcs++;
611 if (!g_cVerbosity) /* We want a bit more info on higher verbosity. */
612 break;
613 }
614 }
615 }
616 }
617
618 if (puTerminalSession)
619 *puTerminalSession = pSessionData->Session;
620
621 LsaFreeReturnBuffer(pSessionData);
622
623 return cNumProcs;
624}
625
626
627/**
628 * Save and noisy string copy.
629 *
630 * @param pwszDst Destination buffer.
631 * @param cbDst Size in bytes - not WCHAR count!
632 * @param pSrc Source string.
633 * @param pszWhat What this is. For the log.
634 */
635static void VBoxServiceVMInfoWinSafeCopy(PWCHAR pwszDst, size_t cbDst, LSA_UNICODE_STRING const *pSrc, const char *pszWhat)
636{
637 Assert(RT_ALIGN(cbDst, sizeof(WCHAR)) == cbDst);
638
639 size_t cbCopy = pSrc->Length;
640 if (cbCopy + sizeof(WCHAR) > cbDst)
641 {
642 VBoxServiceVerbose(0, "%s is too long - %u bytes, buffer %u bytes! It will be truncated.\n",
643 pszWhat, cbCopy, cbDst);
644 cbCopy = cbDst - sizeof(WCHAR);
645 }
646 if (cbCopy)
647 memcpy(pwszDst, pSrc->Buffer, cbCopy);
648 pwszDst[cbCopy / sizeof(WCHAR)] = '\0';
649}
650
651
652/**
653 * Detects whether a user is logged on.
654 *
655 * @returns true if logged in, false if not (or error).
656 * @param pUserInfo Where to return the user information.
657 * @param pSession The session to check.
658 */
659bool VBoxServiceVMInfoWinIsLoggedIn(PVBOXSERVICEVMINFOUSER pUserInfo, PLUID pSession)
660{
661 AssertPtrReturn(pUserInfo, false);
662 if (!pSession)
663 return false;
664
665 PSECURITY_LOGON_SESSION_DATA pSessionData = NULL;
666 NTSTATUS rcNt = LsaGetLogonSessionData(pSession, &pSessionData);
667 if (rcNt != STATUS_SUCCESS)
668 {
669 ULONG ulError = LsaNtStatusToWinError(rcNt);
670 switch (ulError)
671 {
672 case ERROR_NOT_ENOUGH_MEMORY:
673 /* If we don't have enough memory it's hard to judge whether the specified user
674 * is logged in or not, so just assume he/she's not. */
675 VBoxServiceVerbose(3, "Not enough memory to retrieve logon session data!\n");
676 break;
677
678 case ERROR_NO_SUCH_LOGON_SESSION:
679 /* Skip session data which is not valid anymore because it may have been
680 * already terminated. */
681 break;
682
683 default:
684 VBoxServiceError("LsaGetLogonSessionData failed with error %u\n", ulError);
685 break;
686 }
687 if (pSessionData)
688 LsaFreeReturnBuffer(pSessionData);
689 return false;
690 }
691 if (!pSessionData)
692 {
693 VBoxServiceError("Invalid logon session data!\n");
694 return false;
695 }
696
697 VBoxServiceVerbose(3, "Session data: Name=%ls, SessionID=%RU32, LogonID=%ld,%ld, LogonType=%ld\n",
698 pSessionData->UserName.Buffer,
699 pSessionData->Session,
700 pSessionData->LogonId.HighPart, pSessionData->LogonId.LowPart,
701 pSessionData->LogonType);
702
703 if (vboxServiceVMInfoSession0Separation())
704 {
705 /* Starting at Windows Vista user sessions begin with session 1, so
706 * ignore (stale) session 0 users. */
707 if ( pSessionData->Session == 0
708 /* Also check the logon time. */
709 || pSessionData->LogonTime.QuadPart == 0)
710 {
711 LsaFreeReturnBuffer(pSessionData);
712 return false;
713 }
714 }
715
716 /*
717 * Only handle users which can login interactively or logged in
718 * remotely over native RDP.
719 */
720 bool fFoundUser = false;
721 DWORD dwErr = NO_ERROR;
722 if ( IsValidSid(pSessionData->Sid)
723 && ( (SECURITY_LOGON_TYPE)pSessionData->LogonType == Interactive
724 || (SECURITY_LOGON_TYPE)pSessionData->LogonType == RemoteInteractive
725 /* Note: We also need CachedInteractive in case Windows cached the credentials
726 * or just wants to reuse them! */
727 || (SECURITY_LOGON_TYPE)pSessionData->LogonType == CachedInteractive))
728 {
729 VBoxServiceVerbose(3, "Session LogonType=%ld is supported -- looking up SID + type ...\n",
730 pSessionData->LogonType);
731
732 /*
733 * Copy out relevant data.
734 */
735 VBoxServiceVMInfoWinSafeCopy(pUserInfo->wszUser, sizeof(pUserInfo->wszUser),
736 &pSessionData->UserName, "User name");
737 VBoxServiceVMInfoWinSafeCopy(pUserInfo->wszAuthenticationPackage, sizeof(pUserInfo->wszAuthenticationPackage),
738 &pSessionData->AuthenticationPackage, "Authentication pkg name");
739 VBoxServiceVMInfoWinSafeCopy(pUserInfo->wszLogonDomain, sizeof(pUserInfo->wszLogonDomain),
740 &pSessionData->LogonDomain, "Logon domain name");
741
742 TCHAR szOwnerName[_MAX_PATH] = { 0 };
743 DWORD dwOwnerNameSize = sizeof(szOwnerName);
744 TCHAR szDomainName[_MAX_PATH] = { 0 };
745 DWORD dwDomainNameSize = sizeof(szDomainName);
746 SID_NAME_USE enmOwnerType = SidTypeInvalid;
747 if (!LookupAccountSid(NULL,
748 pSessionData->Sid,
749 szOwnerName,
750 &dwOwnerNameSize,
751 szDomainName,
752 &dwDomainNameSize,
753 &enmOwnerType))
754 {
755 DWORD dwErr = GetLastError();
756 /*
757 * If a network time-out prevents the function from finding the name or
758 * if a SID that does not have a corresponding account name (such as a
759 * logon SID that identifies a logon session), we get ERROR_NONE_MAPPED
760 * here that we just skip.
761 */
762 if (dwErr != ERROR_NONE_MAPPED)
763 VBoxServiceError("Failed looking up account info for user=%ls, error=$ld!\n",
764 pUserInfo->wszUser, dwErr);
765 }
766 else
767 {
768 if (enmOwnerType == SidTypeUser) /* Only recognize users; we don't care about the rest! */
769 {
770 VBoxServiceVerbose(3, "Account User=%ls, Session=%ld, LogonID=%ld,%ld, AuthPkg=%ls, Domain=%ls\n",
771 pUserInfo->wszUser, pSessionData->Session, pSessionData->LogonId.HighPart,
772 pSessionData->LogonId.LowPart, pUserInfo->wszAuthenticationPackage,
773 pUserInfo->wszLogonDomain);
774
775 /**
776 * Note: On certain Windows OSes WTSQuerySessionInformation leaks memory when used
777 * under a heavy stress situation. There are hotfixes available from Microsoft.
778 *
779 * See: http://support.microsoft.com/kb/970910
780 */
781 if (!s_fSkipRDPDetection)
782 {
783 /** @todo Only do this once. Later. */
784 OSVERSIONINFOEX OSInfoEx;
785 RT_ZERO(OSInfoEx);
786 OSInfoEx.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
787
788 /* Skip RDP detection on non-NT systems. */
789 if ( !GetVersionEx((LPOSVERSIONINFO) &OSInfoEx)
790 || OSInfoEx.dwPlatformId != VER_PLATFORM_WIN32_NT)
791 {
792 s_fSkipRDPDetection = true;
793 }
794 /* Skip RDP detection on Windows 2000.
795 * For Windows 2000 however we don't have any hotfixes, so just skip the
796 * RDP detection in any case. */
797 if ( OSInfoEx.dwMajorVersion == 5
798 && OSInfoEx.dwMinorVersion == 0)
799 {
800 s_fSkipRDPDetection = true;
801 }
802
803 if (s_fSkipRDPDetection)
804 VBoxServiceVerbose(0, "Detection of logged-in users via RDP is disabled\n");
805 }
806
807 if (!s_fSkipRDPDetection)
808 {
809 /* Detect RDP sessions as well. */
810 LPTSTR pBuffer = NULL;
811 DWORD cbRet = 0;
812 int iState = -1;
813 if (WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE,
814 pSessionData->Session,
815 WTSConnectState,
816 &pBuffer,
817 &cbRet))
818 {
819 if (cbRet)
820 iState = *pBuffer;
821 VBoxServiceVerbose(3, "Account User=%ls, WTSConnectState=%d (%ld)\n",
822 pUserInfo->wszUser, iState, cbRet);
823 if ( iState == WTSActive /* User logged on to WinStation. */
824 || iState == WTSShadow /* Shadowing another WinStation. */
825 || iState == WTSDisconnected) /* WinStation logged on without client. */
826 {
827 /** @todo On Vista and W2K, always "old" user name are still
828 * there. Filter out the old one! */
829 VBoxServiceVerbose(3, "Account User=%ls using TCS/RDP, state=%d \n",
830 pUserInfo->wszUser, iState);
831 fFoundUser = true;
832 }
833 if (pBuffer)
834 WTSFreeMemory(pBuffer);
835 }
836 else
837 {
838 DWORD dwLastErr = GetLastError();
839 switch (dwLastErr)
840 {
841 /*
842 * Terminal services don't run (for example in W2K,
843 * nothing to worry about ...). ... or is on the Vista
844 * fast user switching page!
845 */
846 case ERROR_CTX_WINSTATION_NOT_FOUND:
847 VBoxServiceVerbose(3, "No WinStation found for user=%ls\n",
848 pUserInfo->wszUser);
849 break;
850
851 default:
852 VBoxServiceVerbose(3, "Cannot query WTS connection state for user=%ls, error=%ld\n",
853 pUserInfo->wszUser, dwLastErr);
854 break;
855 }
856
857 fFoundUser = true;
858 }
859 }
860 }
861 else
862 VBoxServiceVerbose(3, "SID owner type=%d not handled, skipping\n",
863 enmOwnerType);
864 }
865
866 VBoxServiceVerbose(3, "Account User=%ls %s logged in\n",
867 pUserInfo->wszUser, fFoundUser ? "is" : "is not");
868 }
869
870 if (fFoundUser)
871 pUserInfo->ulLastSession = pSessionData->Session;
872
873 LsaFreeReturnBuffer(pSessionData);
874 return fFoundUser;
875}
876
877
878static int vboxServiceVMInfoWinWriteLastInput(PVBOXSERVICEVEPROPCACHE pCache,
879 const char *pszUser, const char *pszDomain)
880{
881 AssertPtrReturn(pCache, VERR_INVALID_POINTER);
882 AssertPtrReturn(pszUser, VERR_INVALID_POINTER);
883 /* pszDomain is optional. */
884
885 int rc = VINF_SUCCESS;
886
887 char szPipeName[255];
888 if (RTStrPrintf(szPipeName, sizeof(szPipeName), "%s%s",
889 VBOXTRAY_IPC_PIPE_PREFIX, pszUser))
890 {
891 bool fReportToHost = false;
892 VBoxGuestUserState userState = VBoxGuestUserState_Unknown;
893
894 RTLOCALIPCSESSION hSession;
895 rc = RTLocalIpcSessionConnect(&hSession, szPipeName, 0 /* Flags */);
896 if (RT_SUCCESS(rc))
897 {
898 VBOXTRAYIPCHEADER ipcHdr = { VBOXTRAY_IPC_HDR_MAGIC, 0 /* Header version */,
899 VBOXTRAYIPCMSGTYPE_USERLASTINPUT, 0 /* No msg */ };
900
901 rc = RTLocalIpcSessionWrite(hSession, &ipcHdr, sizeof(ipcHdr));
902
903 VBOXTRAYIPCRES_USERLASTINPUT ipcRes;
904 if (RT_SUCCESS(rc))
905 rc = RTLocalIpcSessionRead(hSession, &ipcRes, sizeof(ipcRes),
906 NULL /* Exact read */);
907 if ( RT_SUCCESS(rc)
908 /* If uLastInput is set to UINT32_MAX VBoxTray was not able to retrieve the
909 * user's last input time. This might happen when running on Windows NT4 or older. */
910 && ipcRes.uLastInput != UINT32_MAX)
911 {
912 userState = (ipcRes.uLastInput * 1000) < g_uVMInfoUserIdleThresholdMS
913 ? VBoxGuestUserState_InUse
914 : VBoxGuestUserState_Idle;
915
916 rc = vboxServiceUserUpdateF(pCache, pszUser, pszDomain, "UsageState",
917 userState == VBoxGuestUserState_InUse
918 ? "InUse" : "Idle");
919
920 /*
921 * Note: vboxServiceUserUpdateF can return VINF_NO_CHANGE in case there wasn't anything
922 * to update. So only report the user's status to host when we really got something
923 * new.
924 */
925 fReportToHost = rc == VINF_SUCCESS;
926 VBoxServiceVerbose(4, "User \"%s\" (domain \"%s\") is idle for %RU32, fReportToHost=%RTbool\n",
927 pszUser, pszDomain ? pszDomain : "<None>", ipcRes.uLastInput, fReportToHost);
928
929#if 0 /* Do we want to write the idle time as well? */
930 /* Also write the user's current idle time, if there is any. */
931 if (userState == VBoxGuestUserState_Idle)
932 rc = vboxServiceUserUpdateF(pCache, pszUser, pszDomain, "IdleTimeMs",
933 "%RU32", ipcRes.uLastInputMs);
934 else
935 rc = vboxServiceUserUpdateF(pCache, pszUser, pszDomain, "IdleTimeMs",
936 NULL /* Delete property */);
937
938 if (RT_SUCCESS(rc))
939#endif
940 }
941#ifdef DEBUG
942 else if (ipcRes.uLastInput == UINT32_MAX)
943 VBoxServiceVerbose(4, "Last input for user \"%s\" is not supported, skipping\n",
944 pszUser, rc);
945
946 VBoxServiceVerbose(4, "Getting last input for user \"%s\" ended with rc=%Rrc\n",
947 pszUser, rc);
948#endif
949 int rc2 = RTLocalIpcSessionClose(hSession);
950 if (RT_SUCCESS(rc))
951 rc = rc2;
952 }
953 else
954 {
955 switch (rc)
956 {
957 case VERR_FILE_NOT_FOUND:
958 {
959 /* No VBoxTray (or too old version which does not support IPC) running
960 for the given user. Not much we can do then. */
961 VBoxServiceVerbose(4, "VBoxTray for user \"%s\" not running (anymore), no last input available\n",
962 pszUser);
963
964 /* Overwrite rc from above. */
965 rc = vboxServiceUserUpdateF(pCache, pszUser, pszDomain,
966 "UsageState", "Idle");
967
968 fReportToHost = rc == VINF_SUCCESS;
969 if (fReportToHost)
970 userState = VBoxGuestUserState_Idle;
971 break;
972 }
973
974 default:
975 VBoxServiceError("Error querying last input for user \"%s\", rc=%Rrc\n",
976 pszUser, rc);
977 break;
978 }
979 }
980
981 if (fReportToHost)
982 {
983 Assert(userState != VBoxGuestUserState_Unknown);
984 int rc2 = VbglR3GuestUserReportState(pszUser, pszDomain, userState,
985 NULL /* No details */, 0);
986 if (RT_FAILURE(rc2))
987 VBoxServiceError("Error reporting usage state %ld for user \"%s\" to host, rc=%Rrc\n",
988 userState, pszUser, rc2);
989
990 if (RT_SUCCESS(rc))
991 rc = rc2;
992 }
993 }
994
995 return rc;
996}
997
998
999/**
1000 * Retrieves the currently logged in users and stores their names along with the
1001 * user count.
1002 *
1003 * @returns VBox status code.
1004 * @param pCachce Property cache to use for storing some of the lookup
1005 * data in between calls.
1006 * @param ppszUserList Where to store the user list (separated by commas).
1007 * Must be freed with RTStrFree().
1008 * @param pcUsersInList Where to store the number of users in the list.
1009 */
1010int VBoxServiceVMInfoWinWriteUsers(PVBOXSERVICEVEPROPCACHE pCache,
1011 char **ppszUserList, uint32_t *pcUsersInList)
1012{
1013 AssertPtrReturn(pCache, VERR_INVALID_POINTER);
1014 AssertPtrReturn(ppszUserList, VERR_INVALID_POINTER);
1015 AssertPtrReturn(pcUsersInList, VERR_INVALID_POINTER);
1016
1017 int rc2 = VbglR3GuestPropConnect(&s_uDebugGuestPropClientID);
1018 AssertRC(rc2);
1019
1020 char *pszUserList = NULL;
1021 uint32_t cUsersInList = 0;
1022
1023 /* This function can report stale or orphaned interactive logon sessions
1024 of already logged off users (especially in Windows 2000). */
1025 PLUID paSessions = NULL;
1026 ULONG cSessions = 0;
1027 NTSTATUS rcNt = LsaEnumerateLogonSessions(&cSessions, &paSessions);
1028 if (rcNt != STATUS_SUCCESS)
1029 {
1030 ULONG ulError = LsaNtStatusToWinError(rcNt);
1031 switch (ulError)
1032 {
1033 case ERROR_NOT_ENOUGH_MEMORY:
1034 VBoxServiceError("Not enough memory to enumerate logon sessions!\n");
1035 break;
1036
1037 case ERROR_SHUTDOWN_IN_PROGRESS:
1038 /* If we're about to shutdown when we were in the middle of enumerating the logon
1039 * sessions, skip the error to not confuse the user with an unnecessary log message. */
1040 VBoxServiceVerbose(3, "Shutdown in progress ...\n");
1041 ulError = ERROR_SUCCESS;
1042 break;
1043
1044 default:
1045 VBoxServiceError("LsaEnumerate failed with error %RU32\n", ulError);
1046 break;
1047 }
1048
1049 if (paSessions)
1050 LsaFreeReturnBuffer(paSessions);
1051
1052 return RTErrConvertFromWin32(ulError);
1053 }
1054 VBoxServiceVerbose(3, "Found %ld sessions\n", cSessions);
1055
1056 PVBOXSERVICEVMINFOPROC paProcs;
1057 DWORD cProcs;
1058 int rc = VBoxServiceVMInfoWinProcessesEnumerate(&paProcs, &cProcs);
1059 if (RT_FAILURE(rc))
1060 {
1061 if (rc == VERR_NO_MEMORY)
1062 VBoxServiceError("Not enough memory to enumerate processes\n");
1063 else
1064 VBoxServiceError("Failed to enumerate processes, rc=%Rrc\n", rc);
1065 }
1066 else
1067 {
1068 PVBOXSERVICEVMINFOUSER pUserInfo;
1069 pUserInfo = (PVBOXSERVICEVMINFOUSER)RTMemAllocZ(cSessions * sizeof(VBOXSERVICEVMINFOUSER) + 1);
1070 if (!pUserInfo)
1071 VBoxServiceError("Not enough memory to store enumerated users!\n");
1072 else
1073 {
1074 ULONG cUniqueUsers = 0;
1075
1076 /**
1077 * Note: The cSessions loop variable does *not* correlate with
1078 * the Windows session ID!
1079 */
1080 for (ULONG i = 0; i < cSessions; i++)
1081 {
1082 VBoxServiceVerbose(3, "Handling session %RU32 (of %RU32)\n", i + 1, cSessions);
1083
1084 VBOXSERVICEVMINFOUSER userSession;
1085 if (VBoxServiceVMInfoWinIsLoggedIn(&userSession, &paSessions[i]))
1086 {
1087 VBoxServiceVerbose(4, "Handling user=%ls, domain=%ls, package=%ls, session=%RU32\n",
1088 userSession.wszUser, userSession.wszLogonDomain, userSession.wszAuthenticationPackage,
1089 userSession.ulLastSession);
1090
1091 /* Retrieve assigned processes of current session. */
1092 uint32_t cCurSessionProcs = VBoxServiceVMInfoWinSessionHasProcesses(&paSessions[i], paProcs, cProcs,
1093 NULL /* Terminal session ID */);
1094 /* Don't return here when current session does not have assigned processes
1095 * anymore -- in that case we have to search through the unique users list below
1096 * and see if got a stale user/session entry. */
1097
1098 if (g_cVerbosity > 3)
1099 {
1100 char szDebugSessionPath[255]; RTStrPrintf(szDebugSessionPath, sizeof(szDebugSessionPath), "/VirtualBox/GuestInfo/Debug/LSA/Session/%RU32",
1101 userSession.ulLastSession);
1102 VBoxServiceWritePropF(s_uDebugGuestPropClientID, szDebugSessionPath,
1103 "#%RU32: cSessionProcs=%RU32 (of %RU32 procs total)", s_uDebugIter, cCurSessionProcs, cProcs);
1104 }
1105
1106 bool fFoundUser = false;
1107 for (ULONG a = 0; a < cUniqueUsers; a++)
1108 {
1109 PVBOXSERVICEVMINFOUSER pCurUser = &pUserInfo[a];
1110 AssertPtr(pCurUser);
1111
1112 if ( !wcscmp(userSession.wszUser, pCurUser->wszUser)
1113 && !wcscmp(userSession.wszLogonDomain, pCurUser->wszLogonDomain)
1114 && !wcscmp(userSession.wszAuthenticationPackage, pCurUser->wszAuthenticationPackage))
1115 {
1116 /*
1117 * Only respect the highest session for the current user.
1118 */
1119 if (userSession.ulLastSession > pCurUser->ulLastSession)
1120 {
1121 VBoxServiceVerbose(4, "Updating user=%ls to %u processes (last used session: %RU32)\n",
1122 pCurUser->wszUser, cCurSessionProcs, userSession.ulLastSession);
1123
1124 if (!cCurSessionProcs)
1125 VBoxServiceVerbose(3, "Stale session for user=%ls detected! Processes: %RU32 -> %RU32, Session: %RU32 -> %RU32\n",
1126 pCurUser->wszUser,
1127 pCurUser->ulNumProcs, cCurSessionProcs,
1128 pCurUser->ulLastSession, userSession.ulLastSession);
1129
1130 pCurUser->ulNumProcs = cCurSessionProcs;
1131 pCurUser->ulLastSession = userSession.ulLastSession;
1132 }
1133 /* There can be multiple session objects using the same session ID for the
1134 * current user -- so when we got the same session again just add the found
1135 * processes to it. */
1136 else if (pCurUser->ulLastSession == userSession.ulLastSession)
1137 {
1138 VBoxServiceVerbose(4, "Updating processes for user=%ls (old procs=%RU32, new procs=%RU32, session=%RU32)\n",
1139 pCurUser->wszUser, pCurUser->ulNumProcs, cCurSessionProcs, pCurUser->ulLastSession);
1140
1141 pCurUser->ulNumProcs = cCurSessionProcs;
1142 }
1143
1144 fFoundUser = true;
1145 break;
1146 }
1147 }
1148
1149 if (!fFoundUser)
1150 {
1151 VBoxServiceVerbose(4, "Adding new user=%ls (session=%RU32) with %RU32 processes\n",
1152 userSession.wszUser, userSession.ulLastSession, cCurSessionProcs);
1153
1154 memcpy(&pUserInfo[cUniqueUsers], &userSession, sizeof(VBOXSERVICEVMINFOUSER));
1155 pUserInfo[cUniqueUsers].ulNumProcs = cCurSessionProcs;
1156 cUniqueUsers++;
1157 Assert(cUniqueUsers <= cSessions);
1158 }
1159 }
1160 }
1161
1162 if (g_cVerbosity > 3)
1163 VBoxServiceWritePropF(s_uDebugGuestPropClientID, "/VirtualBox/GuestInfo/Debug/LSA",
1164 "#%RU32: cSessions=%RU32, cProcs=%RU32, cUniqueUsers=%RU32",
1165 s_uDebugIter, cSessions, cProcs, cUniqueUsers);
1166
1167 VBoxServiceVerbose(3, "Found %u unique logged-in user(s)\n",
1168 cUniqueUsers);
1169
1170 for (ULONG i = 0; i < cUniqueUsers; i++)
1171 {
1172 if (g_cVerbosity > 3)
1173 {
1174 char szDebugUserPath[255]; RTStrPrintf(szDebugUserPath, sizeof(szDebugUserPath), "/VirtualBox/GuestInfo/Debug/LSA/User/%RU32", i);
1175 VBoxServiceWritePropF(s_uDebugGuestPropClientID, szDebugUserPath,
1176 "#%RU32: szName=%ls, sessionID=%RU32, cProcs=%RU32",
1177 s_uDebugIter, pUserInfo[i].wszUser, pUserInfo[i].ulLastSession, pUserInfo[i].ulNumProcs);
1178 }
1179
1180 bool fAddUser = false;
1181 if (pUserInfo[i].ulNumProcs)
1182 fAddUser = true;
1183
1184 if (fAddUser)
1185 {
1186 VBoxServiceVerbose(3, "User \"%ls\" has %RU32 interactive processes (session=%RU32)\n",
1187 pUserInfo[i].wszUser, pUserInfo[i].ulNumProcs, pUserInfo[i].ulLastSession);
1188
1189 if (cUsersInList > 0)
1190 {
1191 rc = RTStrAAppend(&pszUserList, ",");
1192 AssertRCBreakStmt(rc, RTStrFree(pszUserList));
1193 }
1194
1195 cUsersInList += 1;
1196
1197 char *pszUser = NULL;
1198 char *pszDomain = NULL;
1199 rc = RTUtf16ToUtf8(pUserInfo[i].wszUser, &pszUser);
1200 if ( RT_SUCCESS(rc)
1201 && pUserInfo[i].wszLogonDomain)
1202 rc = RTUtf16ToUtf8(pUserInfo[i].wszLogonDomain, &pszDomain);
1203 if (RT_SUCCESS(rc))
1204 {
1205 /* Append user to users list. */
1206 rc = RTStrAAppend(&pszUserList, pszUser);
1207
1208 /* Do idle detection. */
1209 if (RT_SUCCESS(rc))
1210 rc = vboxServiceVMInfoWinWriteLastInput(pCache, pszUser, pszDomain);
1211 }
1212 else
1213 rc = RTStrAAppend(&pszUserList, "<string-conversion-error>");
1214
1215 RTStrFree(pszUser);
1216 RTStrFree(pszDomain);
1217
1218 AssertRCBreakStmt(rc, RTStrFree(pszUserList));
1219 }
1220 }
1221
1222 RTMemFree(pUserInfo);
1223 }
1224 VBoxServiceVMInfoWinProcessesFree(cProcs, paProcs);
1225 }
1226 if (paSessions)
1227 LsaFreeReturnBuffer(paSessions);
1228
1229 if (RT_SUCCESS(rc))
1230 {
1231 *ppszUserList = pszUserList;
1232 *pcUsersInList = cUsersInList;
1233 }
1234
1235 s_uDebugIter++;
1236 VbglR3GuestPropDisconnect(s_uDebugGuestPropClientID);
1237
1238 return rc;
1239}
1240
1241#endif /* TARGET_NT4 */
1242
1243int VBoxServiceWinGetComponentVersions(uint32_t uClientID)
1244{
1245 int rc;
1246 char szSysDir[_MAX_PATH] = {0};
1247 char szWinDir[_MAX_PATH] = {0};
1248 char szDriversDir[_MAX_PATH + 32] = {0};
1249
1250 /* ASSUME: szSysDir and szWinDir and derivatives are always ASCII compatible. */
1251 GetSystemDirectory(szSysDir, _MAX_PATH);
1252 GetWindowsDirectory(szWinDir, _MAX_PATH);
1253 RTStrPrintf(szDriversDir, sizeof(szDriversDir), "%s\\drivers", szSysDir);
1254#ifdef RT_ARCH_AMD64
1255 char szSysWowDir[_MAX_PATH + 32] = {0};
1256 RTStrPrintf(szSysWowDir, sizeof(szSysWowDir), "%s\\SysWow64", szWinDir);
1257#endif
1258
1259 /* The file information table. */
1260#ifndef TARGET_NT4
1261 const VBOXSERVICEVMINFOFILE aVBoxFiles[] =
1262 {
1263 { szSysDir, "VBoxControl.exe" },
1264 { szSysDir, "VBoxHook.dll" },
1265 { szSysDir, "VBoxDisp.dll" },
1266 { szSysDir, "VBoxMRXNP.dll" },
1267 { szSysDir, "VBoxService.exe" },
1268 { szSysDir, "VBoxTray.exe" },
1269 { szSysDir, "VBoxGINA.dll" },
1270 { szSysDir, "VBoxCredProv.dll" },
1271
1272 /* On 64-bit we don't yet have the OpenGL DLLs in native format.
1273 So just enumerate the 32-bit files in the SYSWOW directory. */
1274# ifdef RT_ARCH_AMD64
1275 { szSysWowDir, "VBoxOGLarrayspu.dll" },
1276 { szSysWowDir, "VBoxOGLcrutil.dll" },
1277 { szSysWowDir, "VBoxOGLerrorspu.dll" },
1278 { szSysWowDir, "VBoxOGLpackspu.dll" },
1279 { szSysWowDir, "VBoxOGLpassthroughspu.dll" },
1280 { szSysWowDir, "VBoxOGLfeedbackspu.dll" },
1281 { szSysWowDir, "VBoxOGL.dll" },
1282# else /* !RT_ARCH_AMD64 */
1283 { szSysDir, "VBoxOGLarrayspu.dll" },
1284 { szSysDir, "VBoxOGLcrutil.dll" },
1285 { szSysDir, "VBoxOGLerrorspu.dll" },
1286 { szSysDir, "VBoxOGLpackspu.dll" },
1287 { szSysDir, "VBoxOGLpassthroughspu.dll" },
1288 { szSysDir, "VBoxOGLfeedbackspu.dll" },
1289 { szSysDir, "VBoxOGL.dll" },
1290# endif /* !RT_ARCH_AMD64 */
1291
1292 { szDriversDir, "VBoxGuest.sys" },
1293 { szDriversDir, "VBoxMouse.sys" },
1294 { szDriversDir, "VBoxSF.sys" },
1295 { szDriversDir, "VBoxVideo.sys" },
1296 };
1297
1298#else /* TARGET_NT4 */
1299 const VBOXSERVICEVMINFOFILE aVBoxFiles[] =
1300 {
1301 { szSysDir, "VBoxControl.exe" },
1302 { szSysDir, "VBoxHook.dll" },
1303 { szSysDir, "VBoxDisp.dll" },
1304 { szSysDir, "VBoxServiceNT.exe" },
1305 { szSysDir, "VBoxTray.exe" },
1306
1307 { szDriversDir, "VBoxGuestNT.sys" },
1308 { szDriversDir, "VBoxMouseNT.sys" },
1309 { szDriversDir, "VBoxVideo.sys" },
1310 };
1311#endif /* TARGET_NT4 */
1312
1313 for (unsigned i = 0; i < RT_ELEMENTS(aVBoxFiles); i++)
1314 {
1315 char szVer[128];
1316 VBoxServiceGetFileVersionString(aVBoxFiles[i].pszFilePath, aVBoxFiles[i].pszFileName, szVer, sizeof(szVer));
1317 char szPropPath[256];
1318 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestAdd/Components/%s", aVBoxFiles[i].pszFileName);
1319 rc = VBoxServiceWritePropF(uClientID, szPropPath, "%s", szVer);
1320 }
1321
1322 return VINF_SUCCESS;
1323}
1324
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette