VirtualBox

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

Last change on this file since 44358 was 44358, checked in by vboxsync, 12 years ago

PDMAsyncCompletion: PVM -> PUVM (one instance), internalize internal APIs where possible (not all because of test cases). API docs lives where the implmentation lives, NOT in headers (IPRT is the exception).

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette