VirtualBox

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

Last change on this file since 98103 was 98103, checked in by vboxsync, 23 months ago

Copyright year updates by scm.

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