VirtualBox

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

Last change on this file since 81305 was 80670, checked in by vboxsync, 5 years ago

Main/Guest: Don't start the statistics collection thread until needed. Assert and check every timer call.

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