VirtualBox

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

Last change on this file since 79812 was 79294, checked in by vboxsync, 6 years ago

Main/Guest::init: Better error and object status handling - DnD should *NOT* hide event source creation trouble!

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