VirtualBox

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

Last change on this file since 55628 was 54120, checked in by vboxsync, 10 years ago

Windows guest additions: cleanup, removed obsolete MMR code.

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