VirtualBox

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

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

GuestImpl.cpp: Work around the version/revision mixup bug in the 3.1.0 guest additions. (just happened to be installed in a vm I was running...)

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