VirtualBox

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

Last change on this file since 40066 was 40020, checked in by vboxsync, 13 years ago

GuestCtrl/Main: Fixed some locking issues + callback cleanup hangs; fixed a memory leak on callback creation.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 34.5 KB
Line 
1/* $Id: GuestImpl.cpp 40020 2012-02-07 16:02:30Z vboxsync $ */
2/** @file
3 * VirtualBox COM class implementation: Guest
4 */
5
6/*
7 * Copyright (C) 2006-2012 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
20#include "Global.h"
21#include "ConsoleImpl.h"
22#include "ProgressImpl.h"
23#ifdef VBOX_WITH_DRAG_AND_DROP
24# include "GuestDnDImpl.h"
25#endif
26#include "VMMDev.h"
27
28#include "AutoCaller.h"
29#include "Logging.h"
30
31#include <VBox/VMMDev.h>
32#ifdef VBOX_WITH_GUEST_CONTROL
33# include <VBox/com/array.h>
34# include <VBox/com/ErrorInfo.h>
35#endif
36#include <iprt/cpp/utils.h>
37#include <VBox/vmm/pgm.h>
38#include <VBox/version.h>
39
40// defines
41/////////////////////////////////////////////////////////////////////////////
42
43// constructor / destructor
44/////////////////////////////////////////////////////////////////////////////
45
46DEFINE_EMPTY_CTOR_DTOR (Guest)
47
48HRESULT Guest::FinalConstruct()
49{
50 return BaseFinalConstruct();
51}
52
53void Guest::FinalRelease()
54{
55 uninit ();
56 BaseFinalRelease();
57}
58
59// public methods only for internal purposes
60/////////////////////////////////////////////////////////////////////////////
61
62/**
63 * Initializes the guest object.
64 */
65HRESULT Guest::init(Console *aParent)
66{
67 LogFlowThisFunc(("aParent=%p\n", aParent));
68
69 ComAssertRet(aParent, E_INVALIDARG);
70
71 /* Enclose the state transition NotReady->InInit->Ready */
72 AutoInitSpan autoInitSpan(this);
73 AssertReturn(autoInitSpan.isOk(), E_FAIL);
74
75 unconst(mParent) = aParent;
76
77 /* Confirm a successful initialization when it's the case */
78 autoInitSpan.setSucceeded();
79
80 ULONG aMemoryBalloonSize;
81 HRESULT ret = mParent->machine()->COMGETTER(MemoryBalloonSize)(&aMemoryBalloonSize);
82 if (ret == S_OK)
83 mMemoryBalloonSize = aMemoryBalloonSize;
84 else
85 mMemoryBalloonSize = 0; /* Default is no ballooning */
86
87 BOOL fPageFusionEnabled;
88 ret = mParent->machine()->COMGETTER(PageFusionEnabled)(&fPageFusionEnabled);
89 if (ret == S_OK)
90 mfPageFusionEnabled = fPageFusionEnabled;
91 else
92 mfPageFusionEnabled = false; /* Default is no page fusion*/
93
94 mStatUpdateInterval = 0; /* Default is not to report guest statistics at all */
95
96 /* Clear statistics. */
97 for (unsigned i = 0 ; i < GUESTSTATTYPE_MAX; i++)
98 mCurrentGuestStat[i] = 0;
99
100#ifdef VBOX_WITH_GUEST_CONTROL
101 /* Init the context ID counter at 1000. */
102 mNextContextID = 1000;
103#endif
104
105#ifdef VBOX_WITH_DRAG_AND_DROP
106 m_pGuestDnD = new GuestDnD(this);
107#endif
108
109 return S_OK;
110}
111
112/**
113 * Uninitializes the instance and sets the ready flag to FALSE.
114 * Called either from FinalRelease() or by the parent when it gets destroyed.
115 */
116void Guest::uninit()
117{
118 LogFlowThisFunc(("\n"));
119
120 /* Enclose the state transition Ready->InUninit->NotReady */
121 AutoUninitSpan autoUninitSpan(this);
122 if (autoUninitSpan.uninitDone())
123 return;
124
125#ifdef VBOX_WITH_GUEST_CONTROL
126 /* Scope write lock as much as possible. */
127 {
128 /*
129 * Cleanup must be done *before* AutoUninitSpan to cancel all
130 * all outstanding waits in API functions (which hold AutoCaller
131 * ref counts).
132 */
133 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
134
135 /* Notify left over callbacks that we are about to shutdown ... */
136 CallbackMapIter it;
137 for (it = mCallbackMap.begin(); it != mCallbackMap.end(); it++)
138 {
139 int rc2 = callbackNotifyEx(it->first, VERR_CANCELLED,
140 Guest::tr("VM is shutting down, canceling uncompleted guest requests ..."));
141 AssertRC(rc2);
142 }
143
144 /* Destroy left over callback data. */
145 for (it = mCallbackMap.begin(); it != mCallbackMap.end(); it++)
146 callbackDestroy(it->first);
147
148 /* Clear process map (remove all callbacks). */
149 mGuestProcessMap.clear();
150 }
151#endif
152
153#ifdef VBOX_WITH_DRAG_AND_DROP
154 delete m_pGuestDnD;
155 m_pGuestDnD = NULL;
156#endif
157
158 unconst(mParent) = NULL;
159}
160
161// IGuest properties
162/////////////////////////////////////////////////////////////////////////////
163
164STDMETHODIMP Guest::COMGETTER(OSTypeId)(BSTR *a_pbstrOSTypeId)
165{
166 CheckComArgOutPointerValid(a_pbstrOSTypeId);
167
168 AutoCaller autoCaller(this);
169 HRESULT hrc = autoCaller.rc();
170 if (SUCCEEDED(hrc))
171 {
172 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
173 if (!mData.mInterfaceVersion.isEmpty())
174 mData.mOSTypeId.cloneTo(a_pbstrOSTypeId);
175 else
176 {
177 /* Redirect the call to IMachine if no additions are installed. */
178 ComPtr<IMachine> ptrMachine(mParent->machine());
179 alock.release();
180 hrc = ptrMachine->COMGETTER(OSTypeId)(a_pbstrOSTypeId);
181 }
182 }
183 return hrc;
184}
185
186STDMETHODIMP Guest::COMGETTER(AdditionsRunLevel) (AdditionsRunLevelType_T *aRunLevel)
187{
188 AutoCaller autoCaller(this);
189 if (FAILED(autoCaller.rc())) return autoCaller.rc();
190
191 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
192
193 *aRunLevel = mData.mAdditionsRunLevel;
194
195 return S_OK;
196}
197
198STDMETHODIMP Guest::COMGETTER(AdditionsVersion)(BSTR *a_pbstrAdditionsVersion)
199{
200 CheckComArgOutPointerValid(a_pbstrAdditionsVersion);
201
202 AutoCaller autoCaller(this);
203 HRESULT hrc = autoCaller.rc();
204 if (SUCCEEDED(hrc))
205 {
206 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
207
208 /*
209 * Return the ReportGuestInfo2 version info if available.
210 */
211 if ( !mData.mAdditionsVersionNew.isEmpty()
212 || mData.mAdditionsRunLevel <= AdditionsRunLevelType_None)
213 mData.mAdditionsVersionNew.cloneTo(a_pbstrAdditionsVersion);
214 else
215 {
216 /*
217 * If we're running older guest additions (< 3.2.0) try get it from
218 * the guest properties. Detected switched around Version and
219 * Revision in early 3.1.x releases (see r57115).
220 */
221 ComPtr<IMachine> ptrMachine = mParent->machine();
222 alock.release(); /* No need to hold this during the IPC fun. */
223
224 Bstr bstr;
225 hrc = ptrMachine->GetGuestPropertyValue(Bstr("/VirtualBox/GuestAdd/Version").raw(), bstr.asOutParam());
226 if (SUCCEEDED(hrc))
227 {
228 Utf8Str str(bstr);
229 if (str.count('.') == 0)
230 hrc = ptrMachine->GetGuestPropertyValue(Bstr("/VirtualBox/GuestAdd/Revision").raw(), bstr.asOutParam());
231 str = bstr;
232 if (str.count('.') != 2)
233 hrc = E_FAIL;
234 }
235
236 if (SUCCEEDED(hrc))
237 bstr.detachTo(a_pbstrAdditionsVersion);
238 else
239 {
240 /* Returning 1.4 is better than nothing. */
241 alock.acquire();
242 mData.mInterfaceVersion.cloneTo(a_pbstrAdditionsVersion);
243 hrc = S_OK;
244 }
245 }
246 }
247 return hrc;
248}
249
250STDMETHODIMP Guest::COMGETTER(AdditionsRevision)(ULONG *a_puAdditionsRevision)
251{
252 CheckComArgOutPointerValid(a_puAdditionsRevision);
253
254 AutoCaller autoCaller(this);
255 HRESULT hrc = autoCaller.rc();
256 if (SUCCEEDED(hrc))
257 {
258 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
259
260 /*
261 * Return the ReportGuestInfo2 version info if available.
262 */
263 if ( !mData.mAdditionsVersionNew.isEmpty()
264 || mData.mAdditionsRunLevel <= AdditionsRunLevelType_None)
265 *a_puAdditionsRevision = mData.mAdditionsRevision;
266 else
267 {
268 /*
269 * If we're running older guest additions (< 3.2.0) try get it from
270 * the guest properties. Detected switched around Version and
271 * Revision in early 3.1.x releases (see r57115).
272 */
273 ComPtr<IMachine> ptrMachine = mParent->machine();
274 alock.release(); /* No need to hold this during the IPC fun. */
275
276 Bstr bstr;
277 hrc = ptrMachine->GetGuestPropertyValue(Bstr("/VirtualBox/GuestAdd/Revision").raw(), bstr.asOutParam());
278 if (SUCCEEDED(hrc))
279 {
280 Utf8Str str(bstr);
281 uint32_t uRevision;
282 int vrc = RTStrToUInt32Full(str.c_str(), 0, &uRevision);
283 if (vrc != VINF_SUCCESS && str.count('.') == 2)
284 {
285 hrc = ptrMachine->GetGuestPropertyValue(Bstr("/VirtualBox/GuestAdd/Version").raw(), bstr.asOutParam());
286 if (SUCCEEDED(hrc))
287 {
288 str = bstr;
289 vrc = RTStrToUInt32Full(str.c_str(), 0, &uRevision);
290 }
291 }
292 if (vrc == VINF_SUCCESS)
293 *a_puAdditionsRevision = uRevision;
294 else
295 hrc = VBOX_E_IPRT_ERROR;
296 }
297 if (FAILED(hrc))
298 {
299 /* Return 0 if we don't know. */
300 *a_puAdditionsRevision = 0;
301 hrc = S_OK;
302 }
303 }
304 }
305 return hrc;
306}
307
308STDMETHODIMP Guest::COMGETTER(Facilities)(ComSafeArrayOut(IAdditionsFacility*, aFacilities))
309{
310 CheckComArgOutSafeArrayPointerValid(aFacilities);
311
312 AutoCaller autoCaller(this);
313 if (FAILED(autoCaller.rc())) return autoCaller.rc();
314
315 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
316
317 SafeIfaceArray<IAdditionsFacility> fac(mData.mFacilityMap);
318 fac.detachTo(ComSafeArrayOutArg(aFacilities));
319
320 return S_OK;
321}
322
323BOOL Guest::isPageFusionEnabled()
324{
325 AutoCaller autoCaller(this);
326 if (FAILED(autoCaller.rc())) return false;
327
328 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
329
330 return mfPageFusionEnabled;
331}
332
333STDMETHODIMP Guest::COMGETTER(MemoryBalloonSize)(ULONG *aMemoryBalloonSize)
334{
335 CheckComArgOutPointerValid(aMemoryBalloonSize);
336
337 AutoCaller autoCaller(this);
338 if (FAILED(autoCaller.rc())) return autoCaller.rc();
339
340 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
341
342 *aMemoryBalloonSize = mMemoryBalloonSize;
343
344 return S_OK;
345}
346
347STDMETHODIMP Guest::COMSETTER(MemoryBalloonSize)(ULONG aMemoryBalloonSize)
348{
349 AutoCaller autoCaller(this);
350 if (FAILED(autoCaller.rc())) return autoCaller.rc();
351
352 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
353
354 /* We must be 100% sure that IMachine::COMSETTER(MemoryBalloonSize)
355 * does not call us back in any way! */
356 HRESULT ret = mParent->machine()->COMSETTER(MemoryBalloonSize)(aMemoryBalloonSize);
357 if (ret == S_OK)
358 {
359 mMemoryBalloonSize = aMemoryBalloonSize;
360 /* forward the information to the VMM device */
361 VMMDev *pVMMDev = mParent->getVMMDev();
362 /* MUST release all locks before calling VMM device as its critsect
363 * has higher lock order than anything in Main. */
364 alock.release();
365 if (pVMMDev)
366 {
367 PPDMIVMMDEVPORT pVMMDevPort = pVMMDev->getVMMDevPort();
368 if (pVMMDevPort)
369 pVMMDevPort->pfnSetMemoryBalloon(pVMMDevPort, aMemoryBalloonSize);
370 }
371 }
372
373 return ret;
374}
375
376STDMETHODIMP Guest::COMGETTER(StatisticsUpdateInterval)(ULONG *aUpdateInterval)
377{
378 CheckComArgOutPointerValid(aUpdateInterval);
379
380 AutoCaller autoCaller(this);
381 if (FAILED(autoCaller.rc())) return autoCaller.rc();
382
383 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
384
385 *aUpdateInterval = mStatUpdateInterval;
386 return S_OK;
387}
388
389STDMETHODIMP Guest::COMSETTER(StatisticsUpdateInterval)(ULONG aUpdateInterval)
390{
391 AutoCaller autoCaller(this);
392 if (FAILED(autoCaller.rc())) return autoCaller.rc();
393
394 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
395
396 mStatUpdateInterval = aUpdateInterval;
397 /* forward the information to the VMM device */
398 VMMDev *pVMMDev = mParent->getVMMDev();
399 /* MUST release all locks before calling VMM device as its critsect
400 * has higher lock order than anything in Main. */
401 alock.release();
402 if (pVMMDev)
403 {
404 PPDMIVMMDEVPORT pVMMDevPort = pVMMDev->getVMMDevPort();
405 if (pVMMDevPort)
406 pVMMDevPort->pfnSetStatisticsInterval(pVMMDevPort, aUpdateInterval);
407 }
408
409 return S_OK;
410}
411
412STDMETHODIMP Guest::InternalGetStatistics(ULONG *aCpuUser, ULONG *aCpuKernel, ULONG *aCpuIdle,
413 ULONG *aMemTotal, ULONG *aMemFree, ULONG *aMemBalloon, ULONG *aMemShared,
414 ULONG *aMemCache, ULONG *aPageTotal,
415 ULONG *aMemAllocTotal, ULONG *aMemFreeTotal, ULONG *aMemBalloonTotal, ULONG *aMemSharedTotal)
416{
417 CheckComArgOutPointerValid(aCpuUser);
418 CheckComArgOutPointerValid(aCpuKernel);
419 CheckComArgOutPointerValid(aCpuIdle);
420 CheckComArgOutPointerValid(aMemTotal);
421 CheckComArgOutPointerValid(aMemFree);
422 CheckComArgOutPointerValid(aMemBalloon);
423 CheckComArgOutPointerValid(aMemShared);
424 CheckComArgOutPointerValid(aMemCache);
425 CheckComArgOutPointerValid(aPageTotal);
426 CheckComArgOutPointerValid(aMemAllocTotal);
427 CheckComArgOutPointerValid(aMemFreeTotal);
428 CheckComArgOutPointerValid(aMemBalloonTotal);
429 CheckComArgOutPointerValid(aMemSharedTotal);
430
431 AutoCaller autoCaller(this);
432 if (FAILED(autoCaller.rc())) return autoCaller.rc();
433
434 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
435
436 *aCpuUser = mCurrentGuestStat[GUESTSTATTYPE_CPUUSER];
437 *aCpuKernel = mCurrentGuestStat[GUESTSTATTYPE_CPUKERNEL];
438 *aCpuIdle = mCurrentGuestStat[GUESTSTATTYPE_CPUIDLE];
439 *aMemTotal = mCurrentGuestStat[GUESTSTATTYPE_MEMTOTAL] * (_4K/_1K); /* page (4K) -> 1KB units */
440 *aMemFree = mCurrentGuestStat[GUESTSTATTYPE_MEMFREE] * (_4K/_1K); /* page (4K) -> 1KB units */
441 *aMemBalloon = mCurrentGuestStat[GUESTSTATTYPE_MEMBALLOON] * (_4K/_1K); /* page (4K) -> 1KB units */
442 *aMemCache = mCurrentGuestStat[GUESTSTATTYPE_MEMCACHE] * (_4K/_1K); /* page (4K) -> 1KB units */
443 *aPageTotal = mCurrentGuestStat[GUESTSTATTYPE_PAGETOTAL] * (_4K/_1K); /* page (4K) -> 1KB units */
444
445 /* MUST release all locks before calling any PGM statistics queries,
446 * as they are executed by EMT and that might deadlock us by VMM device
447 * activity which waits for the Guest object lock. */
448 alock.release();
449 Console::SafeVMPtr pVM (mParent);
450 if (pVM.isOk())
451 {
452 uint64_t uFreeTotal, uAllocTotal, uBalloonedTotal, uSharedTotal;
453 *aMemFreeTotal = 0;
454 int rc = PGMR3QueryGlobalMemoryStats(pVM.raw(), &uAllocTotal, &uFreeTotal, &uBalloonedTotal, &uSharedTotal);
455 AssertRC(rc);
456 if (rc == VINF_SUCCESS)
457 {
458 *aMemAllocTotal = (ULONG)(uAllocTotal / _1K); /* bytes -> KB */
459 *aMemFreeTotal = (ULONG)(uFreeTotal / _1K);
460 *aMemBalloonTotal = (ULONG)(uBalloonedTotal / _1K);
461 *aMemSharedTotal = (ULONG)(uSharedTotal / _1K);
462 }
463 else
464 return E_FAIL;
465
466 /* Query the missing per-VM memory statistics. */
467 *aMemShared = 0;
468 uint64_t uTotalMem, uPrivateMem, uSharedMem, uZeroMem;
469 rc = PGMR3QueryMemoryStats(pVM.raw(), &uTotalMem, &uPrivateMem, &uSharedMem, &uZeroMem);
470 if (rc == VINF_SUCCESS)
471 {
472 *aMemShared = (ULONG)(uSharedMem / _1K);
473 }
474 else
475 return E_FAIL;
476 }
477 else
478 {
479 *aMemAllocTotal = 0;
480 *aMemFreeTotal = 0;
481 *aMemBalloonTotal = 0;
482 *aMemSharedTotal = 0;
483 *aMemShared = 0;
484 return E_FAIL;
485 }
486
487 return S_OK;
488}
489
490HRESULT Guest::setStatistic(ULONG aCpuId, GUESTSTATTYPE enmType, ULONG aVal)
491{
492 AutoCaller autoCaller(this);
493 if (FAILED(autoCaller.rc())) return autoCaller.rc();
494
495 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
496
497 if (enmType >= GUESTSTATTYPE_MAX)
498 return E_INVALIDARG;
499
500 mCurrentGuestStat[enmType] = aVal;
501 return S_OK;
502}
503
504/**
505 * Returns the status of a specified Guest Additions facility.
506 *
507 * @return aStatus Current status of specified facility.
508 * @param aType Facility to get the status from.
509 * @param aTimestamp Timestamp of last facility status update in ms (optional).
510 */
511STDMETHODIMP Guest::GetFacilityStatus(AdditionsFacilityType_T aType, LONG64 *aTimestamp, AdditionsFacilityStatus_T *aStatus)
512{
513 AutoCaller autoCaller(this);
514 if (FAILED(autoCaller.rc())) return autoCaller.rc();
515
516 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
517
518 CheckComArgNotNull(aStatus);
519 /* Not checking for aTimestamp is intentional; it's optional. */
520
521 FacilityMapIterConst it = mData.mFacilityMap.find(aType);
522 if (it != mData.mFacilityMap.end())
523 {
524 AdditionsFacility *pFacility = it->second;
525 ComAssert(pFacility);
526 *aStatus = pFacility->getStatus();
527 if (aTimestamp)
528 *aTimestamp = pFacility->getLastUpdated();
529 }
530 else
531 {
532 /*
533 * Do not fail here -- could be that the facility never has been brought up (yet) but
534 * the host wants to have its status anyway. So just tell we don't know at this point.
535 */
536 *aStatus = AdditionsFacilityStatus_Unknown;
537 if (aTimestamp)
538 *aTimestamp = RTTimeMilliTS();
539 }
540 return S_OK;
541}
542
543STDMETHODIMP Guest::GetAdditionsStatus(AdditionsRunLevelType_T aLevel, BOOL *aActive)
544{
545 AutoCaller autoCaller(this);
546 if (FAILED(autoCaller.rc())) return autoCaller.rc();
547
548 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
549
550 HRESULT rc = S_OK;
551 switch (aLevel)
552 {
553 case AdditionsRunLevelType_System:
554 *aActive = (mData.mAdditionsRunLevel > AdditionsRunLevelType_None);
555 break;
556
557 case AdditionsRunLevelType_Userland:
558 *aActive = (mData.mAdditionsRunLevel >= AdditionsRunLevelType_Userland);
559 break;
560
561 case AdditionsRunLevelType_Desktop:
562 *aActive = (mData.mAdditionsRunLevel >= AdditionsRunLevelType_Desktop);
563 break;
564
565 default:
566 rc = setError(VBOX_E_NOT_SUPPORTED,
567 tr("Invalid status level defined: %u"), aLevel);
568 break;
569 }
570
571 return rc;
572}
573
574STDMETHODIMP Guest::SetCredentials(IN_BSTR aUserName, IN_BSTR aPassword,
575 IN_BSTR aDomain, BOOL aAllowInteractiveLogon)
576{
577 AutoCaller autoCaller(this);
578 if (FAILED(autoCaller.rc())) return autoCaller.rc();
579
580 /* forward the information to the VMM device */
581 VMMDev *pVMMDev = mParent->getVMMDev();
582 if (pVMMDev)
583 {
584 PPDMIVMMDEVPORT pVMMDevPort = pVMMDev->getVMMDevPort();
585 if (pVMMDevPort)
586 {
587 uint32_t u32Flags = VMMDEV_SETCREDENTIALS_GUESTLOGON;
588 if (!aAllowInteractiveLogon)
589 u32Flags = VMMDEV_SETCREDENTIALS_NOLOCALLOGON;
590
591 pVMMDevPort->pfnSetCredentials(pVMMDevPort,
592 Utf8Str(aUserName).c_str(),
593 Utf8Str(aPassword).c_str(),
594 Utf8Str(aDomain).c_str(),
595 u32Flags);
596 return S_OK;
597 }
598 }
599
600 return setError(VBOX_E_VM_ERROR,
601 tr("VMM device is not available (is the VM running?)"));
602}
603
604STDMETHODIMP Guest::DragHGEnter(ULONG uScreenId, ULONG uX, ULONG uY, DragAndDropAction_T defaultAction, ComSafeArrayIn(DragAndDropAction_T, allowedActions), ComSafeArrayIn(IN_BSTR, formats), DragAndDropAction_T *pResultAction)
605{
606 /* Input validation */
607 CheckComArgSafeArrayNotNull(allowedActions);
608 CheckComArgSafeArrayNotNull(formats);
609 CheckComArgOutPointerValid(pResultAction);
610
611 AutoCaller autoCaller(this);
612 if (FAILED(autoCaller.rc())) return autoCaller.rc();
613
614#ifdef VBOX_WITH_DRAG_AND_DROP
615 return m_pGuestDnD->dragHGEnter(uScreenId, uX, uY, defaultAction, ComSafeArrayInArg(allowedActions), ComSafeArrayInArg(formats), pResultAction);
616#else /* VBOX_WITH_DRAG_AND_DROP */
617 ReturnComNotImplemented();
618#endif /* !VBOX_WITH_DRAG_AND_DROP */
619}
620
621STDMETHODIMP Guest::DragHGMove(ULONG uScreenId, ULONG uX, ULONG uY, DragAndDropAction_T defaultAction, ComSafeArrayIn(DragAndDropAction_T, allowedActions), ComSafeArrayIn(IN_BSTR, formats), DragAndDropAction_T *pResultAction)
622{
623 /* Input validation */
624 CheckComArgSafeArrayNotNull(allowedActions);
625 CheckComArgSafeArrayNotNull(formats);
626 CheckComArgOutPointerValid(pResultAction);
627
628 AutoCaller autoCaller(this);
629 if (FAILED(autoCaller.rc())) return autoCaller.rc();
630
631#ifdef VBOX_WITH_DRAG_AND_DROP
632 return m_pGuestDnD->dragHGMove(uScreenId, uX, uY, defaultAction, ComSafeArrayInArg(allowedActions), ComSafeArrayInArg(formats), pResultAction);
633#else /* VBOX_WITH_DRAG_AND_DROP */
634 ReturnComNotImplemented();
635#endif /* !VBOX_WITH_DRAG_AND_DROP */
636}
637
638STDMETHODIMP Guest::DragHGLeave(ULONG uScreenId)
639{
640 AutoCaller autoCaller(this);
641 if (FAILED(autoCaller.rc())) return autoCaller.rc();
642
643#ifdef VBOX_WITH_DRAG_AND_DROP
644 return m_pGuestDnD->dragHGLeave(uScreenId);
645#else /* VBOX_WITH_DRAG_AND_DROP */
646 ReturnComNotImplemented();
647#endif /* !VBOX_WITH_DRAG_AND_DROP */
648}
649
650STDMETHODIMP Guest::DragHGDrop(ULONG uScreenId, ULONG uX, ULONG uY, DragAndDropAction_T defaultAction, ComSafeArrayIn(DragAndDropAction_T, allowedActions), ComSafeArrayIn(IN_BSTR, formats), BSTR *pstrFormat, DragAndDropAction_T *pResultAction)
651{
652 /* Input validation */
653 CheckComArgSafeArrayNotNull(allowedActions);
654 CheckComArgSafeArrayNotNull(formats);
655 CheckComArgOutPointerValid(pstrFormat);
656 CheckComArgOutPointerValid(pResultAction);
657
658 AutoCaller autoCaller(this);
659 if (FAILED(autoCaller.rc())) return autoCaller.rc();
660
661#ifdef VBOX_WITH_DRAG_AND_DROP
662 return m_pGuestDnD->dragHGDrop(uScreenId, uX, uY, defaultAction, ComSafeArrayInArg(allowedActions), ComSafeArrayInArg(formats), pstrFormat, pResultAction);
663#else /* VBOX_WITH_DRAG_AND_DROP */
664 ReturnComNotImplemented();
665#endif /* !VBOX_WITH_DRAG_AND_DROP */
666}
667
668STDMETHODIMP Guest::DragHGPutData(ULONG uScreenId, IN_BSTR bstrFormat, ComSafeArrayIn(BYTE, data), IProgress **ppProgress)
669{
670 /* Input validation */
671 CheckComArgStrNotEmptyOrNull(bstrFormat);
672 CheckComArgSafeArrayNotNull(data);
673 CheckComArgOutPointerValid(ppProgress);
674
675 AutoCaller autoCaller(this);
676 if (FAILED(autoCaller.rc())) return autoCaller.rc();
677
678#ifdef VBOX_WITH_DRAG_AND_DROP
679 return m_pGuestDnD->dragHGPutData(uScreenId, bstrFormat, ComSafeArrayInArg(data), ppProgress);
680#else /* VBOX_WITH_DRAG_AND_DROP */
681 ReturnComNotImplemented();
682#endif /* !VBOX_WITH_DRAG_AND_DROP */
683}
684
685STDMETHODIMP Guest::DragGHPending(ULONG uScreenId, ComSafeArrayOut(BSTR, formats), ComSafeArrayOut(DragAndDropAction_T, allowedActions), DragAndDropAction_T *pDefaultAction)
686{
687 /* Input validation */
688 CheckComArgSafeArrayNotNull(formats);
689 CheckComArgSafeArrayNotNull(allowedActions);
690 CheckComArgOutPointerValid(pDefaultAction);
691
692 AutoCaller autoCaller(this);
693 if (FAILED(autoCaller.rc())) return autoCaller.rc();
694
695#ifdef VBOX_WITH_DRAG_AND_DROP
696 return m_pGuestDnD->dragGHPending(uScreenId, ComSafeArrayOutArg(formats), ComSafeArrayOutArg(allowedActions), pDefaultAction);
697#else /* VBOX_WITH_DRAG_AND_DROP */
698 ReturnComNotImplemented();
699#endif /* !VBOX_WITH_DRAG_AND_DROP */
700}
701
702STDMETHODIMP Guest::DragGHDropped(IN_BSTR bstrFormat, DragAndDropAction_T action, IProgress **ppProgress)
703{
704 /* Input validation */
705 CheckComArgStrNotEmptyOrNull(bstrFormat);
706 CheckComArgOutPointerValid(ppProgress);
707
708 AutoCaller autoCaller(this);
709 if (FAILED(autoCaller.rc())) return autoCaller.rc();
710
711#ifdef VBOX_WITH_DRAG_AND_DROP
712 return m_pGuestDnD->dragGHDropped(bstrFormat, action, ppProgress);
713#else /* VBOX_WITH_DRAG_AND_DROP */
714 ReturnComNotImplemented();
715#endif /* !VBOX_WITH_DRAG_AND_DROP */
716}
717
718STDMETHODIMP Guest::DragGHGetData(ComSafeArrayOut(BYTE, data))
719{
720 /* Input validation */
721 CheckComArgSafeArrayNotNull(data);
722
723 AutoCaller autoCaller(this);
724 if (FAILED(autoCaller.rc())) return autoCaller.rc();
725
726#ifdef VBOX_WITH_DRAG_AND_DROP
727 return m_pGuestDnD->dragGHGetData(ComSafeArrayOutArg(data));
728#else /* VBOX_WITH_DRAG_AND_DROP */
729 ReturnComNotImplemented();
730#endif /* !VBOX_WITH_DRAG_AND_DROP */
731}
732
733// public methods only for internal purposes
734/////////////////////////////////////////////////////////////////////////////
735
736/**
737 * Sets the general Guest Additions information like
738 * API (interface) version and OS type. Gets called by
739 * vmmdevUpdateGuestInfo.
740 *
741 * @param aInterfaceVersion
742 * @param aOsType
743 */
744void Guest::setAdditionsInfo(Bstr aInterfaceVersion, VBOXOSTYPE aOsType)
745{
746 RTTIMESPEC TimeSpecTS;
747 RTTimeNow(&TimeSpecTS);
748
749 AutoCaller autoCaller(this);
750 AssertComRCReturnVoid(autoCaller.rc());
751
752 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
753
754
755 /*
756 * Note: The Guest Additions API (interface) version is deprecated
757 * and will not be used anymore! We might need it to at least report
758 * something as version number if *really* ancient Guest Additions are
759 * installed (without the guest version + revision properties having set).
760 */
761 mData.mInterfaceVersion = aInterfaceVersion;
762
763 /*
764 * Older Additions rely on the Additions API version whether they
765 * are assumed to be active or not. Since newer Additions do report
766 * the Additions version *before* calling this function (by calling
767 * VMMDevReportGuestInfo2, VMMDevReportGuestStatus, VMMDevReportGuestInfo,
768 * in that order) we can tell apart old and new Additions here. Old
769 * Additions never would set VMMDevReportGuestInfo2 (which set mData.mAdditionsVersion)
770 * so they just rely on the aInterfaceVersion string (which gets set by
771 * VMMDevReportGuestInfo).
772 *
773 * So only mark the Additions as being active (run level = system) when we
774 * don't have the Additions version set.
775 */
776 if (mData.mAdditionsVersionNew.isEmpty())
777 {
778 if (aInterfaceVersion.isEmpty())
779 mData.mAdditionsRunLevel = AdditionsRunLevelType_None;
780 else
781 {
782 mData.mAdditionsRunLevel = AdditionsRunLevelType_System;
783
784 /*
785 * To keep it compatible with the old Guest Additions behavior we need to set the
786 * "graphics" (feature) facility to active as soon as we got the Guest Additions
787 * interface version.
788 */
789 facilityUpdate(VBoxGuestFacilityType_Graphics, VBoxGuestFacilityStatus_Active, 0 /*fFlags*/, &TimeSpecTS);
790 }
791 }
792
793 /*
794 * Older Additions didn't have this finer grained capability bit,
795 * so enable it by default. Newer Additions will not enable this here
796 * and use the setSupportedFeatures function instead.
797 */
798 /** @todo r=bird: I don't get the above comment nor the code below...
799 * One talks about capability bits, the one always does something to a facility.
800 * Then there is the comment below it all, which is placed like it addresses the
801 * mOSTypeId, but talks about something which doesn't remotely like mOSTypeId...
802 *
803 * Andy, could you please try clarify and make the comments shorter and more
804 * coherent! Also, explain why this is important and what depends on it.
805 *
806 * PS. There is the VMMDEV_GUEST_SUPPORTS_GRAPHICS capability* report... It
807 * should come in pretty quickly after this update, normally.
808 */
809 facilityUpdate(VBoxGuestFacilityType_Graphics,
810 facilityIsActive(VBoxGuestFacilityType_VBoxGuestDriver)
811 ? VBoxGuestFacilityStatus_Active : VBoxGuestFacilityStatus_Inactive,
812 0 /*fFlags*/, &TimeSpecTS); /** @todo the timestamp isn't gonna be right here on saved state restore. */
813
814 /*
815 * Note! There is a race going on between setting mAdditionsRunLevel and
816 * mSupportsGraphics here and disabling/enabling it later according to
817 * its real status when using new(er) Guest Additions.
818 */
819 mData.mOSTypeId = Global::OSTypeId(aOsType);
820}
821
822/**
823 * Sets the Guest Additions version information details.
824 *
825 * Gets called by vmmdevUpdateGuestInfo2 and vmmdevUpdateGuestInfo (to clear the
826 * state).
827 *
828 * @param a_uFullVersion VBoxGuestInfo2::additionsMajor,
829 * VBoxGuestInfo2::additionsMinor and
830 * VBoxGuestInfo2::additionsBuild combined into
831 * one value by VBOX_FULL_VERSION_MAKE.
832 *
833 * When this is 0, it's vmmdevUpdateGuestInfo
834 * calling to reset the state.
835 *
836 * @param a_pszName Build type tag and/or publisher tag, empty
837 * string if neiter of those are present.
838 * @param a_uRevision See VBoxGuestInfo2::additionsRevision.
839 * @param a_fFeatures See VBoxGuestInfo2::additionsFeatures.
840 */
841void Guest::setAdditionsInfo2(uint32_t a_uFullVersion, const char *a_pszName, uint32_t a_uRevision, uint32_t a_fFeatures)
842{
843 AutoCaller autoCaller(this);
844 AssertComRCReturnVoid(autoCaller.rc());
845
846 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
847
848 if (a_uFullVersion)
849 {
850 mData.mAdditionsVersionNew = BstrFmt(*a_pszName ? "%u.%u.%u_%s" : "%u.%u.%u",
851 VBOX_FULL_VERSION_GET_MAJOR(a_uFullVersion),
852 VBOX_FULL_VERSION_GET_MINOR(a_uFullVersion),
853 VBOX_FULL_VERSION_GET_BUILD(a_uFullVersion),
854 a_pszName);
855 mData.mAdditionsVersionFull = a_uFullVersion;
856 mData.mAdditionsRevision = a_uRevision;
857 mData.mAdditionsFeatures = a_fFeatures;
858 }
859 else
860 {
861 Assert(!a_fFeatures && !a_uRevision && !*a_pszName);
862 mData.mAdditionsVersionNew.setNull();
863 mData.mAdditionsVersionFull = 0;
864 mData.mAdditionsRevision = 0;
865 mData.mAdditionsFeatures = 0;
866 }
867}
868
869bool Guest::facilityIsActive(VBoxGuestFacilityType enmFacility)
870{
871 Assert(enmFacility < INT32_MAX);
872 FacilityMapIterConst it = mData.mFacilityMap.find((AdditionsFacilityType_T)enmFacility);
873 if (it != mData.mFacilityMap.end())
874 {
875 AdditionsFacility *pFac = it->second;
876 return (pFac->getStatus() == AdditionsFacilityStatus_Active);
877 }
878 return false;
879}
880
881void Guest::facilityUpdate(VBoxGuestFacilityType a_enmFacility, VBoxGuestFacilityStatus a_enmStatus,
882 uint32_t a_fFlags, PCRTTIMESPEC a_pTimeSpecTS)
883{
884 AssertReturnVoid( a_enmFacility < VBoxGuestFacilityType_All
885 && a_enmFacility > VBoxGuestFacilityType_Unknown);
886
887 FacilityMapIter it = mData.mFacilityMap.find((AdditionsFacilityType_T)a_enmFacility);
888 if (it != mData.mFacilityMap.end())
889 {
890 AdditionsFacility *pFac = it->second;
891 pFac->update((AdditionsFacilityStatus_T)a_enmStatus, a_fFlags, a_pTimeSpecTS);
892 }
893 else
894 {
895 if (mData.mFacilityMap.size() > 64)
896 {
897 /* The easy way out for now. We could automatically destroy
898 inactive facilities like VMMDev does if we like... */
899 AssertFailedReturnVoid();
900 }
901
902 ComObjPtr<AdditionsFacility> ptrFac;
903 ptrFac.createObject();
904 AssertReturnVoid(!ptrFac.isNull());
905
906 HRESULT hrc = ptrFac->init(this, (AdditionsFacilityType_T)a_enmFacility, (AdditionsFacilityStatus_T)a_enmStatus,
907 a_fFlags, a_pTimeSpecTS);
908 if (SUCCEEDED(hrc))
909 mData.mFacilityMap.insert(std::make_pair((AdditionsFacilityType_T)a_enmFacility, ptrFac));
910 }
911}
912
913/**
914 * Sets the status of a certain Guest Additions facility.
915 *
916 * Gets called by vmmdevUpdateGuestStatus, which just passes the report along.
917 *
918 * @param a_pInterface Pointer to this interface.
919 * @param a_enmFacility The facility.
920 * @param a_enmStatus The status.
921 * @param a_fFlags Flags assoicated with the update. Currently
922 * reserved and should be ignored.
923 * @param a_pTimeSpecTS Pointer to the timestamp of this report.
924 * @sa PDMIVMMDEVCONNECTOR::pfnUpdateGuestStatus, vmmdevUpdateGuestStatus
925 * @thread The emulation thread.
926 */
927void Guest::setAdditionsStatus(VBoxGuestFacilityType a_enmFacility, VBoxGuestFacilityStatus a_enmStatus,
928 uint32_t a_fFlags, PCRTTIMESPEC a_pTimeSpecTS)
929{
930 Assert( a_enmFacility > VBoxGuestFacilityType_Unknown
931 && a_enmFacility <= VBoxGuestFacilityType_All); /* Paranoia, VMMDev checks for this. */
932
933 AutoCaller autoCaller(this);
934 AssertComRCReturnVoid(autoCaller.rc());
935
936 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
937
938 /*
939 * Set a specific facility status.
940 */
941 if (a_enmFacility == VBoxGuestFacilityType_All)
942 for (FacilityMapIter it = mData.mFacilityMap.begin(); it != mData.mFacilityMap.end(); ++it)
943 facilityUpdate((VBoxGuestFacilityType)it->first, a_enmStatus, a_fFlags, a_pTimeSpecTS);
944 else /* Update one facility only. */
945 facilityUpdate(a_enmFacility, a_enmStatus, a_fFlags, a_pTimeSpecTS);
946
947 /*
948 * Recalc the runlevel.
949 */
950 if (facilityIsActive(VBoxGuestFacilityType_VBoxTrayClient))
951 mData.mAdditionsRunLevel = AdditionsRunLevelType_Desktop;
952 else if (facilityIsActive(VBoxGuestFacilityType_VBoxService))
953 mData.mAdditionsRunLevel = AdditionsRunLevelType_Userland;
954 else if (facilityIsActive(VBoxGuestFacilityType_VBoxGuestDriver))
955 mData.mAdditionsRunLevel = AdditionsRunLevelType_System;
956 else
957 mData.mAdditionsRunLevel = AdditionsRunLevelType_None;
958}
959
960/**
961 * Sets the supported features (and whether they are active or not).
962 *
963 * @param fCaps Guest capability bit mask (VMMDEV_GUEST_SUPPORTS_XXX).
964 */
965void Guest::setSupportedFeatures(uint32_t aCaps)
966{
967 AutoCaller autoCaller(this);
968 AssertComRCReturnVoid(autoCaller.rc());
969
970 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
971
972 /** @todo A nit: The timestamp is wrong on saved state restore. Would be better
973 * to move the graphics and seamless capability -> facility translation to
974 * VMMDev so this could be saved. */
975 RTTIMESPEC TimeSpecTS;
976 RTTimeNow(&TimeSpecTS);
977
978 facilityUpdate(VBoxGuestFacilityType_Seamless,
979 aCaps & VMMDEV_GUEST_SUPPORTS_SEAMLESS ? VBoxGuestFacilityStatus_Active : VBoxGuestFacilityStatus_Inactive,
980 0 /*fFlags*/, &TimeSpecTS);
981 /** @todo Add VMMDEV_GUEST_SUPPORTS_GUEST_HOST_WINDOW_MAPPING */
982 facilityUpdate(VBoxGuestFacilityType_Graphics,
983 aCaps & VMMDEV_GUEST_SUPPORTS_GRAPHICS ? VBoxGuestFacilityStatus_Active : VBoxGuestFacilityStatus_Inactive,
984 0 /*fFlags*/, &TimeSpecTS);
985}
986/* vi: set tabstop=4 shiftwidth=4 expandtab: */
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