VirtualBox

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

Last change on this file since 56030 was 56030, checked in by vboxsync, 10 years ago

postfix iterator => prefix iterator and stlSize => stlEmpty where appropriate

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