VirtualBox

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

Last change on this file since 27866 was 26326, checked in by vboxsync, 15 years ago

Updates for guest statistics and memory ballooning

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.7 KB
Line 
1/* $Id: VBoxServiceVMInfo-win.cpp 26326 2010-02-08 13:22:24Z vboxsync $ */
2/** @file
3 * VBoxService - Virtual Machine Information for the Host, Windows specifics.
4 */
5
6/*
7 * Copyright (C) 2009-2010 Sun Microsystems, Inc.
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 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22
23/*******************************************************************************
24* Header Files *
25*******************************************************************************/
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/mem.h>
33#include <iprt/thread.h>
34#include <iprt/string.h>
35#include <iprt/semaphore.h>
36#include <iprt/system.h>
37#include <iprt/time.h>
38#include <VBox/VBoxGuestLib.h>
39#include "VBoxServiceInternal.h"
40#include "VBoxServiceUtils.h"
41
42
43/*******************************************************************************
44* Global Variables *
45*******************************************************************************/
46/** Function prototypes for dynamic loading. */
47PFNWTSGETACTIVECONSOLESESSIONID g_pfnWTSGetActiveConsoleSessionId = NULL;
48
49
50#ifndef TARGET_NT4
51
52/**
53 * Fills in more data for a process.
54 *
55 * @returns VBox status code.
56 * @param pProc The process structure to fill data into.
57 * @param tkClass The kind of token information to get.
58 */
59static int VBoxServiceVMInfoWinProcessesGetTokenInfo(PVBOXSERVICEVMINFOPROC pProc,
60 TOKEN_INFORMATION_CLASS tkClass)
61{
62 AssertPtr(pProc);
63 HANDLE h = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pProc->id);
64 if (h == NULL)
65 return RTErrConvertFromWin32(GetLastError());
66
67 int rc = VERR_NO_MEMORY;
68 HANDLE hToken;
69 if (OpenProcessToken(h, TOKEN_QUERY, &hToken))
70 {
71 void *pvTokenInfo = NULL;
72 DWORD dwTokenInfoSize;
73 switch (tkClass)
74 {
75 case TokenStatistics:
76 dwTokenInfoSize = sizeof(TOKEN_STATISTICS);
77 pvTokenInfo = RTMemAlloc(dwTokenInfoSize);
78 break;
79
80 /** @todo Implement more token classes here. */
81
82 default:
83 VBoxServiceError("Token class not implemented: %ld", tkClass);
84 rc = VERR_NOT_IMPLEMENTED;
85 break;
86 }
87
88 if (pvTokenInfo)
89 {
90 DWORD dwRetLength;
91 if (GetTokenInformation(hToken, tkClass, pvTokenInfo, dwTokenInfoSize, &dwRetLength))
92 {
93 switch (tkClass)
94 {
95 case TokenStatistics:
96 {
97 TOKEN_STATISTICS *pStats = (TOKEN_STATISTICS*)pvTokenInfo;
98 pProc->luid = pStats->AuthenticationId;
99 /** @todo Add more information of TOKEN_STATISTICS as needed. */
100 break;
101 }
102
103 default:
104 /* Should never get here! */
105 break;
106 }
107 rc = VINF_SUCCESS;
108 }
109 else
110 rc = RTErrConvertFromWin32(GetLastError());
111 RTMemFree(pvTokenInfo);
112 }
113 CloseHandle(hToken);
114 }
115 else
116 rc = RTErrConvertFromWin32(GetLastError());
117 CloseHandle(h);
118 return rc;
119}
120
121
122/**
123 * Enumerate all the processes in the system and get the logon user IDs for
124 * them.
125 *
126 * @returns VBox status code.
127 * @param ppaProcs Where to return the process snapshot. This must be
128 * freed by calling VBoxServiceVMInfoWinProcessesFree.
129 *
130 * @param pcProcs Where to store the returned process count.
131 */
132int VBoxServiceVMInfoWinProcessesEnumerate(PVBOXSERVICEVMINFOPROC *ppaProcs, PDWORD pcProcs)
133{
134 AssertPtr(ppaProcs);
135 AssertPtr(pcProcs);
136
137 /*
138 * Call EnumProcesses with an increasingly larger buffer until it all fits
139 * or we think something is screwed up.
140 */
141 DWORD cProcesses = 64;
142 PDWORD paPids = NULL;
143 int rc = VINF_SUCCESS;
144 do
145 {
146 /* Allocate / grow the buffer first. */
147 cProcesses *= 2;
148 void *pvNew = RTMemRealloc(paPids, cProcesses * sizeof(DWORD));
149 if (!pvNew)
150 {
151 rc = VERR_NO_MEMORY;
152 break;
153 }
154 paPids = (PDWORD)pvNew;
155
156 /* Query the processes. Not the cbRet == buffer size means there could be more work to be done. */
157 DWORD cbRet;
158 if (!EnumProcesses(paPids, cProcesses * sizeof(DWORD), &cbRet))
159 {
160 rc = RTErrConvertFromWin32(GetLastError());
161 break;
162 }
163 if (cbRet < cProcesses * sizeof(DWORD))
164 {
165 cProcesses = cbRet / sizeof(DWORD);
166 break;
167 }
168 } while (cProcesses <= 32768); /* Should be enough; see: http://blogs.technet.com/markrussinovich/archive/2009/07/08/3261309.aspx */
169 if (RT_SUCCESS(rc))
170 {
171 /*
172 * Allocate out process structures and fill data into them.
173 * We currently only try lookup their LUID's.
174 */
175 PVBOXSERVICEVMINFOPROC paProcs;
176 paProcs = (PVBOXSERVICEVMINFOPROC)RTMemAllocZ(cProcesses * sizeof(VBOXSERVICEVMINFOPROC));
177 if (paProcs)
178 {
179 for (DWORD i = 0; i < cProcesses; i++)
180 {
181 paProcs[i].id = paPids[i];
182 rc = VBoxServiceVMInfoWinProcessesGetTokenInfo(&paProcs[i], TokenStatistics);
183 if (RT_FAILURE(rc))
184 {
185 /* Because some processes cannot be opened/parsed on
186 Windows, we should not consider to be this an error here. */
187 rc = VINF_SUCCESS;
188 }
189 }
190
191 /* Save number of processes */
192 if (RT_SUCCESS(rc))
193 {
194 *pcProcs = cProcesses;
195 *ppaProcs = paProcs;
196 }
197 else
198 RTMemFree(paProcs);
199 }
200 else
201 rc = VERR_NO_MEMORY;
202 }
203
204 RTMemFree(paPids);
205 return rc;
206}
207
208/**
209 * Frees the process structures returned by
210 * VBoxServiceVMInfoWinProcessesEnumerate() before.
211 *
212 * @param paProcs What
213 */
214void VBoxServiceVMInfoWinProcessesFree(PVBOXSERVICEVMINFOPROC paProcs)
215{
216 RTMemFree(paProcs);
217}
218
219/**
220 * Determins whether the specified session has processes on the system.
221 *
222 * @returns true if it has, false if it doesn't.
223 * @param pSession The session.
224 * @param paProcs The process snapshot.
225 * @param cProcs The number of processes in the snaphot.
226 */
227bool VBoxServiceVMInfoWinSessionHasProcesses(PLUID pSession, VBOXSERVICEVMINFOPROC const *paProcs, DWORD cProcs)
228{
229 AssertPtr(pSession);
230
231 if (!cProcs) /* To be on the safe side. */
232 return false;
233 AssertPtr(paProcs);
234
235 PSECURITY_LOGON_SESSION_DATA pSessionData = NULL;
236 NTSTATUS rcNt = LsaGetLogonSessionData(pSession, &pSessionData);
237 if (rcNt != STATUS_SUCCESS)
238 {
239 VBoxServiceError("Could not get logon session data! rcNt=%#x", rcNt);
240 return false;
241 }
242 AssertPtrReturn(pSessionData, false);
243
244 /*
245 * Even if a user seems to be logged in, it could be a stale/orphaned logon
246 * session. So check if we have some processes bound to it by comparing the
247 * session <-> process LUIDs.
248 */
249 for (DWORD i = 0; i < cProcs; i++)
250 {
251 /*VBoxServiceVerbose(3, "%ld:%ld <-> %ld:%ld\n",
252 paProcs[i].luid.HighPart, paProcs[i].luid.LowPart,
253 pSessionData->LogonId.HighPart, pSessionData->LogonId.LowPart);*/
254 if ( paProcs[i].luid.HighPart == pSessionData->LogonId.HighPart
255 && paProcs[i].luid.LowPart == pSessionData->LogonId.LowPart)
256 {
257 VBoxServiceVerbose(3, "Users: Session %ld:%ld has active processes\n",
258 pSessionData->LogonId.HighPart, pSessionData->LogonId.LowPart);
259 LsaFreeReturnBuffer(pSessionData);
260 return true;
261 }
262 }
263 LsaFreeReturnBuffer(pSessionData);
264 return false;
265}
266
267
268/**
269 * Save and noisy string copy.
270 *
271 * @param pwszDst Destination buffer.
272 * @param cbDst Size in bytes - not WCHAR count!
273 * @param pSrc Source string.
274 * @param pszWhat What this is. For the log.
275 */
276static void VBoxServiceVMInfoWinSafeCopy(PWCHAR pwszDst, size_t cbDst, LSA_UNICODE_STRING const *pSrc, const char *pszWhat)
277{
278 Assert(RT_ALIGN(cbDst, sizeof(WCHAR)) == cbDst);
279
280 size_t cbCopy = pSrc->Length;
281 if (cbCopy + sizeof(WCHAR) > cbDst)
282 {
283 VBoxServiceVerbose(0, "%s is too long - %u bytes, buffer %u bytes! It will be truncated.\n",
284 pszWhat, cbCopy, cbDst);
285 cbCopy = cbDst - sizeof(WCHAR);
286 }
287 if (cbCopy)
288 memcpy(pwszDst, pSrc->Buffer, cbCopy);
289 pwszDst[cbCopy / sizeof(WCHAR)] = '\0';
290}
291
292
293/**
294 * Detects whether a user is logged on based on the enumerated processes.
295 *
296 * @returns true if logged in, false if not (or error).
297 * @param a_pUserInfo Where to return the user information.
298 * @param a_pSession The session to check.
299 */
300bool VBoxServiceVMInfoWinIsLoggedIn(PVBOXSERVICEVMINFOUSER a_pUserInfo, PLUID a_pSession)
301{
302 if (!a_pSession)
303 return false;
304
305 PSECURITY_LOGON_SESSION_DATA pSessionData = NULL;
306 NTSTATUS rcNt = LsaGetLogonSessionData(a_pSession, &pSessionData);
307 if (rcNt != STATUS_SUCCESS)
308 {
309 VBoxServiceError("LsaGetLogonSessionData failed, LSA error %#x\n", LsaNtStatusToWinError(rcNt));
310 if (pSessionData)
311 LsaFreeReturnBuffer(pSessionData);
312 return false;
313 }
314 if (!pSessionData)
315 {
316 VBoxServiceError("Invalid logon session data.\n");
317 return false;
318 }
319 VBoxServiceVerbose(3, "Users: Session data: Name = %ls, Len = %d, SID = %s, LogonID = %d,%d\n",
320 pSessionData->UserName.Buffer,
321 pSessionData->UserName.Length,
322 pSessionData->Sid != NULL ? "1" : "0",
323 pSessionData->LogonId.HighPart, pSessionData->LogonId.LowPart);
324
325 bool fFoundUser = false;
326 if ( pSessionData->UserName.Buffer != NULL
327 && pSessionData->Sid != NULL
328 && pSessionData->LogonId.LowPart != 0)
329 {
330 /*
331 * Copy out the data.
332 */
333 VBoxServiceVMInfoWinSafeCopy(a_pUserInfo->wszUser, sizeof(a_pUserInfo->wszUser),
334 &pSessionData->UserName, "User name");
335 VBoxServiceVMInfoWinSafeCopy(a_pUserInfo->wszAuthenticationPackage, sizeof(a_pUserInfo->wszAuthenticationPackage),
336 &pSessionData->AuthenticationPackage, "Authentication pkg name");
337 VBoxServiceVMInfoWinSafeCopy(a_pUserInfo->wszLogonDomain, sizeof(a_pUserInfo->wszLogonDomain),
338 &pSessionData->LogonDomain, "Logon domain name");
339
340
341 /*
342 * Only handle users which can login interactively or logged in
343 * remotely over native RDP.
344 */
345 /** @todo r=bird: Whey don't we check this before copying the data? */
346 if ( ( (SECURITY_LOGON_TYPE)pSessionData->LogonType == Interactive
347 || (SECURITY_LOGON_TYPE)pSessionData->LogonType == RemoteInteractive)
348 && pSessionData->Sid != NULL)
349 {
350 TCHAR szOwnerName[_MAX_PATH] = { 0 };
351 DWORD dwOwnerNameSize = sizeof(szOwnerName);
352 TCHAR szDomainName[_MAX_PATH] = { 0 };
353 DWORD dwDomainNameSize = sizeof(szDomainName);
354 SID_NAME_USE enmOwnerType = SidTypeInvalid;
355 if (LookupAccountSid(NULL,
356 pSessionData->Sid,
357 szOwnerName,
358 &dwOwnerNameSize,
359 szDomainName,
360 &dwDomainNameSize,
361 &enmOwnerType))
362 {
363 VBoxServiceVerbose(3, "Account User=%ls, Session=%ld, LUID=%ld,%ld, AuthPkg=%ls, Domain=%ls\n",
364 a_pUserInfo->wszUser, pSessionData->Session, pSessionData->LogonId.HighPart,
365 pSessionData->LogonId.LowPart, a_pUserInfo->wszAuthenticationPackage,
366 a_pUserInfo->wszLogonDomain);
367
368#if 1 /** @todo If we don't use this, drop it? */
369 /* The session ID increments/decrements on Vista often! So don't compare
370 the session data SID with the current SID here. */
371 DWORD dwActiveSession = 0;
372 if (g_pfnWTSGetActiveConsoleSessionId != NULL) /* Check terminal session ID. */
373 dwActiveSession = g_pfnWTSGetActiveConsoleSessionId();
374 /*VBoxServiceVerbose(3, ("Users: Current active session ID: %ld\n", dwActiveSession));*/
375#endif
376
377 if (enmOwnerType == SidTypeUser)
378 {
379 /* Detect RDP sessions as well. */
380 LPTSTR pBuffer = NULL;
381 DWORD cbRet = 0;
382 int iState = 0;
383 if (WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE,
384 WTS_CURRENT_SESSION,
385 WTSConnectState,
386 &pBuffer,
387 &cbRet))
388 {
389 /*VBoxServiceVerbose(3, ("Users: WTSQuerySessionInformation returned %ld bytes, p=%p, state=%d\n", cbRet, pBuffer, pBuffer != NULL ? (INT)*pBuffer : -1));*/
390 if(cbRet)
391 iState = *pBuffer;
392
393 if ( iState == WTSActive /* User logged on to WinStation. */
394 || iState == WTSShadow /* Shadowing another WinStation. */
395 || iState == WTSDisconnected) /* WinStation logged on without client. */
396 {
397 /** @todo On Vista and W2K, always "old" user name are still
398 * there. Filter out the old one! */
399 VBoxServiceVerbose(3, "Users: Account User=%ls is logged in via TCS/RDP. State=%d\n",
400 a_pUserInfo->wszUser, iState);
401 fFoundUser = true;
402 }
403
404 if (pBuffer)
405 WTSFreeMemory(pBuffer);
406 }
407 else
408 {
409 /*
410 * Terminal services don't run (for example in W2K,
411 * nothing to worry about ...). ... or is on the Vista
412 * fast user switching page!
413 */
414 fFoundUser = true;
415 }
416 }
417 }
418 }
419 }
420
421 LsaFreeReturnBuffer(pSessionData);
422 return fFoundUser;
423}
424
425#endif /* TARGET_NT4 */
426
427int VBoxServiceWinGetComponentVersions(uint32_t uClientID)
428{
429 int rc;
430 char szSysDir[_MAX_PATH] = {0};
431 char szWinDir[_MAX_PATH] = {0};
432 char szDriversDir[_MAX_PATH + 32] = {0};
433
434 /* ASSUME: szSysDir and szWinDir and derivatives are always ASCII compatible. */
435 GetSystemDirectory(szSysDir, _MAX_PATH);
436 GetWindowsDirectory(szWinDir, _MAX_PATH);
437 RTStrPrintf(szDriversDir, sizeof(szDriversDir), "%s\\drivers", szSysDir);
438#ifdef RT_ARCH_AMD64
439 char szSysWowDir[_MAX_PATH + 32] = {0};
440 RTStrPrintf(szSysWowDir, sizeof(szSysWowDir), "%s\\SysWow64", szWinDir);
441#endif
442
443 /* The file information table. */
444#ifndef TARGET_NT4
445 const VBOXSERVICEVMINFOFILE aVBoxFiles[] =
446 {
447 { szSysDir, "VBoxControl.exe" },
448 { szSysDir, "VBoxHook.dll" },
449 { szSysDir, "VBoxDisp.dll" },
450 { szSysDir, "VBoxMRXNP.dll" },
451 { szSysDir, "VBoxService.exe" },
452 { szSysDir, "VBoxTray.exe" },
453 { szSysDir, "VBoxGINA.dll" },
454 { szSysDir, "VBoxCredProv.dll" },
455
456 /* On 64-bit we don't yet have the OpenGL DLLs in native format.
457 So just enumerate the 32-bit files in the SYSWOW directory. */
458# ifdef RT_ARCH_AMD64
459 { szSysWowDir, "VBoxOGLarrayspu.dll" },
460 { szSysWowDir, "VBoxOGLcrutil.dll" },
461 { szSysWowDir, "VBoxOGLerrorspu.dll" },
462 { szSysWowDir, "VBoxOGLpackspu.dll" },
463 { szSysWowDir, "VBoxOGLpassthroughspu.dll" },
464 { szSysWowDir, "VBoxOGLfeedbackspu.dll" },
465 { szSysWowDir, "VBoxOGL.dll" },
466# else /* !RT_ARCH_AMD64 */
467 { szSysDir, "VBoxOGLarrayspu.dll" },
468 { szSysDir, "VBoxOGLcrutil.dll" },
469 { szSysDir, "VBoxOGLerrorspu.dll" },
470 { szSysDir, "VBoxOGLpackspu.dll" },
471 { szSysDir, "VBoxOGLpassthroughspu.dll" },
472 { szSysDir, "VBoxOGLfeedbackspu.dll" },
473 { szSysDir, "VBoxOGL.dll" },
474# endif /* !RT_ARCH_AMD64 */
475
476 { szDriversDir, "VBoxGuest.sys" },
477 { szDriversDir, "VBoxMouse.sys" },
478 { szDriversDir, "VBoxSF.sys" },
479 { szDriversDir, "VBoxVideo.sys" },
480 };
481
482#else /* TARGET_NT4 */
483 const VBOXSERVICEVMINFOFILE aVBoxFiles[] =
484 {
485 { szSysDir, "VBoxControl.exe" },
486 { szSysDir, "VBoxHook.dll" },
487 { szSysDir, "VBoxDisp.dll" },
488 { szSysDir, "VBoxService.exe" },
489 { szSysDir, "VBoxTray.exe" },
490
491 { szDriversDir, "VBoxGuestNT.sys" },
492 { szDriversDir, "VBoxMouseNT.sys" },
493 { szDriversDir, "VBoxVideo.sys" },
494 };
495#endif /* TARGET_NT4 */
496
497 for (unsigned i = 0; i < RT_ELEMENTS(aVBoxFiles); i++)
498 {
499 char szVer[128];
500 VBoxServiceGetFileVersionString(aVBoxFiles[i].pszFilePath, aVBoxFiles[i].pszFileName, szVer, sizeof(szVer));
501 char szPropPath[256];
502 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestAdd/Components/%s", aVBoxFiles[i].pszFileName);
503 rc = VBoxServiceWritePropF(uClientID, szPropPath, "%s", szVer);
504 }
505
506 return VINF_SUCCESS;
507}
508
Note: See TracBrowser for help on using the repository browser.

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