VirtualBox

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

Last change on this file since 58212 was 57425, checked in by vboxsync, 9 years ago

DECLCALLBACK

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