VirtualBox

source: vbox/trunk/src/VBox/Main/src-client/ConsoleImpl.cpp@ 47469

Last change on this file since 47469 was 47342, checked in by vboxsync, 11 years ago

Main: Cache information about USB controller availability locally in Console object.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 338.3 KB
Line 
1/* $Id: ConsoleImpl.cpp 47342 2013-07-23 13:59:12Z vboxsync $ */
2/** @file
3 * VBox Console COM Class implementation
4 */
5
6/*
7 * Copyright (C) 2005-2013 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/** @todo Move the TAP mess back into the driver! */
19#if defined(RT_OS_WINDOWS)
20#elif defined(RT_OS_LINUX)
21# include <errno.h>
22# include <sys/ioctl.h>
23# include <sys/poll.h>
24# include <sys/fcntl.h>
25# include <sys/types.h>
26# include <sys/wait.h>
27# include <net/if.h>
28# include <linux/if_tun.h>
29# include <stdio.h>
30# include <stdlib.h>
31# include <string.h>
32#elif defined(RT_OS_FREEBSD)
33# include <errno.h>
34# include <sys/ioctl.h>
35# include <sys/poll.h>
36# include <sys/fcntl.h>
37# include <sys/types.h>
38# include <sys/wait.h>
39# include <stdio.h>
40# include <stdlib.h>
41# include <string.h>
42#elif defined(RT_OS_SOLARIS)
43# include <iprt/coredumper.h>
44#endif
45
46#include "ConsoleImpl.h"
47
48#include "Global.h"
49#include "VirtualBoxErrorInfoImpl.h"
50#include "GuestImpl.h"
51#include "KeyboardImpl.h"
52#include "MouseImpl.h"
53#include "DisplayImpl.h"
54#include "MachineDebuggerImpl.h"
55#include "USBDeviceImpl.h"
56#include "RemoteUSBDeviceImpl.h"
57#include "SharedFolderImpl.h"
58#include "AudioSnifferInterface.h"
59#include "Nvram.h"
60#ifdef VBOX_WITH_USB_VIDEO
61# include "UsbWebcamInterface.h"
62#endif
63#ifdef VBOX_WITH_USB_CARDREADER
64# include "UsbCardReader.h"
65#endif
66#include "ProgressImpl.h"
67#include "ConsoleVRDPServer.h"
68#include "VMMDev.h"
69#ifdef VBOX_WITH_EXTPACK
70# include "ExtPackManagerImpl.h"
71#endif
72#include "BusAssignmentManager.h"
73
74#include "VBoxEvents.h"
75#include "AutoCaller.h"
76#include "Logging.h"
77
78#include <VBox/com/array.h>
79#include "VBox/com/ErrorInfo.h"
80#include <VBox/com/listeners.h>
81
82#include <iprt/asm.h>
83#include <iprt/buildconfig.h>
84#include <iprt/cpp/utils.h>
85#include <iprt/dir.h>
86#include <iprt/file.h>
87#include <iprt/ldr.h>
88#include <iprt/path.h>
89#include <iprt/process.h>
90#include <iprt/string.h>
91#include <iprt/system.h>
92
93#include <VBox/vmm/vmapi.h>
94#include <VBox/vmm/vmm.h>
95#include <VBox/vmm/pdmapi.h>
96#include <VBox/vmm/pdmasynccompletion.h>
97#include <VBox/vmm/pdmnetifs.h>
98#ifdef VBOX_WITH_USB
99# include <VBox/vmm/pdmusb.h>
100#endif
101#ifdef VBOX_WITH_NETSHAPER
102# include <VBox/vmm/pdmnetshaper.h>
103#endif /* VBOX_WITH_NETSHAPER */
104#include <VBox/vmm/mm.h>
105#include <VBox/vmm/ftm.h>
106#include <VBox/vmm/ssm.h>
107#include <VBox/err.h>
108#include <VBox/param.h>
109#include <VBox/vusb.h>
110
111#include <VBox/VMMDev.h>
112
113#include <VBox/HostServices/VBoxClipboardSvc.h>
114#include <VBox/HostServices/DragAndDropSvc.h>
115#ifdef VBOX_WITH_GUEST_PROPS
116# include <VBox/HostServices/GuestPropertySvc.h>
117# include <VBox/com/array.h>
118#endif
119
120#include <set>
121#include <algorithm>
122#include <memory> // for auto_ptr
123#include <vector>
124
125
126// VMTask and friends
127////////////////////////////////////////////////////////////////////////////////
128
129/**
130 * Task structure for asynchronous VM operations.
131 *
132 * Once created, the task structure adds itself as a Console caller. This means:
133 *
134 * 1. The user must check for #rc() before using the created structure
135 * (e.g. passing it as a thread function argument). If #rc() returns a
136 * failure, the Console object may not be used by the task (see
137 * Console::addCaller() for more details).
138 * 2. On successful initialization, the structure keeps the Console caller
139 * until destruction (to ensure Console remains in the Ready state and won't
140 * be accidentally uninitialized). Forgetting to delete the created task
141 * will lead to Console::uninit() stuck waiting for releasing all added
142 * callers.
143 *
144 * If \a aUsesVMPtr parameter is true, the task structure will also add itself
145 * as a Console::mpUVM caller with the same meaning as above. See
146 * Console::addVMCaller() for more info.
147 */
148struct VMTask
149{
150 VMTask(Console *aConsole,
151 Progress *aProgress,
152 const ComPtr<IProgress> &aServerProgress,
153 bool aUsesVMPtr)
154 : mConsole(aConsole),
155 mConsoleCaller(aConsole),
156 mProgress(aProgress),
157 mServerProgress(aServerProgress),
158 mpUVM(NULL),
159 mRC(E_FAIL),
160 mpSafeVMPtr(NULL)
161 {
162 AssertReturnVoid(aConsole);
163 mRC = mConsoleCaller.rc();
164 if (FAILED(mRC))
165 return;
166 if (aUsesVMPtr)
167 {
168 mpSafeVMPtr = new Console::SafeVMPtr(aConsole);
169 if (mpSafeVMPtr->isOk())
170 mpUVM = mpSafeVMPtr->rawUVM();
171 else
172 mRC = mpSafeVMPtr->rc();
173 }
174 }
175
176 ~VMTask()
177 {
178 releaseVMCaller();
179 }
180
181 HRESULT rc() const { return mRC; }
182 bool isOk() const { return SUCCEEDED(rc()); }
183
184 /** Releases the VM caller before destruction. Not normally necessary. */
185 void releaseVMCaller()
186 {
187 if (mpSafeVMPtr)
188 {
189 delete mpSafeVMPtr;
190 mpSafeVMPtr = NULL;
191 }
192 }
193
194 const ComObjPtr<Console> mConsole;
195 AutoCaller mConsoleCaller;
196 const ComObjPtr<Progress> mProgress;
197 Utf8Str mErrorMsg;
198 const ComPtr<IProgress> mServerProgress;
199 PUVM mpUVM;
200
201private:
202 HRESULT mRC;
203 Console::SafeVMPtr *mpSafeVMPtr;
204};
205
206struct VMTakeSnapshotTask : public VMTask
207{
208 VMTakeSnapshotTask(Console *aConsole,
209 Progress *aProgress,
210 IN_BSTR aName,
211 IN_BSTR aDescription)
212 : VMTask(aConsole, aProgress, NULL /* aServerProgress */,
213 false /* aUsesVMPtr */),
214 bstrName(aName),
215 bstrDescription(aDescription),
216 lastMachineState(MachineState_Null)
217 {}
218
219 Bstr bstrName,
220 bstrDescription;
221 Bstr bstrSavedStateFile; // received from BeginTakeSnapshot()
222 MachineState_T lastMachineState;
223 bool fTakingSnapshotOnline;
224 ULONG ulMemSize;
225};
226
227struct VMPowerUpTask : public VMTask
228{
229 VMPowerUpTask(Console *aConsole,
230 Progress *aProgress)
231 : VMTask(aConsole, aProgress, NULL /* aServerProgress */,
232 false /* aUsesVMPtr */),
233 mConfigConstructor(NULL),
234 mStartPaused(false),
235 mTeleporterEnabled(FALSE),
236 mEnmFaultToleranceState(FaultToleranceState_Inactive)
237 {}
238
239 PFNCFGMCONSTRUCTOR mConfigConstructor;
240 Utf8Str mSavedStateFile;
241 Console::SharedFolderDataMap mSharedFolders;
242 bool mStartPaused;
243 BOOL mTeleporterEnabled;
244 FaultToleranceState_T mEnmFaultToleranceState;
245
246 /* array of progress objects for hard disk reset operations */
247 typedef std::list<ComPtr<IProgress> > ProgressList;
248 ProgressList hardDiskProgresses;
249};
250
251struct VMPowerDownTask : public VMTask
252{
253 VMPowerDownTask(Console *aConsole,
254 const ComPtr<IProgress> &aServerProgress)
255 : VMTask(aConsole, NULL /* aProgress */, aServerProgress,
256 true /* aUsesVMPtr */)
257 {}
258};
259
260struct VMSaveTask : public VMTask
261{
262 VMSaveTask(Console *aConsole,
263 const ComPtr<IProgress> &aServerProgress,
264 const Utf8Str &aSavedStateFile,
265 MachineState_T aMachineStateBefore,
266 Reason_T aReason)
267 : VMTask(aConsole, NULL /* aProgress */, aServerProgress,
268 true /* aUsesVMPtr */),
269 mSavedStateFile(aSavedStateFile),
270 mMachineStateBefore(aMachineStateBefore),
271 mReason(aReason)
272 {}
273
274 Utf8Str mSavedStateFile;
275 /* The local machine state we had before. Required if something fails */
276 MachineState_T mMachineStateBefore;
277 /* The reason for saving state */
278 Reason_T mReason;
279};
280
281// Handler for global events
282////////////////////////////////////////////////////////////////////////////////
283inline static const char *networkAdapterTypeToName(NetworkAdapterType_T adapterType);
284
285class VmEventListener {
286public:
287 VmEventListener()
288 {}
289
290
291 HRESULT init(Console *aConsole)
292 {
293 mConsole = aConsole;
294 return S_OK;
295 }
296
297 void uninit()
298 {
299 }
300
301 virtual ~VmEventListener()
302 {
303 }
304
305 STDMETHOD(HandleEvent)(VBoxEventType_T aType, IEvent * aEvent)
306 {
307 switch(aType)
308 {
309 case VBoxEventType_OnNATRedirect:
310 {
311 Bstr id;
312 ComPtr<IMachine> pMachine = mConsole->machine();
313 ComPtr<INATRedirectEvent> pNREv = aEvent;
314 HRESULT rc = E_FAIL;
315 Assert(pNREv);
316
317 Bstr interestedId;
318 rc = pMachine->COMGETTER(Id)(interestedId.asOutParam());
319 AssertComRC(rc);
320 rc = pNREv->COMGETTER(MachineId)(id.asOutParam());
321 AssertComRC(rc);
322 if (id != interestedId)
323 break;
324 /* now we can operate with redirects */
325 NATProtocol_T proto;
326 pNREv->COMGETTER(Proto)(&proto);
327 BOOL fRemove;
328 pNREv->COMGETTER(Remove)(&fRemove);
329 bool fUdp = (proto == NATProtocol_UDP);
330 Bstr hostIp, guestIp;
331 LONG hostPort, guestPort;
332 pNREv->COMGETTER(HostIP)(hostIp.asOutParam());
333 pNREv->COMGETTER(HostPort)(&hostPort);
334 pNREv->COMGETTER(GuestIP)(guestIp.asOutParam());
335 pNREv->COMGETTER(GuestPort)(&guestPort);
336 ULONG ulSlot;
337 rc = pNREv->COMGETTER(Slot)(&ulSlot);
338 AssertComRC(rc);
339 if (FAILED(rc))
340 break;
341 mConsole->onNATRedirectRuleChange(ulSlot, fRemove, proto, hostIp.raw(), hostPort, guestIp.raw(), guestPort);
342 }
343 break;
344
345 case VBoxEventType_OnHostPCIDevicePlug:
346 {
347 // handle if needed
348 break;
349 }
350
351 default:
352 AssertFailed();
353 }
354 return S_OK;
355 }
356private:
357 Console *mConsole;
358};
359
360typedef ListenerImpl<VmEventListener, Console*> VmEventListenerImpl;
361
362
363VBOX_LISTENER_DECLARE(VmEventListenerImpl)
364
365
366// constructor / destructor
367/////////////////////////////////////////////////////////////////////////////
368
369Console::Console()
370 : mSavedStateDataLoaded(false)
371 , mConsoleVRDPServer(NULL)
372 , mfVRDEChangeInProcess(false)
373 , mfVRDEChangePending(false)
374 , mpUVM(NULL)
375 , mVMCallers(0)
376 , mVMZeroCallersSem(NIL_RTSEMEVENT)
377 , mVMDestroying(false)
378 , mVMPoweredOff(false)
379 , mVMIsAlreadyPoweringOff(false)
380 , mfSnapshotFolderSizeWarningShown(false)
381 , mfSnapshotFolderExt4WarningShown(false)
382 , mfSnapshotFolderDiskTypeShown(false)
383 , mfVMHasUsbController(false)
384 , mpVmm2UserMethods(NULL)
385 , m_pVMMDev(NULL)
386 , mAudioSniffer(NULL)
387 , mNvram(NULL)
388#ifdef VBOX_WITH_USB_VIDEO
389 , mEmWebcam(NULL)
390#endif
391#ifdef VBOX_WITH_USB_CARDREADER
392 , mUsbCardReader(NULL)
393#endif
394 , mBusMgr(NULL)
395 , mVMStateChangeCallbackDisabled(false)
396 , mfUseHostClipboard(true)
397 , mMachineState(MachineState_PoweredOff)
398{
399}
400
401Console::~Console()
402{}
403
404HRESULT Console::FinalConstruct()
405{
406 LogFlowThisFunc(("\n"));
407
408 RT_ZERO(mapStorageLeds);
409 RT_ZERO(mapNetworkLeds);
410 RT_ZERO(mapUSBLed);
411 RT_ZERO(mapSharedFolderLed);
412
413 for (unsigned i = 0; i < RT_ELEMENTS(maStorageDevType); ++i)
414 maStorageDevType[i] = DeviceType_Null;
415
416 MYVMM2USERMETHODS *pVmm2UserMethods = (MYVMM2USERMETHODS *)RTMemAllocZ(sizeof(*mpVmm2UserMethods) + sizeof(Console *));
417 if (!pVmm2UserMethods)
418 return E_OUTOFMEMORY;
419 pVmm2UserMethods->u32Magic = VMM2USERMETHODS_MAGIC;
420 pVmm2UserMethods->u32Version = VMM2USERMETHODS_VERSION;
421 pVmm2UserMethods->pfnSaveState = Console::vmm2User_SaveState;
422 pVmm2UserMethods->pfnNotifyEmtInit = Console::vmm2User_NotifyEmtInit;
423 pVmm2UserMethods->pfnNotifyEmtTerm = Console::vmm2User_NotifyEmtTerm;
424 pVmm2UserMethods->pfnNotifyPdmtInit = Console::vmm2User_NotifyPdmtInit;
425 pVmm2UserMethods->pfnNotifyPdmtTerm = Console::vmm2User_NotifyPdmtTerm;
426 pVmm2UserMethods->u32EndMagic = VMM2USERMETHODS_MAGIC;
427 pVmm2UserMethods->pConsole = this;
428 mpVmm2UserMethods = pVmm2UserMethods;
429
430 return BaseFinalConstruct();
431}
432
433void Console::FinalRelease()
434{
435 LogFlowThisFunc(("\n"));
436
437 uninit();
438
439 BaseFinalRelease();
440}
441
442// public initializer/uninitializer for internal purposes only
443/////////////////////////////////////////////////////////////////////////////
444
445HRESULT Console::init(IMachine *aMachine, IInternalMachineControl *aControl, LockType_T aLockType)
446{
447 AssertReturn(aMachine && aControl, E_INVALIDARG);
448
449 /* Enclose the state transition NotReady->InInit->Ready */
450 AutoInitSpan autoInitSpan(this);
451 AssertReturn(autoInitSpan.isOk(), E_FAIL);
452
453 LogFlowThisFuncEnter();
454 LogFlowThisFunc(("aMachine=%p, aControl=%p\n", aMachine, aControl));
455
456 HRESULT rc = E_FAIL;
457
458 unconst(mMachine) = aMachine;
459 unconst(mControl) = aControl;
460
461 /* Cache essential properties and objects, and create child objects */
462
463 rc = mMachine->COMGETTER(State)(&mMachineState);
464 AssertComRCReturnRC(rc);
465
466#ifdef VBOX_WITH_EXTPACK
467 unconst(mptrExtPackManager).createObject();
468 rc = mptrExtPackManager->initExtPackManager(NULL, VBOXEXTPACKCTX_VM_PROCESS);
469 AssertComRCReturnRC(rc);
470#endif
471
472 // Event source may be needed by other children
473 unconst(mEventSource).createObject();
474 rc = mEventSource->init(static_cast<IConsole*>(this));
475 AssertComRCReturnRC(rc);
476
477 mcAudioRefs = 0;
478 mcVRDPClients = 0;
479 mu32SingleRDPClientId = 0;
480 mcGuestCredentialsProvided = false;
481
482 /* Now the VM specific parts */
483 if (aLockType == LockType_VM)
484 {
485 rc = mMachine->COMGETTER(VRDEServer)(unconst(mVRDEServer).asOutParam());
486 AssertComRCReturnRC(rc);
487
488 unconst(mGuest).createObject();
489 rc = mGuest->init(this);
490 AssertComRCReturnRC(rc);
491
492 unconst(mKeyboard).createObject();
493 rc = mKeyboard->init(this);
494 AssertComRCReturnRC(rc);
495
496 unconst(mMouse).createObject();
497 rc = mMouse->init(this);
498 AssertComRCReturnRC(rc);
499
500 unconst(mDisplay).createObject();
501 rc = mDisplay->init(this);
502 AssertComRCReturnRC(rc);
503
504 unconst(mVRDEServerInfo).createObject();
505 rc = mVRDEServerInfo->init(this);
506 AssertComRCReturnRC(rc);
507
508 /* Grab global and machine shared folder lists */
509
510 rc = fetchSharedFolders(true /* aGlobal */);
511 AssertComRCReturnRC(rc);
512 rc = fetchSharedFolders(false /* aGlobal */);
513 AssertComRCReturnRC(rc);
514
515 /* Create other child objects */
516
517 unconst(mConsoleVRDPServer) = new ConsoleVRDPServer(this);
518 AssertReturn(mConsoleVRDPServer, E_FAIL);
519
520 /* Figure out size of meAttachmentType vector */
521 ComPtr<IVirtualBox> pVirtualBox;
522 rc = aMachine->COMGETTER(Parent)(pVirtualBox.asOutParam());
523 AssertComRC(rc);
524 ComPtr<ISystemProperties> pSystemProperties;
525 if (pVirtualBox)
526 pVirtualBox->COMGETTER(SystemProperties)(pSystemProperties.asOutParam());
527 ChipsetType_T chipsetType = ChipsetType_PIIX3;
528 aMachine->COMGETTER(ChipsetType)(&chipsetType);
529 ULONG maxNetworkAdapters = 0;
530 if (pSystemProperties)
531 pSystemProperties->GetMaxNetworkAdapters(chipsetType, &maxNetworkAdapters);
532 meAttachmentType.resize(maxNetworkAdapters);
533 for (ULONG slot = 0; slot < maxNetworkAdapters; ++slot)
534 meAttachmentType[slot] = NetworkAttachmentType_Null;
535
536 // VirtualBox 4.0: We no longer initialize the VMMDev instance here,
537 // which starts the HGCM thread. Instead, this is now done in the
538 // power-up thread when a VM is actually being powered up to avoid
539 // having HGCM threads all over the place every time a session is
540 // opened, even if that session will not run a VM.
541 // unconst(m_pVMMDev) = new VMMDev(this);
542 // AssertReturn(mVMMDev, E_FAIL);
543
544 unconst(mAudioSniffer) = new AudioSniffer(this);
545 AssertReturn(mAudioSniffer, E_FAIL);
546
547 FirmwareType_T enmFirmwareType;
548 mMachine->COMGETTER(FirmwareType)(&enmFirmwareType);
549 if ( enmFirmwareType == FirmwareType_EFI
550 || enmFirmwareType == FirmwareType_EFI32
551 || enmFirmwareType == FirmwareType_EFI64
552 || enmFirmwareType == FirmwareType_EFIDUAL)
553 {
554 unconst(mNvram) = new Nvram(this);
555 AssertReturn(mNvram, E_FAIL);
556 }
557
558#ifdef VBOX_WITH_USB_VIDEO
559 unconst(mEmWebcam) = new EmWebcam(this);
560 AssertReturn(mEmWebcam, E_FAIL);
561#endif
562#ifdef VBOX_WITH_USB_CARDREADER
563 unconst(mUsbCardReader) = new UsbCardReader(this);
564 AssertReturn(mUsbCardReader, E_FAIL);
565#endif
566
567 /* VirtualBox events registration. */
568 {
569 ComPtr<IEventSource> pES;
570 rc = pVirtualBox->COMGETTER(EventSource)(pES.asOutParam());
571 AssertComRC(rc);
572 ComObjPtr<VmEventListenerImpl> aVmListener;
573 aVmListener.createObject();
574 aVmListener->init(new VmEventListener(), this);
575 mVmListener = aVmListener;
576 com::SafeArray<VBoxEventType_T> eventTypes;
577 eventTypes.push_back(VBoxEventType_OnNATRedirect);
578 eventTypes.push_back(VBoxEventType_OnHostPCIDevicePlug);
579 rc = pES->RegisterListener(aVmListener, ComSafeArrayAsInParam(eventTypes), true);
580 AssertComRC(rc);
581 }
582 }
583
584 /* Confirm a successful initialization when it's the case */
585 autoInitSpan.setSucceeded();
586
587#ifdef VBOX_WITH_EXTPACK
588 /* Let the extension packs have a go at things (hold no locks). */
589 if (SUCCEEDED(rc))
590 mptrExtPackManager->callAllConsoleReadyHooks(this);
591#endif
592
593 LogFlowThisFuncLeave();
594
595 return S_OK;
596}
597
598/**
599 * Uninitializes the Console object.
600 */
601void Console::uninit()
602{
603 LogFlowThisFuncEnter();
604
605 /* Enclose the state transition Ready->InUninit->NotReady */
606 AutoUninitSpan autoUninitSpan(this);
607 if (autoUninitSpan.uninitDone())
608 {
609 LogFlowThisFunc(("Already uninitialized.\n"));
610 LogFlowThisFuncLeave();
611 return;
612 }
613
614 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
615 if (mVmListener)
616 {
617 ComPtr<IEventSource> pES;
618 ComPtr<IVirtualBox> pVirtualBox;
619 HRESULT rc = mMachine->COMGETTER(Parent)(pVirtualBox.asOutParam());
620 AssertComRC(rc);
621 if (SUCCEEDED(rc) && !pVirtualBox.isNull())
622 {
623 rc = pVirtualBox->COMGETTER(EventSource)(pES.asOutParam());
624 AssertComRC(rc);
625 if (!pES.isNull())
626 {
627 rc = pES->UnregisterListener(mVmListener);
628 AssertComRC(rc);
629 }
630 }
631 mVmListener.setNull();
632 }
633
634 /* power down the VM if necessary */
635 if (mpUVM)
636 {
637 powerDown();
638 Assert(mpUVM == NULL);
639 }
640
641 if (mVMZeroCallersSem != NIL_RTSEMEVENT)
642 {
643 RTSemEventDestroy(mVMZeroCallersSem);
644 mVMZeroCallersSem = NIL_RTSEMEVENT;
645 }
646
647 if (mpVmm2UserMethods)
648 {
649 RTMemFree((void *)mpVmm2UserMethods);
650 mpVmm2UserMethods = NULL;
651 }
652
653 if (mNvram)
654 {
655 delete mNvram;
656 unconst(mNvram) = NULL;
657 }
658
659#ifdef VBOX_WITH_USB_VIDEO
660 if (mEmWebcam)
661 {
662 delete mEmWebcam;
663 unconst(mEmWebcam) = NULL;
664 }
665#endif
666
667#ifdef VBOX_WITH_USB_CARDREADER
668 if (mUsbCardReader)
669 {
670 delete mUsbCardReader;
671 unconst(mUsbCardReader) = NULL;
672 }
673#endif
674
675 if (mAudioSniffer)
676 {
677 delete mAudioSniffer;
678 unconst(mAudioSniffer) = NULL;
679 }
680
681 // if the VM had a VMMDev with an HGCM thread, then remove that here
682 if (m_pVMMDev)
683 {
684 delete m_pVMMDev;
685 unconst(m_pVMMDev) = NULL;
686 }
687
688 if (mBusMgr)
689 {
690 mBusMgr->Release();
691 mBusMgr = NULL;
692 }
693
694 m_mapGlobalSharedFolders.clear();
695 m_mapMachineSharedFolders.clear();
696 m_mapSharedFolders.clear(); // console instances
697
698 mRemoteUSBDevices.clear();
699 mUSBDevices.clear();
700
701 if (mVRDEServerInfo)
702 {
703 mVRDEServerInfo->uninit();
704 unconst(mVRDEServerInfo).setNull();
705 }
706
707 if (mDebugger)
708 {
709 mDebugger->uninit();
710 unconst(mDebugger).setNull();
711 }
712
713 if (mDisplay)
714 {
715 mDisplay->uninit();
716 unconst(mDisplay).setNull();
717 }
718
719 if (mMouse)
720 {
721 mMouse->uninit();
722 unconst(mMouse).setNull();
723 }
724
725 if (mKeyboard)
726 {
727 mKeyboard->uninit();
728 unconst(mKeyboard).setNull();
729 }
730
731 if (mGuest)
732 {
733 mGuest->uninit();
734 unconst(mGuest).setNull();
735 }
736
737 if (mConsoleVRDPServer)
738 {
739 delete mConsoleVRDPServer;
740 unconst(mConsoleVRDPServer) = NULL;
741 }
742
743 unconst(mVRDEServer).setNull();
744
745 unconst(mControl).setNull();
746 unconst(mMachine).setNull();
747
748 // we don't perform uninit() as it's possible that some pending event refers to this source
749 unconst(mEventSource).setNull();
750
751#ifdef CONSOLE_WITH_EVENT_CACHE
752 mCallbackData.clear();
753#endif
754
755 LogFlowThisFuncLeave();
756}
757
758#ifdef VBOX_WITH_GUEST_PROPS
759
760/**
761 * Handles guest properties on a VM reset.
762 *
763 * We must delete properties that are flagged TRANSRESET.
764 *
765 * @todo r=bird: Would be more efficient if we added a request to the HGCM
766 * service to do this instead of detouring thru VBoxSVC.
767 * (IMachine::SetGuestProperty ends up in VBoxSVC, which in turns calls
768 * back into the VM process and the HGCM service.)
769 */
770void Console::guestPropertiesHandleVMReset(void)
771{
772 com::SafeArray<BSTR> arrNames;
773 com::SafeArray<BSTR> arrValues;
774 com::SafeArray<LONG64> arrTimestamps;
775 com::SafeArray<BSTR> arrFlags;
776 HRESULT hrc = enumerateGuestProperties(Bstr("*").raw(),
777 ComSafeArrayAsOutParam(arrNames),
778 ComSafeArrayAsOutParam(arrValues),
779 ComSafeArrayAsOutParam(arrTimestamps),
780 ComSafeArrayAsOutParam(arrFlags));
781 if (SUCCEEDED(hrc))
782 {
783 for (size_t i = 0; i < arrFlags.size(); i++)
784 {
785 /* Delete all properties which have the flag "TRANSRESET". */
786 if (Utf8Str(arrFlags[i]).contains("TRANSRESET", Utf8Str::CaseInsensitive))
787 {
788 hrc = mMachine->DeleteGuestProperty(arrNames[i]);
789 if (FAILED(hrc))
790 LogRel(("RESET: Could not delete transient property \"%ls\", rc=%Rhrc\n",
791 arrNames[i], hrc));
792 }
793 }
794 }
795 else
796 LogRel(("RESET: Unable to enumerate guest properties, rc=%Rhrc\n", hrc));
797}
798
799bool Console::guestPropertiesVRDPEnabled(void)
800{
801 Bstr value;
802 HRESULT hrc = mMachine->GetExtraData(Bstr("VBoxInternal2/EnableGuestPropertiesVRDP").raw(),
803 value.asOutParam());
804 if ( hrc == S_OK
805 && value == "1")
806 return true;
807 return false;
808}
809
810void Console::guestPropertiesVRDPUpdateLogon(uint32_t u32ClientId, const char *pszUser, const char *pszDomain)
811{
812 if (!guestPropertiesVRDPEnabled())
813 return;
814
815 LogFlowFunc(("\n"));
816
817 char szPropNm[256];
818 Bstr bstrReadOnlyGuest(L"RDONLYGUEST");
819
820 RTStrPrintf(szPropNm, sizeof(szPropNm), "/VirtualBox/HostInfo/VRDP/Client/%u/Name", u32ClientId);
821 Bstr clientName;
822 mVRDEServerInfo->COMGETTER(ClientName)(clientName.asOutParam());
823
824 mMachine->SetGuestProperty(Bstr(szPropNm).raw(),
825 clientName.raw(),
826 bstrReadOnlyGuest.raw());
827
828 RTStrPrintf(szPropNm, sizeof(szPropNm), "/VirtualBox/HostInfo/VRDP/Client/%u/User", u32ClientId);
829 mMachine->SetGuestProperty(Bstr(szPropNm).raw(),
830 Bstr(pszUser).raw(),
831 bstrReadOnlyGuest.raw());
832
833 RTStrPrintf(szPropNm, sizeof(szPropNm), "/VirtualBox/HostInfo/VRDP/Client/%u/Domain", u32ClientId);
834 mMachine->SetGuestProperty(Bstr(szPropNm).raw(),
835 Bstr(pszDomain).raw(),
836 bstrReadOnlyGuest.raw());
837
838 char szClientId[64];
839 RTStrPrintf(szClientId, sizeof(szClientId), "%u", u32ClientId);
840 mMachine->SetGuestProperty(Bstr("/VirtualBox/HostInfo/VRDP/LastConnectedClient").raw(),
841 Bstr(szClientId).raw(),
842 bstrReadOnlyGuest.raw());
843
844 return;
845}
846
847void Console::guestPropertiesVRDPUpdateActiveClient(uint32_t u32ClientId)
848{
849 if (!guestPropertiesVRDPEnabled())
850 return;
851
852 LogFlowFunc(("%d\n", u32ClientId));
853
854 Bstr bstrFlags(L"RDONLYGUEST,TRANSIENT");
855
856 char szClientId[64];
857 RTStrPrintf(szClientId, sizeof(szClientId), "%u", u32ClientId);
858
859 mMachine->SetGuestProperty(Bstr("/VirtualBox/HostInfo/VRDP/ActiveClient").raw(),
860 Bstr(szClientId).raw(),
861 bstrFlags.raw());
862
863 return;
864}
865
866void Console::guestPropertiesVRDPUpdateNameChange(uint32_t u32ClientId, const char *pszName)
867{
868 if (!guestPropertiesVRDPEnabled())
869 return;
870
871 LogFlowFunc(("\n"));
872
873 char szPropNm[256];
874 Bstr bstrReadOnlyGuest(L"RDONLYGUEST");
875
876 RTStrPrintf(szPropNm, sizeof(szPropNm), "/VirtualBox/HostInfo/VRDP/Client/%u/Name", u32ClientId);
877 Bstr clientName(pszName);
878
879 mMachine->SetGuestProperty(Bstr(szPropNm).raw(),
880 clientName.raw(),
881 bstrReadOnlyGuest.raw());
882
883}
884
885void Console::guestPropertiesVRDPUpdateIPAddrChange(uint32_t u32ClientId, const char *pszIPAddr)
886{
887 if (!guestPropertiesVRDPEnabled())
888 return;
889
890 LogFlowFunc(("\n"));
891
892 char szPropNm[256];
893 Bstr bstrReadOnlyGuest(L"RDONLYGUEST");
894
895 RTStrPrintf(szPropNm, sizeof(szPropNm), "/VirtualBox/HostInfo/VRDP/Client/%u/IPAddr", u32ClientId);
896 Bstr clientIPAddr(pszIPAddr);
897
898 mMachine->SetGuestProperty(Bstr(szPropNm).raw(),
899 clientIPAddr.raw(),
900 bstrReadOnlyGuest.raw());
901
902}
903
904void Console::guestPropertiesVRDPUpdateLocationChange(uint32_t u32ClientId, const char *pszLocation)
905{
906 if (!guestPropertiesVRDPEnabled())
907 return;
908
909 LogFlowFunc(("\n"));
910
911 char szPropNm[256];
912 Bstr bstrReadOnlyGuest(L"RDONLYGUEST");
913
914 RTStrPrintf(szPropNm, sizeof(szPropNm), "/VirtualBox/HostInfo/VRDP/Client/%u/Location", u32ClientId);
915 Bstr clientLocation(pszLocation);
916
917 mMachine->SetGuestProperty(Bstr(szPropNm).raw(),
918 clientLocation.raw(),
919 bstrReadOnlyGuest.raw());
920
921}
922
923void Console::guestPropertiesVRDPUpdateOtherInfoChange(uint32_t u32ClientId, const char *pszOtherInfo)
924{
925 if (!guestPropertiesVRDPEnabled())
926 return;
927
928 LogFlowFunc(("\n"));
929
930 char szPropNm[256];
931 Bstr bstrReadOnlyGuest(L"RDONLYGUEST");
932
933 RTStrPrintf(szPropNm, sizeof(szPropNm), "/VirtualBox/HostInfo/VRDP/Client/%u/OtherInfo", u32ClientId);
934 Bstr clientOtherInfo(pszOtherInfo);
935
936 mMachine->SetGuestProperty(Bstr(szPropNm).raw(),
937 clientOtherInfo.raw(),
938 bstrReadOnlyGuest.raw());
939
940}
941
942void Console::guestPropertiesVRDPUpdateClientAttach(uint32_t u32ClientId, bool fAttached)
943{
944 if (!guestPropertiesVRDPEnabled())
945 return;
946
947 LogFlowFunc(("\n"));
948
949 Bstr bstrReadOnlyGuest(L"RDONLYGUEST");
950
951 char szPropNm[256];
952 RTStrPrintf(szPropNm, sizeof(szPropNm), "/VirtualBox/HostInfo/VRDP/Client/%u/Attach", u32ClientId);
953
954 Bstr bstrValue = fAttached? "1": "0";
955
956 mMachine->SetGuestProperty(Bstr(szPropNm).raw(),
957 bstrValue.raw(),
958 bstrReadOnlyGuest.raw());
959}
960
961void Console::guestPropertiesVRDPUpdateDisconnect(uint32_t u32ClientId)
962{
963 if (!guestPropertiesVRDPEnabled())
964 return;
965
966 LogFlowFunc(("\n"));
967
968 Bstr bstrReadOnlyGuest(L"RDONLYGUEST");
969
970 char szPropNm[256];
971 RTStrPrintf(szPropNm, sizeof(szPropNm), "/VirtualBox/HostInfo/VRDP/Client/%u/Name", u32ClientId);
972 mMachine->SetGuestProperty(Bstr(szPropNm).raw(), NULL,
973 bstrReadOnlyGuest.raw());
974
975 RTStrPrintf(szPropNm, sizeof(szPropNm), "/VirtualBox/HostInfo/VRDP/Client/%u/User", u32ClientId);
976 mMachine->SetGuestProperty(Bstr(szPropNm).raw(), NULL,
977 bstrReadOnlyGuest.raw());
978
979 RTStrPrintf(szPropNm, sizeof(szPropNm), "/VirtualBox/HostInfo/VRDP/Client/%u/Domain", u32ClientId);
980 mMachine->SetGuestProperty(Bstr(szPropNm).raw(), NULL,
981 bstrReadOnlyGuest.raw());
982
983 RTStrPrintf(szPropNm, sizeof(szPropNm), "/VirtualBox/HostInfo/VRDP/Client/%u/Attach", u32ClientId);
984 mMachine->SetGuestProperty(Bstr(szPropNm).raw(), NULL,
985 bstrReadOnlyGuest.raw());
986
987 char szClientId[64];
988 RTStrPrintf(szClientId, sizeof(szClientId), "%d", u32ClientId);
989 mMachine->SetGuestProperty(Bstr("/VirtualBox/HostInfo/VRDP/LastDisconnectedClient").raw(),
990 Bstr(szClientId).raw(),
991 bstrReadOnlyGuest.raw());
992
993 return;
994}
995
996#endif /* VBOX_WITH_GUEST_PROPS */
997
998#ifdef VBOX_WITH_EXTPACK
999/**
1000 * Used by VRDEServer and others to talke to the extension pack manager.
1001 *
1002 * @returns The extension pack manager.
1003 */
1004ExtPackManager *Console::getExtPackManager()
1005{
1006 return mptrExtPackManager;
1007}
1008#endif
1009
1010
1011int Console::VRDPClientLogon(uint32_t u32ClientId, const char *pszUser, const char *pszPassword, const char *pszDomain)
1012{
1013 LogFlowFuncEnter();
1014 LogFlowFunc(("%d, %s, %s, %s\n", u32ClientId, pszUser, pszPassword, pszDomain));
1015
1016 AutoCaller autoCaller(this);
1017 if (!autoCaller.isOk())
1018 {
1019 /* Console has been already uninitialized, deny request */
1020 LogRel(("AUTH: Access denied (Console uninitialized).\n"));
1021 LogFlowFuncLeave();
1022 return VERR_ACCESS_DENIED;
1023 }
1024
1025 Bstr id;
1026 HRESULT hrc = mMachine->COMGETTER(Id)(id.asOutParam());
1027 Guid uuid = Guid(id);
1028
1029 AssertComRCReturn(hrc, VERR_ACCESS_DENIED);
1030
1031 AuthType_T authType = AuthType_Null;
1032 hrc = mVRDEServer->COMGETTER(AuthType)(&authType);
1033 AssertComRCReturn(hrc, VERR_ACCESS_DENIED);
1034
1035 ULONG authTimeout = 0;
1036 hrc = mVRDEServer->COMGETTER(AuthTimeout)(&authTimeout);
1037 AssertComRCReturn(hrc, VERR_ACCESS_DENIED);
1038
1039 AuthResult result = AuthResultAccessDenied;
1040 AuthGuestJudgement guestJudgement = AuthGuestNotAsked;
1041
1042 LogFlowFunc(("Auth type %d\n", authType));
1043
1044 LogRel(("AUTH: User: [%s]. Domain: [%s]. Authentication type: [%s]\n",
1045 pszUser, pszDomain,
1046 authType == AuthType_Null?
1047 "Null":
1048 (authType == AuthType_External?
1049 "External":
1050 (authType == AuthType_Guest?
1051 "Guest":
1052 "INVALID"
1053 )
1054 )
1055 ));
1056
1057 switch (authType)
1058 {
1059 case AuthType_Null:
1060 {
1061 result = AuthResultAccessGranted;
1062 break;
1063 }
1064
1065 case AuthType_External:
1066 {
1067 /* Call the external library. */
1068 result = mConsoleVRDPServer->Authenticate(uuid, guestJudgement, pszUser, pszPassword, pszDomain, u32ClientId);
1069
1070 if (result != AuthResultDelegateToGuest)
1071 {
1072 break;
1073 }
1074
1075 LogRel(("AUTH: Delegated to guest.\n"));
1076
1077 LogFlowFunc(("External auth asked for guest judgement\n"));
1078 } /* pass through */
1079
1080 case AuthType_Guest:
1081 {
1082 guestJudgement = AuthGuestNotReacted;
1083
1084 // @todo r=dj locking required here for m_pVMMDev?
1085 PPDMIVMMDEVPORT pDevPort;
1086 if ( (m_pVMMDev)
1087 && ((pDevPort = m_pVMMDev->getVMMDevPort()))
1088 )
1089 {
1090 /* Issue the request to guest. Assume that the call does not require EMT. It should not. */
1091
1092 /* Ask the guest to judge these credentials. */
1093 uint32_t u32GuestFlags = VMMDEV_SETCREDENTIALS_JUDGE;
1094
1095 int rc = pDevPort->pfnSetCredentials(pDevPort, pszUser, pszPassword, pszDomain, u32GuestFlags);
1096
1097 if (RT_SUCCESS(rc))
1098 {
1099 /* Wait for guest. */
1100 rc = m_pVMMDev->WaitCredentialsJudgement(authTimeout, &u32GuestFlags);
1101
1102 if (RT_SUCCESS(rc))
1103 {
1104 switch (u32GuestFlags & (VMMDEV_CREDENTIALS_JUDGE_OK | VMMDEV_CREDENTIALS_JUDGE_DENY | VMMDEV_CREDENTIALS_JUDGE_NOJUDGEMENT))
1105 {
1106 case VMMDEV_CREDENTIALS_JUDGE_DENY: guestJudgement = AuthGuestAccessDenied; break;
1107 case VMMDEV_CREDENTIALS_JUDGE_NOJUDGEMENT: guestJudgement = AuthGuestNoJudgement; break;
1108 case VMMDEV_CREDENTIALS_JUDGE_OK: guestJudgement = AuthGuestAccessGranted; break;
1109 default:
1110 LogFlowFunc(("Invalid guest flags %08X!!!\n", u32GuestFlags)); break;
1111 }
1112 }
1113 else
1114 {
1115 LogFlowFunc(("Wait for credentials judgement rc = %Rrc!!!\n", rc));
1116 }
1117
1118 LogFlowFunc(("Guest judgement %d\n", guestJudgement));
1119 }
1120 else
1121 {
1122 LogFlowFunc(("Could not set credentials rc = %Rrc!!!\n", rc));
1123 }
1124 }
1125
1126 if (authType == AuthType_External)
1127 {
1128 LogRel(("AUTH: Guest judgement %d.\n", guestJudgement));
1129 LogFlowFunc(("External auth called again with guest judgement = %d\n", guestJudgement));
1130 result = mConsoleVRDPServer->Authenticate(uuid, guestJudgement, pszUser, pszPassword, pszDomain, u32ClientId);
1131 }
1132 else
1133 {
1134 switch (guestJudgement)
1135 {
1136 case AuthGuestAccessGranted:
1137 result = AuthResultAccessGranted;
1138 break;
1139 default:
1140 result = AuthResultAccessDenied;
1141 break;
1142 }
1143 }
1144 } break;
1145
1146 default:
1147 AssertFailed();
1148 }
1149
1150 LogFlowFunc(("Result = %d\n", result));
1151 LogFlowFuncLeave();
1152
1153 if (result != AuthResultAccessGranted)
1154 {
1155 /* Reject. */
1156 LogRel(("AUTH: Access denied.\n"));
1157 return VERR_ACCESS_DENIED;
1158 }
1159
1160 LogRel(("AUTH: Access granted.\n"));
1161
1162 /* Multiconnection check must be made after authentication, so bad clients would not interfere with a good one. */
1163 BOOL allowMultiConnection = FALSE;
1164 hrc = mVRDEServer->COMGETTER(AllowMultiConnection)(&allowMultiConnection);
1165 AssertComRCReturn(hrc, VERR_ACCESS_DENIED);
1166
1167 BOOL reuseSingleConnection = FALSE;
1168 hrc = mVRDEServer->COMGETTER(ReuseSingleConnection)(&reuseSingleConnection);
1169 AssertComRCReturn(hrc, VERR_ACCESS_DENIED);
1170
1171 LogFlowFunc(("allowMultiConnection %d, reuseSingleConnection = %d, mcVRDPClients = %d, mu32SingleRDPClientId = %d\n", allowMultiConnection, reuseSingleConnection, mcVRDPClients, mu32SingleRDPClientId));
1172
1173 if (allowMultiConnection == FALSE)
1174 {
1175 /* Note: the 'mcVRDPClients' variable is incremented in ClientConnect callback, which is called when the client
1176 * is successfully connected, that is after the ClientLogon callback. Therefore the mcVRDPClients
1177 * value is 0 for first client.
1178 */
1179 if (mcVRDPClients != 0)
1180 {
1181 Assert(mcVRDPClients == 1);
1182 /* There is a client already.
1183 * If required drop the existing client connection and let the connecting one in.
1184 */
1185 if (reuseSingleConnection)
1186 {
1187 LogRel(("AUTH: Multiple connections are not enabled. Disconnecting existing client.\n"));
1188 mConsoleVRDPServer->DisconnectClient(mu32SingleRDPClientId, false);
1189 }
1190 else
1191 {
1192 /* Reject. */
1193 LogRel(("AUTH: Multiple connections are not enabled. Access denied.\n"));
1194 return VERR_ACCESS_DENIED;
1195 }
1196 }
1197
1198 /* Save the connected client id. From now on it will be necessary to disconnect this one. */
1199 mu32SingleRDPClientId = u32ClientId;
1200 }
1201
1202#ifdef VBOX_WITH_GUEST_PROPS
1203 guestPropertiesVRDPUpdateLogon(u32ClientId, pszUser, pszDomain);
1204#endif /* VBOX_WITH_GUEST_PROPS */
1205
1206 /* Check if the successfully verified credentials are to be sent to the guest. */
1207 BOOL fProvideGuestCredentials = FALSE;
1208
1209 Bstr value;
1210 hrc = mMachine->GetExtraData(Bstr("VRDP/ProvideGuestCredentials").raw(),
1211 value.asOutParam());
1212 if (SUCCEEDED(hrc) && value == "1")
1213 {
1214 /* Provide credentials only if there are no logged in users. */
1215 Bstr noLoggedInUsersValue;
1216 LONG64 ul64Timestamp = 0;
1217 Bstr flags;
1218
1219 hrc = getGuestProperty(Bstr("/VirtualBox/GuestInfo/OS/NoLoggedInUsers").raw(),
1220 noLoggedInUsersValue.asOutParam(), &ul64Timestamp, flags.asOutParam());
1221
1222 if (SUCCEEDED(hrc) && noLoggedInUsersValue != Bstr("false"))
1223 {
1224 /* And only if there are no connected clients. */
1225 if (ASMAtomicCmpXchgBool(&mcGuestCredentialsProvided, true, false))
1226 {
1227 fProvideGuestCredentials = TRUE;
1228 }
1229 }
1230 }
1231
1232 // @todo r=dj locking required here for m_pVMMDev?
1233 if ( fProvideGuestCredentials
1234 && m_pVMMDev)
1235 {
1236 uint32_t u32GuestFlags = VMMDEV_SETCREDENTIALS_GUESTLOGON;
1237
1238 PPDMIVMMDEVPORT pDevPort = m_pVMMDev->getVMMDevPort();
1239 if (pDevPort)
1240 {
1241 int rc = pDevPort->pfnSetCredentials(m_pVMMDev->getVMMDevPort(),
1242 pszUser, pszPassword, pszDomain, u32GuestFlags);
1243 AssertRC(rc);
1244 }
1245 }
1246
1247 return VINF_SUCCESS;
1248}
1249
1250void Console::VRDPClientStatusChange(uint32_t u32ClientId, const char *pszStatus)
1251{
1252 LogFlowFuncEnter();
1253
1254 AutoCaller autoCaller(this);
1255 AssertComRCReturnVoid(autoCaller.rc());
1256
1257 LogFlowFunc(("%s\n", pszStatus));
1258
1259#ifdef VBOX_WITH_GUEST_PROPS
1260 /* Parse the status string. */
1261 if (RTStrICmp(pszStatus, "ATTACH") == 0)
1262 {
1263 guestPropertiesVRDPUpdateClientAttach(u32ClientId, true);
1264 }
1265 else if (RTStrICmp(pszStatus, "DETACH") == 0)
1266 {
1267 guestPropertiesVRDPUpdateClientAttach(u32ClientId, false);
1268 }
1269 else if (RTStrNICmp(pszStatus, "NAME=", strlen("NAME=")) == 0)
1270 {
1271 guestPropertiesVRDPUpdateNameChange(u32ClientId, pszStatus + strlen("NAME="));
1272 }
1273 else if (RTStrNICmp(pszStatus, "CIPA=", strlen("CIPA=")) == 0)
1274 {
1275 guestPropertiesVRDPUpdateIPAddrChange(u32ClientId, pszStatus + strlen("CIPA="));
1276 }
1277 else if (RTStrNICmp(pszStatus, "CLOCATION=", strlen("CLOCATION=")) == 0)
1278 {
1279 guestPropertiesVRDPUpdateLocationChange(u32ClientId, pszStatus + strlen("CLOCATION="));
1280 }
1281 else if (RTStrNICmp(pszStatus, "COINFO=", strlen("COINFO=")) == 0)
1282 {
1283 guestPropertiesVRDPUpdateOtherInfoChange(u32ClientId, pszStatus + strlen("COINFO="));
1284 }
1285#endif
1286
1287 LogFlowFuncLeave();
1288}
1289
1290void Console::VRDPClientConnect(uint32_t u32ClientId)
1291{
1292 LogFlowFuncEnter();
1293
1294 AutoCaller autoCaller(this);
1295 AssertComRCReturnVoid(autoCaller.rc());
1296
1297 uint32_t u32Clients = ASMAtomicIncU32(&mcVRDPClients);
1298 VMMDev *pDev;
1299 PPDMIVMMDEVPORT pPort;
1300 if ( (u32Clients == 1)
1301 && ((pDev = getVMMDev()))
1302 && ((pPort = pDev->getVMMDevPort()))
1303 )
1304 {
1305 pPort->pfnVRDPChange(pPort,
1306 true,
1307 VRDP_EXPERIENCE_LEVEL_FULL); // @todo configurable
1308 }
1309
1310 NOREF(u32ClientId);
1311 mDisplay->VideoAccelVRDP(true);
1312
1313#ifdef VBOX_WITH_GUEST_PROPS
1314 guestPropertiesVRDPUpdateActiveClient(u32ClientId);
1315#endif /* VBOX_WITH_GUEST_PROPS */
1316
1317 LogFlowFuncLeave();
1318 return;
1319}
1320
1321void Console::VRDPClientDisconnect(uint32_t u32ClientId,
1322 uint32_t fu32Intercepted)
1323{
1324 LogFlowFuncEnter();
1325
1326 AutoCaller autoCaller(this);
1327 AssertComRCReturnVoid(autoCaller.rc());
1328
1329 AssertReturnVoid(mConsoleVRDPServer);
1330
1331 uint32_t u32Clients = ASMAtomicDecU32(&mcVRDPClients);
1332 VMMDev *pDev;
1333 PPDMIVMMDEVPORT pPort;
1334
1335 if ( (u32Clients == 0)
1336 && ((pDev = getVMMDev()))
1337 && ((pPort = pDev->getVMMDevPort()))
1338 )
1339 {
1340 pPort->pfnVRDPChange(pPort,
1341 false,
1342 0);
1343 }
1344
1345 mDisplay->VideoAccelVRDP(false);
1346
1347 if (fu32Intercepted & VRDE_CLIENT_INTERCEPT_USB)
1348 {
1349 mConsoleVRDPServer->USBBackendDelete(u32ClientId);
1350 }
1351
1352 if (fu32Intercepted & VRDE_CLIENT_INTERCEPT_CLIPBOARD)
1353 {
1354 mConsoleVRDPServer->ClipboardDelete(u32ClientId);
1355 }
1356
1357 if (fu32Intercepted & VRDE_CLIENT_INTERCEPT_AUDIO)
1358 {
1359 mcAudioRefs--;
1360
1361 if (mcAudioRefs <= 0)
1362 {
1363 if (mAudioSniffer)
1364 {
1365 PPDMIAUDIOSNIFFERPORT port = mAudioSniffer->getAudioSnifferPort();
1366 if (port)
1367 {
1368 port->pfnSetup(port, false, false);
1369 }
1370 }
1371 }
1372 }
1373
1374 Bstr uuid;
1375 HRESULT hrc = mMachine->COMGETTER(Id)(uuid.asOutParam());
1376 AssertComRC(hrc);
1377
1378 AuthType_T authType = AuthType_Null;
1379 hrc = mVRDEServer->COMGETTER(AuthType)(&authType);
1380 AssertComRC(hrc);
1381
1382 if (authType == AuthType_External)
1383 mConsoleVRDPServer->AuthDisconnect(uuid, u32ClientId);
1384
1385#ifdef VBOX_WITH_GUEST_PROPS
1386 guestPropertiesVRDPUpdateDisconnect(u32ClientId);
1387 if (u32Clients == 0)
1388 guestPropertiesVRDPUpdateActiveClient(0);
1389#endif /* VBOX_WITH_GUEST_PROPS */
1390
1391 if (u32Clients == 0)
1392 mcGuestCredentialsProvided = false;
1393
1394 LogFlowFuncLeave();
1395 return;
1396}
1397
1398void Console::VRDPInterceptAudio(uint32_t u32ClientId)
1399{
1400 LogFlowFuncEnter();
1401
1402 AutoCaller autoCaller(this);
1403 AssertComRCReturnVoid(autoCaller.rc());
1404
1405 LogFlowFunc(("mAudioSniffer %p, u32ClientId %d.\n",
1406 mAudioSniffer, u32ClientId));
1407 NOREF(u32ClientId);
1408
1409 ++mcAudioRefs;
1410
1411 if (mcAudioRefs == 1)
1412 {
1413 if (mAudioSniffer)
1414 {
1415 PPDMIAUDIOSNIFFERPORT port = mAudioSniffer->getAudioSnifferPort();
1416 if (port)
1417 {
1418 port->pfnSetup(port, true, true);
1419 }
1420 }
1421 }
1422
1423 LogFlowFuncLeave();
1424 return;
1425}
1426
1427void Console::VRDPInterceptUSB(uint32_t u32ClientId, void **ppvIntercept)
1428{
1429 LogFlowFuncEnter();
1430
1431 AutoCaller autoCaller(this);
1432 AssertComRCReturnVoid(autoCaller.rc());
1433
1434 AssertReturnVoid(mConsoleVRDPServer);
1435
1436 mConsoleVRDPServer->USBBackendCreate(u32ClientId, ppvIntercept);
1437
1438 LogFlowFuncLeave();
1439 return;
1440}
1441
1442void Console::VRDPInterceptClipboard(uint32_t u32ClientId)
1443{
1444 LogFlowFuncEnter();
1445
1446 AutoCaller autoCaller(this);
1447 AssertComRCReturnVoid(autoCaller.rc());
1448
1449 AssertReturnVoid(mConsoleVRDPServer);
1450
1451 mConsoleVRDPServer->ClipboardCreate(u32ClientId);
1452
1453 LogFlowFuncLeave();
1454 return;
1455}
1456
1457
1458//static
1459const char *Console::sSSMConsoleUnit = "ConsoleData";
1460//static
1461uint32_t Console::sSSMConsoleVer = 0x00010001;
1462
1463inline static const char *networkAdapterTypeToName(NetworkAdapterType_T adapterType)
1464{
1465 switch (adapterType)
1466 {
1467 case NetworkAdapterType_Am79C970A:
1468 case NetworkAdapterType_Am79C973:
1469 return "pcnet";
1470#ifdef VBOX_WITH_E1000
1471 case NetworkAdapterType_I82540EM:
1472 case NetworkAdapterType_I82543GC:
1473 case NetworkAdapterType_I82545EM:
1474 return "e1000";
1475#endif
1476#ifdef VBOX_WITH_VIRTIO
1477 case NetworkAdapterType_Virtio:
1478 return "virtio-net";
1479#endif
1480 default:
1481 AssertFailed();
1482 return "unknown";
1483 }
1484 return NULL;
1485}
1486
1487/**
1488 * Loads various console data stored in the saved state file.
1489 * This method does validation of the state file and returns an error info
1490 * when appropriate.
1491 *
1492 * The method does nothing if the machine is not in the Saved file or if
1493 * console data from it has already been loaded.
1494 *
1495 * @note The caller must lock this object for writing.
1496 */
1497HRESULT Console::loadDataFromSavedState()
1498{
1499 if (mMachineState != MachineState_Saved || mSavedStateDataLoaded)
1500 return S_OK;
1501
1502 Bstr savedStateFile;
1503 HRESULT rc = mMachine->COMGETTER(StateFilePath)(savedStateFile.asOutParam());
1504 if (FAILED(rc))
1505 return rc;
1506
1507 PSSMHANDLE ssm;
1508 int vrc = SSMR3Open(Utf8Str(savedStateFile).c_str(), 0, &ssm);
1509 if (RT_SUCCESS(vrc))
1510 {
1511 uint32_t version = 0;
1512 vrc = SSMR3Seek(ssm, sSSMConsoleUnit, 0 /* iInstance */, &version);
1513 if (SSM_VERSION_MAJOR(version) == SSM_VERSION_MAJOR(sSSMConsoleVer))
1514 {
1515 if (RT_SUCCESS(vrc))
1516 vrc = loadStateFileExecInternal(ssm, version);
1517 else if (vrc == VERR_SSM_UNIT_NOT_FOUND)
1518 vrc = VINF_SUCCESS;
1519 }
1520 else
1521 vrc = VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
1522
1523 SSMR3Close(ssm);
1524 }
1525
1526 if (RT_FAILURE(vrc))
1527 rc = setError(VBOX_E_FILE_ERROR,
1528 tr("The saved state file '%ls' is invalid (%Rrc). Delete the saved state and try again"),
1529 savedStateFile.raw(), vrc);
1530
1531 mSavedStateDataLoaded = true;
1532
1533 return rc;
1534}
1535
1536/**
1537 * Callback handler to save various console data to the state file,
1538 * called when the user saves the VM state.
1539 *
1540 * @param pvUser pointer to Console
1541 *
1542 * @note Locks the Console object for reading.
1543 */
1544//static
1545DECLCALLBACK(void)
1546Console::saveStateFileExec(PSSMHANDLE pSSM, void *pvUser)
1547{
1548 LogFlowFunc(("\n"));
1549
1550 Console *that = static_cast<Console *>(pvUser);
1551 AssertReturnVoid(that);
1552
1553 AutoCaller autoCaller(that);
1554 AssertComRCReturnVoid(autoCaller.rc());
1555
1556 AutoReadLock alock(that COMMA_LOCKVAL_SRC_POS);
1557
1558 int vrc = SSMR3PutU32(pSSM, (uint32_t)that->m_mapSharedFolders.size());
1559 AssertRC(vrc);
1560
1561 for (SharedFolderMap::const_iterator it = that->m_mapSharedFolders.begin();
1562 it != that->m_mapSharedFolders.end();
1563 ++it)
1564 {
1565 SharedFolder *pSF = (*it).second;
1566 AutoCaller sfCaller(pSF);
1567 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
1568
1569 Utf8Str name = pSF->getName();
1570 vrc = SSMR3PutU32(pSSM, (uint32_t)name.length() + 1 /* term. 0 */);
1571 AssertRC(vrc);
1572 vrc = SSMR3PutStrZ(pSSM, name.c_str());
1573 AssertRC(vrc);
1574
1575 Utf8Str hostPath = pSF->getHostPath();
1576 vrc = SSMR3PutU32(pSSM, (uint32_t)hostPath.length() + 1 /* term. 0 */);
1577 AssertRC(vrc);
1578 vrc = SSMR3PutStrZ(pSSM, hostPath.c_str());
1579 AssertRC(vrc);
1580
1581 vrc = SSMR3PutBool(pSSM, !!pSF->isWritable());
1582 AssertRC(vrc);
1583
1584 vrc = SSMR3PutBool(pSSM, !!pSF->isAutoMounted());
1585 AssertRC(vrc);
1586 }
1587
1588 return;
1589}
1590
1591/**
1592 * Callback handler to load various console data from the state file.
1593 * Called when the VM is being restored from the saved state.
1594 *
1595 * @param pvUser pointer to Console
1596 * @param uVersion Console unit version.
1597 * Should match sSSMConsoleVer.
1598 * @param uPass The data pass.
1599 *
1600 * @note Should locks the Console object for writing, if necessary.
1601 */
1602//static
1603DECLCALLBACK(int)
1604Console::loadStateFileExec(PSSMHANDLE pSSM, void *pvUser, uint32_t uVersion, uint32_t uPass)
1605{
1606 LogFlowFunc(("\n"));
1607
1608 if (SSM_VERSION_MAJOR_CHANGED(uVersion, sSSMConsoleVer))
1609 return VERR_VERSION_MISMATCH;
1610 Assert(uPass == SSM_PASS_FINAL); NOREF(uPass);
1611
1612 Console *that = static_cast<Console *>(pvUser);
1613 AssertReturn(that, VERR_INVALID_PARAMETER);
1614
1615 /* Currently, nothing to do when we've been called from VMR3Load*. */
1616 return SSMR3SkipToEndOfUnit(pSSM);
1617}
1618
1619/**
1620 * Method to load various console data from the state file.
1621 * Called from #loadDataFromSavedState.
1622 *
1623 * @param pvUser pointer to Console
1624 * @param u32Version Console unit version.
1625 * Should match sSSMConsoleVer.
1626 *
1627 * @note Locks the Console object for writing.
1628 */
1629int
1630Console::loadStateFileExecInternal(PSSMHANDLE pSSM, uint32_t u32Version)
1631{
1632 AutoCaller autoCaller(this);
1633 AssertComRCReturn(autoCaller.rc(), VERR_ACCESS_DENIED);
1634
1635 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1636
1637 AssertReturn(m_mapSharedFolders.size() == 0, VERR_INTERNAL_ERROR);
1638
1639 uint32_t size = 0;
1640 int vrc = SSMR3GetU32(pSSM, &size);
1641 AssertRCReturn(vrc, vrc);
1642
1643 for (uint32_t i = 0; i < size; ++i)
1644 {
1645 Utf8Str strName;
1646 Utf8Str strHostPath;
1647 bool writable = true;
1648 bool autoMount = false;
1649
1650 uint32_t szBuf = 0;
1651 char *buf = NULL;
1652
1653 vrc = SSMR3GetU32(pSSM, &szBuf);
1654 AssertRCReturn(vrc, vrc);
1655 buf = new char[szBuf];
1656 vrc = SSMR3GetStrZ(pSSM, buf, szBuf);
1657 AssertRC(vrc);
1658 strName = buf;
1659 delete[] buf;
1660
1661 vrc = SSMR3GetU32(pSSM, &szBuf);
1662 AssertRCReturn(vrc, vrc);
1663 buf = new char[szBuf];
1664 vrc = SSMR3GetStrZ(pSSM, buf, szBuf);
1665 AssertRC(vrc);
1666 strHostPath = buf;
1667 delete[] buf;
1668
1669 if (u32Version > 0x00010000)
1670 SSMR3GetBool(pSSM, &writable);
1671
1672 if (u32Version > 0x00010000) // ???
1673 SSMR3GetBool(pSSM, &autoMount);
1674
1675 ComObjPtr<SharedFolder> pSharedFolder;
1676 pSharedFolder.createObject();
1677 HRESULT rc = pSharedFolder->init(this,
1678 strName,
1679 strHostPath,
1680 writable,
1681 autoMount,
1682 false /* fFailOnError */);
1683 AssertComRCReturn(rc, VERR_INTERNAL_ERROR);
1684
1685 m_mapSharedFolders.insert(std::make_pair(strName, pSharedFolder));
1686 }
1687
1688 return VINF_SUCCESS;
1689}
1690
1691#ifdef VBOX_WITH_GUEST_PROPS
1692
1693// static
1694DECLCALLBACK(int) Console::doGuestPropNotification(void *pvExtension,
1695 uint32_t u32Function,
1696 void *pvParms,
1697 uint32_t cbParms)
1698{
1699 using namespace guestProp;
1700
1701 Assert(u32Function == 0); NOREF(u32Function);
1702
1703 /*
1704 * No locking, as this is purely a notification which does not make any
1705 * changes to the object state.
1706 */
1707 PHOSTCALLBACKDATA pCBData = reinterpret_cast<PHOSTCALLBACKDATA>(pvParms);
1708 AssertReturn(sizeof(HOSTCALLBACKDATA) == cbParms, VERR_INVALID_PARAMETER);
1709 AssertReturn(HOSTCALLBACKMAGIC == pCBData->u32Magic, VERR_INVALID_PARAMETER);
1710 LogFlow(("Console::doGuestPropNotification: pCBData={.pcszName=%s, .pcszValue=%s, .pcszFlags=%s}\n",
1711 pCBData->pcszName, pCBData->pcszValue, pCBData->pcszFlags));
1712
1713 int rc;
1714 Bstr name(pCBData->pcszName);
1715 Bstr value(pCBData->pcszValue);
1716 Bstr flags(pCBData->pcszFlags);
1717 ComObjPtr<Console> pConsole = reinterpret_cast<Console *>(pvExtension);
1718 HRESULT hrc = pConsole->mControl->PushGuestProperty(name.raw(),
1719 value.raw(),
1720 pCBData->u64Timestamp,
1721 flags.raw());
1722 if (SUCCEEDED(hrc))
1723 rc = VINF_SUCCESS;
1724 else
1725 {
1726 LogFlow(("Console::doGuestPropNotification: hrc=%Rhrc pCBData={.pcszName=%s, .pcszValue=%s, .pcszFlags=%s}\n",
1727 hrc, pCBData->pcszName, pCBData->pcszValue, pCBData->pcszFlags));
1728 rc = Global::vboxStatusCodeFromCOM(hrc);
1729 }
1730 return rc;
1731}
1732
1733HRESULT Console::doEnumerateGuestProperties(CBSTR aPatterns,
1734 ComSafeArrayOut(BSTR, aNames),
1735 ComSafeArrayOut(BSTR, aValues),
1736 ComSafeArrayOut(LONG64, aTimestamps),
1737 ComSafeArrayOut(BSTR, aFlags))
1738{
1739 AssertReturn(m_pVMMDev, E_FAIL);
1740
1741 using namespace guestProp;
1742
1743 VBOXHGCMSVCPARM parm[3];
1744
1745 Utf8Str utf8Patterns(aPatterns);
1746 parm[0].type = VBOX_HGCM_SVC_PARM_PTR;
1747 parm[0].u.pointer.addr = (void*)utf8Patterns.c_str();
1748 parm[0].u.pointer.size = (uint32_t)utf8Patterns.length() + 1;
1749
1750 /*
1751 * Now things get slightly complicated. Due to a race with the guest adding
1752 * properties, there is no good way to know how much to enlarge a buffer for
1753 * the service to enumerate into. We choose a decent starting size and loop a
1754 * few times, each time retrying with the size suggested by the service plus
1755 * one Kb.
1756 */
1757 size_t cchBuf = 4096;
1758 Utf8Str Utf8Buf;
1759 int vrc = VERR_BUFFER_OVERFLOW;
1760 for (unsigned i = 0; i < 10 && (VERR_BUFFER_OVERFLOW == vrc); ++i)
1761 {
1762 try
1763 {
1764 Utf8Buf.reserve(cchBuf + 1024);
1765 }
1766 catch(...)
1767 {
1768 return E_OUTOFMEMORY;
1769 }
1770 parm[1].type = VBOX_HGCM_SVC_PARM_PTR;
1771 parm[1].u.pointer.addr = Utf8Buf.mutableRaw();
1772 parm[1].u.pointer.size = (uint32_t)cchBuf + 1024;
1773 vrc = m_pVMMDev->hgcmHostCall("VBoxGuestPropSvc", ENUM_PROPS_HOST, 3,
1774 &parm[0]);
1775 Utf8Buf.jolt();
1776 if (parm[2].type != VBOX_HGCM_SVC_PARM_32BIT)
1777 return setError(E_FAIL, tr("Internal application error"));
1778 cchBuf = parm[2].u.uint32;
1779 }
1780 if (VERR_BUFFER_OVERFLOW == vrc)
1781 return setError(E_UNEXPECTED,
1782 tr("Temporary failure due to guest activity, please retry"));
1783
1784 /*
1785 * Finally we have to unpack the data returned by the service into the safe
1786 * arrays supplied by the caller. We start by counting the number of entries.
1787 */
1788 const char *pszBuf
1789 = reinterpret_cast<const char *>(parm[1].u.pointer.addr);
1790 unsigned cEntries = 0;
1791 /* The list is terminated by a zero-length string at the end of a set
1792 * of four strings. */
1793 for (size_t i = 0; strlen(pszBuf + i) != 0; )
1794 {
1795 /* We are counting sets of four strings. */
1796 for (unsigned j = 0; j < 4; ++j)
1797 i += strlen(pszBuf + i) + 1;
1798 ++cEntries;
1799 }
1800
1801 /*
1802 * And now we create the COM safe arrays and fill them in.
1803 */
1804 com::SafeArray<BSTR> names(cEntries);
1805 com::SafeArray<BSTR> values(cEntries);
1806 com::SafeArray<LONG64> timestamps(cEntries);
1807 com::SafeArray<BSTR> flags(cEntries);
1808 size_t iBuf = 0;
1809 /* Rely on the service to have formated the data correctly. */
1810 for (unsigned i = 0; i < cEntries; ++i)
1811 {
1812 size_t cchName = strlen(pszBuf + iBuf);
1813 Bstr(pszBuf + iBuf).detachTo(&names[i]);
1814 iBuf += cchName + 1;
1815 size_t cchValue = strlen(pszBuf + iBuf);
1816 Bstr(pszBuf + iBuf).detachTo(&values[i]);
1817 iBuf += cchValue + 1;
1818 size_t cchTimestamp = strlen(pszBuf + iBuf);
1819 timestamps[i] = RTStrToUInt64(pszBuf + iBuf);
1820 iBuf += cchTimestamp + 1;
1821 size_t cchFlags = strlen(pszBuf + iBuf);
1822 Bstr(pszBuf + iBuf).detachTo(&flags[i]);
1823 iBuf += cchFlags + 1;
1824 }
1825 names.detachTo(ComSafeArrayOutArg(aNames));
1826 values.detachTo(ComSafeArrayOutArg(aValues));
1827 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
1828 flags.detachTo(ComSafeArrayOutArg(aFlags));
1829 return S_OK;
1830}
1831
1832#endif /* VBOX_WITH_GUEST_PROPS */
1833
1834
1835// IConsole properties
1836/////////////////////////////////////////////////////////////////////////////
1837
1838STDMETHODIMP Console::COMGETTER(Machine)(IMachine **aMachine)
1839{
1840 CheckComArgOutPointerValid(aMachine);
1841
1842 AutoCaller autoCaller(this);
1843 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1844
1845 /* mMachine is constant during life time, no need to lock */
1846 mMachine.queryInterfaceTo(aMachine);
1847
1848 /* callers expect to get a valid reference, better fail than crash them */
1849 if (mMachine.isNull())
1850 return E_FAIL;
1851
1852 return S_OK;
1853}
1854
1855STDMETHODIMP Console::COMGETTER(State)(MachineState_T *aMachineState)
1856{
1857 CheckComArgOutPointerValid(aMachineState);
1858
1859 AutoCaller autoCaller(this);
1860 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1861
1862 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1863
1864 /* we return our local state (since it's always the same as on the server) */
1865 *aMachineState = mMachineState;
1866
1867 return S_OK;
1868}
1869
1870STDMETHODIMP Console::COMGETTER(Guest)(IGuest **aGuest)
1871{
1872 CheckComArgOutPointerValid(aGuest);
1873
1874 AutoCaller autoCaller(this);
1875 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1876
1877 /* mGuest is constant during life time, no need to lock */
1878 mGuest.queryInterfaceTo(aGuest);
1879
1880 return S_OK;
1881}
1882
1883STDMETHODIMP Console::COMGETTER(Keyboard)(IKeyboard **aKeyboard)
1884{
1885 CheckComArgOutPointerValid(aKeyboard);
1886
1887 AutoCaller autoCaller(this);
1888 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1889
1890 /* mKeyboard is constant during life time, no need to lock */
1891 mKeyboard.queryInterfaceTo(aKeyboard);
1892
1893 return S_OK;
1894}
1895
1896STDMETHODIMP Console::COMGETTER(Mouse)(IMouse **aMouse)
1897{
1898 CheckComArgOutPointerValid(aMouse);
1899
1900 AutoCaller autoCaller(this);
1901 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1902
1903 /* mMouse is constant during life time, no need to lock */
1904 mMouse.queryInterfaceTo(aMouse);
1905
1906 return S_OK;
1907}
1908
1909STDMETHODIMP Console::COMGETTER(Display)(IDisplay **aDisplay)
1910{
1911 CheckComArgOutPointerValid(aDisplay);
1912
1913 AutoCaller autoCaller(this);
1914 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1915
1916 /* mDisplay is constant during life time, no need to lock */
1917 mDisplay.queryInterfaceTo(aDisplay);
1918
1919 return S_OK;
1920}
1921
1922STDMETHODIMP Console::COMGETTER(Debugger)(IMachineDebugger **aDebugger)
1923{
1924 CheckComArgOutPointerValid(aDebugger);
1925
1926 AutoCaller autoCaller(this);
1927 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1928
1929 /* we need a write lock because of the lazy mDebugger initialization*/
1930 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1931
1932 /* check if we have to create the debugger object */
1933 if (!mDebugger)
1934 {
1935 unconst(mDebugger).createObject();
1936 mDebugger->init(this);
1937 }
1938
1939 mDebugger.queryInterfaceTo(aDebugger);
1940
1941 return S_OK;
1942}
1943
1944STDMETHODIMP Console::COMGETTER(USBDevices)(ComSafeArrayOut(IUSBDevice *, aUSBDevices))
1945{
1946 CheckComArgOutSafeArrayPointerValid(aUSBDevices);
1947
1948 AutoCaller autoCaller(this);
1949 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1950
1951 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1952
1953 SafeIfaceArray<IUSBDevice> collection(mUSBDevices);
1954 collection.detachTo(ComSafeArrayOutArg(aUSBDevices));
1955
1956 return S_OK;
1957}
1958
1959STDMETHODIMP Console::COMGETTER(RemoteUSBDevices)(ComSafeArrayOut(IHostUSBDevice *, aRemoteUSBDevices))
1960{
1961 CheckComArgOutSafeArrayPointerValid(aRemoteUSBDevices);
1962
1963 AutoCaller autoCaller(this);
1964 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1965
1966 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1967
1968 SafeIfaceArray<IHostUSBDevice> collection(mRemoteUSBDevices);
1969 collection.detachTo(ComSafeArrayOutArg(aRemoteUSBDevices));
1970
1971 return S_OK;
1972}
1973
1974STDMETHODIMP Console::COMGETTER(VRDEServerInfo)(IVRDEServerInfo **aVRDEServerInfo)
1975{
1976 CheckComArgOutPointerValid(aVRDEServerInfo);
1977
1978 AutoCaller autoCaller(this);
1979 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1980
1981 /* mVRDEServerInfo is constant during life time, no need to lock */
1982 mVRDEServerInfo.queryInterfaceTo(aVRDEServerInfo);
1983
1984 return S_OK;
1985}
1986
1987STDMETHODIMP
1988Console::COMGETTER(SharedFolders)(ComSafeArrayOut(ISharedFolder *, aSharedFolders))
1989{
1990 CheckComArgOutSafeArrayPointerValid(aSharedFolders);
1991
1992 AutoCaller autoCaller(this);
1993 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1994
1995 /* loadDataFromSavedState() needs a write lock */
1996 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1997
1998 /* Read console data stored in the saved state file (if not yet done) */
1999 HRESULT rc = loadDataFromSavedState();
2000 if (FAILED(rc)) return rc;
2001
2002 SafeIfaceArray<ISharedFolder> sf(m_mapSharedFolders);
2003 sf.detachTo(ComSafeArrayOutArg(aSharedFolders));
2004
2005 return S_OK;
2006}
2007
2008
2009STDMETHODIMP Console::COMGETTER(EventSource)(IEventSource ** aEventSource)
2010{
2011 CheckComArgOutPointerValid(aEventSource);
2012
2013 AutoCaller autoCaller(this);
2014 HRESULT hrc = autoCaller.rc();
2015 if (SUCCEEDED(hrc))
2016 {
2017 // no need to lock - lifetime constant
2018 mEventSource.queryInterfaceTo(aEventSource);
2019 }
2020
2021 return hrc;
2022}
2023
2024STDMETHODIMP Console::COMGETTER(AttachedPCIDevices)(ComSafeArrayOut(IPCIDeviceAttachment *, aAttachments))
2025{
2026 CheckComArgOutSafeArrayPointerValid(aAttachments);
2027
2028 AutoCaller autoCaller(this);
2029 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2030
2031 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2032
2033 if (mBusMgr)
2034 mBusMgr->listAttachedPCIDevices(ComSafeArrayOutArg(aAttachments));
2035 else
2036 {
2037 com::SafeIfaceArray<IPCIDeviceAttachment> result((size_t)0);
2038 result.detachTo(ComSafeArrayOutArg(aAttachments));
2039 }
2040
2041 return S_OK;
2042}
2043
2044STDMETHODIMP Console::COMGETTER(UseHostClipboard)(BOOL *aUseHostClipboard)
2045{
2046 CheckComArgOutPointerValid(aUseHostClipboard);
2047
2048 AutoCaller autoCaller(this);
2049 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2050
2051 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2052
2053 *aUseHostClipboard = mfUseHostClipboard;
2054
2055 return S_OK;
2056}
2057
2058STDMETHODIMP Console::COMSETTER(UseHostClipboard)(BOOL aUseHostClipboard)
2059{
2060 AutoCaller autoCaller(this);
2061 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2062
2063 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2064
2065 mfUseHostClipboard = !!aUseHostClipboard;
2066
2067 return S_OK;
2068}
2069
2070// IConsole methods
2071/////////////////////////////////////////////////////////////////////////////
2072
2073
2074STDMETHODIMP Console::PowerUp(IProgress **aProgress)
2075{
2076 return powerUp(aProgress, false /* aPaused */);
2077}
2078
2079STDMETHODIMP Console::PowerUpPaused(IProgress **aProgress)
2080{
2081 return powerUp(aProgress, true /* aPaused */);
2082}
2083
2084STDMETHODIMP Console::PowerDown(IProgress **aProgress)
2085{
2086 LogFlowThisFuncEnter();
2087
2088 CheckComArgOutPointerValid(aProgress);
2089
2090 AutoCaller autoCaller(this);
2091 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2092
2093 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2094
2095 LogFlowThisFunc(("mMachineState=%d\n", mMachineState));
2096 switch (mMachineState)
2097 {
2098 case MachineState_Running:
2099 case MachineState_Paused:
2100 case MachineState_Stuck:
2101 break;
2102
2103 /* Try cancel the teleportation. */
2104 case MachineState_Teleporting:
2105 case MachineState_TeleportingPausedVM:
2106 if (!mptrCancelableProgress.isNull())
2107 {
2108 HRESULT hrc = mptrCancelableProgress->Cancel();
2109 if (SUCCEEDED(hrc))
2110 break;
2111 }
2112 return setError(VBOX_E_INVALID_VM_STATE, tr("Cannot power down at this point in a teleportation"));
2113
2114 /* Try cancel the live snapshot. */
2115 case MachineState_LiveSnapshotting:
2116 if (!mptrCancelableProgress.isNull())
2117 {
2118 HRESULT hrc = mptrCancelableProgress->Cancel();
2119 if (SUCCEEDED(hrc))
2120 break;
2121 }
2122 return setError(VBOX_E_INVALID_VM_STATE, tr("Cannot power down at this point in a live snapshot"));
2123
2124 /* Try cancel the FT sync. */
2125 case MachineState_FaultTolerantSyncing:
2126 if (!mptrCancelableProgress.isNull())
2127 {
2128 HRESULT hrc = mptrCancelableProgress->Cancel();
2129 if (SUCCEEDED(hrc))
2130 break;
2131 }
2132 return setError(VBOX_E_INVALID_VM_STATE, tr("Cannot power down at this point in a fault tolerant sync"));
2133
2134 /* extra nice error message for a common case */
2135 case MachineState_Saved:
2136 return setError(VBOX_E_INVALID_VM_STATE, tr("Cannot power down a saved virtual machine"));
2137 case MachineState_Stopping:
2138 return setError(VBOX_E_INVALID_VM_STATE, tr("The virtual machine is being powered down"));
2139 default:
2140 return setError(VBOX_E_INVALID_VM_STATE,
2141 tr("Invalid machine state: %s (must be Running, Paused or Stuck)"),
2142 Global::stringifyMachineState(mMachineState));
2143 }
2144
2145 LogFlowThisFunc(("Initiating SHUTDOWN request...\n"));
2146
2147 /* memorize the current machine state */
2148 MachineState_T lastMachineState = mMachineState;
2149
2150 HRESULT rc = S_OK;
2151 bool fBeganPowerDown = false;
2152
2153 do
2154 {
2155 ComPtr<IProgress> pProgress;
2156
2157 /*
2158 * request a progress object from the server
2159 * (this will set the machine state to Stopping on the server to block
2160 * others from accessing this machine)
2161 */
2162 rc = mControl->BeginPoweringDown(pProgress.asOutParam());
2163 if (FAILED(rc))
2164 break;
2165
2166 fBeganPowerDown = true;
2167
2168 /* sync the state with the server */
2169 setMachineStateLocally(MachineState_Stopping);
2170
2171 /* setup task object and thread to carry out the operation asynchronously */
2172 std::auto_ptr<VMPowerDownTask> task(new VMPowerDownTask(this, pProgress));
2173 AssertBreakStmt(task->isOk(), rc = E_FAIL);
2174
2175 int vrc = RTThreadCreate(NULL, Console::powerDownThread,
2176 (void *) task.get(), 0,
2177 RTTHREADTYPE_MAIN_WORKER, 0,
2178 "VMPwrDwn");
2179 if (RT_FAILURE(vrc))
2180 {
2181 rc = setError(E_FAIL, "Could not create VMPowerDown thread (%Rrc)", vrc);
2182 break;
2183 }
2184
2185 /* task is now owned by powerDownThread(), so release it */
2186 task.release();
2187
2188 /* pass the progress to the caller */
2189 pProgress.queryInterfaceTo(aProgress);
2190 }
2191 while (0);
2192
2193 if (FAILED(rc))
2194 {
2195 /* preserve existing error info */
2196 ErrorInfoKeeper eik;
2197
2198 if (fBeganPowerDown)
2199 {
2200 /*
2201 * cancel the requested power down procedure.
2202 * This will reset the machine state to the state it had right
2203 * before calling mControl->BeginPoweringDown().
2204 */
2205 mControl->EndPoweringDown(eik.getResultCode(), eik.getText().raw()); }
2206
2207 setMachineStateLocally(lastMachineState);
2208 }
2209
2210 LogFlowThisFunc(("rc=%Rhrc\n", rc));
2211 LogFlowThisFuncLeave();
2212
2213 return rc;
2214}
2215
2216STDMETHODIMP Console::Reset()
2217{
2218 LogFlowThisFuncEnter();
2219
2220 AutoCaller autoCaller(this);
2221 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2222
2223 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2224
2225 LogFlowThisFunc(("mMachineState=%d\n", mMachineState));
2226 if ( mMachineState != MachineState_Running
2227 && mMachineState != MachineState_Teleporting
2228 && mMachineState != MachineState_LiveSnapshotting
2229 /** @todo r=bird: This should be allowed on paused VMs as well. Later. */
2230 )
2231 return setInvalidMachineStateError();
2232
2233 /* protect mpUVM */
2234 SafeVMPtr ptrVM(this);
2235 if (!ptrVM.isOk())
2236 return ptrVM.rc();
2237
2238 /* release the lock before a VMR3* call (EMT will call us back)! */
2239 alock.release();
2240
2241 int vrc = VMR3Reset(ptrVM.rawUVM());
2242
2243 HRESULT rc = RT_SUCCESS(vrc) ? S_OK :
2244 setError(VBOX_E_VM_ERROR,
2245 tr("Could not reset the machine (%Rrc)"),
2246 vrc);
2247
2248 LogFlowThisFunc(("mMachineState=%d, rc=%Rhrc\n", mMachineState, rc));
2249 LogFlowThisFuncLeave();
2250 return rc;
2251}
2252
2253/*static*/ DECLCALLBACK(int) Console::unplugCpu(Console *pThis, PUVM pUVM, VMCPUID idCpu)
2254{
2255 LogFlowFunc(("pThis=%p pVM=%p idCpu=%u\n", pThis, pUVM, idCpu));
2256
2257 AssertReturn(pThis, VERR_INVALID_PARAMETER);
2258
2259 int vrc = PDMR3DeviceDetach(pUVM, "acpi", 0, idCpu, 0);
2260 Log(("UnplugCpu: rc=%Rrc\n", vrc));
2261
2262 return vrc;
2263}
2264
2265HRESULT Console::doCPURemove(ULONG aCpu, PUVM pUVM)
2266{
2267 HRESULT rc = S_OK;
2268
2269 LogFlowThisFuncEnter();
2270
2271 AutoCaller autoCaller(this);
2272 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2273
2274 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2275
2276 LogFlowThisFunc(("mMachineState=%d\n", mMachineState));
2277 AssertReturn(m_pVMMDev, E_FAIL);
2278 PPDMIVMMDEVPORT pVmmDevPort = m_pVMMDev->getVMMDevPort();
2279 AssertReturn(pVmmDevPort, E_FAIL);
2280
2281 if ( mMachineState != MachineState_Running
2282 && mMachineState != MachineState_Teleporting
2283 && mMachineState != MachineState_LiveSnapshotting
2284 )
2285 return setInvalidMachineStateError();
2286
2287 /* Check if the CPU is present */
2288 BOOL fCpuAttached;
2289 rc = mMachine->GetCPUStatus(aCpu, &fCpuAttached);
2290 if (FAILED(rc))
2291 return rc;
2292 if (!fCpuAttached)
2293 return setError(E_FAIL, tr("CPU %d is not attached"), aCpu);
2294
2295 /* Leave the lock before any EMT/VMMDev call. */
2296 alock.release();
2297 bool fLocked = true;
2298
2299 /* Check if the CPU is unlocked */
2300 PPDMIBASE pBase;
2301 int vrc = PDMR3QueryDeviceLun(pUVM, "acpi", 0, aCpu, &pBase);
2302 if (RT_SUCCESS(vrc))
2303 {
2304 Assert(pBase);
2305 PPDMIACPIPORT pApicPort = PDMIBASE_QUERY_INTERFACE(pBase, PDMIACPIPORT);
2306
2307 /* Notify the guest if possible. */
2308 uint32_t idCpuCore, idCpuPackage;
2309 vrc = VMR3GetCpuCoreAndPackageIdFromCpuId(pUVM, aCpu, &idCpuCore, &idCpuPackage); AssertRC(vrc);
2310 if (RT_SUCCESS(vrc))
2311 vrc = pVmmDevPort->pfnCpuHotUnplug(pVmmDevPort, idCpuCore, idCpuPackage);
2312 if (RT_SUCCESS(vrc))
2313 {
2314 unsigned cTries = 100;
2315 do
2316 {
2317 /* It will take some time until the event is processed in the guest. Wait... */
2318 vrc = pApicPort ? pApicPort->pfnGetCpuStatus(pApicPort, aCpu, &fLocked) : VERR_INVALID_POINTER;
2319 if (RT_SUCCESS(vrc) && !fLocked)
2320 break;
2321
2322 /* Sleep a bit */
2323 RTThreadSleep(100);
2324 } while (cTries-- > 0);
2325 }
2326 else if (vrc == VERR_CPU_HOTPLUG_NOT_MONITORED_BY_GUEST)
2327 {
2328 /* Query one time. It is possible that the user ejected the CPU. */
2329 vrc = pApicPort ? pApicPort->pfnGetCpuStatus(pApicPort, aCpu, &fLocked) : VERR_INVALID_POINTER;
2330 }
2331 }
2332
2333 /* If the CPU was unlocked we can detach it now. */
2334 if (RT_SUCCESS(vrc) && !fLocked)
2335 {
2336 /*
2337 * Call worker in EMT, that's faster and safer than doing everything
2338 * using VMR3ReqCall.
2339 */
2340 PVMREQ pReq;
2341 vrc = VMR3ReqCallU(pUVM, 0, &pReq, 0 /* no wait! */, VMREQFLAGS_VBOX_STATUS,
2342 (PFNRT)Console::unplugCpu, 3,
2343 this, pUVM, (VMCPUID)aCpu);
2344 if (vrc == VERR_TIMEOUT || RT_SUCCESS(vrc))
2345 {
2346 vrc = VMR3ReqWait(pReq, RT_INDEFINITE_WAIT);
2347 AssertRC(vrc);
2348 if (RT_SUCCESS(vrc))
2349 vrc = pReq->iStatus;
2350 }
2351 VMR3ReqFree(pReq);
2352
2353 if (RT_SUCCESS(vrc))
2354 {
2355 /* Detach it from the VM */
2356 vrc = VMR3HotUnplugCpu(pUVM, aCpu);
2357 AssertRC(vrc);
2358 }
2359 else
2360 rc = setError(VBOX_E_VM_ERROR,
2361 tr("Hot-Remove failed (rc=%Rrc)"), vrc);
2362 }
2363 else
2364 rc = setError(VBOX_E_VM_ERROR,
2365 tr("Hot-Remove was aborted because the CPU may still be used by the guest"), VERR_RESOURCE_BUSY);
2366
2367 LogFlowThisFunc(("mMachineState=%d, rc=%Rhrc\n", mMachineState, rc));
2368 LogFlowThisFuncLeave();
2369 return rc;
2370}
2371
2372/*static*/ DECLCALLBACK(int) Console::plugCpu(Console *pThis, PUVM pUVM, VMCPUID idCpu)
2373{
2374 LogFlowFunc(("pThis=%p uCpu=%u\n", pThis, idCpu));
2375
2376 AssertReturn(pThis, VERR_INVALID_PARAMETER);
2377
2378 int rc = VMR3HotPlugCpu(pUVM, idCpu);
2379 AssertRC(rc);
2380
2381 PCFGMNODE pInst = CFGMR3GetChild(CFGMR3GetRootU(pUVM), "Devices/acpi/0/");
2382 AssertRelease(pInst);
2383 /* nuke anything which might have been left behind. */
2384 CFGMR3RemoveNode(CFGMR3GetChildF(pInst, "LUN#%u", idCpu));
2385
2386#define RC_CHECK() do { if (RT_FAILURE(rc)) { AssertReleaseRC(rc); break; } } while (0)
2387
2388 PCFGMNODE pLunL0;
2389 PCFGMNODE pCfg;
2390 rc = CFGMR3InsertNodeF(pInst, &pLunL0, "LUN#%u", idCpu); RC_CHECK();
2391 rc = CFGMR3InsertString(pLunL0, "Driver", "ACPICpu"); RC_CHECK();
2392 rc = CFGMR3InsertNode(pLunL0, "Config", &pCfg); RC_CHECK();
2393
2394 /*
2395 * Attach the driver.
2396 */
2397 PPDMIBASE pBase;
2398 rc = PDMR3DeviceAttach(pUVM, "acpi", 0, idCpu, 0, &pBase); RC_CHECK();
2399
2400 Log(("PlugCpu: rc=%Rrc\n", rc));
2401
2402 CFGMR3Dump(pInst);
2403
2404#undef RC_CHECK
2405
2406 return VINF_SUCCESS;
2407}
2408
2409HRESULT Console::doCPUAdd(ULONG aCpu, PUVM pUVM)
2410{
2411 HRESULT rc = S_OK;
2412
2413 LogFlowThisFuncEnter();
2414
2415 AutoCaller autoCaller(this);
2416 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2417
2418 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2419
2420 LogFlowThisFunc(("mMachineState=%d\n", mMachineState));
2421 if ( mMachineState != MachineState_Running
2422 && mMachineState != MachineState_Teleporting
2423 && mMachineState != MachineState_LiveSnapshotting
2424 /** @todo r=bird: This should be allowed on paused VMs as well. Later. */
2425 )
2426 return setInvalidMachineStateError();
2427
2428 AssertReturn(m_pVMMDev, E_FAIL);
2429 PPDMIVMMDEVPORT pDevPort = m_pVMMDev->getVMMDevPort();
2430 AssertReturn(pDevPort, E_FAIL);
2431
2432 /* Check if the CPU is present */
2433 BOOL fCpuAttached;
2434 rc = mMachine->GetCPUStatus(aCpu, &fCpuAttached);
2435 if (FAILED(rc)) return rc;
2436
2437 if (fCpuAttached)
2438 return setError(E_FAIL,
2439 tr("CPU %d is already attached"), aCpu);
2440
2441 /*
2442 * Call worker in EMT, that's faster and safer than doing everything
2443 * using VMR3ReqCall. Note that we separate VMR3ReqCall from VMR3ReqWait
2444 * here to make requests from under the lock in order to serialize them.
2445 */
2446 PVMREQ pReq;
2447 int vrc = VMR3ReqCallU(pUVM, 0, &pReq, 0 /* no wait! */, VMREQFLAGS_VBOX_STATUS,
2448 (PFNRT)Console::plugCpu, 3,
2449 this, pUVM, aCpu);
2450
2451 /* release the lock before a VMR3* call (EMT will call us back)! */
2452 alock.release();
2453
2454 if (vrc == VERR_TIMEOUT || RT_SUCCESS(vrc))
2455 {
2456 vrc = VMR3ReqWait(pReq, RT_INDEFINITE_WAIT);
2457 AssertRC(vrc);
2458 if (RT_SUCCESS(vrc))
2459 vrc = pReq->iStatus;
2460 }
2461 VMR3ReqFree(pReq);
2462
2463 rc = RT_SUCCESS(vrc) ? S_OK :
2464 setError(VBOX_E_VM_ERROR,
2465 tr("Could not add CPU to the machine (%Rrc)"),
2466 vrc);
2467
2468 if (RT_SUCCESS(vrc))
2469 {
2470 /* Notify the guest if possible. */
2471 uint32_t idCpuCore, idCpuPackage;
2472 vrc = VMR3GetCpuCoreAndPackageIdFromCpuId(pUVM, aCpu, &idCpuCore, &idCpuPackage); AssertRC(vrc);
2473 if (RT_SUCCESS(vrc))
2474 vrc = pDevPort->pfnCpuHotPlug(pDevPort, idCpuCore, idCpuPackage);
2475 /** @todo warning if the guest doesn't support it */
2476 }
2477
2478 LogFlowThisFunc(("mMachineState=%d, rc=%Rhrc\n", mMachineState, rc));
2479 LogFlowThisFuncLeave();
2480 return rc;
2481}
2482
2483STDMETHODIMP Console::Pause()
2484{
2485 LogFlowThisFuncEnter();
2486
2487 HRESULT rc = pause(Reason_Unspecified);
2488
2489 LogFlowThisFunc(("rc=%Rhrc\n", rc));
2490 LogFlowThisFuncLeave();
2491 return rc;
2492}
2493
2494STDMETHODIMP Console::Resume()
2495{
2496 LogFlowThisFuncEnter();
2497
2498 HRESULT rc = resume(Reason_Unspecified);
2499
2500 LogFlowThisFunc(("rc=%Rhrc\n", rc));
2501 LogFlowThisFuncLeave();
2502 return rc;
2503}
2504
2505STDMETHODIMP Console::PowerButton()
2506{
2507 LogFlowThisFuncEnter();
2508
2509 AutoCaller autoCaller(this);
2510 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2511
2512 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2513
2514 if ( mMachineState != MachineState_Running
2515 && mMachineState != MachineState_Teleporting
2516 && mMachineState != MachineState_LiveSnapshotting
2517 )
2518 return setInvalidMachineStateError();
2519
2520 /* get the VM handle. */
2521 SafeVMPtr ptrVM(this);
2522 if (!ptrVM.isOk())
2523 return ptrVM.rc();
2524
2525 // no need to release lock, as there are no cross-thread callbacks
2526
2527 /* get the acpi device interface and press the button. */
2528 PPDMIBASE pBase;
2529 int vrc = PDMR3QueryDeviceLun(ptrVM.rawUVM(), "acpi", 0, 0, &pBase);
2530 if (RT_SUCCESS(vrc))
2531 {
2532 Assert(pBase);
2533 PPDMIACPIPORT pPort = PDMIBASE_QUERY_INTERFACE(pBase, PDMIACPIPORT);
2534 if (pPort)
2535 vrc = pPort->pfnPowerButtonPress(pPort);
2536 else
2537 vrc = VERR_PDM_MISSING_INTERFACE;
2538 }
2539
2540 HRESULT rc = RT_SUCCESS(vrc) ? S_OK :
2541 setError(VBOX_E_PDM_ERROR,
2542 tr("Controlled power off failed (%Rrc)"),
2543 vrc);
2544
2545 LogFlowThisFunc(("rc=%Rhrc\n", rc));
2546 LogFlowThisFuncLeave();
2547 return rc;
2548}
2549
2550STDMETHODIMP Console::GetPowerButtonHandled(BOOL *aHandled)
2551{
2552 LogFlowThisFuncEnter();
2553
2554 CheckComArgOutPointerValid(aHandled);
2555
2556 *aHandled = FALSE;
2557
2558 AutoCaller autoCaller(this);
2559
2560 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2561
2562 if ( mMachineState != MachineState_Running
2563 && mMachineState != MachineState_Teleporting
2564 && mMachineState != MachineState_LiveSnapshotting
2565 )
2566 return setInvalidMachineStateError();
2567
2568 /* get the VM handle. */
2569 SafeVMPtr ptrVM(this);
2570 if (!ptrVM.isOk())
2571 return ptrVM.rc();
2572
2573 // no need to release lock, as there are no cross-thread callbacks
2574
2575 /* get the acpi device interface and check if the button press was handled. */
2576 PPDMIBASE pBase;
2577 int vrc = PDMR3QueryDeviceLun(ptrVM.rawUVM(), "acpi", 0, 0, &pBase);
2578 if (RT_SUCCESS(vrc))
2579 {
2580 Assert(pBase);
2581 PPDMIACPIPORT pPort = PDMIBASE_QUERY_INTERFACE(pBase, PDMIACPIPORT);
2582 if (pPort)
2583 {
2584 bool fHandled = false;
2585 vrc = pPort->pfnGetPowerButtonHandled(pPort, &fHandled);
2586 if (RT_SUCCESS(vrc))
2587 *aHandled = fHandled;
2588 }
2589 else
2590 vrc = VERR_PDM_MISSING_INTERFACE;
2591 }
2592
2593 HRESULT rc = RT_SUCCESS(vrc) ? S_OK :
2594 setError(VBOX_E_PDM_ERROR,
2595 tr("Checking if the ACPI Power Button event was handled by the guest OS failed (%Rrc)"),
2596 vrc);
2597
2598 LogFlowThisFunc(("rc=%Rhrc\n", rc));
2599 LogFlowThisFuncLeave();
2600 return rc;
2601}
2602
2603STDMETHODIMP Console::GetGuestEnteredACPIMode(BOOL *aEntered)
2604{
2605 LogFlowThisFuncEnter();
2606
2607 CheckComArgOutPointerValid(aEntered);
2608
2609 *aEntered = FALSE;
2610
2611 AutoCaller autoCaller(this);
2612
2613 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2614
2615 if ( mMachineState != MachineState_Running
2616 && mMachineState != MachineState_Teleporting
2617 && mMachineState != MachineState_LiveSnapshotting
2618 )
2619 return setError(VBOX_E_INVALID_VM_STATE,
2620 tr("Invalid machine state %s when checking if the guest entered the ACPI mode)"),
2621 Global::stringifyMachineState(mMachineState));
2622
2623 /* get the VM handle. */
2624 SafeVMPtr ptrVM(this);
2625 if (!ptrVM.isOk())
2626 return ptrVM.rc();
2627
2628 // no need to release lock, as there are no cross-thread callbacks
2629
2630 /* get the acpi device interface and query the information. */
2631 PPDMIBASE pBase;
2632 int vrc = PDMR3QueryDeviceLun(ptrVM.rawUVM(), "acpi", 0, 0, &pBase);
2633 if (RT_SUCCESS(vrc))
2634 {
2635 Assert(pBase);
2636 PPDMIACPIPORT pPort = PDMIBASE_QUERY_INTERFACE(pBase, PDMIACPIPORT);
2637 if (pPort)
2638 {
2639 bool fEntered = false;
2640 vrc = pPort->pfnGetGuestEnteredACPIMode(pPort, &fEntered);
2641 if (RT_SUCCESS(vrc))
2642 *aEntered = fEntered;
2643 }
2644 else
2645 vrc = VERR_PDM_MISSING_INTERFACE;
2646 }
2647
2648 LogFlowThisFuncLeave();
2649 return S_OK;
2650}
2651
2652STDMETHODIMP Console::SleepButton()
2653{
2654 LogFlowThisFuncEnter();
2655
2656 AutoCaller autoCaller(this);
2657 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2658
2659 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2660
2661 if ( mMachineState != MachineState_Running
2662 && mMachineState != MachineState_Teleporting
2663 && mMachineState != MachineState_LiveSnapshotting)
2664 return setInvalidMachineStateError();
2665
2666 /* get the VM handle. */
2667 SafeVMPtr ptrVM(this);
2668 if (!ptrVM.isOk())
2669 return ptrVM.rc();
2670
2671 // no need to release lock, as there are no cross-thread callbacks
2672
2673 /* get the acpi device interface and press the sleep button. */
2674 PPDMIBASE pBase;
2675 int vrc = PDMR3QueryDeviceLun(ptrVM.rawUVM(), "acpi", 0, 0, &pBase);
2676 if (RT_SUCCESS(vrc))
2677 {
2678 Assert(pBase);
2679 PPDMIACPIPORT pPort = PDMIBASE_QUERY_INTERFACE(pBase, PDMIACPIPORT);
2680 if (pPort)
2681 vrc = pPort->pfnSleepButtonPress(pPort);
2682 else
2683 vrc = VERR_PDM_MISSING_INTERFACE;
2684 }
2685
2686 HRESULT rc = RT_SUCCESS(vrc) ? S_OK :
2687 setError(VBOX_E_PDM_ERROR,
2688 tr("Sending sleep button event failed (%Rrc)"),
2689 vrc);
2690
2691 LogFlowThisFunc(("rc=%Rhrc\n", rc));
2692 LogFlowThisFuncLeave();
2693 return rc;
2694}
2695
2696STDMETHODIMP Console::SaveState(IProgress **aProgress)
2697{
2698 LogFlowThisFuncEnter();
2699
2700 HRESULT rc = saveState(Reason_Unspecified, aProgress);
2701
2702 LogFlowThisFunc(("rc=%Rhrc\n", rc));
2703 LogFlowThisFuncLeave();
2704 return rc;
2705}
2706
2707STDMETHODIMP Console::AdoptSavedState(IN_BSTR aSavedStateFile)
2708{
2709 CheckComArgStrNotEmptyOrNull(aSavedStateFile);
2710
2711 AutoCaller autoCaller(this);
2712 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2713
2714 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2715
2716 if ( mMachineState != MachineState_PoweredOff
2717 && mMachineState != MachineState_Teleported
2718 && mMachineState != MachineState_Aborted
2719 )
2720 return setError(VBOX_E_INVALID_VM_STATE,
2721 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
2722 Global::stringifyMachineState(mMachineState));
2723
2724 return mControl->AdoptSavedState(aSavedStateFile);
2725}
2726
2727STDMETHODIMP Console::DiscardSavedState(BOOL aRemoveFile)
2728{
2729 AutoCaller autoCaller(this);
2730 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2731
2732 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2733
2734 if (mMachineState != MachineState_Saved)
2735 return setError(VBOX_E_INVALID_VM_STATE,
2736 tr("Cannot delete the machine state as the machine is not in the saved state (machine state: %s)"),
2737 Global::stringifyMachineState(mMachineState));
2738
2739 HRESULT rc = mControl->SetRemoveSavedStateFile(aRemoveFile);
2740 if (FAILED(rc)) return rc;
2741
2742 /*
2743 * Saved -> PoweredOff transition will be detected in the SessionMachine
2744 * and properly handled.
2745 */
2746 rc = setMachineState(MachineState_PoweredOff);
2747
2748 return rc;
2749}
2750
2751/** read the value of a LED. */
2752inline uint32_t readAndClearLed(PPDMLED pLed)
2753{
2754 if (!pLed)
2755 return 0;
2756 uint32_t u32 = pLed->Actual.u32 | pLed->Asserted.u32;
2757 pLed->Asserted.u32 = 0;
2758 return u32;
2759}
2760
2761STDMETHODIMP Console::GetDeviceActivity(DeviceType_T aDeviceType,
2762 DeviceActivity_T *aDeviceActivity)
2763{
2764 CheckComArgNotNull(aDeviceActivity);
2765
2766 AutoCaller autoCaller(this);
2767 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2768
2769 /*
2770 * Note: we don't lock the console object here because
2771 * readAndClearLed() should be thread safe.
2772 */
2773
2774 /* Get LED array to read */
2775 PDMLEDCORE SumLed = {0};
2776 switch (aDeviceType)
2777 {
2778 case DeviceType_Floppy:
2779 case DeviceType_DVD:
2780 case DeviceType_HardDisk:
2781 {
2782 for (unsigned i = 0; i < RT_ELEMENTS(mapStorageLeds); ++i)
2783 if (maStorageDevType[i] == aDeviceType)
2784 SumLed.u32 |= readAndClearLed(mapStorageLeds[i]);
2785 break;
2786 }
2787
2788 case DeviceType_Network:
2789 {
2790 for (unsigned i = 0; i < RT_ELEMENTS(mapNetworkLeds); ++i)
2791 SumLed.u32 |= readAndClearLed(mapNetworkLeds[i]);
2792 break;
2793 }
2794
2795 case DeviceType_USB:
2796 {
2797 for (unsigned i = 0; i < RT_ELEMENTS(mapUSBLed); ++i)
2798 SumLed.u32 |= readAndClearLed(mapUSBLed[i]);
2799 break;
2800 }
2801
2802 case DeviceType_SharedFolder:
2803 {
2804 SumLed.u32 |= readAndClearLed(mapSharedFolderLed);
2805 break;
2806 }
2807
2808 default:
2809 return setError(E_INVALIDARG,
2810 tr("Invalid device type: %d"),
2811 aDeviceType);
2812 }
2813
2814 /* Compose the result */
2815 switch (SumLed.u32 & (PDMLED_READING | PDMLED_WRITING))
2816 {
2817 case 0:
2818 *aDeviceActivity = DeviceActivity_Idle;
2819 break;
2820 case PDMLED_READING:
2821 *aDeviceActivity = DeviceActivity_Reading;
2822 break;
2823 case PDMLED_WRITING:
2824 case PDMLED_READING | PDMLED_WRITING:
2825 *aDeviceActivity = DeviceActivity_Writing;
2826 break;
2827 }
2828
2829 return S_OK;
2830}
2831
2832STDMETHODIMP Console::AttachUSBDevice(IN_BSTR aId)
2833{
2834#ifdef VBOX_WITH_USB
2835 AutoCaller autoCaller(this);
2836 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2837
2838 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2839
2840 if ( mMachineState != MachineState_Running
2841 && mMachineState != MachineState_Paused)
2842 return setError(VBOX_E_INVALID_VM_STATE,
2843 tr("Cannot attach a USB device to the machine which is not running or paused (machine state: %s)"),
2844 Global::stringifyMachineState(mMachineState));
2845
2846 /* Get the VM handle. */
2847 SafeVMPtr ptrVM(this);
2848 if (!ptrVM.isOk())
2849 return ptrVM.rc();
2850
2851 /* Don't proceed unless we have a USB controller. */
2852 if (!mfVMHasUsbController)
2853 return setError(VBOX_E_PDM_ERROR,
2854 tr("The virtual machine does not have a USB controller"));
2855
2856 /* release the lock because the USB Proxy service may call us back
2857 * (via onUSBDeviceAttach()) */
2858 alock.release();
2859
2860 /* Request the device capture */
2861 return mControl->CaptureUSBDevice(aId);
2862
2863#else /* !VBOX_WITH_USB */
2864 return setError(VBOX_E_PDM_ERROR,
2865 tr("The virtual machine does not have a USB controller"));
2866#endif /* !VBOX_WITH_USB */
2867}
2868
2869STDMETHODIMP Console::DetachUSBDevice(IN_BSTR aId, IUSBDevice **aDevice)
2870{
2871#ifdef VBOX_WITH_USB
2872 CheckComArgOutPointerValid(aDevice);
2873
2874 AutoCaller autoCaller(this);
2875 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2876
2877 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2878
2879 /* Find it. */
2880 ComObjPtr<OUSBDevice> pUSBDevice;
2881 USBDeviceList::iterator it = mUSBDevices.begin();
2882 Guid uuid(aId);
2883 while (it != mUSBDevices.end())
2884 {
2885 if ((*it)->id() == uuid)
2886 {
2887 pUSBDevice = *it;
2888 break;
2889 }
2890 ++it;
2891 }
2892
2893 if (!pUSBDevice)
2894 return setError(E_INVALIDARG,
2895 tr("USB device with UUID {%RTuuid} is not attached to this machine"),
2896 Guid(aId).raw());
2897
2898 /* Remove the device from the collection, it is re-added below for failures */
2899 mUSBDevices.erase(it);
2900
2901 /*
2902 * Inform the USB device and USB proxy about what's cooking.
2903 */
2904 alock.release();
2905 HRESULT rc = mControl->DetachUSBDevice(aId, false /* aDone */);
2906 if (FAILED(rc))
2907 {
2908 /* Re-add the device to the collection */
2909 alock.acquire();
2910 mUSBDevices.push_back(pUSBDevice);
2911 return rc;
2912 }
2913
2914 /* Request the PDM to detach the USB device. */
2915 rc = detachUSBDevice(pUSBDevice);
2916 if (SUCCEEDED(rc))
2917 {
2918 /* Request the device release. Even if it fails, the device will
2919 * remain as held by proxy, which is OK for us (the VM process). */
2920 rc = mControl->DetachUSBDevice(aId, true /* aDone */);
2921 }
2922 else
2923 {
2924 /* Re-add the device to the collection */
2925 alock.acquire();
2926 mUSBDevices.push_back(pUSBDevice);
2927 }
2928
2929 return rc;
2930
2931
2932#else /* !VBOX_WITH_USB */
2933 return setError(VBOX_E_PDM_ERROR,
2934 tr("The virtual machine does not have a USB controller"));
2935#endif /* !VBOX_WITH_USB */
2936}
2937
2938STDMETHODIMP Console::FindUSBDeviceByAddress(IN_BSTR aAddress, IUSBDevice **aDevice)
2939{
2940#ifdef VBOX_WITH_USB
2941 CheckComArgStrNotEmptyOrNull(aAddress);
2942 CheckComArgOutPointerValid(aDevice);
2943
2944 *aDevice = NULL;
2945
2946 SafeIfaceArray<IUSBDevice> devsvec;
2947 HRESULT rc = COMGETTER(USBDevices)(ComSafeArrayAsOutParam(devsvec));
2948 if (FAILED(rc)) return rc;
2949
2950 for (size_t i = 0; i < devsvec.size(); ++i)
2951 {
2952 Bstr address;
2953 rc = devsvec[i]->COMGETTER(Address)(address.asOutParam());
2954 if (FAILED(rc)) return rc;
2955 if (address == aAddress)
2956 {
2957 ComObjPtr<OUSBDevice> pUSBDevice;
2958 pUSBDevice.createObject();
2959 pUSBDevice->init(devsvec[i]);
2960 return pUSBDevice.queryInterfaceTo(aDevice);
2961 }
2962 }
2963
2964 return setErrorNoLog(VBOX_E_OBJECT_NOT_FOUND,
2965 tr("Could not find a USB device with address '%ls'"),
2966 aAddress);
2967
2968#else /* !VBOX_WITH_USB */
2969 return E_NOTIMPL;
2970#endif /* !VBOX_WITH_USB */
2971}
2972
2973STDMETHODIMP Console::FindUSBDeviceById(IN_BSTR aId, IUSBDevice **aDevice)
2974{
2975#ifdef VBOX_WITH_USB
2976 CheckComArgExpr(aId, Guid(aId).isValid());
2977 CheckComArgOutPointerValid(aDevice);
2978
2979 *aDevice = NULL;
2980
2981 SafeIfaceArray<IUSBDevice> devsvec;
2982 HRESULT rc = COMGETTER(USBDevices)(ComSafeArrayAsOutParam(devsvec));
2983 if (FAILED(rc)) return rc;
2984
2985 for (size_t i = 0; i < devsvec.size(); ++i)
2986 {
2987 Bstr id;
2988 rc = devsvec[i]->COMGETTER(Id)(id.asOutParam());
2989 if (FAILED(rc)) return rc;
2990 if (id == aId)
2991 {
2992 ComObjPtr<OUSBDevice> pUSBDevice;
2993 pUSBDevice.createObject();
2994 pUSBDevice->init(devsvec[i]);
2995 return pUSBDevice.queryInterfaceTo(aDevice);
2996 }
2997 }
2998
2999 return setErrorNoLog(VBOX_E_OBJECT_NOT_FOUND,
3000 tr("Could not find a USB device with uuid {%RTuuid}"),
3001 Guid(aId).raw());
3002
3003#else /* !VBOX_WITH_USB */
3004 return E_NOTIMPL;
3005#endif /* !VBOX_WITH_USB */
3006}
3007
3008STDMETHODIMP
3009Console::CreateSharedFolder(IN_BSTR aName, IN_BSTR aHostPath, BOOL aWritable, BOOL aAutoMount)
3010{
3011 CheckComArgStrNotEmptyOrNull(aName);
3012 CheckComArgStrNotEmptyOrNull(aHostPath);
3013
3014 LogFlowThisFunc(("Entering for '%ls' -> '%ls'\n", aName, aHostPath));
3015
3016 AutoCaller autoCaller(this);
3017 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3018
3019 Utf8Str strName(aName);
3020 Utf8Str strHostPath(aHostPath);
3021
3022 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3023
3024 /// @todo see @todo in AttachUSBDevice() about the Paused state
3025 if (mMachineState == MachineState_Saved)
3026 return setError(VBOX_E_INVALID_VM_STATE,
3027 tr("Cannot create a transient shared folder on the machine in the saved state"));
3028 if ( mMachineState != MachineState_PoweredOff
3029 && mMachineState != MachineState_Teleported
3030 && mMachineState != MachineState_Aborted
3031 && mMachineState != MachineState_Running
3032 && mMachineState != MachineState_Paused
3033 )
3034 return setError(VBOX_E_INVALID_VM_STATE,
3035 tr("Cannot create a transient shared folder on the machine while it is changing the state (machine state: %s)"),
3036 Global::stringifyMachineState(mMachineState));
3037
3038 ComObjPtr<SharedFolder> pSharedFolder;
3039 HRESULT rc = findSharedFolder(strName, pSharedFolder, false /* aSetError */);
3040 if (SUCCEEDED(rc))
3041 return setError(VBOX_E_FILE_ERROR,
3042 tr("Shared folder named '%s' already exists"),
3043 strName.c_str());
3044
3045 pSharedFolder.createObject();
3046 rc = pSharedFolder->init(this,
3047 strName,
3048 strHostPath,
3049 !!aWritable,
3050 !!aAutoMount,
3051 true /* fFailOnError */);
3052 if (FAILED(rc)) return rc;
3053
3054 /* If the VM is online and supports shared folders, share this folder
3055 * under the specified name. (Ignore any failure to obtain the VM handle.) */
3056 SafeVMPtrQuiet ptrVM(this);
3057 if ( ptrVM.isOk()
3058 && m_pVMMDev
3059 && m_pVMMDev->isShFlActive()
3060 )
3061 {
3062 /* first, remove the machine or the global folder if there is any */
3063 SharedFolderDataMap::const_iterator it;
3064 if (findOtherSharedFolder(aName, it))
3065 {
3066 rc = removeSharedFolder(aName);
3067 if (FAILED(rc))
3068 return rc;
3069 }
3070
3071 /* second, create the given folder */
3072 rc = createSharedFolder(aName, SharedFolderData(aHostPath, !!aWritable, !!aAutoMount));
3073 if (FAILED(rc))
3074 return rc;
3075 }
3076
3077 m_mapSharedFolders.insert(std::make_pair(aName, pSharedFolder));
3078
3079 /* Notify console callbacks after the folder is added to the list. */
3080 alock.release();
3081 fireSharedFolderChangedEvent(mEventSource, Scope_Session);
3082
3083 LogFlowThisFunc(("Leaving for '%ls' -> '%ls'\n", aName, aHostPath));
3084
3085 return rc;
3086}
3087
3088STDMETHODIMP Console::RemoveSharedFolder(IN_BSTR aName)
3089{
3090 CheckComArgStrNotEmptyOrNull(aName);
3091
3092 AutoCaller autoCaller(this);
3093 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3094
3095 LogFlowThisFunc(("Entering for '%ls'\n", aName));
3096
3097 Utf8Str strName(aName);
3098
3099 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3100
3101 /// @todo see @todo in AttachUSBDevice() about the Paused state
3102 if (mMachineState == MachineState_Saved)
3103 return setError(VBOX_E_INVALID_VM_STATE,
3104 tr("Cannot remove a transient shared folder from the machine in the saved state"));
3105 if ( mMachineState != MachineState_PoweredOff
3106 && mMachineState != MachineState_Teleported
3107 && mMachineState != MachineState_Aborted
3108 && mMachineState != MachineState_Running
3109 && mMachineState != MachineState_Paused
3110 )
3111 return setError(VBOX_E_INVALID_VM_STATE,
3112 tr("Cannot remove a transient shared folder from the machine while it is changing the state (machine state: %s)"),
3113 Global::stringifyMachineState(mMachineState));
3114
3115 ComObjPtr<SharedFolder> pSharedFolder;
3116 HRESULT rc = findSharedFolder(aName, pSharedFolder, true /* aSetError */);
3117 if (FAILED(rc)) return rc;
3118
3119 /* protect the VM handle (if not NULL) */
3120 SafeVMPtrQuiet ptrVM(this);
3121 if ( ptrVM.isOk()
3122 && m_pVMMDev
3123 && m_pVMMDev->isShFlActive()
3124 )
3125 {
3126 /* if the VM is online and supports shared folders, UNshare this
3127 * folder. */
3128
3129 /* first, remove the given folder */
3130 rc = removeSharedFolder(strName);
3131 if (FAILED(rc)) return rc;
3132
3133 /* first, remove the machine or the global folder if there is any */
3134 SharedFolderDataMap::const_iterator it;
3135 if (findOtherSharedFolder(strName, it))
3136 {
3137 rc = createSharedFolder(strName, it->second);
3138 /* don't check rc here because we need to remove the console
3139 * folder from the collection even on failure */
3140 }
3141 }
3142
3143 m_mapSharedFolders.erase(strName);
3144
3145 /* Notify console callbacks after the folder is removed from the list. */
3146 alock.release();
3147 fireSharedFolderChangedEvent(mEventSource, Scope_Session);
3148
3149 LogFlowThisFunc(("Leaving for '%ls'\n", aName));
3150
3151 return rc;
3152}
3153
3154STDMETHODIMP Console::TakeSnapshot(IN_BSTR aName,
3155 IN_BSTR aDescription,
3156 IProgress **aProgress)
3157{
3158 LogFlowThisFuncEnter();
3159
3160 CheckComArgStrNotEmptyOrNull(aName);
3161 CheckComArgOutPointerValid(aProgress);
3162
3163 AutoCaller autoCaller(this);
3164 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3165
3166 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3167 LogFlowThisFunc(("aName='%ls' mMachineState=%d\n", aName, mMachineState));
3168
3169 if (Global::IsTransient(mMachineState))
3170 return setError(VBOX_E_INVALID_VM_STATE,
3171 tr("Cannot take a snapshot of the machine while it is changing the state (machine state: %s)"),
3172 Global::stringifyMachineState(mMachineState));
3173
3174 HRESULT rc = S_OK;
3175
3176 /* prepare the progress object:
3177 a) count the no. of hard disk attachments to get a matching no. of progress sub-operations */
3178 ULONG cOperations = 2; // always at least setting up + finishing up
3179 ULONG ulTotalOperationsWeight = 2; // one each for setting up + finishing up
3180 SafeIfaceArray<IMediumAttachment> aMediumAttachments;
3181 rc = mMachine->COMGETTER(MediumAttachments)(ComSafeArrayAsOutParam(aMediumAttachments));
3182 if (FAILED(rc))
3183 return setError(rc, tr("Cannot get medium attachments of the machine"));
3184
3185 ULONG ulMemSize;
3186 rc = mMachine->COMGETTER(MemorySize)(&ulMemSize);
3187 if (FAILED(rc))
3188 return rc;
3189
3190 for (size_t i = 0;
3191 i < aMediumAttachments.size();
3192 ++i)
3193 {
3194 DeviceType_T type;
3195 rc = aMediumAttachments[i]->COMGETTER(Type)(&type);
3196 if (FAILED(rc))
3197 return rc;
3198
3199 if (type == DeviceType_HardDisk)
3200 {
3201 ++cOperations;
3202
3203 // assume that creating a diff image takes as long as saving a 1MB state
3204 // (note, the same value must be used in SessionMachine::BeginTakingSnapshot() on the server!)
3205 ulTotalOperationsWeight += 1;
3206 }
3207 }
3208
3209 // b) one extra sub-operations for online snapshots OR offline snapshots that have a saved state (needs to be copied)
3210 bool const fTakingSnapshotOnline = Global::IsOnline(mMachineState);
3211
3212 LogFlowFunc(("fTakingSnapshotOnline = %d, mMachineState = %d\n", fTakingSnapshotOnline, mMachineState));
3213
3214 if (fTakingSnapshotOnline)
3215 {
3216 ++cOperations;
3217 ulTotalOperationsWeight += ulMemSize;
3218 }
3219
3220 // finally, create the progress object
3221 ComObjPtr<Progress> pProgress;
3222 pProgress.createObject();
3223 rc = pProgress->init(static_cast<IConsole *>(this),
3224 Bstr(tr("Taking a snapshot of the virtual machine")).raw(),
3225 (mMachineState >= MachineState_FirstOnline)
3226 && (mMachineState <= MachineState_LastOnline) /* aCancelable */,
3227 cOperations,
3228 ulTotalOperationsWeight,
3229 Bstr(tr("Setting up snapshot operation")).raw(), // first sub-op description
3230 1); // ulFirstOperationWeight
3231
3232 if (FAILED(rc))
3233 return rc;
3234
3235 VMTakeSnapshotTask *pTask;
3236 if (!(pTask = new VMTakeSnapshotTask(this, pProgress, aName, aDescription)))
3237 return E_OUTOFMEMORY;
3238
3239 Assert(pTask->mProgress);
3240
3241 try
3242 {
3243 mptrCancelableProgress = pProgress;
3244
3245 /*
3246 * If we fail here it means a PowerDown() call happened on another
3247 * thread while we were doing Pause() (which releases the Console lock).
3248 * We assign PowerDown() a higher precedence than TakeSnapshot(),
3249 * therefore just return the error to the caller.
3250 */
3251 rc = pTask->rc();
3252 if (FAILED(rc)) throw rc;
3253
3254 pTask->ulMemSize = ulMemSize;
3255
3256 /* memorize the current machine state */
3257 pTask->lastMachineState = mMachineState;
3258 pTask->fTakingSnapshotOnline = fTakingSnapshotOnline;
3259
3260 int vrc = RTThreadCreate(NULL,
3261 Console::fntTakeSnapshotWorker,
3262 (void *)pTask,
3263 0,
3264 RTTHREADTYPE_MAIN_WORKER,
3265 0,
3266 "TakeSnap");
3267 if (FAILED(vrc))
3268 throw setError(E_FAIL,
3269 tr("Could not create VMTakeSnap thread (%Rrc)"),
3270 vrc);
3271
3272 pTask->mProgress.queryInterfaceTo(aProgress);
3273 }
3274 catch (HRESULT erc)
3275 {
3276 delete pTask;
3277 rc = erc;
3278 mptrCancelableProgress.setNull();
3279 }
3280
3281 LogFlowThisFunc(("rc=%Rhrc\n", rc));
3282 LogFlowThisFuncLeave();
3283 return rc;
3284}
3285
3286STDMETHODIMP Console::DeleteSnapshot(IN_BSTR aId, IProgress **aProgress)
3287{
3288 CheckComArgExpr(aId, Guid(aId).isValid());
3289 CheckComArgOutPointerValid(aProgress);
3290
3291 AutoCaller autoCaller(this);
3292 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3293
3294 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3295
3296 if (Global::IsTransient(mMachineState))
3297 return setError(VBOX_E_INVALID_VM_STATE,
3298 tr("Cannot delete a snapshot of the machine while it is changing the state (machine state: %s)"),
3299 Global::stringifyMachineState(mMachineState));
3300
3301 MachineState_T machineState = MachineState_Null;
3302 HRESULT rc = mControl->DeleteSnapshot(this, aId, aId, FALSE /* fDeleteAllChildren */, &machineState, aProgress);
3303 if (FAILED(rc)) return rc;
3304
3305 setMachineStateLocally(machineState);
3306 return S_OK;
3307}
3308
3309STDMETHODIMP Console::DeleteSnapshotAndAllChildren(IN_BSTR aId, IProgress **aProgress)
3310{
3311 CheckComArgExpr(aId, Guid(aId).isValid());
3312 CheckComArgOutPointerValid(aProgress);
3313
3314 AutoCaller autoCaller(this);
3315 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3316
3317 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3318
3319 if (Global::IsTransient(mMachineState))
3320 return setError(VBOX_E_INVALID_VM_STATE,
3321 tr("Cannot delete a snapshot of the machine while it is changing the state (machine state: %s)"),
3322 Global::stringifyMachineState(mMachineState));
3323
3324 MachineState_T machineState = MachineState_Null;
3325 HRESULT rc = mControl->DeleteSnapshot(this, aId, aId, TRUE /* fDeleteAllChildren */, &machineState, aProgress);
3326 if (FAILED(rc)) return rc;
3327
3328 setMachineStateLocally(machineState);
3329 return S_OK;
3330}
3331
3332STDMETHODIMP Console::DeleteSnapshotRange(IN_BSTR aStartId, IN_BSTR aEndId, IProgress **aProgress)
3333{
3334 CheckComArgExpr(aStartId, Guid(aStartId).isValid());
3335 CheckComArgExpr(aEndId, Guid(aEndId).isValid());
3336 CheckComArgOutPointerValid(aProgress);
3337
3338 AutoCaller autoCaller(this);
3339 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3340
3341 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3342
3343 if (Global::IsTransient(mMachineState))
3344 return setError(VBOX_E_INVALID_VM_STATE,
3345 tr("Cannot delete a snapshot of the machine while it is changing the state (machine state: %s)"),
3346 Global::stringifyMachineState(mMachineState));
3347
3348 MachineState_T machineState = MachineState_Null;
3349 HRESULT rc = mControl->DeleteSnapshot(this, aStartId, aEndId, FALSE /* fDeleteAllChildren */, &machineState, aProgress);
3350 if (FAILED(rc)) return rc;
3351
3352 setMachineStateLocally(machineState);
3353 return S_OK;
3354}
3355
3356STDMETHODIMP Console::RestoreSnapshot(ISnapshot *aSnapshot, IProgress **aProgress)
3357{
3358 AutoCaller autoCaller(this);
3359 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3360
3361 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3362
3363 if (Global::IsOnlineOrTransient(mMachineState))
3364 return setError(VBOX_E_INVALID_VM_STATE,
3365 tr("Cannot delete the current state of the running machine (machine state: %s)"),
3366 Global::stringifyMachineState(mMachineState));
3367
3368 MachineState_T machineState = MachineState_Null;
3369 HRESULT rc = mControl->RestoreSnapshot(this, aSnapshot, &machineState, aProgress);
3370 if (FAILED(rc)) return rc;
3371
3372 setMachineStateLocally(machineState);
3373 return S_OK;
3374}
3375
3376// Non-interface public methods
3377/////////////////////////////////////////////////////////////////////////////
3378
3379/*static*/
3380HRESULT Console::setErrorStatic(HRESULT aResultCode, const char *pcsz, ...)
3381{
3382 va_list args;
3383 va_start(args, pcsz);
3384 HRESULT rc = setErrorInternal(aResultCode,
3385 getStaticClassIID(),
3386 getStaticComponentName(),
3387 Utf8Str(pcsz, args),
3388 false /* aWarning */,
3389 true /* aLogIt */);
3390 va_end(args);
3391 return rc;
3392}
3393
3394HRESULT Console::setInvalidMachineStateError()
3395{
3396 return setError(VBOX_E_INVALID_VM_STATE,
3397 tr("Invalid machine state: %s"),
3398 Global::stringifyMachineState(mMachineState));
3399}
3400
3401
3402/* static */
3403const char *Console::convertControllerTypeToDev(StorageControllerType_T enmCtrlType)
3404{
3405 switch (enmCtrlType)
3406 {
3407 case StorageControllerType_LsiLogic:
3408 return "lsilogicscsi";
3409 case StorageControllerType_BusLogic:
3410 return "buslogic";
3411 case StorageControllerType_LsiLogicSas:
3412 return "lsilogicsas";
3413 case StorageControllerType_IntelAhci:
3414 return "ahci";
3415 case StorageControllerType_PIIX3:
3416 case StorageControllerType_PIIX4:
3417 case StorageControllerType_ICH6:
3418 return "piix3ide";
3419 case StorageControllerType_I82078:
3420 return "i82078";
3421 default:
3422 return NULL;
3423 }
3424}
3425
3426HRESULT Console::convertBusPortDeviceToLun(StorageBus_T enmBus, LONG port, LONG device, unsigned &uLun)
3427{
3428 switch (enmBus)
3429 {
3430 case StorageBus_IDE:
3431 case StorageBus_Floppy:
3432 {
3433 AssertMsgReturn(port < 2 && port >= 0, ("%d\n", port), E_INVALIDARG);
3434 AssertMsgReturn(device < 2 && device >= 0, ("%d\n", device), E_INVALIDARG);
3435 uLun = 2 * port + device;
3436 return S_OK;
3437 }
3438 case StorageBus_SATA:
3439 case StorageBus_SCSI:
3440 case StorageBus_SAS:
3441 {
3442 uLun = port;
3443 return S_OK;
3444 }
3445 default:
3446 uLun = 0;
3447 AssertMsgFailedReturn(("%d\n", enmBus), E_INVALIDARG);
3448 }
3449}
3450
3451// private methods
3452/////////////////////////////////////////////////////////////////////////////
3453
3454/**
3455 * Process a medium change.
3456 *
3457 * @param aMediumAttachment The medium attachment with the new medium state.
3458 * @param fForce Force medium chance, if it is locked or not.
3459 * @param pUVM Safe VM handle.
3460 *
3461 * @note Locks this object for writing.
3462 */
3463HRESULT Console::doMediumChange(IMediumAttachment *aMediumAttachment, bool fForce, PUVM pUVM)
3464{
3465 AutoCaller autoCaller(this);
3466 AssertComRCReturnRC(autoCaller.rc());
3467
3468 /* We will need to release the write lock before calling EMT */
3469 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3470
3471 HRESULT rc = S_OK;
3472 const char *pszDevice = NULL;
3473
3474 SafeIfaceArray<IStorageController> ctrls;
3475 rc = mMachine->COMGETTER(StorageControllers)(ComSafeArrayAsOutParam(ctrls));
3476 AssertComRC(rc);
3477 IMedium *pMedium;
3478 rc = aMediumAttachment->COMGETTER(Medium)(&pMedium);
3479 AssertComRC(rc);
3480 Bstr mediumLocation;
3481 if (pMedium)
3482 {
3483 rc = pMedium->COMGETTER(Location)(mediumLocation.asOutParam());
3484 AssertComRC(rc);
3485 }
3486
3487 Bstr attCtrlName;
3488 rc = aMediumAttachment->COMGETTER(Controller)(attCtrlName.asOutParam());
3489 AssertComRC(rc);
3490 ComPtr<IStorageController> pStorageController;
3491 for (size_t i = 0; i < ctrls.size(); ++i)
3492 {
3493 Bstr ctrlName;
3494 rc = ctrls[i]->COMGETTER(Name)(ctrlName.asOutParam());
3495 AssertComRC(rc);
3496 if (attCtrlName == ctrlName)
3497 {
3498 pStorageController = ctrls[i];
3499 break;
3500 }
3501 }
3502 if (pStorageController.isNull())
3503 return setError(E_FAIL,
3504 tr("Could not find storage controller '%ls'"), attCtrlName.raw());
3505
3506 StorageControllerType_T enmCtrlType;
3507 rc = pStorageController->COMGETTER(ControllerType)(&enmCtrlType);
3508 AssertComRC(rc);
3509 pszDevice = convertControllerTypeToDev(enmCtrlType);
3510
3511 StorageBus_T enmBus;
3512 rc = pStorageController->COMGETTER(Bus)(&enmBus);
3513 AssertComRC(rc);
3514 ULONG uInstance;
3515 rc = pStorageController->COMGETTER(Instance)(&uInstance);
3516 AssertComRC(rc);
3517 BOOL fUseHostIOCache;
3518 rc = pStorageController->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
3519 AssertComRC(rc);
3520
3521 /*
3522 * Call worker in EMT, that's faster and safer than doing everything
3523 * using VMR3ReqCall. Note that we separate VMR3ReqCall from VMR3ReqWait
3524 * here to make requests from under the lock in order to serialize them.
3525 */
3526 PVMREQ pReq;
3527 int vrc = VMR3ReqCallU(pUVM,
3528 VMCPUID_ANY,
3529 &pReq,
3530 0 /* no wait! */,
3531 VMREQFLAGS_VBOX_STATUS,
3532 (PFNRT)Console::changeRemovableMedium,
3533 8,
3534 this,
3535 pUVM,
3536 pszDevice,
3537 uInstance,
3538 enmBus,
3539 fUseHostIOCache,
3540 aMediumAttachment,
3541 fForce);
3542
3543 /* release the lock before waiting for a result (EMT will call us back!) */
3544 alock.release();
3545
3546 if (vrc == VERR_TIMEOUT || RT_SUCCESS(vrc))
3547 {
3548 vrc = VMR3ReqWait(pReq, RT_INDEFINITE_WAIT);
3549 AssertRC(vrc);
3550 if (RT_SUCCESS(vrc))
3551 vrc = pReq->iStatus;
3552 }
3553 VMR3ReqFree(pReq);
3554
3555 if (RT_SUCCESS(vrc))
3556 {
3557 LogFlowThisFunc(("Returns S_OK\n"));
3558 return S_OK;
3559 }
3560
3561 if (pMedium)
3562 return setError(E_FAIL,
3563 tr("Could not mount the media/drive '%ls' (%Rrc)"),
3564 mediumLocation.raw(), vrc);
3565
3566 return setError(E_FAIL,
3567 tr("Could not unmount the currently mounted media/drive (%Rrc)"),
3568 vrc);
3569}
3570
3571/**
3572 * Performs the medium change in EMT.
3573 *
3574 * @returns VBox status code.
3575 *
3576 * @param pThis Pointer to the Console object.
3577 * @param pUVM The VM handle.
3578 * @param pcszDevice The PDM device name.
3579 * @param uInstance The PDM device instance.
3580 * @param uLun The PDM LUN number of the drive.
3581 * @param fHostDrive True if this is a host drive attachment.
3582 * @param pszPath The path to the media / drive which is now being mounted / captured.
3583 * If NULL no media or drive is attached and the LUN will be configured with
3584 * the default block driver with no media. This will also be the state if
3585 * mounting / capturing the specified media / drive fails.
3586 * @param pszFormat Medium format string, usually "RAW".
3587 * @param fPassthrough Enables using passthrough mode of the host DVD drive if applicable.
3588 *
3589 * @thread EMT
3590 */
3591DECLCALLBACK(int) Console::changeRemovableMedium(Console *pConsole,
3592 PUVM pUVM,
3593 const char *pcszDevice,
3594 unsigned uInstance,
3595 StorageBus_T enmBus,
3596 bool fUseHostIOCache,
3597 IMediumAttachment *aMediumAtt,
3598 bool fForce)
3599{
3600 LogFlowFunc(("pConsole=%p uInstance=%u pszDevice=%p:{%s} enmBus=%u, aMediumAtt=%p, fForce=%d\n",
3601 pConsole, uInstance, pcszDevice, pcszDevice, enmBus, aMediumAtt, fForce));
3602
3603 AssertReturn(pConsole, VERR_INVALID_PARAMETER);
3604
3605 AutoCaller autoCaller(pConsole);
3606 AssertComRCReturn(autoCaller.rc(), VERR_ACCESS_DENIED);
3607
3608 /*
3609 * Suspend the VM first.
3610 *
3611 * The VM must not be running since it might have pending I/O to
3612 * the drive which is being changed.
3613 */
3614 bool fResume;
3615 VMSTATE enmVMState = VMR3GetStateU(pUVM);
3616 switch (enmVMState)
3617 {
3618 case VMSTATE_RESETTING:
3619 case VMSTATE_RUNNING:
3620 {
3621 LogFlowFunc(("Suspending the VM...\n"));
3622 /* disable the callback to prevent Console-level state change */
3623 pConsole->mVMStateChangeCallbackDisabled = true;
3624 int rc = VMR3Suspend(pUVM, VMSUSPENDREASON_RECONFIG);
3625 pConsole->mVMStateChangeCallbackDisabled = false;
3626 AssertRCReturn(rc, rc);
3627 fResume = true;
3628 break;
3629 }
3630
3631 case VMSTATE_SUSPENDED:
3632 case VMSTATE_CREATED:
3633 case VMSTATE_OFF:
3634 fResume = false;
3635 break;
3636
3637 case VMSTATE_RUNNING_LS:
3638 case VMSTATE_RUNNING_FT:
3639 return setErrorInternal(VBOX_E_INVALID_VM_STATE,
3640 COM_IIDOF(IConsole),
3641 getStaticComponentName(),
3642 (enmVMState == VMSTATE_RUNNING_LS) ? Utf8Str(tr("Cannot change drive during live migration")) : Utf8Str(tr("Cannot change drive during fault tolerant syncing")),
3643 false /*aWarning*/,
3644 true /*aLogIt*/);
3645
3646 default:
3647 AssertMsgFailedReturn(("enmVMState=%d\n", enmVMState), VERR_ACCESS_DENIED);
3648 }
3649
3650 /* Determine the base path for the device instance. */
3651 PCFGMNODE pCtlInst;
3652 pCtlInst = CFGMR3GetChildF(CFGMR3GetRootU(pUVM), "Devices/%s/%u/", pcszDevice, uInstance);
3653 AssertReturn(pCtlInst, VERR_INTERNAL_ERROR);
3654
3655 int rc = VINF_SUCCESS;
3656 int rcRet = VINF_SUCCESS;
3657
3658 rcRet = pConsole->configMediumAttachment(pCtlInst,
3659 pcszDevice,
3660 uInstance,
3661 enmBus,
3662 fUseHostIOCache,
3663 false /* fSetupMerge */,
3664 false /* fBuiltinIOCache */,
3665 0 /* uMergeSource */,
3666 0 /* uMergeTarget */,
3667 aMediumAtt,
3668 pConsole->mMachineState,
3669 NULL /* phrc */,
3670 true /* fAttachDetach */,
3671 fForce /* fForceUnmount */,
3672 false /* fHotplug */,
3673 pUVM,
3674 NULL /* paLedDevType */);
3675 /** @todo this dumps everything attached to this device instance, which
3676 * is more than necessary. Dumping the changed LUN would be enough. */
3677 CFGMR3Dump(pCtlInst);
3678
3679 /*
3680 * Resume the VM if necessary.
3681 */
3682 if (fResume)
3683 {
3684 LogFlowFunc(("Resuming the VM...\n"));
3685 /* disable the callback to prevent Console-level state change */
3686 pConsole->mVMStateChangeCallbackDisabled = true;
3687 rc = VMR3Resume(pUVM, VMRESUMEREASON_RECONFIG);
3688 pConsole->mVMStateChangeCallbackDisabled = false;
3689 AssertRC(rc);
3690 if (RT_FAILURE(rc))
3691 {
3692 /* too bad, we failed. try to sync the console state with the VMM state */
3693 vmstateChangeCallback(pUVM, VMSTATE_SUSPENDED, enmVMState, pConsole);
3694 }
3695 /// @todo (r=dmik) if we failed with drive mount, then the VMR3Resume
3696 // error (if any) will be hidden from the caller. For proper reporting
3697 // of such multiple errors to the caller we need to enhance the
3698 // IVirtualBoxError interface. For now, give the first error the higher
3699 // priority.
3700 if (RT_SUCCESS(rcRet))
3701 rcRet = rc;
3702 }
3703
3704 LogFlowFunc(("Returning %Rrc\n", rcRet));
3705 return rcRet;
3706}
3707
3708
3709/**
3710 * Attach a new storage device to the VM.
3711 *
3712 * @param aMediumAttachment The medium attachment which is added.
3713 * @param pUVM Safe VM handle.
3714 * @param fSilent Flag whether to notify the guest about the attached device.
3715 *
3716 * @note Locks this object for writing.
3717 */
3718HRESULT Console::doStorageDeviceAttach(IMediumAttachment *aMediumAttachment, PUVM pUVM, bool fSilent)
3719{
3720 AutoCaller autoCaller(this);
3721 AssertComRCReturnRC(autoCaller.rc());
3722
3723 /* We will need to release the write lock before calling EMT */
3724 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3725
3726 HRESULT rc = S_OK;
3727 const char *pszDevice = NULL;
3728
3729 SafeIfaceArray<IStorageController> ctrls;
3730 rc = mMachine->COMGETTER(StorageControllers)(ComSafeArrayAsOutParam(ctrls));
3731 AssertComRC(rc);
3732 IMedium *pMedium;
3733 rc = aMediumAttachment->COMGETTER(Medium)(&pMedium);
3734 AssertComRC(rc);
3735 Bstr mediumLocation;
3736 if (pMedium)
3737 {
3738 rc = pMedium->COMGETTER(Location)(mediumLocation.asOutParam());
3739 AssertComRC(rc);
3740 }
3741
3742 Bstr attCtrlName;
3743 rc = aMediumAttachment->COMGETTER(Controller)(attCtrlName.asOutParam());
3744 AssertComRC(rc);
3745 ComPtr<IStorageController> pStorageController;
3746 for (size_t i = 0; i < ctrls.size(); ++i)
3747 {
3748 Bstr ctrlName;
3749 rc = ctrls[i]->COMGETTER(Name)(ctrlName.asOutParam());
3750 AssertComRC(rc);
3751 if (attCtrlName == ctrlName)
3752 {
3753 pStorageController = ctrls[i];
3754 break;
3755 }
3756 }
3757 if (pStorageController.isNull())
3758 return setError(E_FAIL,
3759 tr("Could not find storage controller '%ls'"), attCtrlName.raw());
3760
3761 StorageControllerType_T enmCtrlType;
3762 rc = pStorageController->COMGETTER(ControllerType)(&enmCtrlType);
3763 AssertComRC(rc);
3764 pszDevice = convertControllerTypeToDev(enmCtrlType);
3765
3766 StorageBus_T enmBus;
3767 rc = pStorageController->COMGETTER(Bus)(&enmBus);
3768 AssertComRC(rc);
3769 ULONG uInstance;
3770 rc = pStorageController->COMGETTER(Instance)(&uInstance);
3771 AssertComRC(rc);
3772 BOOL fUseHostIOCache;
3773 rc = pStorageController->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
3774 AssertComRC(rc);
3775
3776 /*
3777 * Call worker in EMT, that's faster and safer than doing everything
3778 * using VMR3ReqCall. Note that we separate VMR3ReqCall from VMR3ReqWait
3779 * here to make requests from under the lock in order to serialize them.
3780 */
3781 PVMREQ pReq;
3782 int vrc = VMR3ReqCallU(pUVM,
3783 VMCPUID_ANY,
3784 &pReq,
3785 0 /* no wait! */,
3786 VMREQFLAGS_VBOX_STATUS,
3787 (PFNRT)Console::attachStorageDevice,
3788 8,
3789 this,
3790 pUVM,
3791 pszDevice,
3792 uInstance,
3793 enmBus,
3794 fUseHostIOCache,
3795 aMediumAttachment,
3796 fSilent);
3797
3798 /* release the lock before waiting for a result (EMT will call us back!) */
3799 alock.release();
3800
3801 if (vrc == VERR_TIMEOUT || RT_SUCCESS(vrc))
3802 {
3803 vrc = VMR3ReqWait(pReq, RT_INDEFINITE_WAIT);
3804 AssertRC(vrc);
3805 if (RT_SUCCESS(vrc))
3806 vrc = pReq->iStatus;
3807 }
3808 VMR3ReqFree(pReq);
3809
3810 if (RT_SUCCESS(vrc))
3811 {
3812 LogFlowThisFunc(("Returns S_OK\n"));
3813 return S_OK;
3814 }
3815
3816 if (!pMedium)
3817 return setError(E_FAIL,
3818 tr("Could not mount the media/drive '%ls' (%Rrc)"),
3819 mediumLocation.raw(), vrc);
3820
3821 return setError(E_FAIL,
3822 tr("Could not unmount the currently mounted media/drive (%Rrc)"),
3823 vrc);
3824}
3825
3826
3827/**
3828 * Performs the storage attach operation in EMT.
3829 *
3830 * @returns VBox status code.
3831 *
3832 * @param pThis Pointer to the Console object.
3833 * @param pUVM The VM handle.
3834 * @param pcszDevice The PDM device name.
3835 * @param uInstance The PDM device instance.
3836 * @param fSilent Flag whether to inform the guest about the attached device.
3837 *
3838 * @thread EMT
3839 */
3840DECLCALLBACK(int) Console::attachStorageDevice(Console *pConsole,
3841 PUVM pUVM,
3842 const char *pcszDevice,
3843 unsigned uInstance,
3844 StorageBus_T enmBus,
3845 bool fUseHostIOCache,
3846 IMediumAttachment *aMediumAtt,
3847 bool fSilent)
3848{
3849 LogFlowFunc(("pConsole=%p uInstance=%u pszDevice=%p:{%s} enmBus=%u, aMediumAtt=%p\n",
3850 pConsole, uInstance, pcszDevice, pcszDevice, enmBus, aMediumAtt));
3851
3852 AssertReturn(pConsole, VERR_INVALID_PARAMETER);
3853
3854 AutoCaller autoCaller(pConsole);
3855 AssertComRCReturn(autoCaller.rc(), VERR_ACCESS_DENIED);
3856
3857 /*
3858 * Suspend the VM first.
3859 *
3860 * The VM must not be running since it might have pending I/O to
3861 * the drive which is being changed.
3862 */
3863 bool fResume;
3864 VMSTATE enmVMState = VMR3GetStateU(pUVM);
3865 switch (enmVMState)
3866 {
3867 case VMSTATE_RESETTING:
3868 case VMSTATE_RUNNING:
3869 {
3870 LogFlowFunc(("Suspending the VM...\n"));
3871 /* disable the callback to prevent Console-level state change */
3872 pConsole->mVMStateChangeCallbackDisabled = true;
3873 int rc = VMR3Suspend(pUVM, VMSUSPENDREASON_RECONFIG);
3874 pConsole->mVMStateChangeCallbackDisabled = false;
3875 AssertRCReturn(rc, rc);
3876 fResume = true;
3877 break;
3878 }
3879
3880 case VMSTATE_SUSPENDED:
3881 case VMSTATE_CREATED:
3882 case VMSTATE_OFF:
3883 fResume = false;
3884 break;
3885
3886 case VMSTATE_RUNNING_LS:
3887 case VMSTATE_RUNNING_FT:
3888 return setErrorInternal(VBOX_E_INVALID_VM_STATE,
3889 COM_IIDOF(IConsole),
3890 getStaticComponentName(),
3891 (enmVMState == VMSTATE_RUNNING_LS) ? Utf8Str(tr("Cannot change drive during live migration")) : Utf8Str(tr("Cannot change drive during fault tolerant syncing")),
3892 false /*aWarning*/,
3893 true /*aLogIt*/);
3894
3895 default:
3896 AssertMsgFailedReturn(("enmVMState=%d\n", enmVMState), VERR_ACCESS_DENIED);
3897 }
3898
3899 /* Determine the base path for the device instance. */
3900 PCFGMNODE pCtlInst;
3901 pCtlInst = CFGMR3GetChildF(CFGMR3GetRootU(pUVM), "Devices/%s/%u/", pcszDevice, uInstance);
3902 AssertReturn(pCtlInst, VERR_INTERNAL_ERROR);
3903
3904 int rc = VINF_SUCCESS;
3905 int rcRet = VINF_SUCCESS;
3906
3907 rcRet = pConsole->configMediumAttachment(pCtlInst,
3908 pcszDevice,
3909 uInstance,
3910 enmBus,
3911 fUseHostIOCache,
3912 false /* fSetupMerge */,
3913 false /* fBuiltinIOCache */,
3914 0 /* uMergeSource */,
3915 0 /* uMergeTarget */,
3916 aMediumAtt,
3917 pConsole->mMachineState,
3918 NULL /* phrc */,
3919 true /* fAttachDetach */,
3920 false /* fForceUnmount */,
3921 !fSilent /* fHotplug */,
3922 pUVM,
3923 NULL /* paLedDevType */);
3924 /** @todo this dumps everything attached to this device instance, which
3925 * is more than necessary. Dumping the changed LUN would be enough. */
3926 CFGMR3Dump(pCtlInst);
3927
3928 /*
3929 * Resume the VM if necessary.
3930 */
3931 if (fResume)
3932 {
3933 LogFlowFunc(("Resuming the VM...\n"));
3934 /* disable the callback to prevent Console-level state change */
3935 pConsole->mVMStateChangeCallbackDisabled = true;
3936 rc = VMR3Resume(pUVM, VMRESUMEREASON_RECONFIG);
3937 pConsole->mVMStateChangeCallbackDisabled = false;
3938 AssertRC(rc);
3939 if (RT_FAILURE(rc))
3940 {
3941 /* too bad, we failed. try to sync the console state with the VMM state */
3942 vmstateChangeCallback(pUVM, VMSTATE_SUSPENDED, enmVMState, pConsole);
3943 }
3944 /** @todo if we failed with drive mount, then the VMR3Resume
3945 * error (if any) will be hidden from the caller. For proper reporting
3946 * of such multiple errors to the caller we need to enhance the
3947 * IVirtualBoxError interface. For now, give the first error the higher
3948 * priority.
3949 */
3950 if (RT_SUCCESS(rcRet))
3951 rcRet = rc;
3952 }
3953
3954 LogFlowFunc(("Returning %Rrc\n", rcRet));
3955 return rcRet;
3956}
3957
3958/**
3959 * Attach a new storage device to the VM.
3960 *
3961 * @param aMediumAttachment The medium attachment which is added.
3962 * @param pUVM Safe VM handle.
3963 * @param fSilent Flag whether to notify the guest about the detached device.
3964 *
3965 * @note Locks this object for writing.
3966 */
3967HRESULT Console::doStorageDeviceDetach(IMediumAttachment *aMediumAttachment, PUVM pUVM, bool fSilent)
3968{
3969 AutoCaller autoCaller(this);
3970 AssertComRCReturnRC(autoCaller.rc());
3971
3972 /* We will need to release the write lock before calling EMT */
3973 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3974
3975 HRESULT rc = S_OK;
3976 const char *pszDevice = NULL;
3977
3978 SafeIfaceArray<IStorageController> ctrls;
3979 rc = mMachine->COMGETTER(StorageControllers)(ComSafeArrayAsOutParam(ctrls));
3980 AssertComRC(rc);
3981 IMedium *pMedium;
3982 rc = aMediumAttachment->COMGETTER(Medium)(&pMedium);
3983 AssertComRC(rc);
3984 Bstr mediumLocation;
3985 if (pMedium)
3986 {
3987 rc = pMedium->COMGETTER(Location)(mediumLocation.asOutParam());
3988 AssertComRC(rc);
3989 }
3990
3991 Bstr attCtrlName;
3992 rc = aMediumAttachment->COMGETTER(Controller)(attCtrlName.asOutParam());
3993 AssertComRC(rc);
3994 ComPtr<IStorageController> pStorageController;
3995 for (size_t i = 0; i < ctrls.size(); ++i)
3996 {
3997 Bstr ctrlName;
3998 rc = ctrls[i]->COMGETTER(Name)(ctrlName.asOutParam());
3999 AssertComRC(rc);
4000 if (attCtrlName == ctrlName)
4001 {
4002 pStorageController = ctrls[i];
4003 break;
4004 }
4005 }
4006 if (pStorageController.isNull())
4007 return setError(E_FAIL,
4008 tr("Could not find storage controller '%ls'"), attCtrlName.raw());
4009
4010 StorageControllerType_T enmCtrlType;
4011 rc = pStorageController->COMGETTER(ControllerType)(&enmCtrlType);
4012 AssertComRC(rc);
4013 pszDevice = convertControllerTypeToDev(enmCtrlType);
4014
4015 StorageBus_T enmBus;
4016 rc = pStorageController->COMGETTER(Bus)(&enmBus);
4017 AssertComRC(rc);
4018 ULONG uInstance;
4019 rc = pStorageController->COMGETTER(Instance)(&uInstance);
4020 AssertComRC(rc);
4021
4022 /*
4023 * Call worker in EMT, that's faster and safer than doing everything
4024 * using VMR3ReqCall. Note that we separate VMR3ReqCall from VMR3ReqWait
4025 * here to make requests from under the lock in order to serialize them.
4026 */
4027 PVMREQ pReq;
4028 int vrc = VMR3ReqCallU(pUVM,
4029 VMCPUID_ANY,
4030 &pReq,
4031 0 /* no wait! */,
4032 VMREQFLAGS_VBOX_STATUS,
4033 (PFNRT)Console::detachStorageDevice,
4034 7,
4035 this,
4036 pUVM,
4037 pszDevice,
4038 uInstance,
4039 enmBus,
4040 aMediumAttachment,
4041 fSilent);
4042
4043 /* release the lock before waiting for a result (EMT will call us back!) */
4044 alock.release();
4045
4046 if (vrc == VERR_TIMEOUT || RT_SUCCESS(vrc))
4047 {
4048 vrc = VMR3ReqWait(pReq, RT_INDEFINITE_WAIT);
4049 AssertRC(vrc);
4050 if (RT_SUCCESS(vrc))
4051 vrc = pReq->iStatus;
4052 }
4053 VMR3ReqFree(pReq);
4054
4055 if (RT_SUCCESS(vrc))
4056 {
4057 LogFlowThisFunc(("Returns S_OK\n"));
4058 return S_OK;
4059 }
4060
4061 if (!pMedium)
4062 return setError(E_FAIL,
4063 tr("Could not mount the media/drive '%ls' (%Rrc)"),
4064 mediumLocation.raw(), vrc);
4065
4066 return setError(E_FAIL,
4067 tr("Could not unmount the currently mounted media/drive (%Rrc)"),
4068 vrc);
4069}
4070
4071/**
4072 * Performs the storage detach operation in EMT.
4073 *
4074 * @returns VBox status code.
4075 *
4076 * @param pThis Pointer to the Console object.
4077 * @param pUVM The VM handle.
4078 * @param pcszDevice The PDM device name.
4079 * @param uInstance The PDM device instance.
4080 * @param fSilent Flag whether to notify the guest about the detached device.
4081 *
4082 * @thread EMT
4083 */
4084DECLCALLBACK(int) Console::detachStorageDevice(Console *pConsole,
4085 PUVM pUVM,
4086 const char *pcszDevice,
4087 unsigned uInstance,
4088 StorageBus_T enmBus,
4089 IMediumAttachment *pMediumAtt,
4090 bool fSilent)
4091{
4092 LogFlowFunc(("pConsole=%p uInstance=%u pszDevice=%p:{%s} enmBus=%u, pMediumAtt=%p\n",
4093 pConsole, uInstance, pcszDevice, pcszDevice, enmBus, pMediumAtt));
4094
4095 AssertReturn(pConsole, VERR_INVALID_PARAMETER);
4096
4097 AutoCaller autoCaller(pConsole);
4098 AssertComRCReturn(autoCaller.rc(), VERR_ACCESS_DENIED);
4099
4100 /*
4101 * Suspend the VM first.
4102 *
4103 * The VM must not be running since it might have pending I/O to
4104 * the drive which is being changed.
4105 */
4106 bool fResume;
4107 VMSTATE enmVMState = VMR3GetStateU(pUVM);
4108 switch (enmVMState)
4109 {
4110 case VMSTATE_RESETTING:
4111 case VMSTATE_RUNNING:
4112 {
4113 LogFlowFunc(("Suspending the VM...\n"));
4114 /* disable the callback to prevent Console-level state change */
4115 pConsole->mVMStateChangeCallbackDisabled = true;
4116 int rc = VMR3Suspend(pUVM, VMSUSPENDREASON_RECONFIG);
4117 pConsole->mVMStateChangeCallbackDisabled = false;
4118 AssertRCReturn(rc, rc);
4119 fResume = true;
4120 break;
4121 }
4122
4123 case VMSTATE_SUSPENDED:
4124 case VMSTATE_CREATED:
4125 case VMSTATE_OFF:
4126 fResume = false;
4127 break;
4128
4129 case VMSTATE_RUNNING_LS:
4130 case VMSTATE_RUNNING_FT:
4131 return setErrorInternal(VBOX_E_INVALID_VM_STATE,
4132 COM_IIDOF(IConsole),
4133 getStaticComponentName(),
4134 (enmVMState == VMSTATE_RUNNING_LS) ? Utf8Str(tr("Cannot change drive during live migration")) : Utf8Str(tr("Cannot change drive during fault tolerant syncing")),
4135 false /*aWarning*/,
4136 true /*aLogIt*/);
4137
4138 default:
4139 AssertMsgFailedReturn(("enmVMState=%d\n", enmVMState), VERR_ACCESS_DENIED);
4140 }
4141
4142 /* Determine the base path for the device instance. */
4143 PCFGMNODE pCtlInst;
4144 pCtlInst = CFGMR3GetChildF(CFGMR3GetRootU(pUVM), "Devices/%s/%u/", pcszDevice, uInstance);
4145 AssertReturn(pCtlInst, VERR_INTERNAL_ERROR);
4146
4147#define H() AssertMsgReturn(!FAILED(hrc), ("hrc=%Rhrc\n", hrc), VERR_GENERAL_FAILURE)
4148
4149 HRESULT hrc;
4150 int rc = VINF_SUCCESS;
4151 int rcRet = VINF_SUCCESS;
4152 unsigned uLUN;
4153 LONG lDev;
4154 LONG lPort;
4155 DeviceType_T lType;
4156 PCFGMNODE pLunL0 = NULL;
4157 PCFGMNODE pCfg = NULL;
4158
4159 hrc = pMediumAtt->COMGETTER(Device)(&lDev); H();
4160 hrc = pMediumAtt->COMGETTER(Port)(&lPort); H();
4161 hrc = pMediumAtt->COMGETTER(Type)(&lType); H();
4162 hrc = Console::convertBusPortDeviceToLun(enmBus, lPort, lDev, uLUN); H();
4163
4164#undef H
4165
4166 /* First check if the LUN really exists. */
4167 pLunL0 = CFGMR3GetChildF(pCtlInst, "LUN#%u", uLUN);
4168 if (pLunL0)
4169 {
4170 uint32_t fFlags = 0;
4171
4172 if (fSilent)
4173 fFlags |= PDM_TACH_FLAGS_NOT_HOT_PLUG;
4174
4175 rc = PDMR3DeviceDetach(pUVM, pcszDevice, uInstance, uLUN, fFlags);
4176 if (rc == VERR_PDM_NO_DRIVER_ATTACHED_TO_LUN)
4177 rc = VINF_SUCCESS;
4178 AssertRCReturn(rc, rc);
4179 CFGMR3RemoveNode(pLunL0);
4180
4181 Utf8Str devicePath = Utf8StrFmt("%s/%u/LUN#%u", pcszDevice, uInstance, uLUN);
4182 pConsole->mapMediumAttachments.erase(devicePath);
4183
4184 }
4185 else
4186 AssertFailedReturn(VERR_INTERNAL_ERROR);
4187
4188 CFGMR3Dump(pCtlInst);
4189
4190 /*
4191 * Resume the VM if necessary.
4192 */
4193 if (fResume)
4194 {
4195 LogFlowFunc(("Resuming the VM...\n"));
4196 /* disable the callback to prevent Console-level state change */
4197 pConsole->mVMStateChangeCallbackDisabled = true;
4198 rc = VMR3Resume(pUVM, VMRESUMEREASON_RECONFIG);
4199 pConsole->mVMStateChangeCallbackDisabled = false;
4200 AssertRC(rc);
4201 if (RT_FAILURE(rc))
4202 {
4203 /* too bad, we failed. try to sync the console state with the VMM state */
4204 vmstateChangeCallback(pUVM, VMSTATE_SUSPENDED, enmVMState, pConsole);
4205 }
4206 /** @todo: if we failed with drive mount, then the VMR3Resume
4207 * error (if any) will be hidden from the caller. For proper reporting
4208 * of such multiple errors to the caller we need to enhance the
4209 * IVirtualBoxError interface. For now, give the first error the higher
4210 * priority.
4211 */
4212 if (RT_SUCCESS(rcRet))
4213 rcRet = rc;
4214 }
4215
4216 LogFlowFunc(("Returning %Rrc\n", rcRet));
4217 return rcRet;
4218}
4219
4220/**
4221 * Called by IInternalSessionControl::OnNetworkAdapterChange().
4222 *
4223 * @note Locks this object for writing.
4224 */
4225HRESULT Console::onNetworkAdapterChange(INetworkAdapter *aNetworkAdapter, BOOL changeAdapter)
4226{
4227 LogFlowThisFunc(("\n"));
4228
4229 AutoCaller autoCaller(this);
4230 AssertComRCReturnRC(autoCaller.rc());
4231
4232 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4233
4234 HRESULT rc = S_OK;
4235
4236 /* don't trigger network changes if the VM isn't running */
4237 SafeVMPtrQuiet ptrVM(this);
4238 if (ptrVM.isOk())
4239 {
4240 /* Get the properties we need from the adapter */
4241 BOOL fCableConnected, fTraceEnabled;
4242 rc = aNetworkAdapter->COMGETTER(CableConnected)(&fCableConnected);
4243 AssertComRC(rc);
4244 if (SUCCEEDED(rc))
4245 {
4246 rc = aNetworkAdapter->COMGETTER(TraceEnabled)(&fTraceEnabled);
4247 AssertComRC(rc);
4248 }
4249 if (SUCCEEDED(rc))
4250 {
4251 ULONG ulInstance;
4252 rc = aNetworkAdapter->COMGETTER(Slot)(&ulInstance);
4253 AssertComRC(rc);
4254 if (SUCCEEDED(rc))
4255 {
4256 /*
4257 * Find the adapter instance, get the config interface and update
4258 * the link state.
4259 */
4260 NetworkAdapterType_T adapterType;
4261 rc = aNetworkAdapter->COMGETTER(AdapterType)(&adapterType);
4262 AssertComRC(rc);
4263 const char *pszAdapterName = networkAdapterTypeToName(adapterType);
4264
4265 // prevent cross-thread deadlocks, don't need the lock any more
4266 alock.release();
4267
4268 PPDMIBASE pBase;
4269 int vrc = PDMR3QueryDeviceLun(ptrVM.rawUVM(), pszAdapterName, ulInstance, 0, &pBase);
4270 if (RT_SUCCESS(vrc))
4271 {
4272 Assert(pBase);
4273 PPDMINETWORKCONFIG pINetCfg;
4274 pINetCfg = PDMIBASE_QUERY_INTERFACE(pBase, PDMINETWORKCONFIG);
4275 if (pINetCfg)
4276 {
4277 Log(("Console::onNetworkAdapterChange: setting link state to %d\n",
4278 fCableConnected));
4279 vrc = pINetCfg->pfnSetLinkState(pINetCfg,
4280 fCableConnected ? PDMNETWORKLINKSTATE_UP
4281 : PDMNETWORKLINKSTATE_DOWN);
4282 ComAssertRC(vrc);
4283 }
4284 if (RT_SUCCESS(vrc) && changeAdapter)
4285 {
4286 VMSTATE enmVMState = VMR3GetStateU(ptrVM.rawUVM());
4287 if ( enmVMState == VMSTATE_RUNNING /** @todo LiveMigration: Forbid or deal correctly with the _LS variants */
4288 || enmVMState == VMSTATE_SUSPENDED)
4289 {
4290 if (fTraceEnabled && fCableConnected && pINetCfg)
4291 {
4292 vrc = pINetCfg->pfnSetLinkState(pINetCfg, PDMNETWORKLINKSTATE_DOWN);
4293 ComAssertRC(vrc);
4294 }
4295
4296 rc = doNetworkAdapterChange(ptrVM.rawUVM(), pszAdapterName, ulInstance, 0, aNetworkAdapter);
4297
4298 if (fTraceEnabled && fCableConnected && pINetCfg)
4299 {
4300 vrc = pINetCfg->pfnSetLinkState(pINetCfg, PDMNETWORKLINKSTATE_UP);
4301 ComAssertRC(vrc);
4302 }
4303 }
4304 }
4305 }
4306 else if (vrc == VERR_PDM_DEVICE_INSTANCE_NOT_FOUND)
4307 return setError(E_FAIL,
4308 tr("The network adapter #%u is not enabled"), ulInstance);
4309 else
4310 ComAssertRC(vrc);
4311
4312 if (RT_FAILURE(vrc))
4313 rc = E_FAIL;
4314
4315 alock.acquire();
4316 }
4317 }
4318 ptrVM.release();
4319 }
4320
4321 // definitely don't need the lock any more
4322 alock.release();
4323
4324 /* notify console callbacks on success */
4325 if (SUCCEEDED(rc))
4326 fireNetworkAdapterChangedEvent(mEventSource, aNetworkAdapter);
4327
4328 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
4329 return rc;
4330}
4331
4332/**
4333 * Called by IInternalSessionControl::OnNATEngineChange().
4334 *
4335 * @note Locks this object for writing.
4336 */
4337HRESULT Console::onNATRedirectRuleChange(ULONG ulInstance, BOOL aNatRuleRemove,
4338 NATProtocol_T aProto, IN_BSTR aHostIP, LONG aHostPort, IN_BSTR aGuestIP, LONG aGuestPort)
4339{
4340 LogFlowThisFunc(("\n"));
4341
4342 AutoCaller autoCaller(this);
4343 AssertComRCReturnRC(autoCaller.rc());
4344
4345 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4346
4347 HRESULT rc = S_OK;
4348
4349 /* don't trigger NAT engine changes if the VM isn't running */
4350 SafeVMPtrQuiet ptrVM(this);
4351 if (ptrVM.isOk())
4352 {
4353 do
4354 {
4355 ComPtr<INetworkAdapter> pNetworkAdapter;
4356 rc = machine()->GetNetworkAdapter(ulInstance, pNetworkAdapter.asOutParam());
4357 if ( FAILED(rc)
4358 || pNetworkAdapter.isNull())
4359 break;
4360
4361 /*
4362 * Find the adapter instance, get the config interface and update
4363 * the link state.
4364 */
4365 NetworkAdapterType_T adapterType;
4366 rc = pNetworkAdapter->COMGETTER(AdapterType)(&adapterType);
4367 if (FAILED(rc))
4368 {
4369 AssertComRC(rc);
4370 rc = E_FAIL;
4371 break;
4372 }
4373
4374 const char *pszAdapterName = networkAdapterTypeToName(adapterType);
4375 PPDMIBASE pBase;
4376 int vrc = PDMR3QueryLun(ptrVM.rawUVM(), pszAdapterName, ulInstance, 0, &pBase);
4377 if (RT_FAILURE(vrc))
4378 {
4379 ComAssertRC(vrc);
4380 rc = E_FAIL;
4381 break;
4382 }
4383
4384 NetworkAttachmentType_T attachmentType;
4385 rc = pNetworkAdapter->COMGETTER(AttachmentType)(&attachmentType);
4386 if ( FAILED(rc)
4387 || attachmentType != NetworkAttachmentType_NAT)
4388 {
4389 rc = E_FAIL;
4390 break;
4391 }
4392
4393 /* look down for PDMINETWORKNATCONFIG interface */
4394 PPDMINETWORKNATCONFIG pNetNatCfg = NULL;
4395 while (pBase)
4396 {
4397 pNetNatCfg = (PPDMINETWORKNATCONFIG)pBase->pfnQueryInterface(pBase, PDMINETWORKNATCONFIG_IID);
4398 if (pNetNatCfg)
4399 break;
4400 /** @todo r=bird: This stinks! */
4401 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pBase);
4402 pBase = pDrvIns->pDownBase;
4403 }
4404 if (!pNetNatCfg)
4405 break;
4406
4407 bool fUdp = aProto == NATProtocol_UDP;
4408 vrc = pNetNatCfg->pfnRedirectRuleCommand(pNetNatCfg, !!aNatRuleRemove, fUdp,
4409 Utf8Str(aHostIP).c_str(), aHostPort, Utf8Str(aGuestIP).c_str(),
4410 aGuestPort);
4411 if (RT_FAILURE(vrc))
4412 rc = E_FAIL;
4413 } while (0); /* break loop */
4414 ptrVM.release();
4415 }
4416
4417 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
4418 return rc;
4419}
4420
4421VMMDevMouseInterface *Console::getVMMDevMouseInterface()
4422{
4423 return m_pVMMDev;
4424}
4425
4426DisplayMouseInterface *Console::getDisplayMouseInterface()
4427{
4428 return mDisplay;
4429}
4430
4431/**
4432 * Process a network adaptor change.
4433 *
4434 * @returns COM status code.
4435 *
4436 * @parma pUVM The VM handle (caller hold this safely).
4437 * @param pszDevice The PDM device name.
4438 * @param uInstance The PDM device instance.
4439 * @param uLun The PDM LUN number of the drive.
4440 * @param aNetworkAdapter The network adapter whose attachment needs to be changed
4441 */
4442HRESULT Console::doNetworkAdapterChange(PUVM pUVM,
4443 const char *pszDevice,
4444 unsigned uInstance,
4445 unsigned uLun,
4446 INetworkAdapter *aNetworkAdapter)
4447{
4448 LogFlowThisFunc(("pszDevice=%p:{%s} uInstance=%u uLun=%u aNetworkAdapter=%p\n",
4449 pszDevice, pszDevice, uInstance, uLun, aNetworkAdapter));
4450
4451 AutoCaller autoCaller(this);
4452 AssertComRCReturnRC(autoCaller.rc());
4453
4454 /*
4455 * Call worker in EMT, that's faster and safer than doing everything
4456 * using VM3ReqCall. Note that we separate VMR3ReqCall from VMR3ReqWait
4457 * here to make requests from under the lock in order to serialize them.
4458 */
4459 PVMREQ pReq;
4460 int vrc = VMR3ReqCallU(pUVM, 0 /*idDstCpu*/, &pReq, 0 /* no wait! */, VMREQFLAGS_VBOX_STATUS,
4461 (PFNRT)Console::changeNetworkAttachment, 6,
4462 this, pUVM, pszDevice, uInstance, uLun, aNetworkAdapter);
4463
4464 if (vrc == VERR_TIMEOUT || RT_SUCCESS(vrc))
4465 {
4466 vrc = VMR3ReqWait(pReq, RT_INDEFINITE_WAIT);
4467 AssertRC(vrc);
4468 if (RT_SUCCESS(vrc))
4469 vrc = pReq->iStatus;
4470 }
4471 VMR3ReqFree(pReq);
4472
4473 if (RT_SUCCESS(vrc))
4474 {
4475 LogFlowThisFunc(("Returns S_OK\n"));
4476 return S_OK;
4477 }
4478
4479 return setError(E_FAIL,
4480 tr("Could not change the network adaptor attachement type (%Rrc)"),
4481 vrc);
4482}
4483
4484
4485/**
4486 * Performs the Network Adaptor change in EMT.
4487 *
4488 * @returns VBox status code.
4489 *
4490 * @param pThis Pointer to the Console object.
4491 * @param pUVM The VM handle.
4492 * @param pszDevice The PDM device name.
4493 * @param uInstance The PDM device instance.
4494 * @param uLun The PDM LUN number of the drive.
4495 * @param aNetworkAdapter The network adapter whose attachment needs to be changed
4496 *
4497 * @thread EMT
4498 * @note Locks the Console object for writing.
4499 */
4500DECLCALLBACK(int) Console::changeNetworkAttachment(Console *pThis,
4501 PUVM pUVM,
4502 const char *pszDevice,
4503 unsigned uInstance,
4504 unsigned uLun,
4505 INetworkAdapter *aNetworkAdapter)
4506{
4507 LogFlowFunc(("pThis=%p pszDevice=%p:{%s} uInstance=%u uLun=%u aNetworkAdapter=%p\n",
4508 pThis, pszDevice, pszDevice, uInstance, uLun, aNetworkAdapter));
4509
4510 AssertReturn(pThis, VERR_INVALID_PARAMETER);
4511
4512 AutoCaller autoCaller(pThis);
4513 AssertComRCReturn(autoCaller.rc(), VERR_ACCESS_DENIED);
4514
4515 ComPtr<IVirtualBox> pVirtualBox;
4516 pThis->mMachine->COMGETTER(Parent)(pVirtualBox.asOutParam());
4517 ComPtr<ISystemProperties> pSystemProperties;
4518 if (pVirtualBox)
4519 pVirtualBox->COMGETTER(SystemProperties)(pSystemProperties.asOutParam());
4520 ChipsetType_T chipsetType = ChipsetType_PIIX3;
4521 pThis->mMachine->COMGETTER(ChipsetType)(&chipsetType);
4522 ULONG maxNetworkAdapters = 0;
4523 if (pSystemProperties)
4524 pSystemProperties->GetMaxNetworkAdapters(chipsetType, &maxNetworkAdapters);
4525 AssertMsg( ( !strcmp(pszDevice, "pcnet")
4526 || !strcmp(pszDevice, "e1000")
4527 || !strcmp(pszDevice, "virtio-net"))
4528 && uLun == 0
4529 && uInstance < maxNetworkAdapters,
4530 ("pszDevice=%s uLun=%d uInstance=%d\n", pszDevice, uLun, uInstance));
4531 Log(("pszDevice=%s uLun=%d uInstance=%d\n", pszDevice, uLun, uInstance));
4532
4533 /*
4534 * Suspend the VM first.
4535 *
4536 * The VM must not be running since it might have pending I/O to
4537 * the drive which is being changed.
4538 */
4539 bool fResume;
4540 VMSTATE enmVMState = VMR3GetStateU(pUVM);
4541 switch (enmVMState)
4542 {
4543 case VMSTATE_RESETTING:
4544 case VMSTATE_RUNNING:
4545 {
4546 LogFlowFunc(("Suspending the VM...\n"));
4547 /* disable the callback to prevent Console-level state change */
4548 pThis->mVMStateChangeCallbackDisabled = true;
4549 int rc = VMR3Suspend(pUVM, VMSUSPENDREASON_RECONFIG);
4550 pThis->mVMStateChangeCallbackDisabled = false;
4551 AssertRCReturn(rc, rc);
4552 fResume = true;
4553 break;
4554 }
4555
4556 case VMSTATE_SUSPENDED:
4557 case VMSTATE_CREATED:
4558 case VMSTATE_OFF:
4559 fResume = false;
4560 break;
4561
4562 default:
4563 AssertLogRelMsgFailedReturn(("enmVMState=%d\n", enmVMState), VERR_ACCESS_DENIED);
4564 }
4565
4566 int rc = VINF_SUCCESS;
4567 int rcRet = VINF_SUCCESS;
4568
4569 PCFGMNODE pCfg = NULL; /* /Devices/Dev/.../Config/ */
4570 PCFGMNODE pLunL0 = NULL; /* /Devices/Dev/0/LUN#0/ */
4571 PCFGMNODE pInst = CFGMR3GetChildF(CFGMR3GetRootU(pUVM), "Devices/%s/%d/", pszDevice, uInstance);
4572 AssertRelease(pInst);
4573
4574 rcRet = pThis->configNetwork(pszDevice, uInstance, uLun, aNetworkAdapter, pCfg, pLunL0, pInst,
4575 true /*fAttachDetach*/, false /*fIgnoreConnectFailure*/);
4576
4577 /*
4578 * Resume the VM if necessary.
4579 */
4580 if (fResume)
4581 {
4582 LogFlowFunc(("Resuming the VM...\n"));
4583 /* disable the callback to prevent Console-level state change */
4584 pThis->mVMStateChangeCallbackDisabled = true;
4585 rc = VMR3Resume(pUVM, VMRESUMEREASON_RECONFIG);
4586 pThis->mVMStateChangeCallbackDisabled = false;
4587 AssertRC(rc);
4588 if (RT_FAILURE(rc))
4589 {
4590 /* too bad, we failed. try to sync the console state with the VMM state */
4591 vmstateChangeCallback(pUVM, VMSTATE_SUSPENDED, enmVMState, pThis);
4592 }
4593 /// @todo (r=dmik) if we failed with drive mount, then the VMR3Resume
4594 // error (if any) will be hidden from the caller. For proper reporting
4595 // of such multiple errors to the caller we need to enhance the
4596 // IVirtualBoxError interface. For now, give the first error the higher
4597 // priority.
4598 if (RT_SUCCESS(rcRet))
4599 rcRet = rc;
4600 }
4601
4602 LogFlowFunc(("Returning %Rrc\n", rcRet));
4603 return rcRet;
4604}
4605
4606
4607/**
4608 * Called by IInternalSessionControl::OnSerialPortChange().
4609 */
4610HRESULT Console::onSerialPortChange(ISerialPort *aSerialPort)
4611{
4612 LogFlowThisFunc(("\n"));
4613
4614 AutoCaller autoCaller(this);
4615 AssertComRCReturnRC(autoCaller.rc());
4616
4617 fireSerialPortChangedEvent(mEventSource, aSerialPort);
4618
4619 LogFlowThisFunc(("Leaving rc=%#x\n", S_OK));
4620 return S_OK;
4621}
4622
4623/**
4624 * Called by IInternalSessionControl::OnParallelPortChange().
4625 */
4626HRESULT Console::onParallelPortChange(IParallelPort *aParallelPort)
4627{
4628 LogFlowThisFunc(("\n"));
4629
4630 AutoCaller autoCaller(this);
4631 AssertComRCReturnRC(autoCaller.rc());
4632
4633 fireParallelPortChangedEvent(mEventSource, aParallelPort);
4634
4635 LogFlowThisFunc(("Leaving rc=%#x\n", S_OK));
4636 return S_OK;
4637}
4638
4639/**
4640 * Called by IInternalSessionControl::OnStorageControllerChange().
4641 */
4642HRESULT Console::onStorageControllerChange()
4643{
4644 LogFlowThisFunc(("\n"));
4645
4646 AutoCaller autoCaller(this);
4647 AssertComRCReturnRC(autoCaller.rc());
4648
4649 fireStorageControllerChangedEvent(mEventSource);
4650
4651 LogFlowThisFunc(("Leaving rc=%#x\n", S_OK));
4652 return S_OK;
4653}
4654
4655/**
4656 * Called by IInternalSessionControl::OnMediumChange().
4657 */
4658HRESULT Console::onMediumChange(IMediumAttachment *aMediumAttachment, BOOL aForce)
4659{
4660 LogFlowThisFunc(("\n"));
4661
4662 AutoCaller autoCaller(this);
4663 AssertComRCReturnRC(autoCaller.rc());
4664
4665 HRESULT rc = S_OK;
4666
4667 /* don't trigger medium changes if the VM isn't running */
4668 SafeVMPtrQuiet ptrVM(this);
4669 if (ptrVM.isOk())
4670 {
4671 rc = doMediumChange(aMediumAttachment, !!aForce, ptrVM.rawUVM());
4672 ptrVM.release();
4673 }
4674
4675 /* notify console callbacks on success */
4676 if (SUCCEEDED(rc))
4677 fireMediumChangedEvent(mEventSource, aMediumAttachment);
4678
4679 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
4680 return rc;
4681}
4682
4683/**
4684 * Called by IInternalSessionControl::OnCPUChange().
4685 *
4686 * @note Locks this object for writing.
4687 */
4688HRESULT Console::onCPUChange(ULONG aCPU, BOOL aRemove)
4689{
4690 LogFlowThisFunc(("\n"));
4691
4692 AutoCaller autoCaller(this);
4693 AssertComRCReturnRC(autoCaller.rc());
4694
4695 HRESULT rc = S_OK;
4696
4697 /* don't trigger CPU changes if the VM isn't running */
4698 SafeVMPtrQuiet ptrVM(this);
4699 if (ptrVM.isOk())
4700 {
4701 if (aRemove)
4702 rc = doCPURemove(aCPU, ptrVM.rawUVM());
4703 else
4704 rc = doCPUAdd(aCPU, ptrVM.rawUVM());
4705 ptrVM.release();
4706 }
4707
4708 /* notify console callbacks on success */
4709 if (SUCCEEDED(rc))
4710 fireCPUChangedEvent(mEventSource, aCPU, aRemove);
4711
4712 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
4713 return rc;
4714}
4715
4716/**
4717 * Called by IInternalSessionControl::OnCpuExecutionCapChange().
4718 *
4719 * @note Locks this object for writing.
4720 */
4721HRESULT Console::onCPUExecutionCapChange(ULONG aExecutionCap)
4722{
4723 LogFlowThisFunc(("\n"));
4724
4725 AutoCaller autoCaller(this);
4726 AssertComRCReturnRC(autoCaller.rc());
4727
4728 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4729
4730 HRESULT rc = S_OK;
4731
4732 /* don't trigger the CPU priority change if the VM isn't running */
4733 SafeVMPtrQuiet ptrVM(this);
4734 if (ptrVM.isOk())
4735 {
4736 if ( mMachineState == MachineState_Running
4737 || mMachineState == MachineState_Teleporting
4738 || mMachineState == MachineState_LiveSnapshotting
4739 )
4740 {
4741 /* No need to call in the EMT thread. */
4742 rc = VMR3SetCpuExecutionCap(ptrVM.rawUVM(), aExecutionCap);
4743 }
4744 else
4745 rc = setInvalidMachineStateError();
4746 ptrVM.release();
4747 }
4748
4749 /* notify console callbacks on success */
4750 if (SUCCEEDED(rc))
4751 {
4752 alock.release();
4753 fireCPUExecutionCapChangedEvent(mEventSource, aExecutionCap);
4754 }
4755
4756 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
4757 return rc;
4758}
4759
4760/**
4761 * Called by IInternalSessionControl::OnClipboardModeChange().
4762 *
4763 * @note Locks this object for writing.
4764 */
4765HRESULT Console::onClipboardModeChange(ClipboardMode_T aClipboardMode)
4766{
4767 LogFlowThisFunc(("\n"));
4768
4769 AutoCaller autoCaller(this);
4770 AssertComRCReturnRC(autoCaller.rc());
4771
4772 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4773
4774 HRESULT rc = S_OK;
4775
4776 /* don't trigger the clipboard mode change if the VM isn't running */
4777 SafeVMPtrQuiet ptrVM(this);
4778 if (ptrVM.isOk())
4779 {
4780 if ( mMachineState == MachineState_Running
4781 || mMachineState == MachineState_Teleporting
4782 || mMachineState == MachineState_LiveSnapshotting)
4783 changeClipboardMode(aClipboardMode);
4784 else
4785 rc = setInvalidMachineStateError();
4786 ptrVM.release();
4787 }
4788
4789 /* notify console callbacks on success */
4790 if (SUCCEEDED(rc))
4791 {
4792 alock.release();
4793 fireClipboardModeChangedEvent(mEventSource, aClipboardMode);
4794 }
4795
4796 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
4797 return rc;
4798}
4799
4800/**
4801 * Called by IInternalSessionControl::OnDragAndDropModeChange().
4802 *
4803 * @note Locks this object for writing.
4804 */
4805HRESULT Console::onDragAndDropModeChange(DragAndDropMode_T aDragAndDropMode)
4806{
4807 LogFlowThisFunc(("\n"));
4808
4809 AutoCaller autoCaller(this);
4810 AssertComRCReturnRC(autoCaller.rc());
4811
4812 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4813
4814 HRESULT rc = S_OK;
4815
4816 /* don't trigger the drag'n'drop mode change if the VM isn't running */
4817 SafeVMPtrQuiet ptrVM(this);
4818 if (ptrVM.isOk())
4819 {
4820 if ( mMachineState == MachineState_Running
4821 || mMachineState == MachineState_Teleporting
4822 || mMachineState == MachineState_LiveSnapshotting)
4823 changeDragAndDropMode(aDragAndDropMode);
4824 else
4825 rc = setInvalidMachineStateError();
4826 ptrVM.release();
4827 }
4828
4829 /* notify console callbacks on success */
4830 if (SUCCEEDED(rc))
4831 {
4832 alock.release();
4833 fireDragAndDropModeChangedEvent(mEventSource, aDragAndDropMode);
4834 }
4835
4836 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
4837 return rc;
4838}
4839
4840/**
4841 * Called by IInternalSessionControl::OnVRDEServerChange().
4842 *
4843 * @note Locks this object for writing.
4844 */
4845HRESULT Console::onVRDEServerChange(BOOL aRestart)
4846{
4847 AutoCaller autoCaller(this);
4848 AssertComRCReturnRC(autoCaller.rc());
4849
4850 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4851
4852 HRESULT rc = S_OK;
4853
4854 /* don't trigger VRDE server changes if the VM isn't running */
4855 SafeVMPtrQuiet ptrVM(this);
4856 if (ptrVM.isOk())
4857 {
4858 /* Serialize. */
4859 if (mfVRDEChangeInProcess)
4860 mfVRDEChangePending = true;
4861 else
4862 {
4863 do {
4864 mfVRDEChangeInProcess = true;
4865 mfVRDEChangePending = false;
4866
4867 if ( mVRDEServer
4868 && ( mMachineState == MachineState_Running
4869 || mMachineState == MachineState_Teleporting
4870 || mMachineState == MachineState_LiveSnapshotting
4871 || mMachineState == MachineState_Paused
4872 )
4873 )
4874 {
4875 BOOL vrdpEnabled = FALSE;
4876
4877 rc = mVRDEServer->COMGETTER(Enabled)(&vrdpEnabled);
4878 ComAssertComRCRetRC(rc);
4879
4880 if (aRestart)
4881 {
4882 /* VRDP server may call this Console object back from other threads (VRDP INPUT or OUTPUT). */
4883 alock.release();
4884
4885 if (vrdpEnabled)
4886 {
4887 // If there was no VRDP server started the 'stop' will do nothing.
4888 // However if a server was started and this notification was called,
4889 // we have to restart the server.
4890 mConsoleVRDPServer->Stop();
4891
4892 if (RT_FAILURE(mConsoleVRDPServer->Launch()))
4893 rc = E_FAIL;
4894 else
4895 mConsoleVRDPServer->EnableConnections();
4896 }
4897 else
4898 mConsoleVRDPServer->Stop();
4899
4900 alock.acquire();
4901 }
4902 }
4903 else
4904 rc = setInvalidMachineStateError();
4905
4906 mfVRDEChangeInProcess = false;
4907 } while (mfVRDEChangePending && SUCCEEDED(rc));
4908 }
4909
4910 ptrVM.release();
4911 }
4912
4913 /* notify console callbacks on success */
4914 if (SUCCEEDED(rc))
4915 {
4916 alock.release();
4917 fireVRDEServerChangedEvent(mEventSource);
4918 }
4919
4920 return rc;
4921}
4922
4923void Console::onVRDEServerInfoChange()
4924{
4925 AutoCaller autoCaller(this);
4926 AssertComRCReturnVoid(autoCaller.rc());
4927
4928 fireVRDEServerInfoChangedEvent(mEventSource);
4929}
4930
4931HRESULT Console::onVideoCaptureChange()
4932{
4933 AutoCaller autoCaller(this);
4934 AssertComRCReturnRC(autoCaller.rc());
4935
4936 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4937
4938 HRESULT rc = S_OK;
4939
4940 /* don't trigger video capture changes if the VM isn't running */
4941 SafeVMPtrQuiet ptrVM(this);
4942 if (ptrVM.isOk())
4943 {
4944 BOOL fEnabled;
4945 rc = mMachine->COMGETTER(VideoCaptureEnabled)(&fEnabled);
4946 SafeArray<BOOL> screens;
4947 if (SUCCEEDED(rc))
4948 rc = mMachine->COMGETTER(VideoCaptureScreens)(ComSafeArrayAsOutParam(screens));
4949 if (mDisplay)
4950 {
4951 int vrc = VINF_SUCCESS;
4952 if (SUCCEEDED(rc))
4953 vrc = mDisplay->VideoCaptureEnableScreens(ComSafeArrayAsInParam(screens));
4954 if (RT_SUCCESS(vrc))
4955 {
4956 if (fEnabled)
4957 {
4958 vrc = mDisplay->VideoCaptureStart();
4959 if (RT_FAILURE(vrc))
4960 rc = setError(E_FAIL, tr("Unable to start video capturing (%Rrc)"), vrc);
4961 }
4962 else
4963 mDisplay->VideoCaptureStop();
4964 }
4965 else
4966 rc = setError(E_FAIL, tr("Unable to set screens for capturing (%Rrc)"), vrc);
4967 }
4968 ptrVM.release();
4969 }
4970
4971 /* notify console callbacks on success */
4972 if (SUCCEEDED(rc))
4973 {
4974 alock.release();
4975 fireVideoCaptureChangedEvent(mEventSource);
4976 }
4977
4978 return rc;
4979}
4980
4981/**
4982 * Called by IInternalSessionControl::OnUSBControllerChange().
4983 */
4984HRESULT Console::onUSBControllerChange()
4985{
4986 LogFlowThisFunc(("\n"));
4987
4988 AutoCaller autoCaller(this);
4989 AssertComRCReturnRC(autoCaller.rc());
4990
4991 fireUSBControllerChangedEvent(mEventSource);
4992
4993 return S_OK;
4994}
4995
4996/**
4997 * Called by IInternalSessionControl::OnSharedFolderChange().
4998 *
4999 * @note Locks this object for writing.
5000 */
5001HRESULT Console::onSharedFolderChange(BOOL aGlobal)
5002{
5003 LogFlowThisFunc(("aGlobal=%RTbool\n", aGlobal));
5004
5005 AutoCaller autoCaller(this);
5006 AssertComRCReturnRC(autoCaller.rc());
5007
5008 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5009
5010 HRESULT rc = fetchSharedFolders(aGlobal);
5011
5012 /* notify console callbacks on success */
5013 if (SUCCEEDED(rc))
5014 {
5015 alock.release();
5016 fireSharedFolderChangedEvent(mEventSource, aGlobal ? (Scope_T)Scope_Global : (Scope_T)Scope_Machine);
5017 }
5018
5019 return rc;
5020}
5021
5022/**
5023 * Called by IInternalSessionControl::OnUSBDeviceAttach() or locally by
5024 * processRemoteUSBDevices() after IInternalMachineControl::RunUSBDeviceFilters()
5025 * returns TRUE for a given remote USB device.
5026 *
5027 * @return S_OK if the device was attached to the VM.
5028 * @return failure if not attached.
5029 *
5030 * @param aDevice
5031 * The device in question.
5032 * @param aMaskedIfs
5033 * The interfaces to hide from the guest.
5034 *
5035 * @note Locks this object for writing.
5036 */
5037HRESULT Console::onUSBDeviceAttach(IUSBDevice *aDevice, IVirtualBoxErrorInfo *aError, ULONG aMaskedIfs)
5038{
5039#ifdef VBOX_WITH_USB
5040 LogFlowThisFunc(("aDevice=%p aError=%p\n", aDevice, aError));
5041
5042 AutoCaller autoCaller(this);
5043 ComAssertComRCRetRC(autoCaller.rc());
5044
5045 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5046
5047 /* Get the VM pointer (we don't need error info, since it's a callback). */
5048 SafeVMPtrQuiet ptrVM(this);
5049 if (!ptrVM.isOk())
5050 {
5051 /* The VM may be no more operational when this message arrives
5052 * (e.g. it may be Saving or Stopping or just PoweredOff) --
5053 * autoVMCaller.rc() will return a failure in this case. */
5054 LogFlowThisFunc(("Attach request ignored (mMachineState=%d).\n",
5055 mMachineState));
5056 return ptrVM.rc();
5057 }
5058
5059 if (aError != NULL)
5060 {
5061 /* notify callbacks about the error */
5062 alock.release();
5063 onUSBDeviceStateChange(aDevice, true /* aAttached */, aError);
5064 return S_OK;
5065 }
5066
5067 /* Don't proceed unless there's at least one USB hub. */
5068 if (!PDMR3UsbHasHub(ptrVM.rawUVM()))
5069 {
5070 LogFlowThisFunc(("Attach request ignored (no USB controller).\n"));
5071 return E_FAIL;
5072 }
5073
5074 alock.release();
5075 HRESULT rc = attachUSBDevice(aDevice, aMaskedIfs);
5076 if (FAILED(rc))
5077 {
5078 /* take the current error info */
5079 com::ErrorInfoKeeper eik;
5080 /* the error must be a VirtualBoxErrorInfo instance */
5081 ComPtr<IVirtualBoxErrorInfo> pError = eik.takeError();
5082 Assert(!pError.isNull());
5083 if (!pError.isNull())
5084 {
5085 /* notify callbacks about the error */
5086 onUSBDeviceStateChange(aDevice, true /* aAttached */, pError);
5087 }
5088 }
5089
5090 return rc;
5091
5092#else /* !VBOX_WITH_USB */
5093 return E_FAIL;
5094#endif /* !VBOX_WITH_USB */
5095}
5096
5097/**
5098 * Called by IInternalSessionControl::OnUSBDeviceDetach() and locally by
5099 * processRemoteUSBDevices().
5100 *
5101 * @note Locks this object for writing.
5102 */
5103HRESULT Console::onUSBDeviceDetach(IN_BSTR aId,
5104 IVirtualBoxErrorInfo *aError)
5105{
5106#ifdef VBOX_WITH_USB
5107 Guid Uuid(aId);
5108 LogFlowThisFunc(("aId={%RTuuid} aError=%p\n", Uuid.raw(), aError));
5109
5110 AutoCaller autoCaller(this);
5111 AssertComRCReturnRC(autoCaller.rc());
5112
5113 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5114
5115 /* Find the device. */
5116 ComObjPtr<OUSBDevice> pUSBDevice;
5117 USBDeviceList::iterator it = mUSBDevices.begin();
5118 while (it != mUSBDevices.end())
5119 {
5120 LogFlowThisFunc(("it={%RTuuid}\n", (*it)->id().raw()));
5121 if ((*it)->id() == Uuid)
5122 {
5123 pUSBDevice = *it;
5124 break;
5125 }
5126 ++it;
5127 }
5128
5129
5130 if (pUSBDevice.isNull())
5131 {
5132 LogFlowThisFunc(("USB device not found.\n"));
5133
5134 /* The VM may be no more operational when this message arrives
5135 * (e.g. it may be Saving or Stopping or just PoweredOff). Use
5136 * AutoVMCaller to detect it -- AutoVMCaller::rc() will return a
5137 * failure in this case. */
5138
5139 AutoVMCallerQuiet autoVMCaller(this);
5140 if (FAILED(autoVMCaller.rc()))
5141 {
5142 LogFlowThisFunc(("Detach request ignored (mMachineState=%d).\n",
5143 mMachineState));
5144 return autoVMCaller.rc();
5145 }
5146
5147 /* the device must be in the list otherwise */
5148 AssertFailedReturn(E_FAIL);
5149 }
5150
5151 if (aError != NULL)
5152 {
5153 /* notify callback about an error */
5154 alock.release();
5155 onUSBDeviceStateChange(pUSBDevice, false /* aAttached */, aError);
5156 return S_OK;
5157 }
5158
5159 /* Remove the device from the collection, it is re-added below for failures */
5160 mUSBDevices.erase(it);
5161
5162 alock.release();
5163 HRESULT rc = detachUSBDevice(pUSBDevice);
5164 if (FAILED(rc))
5165 {
5166 /* Re-add the device to the collection */
5167 alock.acquire();
5168 mUSBDevices.push_back(pUSBDevice);
5169 alock.release();
5170 /* take the current error info */
5171 com::ErrorInfoKeeper eik;
5172 /* the error must be a VirtualBoxErrorInfo instance */
5173 ComPtr<IVirtualBoxErrorInfo> pError = eik.takeError();
5174 Assert(!pError.isNull());
5175 if (!pError.isNull())
5176 {
5177 /* notify callbacks about the error */
5178 onUSBDeviceStateChange(pUSBDevice, false /* aAttached */, pError);
5179 }
5180 }
5181
5182 return rc;
5183
5184#else /* !VBOX_WITH_USB */
5185 return E_FAIL;
5186#endif /* !VBOX_WITH_USB */
5187}
5188
5189/**
5190 * Called by IInternalSessionControl::OnBandwidthGroupChange().
5191 *
5192 * @note Locks this object for writing.
5193 */
5194HRESULT Console::onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
5195{
5196 LogFlowThisFunc(("\n"));
5197
5198 AutoCaller autoCaller(this);
5199 AssertComRCReturnRC(autoCaller.rc());
5200
5201 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5202
5203 HRESULT rc = S_OK;
5204
5205 /* don't trigger bandwidth group changes if the VM isn't running */
5206 SafeVMPtrQuiet ptrVM(this);
5207 if (ptrVM.isOk())
5208 {
5209 if ( mMachineState == MachineState_Running
5210 || mMachineState == MachineState_Teleporting
5211 || mMachineState == MachineState_LiveSnapshotting
5212 )
5213 {
5214 /* No need to call in the EMT thread. */
5215 LONG64 cMax;
5216 Bstr strName;
5217 BandwidthGroupType_T enmType;
5218 rc = aBandwidthGroup->COMGETTER(Name)(strName.asOutParam());
5219 if (SUCCEEDED(rc))
5220 rc = aBandwidthGroup->COMGETTER(MaxBytesPerSec)(&cMax);
5221 if (SUCCEEDED(rc))
5222 rc = aBandwidthGroup->COMGETTER(Type)(&enmType);
5223
5224 if (SUCCEEDED(rc))
5225 {
5226 int vrc = VINF_SUCCESS;
5227 if (enmType == BandwidthGroupType_Disk)
5228 vrc = PDMR3AsyncCompletionBwMgrSetMaxForFile(ptrVM.rawUVM(), Utf8Str(strName).c_str(), cMax);
5229#ifdef VBOX_WITH_NETSHAPER
5230 else if (enmType == BandwidthGroupType_Network)
5231 vrc = PDMR3NsBwGroupSetLimit(ptrVM.rawUVM(), Utf8Str(strName).c_str(), cMax);
5232 else
5233 rc = E_NOTIMPL;
5234#endif /* VBOX_WITH_NETSHAPER */
5235 AssertRC(vrc);
5236 }
5237 }
5238 else
5239 rc = setInvalidMachineStateError();
5240 ptrVM.release();
5241 }
5242
5243 /* notify console callbacks on success */
5244 if (SUCCEEDED(rc))
5245 {
5246 alock.release();
5247 fireBandwidthGroupChangedEvent(mEventSource, aBandwidthGroup);
5248 }
5249
5250 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
5251 return rc;
5252}
5253
5254/**
5255 * Called by IInternalSessionControl::OnStorageDeviceChange().
5256 *
5257 * @note Locks this object for writing.
5258 */
5259HRESULT Console::onStorageDeviceChange(IMediumAttachment *aMediumAttachment, BOOL aRemove, BOOL aSilent)
5260{
5261 LogFlowThisFunc(("\n"));
5262
5263 AutoCaller autoCaller(this);
5264 AssertComRCReturnRC(autoCaller.rc());
5265
5266 HRESULT rc = S_OK;
5267
5268 /* don't trigger medium changes if the VM isn't running */
5269 SafeVMPtrQuiet ptrVM(this);
5270 if (ptrVM.isOk())
5271 {
5272 if (aRemove)
5273 rc = doStorageDeviceDetach(aMediumAttachment, ptrVM.rawUVM(), RT_BOOL(aSilent));
5274 else
5275 rc = doStorageDeviceAttach(aMediumAttachment, ptrVM.rawUVM(), RT_BOOL(aSilent));
5276 ptrVM.release();
5277 }
5278
5279 /* notify console callbacks on success */
5280 if (SUCCEEDED(rc))
5281 fireStorageDeviceChangedEvent(mEventSource, aMediumAttachment, aRemove, aSilent);
5282
5283 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
5284 return rc;
5285}
5286
5287/**
5288 * @note Temporarily locks this object for writing.
5289 */
5290HRESULT Console::getGuestProperty(IN_BSTR aName, BSTR *aValue,
5291 LONG64 *aTimestamp, BSTR *aFlags)
5292{
5293#ifndef VBOX_WITH_GUEST_PROPS
5294 ReturnComNotImplemented();
5295#else /* VBOX_WITH_GUEST_PROPS */
5296 if (!VALID_PTR(aName))
5297 return E_INVALIDARG;
5298 if (!VALID_PTR(aValue))
5299 return E_POINTER;
5300 if ((aTimestamp != NULL) && !VALID_PTR(aTimestamp))
5301 return E_POINTER;
5302 if ((aFlags != NULL) && !VALID_PTR(aFlags))
5303 return E_POINTER;
5304
5305 AutoCaller autoCaller(this);
5306 AssertComRCReturnRC(autoCaller.rc());
5307
5308 /* protect mpUVM (if not NULL) */
5309 SafeVMPtrQuiet ptrVM(this);
5310 if (FAILED(ptrVM.rc()))
5311 return ptrVM.rc();
5312
5313 /* Note: validity of mVMMDev which is bound to uninit() is guaranteed by
5314 * ptrVM, so there is no need to hold a lock of this */
5315
5316 HRESULT rc = E_UNEXPECTED;
5317 using namespace guestProp;
5318
5319 try
5320 {
5321 VBOXHGCMSVCPARM parm[4];
5322 Utf8Str Utf8Name = aName;
5323 char szBuffer[MAX_VALUE_LEN + MAX_FLAGS_LEN];
5324
5325 parm[0].type = VBOX_HGCM_SVC_PARM_PTR;
5326 parm[0].u.pointer.addr = (void*)Utf8Name.c_str();
5327 /* The + 1 is the null terminator */
5328 parm[0].u.pointer.size = (uint32_t)Utf8Name.length() + 1;
5329 parm[1].type = VBOX_HGCM_SVC_PARM_PTR;
5330 parm[1].u.pointer.addr = szBuffer;
5331 parm[1].u.pointer.size = sizeof(szBuffer);
5332 int vrc = m_pVMMDev->hgcmHostCall("VBoxGuestPropSvc", GET_PROP_HOST,
5333 4, &parm[0]);
5334 /* The returned string should never be able to be greater than our buffer */
5335 AssertLogRel(vrc != VERR_BUFFER_OVERFLOW);
5336 AssertLogRel(RT_FAILURE(vrc) || VBOX_HGCM_SVC_PARM_64BIT == parm[2].type);
5337 if (RT_SUCCESS(vrc) || (VERR_NOT_FOUND == vrc))
5338 {
5339 rc = S_OK;
5340 if (vrc != VERR_NOT_FOUND)
5341 {
5342 Utf8Str strBuffer(szBuffer);
5343 strBuffer.cloneTo(aValue);
5344
5345 if (aTimestamp)
5346 *aTimestamp = parm[2].u.uint64;
5347
5348 if (aFlags)
5349 {
5350 size_t iFlags = strBuffer.length() + 1;
5351 Utf8Str(szBuffer + iFlags).cloneTo(aFlags);
5352 }
5353 }
5354 else
5355 aValue = NULL;
5356 }
5357 else
5358 rc = setError(E_UNEXPECTED,
5359 tr("The service call failed with the error %Rrc"),
5360 vrc);
5361 }
5362 catch(std::bad_alloc & /*e*/)
5363 {
5364 rc = E_OUTOFMEMORY;
5365 }
5366 return rc;
5367#endif /* VBOX_WITH_GUEST_PROPS */
5368}
5369
5370/**
5371 * @note Temporarily locks this object for writing.
5372 */
5373HRESULT Console::setGuestProperty(IN_BSTR aName, IN_BSTR aValue, IN_BSTR aFlags)
5374{
5375#ifndef VBOX_WITH_GUEST_PROPS
5376 ReturnComNotImplemented();
5377#else /* VBOX_WITH_GUEST_PROPS */
5378 if (!RT_VALID_PTR(aName))
5379 return setError(E_INVALIDARG, tr("Name cannot be NULL or an invalid pointer"));
5380 if (aValue != NULL && !RT_VALID_PTR(aValue))
5381 return setError(E_INVALIDARG, tr("Invalid value pointer"));
5382 if (aFlags != NULL && !RT_VALID_PTR(aFlags))
5383 return setError(E_INVALIDARG, tr("Invalid flags pointer"));
5384
5385 AutoCaller autoCaller(this);
5386 AssertComRCReturnRC(autoCaller.rc());
5387
5388 /* protect mpUVM (if not NULL) */
5389 SafeVMPtrQuiet ptrVM(this);
5390 if (FAILED(ptrVM.rc()))
5391 return ptrVM.rc();
5392
5393 /* Note: validity of mVMMDev which is bound to uninit() is guaranteed by
5394 * ptrVM, so there is no need to hold a lock of this */
5395
5396 using namespace guestProp;
5397
5398 VBOXHGCMSVCPARM parm[3];
5399
5400 Utf8Str Utf8Name = aName;
5401 parm[0].type = VBOX_HGCM_SVC_PARM_PTR;
5402 parm[0].u.pointer.addr = (void*)Utf8Name.c_str();
5403 /* The + 1 is the null terminator */
5404 parm[0].u.pointer.size = (uint32_t)Utf8Name.length() + 1;
5405
5406 Utf8Str Utf8Value;
5407 if (aValue != NULL)
5408 {
5409 Utf8Value = aValue;
5410 parm[1].type = VBOX_HGCM_SVC_PARM_PTR;
5411 parm[1].u.pointer.addr = (void *)Utf8Value.c_str();
5412 /* The + 1 is the null terminator */
5413 parm[1].u.pointer.size = (uint32_t)Utf8Value.length() + 1;
5414 }
5415
5416 Utf8Str Utf8Flags;
5417 if (aFlags != NULL)
5418 {
5419 Utf8Flags = aFlags;
5420 parm[2].type = VBOX_HGCM_SVC_PARM_PTR;
5421 parm[2].u.pointer.addr = (void*)Utf8Flags.c_str();
5422 /* The + 1 is the null terminator */
5423 parm[2].u.pointer.size = (uint32_t)Utf8Flags.length() + 1;
5424 }
5425
5426 int vrc;
5427 if (aValue != NULL && aFlags != NULL)
5428 vrc = m_pVMMDev->hgcmHostCall("VBoxGuestPropSvc", SET_PROP_HOST,
5429 3, &parm[0]);
5430 else if (aValue != NULL)
5431 vrc = m_pVMMDev->hgcmHostCall("VBoxGuestPropSvc", SET_PROP_VALUE_HOST,
5432 2, &parm[0]);
5433 else
5434 vrc = m_pVMMDev->hgcmHostCall("VBoxGuestPropSvc", DEL_PROP_HOST,
5435 1, &parm[0]);
5436 HRESULT hrc;
5437 if (RT_SUCCESS(vrc))
5438 hrc = S_OK;
5439 else
5440 hrc = setError(E_UNEXPECTED, tr("The service call failed with the error %Rrc"), vrc);
5441 return hrc;
5442#endif /* VBOX_WITH_GUEST_PROPS */
5443}
5444
5445
5446/**
5447 * @note Temporarily locks this object for writing.
5448 */
5449HRESULT Console::enumerateGuestProperties(IN_BSTR aPatterns,
5450 ComSafeArrayOut(BSTR, aNames),
5451 ComSafeArrayOut(BSTR, aValues),
5452 ComSafeArrayOut(LONG64, aTimestamps),
5453 ComSafeArrayOut(BSTR, aFlags))
5454{
5455#ifndef VBOX_WITH_GUEST_PROPS
5456 ReturnComNotImplemented();
5457#else /* VBOX_WITH_GUEST_PROPS */
5458 if (!VALID_PTR(aPatterns) && (aPatterns != NULL))
5459 return E_POINTER;
5460 if (ComSafeArrayOutIsNull(aNames))
5461 return E_POINTER;
5462 if (ComSafeArrayOutIsNull(aValues))
5463 return E_POINTER;
5464 if (ComSafeArrayOutIsNull(aTimestamps))
5465 return E_POINTER;
5466 if (ComSafeArrayOutIsNull(aFlags))
5467 return E_POINTER;
5468
5469 AutoCaller autoCaller(this);
5470 AssertComRCReturnRC(autoCaller.rc());
5471
5472 /* protect mpUVM (if not NULL) */
5473 AutoVMCallerWeak autoVMCaller(this);
5474 if (FAILED(autoVMCaller.rc()))
5475 return autoVMCaller.rc();
5476
5477 /* Note: validity of mVMMDev which is bound to uninit() is guaranteed by
5478 * autoVMCaller, so there is no need to hold a lock of this */
5479
5480 return doEnumerateGuestProperties(aPatterns, ComSafeArrayOutArg(aNames),
5481 ComSafeArrayOutArg(aValues),
5482 ComSafeArrayOutArg(aTimestamps),
5483 ComSafeArrayOutArg(aFlags));
5484#endif /* VBOX_WITH_GUEST_PROPS */
5485}
5486
5487
5488/*
5489 * Internal: helper function for connecting progress reporting
5490 */
5491static int onlineMergeMediumProgress(void *pvUser, unsigned uPercentage)
5492{
5493 HRESULT rc = S_OK;
5494 IProgress *pProgress = static_cast<IProgress *>(pvUser);
5495 if (pProgress)
5496 rc = pProgress->SetCurrentOperationProgress(uPercentage);
5497 return SUCCEEDED(rc) ? VINF_SUCCESS : VERR_GENERAL_FAILURE;
5498}
5499
5500/**
5501 * @note Temporarily locks this object for writing. bird: And/or reading?
5502 */
5503HRESULT Console::onlineMergeMedium(IMediumAttachment *aMediumAttachment,
5504 ULONG aSourceIdx, ULONG aTargetIdx,
5505 IMedium *aSource, IMedium *aTarget,
5506 BOOL aMergeForward,
5507 IMedium *aParentForTarget,
5508 ComSafeArrayIn(IMedium *, aChildrenToReparent),
5509 IProgress *aProgress)
5510{
5511 AutoCaller autoCaller(this);
5512 AssertComRCReturnRC(autoCaller.rc());
5513
5514 HRESULT rc = S_OK;
5515 int vrc = VINF_SUCCESS;
5516
5517 /* Get the VM - must be done before the read-locking. */
5518 SafeVMPtr ptrVM(this);
5519 if (!ptrVM.isOk())
5520 return ptrVM.rc();
5521
5522 /* We will need to release the lock before doing the actual merge */
5523 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5524
5525 /* paranoia - we don't want merges to happen while teleporting etc. */
5526 switch (mMachineState)
5527 {
5528 case MachineState_DeletingSnapshotOnline:
5529 case MachineState_DeletingSnapshotPaused:
5530 break;
5531
5532 default:
5533 return setInvalidMachineStateError();
5534 }
5535
5536 /** @todo AssertComRC -> AssertComRCReturn! Could potentially end up
5537 * using uninitialized variables here. */
5538 BOOL fBuiltinIOCache;
5539 rc = mMachine->COMGETTER(IOCacheEnabled)(&fBuiltinIOCache);
5540 AssertComRC(rc);
5541 SafeIfaceArray<IStorageController> ctrls;
5542 rc = mMachine->COMGETTER(StorageControllers)(ComSafeArrayAsOutParam(ctrls));
5543 AssertComRC(rc);
5544 LONG lDev;
5545 rc = aMediumAttachment->COMGETTER(Device)(&lDev);
5546 AssertComRC(rc);
5547 LONG lPort;
5548 rc = aMediumAttachment->COMGETTER(Port)(&lPort);
5549 AssertComRC(rc);
5550 IMedium *pMedium;
5551 rc = aMediumAttachment->COMGETTER(Medium)(&pMedium);
5552 AssertComRC(rc);
5553 Bstr mediumLocation;
5554 if (pMedium)
5555 {
5556 rc = pMedium->COMGETTER(Location)(mediumLocation.asOutParam());
5557 AssertComRC(rc);
5558 }
5559
5560 Bstr attCtrlName;
5561 rc = aMediumAttachment->COMGETTER(Controller)(attCtrlName.asOutParam());
5562 AssertComRC(rc);
5563 ComPtr<IStorageController> pStorageController;
5564 for (size_t i = 0; i < ctrls.size(); ++i)
5565 {
5566 Bstr ctrlName;
5567 rc = ctrls[i]->COMGETTER(Name)(ctrlName.asOutParam());
5568 AssertComRC(rc);
5569 if (attCtrlName == ctrlName)
5570 {
5571 pStorageController = ctrls[i];
5572 break;
5573 }
5574 }
5575 if (pStorageController.isNull())
5576 return setError(E_FAIL,
5577 tr("Could not find storage controller '%ls'"),
5578 attCtrlName.raw());
5579
5580 StorageControllerType_T enmCtrlType;
5581 rc = pStorageController->COMGETTER(ControllerType)(&enmCtrlType);
5582 AssertComRC(rc);
5583 const char *pcszDevice = convertControllerTypeToDev(enmCtrlType);
5584
5585 StorageBus_T enmBus;
5586 rc = pStorageController->COMGETTER(Bus)(&enmBus);
5587 AssertComRC(rc);
5588 ULONG uInstance;
5589 rc = pStorageController->COMGETTER(Instance)(&uInstance);
5590 AssertComRC(rc);
5591 BOOL fUseHostIOCache;
5592 rc = pStorageController->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
5593 AssertComRC(rc);
5594
5595 unsigned uLUN;
5596 rc = Console::convertBusPortDeviceToLun(enmBus, lPort, lDev, uLUN);
5597 AssertComRCReturnRC(rc);
5598
5599 alock.release();
5600
5601 /* Pause the VM, as it might have pending IO on this drive */
5602 VMSTATE enmVMState = VMR3GetStateU(ptrVM.rawUVM());
5603 if (mMachineState == MachineState_DeletingSnapshotOnline)
5604 {
5605 LogFlowFunc(("Suspending the VM...\n"));
5606 /* disable the callback to prevent Console-level state change */
5607 mVMStateChangeCallbackDisabled = true;
5608 int vrc2 = VMR3Suspend(ptrVM.rawUVM(), VMSUSPENDREASON_RECONFIG);
5609 mVMStateChangeCallbackDisabled = false;
5610 AssertRCReturn(vrc2, E_FAIL);
5611 }
5612
5613 vrc = VMR3ReqCallWaitU(ptrVM.rawUVM(),
5614 VMCPUID_ANY,
5615 (PFNRT)reconfigureMediumAttachment,
5616 13,
5617 this,
5618 ptrVM.rawUVM(),
5619 pcszDevice,
5620 uInstance,
5621 enmBus,
5622 fUseHostIOCache,
5623 fBuiltinIOCache,
5624 true /* fSetupMerge */,
5625 aSourceIdx,
5626 aTargetIdx,
5627 aMediumAttachment,
5628 mMachineState,
5629 &rc);
5630 /* error handling is after resuming the VM */
5631
5632 if (mMachineState == MachineState_DeletingSnapshotOnline)
5633 {
5634 LogFlowFunc(("Resuming the VM...\n"));
5635 /* disable the callback to prevent Console-level state change */
5636 mVMStateChangeCallbackDisabled = true;
5637 int vrc2 = VMR3Resume(ptrVM.rawUVM(), VMRESUMEREASON_RECONFIG);
5638 mVMStateChangeCallbackDisabled = false;
5639 if (RT_FAILURE(vrc2))
5640 {
5641 /* too bad, we failed. try to sync the console state with the VMM state */
5642 AssertLogRelRC(vrc2);
5643 vmstateChangeCallback(ptrVM.rawUVM(), VMSTATE_SUSPENDED, enmVMState, this);
5644 }
5645 }
5646
5647 if (RT_FAILURE(vrc))
5648 return setError(E_FAIL, tr("%Rrc"), vrc);
5649 if (FAILED(rc))
5650 return rc;
5651
5652 PPDMIBASE pIBase = NULL;
5653 PPDMIMEDIA pIMedium = NULL;
5654 vrc = PDMR3QueryDriverOnLun(ptrVM.rawUVM(), pcszDevice, uInstance, uLUN, "VD", &pIBase);
5655 if (RT_SUCCESS(vrc))
5656 {
5657 if (pIBase)
5658 {
5659 pIMedium = (PPDMIMEDIA)pIBase->pfnQueryInterface(pIBase, PDMIMEDIA_IID);
5660 if (!pIMedium)
5661 return setError(E_FAIL, tr("could not query medium interface of controller"));
5662 }
5663 else
5664 return setError(E_FAIL, tr("could not query base interface of controller"));
5665 }
5666
5667 /* Finally trigger the merge. */
5668 vrc = pIMedium->pfnMerge(pIMedium, onlineMergeMediumProgress, aProgress);
5669 if (RT_FAILURE(vrc))
5670 return setError(E_FAIL, tr("Failed to perform an online medium merge (%Rrc)"), vrc);
5671
5672 /* Pause the VM, as it might have pending IO on this drive */
5673 enmVMState = VMR3GetStateU(ptrVM.rawUVM());
5674 if (mMachineState == MachineState_DeletingSnapshotOnline)
5675 {
5676 LogFlowFunc(("Suspending the VM...\n"));
5677 /* disable the callback to prevent Console-level state change */
5678 mVMStateChangeCallbackDisabled = true;
5679 int vrc2 = VMR3Suspend(ptrVM.rawUVM(), VMSUSPENDREASON_RECONFIG);
5680 mVMStateChangeCallbackDisabled = false;
5681 AssertRCReturn(vrc2, E_FAIL);
5682 }
5683
5684 /* Update medium chain and state now, so that the VM can continue. */
5685 rc = mControl->FinishOnlineMergeMedium(aMediumAttachment, aSource, aTarget,
5686 aMergeForward, aParentForTarget,
5687 ComSafeArrayInArg(aChildrenToReparent));
5688
5689 vrc = VMR3ReqCallWaitU(ptrVM.rawUVM(),
5690 VMCPUID_ANY,
5691 (PFNRT)reconfigureMediumAttachment,
5692 13,
5693 this,
5694 ptrVM.rawUVM(),
5695 pcszDevice,
5696 uInstance,
5697 enmBus,
5698 fUseHostIOCache,
5699 fBuiltinIOCache,
5700 false /* fSetupMerge */,
5701 0 /* uMergeSource */,
5702 0 /* uMergeTarget */,
5703 aMediumAttachment,
5704 mMachineState,
5705 &rc);
5706 /* error handling is after resuming the VM */
5707
5708 if (mMachineState == MachineState_DeletingSnapshotOnline)
5709 {
5710 LogFlowFunc(("Resuming the VM...\n"));
5711 /* disable the callback to prevent Console-level state change */
5712 mVMStateChangeCallbackDisabled = true;
5713 int vrc2 = VMR3Resume(ptrVM.rawUVM(), VMRESUMEREASON_RECONFIG);
5714 mVMStateChangeCallbackDisabled = false;
5715 AssertRC(vrc2);
5716 if (RT_FAILURE(vrc2))
5717 {
5718 /* too bad, we failed. try to sync the console state with the VMM state */
5719 vmstateChangeCallback(ptrVM.rawUVM(), VMSTATE_SUSPENDED, enmVMState, this);
5720 }
5721 }
5722
5723 if (RT_FAILURE(vrc))
5724 return setError(E_FAIL, tr("%Rrc"), vrc);
5725 if (FAILED(rc))
5726 return rc;
5727
5728 return rc;
5729}
5730
5731
5732/**
5733 * Merely passes the call to Guest::enableVMMStatistics().
5734 */
5735void Console::enableVMMStatistics(BOOL aEnable)
5736{
5737 if (mGuest)
5738 mGuest->enableVMMStatistics(aEnable);
5739}
5740
5741/**
5742 * Worker for Console::Pause and internal entry point for pausing a VM for
5743 * a specific reason.
5744 */
5745HRESULT Console::pause(Reason_T aReason)
5746{
5747 LogFlowThisFuncEnter();
5748
5749 AutoCaller autoCaller(this);
5750 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5751
5752 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5753
5754 switch (mMachineState)
5755 {
5756 case MachineState_Running:
5757 case MachineState_Teleporting:
5758 case MachineState_LiveSnapshotting:
5759 break;
5760
5761 case MachineState_Paused:
5762 case MachineState_TeleportingPausedVM:
5763 case MachineState_Saving:
5764 return setError(VBOX_E_INVALID_VM_STATE, tr("Already paused"));
5765
5766 default:
5767 return setInvalidMachineStateError();
5768 }
5769
5770 /* get the VM handle. */
5771 SafeVMPtr ptrVM(this);
5772 if (!ptrVM.isOk())
5773 return ptrVM.rc();
5774
5775 /* release the lock before a VMR3* call (EMT will call us back)! */
5776 alock.release();
5777
5778 LogFlowThisFunc(("Sending PAUSE request...\n"));
5779 if (aReason != Reason_Unspecified)
5780 LogRel(("Pausing VM execution, reason \"%s\"\n", Global::stringifyReason(aReason)));
5781
5782 /** @todo r=klaus make use of aReason */
5783 VMSUSPENDREASON enmReason = VMSUSPENDREASON_USER;
5784 if (aReason == Reason_HostSuspend)
5785 enmReason = VMSUSPENDREASON_HOST_SUSPEND;
5786 else if (aReason == Reason_HostBatteryLow)
5787 enmReason = VMSUSPENDREASON_HOST_BATTERY_LOW;
5788 int vrc = VMR3Suspend(ptrVM.rawUVM(), enmReason);
5789
5790 HRESULT hrc = S_OK;
5791 if (RT_FAILURE(vrc))
5792 hrc = setError(VBOX_E_VM_ERROR, tr("Could not suspend the machine execution (%Rrc)"), vrc);
5793
5794 LogFlowThisFunc(("hrc=%Rhrc\n", hrc));
5795 LogFlowThisFuncLeave();
5796 return hrc;
5797}
5798
5799/**
5800 * Worker for Console::Resume and internal entry point for resuming a VM for
5801 * a specific reason.
5802 */
5803HRESULT Console::resume(Reason_T aReason)
5804{
5805 LogFlowThisFuncEnter();
5806
5807 AutoCaller autoCaller(this);
5808 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5809
5810 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5811
5812 if (mMachineState != MachineState_Paused)
5813 return setError(VBOX_E_INVALID_VM_STATE,
5814 tr("Cannot resume the machine as it is not paused (machine state: %s)"),
5815 Global::stringifyMachineState(mMachineState));
5816
5817 /* get the VM handle. */
5818 SafeVMPtr ptrVM(this);
5819 if (!ptrVM.isOk())
5820 return ptrVM.rc();
5821
5822 /* release the lock before a VMR3* call (EMT will call us back)! */
5823 alock.release();
5824
5825 LogFlowThisFunc(("Sending RESUME request...\n"));
5826 if (aReason != Reason_Unspecified)
5827 LogRel(("Resuming VM execution, reason \"%s\"\n", Global::stringifyReason(aReason)));
5828
5829 int vrc;
5830 if (VMR3GetStateU(ptrVM.rawUVM()) == VMSTATE_CREATED)
5831 {
5832#ifdef VBOX_WITH_EXTPACK
5833 vrc = mptrExtPackManager->callAllVmPowerOnHooks(this, VMR3GetVM(ptrVM.rawUVM()));
5834#else
5835 vrc = VINF_SUCCESS;
5836#endif
5837 if (RT_SUCCESS(vrc))
5838 vrc = VMR3PowerOn(ptrVM.rawUVM()); /* (PowerUpPaused) */
5839 }
5840 else
5841 {
5842 VMRESUMEREASON enmReason = VMRESUMEREASON_USER;
5843 if (aReason == Reason_HostResume)
5844 enmReason = VMRESUMEREASON_HOST_RESUME;
5845 vrc = VMR3Resume(ptrVM.rawUVM(), enmReason);
5846 }
5847
5848 HRESULT rc = RT_SUCCESS(vrc) ? S_OK :
5849 setError(VBOX_E_VM_ERROR,
5850 tr("Could not resume the machine execution (%Rrc)"),
5851 vrc);
5852
5853 LogFlowThisFunc(("rc=%Rhrc\n", rc));
5854 LogFlowThisFuncLeave();
5855 return rc;
5856}
5857
5858/**
5859 * Worker for Console::SaveState and internal entry point for saving state of
5860 * a VM for a specific reason.
5861 */
5862HRESULT Console::saveState(Reason_T aReason, IProgress **aProgress)
5863{
5864 LogFlowThisFuncEnter();
5865
5866 CheckComArgOutPointerValid(aProgress);
5867
5868 AutoCaller autoCaller(this);
5869 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5870
5871 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5872
5873 LogFlowThisFunc(("mMachineState=%d\n", mMachineState));
5874 if ( mMachineState != MachineState_Running
5875 && mMachineState != MachineState_Paused)
5876 {
5877 return setError(VBOX_E_INVALID_VM_STATE,
5878 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
5879 Global::stringifyMachineState(mMachineState));
5880 }
5881
5882 if (aReason != Reason_Unspecified)
5883 LogRel(("Saving state of VM, reason \"%s\"\n", Global::stringifyReason(aReason)));
5884
5885 /* memorize the current machine state */
5886 MachineState_T lastMachineState = mMachineState;
5887
5888 if (mMachineState == MachineState_Running)
5889 {
5890 /* get the VM handle. */
5891 SafeVMPtr ptrVM(this);
5892 if (!ptrVM.isOk())
5893 return ptrVM.rc();
5894
5895 /* release the lock before a VMR3* call (EMT will call us back)! */
5896 alock.release();
5897 VMSUSPENDREASON enmReason = VMSUSPENDREASON_USER;
5898 if (aReason == Reason_HostSuspend)
5899 enmReason = VMSUSPENDREASON_HOST_SUSPEND;
5900 else if (aReason == Reason_HostBatteryLow)
5901 enmReason = VMSUSPENDREASON_HOST_BATTERY_LOW;
5902 int vrc = VMR3Suspend(ptrVM.rawUVM(), enmReason);
5903 alock.acquire();
5904
5905 HRESULT hrc = S_OK;
5906 if (RT_FAILURE(vrc))
5907 hrc = setError(VBOX_E_VM_ERROR, tr("Could not suspend the machine execution (%Rrc)"), vrc);
5908 if (FAILED(hrc))
5909 return hrc;
5910 }
5911
5912 HRESULT rc = S_OK;
5913 bool fBeganSavingState = false;
5914 bool fTaskCreationFailed = false;
5915
5916 do
5917 {
5918 ComPtr<IProgress> pProgress;
5919 Bstr stateFilePath;
5920
5921 /*
5922 * request a saved state file path from the server
5923 * (this will set the machine state to Saving on the server to block
5924 * others from accessing this machine)
5925 */
5926 rc = mControl->BeginSavingState(pProgress.asOutParam(),
5927 stateFilePath.asOutParam());
5928 if (FAILED(rc))
5929 break;
5930
5931 fBeganSavingState = true;
5932
5933 /* sync the state with the server */
5934 setMachineStateLocally(MachineState_Saving);
5935
5936 /* ensure the directory for the saved state file exists */
5937 {
5938 Utf8Str dir = stateFilePath;
5939 dir.stripFilename();
5940 if (!RTDirExists(dir.c_str()))
5941 {
5942 int vrc = RTDirCreateFullPath(dir.c_str(), 0700);
5943 if (RT_FAILURE(vrc))
5944 {
5945 rc = setError(VBOX_E_FILE_ERROR,
5946 tr("Could not create a directory '%s' to save the state to (%Rrc)"),
5947 dir.c_str(), vrc);
5948 break;
5949 }
5950 }
5951 }
5952
5953 /* Create a task object early to ensure mpUVM protection is successful. */
5954 std::auto_ptr<VMSaveTask> task(new VMSaveTask(this, pProgress,
5955 stateFilePath,
5956 lastMachineState,
5957 aReason));
5958 rc = task->rc();
5959 /*
5960 * If we fail here it means a PowerDown() call happened on another
5961 * thread while we were doing Pause() (which releases the Console lock).
5962 * We assign PowerDown() a higher precedence than SaveState(),
5963 * therefore just return the error to the caller.
5964 */
5965 if (FAILED(rc))
5966 {
5967 fTaskCreationFailed = true;
5968 break;
5969 }
5970
5971 /* create a thread to wait until the VM state is saved */
5972 int vrc = RTThreadCreate(NULL, Console::saveStateThread, (void *)task.get(),
5973 0, RTTHREADTYPE_MAIN_WORKER, 0, "VMSave");
5974 if (RT_FAILURE(vrc))
5975 {
5976 rc = setError(E_FAIL, "Could not create VMSave thread (%Rrc)", vrc);
5977 break;
5978 }
5979
5980 /* task is now owned by saveStateThread(), so release it */
5981 task.release();
5982
5983 /* return the progress to the caller */
5984 pProgress.queryInterfaceTo(aProgress);
5985 } while (0);
5986
5987 if (FAILED(rc) && !fTaskCreationFailed)
5988 {
5989 /* preserve existing error info */
5990 ErrorInfoKeeper eik;
5991
5992 if (fBeganSavingState)
5993 {
5994 /*
5995 * cancel the requested save state procedure.
5996 * This will reset the machine state to the state it had right
5997 * before calling mControl->BeginSavingState().
5998 */
5999 mControl->EndSavingState(eik.getResultCode(), eik.getText().raw());
6000 }
6001
6002 if (lastMachineState == MachineState_Running)
6003 {
6004 /* restore the paused state if appropriate */
6005 setMachineStateLocally(MachineState_Paused);
6006 /* restore the running state if appropriate */
6007 SafeVMPtr ptrVM(this);
6008 if (ptrVM.isOk())
6009 {
6010 alock.release();
6011 VMR3Resume(ptrVM.rawUVM(), VMRESUMEREASON_STATE_RESTORED);
6012 alock.acquire();
6013 }
6014 }
6015 else
6016 setMachineStateLocally(lastMachineState);
6017 }
6018
6019 LogFlowThisFunc(("rc=%Rhrc\n", rc));
6020 LogFlowThisFuncLeave();
6021 return rc;
6022}
6023
6024/**
6025 * Gets called by Session::UpdateMachineState()
6026 * (IInternalSessionControl::updateMachineState()).
6027 *
6028 * Must be called only in certain cases (see the implementation).
6029 *
6030 * @note Locks this object for writing.
6031 */
6032HRESULT Console::updateMachineState(MachineState_T aMachineState)
6033{
6034 AutoCaller autoCaller(this);
6035 AssertComRCReturnRC(autoCaller.rc());
6036
6037 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6038
6039 AssertReturn( mMachineState == MachineState_Saving
6040 || mMachineState == MachineState_LiveSnapshotting
6041 || mMachineState == MachineState_RestoringSnapshot
6042 || mMachineState == MachineState_DeletingSnapshot
6043 || mMachineState == MachineState_DeletingSnapshotOnline
6044 || mMachineState == MachineState_DeletingSnapshotPaused
6045 , E_FAIL);
6046
6047 return setMachineStateLocally(aMachineState);
6048}
6049
6050#ifdef CONSOLE_WITH_EVENT_CACHE
6051/**
6052 * @note Locks this object for writing.
6053 */
6054#endif
6055void Console::onMousePointerShapeChange(bool fVisible, bool fAlpha,
6056 uint32_t xHot, uint32_t yHot,
6057 uint32_t width, uint32_t height,
6058 ComSafeArrayIn(BYTE,pShape))
6059{
6060#if 0
6061 LogFlowThisFuncEnter();
6062 LogFlowThisFunc(("fVisible=%d, fAlpha=%d, xHot = %d, yHot = %d, width=%d, height=%d, shape=%p\n",
6063 fVisible, fAlpha, xHot, yHot, width, height, pShape));
6064#endif
6065
6066 AutoCaller autoCaller(this);
6067 AssertComRCReturnVoid(autoCaller.rc());
6068
6069#ifdef CONSOLE_WITH_EVENT_CACHE
6070 {
6071 /* We need a write lock because we alter the cached callback data */
6072 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6073
6074 /* Save the callback arguments */
6075 mCallbackData.mpsc.visible = fVisible;
6076 mCallbackData.mpsc.alpha = fAlpha;
6077 mCallbackData.mpsc.xHot = xHot;
6078 mCallbackData.mpsc.yHot = yHot;
6079 mCallbackData.mpsc.width = width;
6080 mCallbackData.mpsc.height = height;
6081
6082 /* start with not valid */
6083 bool wasValid = mCallbackData.mpsc.valid;
6084 mCallbackData.mpsc.valid = false;
6085
6086 com::SafeArray<BYTE> aShape(ComSafeArrayInArg(pShape));
6087 if (aShape.size() != 0)
6088 mCallbackData.mpsc.shape.initFrom(aShape);
6089 else
6090 mCallbackData.mpsc.shape.resize(0);
6091 mCallbackData.mpsc.valid = true;
6092 }
6093#endif
6094
6095 fireMousePointerShapeChangedEvent(mEventSource, fVisible, fAlpha, xHot, yHot, width, height, ComSafeArrayInArg(pShape));
6096
6097#if 0
6098 LogFlowThisFuncLeave();
6099#endif
6100}
6101
6102#ifdef CONSOLE_WITH_EVENT_CACHE
6103/**
6104 * @note Locks this object for writing.
6105 */
6106#endif
6107void Console::onMouseCapabilityChange(BOOL supportsAbsolute, BOOL supportsRelative,
6108 BOOL supportsMT, BOOL needsHostCursor)
6109{
6110 LogFlowThisFunc(("supportsAbsolute=%d supportsRelative=%d needsHostCursor=%d\n",
6111 supportsAbsolute, supportsRelative, needsHostCursor));
6112
6113 AutoCaller autoCaller(this);
6114 AssertComRCReturnVoid(autoCaller.rc());
6115
6116#ifdef CONSOLE_WITH_EVENT_CACHE
6117 {
6118 /* We need a write lock because we alter the cached callback data */
6119 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6120
6121 /* save the callback arguments */
6122 mCallbackData.mcc.supportsAbsolute = supportsAbsolute;
6123 mCallbackData.mcc.supportsRelative = supportsRelative;
6124 mCallbackData.mcc.needsHostCursor = needsHostCursor;
6125 mCallbackData.mcc.valid = true;
6126 }
6127#endif
6128
6129 fireMouseCapabilityChangedEvent(mEventSource, supportsAbsolute, supportsRelative, supportsMT, needsHostCursor);
6130}
6131
6132void Console::onStateChange(MachineState_T machineState)
6133{
6134 AutoCaller autoCaller(this);
6135 AssertComRCReturnVoid(autoCaller.rc());
6136 fireStateChangedEvent(mEventSource, machineState);
6137}
6138
6139void Console::onAdditionsStateChange()
6140{
6141 AutoCaller autoCaller(this);
6142 AssertComRCReturnVoid(autoCaller.rc());
6143
6144 fireAdditionsStateChangedEvent(mEventSource);
6145}
6146
6147/**
6148 * @remarks This notification only is for reporting an incompatible
6149 * Guest Additions interface, *not* the Guest Additions version!
6150 *
6151 * The user will be notified inside the guest if new Guest
6152 * Additions are available (via VBoxTray/VBoxClient).
6153 */
6154void Console::onAdditionsOutdated()
6155{
6156 AutoCaller autoCaller(this);
6157 AssertComRCReturnVoid(autoCaller.rc());
6158
6159 /** @todo implement this */
6160}
6161
6162#ifdef CONSOLE_WITH_EVENT_CACHE
6163/**
6164 * @note Locks this object for writing.
6165 */
6166#endif
6167void Console::onKeyboardLedsChange(bool fNumLock, bool fCapsLock, bool fScrollLock)
6168{
6169 AutoCaller autoCaller(this);
6170 AssertComRCReturnVoid(autoCaller.rc());
6171
6172#ifdef CONSOLE_WITH_EVENT_CACHE
6173 {
6174 /* We need a write lock because we alter the cached callback data */
6175 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6176
6177 /* save the callback arguments */
6178 mCallbackData.klc.numLock = fNumLock;
6179 mCallbackData.klc.capsLock = fCapsLock;
6180 mCallbackData.klc.scrollLock = fScrollLock;
6181 mCallbackData.klc.valid = true;
6182 }
6183#endif
6184
6185 fireKeyboardLedsChangedEvent(mEventSource, fNumLock, fCapsLock, fScrollLock);
6186}
6187
6188void Console::onUSBDeviceStateChange(IUSBDevice *aDevice, bool aAttached,
6189 IVirtualBoxErrorInfo *aError)
6190{
6191 AutoCaller autoCaller(this);
6192 AssertComRCReturnVoid(autoCaller.rc());
6193
6194 fireUSBDeviceStateChangedEvent(mEventSource, aDevice, aAttached, aError);
6195}
6196
6197void Console::onRuntimeError(BOOL aFatal, IN_BSTR aErrorID, IN_BSTR aMessage)
6198{
6199 AutoCaller autoCaller(this);
6200 AssertComRCReturnVoid(autoCaller.rc());
6201
6202 fireRuntimeErrorEvent(mEventSource, aFatal, aErrorID, aMessage);
6203}
6204
6205HRESULT Console::onShowWindow(BOOL aCheck, BOOL *aCanShow, LONG64 *aWinId)
6206{
6207 AssertReturn(aCanShow, E_POINTER);
6208 AssertReturn(aWinId, E_POINTER);
6209
6210 *aCanShow = FALSE;
6211 *aWinId = 0;
6212
6213 AutoCaller autoCaller(this);
6214 AssertComRCReturnRC(autoCaller.rc());
6215
6216 VBoxEventDesc evDesc;
6217 if (aCheck)
6218 {
6219 evDesc.init(mEventSource, VBoxEventType_OnCanShowWindow);
6220 BOOL fDelivered = evDesc.fire(5000); /* Wait up to 5 secs for delivery */
6221 //Assert(fDelivered);
6222 if (fDelivered)
6223 {
6224 ComPtr<IEvent> pEvent;
6225 evDesc.getEvent(pEvent.asOutParam());
6226 // bit clumsy
6227 ComPtr<ICanShowWindowEvent> pCanShowEvent = pEvent;
6228 if (pCanShowEvent)
6229 {
6230 BOOL fVetoed = FALSE;
6231 pCanShowEvent->IsVetoed(&fVetoed);
6232 *aCanShow = !fVetoed;
6233 }
6234 else
6235 {
6236 AssertFailed();
6237 *aCanShow = TRUE;
6238 }
6239 }
6240 else
6241 *aCanShow = TRUE;
6242 }
6243 else
6244 {
6245 evDesc.init(mEventSource, VBoxEventType_OnShowWindow, INT64_C(0));
6246 BOOL fDelivered = evDesc.fire(5000); /* Wait up to 5 secs for delivery */
6247 //Assert(fDelivered);
6248 if (fDelivered)
6249 {
6250 ComPtr<IEvent> pEvent;
6251 evDesc.getEvent(pEvent.asOutParam());
6252 ComPtr<IShowWindowEvent> pShowEvent = pEvent;
6253 if (pShowEvent)
6254 {
6255 LONG64 iEvWinId = 0;
6256 pShowEvent->COMGETTER(WinId)(&iEvWinId);
6257 if (iEvWinId != 0 && *aWinId == 0)
6258 *aWinId = iEvWinId;
6259 }
6260 else
6261 AssertFailed();
6262 }
6263 }
6264
6265 return S_OK;
6266}
6267
6268// private methods
6269////////////////////////////////////////////////////////////////////////////////
6270
6271/**
6272 * Increases the usage counter of the mpUVM pointer.
6273 *
6274 * Guarantees that VMR3Destroy() will not be called on it at least until
6275 * releaseVMCaller() is called.
6276 *
6277 * If this method returns a failure, the caller is not allowed to use mpUVM and
6278 * may return the failed result code to the upper level. This method sets the
6279 * extended error info on failure if \a aQuiet is false.
6280 *
6281 * Setting \a aQuiet to true is useful for methods that don't want to return
6282 * the failed result code to the caller when this method fails (e.g. need to
6283 * silently check for the mpUVM availability).
6284 *
6285 * When mpUVM is NULL but \a aAllowNullVM is true, a corresponding error will be
6286 * returned instead of asserting. Having it false is intended as a sanity check
6287 * for methods that have checked mMachineState and expect mpUVM *NOT* to be
6288 * NULL.
6289 *
6290 * @param aQuiet true to suppress setting error info
6291 * @param aAllowNullVM true to accept mpUVM being NULL and return a failure
6292 * (otherwise this method will assert if mpUVM is NULL)
6293 *
6294 * @note Locks this object for writing.
6295 */
6296HRESULT Console::addVMCaller(bool aQuiet /* = false */,
6297 bool aAllowNullVM /* = false */)
6298{
6299 AutoCaller autoCaller(this);
6300 /** @todo Fix race during console/VM reference destruction, refer @bugref{6318}
6301 * comment 25. */
6302 if (FAILED(autoCaller.rc()))
6303 return autoCaller.rc();
6304
6305 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6306
6307 if (mVMDestroying)
6308 {
6309 /* powerDown() is waiting for all callers to finish */
6310 return aQuiet ? E_ACCESSDENIED : setError(E_ACCESSDENIED,
6311 tr("The virtual machine is being powered down"));
6312 }
6313
6314 if (mpUVM == NULL)
6315 {
6316 Assert(aAllowNullVM == true);
6317
6318 /* The machine is not powered up */
6319 return aQuiet ? E_ACCESSDENIED : setError(E_ACCESSDENIED,
6320 tr("The virtual machine is not powered up"));
6321 }
6322
6323 ++mVMCallers;
6324
6325 return S_OK;
6326}
6327
6328/**
6329 * Decreases the usage counter of the mpUVM pointer.
6330 *
6331 * Must always complete the addVMCaller() call after the mpUVM pointer is no
6332 * more necessary.
6333 *
6334 * @note Locks this object for writing.
6335 */
6336void Console::releaseVMCaller()
6337{
6338 AutoCaller autoCaller(this);
6339 AssertComRCReturnVoid(autoCaller.rc());
6340
6341 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6342
6343 AssertReturnVoid(mpUVM != NULL);
6344
6345 Assert(mVMCallers > 0);
6346 --mVMCallers;
6347
6348 if (mVMCallers == 0 && mVMDestroying)
6349 {
6350 /* inform powerDown() there are no more callers */
6351 RTSemEventSignal(mVMZeroCallersSem);
6352 }
6353}
6354
6355
6356HRESULT Console::safeVMPtrRetainer(PUVM *a_ppUVM, bool a_Quiet)
6357{
6358 *a_ppUVM = NULL;
6359
6360 AutoCaller autoCaller(this);
6361 AssertComRCReturnRC(autoCaller.rc());
6362 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6363
6364 /*
6365 * Repeat the checks done by addVMCaller.
6366 */
6367 if (mVMDestroying) /* powerDown() is waiting for all callers to finish */
6368 return a_Quiet
6369 ? E_ACCESSDENIED
6370 : setError(E_ACCESSDENIED, tr("The virtual machine is being powered down"));
6371 PUVM pUVM = mpUVM;
6372 if (!pUVM)
6373 return a_Quiet
6374 ? E_ACCESSDENIED
6375 : setError(E_ACCESSDENIED, tr("The virtual machine is powered off"));
6376
6377 /*
6378 * Retain a reference to the user mode VM handle and get the global handle.
6379 */
6380 uint32_t cRefs = VMR3RetainUVM(pUVM);
6381 if (cRefs == UINT32_MAX)
6382 return a_Quiet
6383 ? E_ACCESSDENIED
6384 : setError(E_ACCESSDENIED, tr("The virtual machine is powered off"));
6385
6386 /* done */
6387 *a_ppUVM = pUVM;
6388 return S_OK;
6389}
6390
6391void Console::safeVMPtrReleaser(PUVM *a_ppUVM)
6392{
6393 if (*a_ppUVM)
6394 VMR3ReleaseUVM(*a_ppUVM);
6395 *a_ppUVM = NULL;
6396}
6397
6398
6399/**
6400 * Initialize the release logging facility. In case something
6401 * goes wrong, there will be no release logging. Maybe in the future
6402 * we can add some logic to use different file names in this case.
6403 * Note that the logic must be in sync with Machine::DeleteSettings().
6404 */
6405HRESULT Console::consoleInitReleaseLog(const ComPtr<IMachine> aMachine)
6406{
6407 HRESULT hrc = S_OK;
6408
6409 Bstr logFolder;
6410 hrc = aMachine->COMGETTER(LogFolder)(logFolder.asOutParam());
6411 if (FAILED(hrc))
6412 return hrc;
6413
6414 Utf8Str logDir = logFolder;
6415
6416 /* make sure the Logs folder exists */
6417 Assert(logDir.length());
6418 if (!RTDirExists(logDir.c_str()))
6419 RTDirCreateFullPath(logDir.c_str(), 0700);
6420
6421 Utf8Str logFile = Utf8StrFmt("%s%cVBox.log",
6422 logDir.c_str(), RTPATH_DELIMITER);
6423 Utf8Str pngFile = Utf8StrFmt("%s%cVBox.png",
6424 logDir.c_str(), RTPATH_DELIMITER);
6425
6426 /*
6427 * Age the old log files
6428 * Rename .(n-1) to .(n), .(n-2) to .(n-1), ..., and the last log file to .1
6429 * Overwrite target files in case they exist.
6430 */
6431 ComPtr<IVirtualBox> pVirtualBox;
6432 aMachine->COMGETTER(Parent)(pVirtualBox.asOutParam());
6433 ComPtr<ISystemProperties> pSystemProperties;
6434 pVirtualBox->COMGETTER(SystemProperties)(pSystemProperties.asOutParam());
6435 ULONG cHistoryFiles = 3;
6436 pSystemProperties->COMGETTER(LogHistoryCount)(&cHistoryFiles);
6437 if (cHistoryFiles)
6438 {
6439 for (int i = cHistoryFiles-1; i >= 0; i--)
6440 {
6441 Utf8Str *files[] = { &logFile, &pngFile };
6442 Utf8Str oldName, newName;
6443
6444 for (unsigned int j = 0; j < RT_ELEMENTS(files); ++j)
6445 {
6446 if (i > 0)
6447 oldName = Utf8StrFmt("%s.%d", files[j]->c_str(), i);
6448 else
6449 oldName = *files[j];
6450 newName = Utf8StrFmt("%s.%d", files[j]->c_str(), i + 1);
6451 /* If the old file doesn't exist, delete the new file (if it
6452 * exists) to provide correct rotation even if the sequence is
6453 * broken */
6454 if ( RTFileRename(oldName.c_str(), newName.c_str(), RTFILEMOVE_FLAGS_REPLACE)
6455 == VERR_FILE_NOT_FOUND)
6456 RTFileDelete(newName.c_str());
6457 }
6458 }
6459 }
6460
6461 char szError[RTPATH_MAX + 128];
6462 int vrc = com::VBoxLogRelCreate("VM", logFile.c_str(),
6463 RTLOGFLAGS_PREFIX_TIME_PROG | RTLOGFLAGS_RESTRICT_GROUPS,
6464 "all all.restrict -default.restrict",
6465 "VBOX_RELEASE_LOG", RTLOGDEST_FILE,
6466 32768 /* cMaxEntriesPerGroup */,
6467 0 /* cHistory */, 0 /* uHistoryFileTime */,
6468 0 /* uHistoryFileSize */, szError, sizeof(szError));
6469 if (RT_FAILURE(vrc))
6470 hrc = setError(E_FAIL, tr("Failed to open release log (%s, %Rrc)"),
6471 szError, vrc);
6472
6473 /* If we've made any directory changes, flush the directory to increase
6474 the likelihood that the log file will be usable after a system panic.
6475
6476 Tip: Try 'export VBOX_RELEASE_LOG_FLAGS=flush' if the last bits of the log
6477 is missing. Just don't have too high hopes for this to help. */
6478 if (SUCCEEDED(hrc) || cHistoryFiles)
6479 RTDirFlush(logDir.c_str());
6480
6481 return hrc;
6482}
6483
6484/**
6485 * Common worker for PowerUp and PowerUpPaused.
6486 *
6487 * @returns COM status code.
6488 *
6489 * @param aProgress Where to return the progress object.
6490 * @param aPaused true if PowerUpPaused called.
6491 */
6492HRESULT Console::powerUp(IProgress **aProgress, bool aPaused)
6493{
6494
6495 LogFlowThisFuncEnter();
6496
6497 CheckComArgOutPointerValid(aProgress);
6498
6499 AutoCaller autoCaller(this);
6500 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6501
6502 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6503
6504 LogFlowThisFunc(("mMachineState=%d\n", mMachineState));
6505 HRESULT rc = S_OK;
6506 ComObjPtr<Progress> pPowerupProgress;
6507 bool fBeganPoweringUp = false;
6508
6509 LONG cOperations = 1;
6510 LONG ulTotalOperationsWeight = 1;
6511
6512 try
6513 {
6514
6515 if (Global::IsOnlineOrTransient(mMachineState))
6516 throw setError(VBOX_E_INVALID_VM_STATE,
6517 tr("The virtual machine is already running or busy (machine state: %s)"),
6518 Global::stringifyMachineState(mMachineState));
6519
6520 /* Set up release logging as early as possible after the check if
6521 * there is already a running VM which we shouldn't disturb. */
6522 rc = consoleInitReleaseLog(mMachine);
6523 if (FAILED(rc))
6524 throw rc;
6525
6526 /* test and clear the TeleporterEnabled property */
6527 BOOL fTeleporterEnabled;
6528 rc = mMachine->COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
6529 if (FAILED(rc))
6530 throw rc;
6531
6532#if 0 /** @todo we should save it afterwards, but that isn't necessarily a good idea. Find a better place for this (VBoxSVC). */
6533 if (fTeleporterEnabled)
6534 {
6535 rc = mMachine->COMSETTER(TeleporterEnabled)(FALSE);
6536 if (FAILED(rc))
6537 throw rc;
6538 }
6539#endif
6540
6541 /* test the FaultToleranceState property */
6542 FaultToleranceState_T enmFaultToleranceState;
6543 rc = mMachine->COMGETTER(FaultToleranceState)(&enmFaultToleranceState);
6544 if (FAILED(rc))
6545 throw rc;
6546 BOOL fFaultToleranceSyncEnabled = (enmFaultToleranceState == FaultToleranceState_Standby);
6547
6548 /* Create a progress object to track progress of this operation. Must
6549 * be done as early as possible (together with BeginPowerUp()) as this
6550 * is vital for communicating as much as possible early powerup
6551 * failure information to the API caller */
6552 pPowerupProgress.createObject();
6553 Bstr progressDesc;
6554 if (mMachineState == MachineState_Saved)
6555 progressDesc = tr("Restoring virtual machine");
6556 else if (fTeleporterEnabled)
6557 progressDesc = tr("Teleporting virtual machine");
6558 else if (fFaultToleranceSyncEnabled)
6559 progressDesc = tr("Fault Tolerance syncing of remote virtual machine");
6560 else
6561 progressDesc = tr("Starting virtual machine");
6562
6563 /* Check all types of shared folders and compose a single list */
6564 SharedFolderDataMap sharedFolders;
6565 {
6566 /* first, insert global folders */
6567 for (SharedFolderDataMap::const_iterator it = m_mapGlobalSharedFolders.begin();
6568 it != m_mapGlobalSharedFolders.end();
6569 ++it)
6570 {
6571 const SharedFolderData &d = it->second;
6572 sharedFolders[it->first] = d;
6573 }
6574
6575 /* second, insert machine folders */
6576 for (SharedFolderDataMap::const_iterator it = m_mapMachineSharedFolders.begin();
6577 it != m_mapMachineSharedFolders.end();
6578 ++it)
6579 {
6580 const SharedFolderData &d = it->second;
6581 sharedFolders[it->first] = d;
6582 }
6583
6584 /* third, insert console folders */
6585 for (SharedFolderMap::const_iterator it = m_mapSharedFolders.begin();
6586 it != m_mapSharedFolders.end();
6587 ++it)
6588 {
6589 SharedFolder *pSF = it->second;
6590 AutoCaller sfCaller(pSF);
6591 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
6592 sharedFolders[it->first] = SharedFolderData(pSF->getHostPath(),
6593 pSF->isWritable(),
6594 pSF->isAutoMounted());
6595 }
6596 }
6597
6598 Bstr savedStateFile;
6599
6600 /*
6601 * Saved VMs will have to prove that their saved states seem kosher.
6602 */
6603 if (mMachineState == MachineState_Saved)
6604 {
6605 rc = mMachine->COMGETTER(StateFilePath)(savedStateFile.asOutParam());
6606 if (FAILED(rc))
6607 throw rc;
6608 ComAssertRet(!savedStateFile.isEmpty(), E_FAIL);
6609 int vrc = SSMR3ValidateFile(Utf8Str(savedStateFile).c_str(), false /* fChecksumIt */);
6610 if (RT_FAILURE(vrc))
6611 throw setError(VBOX_E_FILE_ERROR,
6612 tr("VM cannot start because the saved state file '%ls' is invalid (%Rrc). Delete the saved state prior to starting the VM"),
6613 savedStateFile.raw(), vrc);
6614 }
6615
6616 /* Setup task object and thread to carry out the operaton
6617 * Asycnhronously */
6618 std::auto_ptr<VMPowerUpTask> task(new VMPowerUpTask(this, pPowerupProgress));
6619 ComAssertComRCRetRC(task->rc());
6620
6621 task->mConfigConstructor = configConstructor;
6622 task->mSharedFolders = sharedFolders;
6623 task->mStartPaused = aPaused;
6624 if (mMachineState == MachineState_Saved)
6625 task->mSavedStateFile = savedStateFile;
6626 task->mTeleporterEnabled = fTeleporterEnabled;
6627 task->mEnmFaultToleranceState = enmFaultToleranceState;
6628
6629 /* Reset differencing hard disks for which autoReset is true,
6630 * but only if the machine has no snapshots OR the current snapshot
6631 * is an OFFLINE snapshot; otherwise we would reset the current
6632 * differencing image of an ONLINE snapshot which contains the disk
6633 * state of the machine while it was previously running, but without
6634 * the corresponding machine state, which is equivalent to powering
6635 * off a running machine and not good idea
6636 */
6637 ComPtr<ISnapshot> pCurrentSnapshot;
6638 rc = mMachine->COMGETTER(CurrentSnapshot)(pCurrentSnapshot.asOutParam());
6639 if (FAILED(rc))
6640 throw rc;
6641
6642 BOOL fCurrentSnapshotIsOnline = false;
6643 if (pCurrentSnapshot)
6644 {
6645 rc = pCurrentSnapshot->COMGETTER(Online)(&fCurrentSnapshotIsOnline);
6646 if (FAILED(rc))
6647 throw rc;
6648 }
6649
6650 if (!fCurrentSnapshotIsOnline)
6651 {
6652 LogFlowThisFunc(("Looking for immutable images to reset\n"));
6653
6654 com::SafeIfaceArray<IMediumAttachment> atts;
6655 rc = mMachine->COMGETTER(MediumAttachments)(ComSafeArrayAsOutParam(atts));
6656 if (FAILED(rc))
6657 throw rc;
6658
6659 for (size_t i = 0;
6660 i < atts.size();
6661 ++i)
6662 {
6663 DeviceType_T devType;
6664 rc = atts[i]->COMGETTER(Type)(&devType);
6665 /** @todo later applies to floppies as well */
6666 if (devType == DeviceType_HardDisk)
6667 {
6668 ComPtr<IMedium> pMedium;
6669 rc = atts[i]->COMGETTER(Medium)(pMedium.asOutParam());
6670 if (FAILED(rc))
6671 throw rc;
6672
6673 /* needs autoreset? */
6674 BOOL autoReset = FALSE;
6675 rc = pMedium->COMGETTER(AutoReset)(&autoReset);
6676 if (FAILED(rc))
6677 throw rc;
6678
6679 if (autoReset)
6680 {
6681 ComPtr<IProgress> pResetProgress;
6682 rc = pMedium->Reset(pResetProgress.asOutParam());
6683 if (FAILED(rc))
6684 throw rc;
6685
6686 /* save for later use on the powerup thread */
6687 task->hardDiskProgresses.push_back(pResetProgress);
6688 }
6689 }
6690 }
6691 }
6692 else
6693 LogFlowThisFunc(("Machine has a current snapshot which is online, skipping immutable images reset\n"));
6694
6695 /* setup task object and thread to carry out the operation
6696 * asynchronously */
6697
6698#ifdef VBOX_WITH_EXTPACK
6699 mptrExtPackManager->dumpAllToReleaseLog();
6700#endif
6701
6702#ifdef RT_OS_SOLARIS
6703 /* setup host core dumper for the VM */
6704 Bstr value;
6705 HRESULT hrc = mMachine->GetExtraData(Bstr("VBoxInternal2/CoreDumpEnabled").raw(), value.asOutParam());
6706 if (SUCCEEDED(hrc) && value == "1")
6707 {
6708 Bstr coreDumpDir, coreDumpReplaceSys, coreDumpLive;
6709 mMachine->GetExtraData(Bstr("VBoxInternal2/CoreDumpDir").raw(), coreDumpDir.asOutParam());
6710 mMachine->GetExtraData(Bstr("VBoxInternal2/CoreDumpReplaceSystemDump").raw(), coreDumpReplaceSys.asOutParam());
6711 mMachine->GetExtraData(Bstr("VBoxInternal2/CoreDumpLive").raw(), coreDumpLive.asOutParam());
6712
6713 uint32_t fCoreFlags = 0;
6714 if ( coreDumpReplaceSys.isEmpty() == false
6715 && Utf8Str(coreDumpReplaceSys).toUInt32() == 1)
6716 fCoreFlags |= RTCOREDUMPER_FLAGS_REPLACE_SYSTEM_DUMP;
6717
6718 if ( coreDumpLive.isEmpty() == false
6719 && Utf8Str(coreDumpLive).toUInt32() == 1)
6720 fCoreFlags |= RTCOREDUMPER_FLAGS_LIVE_CORE;
6721
6722 Utf8Str strDumpDir(coreDumpDir);
6723 const char *pszDumpDir = strDumpDir.c_str();
6724 if ( pszDumpDir
6725 && *pszDumpDir == '\0')
6726 pszDumpDir = NULL;
6727
6728 int vrc;
6729 if ( pszDumpDir
6730 && !RTDirExists(pszDumpDir))
6731 {
6732 /*
6733 * Try create the directory.
6734 */
6735 vrc = RTDirCreateFullPath(pszDumpDir, 0700);
6736 if (RT_FAILURE(vrc))
6737 throw setError(E_FAIL, "Failed to setup CoreDumper. Couldn't create dump directory '%s' (%Rrc)\n", pszDumpDir, vrc);
6738 }
6739
6740 vrc = RTCoreDumperSetup(pszDumpDir, fCoreFlags);
6741 if (RT_FAILURE(vrc))
6742 throw setError(E_FAIL, "Failed to setup CoreDumper (%Rrc)", vrc);
6743 else
6744 LogRel(("CoreDumper setup successful. pszDumpDir=%s fFlags=%#x\n", pszDumpDir ? pszDumpDir : ".", fCoreFlags));
6745 }
6746#endif
6747
6748
6749 // If there is immutable drive the process that.
6750 VMPowerUpTask::ProgressList progresses(task->hardDiskProgresses);
6751 if (aProgress && progresses.size() > 0){
6752
6753 for (VMPowerUpTask::ProgressList::const_iterator it = progresses.begin(); it != progresses.end(); ++it)
6754 {
6755 ++cOperations;
6756 ulTotalOperationsWeight += 1;
6757 }
6758 rc = pPowerupProgress->init(static_cast<IConsole *>(this),
6759 progressDesc.raw(),
6760 TRUE, // Cancelable
6761 cOperations,
6762 ulTotalOperationsWeight,
6763 Bstr(tr("Starting Hard Disk operations")).raw(),
6764 1,
6765 NULL);
6766 AssertComRCReturnRC(rc);
6767 }
6768 else if ( mMachineState == MachineState_Saved
6769 || (!fTeleporterEnabled && !fFaultToleranceSyncEnabled))
6770 {
6771 rc = pPowerupProgress->init(static_cast<IConsole *>(this),
6772 progressDesc.raw(),
6773 FALSE /* aCancelable */);
6774 }
6775 else if (fTeleporterEnabled)
6776 {
6777 rc = pPowerupProgress->init(static_cast<IConsole *>(this),
6778 progressDesc.raw(),
6779 TRUE /* aCancelable */,
6780 3 /* cOperations */,
6781 10 /* ulTotalOperationsWeight */,
6782 Bstr(tr("Teleporting virtual machine")).raw(),
6783 1 /* ulFirstOperationWeight */,
6784 NULL);
6785 }
6786 else if (fFaultToleranceSyncEnabled)
6787 {
6788 rc = pPowerupProgress->init(static_cast<IConsole *>(this),
6789 progressDesc.raw(),
6790 TRUE /* aCancelable */,
6791 3 /* cOperations */,
6792 10 /* ulTotalOperationsWeight */,
6793 Bstr(tr("Fault Tolerance syncing of remote virtual machine")).raw(),
6794 1 /* ulFirstOperationWeight */,
6795 NULL);
6796 }
6797
6798 if (FAILED(rc))
6799 throw rc;
6800
6801 /* Tell VBoxSVC and Machine about the progress object so they can
6802 combine/proxy it to any openRemoteSession caller. */
6803 LogFlowThisFunc(("Calling BeginPowerUp...\n"));
6804 rc = mControl->BeginPowerUp(pPowerupProgress);
6805 if (FAILED(rc))
6806 {
6807 LogFlowThisFunc(("BeginPowerUp failed\n"));
6808 throw rc;
6809 }
6810 fBeganPoweringUp = true;
6811
6812 LogFlowThisFunc(("Checking if canceled...\n"));
6813 BOOL fCanceled;
6814 rc = pPowerupProgress->COMGETTER(Canceled)(&fCanceled);
6815 if (FAILED(rc))
6816 throw rc;
6817
6818 if (fCanceled)
6819 {
6820 LogFlowThisFunc(("Canceled in BeginPowerUp\n"));
6821 throw setError(E_FAIL, tr("Powerup was canceled"));
6822 }
6823 LogFlowThisFunc(("Not canceled yet.\n"));
6824
6825 /** @todo this code prevents starting a VM with unavailable bridged
6826 * networking interface. The only benefit is a slightly better error
6827 * message, which should be moved to the driver code. This is the
6828 * only reason why I left the code in for now. The driver allows
6829 * unavailable bridged networking interfaces in certain circumstances,
6830 * and this is sabotaged by this check. The VM will initially have no
6831 * network connectivity, but the user can fix this at runtime. */
6832#if 0
6833 /* the network cards will undergo a quick consistency check */
6834 for (ULONG slot = 0;
6835 slot < maxNetworkAdapters;
6836 ++slot)
6837 {
6838 ComPtr<INetworkAdapter> pNetworkAdapter;
6839 mMachine->GetNetworkAdapter(slot, pNetworkAdapter.asOutParam());
6840 BOOL enabled = FALSE;
6841 pNetworkAdapter->COMGETTER(Enabled)(&enabled);
6842 if (!enabled)
6843 continue;
6844
6845 NetworkAttachmentType_T netattach;
6846 pNetworkAdapter->COMGETTER(AttachmentType)(&netattach);
6847 switch (netattach)
6848 {
6849 case NetworkAttachmentType_Bridged:
6850 {
6851 /* a valid host interface must have been set */
6852 Bstr hostif;
6853 pNetworkAdapter->COMGETTER(HostInterface)(hostif.asOutParam());
6854 if (hostif.isEmpty())
6855 {
6856 throw setError(VBOX_E_HOST_ERROR,
6857 tr("VM cannot start because host interface networking requires a host interface name to be set"));
6858 }
6859 ComPtr<IVirtualBox> pVirtualBox;
6860 mMachine->COMGETTER(Parent)(pVirtualBox.asOutParam());
6861 ComPtr<IHost> pHost;
6862 pVirtualBox->COMGETTER(Host)(pHost.asOutParam());
6863 ComPtr<IHostNetworkInterface> pHostInterface;
6864 if (!SUCCEEDED(pHost->FindHostNetworkInterfaceByName(hostif.raw(),
6865 pHostInterface.asOutParam())))
6866 {
6867 throw setError(VBOX_E_HOST_ERROR,
6868 tr("VM cannot start because the host interface '%ls' does not exist"),
6869 hostif.raw());
6870 }
6871 break;
6872 }
6873 default:
6874 break;
6875 }
6876 }
6877#endif // 0
6878
6879 /* Read console data stored in the saved state file (if not yet done) */
6880 rc = loadDataFromSavedState();
6881 if (FAILED(rc))
6882 throw rc;
6883
6884 /* setup task object and thread to carry out the operation
6885 * asynchronously */
6886 if (aProgress){
6887 rc = pPowerupProgress.queryInterfaceTo(aProgress);
6888 AssertComRCReturnRC(rc);
6889 }
6890
6891 int vrc = RTThreadCreate(NULL, Console::powerUpThread,
6892 (void *)task.get(), 0,
6893 RTTHREADTYPE_MAIN_WORKER, 0, "VMPwrUp");
6894 if (RT_FAILURE(vrc))
6895 throw setError(E_FAIL, "Could not create VMPowerUp thread (%Rrc)", vrc);
6896
6897 /* task is now owned by powerUpThread(), so release it */
6898 task.release();
6899
6900 /* finally, set the state: no right to fail in this method afterwards
6901 * since we've already started the thread and it is now responsible for
6902 * any error reporting and appropriate state change! */
6903 if (mMachineState == MachineState_Saved)
6904 setMachineState(MachineState_Restoring);
6905 else if (fTeleporterEnabled)
6906 setMachineState(MachineState_TeleportingIn);
6907 else if (enmFaultToleranceState == FaultToleranceState_Standby)
6908 setMachineState(MachineState_FaultTolerantSyncing);
6909 else
6910 setMachineState(MachineState_Starting);
6911 }
6912 catch (HRESULT aRC) { rc = aRC; }
6913
6914 if (FAILED(rc) && fBeganPoweringUp)
6915 {
6916
6917 /* The progress object will fetch the current error info */
6918 if (!pPowerupProgress.isNull())
6919 pPowerupProgress->notifyComplete(rc);
6920
6921 /* Save the error info across the IPC below. Can't be done before the
6922 * progress notification above, as saving the error info deletes it
6923 * from the current context, and thus the progress object wouldn't be
6924 * updated correctly. */
6925 ErrorInfoKeeper eik;
6926
6927 /* signal end of operation */
6928 mControl->EndPowerUp(rc);
6929 }
6930
6931 LogFlowThisFunc(("mMachineState=%d, rc=%Rhrc\n", mMachineState, rc));
6932 LogFlowThisFuncLeave();
6933 return rc;
6934}
6935
6936/**
6937 * Internal power off worker routine.
6938 *
6939 * This method may be called only at certain places with the following meaning
6940 * as shown below:
6941 *
6942 * - if the machine state is either Running or Paused, a normal
6943 * Console-initiated powerdown takes place (e.g. PowerDown());
6944 * - if the machine state is Saving, saveStateThread() has successfully done its
6945 * job;
6946 * - if the machine state is Starting or Restoring, powerUpThread() has failed
6947 * to start/load the VM;
6948 * - if the machine state is Stopping, the VM has powered itself off (i.e. not
6949 * as a result of the powerDown() call).
6950 *
6951 * Calling it in situations other than the above will cause unexpected behavior.
6952 *
6953 * Note that this method should be the only one that destroys mpUVM and sets it
6954 * to NULL.
6955 *
6956 * @param aProgress Progress object to run (may be NULL).
6957 *
6958 * @note Locks this object for writing.
6959 *
6960 * @note Never call this method from a thread that called addVMCaller() or
6961 * instantiated an AutoVMCaller object; first call releaseVMCaller() or
6962 * release(). Otherwise it will deadlock.
6963 */
6964HRESULT Console::powerDown(IProgress *aProgress /*= NULL*/)
6965{
6966 LogFlowThisFuncEnter();
6967
6968 AutoCaller autoCaller(this);
6969 AssertComRCReturnRC(autoCaller.rc());
6970
6971 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6972
6973 /* Total # of steps for the progress object. Must correspond to the
6974 * number of "advance percent count" comments in this method! */
6975 enum { StepCount = 7 };
6976 /* current step */
6977 ULONG step = 0;
6978
6979 HRESULT rc = S_OK;
6980 int vrc = VINF_SUCCESS;
6981
6982 /* sanity */
6983 Assert(mVMDestroying == false);
6984
6985 PUVM pUVM = mpUVM; Assert(pUVM != NULL);
6986 uint32_t cRefs = VMR3RetainUVM(pUVM); Assert(cRefs != UINT32_MAX);
6987
6988 AssertMsg( mMachineState == MachineState_Running
6989 || mMachineState == MachineState_Paused
6990 || mMachineState == MachineState_Stuck
6991 || mMachineState == MachineState_Starting
6992 || mMachineState == MachineState_Stopping
6993 || mMachineState == MachineState_Saving
6994 || mMachineState == MachineState_Restoring
6995 || mMachineState == MachineState_TeleportingPausedVM
6996 || mMachineState == MachineState_FaultTolerantSyncing
6997 || mMachineState == MachineState_TeleportingIn
6998 , ("Invalid machine state: %s\n", Global::stringifyMachineState(mMachineState)));
6999
7000 LogRel(("Console::powerDown(): A request to power off the VM has been issued (mMachineState=%s, InUninit=%d)\n",
7001 Global::stringifyMachineState(mMachineState), autoCaller.state() == InUninit));
7002
7003 /* Check if we need to power off the VM. In case of mVMPoweredOff=true, the
7004 * VM has already powered itself off in vmstateChangeCallback() and is just
7005 * notifying Console about that. In case of Starting or Restoring,
7006 * powerUpThread() is calling us on failure, so the VM is already off at
7007 * that point. */
7008 if ( !mVMPoweredOff
7009 && ( mMachineState == MachineState_Starting
7010 || mMachineState == MachineState_Restoring
7011 || mMachineState == MachineState_FaultTolerantSyncing
7012 || mMachineState == MachineState_TeleportingIn)
7013 )
7014 mVMPoweredOff = true;
7015
7016 /*
7017 * Go to Stopping state if not already there.
7018 *
7019 * Note that we don't go from Saving/Restoring to Stopping because
7020 * vmstateChangeCallback() needs it to set the state to Saved on
7021 * VMSTATE_TERMINATED. In terms of protecting from inappropriate operations
7022 * while leaving the lock below, Saving or Restoring should be fine too.
7023 * Ditto for TeleportingPausedVM -> Teleported.
7024 */
7025 if ( mMachineState != MachineState_Saving
7026 && mMachineState != MachineState_Restoring
7027 && mMachineState != MachineState_Stopping
7028 && mMachineState != MachineState_TeleportingIn
7029 && mMachineState != MachineState_TeleportingPausedVM
7030 && mMachineState != MachineState_FaultTolerantSyncing
7031 )
7032 setMachineState(MachineState_Stopping);
7033
7034 /* ----------------------------------------------------------------------
7035 * DONE with necessary state changes, perform the power down actions (it's
7036 * safe to release the object lock now if needed)
7037 * ---------------------------------------------------------------------- */
7038
7039 /* Stop the VRDP server to prevent new clients connection while VM is being
7040 * powered off. */
7041 if (mConsoleVRDPServer)
7042 {
7043 LogFlowThisFunc(("Stopping VRDP server...\n"));
7044
7045 /* Leave the lock since EMT will call us back as addVMCaller()
7046 * in updateDisplayData(). */
7047 alock.release();
7048
7049 mConsoleVRDPServer->Stop();
7050
7051 alock.acquire();
7052 }
7053
7054 /* advance percent count */
7055 if (aProgress)
7056 aProgress->SetCurrentOperationProgress(99 * (++step) / StepCount );
7057
7058
7059 /* ----------------------------------------------------------------------
7060 * Now, wait for all mpUVM callers to finish their work if there are still
7061 * some on other threads. NO methods that need mpUVM (or initiate other calls
7062 * that need it) may be called after this point
7063 * ---------------------------------------------------------------------- */
7064
7065 /* go to the destroying state to prevent from adding new callers */
7066 mVMDestroying = true;
7067
7068 if (mVMCallers > 0)
7069 {
7070 /* lazy creation */
7071 if (mVMZeroCallersSem == NIL_RTSEMEVENT)
7072 RTSemEventCreate(&mVMZeroCallersSem);
7073
7074 LogFlowThisFunc(("Waiting for mpUVM callers (%d) to drop to zero...\n", mVMCallers));
7075
7076 alock.release();
7077
7078 RTSemEventWait(mVMZeroCallersSem, RT_INDEFINITE_WAIT);
7079
7080 alock.acquire();
7081 }
7082
7083 /* advance percent count */
7084 if (aProgress)
7085 aProgress->SetCurrentOperationProgress(99 * (++step) / StepCount );
7086
7087 vrc = VINF_SUCCESS;
7088
7089 /*
7090 * Power off the VM if not already done that.
7091 * Leave the lock since EMT will call vmstateChangeCallback.
7092 *
7093 * Note that VMR3PowerOff() may fail here (invalid VMSTATE) if the
7094 * VM-(guest-)initiated power off happened in parallel a ms before this
7095 * call. So far, we let this error pop up on the user's side.
7096 */
7097 if (!mVMPoweredOff)
7098 {
7099 LogFlowThisFunc(("Powering off the VM...\n"));
7100 alock.release();
7101 vrc = VMR3PowerOff(pUVM);
7102#ifdef VBOX_WITH_EXTPACK
7103 mptrExtPackManager->callAllVmPowerOffHooks(this, VMR3GetVM(pUVM));
7104#endif
7105 alock.acquire();
7106 }
7107
7108 /* advance percent count */
7109 if (aProgress)
7110 aProgress->SetCurrentOperationProgress(99 * (++step) / StepCount );
7111
7112#ifdef VBOX_WITH_HGCM
7113 /* Shutdown HGCM services before destroying the VM. */
7114 if (m_pVMMDev)
7115 {
7116 LogFlowThisFunc(("Shutdown HGCM...\n"));
7117
7118 /* Leave the lock since EMT will call us back as addVMCaller() */
7119 alock.release();
7120
7121 m_pVMMDev->hgcmShutdown();
7122
7123 alock.acquire();
7124 }
7125
7126 /* advance percent count */
7127 if (aProgress)
7128 aProgress->SetCurrentOperationProgress(99 * (++step) / StepCount);
7129
7130#endif /* VBOX_WITH_HGCM */
7131
7132 LogFlowThisFunc(("Ready for VM destruction.\n"));
7133
7134 /* If we are called from Console::uninit(), then try to destroy the VM even
7135 * on failure (this will most likely fail too, but what to do?..) */
7136 if (RT_SUCCESS(vrc) || autoCaller.state() == InUninit)
7137 {
7138 /* If the machine has a USB controller, release all USB devices
7139 * (symmetric to the code in captureUSBDevices()) */
7140 if (mfVMHasUsbController)
7141 {
7142 alock.release();
7143 detachAllUSBDevices(false /* aDone */);
7144 alock.acquire();
7145 }
7146
7147 /* Now we've got to destroy the VM as well. (mpUVM is not valid beyond
7148 * this point). We release the lock before calling VMR3Destroy() because
7149 * it will result into calling destructors of drivers associated with
7150 * Console children which may in turn try to lock Console (e.g. by
7151 * instantiating SafeVMPtr to access mpUVM). It's safe here because
7152 * mVMDestroying is set which should prevent any activity. */
7153
7154 /* Set mpUVM to NULL early just in case if some old code is not using
7155 * addVMCaller()/releaseVMCaller(). (We have our own ref on pUVM.) */
7156 VMR3ReleaseUVM(mpUVM);
7157 mpUVM = NULL;
7158
7159 LogFlowThisFunc(("Destroying the VM...\n"));
7160
7161 alock.release();
7162
7163 vrc = VMR3Destroy(pUVM);
7164
7165 /* take the lock again */
7166 alock.acquire();
7167
7168 /* advance percent count */
7169 if (aProgress)
7170 aProgress->SetCurrentOperationProgress(99 * (++step) / StepCount);
7171
7172 if (RT_SUCCESS(vrc))
7173 {
7174 LogFlowThisFunc(("Machine has been destroyed (mMachineState=%d)\n",
7175 mMachineState));
7176 /* Note: the Console-level machine state change happens on the
7177 * VMSTATE_TERMINATE state change in vmstateChangeCallback(). If
7178 * powerDown() is called from EMT (i.e. from vmstateChangeCallback()
7179 * on receiving VM-initiated VMSTATE_OFF), VMSTATE_TERMINATE hasn't
7180 * occurred yet. This is okay, because mMachineState is already
7181 * Stopping in this case, so any other attempt to call PowerDown()
7182 * will be rejected. */
7183 }
7184 else
7185 {
7186 /* bad bad bad, but what to do? (Give Console our UVM ref.) */
7187 mpUVM = pUVM;
7188 pUVM = NULL;
7189 rc = setError(VBOX_E_VM_ERROR,
7190 tr("Could not destroy the machine. (Error: %Rrc)"),
7191 vrc);
7192 }
7193
7194 /* Complete the detaching of the USB devices. */
7195 if (mfVMHasUsbController)
7196 {
7197 alock.release();
7198 detachAllUSBDevices(true /* aDone */);
7199 alock.acquire();
7200 }
7201
7202 /* advance percent count */
7203 if (aProgress)
7204 aProgress->SetCurrentOperationProgress(99 * (++step) / StepCount);
7205 }
7206 else
7207 {
7208 rc = setError(VBOX_E_VM_ERROR,
7209 tr("Could not power off the machine. (Error: %Rrc)"),
7210 vrc);
7211 }
7212
7213 /*
7214 * Finished with the destruction.
7215 *
7216 * Note that if something impossible happened and we've failed to destroy
7217 * the VM, mVMDestroying will remain true and mMachineState will be
7218 * something like Stopping, so most Console methods will return an error
7219 * to the caller.
7220 */
7221 if (pUVM != NULL)
7222 VMR3ReleaseUVM(pUVM);
7223 else
7224 mVMDestroying = false;
7225
7226#ifdef CONSOLE_WITH_EVENT_CACHE
7227 if (SUCCEEDED(rc))
7228 mCallbackData.clear();
7229#endif
7230
7231 LogFlowThisFuncLeave();
7232 return rc;
7233}
7234
7235/**
7236 * @note Locks this object for writing.
7237 */
7238HRESULT Console::setMachineState(MachineState_T aMachineState,
7239 bool aUpdateServer /* = true */)
7240{
7241 AutoCaller autoCaller(this);
7242 AssertComRCReturnRC(autoCaller.rc());
7243
7244 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7245
7246 HRESULT rc = S_OK;
7247
7248 if (mMachineState != aMachineState)
7249 {
7250 LogThisFunc(("machineState=%s -> %s aUpdateServer=%RTbool\n",
7251 Global::stringifyMachineState(mMachineState), Global::stringifyMachineState(aMachineState), aUpdateServer));
7252 mMachineState = aMachineState;
7253
7254 /// @todo (dmik)
7255 // possibly, we need to redo onStateChange() using the dedicated
7256 // Event thread, like it is done in VirtualBox. This will make it
7257 // much safer (no deadlocks possible if someone tries to use the
7258 // console from the callback), however, listeners will lose the
7259 // ability to synchronously react to state changes (is it really
7260 // necessary??)
7261 LogFlowThisFunc(("Doing onStateChange()...\n"));
7262 onStateChange(aMachineState);
7263 LogFlowThisFunc(("Done onStateChange()\n"));
7264
7265 if (aUpdateServer)
7266 {
7267 /* Server notification MUST be done from under the lock; otherwise
7268 * the machine state here and on the server might go out of sync
7269 * which can lead to various unexpected results (like the machine
7270 * state being >= MachineState_Running on the server, while the
7271 * session state is already SessionState_Unlocked at the same time
7272 * there).
7273 *
7274 * Cross-lock conditions should be carefully watched out: calling
7275 * UpdateState we will require Machine and SessionMachine locks
7276 * (remember that here we're holding the Console lock here, and also
7277 * all locks that have been acquire by the thread before calling
7278 * this method).
7279 */
7280 LogFlowThisFunc(("Doing mControl->UpdateState()...\n"));
7281 rc = mControl->UpdateState(aMachineState);
7282 LogFlowThisFunc(("mControl->UpdateState()=%Rhrc\n", rc));
7283 }
7284 }
7285
7286 return rc;
7287}
7288
7289/**
7290 * Searches for a shared folder with the given logical name
7291 * in the collection of shared folders.
7292 *
7293 * @param aName logical name of the shared folder
7294 * @param aSharedFolder where to return the found object
7295 * @param aSetError whether to set the error info if the folder is
7296 * not found
7297 * @return
7298 * S_OK when found or E_INVALIDARG when not found
7299 *
7300 * @note The caller must lock this object for writing.
7301 */
7302HRESULT Console::findSharedFolder(const Utf8Str &strName,
7303 ComObjPtr<SharedFolder> &aSharedFolder,
7304 bool aSetError /* = false */)
7305{
7306 /* sanity check */
7307 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
7308
7309 SharedFolderMap::const_iterator it = m_mapSharedFolders.find(strName);
7310 if (it != m_mapSharedFolders.end())
7311 {
7312 aSharedFolder = it->second;
7313 return S_OK;
7314 }
7315
7316 if (aSetError)
7317 setError(VBOX_E_FILE_ERROR,
7318 tr("Could not find a shared folder named '%s'."),
7319 strName.c_str());
7320
7321 return VBOX_E_FILE_ERROR;
7322}
7323
7324/**
7325 * Fetches the list of global or machine shared folders from the server.
7326 *
7327 * @param aGlobal true to fetch global folders.
7328 *
7329 * @note The caller must lock this object for writing.
7330 */
7331HRESULT Console::fetchSharedFolders(BOOL aGlobal)
7332{
7333 /* sanity check */
7334 AssertReturn(AutoCaller(this).state() == InInit ||
7335 isWriteLockOnCurrentThread(), E_FAIL);
7336
7337 LogFlowThisFunc(("Entering\n"));
7338
7339 /* Check if we're online and keep it that way. */
7340 SafeVMPtrQuiet ptrVM(this);
7341 AutoVMCallerQuietWeak autoVMCaller(this);
7342 bool const online = ptrVM.isOk()
7343 && m_pVMMDev
7344 && m_pVMMDev->isShFlActive();
7345
7346 HRESULT rc = S_OK;
7347
7348 try
7349 {
7350 if (aGlobal)
7351 {
7352 /// @todo grab & process global folders when they are done
7353 }
7354 else
7355 {
7356 SharedFolderDataMap oldFolders;
7357 if (online)
7358 oldFolders = m_mapMachineSharedFolders;
7359
7360 m_mapMachineSharedFolders.clear();
7361
7362 SafeIfaceArray<ISharedFolder> folders;
7363 rc = mMachine->COMGETTER(SharedFolders)(ComSafeArrayAsOutParam(folders));
7364 if (FAILED(rc)) throw rc;
7365
7366 for (size_t i = 0; i < folders.size(); ++i)
7367 {
7368 ComPtr<ISharedFolder> pSharedFolder = folders[i];
7369
7370 Bstr bstrName;
7371 Bstr bstrHostPath;
7372 BOOL writable;
7373 BOOL autoMount;
7374
7375 rc = pSharedFolder->COMGETTER(Name)(bstrName.asOutParam());
7376 if (FAILED(rc)) throw rc;
7377 Utf8Str strName(bstrName);
7378
7379 rc = pSharedFolder->COMGETTER(HostPath)(bstrHostPath.asOutParam());
7380 if (FAILED(rc)) throw rc;
7381 Utf8Str strHostPath(bstrHostPath);
7382
7383 rc = pSharedFolder->COMGETTER(Writable)(&writable);
7384 if (FAILED(rc)) throw rc;
7385
7386 rc = pSharedFolder->COMGETTER(AutoMount)(&autoMount);
7387 if (FAILED(rc)) throw rc;
7388
7389 m_mapMachineSharedFolders.insert(std::make_pair(strName,
7390 SharedFolderData(strHostPath, !!writable, !!autoMount)));
7391
7392 /* send changes to HGCM if the VM is running */
7393 if (online)
7394 {
7395 SharedFolderDataMap::iterator it = oldFolders.find(strName);
7396 if ( it == oldFolders.end()
7397 || it->second.m_strHostPath != strHostPath)
7398 {
7399 /* a new machine folder is added or
7400 * the existing machine folder is changed */
7401 if (m_mapSharedFolders.find(strName) != m_mapSharedFolders.end())
7402 ; /* the console folder exists, nothing to do */
7403 else
7404 {
7405 /* remove the old machine folder (when changed)
7406 * or the global folder if any (when new) */
7407 if ( it != oldFolders.end()
7408 || m_mapGlobalSharedFolders.find(strName) != m_mapGlobalSharedFolders.end()
7409 )
7410 {
7411 rc = removeSharedFolder(strName);
7412 if (FAILED(rc)) throw rc;
7413 }
7414
7415 /* create the new machine folder */
7416 rc = createSharedFolder(strName,
7417 SharedFolderData(strHostPath, !!writable, !!autoMount));
7418 if (FAILED(rc)) throw rc;
7419 }
7420 }
7421 /* forget the processed (or identical) folder */
7422 if (it != oldFolders.end())
7423 oldFolders.erase(it);
7424 }
7425 }
7426
7427 /* process outdated (removed) folders */
7428 if (online)
7429 {
7430 for (SharedFolderDataMap::const_iterator it = oldFolders.begin();
7431 it != oldFolders.end(); ++it)
7432 {
7433 if (m_mapSharedFolders.find(it->first) != m_mapSharedFolders.end())
7434 ; /* the console folder exists, nothing to do */
7435 else
7436 {
7437 /* remove the outdated machine folder */
7438 rc = removeSharedFolder(it->first);
7439 if (FAILED(rc)) throw rc;
7440
7441 /* create the global folder if there is any */
7442 SharedFolderDataMap::const_iterator git =
7443 m_mapGlobalSharedFolders.find(it->first);
7444 if (git != m_mapGlobalSharedFolders.end())
7445 {
7446 rc = createSharedFolder(git->first, git->second);
7447 if (FAILED(rc)) throw rc;
7448 }
7449 }
7450 }
7451 }
7452 }
7453 }
7454 catch (HRESULT rc2)
7455 {
7456 if (online)
7457 setVMRuntimeErrorCallbackF(0, "BrokenSharedFolder",
7458 N_("Broken shared folder!"));
7459 }
7460
7461 LogFlowThisFunc(("Leaving\n"));
7462
7463 return rc;
7464}
7465
7466/**
7467 * Searches for a shared folder with the given name in the list of machine
7468 * shared folders and then in the list of the global shared folders.
7469 *
7470 * @param aName Name of the folder to search for.
7471 * @param aIt Where to store the pointer to the found folder.
7472 * @return @c true if the folder was found and @c false otherwise.
7473 *
7474 * @note The caller must lock this object for reading.
7475 */
7476bool Console::findOtherSharedFolder(const Utf8Str &strName,
7477 SharedFolderDataMap::const_iterator &aIt)
7478{
7479 /* sanity check */
7480 AssertReturn(isWriteLockOnCurrentThread(), false);
7481
7482 /* first, search machine folders */
7483 aIt = m_mapMachineSharedFolders.find(strName);
7484 if (aIt != m_mapMachineSharedFolders.end())
7485 return true;
7486
7487 /* second, search machine folders */
7488 aIt = m_mapGlobalSharedFolders.find(strName);
7489 if (aIt != m_mapGlobalSharedFolders.end())
7490 return true;
7491
7492 return false;
7493}
7494
7495/**
7496 * Calls the HGCM service to add a shared folder definition.
7497 *
7498 * @param aName Shared folder name.
7499 * @param aHostPath Shared folder path.
7500 *
7501 * @note Must be called from under AutoVMCaller and when mpUVM != NULL!
7502 * @note Doesn't lock anything.
7503 */
7504HRESULT Console::createSharedFolder(const Utf8Str &strName, const SharedFolderData &aData)
7505{
7506 ComAssertRet(strName.isNotEmpty(), E_FAIL);
7507 ComAssertRet(aData.m_strHostPath.isNotEmpty(), E_FAIL);
7508
7509 /* sanity checks */
7510 AssertReturn(mpUVM, E_FAIL);
7511 AssertReturn(m_pVMMDev && m_pVMMDev->isShFlActive(), E_FAIL);
7512
7513 VBOXHGCMSVCPARM parms[SHFL_CPARMS_ADD_MAPPING];
7514 SHFLSTRING *pFolderName, *pMapName;
7515 size_t cbString;
7516
7517 Bstr value;
7518 HRESULT hrc = mMachine->GetExtraData(BstrFmt("VBoxInternal2/SharedFoldersEnableSymlinksCreate/%s",
7519 strName.c_str()).raw(),
7520 value.asOutParam());
7521 bool fSymlinksCreate = hrc == S_OK && value == "1";
7522
7523 Log(("Adding shared folder '%s' -> '%s'\n", strName.c_str(), aData.m_strHostPath.c_str()));
7524
7525 // check whether the path is valid and exists
7526 char hostPathFull[RTPATH_MAX];
7527 int vrc = RTPathAbsEx(NULL,
7528 aData.m_strHostPath.c_str(),
7529 hostPathFull,
7530 sizeof(hostPathFull));
7531
7532 bool fMissing = false;
7533 if (RT_FAILURE(vrc))
7534 return setError(E_INVALIDARG,
7535 tr("Invalid shared folder path: '%s' (%Rrc)"),
7536 aData.m_strHostPath.c_str(), vrc);
7537 if (!RTPathExists(hostPathFull))
7538 fMissing = true;
7539
7540 /* Check whether the path is full (absolute) */
7541 if (RTPathCompare(aData.m_strHostPath.c_str(), hostPathFull) != 0)
7542 return setError(E_INVALIDARG,
7543 tr("Shared folder path '%s' is not absolute"),
7544 aData.m_strHostPath.c_str());
7545
7546 // now that we know the path is good, give it to HGCM
7547
7548 Bstr bstrName(strName);
7549 Bstr bstrHostPath(aData.m_strHostPath);
7550
7551 cbString = (bstrHostPath.length() + 1) * sizeof(RTUTF16);
7552 if (cbString >= UINT16_MAX)
7553 return setError(E_INVALIDARG, tr("The name is too long"));
7554 pFolderName = (SHFLSTRING*)RTMemAllocZ(sizeof(SHFLSTRING) + cbString);
7555 Assert(pFolderName);
7556 memcpy(pFolderName->String.ucs2, bstrHostPath.raw(), cbString);
7557
7558 pFolderName->u16Size = (uint16_t)cbString;
7559 pFolderName->u16Length = (uint16_t)cbString - sizeof(RTUTF16);
7560
7561 parms[0].type = VBOX_HGCM_SVC_PARM_PTR;
7562 parms[0].u.pointer.addr = pFolderName;
7563 parms[0].u.pointer.size = sizeof(SHFLSTRING) + (uint16_t)cbString;
7564
7565 cbString = (bstrName.length() + 1) * sizeof(RTUTF16);
7566 if (cbString >= UINT16_MAX)
7567 {
7568 RTMemFree(pFolderName);
7569 return setError(E_INVALIDARG, tr("The host path is too long"));
7570 }
7571 pMapName = (SHFLSTRING*)RTMemAllocZ(sizeof(SHFLSTRING) + cbString);
7572 Assert(pMapName);
7573 memcpy(pMapName->String.ucs2, bstrName.raw(), cbString);
7574
7575 pMapName->u16Size = (uint16_t)cbString;
7576 pMapName->u16Length = (uint16_t)cbString - sizeof(RTUTF16);
7577
7578 parms[1].type = VBOX_HGCM_SVC_PARM_PTR;
7579 parms[1].u.pointer.addr = pMapName;
7580 parms[1].u.pointer.size = sizeof(SHFLSTRING) + (uint16_t)cbString;
7581
7582 parms[2].type = VBOX_HGCM_SVC_PARM_32BIT;
7583 parms[2].u.uint32 = (aData.m_fWritable ? SHFL_ADD_MAPPING_F_WRITABLE : 0)
7584 | (aData.m_fAutoMount ? SHFL_ADD_MAPPING_F_AUTOMOUNT : 0)
7585 | (fSymlinksCreate ? SHFL_ADD_MAPPING_F_CREATE_SYMLINKS : 0)
7586 | (fMissing ? SHFL_ADD_MAPPING_F_MISSING : 0)
7587 ;
7588
7589 vrc = m_pVMMDev->hgcmHostCall("VBoxSharedFolders",
7590 SHFL_FN_ADD_MAPPING,
7591 SHFL_CPARMS_ADD_MAPPING, &parms[0]);
7592 RTMemFree(pFolderName);
7593 RTMemFree(pMapName);
7594
7595 if (RT_FAILURE(vrc))
7596 return setError(E_FAIL,
7597 tr("Could not create a shared folder '%s' mapped to '%s' (%Rrc)"),
7598 strName.c_str(), aData.m_strHostPath.c_str(), vrc);
7599
7600 if (fMissing)
7601 return setError(E_INVALIDARG,
7602 tr("Shared folder path '%s' does not exist on the host"),
7603 aData.m_strHostPath.c_str());
7604
7605 return S_OK;
7606}
7607
7608/**
7609 * Calls the HGCM service to remove the shared folder definition.
7610 *
7611 * @param aName Shared folder name.
7612 *
7613 * @note Must be called from under AutoVMCaller and when mpUVM != NULL!
7614 * @note Doesn't lock anything.
7615 */
7616HRESULT Console::removeSharedFolder(const Utf8Str &strName)
7617{
7618 ComAssertRet(strName.isNotEmpty(), E_FAIL);
7619
7620 /* sanity checks */
7621 AssertReturn(mpUVM, E_FAIL);
7622 AssertReturn(m_pVMMDev && m_pVMMDev->isShFlActive(), E_FAIL);
7623
7624 VBOXHGCMSVCPARM parms;
7625 SHFLSTRING *pMapName;
7626 size_t cbString;
7627
7628 Log(("Removing shared folder '%s'\n", strName.c_str()));
7629
7630 Bstr bstrName(strName);
7631 cbString = (bstrName.length() + 1) * sizeof(RTUTF16);
7632 if (cbString >= UINT16_MAX)
7633 return setError(E_INVALIDARG, tr("The name is too long"));
7634 pMapName = (SHFLSTRING *) RTMemAllocZ(sizeof(SHFLSTRING) + cbString);
7635 Assert(pMapName);
7636 memcpy(pMapName->String.ucs2, bstrName.raw(), cbString);
7637
7638 pMapName->u16Size = (uint16_t)cbString;
7639 pMapName->u16Length = (uint16_t)cbString - sizeof(RTUTF16);
7640
7641 parms.type = VBOX_HGCM_SVC_PARM_PTR;
7642 parms.u.pointer.addr = pMapName;
7643 parms.u.pointer.size = sizeof(SHFLSTRING) + (uint16_t)cbString;
7644
7645 int vrc = m_pVMMDev->hgcmHostCall("VBoxSharedFolders",
7646 SHFL_FN_REMOVE_MAPPING,
7647 1, &parms);
7648 RTMemFree(pMapName);
7649 if (RT_FAILURE(vrc))
7650 return setError(E_FAIL,
7651 tr("Could not remove the shared folder '%s' (%Rrc)"),
7652 strName.c_str(), vrc);
7653
7654 return S_OK;
7655}
7656
7657/** @callback_method_impl{FNVMATSTATE}
7658 *
7659 * @note Locks the Console object for writing.
7660 * @remarks The @a pUVM parameter can be NULL in one case where powerUpThread()
7661 * calls after the VM was destroyed.
7662 */
7663DECLCALLBACK(void) Console::vmstateChangeCallback(PUVM pUVM, VMSTATE enmState, VMSTATE enmOldState, void *pvUser)
7664{
7665 LogFlowFunc(("Changing state from %s to %s (pUVM=%p)\n",
7666 VMR3GetStateName(enmOldState), VMR3GetStateName(enmState), pUVM));
7667
7668 Console *that = static_cast<Console *>(pvUser);
7669 AssertReturnVoid(that);
7670
7671 AutoCaller autoCaller(that);
7672
7673 /* Note that we must let this method proceed even if Console::uninit() has
7674 * been already called. In such case this VMSTATE change is a result of:
7675 * 1) powerDown() called from uninit() itself, or
7676 * 2) VM-(guest-)initiated power off. */
7677 AssertReturnVoid( autoCaller.isOk()
7678 || autoCaller.state() == InUninit);
7679
7680 switch (enmState)
7681 {
7682 /*
7683 * The VM has terminated
7684 */
7685 case VMSTATE_OFF:
7686 {
7687 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
7688
7689 if (that->mVMStateChangeCallbackDisabled)
7690 break;
7691
7692 /* Do we still think that it is running? It may happen if this is a
7693 * VM-(guest-)initiated shutdown/poweroff.
7694 */
7695 if ( that->mMachineState != MachineState_Stopping
7696 && that->mMachineState != MachineState_Saving
7697 && that->mMachineState != MachineState_Restoring
7698 && that->mMachineState != MachineState_TeleportingIn
7699 && that->mMachineState != MachineState_FaultTolerantSyncing
7700 && that->mMachineState != MachineState_TeleportingPausedVM
7701 && !that->mVMIsAlreadyPoweringOff
7702 )
7703 {
7704 LogFlowFunc(("VM has powered itself off but Console still thinks it is running. Notifying.\n"));
7705
7706 /* prevent powerDown() from calling VMR3PowerOff() again */
7707 Assert(that->mVMPoweredOff == false);
7708 that->mVMPoweredOff = true;
7709
7710 /*
7711 * request a progress object from the server
7712 * (this will set the machine state to Stopping on the server
7713 * to block others from accessing this machine)
7714 */
7715 ComPtr<IProgress> pProgress;
7716 HRESULT rc = that->mControl->BeginPoweringDown(pProgress.asOutParam());
7717 AssertComRC(rc);
7718
7719 /* sync the state with the server */
7720 that->setMachineStateLocally(MachineState_Stopping);
7721
7722 /* Setup task object and thread to carry out the operation
7723 * asynchronously (if we call powerDown() right here but there
7724 * is one or more mpUVM callers (added with addVMCaller()) we'll
7725 * deadlock).
7726 */
7727 std::auto_ptr<VMPowerDownTask> task(new VMPowerDownTask(that, pProgress));
7728
7729 /* If creating a task failed, this can currently mean one of
7730 * two: either Console::uninit() has been called just a ms
7731 * before (so a powerDown() call is already on the way), or
7732 * powerDown() itself is being already executed. Just do
7733 * nothing.
7734 */
7735 if (!task->isOk())
7736 {
7737 LogFlowFunc(("Console is already being uninitialized.\n"));
7738 break;
7739 }
7740
7741 int vrc = RTThreadCreate(NULL, Console::powerDownThread,
7742 (void *)task.get(), 0,
7743 RTTHREADTYPE_MAIN_WORKER, 0,
7744 "VMPwrDwn");
7745 AssertMsgRCBreak(vrc, ("Could not create VMPowerDown thread (%Rrc)\n", vrc));
7746
7747 /* task is now owned by powerDownThread(), so release it */
7748 task.release();
7749 }
7750 break;
7751 }
7752
7753 /* The VM has been completely destroyed.
7754 *
7755 * Note: This state change can happen at two points:
7756 * 1) At the end of VMR3Destroy() if it was not called from EMT.
7757 * 2) At the end of vmR3EmulationThread if VMR3Destroy() was
7758 * called by EMT.
7759 */
7760 case VMSTATE_TERMINATED:
7761 {
7762 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
7763
7764 if (that->mVMStateChangeCallbackDisabled)
7765 break;
7766
7767 /* Terminate host interface networking. If pUVM is NULL, we've been
7768 * manually called from powerUpThread() either before calling
7769 * VMR3Create() or after VMR3Create() failed, so no need to touch
7770 * networking.
7771 */
7772 if (pUVM)
7773 that->powerDownHostInterfaces();
7774
7775 /* From now on the machine is officially powered down or remains in
7776 * the Saved state.
7777 */
7778 switch (that->mMachineState)
7779 {
7780 default:
7781 AssertFailed();
7782 /* fall through */
7783 case MachineState_Stopping:
7784 /* successfully powered down */
7785 that->setMachineState(MachineState_PoweredOff);
7786 break;
7787 case MachineState_Saving:
7788 /* successfully saved */
7789 that->setMachineState(MachineState_Saved);
7790 break;
7791 case MachineState_Starting:
7792 /* failed to start, but be patient: set back to PoweredOff
7793 * (for similarity with the below) */
7794 that->setMachineState(MachineState_PoweredOff);
7795 break;
7796 case MachineState_Restoring:
7797 /* failed to load the saved state file, but be patient: set
7798 * back to Saved (to preserve the saved state file) */
7799 that->setMachineState(MachineState_Saved);
7800 break;
7801 case MachineState_TeleportingIn:
7802 /* Teleportation failed or was canceled. Back to powered off. */
7803 that->setMachineState(MachineState_PoweredOff);
7804 break;
7805 case MachineState_TeleportingPausedVM:
7806 /* Successfully teleported the VM. */
7807 that->setMachineState(MachineState_Teleported);
7808 break;
7809 case MachineState_FaultTolerantSyncing:
7810 /* Fault tolerant sync failed or was canceled. Back to powered off. */
7811 that->setMachineState(MachineState_PoweredOff);
7812 break;
7813 }
7814 break;
7815 }
7816
7817 case VMSTATE_RESETTING:
7818 {
7819#ifdef VBOX_WITH_GUEST_PROPS
7820 /* Do not take any read/write locks here! */
7821 that->guestPropertiesHandleVMReset();
7822#endif
7823 break;
7824 }
7825
7826 case VMSTATE_SUSPENDED:
7827 {
7828 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
7829
7830 if (that->mVMStateChangeCallbackDisabled)
7831 break;
7832
7833 switch (that->mMachineState)
7834 {
7835 case MachineState_Teleporting:
7836 that->setMachineState(MachineState_TeleportingPausedVM);
7837 break;
7838
7839 case MachineState_LiveSnapshotting:
7840 that->setMachineState(MachineState_Saving);
7841 break;
7842
7843 case MachineState_TeleportingPausedVM:
7844 case MachineState_Saving:
7845 case MachineState_Restoring:
7846 case MachineState_Stopping:
7847 case MachineState_TeleportingIn:
7848 case MachineState_FaultTolerantSyncing:
7849 /* The worker thread handles the transition. */
7850 break;
7851
7852 default:
7853 AssertMsgFailed(("%s\n", Global::stringifyMachineState(that->mMachineState)));
7854 case MachineState_Running:
7855 that->setMachineState(MachineState_Paused);
7856 break;
7857
7858 case MachineState_Paused:
7859 /* Nothing to do. */
7860 break;
7861 }
7862 break;
7863 }
7864
7865 case VMSTATE_SUSPENDED_LS:
7866 case VMSTATE_SUSPENDED_EXT_LS:
7867 {
7868 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
7869 if (that->mVMStateChangeCallbackDisabled)
7870 break;
7871 switch (that->mMachineState)
7872 {
7873 case MachineState_Teleporting:
7874 that->setMachineState(MachineState_TeleportingPausedVM);
7875 break;
7876
7877 case MachineState_LiveSnapshotting:
7878 that->setMachineState(MachineState_Saving);
7879 break;
7880
7881 case MachineState_TeleportingPausedVM:
7882 case MachineState_Saving:
7883 /* ignore */
7884 break;
7885
7886 default:
7887 AssertMsgFailed(("%s/%s -> %s\n", Global::stringifyMachineState(that->mMachineState), VMR3GetStateName(enmOldState), VMR3GetStateName(enmState) ));
7888 that->setMachineState(MachineState_Paused);
7889 break;
7890 }
7891 break;
7892 }
7893
7894 case VMSTATE_RUNNING:
7895 {
7896 if ( enmOldState == VMSTATE_POWERING_ON
7897 || enmOldState == VMSTATE_RESUMING
7898 || enmOldState == VMSTATE_RUNNING_FT)
7899 {
7900 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
7901
7902 if (that->mVMStateChangeCallbackDisabled)
7903 break;
7904
7905 Assert( ( ( that->mMachineState == MachineState_Starting
7906 || that->mMachineState == MachineState_Paused)
7907 && enmOldState == VMSTATE_POWERING_ON)
7908 || ( ( that->mMachineState == MachineState_Restoring
7909 || that->mMachineState == MachineState_TeleportingIn
7910 || that->mMachineState == MachineState_Paused
7911 || that->mMachineState == MachineState_Saving
7912 )
7913 && enmOldState == VMSTATE_RESUMING)
7914 || ( that->mMachineState == MachineState_FaultTolerantSyncing
7915 && enmOldState == VMSTATE_RUNNING_FT));
7916
7917 that->setMachineState(MachineState_Running);
7918 }
7919
7920 break;
7921 }
7922
7923 case VMSTATE_RUNNING_LS:
7924 AssertMsg( that->mMachineState == MachineState_LiveSnapshotting
7925 || that->mMachineState == MachineState_Teleporting,
7926 ("%s/%s -> %s\n", Global::stringifyMachineState(that->mMachineState), VMR3GetStateName(enmOldState), VMR3GetStateName(enmState) ));
7927 break;
7928
7929 case VMSTATE_RUNNING_FT:
7930 AssertMsg(that->mMachineState == MachineState_FaultTolerantSyncing,
7931 ("%s/%s -> %s\n", Global::stringifyMachineState(that->mMachineState), VMR3GetStateName(enmOldState), VMR3GetStateName(enmState) ));
7932 break;
7933
7934 case VMSTATE_FATAL_ERROR:
7935 {
7936 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
7937
7938 if (that->mVMStateChangeCallbackDisabled)
7939 break;
7940
7941 /* Fatal errors are only for running VMs. */
7942 Assert(Global::IsOnline(that->mMachineState));
7943
7944 /* Note! 'Pause' is used here in want of something better. There
7945 * are currently only two places where fatal errors might be
7946 * raised, so it is not worth adding a new externally
7947 * visible state for this yet. */
7948 that->setMachineState(MachineState_Paused);
7949 break;
7950 }
7951
7952 case VMSTATE_GURU_MEDITATION:
7953 {
7954 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
7955
7956 if (that->mVMStateChangeCallbackDisabled)
7957 break;
7958
7959 /* Guru are only for running VMs */
7960 Assert(Global::IsOnline(that->mMachineState));
7961
7962 that->setMachineState(MachineState_Stuck);
7963 break;
7964 }
7965
7966 default: /* shut up gcc */
7967 break;
7968 }
7969}
7970
7971/**
7972 * Changes the clipboard mode.
7973 *
7974 * @param aClipboardMode new clipboard mode.
7975 */
7976void Console::changeClipboardMode(ClipboardMode_T aClipboardMode)
7977{
7978 VMMDev *pVMMDev = m_pVMMDev;
7979 Assert(pVMMDev);
7980
7981 VBOXHGCMSVCPARM parm;
7982 parm.type = VBOX_HGCM_SVC_PARM_32BIT;
7983
7984 switch (aClipboardMode)
7985 {
7986 default:
7987 case ClipboardMode_Disabled:
7988 LogRel(("Shared clipboard mode: Off\n"));
7989 parm.u.uint32 = VBOX_SHARED_CLIPBOARD_MODE_OFF;
7990 break;
7991 case ClipboardMode_GuestToHost:
7992 LogRel(("Shared clipboard mode: Guest to Host\n"));
7993 parm.u.uint32 = VBOX_SHARED_CLIPBOARD_MODE_GUEST_TO_HOST;
7994 break;
7995 case ClipboardMode_HostToGuest:
7996 LogRel(("Shared clipboard mode: Host to Guest\n"));
7997 parm.u.uint32 = VBOX_SHARED_CLIPBOARD_MODE_HOST_TO_GUEST;
7998 break;
7999 case ClipboardMode_Bidirectional:
8000 LogRel(("Shared clipboard mode: Bidirectional\n"));
8001 parm.u.uint32 = VBOX_SHARED_CLIPBOARD_MODE_BIDIRECTIONAL;
8002 break;
8003 }
8004
8005 pVMMDev->hgcmHostCall("VBoxSharedClipboard", VBOX_SHARED_CLIPBOARD_HOST_FN_SET_MODE, 1, &parm);
8006}
8007
8008/**
8009 * Changes the drag'n_drop mode.
8010 *
8011 * @param aDragAndDropMode new drag'n'drop mode.
8012 */
8013void Console::changeDragAndDropMode(DragAndDropMode_T aDragAndDropMode)
8014{
8015 VMMDev *pVMMDev = m_pVMMDev;
8016 Assert(pVMMDev);
8017
8018 VBOXHGCMSVCPARM parm;
8019 parm.type = VBOX_HGCM_SVC_PARM_32BIT;
8020
8021 switch (aDragAndDropMode)
8022 {
8023 default:
8024 case DragAndDropMode_Disabled:
8025 LogRel(("Drag'n'drop mode: Off\n"));
8026 parm.u.uint32 = VBOX_DRAG_AND_DROP_MODE_OFF;
8027 break;
8028 case DragAndDropMode_GuestToHost:
8029 LogRel(("Drag'n'drop mode: Guest to Host\n"));
8030 parm.u.uint32 = VBOX_DRAG_AND_DROP_MODE_GUEST_TO_HOST;
8031 break;
8032 case DragAndDropMode_HostToGuest:
8033 LogRel(("Drag'n'drop mode: Host to Guest\n"));
8034 parm.u.uint32 = VBOX_DRAG_AND_DROP_MODE_HOST_TO_GUEST;
8035 break;
8036 case DragAndDropMode_Bidirectional:
8037 LogRel(("Drag'n'drop mode: Bidirectional\n"));
8038 parm.u.uint32 = VBOX_DRAG_AND_DROP_MODE_BIDIRECTIONAL;
8039 break;
8040 }
8041
8042 pVMMDev->hgcmHostCall("VBoxDragAndDropSvc", DragAndDropSvc::HOST_DND_SET_MODE, 1, &parm);
8043}
8044
8045#ifdef VBOX_WITH_USB
8046/**
8047 * Sends a request to VMM to attach the given host device.
8048 * After this method succeeds, the attached device will appear in the
8049 * mUSBDevices collection.
8050 *
8051 * @param aHostDevice device to attach
8052 *
8053 * @note Synchronously calls EMT.
8054 */
8055HRESULT Console::attachUSBDevice(IUSBDevice *aHostDevice, ULONG aMaskedIfs)
8056{
8057 AssertReturn(aHostDevice, E_FAIL);
8058 AssertReturn(!isWriteLockOnCurrentThread(), E_FAIL);
8059
8060 HRESULT hrc;
8061
8062 /*
8063 * Get the address and the Uuid, and call the pfnCreateProxyDevice roothub
8064 * method in EMT (using usbAttachCallback()).
8065 */
8066 Bstr BstrAddress;
8067 hrc = aHostDevice->COMGETTER(Address)(BstrAddress.asOutParam());
8068 ComAssertComRCRetRC(hrc);
8069
8070 Utf8Str Address(BstrAddress);
8071
8072 Bstr id;
8073 hrc = aHostDevice->COMGETTER(Id)(id.asOutParam());
8074 ComAssertComRCRetRC(hrc);
8075 Guid uuid(id);
8076
8077 BOOL fRemote = FALSE;
8078 hrc = aHostDevice->COMGETTER(Remote)(&fRemote);
8079 ComAssertComRCRetRC(hrc);
8080
8081 /* Get the VM handle. */
8082 SafeVMPtr ptrVM(this);
8083 if (!ptrVM.isOk())
8084 return ptrVM.rc();
8085
8086 LogFlowThisFunc(("Proxying USB device '%s' {%RTuuid}...\n",
8087 Address.c_str(), uuid.raw()));
8088
8089 void *pvRemoteBackend = NULL;
8090 if (fRemote)
8091 {
8092 RemoteUSBDevice *pRemoteUSBDevice = static_cast<RemoteUSBDevice *>(aHostDevice);
8093 pvRemoteBackend = consoleVRDPServer()->USBBackendRequestPointer(pRemoteUSBDevice->clientId(), &uuid);
8094 if (!pvRemoteBackend)
8095 return E_INVALIDARG; /* The clientId is invalid then. */
8096 }
8097
8098 USHORT portVersion = 1;
8099 hrc = aHostDevice->COMGETTER(PortVersion)(&portVersion);
8100 AssertComRCReturnRC(hrc);
8101 Assert(portVersion == 1 || portVersion == 2);
8102
8103 int vrc = VMR3ReqCallWaitU(ptrVM.rawUVM(), 0 /* idDstCpu (saved state, see #6232) */,
8104 (PFNRT)usbAttachCallback, 9,
8105 this, ptrVM.rawUVM(), aHostDevice, uuid.raw(), fRemote,
8106 Address.c_str(), pvRemoteBackend, portVersion, aMaskedIfs);
8107
8108 if (RT_SUCCESS(vrc))
8109 {
8110 /* Create a OUSBDevice and add it to the device list */
8111 ComObjPtr<OUSBDevice> pUSBDevice;
8112 pUSBDevice.createObject();
8113 hrc = pUSBDevice->init(aHostDevice);
8114 AssertComRC(hrc);
8115
8116 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8117 mUSBDevices.push_back(pUSBDevice);
8118 LogFlowFunc(("Attached device {%RTuuid}\n", pUSBDevice->id().raw()));
8119
8120 /* notify callbacks */
8121 alock.release();
8122 onUSBDeviceStateChange(pUSBDevice, true /* aAttached */, NULL);
8123 }
8124 else
8125 {
8126 LogWarningThisFunc(("Failed to create proxy device for '%s' {%RTuuid} (%Rrc)\n",
8127 Address.c_str(), uuid.raw(), vrc));
8128
8129 switch (vrc)
8130 {
8131 case VERR_VUSB_NO_PORTS:
8132 hrc = setError(E_FAIL, tr("Failed to attach the USB device. (No available ports on the USB controller)."));
8133 break;
8134 case VERR_VUSB_USBFS_PERMISSION:
8135 hrc = setError(E_FAIL, tr("Not permitted to open the USB device, check usbfs options"));
8136 break;
8137 default:
8138 hrc = setError(E_FAIL, tr("Failed to create a proxy device for the USB device. (Error: %Rrc)"), vrc);
8139 break;
8140 }
8141 }
8142
8143 return hrc;
8144}
8145
8146/**
8147 * USB device attach callback used by AttachUSBDevice().
8148 * Note that AttachUSBDevice() doesn't return until this callback is executed,
8149 * so we don't use AutoCaller and don't care about reference counters of
8150 * interface pointers passed in.
8151 *
8152 * @thread EMT
8153 * @note Locks the console object for writing.
8154 */
8155//static
8156DECLCALLBACK(int)
8157Console::usbAttachCallback(Console *that, PUVM pUVM, IUSBDevice *aHostDevice, PCRTUUID aUuid, bool aRemote,
8158 const char *aAddress, void *pvRemoteBackend, USHORT aPortVersion, ULONG aMaskedIfs)
8159{
8160 LogFlowFuncEnter();
8161 LogFlowFunc(("that={%p} aUuid={%RTuuid}\n", that, aUuid));
8162
8163 AssertReturn(that && aUuid, VERR_INVALID_PARAMETER);
8164 AssertReturn(!that->isWriteLockOnCurrentThread(), VERR_GENERAL_FAILURE);
8165
8166 int vrc = PDMR3UsbCreateProxyDevice(pUVM, aUuid, aRemote, aAddress, pvRemoteBackend,
8167 aPortVersion == 1 ? VUSB_STDVER_11 : VUSB_STDVER_20, aMaskedIfs);
8168 LogFlowFunc(("vrc=%Rrc\n", vrc));
8169 LogFlowFuncLeave();
8170 return vrc;
8171}
8172
8173/**
8174 * Sends a request to VMM to detach the given host device. After this method
8175 * succeeds, the detached device will disappear from the mUSBDevices
8176 * collection.
8177 *
8178 * @param aHostDevice device to attach
8179 *
8180 * @note Synchronously calls EMT.
8181 */
8182HRESULT Console::detachUSBDevice(const ComObjPtr<OUSBDevice> &aHostDevice)
8183{
8184 AssertReturn(!isWriteLockOnCurrentThread(), E_FAIL);
8185
8186 /* Get the VM handle. */
8187 SafeVMPtr ptrVM(this);
8188 if (!ptrVM.isOk())
8189 return ptrVM.rc();
8190
8191 /* if the device is attached, then there must at least one USB hub. */
8192 AssertReturn(PDMR3UsbHasHub(ptrVM.rawUVM()), E_FAIL);
8193
8194 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8195 LogFlowThisFunc(("Detaching USB proxy device {%RTuuid}...\n",
8196 aHostDevice->id().raw()));
8197
8198 /*
8199 * If this was a remote device, release the backend pointer.
8200 * The pointer was requested in usbAttachCallback.
8201 */
8202 BOOL fRemote = FALSE;
8203
8204 HRESULT hrc2 = aHostDevice->COMGETTER(Remote)(&fRemote);
8205 if (FAILED(hrc2))
8206 setErrorStatic(hrc2, "GetRemote() failed");
8207
8208 PCRTUUID pUuid = aHostDevice->id().raw();
8209 if (fRemote)
8210 {
8211 Guid guid(*pUuid);
8212 consoleVRDPServer()->USBBackendReleasePointer(&guid);
8213 }
8214
8215 alock.release();
8216 int vrc = VMR3ReqCallWaitU(ptrVM.rawUVM(), 0 /* idDstCpu (saved state, see #6232) */,
8217 (PFNRT)usbDetachCallback, 5,
8218 this, ptrVM.rawUVM(), pUuid);
8219 if (RT_SUCCESS(vrc))
8220 {
8221 LogFlowFunc(("Detached device {%RTuuid}\n", pUuid));
8222
8223 /* notify callbacks */
8224 onUSBDeviceStateChange(aHostDevice, false /* aAttached */, NULL);
8225 }
8226
8227 ComAssertRCRet(vrc, E_FAIL);
8228
8229 return S_OK;
8230}
8231
8232/**
8233 * USB device detach callback used by DetachUSBDevice().
8234 *
8235 * Note that DetachUSBDevice() doesn't return until this callback is executed,
8236 * so we don't use AutoCaller and don't care about reference counters of
8237 * interface pointers passed in.
8238 *
8239 * @thread EMT
8240 */
8241//static
8242DECLCALLBACK(int)
8243Console::usbDetachCallback(Console *that, PUVM pUVM, PCRTUUID aUuid)
8244{
8245 LogFlowFuncEnter();
8246 LogFlowFunc(("that={%p} aUuid={%RTuuid}\n", that, aUuid));
8247
8248 AssertReturn(that && aUuid, VERR_INVALID_PARAMETER);
8249 AssertReturn(!that->isWriteLockOnCurrentThread(), VERR_GENERAL_FAILURE);
8250
8251 int vrc = PDMR3UsbDetachDevice(pUVM, aUuid);
8252
8253 LogFlowFunc(("vrc=%Rrc\n", vrc));
8254 LogFlowFuncLeave();
8255 return vrc;
8256}
8257#endif /* VBOX_WITH_USB */
8258
8259/* Note: FreeBSD needs this whether netflt is used or not. */
8260#if ((defined(RT_OS_LINUX) && !defined(VBOX_WITH_NETFLT)) || defined(RT_OS_FREEBSD))
8261/**
8262 * Helper function to handle host interface device creation and attachment.
8263 *
8264 * @param networkAdapter the network adapter which attachment should be reset
8265 * @return COM status code
8266 *
8267 * @note The caller must lock this object for writing.
8268 *
8269 * @todo Move this back into the driver!
8270 */
8271HRESULT Console::attachToTapInterface(INetworkAdapter *networkAdapter)
8272{
8273 LogFlowThisFunc(("\n"));
8274 /* sanity check */
8275 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
8276
8277# ifdef VBOX_STRICT
8278 /* paranoia */
8279 NetworkAttachmentType_T attachment;
8280 networkAdapter->COMGETTER(AttachmentType)(&attachment);
8281 Assert(attachment == NetworkAttachmentType_Bridged);
8282# endif /* VBOX_STRICT */
8283
8284 HRESULT rc = S_OK;
8285
8286 ULONG slot = 0;
8287 rc = networkAdapter->COMGETTER(Slot)(&slot);
8288 AssertComRC(rc);
8289
8290# ifdef RT_OS_LINUX
8291 /*
8292 * Allocate a host interface device
8293 */
8294 int rcVBox = RTFileOpen(&maTapFD[slot], "/dev/net/tun",
8295 RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_INHERIT);
8296 if (RT_SUCCESS(rcVBox))
8297 {
8298 /*
8299 * Set/obtain the tap interface.
8300 */
8301 struct ifreq IfReq;
8302 RT_ZERO(IfReq);
8303 /* The name of the TAP interface we are using */
8304 Bstr tapDeviceName;
8305 rc = networkAdapter->COMGETTER(BridgedInterface)(tapDeviceName.asOutParam());
8306 if (FAILED(rc))
8307 tapDeviceName.setNull(); /* Is this necessary? */
8308 if (tapDeviceName.isEmpty())
8309 {
8310 LogRel(("No TAP device name was supplied.\n"));
8311 rc = setError(E_FAIL, tr("No TAP device name was supplied for the host networking interface"));
8312 }
8313
8314 if (SUCCEEDED(rc))
8315 {
8316 /* If we are using a static TAP device then try to open it. */
8317 Utf8Str str(tapDeviceName);
8318 RTStrCopy(IfReq.ifr_name, sizeof(IfReq.ifr_name), str.c_str()); /** @todo bitch about names which are too long... */
8319 IfReq.ifr_flags = IFF_TAP | IFF_NO_PI;
8320 rcVBox = ioctl(maTapFD[slot], TUNSETIFF, &IfReq);
8321 if (rcVBox != 0)
8322 {
8323 LogRel(("Failed to open the host network interface %ls\n", tapDeviceName.raw()));
8324 rc = setError(E_FAIL,
8325 tr("Failed to open the host network interface %ls"),
8326 tapDeviceName.raw());
8327 }
8328 }
8329 if (SUCCEEDED(rc))
8330 {
8331 /*
8332 * Make it pollable.
8333 */
8334 if (fcntl(maTapFD[slot], F_SETFL, O_NONBLOCK) != -1)
8335 {
8336 Log(("attachToTapInterface: %RTfile %ls\n", maTapFD[slot], tapDeviceName.raw()));
8337 /*
8338 * Here is the right place to communicate the TAP file descriptor and
8339 * the host interface name to the server if/when it becomes really
8340 * necessary.
8341 */
8342 maTAPDeviceName[slot] = tapDeviceName;
8343 rcVBox = VINF_SUCCESS;
8344 }
8345 else
8346 {
8347 int iErr = errno;
8348
8349 LogRel(("Configuration error: Failed to configure /dev/net/tun non blocking. Error: %s\n", strerror(iErr)));
8350 rcVBox = VERR_HOSTIF_BLOCKING;
8351 rc = setError(E_FAIL,
8352 tr("could not set up the host networking device for non blocking access: %s"),
8353 strerror(errno));
8354 }
8355 }
8356 }
8357 else
8358 {
8359 LogRel(("Configuration error: Failed to open /dev/net/tun rc=%Rrc\n", rcVBox));
8360 switch (rcVBox)
8361 {
8362 case VERR_ACCESS_DENIED:
8363 /* will be handled by our caller */
8364 rc = rcVBox;
8365 break;
8366 default:
8367 rc = setError(E_FAIL,
8368 tr("Could not set up the host networking device: %Rrc"),
8369 rcVBox);
8370 break;
8371 }
8372 }
8373
8374# elif defined(RT_OS_FREEBSD)
8375 /*
8376 * Set/obtain the tap interface.
8377 */
8378 /* The name of the TAP interface we are using */
8379 Bstr tapDeviceName;
8380 rc = networkAdapter->COMGETTER(BridgedInterface)(tapDeviceName.asOutParam());
8381 if (FAILED(rc))
8382 tapDeviceName.setNull(); /* Is this necessary? */
8383 if (tapDeviceName.isEmpty())
8384 {
8385 LogRel(("No TAP device name was supplied.\n"));
8386 rc = setError(E_FAIL, tr("No TAP device name was supplied for the host networking interface"));
8387 }
8388 char szTapdev[1024] = "/dev/";
8389 /* If we are using a static TAP device then try to open it. */
8390 Utf8Str str(tapDeviceName);
8391 if (str.length() + strlen(szTapdev) <= sizeof(szTapdev))
8392 strcat(szTapdev, str.c_str());
8393 else
8394 memcpy(szTapdev + strlen(szTapdev), str.c_str(),
8395 sizeof(szTapdev) - strlen(szTapdev) - 1); /** @todo bitch about names which are too long... */
8396 int rcVBox = RTFileOpen(&maTapFD[slot], szTapdev,
8397 RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_INHERIT | RTFILE_O_NON_BLOCK);
8398
8399 if (RT_SUCCESS(rcVBox))
8400 maTAPDeviceName[slot] = tapDeviceName;
8401 else
8402 {
8403 switch (rcVBox)
8404 {
8405 case VERR_ACCESS_DENIED:
8406 /* will be handled by our caller */
8407 rc = rcVBox;
8408 break;
8409 default:
8410 rc = setError(E_FAIL,
8411 tr("Failed to open the host network interface %ls"),
8412 tapDeviceName.raw());
8413 break;
8414 }
8415 }
8416# else
8417# error "huh?"
8418# endif
8419 /* in case of failure, cleanup. */
8420 if (RT_FAILURE(rcVBox) && SUCCEEDED(rc))
8421 {
8422 LogRel(("General failure attaching to host interface\n"));
8423 rc = setError(E_FAIL,
8424 tr("General failure attaching to host interface"));
8425 }
8426 LogFlowThisFunc(("rc=%d\n", rc));
8427 return rc;
8428}
8429
8430
8431/**
8432 * Helper function to handle detachment from a host interface
8433 *
8434 * @param networkAdapter the network adapter which attachment should be reset
8435 * @return COM status code
8436 *
8437 * @note The caller must lock this object for writing.
8438 *
8439 * @todo Move this back into the driver!
8440 */
8441HRESULT Console::detachFromTapInterface(INetworkAdapter *networkAdapter)
8442{
8443 /* sanity check */
8444 LogFlowThisFunc(("\n"));
8445 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
8446
8447 HRESULT rc = S_OK;
8448# ifdef VBOX_STRICT
8449 /* paranoia */
8450 NetworkAttachmentType_T attachment;
8451 networkAdapter->COMGETTER(AttachmentType)(&attachment);
8452 Assert(attachment == NetworkAttachmentType_Bridged);
8453# endif /* VBOX_STRICT */
8454
8455 ULONG slot = 0;
8456 rc = networkAdapter->COMGETTER(Slot)(&slot);
8457 AssertComRC(rc);
8458
8459 /* is there an open TAP device? */
8460 if (maTapFD[slot] != NIL_RTFILE)
8461 {
8462 /*
8463 * Close the file handle.
8464 */
8465 Bstr tapDeviceName, tapTerminateApplication;
8466 bool isStatic = true;
8467 rc = networkAdapter->COMGETTER(BridgedInterface)(tapDeviceName.asOutParam());
8468 if (FAILED(rc) || tapDeviceName.isEmpty())
8469 {
8470 /* If the name is empty, this is a dynamic TAP device, so close it now,
8471 so that the termination script can remove the interface. Otherwise we still
8472 need the FD to pass to the termination script. */
8473 isStatic = false;
8474 int rcVBox = RTFileClose(maTapFD[slot]);
8475 AssertRC(rcVBox);
8476 maTapFD[slot] = NIL_RTFILE;
8477 }
8478 if (isStatic)
8479 {
8480 /* If we are using a static TAP device, we close it now, after having called the
8481 termination script. */
8482 int rcVBox = RTFileClose(maTapFD[slot]);
8483 AssertRC(rcVBox);
8484 }
8485 /* the TAP device name and handle are no longer valid */
8486 maTapFD[slot] = NIL_RTFILE;
8487 maTAPDeviceName[slot] = "";
8488 }
8489 LogFlowThisFunc(("returning %d\n", rc));
8490 return rc;
8491}
8492#endif /* (RT_OS_LINUX || RT_OS_FREEBSD) && !VBOX_WITH_NETFLT */
8493
8494/**
8495 * Called at power down to terminate host interface networking.
8496 *
8497 * @note The caller must lock this object for writing.
8498 */
8499HRESULT Console::powerDownHostInterfaces()
8500{
8501 LogFlowThisFunc(("\n"));
8502
8503 /* sanity check */
8504 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
8505
8506 /*
8507 * host interface termination handling
8508 */
8509 HRESULT rc = S_OK;
8510 ComPtr<IVirtualBox> pVirtualBox;
8511 mMachine->COMGETTER(Parent)(pVirtualBox.asOutParam());
8512 ComPtr<ISystemProperties> pSystemProperties;
8513 if (pVirtualBox)
8514 pVirtualBox->COMGETTER(SystemProperties)(pSystemProperties.asOutParam());
8515 ChipsetType_T chipsetType = ChipsetType_PIIX3;
8516 mMachine->COMGETTER(ChipsetType)(&chipsetType);
8517 ULONG maxNetworkAdapters = 0;
8518 if (pSystemProperties)
8519 pSystemProperties->GetMaxNetworkAdapters(chipsetType, &maxNetworkAdapters);
8520
8521 for (ULONG slot = 0; slot < maxNetworkAdapters; slot++)
8522 {
8523 ComPtr<INetworkAdapter> pNetworkAdapter;
8524 rc = mMachine->GetNetworkAdapter(slot, pNetworkAdapter.asOutParam());
8525 if (FAILED(rc)) break;
8526
8527 BOOL enabled = FALSE;
8528 pNetworkAdapter->COMGETTER(Enabled)(&enabled);
8529 if (!enabled)
8530 continue;
8531
8532 NetworkAttachmentType_T attachment;
8533 pNetworkAdapter->COMGETTER(AttachmentType)(&attachment);
8534 if (attachment == NetworkAttachmentType_Bridged)
8535 {
8536#if ((defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD)) && !defined(VBOX_WITH_NETFLT))
8537 HRESULT rc2 = detachFromTapInterface(pNetworkAdapter);
8538 if (FAILED(rc2) && SUCCEEDED(rc))
8539 rc = rc2;
8540#endif /* (RT_OS_LINUX || RT_OS_FREEBSD) && !VBOX_WITH_NETFLT */
8541 }
8542 }
8543
8544 return rc;
8545}
8546
8547
8548/**
8549 * Process callback handler for VMR3LoadFromFile, VMR3LoadFromStream, VMR3Save
8550 * and VMR3Teleport.
8551 *
8552 * @param pUVM The user mode VM handle.
8553 * @param uPercent Completion percentage (0-100).
8554 * @param pvUser Pointer to an IProgress instance.
8555 * @return VINF_SUCCESS.
8556 */
8557/*static*/
8558DECLCALLBACK(int) Console::stateProgressCallback(PUVM pUVM, unsigned uPercent, void *pvUser)
8559{
8560 IProgress *pProgress = static_cast<IProgress *>(pvUser);
8561
8562 /* update the progress object */
8563 if (pProgress)
8564 pProgress->SetCurrentOperationProgress(uPercent);
8565
8566 NOREF(pUVM);
8567 return VINF_SUCCESS;
8568}
8569
8570/**
8571 * @copydoc FNVMATERROR
8572 *
8573 * @remarks Might be some tiny serialization concerns with access to the string
8574 * object here...
8575 */
8576/*static*/ DECLCALLBACK(void)
8577Console::genericVMSetErrorCallback(PUVM pUVM, void *pvUser, int rc, RT_SRC_POS_DECL,
8578 const char *pszErrorFmt, va_list va)
8579{
8580 Utf8Str *pErrorText = (Utf8Str *)pvUser;
8581 AssertPtr(pErrorText);
8582
8583 /* We ignore RT_SRC_POS_DECL arguments to avoid confusion of end-users. */
8584 va_list va2;
8585 va_copy(va2, va);
8586
8587 /* Append to any the existing error message. */
8588 if (pErrorText->length())
8589 *pErrorText = Utf8StrFmt("%s.\n%N (%Rrc)", pErrorText->c_str(),
8590 pszErrorFmt, &va2, rc, rc);
8591 else
8592 *pErrorText = Utf8StrFmt("%N (%Rrc)", pszErrorFmt, &va2, rc, rc);
8593
8594 va_end(va2);
8595
8596 NOREF(pUVM);
8597}
8598
8599/**
8600 * VM runtime error callback function.
8601 * See VMSetRuntimeError for the detailed description of parameters.
8602 *
8603 * @param pUVM The user mode VM handle. Ignored, so passing NULL
8604 * is fine.
8605 * @param pvUser The user argument, pointer to the Console instance.
8606 * @param fFlags The action flags. See VMSETRTERR_FLAGS_*.
8607 * @param pszErrorId Error ID string.
8608 * @param pszFormat Error message format string.
8609 * @param va Error message arguments.
8610 * @thread EMT.
8611 */
8612/* static */ DECLCALLBACK(void)
8613Console::setVMRuntimeErrorCallback(PUVM pUVM, void *pvUser, uint32_t fFlags,
8614 const char *pszErrorId,
8615 const char *pszFormat, va_list va)
8616{
8617 bool const fFatal = !!(fFlags & VMSETRTERR_FLAGS_FATAL);
8618 LogFlowFuncEnter();
8619
8620 Console *that = static_cast<Console *>(pvUser);
8621 AssertReturnVoid(that);
8622
8623 Utf8Str message(pszFormat, va);
8624
8625 LogRel(("Console: VM runtime error: fatal=%RTbool, errorID=%s message=\"%s\"\n",
8626 fFatal, pszErrorId, message.c_str()));
8627
8628 that->onRuntimeError(BOOL(fFatal), Bstr(pszErrorId).raw(), Bstr(message).raw());
8629
8630 LogFlowFuncLeave(); NOREF(pUVM);
8631}
8632
8633/**
8634 * Captures USB devices that match filters of the VM.
8635 * Called at VM startup.
8636 *
8637 * @param pUVM The VM handle.
8638 */
8639HRESULT Console::captureUSBDevices(PUVM pUVM)
8640{
8641 LogFlowThisFunc(("\n"));
8642
8643 /* sanity check */
8644 AssertReturn(!isWriteLockOnCurrentThread(), E_FAIL);
8645 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8646
8647 /* If the machine has a USB controller, ask the USB proxy service to
8648 * capture devices */
8649 if (mfVMHasUsbController)
8650 {
8651 /* release the lock before calling Host in VBoxSVC since Host may call
8652 * us back from under its lock (e.g. onUSBDeviceAttach()) which would
8653 * produce an inter-process dead-lock otherwise. */
8654 alock.release();
8655
8656 HRESULT hrc = mControl->AutoCaptureUSBDevices();
8657 ComAssertComRCRetRC(hrc);
8658 }
8659
8660 return S_OK;
8661}
8662
8663
8664/**
8665 * Detach all USB device which are attached to the VM for the
8666 * purpose of clean up and such like.
8667 */
8668void Console::detachAllUSBDevices(bool aDone)
8669{
8670 LogFlowThisFunc(("aDone=%RTbool\n", aDone));
8671
8672 /* sanity check */
8673 AssertReturnVoid(!isWriteLockOnCurrentThread());
8674 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8675
8676 mUSBDevices.clear();
8677
8678 /* release the lock before calling Host in VBoxSVC since Host may call
8679 * us back from under its lock (e.g. onUSBDeviceAttach()) which would
8680 * produce an inter-process dead-lock otherwise. */
8681 alock.release();
8682
8683 mControl->DetachAllUSBDevices(aDone);
8684}
8685
8686/**
8687 * @note Locks this object for writing.
8688 */
8689void Console::processRemoteUSBDevices(uint32_t u32ClientId, VRDEUSBDEVICEDESC *pDevList, uint32_t cbDevList, bool fDescExt)
8690{
8691 LogFlowThisFuncEnter();
8692 LogFlowThisFunc(("u32ClientId = %d, pDevList=%p, cbDevList = %d, fDescExt = %d\n", u32ClientId, pDevList, cbDevList, fDescExt));
8693
8694 AutoCaller autoCaller(this);
8695 if (!autoCaller.isOk())
8696 {
8697 /* Console has been already uninitialized, deny request */
8698 AssertMsgFailed(("Console is already uninitialized\n"));
8699 LogFlowThisFunc(("Console is already uninitialized\n"));
8700 LogFlowThisFuncLeave();
8701 return;
8702 }
8703
8704 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8705
8706 /*
8707 * Mark all existing remote USB devices as dirty.
8708 */
8709 for (RemoteUSBDeviceList::iterator it = mRemoteUSBDevices.begin();
8710 it != mRemoteUSBDevices.end();
8711 ++it)
8712 {
8713 (*it)->dirty(true);
8714 }
8715
8716 /*
8717 * Process the pDevList and add devices those are not already in the mRemoteUSBDevices list.
8718 */
8719 /** @todo (sunlover) REMOTE_USB Strict validation of the pDevList. */
8720 VRDEUSBDEVICEDESC *e = pDevList;
8721
8722 /* The cbDevList condition must be checked first, because the function can
8723 * receive pDevList = NULL and cbDevList = 0 on client disconnect.
8724 */
8725 while (cbDevList >= 2 && e->oNext)
8726 {
8727 /* Sanitize incoming strings in case they aren't valid UTF-8. */
8728 if (e->oManufacturer)
8729 RTStrPurgeEncoding((char *)e + e->oManufacturer);
8730 if (e->oProduct)
8731 RTStrPurgeEncoding((char *)e + e->oProduct);
8732 if (e->oSerialNumber)
8733 RTStrPurgeEncoding((char *)e + e->oSerialNumber);
8734
8735 LogFlowThisFunc(("vendor %04X, product %04X, name = %s\n",
8736 e->idVendor, e->idProduct,
8737 e->oProduct? (char *)e + e->oProduct: ""));
8738
8739 bool fNewDevice = true;
8740
8741 for (RemoteUSBDeviceList::iterator it = mRemoteUSBDevices.begin();
8742 it != mRemoteUSBDevices.end();
8743 ++it)
8744 {
8745 if ((*it)->devId() == e->id
8746 && (*it)->clientId() == u32ClientId)
8747 {
8748 /* The device is already in the list. */
8749 (*it)->dirty(false);
8750 fNewDevice = false;
8751 break;
8752 }
8753 }
8754
8755 if (fNewDevice)
8756 {
8757 LogRel(("Remote USB: ++++ Vendor %04X. Product %04X. Name = [%s].\n",
8758 e->idVendor, e->idProduct, e->oProduct? (char *)e + e->oProduct: ""));
8759
8760 /* Create the device object and add the new device to list. */
8761 ComObjPtr<RemoteUSBDevice> pUSBDevice;
8762 pUSBDevice.createObject();
8763 pUSBDevice->init(u32ClientId, e, fDescExt);
8764
8765 mRemoteUSBDevices.push_back(pUSBDevice);
8766
8767 /* Check if the device is ok for current USB filters. */
8768 BOOL fMatched = FALSE;
8769 ULONG fMaskedIfs = 0;
8770
8771 HRESULT hrc = mControl->RunUSBDeviceFilters(pUSBDevice, &fMatched, &fMaskedIfs);
8772
8773 AssertComRC(hrc);
8774
8775 LogFlowThisFunc(("USB filters return %d %#x\n", fMatched, fMaskedIfs));
8776
8777 if (fMatched)
8778 {
8779 alock.release();
8780 hrc = onUSBDeviceAttach(pUSBDevice, NULL, fMaskedIfs);
8781 alock.acquire();
8782
8783 /// @todo (r=dmik) warning reporting subsystem
8784
8785 if (hrc == S_OK)
8786 {
8787 LogFlowThisFunc(("Device attached\n"));
8788 pUSBDevice->captured(true);
8789 }
8790 }
8791 }
8792
8793 if (cbDevList < e->oNext)
8794 {
8795 LogWarningThisFunc(("cbDevList %d > oNext %d\n",
8796 cbDevList, e->oNext));
8797 break;
8798 }
8799
8800 cbDevList -= e->oNext;
8801
8802 e = (VRDEUSBDEVICEDESC *)((uint8_t *)e + e->oNext);
8803 }
8804
8805 /*
8806 * Remove dirty devices, that is those which are not reported by the server anymore.
8807 */
8808 for (;;)
8809 {
8810 ComObjPtr<RemoteUSBDevice> pUSBDevice;
8811
8812 RemoteUSBDeviceList::iterator it = mRemoteUSBDevices.begin();
8813 while (it != mRemoteUSBDevices.end())
8814 {
8815 if ((*it)->dirty())
8816 {
8817 pUSBDevice = *it;
8818 break;
8819 }
8820
8821 ++it;
8822 }
8823
8824 if (!pUSBDevice)
8825 {
8826 break;
8827 }
8828
8829 USHORT vendorId = 0;
8830 pUSBDevice->COMGETTER(VendorId)(&vendorId);
8831
8832 USHORT productId = 0;
8833 pUSBDevice->COMGETTER(ProductId)(&productId);
8834
8835 Bstr product;
8836 pUSBDevice->COMGETTER(Product)(product.asOutParam());
8837
8838 LogRel(("Remote USB: ---- Vendor %04X. Product %04X. Name = [%ls].\n",
8839 vendorId, productId, product.raw()));
8840
8841 /* Detach the device from VM. */
8842 if (pUSBDevice->captured())
8843 {
8844 Bstr uuid;
8845 pUSBDevice->COMGETTER(Id)(uuid.asOutParam());
8846 alock.release();
8847 onUSBDeviceDetach(uuid.raw(), NULL);
8848 alock.acquire();
8849 }
8850
8851 /* And remove it from the list. */
8852 mRemoteUSBDevices.erase(it);
8853 }
8854
8855 LogFlowThisFuncLeave();
8856}
8857
8858/**
8859 * Progress cancelation callback for fault tolerance VM poweron
8860 */
8861static void faultToleranceProgressCancelCallback(void *pvUser)
8862{
8863 PUVM pUVM = (PUVM)pvUser;
8864
8865 if (pUVM)
8866 FTMR3CancelStandby(pUVM);
8867}
8868
8869/**
8870 * Thread function which starts the VM (also from saved state) and
8871 * track progress.
8872 *
8873 * @param Thread The thread id.
8874 * @param pvUser Pointer to a VMPowerUpTask structure.
8875 * @return VINF_SUCCESS (ignored).
8876 *
8877 * @note Locks the Console object for writing.
8878 */
8879/*static*/
8880DECLCALLBACK(int) Console::powerUpThread(RTTHREAD Thread, void *pvUser)
8881{
8882 LogFlowFuncEnter();
8883
8884 std::auto_ptr<VMPowerUpTask> task(static_cast<VMPowerUpTask *>(pvUser));
8885 AssertReturn(task.get(), VERR_INVALID_PARAMETER);
8886
8887 AssertReturn(!task->mConsole.isNull(), VERR_INVALID_PARAMETER);
8888 AssertReturn(!task->mProgress.isNull(), VERR_INVALID_PARAMETER);
8889
8890 VirtualBoxBase::initializeComForThread();
8891
8892 HRESULT rc = S_OK;
8893 int vrc = VINF_SUCCESS;
8894
8895 /* Set up a build identifier so that it can be seen from core dumps what
8896 * exact build was used to produce the core. */
8897 static char saBuildID[40];
8898 RTStrPrintf(saBuildID, sizeof(saBuildID), "%s%s%s%s VirtualBox %s r%u %s%s%s%s",
8899 "BU", "IL", "DI", "D", RTBldCfgVersion(), RTBldCfgRevision(), "BU", "IL", "DI", "D");
8900
8901 ComObjPtr<Console> pConsole = task->mConsole;
8902
8903 /* Note: no need to use addCaller() because VMPowerUpTask does that */
8904
8905 /* The lock is also used as a signal from the task initiator (which
8906 * releases it only after RTThreadCreate()) that we can start the job */
8907 AutoWriteLock alock(pConsole COMMA_LOCKVAL_SRC_POS);
8908
8909 /* sanity */
8910 Assert(pConsole->mpUVM == NULL);
8911
8912 try
8913 {
8914 // Create the VMM device object, which starts the HGCM thread; do this only
8915 // once for the console, for the pathological case that the same console
8916 // object is used to power up a VM twice. VirtualBox 4.0: we now do that
8917 // here instead of the Console constructor (see Console::init())
8918 if (!pConsole->m_pVMMDev)
8919 {
8920 pConsole->m_pVMMDev = new VMMDev(pConsole);
8921 AssertReturn(pConsole->m_pVMMDev, E_FAIL);
8922 }
8923
8924 /* wait for auto reset ops to complete so that we can successfully lock
8925 * the attached hard disks by calling LockMedia() below */
8926 for (VMPowerUpTask::ProgressList::const_iterator
8927 it = task->hardDiskProgresses.begin();
8928 it != task->hardDiskProgresses.end(); ++it)
8929 {
8930 HRESULT rc2 = (*it)->WaitForCompletion(-1);
8931 AssertComRC(rc2);
8932
8933 rc = task->mProgress->SetNextOperation(BstrFmt(tr("Disk Image Reset Operation - Immutable Image")).raw(), 1);
8934 AssertComRCReturnRC(rc);
8935 }
8936
8937 /*
8938 * Lock attached media. This method will also check their accessibility.
8939 * If we're a teleporter, we'll have to postpone this action so we can
8940 * migrate between local processes.
8941 *
8942 * Note! The media will be unlocked automatically by
8943 * SessionMachine::setMachineState() when the VM is powered down.
8944 */
8945 if ( !task->mTeleporterEnabled
8946 && task->mEnmFaultToleranceState != FaultToleranceState_Standby)
8947 {
8948 rc = pConsole->mControl->LockMedia();
8949 if (FAILED(rc)) throw rc;
8950 }
8951
8952 /* Create the VRDP server. In case of headless operation, this will
8953 * also create the framebuffer, required at VM creation.
8954 */
8955 ConsoleVRDPServer *server = pConsole->consoleVRDPServer();
8956 Assert(server);
8957
8958 /* Does VRDP server call Console from the other thread?
8959 * Not sure (and can change), so release the lock just in case.
8960 */
8961 alock.release();
8962 vrc = server->Launch();
8963 alock.acquire();
8964
8965 if (vrc == VERR_NET_ADDRESS_IN_USE)
8966 {
8967 Utf8Str errMsg;
8968 Bstr bstr;
8969 pConsole->mVRDEServer->GetVRDEProperty(Bstr("TCP/Ports").raw(), bstr.asOutParam());
8970 Utf8Str ports = bstr;
8971 errMsg = Utf8StrFmt(tr("VirtualBox Remote Desktop Extension server can't bind to the port: %s"),
8972 ports.c_str());
8973 LogRel(("VRDE: Warning: failed to launch VRDE server (%Rrc): '%s'\n",
8974 vrc, errMsg.c_str()));
8975 }
8976 else if (vrc == VINF_NOT_SUPPORTED)
8977 {
8978 /* This means that the VRDE is not installed. */
8979 LogRel(("VRDE: VirtualBox Remote Desktop Extension is not available.\n"));
8980 }
8981 else if (RT_FAILURE(vrc))
8982 {
8983 /* Fail, if the server is installed but can't start. */
8984 Utf8Str errMsg;
8985 switch (vrc)
8986 {
8987 case VERR_FILE_NOT_FOUND:
8988 {
8989 /* VRDE library file is missing. */
8990 errMsg = Utf8StrFmt(tr("Could not find the VirtualBox Remote Desktop Extension library."));
8991 break;
8992 }
8993 default:
8994 errMsg = Utf8StrFmt(tr("Failed to launch Remote Desktop Extension server (%Rrc)"),
8995 vrc);
8996 }
8997 LogRel(("VRDE: Failed: (%Rrc), error message: '%s'\n",
8998 vrc, errMsg.c_str()));
8999 throw setErrorStatic(E_FAIL, errMsg.c_str());
9000 }
9001
9002 ComPtr<IMachine> pMachine = pConsole->machine();
9003 ULONG cCpus = 1;
9004 pMachine->COMGETTER(CPUCount)(&cCpus);
9005
9006 /*
9007 * Create the VM
9008 *
9009 * Note! Release the lock since EMT will call Console. It's safe because
9010 * mMachineState is either Starting or Restoring state here.
9011 */
9012 alock.release();
9013
9014 PVM pVM;
9015 vrc = VMR3Create(cCpus,
9016 pConsole->mpVmm2UserMethods,
9017 Console::genericVMSetErrorCallback,
9018 &task->mErrorMsg,
9019 task->mConfigConstructor,
9020 static_cast<Console *>(pConsole),
9021 &pVM, NULL);
9022
9023 alock.acquire();
9024
9025 /* Enable client connections to the server. */
9026 pConsole->consoleVRDPServer()->EnableConnections();
9027
9028 if (RT_SUCCESS(vrc))
9029 {
9030 do
9031 {
9032 /*
9033 * Register our load/save state file handlers
9034 */
9035 vrc = SSMR3RegisterExternal(pConsole->mpUVM, sSSMConsoleUnit, 0 /*iInstance*/, sSSMConsoleVer, 0 /* cbGuess */,
9036 NULL, NULL, NULL,
9037 NULL, saveStateFileExec, NULL,
9038 NULL, loadStateFileExec, NULL,
9039 static_cast<Console *>(pConsole));
9040 AssertRCBreak(vrc);
9041
9042 vrc = static_cast<Console *>(pConsole)->getDisplay()->registerSSM(pConsole->mpUVM);
9043 AssertRC(vrc);
9044 if (RT_FAILURE(vrc))
9045 break;
9046
9047 /*
9048 * Synchronize debugger settings
9049 */
9050 MachineDebugger *machineDebugger = pConsole->getMachineDebugger();
9051 if (machineDebugger)
9052 machineDebugger->flushQueuedSettings();
9053
9054 /*
9055 * Shared Folders
9056 */
9057 if (pConsole->m_pVMMDev->isShFlActive())
9058 {
9059 /* Does the code below call Console from the other thread?
9060 * Not sure, so release the lock just in case. */
9061 alock.release();
9062
9063 for (SharedFolderDataMap::const_iterator it = task->mSharedFolders.begin();
9064 it != task->mSharedFolders.end();
9065 ++it)
9066 {
9067 const SharedFolderData &d = it->second;
9068 rc = pConsole->createSharedFolder(it->first, d);
9069 if (FAILED(rc))
9070 {
9071 ErrorInfoKeeper eik;
9072 pConsole->setVMRuntimeErrorCallbackF(0, "BrokenSharedFolder",
9073 N_("The shared folder '%s' could not be set up: %ls.\n"
9074 "The shared folder setup will not be complete. It is recommended to power down the virtual "
9075 "machine and fix the shared folder settings while the machine is not running"),
9076 it->first.c_str(), eik.getText().raw());
9077 }
9078 }
9079 if (FAILED(rc))
9080 rc = S_OK; // do not fail with broken shared folders
9081
9082 /* acquire the lock again */
9083 alock.acquire();
9084 }
9085
9086 /* release the lock before a lengthy operation */
9087 alock.release();
9088
9089 /*
9090 * Capture USB devices.
9091 */
9092 rc = pConsole->captureUSBDevices(pConsole->mpUVM);
9093 if (FAILED(rc))
9094 break;
9095
9096 /* Load saved state? */
9097 if (task->mSavedStateFile.length())
9098 {
9099 LogFlowFunc(("Restoring saved state from '%s'...\n",
9100 task->mSavedStateFile.c_str()));
9101
9102 vrc = VMR3LoadFromFile(pConsole->mpUVM,
9103 task->mSavedStateFile.c_str(),
9104 Console::stateProgressCallback,
9105 static_cast<IProgress *>(task->mProgress));
9106
9107 if (RT_SUCCESS(vrc))
9108 {
9109 if (task->mStartPaused)
9110 /* done */
9111 pConsole->setMachineState(MachineState_Paused);
9112 else
9113 {
9114 /* Start/Resume the VM execution */
9115#ifdef VBOX_WITH_EXTPACK
9116 vrc = pConsole->mptrExtPackManager->callAllVmPowerOnHooks(pConsole, pVM);
9117#endif
9118 if (RT_SUCCESS(vrc))
9119 vrc = VMR3Resume(pConsole->mpUVM, VMRESUMEREASON_STATE_RESTORED);
9120 AssertLogRelRC(vrc);
9121 }
9122 }
9123
9124 /* Power off in case we failed loading or resuming the VM */
9125 if (RT_FAILURE(vrc))
9126 {
9127 int vrc2 = VMR3PowerOff(pConsole->mpUVM); AssertLogRelRC(vrc2);
9128#ifdef VBOX_WITH_EXTPACK
9129 pConsole->mptrExtPackManager->callAllVmPowerOffHooks(pConsole, pVM);
9130#endif
9131 }
9132 }
9133 else if (task->mTeleporterEnabled)
9134 {
9135 /* -> ConsoleImplTeleporter.cpp */
9136 bool fPowerOffOnFailure;
9137 rc = pConsole->teleporterTrg(pConsole->mpUVM, pMachine, &task->mErrorMsg, task->mStartPaused,
9138 task->mProgress, &fPowerOffOnFailure);
9139 if (FAILED(rc) && fPowerOffOnFailure)
9140 {
9141 ErrorInfoKeeper eik;
9142 int vrc2 = VMR3PowerOff(pConsole->mpUVM); AssertLogRelRC(vrc2);
9143#ifdef VBOX_WITH_EXTPACK
9144 pConsole->mptrExtPackManager->callAllVmPowerOffHooks(pConsole, pVM);
9145#endif
9146 }
9147 }
9148 else if (task->mEnmFaultToleranceState != FaultToleranceState_Inactive)
9149 {
9150 /*
9151 * Get the config.
9152 */
9153 ULONG uPort;
9154 ULONG uInterval;
9155 Bstr bstrAddress, bstrPassword;
9156
9157 rc = pMachine->COMGETTER(FaultTolerancePort)(&uPort);
9158 if (SUCCEEDED(rc))
9159 {
9160 rc = pMachine->COMGETTER(FaultToleranceSyncInterval)(&uInterval);
9161 if (SUCCEEDED(rc))
9162 rc = pMachine->COMGETTER(FaultToleranceAddress)(bstrAddress.asOutParam());
9163 if (SUCCEEDED(rc))
9164 rc = pMachine->COMGETTER(FaultTolerancePassword)(bstrPassword.asOutParam());
9165 }
9166 if (task->mProgress->setCancelCallback(faultToleranceProgressCancelCallback, pConsole->mpUVM))
9167 {
9168 if (SUCCEEDED(rc))
9169 {
9170 Utf8Str strAddress(bstrAddress);
9171 const char *pszAddress = strAddress.isEmpty() ? NULL : strAddress.c_str();
9172 Utf8Str strPassword(bstrPassword);
9173 const char *pszPassword = strPassword.isEmpty() ? NULL : strPassword.c_str();
9174
9175 /* Power on the FT enabled VM. */
9176#ifdef VBOX_WITH_EXTPACK
9177 vrc = pConsole->mptrExtPackManager->callAllVmPowerOnHooks(pConsole, pVM);
9178#endif
9179 if (RT_SUCCESS(vrc))
9180 vrc = FTMR3PowerOn(pConsole->mpUVM,
9181 task->mEnmFaultToleranceState == FaultToleranceState_Master /* fMaster */,
9182 uInterval,
9183 pszAddress,
9184 uPort,
9185 pszPassword);
9186 AssertLogRelRC(vrc);
9187 }
9188 task->mProgress->setCancelCallback(NULL, NULL);
9189 }
9190 else
9191 rc = E_FAIL;
9192 }
9193 else if (task->mStartPaused)
9194 /* done */
9195 pConsole->setMachineState(MachineState_Paused);
9196 else
9197 {
9198 /* Power on the VM (i.e. start executing) */
9199#ifdef VBOX_WITH_EXTPACK
9200 vrc = pConsole->mptrExtPackManager->callAllVmPowerOnHooks(pConsole, pVM);
9201#endif
9202 if (RT_SUCCESS(vrc))
9203 vrc = VMR3PowerOn(pConsole->mpUVM);
9204 AssertLogRelRC(vrc);
9205 }
9206
9207 /* acquire the lock again */
9208 alock.acquire();
9209 }
9210 while (0);
9211
9212 /* On failure, destroy the VM */
9213 if (FAILED(rc) || RT_FAILURE(vrc))
9214 {
9215 /* preserve existing error info */
9216 ErrorInfoKeeper eik;
9217
9218 /* powerDown() will call VMR3Destroy() and do all necessary
9219 * cleanup (VRDP, USB devices) */
9220 alock.release();
9221 HRESULT rc2 = pConsole->powerDown();
9222 alock.acquire();
9223 AssertComRC(rc2);
9224 }
9225 else
9226 {
9227 /*
9228 * Deregister the VMSetError callback. This is necessary as the
9229 * pfnVMAtError() function passed to VMR3Create() is supposed to
9230 * be sticky but our error callback isn't.
9231 */
9232 alock.release();
9233 VMR3AtErrorDeregister(pConsole->mpUVM, Console::genericVMSetErrorCallback, &task->mErrorMsg);
9234 /** @todo register another VMSetError callback? */
9235 alock.acquire();
9236 }
9237 }
9238 else
9239 {
9240 /*
9241 * If VMR3Create() failed it has released the VM memory.
9242 */
9243 VMR3ReleaseUVM(pConsole->mpUVM);
9244 pConsole->mpUVM = NULL;
9245 }
9246
9247 if (SUCCEEDED(rc) && RT_FAILURE(vrc))
9248 {
9249 /* If VMR3Create() or one of the other calls in this function fail,
9250 * an appropriate error message has been set in task->mErrorMsg.
9251 * However since that happens via a callback, the rc status code in
9252 * this function is not updated.
9253 */
9254 if (!task->mErrorMsg.length())
9255 {
9256 /* If the error message is not set but we've got a failure,
9257 * convert the VBox status code into a meaningful error message.
9258 * This becomes unused once all the sources of errors set the
9259 * appropriate error message themselves.
9260 */
9261 AssertMsgFailed(("Missing error message during powerup for status code %Rrc\n", vrc));
9262 task->mErrorMsg = Utf8StrFmt(tr("Failed to start VM execution (%Rrc)"),
9263 vrc);
9264 }
9265
9266 /* Set the error message as the COM error.
9267 * Progress::notifyComplete() will pick it up later. */
9268 throw setErrorStatic(E_FAIL, task->mErrorMsg.c_str());
9269 }
9270 }
9271 catch (HRESULT aRC) { rc = aRC; }
9272
9273 if ( pConsole->mMachineState == MachineState_Starting
9274 || pConsole->mMachineState == MachineState_Restoring
9275 || pConsole->mMachineState == MachineState_TeleportingIn
9276 )
9277 {
9278 /* We are still in the Starting/Restoring state. This means one of:
9279 *
9280 * 1) we failed before VMR3Create() was called;
9281 * 2) VMR3Create() failed.
9282 *
9283 * In both cases, there is no need to call powerDown(), but we still
9284 * need to go back to the PoweredOff/Saved state. Reuse
9285 * vmstateChangeCallback() for that purpose.
9286 */
9287
9288 /* preserve existing error info */
9289 ErrorInfoKeeper eik;
9290
9291 Assert(pConsole->mpUVM == NULL);
9292 vmstateChangeCallback(NULL, VMSTATE_TERMINATED, VMSTATE_CREATING, pConsole);
9293 }
9294
9295 /*
9296 * Evaluate the final result. Note that the appropriate mMachineState value
9297 * is already set by vmstateChangeCallback() in all cases.
9298 */
9299
9300 /* release the lock, don't need it any more */
9301 alock.release();
9302
9303 if (SUCCEEDED(rc))
9304 {
9305 /* Notify the progress object of the success */
9306 task->mProgress->notifyComplete(S_OK);
9307 }
9308 else
9309 {
9310 /* The progress object will fetch the current error info */
9311 task->mProgress->notifyComplete(rc);
9312 LogRel(("Power up failed (vrc=%Rrc, rc=%Rhrc (%#08X))\n", vrc, rc, rc));
9313 }
9314
9315 /* Notify VBoxSVC and any waiting openRemoteSession progress object. */
9316 pConsole->mControl->EndPowerUp(rc);
9317
9318#if defined(RT_OS_WINDOWS)
9319 /* uninitialize COM */
9320 CoUninitialize();
9321#endif
9322
9323 LogFlowFuncLeave();
9324
9325 return VINF_SUCCESS;
9326}
9327
9328
9329/**
9330 * Reconfigures a medium attachment (part of taking or deleting an online snapshot).
9331 *
9332 * @param pConsole Reference to the console object.
9333 * @param pUVM The VM handle.
9334 * @param lInstance The instance of the controller.
9335 * @param pcszDevice The name of the controller type.
9336 * @param enmBus The storage bus type of the controller.
9337 * @param fSetupMerge Whether to set up a medium merge
9338 * @param uMergeSource Merge source image index
9339 * @param uMergeTarget Merge target image index
9340 * @param aMediumAtt The medium attachment.
9341 * @param aMachineState The current machine state.
9342 * @param phrc Where to store com error - only valid if we return VERR_GENERAL_FAILURE.
9343 * @return VBox status code.
9344 */
9345/* static */
9346DECLCALLBACK(int) Console::reconfigureMediumAttachment(Console *pConsole,
9347 PUVM pUVM,
9348 const char *pcszDevice,
9349 unsigned uInstance,
9350 StorageBus_T enmBus,
9351 bool fUseHostIOCache,
9352 bool fBuiltinIOCache,
9353 bool fSetupMerge,
9354 unsigned uMergeSource,
9355 unsigned uMergeTarget,
9356 IMediumAttachment *aMediumAtt,
9357 MachineState_T aMachineState,
9358 HRESULT *phrc)
9359{
9360 LogFlowFunc(("pUVM=%p aMediumAtt=%p phrc=%p\n", pUVM, aMediumAtt, phrc));
9361
9362 int rc;
9363 HRESULT hrc;
9364 Bstr bstr;
9365 *phrc = S_OK;
9366#define RC_CHECK() do { if (RT_FAILURE(rc)) { AssertMsgFailed(("rc=%Rrc\n", rc)); return rc; } } while (0)
9367#define H() do { if (FAILED(hrc)) { AssertMsgFailed(("hrc=%Rhrc (%#x)\n", hrc, hrc)); *phrc = hrc; return VERR_GENERAL_FAILURE; } } while (0)
9368
9369 /* Ignore attachments other than hard disks, since at the moment they are
9370 * not subject to snapshotting in general. */
9371 DeviceType_T lType;
9372 hrc = aMediumAtt->COMGETTER(Type)(&lType); H();
9373 if (lType != DeviceType_HardDisk)
9374 return VINF_SUCCESS;
9375
9376 /* Determine the base path for the device instance. */
9377 PCFGMNODE pCtlInst;
9378 pCtlInst = CFGMR3GetChildF(CFGMR3GetRootU(pUVM), "Devices/%s/%u/", pcszDevice, uInstance);
9379 AssertReturn(pCtlInst, VERR_INTERNAL_ERROR);
9380
9381 /* Update the device instance configuration. */
9382 rc = pConsole->configMediumAttachment(pCtlInst,
9383 pcszDevice,
9384 uInstance,
9385 enmBus,
9386 fUseHostIOCache,
9387 fBuiltinIOCache,
9388 fSetupMerge,
9389 uMergeSource,
9390 uMergeTarget,
9391 aMediumAtt,
9392 aMachineState,
9393 phrc,
9394 true /* fAttachDetach */,
9395 false /* fForceUnmount */,
9396 false /* fHotplug */,
9397 pUVM,
9398 NULL /* paLedDevType */);
9399 /** @todo this dumps everything attached to this device instance, which
9400 * is more than necessary. Dumping the changed LUN would be enough. */
9401 CFGMR3Dump(pCtlInst);
9402 RC_CHECK();
9403
9404#undef RC_CHECK
9405#undef H
9406
9407 LogFlowFunc(("Returns success\n"));
9408 return VINF_SUCCESS;
9409}
9410
9411/**
9412 * Progress cancelation callback employed by Console::fntTakeSnapshotWorker.
9413 */
9414static void takesnapshotProgressCancelCallback(void *pvUser)
9415{
9416 PUVM pUVM = (PUVM)pvUser;
9417 SSMR3Cancel(pUVM);
9418}
9419
9420/**
9421 * Worker thread created by Console::TakeSnapshot.
9422 * @param Thread The current thread (ignored).
9423 * @param pvUser The task.
9424 * @return VINF_SUCCESS (ignored).
9425 */
9426/*static*/
9427DECLCALLBACK(int) Console::fntTakeSnapshotWorker(RTTHREAD Thread, void *pvUser)
9428{
9429 VMTakeSnapshotTask *pTask = (VMTakeSnapshotTask*)pvUser;
9430
9431 // taking a snapshot consists of the following:
9432
9433 // 1) creating a diff image for each virtual hard disk, into which write operations go after
9434 // the snapshot has been created (done in VBoxSVC, in SessionMachine::BeginTakingSnapshot)
9435 // 2) creating a Snapshot object with the state of the machine (hardware + storage,
9436 // done in VBoxSVC, also in SessionMachine::BeginTakingSnapshot)
9437 // 3) saving the state of the virtual machine (here, in the VM process, if the machine is online)
9438
9439 Console *that = pTask->mConsole;
9440 bool fBeganTakingSnapshot = false;
9441 bool fSuspenededBySave = false;
9442
9443 AutoCaller autoCaller(that);
9444 if (FAILED(autoCaller.rc()))
9445 {
9446 that->mptrCancelableProgress.setNull();
9447 return autoCaller.rc();
9448 }
9449
9450 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
9451
9452 HRESULT rc = S_OK;
9453
9454 try
9455 {
9456 /* STEP 1 + 2:
9457 * request creating the diff images on the server and create the snapshot object
9458 * (this will set the machine state to Saving on the server to block
9459 * others from accessing this machine)
9460 */
9461 rc = that->mControl->BeginTakingSnapshot(that,
9462 pTask->bstrName.raw(),
9463 pTask->bstrDescription.raw(),
9464 pTask->mProgress,
9465 pTask->fTakingSnapshotOnline,
9466 pTask->bstrSavedStateFile.asOutParam());
9467 if (FAILED(rc))
9468 throw rc;
9469
9470 fBeganTakingSnapshot = true;
9471
9472 /* Check sanity: for offline snapshots there must not be a saved state
9473 * file name. All other combinations are valid (even though online
9474 * snapshots without saved state file seems inconsistent - there are
9475 * some exotic use cases, which need to be explicitly enabled, see the
9476 * code of SessionMachine::BeginTakingSnapshot. */
9477 if ( !pTask->fTakingSnapshotOnline
9478 && !pTask->bstrSavedStateFile.isEmpty())
9479 throw setErrorStatic(E_FAIL, "Invalid state of saved state file");
9480
9481 /* sync the state with the server */
9482 if (pTask->lastMachineState == MachineState_Running)
9483 that->setMachineStateLocally(MachineState_LiveSnapshotting);
9484 else
9485 that->setMachineStateLocally(MachineState_Saving);
9486
9487 // STEP 3: save the VM state (if online)
9488 if (pTask->fTakingSnapshotOnline)
9489 {
9490 int vrc;
9491 SafeVMPtr ptrVM(that);
9492 if (!ptrVM.isOk())
9493 throw ptrVM.rc();
9494
9495 pTask->mProgress->SetNextOperation(Bstr(tr("Saving the machine state")).raw(),
9496 pTask->ulMemSize); // operation weight, same as computed when setting up progress object
9497 if (!pTask->bstrSavedStateFile.isEmpty())
9498 {
9499 Utf8Str strSavedStateFile(pTask->bstrSavedStateFile);
9500
9501 pTask->mProgress->setCancelCallback(takesnapshotProgressCancelCallback, ptrVM.rawUVM());
9502
9503 alock.release();
9504 LogFlowFunc(("VMR3Save...\n"));
9505 vrc = VMR3Save(ptrVM.rawUVM(),
9506 strSavedStateFile.c_str(),
9507 true /*fContinueAfterwards*/,
9508 Console::stateProgressCallback,
9509 static_cast<IProgress *>(pTask->mProgress),
9510 &fSuspenededBySave);
9511 alock.acquire();
9512 if (RT_FAILURE(vrc))
9513 throw setErrorStatic(E_FAIL,
9514 tr("Failed to save the machine state to '%s' (%Rrc)"),
9515 strSavedStateFile.c_str(), vrc);
9516
9517 pTask->mProgress->setCancelCallback(NULL, NULL);
9518 }
9519 else
9520 LogRel(("Console: skipped saving state as part of online snapshot\n"));
9521
9522 if (!pTask->mProgress->notifyPointOfNoReturn())
9523 throw setErrorStatic(E_FAIL, tr("Canceled"));
9524 that->mptrCancelableProgress.setNull();
9525
9526 // STEP 4: reattach hard disks
9527 LogFlowFunc(("Reattaching new differencing hard disks...\n"));
9528
9529 pTask->mProgress->SetNextOperation(Bstr(tr("Reconfiguring medium attachments")).raw(),
9530 1); // operation weight, same as computed when setting up progress object
9531
9532 com::SafeIfaceArray<IMediumAttachment> atts;
9533 rc = that->mMachine->COMGETTER(MediumAttachments)(ComSafeArrayAsOutParam(atts));
9534 if (FAILED(rc))
9535 throw rc;
9536
9537 for (size_t i = 0;
9538 i < atts.size();
9539 ++i)
9540 {
9541 ComPtr<IStorageController> pStorageController;
9542 Bstr controllerName;
9543 ULONG lInstance;
9544 StorageControllerType_T enmController;
9545 StorageBus_T enmBus;
9546 BOOL fUseHostIOCache;
9547
9548 /*
9549 * We can't pass a storage controller object directly
9550 * (g++ complains about not being able to pass non POD types through '...')
9551 * so we have to query needed values here and pass them.
9552 */
9553 rc = atts[i]->COMGETTER(Controller)(controllerName.asOutParam());
9554 if (FAILED(rc))
9555 throw rc;
9556
9557 rc = that->mMachine->GetStorageControllerByName(controllerName.raw(),
9558 pStorageController.asOutParam());
9559 if (FAILED(rc))
9560 throw rc;
9561
9562 rc = pStorageController->COMGETTER(ControllerType)(&enmController);
9563 if (FAILED(rc))
9564 throw rc;
9565 rc = pStorageController->COMGETTER(Instance)(&lInstance);
9566 if (FAILED(rc))
9567 throw rc;
9568 rc = pStorageController->COMGETTER(Bus)(&enmBus);
9569 if (FAILED(rc))
9570 throw rc;
9571 rc = pStorageController->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
9572 if (FAILED(rc))
9573 throw rc;
9574
9575 const char *pcszDevice = Console::convertControllerTypeToDev(enmController);
9576
9577 BOOL fBuiltinIOCache;
9578 rc = that->mMachine->COMGETTER(IOCacheEnabled)(&fBuiltinIOCache);
9579 if (FAILED(rc))
9580 throw rc;
9581
9582 /*
9583 * don't release the lock since reconfigureMediumAttachment
9584 * isn't going to need the Console lock.
9585 */
9586 vrc = VMR3ReqCallWaitU(ptrVM.rawUVM(),
9587 VMCPUID_ANY,
9588 (PFNRT)reconfigureMediumAttachment,
9589 13,
9590 that,
9591 ptrVM.rawUVM(),
9592 pcszDevice,
9593 lInstance,
9594 enmBus,
9595 fUseHostIOCache,
9596 fBuiltinIOCache,
9597 false /* fSetupMerge */,
9598 0 /* uMergeSource */,
9599 0 /* uMergeTarget */,
9600 atts[i],
9601 that->mMachineState,
9602 &rc);
9603 if (RT_FAILURE(vrc))
9604 throw setErrorStatic(E_FAIL, Console::tr("%Rrc"), vrc);
9605 if (FAILED(rc))
9606 throw rc;
9607 }
9608 }
9609
9610 /*
9611 * finalize the requested snapshot object.
9612 * This will reset the machine state to the state it had right
9613 * before calling mControl->BeginTakingSnapshot().
9614 */
9615 rc = that->mControl->EndTakingSnapshot(TRUE /*aSuccess*/);
9616 // do not throw rc here because we can't call EndTakingSnapshot() twice
9617 LogFlowFunc(("EndTakingSnapshot -> %Rhrc [mMachineState=%s]\n", rc, Global::stringifyMachineState(that->mMachineState)));
9618 }
9619 catch (HRESULT rcThrown)
9620 {
9621 /* preserve existing error info */
9622 ErrorInfoKeeper eik;
9623
9624 if (fBeganTakingSnapshot)
9625 that->mControl->EndTakingSnapshot(FALSE /*aSuccess*/);
9626
9627 rc = rcThrown;
9628 LogFunc(("Caught %Rhrc [mMachineState=%s]\n", rc, Global::stringifyMachineState(that->mMachineState)));
9629 }
9630 Assert(alock.isWriteLockOnCurrentThread());
9631
9632 if (FAILED(rc)) /* Must come before calling setMachineState. */
9633 pTask->mProgress->notifyComplete(rc);
9634
9635 /*
9636 * Fix up the machine state.
9637 *
9638 * For live snapshots we do all the work, for the two other variations we
9639 * just update the local copy.
9640 */
9641 MachineState_T enmMachineState;
9642 that->mMachine->COMGETTER(State)(&enmMachineState);
9643 if ( that->mMachineState == MachineState_LiveSnapshotting
9644 || that->mMachineState == MachineState_Saving)
9645 {
9646
9647 if (!pTask->fTakingSnapshotOnline)
9648 that->setMachineStateLocally(pTask->lastMachineState);
9649 else if (SUCCEEDED(rc))
9650 {
9651 Assert( pTask->lastMachineState == MachineState_Running
9652 || pTask->lastMachineState == MachineState_Paused);
9653 Assert(that->mMachineState == MachineState_Saving);
9654 if (pTask->lastMachineState == MachineState_Running)
9655 {
9656 LogFlowFunc(("VMR3Resume...\n"));
9657 SafeVMPtr ptrVM(that);
9658 alock.release();
9659 int vrc = VMR3Resume(ptrVM.rawUVM(), VMRESUMEREASON_STATE_SAVED);
9660 alock.acquire();
9661 if (RT_FAILURE(vrc))
9662 {
9663 rc = setErrorStatic(VBOX_E_VM_ERROR, tr("Could not resume the machine execution (%Rrc)"), vrc);
9664 pTask->mProgress->notifyComplete(rc);
9665 if (that->mMachineState == MachineState_Saving)
9666 that->setMachineStateLocally(MachineState_Paused);
9667 }
9668 }
9669 else
9670 that->setMachineStateLocally(MachineState_Paused);
9671 }
9672 else
9673 {
9674 /** @todo this could probably be made more generic and reused elsewhere. */
9675 /* paranoid cleanup on for a failed online snapshot. */
9676 VMSTATE enmVMState = VMR3GetStateU(that->mpUVM);
9677 switch (enmVMState)
9678 {
9679 case VMSTATE_RUNNING:
9680 case VMSTATE_RUNNING_LS:
9681 case VMSTATE_DEBUGGING:
9682 case VMSTATE_DEBUGGING_LS:
9683 case VMSTATE_POWERING_OFF:
9684 case VMSTATE_POWERING_OFF_LS:
9685 case VMSTATE_RESETTING:
9686 case VMSTATE_RESETTING_LS:
9687 Assert(!fSuspenededBySave);
9688 that->setMachineState(MachineState_Running);
9689 break;
9690
9691 case VMSTATE_GURU_MEDITATION:
9692 case VMSTATE_GURU_MEDITATION_LS:
9693 that->setMachineState(MachineState_Stuck);
9694 break;
9695
9696 case VMSTATE_FATAL_ERROR:
9697 case VMSTATE_FATAL_ERROR_LS:
9698 if (pTask->lastMachineState == MachineState_Paused)
9699 that->setMachineStateLocally(pTask->lastMachineState);
9700 else
9701 that->setMachineState(MachineState_Paused);
9702 break;
9703
9704 default:
9705 AssertMsgFailed(("%s\n", VMR3GetStateName(enmVMState)));
9706 case VMSTATE_SUSPENDED:
9707 case VMSTATE_SUSPENDED_LS:
9708 case VMSTATE_SUSPENDING:
9709 case VMSTATE_SUSPENDING_LS:
9710 case VMSTATE_SUSPENDING_EXT_LS:
9711 if (fSuspenededBySave)
9712 {
9713 Assert(pTask->lastMachineState == MachineState_Running);
9714 LogFlowFunc(("VMR3Resume (on failure)...\n"));
9715 SafeVMPtr ptrVM(that);
9716 alock.release();
9717 int vrc = VMR3Resume(ptrVM.rawUVM(), VMRESUMEREASON_STATE_SAVED); AssertLogRelRC(vrc);
9718 alock.acquire();
9719 if (RT_FAILURE(vrc))
9720 that->setMachineState(MachineState_Paused);
9721 }
9722 else if (pTask->lastMachineState == MachineState_Paused)
9723 that->setMachineStateLocally(pTask->lastMachineState);
9724 else
9725 that->setMachineState(MachineState_Paused);
9726 break;
9727 }
9728
9729 }
9730 }
9731 /*else: somebody else has change the state... Leave it. */
9732
9733 /* check the remote state to see that we got it right. */
9734 that->mMachine->COMGETTER(State)(&enmMachineState);
9735 AssertLogRelMsg(that->mMachineState == enmMachineState,
9736 ("mMachineState=%s enmMachineState=%s\n", Global::stringifyMachineState(that->mMachineState),
9737 Global::stringifyMachineState(enmMachineState) ));
9738
9739
9740 if (SUCCEEDED(rc)) /* The failure cases are handled above. */
9741 pTask->mProgress->notifyComplete(rc);
9742
9743 delete pTask;
9744
9745 LogFlowFuncLeave();
9746 return VINF_SUCCESS;
9747}
9748
9749/**
9750 * Thread for executing the saved state operation.
9751 *
9752 * @param Thread The thread handle.
9753 * @param pvUser Pointer to a VMSaveTask structure.
9754 * @return VINF_SUCCESS (ignored).
9755 *
9756 * @note Locks the Console object for writing.
9757 */
9758/*static*/
9759DECLCALLBACK(int) Console::saveStateThread(RTTHREAD Thread, void *pvUser)
9760{
9761 LogFlowFuncEnter();
9762
9763 std::auto_ptr<VMSaveTask> task(static_cast<VMSaveTask*>(pvUser));
9764 AssertReturn(task.get(), VERR_INVALID_PARAMETER);
9765
9766 Assert(task->mSavedStateFile.length());
9767 Assert(task->mProgress.isNull());
9768 Assert(!task->mServerProgress.isNull());
9769
9770 const ComObjPtr<Console> &that = task->mConsole;
9771 Utf8Str errMsg;
9772 HRESULT rc = S_OK;
9773
9774 LogFlowFunc(("Saving the state to '%s'...\n", task->mSavedStateFile.c_str()));
9775
9776 bool fSuspenededBySave;
9777 int vrc = VMR3Save(task->mpUVM,
9778 task->mSavedStateFile.c_str(),
9779 false, /*fContinueAfterwards*/
9780 Console::stateProgressCallback,
9781 static_cast<IProgress *>(task->mServerProgress),
9782 &fSuspenededBySave);
9783 if (RT_FAILURE(vrc))
9784 {
9785 errMsg = Utf8StrFmt(Console::tr("Failed to save the machine state to '%s' (%Rrc)"),
9786 task->mSavedStateFile.c_str(), vrc);
9787 rc = E_FAIL;
9788 }
9789 Assert(!fSuspenededBySave);
9790
9791 /* lock the console once we're going to access it */
9792 AutoWriteLock thatLock(that COMMA_LOCKVAL_SRC_POS);
9793
9794 /* synchronize the state with the server */
9795 if (SUCCEEDED(rc))
9796 {
9797 /*
9798 * The machine has been successfully saved, so power it down
9799 * (vmstateChangeCallback() will set state to Saved on success).
9800 * Note: we release the task's VM caller, otherwise it will
9801 * deadlock.
9802 */
9803 task->releaseVMCaller();
9804 thatLock.release();
9805 rc = that->powerDown();
9806 thatLock.acquire();
9807 }
9808
9809 /*
9810 * If we failed, reset the local machine state.
9811 */
9812 if (FAILED(rc))
9813 that->setMachineStateLocally(task->mMachineStateBefore);
9814
9815 /*
9816 * Finalize the requested save state procedure. In case of failure it will
9817 * reset the machine state to the state it had right before calling
9818 * mControl->BeginSavingState(). This must be the last thing because it
9819 * will set the progress to completed, and that means that the frontend
9820 * can immediately uninit the associated console object.
9821 */
9822 that->mControl->EndSavingState(rc, Bstr(errMsg).raw());
9823
9824 LogFlowFuncLeave();
9825 return VINF_SUCCESS;
9826}
9827
9828/**
9829 * Thread for powering down the Console.
9830 *
9831 * @param Thread The thread handle.
9832 * @param pvUser Pointer to the VMTask structure.
9833 * @return VINF_SUCCESS (ignored).
9834 *
9835 * @note Locks the Console object for writing.
9836 */
9837/*static*/
9838DECLCALLBACK(int) Console::powerDownThread(RTTHREAD Thread, void *pvUser)
9839{
9840 LogFlowFuncEnter();
9841
9842 std::auto_ptr<VMPowerDownTask> task(static_cast<VMPowerDownTask *>(pvUser));
9843 AssertReturn(task.get(), VERR_INVALID_PARAMETER);
9844
9845 AssertReturn(task->isOk(), VERR_GENERAL_FAILURE);
9846
9847 Assert(task->mProgress.isNull());
9848
9849 const ComObjPtr<Console> &that = task->mConsole;
9850
9851 /* Note: no need to use addCaller() to protect Console because VMTask does
9852 * that */
9853
9854 /* wait until the method tat started us returns */
9855 AutoWriteLock thatLock(that COMMA_LOCKVAL_SRC_POS);
9856
9857 /* release VM caller to avoid the powerDown() deadlock */
9858 task->releaseVMCaller();
9859
9860 thatLock.release();
9861
9862 that->powerDown(task->mServerProgress);
9863
9864 /* complete the operation */
9865 that->mControl->EndPoweringDown(S_OK, Bstr().raw());
9866
9867 LogFlowFuncLeave();
9868 return VINF_SUCCESS;
9869}
9870
9871
9872/**
9873 * @interface_method_impl{VMM2USERMETHODS,pfnSaveState}
9874 */
9875/*static*/ DECLCALLBACK(int)
9876Console::vmm2User_SaveState(PCVMM2USERMETHODS pThis, PUVM pUVM)
9877{
9878 Console *pConsole = ((MYVMM2USERMETHODS *)pThis)->pConsole;
9879 NOREF(pUVM);
9880
9881 /*
9882 * For now, just call SaveState. We should probably try notify the GUI so
9883 * it can pop up a progress object and stuff.
9884 */
9885 HRESULT hrc = pConsole->SaveState(NULL);
9886 return SUCCEEDED(hrc) ? VINF_SUCCESS : Global::vboxStatusCodeFromCOM(hrc);
9887}
9888
9889/**
9890 * @interface_method_impl{VMM2USERMETHODS,pfnNotifyEmtInit}
9891 */
9892/*static*/ DECLCALLBACK(void)
9893Console::vmm2User_NotifyEmtInit(PCVMM2USERMETHODS pThis, PUVM pUVM, PUVMCPU pUVCpu)
9894{
9895 NOREF(pThis); NOREF(pUVM); NOREF(pUVCpu);
9896 VirtualBoxBase::initializeComForThread();
9897}
9898
9899/**
9900 * @interface_method_impl{VMM2USERMETHODS,pfnNotifyEmtTerm}
9901 */
9902/*static*/ DECLCALLBACK(void)
9903Console::vmm2User_NotifyEmtTerm(PCVMM2USERMETHODS pThis, PUVM pUVM, PUVMCPU pUVCpu)
9904{
9905 NOREF(pThis); NOREF(pUVM); NOREF(pUVCpu);
9906 VirtualBoxBase::uninitializeComForThread();
9907}
9908
9909/**
9910 * @interface_method_impl{VMM2USERMETHODS,pfnNotifyPdmtInit}
9911 */
9912/*static*/ DECLCALLBACK(void)
9913Console::vmm2User_NotifyPdmtInit(PCVMM2USERMETHODS pThis, PUVM pUVM)
9914{
9915 NOREF(pThis); NOREF(pUVM);
9916 VirtualBoxBase::initializeComForThread();
9917}
9918
9919/**
9920 * @interface_method_impl{VMM2USERMETHODS,pfnNotifyPdmtTerm}
9921 */
9922/*static*/ DECLCALLBACK(void)
9923Console::vmm2User_NotifyPdmtTerm(PCVMM2USERMETHODS pThis, PUVM pUVM)
9924{
9925 NOREF(pThis); NOREF(pUVM);
9926 VirtualBoxBase::uninitializeComForThread();
9927}
9928
9929
9930
9931
9932/**
9933 * The Main status driver instance data.
9934 */
9935typedef struct DRVMAINSTATUS
9936{
9937 /** The LED connectors. */
9938 PDMILEDCONNECTORS ILedConnectors;
9939 /** Pointer to the LED ports interface above us. */
9940 PPDMILEDPORTS pLedPorts;
9941 /** Pointer to the array of LED pointers. */
9942 PPDMLED *papLeds;
9943 /** The unit number corresponding to the first entry in the LED array. */
9944 RTUINT iFirstLUN;
9945 /** The unit number corresponding to the last entry in the LED array.
9946 * (The size of the LED array is iLastLUN - iFirstLUN + 1.) */
9947 RTUINT iLastLUN;
9948 /** Pointer to the driver instance. */
9949 PPDMDRVINS pDrvIns;
9950 /** The Media Notify interface. */
9951 PDMIMEDIANOTIFY IMediaNotify;
9952 /** Map for translating PDM storage controller/LUN information to
9953 * IMediumAttachment references. */
9954 Console::MediumAttachmentMap *pmapMediumAttachments;
9955 /** Device name+instance for mapping */
9956 char *pszDeviceInstance;
9957 /** Pointer to the Console object, for driver triggered activities. */
9958 Console *pConsole;
9959} DRVMAINSTATUS, *PDRVMAINSTATUS;
9960
9961
9962/**
9963 * Notification about a unit which have been changed.
9964 *
9965 * The driver must discard any pointers to data owned by
9966 * the unit and requery it.
9967 *
9968 * @param pInterface Pointer to the interface structure containing the called function pointer.
9969 * @param iLUN The unit number.
9970 */
9971DECLCALLBACK(void) Console::drvStatus_UnitChanged(PPDMILEDCONNECTORS pInterface, unsigned iLUN)
9972{
9973 PDRVMAINSTATUS pThis = (PDRVMAINSTATUS)((uintptr_t)pInterface - RT_OFFSETOF(DRVMAINSTATUS, ILedConnectors));
9974 if (iLUN >= pThis->iFirstLUN && iLUN <= pThis->iLastLUN)
9975 {
9976 PPDMLED pLed;
9977 int rc = pThis->pLedPorts->pfnQueryStatusLed(pThis->pLedPorts, iLUN, &pLed);
9978 if (RT_FAILURE(rc))
9979 pLed = NULL;
9980 ASMAtomicWritePtr(&pThis->papLeds[iLUN - pThis->iFirstLUN], pLed);
9981 Log(("drvStatus_UnitChanged: iLUN=%d pLed=%p\n", iLUN, pLed));
9982 }
9983}
9984
9985
9986/**
9987 * Notification about a medium eject.
9988 *
9989 * @returns VBox status.
9990 * @param pInterface Pointer to the interface structure containing the called function pointer.
9991 * @param uLUN The unit number.
9992 */
9993DECLCALLBACK(int) Console::drvStatus_MediumEjected(PPDMIMEDIANOTIFY pInterface, unsigned uLUN)
9994{
9995 PDRVMAINSTATUS pThis = (PDRVMAINSTATUS)((uintptr_t)pInterface - RT_OFFSETOF(DRVMAINSTATUS, IMediaNotify));
9996 PPDMDRVINS pDrvIns = pThis->pDrvIns;
9997 LogFunc(("uLUN=%d\n", uLUN));
9998 if (pThis->pmapMediumAttachments)
9999 {
10000 AutoWriteLock alock(pThis->pConsole COMMA_LOCKVAL_SRC_POS);
10001
10002 ComPtr<IMediumAttachment> pMediumAtt;
10003 Utf8Str devicePath = Utf8StrFmt("%s/LUN#%u", pThis->pszDeviceInstance, uLUN);
10004 Console::MediumAttachmentMap::const_iterator end = pThis->pmapMediumAttachments->end();
10005 Console::MediumAttachmentMap::const_iterator it = pThis->pmapMediumAttachments->find(devicePath);
10006 if (it != end)
10007 pMediumAtt = it->second;
10008 Assert(!pMediumAtt.isNull());
10009 if (!pMediumAtt.isNull())
10010 {
10011 IMedium *pMedium = NULL;
10012 HRESULT rc = pMediumAtt->COMGETTER(Medium)(&pMedium);
10013 AssertComRC(rc);
10014 if (SUCCEEDED(rc) && pMedium)
10015 {
10016 BOOL fHostDrive = FALSE;
10017 rc = pMedium->COMGETTER(HostDrive)(&fHostDrive);
10018 AssertComRC(rc);
10019 if (!fHostDrive)
10020 {
10021 alock.release();
10022
10023 ComPtr<IMediumAttachment> pNewMediumAtt;
10024 rc = pThis->pConsole->mControl->EjectMedium(pMediumAtt, pNewMediumAtt.asOutParam());
10025 if (SUCCEEDED(rc))
10026 fireMediumChangedEvent(pThis->pConsole->mEventSource, pNewMediumAtt);
10027
10028 alock.acquire();
10029 if (pNewMediumAtt != pMediumAtt)
10030 {
10031 pThis->pmapMediumAttachments->erase(devicePath);
10032 pThis->pmapMediumAttachments->insert(std::make_pair(devicePath, pNewMediumAtt));
10033 }
10034 }
10035 }
10036 }
10037 }
10038 return VINF_SUCCESS;
10039}
10040
10041
10042/**
10043 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
10044 */
10045DECLCALLBACK(void *) Console::drvStatus_QueryInterface(PPDMIBASE pInterface, const char *pszIID)
10046{
10047 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
10048 PDRVMAINSTATUS pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINSTATUS);
10049 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
10050 PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDCONNECTORS, &pThis->ILedConnectors);
10051 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIANOTIFY, &pThis->IMediaNotify);
10052 return NULL;
10053}
10054
10055
10056/**
10057 * Destruct a status driver instance.
10058 *
10059 * @returns VBox status.
10060 * @param pDrvIns The driver instance data.
10061 */
10062DECLCALLBACK(void) Console::drvStatus_Destruct(PPDMDRVINS pDrvIns)
10063{
10064 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
10065 PDRVMAINSTATUS pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINSTATUS);
10066 LogFlowFunc(("iInstance=%d\n", pDrvIns->iInstance));
10067
10068 if (pThis->papLeds)
10069 {
10070 unsigned iLed = pThis->iLastLUN - pThis->iFirstLUN + 1;
10071 while (iLed-- > 0)
10072 ASMAtomicWriteNullPtr(&pThis->papLeds[iLed]);
10073 }
10074}
10075
10076
10077/**
10078 * Construct a status driver instance.
10079 *
10080 * @copydoc FNPDMDRVCONSTRUCT
10081 */
10082DECLCALLBACK(int) Console::drvStatus_Construct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
10083{
10084 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
10085 PDRVMAINSTATUS pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINSTATUS);
10086 LogFlowFunc(("iInstance=%d\n", pDrvIns->iInstance));
10087
10088 /*
10089 * Validate configuration.
10090 */
10091 if (!CFGMR3AreValuesValid(pCfg, "papLeds\0pmapMediumAttachments\0DeviceInstance\0pConsole\0First\0Last\0"))
10092 return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
10093 AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
10094 ("Configuration error: Not possible to attach anything to this driver!\n"),
10095 VERR_PDM_DRVINS_NO_ATTACH);
10096
10097 /*
10098 * Data.
10099 */
10100 pDrvIns->IBase.pfnQueryInterface = Console::drvStatus_QueryInterface;
10101 pThis->ILedConnectors.pfnUnitChanged = Console::drvStatus_UnitChanged;
10102 pThis->IMediaNotify.pfnEjected = Console::drvStatus_MediumEjected;
10103 pThis->pDrvIns = pDrvIns;
10104 pThis->pszDeviceInstance = NULL;
10105
10106 /*
10107 * Read config.
10108 */
10109 int rc = CFGMR3QueryPtr(pCfg, "papLeds", (void **)&pThis->papLeds);
10110 if (RT_FAILURE(rc))
10111 {
10112 AssertMsgFailed(("Configuration error: Failed to query the \"papLeds\" value! rc=%Rrc\n", rc));
10113 return rc;
10114 }
10115
10116 rc = CFGMR3QueryPtrDef(pCfg, "pmapMediumAttachments", (void **)&pThis->pmapMediumAttachments, NULL);
10117 if (RT_FAILURE(rc))
10118 {
10119 AssertMsgFailed(("Configuration error: Failed to query the \"pmapMediumAttachments\" value! rc=%Rrc\n", rc));
10120 return rc;
10121 }
10122 if (pThis->pmapMediumAttachments)
10123 {
10124 rc = CFGMR3QueryStringAlloc(pCfg, "DeviceInstance", &pThis->pszDeviceInstance);
10125 if (RT_FAILURE(rc))
10126 {
10127 AssertMsgFailed(("Configuration error: Failed to query the \"DeviceInstance\" value! rc=%Rrc\n", rc));
10128 return rc;
10129 }
10130 rc = CFGMR3QueryPtr(pCfg, "pConsole", (void **)&pThis->pConsole);
10131 if (RT_FAILURE(rc))
10132 {
10133 AssertMsgFailed(("Configuration error: Failed to query the \"pConsole\" value! rc=%Rrc\n", rc));
10134 return rc;
10135 }
10136 }
10137
10138 rc = CFGMR3QueryU32(pCfg, "First", &pThis->iFirstLUN);
10139 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
10140 pThis->iFirstLUN = 0;
10141 else if (RT_FAILURE(rc))
10142 {
10143 AssertMsgFailed(("Configuration error: Failed to query the \"First\" value! rc=%Rrc\n", rc));
10144 return rc;
10145 }
10146
10147 rc = CFGMR3QueryU32(pCfg, "Last", &pThis->iLastLUN);
10148 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
10149 pThis->iLastLUN = 0;
10150 else if (RT_FAILURE(rc))
10151 {
10152 AssertMsgFailed(("Configuration error: Failed to query the \"Last\" value! rc=%Rrc\n", rc));
10153 return rc;
10154 }
10155 if (pThis->iFirstLUN > pThis->iLastLUN)
10156 {
10157 AssertMsgFailed(("Configuration error: Invalid unit range %u-%u\n", pThis->iFirstLUN, pThis->iLastLUN));
10158 return VERR_GENERAL_FAILURE;
10159 }
10160
10161 /*
10162 * Get the ILedPorts interface of the above driver/device and
10163 * query the LEDs we want.
10164 */
10165 pThis->pLedPorts = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMILEDPORTS);
10166 AssertMsgReturn(pThis->pLedPorts, ("Configuration error: No led ports interface above!\n"),
10167 VERR_PDM_MISSING_INTERFACE_ABOVE);
10168
10169 for (unsigned i = pThis->iFirstLUN; i <= pThis->iLastLUN; ++i)
10170 Console::drvStatus_UnitChanged(&pThis->ILedConnectors, i);
10171
10172 return VINF_SUCCESS;
10173}
10174
10175
10176/**
10177 * Console status driver (LED) registration record.
10178 */
10179const PDMDRVREG Console::DrvStatusReg =
10180{
10181 /* u32Version */
10182 PDM_DRVREG_VERSION,
10183 /* szName */
10184 "MainStatus",
10185 /* szRCMod */
10186 "",
10187 /* szR0Mod */
10188 "",
10189 /* pszDescription */
10190 "Main status driver (Main as in the API).",
10191 /* fFlags */
10192 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
10193 /* fClass. */
10194 PDM_DRVREG_CLASS_STATUS,
10195 /* cMaxInstances */
10196 ~0U,
10197 /* cbInstance */
10198 sizeof(DRVMAINSTATUS),
10199 /* pfnConstruct */
10200 Console::drvStatus_Construct,
10201 /* pfnDestruct */
10202 Console::drvStatus_Destruct,
10203 /* pfnRelocate */
10204 NULL,
10205 /* pfnIOCtl */
10206 NULL,
10207 /* pfnPowerOn */
10208 NULL,
10209 /* pfnReset */
10210 NULL,
10211 /* pfnSuspend */
10212 NULL,
10213 /* pfnResume */
10214 NULL,
10215 /* pfnAttach */
10216 NULL,
10217 /* pfnDetach */
10218 NULL,
10219 /* pfnPowerOff */
10220 NULL,
10221 /* pfnSoftReset */
10222 NULL,
10223 /* u32EndVersion */
10224 PDM_DRVREG_VERSION
10225};
10226
10227/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use