VirtualBox

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

Last change on this file was 102533, checked in by vboxsync, 5 months ago

Main/Guest Control: Fixed a shutdown race when uninitializing guest sessions via IGuestSession::Close(): There we need to check if its parent object (IGuest) still is around and in a working shape, and if not, do the cleanup stuff ourselves. Renamed Guest::i_sessionDestroy() -> Guest::i_sessionRemove() to make its function more clear. This race seemed to happen in combination with FE/Qt's guest file manager every now and then. bugref:9135

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

© 2023 Oracle
ContactPrivacy policyTerms of Use