VirtualBox

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

Last change on this file since 94521 was 93468, checked in by vboxsync, 2 years ago

VBoxDbg,VMM/STAM,Main: Converted VBoxDbg to use the VMM function table, extending the STAMR3Enum to include the unit string to avoid needing to call STAMR3GetUnit a lot. The latter would better fit with the XML based viewer, as it only gets the string version of the unit. Had to adjust a user of STAMR3Enum in Main. bugref:10074

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

© 2023 Oracle
ContactPrivacy policyTerms of Use