VirtualBox

source: vbox/trunk/src/VBox/Main/src-client/GuestImpl.cpp@ 63244

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

Main: warnings

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 38.8 KB
Line 
1/* $Id: GuestImpl.cpp 63244 2016-08-10 10:36:09Z vboxsync $ */
2/** @file
3 * VirtualBox COM class implementation: Guest features.
4 */
5
6/*
7 * Copyright (C) 2006-2016 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#include "GuestImpl.h"
19#ifdef VBOX_WITH_GUEST_CONTROL
20# include "GuestSessionImpl.h"
21#endif
22#include "Global.h"
23#include "ConsoleImpl.h"
24#include "ProgressImpl.h"
25#ifdef VBOX_WITH_DRAG_AND_DROP
26# include "GuestDnDPrivate.h"
27#endif
28#include "VMMDev.h"
29
30#include "AutoCaller.h"
31#include "Logging.h"
32#include "Performance.h"
33#include "VBoxEvents.h"
34
35#include <VBox/VMMDev.h>
36#include <iprt/cpp/utils.h>
37#include <iprt/ctype.h>
38#include <iprt/stream.h>
39#include <iprt/timer.h>
40#include <VBox/vmm/pgm.h>
41#include <VBox/version.h>
42
43// defines
44/////////////////////////////////////////////////////////////////////////////
45
46// constructor / destructor
47/////////////////////////////////////////////////////////////////////////////
48
49DEFINE_EMPTY_CTOR_DTOR(Guest)
50
51HRESULT Guest::FinalConstruct()
52{
53 return BaseFinalConstruct();
54}
55
56void Guest::FinalRelease()
57{
58 uninit();
59 BaseFinalRelease();
60}
61
62// public methods only for internal purposes
63/////////////////////////////////////////////////////////////////////////////
64
65/**
66 * Initializes the guest object.
67 */
68HRESULT Guest::init(Console *aParent)
69{
70 LogFlowThisFunc(("aParent=%p\n", aParent));
71
72 ComAssertRet(aParent, E_INVALIDARG);
73
74 /* Enclose the state transition NotReady->InInit->Ready */
75 AutoInitSpan autoInitSpan(this);
76 AssertReturn(autoInitSpan.isOk(), E_FAIL);
77
78 unconst(mParent) = aParent;
79
80 /* Confirm a successful initialization when it's the case */
81 autoInitSpan.setSucceeded();
82
83 ULONG aMemoryBalloonSize;
84 HRESULT hr = mParent->i_machine()->COMGETTER(MemoryBalloonSize)(&aMemoryBalloonSize);
85 if (hr == S_OK) /** @todo r=andy SUCCEEDED? */
86 mMemoryBalloonSize = aMemoryBalloonSize;
87 else
88 mMemoryBalloonSize = 0; /* Default is no ballooning */
89
90 BOOL fPageFusionEnabled;
91 hr = mParent->i_machine()->COMGETTER(PageFusionEnabled)(&fPageFusionEnabled);
92 if (hr == S_OK) /** @todo r=andy SUCCEEDED? */
93 mfPageFusionEnabled = fPageFusionEnabled;
94 else
95 mfPageFusionEnabled = false; /* Default is no page fusion*/
96
97 mStatUpdateInterval = 0; /* Default is not to report guest statistics at all */
98 mCollectVMMStats = false;
99
100 /* Clear statistics. */
101 mNetStatRx = mNetStatTx = 0;
102 mNetStatLastTs = RTTimeNanoTS();
103 for (unsigned i = 0 ; i < GUESTSTATTYPE_MAX; i++)
104 mCurrentGuestStat[i] = 0;
105 mVmValidStats = pm::VMSTATMASK_NONE;
106 RT_ZERO(mCurrentGuestCpuUserStat);
107 RT_ZERO(mCurrentGuestCpuKernelStat);
108 RT_ZERO(mCurrentGuestCpuIdleStat);
109
110 mMagic = GUEST_MAGIC;
111 int vrc = RTTimerLRCreate(&mStatTimer, 1000 /* ms */,
112 &Guest::i_staticUpdateStats, this);
113 AssertMsgRC(vrc, ("Failed to create guest statistics update timer (%Rrc)\n", vrc));
114
115 hr = unconst(mEventSource).createObject();
116 if (SUCCEEDED(hr))
117 hr = mEventSource->init();
118
119 mCpus = 1;
120
121#ifdef VBOX_WITH_DRAG_AND_DROP
122 try
123 {
124 GuestDnD::createInstance(this /* pGuest */);
125 hr = unconst(mDnDSource).createObject();
126 if (SUCCEEDED(hr))
127 hr = mDnDSource->init(this /* pGuest */);
128 if (SUCCEEDED(hr))
129 {
130 hr = unconst(mDnDTarget).createObject();
131 if (SUCCEEDED(hr))
132 hr = mDnDTarget->init(this /* pGuest */);
133 }
134
135 LogFlowFunc(("Drag and drop initializied with hr=%Rhrc\n", hr));
136 }
137 catch (std::bad_alloc &)
138 {
139 hr = E_OUTOFMEMORY;
140 }
141#endif
142
143 LogFlowFunc(("hr=%Rhrc\n", hr));
144 return hr;
145}
146
147/**
148 * Uninitializes the instance and sets the ready flag to FALSE.
149 * Called either from FinalRelease() or by the parent when it gets destroyed.
150 */
151void Guest::uninit()
152{
153 LogFlowThisFunc(("\n"));
154
155 /* Enclose the state transition Ready->InUninit->NotReady */
156 AutoUninitSpan autoUninitSpan(this);
157 if (autoUninitSpan.uninitDone())
158 return;
159
160 /* Destroy stat update timer */
161 int vrc = RTTimerLRDestroy(mStatTimer);
162 AssertMsgRC(vrc, ("Failed to create guest statistics update timer(%Rra)\n", vrc));
163 mStatTimer = NULL;
164 mMagic = 0;
165
166#ifdef VBOX_WITH_GUEST_CONTROL
167 LogFlowThisFunc(("Closing sessions (%RU64 total)\n",
168 mData.mGuestSessions.size()));
169 GuestSessions::iterator itSessions = mData.mGuestSessions.begin();
170 while (itSessions != mData.mGuestSessions.end())
171 {
172# ifdef DEBUG
173 ULONG cRefs = itSessions->second->AddRef();
174 LogFlowThisFunc(("sessionID=%RU32, cRefs=%RU32\n", itSessions->first, cRefs > 1 ? cRefs - 1 : 0));
175 itSessions->second->Release();
176# endif
177 itSessions->second->uninit();
178 ++itSessions;
179 }
180 mData.mGuestSessions.clear();
181#endif
182
183#ifdef VBOX_WITH_DRAG_AND_DROP
184 GuestDnD::destroyInstance();
185 unconst(mDnDSource).setNull();
186 unconst(mDnDTarget).setNull();
187#endif
188
189 unconst(mEventSource).setNull();
190 unconst(mParent) = NULL;
191
192 LogFlowFuncLeave();
193}
194
195/* static */
196DECLCALLBACK(void) Guest::i_staticUpdateStats(RTTIMERLR hTimerLR, void *pvUser, uint64_t iTick)
197{
198 AssertReturnVoid(pvUser != NULL);
199 Guest *guest = static_cast<Guest *>(pvUser);
200 Assert(guest->mMagic == GUEST_MAGIC);
201 if (guest->mMagic == GUEST_MAGIC)
202 guest->i_updateStats(iTick);
203
204 NOREF(hTimerLR);
205}
206
207/* static */
208DECLCALLBACK(int) Guest::i_staticEnumStatsCallback(const char *pszName, STAMTYPE enmType, void *pvSample,
209 STAMUNIT enmUnit, STAMVISIBILITY enmVisiblity,
210 const char *pszDesc, void *pvUser)
211{
212 RT_NOREF(enmVisiblity, pszDesc);
213 AssertLogRelMsgReturn(enmType == STAMTYPE_COUNTER, ("Unexpected sample type %d ('%s')\n", enmType, pszName), VINF_SUCCESS);
214 AssertLogRelMsgReturn(enmUnit == STAMUNIT_BYTES, ("Unexpected sample unit %d ('%s')\n", enmUnit, pszName), VINF_SUCCESS);
215
216 /* Get the base name w/ slash. */
217 const char *pszLastSlash = strrchr(pszName, '/');
218 AssertLogRelMsgReturn(pszLastSlash, ("Unexpected sample '%s'\n", pszName), VINF_SUCCESS);
219
220 /* Receive or transmit? */
221 bool fRx;
222 if (!strcmp(pszLastSlash, "/BytesReceived"))
223 fRx = true;
224 else if (!strcmp(pszLastSlash, "/BytesTransmitted"))
225 fRx = false;
226 else
227 AssertLogRelMsgFailedReturn(("Unexpected sample '%s'\n", pszName), VINF_SUCCESS);
228
229#if 0 /* not used for anything, so don't bother parsing it. */
230 /* Find start of instance number. ASSUMES '/Public/Net/Name<Instance digits>/Bytes...' */
231 do
232 --pszLastSlash;
233 while (pszLastSlash > pszName && RT_C_IS_DIGIT(*pszLastSlash));
234 pszLastSlash++;
235
236 uint8_t uInstance;
237 int rc = RTStrToUInt8Ex(pszLastSlash, NULL, 10, &uInstance);
238 AssertLogRelMsgReturn(RT_SUCCESS(rc) && rc != VWRN_NUMBER_TOO_BIG && rc != VWRN_NEGATIVE_UNSIGNED,
239 ("%Rrc '%s'\n", rc, pszName), VINF_SUCCESS)
240#endif
241
242 /* Add the bytes to our counters. */
243 PSTAMCOUNTER pCnt = (PSTAMCOUNTER)pvSample;
244 Guest *pGuest = (Guest *)pvUser;
245 uint64_t cb = pCnt->c;
246#if 0
247 LogFlowFunc(("%s i=%u d=%s %llu bytes\n", pszName, uInstance, fRx ? "RX" : "TX", cb));
248#else
249 LogFlowFunc(("%s d=%s %llu bytes\n", pszName, fRx ? "RX" : "TX", cb));
250#endif
251 if (fRx)
252 pGuest->mNetStatRx += cb;
253 else
254 pGuest->mNetStatTx += cb;
255
256 return VINF_SUCCESS;
257}
258
259void Guest::i_updateStats(uint64_t iTick)
260{
261 RT_NOREF(iTick);
262
263 uint64_t cbFreeTotal = 0;
264 uint64_t cbAllocTotal = 0;
265 uint64_t cbBalloonedTotal = 0;
266 uint64_t cbSharedTotal = 0;
267 uint64_t cbSharedMem = 0;
268 ULONG uNetStatRx = 0;
269 ULONG uNetStatTx = 0;
270 ULONG aGuestStats[GUESTSTATTYPE_MAX];
271 RT_ZERO(aGuestStats);
272
273 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
274
275 ULONG validStats = mVmValidStats;
276 /* Check if we have anything to report */
277 if (validStats)
278 {
279 mVmValidStats = pm::VMSTATMASK_NONE;
280 memcpy(aGuestStats, mCurrentGuestStat, sizeof(aGuestStats));
281 }
282 alock.release();
283
284 /*
285 * Calling SessionMachine may take time as the object resides in VBoxSVC
286 * process. This is why we took a snapshot of currently collected stats
287 * and released the lock.
288 */
289 Console::SafeVMPtrQuiet ptrVM(mParent);
290 if (ptrVM.isOk())
291 {
292 int rc;
293
294 /*
295 * There is no point in collecting VM shared memory if other memory
296 * statistics are not available yet. Or is there?
297 */
298 if (validStats)
299 {
300 /* Query the missing per-VM memory statistics. */
301 uint64_t cbTotalMemIgn, cbPrivateMemIgn, cbZeroMemIgn;
302 rc = PGMR3QueryMemoryStats(ptrVM.rawUVM(), &cbTotalMemIgn, &cbPrivateMemIgn, &cbSharedMem, &cbZeroMemIgn);
303 if (rc == VINF_SUCCESS)
304 validStats |= pm::VMSTATMASK_GUEST_MEMSHARED;
305 }
306
307 if (mCollectVMMStats)
308 {
309 rc = PGMR3QueryGlobalMemoryStats(ptrVM.rawUVM(), &cbAllocTotal, &cbFreeTotal, &cbBalloonedTotal, &cbSharedTotal);
310 AssertRC(rc);
311 if (rc == VINF_SUCCESS)
312 validStats |= pm::VMSTATMASK_VMM_ALLOC | pm::VMSTATMASK_VMM_FREE
313 | pm::VMSTATMASK_VMM_BALOON | pm::VMSTATMASK_VMM_SHARED;
314 }
315
316 uint64_t uRxPrev = mNetStatRx;
317 uint64_t uTxPrev = mNetStatTx;
318 mNetStatRx = mNetStatTx = 0;
319 rc = STAMR3Enum(ptrVM.rawUVM(), "/Public/Net/*/Bytes*", i_staticEnumStatsCallback, this);
320 AssertRC(rc);
321
322 uint64_t uTsNow = RTTimeNanoTS();
323 uint64_t cNsPassed = uTsNow - mNetStatLastTs;
324 if (cNsPassed >= 1000)
325 {
326 mNetStatLastTs = uTsNow;
327
328 uNetStatRx = (ULONG)((mNetStatRx - uRxPrev) * 1000000 / (cNsPassed / 1000)); /* in bytes per second */
329 uNetStatTx = (ULONG)((mNetStatTx - uTxPrev) * 1000000 / (cNsPassed / 1000)); /* in bytes per second */
330 validStats |= pm::VMSTATMASK_NET_RX | pm::VMSTATMASK_NET_TX;
331 LogFlowThisFunc(("Net Rx=%llu Tx=%llu Ts=%llu Delta=%llu\n", mNetStatRx, mNetStatTx, uTsNow, cNsPassed));
332 }
333 else
334 {
335 /* Can happen on resume or if we're using a non-monotonic clock
336 source for the timer and the time is adjusted. */
337 mNetStatRx = uRxPrev;
338 mNetStatTx = uTxPrev;
339 LogThisFunc(("Net Ts=%llu cNsPassed=%llu - too small interval\n", uTsNow, cNsPassed));
340 }
341 }
342
343 mParent->i_reportVmStatistics(validStats,
344 aGuestStats[GUESTSTATTYPE_CPUUSER],
345 aGuestStats[GUESTSTATTYPE_CPUKERNEL],
346 aGuestStats[GUESTSTATTYPE_CPUIDLE],
347 /* Convert the units for RAM usage stats: page (4K) -> 1KB units */
348 mCurrentGuestStat[GUESTSTATTYPE_MEMTOTAL] * (_4K/_1K),
349 mCurrentGuestStat[GUESTSTATTYPE_MEMFREE] * (_4K/_1K),
350 mCurrentGuestStat[GUESTSTATTYPE_MEMBALLOON] * (_4K/_1K),
351 (ULONG)(cbSharedMem / _1K), /* bytes -> KB */
352 mCurrentGuestStat[GUESTSTATTYPE_MEMCACHE] * (_4K/_1K),
353 mCurrentGuestStat[GUESTSTATTYPE_PAGETOTAL] * (_4K/_1K),
354 (ULONG)(cbAllocTotal / _1K), /* bytes -> KB */
355 (ULONG)(cbFreeTotal / _1K),
356 (ULONG)(cbBalloonedTotal / _1K),
357 (ULONG)(cbSharedTotal / _1K),
358 uNetStatRx,
359 uNetStatTx);
360}
361
362// IGuest properties
363/////////////////////////////////////////////////////////////////////////////
364
365HRESULT Guest::getOSTypeId(com::Utf8Str &aOSTypeId)
366{
367 HRESULT hrc = S_OK;
368 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
369 if (!mData.mInterfaceVersion.isEmpty())
370 aOSTypeId = mData.mOSTypeId;
371 else
372 {
373 /* Redirect the call to IMachine if no additions are installed. */
374 ComPtr<IMachine> ptrMachine(mParent->i_machine());
375 alock.release();
376 BSTR bstr;
377 hrc = ptrMachine->COMGETTER(OSTypeId)(&bstr);
378 aOSTypeId = bstr;
379 }
380 return hrc;
381}
382
383HRESULT Guest::getAdditionsRunLevel(AdditionsRunLevelType_T *aAdditionsRunLevel)
384{
385 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
386
387 *aAdditionsRunLevel = mData.mAdditionsRunLevel;
388
389 return S_OK;
390}
391
392HRESULT Guest::getAdditionsVersion(com::Utf8Str &aAdditionsVersion)
393{
394 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
395 HRESULT hrc = S_OK;
396
397 /*
398 * Return the ReportGuestInfo2 version info if available.
399 */
400 if ( !mData.mAdditionsVersionNew.isEmpty()
401 || mData.mAdditionsRunLevel <= AdditionsRunLevelType_None)
402 aAdditionsVersion = mData.mAdditionsVersionNew;
403 else
404 {
405 /*
406 * If we're running older guest additions (< 3.2.0) try get it from
407 * the guest properties. Detected switched around Version and
408 * Revision in early 3.1.x releases (see r57115).
409 */
410 ComPtr<IMachine> ptrMachine = mParent->i_machine();
411 alock.release(); /* No need to hold this during the IPC fun. */
412
413 Bstr bstr;
414 hrc = ptrMachine->GetGuestPropertyValue(Bstr("/VirtualBox/GuestAdd/Version").raw(), bstr.asOutParam());
415 if ( SUCCEEDED(hrc)
416 && !bstr.isEmpty())
417 {
418 Utf8Str str(bstr);
419 if (str.count('.') == 0)
420 hrc = ptrMachine->GetGuestPropertyValue(Bstr("/VirtualBox/GuestAdd/Revision").raw(), bstr.asOutParam());
421 str = bstr;
422 if (str.count('.') != 2)
423 hrc = E_FAIL;
424 }
425
426 if (SUCCEEDED(hrc))
427 aAdditionsVersion = bstr;
428 else
429 {
430 /* Returning 1.4 is better than nothing. */
431 alock.acquire();
432 aAdditionsVersion = mData.mInterfaceVersion;
433 hrc = S_OK;
434 }
435 }
436 return hrc;
437}
438
439HRESULT Guest::getAdditionsRevision(ULONG *aAdditionsRevision)
440{
441 HRESULT hrc = S_OK;
442 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
443
444 /*
445 * Return the ReportGuestInfo2 version info if available.
446 */
447 if ( !mData.mAdditionsVersionNew.isEmpty()
448 || mData.mAdditionsRunLevel <= AdditionsRunLevelType_None)
449 *aAdditionsRevision = mData.mAdditionsRevision;
450 else
451 {
452 /*
453 * If we're running older guest additions (< 3.2.0) try get it from
454 * the guest properties. Detected switched around Version and
455 * Revision in early 3.1.x releases (see r57115).
456 */
457 ComPtr<IMachine> ptrMachine = mParent->i_machine();
458 alock.release(); /* No need to hold this during the IPC fun. */
459
460 Bstr bstr;
461 hrc = ptrMachine->GetGuestPropertyValue(Bstr("/VirtualBox/GuestAdd/Revision").raw(), bstr.asOutParam());
462 if (SUCCEEDED(hrc))
463 {
464 Utf8Str str(bstr);
465 uint32_t uRevision;
466 int vrc = RTStrToUInt32Full(str.c_str(), 0, &uRevision);
467 if (vrc != VINF_SUCCESS && str.count('.') == 2)
468 {
469 hrc = ptrMachine->GetGuestPropertyValue(Bstr("/VirtualBox/GuestAdd/Version").raw(), bstr.asOutParam());
470 if (SUCCEEDED(hrc))
471 {
472 str = bstr;
473 vrc = RTStrToUInt32Full(str.c_str(), 0, &uRevision);
474 }
475 }
476 if (vrc == VINF_SUCCESS)
477 *aAdditionsRevision = uRevision;
478 else
479 hrc = VBOX_E_IPRT_ERROR;
480 }
481 if (FAILED(hrc))
482 {
483 /* Return 0 if we don't know. */
484 *aAdditionsRevision = 0;
485 hrc = S_OK;
486 }
487 }
488 return hrc;
489}
490
491HRESULT Guest::getDnDSource(ComPtr<IGuestDnDSource> &aDnDSource)
492{
493#ifndef VBOX_WITH_DRAG_AND_DROP
494 ReturnComNotImplemented();
495#else
496 LogFlowThisFuncEnter();
497
498 /* No need to lock - lifetime constant. */
499 HRESULT hr = mDnDSource.queryInterfaceTo(aDnDSource.asOutParam());
500
501 LogFlowFuncLeaveRC(hr);
502 return hr;
503#endif /* VBOX_WITH_DRAG_AND_DROP */
504}
505
506HRESULT Guest::getDnDTarget(ComPtr<IGuestDnDTarget> &aDnDTarget)
507{
508#ifndef VBOX_WITH_DRAG_AND_DROP
509 ReturnComNotImplemented();
510#else
511 LogFlowThisFuncEnter();
512
513 /* No need to lock - lifetime constant. */
514 HRESULT hr = mDnDTarget.queryInterfaceTo(aDnDTarget.asOutParam());
515
516 LogFlowFuncLeaveRC(hr);
517 return hr;
518#endif /* VBOX_WITH_DRAG_AND_DROP */
519}
520
521HRESULT Guest::getEventSource(ComPtr<IEventSource> &aEventSource)
522{
523 LogFlowThisFuncEnter();
524
525 /* No need to lock - lifetime constant. */
526 mEventSource.queryInterfaceTo(aEventSource.asOutParam());
527
528 LogFlowFuncLeaveRC(S_OK);
529 return S_OK;
530}
531
532HRESULT Guest::getFacilities(std::vector<ComPtr<IAdditionsFacility> > &aFacilities)
533{
534 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
535
536 aFacilities.resize(mData.mFacilityMap.size());
537 size_t i = 0;
538 for (FacilityMapIter it = mData.mFacilityMap.begin(); it != mData.mFacilityMap.end(); ++it, ++i)
539 it->second.queryInterfaceTo(aFacilities[i].asOutParam());
540
541 return S_OK;
542}
543
544HRESULT Guest::getSessions(std::vector<ComPtr<IGuestSession> > &aSessions)
545{
546#ifdef VBOX_WITH_GUEST_CONTROL
547 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
548
549 aSessions.resize(mData.mGuestSessions.size());
550 size_t i = 0;
551 for (GuestSessions::iterator it = mData.mGuestSessions.begin(); it != mData.mGuestSessions.end(); ++it, ++i)
552 it->second.queryInterfaceTo(aSessions[i].asOutParam());
553
554 return S_OK;
555#else
556 ReturnComNotImplemented();
557#endif
558}
559
560BOOL Guest::i_isPageFusionEnabled()
561{
562 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
563
564 return mfPageFusionEnabled;
565}
566
567HRESULT Guest::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
568{
569 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
570
571 *aMemoryBalloonSize = mMemoryBalloonSize;
572
573 return S_OK;
574}
575
576HRESULT Guest::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
577{
578 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
579
580 /* We must be 100% sure that IMachine::COMSETTER(MemoryBalloonSize)
581 * does not call us back in any way! */
582 HRESULT ret = mParent->i_machine()->COMSETTER(MemoryBalloonSize)(aMemoryBalloonSize);
583 if (ret == S_OK)
584 {
585 mMemoryBalloonSize = aMemoryBalloonSize;
586 /* forward the information to the VMM device */
587 VMMDev *pVMMDev = mParent->i_getVMMDev();
588 /* MUST release all locks before calling VMM device as its critsect
589 * has higher lock order than anything in Main. */
590 alock.release();
591 if (pVMMDev)
592 {
593 PPDMIVMMDEVPORT pVMMDevPort = pVMMDev->getVMMDevPort();
594 if (pVMMDevPort)
595 pVMMDevPort->pfnSetMemoryBalloon(pVMMDevPort, aMemoryBalloonSize);
596 }
597 }
598
599 return ret;
600}
601
602HRESULT Guest::getStatisticsUpdateInterval(ULONG *aStatisticsUpdateInterval)
603{
604 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
605
606 *aStatisticsUpdateInterval = mStatUpdateInterval;
607 return S_OK;
608}
609
610HRESULT Guest::setStatisticsUpdateInterval(ULONG aStatisticsUpdateInterval)
611{
612 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
613
614 if (mStatUpdateInterval)
615 if (aStatisticsUpdateInterval == 0)
616 RTTimerLRStop(mStatTimer);
617 else
618 RTTimerLRChangeInterval(mStatTimer, aStatisticsUpdateInterval);
619 else
620 if (aStatisticsUpdateInterval != 0)
621 {
622 RTTimerLRChangeInterval(mStatTimer, aStatisticsUpdateInterval);
623 RTTimerLRStart(mStatTimer, 0);
624 }
625 mStatUpdateInterval = aStatisticsUpdateInterval;
626 /* forward the information to the VMM device */
627 VMMDev *pVMMDev = mParent->i_getVMMDev();
628 /* MUST release all locks before calling VMM device as its critsect
629 * has higher lock order than anything in Main. */
630 alock.release();
631 if (pVMMDev)
632 {
633 PPDMIVMMDEVPORT pVMMDevPort = pVMMDev->getVMMDevPort();
634 if (pVMMDevPort)
635 pVMMDevPort->pfnSetStatisticsInterval(pVMMDevPort, aStatisticsUpdateInterval);
636 }
637
638 return S_OK;
639}
640
641
642HRESULT Guest::internalGetStatistics(ULONG *aCpuUser, ULONG *aCpuKernel, ULONG *aCpuIdle,
643 ULONG *aMemTotal, ULONG *aMemFree, ULONG *aMemBalloon,
644 ULONG *aMemShared, ULONG *aMemCache, ULONG *aPageTotal,
645 ULONG *aMemAllocTotal, ULONG *aMemFreeTotal,
646 ULONG *aMemBalloonTotal, ULONG *aMemSharedTotal)
647{
648 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
649
650 *aCpuUser = mCurrentGuestStat[GUESTSTATTYPE_CPUUSER];
651 *aCpuKernel = mCurrentGuestStat[GUESTSTATTYPE_CPUKERNEL];
652 *aCpuIdle = mCurrentGuestStat[GUESTSTATTYPE_CPUIDLE];
653 *aMemTotal = mCurrentGuestStat[GUESTSTATTYPE_MEMTOTAL] * (_4K/_1K); /* page (4K) -> 1KB units */
654 *aMemFree = mCurrentGuestStat[GUESTSTATTYPE_MEMFREE] * (_4K/_1K); /* page (4K) -> 1KB units */
655 *aMemBalloon = mCurrentGuestStat[GUESTSTATTYPE_MEMBALLOON] * (_4K/_1K); /* page (4K) -> 1KB units */
656 *aMemCache = mCurrentGuestStat[GUESTSTATTYPE_MEMCACHE] * (_4K/_1K); /* page (4K) -> 1KB units */
657 *aPageTotal = mCurrentGuestStat[GUESTSTATTYPE_PAGETOTAL] * (_4K/_1K); /* page (4K) -> 1KB units */
658
659 /* Play safe or smth? */
660 *aMemAllocTotal = 0;
661 *aMemFreeTotal = 0;
662 *aMemBalloonTotal = 0;
663 *aMemSharedTotal = 0;
664 *aMemShared = 0;
665
666 /* MUST release all locks before calling any PGM statistics queries,
667 * as they are executed by EMT and that might deadlock us by VMM device
668 * activity which waits for the Guest object lock. */
669 alock.release();
670 Console::SafeVMPtr ptrVM(mParent);
671 if (!ptrVM.isOk())
672 return E_FAIL;
673
674 uint64_t cbFreeTotal, cbAllocTotal, cbBalloonedTotal, cbSharedTotal;
675 int rc = PGMR3QueryGlobalMemoryStats(ptrVM.rawUVM(), &cbAllocTotal, &cbFreeTotal, &cbBalloonedTotal, &cbSharedTotal);
676 AssertRCReturn(rc, E_FAIL);
677
678 *aMemAllocTotal = (ULONG)(cbAllocTotal / _1K); /* bytes -> KB */
679 *aMemFreeTotal = (ULONG)(cbFreeTotal / _1K);
680 *aMemBalloonTotal = (ULONG)(cbBalloonedTotal / _1K);
681 *aMemSharedTotal = (ULONG)(cbSharedTotal / _1K);
682
683 /* Query the missing per-VM memory statistics. */
684 uint64_t cbTotalMemIgn, cbPrivateMemIgn, cbSharedMem, cbZeroMemIgn;
685 rc = PGMR3QueryMemoryStats(ptrVM.rawUVM(), &cbTotalMemIgn, &cbPrivateMemIgn, &cbSharedMem, &cbZeroMemIgn);
686 AssertRCReturn(rc, E_FAIL);
687 *aMemShared = (ULONG)(cbSharedMem / _1K);
688
689 return S_OK;
690}
691
692HRESULT Guest::i_setStatistic(ULONG aCpuId, GUESTSTATTYPE enmType, ULONG aVal)
693{
694 static ULONG indexToPerfMask[] =
695 {
696 pm::VMSTATMASK_GUEST_CPUUSER,
697 pm::VMSTATMASK_GUEST_CPUKERNEL,
698 pm::VMSTATMASK_GUEST_CPUIDLE,
699 pm::VMSTATMASK_GUEST_MEMTOTAL,
700 pm::VMSTATMASK_GUEST_MEMFREE,
701 pm::VMSTATMASK_GUEST_MEMBALLOON,
702 pm::VMSTATMASK_GUEST_MEMCACHE,
703 pm::VMSTATMASK_GUEST_PAGETOTAL,
704 pm::VMSTATMASK_NONE
705 };
706 AutoCaller autoCaller(this);
707 if (FAILED(autoCaller.rc())) return autoCaller.rc();
708
709 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
710
711 if (enmType >= GUESTSTATTYPE_MAX)
712 return E_INVALIDARG;
713
714 if (aCpuId < VMM_MAX_CPU_COUNT)
715 {
716 ULONG *paCpuStats;
717 switch (enmType)
718 {
719 case GUESTSTATTYPE_CPUUSER: paCpuStats = mCurrentGuestCpuUserStat; break;
720 case GUESTSTATTYPE_CPUKERNEL: paCpuStats = mCurrentGuestCpuKernelStat; break;
721 case GUESTSTATTYPE_CPUIDLE: paCpuStats = mCurrentGuestCpuIdleStat; break;
722 default: paCpuStats = NULL; break;
723 }
724 if (paCpuStats)
725 {
726 paCpuStats[aCpuId] = aVal;
727 aVal = 0;
728 for (uint32_t i = 0; i < mCpus && i < VMM_MAX_CPU_COUNT; i++)
729 aVal += paCpuStats[i];
730 aVal /= mCpus;
731 }
732 }
733
734 mCurrentGuestStat[enmType] = aVal;
735 mVmValidStats |= indexToPerfMask[enmType];
736 return S_OK;
737}
738
739/**
740 * Returns the status of a specified Guest Additions facility.
741 *
742 * @return aStatus Current status of specified facility.
743 * @param aType Facility to get the status from.
744 * @param aTimestamp Timestamp of last facility status update in ms (optional).
745 */
746HRESULT Guest::getFacilityStatus(AdditionsFacilityType_T aFacility, LONG64 *aTimestamp, AdditionsFacilityStatus_T *aStatus)
747{
748 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
749
750 /* Not checking for aTimestamp is intentional; it's optional. */
751 FacilityMapIterConst it = mData.mFacilityMap.find(aFacility);
752 if (it != mData.mFacilityMap.end())
753 {
754 AdditionsFacility *pFacility = it->second;
755 ComAssert(pFacility);
756 *aStatus = pFacility->i_getStatus();
757 if (aTimestamp)
758 *aTimestamp = pFacility->i_getLastUpdated();
759 }
760 else
761 {
762 /*
763 * Do not fail here -- could be that the facility never has been brought up (yet) but
764 * the host wants to have its status anyway. So just tell we don't know at this point.
765 */
766 *aStatus = AdditionsFacilityStatus_Unknown;
767 if (aTimestamp)
768 *aTimestamp = RTTimeMilliTS();
769 }
770 return S_OK;
771}
772
773HRESULT Guest::getAdditionsStatus(AdditionsRunLevelType_T aLevel, BOOL *aActive)
774{
775 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
776
777 HRESULT rc = S_OK;
778 switch (aLevel)
779 {
780 case AdditionsRunLevelType_System:
781 *aActive = (mData.mAdditionsRunLevel > AdditionsRunLevelType_None);
782 break;
783
784 case AdditionsRunLevelType_Userland:
785 *aActive = (mData.mAdditionsRunLevel >= AdditionsRunLevelType_Userland);
786 break;
787
788 case AdditionsRunLevelType_Desktop:
789 *aActive = (mData.mAdditionsRunLevel >= AdditionsRunLevelType_Desktop);
790 break;
791
792 default:
793 rc = setError(VBOX_E_NOT_SUPPORTED,
794 tr("Invalid status level defined: %u"), aLevel);
795 break;
796 }
797
798 return rc;
799}
800HRESULT Guest::setCredentials(const com::Utf8Str &aUserName, const com::Utf8Str &aPassword,
801 const com::Utf8Str &aDomain, BOOL aAllowInteractiveLogon)
802{
803 /* Check for magic domain names which are used to pass encryption keys to the disk. */
804 if (Utf8Str(aDomain) == "@@disk")
805 return mParent->i_setDiskEncryptionKeys(aPassword);
806 else if (Utf8Str(aDomain) == "@@mem")
807 {
808 /** @todo */
809 return E_NOTIMPL;
810 }
811 else
812 {
813 /* forward the information to the VMM device */
814 VMMDev *pVMMDev = mParent->i_getVMMDev();
815 if (pVMMDev)
816 {
817 PPDMIVMMDEVPORT pVMMDevPort = pVMMDev->getVMMDevPort();
818 if (pVMMDevPort)
819 {
820 uint32_t u32Flags = VMMDEV_SETCREDENTIALS_GUESTLOGON;
821 if (!aAllowInteractiveLogon)
822 u32Flags = VMMDEV_SETCREDENTIALS_NOLOCALLOGON;
823
824 pVMMDevPort->pfnSetCredentials(pVMMDevPort,
825 aUserName.c_str(),
826 aPassword.c_str(),
827 aDomain.c_str(),
828 u32Flags);
829 return S_OK;
830 }
831 }
832 }
833
834 return setError(VBOX_E_VM_ERROR,
835 tr("VMM device is not available (is the VM running?)"));
836}
837
838// public methods only for internal purposes
839/////////////////////////////////////////////////////////////////////////////
840
841/**
842 * Sets the general Guest Additions information like
843 * API (interface) version and OS type. Gets called by
844 * vmmdevUpdateGuestInfo.
845 *
846 * @param aInterfaceVersion
847 * @param aOsType
848 */
849void Guest::i_setAdditionsInfo(com::Utf8Str aInterfaceVersion, VBOXOSTYPE aOsType)
850{
851 RTTIMESPEC TimeSpecTS;
852 RTTimeNow(&TimeSpecTS);
853
854 AutoCaller autoCaller(this);
855 AssertComRCReturnVoid(autoCaller.rc());
856
857 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
858
859
860 /*
861 * Note: The Guest Additions API (interface) version is deprecated
862 * and will not be used anymore! We might need it to at least report
863 * something as version number if *really* ancient Guest Additions are
864 * installed (without the guest version + revision properties having set).
865 */
866 mData.mInterfaceVersion = aInterfaceVersion;
867
868 /*
869 * Older Additions rely on the Additions API version whether they
870 * are assumed to be active or not. Since newer Additions do report
871 * the Additions version *before* calling this function (by calling
872 * VMMDevReportGuestInfo2, VMMDevReportGuestStatus, VMMDevReportGuestInfo,
873 * in that order) we can tell apart old and new Additions here. Old
874 * Additions never would set VMMDevReportGuestInfo2 (which set mData.mAdditionsVersion)
875 * so they just rely on the aInterfaceVersion string (which gets set by
876 * VMMDevReportGuestInfo).
877 *
878 * So only mark the Additions as being active (run level = system) when we
879 * don't have the Additions version set.
880 */
881 if (mData.mAdditionsVersionNew.isEmpty())
882 {
883 if (aInterfaceVersion.isEmpty())
884 mData.mAdditionsRunLevel = AdditionsRunLevelType_None;
885 else
886 {
887 mData.mAdditionsRunLevel = AdditionsRunLevelType_System;
888
889 /*
890 * To keep it compatible with the old Guest Additions behavior we need to set the
891 * "graphics" (feature) facility to active as soon as we got the Guest Additions
892 * interface version.
893 */
894 i_facilityUpdate(VBoxGuestFacilityType_Graphics, VBoxGuestFacilityStatus_Active, 0 /*fFlags*/, &TimeSpecTS);
895 }
896 }
897
898 /*
899 * Older Additions didn't have this finer grained capability bit,
900 * so enable it by default. Newer Additions will not enable this here
901 * and use the setSupportedFeatures function instead.
902 */
903 /** @todo r=bird: I don't get the above comment nor the code below...
904 * One talks about capability bits, the one always does something to a facility.
905 * Then there is the comment below it all, which is placed like it addresses the
906 * mOSTypeId, but talks about something which doesn't remotely like mOSTypeId...
907 *
908 * Andy, could you please try clarify and make the comments shorter and more
909 * coherent! Also, explain why this is important and what depends on it.
910 *
911 * PS. There is the VMMDEV_GUEST_SUPPORTS_GRAPHICS capability* report... It
912 * should come in pretty quickly after this update, normally.
913 */
914 i_facilityUpdate(VBoxGuestFacilityType_Graphics,
915 i_facilityIsActive(VBoxGuestFacilityType_VBoxGuestDriver)
916 ? VBoxGuestFacilityStatus_Active : VBoxGuestFacilityStatus_Inactive,
917 0 /*fFlags*/, &TimeSpecTS); /** @todo the timestamp isn't gonna be right here on saved state restore. */
918
919 /*
920 * Note! There is a race going on between setting mAdditionsRunLevel and
921 * mSupportsGraphics here and disabling/enabling it later according to
922 * its real status when using new(er) Guest Additions.
923 */
924 mData.mOSType = aOsType;
925 mData.mOSTypeId = Global::OSTypeId(aOsType);
926}
927
928/**
929 * Sets the Guest Additions version information details.
930 *
931 * Gets called by vmmdevUpdateGuestInfo2 and vmmdevUpdateGuestInfo (to clear the
932 * state).
933 *
934 * @param a_uFullVersion VBoxGuestInfo2::additionsMajor,
935 * VBoxGuestInfo2::additionsMinor and
936 * VBoxGuestInfo2::additionsBuild combined into
937 * one value by VBOX_FULL_VERSION_MAKE.
938 *
939 * When this is 0, it's vmmdevUpdateGuestInfo
940 * calling to reset the state.
941 *
942 * @param a_pszName Build type tag and/or publisher tag, empty
943 * string if neiter of those are present.
944 * @param a_uRevision See VBoxGuestInfo2::additionsRevision.
945 * @param a_fFeatures See VBoxGuestInfo2::additionsFeatures.
946 */
947void Guest::i_setAdditionsInfo2(uint32_t a_uFullVersion, const char *a_pszName, uint32_t a_uRevision, uint32_t a_fFeatures)
948{
949 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
950
951 if (a_uFullVersion)
952 {
953 mData.mAdditionsVersionNew = Utf8StrFmt(*a_pszName ? "%u.%u.%u_%s" : "%u.%u.%u",
954 VBOX_FULL_VERSION_GET_MAJOR(a_uFullVersion),
955 VBOX_FULL_VERSION_GET_MINOR(a_uFullVersion),
956 VBOX_FULL_VERSION_GET_BUILD(a_uFullVersion),
957 a_pszName);
958 mData.mAdditionsVersionFull = a_uFullVersion;
959 mData.mAdditionsRevision = a_uRevision;
960 mData.mAdditionsFeatures = a_fFeatures;
961 }
962 else
963 {
964 Assert(!a_fFeatures && !a_uRevision && !*a_pszName);
965 mData.mAdditionsVersionNew.setNull();
966 mData.mAdditionsVersionFull = 0;
967 mData.mAdditionsRevision = 0;
968 mData.mAdditionsFeatures = 0;
969 }
970}
971
972bool Guest::i_facilityIsActive(VBoxGuestFacilityType enmFacility)
973{
974 Assert(enmFacility < INT32_MAX);
975 FacilityMapIterConst it = mData.mFacilityMap.find((AdditionsFacilityType_T)enmFacility);
976 if (it != mData.mFacilityMap.end())
977 {
978 AdditionsFacility *pFac = it->second;
979 return (pFac->i_getStatus() == AdditionsFacilityStatus_Active);
980 }
981 return false;
982}
983
984void Guest::i_facilityUpdate(VBoxGuestFacilityType a_enmFacility, VBoxGuestFacilityStatus a_enmStatus,
985 uint32_t a_fFlags, PCRTTIMESPEC a_pTimeSpecTS)
986{
987 AssertReturnVoid( a_enmFacility < VBoxGuestFacilityType_All
988 && a_enmFacility > VBoxGuestFacilityType_Unknown);
989
990 FacilityMapIter it = mData.mFacilityMap.find((AdditionsFacilityType_T)a_enmFacility);
991 if (it != mData.mFacilityMap.end())
992 {
993 AdditionsFacility *pFac = it->second;
994 pFac->i_update((AdditionsFacilityStatus_T)a_enmStatus, a_fFlags, a_pTimeSpecTS);
995 }
996 else
997 {
998 if (mData.mFacilityMap.size() > 64)
999 {
1000 /* The easy way out for now. We could automatically destroy
1001 inactive facilities like VMMDev does if we like... */
1002 AssertFailedReturnVoid();
1003 }
1004
1005 ComObjPtr<AdditionsFacility> ptrFac;
1006 ptrFac.createObject();
1007 AssertReturnVoid(!ptrFac.isNull());
1008
1009 HRESULT hrc = ptrFac->init(this, (AdditionsFacilityType_T)a_enmFacility, (AdditionsFacilityStatus_T)a_enmStatus,
1010 a_fFlags, a_pTimeSpecTS);
1011 if (SUCCEEDED(hrc))
1012 mData.mFacilityMap.insert(std::make_pair((AdditionsFacilityType_T)a_enmFacility, ptrFac));
1013 }
1014}
1015
1016/**
1017 * Issued by the guest when a guest user changed its state.
1018 *
1019 * @return IPRT status code.
1020 * @param aUser Guest user name.
1021 * @param aDomain Domain of guest user account. Optional.
1022 * @param enmState New state to indicate.
1023 * @param pbDetails Pointer to state details. Optional.
1024 * @param cbDetails Size (in bytes) of state details. Pass 0 if not used.
1025 */
1026void Guest::i_onUserStateChange(Bstr aUser, Bstr aDomain, VBoxGuestUserState enmState,
1027 const uint8_t *pbDetails, uint32_t cbDetails)
1028{
1029 RT_NOREF(pbDetails, cbDetails);
1030 LogFlowThisFunc(("\n"));
1031
1032 AutoCaller autoCaller(this);
1033 AssertComRCReturnVoid(autoCaller.rc());
1034
1035 Bstr strDetails; /** @todo Implement state details here. */
1036
1037 fireGuestUserStateChangedEvent(mEventSource, aUser.raw(), aDomain.raw(),
1038 (GuestUserState_T)enmState, strDetails.raw());
1039 LogFlowFuncLeave();
1040}
1041
1042/**
1043 * Sets the status of a certain Guest Additions facility.
1044 *
1045 * Gets called by vmmdevUpdateGuestStatus, which just passes the report along.
1046 *
1047 * @param a_pInterface Pointer to this interface.
1048 * @param a_enmFacility The facility.
1049 * @param a_enmStatus The status.
1050 * @param a_fFlags Flags assoicated with the update. Currently
1051 * reserved and should be ignored.
1052 * @param a_pTimeSpecTS Pointer to the timestamp of this report.
1053 * @sa PDMIVMMDEVCONNECTOR::pfnUpdateGuestStatus, vmmdevUpdateGuestStatus
1054 * @thread The emulation thread.
1055 */
1056void Guest::i_setAdditionsStatus(VBoxGuestFacilityType a_enmFacility, VBoxGuestFacilityStatus a_enmStatus,
1057 uint32_t a_fFlags, PCRTTIMESPEC a_pTimeSpecTS)
1058{
1059 Assert( a_enmFacility > VBoxGuestFacilityType_Unknown
1060 && a_enmFacility <= VBoxGuestFacilityType_All); /* Paranoia, VMMDev checks for this. */
1061
1062 AutoCaller autoCaller(this);
1063 AssertComRCReturnVoid(autoCaller.rc());
1064
1065 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1066
1067 /*
1068 * Set a specific facility status.
1069 */
1070 if (a_enmFacility == VBoxGuestFacilityType_All)
1071 for (FacilityMapIter it = mData.mFacilityMap.begin(); it != mData.mFacilityMap.end(); ++it)
1072 i_facilityUpdate((VBoxGuestFacilityType)it->first, a_enmStatus, a_fFlags, a_pTimeSpecTS);
1073 else /* Update one facility only. */
1074 i_facilityUpdate(a_enmFacility, a_enmStatus, a_fFlags, a_pTimeSpecTS);
1075
1076 /*
1077 * Recalc the runlevel.
1078 */
1079 if (i_facilityIsActive(VBoxGuestFacilityType_VBoxTrayClient))
1080 mData.mAdditionsRunLevel = AdditionsRunLevelType_Desktop;
1081 else if (i_facilityIsActive(VBoxGuestFacilityType_VBoxService))
1082 mData.mAdditionsRunLevel = AdditionsRunLevelType_Userland;
1083 else if (i_facilityIsActive(VBoxGuestFacilityType_VBoxGuestDriver))
1084 mData.mAdditionsRunLevel = AdditionsRunLevelType_System;
1085 else
1086 mData.mAdditionsRunLevel = AdditionsRunLevelType_None;
1087}
1088
1089/**
1090 * Sets the supported features (and whether they are active or not).
1091 *
1092 * @param fCaps Guest capability bit mask (VMMDEV_GUEST_SUPPORTS_XXX).
1093 */
1094void Guest::i_setSupportedFeatures(uint32_t aCaps)
1095{
1096 AutoCaller autoCaller(this);
1097 AssertComRCReturnVoid(autoCaller.rc());
1098
1099 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1100
1101 /** @todo A nit: The timestamp is wrong on saved state restore. Would be better
1102 * to move the graphics and seamless capability -> facility translation to
1103 * VMMDev so this could be saved. */
1104 RTTIMESPEC TimeSpecTS;
1105 RTTimeNow(&TimeSpecTS);
1106
1107 i_facilityUpdate(VBoxGuestFacilityType_Seamless,
1108 aCaps & VMMDEV_GUEST_SUPPORTS_SEAMLESS ? VBoxGuestFacilityStatus_Active : VBoxGuestFacilityStatus_Inactive,
1109 0 /*fFlags*/, &TimeSpecTS);
1110 /** @todo Add VMMDEV_GUEST_SUPPORTS_GUEST_HOST_WINDOW_MAPPING */
1111}
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