VirtualBox

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

Last change on this file since 67414 was 65120, checked in by vboxsync, 8 years ago

Main: doxygen fixes

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