VirtualBox

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

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

PGM,SSM,STAM: Changed APIs used by Main to take PUVM instead of PVM as the VM handle.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 333.8 KB
Line 
1/* $Id: ConsoleImpl.cpp 44347 2013-01-24 00:27:56Z 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, PVM pVM, unsigned uCpu)
2249{
2250 LogFlowFunc(("pThis=%p pVM=%p uCpu=%u\n", pThis, pVM, uCpu));
2251
2252 AssertReturn(pThis, VERR_INVALID_PARAMETER);
2253
2254 int vrc = PDMR3DeviceDetach(pVM, "acpi", 0, uCpu, 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(pVM, "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, pVM, 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, PVM pVM, unsigned uCpu)
2369{
2370 PUVM pUVM = VMR3GetUVM(pVM);
2371 LogFlowFunc(("pThis=%p uCpu=%u\n", pThis, uCpu));
2372
2373 AssertReturn(pThis, VERR_INVALID_PARAMETER);
2374
2375 int rc = VMR3HotPlugCpu(pUVM, uCpu);
2376 AssertRC(rc);
2377
2378 PCFGMNODE pInst = CFGMR3GetChild(CFGMR3GetRootU(pUVM), "Devices/acpi/0/");
2379 AssertRelease(pInst);
2380 /* nuke anything which might have been left behind. */
2381 CFGMR3RemoveNode(CFGMR3GetChildF(pInst, "LUN#%d", uCpu));
2382
2383#define RC_CHECK() do { if (RT_FAILURE(rc)) { AssertReleaseRC(rc); break; } } while (0)
2384
2385 PCFGMNODE pLunL0;
2386 PCFGMNODE pCfg;
2387 rc = CFGMR3InsertNodeF(pInst, &pLunL0, "LUN#%d", uCpu); RC_CHECK();
2388 rc = CFGMR3InsertString(pLunL0, "Driver", "ACPICpu"); RC_CHECK();
2389 rc = CFGMR3InsertNode(pLunL0, "Config", &pCfg); RC_CHECK();
2390
2391 /*
2392 * Attach the driver.
2393 */
2394 PPDMIBASE pBase;
2395 rc = PDMR3DeviceAttach(pVM, "acpi", 0, uCpu, 0, &pBase); RC_CHECK();
2396
2397 Log(("PlugCpu: rc=%Rrc\n", rc));
2398
2399 CFGMR3Dump(pInst);
2400
2401#undef RC_CHECK
2402
2403 return VINF_SUCCESS;
2404}
2405
2406HRESULT Console::doCPUAdd(ULONG aCpu, PVM pVM)
2407{
2408 PUVM pUVM = VMR3GetUVM(pVM);
2409 HRESULT rc = S_OK;
2410
2411 LogFlowThisFuncEnter();
2412 LogFlowThisFunc(("mMachineState=%d\n", mMachineState));
2413
2414 AutoCaller autoCaller(this);
2415 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2416
2417 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2418
2419 if ( mMachineState != MachineState_Running
2420 && mMachineState != MachineState_Teleporting
2421 && mMachineState != MachineState_LiveSnapshotting
2422 /** @todo r=bird: This should be allowed on paused VMs as well. Later. */
2423 )
2424 return setInvalidMachineStateError();
2425
2426 AssertReturn(m_pVMMDev, E_FAIL);
2427 PPDMIVMMDEVPORT pDevPort = m_pVMMDev->getVMMDevPort();
2428 AssertReturn(pDevPort, E_FAIL);
2429
2430 /* Check if the CPU is present */
2431 BOOL fCpuAttached;
2432 rc = mMachine->GetCPUStatus(aCpu, &fCpuAttached);
2433 if (FAILED(rc)) return rc;
2434
2435 if (fCpuAttached)
2436 return setError(E_FAIL,
2437 tr("CPU %d is already attached"), aCpu);
2438
2439 /*
2440 * Call worker in EMT, that's faster and safer than doing everything
2441 * using VMR3ReqCall. Note that we separate VMR3ReqCall from VMR3ReqWait
2442 * here to make requests from under the lock in order to serialize them.
2443 */
2444 PVMREQ pReq;
2445 int vrc = VMR3ReqCallU(pUVM, 0, &pReq, 0 /* no wait! */, VMREQFLAGS_VBOX_STATUS,
2446 (PFNRT)Console::plugCpu, 3,
2447 this, pVM, aCpu);
2448
2449 /* release the lock before a VMR3* call (EMT will call us back)! */
2450 alock.release();
2451
2452 if (vrc == VERR_TIMEOUT || RT_SUCCESS(vrc))
2453 {
2454 vrc = VMR3ReqWait(pReq, RT_INDEFINITE_WAIT);
2455 AssertRC(vrc);
2456 if (RT_SUCCESS(vrc))
2457 vrc = pReq->iStatus;
2458 }
2459 VMR3ReqFree(pReq);
2460
2461 rc = RT_SUCCESS(vrc) ? S_OK :
2462 setError(VBOX_E_VM_ERROR,
2463 tr("Could not add CPU to the machine (%Rrc)"),
2464 vrc);
2465
2466 if (RT_SUCCESS(vrc))
2467 {
2468 /* Notify the guest if possible. */
2469 uint32_t idCpuCore, idCpuPackage;
2470 vrc = VMR3GetCpuCoreAndPackageIdFromCpuId(pUVM, aCpu, &idCpuCore, &idCpuPackage); AssertRC(vrc);
2471 if (RT_SUCCESS(vrc))
2472 vrc = pDevPort->pfnCpuHotPlug(pDevPort, idCpuCore, idCpuPackage);
2473 /** @todo warning if the guest doesn't support it */
2474 }
2475
2476 LogFlowThisFunc(("mMachineState=%d, rc=%Rhrc\n", mMachineState, rc));
2477 LogFlowThisFuncLeave();
2478 return rc;
2479}
2480
2481STDMETHODIMP Console::Pause()
2482{
2483 LogFlowThisFuncEnter();
2484
2485 AutoCaller autoCaller(this);
2486 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2487
2488 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2489
2490 switch (mMachineState)
2491 {
2492 case MachineState_Running:
2493 case MachineState_Teleporting:
2494 case MachineState_LiveSnapshotting:
2495 break;
2496
2497 case MachineState_Paused:
2498 case MachineState_TeleportingPausedVM:
2499 case MachineState_Saving:
2500 return setError(VBOX_E_INVALID_VM_STATE, tr("Already paused"));
2501
2502 default:
2503 return setInvalidMachineStateError();
2504 }
2505
2506 /* get the VM handle. */
2507 SafeVMPtr ptrVM(this);
2508 if (!ptrVM.isOk())
2509 return ptrVM.rc();
2510
2511 LogFlowThisFunc(("Sending PAUSE request...\n"));
2512
2513 /* release the lock before a VMR3* call (EMT will call us back)! */
2514 alock.release();
2515
2516 int vrc = VMR3Suspend(ptrVM.rawUVM());
2517
2518 HRESULT hrc = S_OK;
2519 if (RT_FAILURE(vrc))
2520 hrc = setError(VBOX_E_VM_ERROR, tr("Could not suspend the machine execution (%Rrc)"), vrc);
2521
2522 LogFlowThisFunc(("hrc=%Rhrc\n", hrc));
2523 LogFlowThisFuncLeave();
2524 return hrc;
2525}
2526
2527STDMETHODIMP Console::Resume()
2528{
2529 LogFlowThisFuncEnter();
2530
2531 AutoCaller autoCaller(this);
2532 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2533
2534 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2535
2536 if (mMachineState != MachineState_Paused)
2537 return setError(VBOX_E_INVALID_VM_STATE,
2538 tr("Cannot resume the machine as it is not paused (machine state: %s)"),
2539 Global::stringifyMachineState(mMachineState));
2540
2541 /* get the VM handle. */
2542 SafeVMPtr ptrVM(this);
2543 if (!ptrVM.isOk())
2544 return ptrVM.rc();
2545
2546 LogFlowThisFunc(("Sending RESUME request...\n"));
2547
2548 /* release the lock before a VMR3* call (EMT will call us back)! */
2549 alock.release();
2550
2551#ifdef VBOX_WITH_EXTPACK
2552 int vrc = mptrExtPackManager->callAllVmPowerOnHooks(this, ptrVM); /** @todo called a few times too many... */
2553#else
2554 int vrc = VINF_SUCCESS;
2555#endif
2556 if (RT_SUCCESS(vrc))
2557 {
2558 if (VMR3GetStateU(ptrVM.rawUVM()) == VMSTATE_CREATED)
2559 vrc = VMR3PowerOn(ptrVM.rawUVM()); /* (PowerUpPaused) */
2560 else
2561 vrc = VMR3Resume(ptrVM.rawUVM());
2562 }
2563
2564 HRESULT rc = RT_SUCCESS(vrc) ? S_OK :
2565 setError(VBOX_E_VM_ERROR,
2566 tr("Could not resume the machine execution (%Rrc)"),
2567 vrc);
2568
2569 LogFlowThisFunc(("rc=%Rhrc\n", rc));
2570 LogFlowThisFuncLeave();
2571 return rc;
2572}
2573
2574STDMETHODIMP Console::PowerButton()
2575{
2576 LogFlowThisFuncEnter();
2577
2578 AutoCaller autoCaller(this);
2579 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2580
2581 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2582
2583 if ( mMachineState != MachineState_Running
2584 && mMachineState != MachineState_Teleporting
2585 && mMachineState != MachineState_LiveSnapshotting
2586 )
2587 return setInvalidMachineStateError();
2588
2589 /* get the VM handle. */
2590 SafeVMPtr ptrVM(this);
2591 if (!ptrVM.isOk())
2592 return ptrVM.rc();
2593
2594 // no need to release lock, as there are no cross-thread callbacks
2595
2596 /* get the acpi device interface and press the button. */
2597 PPDMIBASE pBase;
2598 int vrc = PDMR3QueryDeviceLun(ptrVM, "acpi", 0, 0, &pBase);
2599 if (RT_SUCCESS(vrc))
2600 {
2601 Assert(pBase);
2602 PPDMIACPIPORT pPort = PDMIBASE_QUERY_INTERFACE(pBase, PDMIACPIPORT);
2603 if (pPort)
2604 vrc = pPort->pfnPowerButtonPress(pPort);
2605 else
2606 vrc = VERR_PDM_MISSING_INTERFACE;
2607 }
2608
2609 HRESULT rc = RT_SUCCESS(vrc) ? S_OK :
2610 setError(VBOX_E_PDM_ERROR,
2611 tr("Controlled power off failed (%Rrc)"),
2612 vrc);
2613
2614 LogFlowThisFunc(("rc=%Rhrc\n", rc));
2615 LogFlowThisFuncLeave();
2616 return rc;
2617}
2618
2619STDMETHODIMP Console::GetPowerButtonHandled(BOOL *aHandled)
2620{
2621 LogFlowThisFuncEnter();
2622
2623 CheckComArgOutPointerValid(aHandled);
2624
2625 *aHandled = FALSE;
2626
2627 AutoCaller autoCaller(this);
2628
2629 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2630
2631 if ( mMachineState != MachineState_Running
2632 && mMachineState != MachineState_Teleporting
2633 && mMachineState != MachineState_LiveSnapshotting
2634 )
2635 return setInvalidMachineStateError();
2636
2637 /* get the VM handle. */
2638 SafeVMPtr ptrVM(this);
2639 if (!ptrVM.isOk())
2640 return ptrVM.rc();
2641
2642 // no need to release lock, as there are no cross-thread callbacks
2643
2644 /* get the acpi device interface and check if the button press was handled. */
2645 PPDMIBASE pBase;
2646 int vrc = PDMR3QueryDeviceLun(ptrVM, "acpi", 0, 0, &pBase);
2647 if (RT_SUCCESS(vrc))
2648 {
2649 Assert(pBase);
2650 PPDMIACPIPORT pPort = PDMIBASE_QUERY_INTERFACE(pBase, PDMIACPIPORT);
2651 if (pPort)
2652 {
2653 bool fHandled = false;
2654 vrc = pPort->pfnGetPowerButtonHandled(pPort, &fHandled);
2655 if (RT_SUCCESS(vrc))
2656 *aHandled = fHandled;
2657 }
2658 else
2659 vrc = VERR_PDM_MISSING_INTERFACE;
2660 }
2661
2662 HRESULT rc = RT_SUCCESS(vrc) ? S_OK :
2663 setError(VBOX_E_PDM_ERROR,
2664 tr("Checking if the ACPI Power Button event was handled by the guest OS failed (%Rrc)"),
2665 vrc);
2666
2667 LogFlowThisFunc(("rc=%Rhrc\n", rc));
2668 LogFlowThisFuncLeave();
2669 return rc;
2670}
2671
2672STDMETHODIMP Console::GetGuestEnteredACPIMode(BOOL *aEntered)
2673{
2674 LogFlowThisFuncEnter();
2675
2676 CheckComArgOutPointerValid(aEntered);
2677
2678 *aEntered = FALSE;
2679
2680 AutoCaller autoCaller(this);
2681
2682 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2683
2684 if ( mMachineState != MachineState_Running
2685 && mMachineState != MachineState_Teleporting
2686 && mMachineState != MachineState_LiveSnapshotting
2687 )
2688 return setError(VBOX_E_INVALID_VM_STATE,
2689 tr("Invalid machine state %s when checking if the guest entered the ACPI mode)"),
2690 Global::stringifyMachineState(mMachineState));
2691
2692 /* get the VM handle. */
2693 SafeVMPtr ptrVM(this);
2694 if (!ptrVM.isOk())
2695 return ptrVM.rc();
2696
2697 // no need to release lock, as there are no cross-thread callbacks
2698
2699 /* get the acpi device interface and query the information. */
2700 PPDMIBASE pBase;
2701 int vrc = PDMR3QueryDeviceLun(ptrVM, "acpi", 0, 0, &pBase);
2702 if (RT_SUCCESS(vrc))
2703 {
2704 Assert(pBase);
2705 PPDMIACPIPORT pPort = PDMIBASE_QUERY_INTERFACE(pBase, PDMIACPIPORT);
2706 if (pPort)
2707 {
2708 bool fEntered = false;
2709 vrc = pPort->pfnGetGuestEnteredACPIMode(pPort, &fEntered);
2710 if (RT_SUCCESS(vrc))
2711 *aEntered = fEntered;
2712 }
2713 else
2714 vrc = VERR_PDM_MISSING_INTERFACE;
2715 }
2716
2717 LogFlowThisFuncLeave();
2718 return S_OK;
2719}
2720
2721STDMETHODIMP Console::SleepButton()
2722{
2723 LogFlowThisFuncEnter();
2724
2725 AutoCaller autoCaller(this);
2726 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2727
2728 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2729
2730 if (mMachineState != MachineState_Running) /** @todo Live Migration: ??? */
2731 return setInvalidMachineStateError();
2732
2733 /* get the VM handle. */
2734 SafeVMPtr ptrVM(this);
2735 if (!ptrVM.isOk())
2736 return ptrVM.rc();
2737
2738 // no need to release lock, as there are no cross-thread callbacks
2739
2740 /* get the acpi device interface and press the sleep button. */
2741 PPDMIBASE pBase;
2742 int vrc = PDMR3QueryDeviceLun(ptrVM, "acpi", 0, 0, &pBase);
2743 if (RT_SUCCESS(vrc))
2744 {
2745 Assert(pBase);
2746 PPDMIACPIPORT pPort = PDMIBASE_QUERY_INTERFACE(pBase, PDMIACPIPORT);
2747 if (pPort)
2748 vrc = pPort->pfnSleepButtonPress(pPort);
2749 else
2750 vrc = VERR_PDM_MISSING_INTERFACE;
2751 }
2752
2753 HRESULT rc = RT_SUCCESS(vrc) ? S_OK :
2754 setError(VBOX_E_PDM_ERROR,
2755 tr("Sending sleep button event failed (%Rrc)"),
2756 vrc);
2757
2758 LogFlowThisFunc(("rc=%Rhrc\n", rc));
2759 LogFlowThisFuncLeave();
2760 return rc;
2761}
2762
2763STDMETHODIMP Console::SaveState(IProgress **aProgress)
2764{
2765 LogFlowThisFuncEnter();
2766 LogFlowThisFunc(("mMachineState=%d\n", mMachineState));
2767
2768 CheckComArgOutPointerValid(aProgress);
2769
2770 AutoCaller autoCaller(this);
2771 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2772
2773 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2774
2775 if ( mMachineState != MachineState_Running
2776 && mMachineState != MachineState_Paused)
2777 {
2778 return setError(VBOX_E_INVALID_VM_STATE,
2779 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
2780 Global::stringifyMachineState(mMachineState));
2781 }
2782
2783 /* memorize the current machine state */
2784 MachineState_T lastMachineState = mMachineState;
2785
2786 if (mMachineState == MachineState_Running)
2787 {
2788 /* get the VM handle. */
2789 SafeVMPtr ptrVM(this);
2790 if (!ptrVM.isOk())
2791 return ptrVM.rc();
2792
2793 /* release the lock before a VMR3* call (EMT will call us back)! */
2794 alock.release();
2795 int vrc = VMR3Suspend(ptrVM.rawUVM());
2796 alock.acquire();
2797
2798 HRESULT hrc = S_OK;
2799 if (RT_FAILURE(vrc))
2800 hrc = setError(VBOX_E_VM_ERROR, tr("Could not suspend the machine execution (%Rrc)"), vrc);
2801 if (FAILED(hrc))
2802 return hrc;
2803 }
2804
2805 HRESULT rc = S_OK;
2806 bool fBeganSavingState = false;
2807 bool fTaskCreationFailed = false;
2808
2809 do
2810 {
2811 ComPtr<IProgress> pProgress;
2812 Bstr stateFilePath;
2813
2814 /*
2815 * request a saved state file path from the server
2816 * (this will set the machine state to Saving on the server to block
2817 * others from accessing this machine)
2818 */
2819 rc = mControl->BeginSavingState(pProgress.asOutParam(),
2820 stateFilePath.asOutParam());
2821 if (FAILED(rc))
2822 break;
2823
2824 fBeganSavingState = true;
2825
2826 /* sync the state with the server */
2827 setMachineStateLocally(MachineState_Saving);
2828
2829 /* ensure the directory for the saved state file exists */
2830 {
2831 Utf8Str dir = stateFilePath;
2832 dir.stripFilename();
2833 if (!RTDirExists(dir.c_str()))
2834 {
2835 int vrc = RTDirCreateFullPath(dir.c_str(), 0700);
2836 if (RT_FAILURE(vrc))
2837 {
2838 rc = setError(VBOX_E_FILE_ERROR,
2839 tr("Could not create a directory '%s' to save the state to (%Rrc)"),
2840 dir.c_str(), vrc);
2841 break;
2842 }
2843 }
2844 }
2845
2846 /* create a task object early to ensure mpVM protection is successful */
2847 std::auto_ptr<VMSaveTask> task(new VMSaveTask(this, pProgress,
2848 stateFilePath,
2849 lastMachineState));
2850 rc = task->rc();
2851 /*
2852 * If we fail here it means a PowerDown() call happened on another
2853 * thread while we were doing Pause() (which releases the Console lock).
2854 * We assign PowerDown() a higher precedence than SaveState(),
2855 * therefore just return the error to the caller.
2856 */
2857 if (FAILED(rc))
2858 {
2859 fTaskCreationFailed = true;
2860 break;
2861 }
2862
2863 /* create a thread to wait until the VM state is saved */
2864 int vrc = RTThreadCreate(NULL, Console::saveStateThread, (void *)task.get(),
2865 0, RTTHREADTYPE_MAIN_WORKER, 0, "VMSave");
2866 if (RT_FAILURE(vrc))
2867 {
2868 rc = setError(E_FAIL, "Could not create VMSave thread (%Rrc)", vrc);
2869 break;
2870 }
2871
2872 /* task is now owned by saveStateThread(), so release it */
2873 task.release();
2874
2875 /* return the progress to the caller */
2876 pProgress.queryInterfaceTo(aProgress);
2877 } while (0);
2878
2879 if (FAILED(rc) && !fTaskCreationFailed)
2880 {
2881 /* preserve existing error info */
2882 ErrorInfoKeeper eik;
2883
2884 if (fBeganSavingState)
2885 {
2886 /*
2887 * cancel the requested save state procedure.
2888 * This will reset the machine state to the state it had right
2889 * before calling mControl->BeginSavingState().
2890 */
2891 mControl->EndSavingState(eik.getResultCode(), eik.getText().raw());
2892 }
2893
2894 if (lastMachineState == MachineState_Running)
2895 {
2896 /* restore the paused state if appropriate */
2897 setMachineStateLocally(MachineState_Paused);
2898 /* restore the running state if appropriate */
2899 SafeVMPtr ptrVM(this);
2900 if (ptrVM.isOk())
2901 {
2902 alock.release();
2903 VMR3Resume(ptrVM.rawUVM());
2904 alock.acquire();
2905 }
2906 }
2907 else
2908 setMachineStateLocally(lastMachineState);
2909 }
2910
2911 LogFlowThisFunc(("rc=%Rhrc\n", rc));
2912 LogFlowThisFuncLeave();
2913 return rc;
2914}
2915
2916STDMETHODIMP Console::AdoptSavedState(IN_BSTR aSavedStateFile)
2917{
2918 CheckComArgStrNotEmptyOrNull(aSavedStateFile);
2919
2920 AutoCaller autoCaller(this);
2921 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2922
2923 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2924
2925 if ( mMachineState != MachineState_PoweredOff
2926 && mMachineState != MachineState_Teleported
2927 && mMachineState != MachineState_Aborted
2928 )
2929 return setError(VBOX_E_INVALID_VM_STATE,
2930 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
2931 Global::stringifyMachineState(mMachineState));
2932
2933 return mControl->AdoptSavedState(aSavedStateFile);
2934}
2935
2936STDMETHODIMP Console::DiscardSavedState(BOOL aRemoveFile)
2937{
2938 AutoCaller autoCaller(this);
2939 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2940
2941 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2942
2943 if (mMachineState != MachineState_Saved)
2944 return setError(VBOX_E_INVALID_VM_STATE,
2945 tr("Cannot delete the machine state as the machine is not in the saved state (machine state: %s)"),
2946 Global::stringifyMachineState(mMachineState));
2947
2948 HRESULT rc = mControl->SetRemoveSavedStateFile(aRemoveFile);
2949 if (FAILED(rc)) return rc;
2950
2951 /*
2952 * Saved -> PoweredOff transition will be detected in the SessionMachine
2953 * and properly handled.
2954 */
2955 rc = setMachineState(MachineState_PoweredOff);
2956
2957 return rc;
2958}
2959
2960/** read the value of a LED. */
2961inline uint32_t readAndClearLed(PPDMLED pLed)
2962{
2963 if (!pLed)
2964 return 0;
2965 uint32_t u32 = pLed->Actual.u32 | pLed->Asserted.u32;
2966 pLed->Asserted.u32 = 0;
2967 return u32;
2968}
2969
2970STDMETHODIMP Console::GetDeviceActivity(DeviceType_T aDeviceType,
2971 DeviceActivity_T *aDeviceActivity)
2972{
2973 CheckComArgNotNull(aDeviceActivity);
2974
2975 AutoCaller autoCaller(this);
2976 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2977
2978 /*
2979 * Note: we don't lock the console object here because
2980 * readAndClearLed() should be thread safe.
2981 */
2982
2983 /* Get LED array to read */
2984 PDMLEDCORE SumLed = {0};
2985 switch (aDeviceType)
2986 {
2987 case DeviceType_Floppy:
2988 case DeviceType_DVD:
2989 case DeviceType_HardDisk:
2990 {
2991 for (unsigned i = 0; i < RT_ELEMENTS(mapStorageLeds); ++i)
2992 if (maStorageDevType[i] == aDeviceType)
2993 SumLed.u32 |= readAndClearLed(mapStorageLeds[i]);
2994 break;
2995 }
2996
2997 case DeviceType_Network:
2998 {
2999 for (unsigned i = 0; i < RT_ELEMENTS(mapNetworkLeds); ++i)
3000 SumLed.u32 |= readAndClearLed(mapNetworkLeds[i]);
3001 break;
3002 }
3003
3004 case DeviceType_USB:
3005 {
3006 for (unsigned i = 0; i < RT_ELEMENTS(mapUSBLed); ++i)
3007 SumLed.u32 |= readAndClearLed(mapUSBLed[i]);
3008 break;
3009 }
3010
3011 case DeviceType_SharedFolder:
3012 {
3013 SumLed.u32 |= readAndClearLed(mapSharedFolderLed);
3014 break;
3015 }
3016
3017 default:
3018 return setError(E_INVALIDARG,
3019 tr("Invalid device type: %d"),
3020 aDeviceType);
3021 }
3022
3023 /* Compose the result */
3024 switch (SumLed.u32 & (PDMLED_READING | PDMLED_WRITING))
3025 {
3026 case 0:
3027 *aDeviceActivity = DeviceActivity_Idle;
3028 break;
3029 case PDMLED_READING:
3030 *aDeviceActivity = DeviceActivity_Reading;
3031 break;
3032 case PDMLED_WRITING:
3033 case PDMLED_READING | PDMLED_WRITING:
3034 *aDeviceActivity = DeviceActivity_Writing;
3035 break;
3036 }
3037
3038 return S_OK;
3039}
3040
3041STDMETHODIMP Console::AttachUSBDevice(IN_BSTR aId)
3042{
3043#ifdef VBOX_WITH_USB
3044 AutoCaller autoCaller(this);
3045 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3046
3047 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3048
3049 if ( mMachineState != MachineState_Running
3050 && mMachineState != MachineState_Paused)
3051 return setError(VBOX_E_INVALID_VM_STATE,
3052 tr("Cannot attach a USB device to the machine which is not running or paused (machine state: %s)"),
3053 Global::stringifyMachineState(mMachineState));
3054
3055 /* Get the VM handle. */
3056 SafeVMPtr ptrVM(this);
3057 if (!ptrVM.isOk())
3058 return ptrVM.rc();
3059
3060 /* Don't proceed unless we've found the usb controller. */
3061 PPDMIBASE pBase = NULL;
3062 int vrc = PDMR3QueryLun(ptrVM, "usb-ohci", 0, 0, &pBase);
3063 if (RT_FAILURE(vrc))
3064 return setError(VBOX_E_PDM_ERROR,
3065 tr("The virtual machine does not have a USB controller"));
3066
3067 /* release the lock because the USB Proxy service may call us back
3068 * (via onUSBDeviceAttach()) */
3069 alock.release();
3070
3071 /* Request the device capture */
3072 return mControl->CaptureUSBDevice(aId);
3073
3074#else /* !VBOX_WITH_USB */
3075 return setError(VBOX_E_PDM_ERROR,
3076 tr("The virtual machine does not have a USB controller"));
3077#endif /* !VBOX_WITH_USB */
3078}
3079
3080STDMETHODIMP Console::DetachUSBDevice(IN_BSTR aId, IUSBDevice **aDevice)
3081{
3082#ifdef VBOX_WITH_USB
3083 CheckComArgOutPointerValid(aDevice);
3084
3085 AutoCaller autoCaller(this);
3086 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3087
3088 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3089
3090 /* Find it. */
3091 ComObjPtr<OUSBDevice> pUSBDevice;
3092 USBDeviceList::iterator it = mUSBDevices.begin();
3093 Guid uuid(aId);
3094 while (it != mUSBDevices.end())
3095 {
3096 if ((*it)->id() == uuid)
3097 {
3098 pUSBDevice = *it;
3099 break;
3100 }
3101 ++it;
3102 }
3103
3104 if (!pUSBDevice)
3105 return setError(E_INVALIDARG,
3106 tr("USB device with UUID {%RTuuid} is not attached to this machine"),
3107 Guid(aId).raw());
3108
3109 /* Remove the device from the collection, it is re-added below for failures */
3110 mUSBDevices.erase(it);
3111
3112 /*
3113 * Inform the USB device and USB proxy about what's cooking.
3114 */
3115 alock.release();
3116 HRESULT rc = mControl->DetachUSBDevice(aId, false /* aDone */);
3117 if (FAILED(rc))
3118 {
3119 /* Re-add the device to the collection */
3120 alock.acquire();
3121 mUSBDevices.push_back(pUSBDevice);
3122 return rc;
3123 }
3124
3125 /* Request the PDM to detach the USB device. */
3126 rc = detachUSBDevice(pUSBDevice);
3127 if (SUCCEEDED(rc))
3128 {
3129 /* Request the device release. Even if it fails, the device will
3130 * remain as held by proxy, which is OK for us (the VM process). */
3131 rc = mControl->DetachUSBDevice(aId, true /* aDone */);
3132 }
3133 else
3134 {
3135 /* Re-add the device to the collection */
3136 alock.acquire();
3137 mUSBDevices.push_back(pUSBDevice);
3138 }
3139
3140 return rc;
3141
3142
3143#else /* !VBOX_WITH_USB */
3144 return setError(VBOX_E_PDM_ERROR,
3145 tr("The virtual machine does not have a USB controller"));
3146#endif /* !VBOX_WITH_USB */
3147}
3148
3149STDMETHODIMP Console::FindUSBDeviceByAddress(IN_BSTR aAddress, IUSBDevice **aDevice)
3150{
3151#ifdef VBOX_WITH_USB
3152 CheckComArgStrNotEmptyOrNull(aAddress);
3153 CheckComArgOutPointerValid(aDevice);
3154
3155 *aDevice = NULL;
3156
3157 SafeIfaceArray<IUSBDevice> devsvec;
3158 HRESULT rc = COMGETTER(USBDevices)(ComSafeArrayAsOutParam(devsvec));
3159 if (FAILED(rc)) return rc;
3160
3161 for (size_t i = 0; i < devsvec.size(); ++i)
3162 {
3163 Bstr address;
3164 rc = devsvec[i]->COMGETTER(Address)(address.asOutParam());
3165 if (FAILED(rc)) return rc;
3166 if (address == aAddress)
3167 {
3168 ComObjPtr<OUSBDevice> pUSBDevice;
3169 pUSBDevice.createObject();
3170 pUSBDevice->init(devsvec[i]);
3171 return pUSBDevice.queryInterfaceTo(aDevice);
3172 }
3173 }
3174
3175 return setErrorNoLog(VBOX_E_OBJECT_NOT_FOUND,
3176 tr("Could not find a USB device with address '%ls'"),
3177 aAddress);
3178
3179#else /* !VBOX_WITH_USB */
3180 return E_NOTIMPL;
3181#endif /* !VBOX_WITH_USB */
3182}
3183
3184STDMETHODIMP Console::FindUSBDeviceById(IN_BSTR aId, IUSBDevice **aDevice)
3185{
3186#ifdef VBOX_WITH_USB
3187 CheckComArgExpr(aId, Guid(aId).isValid());
3188 CheckComArgOutPointerValid(aDevice);
3189
3190 *aDevice = NULL;
3191
3192 SafeIfaceArray<IUSBDevice> devsvec;
3193 HRESULT rc = COMGETTER(USBDevices)(ComSafeArrayAsOutParam(devsvec));
3194 if (FAILED(rc)) return rc;
3195
3196 for (size_t i = 0; i < devsvec.size(); ++i)
3197 {
3198 Bstr id;
3199 rc = devsvec[i]->COMGETTER(Id)(id.asOutParam());
3200 if (FAILED(rc)) return rc;
3201 if (id == aId)
3202 {
3203 ComObjPtr<OUSBDevice> pUSBDevice;
3204 pUSBDevice.createObject();
3205 pUSBDevice->init(devsvec[i]);
3206 return pUSBDevice.queryInterfaceTo(aDevice);
3207 }
3208 }
3209
3210 return setErrorNoLog(VBOX_E_OBJECT_NOT_FOUND,
3211 tr("Could not find a USB device with uuid {%RTuuid}"),
3212 Guid(aId).raw());
3213
3214#else /* !VBOX_WITH_USB */
3215 return E_NOTIMPL;
3216#endif /* !VBOX_WITH_USB */
3217}
3218
3219STDMETHODIMP
3220Console::CreateSharedFolder(IN_BSTR aName, IN_BSTR aHostPath, BOOL aWritable, BOOL aAutoMount)
3221{
3222 CheckComArgStrNotEmptyOrNull(aName);
3223 CheckComArgStrNotEmptyOrNull(aHostPath);
3224
3225 LogFlowThisFunc(("Entering for '%ls' -> '%ls'\n", aName, aHostPath));
3226
3227 AutoCaller autoCaller(this);
3228 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3229
3230 Utf8Str strName(aName);
3231 Utf8Str strHostPath(aHostPath);
3232
3233 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3234
3235 /// @todo see @todo in AttachUSBDevice() about the Paused state
3236 if (mMachineState == MachineState_Saved)
3237 return setError(VBOX_E_INVALID_VM_STATE,
3238 tr("Cannot create a transient shared folder on the machine in the saved state"));
3239 if ( mMachineState != MachineState_PoweredOff
3240 && mMachineState != MachineState_Teleported
3241 && mMachineState != MachineState_Aborted
3242 && mMachineState != MachineState_Running
3243 && mMachineState != MachineState_Paused
3244 )
3245 return setError(VBOX_E_INVALID_VM_STATE,
3246 tr("Cannot create a transient shared folder on the machine while it is changing the state (machine state: %s)"),
3247 Global::stringifyMachineState(mMachineState));
3248
3249 ComObjPtr<SharedFolder> pSharedFolder;
3250 HRESULT rc = findSharedFolder(strName, pSharedFolder, false /* aSetError */);
3251 if (SUCCEEDED(rc))
3252 return setError(VBOX_E_FILE_ERROR,
3253 tr("Shared folder named '%s' already exists"),
3254 strName.c_str());
3255
3256 pSharedFolder.createObject();
3257 rc = pSharedFolder->init(this,
3258 strName,
3259 strHostPath,
3260 !!aWritable,
3261 !!aAutoMount,
3262 true /* fFailOnError */);
3263 if (FAILED(rc)) return rc;
3264
3265 /* If the VM is online and supports shared folders, share this folder
3266 * under the specified name. (Ignore any failure to obtain the VM handle.) */
3267 SafeVMPtrQuiet ptrVM(this);
3268 if ( ptrVM.isOk()
3269 && m_pVMMDev
3270 && m_pVMMDev->isShFlActive()
3271 )
3272 {
3273 /* first, remove the machine or the global folder if there is any */
3274 SharedFolderDataMap::const_iterator it;
3275 if (findOtherSharedFolder(aName, it))
3276 {
3277 rc = removeSharedFolder(aName);
3278 if (FAILED(rc))
3279 return rc;
3280 }
3281
3282 /* second, create the given folder */
3283 rc = createSharedFolder(aName, SharedFolderData(aHostPath, !!aWritable, !!aAutoMount));
3284 if (FAILED(rc))
3285 return rc;
3286 }
3287
3288 m_mapSharedFolders.insert(std::make_pair(aName, pSharedFolder));
3289
3290 /* Notify console callbacks after the folder is added to the list. */
3291 alock.release();
3292 fireSharedFolderChangedEvent(mEventSource, Scope_Session);
3293
3294 LogFlowThisFunc(("Leaving for '%ls' -> '%ls'\n", aName, aHostPath));
3295
3296 return rc;
3297}
3298
3299STDMETHODIMP Console::RemoveSharedFolder(IN_BSTR aName)
3300{
3301 CheckComArgStrNotEmptyOrNull(aName);
3302
3303 AutoCaller autoCaller(this);
3304 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3305
3306 LogFlowThisFunc(("Entering for '%ls'\n", aName));
3307
3308 Utf8Str strName(aName);
3309
3310 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3311
3312 /// @todo see @todo in AttachUSBDevice() about the Paused state
3313 if (mMachineState == MachineState_Saved)
3314 return setError(VBOX_E_INVALID_VM_STATE,
3315 tr("Cannot remove a transient shared folder from the machine in the saved state"));
3316 if ( mMachineState != MachineState_PoweredOff
3317 && mMachineState != MachineState_Teleported
3318 && mMachineState != MachineState_Aborted
3319 && mMachineState != MachineState_Running
3320 && mMachineState != MachineState_Paused
3321 )
3322 return setError(VBOX_E_INVALID_VM_STATE,
3323 tr("Cannot remove a transient shared folder from the machine while it is changing the state (machine state: %s)"),
3324 Global::stringifyMachineState(mMachineState));
3325
3326 ComObjPtr<SharedFolder> pSharedFolder;
3327 HRESULT rc = findSharedFolder(aName, pSharedFolder, true /* aSetError */);
3328 if (FAILED(rc)) return rc;
3329
3330 /* protect the VM handle (if not NULL) */
3331 SafeVMPtrQuiet ptrVM(this);
3332 if ( ptrVM.isOk()
3333 && m_pVMMDev
3334 && m_pVMMDev->isShFlActive()
3335 )
3336 {
3337 /* if the VM is online and supports shared folders, UNshare this
3338 * folder. */
3339
3340 /* first, remove the given folder */
3341 rc = removeSharedFolder(strName);
3342 if (FAILED(rc)) return rc;
3343
3344 /* first, remove the machine or the global folder if there is any */
3345 SharedFolderDataMap::const_iterator it;
3346 if (findOtherSharedFolder(strName, it))
3347 {
3348 rc = createSharedFolder(strName, it->second);
3349 /* don't check rc here because we need to remove the console
3350 * folder from the collection even on failure */
3351 }
3352 }
3353
3354 m_mapSharedFolders.erase(strName);
3355
3356 /* Notify console callbacks after the folder is removed from the list. */
3357 alock.release();
3358 fireSharedFolderChangedEvent(mEventSource, Scope_Session);
3359
3360 LogFlowThisFunc(("Leaving for '%ls'\n", aName));
3361
3362 return rc;
3363}
3364
3365STDMETHODIMP Console::TakeSnapshot(IN_BSTR aName,
3366 IN_BSTR aDescription,
3367 IProgress **aProgress)
3368{
3369 LogFlowThisFuncEnter();
3370 LogFlowThisFunc(("aName='%ls' mMachineState=%d\n", aName, mMachineState));
3371
3372 CheckComArgStrNotEmptyOrNull(aName);
3373 CheckComArgOutPointerValid(aProgress);
3374
3375 AutoCaller autoCaller(this);
3376 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3377
3378 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3379
3380 if (Global::IsTransient(mMachineState))
3381 return setError(VBOX_E_INVALID_VM_STATE,
3382 tr("Cannot take a snapshot of the machine while it is changing the state (machine state: %s)"),
3383 Global::stringifyMachineState(mMachineState));
3384
3385 HRESULT rc = S_OK;
3386
3387 /* prepare the progress object:
3388 a) count the no. of hard disk attachments to get a matching no. of progress sub-operations */
3389 ULONG cOperations = 2; // always at least setting up + finishing up
3390 ULONG ulTotalOperationsWeight = 2; // one each for setting up + finishing up
3391 SafeIfaceArray<IMediumAttachment> aMediumAttachments;
3392 rc = mMachine->COMGETTER(MediumAttachments)(ComSafeArrayAsOutParam(aMediumAttachments));
3393 if (FAILED(rc))
3394 return setError(rc, tr("Cannot get medium attachments of the machine"));
3395
3396 ULONG ulMemSize;
3397 rc = mMachine->COMGETTER(MemorySize)(&ulMemSize);
3398 if (FAILED(rc))
3399 return rc;
3400
3401 for (size_t i = 0;
3402 i < aMediumAttachments.size();
3403 ++i)
3404 {
3405 DeviceType_T type;
3406 rc = aMediumAttachments[i]->COMGETTER(Type)(&type);
3407 if (FAILED(rc))
3408 return rc;
3409
3410 if (type == DeviceType_HardDisk)
3411 {
3412 ++cOperations;
3413
3414 // assume that creating a diff image takes as long as saving a 1MB state
3415 // (note, the same value must be used in SessionMachine::BeginTakingSnapshot() on the server!)
3416 ulTotalOperationsWeight += 1;
3417 }
3418 }
3419
3420 // b) one extra sub-operations for online snapshots OR offline snapshots that have a saved state (needs to be copied)
3421 bool const fTakingSnapshotOnline = Global::IsOnline(mMachineState);
3422
3423 LogFlowFunc(("fTakingSnapshotOnline = %d, mMachineState = %d\n", fTakingSnapshotOnline, mMachineState));
3424
3425 if (fTakingSnapshotOnline)
3426 {
3427 ++cOperations;
3428 ulTotalOperationsWeight += ulMemSize;
3429 }
3430
3431 // finally, create the progress object
3432 ComObjPtr<Progress> pProgress;
3433 pProgress.createObject();
3434 rc = pProgress->init(static_cast<IConsole *>(this),
3435 Bstr(tr("Taking a snapshot of the virtual machine")).raw(),
3436 (mMachineState >= MachineState_FirstOnline)
3437 && (mMachineState <= MachineState_LastOnline) /* aCancelable */,
3438 cOperations,
3439 ulTotalOperationsWeight,
3440 Bstr(tr("Setting up snapshot operation")).raw(), // first sub-op description
3441 1); // ulFirstOperationWeight
3442
3443 if (FAILED(rc))
3444 return rc;
3445
3446 VMTakeSnapshotTask *pTask;
3447 if (!(pTask = new VMTakeSnapshotTask(this, pProgress, aName, aDescription)))
3448 return E_OUTOFMEMORY;
3449
3450 Assert(pTask->mProgress);
3451
3452 try
3453 {
3454 mptrCancelableProgress = pProgress;
3455
3456 /*
3457 * If we fail here it means a PowerDown() call happened on another
3458 * thread while we were doing Pause() (which releases the Console lock).
3459 * We assign PowerDown() a higher precedence than TakeSnapshot(),
3460 * therefore just return the error to the caller.
3461 */
3462 rc = pTask->rc();
3463 if (FAILED(rc)) throw rc;
3464
3465 pTask->ulMemSize = ulMemSize;
3466
3467 /* memorize the current machine state */
3468 pTask->lastMachineState = mMachineState;
3469 pTask->fTakingSnapshotOnline = fTakingSnapshotOnline;
3470
3471 int vrc = RTThreadCreate(NULL,
3472 Console::fntTakeSnapshotWorker,
3473 (void *)pTask,
3474 0,
3475 RTTHREADTYPE_MAIN_WORKER,
3476 0,
3477 "TakeSnap");
3478 if (FAILED(vrc))
3479 throw setError(E_FAIL,
3480 tr("Could not create VMTakeSnap thread (%Rrc)"),
3481 vrc);
3482
3483 pTask->mProgress.queryInterfaceTo(aProgress);
3484 }
3485 catch (HRESULT erc)
3486 {
3487 delete pTask;
3488 rc = erc;
3489 mptrCancelableProgress.setNull();
3490 }
3491
3492 LogFlowThisFunc(("rc=%Rhrc\n", rc));
3493 LogFlowThisFuncLeave();
3494 return rc;
3495}
3496
3497STDMETHODIMP Console::DeleteSnapshot(IN_BSTR aId, IProgress **aProgress)
3498{
3499 CheckComArgExpr(aId, Guid(aId).isValid());
3500 CheckComArgOutPointerValid(aProgress);
3501
3502 AutoCaller autoCaller(this);
3503 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3504
3505 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3506
3507 if (Global::IsTransient(mMachineState))
3508 return setError(VBOX_E_INVALID_VM_STATE,
3509 tr("Cannot delete a snapshot of the machine while it is changing the state (machine state: %s)"),
3510 Global::stringifyMachineState(mMachineState));
3511
3512 MachineState_T machineState = MachineState_Null;
3513 HRESULT rc = mControl->DeleteSnapshot(this, aId, aId, FALSE /* fDeleteAllChildren */, &machineState, aProgress);
3514 if (FAILED(rc)) return rc;
3515
3516 setMachineStateLocally(machineState);
3517 return S_OK;
3518}
3519
3520STDMETHODIMP Console::DeleteSnapshotAndAllChildren(IN_BSTR aId, IProgress **aProgress)
3521{
3522 CheckComArgExpr(aId, Guid(aId).isValid());
3523 CheckComArgOutPointerValid(aProgress);
3524
3525 AutoCaller autoCaller(this);
3526 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3527
3528 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3529
3530 if (Global::IsTransient(mMachineState))
3531 return setError(VBOX_E_INVALID_VM_STATE,
3532 tr("Cannot delete a snapshot of the machine while it is changing the state (machine state: %s)"),
3533 Global::stringifyMachineState(mMachineState));
3534
3535 MachineState_T machineState = MachineState_Null;
3536 HRESULT rc = mControl->DeleteSnapshot(this, aId, aId, TRUE /* fDeleteAllChildren */, &machineState, aProgress);
3537 if (FAILED(rc)) return rc;
3538
3539 setMachineStateLocally(machineState);
3540 return S_OK;
3541}
3542
3543STDMETHODIMP Console::DeleteSnapshotRange(IN_BSTR aStartId, IN_BSTR aEndId, IProgress **aProgress)
3544{
3545 CheckComArgExpr(aStartId, Guid(aStartId).isValid());
3546 CheckComArgExpr(aEndId, Guid(aEndId).isValid());
3547 CheckComArgOutPointerValid(aProgress);
3548
3549 AutoCaller autoCaller(this);
3550 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3551
3552 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3553
3554 if (Global::IsTransient(mMachineState))
3555 return setError(VBOX_E_INVALID_VM_STATE,
3556 tr("Cannot delete a snapshot of the machine while it is changing the state (machine state: %s)"),
3557 Global::stringifyMachineState(mMachineState));
3558
3559 MachineState_T machineState = MachineState_Null;
3560 HRESULT rc = mControl->DeleteSnapshot(this, aStartId, aEndId, FALSE /* fDeleteAllChildren */, &machineState, aProgress);
3561 if (FAILED(rc)) return rc;
3562
3563 setMachineStateLocally(machineState);
3564 return S_OK;
3565}
3566
3567STDMETHODIMP Console::RestoreSnapshot(ISnapshot *aSnapshot, IProgress **aProgress)
3568{
3569 AutoCaller autoCaller(this);
3570 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3571
3572 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3573
3574 if (Global::IsOnlineOrTransient(mMachineState))
3575 return setError(VBOX_E_INVALID_VM_STATE,
3576 tr("Cannot delete the current state of the running machine (machine state: %s)"),
3577 Global::stringifyMachineState(mMachineState));
3578
3579 MachineState_T machineState = MachineState_Null;
3580 HRESULT rc = mControl->RestoreSnapshot(this, aSnapshot, &machineState, aProgress);
3581 if (FAILED(rc)) return rc;
3582
3583 setMachineStateLocally(machineState);
3584 return S_OK;
3585}
3586
3587// Non-interface public methods
3588/////////////////////////////////////////////////////////////////////////////
3589
3590/*static*/
3591HRESULT Console::setErrorStatic(HRESULT aResultCode, const char *pcsz, ...)
3592{
3593 va_list args;
3594 va_start(args, pcsz);
3595 HRESULT rc = setErrorInternal(aResultCode,
3596 getStaticClassIID(),
3597 getStaticComponentName(),
3598 Utf8Str(pcsz, args),
3599 false /* aWarning */,
3600 true /* aLogIt */);
3601 va_end(args);
3602 return rc;
3603}
3604
3605HRESULT Console::setInvalidMachineStateError()
3606{
3607 return setError(VBOX_E_INVALID_VM_STATE,
3608 tr("Invalid machine state: %s"),
3609 Global::stringifyMachineState(mMachineState));
3610}
3611
3612
3613/* static */
3614const char *Console::convertControllerTypeToDev(StorageControllerType_T enmCtrlType)
3615{
3616 switch (enmCtrlType)
3617 {
3618 case StorageControllerType_LsiLogic:
3619 return "lsilogicscsi";
3620 case StorageControllerType_BusLogic:
3621 return "buslogic";
3622 case StorageControllerType_LsiLogicSas:
3623 return "lsilogicsas";
3624 case StorageControllerType_IntelAhci:
3625 return "ahci";
3626 case StorageControllerType_PIIX3:
3627 case StorageControllerType_PIIX4:
3628 case StorageControllerType_ICH6:
3629 return "piix3ide";
3630 case StorageControllerType_I82078:
3631 return "i82078";
3632 default:
3633 return NULL;
3634 }
3635}
3636
3637HRESULT Console::convertBusPortDeviceToLun(StorageBus_T enmBus, LONG port, LONG device, unsigned &uLun)
3638{
3639 switch (enmBus)
3640 {
3641 case StorageBus_IDE:
3642 case StorageBus_Floppy:
3643 {
3644 AssertMsgReturn(port < 2 && port >= 0, ("%d\n", port), E_INVALIDARG);
3645 AssertMsgReturn(device < 2 && device >= 0, ("%d\n", device), E_INVALIDARG);
3646 uLun = 2 * port + device;
3647 return S_OK;
3648 }
3649 case StorageBus_SATA:
3650 case StorageBus_SCSI:
3651 case StorageBus_SAS:
3652 {
3653 uLun = port;
3654 return S_OK;
3655 }
3656 default:
3657 uLun = 0;
3658 AssertMsgFailedReturn(("%d\n", enmBus), E_INVALIDARG);
3659 }
3660}
3661
3662// private methods
3663/////////////////////////////////////////////////////////////////////////////
3664
3665/**
3666 * Process a medium change.
3667 *
3668 * @param aMediumAttachment The medium attachment with the new medium state.
3669 * @param fForce Force medium chance, if it is locked or not.
3670 * @param pUVM Safe VM handle.
3671 *
3672 * @note Locks this object for writing.
3673 */
3674HRESULT Console::doMediumChange(IMediumAttachment *aMediumAttachment, bool fForce, PUVM pUVM)
3675{
3676 AutoCaller autoCaller(this);
3677 AssertComRCReturnRC(autoCaller.rc());
3678
3679 /* We will need to release the write lock before calling EMT */
3680 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3681
3682 HRESULT rc = S_OK;
3683 const char *pszDevice = NULL;
3684
3685 SafeIfaceArray<IStorageController> ctrls;
3686 rc = mMachine->COMGETTER(StorageControllers)(ComSafeArrayAsOutParam(ctrls));
3687 AssertComRC(rc);
3688 IMedium *pMedium;
3689 rc = aMediumAttachment->COMGETTER(Medium)(&pMedium);
3690 AssertComRC(rc);
3691 Bstr mediumLocation;
3692 if (pMedium)
3693 {
3694 rc = pMedium->COMGETTER(Location)(mediumLocation.asOutParam());
3695 AssertComRC(rc);
3696 }
3697
3698 Bstr attCtrlName;
3699 rc = aMediumAttachment->COMGETTER(Controller)(attCtrlName.asOutParam());
3700 AssertComRC(rc);
3701 ComPtr<IStorageController> pStorageController;
3702 for (size_t i = 0; i < ctrls.size(); ++i)
3703 {
3704 Bstr ctrlName;
3705 rc = ctrls[i]->COMGETTER(Name)(ctrlName.asOutParam());
3706 AssertComRC(rc);
3707 if (attCtrlName == ctrlName)
3708 {
3709 pStorageController = ctrls[i];
3710 break;
3711 }
3712 }
3713 if (pStorageController.isNull())
3714 return setError(E_FAIL,
3715 tr("Could not find storage controller '%ls'"), attCtrlName.raw());
3716
3717 StorageControllerType_T enmCtrlType;
3718 rc = pStorageController->COMGETTER(ControllerType)(&enmCtrlType);
3719 AssertComRC(rc);
3720 pszDevice = convertControllerTypeToDev(enmCtrlType);
3721
3722 StorageBus_T enmBus;
3723 rc = pStorageController->COMGETTER(Bus)(&enmBus);
3724 AssertComRC(rc);
3725 ULONG uInstance;
3726 rc = pStorageController->COMGETTER(Instance)(&uInstance);
3727 AssertComRC(rc);
3728 BOOL fUseHostIOCache;
3729 rc = pStorageController->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
3730 AssertComRC(rc);
3731
3732 /*
3733 * Call worker in EMT, that's faster and safer than doing everything
3734 * using VMR3ReqCall. Note that we separate VMR3ReqCall from VMR3ReqWait
3735 * here to make requests from under the lock in order to serialize them.
3736 */
3737 PVMREQ pReq;
3738 int vrc = VMR3ReqCallU(pUVM,
3739 VMCPUID_ANY,
3740 &pReq,
3741 0 /* no wait! */,
3742 VMREQFLAGS_VBOX_STATUS,
3743 (PFNRT)Console::changeRemovableMedium,
3744 8,
3745 this,
3746 pUVM,
3747 pszDevice,
3748 uInstance,
3749 enmBus,
3750 fUseHostIOCache,
3751 aMediumAttachment,
3752 fForce);
3753
3754 /* release the lock before waiting for a result (EMT will call us back!) */
3755 alock.release();
3756
3757 if (vrc == VERR_TIMEOUT || RT_SUCCESS(vrc))
3758 {
3759 vrc = VMR3ReqWait(pReq, RT_INDEFINITE_WAIT);
3760 AssertRC(vrc);
3761 if (RT_SUCCESS(vrc))
3762 vrc = pReq->iStatus;
3763 }
3764 VMR3ReqFree(pReq);
3765
3766 if (RT_SUCCESS(vrc))
3767 {
3768 LogFlowThisFunc(("Returns S_OK\n"));
3769 return S_OK;
3770 }
3771
3772 if (pMedium)
3773 return setError(E_FAIL,
3774 tr("Could not mount the media/drive '%ls' (%Rrc)"),
3775 mediumLocation.raw(), vrc);
3776
3777 return setError(E_FAIL,
3778 tr("Could not unmount the currently mounted media/drive (%Rrc)"),
3779 vrc);
3780}
3781
3782/**
3783 * Performs the medium change in EMT.
3784 *
3785 * @returns VBox status code.
3786 *
3787 * @param pThis Pointer to the Console object.
3788 * @param pUVM The VM handle.
3789 * @param pcszDevice The PDM device name.
3790 * @param uInstance The PDM device instance.
3791 * @param uLun The PDM LUN number of the drive.
3792 * @param fHostDrive True if this is a host drive attachment.
3793 * @param pszPath The path to the media / drive which is now being mounted / captured.
3794 * If NULL no media or drive is attached and the LUN will be configured with
3795 * the default block driver with no media. This will also be the state if
3796 * mounting / capturing the specified media / drive fails.
3797 * @param pszFormat Medium format string, usually "RAW".
3798 * @param fPassthrough Enables using passthrough mode of the host DVD drive if applicable.
3799 *
3800 * @thread EMT
3801 */
3802DECLCALLBACK(int) Console::changeRemovableMedium(Console *pConsole,
3803 PUVM pUVM,
3804 const char *pcszDevice,
3805 unsigned uInstance,
3806 StorageBus_T enmBus,
3807 bool fUseHostIOCache,
3808 IMediumAttachment *aMediumAtt,
3809 bool fForce)
3810{
3811 LogFlowFunc(("pConsole=%p uInstance=%u pszDevice=%p:{%s} enmBus=%u, aMediumAtt=%p, fForce=%d\n",
3812 pConsole, uInstance, pcszDevice, pcszDevice, enmBus, aMediumAtt, fForce));
3813
3814 AssertReturn(pConsole, VERR_INVALID_PARAMETER);
3815
3816 AutoCaller autoCaller(pConsole);
3817 AssertComRCReturn(autoCaller.rc(), VERR_ACCESS_DENIED);
3818
3819 /*
3820 * Suspend the VM first.
3821 *
3822 * The VM must not be running since it might have pending I/O to
3823 * the drive which is being changed.
3824 */
3825 bool fResume;
3826 VMSTATE enmVMState = VMR3GetStateU(pUVM);
3827 switch (enmVMState)
3828 {
3829 case VMSTATE_RESETTING:
3830 case VMSTATE_RUNNING:
3831 {
3832 LogFlowFunc(("Suspending the VM...\n"));
3833 /* disable the callback to prevent Console-level state change */
3834 pConsole->mVMStateChangeCallbackDisabled = true;
3835 int rc = VMR3Suspend(pUVM);
3836 pConsole->mVMStateChangeCallbackDisabled = false;
3837 AssertRCReturn(rc, rc);
3838 fResume = true;
3839 break;
3840 }
3841
3842 case VMSTATE_SUSPENDED:
3843 case VMSTATE_CREATED:
3844 case VMSTATE_OFF:
3845 fResume = false;
3846 break;
3847
3848 case VMSTATE_RUNNING_LS:
3849 case VMSTATE_RUNNING_FT:
3850 return setErrorInternal(VBOX_E_INVALID_VM_STATE,
3851 COM_IIDOF(IConsole),
3852 getStaticComponentName(),
3853 (enmVMState == VMSTATE_RUNNING_LS) ? Utf8Str(tr("Cannot change drive during live migration")) : Utf8Str(tr("Cannot change drive during fault tolerant syncing")),
3854 false /*aWarning*/,
3855 true /*aLogIt*/);
3856
3857 default:
3858 AssertMsgFailedReturn(("enmVMState=%d\n", enmVMState), VERR_ACCESS_DENIED);
3859 }
3860
3861 /* Determine the base path for the device instance. */
3862 PCFGMNODE pCtlInst;
3863 pCtlInst = CFGMR3GetChildF(CFGMR3GetRootU(pUVM), "Devices/%s/%u/", pcszDevice, uInstance);
3864 AssertReturn(pCtlInst, VERR_INTERNAL_ERROR);
3865
3866 int rc = VINF_SUCCESS;
3867 int rcRet = VINF_SUCCESS;
3868
3869 rcRet = pConsole->configMediumAttachment(pCtlInst,
3870 pcszDevice,
3871 uInstance,
3872 enmBus,
3873 fUseHostIOCache,
3874 false /* fSetupMerge */,
3875 false /* fBuiltinIOCache */,
3876 0 /* uMergeSource */,
3877 0 /* uMergeTarget */,
3878 aMediumAtt,
3879 pConsole->mMachineState,
3880 NULL /* phrc */,
3881 true /* fAttachDetach */,
3882 fForce /* fForceUnmount */,
3883 false /* fHotplug */,
3884 pUVM,
3885 NULL /* paLedDevType */);
3886 /** @todo this dumps everything attached to this device instance, which
3887 * is more than necessary. Dumping the changed LUN would be enough. */
3888 CFGMR3Dump(pCtlInst);
3889
3890 /*
3891 * Resume the VM if necessary.
3892 */
3893 if (fResume)
3894 {
3895 LogFlowFunc(("Resuming the VM...\n"));
3896 /* disable the callback to prevent Console-level state change */
3897 pConsole->mVMStateChangeCallbackDisabled = true;
3898 rc = VMR3Resume(pUVM);
3899 pConsole->mVMStateChangeCallbackDisabled = false;
3900 AssertRC(rc);
3901 if (RT_FAILURE(rc))
3902 {
3903 /* too bad, we failed. try to sync the console state with the VMM state */
3904 vmstateChangeCallback(VMR3GetVM(pUVM), VMSTATE_SUSPENDED, enmVMState, pConsole);
3905 }
3906 /// @todo (r=dmik) if we failed with drive mount, then the VMR3Resume
3907 // error (if any) will be hidden from the caller. For proper reporting
3908 // of such multiple errors to the caller we need to enhance the
3909 // IVirtualBoxError interface. For now, give the first error the higher
3910 // priority.
3911 if (RT_SUCCESS(rcRet))
3912 rcRet = rc;
3913 }
3914
3915 LogFlowFunc(("Returning %Rrc\n", rcRet));
3916 return rcRet;
3917}
3918
3919
3920/**
3921 * Attach a new storage device to the VM.
3922 *
3923 * @param aMediumAttachment The medium attachment which is added.
3924 * @param pUVM Safe VM handle.
3925 *
3926 * @note Locks this object for writing.
3927 */
3928HRESULT Console::doStorageDeviceAttach(IMediumAttachment *aMediumAttachment, PUVM pUVM)
3929{
3930 AutoCaller autoCaller(this);
3931 AssertComRCReturnRC(autoCaller.rc());
3932
3933 /* We will need to release the write lock before calling EMT */
3934 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3935
3936 HRESULT rc = S_OK;
3937 const char *pszDevice = NULL;
3938
3939 SafeIfaceArray<IStorageController> ctrls;
3940 rc = mMachine->COMGETTER(StorageControllers)(ComSafeArrayAsOutParam(ctrls));
3941 AssertComRC(rc);
3942 IMedium *pMedium;
3943 rc = aMediumAttachment->COMGETTER(Medium)(&pMedium);
3944 AssertComRC(rc);
3945 Bstr mediumLocation;
3946 if (pMedium)
3947 {
3948 rc = pMedium->COMGETTER(Location)(mediumLocation.asOutParam());
3949 AssertComRC(rc);
3950 }
3951
3952 Bstr attCtrlName;
3953 rc = aMediumAttachment->COMGETTER(Controller)(attCtrlName.asOutParam());
3954 AssertComRC(rc);
3955 ComPtr<IStorageController> pStorageController;
3956 for (size_t i = 0; i < ctrls.size(); ++i)
3957 {
3958 Bstr ctrlName;
3959 rc = ctrls[i]->COMGETTER(Name)(ctrlName.asOutParam());
3960 AssertComRC(rc);
3961 if (attCtrlName == ctrlName)
3962 {
3963 pStorageController = ctrls[i];
3964 break;
3965 }
3966 }
3967 if (pStorageController.isNull())
3968 return setError(E_FAIL,
3969 tr("Could not find storage controller '%ls'"), attCtrlName.raw());
3970
3971 StorageControllerType_T enmCtrlType;
3972 rc = pStorageController->COMGETTER(ControllerType)(&enmCtrlType);
3973 AssertComRC(rc);
3974 pszDevice = convertControllerTypeToDev(enmCtrlType);
3975
3976 StorageBus_T enmBus;
3977 rc = pStorageController->COMGETTER(Bus)(&enmBus);
3978 AssertComRC(rc);
3979 ULONG uInstance;
3980 rc = pStorageController->COMGETTER(Instance)(&uInstance);
3981 AssertComRC(rc);
3982 BOOL fUseHostIOCache;
3983 rc = pStorageController->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
3984 AssertComRC(rc);
3985
3986 /*
3987 * Call worker in EMT, that's faster and safer than doing everything
3988 * using VMR3ReqCall. Note that we separate VMR3ReqCall from VMR3ReqWait
3989 * here to make requests from under the lock in order to serialize them.
3990 */
3991 PVMREQ pReq;
3992 int vrc = VMR3ReqCallU(pUVM,
3993 VMCPUID_ANY,
3994 &pReq,
3995 0 /* no wait! */,
3996 VMREQFLAGS_VBOX_STATUS,
3997 (PFNRT)Console::attachStorageDevice,
3998 7,
3999 this,
4000 pUVM,
4001 pszDevice,
4002 uInstance,
4003 enmBus,
4004 fUseHostIOCache,
4005 aMediumAttachment);
4006
4007 /* release the lock before waiting for a result (EMT will call us back!) */
4008 alock.release();
4009
4010 if (vrc == VERR_TIMEOUT || RT_SUCCESS(vrc))
4011 {
4012 vrc = VMR3ReqWait(pReq, RT_INDEFINITE_WAIT);
4013 AssertRC(vrc);
4014 if (RT_SUCCESS(vrc))
4015 vrc = pReq->iStatus;
4016 }
4017 VMR3ReqFree(pReq);
4018
4019 if (RT_SUCCESS(vrc))
4020 {
4021 LogFlowThisFunc(("Returns S_OK\n"));
4022 return S_OK;
4023 }
4024
4025 if (!pMedium)
4026 return setError(E_FAIL,
4027 tr("Could not mount the media/drive '%ls' (%Rrc)"),
4028 mediumLocation.raw(), vrc);
4029
4030 return setError(E_FAIL,
4031 tr("Could not unmount the currently mounted media/drive (%Rrc)"),
4032 vrc);
4033}
4034
4035
4036/**
4037 * Performs the storage attach operation in EMT.
4038 *
4039 * @returns VBox status code.
4040 *
4041 * @param pThis Pointer to the Console object.
4042 * @param pUVM The VM handle.
4043 * @param pcszDevice The PDM device name.
4044 * @param uInstance The PDM device instance.
4045 *
4046 * @thread EMT
4047 */
4048DECLCALLBACK(int) Console::attachStorageDevice(Console *pConsole,
4049 PUVM pUVM,
4050 const char *pcszDevice,
4051 unsigned uInstance,
4052 StorageBus_T enmBus,
4053 bool fUseHostIOCache,
4054 IMediumAttachment *aMediumAtt)
4055{
4056 LogFlowFunc(("pConsole=%p uInstance=%u pszDevice=%p:{%s} enmBus=%u, aMediumAtt=%p\n",
4057 pConsole, uInstance, pcszDevice, pcszDevice, enmBus, aMediumAtt));
4058
4059 AssertReturn(pConsole, VERR_INVALID_PARAMETER);
4060
4061 AutoCaller autoCaller(pConsole);
4062 AssertComRCReturn(autoCaller.rc(), VERR_ACCESS_DENIED);
4063
4064 /*
4065 * Suspend the VM first.
4066 *
4067 * The VM must not be running since it might have pending I/O to
4068 * the drive which is being changed.
4069 */
4070 bool fResume;
4071 VMSTATE enmVMState = VMR3GetStateU(pUVM);
4072 switch (enmVMState)
4073 {
4074 case VMSTATE_RESETTING:
4075 case VMSTATE_RUNNING:
4076 {
4077 LogFlowFunc(("Suspending the VM...\n"));
4078 /* disable the callback to prevent Console-level state change */
4079 pConsole->mVMStateChangeCallbackDisabled = true;
4080 int rc = VMR3Suspend(pUVM);
4081 pConsole->mVMStateChangeCallbackDisabled = false;
4082 AssertRCReturn(rc, rc);
4083 fResume = true;
4084 break;
4085 }
4086
4087 case VMSTATE_SUSPENDED:
4088 case VMSTATE_CREATED:
4089 case VMSTATE_OFF:
4090 fResume = false;
4091 break;
4092
4093 case VMSTATE_RUNNING_LS:
4094 case VMSTATE_RUNNING_FT:
4095 return setErrorInternal(VBOX_E_INVALID_VM_STATE,
4096 COM_IIDOF(IConsole),
4097 getStaticComponentName(),
4098 (enmVMState == VMSTATE_RUNNING_LS) ? Utf8Str(tr("Cannot change drive during live migration")) : Utf8Str(tr("Cannot change drive during fault tolerant syncing")),
4099 false /*aWarning*/,
4100 true /*aLogIt*/);
4101
4102 default:
4103 AssertMsgFailedReturn(("enmVMState=%d\n", enmVMState), VERR_ACCESS_DENIED);
4104 }
4105
4106 /* Determine the base path for the device instance. */
4107 PCFGMNODE pCtlInst;
4108 pCtlInst = CFGMR3GetChildF(CFGMR3GetRootU(pUVM), "Devices/%s/%u/", pcszDevice, uInstance);
4109 AssertReturn(pCtlInst, VERR_INTERNAL_ERROR);
4110
4111 int rc = VINF_SUCCESS;
4112 int rcRet = VINF_SUCCESS;
4113
4114 rcRet = pConsole->configMediumAttachment(pCtlInst,
4115 pcszDevice,
4116 uInstance,
4117 enmBus,
4118 fUseHostIOCache,
4119 false /* fSetupMerge */,
4120 false /* fBuiltinIOCache */,
4121 0 /* uMergeSource */,
4122 0 /* uMergeTarget */,
4123 aMediumAtt,
4124 pConsole->mMachineState,
4125 NULL /* phrc */,
4126 true /* fAttachDetach */,
4127 false /* fForceUnmount */,
4128 true /* fHotplug */,
4129 pUVM,
4130 NULL /* paLedDevType */);
4131 /** @todo this dumps everything attached to this device instance, which
4132 * is more than necessary. Dumping the changed LUN would be enough. */
4133 CFGMR3Dump(pCtlInst);
4134
4135 /*
4136 * Resume the VM if necessary.
4137 */
4138 if (fResume)
4139 {
4140 LogFlowFunc(("Resuming the VM...\n"));
4141 /* disable the callback to prevent Console-level state change */
4142 pConsole->mVMStateChangeCallbackDisabled = true;
4143 rc = VMR3Resume(pUVM);
4144 pConsole->mVMStateChangeCallbackDisabled = false;
4145 AssertRC(rc);
4146 if (RT_FAILURE(rc))
4147 {
4148 /* too bad, we failed. try to sync the console state with the VMM state */
4149 vmstateChangeCallback(VMR3GetVM(pUVM), VMSTATE_SUSPENDED, enmVMState, pConsole);
4150 }
4151 /** @todo: if we failed with drive mount, then the VMR3Resume
4152 * error (if any) will be hidden from the caller. For proper reporting
4153 * of such multiple errors to the caller we need to enhance the
4154 * IVirtualBoxError interface. For now, give the first error the higher
4155 * priority.
4156 */
4157 if (RT_SUCCESS(rcRet))
4158 rcRet = rc;
4159 }
4160
4161 LogFlowFunc(("Returning %Rrc\n", rcRet));
4162 return rcRet;
4163}
4164
4165/**
4166 * Attach a new storage device to the VM.
4167 *
4168 * @param aMediumAttachment The medium attachment which is added.
4169 * @param pUVM Safe VM handle.
4170 *
4171 * @note Locks this object for writing.
4172 */
4173HRESULT Console::doStorageDeviceDetach(IMediumAttachment *aMediumAttachment, PUVM pUVM)
4174{
4175 AutoCaller autoCaller(this);
4176 AssertComRCReturnRC(autoCaller.rc());
4177
4178 /* We will need to release the write lock before calling EMT */
4179 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4180
4181 HRESULT rc = S_OK;
4182 const char *pszDevice = NULL;
4183
4184 SafeIfaceArray<IStorageController> ctrls;
4185 rc = mMachine->COMGETTER(StorageControllers)(ComSafeArrayAsOutParam(ctrls));
4186 AssertComRC(rc);
4187 IMedium *pMedium;
4188 rc = aMediumAttachment->COMGETTER(Medium)(&pMedium);
4189 AssertComRC(rc);
4190 Bstr mediumLocation;
4191 if (pMedium)
4192 {
4193 rc = pMedium->COMGETTER(Location)(mediumLocation.asOutParam());
4194 AssertComRC(rc);
4195 }
4196
4197 Bstr attCtrlName;
4198 rc = aMediumAttachment->COMGETTER(Controller)(attCtrlName.asOutParam());
4199 AssertComRC(rc);
4200 ComPtr<IStorageController> pStorageController;
4201 for (size_t i = 0; i < ctrls.size(); ++i)
4202 {
4203 Bstr ctrlName;
4204 rc = ctrls[i]->COMGETTER(Name)(ctrlName.asOutParam());
4205 AssertComRC(rc);
4206 if (attCtrlName == ctrlName)
4207 {
4208 pStorageController = ctrls[i];
4209 break;
4210 }
4211 }
4212 if (pStorageController.isNull())
4213 return setError(E_FAIL,
4214 tr("Could not find storage controller '%ls'"), attCtrlName.raw());
4215
4216 StorageControllerType_T enmCtrlType;
4217 rc = pStorageController->COMGETTER(ControllerType)(&enmCtrlType);
4218 AssertComRC(rc);
4219 pszDevice = convertControllerTypeToDev(enmCtrlType);
4220
4221 StorageBus_T enmBus;
4222 rc = pStorageController->COMGETTER(Bus)(&enmBus);
4223 AssertComRC(rc);
4224 ULONG uInstance;
4225 rc = pStorageController->COMGETTER(Instance)(&uInstance);
4226 AssertComRC(rc);
4227
4228 /*
4229 * Call worker in EMT, that's faster and safer than doing everything
4230 * using VMR3ReqCall. Note that we separate VMR3ReqCall from VMR3ReqWait
4231 * here to make requests from under the lock in order to serialize them.
4232 */
4233 PVMREQ pReq;
4234 int vrc = VMR3ReqCallU(pUVM,
4235 VMCPUID_ANY,
4236 &pReq,
4237 0 /* no wait! */,
4238 VMREQFLAGS_VBOX_STATUS,
4239 (PFNRT)Console::detachStorageDevice,
4240 6,
4241 this,
4242 pUVM,
4243 pszDevice,
4244 uInstance,
4245 enmBus,
4246 aMediumAttachment);
4247
4248 /* release the lock before waiting for a result (EMT will call us back!) */
4249 alock.release();
4250
4251 if (vrc == VERR_TIMEOUT || RT_SUCCESS(vrc))
4252 {
4253 vrc = VMR3ReqWait(pReq, RT_INDEFINITE_WAIT);
4254 AssertRC(vrc);
4255 if (RT_SUCCESS(vrc))
4256 vrc = pReq->iStatus;
4257 }
4258 VMR3ReqFree(pReq);
4259
4260 if (RT_SUCCESS(vrc))
4261 {
4262 LogFlowThisFunc(("Returns S_OK\n"));
4263 return S_OK;
4264 }
4265
4266 if (!pMedium)
4267 return setError(E_FAIL,
4268 tr("Could not mount the media/drive '%ls' (%Rrc)"),
4269 mediumLocation.raw(), vrc);
4270
4271 return setError(E_FAIL,
4272 tr("Could not unmount the currently mounted media/drive (%Rrc)"),
4273 vrc);
4274}
4275
4276/**
4277 * Performs the storage detach operation in EMT.
4278 *
4279 * @returns VBox status code.
4280 *
4281 * @param pThis Pointer to the Console object.
4282 * @param pUVM The VM handle.
4283 * @param pcszDevice The PDM device name.
4284 * @param uInstance The PDM device instance.
4285 *
4286 * @thread EMT
4287 */
4288DECLCALLBACK(int) Console::detachStorageDevice(Console *pConsole,
4289 PUVM pUVM,
4290 const char *pcszDevice,
4291 unsigned uInstance,
4292 StorageBus_T enmBus,
4293 IMediumAttachment *pMediumAtt)
4294{
4295 LogFlowFunc(("pConsole=%p uInstance=%u pszDevice=%p:{%s} enmBus=%u, pMediumAtt=%p\n",
4296 pConsole, uInstance, pcszDevice, pcszDevice, enmBus, pMediumAtt));
4297
4298 AssertReturn(pConsole, VERR_INVALID_PARAMETER);
4299
4300 AutoCaller autoCaller(pConsole);
4301 AssertComRCReturn(autoCaller.rc(), VERR_ACCESS_DENIED);
4302
4303 /*
4304 * Suspend the VM first.
4305 *
4306 * The VM must not be running since it might have pending I/O to
4307 * the drive which is being changed.
4308 */
4309 bool fResume;
4310 VMSTATE enmVMState = VMR3GetStateU(pUVM);
4311 switch (enmVMState)
4312 {
4313 case VMSTATE_RESETTING:
4314 case VMSTATE_RUNNING:
4315 {
4316 LogFlowFunc(("Suspending the VM...\n"));
4317 /* disable the callback to prevent Console-level state change */
4318 pConsole->mVMStateChangeCallbackDisabled = true;
4319 int rc = VMR3Suspend(pUVM);
4320 pConsole->mVMStateChangeCallbackDisabled = false;
4321 AssertRCReturn(rc, rc);
4322 fResume = true;
4323 break;
4324 }
4325
4326 case VMSTATE_SUSPENDED:
4327 case VMSTATE_CREATED:
4328 case VMSTATE_OFF:
4329 fResume = false;
4330 break;
4331
4332 case VMSTATE_RUNNING_LS:
4333 case VMSTATE_RUNNING_FT:
4334 return setErrorInternal(VBOX_E_INVALID_VM_STATE,
4335 COM_IIDOF(IConsole),
4336 getStaticComponentName(),
4337 (enmVMState == VMSTATE_RUNNING_LS) ? Utf8Str(tr("Cannot change drive during live migration")) : Utf8Str(tr("Cannot change drive during fault tolerant syncing")),
4338 false /*aWarning*/,
4339 true /*aLogIt*/);
4340
4341 default:
4342 AssertMsgFailedReturn(("enmVMState=%d\n", enmVMState), VERR_ACCESS_DENIED);
4343 }
4344
4345 /* Determine the base path for the device instance. */
4346 PCFGMNODE pCtlInst;
4347 pCtlInst = CFGMR3GetChildF(CFGMR3GetRootU(pUVM), "Devices/%s/%u/", pcszDevice, uInstance);
4348 AssertReturn(pCtlInst, VERR_INTERNAL_ERROR);
4349
4350#define H() AssertMsgReturn(!FAILED(hrc), ("hrc=%Rhrc\n", hrc), VERR_GENERAL_FAILURE)
4351
4352 HRESULT hrc;
4353 int rc = VINF_SUCCESS;
4354 int rcRet = VINF_SUCCESS;
4355 unsigned uLUN;
4356 LONG lDev;
4357 LONG lPort;
4358 DeviceType_T lType;
4359 PCFGMNODE pLunL0 = NULL;
4360 PCFGMNODE pCfg = NULL;
4361
4362 hrc = pMediumAtt->COMGETTER(Device)(&lDev); H();
4363 hrc = pMediumAtt->COMGETTER(Port)(&lPort); H();
4364 hrc = pMediumAtt->COMGETTER(Type)(&lType); H();
4365 hrc = Console::convertBusPortDeviceToLun(enmBus, lPort, lDev, uLUN); H();
4366
4367#undef H
4368
4369 /* First check if the LUN really exists. */
4370 pLunL0 = CFGMR3GetChildF(pCtlInst, "LUN#%u", uLUN);
4371 if (pLunL0)
4372 {
4373 rc = PDMR3DeviceDetach(VMR3GetVM(pUVM), pcszDevice, uInstance, uLUN, 0);
4374 if (rc == VERR_PDM_NO_DRIVER_ATTACHED_TO_LUN)
4375 rc = VINF_SUCCESS;
4376 AssertRCReturn(rc, rc);
4377 CFGMR3RemoveNode(pLunL0);
4378
4379 Utf8Str devicePath = Utf8StrFmt("%s/%u/LUN#%u", pcszDevice, uInstance, uLUN);
4380 pConsole->mapMediumAttachments.erase(devicePath);
4381
4382 }
4383 else
4384 AssertFailedReturn(VERR_INTERNAL_ERROR);
4385
4386 CFGMR3Dump(pCtlInst);
4387
4388 /*
4389 * Resume the VM if necessary.
4390 */
4391 if (fResume)
4392 {
4393 LogFlowFunc(("Resuming the VM...\n"));
4394 /* disable the callback to prevent Console-level state change */
4395 pConsole->mVMStateChangeCallbackDisabled = true;
4396 rc = VMR3Resume(pUVM);
4397 pConsole->mVMStateChangeCallbackDisabled = false;
4398 AssertRC(rc);
4399 if (RT_FAILURE(rc))
4400 {
4401 /* too bad, we failed. try to sync the console state with the VMM state */
4402 vmstateChangeCallback(VMR3GetVM(pUVM), VMSTATE_SUSPENDED, enmVMState, pConsole);
4403 }
4404 /** @todo: if we failed with drive mount, then the VMR3Resume
4405 * error (if any) will be hidden from the caller. For proper reporting
4406 * of such multiple errors to the caller we need to enhance the
4407 * IVirtualBoxError interface. For now, give the first error the higher
4408 * priority.
4409 */
4410 if (RT_SUCCESS(rcRet))
4411 rcRet = rc;
4412 }
4413
4414 LogFlowFunc(("Returning %Rrc\n", rcRet));
4415 return rcRet;
4416}
4417
4418/**
4419 * Called by IInternalSessionControl::OnNetworkAdapterChange().
4420 *
4421 * @note Locks this object for writing.
4422 */
4423HRESULT Console::onNetworkAdapterChange(INetworkAdapter *aNetworkAdapter, BOOL changeAdapter)
4424{
4425 LogFlowThisFunc(("\n"));
4426
4427 AutoCaller autoCaller(this);
4428 AssertComRCReturnRC(autoCaller.rc());
4429
4430 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4431
4432 HRESULT rc = S_OK;
4433
4434 /* don't trigger network change if the VM isn't running */
4435 SafeVMPtrQuiet ptrVM(this);
4436 if (ptrVM.isOk())
4437 {
4438 /* Get the properties we need from the adapter */
4439 BOOL fCableConnected, fTraceEnabled;
4440 rc = aNetworkAdapter->COMGETTER(CableConnected)(&fCableConnected);
4441 AssertComRC(rc);
4442 if (SUCCEEDED(rc))
4443 {
4444 rc = aNetworkAdapter->COMGETTER(TraceEnabled)(&fTraceEnabled);
4445 AssertComRC(rc);
4446 }
4447 if (SUCCEEDED(rc))
4448 {
4449 ULONG ulInstance;
4450 rc = aNetworkAdapter->COMGETTER(Slot)(&ulInstance);
4451 AssertComRC(rc);
4452 if (SUCCEEDED(rc))
4453 {
4454 /*
4455 * Find the adapter instance, get the config interface and update
4456 * the link state.
4457 */
4458 NetworkAdapterType_T adapterType;
4459 rc = aNetworkAdapter->COMGETTER(AdapterType)(&adapterType);
4460 AssertComRC(rc);
4461 const char *pszAdapterName = networkAdapterTypeToName(adapterType);
4462
4463 // prevent cross-thread deadlocks, don't need the lock any more
4464 alock.release();
4465
4466 PPDMIBASE pBase;
4467 int vrc = PDMR3QueryDeviceLun(ptrVM, pszAdapterName, ulInstance, 0, &pBase);
4468 if (RT_SUCCESS(vrc))
4469 {
4470 Assert(pBase);
4471 PPDMINETWORKCONFIG pINetCfg;
4472 pINetCfg = PDMIBASE_QUERY_INTERFACE(pBase, PDMINETWORKCONFIG);
4473 if (pINetCfg)
4474 {
4475 Log(("Console::onNetworkAdapterChange: setting link state to %d\n",
4476 fCableConnected));
4477 vrc = pINetCfg->pfnSetLinkState(pINetCfg,
4478 fCableConnected ? PDMNETWORKLINKSTATE_UP
4479 : PDMNETWORKLINKSTATE_DOWN);
4480 ComAssertRC(vrc);
4481 }
4482 if (RT_SUCCESS(vrc) && changeAdapter)
4483 {
4484 VMSTATE enmVMState = VMR3GetStateU(ptrVM.rawUVM());
4485 if ( enmVMState == VMSTATE_RUNNING /** @todo LiveMigration: Forbid or deal correctly with the _LS variants */
4486 || enmVMState == VMSTATE_SUSPENDED)
4487 {
4488 if (fTraceEnabled && fCableConnected && pINetCfg)
4489 {
4490 vrc = pINetCfg->pfnSetLinkState(pINetCfg, PDMNETWORKLINKSTATE_DOWN);
4491 ComAssertRC(vrc);
4492 }
4493
4494 rc = doNetworkAdapterChange(ptrVM.rawUVM(), pszAdapterName, ulInstance, 0, aNetworkAdapter);
4495
4496 if (fTraceEnabled && fCableConnected && pINetCfg)
4497 {
4498 vrc = pINetCfg->pfnSetLinkState(pINetCfg, PDMNETWORKLINKSTATE_UP);
4499 ComAssertRC(vrc);
4500 }
4501 }
4502 }
4503 }
4504 else if (vrc == VERR_PDM_DEVICE_INSTANCE_NOT_FOUND)
4505 return setError(E_FAIL,
4506 tr("The network adapter #%u is not enabled"), ulInstance);
4507 else
4508 ComAssertRC(vrc);
4509
4510 if (RT_FAILURE(vrc))
4511 rc = E_FAIL;
4512
4513 alock.acquire();
4514 }
4515 }
4516 ptrVM.release();
4517 }
4518
4519 // definitely don't need the lock any more
4520 alock.release();
4521
4522 /* notify console callbacks on success */
4523 if (SUCCEEDED(rc))
4524 fireNetworkAdapterChangedEvent(mEventSource, aNetworkAdapter);
4525
4526 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
4527 return rc;
4528}
4529
4530/**
4531 * Called by IInternalSessionControl::OnNATEngineChange().
4532 *
4533 * @note Locks this object for writing.
4534 */
4535HRESULT Console::onNATRedirectRuleChange(ULONG ulInstance, BOOL aNatRuleRemove,
4536 NATProtocol_T aProto, IN_BSTR aHostIP, LONG aHostPort, IN_BSTR aGuestIP, LONG aGuestPort)
4537{
4538 LogFlowThisFunc(("\n"));
4539
4540 AutoCaller autoCaller(this);
4541 AssertComRCReturnRC(autoCaller.rc());
4542
4543 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4544
4545 HRESULT rc = S_OK;
4546
4547 /* don't trigger nat engine change if the VM isn't running */
4548 SafeVMPtrQuiet ptrVM(this);
4549 if (ptrVM.isOk())
4550 {
4551 do
4552 {
4553 ComPtr<INetworkAdapter> pNetworkAdapter;
4554 rc = machine()->GetNetworkAdapter(ulInstance, pNetworkAdapter.asOutParam());
4555 if ( FAILED(rc)
4556 || pNetworkAdapter.isNull())
4557 break;
4558
4559 /*
4560 * Find the adapter instance, get the config interface and update
4561 * the link state.
4562 */
4563 NetworkAdapterType_T adapterType;
4564 rc = pNetworkAdapter->COMGETTER(AdapterType)(&adapterType);
4565 if (FAILED(rc))
4566 {
4567 AssertComRC(rc);
4568 rc = E_FAIL;
4569 break;
4570 }
4571
4572 const char *pszAdapterName = networkAdapterTypeToName(adapterType);
4573 PPDMIBASE pBase;
4574 int vrc = PDMR3QueryLun(ptrVM, pszAdapterName, ulInstance, 0, &pBase);
4575 if (RT_FAILURE(vrc))
4576 {
4577 ComAssertRC(vrc);
4578 rc = E_FAIL;
4579 break;
4580 }
4581
4582 NetworkAttachmentType_T attachmentType;
4583 rc = pNetworkAdapter->COMGETTER(AttachmentType)(&attachmentType);
4584 if ( FAILED(rc)
4585 || attachmentType != NetworkAttachmentType_NAT)
4586 {
4587 rc = E_FAIL;
4588 break;
4589 }
4590
4591 /* look down for PDMINETWORKNATCONFIG interface */
4592 PPDMINETWORKNATCONFIG pNetNatCfg = NULL;
4593 while (pBase)
4594 {
4595 pNetNatCfg = (PPDMINETWORKNATCONFIG)pBase->pfnQueryInterface(pBase, PDMINETWORKNATCONFIG_IID);
4596 if (pNetNatCfg)
4597 break;
4598 /** @todo r=bird: This stinks! */
4599 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pBase);
4600 pBase = pDrvIns->pDownBase;
4601 }
4602 if (!pNetNatCfg)
4603 break;
4604
4605 bool fUdp = aProto == NATProtocol_UDP;
4606 vrc = pNetNatCfg->pfnRedirectRuleCommand(pNetNatCfg, !!aNatRuleRemove, fUdp,
4607 Utf8Str(aHostIP).c_str(), aHostPort, Utf8Str(aGuestIP).c_str(),
4608 aGuestPort);
4609 if (RT_FAILURE(vrc))
4610 rc = E_FAIL;
4611 } while (0); /* break loop */
4612 ptrVM.release();
4613 }
4614
4615 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
4616 return rc;
4617}
4618
4619
4620/**
4621 * Process a network adaptor change.
4622 *
4623 * @returns COM status code.
4624 *
4625 * @parma pUVM The VM handle (caller hold this safely).
4626 * @param pszDevice The PDM device name.
4627 * @param uInstance The PDM device instance.
4628 * @param uLun The PDM LUN number of the drive.
4629 * @param aNetworkAdapter The network adapter whose attachment needs to be changed
4630 */
4631HRESULT Console::doNetworkAdapterChange(PUVM pUVM,
4632 const char *pszDevice,
4633 unsigned uInstance,
4634 unsigned uLun,
4635 INetworkAdapter *aNetworkAdapter)
4636{
4637 LogFlowThisFunc(("pszDevice=%p:{%s} uInstance=%u uLun=%u aNetworkAdapter=%p\n",
4638 pszDevice, pszDevice, uInstance, uLun, aNetworkAdapter));
4639
4640 AutoCaller autoCaller(this);
4641 AssertComRCReturnRC(autoCaller.rc());
4642
4643 /*
4644 * Call worker in EMT, that's faster and safer than doing everything
4645 * using VM3ReqCall. Note that we separate VMR3ReqCall from VMR3ReqWait
4646 * here to make requests from under the lock in order to serialize them.
4647 */
4648 PVMREQ pReq;
4649 int vrc = VMR3ReqCallU(pUVM, 0 /*idDstCpu*/, &pReq, 0 /* no wait! */, VMREQFLAGS_VBOX_STATUS,
4650 (PFNRT)Console::changeNetworkAttachment, 6,
4651 this, pUVM, pszDevice, uInstance, uLun, aNetworkAdapter);
4652
4653 if (vrc == VERR_TIMEOUT || RT_SUCCESS(vrc))
4654 {
4655 vrc = VMR3ReqWait(pReq, RT_INDEFINITE_WAIT);
4656 AssertRC(vrc);
4657 if (RT_SUCCESS(vrc))
4658 vrc = pReq->iStatus;
4659 }
4660 VMR3ReqFree(pReq);
4661
4662 if (RT_SUCCESS(vrc))
4663 {
4664 LogFlowThisFunc(("Returns S_OK\n"));
4665 return S_OK;
4666 }
4667
4668 return setError(E_FAIL,
4669 tr("Could not change the network adaptor attachement type (%Rrc)"),
4670 vrc);
4671}
4672
4673
4674/**
4675 * Performs the Network Adaptor change in EMT.
4676 *
4677 * @returns VBox status code.
4678 *
4679 * @param pThis Pointer to the Console object.
4680 * @param pUVM The VM handle.
4681 * @param pszDevice The PDM device name.
4682 * @param uInstance The PDM device instance.
4683 * @param uLun The PDM LUN number of the drive.
4684 * @param aNetworkAdapter The network adapter whose attachment needs to be changed
4685 *
4686 * @thread EMT
4687 * @note Locks the Console object for writing.
4688 */
4689DECLCALLBACK(int) Console::changeNetworkAttachment(Console *pThis,
4690 PUVM pUVM,
4691 const char *pszDevice,
4692 unsigned uInstance,
4693 unsigned uLun,
4694 INetworkAdapter *aNetworkAdapter)
4695{
4696 LogFlowFunc(("pThis=%p pszDevice=%p:{%s} uInstance=%u uLun=%u aNetworkAdapter=%p\n",
4697 pThis, pszDevice, pszDevice, uInstance, uLun, aNetworkAdapter));
4698
4699 AssertReturn(pThis, VERR_INVALID_PARAMETER);
4700
4701 AutoCaller autoCaller(pThis);
4702 AssertComRCReturn(autoCaller.rc(), VERR_ACCESS_DENIED);
4703
4704 ComPtr<IVirtualBox> pVirtualBox;
4705 pThis->mMachine->COMGETTER(Parent)(pVirtualBox.asOutParam());
4706 ComPtr<ISystemProperties> pSystemProperties;
4707 if (pVirtualBox)
4708 pVirtualBox->COMGETTER(SystemProperties)(pSystemProperties.asOutParam());
4709 ChipsetType_T chipsetType = ChipsetType_PIIX3;
4710 pThis->mMachine->COMGETTER(ChipsetType)(&chipsetType);
4711 ULONG maxNetworkAdapters = 0;
4712 if (pSystemProperties)
4713 pSystemProperties->GetMaxNetworkAdapters(chipsetType, &maxNetworkAdapters);
4714 AssertMsg( ( !strcmp(pszDevice, "pcnet")
4715 || !strcmp(pszDevice, "e1000")
4716 || !strcmp(pszDevice, "virtio-net"))
4717 && uLun == 0
4718 && uInstance < maxNetworkAdapters,
4719 ("pszDevice=%s uLun=%d uInstance=%d\n", pszDevice, uLun, uInstance));
4720 Log(("pszDevice=%s uLun=%d uInstance=%d\n", pszDevice, uLun, uInstance));
4721
4722 /*
4723 * Suspend the VM first.
4724 *
4725 * The VM must not be running since it might have pending I/O to
4726 * the drive which is being changed.
4727 */
4728 bool fResume;
4729 VMSTATE enmVMState = VMR3GetStateU(pUVM);
4730 switch (enmVMState)
4731 {
4732 case VMSTATE_RESETTING:
4733 case VMSTATE_RUNNING:
4734 {
4735 LogFlowFunc(("Suspending the VM...\n"));
4736 /* disable the callback to prevent Console-level state change */
4737 pThis->mVMStateChangeCallbackDisabled = true;
4738 int rc = VMR3Suspend(pUVM);
4739 pThis->mVMStateChangeCallbackDisabled = false;
4740 AssertRCReturn(rc, rc);
4741 fResume = true;
4742 break;
4743 }
4744
4745 case VMSTATE_SUSPENDED:
4746 case VMSTATE_CREATED:
4747 case VMSTATE_OFF:
4748 fResume = false;
4749 break;
4750
4751 default:
4752 AssertLogRelMsgFailedReturn(("enmVMState=%d\n", enmVMState), VERR_ACCESS_DENIED);
4753 }
4754
4755 int rc = VINF_SUCCESS;
4756 int rcRet = VINF_SUCCESS;
4757
4758 PCFGMNODE pCfg = NULL; /* /Devices/Dev/.../Config/ */
4759 PCFGMNODE pLunL0 = NULL; /* /Devices/Dev/0/LUN#0/ */
4760 PCFGMNODE pInst = CFGMR3GetChildF(CFGMR3GetRootU(pUVM), "Devices/%s/%d/", pszDevice, uInstance);
4761 AssertRelease(pInst);
4762
4763 rcRet = pThis->configNetwork(pszDevice, uInstance, uLun, aNetworkAdapter, pCfg, pLunL0, pInst,
4764 true /*fAttachDetach*/, false /*fIgnoreConnectFailure*/);
4765
4766 /*
4767 * Resume the VM if necessary.
4768 */
4769 if (fResume)
4770 {
4771 LogFlowFunc(("Resuming the VM...\n"));
4772 /* disable the callback to prevent Console-level state change */
4773 pThis->mVMStateChangeCallbackDisabled = true;
4774 rc = VMR3Resume(pUVM);
4775 pThis->mVMStateChangeCallbackDisabled = false;
4776 AssertRC(rc);
4777 if (RT_FAILURE(rc))
4778 {
4779 /* too bad, we failed. try to sync the console state with the VMM state */
4780 vmstateChangeCallback(VMR3GetVM(pUVM), VMSTATE_SUSPENDED, enmVMState, pThis);
4781 }
4782 /// @todo (r=dmik) if we failed with drive mount, then the VMR3Resume
4783 // error (if any) will be hidden from the caller. For proper reporting
4784 // of such multiple errors to the caller we need to enhance the
4785 // IVirtualBoxError interface. For now, give the first error the higher
4786 // priority.
4787 if (RT_SUCCESS(rcRet))
4788 rcRet = rc;
4789 }
4790
4791 LogFlowFunc(("Returning %Rrc\n", rcRet));
4792 return rcRet;
4793}
4794
4795
4796/**
4797 * Called by IInternalSessionControl::OnSerialPortChange().
4798 */
4799HRESULT Console::onSerialPortChange(ISerialPort *aSerialPort)
4800{
4801 LogFlowThisFunc(("\n"));
4802
4803 AutoCaller autoCaller(this);
4804 AssertComRCReturnRC(autoCaller.rc());
4805
4806 fireSerialPortChangedEvent(mEventSource, aSerialPort);
4807
4808 LogFlowThisFunc(("Leaving rc=%#x\n", S_OK));
4809 return S_OK;
4810}
4811
4812/**
4813 * Called by IInternalSessionControl::OnParallelPortChange().
4814 */
4815HRESULT Console::onParallelPortChange(IParallelPort *aParallelPort)
4816{
4817 LogFlowThisFunc(("\n"));
4818
4819 AutoCaller autoCaller(this);
4820 AssertComRCReturnRC(autoCaller.rc());
4821
4822 fireParallelPortChangedEvent(mEventSource, aParallelPort);
4823
4824 LogFlowThisFunc(("Leaving rc=%#x\n", S_OK));
4825 return S_OK;
4826}
4827
4828/**
4829 * Called by IInternalSessionControl::OnStorageControllerChange().
4830 */
4831HRESULT Console::onStorageControllerChange()
4832{
4833 LogFlowThisFunc(("\n"));
4834
4835 AutoCaller autoCaller(this);
4836 AssertComRCReturnRC(autoCaller.rc());
4837
4838 fireStorageControllerChangedEvent(mEventSource);
4839
4840 LogFlowThisFunc(("Leaving rc=%#x\n", S_OK));
4841 return S_OK;
4842}
4843
4844/**
4845 * Called by IInternalSessionControl::OnMediumChange().
4846 */
4847HRESULT Console::onMediumChange(IMediumAttachment *aMediumAttachment, BOOL aForce)
4848{
4849 LogFlowThisFunc(("\n"));
4850
4851 AutoCaller autoCaller(this);
4852 AssertComRCReturnRC(autoCaller.rc());
4853
4854 HRESULT rc = S_OK;
4855
4856 /* don't trigger medium change if the VM isn't running */
4857 SafeVMPtrQuiet ptrVM(this);
4858 if (ptrVM.isOk())
4859 {
4860 rc = doMediumChange(aMediumAttachment, !!aForce, ptrVM.rawUVM());
4861 ptrVM.release();
4862 }
4863
4864 /* notify console callbacks on success */
4865 if (SUCCEEDED(rc))
4866 fireMediumChangedEvent(mEventSource, aMediumAttachment);
4867
4868 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
4869 return rc;
4870}
4871
4872/**
4873 * Called by IInternalSessionControl::OnCPUChange().
4874 *
4875 * @note Locks this object for writing.
4876 */
4877HRESULT Console::onCPUChange(ULONG aCPU, BOOL aRemove)
4878{
4879 LogFlowThisFunc(("\n"));
4880
4881 AutoCaller autoCaller(this);
4882 AssertComRCReturnRC(autoCaller.rc());
4883
4884 HRESULT rc = S_OK;
4885
4886 /* don't trigger CPU change if the VM isn't running */
4887 SafeVMPtrQuiet ptrVM(this);
4888 if (ptrVM.isOk())
4889 {
4890 if (aRemove)
4891 rc = doCPURemove(aCPU, ptrVM);
4892 else
4893 rc = doCPUAdd(aCPU, ptrVM);
4894 ptrVM.release();
4895 }
4896
4897 /* notify console callbacks on success */
4898 if (SUCCEEDED(rc))
4899 fireCPUChangedEvent(mEventSource, aCPU, aRemove);
4900
4901 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
4902 return rc;
4903}
4904
4905/**
4906 * Called by IInternalSessionControl::OnCpuExecutionCapChange().
4907 *
4908 * @note Locks this object for writing.
4909 */
4910HRESULT Console::onCPUExecutionCapChange(ULONG aExecutionCap)
4911{
4912 LogFlowThisFunc(("\n"));
4913
4914 AutoCaller autoCaller(this);
4915 AssertComRCReturnRC(autoCaller.rc());
4916
4917 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4918
4919 HRESULT rc = S_OK;
4920
4921 /* don't trigger the CPU priority change if the VM isn't running */
4922 SafeVMPtrQuiet ptrVM(this);
4923 if (ptrVM.isOk())
4924 {
4925 if ( mMachineState == MachineState_Running
4926 || mMachineState == MachineState_Teleporting
4927 || mMachineState == MachineState_LiveSnapshotting
4928 )
4929 {
4930 /* No need to call in the EMT thread. */
4931 rc = VMR3SetCpuExecutionCap(ptrVM.rawUVM(), aExecutionCap);
4932 }
4933 else
4934 rc = setInvalidMachineStateError();
4935 ptrVM.release();
4936 }
4937
4938 /* notify console callbacks on success */
4939 if (SUCCEEDED(rc))
4940 {
4941 alock.release();
4942 fireCPUExecutionCapChangedEvent(mEventSource, aExecutionCap);
4943 }
4944
4945 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
4946 return rc;
4947}
4948
4949/**
4950 * Called by IInternalSessionControl::OnClipboardModeChange().
4951 *
4952 * @note Locks this object for writing.
4953 */
4954HRESULT Console::onClipboardModeChange(ClipboardMode_T aClipboardMode)
4955{
4956 LogFlowThisFunc(("\n"));
4957
4958 AutoCaller autoCaller(this);
4959 AssertComRCReturnRC(autoCaller.rc());
4960
4961 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4962
4963 HRESULT rc = S_OK;
4964
4965 /* don't trigger the Clipboard mode change if the VM isn't running */
4966 SafeVMPtrQuiet ptrVM(this);
4967 if (ptrVM.isOk())
4968 {
4969 if ( mMachineState == MachineState_Running
4970 || mMachineState == MachineState_Teleporting
4971 || mMachineState == MachineState_LiveSnapshotting)
4972 changeClipboardMode(aClipboardMode);
4973 else
4974 rc = setInvalidMachineStateError();
4975 ptrVM.release();
4976 }
4977
4978 /* notify console callbacks on success */
4979 if (SUCCEEDED(rc))
4980 {
4981 alock.release();
4982 fireClipboardModeChangedEvent(mEventSource, aClipboardMode);
4983 }
4984
4985 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
4986 return rc;
4987}
4988
4989/**
4990 * Called by IInternalSessionControl::OnDragAndDropModeChange().
4991 *
4992 * @note Locks this object for writing.
4993 */
4994HRESULT Console::onDragAndDropModeChange(DragAndDropMode_T aDragAndDropMode)
4995{
4996 LogFlowThisFunc(("\n"));
4997
4998 AutoCaller autoCaller(this);
4999 AssertComRCReturnRC(autoCaller.rc());
5000
5001 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5002
5003 HRESULT rc = S_OK;
5004
5005 /* don't trigger the Drag'n'drop mode change if the VM isn't running */
5006 SafeVMPtrQuiet ptrVM(this);
5007 if (ptrVM.isOk())
5008 {
5009 if ( mMachineState == MachineState_Running
5010 || mMachineState == MachineState_Teleporting
5011 || mMachineState == MachineState_LiveSnapshotting)
5012 changeDragAndDropMode(aDragAndDropMode);
5013 else
5014 rc = setInvalidMachineStateError();
5015 ptrVM.release();
5016 }
5017
5018 /* notify console callbacks on success */
5019 if (SUCCEEDED(rc))
5020 {
5021 alock.release();
5022 fireDragAndDropModeChangedEvent(mEventSource, aDragAndDropMode);
5023 }
5024
5025 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
5026 return rc;
5027}
5028
5029/**
5030 * Called by IInternalSessionControl::OnVRDEServerChange().
5031 *
5032 * @note Locks this object for writing.
5033 */
5034HRESULT Console::onVRDEServerChange(BOOL aRestart)
5035{
5036 AutoCaller autoCaller(this);
5037 AssertComRCReturnRC(autoCaller.rc());
5038
5039 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5040
5041 HRESULT rc = S_OK;
5042
5043 if ( mVRDEServer
5044 && ( mMachineState == MachineState_Running
5045 || mMachineState == MachineState_Teleporting
5046 || mMachineState == MachineState_LiveSnapshotting
5047 )
5048 )
5049 {
5050 BOOL vrdpEnabled = FALSE;
5051
5052 rc = mVRDEServer->COMGETTER(Enabled)(&vrdpEnabled);
5053 ComAssertComRCRetRC(rc);
5054
5055 if (aRestart)
5056 {
5057 /* VRDP server may call this Console object back from other threads (VRDP INPUT or OUTPUT). */
5058 alock.release();
5059
5060 if (vrdpEnabled)
5061 {
5062 // If there was no VRDP server started the 'stop' will do nothing.
5063 // However if a server was started and this notification was called,
5064 // we have to restart the server.
5065 mConsoleVRDPServer->Stop();
5066
5067 if (RT_FAILURE(mConsoleVRDPServer->Launch()))
5068 rc = E_FAIL;
5069 else
5070 mConsoleVRDPServer->EnableConnections();
5071 }
5072 else
5073 {
5074 mConsoleVRDPServer->Stop();
5075 }
5076
5077 alock.acquire();
5078 }
5079 }
5080
5081 /* notify console callbacks on success */
5082 if (SUCCEEDED(rc))
5083 {
5084 alock.release();
5085 fireVRDEServerChangedEvent(mEventSource);
5086 }
5087
5088 return rc;
5089}
5090
5091void Console::onVRDEServerInfoChange()
5092{
5093 AutoCaller autoCaller(this);
5094 AssertComRCReturnVoid(autoCaller.rc());
5095
5096 fireVRDEServerInfoChangedEvent(mEventSource);
5097}
5098
5099
5100/**
5101 * Called by IInternalSessionControl::OnUSBControllerChange().
5102 */
5103HRESULT Console::onUSBControllerChange()
5104{
5105 LogFlowThisFunc(("\n"));
5106
5107 AutoCaller autoCaller(this);
5108 AssertComRCReturnRC(autoCaller.rc());
5109
5110 fireUSBControllerChangedEvent(mEventSource);
5111
5112 return S_OK;
5113}
5114
5115/**
5116 * Called by IInternalSessionControl::OnSharedFolderChange().
5117 *
5118 * @note Locks this object for writing.
5119 */
5120HRESULT Console::onSharedFolderChange(BOOL aGlobal)
5121{
5122 LogFlowThisFunc(("aGlobal=%RTbool\n", aGlobal));
5123
5124 AutoCaller autoCaller(this);
5125 AssertComRCReturnRC(autoCaller.rc());
5126
5127 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5128
5129 HRESULT rc = fetchSharedFolders(aGlobal);
5130
5131 /* notify console callbacks on success */
5132 if (SUCCEEDED(rc))
5133 {
5134 alock.release();
5135 fireSharedFolderChangedEvent(mEventSource, aGlobal ? (Scope_T)Scope_Global : (Scope_T)Scope_Machine);
5136 }
5137
5138 return rc;
5139}
5140
5141/**
5142 * Called by IInternalSessionControl::OnUSBDeviceAttach() or locally by
5143 * processRemoteUSBDevices() after IInternalMachineControl::RunUSBDeviceFilters()
5144 * returns TRUE for a given remote USB device.
5145 *
5146 * @return S_OK if the device was attached to the VM.
5147 * @return failure if not attached.
5148 *
5149 * @param aDevice
5150 * The device in question.
5151 * @param aMaskedIfs
5152 * The interfaces to hide from the guest.
5153 *
5154 * @note Locks this object for writing.
5155 */
5156HRESULT Console::onUSBDeviceAttach(IUSBDevice *aDevice, IVirtualBoxErrorInfo *aError, ULONG aMaskedIfs)
5157{
5158#ifdef VBOX_WITH_USB
5159 LogFlowThisFunc(("aDevice=%p aError=%p\n", aDevice, aError));
5160
5161 AutoCaller autoCaller(this);
5162 ComAssertComRCRetRC(autoCaller.rc());
5163
5164 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5165
5166 /* Get the VM pointer (we don't need error info, since it's a callback). */
5167 SafeVMPtrQuiet ptrVM(this);
5168 if (!ptrVM.isOk())
5169 {
5170 /* The VM may be no more operational when this message arrives
5171 * (e.g. it may be Saving or Stopping or just PoweredOff) --
5172 * autoVMCaller.rc() will return a failure in this case. */
5173 LogFlowThisFunc(("Attach request ignored (mMachineState=%d).\n",
5174 mMachineState));
5175 return ptrVM.rc();
5176 }
5177
5178 if (aError != NULL)
5179 {
5180 /* notify callbacks about the error */
5181 alock.release();
5182 onUSBDeviceStateChange(aDevice, true /* aAttached */, aError);
5183 return S_OK;
5184 }
5185
5186 /* Don't proceed unless there's at least one USB hub. */
5187 if (!PDMR3USBHasHub(ptrVM))
5188 {
5189 LogFlowThisFunc(("Attach request ignored (no USB controller).\n"));
5190 return E_FAIL;
5191 }
5192
5193 alock.release();
5194 HRESULT rc = attachUSBDevice(aDevice, aMaskedIfs);
5195 if (FAILED(rc))
5196 {
5197 /* take the current error info */
5198 com::ErrorInfoKeeper eik;
5199 /* the error must be a VirtualBoxErrorInfo instance */
5200 ComPtr<IVirtualBoxErrorInfo> pError = eik.takeError();
5201 Assert(!pError.isNull());
5202 if (!pError.isNull())
5203 {
5204 /* notify callbacks about the error */
5205 onUSBDeviceStateChange(aDevice, true /* aAttached */, pError);
5206 }
5207 }
5208
5209 return rc;
5210
5211#else /* !VBOX_WITH_USB */
5212 return E_FAIL;
5213#endif /* !VBOX_WITH_USB */
5214}
5215
5216/**
5217 * Called by IInternalSessionControl::OnUSBDeviceDetach() and locally by
5218 * processRemoteUSBDevices().
5219 *
5220 * @note Locks this object for writing.
5221 */
5222HRESULT Console::onUSBDeviceDetach(IN_BSTR aId,
5223 IVirtualBoxErrorInfo *aError)
5224{
5225#ifdef VBOX_WITH_USB
5226 Guid Uuid(aId);
5227 LogFlowThisFunc(("aId={%RTuuid} aError=%p\n", Uuid.raw(), aError));
5228
5229 AutoCaller autoCaller(this);
5230 AssertComRCReturnRC(autoCaller.rc());
5231
5232 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5233
5234 /* Find the device. */
5235 ComObjPtr<OUSBDevice> pUSBDevice;
5236 USBDeviceList::iterator it = mUSBDevices.begin();
5237 while (it != mUSBDevices.end())
5238 {
5239 LogFlowThisFunc(("it={%RTuuid}\n", (*it)->id().raw()));
5240 if ((*it)->id() == Uuid)
5241 {
5242 pUSBDevice = *it;
5243 break;
5244 }
5245 ++it;
5246 }
5247
5248
5249 if (pUSBDevice.isNull())
5250 {
5251 LogFlowThisFunc(("USB device not found.\n"));
5252
5253 /* The VM may be no more operational when this message arrives
5254 * (e.g. it may be Saving or Stopping or just PoweredOff). Use
5255 * AutoVMCaller to detect it -- AutoVMCaller::rc() will return a
5256 * failure in this case. */
5257
5258 AutoVMCallerQuiet autoVMCaller(this);
5259 if (FAILED(autoVMCaller.rc()))
5260 {
5261 LogFlowThisFunc(("Detach request ignored (mMachineState=%d).\n",
5262 mMachineState));
5263 return autoVMCaller.rc();
5264 }
5265
5266 /* the device must be in the list otherwise */
5267 AssertFailedReturn(E_FAIL);
5268 }
5269
5270 if (aError != NULL)
5271 {
5272 /* notify callback about an error */
5273 alock.release();
5274 onUSBDeviceStateChange(pUSBDevice, false /* aAttached */, aError);
5275 return S_OK;
5276 }
5277
5278 /* Remove the device from the collection, it is re-added below for failures */
5279 mUSBDevices.erase(it);
5280
5281 alock.release();
5282 HRESULT rc = detachUSBDevice(pUSBDevice);
5283 if (FAILED(rc))
5284 {
5285 /* Re-add the device to the collection */
5286 alock.acquire();
5287 mUSBDevices.push_back(pUSBDevice);
5288 alock.release();
5289 /* take the current error info */
5290 com::ErrorInfoKeeper eik;
5291 /* the error must be a VirtualBoxErrorInfo instance */
5292 ComPtr<IVirtualBoxErrorInfo> pError = eik.takeError();
5293 Assert(!pError.isNull());
5294 if (!pError.isNull())
5295 {
5296 /* notify callbacks about the error */
5297 onUSBDeviceStateChange(pUSBDevice, false /* aAttached */, pError);
5298 }
5299 }
5300
5301 return rc;
5302
5303#else /* !VBOX_WITH_USB */
5304 return E_FAIL;
5305#endif /* !VBOX_WITH_USB */
5306}
5307
5308/**
5309 * Called by IInternalSessionControl::OnBandwidthGroupChange().
5310 *
5311 * @note Locks this object for writing.
5312 */
5313HRESULT Console::onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
5314{
5315 LogFlowThisFunc(("\n"));
5316
5317 AutoCaller autoCaller(this);
5318 AssertComRCReturnRC(autoCaller.rc());
5319
5320 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5321
5322 HRESULT rc = S_OK;
5323
5324 /* don't trigger the CPU priority change if the VM isn't running */
5325 SafeVMPtrQuiet ptrVM(this);
5326 if (ptrVM.isOk())
5327 {
5328 if ( mMachineState == MachineState_Running
5329 || mMachineState == MachineState_Teleporting
5330 || mMachineState == MachineState_LiveSnapshotting
5331 )
5332 {
5333 /* No need to call in the EMT thread. */
5334 LONG64 cMax;
5335 Bstr strName;
5336 BandwidthGroupType_T enmType;
5337 rc = aBandwidthGroup->COMGETTER(Name)(strName.asOutParam());
5338 if (SUCCEEDED(rc))
5339 rc = aBandwidthGroup->COMGETTER(MaxBytesPerSec)(&cMax);
5340 if (SUCCEEDED(rc))
5341 rc = aBandwidthGroup->COMGETTER(Type)(&enmType);
5342
5343 if (SUCCEEDED(rc))
5344 {
5345 int vrc = VINF_SUCCESS;
5346 if (enmType == BandwidthGroupType_Disk)
5347 vrc = PDMR3AsyncCompletionBwMgrSetMaxForFile(ptrVM, Utf8Str(strName).c_str(),
5348 cMax);
5349#ifdef VBOX_WITH_NETSHAPER
5350 else if (enmType == BandwidthGroupType_Network)
5351 vrc = PDMR3NsBwGroupSetLimit(ptrVM, Utf8Str(strName).c_str(),
5352 cMax);
5353 else
5354 rc = E_NOTIMPL;
5355#endif /* VBOX_WITH_NETSHAPER */
5356 AssertRC(vrc);
5357 }
5358 }
5359 else
5360 rc = setInvalidMachineStateError();
5361 ptrVM.release();
5362 }
5363
5364 /* notify console callbacks on success */
5365 if (SUCCEEDED(rc))
5366 {
5367 alock.release();
5368 fireBandwidthGroupChangedEvent(mEventSource, aBandwidthGroup);
5369 }
5370
5371 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
5372 return rc;
5373}
5374
5375/**
5376 * Called by IInternalSessionControl::OnStorageDeviceChange().
5377 *
5378 * @note Locks this object for writing.
5379 */
5380HRESULT Console::onStorageDeviceChange(IMediumAttachment *aMediumAttachment, BOOL aRemove)
5381{
5382 LogFlowThisFunc(("\n"));
5383
5384 AutoCaller autoCaller(this);
5385 AssertComRCReturnRC(autoCaller.rc());
5386
5387 HRESULT rc = S_OK;
5388
5389 /* don't trigger medium change if the VM isn't running */
5390 SafeVMPtrQuiet ptrVM(this);
5391 if (ptrVM.isOk())
5392 {
5393 if (aRemove)
5394 rc = doStorageDeviceDetach(aMediumAttachment, ptrVM.rawUVM());
5395 else
5396 rc = doStorageDeviceAttach(aMediumAttachment, ptrVM.rawUVM());
5397 ptrVM.release();
5398 }
5399
5400 /* notify console callbacks on success */
5401 if (SUCCEEDED(rc))
5402 fireStorageDeviceChangedEvent(mEventSource, aMediumAttachment, aRemove);
5403
5404 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
5405 return rc;
5406}
5407
5408/**
5409 * @note Temporarily locks this object for writing.
5410 */
5411HRESULT Console::getGuestProperty(IN_BSTR aName, BSTR *aValue,
5412 LONG64 *aTimestamp, BSTR *aFlags)
5413{
5414#ifndef VBOX_WITH_GUEST_PROPS
5415 ReturnComNotImplemented();
5416#else /* VBOX_WITH_GUEST_PROPS */
5417 if (!VALID_PTR(aName))
5418 return E_INVALIDARG;
5419 if (!VALID_PTR(aValue))
5420 return E_POINTER;
5421 if ((aTimestamp != NULL) && !VALID_PTR(aTimestamp))
5422 return E_POINTER;
5423 if ((aFlags != NULL) && !VALID_PTR(aFlags))
5424 return E_POINTER;
5425
5426 AutoCaller autoCaller(this);
5427 AssertComRCReturnRC(autoCaller.rc());
5428
5429 /* protect mpVM (if not NULL) */
5430 AutoVMCallerWeak autoVMCaller(this);
5431 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
5432
5433 /* Note: validity of mVMMDev which is bound to uninit() is guaranteed by
5434 * autoVMCaller, so there is no need to hold a lock of this */
5435
5436 HRESULT rc = E_UNEXPECTED;
5437 using namespace guestProp;
5438
5439 try
5440 {
5441 VBOXHGCMSVCPARM parm[4];
5442 Utf8Str Utf8Name = aName;
5443 char szBuffer[MAX_VALUE_LEN + MAX_FLAGS_LEN];
5444
5445 parm[0].type = VBOX_HGCM_SVC_PARM_PTR;
5446 parm[0].u.pointer.addr = (void*)Utf8Name.c_str();
5447 /* The + 1 is the null terminator */
5448 parm[0].u.pointer.size = (uint32_t)Utf8Name.length() + 1;
5449 parm[1].type = VBOX_HGCM_SVC_PARM_PTR;
5450 parm[1].u.pointer.addr = szBuffer;
5451 parm[1].u.pointer.size = sizeof(szBuffer);
5452 int vrc = m_pVMMDev->hgcmHostCall("VBoxGuestPropSvc", GET_PROP_HOST,
5453 4, &parm[0]);
5454 /* The returned string should never be able to be greater than our buffer */
5455 AssertLogRel(vrc != VERR_BUFFER_OVERFLOW);
5456 AssertLogRel(RT_FAILURE(vrc) || VBOX_HGCM_SVC_PARM_64BIT == parm[2].type);
5457 if (RT_SUCCESS(vrc) || (VERR_NOT_FOUND == vrc))
5458 {
5459 rc = S_OK;
5460 if (vrc != VERR_NOT_FOUND)
5461 {
5462 Utf8Str strBuffer(szBuffer);
5463 strBuffer.cloneTo(aValue);
5464
5465 if (aTimestamp)
5466 *aTimestamp = parm[2].u.uint64;
5467
5468 if (aFlags)
5469 {
5470 size_t iFlags = strBuffer.length() + 1;
5471 Utf8Str(szBuffer + iFlags).cloneTo(aFlags);
5472 }
5473 }
5474 else
5475 aValue = NULL;
5476 }
5477 else
5478 rc = setError(E_UNEXPECTED,
5479 tr("The service call failed with the error %Rrc"),
5480 vrc);
5481 }
5482 catch(std::bad_alloc & /*e*/)
5483 {
5484 rc = E_OUTOFMEMORY;
5485 }
5486 return rc;
5487#endif /* VBOX_WITH_GUEST_PROPS */
5488}
5489
5490/**
5491 * @note Temporarily locks this object for writing.
5492 */
5493HRESULT Console::setGuestProperty(IN_BSTR aName, IN_BSTR aValue, IN_BSTR aFlags)
5494{
5495#ifndef VBOX_WITH_GUEST_PROPS
5496 ReturnComNotImplemented();
5497#else /* VBOX_WITH_GUEST_PROPS */
5498 if (!VALID_PTR(aName))
5499 return E_INVALIDARG;
5500 if ((aValue != NULL) && !VALID_PTR(aValue))
5501 return E_INVALIDARG;
5502 if ((aFlags != NULL) && !VALID_PTR(aFlags))
5503 return E_INVALIDARG;
5504
5505 AutoCaller autoCaller(this);
5506 AssertComRCReturnRC(autoCaller.rc());
5507
5508 /* protect mpVM (if not NULL) */
5509 AutoVMCallerWeak autoVMCaller(this);
5510 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
5511
5512 /* Note: validity of mVMMDev which is bound to uninit() is guaranteed by
5513 * autoVMCaller, so there is no need to hold a lock of this */
5514
5515 HRESULT rc = E_UNEXPECTED;
5516 using namespace guestProp;
5517
5518 VBOXHGCMSVCPARM parm[3];
5519 Utf8Str Utf8Name = aName;
5520 int vrc = VINF_SUCCESS;
5521
5522 parm[0].type = VBOX_HGCM_SVC_PARM_PTR;
5523 parm[0].u.pointer.addr = (void*)Utf8Name.c_str();
5524 /* The + 1 is the null terminator */
5525 parm[0].u.pointer.size = (uint32_t)Utf8Name.length() + 1;
5526 Utf8Str Utf8Value = aValue;
5527 if (aValue != NULL)
5528 {
5529 parm[1].type = VBOX_HGCM_SVC_PARM_PTR;
5530 parm[1].u.pointer.addr = (void*)Utf8Value.c_str();
5531 /* The + 1 is the null terminator */
5532 parm[1].u.pointer.size = (uint32_t)Utf8Value.length() + 1;
5533 }
5534 Utf8Str Utf8Flags = aFlags;
5535 if (aFlags != NULL)
5536 {
5537 parm[2].type = VBOX_HGCM_SVC_PARM_PTR;
5538 parm[2].u.pointer.addr = (void*)Utf8Flags.c_str();
5539 /* The + 1 is the null terminator */
5540 parm[2].u.pointer.size = (uint32_t)Utf8Flags.length() + 1;
5541 }
5542 if ((aValue != NULL) && (aFlags != NULL))
5543 vrc = m_pVMMDev->hgcmHostCall("VBoxGuestPropSvc", SET_PROP_HOST,
5544 3, &parm[0]);
5545 else if (aValue != NULL)
5546 vrc = m_pVMMDev->hgcmHostCall("VBoxGuestPropSvc", SET_PROP_VALUE_HOST,
5547 2, &parm[0]);
5548 else
5549 vrc = m_pVMMDev->hgcmHostCall("VBoxGuestPropSvc", DEL_PROP_HOST,
5550 1, &parm[0]);
5551 if (RT_SUCCESS(vrc))
5552 rc = S_OK;
5553 else
5554 rc = setError(E_UNEXPECTED,
5555 tr("The service call failed with the error %Rrc"),
5556 vrc);
5557 return rc;
5558#endif /* VBOX_WITH_GUEST_PROPS */
5559}
5560
5561
5562/**
5563 * @note Temporarily locks this object for writing.
5564 */
5565HRESULT Console::enumerateGuestProperties(IN_BSTR aPatterns,
5566 ComSafeArrayOut(BSTR, aNames),
5567 ComSafeArrayOut(BSTR, aValues),
5568 ComSafeArrayOut(LONG64, aTimestamps),
5569 ComSafeArrayOut(BSTR, aFlags))
5570{
5571#ifndef VBOX_WITH_GUEST_PROPS
5572 ReturnComNotImplemented();
5573#else /* VBOX_WITH_GUEST_PROPS */
5574 if (!VALID_PTR(aPatterns) && (aPatterns != NULL))
5575 return E_POINTER;
5576 if (ComSafeArrayOutIsNull(aNames))
5577 return E_POINTER;
5578 if (ComSafeArrayOutIsNull(aValues))
5579 return E_POINTER;
5580 if (ComSafeArrayOutIsNull(aTimestamps))
5581 return E_POINTER;
5582 if (ComSafeArrayOutIsNull(aFlags))
5583 return E_POINTER;
5584
5585 AutoCaller autoCaller(this);
5586 AssertComRCReturnRC(autoCaller.rc());
5587
5588 /* protect mpVM (if not NULL) */
5589 AutoVMCallerWeak autoVMCaller(this);
5590 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
5591
5592 /* Note: validity of mVMMDev which is bound to uninit() is guaranteed by
5593 * autoVMCaller, so there is no need to hold a lock of this */
5594
5595 return doEnumerateGuestProperties(aPatterns, ComSafeArrayOutArg(aNames),
5596 ComSafeArrayOutArg(aValues),
5597 ComSafeArrayOutArg(aTimestamps),
5598 ComSafeArrayOutArg(aFlags));
5599#endif /* VBOX_WITH_GUEST_PROPS */
5600}
5601
5602
5603/*
5604 * Internal: helper function for connecting progress reporting
5605 */
5606static int onlineMergeMediumProgress(void *pvUser, unsigned uPercentage)
5607{
5608 HRESULT rc = S_OK;
5609 IProgress *pProgress = static_cast<IProgress *>(pvUser);
5610 if (pProgress)
5611 rc = pProgress->SetCurrentOperationProgress(uPercentage);
5612 return SUCCEEDED(rc) ? VINF_SUCCESS : VERR_GENERAL_FAILURE;
5613}
5614
5615/**
5616 * @note Temporarily locks this object for writing. bird: And/or reading?
5617 */
5618HRESULT Console::onlineMergeMedium(IMediumAttachment *aMediumAttachment,
5619 ULONG aSourceIdx, ULONG aTargetIdx,
5620 IMedium *aSource, IMedium *aTarget,
5621 BOOL aMergeForward,
5622 IMedium *aParentForTarget,
5623 ComSafeArrayIn(IMedium *, aChildrenToReparent),
5624 IProgress *aProgress)
5625{
5626 AutoCaller autoCaller(this);
5627 AssertComRCReturnRC(autoCaller.rc());
5628
5629 HRESULT rc = S_OK;
5630 int vrc = VINF_SUCCESS;
5631
5632 /* Get the VM - must be done before the read-locking. */
5633 SafeVMPtr ptrVM(this);
5634 if (!ptrVM.isOk())
5635 return ptrVM.rc();
5636
5637 /* We will need to release the lock before doing the actual merge */
5638 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5639
5640 /* paranoia - we don't want merges to happen while teleporting etc. */
5641 switch (mMachineState)
5642 {
5643 case MachineState_DeletingSnapshotOnline:
5644 case MachineState_DeletingSnapshotPaused:
5645 break;
5646
5647 default:
5648 return setInvalidMachineStateError();
5649 }
5650
5651 /** @todo AssertComRC -> AssertComRCReturn! Could potentially end up
5652 * using uninitialized variables here. */
5653 BOOL fBuiltinIOCache;
5654 rc = mMachine->COMGETTER(IOCacheEnabled)(&fBuiltinIOCache);
5655 AssertComRC(rc);
5656 SafeIfaceArray<IStorageController> ctrls;
5657 rc = mMachine->COMGETTER(StorageControllers)(ComSafeArrayAsOutParam(ctrls));
5658 AssertComRC(rc);
5659 LONG lDev;
5660 rc = aMediumAttachment->COMGETTER(Device)(&lDev);
5661 AssertComRC(rc);
5662 LONG lPort;
5663 rc = aMediumAttachment->COMGETTER(Port)(&lPort);
5664 AssertComRC(rc);
5665 IMedium *pMedium;
5666 rc = aMediumAttachment->COMGETTER(Medium)(&pMedium);
5667 AssertComRC(rc);
5668 Bstr mediumLocation;
5669 if (pMedium)
5670 {
5671 rc = pMedium->COMGETTER(Location)(mediumLocation.asOutParam());
5672 AssertComRC(rc);
5673 }
5674
5675 Bstr attCtrlName;
5676 rc = aMediumAttachment->COMGETTER(Controller)(attCtrlName.asOutParam());
5677 AssertComRC(rc);
5678 ComPtr<IStorageController> pStorageController;
5679 for (size_t i = 0; i < ctrls.size(); ++i)
5680 {
5681 Bstr ctrlName;
5682 rc = ctrls[i]->COMGETTER(Name)(ctrlName.asOutParam());
5683 AssertComRC(rc);
5684 if (attCtrlName == ctrlName)
5685 {
5686 pStorageController = ctrls[i];
5687 break;
5688 }
5689 }
5690 if (pStorageController.isNull())
5691 return setError(E_FAIL,
5692 tr("Could not find storage controller '%ls'"),
5693 attCtrlName.raw());
5694
5695 StorageControllerType_T enmCtrlType;
5696 rc = pStorageController->COMGETTER(ControllerType)(&enmCtrlType);
5697 AssertComRC(rc);
5698 const char *pcszDevice = convertControllerTypeToDev(enmCtrlType);
5699
5700 StorageBus_T enmBus;
5701 rc = pStorageController->COMGETTER(Bus)(&enmBus);
5702 AssertComRC(rc);
5703 ULONG uInstance;
5704 rc = pStorageController->COMGETTER(Instance)(&uInstance);
5705 AssertComRC(rc);
5706 BOOL fUseHostIOCache;
5707 rc = pStorageController->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
5708 AssertComRC(rc);
5709
5710 unsigned uLUN;
5711 rc = Console::convertBusPortDeviceToLun(enmBus, lPort, lDev, uLUN);
5712 AssertComRCReturnRC(rc);
5713
5714 alock.release();
5715
5716 /* Pause the VM, as it might have pending IO on this drive */
5717 VMSTATE enmVMState = VMR3GetStateU(ptrVM.rawUVM());
5718 if (mMachineState == MachineState_DeletingSnapshotOnline)
5719 {
5720 LogFlowFunc(("Suspending the VM...\n"));
5721 /* disable the callback to prevent Console-level state change */
5722 mVMStateChangeCallbackDisabled = true;
5723 int vrc2 = VMR3Suspend(ptrVM.rawUVM());
5724 mVMStateChangeCallbackDisabled = false;
5725 AssertRCReturn(vrc2, E_FAIL);
5726 }
5727
5728 vrc = VMR3ReqCallWaitU(ptrVM.rawUVM(),
5729 VMCPUID_ANY,
5730 (PFNRT)reconfigureMediumAttachment,
5731 13,
5732 this,
5733 ptrVM.rawUVM(),
5734 pcszDevice,
5735 uInstance,
5736 enmBus,
5737 fUseHostIOCache,
5738 fBuiltinIOCache,
5739 true /* fSetupMerge */,
5740 aSourceIdx,
5741 aTargetIdx,
5742 aMediumAttachment,
5743 mMachineState,
5744 &rc);
5745 /* error handling is after resuming the VM */
5746
5747 if (mMachineState == MachineState_DeletingSnapshotOnline)
5748 {
5749 LogFlowFunc(("Resuming the VM...\n"));
5750 /* disable the callback to prevent Console-level state change */
5751 mVMStateChangeCallbackDisabled = true;
5752 int vrc2 = VMR3Resume(ptrVM.rawUVM());
5753 mVMStateChangeCallbackDisabled = false;
5754 if (RT_FAILURE(vrc2))
5755 {
5756 /* too bad, we failed. try to sync the console state with the VMM state */
5757 AssertLogRelRC(vrc2);
5758 vmstateChangeCallback(ptrVM, VMSTATE_SUSPENDED, enmVMState, this);
5759 }
5760 }
5761
5762 if (RT_FAILURE(vrc))
5763 return setError(E_FAIL, tr("%Rrc"), vrc);
5764 if (FAILED(rc))
5765 return rc;
5766
5767 PPDMIBASE pIBase = NULL;
5768 PPDMIMEDIA pIMedium = NULL;
5769 vrc = PDMR3QueryDriverOnLun(ptrVM, pcszDevice, uInstance, uLUN, "VD", &pIBase);
5770 if (RT_SUCCESS(vrc))
5771 {
5772 if (pIBase)
5773 {
5774 pIMedium = (PPDMIMEDIA)pIBase->pfnQueryInterface(pIBase, PDMIMEDIA_IID);
5775 if (!pIMedium)
5776 return setError(E_FAIL, tr("could not query medium interface of controller"));
5777 }
5778 else
5779 return setError(E_FAIL, tr("could not query base interface of controller"));
5780 }
5781
5782 /* Finally trigger the merge. */
5783 vrc = pIMedium->pfnMerge(pIMedium, onlineMergeMediumProgress, aProgress);
5784 if (RT_FAILURE(vrc))
5785 return setError(E_FAIL, tr("Failed to perform an online medium merge (%Rrc)"), vrc);
5786
5787 /* Pause the VM, as it might have pending IO on this drive */
5788 enmVMState = VMR3GetStateU(ptrVM.rawUVM());
5789 if (mMachineState == MachineState_DeletingSnapshotOnline)
5790 {
5791 LogFlowFunc(("Suspending the VM...\n"));
5792 /* disable the callback to prevent Console-level state change */
5793 mVMStateChangeCallbackDisabled = true;
5794 int vrc2 = VMR3Suspend(ptrVM.rawUVM());
5795 mVMStateChangeCallbackDisabled = false;
5796 AssertRCReturn(vrc2, E_FAIL);
5797 }
5798
5799 /* Update medium chain and state now, so that the VM can continue. */
5800 rc = mControl->FinishOnlineMergeMedium(aMediumAttachment, aSource, aTarget,
5801 aMergeForward, aParentForTarget,
5802 ComSafeArrayInArg(aChildrenToReparent));
5803
5804 vrc = VMR3ReqCallWaitU(ptrVM.rawUVM(),
5805 VMCPUID_ANY,
5806 (PFNRT)reconfigureMediumAttachment,
5807 13,
5808 this,
5809 ptrVM.rawUVM(),
5810 pcszDevice,
5811 uInstance,
5812 enmBus,
5813 fUseHostIOCache,
5814 fBuiltinIOCache,
5815 false /* fSetupMerge */,
5816 0 /* uMergeSource */,
5817 0 /* uMergeTarget */,
5818 aMediumAttachment,
5819 mMachineState,
5820 &rc);
5821 /* error handling is after resuming the VM */
5822
5823 if (mMachineState == MachineState_DeletingSnapshotOnline)
5824 {
5825 LogFlowFunc(("Resuming the VM...\n"));
5826 /* disable the callback to prevent Console-level state change */
5827 mVMStateChangeCallbackDisabled = true;
5828 int vrc2 = VMR3Resume(ptrVM.rawUVM());
5829 mVMStateChangeCallbackDisabled = false;
5830 AssertRC(vrc2);
5831 if (RT_FAILURE(vrc2))
5832 {
5833 /* too bad, we failed. try to sync the console state with the VMM state */
5834 vmstateChangeCallback(ptrVM, VMSTATE_SUSPENDED, enmVMState, this);
5835 }
5836 }
5837
5838 if (RT_FAILURE(vrc))
5839 return setError(E_FAIL, tr("%Rrc"), vrc);
5840 if (FAILED(rc))
5841 return rc;
5842
5843 return rc;
5844}
5845
5846
5847/**
5848 * Merely passes the call to Guest::enableVMMStatistics().
5849 */
5850void Console::enableVMMStatistics(BOOL aEnable)
5851{
5852 if (mGuest)
5853 mGuest->enableVMMStatistics(aEnable);
5854}
5855
5856/**
5857 * Gets called by Session::UpdateMachineState()
5858 * (IInternalSessionControl::updateMachineState()).
5859 *
5860 * Must be called only in certain cases (see the implementation).
5861 *
5862 * @note Locks this object for writing.
5863 */
5864HRESULT Console::updateMachineState(MachineState_T aMachineState)
5865{
5866 AutoCaller autoCaller(this);
5867 AssertComRCReturnRC(autoCaller.rc());
5868
5869 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5870
5871 AssertReturn( mMachineState == MachineState_Saving
5872 || mMachineState == MachineState_LiveSnapshotting
5873 || mMachineState == MachineState_RestoringSnapshot
5874 || mMachineState == MachineState_DeletingSnapshot
5875 || mMachineState == MachineState_DeletingSnapshotOnline
5876 || mMachineState == MachineState_DeletingSnapshotPaused
5877 , E_FAIL);
5878
5879 return setMachineStateLocally(aMachineState);
5880}
5881
5882#ifdef CONSOLE_WITH_EVENT_CACHE
5883/**
5884 * @note Locks this object for writing.
5885 */
5886#endif
5887void Console::onMousePointerShapeChange(bool fVisible, bool fAlpha,
5888 uint32_t xHot, uint32_t yHot,
5889 uint32_t width, uint32_t height,
5890 ComSafeArrayIn(BYTE,pShape))
5891{
5892#if 0
5893 LogFlowThisFuncEnter();
5894 LogFlowThisFunc(("fVisible=%d, fAlpha=%d, xHot = %d, yHot = %d, width=%d, height=%d, shape=%p\n",
5895 fVisible, fAlpha, xHot, yHot, width, height, pShape));
5896#endif
5897
5898 AutoCaller autoCaller(this);
5899 AssertComRCReturnVoid(autoCaller.rc());
5900
5901#ifdef CONSOLE_WITH_EVENT_CACHE
5902 {
5903 /* We need a write lock because we alter the cached callback data */
5904 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5905
5906 /* Save the callback arguments */
5907 mCallbackData.mpsc.visible = fVisible;
5908 mCallbackData.mpsc.alpha = fAlpha;
5909 mCallbackData.mpsc.xHot = xHot;
5910 mCallbackData.mpsc.yHot = yHot;
5911 mCallbackData.mpsc.width = width;
5912 mCallbackData.mpsc.height = height;
5913
5914 /* start with not valid */
5915 bool wasValid = mCallbackData.mpsc.valid;
5916 mCallbackData.mpsc.valid = false;
5917
5918 com::SafeArray<BYTE> aShape(ComSafeArrayInArg(pShape));
5919 if (aShape.size() != 0)
5920 mCallbackData.mpsc.shape.initFrom(aShape);
5921 else
5922 mCallbackData.mpsc.shape.resize(0);
5923 mCallbackData.mpsc.valid = true;
5924 }
5925#endif
5926
5927 fireMousePointerShapeChangedEvent(mEventSource, fVisible, fAlpha, xHot, yHot, width, height, ComSafeArrayInArg(pShape));
5928
5929#if 0
5930 LogFlowThisFuncLeave();
5931#endif
5932}
5933
5934#ifdef CONSOLE_WITH_EVENT_CACHE
5935/**
5936 * @note Locks this object for writing.
5937 */
5938#endif
5939void Console::onMouseCapabilityChange(BOOL supportsAbsolute, BOOL supportsRelative, BOOL needsHostCursor)
5940{
5941 LogFlowThisFunc(("supportsAbsolute=%d supportsRelative=%d needsHostCursor=%d\n",
5942 supportsAbsolute, supportsRelative, needsHostCursor));
5943
5944 AutoCaller autoCaller(this);
5945 AssertComRCReturnVoid(autoCaller.rc());
5946
5947#ifdef CONSOLE_WITH_EVENT_CACHE
5948 {
5949 /* We need a write lock because we alter the cached callback data */
5950 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5951
5952 /* save the callback arguments */
5953 mCallbackData.mcc.supportsAbsolute = supportsAbsolute;
5954 mCallbackData.mcc.supportsRelative = supportsRelative;
5955 mCallbackData.mcc.needsHostCursor = needsHostCursor;
5956 mCallbackData.mcc.valid = true;
5957 }
5958#endif
5959
5960 fireMouseCapabilityChangedEvent(mEventSource, supportsAbsolute, supportsRelative, needsHostCursor);
5961}
5962
5963void Console::onStateChange(MachineState_T machineState)
5964{
5965 AutoCaller autoCaller(this);
5966 AssertComRCReturnVoid(autoCaller.rc());
5967 fireStateChangedEvent(mEventSource, machineState);
5968}
5969
5970void Console::onAdditionsStateChange()
5971{
5972 AutoCaller autoCaller(this);
5973 AssertComRCReturnVoid(autoCaller.rc());
5974
5975 fireAdditionsStateChangedEvent(mEventSource);
5976}
5977
5978/**
5979 * @remarks This notification only is for reporting an incompatible
5980 * Guest Additions interface, *not* the Guest Additions version!
5981 *
5982 * The user will be notified inside the guest if new Guest
5983 * Additions are available (via VBoxTray/VBoxClient).
5984 */
5985void Console::onAdditionsOutdated()
5986{
5987 AutoCaller autoCaller(this);
5988 AssertComRCReturnVoid(autoCaller.rc());
5989
5990 /** @todo implement this */
5991}
5992
5993#ifdef CONSOLE_WITH_EVENT_CACHE
5994/**
5995 * @note Locks this object for writing.
5996 */
5997#endif
5998void Console::onKeyboardLedsChange(bool fNumLock, bool fCapsLock, bool fScrollLock)
5999{
6000 AutoCaller autoCaller(this);
6001 AssertComRCReturnVoid(autoCaller.rc());
6002
6003#ifdef CONSOLE_WITH_EVENT_CACHE
6004 {
6005 /* We need a write lock because we alter the cached callback data */
6006 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6007
6008 /* save the callback arguments */
6009 mCallbackData.klc.numLock = fNumLock;
6010 mCallbackData.klc.capsLock = fCapsLock;
6011 mCallbackData.klc.scrollLock = fScrollLock;
6012 mCallbackData.klc.valid = true;
6013 }
6014#endif
6015
6016 fireKeyboardLedsChangedEvent(mEventSource, fNumLock, fCapsLock, fScrollLock);
6017}
6018
6019void Console::onUSBDeviceStateChange(IUSBDevice *aDevice, bool aAttached,
6020 IVirtualBoxErrorInfo *aError)
6021{
6022 AutoCaller autoCaller(this);
6023 AssertComRCReturnVoid(autoCaller.rc());
6024
6025 fireUSBDeviceStateChangedEvent(mEventSource, aDevice, aAttached, aError);
6026}
6027
6028void Console::onRuntimeError(BOOL aFatal, IN_BSTR aErrorID, IN_BSTR aMessage)
6029{
6030 AutoCaller autoCaller(this);
6031 AssertComRCReturnVoid(autoCaller.rc());
6032
6033 fireRuntimeErrorEvent(mEventSource, aFatal, aErrorID, aMessage);
6034}
6035
6036HRESULT Console::onShowWindow(BOOL aCheck, BOOL *aCanShow, LONG64 *aWinId)
6037{
6038 AssertReturn(aCanShow, E_POINTER);
6039 AssertReturn(aWinId, E_POINTER);
6040
6041 *aCanShow = FALSE;
6042 *aWinId = 0;
6043
6044 AutoCaller autoCaller(this);
6045 AssertComRCReturnRC(autoCaller.rc());
6046
6047 VBoxEventDesc evDesc;
6048 if (aCheck)
6049 {
6050 evDesc.init(mEventSource, VBoxEventType_OnCanShowWindow);
6051 BOOL fDelivered = evDesc.fire(5000); /* Wait up to 5 secs for delivery */
6052 //Assert(fDelivered);
6053 if (fDelivered)
6054 {
6055 ComPtr<IEvent> pEvent;
6056 evDesc.getEvent(pEvent.asOutParam());
6057 // bit clumsy
6058 ComPtr<ICanShowWindowEvent> pCanShowEvent = pEvent;
6059 if (pCanShowEvent)
6060 {
6061 BOOL fVetoed = FALSE;
6062 pCanShowEvent->IsVetoed(&fVetoed);
6063 *aCanShow = !fVetoed;
6064 }
6065 else
6066 {
6067 AssertFailed();
6068 *aCanShow = TRUE;
6069 }
6070 }
6071 else
6072 *aCanShow = TRUE;
6073 }
6074 else
6075 {
6076 evDesc.init(mEventSource, VBoxEventType_OnShowWindow, INT64_C(0));
6077 BOOL fDelivered = evDesc.fire(5000); /* Wait up to 5 secs for delivery */
6078 //Assert(fDelivered);
6079 if (fDelivered)
6080 {
6081 ComPtr<IEvent> pEvent;
6082 evDesc.getEvent(pEvent.asOutParam());
6083 ComPtr<IShowWindowEvent> pShowEvent = pEvent;
6084 if (pShowEvent)
6085 {
6086 LONG64 iEvWinId = 0;
6087 pShowEvent->COMGETTER(WinId)(&iEvWinId);
6088 if (iEvWinId != 0 && *aWinId == 0)
6089 *aWinId = iEvWinId;
6090 }
6091 else
6092 AssertFailed();
6093 }
6094 }
6095
6096 return S_OK;
6097}
6098
6099// private methods
6100////////////////////////////////////////////////////////////////////////////////
6101
6102/**
6103 * Increases the usage counter of the mpVM pointer. Guarantees that
6104 * VMR3Destroy() will not be called on it at least until releaseVMCaller()
6105 * is called.
6106 *
6107 * If this method returns a failure, the caller is not allowed to use mpVM
6108 * and may return the failed result code to the upper level. This method sets
6109 * the extended error info on failure if \a aQuiet is false.
6110 *
6111 * Setting \a aQuiet to true is useful for methods that don't want to return
6112 * the failed result code to the caller when this method fails (e.g. need to
6113 * silently check for the mpVM availability).
6114 *
6115 * When mpVM is NULL but \a aAllowNullVM is true, a corresponding error will be
6116 * returned instead of asserting. Having it false is intended as a sanity check
6117 * for methods that have checked mMachineState and expect mpVM *NOT* to be NULL.
6118 *
6119 * @param aQuiet true to suppress setting error info
6120 * @param aAllowNullVM true to accept mpVM being NULL and return a failure
6121 * (otherwise this method will assert if mpVM is NULL)
6122 *
6123 * @note Locks this object for writing.
6124 */
6125HRESULT Console::addVMCaller(bool aQuiet /* = false */,
6126 bool aAllowNullVM /* = false */)
6127{
6128 AutoCaller autoCaller(this);
6129 /** @todo Fix race during console/VM reference destruction, refer @bugref{6318}
6130 * comment 25. */
6131 if (FAILED(autoCaller.rc()))
6132 return autoCaller.rc();
6133
6134 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6135
6136 if (mVMDestroying)
6137 {
6138 /* powerDown() is waiting for all callers to finish */
6139 return aQuiet ? E_ACCESSDENIED : setError(E_ACCESSDENIED,
6140 tr("The virtual machine is being powered down"));
6141 }
6142
6143 if (mpUVM == NULL)
6144 {
6145 Assert(aAllowNullVM == true);
6146
6147 /* The machine is not powered up */
6148 return aQuiet ? E_ACCESSDENIED : setError(E_ACCESSDENIED,
6149 tr("The virtual machine is not powered up"));
6150 }
6151
6152 ++mVMCallers;
6153
6154 return S_OK;
6155}
6156
6157/**
6158 * Decreases the usage counter of the mpVM pointer. Must always complete
6159 * the addVMCaller() call after the mpVM pointer is no more necessary.
6160 *
6161 * @note Locks this object for writing.
6162 */
6163void Console::releaseVMCaller()
6164{
6165 AutoCaller autoCaller(this);
6166 AssertComRCReturnVoid(autoCaller.rc());
6167
6168 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6169
6170 AssertReturnVoid(mpUVM != NULL);
6171
6172 Assert(mVMCallers > 0);
6173 --mVMCallers;
6174
6175 if (mVMCallers == 0 && mVMDestroying)
6176 {
6177 /* inform powerDown() there are no more callers */
6178 RTSemEventSignal(mVMZeroCallersSem);
6179 }
6180}
6181
6182
6183HRESULT Console::safeVMPtrRetainer(PVM *a_ppVM, PUVM *a_ppUVM, bool a_Quiet)
6184{
6185 *a_ppVM = NULL;
6186 *a_ppUVM = NULL;
6187
6188 AutoCaller autoCaller(this);
6189 AssertComRCReturnRC(autoCaller.rc());
6190 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6191
6192 /*
6193 * Repeat the checks done by addVMCaller.
6194 */
6195 if (mVMDestroying) /* powerDown() is waiting for all callers to finish */
6196 return a_Quiet
6197 ? E_ACCESSDENIED
6198 : setError(E_ACCESSDENIED, tr("The virtual machine is being powered down"));
6199 PUVM pUVM = mpUVM;
6200 if (!pUVM)
6201 return a_Quiet
6202 ? E_ACCESSDENIED
6203 : setError(E_ACCESSDENIED, tr("The virtual machine is powered off"));
6204
6205 /*
6206 * Retain a reference to the user mode VM handle and get the global handle.
6207 */
6208 uint32_t cRefs = VMR3RetainUVM(pUVM);
6209 if (cRefs == UINT32_MAX)
6210 return a_Quiet
6211 ? E_ACCESSDENIED
6212 : setError(E_ACCESSDENIED, tr("The virtual machine is powered off"));
6213
6214 PVM pVM = VMR3GetVM(pUVM);
6215 if (!pVM)
6216 {
6217 VMR3ReleaseUVM(pUVM);
6218 return a_Quiet
6219 ? E_ACCESSDENIED
6220 : setError(E_ACCESSDENIED, tr("The virtual machine is powered off"));
6221 }
6222
6223 /* done */
6224 *a_ppVM = pVM;
6225 *a_ppUVM = pUVM;
6226 return S_OK;
6227}
6228
6229void Console::safeVMPtrReleaser(PVM *a_ppVM, PUVM *a_ppUVM)
6230{
6231 if (*a_ppVM && *a_ppUVM)
6232 VMR3ReleaseUVM(*a_ppUVM);
6233 *a_ppVM = NULL;
6234 *a_ppUVM = NULL;
6235}
6236
6237
6238/**
6239 * Initialize the release logging facility. In case something
6240 * goes wrong, there will be no release logging. Maybe in the future
6241 * we can add some logic to use different file names in this case.
6242 * Note that the logic must be in sync with Machine::DeleteSettings().
6243 */
6244HRESULT Console::consoleInitReleaseLog(const ComPtr<IMachine> aMachine)
6245{
6246 HRESULT hrc = S_OK;
6247
6248 Bstr logFolder;
6249 hrc = aMachine->COMGETTER(LogFolder)(logFolder.asOutParam());
6250 if (FAILED(hrc))
6251 return hrc;
6252
6253 Utf8Str logDir = logFolder;
6254
6255 /* make sure the Logs folder exists */
6256 Assert(logDir.length());
6257 if (!RTDirExists(logDir.c_str()))
6258 RTDirCreateFullPath(logDir.c_str(), 0700);
6259
6260 Utf8Str logFile = Utf8StrFmt("%s%cVBox.log",
6261 logDir.c_str(), RTPATH_DELIMITER);
6262 Utf8Str pngFile = Utf8StrFmt("%s%cVBox.png",
6263 logDir.c_str(), RTPATH_DELIMITER);
6264
6265 /*
6266 * Age the old log files
6267 * Rename .(n-1) to .(n), .(n-2) to .(n-1), ..., and the last log file to .1
6268 * Overwrite target files in case they exist.
6269 */
6270 ComPtr<IVirtualBox> pVirtualBox;
6271 aMachine->COMGETTER(Parent)(pVirtualBox.asOutParam());
6272 ComPtr<ISystemProperties> pSystemProperties;
6273 pVirtualBox->COMGETTER(SystemProperties)(pSystemProperties.asOutParam());
6274 ULONG cHistoryFiles = 3;
6275 pSystemProperties->COMGETTER(LogHistoryCount)(&cHistoryFiles);
6276 if (cHistoryFiles)
6277 {
6278 for (int i = cHistoryFiles-1; i >= 0; i--)
6279 {
6280 Utf8Str *files[] = { &logFile, &pngFile };
6281 Utf8Str oldName, newName;
6282
6283 for (unsigned int j = 0; j < RT_ELEMENTS(files); ++j)
6284 {
6285 if (i > 0)
6286 oldName = Utf8StrFmt("%s.%d", files[j]->c_str(), i);
6287 else
6288 oldName = *files[j];
6289 newName = Utf8StrFmt("%s.%d", files[j]->c_str(), i + 1);
6290 /* If the old file doesn't exist, delete the new file (if it
6291 * exists) to provide correct rotation even if the sequence is
6292 * broken */
6293 if ( RTFileRename(oldName.c_str(), newName.c_str(), RTFILEMOVE_FLAGS_REPLACE)
6294 == VERR_FILE_NOT_FOUND)
6295 RTFileDelete(newName.c_str());
6296 }
6297 }
6298 }
6299
6300 char szError[RTPATH_MAX + 128];
6301 int vrc = com::VBoxLogRelCreate("VM", logFile.c_str(),
6302 RTLOGFLAGS_PREFIX_TIME_PROG | RTLOGFLAGS_RESTRICT_GROUPS,
6303 "all all.restrict default.unrestricted",
6304 "VBOX_RELEASE_LOG", RTLOGDEST_FILE,
6305 32768 /* cMaxEntriesPerGroup */,
6306 0 /* cHistory */, 0 /* uHistoryFileTime */,
6307 0 /* uHistoryFileSize */, szError, sizeof(szError));
6308 if (RT_FAILURE(vrc))
6309 hrc = setError(E_FAIL, tr("Failed to open release log (%s, %Rrc)"),
6310 szError, vrc);
6311
6312 /* If we've made any directory changes, flush the directory to increase
6313 the likelihood that the log file will be usable after a system panic.
6314
6315 Tip: Try 'export VBOX_RELEASE_LOG_FLAGS=flush' if the last bits of the log
6316 is missing. Just don't have too high hopes for this to help. */
6317 if (SUCCEEDED(hrc) || cHistoryFiles)
6318 RTDirFlush(logDir.c_str());
6319
6320 return hrc;
6321}
6322
6323/**
6324 * Common worker for PowerUp and PowerUpPaused.
6325 *
6326 * @returns COM status code.
6327 *
6328 * @param aProgress Where to return the progress object.
6329 * @param aPaused true if PowerUpPaused called.
6330 */
6331HRESULT Console::powerUp(IProgress **aProgress, bool aPaused)
6332{
6333
6334 LogFlowThisFuncEnter();
6335 LogFlowThisFunc(("mMachineState=%d\n", mMachineState));
6336
6337 CheckComArgOutPointerValid(aProgress);
6338
6339 AutoCaller autoCaller(this);
6340 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6341
6342 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6343
6344 HRESULT rc = S_OK;
6345 ComObjPtr<Progress> pPowerupProgress;
6346 bool fBeganPoweringUp = false;
6347
6348 LONG cOperations = 1;
6349 LONG ulTotalOperationsWeight = 1;
6350
6351 try
6352 {
6353
6354 if (Global::IsOnlineOrTransient(mMachineState))
6355 throw setError(VBOX_E_INVALID_VM_STATE,
6356 tr("The virtual machine is already running or busy (machine state: %s)"),
6357 Global::stringifyMachineState(mMachineState));
6358
6359 /* Set up release logging as early as possible after the check if
6360 * there is already a running VM which we shouldn't disturb. */
6361 rc = consoleInitReleaseLog(mMachine);
6362 if (FAILED(rc))
6363 throw rc;
6364
6365 /* test and clear the TeleporterEnabled property */
6366 BOOL fTeleporterEnabled;
6367 rc = mMachine->COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
6368 if (FAILED(rc))
6369 throw rc;
6370
6371#if 0 /** @todo we should save it afterwards, but that isn't necessarily a good idea. Find a better place for this (VBoxSVC). */
6372 if (fTeleporterEnabled)
6373 {
6374 rc = mMachine->COMSETTER(TeleporterEnabled)(FALSE);
6375 if (FAILED(rc))
6376 throw rc;
6377 }
6378#endif
6379
6380 /* test the FaultToleranceState property */
6381 FaultToleranceState_T enmFaultToleranceState;
6382 rc = mMachine->COMGETTER(FaultToleranceState)(&enmFaultToleranceState);
6383 if (FAILED(rc))
6384 throw rc;
6385 BOOL fFaultToleranceSyncEnabled = (enmFaultToleranceState == FaultToleranceState_Standby);
6386
6387 /* Create a progress object to track progress of this operation. Must
6388 * be done as early as possible (together with BeginPowerUp()) as this
6389 * is vital for communicating as much as possible early powerup
6390 * failure information to the API caller */
6391 pPowerupProgress.createObject();
6392 Bstr progressDesc;
6393 if (mMachineState == MachineState_Saved)
6394 progressDesc = tr("Restoring virtual machine");
6395 else if (fTeleporterEnabled)
6396 progressDesc = tr("Teleporting virtual machine");
6397 else if (fFaultToleranceSyncEnabled)
6398 progressDesc = tr("Fault Tolerance syncing of remote virtual machine");
6399 else
6400 progressDesc = tr("Starting virtual machine");
6401
6402 /* Check all types of shared folders and compose a single list */
6403 SharedFolderDataMap sharedFolders;
6404 {
6405 /* first, insert global folders */
6406 for (SharedFolderDataMap::const_iterator it = m_mapGlobalSharedFolders.begin();
6407 it != m_mapGlobalSharedFolders.end();
6408 ++it)
6409 {
6410 const SharedFolderData &d = it->second;
6411 sharedFolders[it->first] = d;
6412 }
6413
6414 /* second, insert machine folders */
6415 for (SharedFolderDataMap::const_iterator it = m_mapMachineSharedFolders.begin();
6416 it != m_mapMachineSharedFolders.end();
6417 ++it)
6418 {
6419 const SharedFolderData &d = it->second;
6420 sharedFolders[it->first] = d;
6421 }
6422
6423 /* third, insert console folders */
6424 for (SharedFolderMap::const_iterator it = m_mapSharedFolders.begin();
6425 it != m_mapSharedFolders.end();
6426 ++it)
6427 {
6428 SharedFolder *pSF = it->second;
6429 AutoCaller sfCaller(pSF);
6430 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
6431 sharedFolders[it->first] = SharedFolderData(pSF->getHostPath(),
6432 pSF->isWritable(),
6433 pSF->isAutoMounted());
6434 }
6435 }
6436
6437 Bstr savedStateFile;
6438
6439 /*
6440 * Saved VMs will have to prove that their saved states seem kosher.
6441 */
6442 if (mMachineState == MachineState_Saved)
6443 {
6444 rc = mMachine->COMGETTER(StateFilePath)(savedStateFile.asOutParam());
6445 if (FAILED(rc))
6446 throw rc;
6447 ComAssertRet(!savedStateFile.isEmpty(), E_FAIL);
6448 int vrc = SSMR3ValidateFile(Utf8Str(savedStateFile).c_str(), false /* fChecksumIt */);
6449 if (RT_FAILURE(vrc))
6450 throw setError(VBOX_E_FILE_ERROR,
6451 tr("VM cannot start because the saved state file '%ls' is invalid (%Rrc). Delete the saved state prior to starting the VM"),
6452 savedStateFile.raw(), vrc);
6453 }
6454
6455 /* Setup task object and thread to carry out the operaton
6456 * Asycnhronously */
6457 std::auto_ptr<VMPowerUpTask> task(new VMPowerUpTask(this, pPowerupProgress));
6458 ComAssertComRCRetRC(task->rc());
6459
6460 task->mConfigConstructor = configConstructor;
6461 task->mSharedFolders = sharedFolders;
6462 task->mStartPaused = aPaused;
6463 if (mMachineState == MachineState_Saved)
6464 task->mSavedStateFile = savedStateFile;
6465 task->mTeleporterEnabled = fTeleporterEnabled;
6466 task->mEnmFaultToleranceState = enmFaultToleranceState;
6467
6468 /* Reset differencing hard disks for which autoReset is true,
6469 * but only if the machine has no snapshots OR the current snapshot
6470 * is an OFFLINE snapshot; otherwise we would reset the current
6471 * differencing image of an ONLINE snapshot which contains the disk
6472 * state of the machine while it was previously running, but without
6473 * the corresponding machine state, which is equivalent to powering
6474 * off a running machine and not good idea
6475 */
6476 ComPtr<ISnapshot> pCurrentSnapshot;
6477 rc = mMachine->COMGETTER(CurrentSnapshot)(pCurrentSnapshot.asOutParam());
6478 if (FAILED(rc))
6479 throw rc;
6480
6481 BOOL fCurrentSnapshotIsOnline = false;
6482 if (pCurrentSnapshot)
6483 {
6484 rc = pCurrentSnapshot->COMGETTER(Online)(&fCurrentSnapshotIsOnline);
6485 if (FAILED(rc))
6486 throw rc;
6487 }
6488
6489 if (!fCurrentSnapshotIsOnline)
6490 {
6491 LogFlowThisFunc(("Looking for immutable images to reset\n"));
6492
6493 com::SafeIfaceArray<IMediumAttachment> atts;
6494 rc = mMachine->COMGETTER(MediumAttachments)(ComSafeArrayAsOutParam(atts));
6495 if (FAILED(rc))
6496 throw rc;
6497
6498 for (size_t i = 0;
6499 i < atts.size();
6500 ++i)
6501 {
6502 DeviceType_T devType;
6503 rc = atts[i]->COMGETTER(Type)(&devType);
6504 /** @todo later applies to floppies as well */
6505 if (devType == DeviceType_HardDisk)
6506 {
6507 ComPtr<IMedium> pMedium;
6508 rc = atts[i]->COMGETTER(Medium)(pMedium.asOutParam());
6509 if (FAILED(rc))
6510 throw rc;
6511
6512 /* needs autoreset? */
6513 BOOL autoReset = FALSE;
6514 rc = pMedium->COMGETTER(AutoReset)(&autoReset);
6515 if (FAILED(rc))
6516 throw rc;
6517
6518 if (autoReset)
6519 {
6520 ComPtr<IProgress> pResetProgress;
6521 rc = pMedium->Reset(pResetProgress.asOutParam());
6522 if (FAILED(rc))
6523 throw rc;
6524
6525 /* save for later use on the powerup thread */
6526 task->hardDiskProgresses.push_back(pResetProgress);
6527 }
6528 }
6529 }
6530 }
6531 else
6532 LogFlowThisFunc(("Machine has a current snapshot which is online, skipping immutable images reset\n"));
6533
6534 /* setup task object and thread to carry out the operation
6535 * asynchronously */
6536
6537#ifdef VBOX_WITH_EXTPACK
6538 mptrExtPackManager->dumpAllToReleaseLog();
6539#endif
6540
6541#ifdef RT_OS_SOLARIS
6542 /* setup host core dumper for the VM */
6543 Bstr value;
6544 HRESULT hrc = mMachine->GetExtraData(Bstr("VBoxInternal2/CoreDumpEnabled").raw(), value.asOutParam());
6545 if (SUCCEEDED(hrc) && value == "1")
6546 {
6547 Bstr coreDumpDir, coreDumpReplaceSys, coreDumpLive;
6548 mMachine->GetExtraData(Bstr("VBoxInternal2/CoreDumpDir").raw(), coreDumpDir.asOutParam());
6549 mMachine->GetExtraData(Bstr("VBoxInternal2/CoreDumpReplaceSystemDump").raw(), coreDumpReplaceSys.asOutParam());
6550 mMachine->GetExtraData(Bstr("VBoxInternal2/CoreDumpLive").raw(), coreDumpLive.asOutParam());
6551
6552 uint32_t fCoreFlags = 0;
6553 if ( coreDumpReplaceSys.isEmpty() == false
6554 && Utf8Str(coreDumpReplaceSys).toUInt32() == 1)
6555 fCoreFlags |= RTCOREDUMPER_FLAGS_REPLACE_SYSTEM_DUMP;
6556
6557 if ( coreDumpLive.isEmpty() == false
6558 && Utf8Str(coreDumpLive).toUInt32() == 1)
6559 fCoreFlags |= RTCOREDUMPER_FLAGS_LIVE_CORE;
6560
6561 Utf8Str strDumpDir(coreDumpDir);
6562 const char *pszDumpDir = strDumpDir.c_str();
6563 if ( pszDumpDir
6564 && *pszDumpDir == '\0')
6565 pszDumpDir = NULL;
6566
6567 int vrc;
6568 if ( pszDumpDir
6569 && !RTDirExists(pszDumpDir))
6570 {
6571 /*
6572 * Try create the directory.
6573 */
6574 vrc = RTDirCreateFullPath(pszDumpDir, 0700);
6575 if (RT_FAILURE(vrc))
6576 throw setError(E_FAIL, "Failed to setup CoreDumper. Couldn't create dump directory '%s' (%Rrc)\n", pszDumpDir, vrc);
6577 }
6578
6579 vrc = RTCoreDumperSetup(pszDumpDir, fCoreFlags);
6580 if (RT_FAILURE(vrc))
6581 throw setError(E_FAIL, "Failed to setup CoreDumper (%Rrc)", vrc);
6582 else
6583 LogRel(("CoreDumper setup successful. pszDumpDir=%s fFlags=%#x\n", pszDumpDir ? pszDumpDir : ".", fCoreFlags));
6584 }
6585#endif
6586
6587
6588 // If there is immutable drive the process that.
6589 VMPowerUpTask::ProgressList progresses(task->hardDiskProgresses);
6590 if (aProgress && progresses.size() > 0){
6591
6592 for (VMPowerUpTask::ProgressList::const_iterator it = progresses.begin(); it != progresses.end(); ++it)
6593 {
6594 ++cOperations;
6595 ulTotalOperationsWeight += 1;
6596 }
6597 rc = pPowerupProgress->init(static_cast<IConsole *>(this),
6598 progressDesc.raw(),
6599 TRUE, // Cancelable
6600 cOperations,
6601 ulTotalOperationsWeight,
6602 Bstr(tr("Starting Hard Disk operations")).raw(),
6603 1,
6604 NULL);
6605 AssertComRCReturnRC(rc);
6606 }
6607 else if ( mMachineState == MachineState_Saved
6608 || (!fTeleporterEnabled && !fFaultToleranceSyncEnabled))
6609 {
6610 rc = pPowerupProgress->init(static_cast<IConsole *>(this),
6611 progressDesc.raw(),
6612 FALSE /* aCancelable */);
6613 }
6614 else if (fTeleporterEnabled)
6615 {
6616 rc = pPowerupProgress->init(static_cast<IConsole *>(this),
6617 progressDesc.raw(),
6618 TRUE /* aCancelable */,
6619 3 /* cOperations */,
6620 10 /* ulTotalOperationsWeight */,
6621 Bstr(tr("Teleporting virtual machine")).raw(),
6622 1 /* ulFirstOperationWeight */,
6623 NULL);
6624 }
6625 else if (fFaultToleranceSyncEnabled)
6626 {
6627 rc = pPowerupProgress->init(static_cast<IConsole *>(this),
6628 progressDesc.raw(),
6629 TRUE /* aCancelable */,
6630 3 /* cOperations */,
6631 10 /* ulTotalOperationsWeight */,
6632 Bstr(tr("Fault Tolerance syncing of remote virtual machine")).raw(),
6633 1 /* ulFirstOperationWeight */,
6634 NULL);
6635 }
6636
6637 if (FAILED(rc))
6638 throw rc;
6639
6640 /* Tell VBoxSVC and Machine about the progress object so they can
6641 combine/proxy it to any openRemoteSession caller. */
6642 LogFlowThisFunc(("Calling BeginPowerUp...\n"));
6643 rc = mControl->BeginPowerUp(pPowerupProgress);
6644 if (FAILED(rc))
6645 {
6646 LogFlowThisFunc(("BeginPowerUp failed\n"));
6647 throw rc;
6648 }
6649 fBeganPoweringUp = true;
6650
6651 LogFlowThisFunc(("Checking if canceled...\n"));
6652 BOOL fCanceled;
6653 rc = pPowerupProgress->COMGETTER(Canceled)(&fCanceled);
6654 if (FAILED(rc))
6655 throw rc;
6656
6657 if (fCanceled)
6658 {
6659 LogFlowThisFunc(("Canceled in BeginPowerUp\n"));
6660 throw setError(E_FAIL, tr("Powerup was canceled"));
6661 }
6662 LogFlowThisFunc(("Not canceled yet.\n"));
6663
6664 /** @todo this code prevents starting a VM with unavailable bridged
6665 * networking interface. The only benefit is a slightly better error
6666 * message, which should be moved to the driver code. This is the
6667 * only reason why I left the code in for now. The driver allows
6668 * unavailable bridged networking interfaces in certain circumstances,
6669 * and this is sabotaged by this check. The VM will initially have no
6670 * network connectivity, but the user can fix this at runtime. */
6671#if 0
6672 /* the network cards will undergo a quick consistency check */
6673 for (ULONG slot = 0;
6674 slot < maxNetworkAdapters;
6675 ++slot)
6676 {
6677 ComPtr<INetworkAdapter> pNetworkAdapter;
6678 mMachine->GetNetworkAdapter(slot, pNetworkAdapter.asOutParam());
6679 BOOL enabled = FALSE;
6680 pNetworkAdapter->COMGETTER(Enabled)(&enabled);
6681 if (!enabled)
6682 continue;
6683
6684 NetworkAttachmentType_T netattach;
6685 pNetworkAdapter->COMGETTER(AttachmentType)(&netattach);
6686 switch (netattach)
6687 {
6688 case NetworkAttachmentType_Bridged:
6689 {
6690 /* a valid host interface must have been set */
6691 Bstr hostif;
6692 pNetworkAdapter->COMGETTER(HostInterface)(hostif.asOutParam());
6693 if (hostif.isEmpty())
6694 {
6695 throw setError(VBOX_E_HOST_ERROR,
6696 tr("VM cannot start because host interface networking requires a host interface name to be set"));
6697 }
6698 ComPtr<IVirtualBox> pVirtualBox;
6699 mMachine->COMGETTER(Parent)(pVirtualBox.asOutParam());
6700 ComPtr<IHost> pHost;
6701 pVirtualBox->COMGETTER(Host)(pHost.asOutParam());
6702 ComPtr<IHostNetworkInterface> pHostInterface;
6703 if (!SUCCEEDED(pHost->FindHostNetworkInterfaceByName(hostif.raw(),
6704 pHostInterface.asOutParam())))
6705 {
6706 throw setError(VBOX_E_HOST_ERROR,
6707 tr("VM cannot start because the host interface '%ls' does not exist"),
6708 hostif.raw());
6709 }
6710 break;
6711 }
6712 default:
6713 break;
6714 }
6715 }
6716#endif // 0
6717
6718 /* Read console data stored in the saved state file (if not yet done) */
6719 rc = loadDataFromSavedState();
6720 if (FAILED(rc))
6721 throw rc;
6722
6723 /* setup task object and thread to carry out the operation
6724 * asynchronously */
6725 if (aProgress){
6726 rc = pPowerupProgress.queryInterfaceTo(aProgress);
6727 AssertComRCReturnRC(rc);
6728 }
6729
6730 int vrc = RTThreadCreate(NULL, Console::powerUpThread,
6731 (void *)task.get(), 0,
6732 RTTHREADTYPE_MAIN_WORKER, 0, "VMPwrUp");
6733 if (RT_FAILURE(vrc))
6734 throw setError(E_FAIL, "Could not create VMPowerUp thread (%Rrc)", vrc);
6735
6736 /* task is now owned by powerUpThread(), so release it */
6737 task.release();
6738
6739 /* finally, set the state: no right to fail in this method afterwards
6740 * since we've already started the thread and it is now responsible for
6741 * any error reporting and appropriate state change! */
6742 if (mMachineState == MachineState_Saved)
6743 setMachineState(MachineState_Restoring);
6744 else if (fTeleporterEnabled)
6745 setMachineState(MachineState_TeleportingIn);
6746 else if (enmFaultToleranceState == FaultToleranceState_Standby)
6747 setMachineState(MachineState_FaultTolerantSyncing);
6748 else
6749 setMachineState(MachineState_Starting);
6750 }
6751 catch (HRESULT aRC) { rc = aRC; }
6752
6753 if (FAILED(rc) && fBeganPoweringUp)
6754 {
6755
6756 /* The progress object will fetch the current error info */
6757 if (!pPowerupProgress.isNull())
6758 pPowerupProgress->notifyComplete(rc);
6759
6760 /* Save the error info across the IPC below. Can't be done before the
6761 * progress notification above, as saving the error info deletes it
6762 * from the current context, and thus the progress object wouldn't be
6763 * updated correctly. */
6764 ErrorInfoKeeper eik;
6765
6766 /* signal end of operation */
6767 mControl->EndPowerUp(rc);
6768 }
6769
6770 LogFlowThisFunc(("mMachineState=%d, rc=%Rhrc\n", mMachineState, rc));
6771 LogFlowThisFuncLeave();
6772 return rc;
6773}
6774
6775/**
6776 * Internal power off worker routine.
6777 *
6778 * This method may be called only at certain places with the following meaning
6779 * as shown below:
6780 *
6781 * - if the machine state is either Running or Paused, a normal
6782 * Console-initiated powerdown takes place (e.g. PowerDown());
6783 * - if the machine state is Saving, saveStateThread() has successfully done its
6784 * job;
6785 * - if the machine state is Starting or Restoring, powerUpThread() has failed
6786 * to start/load the VM;
6787 * - if the machine state is Stopping, the VM has powered itself off (i.e. not
6788 * as a result of the powerDown() call).
6789 *
6790 * Calling it in situations other than the above will cause unexpected behavior.
6791 *
6792 * Note that this method should be the only one that destroys mpVM and sets it
6793 * to NULL.
6794 *
6795 * @param aProgress Progress object to run (may be NULL).
6796 *
6797 * @note Locks this object for writing.
6798 *
6799 * @note Never call this method from a thread that called addVMCaller() or
6800 * instantiated an AutoVMCaller object; first call releaseVMCaller() or
6801 * release(). Otherwise it will deadlock.
6802 */
6803HRESULT Console::powerDown(IProgress *aProgress /*= NULL*/)
6804{
6805 LogFlowThisFuncEnter();
6806
6807 AutoCaller autoCaller(this);
6808 AssertComRCReturnRC(autoCaller.rc());
6809
6810 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6811
6812 /* Total # of steps for the progress object. Must correspond to the
6813 * number of "advance percent count" comments in this method! */
6814 enum { StepCount = 7 };
6815 /* current step */
6816 ULONG step = 0;
6817
6818 HRESULT rc = S_OK;
6819 int vrc = VINF_SUCCESS;
6820
6821 /* sanity */
6822 Assert(mVMDestroying == false);
6823
6824 PUVM pUVM = mpUVM; Assert(pUVM != NULL);
6825 uint32_t cRefs = VMR3RetainUVM(pUVM); Assert(cRefs != UINT32_MAX);
6826
6827 AssertMsg( mMachineState == MachineState_Running
6828 || mMachineState == MachineState_Paused
6829 || mMachineState == MachineState_Stuck
6830 || mMachineState == MachineState_Starting
6831 || mMachineState == MachineState_Stopping
6832 || mMachineState == MachineState_Saving
6833 || mMachineState == MachineState_Restoring
6834 || mMachineState == MachineState_TeleportingPausedVM
6835 || mMachineState == MachineState_FaultTolerantSyncing
6836 || mMachineState == MachineState_TeleportingIn
6837 , ("Invalid machine state: %s\n", Global::stringifyMachineState(mMachineState)));
6838
6839 LogRel(("Console::powerDown(): A request to power off the VM has been issued (mMachineState=%s, InUninit=%d)\n",
6840 Global::stringifyMachineState(mMachineState), autoCaller.state() == InUninit));
6841
6842 /* Check if we need to power off the VM. In case of mVMPoweredOff=true, the
6843 * VM has already powered itself off in vmstateChangeCallback() and is just
6844 * notifying Console about that. In case of Starting or Restoring,
6845 * powerUpThread() is calling us on failure, so the VM is already off at
6846 * that point. */
6847 if ( !mVMPoweredOff
6848 && ( mMachineState == MachineState_Starting
6849 || mMachineState == MachineState_Restoring
6850 || mMachineState == MachineState_FaultTolerantSyncing
6851 || mMachineState == MachineState_TeleportingIn)
6852 )
6853 mVMPoweredOff = true;
6854
6855 /*
6856 * Go to Stopping state if not already there.
6857 *
6858 * Note that we don't go from Saving/Restoring to Stopping because
6859 * vmstateChangeCallback() needs it to set the state to Saved on
6860 * VMSTATE_TERMINATED. In terms of protecting from inappropriate operations
6861 * while leaving the lock below, Saving or Restoring should be fine too.
6862 * Ditto for TeleportingPausedVM -> Teleported.
6863 */
6864 if ( mMachineState != MachineState_Saving
6865 && mMachineState != MachineState_Restoring
6866 && mMachineState != MachineState_Stopping
6867 && mMachineState != MachineState_TeleportingIn
6868 && mMachineState != MachineState_TeleportingPausedVM
6869 && mMachineState != MachineState_FaultTolerantSyncing
6870 )
6871 setMachineState(MachineState_Stopping);
6872
6873 /* ----------------------------------------------------------------------
6874 * DONE with necessary state changes, perform the power down actions (it's
6875 * safe to release the object lock now if needed)
6876 * ---------------------------------------------------------------------- */
6877
6878 /* Stop the VRDP server to prevent new clients connection while VM is being
6879 * powered off. */
6880 if (mConsoleVRDPServer)
6881 {
6882 LogFlowThisFunc(("Stopping VRDP server...\n"));
6883
6884 /* Leave the lock since EMT will call us back as addVMCaller()
6885 * in updateDisplayData(). */
6886 alock.release();
6887
6888 mConsoleVRDPServer->Stop();
6889
6890 alock.acquire();
6891 }
6892
6893 /* advance percent count */
6894 if (aProgress)
6895 aProgress->SetCurrentOperationProgress(99 * (++step) / StepCount );
6896
6897
6898 /* ----------------------------------------------------------------------
6899 * Now, wait for all mpVM callers to finish their work if there are still
6900 * some on other threads. NO methods that need mpVM (or initiate other calls
6901 * that need it) may be called after this point
6902 * ---------------------------------------------------------------------- */
6903
6904 /* go to the destroying state to prevent from adding new callers */
6905 mVMDestroying = true;
6906
6907 if (mVMCallers > 0)
6908 {
6909 /* lazy creation */
6910 if (mVMZeroCallersSem == NIL_RTSEMEVENT)
6911 RTSemEventCreate(&mVMZeroCallersSem);
6912
6913 LogFlowThisFunc(("Waiting for mpVM callers (%d) to drop to zero...\n",
6914 mVMCallers));
6915
6916 alock.release();
6917
6918 RTSemEventWait(mVMZeroCallersSem, RT_INDEFINITE_WAIT);
6919
6920 alock.acquire();
6921 }
6922
6923 /* advance percent count */
6924 if (aProgress)
6925 aProgress->SetCurrentOperationProgress(99 * (++step) / StepCount );
6926
6927 vrc = VINF_SUCCESS;
6928
6929 /*
6930 * Power off the VM if not already done that.
6931 * Leave the lock since EMT will call vmstateChangeCallback.
6932 *
6933 * Note that VMR3PowerOff() may fail here (invalid VMSTATE) if the
6934 * VM-(guest-)initiated power off happened in parallel a ms before this
6935 * call. So far, we let this error pop up on the user's side.
6936 */
6937 if (!mVMPoweredOff)
6938 {
6939 LogFlowThisFunc(("Powering off the VM...\n"));
6940 alock.release();
6941 vrc = VMR3PowerOff(pUVM);
6942#ifdef VBOX_WITH_EXTPACK
6943 mptrExtPackManager->callAllVmPowerOffHooks(this, VMR3GetVM(pUVM));
6944#endif
6945 alock.acquire();
6946 }
6947
6948 /* advance percent count */
6949 if (aProgress)
6950 aProgress->SetCurrentOperationProgress(99 * (++step) / StepCount );
6951
6952#ifdef VBOX_WITH_HGCM
6953 /* Shutdown HGCM services before destroying the VM. */
6954 if (m_pVMMDev)
6955 {
6956 LogFlowThisFunc(("Shutdown HGCM...\n"));
6957
6958 /* Leave the lock since EMT will call us back as addVMCaller() */
6959 alock.release();
6960
6961 m_pVMMDev->hgcmShutdown();
6962
6963 alock.acquire();
6964 }
6965
6966 /* advance percent count */
6967 if (aProgress)
6968 aProgress->SetCurrentOperationProgress(99 * (++step) / StepCount);
6969
6970#endif /* VBOX_WITH_HGCM */
6971
6972 LogFlowThisFunc(("Ready for VM destruction.\n"));
6973
6974 /* If we are called from Console::uninit(), then try to destroy the VM even
6975 * on failure (this will most likely fail too, but what to do?..) */
6976 if (RT_SUCCESS(vrc) || autoCaller.state() == InUninit)
6977 {
6978 /* If the machine has an USB controller, release all USB devices
6979 * (symmetric to the code in captureUSBDevices()) */
6980 bool fHasUSBController = false;
6981 {
6982 PPDMIBASE pBase;
6983 vrc = PDMR3QueryLun(VMR3GetVM(pUVM), "usb-ohci", 0, 0, &pBase);
6984 if (RT_SUCCESS(vrc))
6985 {
6986 fHasUSBController = true;
6987 alock.release();
6988 detachAllUSBDevices(false /* aDone */);
6989 alock.acquire();
6990 }
6991 }
6992
6993 /* Now we've got to destroy the VM as well. (mpVM is not valid beyond
6994 * this point). We release the lock before calling VMR3Destroy() because
6995 * it will result into calling destructors of drivers associated with
6996 * Console children which may in turn try to lock Console (e.g. by
6997 * instantiating SafeVMPtr to access mpVM). It's safe here because
6998 * mVMDestroying is set which should prevent any activity. */
6999
7000 /* Set mpUVM to NULL early just in case if some old code is not using
7001 * addVMCaller()/releaseVMCaller(). (We have our own ref on pUVM.) */
7002 VMR3ReleaseUVM(mpUVM);
7003 mpUVM = NULL;
7004
7005 LogFlowThisFunc(("Destroying the VM...\n"));
7006
7007 alock.release();
7008
7009 vrc = VMR3Destroy(pUVM);
7010
7011 /* take the lock again */
7012 alock.acquire();
7013
7014 /* advance percent count */
7015 if (aProgress)
7016 aProgress->SetCurrentOperationProgress(99 * (++step) / StepCount);
7017
7018 if (RT_SUCCESS(vrc))
7019 {
7020 LogFlowThisFunc(("Machine has been destroyed (mMachineState=%d)\n",
7021 mMachineState));
7022 /* Note: the Console-level machine state change happens on the
7023 * VMSTATE_TERMINATE state change in vmstateChangeCallback(). If
7024 * powerDown() is called from EMT (i.e. from vmstateChangeCallback()
7025 * on receiving VM-initiated VMSTATE_OFF), VMSTATE_TERMINATE hasn't
7026 * occurred yet. This is okay, because mMachineState is already
7027 * Stopping in this case, so any other attempt to call PowerDown()
7028 * will be rejected. */
7029 }
7030 else
7031 {
7032 /* bad bad bad, but what to do? (Give Console our UVM ref.) */
7033 mpUVM = pUVM;
7034 pUVM = NULL;
7035 rc = setError(VBOX_E_VM_ERROR,
7036 tr("Could not destroy the machine. (Error: %Rrc)"),
7037 vrc);
7038 }
7039
7040 /* Complete the detaching of the USB devices. */
7041 if (fHasUSBController)
7042 {
7043 alock.release();
7044 detachAllUSBDevices(true /* aDone */);
7045 alock.acquire();
7046 }
7047
7048 /* advance percent count */
7049 if (aProgress)
7050 aProgress->SetCurrentOperationProgress(99 * (++step) / StepCount);
7051 }
7052 else
7053 {
7054 rc = setError(VBOX_E_VM_ERROR,
7055 tr("Could not power off the machine. (Error: %Rrc)"),
7056 vrc);
7057 }
7058
7059 /*
7060 * Finished with the destruction.
7061 *
7062 * Note that if something impossible happened and we've failed to destroy
7063 * the VM, mVMDestroying will remain true and mMachineState will be
7064 * something like Stopping, so most Console methods will return an error
7065 * to the caller.
7066 */
7067 if (mpUVM != NULL)
7068 VMR3ReleaseUVM(pUVM);
7069 else
7070 mVMDestroying = false;
7071
7072#ifdef CONSOLE_WITH_EVENT_CACHE
7073 if (SUCCEEDED(rc))
7074 mCallbackData.clear();
7075#endif
7076
7077 LogFlowThisFuncLeave();
7078 return rc;
7079}
7080
7081/**
7082 * @note Locks this object for writing.
7083 */
7084HRESULT Console::setMachineState(MachineState_T aMachineState,
7085 bool aUpdateServer /* = true */)
7086{
7087 AutoCaller autoCaller(this);
7088 AssertComRCReturnRC(autoCaller.rc());
7089
7090 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7091
7092 HRESULT rc = S_OK;
7093
7094 if (mMachineState != aMachineState)
7095 {
7096 LogThisFunc(("machineState=%s -> %s aUpdateServer=%RTbool\n",
7097 Global::stringifyMachineState(mMachineState), Global::stringifyMachineState(aMachineState), aUpdateServer));
7098 mMachineState = aMachineState;
7099
7100 /// @todo (dmik)
7101 // possibly, we need to redo onStateChange() using the dedicated
7102 // Event thread, like it is done in VirtualBox. This will make it
7103 // much safer (no deadlocks possible if someone tries to use the
7104 // console from the callback), however, listeners will lose the
7105 // ability to synchronously react to state changes (is it really
7106 // necessary??)
7107 LogFlowThisFunc(("Doing onStateChange()...\n"));
7108 onStateChange(aMachineState);
7109 LogFlowThisFunc(("Done onStateChange()\n"));
7110
7111 if (aUpdateServer)
7112 {
7113 /* Server notification MUST be done from under the lock; otherwise
7114 * the machine state here and on the server might go out of sync
7115 * which can lead to various unexpected results (like the machine
7116 * state being >= MachineState_Running on the server, while the
7117 * session state is already SessionState_Unlocked at the same time
7118 * there).
7119 *
7120 * Cross-lock conditions should be carefully watched out: calling
7121 * UpdateState we will require Machine and SessionMachine locks
7122 * (remember that here we're holding the Console lock here, and also
7123 * all locks that have been acquire by the thread before calling
7124 * this method).
7125 */
7126 LogFlowThisFunc(("Doing mControl->UpdateState()...\n"));
7127 rc = mControl->UpdateState(aMachineState);
7128 LogFlowThisFunc(("mControl->UpdateState()=%Rhrc\n", rc));
7129 }
7130 }
7131
7132 return rc;
7133}
7134
7135/**
7136 * Searches for a shared folder with the given logical name
7137 * in the collection of shared folders.
7138 *
7139 * @param aName logical name of the shared folder
7140 * @param aSharedFolder where to return the found object
7141 * @param aSetError whether to set the error info if the folder is
7142 * not found
7143 * @return
7144 * S_OK when found or E_INVALIDARG when not found
7145 *
7146 * @note The caller must lock this object for writing.
7147 */
7148HRESULT Console::findSharedFolder(const Utf8Str &strName,
7149 ComObjPtr<SharedFolder> &aSharedFolder,
7150 bool aSetError /* = false */)
7151{
7152 /* sanity check */
7153 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
7154
7155 SharedFolderMap::const_iterator it = m_mapSharedFolders.find(strName);
7156 if (it != m_mapSharedFolders.end())
7157 {
7158 aSharedFolder = it->second;
7159 return S_OK;
7160 }
7161
7162 if (aSetError)
7163 setError(VBOX_E_FILE_ERROR,
7164 tr("Could not find a shared folder named '%s'."),
7165 strName.c_str());
7166
7167 return VBOX_E_FILE_ERROR;
7168}
7169
7170/**
7171 * Fetches the list of global or machine shared folders from the server.
7172 *
7173 * @param aGlobal true to fetch global folders.
7174 *
7175 * @note The caller must lock this object for writing.
7176 */
7177HRESULT Console::fetchSharedFolders(BOOL aGlobal)
7178{
7179 /* sanity check */
7180 AssertReturn(AutoCaller(this).state() == InInit ||
7181 isWriteLockOnCurrentThread(), E_FAIL);
7182
7183 LogFlowThisFunc(("Entering\n"));
7184
7185 /* Check if we're online and keep it that way. */
7186 SafeVMPtrQuiet ptrVM(this);
7187 AutoVMCallerQuietWeak autoVMCaller(this);
7188 bool const online = ptrVM.isOk()
7189 && m_pVMMDev
7190 && m_pVMMDev->isShFlActive();
7191
7192 HRESULT rc = S_OK;
7193
7194 try
7195 {
7196 if (aGlobal)
7197 {
7198 /// @todo grab & process global folders when they are done
7199 }
7200 else
7201 {
7202 SharedFolderDataMap oldFolders;
7203 if (online)
7204 oldFolders = m_mapMachineSharedFolders;
7205
7206 m_mapMachineSharedFolders.clear();
7207
7208 SafeIfaceArray<ISharedFolder> folders;
7209 rc = mMachine->COMGETTER(SharedFolders)(ComSafeArrayAsOutParam(folders));
7210 if (FAILED(rc)) throw rc;
7211
7212 for (size_t i = 0; i < folders.size(); ++i)
7213 {
7214 ComPtr<ISharedFolder> pSharedFolder = folders[i];
7215
7216 Bstr bstrName;
7217 Bstr bstrHostPath;
7218 BOOL writable;
7219 BOOL autoMount;
7220
7221 rc = pSharedFolder->COMGETTER(Name)(bstrName.asOutParam());
7222 if (FAILED(rc)) throw rc;
7223 Utf8Str strName(bstrName);
7224
7225 rc = pSharedFolder->COMGETTER(HostPath)(bstrHostPath.asOutParam());
7226 if (FAILED(rc)) throw rc;
7227 Utf8Str strHostPath(bstrHostPath);
7228
7229 rc = pSharedFolder->COMGETTER(Writable)(&writable);
7230 if (FAILED(rc)) throw rc;
7231
7232 rc = pSharedFolder->COMGETTER(AutoMount)(&autoMount);
7233 if (FAILED(rc)) throw rc;
7234
7235 m_mapMachineSharedFolders.insert(std::make_pair(strName,
7236 SharedFolderData(strHostPath, !!writable, !!autoMount)));
7237
7238 /* send changes to HGCM if the VM is running */
7239 if (online)
7240 {
7241 SharedFolderDataMap::iterator it = oldFolders.find(strName);
7242 if ( it == oldFolders.end()
7243 || it->second.m_strHostPath != strHostPath)
7244 {
7245 /* a new machine folder is added or
7246 * the existing machine folder is changed */
7247 if (m_mapSharedFolders.find(strName) != m_mapSharedFolders.end())
7248 ; /* the console folder exists, nothing to do */
7249 else
7250 {
7251 /* remove the old machine folder (when changed)
7252 * or the global folder if any (when new) */
7253 if ( it != oldFolders.end()
7254 || m_mapGlobalSharedFolders.find(strName) != m_mapGlobalSharedFolders.end()
7255 )
7256 {
7257 rc = removeSharedFolder(strName);
7258 if (FAILED(rc)) throw rc;
7259 }
7260
7261 /* create the new machine folder */
7262 rc = createSharedFolder(strName,
7263 SharedFolderData(strHostPath, !!writable, !!autoMount));
7264 if (FAILED(rc)) throw rc;
7265 }
7266 }
7267 /* forget the processed (or identical) folder */
7268 if (it != oldFolders.end())
7269 oldFolders.erase(it);
7270 }
7271 }
7272
7273 /* process outdated (removed) folders */
7274 if (online)
7275 {
7276 for (SharedFolderDataMap::const_iterator it = oldFolders.begin();
7277 it != oldFolders.end(); ++it)
7278 {
7279 if (m_mapSharedFolders.find(it->first) != m_mapSharedFolders.end())
7280 ; /* the console folder exists, nothing to do */
7281 else
7282 {
7283 /* remove the outdated machine folder */
7284 rc = removeSharedFolder(it->first);
7285 if (FAILED(rc)) throw rc;
7286
7287 /* create the global folder if there is any */
7288 SharedFolderDataMap::const_iterator git =
7289 m_mapGlobalSharedFolders.find(it->first);
7290 if (git != m_mapGlobalSharedFolders.end())
7291 {
7292 rc = createSharedFolder(git->first, git->second);
7293 if (FAILED(rc)) throw rc;
7294 }
7295 }
7296 }
7297 }
7298 }
7299 }
7300 catch (HRESULT rc2)
7301 {
7302 if (online)
7303 setVMRuntimeErrorCallbackF(ptrVM, this, 0, "BrokenSharedFolder",
7304 N_("Broken shared folder!"));
7305 }
7306
7307 LogFlowThisFunc(("Leaving\n"));
7308
7309 return rc;
7310}
7311
7312/**
7313 * Searches for a shared folder with the given name in the list of machine
7314 * shared folders and then in the list of the global shared folders.
7315 *
7316 * @param aName Name of the folder to search for.
7317 * @param aIt Where to store the pointer to the found folder.
7318 * @return @c true if the folder was found and @c false otherwise.
7319 *
7320 * @note The caller must lock this object for reading.
7321 */
7322bool Console::findOtherSharedFolder(const Utf8Str &strName,
7323 SharedFolderDataMap::const_iterator &aIt)
7324{
7325 /* sanity check */
7326 AssertReturn(isWriteLockOnCurrentThread(), false);
7327
7328 /* first, search machine folders */
7329 aIt = m_mapMachineSharedFolders.find(strName);
7330 if (aIt != m_mapMachineSharedFolders.end())
7331 return true;
7332
7333 /* second, search machine folders */
7334 aIt = m_mapGlobalSharedFolders.find(strName);
7335 if (aIt != m_mapGlobalSharedFolders.end())
7336 return true;
7337
7338 return false;
7339}
7340
7341/**
7342 * Calls the HGCM service to add a shared folder definition.
7343 *
7344 * @param aName Shared folder name.
7345 * @param aHostPath Shared folder path.
7346 *
7347 * @note Must be called from under AutoVMCaller and when mpVM != NULL!
7348 * @note Doesn't lock anything.
7349 */
7350HRESULT Console::createSharedFolder(const Utf8Str &strName, const SharedFolderData &aData)
7351{
7352 ComAssertRet(strName.isNotEmpty(), E_FAIL);
7353 ComAssertRet(aData.m_strHostPath.isNotEmpty(), E_FAIL);
7354
7355 /* sanity checks */
7356 AssertReturn(mpUVM, E_FAIL);
7357 AssertReturn(m_pVMMDev && m_pVMMDev->isShFlActive(), E_FAIL);
7358
7359 VBOXHGCMSVCPARM parms[SHFL_CPARMS_ADD_MAPPING];
7360 SHFLSTRING *pFolderName, *pMapName;
7361 size_t cbString;
7362
7363 Bstr value;
7364 HRESULT hrc = mMachine->GetExtraData(BstrFmt("VBoxInternal2/SharedFoldersEnableSymlinksCreate/%s",
7365 strName.c_str()).raw(),
7366 value.asOutParam());
7367 bool fSymlinksCreate = hrc == S_OK && value == "1";
7368
7369 Log(("Adding shared folder '%s' -> '%s'\n", strName.c_str(), aData.m_strHostPath.c_str()));
7370
7371 // check whether the path is valid and exists
7372 char hostPathFull[RTPATH_MAX];
7373 int vrc = RTPathAbsEx(NULL,
7374 aData.m_strHostPath.c_str(),
7375 hostPathFull,
7376 sizeof(hostPathFull));
7377
7378 bool fMissing = false;
7379 if (RT_FAILURE(vrc))
7380 return setError(E_INVALIDARG,
7381 tr("Invalid shared folder path: '%s' (%Rrc)"),
7382 aData.m_strHostPath.c_str(), vrc);
7383 if (!RTPathExists(hostPathFull))
7384 fMissing = true;
7385
7386 /* Check whether the path is full (absolute) */
7387 if (RTPathCompare(aData.m_strHostPath.c_str(), hostPathFull) != 0)
7388 return setError(E_INVALIDARG,
7389 tr("Shared folder path '%s' is not absolute"),
7390 aData.m_strHostPath.c_str());
7391
7392 // now that we know the path is good, give it to HGCM
7393
7394 Bstr bstrName(strName);
7395 Bstr bstrHostPath(aData.m_strHostPath);
7396
7397 cbString = (bstrHostPath.length() + 1) * sizeof(RTUTF16);
7398 if (cbString >= UINT16_MAX)
7399 return setError(E_INVALIDARG, tr("The name is too long"));
7400 pFolderName = (SHFLSTRING*)RTMemAllocZ(sizeof(SHFLSTRING) + cbString);
7401 Assert(pFolderName);
7402 memcpy(pFolderName->String.ucs2, bstrHostPath.raw(), cbString);
7403
7404 pFolderName->u16Size = (uint16_t)cbString;
7405 pFolderName->u16Length = (uint16_t)cbString - sizeof(RTUTF16);
7406
7407 parms[0].type = VBOX_HGCM_SVC_PARM_PTR;
7408 parms[0].u.pointer.addr = pFolderName;
7409 parms[0].u.pointer.size = sizeof(SHFLSTRING) + (uint16_t)cbString;
7410
7411 cbString = (bstrName.length() + 1) * sizeof(RTUTF16);
7412 if (cbString >= UINT16_MAX)
7413 {
7414 RTMemFree(pFolderName);
7415 return setError(E_INVALIDARG, tr("The host path is too long"));
7416 }
7417 pMapName = (SHFLSTRING*)RTMemAllocZ(sizeof(SHFLSTRING) + cbString);
7418 Assert(pMapName);
7419 memcpy(pMapName->String.ucs2, bstrName.raw(), cbString);
7420
7421 pMapName->u16Size = (uint16_t)cbString;
7422 pMapName->u16Length = (uint16_t)cbString - sizeof(RTUTF16);
7423
7424 parms[1].type = VBOX_HGCM_SVC_PARM_PTR;
7425 parms[1].u.pointer.addr = pMapName;
7426 parms[1].u.pointer.size = sizeof(SHFLSTRING) + (uint16_t)cbString;
7427
7428 parms[2].type = VBOX_HGCM_SVC_PARM_32BIT;
7429 parms[2].u.uint32 = (aData.m_fWritable ? SHFL_ADD_MAPPING_F_WRITABLE : 0)
7430 | (aData.m_fAutoMount ? SHFL_ADD_MAPPING_F_AUTOMOUNT : 0)
7431 | (fSymlinksCreate ? SHFL_ADD_MAPPING_F_CREATE_SYMLINKS : 0)
7432 | (fMissing ? SHFL_ADD_MAPPING_F_MISSING : 0)
7433 ;
7434
7435 vrc = m_pVMMDev->hgcmHostCall("VBoxSharedFolders",
7436 SHFL_FN_ADD_MAPPING,
7437 SHFL_CPARMS_ADD_MAPPING, &parms[0]);
7438 RTMemFree(pFolderName);
7439 RTMemFree(pMapName);
7440
7441 if (RT_FAILURE(vrc))
7442 return setError(E_FAIL,
7443 tr("Could not create a shared folder '%s' mapped to '%s' (%Rrc)"),
7444 strName.c_str(), aData.m_strHostPath.c_str(), vrc);
7445
7446 if (fMissing)
7447 return setError(E_INVALIDARG,
7448 tr("Shared folder path '%s' does not exist on the host"),
7449 aData.m_strHostPath.c_str());
7450
7451 return S_OK;
7452}
7453
7454/**
7455 * Calls the HGCM service to remove the shared folder definition.
7456 *
7457 * @param aName Shared folder name.
7458 *
7459 * @note Must be called from under AutoVMCaller and when mpVM != NULL!
7460 * @note Doesn't lock anything.
7461 */
7462HRESULT Console::removeSharedFolder(const Utf8Str &strName)
7463{
7464 ComAssertRet(strName.isNotEmpty(), E_FAIL);
7465
7466 /* sanity checks */
7467 AssertReturn(mpUVM, E_FAIL);
7468 AssertReturn(m_pVMMDev && m_pVMMDev->isShFlActive(), E_FAIL);
7469
7470 VBOXHGCMSVCPARM parms;
7471 SHFLSTRING *pMapName;
7472 size_t cbString;
7473
7474 Log(("Removing shared folder '%s'\n", strName.c_str()));
7475
7476 Bstr bstrName(strName);
7477 cbString = (bstrName.length() + 1) * sizeof(RTUTF16);
7478 if (cbString >= UINT16_MAX)
7479 return setError(E_INVALIDARG, tr("The name is too long"));
7480 pMapName = (SHFLSTRING *) RTMemAllocZ(sizeof(SHFLSTRING) + cbString);
7481 Assert(pMapName);
7482 memcpy(pMapName->String.ucs2, bstrName.raw(), cbString);
7483
7484 pMapName->u16Size = (uint16_t)cbString;
7485 pMapName->u16Length = (uint16_t)cbString - sizeof(RTUTF16);
7486
7487 parms.type = VBOX_HGCM_SVC_PARM_PTR;
7488 parms.u.pointer.addr = pMapName;
7489 parms.u.pointer.size = sizeof(SHFLSTRING) + (uint16_t)cbString;
7490
7491 int vrc = m_pVMMDev->hgcmHostCall("VBoxSharedFolders",
7492 SHFL_FN_REMOVE_MAPPING,
7493 1, &parms);
7494 RTMemFree(pMapName);
7495 if (RT_FAILURE(vrc))
7496 return setError(E_FAIL,
7497 tr("Could not remove the shared folder '%s' (%Rrc)"),
7498 strName.c_str(), vrc);
7499
7500 return S_OK;
7501}
7502
7503/**
7504 * VM state callback function. Called by the VMM
7505 * using its state machine states.
7506 *
7507 * Primarily used to handle VM initiated power off, suspend and state saving,
7508 * but also for doing termination completed work (VMSTATE_TERMINATE).
7509 *
7510 * In general this function is called in the context of the EMT.
7511 *
7512 * @param aVM The VM handle.
7513 * @param aState The new state.
7514 * @param aOldState The old state.
7515 * @param aUser The user argument (pointer to the Console object).
7516 *
7517 * @note Locks the Console object for writing.
7518 */
7519DECLCALLBACK(void) Console::vmstateChangeCallback(PVM aVM,
7520 VMSTATE aState,
7521 VMSTATE aOldState,
7522 void *aUser)
7523{
7524 LogFlowFunc(("Changing state from %s to %s (aVM=%p)\n",
7525 VMR3GetStateName(aOldState), VMR3GetStateName(aState), aVM));
7526
7527 Console *that = static_cast<Console *>(aUser);
7528 AssertReturnVoid(that);
7529
7530 AutoCaller autoCaller(that);
7531
7532 /* Note that we must let this method proceed even if Console::uninit() has
7533 * been already called. In such case this VMSTATE change is a result of:
7534 * 1) powerDown() called from uninit() itself, or
7535 * 2) VM-(guest-)initiated power off. */
7536 AssertReturnVoid( autoCaller.isOk()
7537 || autoCaller.state() == InUninit);
7538
7539 switch (aState)
7540 {
7541 /*
7542 * The VM has terminated
7543 */
7544 case VMSTATE_OFF:
7545 {
7546 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
7547
7548 if (that->mVMStateChangeCallbackDisabled)
7549 break;
7550
7551 /* Do we still think that it is running? It may happen if this is a
7552 * VM-(guest-)initiated shutdown/poweroff.
7553 */
7554 if ( that->mMachineState != MachineState_Stopping
7555 && that->mMachineState != MachineState_Saving
7556 && that->mMachineState != MachineState_Restoring
7557 && that->mMachineState != MachineState_TeleportingIn
7558 && that->mMachineState != MachineState_FaultTolerantSyncing
7559 && that->mMachineState != MachineState_TeleportingPausedVM
7560 && !that->mVMIsAlreadyPoweringOff
7561 )
7562 {
7563 LogFlowFunc(("VM has powered itself off but Console still thinks it is running. Notifying.\n"));
7564
7565 /* prevent powerDown() from calling VMR3PowerOff() again */
7566 Assert(that->mVMPoweredOff == false);
7567 that->mVMPoweredOff = true;
7568
7569 /*
7570 * request a progress object from the server
7571 * (this will set the machine state to Stopping on the server
7572 * to block others from accessing this machine)
7573 */
7574 ComPtr<IProgress> pProgress;
7575 HRESULT rc = that->mControl->BeginPoweringDown(pProgress.asOutParam());
7576 AssertComRC(rc);
7577
7578 /* sync the state with the server */
7579 that->setMachineStateLocally(MachineState_Stopping);
7580
7581 /* Setup task object and thread to carry out the operation
7582 * asynchronously (if we call powerDown() right here but there
7583 * is one or more mpVM callers (added with addVMCaller()) we'll
7584 * deadlock).
7585 */
7586 std::auto_ptr<VMPowerDownTask> task(new VMPowerDownTask(that,
7587 pProgress));
7588
7589 /* If creating a task failed, this can currently mean one of
7590 * two: either Console::uninit() has been called just a ms
7591 * before (so a powerDown() call is already on the way), or
7592 * powerDown() itself is being already executed. Just do
7593 * nothing.
7594 */
7595 if (!task->isOk())
7596 {
7597 LogFlowFunc(("Console is already being uninitialized.\n"));
7598 break;
7599 }
7600
7601 int vrc = RTThreadCreate(NULL, Console::powerDownThread,
7602 (void *) task.get(), 0,
7603 RTTHREADTYPE_MAIN_WORKER, 0,
7604 "VMPwrDwn");
7605 AssertMsgRCBreak(vrc, ("Could not create VMPowerDown thread (%Rrc)\n", vrc));
7606
7607 /* task is now owned by powerDownThread(), so release it */
7608 task.release();
7609 }
7610 break;
7611 }
7612
7613 /* The VM has been completely destroyed.
7614 *
7615 * Note: This state change can happen at two points:
7616 * 1) At the end of VMR3Destroy() if it was not called from EMT.
7617 * 2) At the end of vmR3EmulationThread if VMR3Destroy() was
7618 * called by EMT.
7619 */
7620 case VMSTATE_TERMINATED:
7621 {
7622 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
7623
7624 if (that->mVMStateChangeCallbackDisabled)
7625 break;
7626
7627 /* Terminate host interface networking. If aVM is NULL, we've been
7628 * manually called from powerUpThread() either before calling
7629 * VMR3Create() or after VMR3Create() failed, so no need to touch
7630 * networking.
7631 */
7632 if (aVM)
7633 that->powerDownHostInterfaces();
7634
7635 /* From now on the machine is officially powered down or remains in
7636 * the Saved state.
7637 */
7638 switch (that->mMachineState)
7639 {
7640 default:
7641 AssertFailed();
7642 /* fall through */
7643 case MachineState_Stopping:
7644 /* successfully powered down */
7645 that->setMachineState(MachineState_PoweredOff);
7646 break;
7647 case MachineState_Saving:
7648 /* successfully saved */
7649 that->setMachineState(MachineState_Saved);
7650 break;
7651 case MachineState_Starting:
7652 /* failed to start, but be patient: set back to PoweredOff
7653 * (for similarity with the below) */
7654 that->setMachineState(MachineState_PoweredOff);
7655 break;
7656 case MachineState_Restoring:
7657 /* failed to load the saved state file, but be patient: set
7658 * back to Saved (to preserve the saved state file) */
7659 that->setMachineState(MachineState_Saved);
7660 break;
7661 case MachineState_TeleportingIn:
7662 /* Teleportation failed or was canceled. Back to powered off. */
7663 that->setMachineState(MachineState_PoweredOff);
7664 break;
7665 case MachineState_TeleportingPausedVM:
7666 /* Successfully teleported the VM. */
7667 that->setMachineState(MachineState_Teleported);
7668 break;
7669 case MachineState_FaultTolerantSyncing:
7670 /* Fault tolerant sync failed or was canceled. Back to powered off. */
7671 that->setMachineState(MachineState_PoweredOff);
7672 break;
7673 }
7674 break;
7675 }
7676
7677 case VMSTATE_RESETTING:
7678 {
7679#ifdef VBOX_WITH_GUEST_PROPS
7680 /* Do not take any read/write locks here! */
7681 that->guestPropertiesHandleVMReset();
7682#endif
7683 break;
7684 }
7685
7686 case VMSTATE_SUSPENDED:
7687 {
7688 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
7689
7690 if (that->mVMStateChangeCallbackDisabled)
7691 break;
7692
7693 switch (that->mMachineState)
7694 {
7695 case MachineState_Teleporting:
7696 that->setMachineState(MachineState_TeleportingPausedVM);
7697 break;
7698
7699 case MachineState_LiveSnapshotting:
7700 that->setMachineState(MachineState_Saving);
7701 break;
7702
7703 case MachineState_TeleportingPausedVM:
7704 case MachineState_Saving:
7705 case MachineState_Restoring:
7706 case MachineState_Stopping:
7707 case MachineState_TeleportingIn:
7708 case MachineState_FaultTolerantSyncing:
7709 /* The worker thread handles the transition. */
7710 break;
7711
7712 default:
7713 AssertMsgFailed(("%s\n", Global::stringifyMachineState(that->mMachineState)));
7714 case MachineState_Running:
7715 that->setMachineState(MachineState_Paused);
7716 break;
7717
7718 case MachineState_Paused:
7719 /* Nothing to do. */
7720 break;
7721 }
7722 break;
7723 }
7724
7725 case VMSTATE_SUSPENDED_LS:
7726 case VMSTATE_SUSPENDED_EXT_LS:
7727 {
7728 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
7729 if (that->mVMStateChangeCallbackDisabled)
7730 break;
7731 switch (that->mMachineState)
7732 {
7733 case MachineState_Teleporting:
7734 that->setMachineState(MachineState_TeleportingPausedVM);
7735 break;
7736
7737 case MachineState_LiveSnapshotting:
7738 that->setMachineState(MachineState_Saving);
7739 break;
7740
7741 case MachineState_TeleportingPausedVM:
7742 case MachineState_Saving:
7743 /* ignore */
7744 break;
7745
7746 default:
7747 AssertMsgFailed(("%s/%s -> %s\n", Global::stringifyMachineState(that->mMachineState), VMR3GetStateName(aOldState), VMR3GetStateName(aState) ));
7748 that->setMachineState(MachineState_Paused);
7749 break;
7750 }
7751 break;
7752 }
7753
7754 case VMSTATE_RUNNING:
7755 {
7756 if ( aOldState == VMSTATE_POWERING_ON
7757 || aOldState == VMSTATE_RESUMING
7758 || aOldState == VMSTATE_RUNNING_FT)
7759 {
7760 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
7761
7762 if (that->mVMStateChangeCallbackDisabled)
7763 break;
7764
7765 Assert( ( ( that->mMachineState == MachineState_Starting
7766 || that->mMachineState == MachineState_Paused)
7767 && aOldState == VMSTATE_POWERING_ON)
7768 || ( ( that->mMachineState == MachineState_Restoring
7769 || that->mMachineState == MachineState_TeleportingIn
7770 || that->mMachineState == MachineState_Paused
7771 || that->mMachineState == MachineState_Saving
7772 )
7773 && aOldState == VMSTATE_RESUMING)
7774 || ( that->mMachineState == MachineState_FaultTolerantSyncing
7775 && aOldState == VMSTATE_RUNNING_FT));
7776
7777 that->setMachineState(MachineState_Running);
7778 }
7779
7780 break;
7781 }
7782
7783 case VMSTATE_RUNNING_LS:
7784 AssertMsg( that->mMachineState == MachineState_LiveSnapshotting
7785 || that->mMachineState == MachineState_Teleporting,
7786 ("%s/%s -> %s\n", Global::stringifyMachineState(that->mMachineState), VMR3GetStateName(aOldState), VMR3GetStateName(aState) ));
7787 break;
7788
7789 case VMSTATE_RUNNING_FT:
7790 AssertMsg(that->mMachineState == MachineState_FaultTolerantSyncing,
7791 ("%s/%s -> %s\n", Global::stringifyMachineState(that->mMachineState), VMR3GetStateName(aOldState), VMR3GetStateName(aState) ));
7792 break;
7793
7794 case VMSTATE_FATAL_ERROR:
7795 {
7796 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
7797
7798 if (that->mVMStateChangeCallbackDisabled)
7799 break;
7800
7801 /* Fatal errors are only for running VMs. */
7802 Assert(Global::IsOnline(that->mMachineState));
7803
7804 /* Note! 'Pause' is used here in want of something better. There
7805 * are currently only two places where fatal errors might be
7806 * raised, so it is not worth adding a new externally
7807 * visible state for this yet. */
7808 that->setMachineState(MachineState_Paused);
7809 break;
7810 }
7811
7812 case VMSTATE_GURU_MEDITATION:
7813 {
7814 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
7815
7816 if (that->mVMStateChangeCallbackDisabled)
7817 break;
7818
7819 /* Guru are only for running VMs */
7820 Assert(Global::IsOnline(that->mMachineState));
7821
7822 that->setMachineState(MachineState_Stuck);
7823 break;
7824 }
7825
7826 default: /* shut up gcc */
7827 break;
7828 }
7829}
7830
7831/**
7832 * Changes the clipboard mode.
7833 *
7834 * @param aClipboardMode new clipboard mode.
7835 */
7836void Console::changeClipboardMode(ClipboardMode_T aClipboardMode)
7837{
7838 VMMDev *pVMMDev = m_pVMMDev;
7839 Assert(pVMMDev);
7840
7841 VBOXHGCMSVCPARM parm;
7842 parm.type = VBOX_HGCM_SVC_PARM_32BIT;
7843
7844 switch (aClipboardMode)
7845 {
7846 default:
7847 case ClipboardMode_Disabled:
7848 LogRel(("Shared clipboard mode: Off\n"));
7849 parm.u.uint32 = VBOX_SHARED_CLIPBOARD_MODE_OFF;
7850 break;
7851 case ClipboardMode_GuestToHost:
7852 LogRel(("Shared clipboard mode: Guest to Host\n"));
7853 parm.u.uint32 = VBOX_SHARED_CLIPBOARD_MODE_GUEST_TO_HOST;
7854 break;
7855 case ClipboardMode_HostToGuest:
7856 LogRel(("Shared clipboard mode: Host to Guest\n"));
7857 parm.u.uint32 = VBOX_SHARED_CLIPBOARD_MODE_HOST_TO_GUEST;
7858 break;
7859 case ClipboardMode_Bidirectional:
7860 LogRel(("Shared clipboard mode: Bidirectional\n"));
7861 parm.u.uint32 = VBOX_SHARED_CLIPBOARD_MODE_BIDIRECTIONAL;
7862 break;
7863 }
7864
7865 pVMMDev->hgcmHostCall("VBoxSharedClipboard", VBOX_SHARED_CLIPBOARD_HOST_FN_SET_MODE, 1, &parm);
7866}
7867
7868/**
7869 * Changes the drag'n_drop mode.
7870 *
7871 * @param aDragAndDropMode new drag'n'drop mode.
7872 */
7873void Console::changeDragAndDropMode(DragAndDropMode_T aDragAndDropMode)
7874{
7875 VMMDev *pVMMDev = m_pVMMDev;
7876 Assert(pVMMDev);
7877
7878 VBOXHGCMSVCPARM parm;
7879 parm.type = VBOX_HGCM_SVC_PARM_32BIT;
7880
7881 switch (aDragAndDropMode)
7882 {
7883 default:
7884 case DragAndDropMode_Disabled:
7885 LogRel(("Drag'n'drop mode: Off\n"));
7886 parm.u.uint32 = VBOX_DRAG_AND_DROP_MODE_OFF;
7887 break;
7888 case DragAndDropMode_GuestToHost:
7889 LogRel(("Drag'n'drop mode: Guest to Host\n"));
7890 parm.u.uint32 = VBOX_DRAG_AND_DROP_MODE_GUEST_TO_HOST;
7891 break;
7892 case DragAndDropMode_HostToGuest:
7893 LogRel(("Drag'n'drop mode: Host to Guest\n"));
7894 parm.u.uint32 = VBOX_DRAG_AND_DROP_MODE_HOST_TO_GUEST;
7895 break;
7896 case DragAndDropMode_Bidirectional:
7897 LogRel(("Drag'n'drop mode: Bidirectional\n"));
7898 parm.u.uint32 = VBOX_DRAG_AND_DROP_MODE_BIDIRECTIONAL;
7899 break;
7900 }
7901
7902 pVMMDev->hgcmHostCall("VBoxDragAndDropSvc", DragAndDropSvc::HOST_DND_SET_MODE, 1, &parm);
7903}
7904
7905#ifdef VBOX_WITH_USB
7906/**
7907 * Sends a request to VMM to attach the given host device.
7908 * After this method succeeds, the attached device will appear in the
7909 * mUSBDevices collection.
7910 *
7911 * @param aHostDevice device to attach
7912 *
7913 * @note Synchronously calls EMT.
7914 */
7915HRESULT Console::attachUSBDevice(IUSBDevice *aHostDevice, ULONG aMaskedIfs)
7916{
7917 AssertReturn(aHostDevice, E_FAIL);
7918 AssertReturn(!isWriteLockOnCurrentThread(), E_FAIL);
7919
7920 HRESULT hrc;
7921
7922 /*
7923 * Get the address and the Uuid, and call the pfnCreateProxyDevice roothub
7924 * method in EMT (using usbAttachCallback()).
7925 */
7926 Bstr BstrAddress;
7927 hrc = aHostDevice->COMGETTER(Address)(BstrAddress.asOutParam());
7928 ComAssertComRCRetRC(hrc);
7929
7930 Utf8Str Address(BstrAddress);
7931
7932 Bstr id;
7933 hrc = aHostDevice->COMGETTER(Id)(id.asOutParam());
7934 ComAssertComRCRetRC(hrc);
7935 Guid uuid(id);
7936
7937 BOOL fRemote = FALSE;
7938 hrc = aHostDevice->COMGETTER(Remote)(&fRemote);
7939 ComAssertComRCRetRC(hrc);
7940
7941 /* Get the VM handle. */
7942 SafeVMPtr ptrVM(this);
7943 if (!ptrVM.isOk())
7944 return ptrVM.rc();
7945
7946 LogFlowThisFunc(("Proxying USB device '%s' {%RTuuid}...\n",
7947 Address.c_str(), uuid.raw()));
7948
7949 void *pvRemoteBackend = NULL;
7950 if (fRemote)
7951 {
7952 RemoteUSBDevice *pRemoteUSBDevice = static_cast<RemoteUSBDevice *>(aHostDevice);
7953 pvRemoteBackend = consoleVRDPServer()->USBBackendRequestPointer(pRemoteUSBDevice->clientId(), &uuid);
7954 if (!pvRemoteBackend)
7955 return E_INVALIDARG; /* The clientId is invalid then. */
7956 }
7957
7958 USHORT portVersion = 1;
7959 hrc = aHostDevice->COMGETTER(PortVersion)(&portVersion);
7960 AssertComRCReturnRC(hrc);
7961 Assert(portVersion == 1 || portVersion == 2);
7962
7963 int vrc = VMR3ReqCallWaitU(ptrVM.rawUVM(), 0 /* idDstCpu (saved state, see #6232) */,
7964 (PFNRT)usbAttachCallback, 9,
7965 this, ptrVM.rawUVM(), aHostDevice, uuid.raw(), fRemote,
7966 Address.c_str(), pvRemoteBackend, portVersion, aMaskedIfs);
7967
7968 if (RT_SUCCESS(vrc))
7969 {
7970 /* Create a OUSBDevice and add it to the device list */
7971 ComObjPtr<OUSBDevice> pUSBDevice;
7972 pUSBDevice.createObject();
7973 hrc = pUSBDevice->init(aHostDevice);
7974 AssertComRC(hrc);
7975
7976 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7977 mUSBDevices.push_back(pUSBDevice);
7978 LogFlowFunc(("Attached device {%RTuuid}\n", pUSBDevice->id().raw()));
7979
7980 /* notify callbacks */
7981 alock.release();
7982 onUSBDeviceStateChange(pUSBDevice, true /* aAttached */, NULL);
7983 }
7984 else
7985 {
7986 LogWarningThisFunc(("Failed to create proxy device for '%s' {%RTuuid} (%Rrc)\n",
7987 Address.c_str(), uuid.raw(), vrc));
7988
7989 switch (vrc)
7990 {
7991 case VERR_VUSB_NO_PORTS:
7992 hrc = setError(E_FAIL, tr("Failed to attach the USB device. (No available ports on the USB controller)."));
7993 break;
7994 case VERR_VUSB_USBFS_PERMISSION:
7995 hrc = setError(E_FAIL, tr("Not permitted to open the USB device, check usbfs options"));
7996 break;
7997 default:
7998 hrc = setError(E_FAIL, tr("Failed to create a proxy device for the USB device. (Error: %Rrc)"), vrc);
7999 break;
8000 }
8001 }
8002
8003 return hrc;
8004}
8005
8006/**
8007 * USB device attach callback used by AttachUSBDevice().
8008 * Note that AttachUSBDevice() doesn't return until this callback is executed,
8009 * so we don't use AutoCaller and don't care about reference counters of
8010 * interface pointers passed in.
8011 *
8012 * @thread EMT
8013 * @note Locks the console object for writing.
8014 */
8015//static
8016DECLCALLBACK(int)
8017Console::usbAttachCallback(Console *that, PUVM pUVM, IUSBDevice *aHostDevice, PCRTUUID aUuid, bool aRemote,
8018 const char *aAddress, void *pvRemoteBackend, USHORT aPortVersion, ULONG aMaskedIfs)
8019{
8020 LogFlowFuncEnter();
8021 LogFlowFunc(("that={%p} aUuid={%RTuuid}\n", that, aUuid));
8022
8023 AssertReturn(that && aUuid, VERR_INVALID_PARAMETER);
8024 AssertReturn(!that->isWriteLockOnCurrentThread(), VERR_GENERAL_FAILURE);
8025
8026 int vrc = PDMR3USBCreateProxyDevice(VMR3GetVM(pUVM), aUuid, aRemote, aAddress, pvRemoteBackend,
8027 aPortVersion == 1 ? VUSB_STDVER_11 : VUSB_STDVER_20, aMaskedIfs);
8028 LogFlowFunc(("vrc=%Rrc\n", vrc));
8029 LogFlowFuncLeave();
8030 return vrc;
8031}
8032
8033/**
8034 * Sends a request to VMM to detach the given host device. After this method
8035 * succeeds, the detached device will disappear from the mUSBDevices
8036 * collection.
8037 *
8038 * @param aHostDevice device to attach
8039 *
8040 * @note Synchronously calls EMT.
8041 */
8042HRESULT Console::detachUSBDevice(const ComObjPtr<OUSBDevice> &aHostDevice)
8043{
8044 AssertReturn(!isWriteLockOnCurrentThread(), E_FAIL);
8045
8046 /* Get the VM handle. */
8047 SafeVMPtr ptrVM(this);
8048 if (!ptrVM.isOk())
8049 return ptrVM.rc();
8050
8051 /* if the device is attached, then there must at least one USB hub. */
8052 AssertReturn(PDMR3USBHasHub(ptrVM), E_FAIL);
8053
8054 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8055 LogFlowThisFunc(("Detaching USB proxy device {%RTuuid}...\n",
8056 aHostDevice->id().raw()));
8057
8058 /*
8059 * If this was a remote device, release the backend pointer.
8060 * The pointer was requested in usbAttachCallback.
8061 */
8062 BOOL fRemote = FALSE;
8063
8064 HRESULT hrc2 = aHostDevice->COMGETTER(Remote)(&fRemote);
8065 if (FAILED(hrc2))
8066 setErrorStatic(hrc2, "GetRemote() failed");
8067
8068 PCRTUUID pUuid = aHostDevice->id().raw();
8069 if (fRemote)
8070 {
8071 Guid guid(*pUuid);
8072 consoleVRDPServer()->USBBackendReleasePointer(&guid);
8073 }
8074
8075 alock.release();
8076 int vrc = VMR3ReqCallWaitU(ptrVM.rawUVM(), 0 /* idDstCpu (saved state, see #6232) */,
8077 (PFNRT)usbDetachCallback, 5,
8078 this, ptrVM.rawUVM(), pUuid);
8079 if (RT_SUCCESS(vrc))
8080 {
8081 LogFlowFunc(("Detached device {%RTuuid}\n", pUuid));
8082
8083 /* notify callbacks */
8084 onUSBDeviceStateChange(aHostDevice, false /* aAttached */, NULL);
8085 }
8086
8087 ComAssertRCRet(vrc, E_FAIL);
8088
8089 return S_OK;
8090}
8091
8092/**
8093 * USB device detach callback used by DetachUSBDevice().
8094 *
8095 * Note that DetachUSBDevice() doesn't return until this callback is executed,
8096 * so we don't use AutoCaller and don't care about reference counters of
8097 * interface pointers passed in.
8098 *
8099 * @thread EMT
8100 */
8101//static
8102DECLCALLBACK(int)
8103Console::usbDetachCallback(Console *that, PUVM pUVM, PCRTUUID aUuid)
8104{
8105 LogFlowFuncEnter();
8106 LogFlowFunc(("that={%p} aUuid={%RTuuid}\n", that, aUuid));
8107
8108 AssertReturn(that && aUuid, VERR_INVALID_PARAMETER);
8109 AssertReturn(!that->isWriteLockOnCurrentThread(), VERR_GENERAL_FAILURE);
8110
8111 int vrc = PDMR3USBDetachDevice(VMR3GetVM(pUVM), aUuid);
8112
8113 LogFlowFunc(("vrc=%Rrc\n", vrc));
8114 LogFlowFuncLeave();
8115 return vrc;
8116}
8117#endif /* VBOX_WITH_USB */
8118
8119/* Note: FreeBSD needs this whether netflt is used or not. */
8120#if ((defined(RT_OS_LINUX) && !defined(VBOX_WITH_NETFLT)) || defined(RT_OS_FREEBSD))
8121/**
8122 * Helper function to handle host interface device creation and attachment.
8123 *
8124 * @param networkAdapter the network adapter which attachment should be reset
8125 * @return COM status code
8126 *
8127 * @note The caller must lock this object for writing.
8128 *
8129 * @todo Move this back into the driver!
8130 */
8131HRESULT Console::attachToTapInterface(INetworkAdapter *networkAdapter)
8132{
8133 LogFlowThisFunc(("\n"));
8134 /* sanity check */
8135 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
8136
8137# ifdef VBOX_STRICT
8138 /* paranoia */
8139 NetworkAttachmentType_T attachment;
8140 networkAdapter->COMGETTER(AttachmentType)(&attachment);
8141 Assert(attachment == NetworkAttachmentType_Bridged);
8142# endif /* VBOX_STRICT */
8143
8144 HRESULT rc = S_OK;
8145
8146 ULONG slot = 0;
8147 rc = networkAdapter->COMGETTER(Slot)(&slot);
8148 AssertComRC(rc);
8149
8150# ifdef RT_OS_LINUX
8151 /*
8152 * Allocate a host interface device
8153 */
8154 int rcVBox = RTFileOpen(&maTapFD[slot], "/dev/net/tun",
8155 RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_INHERIT);
8156 if (RT_SUCCESS(rcVBox))
8157 {
8158 /*
8159 * Set/obtain the tap interface.
8160 */
8161 struct ifreq IfReq;
8162 memset(&IfReq, 0, sizeof(IfReq));
8163 /* The name of the TAP interface we are using */
8164 Bstr tapDeviceName;
8165 rc = networkAdapter->COMGETTER(BridgedInterface)(tapDeviceName.asOutParam());
8166 if (FAILED(rc))
8167 tapDeviceName.setNull(); /* Is this necessary? */
8168 if (tapDeviceName.isEmpty())
8169 {
8170 LogRel(("No TAP device name was supplied.\n"));
8171 rc = setError(E_FAIL, tr("No TAP device name was supplied for the host networking interface"));
8172 }
8173
8174 if (SUCCEEDED(rc))
8175 {
8176 /* If we are using a static TAP device then try to open it. */
8177 Utf8Str str(tapDeviceName);
8178 if (str.length() <= sizeof(IfReq.ifr_name))
8179 strcpy(IfReq.ifr_name, str.c_str());
8180 else
8181 memcpy(IfReq.ifr_name, str.c_str(), sizeof(IfReq.ifr_name) - 1); /** @todo bitch about names which are too long... */
8182 IfReq.ifr_flags = IFF_TAP | IFF_NO_PI;
8183 rcVBox = ioctl(maTapFD[slot], TUNSETIFF, &IfReq);
8184 if (rcVBox != 0)
8185 {
8186 LogRel(("Failed to open the host network interface %ls\n", tapDeviceName.raw()));
8187 rc = setError(E_FAIL,
8188 tr("Failed to open the host network interface %ls"),
8189 tapDeviceName.raw());
8190 }
8191 }
8192 if (SUCCEEDED(rc))
8193 {
8194 /*
8195 * Make it pollable.
8196 */
8197 if (fcntl(maTapFD[slot], F_SETFL, O_NONBLOCK) != -1)
8198 {
8199 Log(("attachToTapInterface: %RTfile %ls\n", maTapFD[slot], tapDeviceName.raw()));
8200 /*
8201 * Here is the right place to communicate the TAP file descriptor and
8202 * the host interface name to the server if/when it becomes really
8203 * necessary.
8204 */
8205 maTAPDeviceName[slot] = tapDeviceName;
8206 rcVBox = VINF_SUCCESS;
8207 }
8208 else
8209 {
8210 int iErr = errno;
8211
8212 LogRel(("Configuration error: Failed to configure /dev/net/tun non blocking. Error: %s\n", strerror(iErr)));
8213 rcVBox = VERR_HOSTIF_BLOCKING;
8214 rc = setError(E_FAIL,
8215 tr("could not set up the host networking device for non blocking access: %s"),
8216 strerror(errno));
8217 }
8218 }
8219 }
8220 else
8221 {
8222 LogRel(("Configuration error: Failed to open /dev/net/tun rc=%Rrc\n", rcVBox));
8223 switch (rcVBox)
8224 {
8225 case VERR_ACCESS_DENIED:
8226 /* will be handled by our caller */
8227 rc = rcVBox;
8228 break;
8229 default:
8230 rc = setError(E_FAIL,
8231 tr("Could not set up the host networking device: %Rrc"),
8232 rcVBox);
8233 break;
8234 }
8235 }
8236
8237# elif defined(RT_OS_FREEBSD)
8238 /*
8239 * Set/obtain the tap interface.
8240 */
8241 /* The name of the TAP interface we are using */
8242 Bstr tapDeviceName;
8243 rc = networkAdapter->COMGETTER(BridgedInterface)(tapDeviceName.asOutParam());
8244 if (FAILED(rc))
8245 tapDeviceName.setNull(); /* Is this necessary? */
8246 if (tapDeviceName.isEmpty())
8247 {
8248 LogRel(("No TAP device name was supplied.\n"));
8249 rc = setError(E_FAIL, tr("No TAP device name was supplied for the host networking interface"));
8250 }
8251 char szTapdev[1024] = "/dev/";
8252 /* If we are using a static TAP device then try to open it. */
8253 Utf8Str str(tapDeviceName);
8254 if (str.length() + strlen(szTapdev) <= sizeof(szTapdev))
8255 strcat(szTapdev, str.c_str());
8256 else
8257 memcpy(szTapdev + strlen(szTapdev), str.c_str(),
8258 sizeof(szTapdev) - strlen(szTapdev) - 1); /** @todo bitch about names which are too long... */
8259 int rcVBox = RTFileOpen(&maTapFD[slot], szTapdev,
8260 RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_INHERIT | RTFILE_O_NON_BLOCK);
8261
8262 if (RT_SUCCESS(rcVBox))
8263 maTAPDeviceName[slot] = tapDeviceName;
8264 else
8265 {
8266 switch (rcVBox)
8267 {
8268 case VERR_ACCESS_DENIED:
8269 /* will be handled by our caller */
8270 rc = rcVBox;
8271 break;
8272 default:
8273 rc = setError(E_FAIL,
8274 tr("Failed to open the host network interface %ls"),
8275 tapDeviceName.raw());
8276 break;
8277 }
8278 }
8279# else
8280# error "huh?"
8281# endif
8282 /* in case of failure, cleanup. */
8283 if (RT_FAILURE(rcVBox) && SUCCEEDED(rc))
8284 {
8285 LogRel(("General failure attaching to host interface\n"));
8286 rc = setError(E_FAIL,
8287 tr("General failure attaching to host interface"));
8288 }
8289 LogFlowThisFunc(("rc=%d\n", rc));
8290 return rc;
8291}
8292
8293
8294/**
8295 * Helper function to handle detachment from a host interface
8296 *
8297 * @param networkAdapter the network adapter which attachment should be reset
8298 * @return COM status code
8299 *
8300 * @note The caller must lock this object for writing.
8301 *
8302 * @todo Move this back into the driver!
8303 */
8304HRESULT Console::detachFromTapInterface(INetworkAdapter *networkAdapter)
8305{
8306 /* sanity check */
8307 LogFlowThisFunc(("\n"));
8308 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
8309
8310 HRESULT rc = S_OK;
8311# ifdef VBOX_STRICT
8312 /* paranoia */
8313 NetworkAttachmentType_T attachment;
8314 networkAdapter->COMGETTER(AttachmentType)(&attachment);
8315 Assert(attachment == NetworkAttachmentType_Bridged);
8316# endif /* VBOX_STRICT */
8317
8318 ULONG slot = 0;
8319 rc = networkAdapter->COMGETTER(Slot)(&slot);
8320 AssertComRC(rc);
8321
8322 /* is there an open TAP device? */
8323 if (maTapFD[slot] != NIL_RTFILE)
8324 {
8325 /*
8326 * Close the file handle.
8327 */
8328 Bstr tapDeviceName, tapTerminateApplication;
8329 bool isStatic = true;
8330 rc = networkAdapter->COMGETTER(BridgedInterface)(tapDeviceName.asOutParam());
8331 if (FAILED(rc) || tapDeviceName.isEmpty())
8332 {
8333 /* If the name is empty, this is a dynamic TAP device, so close it now,
8334 so that the termination script can remove the interface. Otherwise we still
8335 need the FD to pass to the termination script. */
8336 isStatic = false;
8337 int rcVBox = RTFileClose(maTapFD[slot]);
8338 AssertRC(rcVBox);
8339 maTapFD[slot] = NIL_RTFILE;
8340 }
8341 if (isStatic)
8342 {
8343 /* If we are using a static TAP device, we close it now, after having called the
8344 termination script. */
8345 int rcVBox = RTFileClose(maTapFD[slot]);
8346 AssertRC(rcVBox);
8347 }
8348 /* the TAP device name and handle are no longer valid */
8349 maTapFD[slot] = NIL_RTFILE;
8350 maTAPDeviceName[slot] = "";
8351 }
8352 LogFlowThisFunc(("returning %d\n", rc));
8353 return rc;
8354}
8355#endif /* (RT_OS_LINUX || RT_OS_FREEBSD) && !VBOX_WITH_NETFLT */
8356
8357/**
8358 * Called at power down to terminate host interface networking.
8359 *
8360 * @note The caller must lock this object for writing.
8361 */
8362HRESULT Console::powerDownHostInterfaces()
8363{
8364 LogFlowThisFunc(("\n"));
8365
8366 /* sanity check */
8367 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
8368
8369 /*
8370 * host interface termination handling
8371 */
8372 HRESULT rc = S_OK;
8373 ComPtr<IVirtualBox> pVirtualBox;
8374 mMachine->COMGETTER(Parent)(pVirtualBox.asOutParam());
8375 ComPtr<ISystemProperties> pSystemProperties;
8376 if (pVirtualBox)
8377 pVirtualBox->COMGETTER(SystemProperties)(pSystemProperties.asOutParam());
8378 ChipsetType_T chipsetType = ChipsetType_PIIX3;
8379 mMachine->COMGETTER(ChipsetType)(&chipsetType);
8380 ULONG maxNetworkAdapters = 0;
8381 if (pSystemProperties)
8382 pSystemProperties->GetMaxNetworkAdapters(chipsetType, &maxNetworkAdapters);
8383
8384 for (ULONG slot = 0; slot < maxNetworkAdapters; slot++)
8385 {
8386 ComPtr<INetworkAdapter> pNetworkAdapter;
8387 rc = mMachine->GetNetworkAdapter(slot, pNetworkAdapter.asOutParam());
8388 if (FAILED(rc)) break;
8389
8390 BOOL enabled = FALSE;
8391 pNetworkAdapter->COMGETTER(Enabled)(&enabled);
8392 if (!enabled)
8393 continue;
8394
8395 NetworkAttachmentType_T attachment;
8396 pNetworkAdapter->COMGETTER(AttachmentType)(&attachment);
8397 if (attachment == NetworkAttachmentType_Bridged)
8398 {
8399#if ((defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD)) && !defined(VBOX_WITH_NETFLT))
8400 HRESULT rc2 = detachFromTapInterface(pNetworkAdapter);
8401 if (FAILED(rc2) && SUCCEEDED(rc))
8402 rc = rc2;
8403#endif /* (RT_OS_LINUX || RT_OS_FREEBSD) && !VBOX_WITH_NETFLT */
8404 }
8405 }
8406
8407 return rc;
8408}
8409
8410
8411/**
8412 * Process callback handler for VMR3LoadFromFile, VMR3LoadFromStream, VMR3Save
8413 * and VMR3Teleport.
8414 *
8415 * @param pVM The VM handle.
8416 * @param uPercent Completion percentage (0-100).
8417 * @param pvUser Pointer to an IProgress instance.
8418 * @return VINF_SUCCESS.
8419 */
8420/*static*/
8421DECLCALLBACK(int) Console::stateProgressCallback(PVM pVM, unsigned uPercent, void *pvUser)
8422{
8423 IProgress *pProgress = static_cast<IProgress *>(pvUser);
8424
8425 /* update the progress object */
8426 if (pProgress)
8427 pProgress->SetCurrentOperationProgress(uPercent);
8428
8429 return VINF_SUCCESS;
8430}
8431
8432/**
8433 * @copydoc FNVMATERROR
8434 *
8435 * @remarks Might be some tiny serialization concerns with access to the string
8436 * object here...
8437 */
8438/*static*/ DECLCALLBACK(void)
8439Console::genericVMSetErrorCallback(PVM pVM, void *pvUser, int rc, RT_SRC_POS_DECL,
8440 const char *pszErrorFmt, va_list va)
8441{
8442 Utf8Str *pErrorText = (Utf8Str *)pvUser;
8443 AssertPtr(pErrorText);
8444
8445 /* We ignore RT_SRC_POS_DECL arguments to avoid confusion of end-users. */
8446 va_list va2;
8447 va_copy(va2, va);
8448
8449 /* Append to any the existing error message. */
8450 if (pErrorText->length())
8451 *pErrorText = Utf8StrFmt("%s.\n%N (%Rrc)", pErrorText->c_str(),
8452 pszErrorFmt, &va2, rc, rc);
8453 else
8454 *pErrorText = Utf8StrFmt("%N (%Rrc)", pszErrorFmt, &va2, rc, rc);
8455
8456 va_end(va2);
8457}
8458
8459/**
8460 * VM runtime error callback function.
8461 * See VMSetRuntimeError for the detailed description of parameters.
8462 *
8463 * @param pVM The VM handle.
8464 * @param pvUser The user argument.
8465 * @param fFlags The action flags. See VMSETRTERR_FLAGS_*.
8466 * @param pszErrorId Error ID string.
8467 * @param pszFormat Error message format string.
8468 * @param va Error message arguments.
8469 * @thread EMT.
8470 */
8471/* static */ DECLCALLBACK(void)
8472Console::setVMRuntimeErrorCallback(PVM pVM, void *pvUser, uint32_t fFlags,
8473 const char *pszErrorId,
8474 const char *pszFormat, va_list va)
8475{
8476 bool const fFatal = !!(fFlags & VMSETRTERR_FLAGS_FATAL);
8477 LogFlowFuncEnter();
8478
8479 Console *that = static_cast<Console *>(pvUser);
8480 AssertReturnVoid(that);
8481
8482 Utf8Str message(pszFormat, va);
8483
8484 LogRel(("Console: VM runtime error: fatal=%RTbool, errorID=%s message=\"%s\"\n",
8485 fFatal, pszErrorId, message.c_str()));
8486
8487 that->onRuntimeError(BOOL(fFatal), Bstr(pszErrorId).raw(),
8488 Bstr(message).raw());
8489
8490 LogFlowFuncLeave();
8491}
8492
8493/**
8494 * Captures USB devices that match filters of the VM.
8495 * Called at VM startup.
8496 *
8497 * @param pUVM The VM handle.
8498 */
8499HRESULT Console::captureUSBDevices(PUVM pUVM)
8500{
8501 LogFlowThisFunc(("\n"));
8502
8503 /* sanity check */
8504 AssertReturn(!isWriteLockOnCurrentThread(), E_FAIL);
8505 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8506
8507 /* If the machine has an USB controller, ask the USB proxy service to
8508 * capture devices */
8509 PPDMIBASE pBase;
8510 int vrc = PDMR3QueryLun(VMR3GetVM(pUVM), "usb-ohci", 0, 0, &pBase);
8511 if (RT_SUCCESS(vrc))
8512 {
8513 /* release the lock before calling Host in VBoxSVC since Host may call
8514 * us back from under its lock (e.g. onUSBDeviceAttach()) which would
8515 * produce an inter-process dead-lock otherwise. */
8516 alock.release();
8517
8518 HRESULT hrc = mControl->AutoCaptureUSBDevices();
8519 ComAssertComRCRetRC(hrc);
8520 }
8521 else if ( vrc == VERR_PDM_DEVICE_NOT_FOUND
8522 || vrc == VERR_PDM_DEVICE_INSTANCE_NOT_FOUND)
8523 vrc = VINF_SUCCESS;
8524 else
8525 AssertRC(vrc);
8526
8527 return RT_SUCCESS(vrc) ? S_OK : E_FAIL;
8528}
8529
8530
8531/**
8532 * Detach all USB device which are attached to the VM for the
8533 * purpose of clean up and such like.
8534 */
8535void Console::detachAllUSBDevices(bool aDone)
8536{
8537 LogFlowThisFunc(("aDone=%RTbool\n", aDone));
8538
8539 /* sanity check */
8540 AssertReturnVoid(!isWriteLockOnCurrentThread());
8541 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8542
8543 mUSBDevices.clear();
8544
8545 /* release the lock before calling Host in VBoxSVC since Host may call
8546 * us back from under its lock (e.g. onUSBDeviceAttach()) which would
8547 * produce an inter-process dead-lock otherwise. */
8548 alock.release();
8549
8550 mControl->DetachAllUSBDevices(aDone);
8551}
8552
8553/**
8554 * @note Locks this object for writing.
8555 */
8556void Console::processRemoteUSBDevices(uint32_t u32ClientId, VRDEUSBDEVICEDESC *pDevList, uint32_t cbDevList, bool fDescExt)
8557{
8558 LogFlowThisFuncEnter();
8559 LogFlowThisFunc(("u32ClientId = %d, pDevList=%p, cbDevList = %d, fDescExt = %d\n", u32ClientId, pDevList, cbDevList, fDescExt));
8560
8561 AutoCaller autoCaller(this);
8562 if (!autoCaller.isOk())
8563 {
8564 /* Console has been already uninitialized, deny request */
8565 AssertMsgFailed(("Console is already uninitialized\n"));
8566 LogFlowThisFunc(("Console is already uninitialized\n"));
8567 LogFlowThisFuncLeave();
8568 return;
8569 }
8570
8571 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8572
8573 /*
8574 * Mark all existing remote USB devices as dirty.
8575 */
8576 for (RemoteUSBDeviceList::iterator it = mRemoteUSBDevices.begin();
8577 it != mRemoteUSBDevices.end();
8578 ++it)
8579 {
8580 (*it)->dirty(true);
8581 }
8582
8583 /*
8584 * Process the pDevList and add devices those are not already in the mRemoteUSBDevices list.
8585 */
8586 /** @todo (sunlover) REMOTE_USB Strict validation of the pDevList. */
8587 VRDEUSBDEVICEDESC *e = pDevList;
8588
8589 /* The cbDevList condition must be checked first, because the function can
8590 * receive pDevList = NULL and cbDevList = 0 on client disconnect.
8591 */
8592 while (cbDevList >= 2 && e->oNext)
8593 {
8594 /* Sanitize incoming strings in case they aren't valid UTF-8. */
8595 if (e->oManufacturer)
8596 RTStrPurgeEncoding((char *)e + e->oManufacturer);
8597 if (e->oProduct)
8598 RTStrPurgeEncoding((char *)e + e->oProduct);
8599 if (e->oSerialNumber)
8600 RTStrPurgeEncoding((char *)e + e->oSerialNumber);
8601
8602 LogFlowThisFunc(("vendor %04X, product %04X, name = %s\n",
8603 e->idVendor, e->idProduct,
8604 e->oProduct? (char *)e + e->oProduct: ""));
8605
8606 bool fNewDevice = true;
8607
8608 for (RemoteUSBDeviceList::iterator it = mRemoteUSBDevices.begin();
8609 it != mRemoteUSBDevices.end();
8610 ++it)
8611 {
8612 if ((*it)->devId() == e->id
8613 && (*it)->clientId() == u32ClientId)
8614 {
8615 /* The device is already in the list. */
8616 (*it)->dirty(false);
8617 fNewDevice = false;
8618 break;
8619 }
8620 }
8621
8622 if (fNewDevice)
8623 {
8624 LogRel(("Remote USB: ++++ Vendor %04X. Product %04X. Name = [%s].\n",
8625 e->idVendor, e->idProduct, e->oProduct? (char *)e + e->oProduct: ""));
8626
8627 /* Create the device object and add the new device to list. */
8628 ComObjPtr<RemoteUSBDevice> pUSBDevice;
8629 pUSBDevice.createObject();
8630 pUSBDevice->init(u32ClientId, e, fDescExt);
8631
8632 mRemoteUSBDevices.push_back(pUSBDevice);
8633
8634 /* Check if the device is ok for current USB filters. */
8635 BOOL fMatched = FALSE;
8636 ULONG fMaskedIfs = 0;
8637
8638 HRESULT hrc = mControl->RunUSBDeviceFilters(pUSBDevice, &fMatched, &fMaskedIfs);
8639
8640 AssertComRC(hrc);
8641
8642 LogFlowThisFunc(("USB filters return %d %#x\n", fMatched, fMaskedIfs));
8643
8644 if (fMatched)
8645 {
8646 alock.release();
8647 hrc = onUSBDeviceAttach(pUSBDevice, NULL, fMaskedIfs);
8648 alock.acquire();
8649
8650 /// @todo (r=dmik) warning reporting subsystem
8651
8652 if (hrc == S_OK)
8653 {
8654 LogFlowThisFunc(("Device attached\n"));
8655 pUSBDevice->captured(true);
8656 }
8657 }
8658 }
8659
8660 if (cbDevList < e->oNext)
8661 {
8662 LogWarningThisFunc(("cbDevList %d > oNext %d\n",
8663 cbDevList, e->oNext));
8664 break;
8665 }
8666
8667 cbDevList -= e->oNext;
8668
8669 e = (VRDEUSBDEVICEDESC *)((uint8_t *)e + e->oNext);
8670 }
8671
8672 /*
8673 * Remove dirty devices, that is those which are not reported by the server anymore.
8674 */
8675 for (;;)
8676 {
8677 ComObjPtr<RemoteUSBDevice> pUSBDevice;
8678
8679 RemoteUSBDeviceList::iterator it = mRemoteUSBDevices.begin();
8680 while (it != mRemoteUSBDevices.end())
8681 {
8682 if ((*it)->dirty())
8683 {
8684 pUSBDevice = *it;
8685 break;
8686 }
8687
8688 ++it;
8689 }
8690
8691 if (!pUSBDevice)
8692 {
8693 break;
8694 }
8695
8696 USHORT vendorId = 0;
8697 pUSBDevice->COMGETTER(VendorId)(&vendorId);
8698
8699 USHORT productId = 0;
8700 pUSBDevice->COMGETTER(ProductId)(&productId);
8701
8702 Bstr product;
8703 pUSBDevice->COMGETTER(Product)(product.asOutParam());
8704
8705 LogRel(("Remote USB: ---- Vendor %04X. Product %04X. Name = [%ls].\n",
8706 vendorId, productId, product.raw()));
8707
8708 /* Detach the device from VM. */
8709 if (pUSBDevice->captured())
8710 {
8711 Bstr uuid;
8712 pUSBDevice->COMGETTER(Id)(uuid.asOutParam());
8713 alock.release();
8714 onUSBDeviceDetach(uuid.raw(), NULL);
8715 alock.acquire();
8716 }
8717
8718 /* And remove it from the list. */
8719 mRemoteUSBDevices.erase(it);
8720 }
8721
8722 LogFlowThisFuncLeave();
8723}
8724
8725/**
8726 * Progress cancelation callback for fault tolerance VM poweron
8727 */
8728static void faultToleranceProgressCancelCallback(void *pvUser)
8729{
8730 PVM pVM = (PVM)pvUser;
8731
8732 if (pVM)
8733 FTMR3CancelStandby(pVM);
8734}
8735
8736/**
8737 * Thread function which starts the VM (also from saved state) and
8738 * track progress.
8739 *
8740 * @param Thread The thread id.
8741 * @param pvUser Pointer to a VMPowerUpTask structure.
8742 * @return VINF_SUCCESS (ignored).
8743 *
8744 * @note Locks the Console object for writing.
8745 */
8746/*static*/
8747DECLCALLBACK(int) Console::powerUpThread(RTTHREAD Thread, void *pvUser)
8748{
8749 LogFlowFuncEnter();
8750
8751 std::auto_ptr<VMPowerUpTask> task(static_cast<VMPowerUpTask *>(pvUser));
8752 AssertReturn(task.get(), VERR_INVALID_PARAMETER);
8753
8754 AssertReturn(!task->mConsole.isNull(), VERR_INVALID_PARAMETER);
8755 AssertReturn(!task->mProgress.isNull(), VERR_INVALID_PARAMETER);
8756
8757 VirtualBoxBase::initializeComForThread();
8758
8759 HRESULT rc = S_OK;
8760 int vrc = VINF_SUCCESS;
8761
8762 /* Set up a build identifier so that it can be seen from core dumps what
8763 * exact build was used to produce the core. */
8764 static char saBuildID[40];
8765 RTStrPrintf(saBuildID, sizeof(saBuildID), "%s%s%s%s VirtualBox %s r%u %s%s%s%s",
8766 "BU", "IL", "DI", "D", RTBldCfgVersion(), RTBldCfgRevision(), "BU", "IL", "DI", "D");
8767
8768 ComObjPtr<Console> pConsole = task->mConsole;
8769
8770 /* Note: no need to use addCaller() because VMPowerUpTask does that */
8771
8772 /* The lock is also used as a signal from the task initiator (which
8773 * releases it only after RTThreadCreate()) that we can start the job */
8774 AutoWriteLock alock(pConsole COMMA_LOCKVAL_SRC_POS);
8775
8776 /* sanity */
8777 Assert(pConsole->mpUVM == NULL);
8778
8779 try
8780 {
8781 // Create the VMM device object, which starts the HGCM thread; do this only
8782 // once for the console, for the pathological case that the same console
8783 // object is used to power up a VM twice. VirtualBox 4.0: we now do that
8784 // here instead of the Console constructor (see Console::init())
8785 if (!pConsole->m_pVMMDev)
8786 {
8787 pConsole->m_pVMMDev = new VMMDev(pConsole);
8788 AssertReturn(pConsole->m_pVMMDev, E_FAIL);
8789 }
8790
8791 /* wait for auto reset ops to complete so that we can successfully lock
8792 * the attached hard disks by calling LockMedia() below */
8793 for (VMPowerUpTask::ProgressList::const_iterator
8794 it = task->hardDiskProgresses.begin();
8795 it != task->hardDiskProgresses.end(); ++it)
8796 {
8797 HRESULT rc2 = (*it)->WaitForCompletion(-1);
8798 AssertComRC(rc2);
8799
8800 rc = task->mProgress->SetNextOperation(BstrFmt(tr("Disk Image Reset Operation - Immutable Image")).raw(), 1);
8801 AssertComRCReturnRC(rc);
8802 }
8803
8804 /*
8805 * Lock attached media. This method will also check their accessibility.
8806 * If we're a teleporter, we'll have to postpone this action so we can
8807 * migrate between local processes.
8808 *
8809 * Note! The media will be unlocked automatically by
8810 * SessionMachine::setMachineState() when the VM is powered down.
8811 */
8812 if ( !task->mTeleporterEnabled
8813 && task->mEnmFaultToleranceState != FaultToleranceState_Standby)
8814 {
8815 rc = pConsole->mControl->LockMedia();
8816 if (FAILED(rc)) throw rc;
8817 }
8818
8819 /* Create the VRDP server. In case of headless operation, this will
8820 * also create the framebuffer, required at VM creation.
8821 */
8822 ConsoleVRDPServer *server = pConsole->consoleVRDPServer();
8823 Assert(server);
8824
8825 /* Does VRDP server call Console from the other thread?
8826 * Not sure (and can change), so release the lock just in case.
8827 */
8828 alock.release();
8829 vrc = server->Launch();
8830 alock.acquire();
8831
8832 if (vrc == VERR_NET_ADDRESS_IN_USE)
8833 {
8834 Utf8Str errMsg;
8835 Bstr bstr;
8836 pConsole->mVRDEServer->GetVRDEProperty(Bstr("TCP/Ports").raw(), bstr.asOutParam());
8837 Utf8Str ports = bstr;
8838 errMsg = Utf8StrFmt(tr("VirtualBox Remote Desktop Extension server can't bind to the port: %s"),
8839 ports.c_str());
8840 LogRel(("VRDE: Warning: failed to launch VRDE server (%Rrc): '%s'\n",
8841 vrc, errMsg.c_str()));
8842 }
8843 else if (vrc == VINF_NOT_SUPPORTED)
8844 {
8845 /* This means that the VRDE is not installed. */
8846 LogRel(("VRDE: VirtualBox Remote Desktop Extension is not available.\n"));
8847 }
8848 else if (RT_FAILURE(vrc))
8849 {
8850 /* Fail, if the server is installed but can't start. */
8851 Utf8Str errMsg;
8852 switch (vrc)
8853 {
8854 case VERR_FILE_NOT_FOUND:
8855 {
8856 /* VRDE library file is missing. */
8857 errMsg = Utf8StrFmt(tr("Could not find the VirtualBox Remote Desktop Extension library."));
8858 break;
8859 }
8860 default:
8861 errMsg = Utf8StrFmt(tr("Failed to launch Remote Desktop Extension server (%Rrc)"),
8862 vrc);
8863 }
8864 LogRel(("VRDE: Failed: (%Rrc), error message: '%s'\n",
8865 vrc, errMsg.c_str()));
8866 throw setErrorStatic(E_FAIL, errMsg.c_str());
8867 }
8868
8869 ComPtr<IMachine> pMachine = pConsole->machine();
8870 ULONG cCpus = 1;
8871 pMachine->COMGETTER(CPUCount)(&cCpus);
8872
8873 /*
8874 * Create the VM
8875 *
8876 * Note! Release the lock since EMT will call Console. It's safe because
8877 * mMachineState is either Starting or Restoring state here.
8878 */
8879 alock.release();
8880
8881 PVM pVM;
8882 vrc = VMR3Create(cCpus,
8883 pConsole->mpVmm2UserMethods,
8884 Console::genericVMSetErrorCallback,
8885 &task->mErrorMsg,
8886 task->mConfigConstructor,
8887 static_cast<Console *>(pConsole),
8888 &pVM, NULL);
8889
8890 alock.acquire();
8891
8892 /* Enable client connections to the server. */
8893 pConsole->consoleVRDPServer()->EnableConnections();
8894
8895 if (RT_SUCCESS(vrc))
8896 {
8897 do
8898 {
8899 /*
8900 * Register our load/save state file handlers
8901 */
8902 vrc = SSMR3RegisterExternal(pConsole->mpUVM, sSSMConsoleUnit, 0 /*iInstance*/, sSSMConsoleVer, 0 /* cbGuess */,
8903 NULL, NULL, NULL,
8904 NULL, saveStateFileExec, NULL,
8905 NULL, loadStateFileExec, NULL,
8906 static_cast<Console *>(pConsole));
8907 AssertRCBreak(vrc);
8908
8909 vrc = static_cast<Console *>(pConsole)->getDisplay()->registerSSM(pConsole->mpUVM);
8910 AssertRC(vrc);
8911 if (RT_FAILURE(vrc))
8912 break;
8913
8914 /*
8915 * Synchronize debugger settings
8916 */
8917 MachineDebugger *machineDebugger = pConsole->getMachineDebugger();
8918 if (machineDebugger)
8919 machineDebugger->flushQueuedSettings();
8920
8921 /*
8922 * Shared Folders
8923 */
8924 if (pConsole->m_pVMMDev->isShFlActive())
8925 {
8926 /* Does the code below call Console from the other thread?
8927 * Not sure, so release the lock just in case. */
8928 alock.release();
8929
8930 for (SharedFolderDataMap::const_iterator it = task->mSharedFolders.begin();
8931 it != task->mSharedFolders.end();
8932 ++it)
8933 {
8934 const SharedFolderData &d = it->second;
8935 rc = pConsole->createSharedFolder(it->first, d);
8936 if (FAILED(rc))
8937 {
8938 ErrorInfoKeeper eik;
8939 setVMRuntimeErrorCallbackF(pVM, pConsole, 0, "BrokenSharedFolder",
8940 N_("The shared folder '%s' could not be set up: %ls.\n"
8941 "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"),
8942 it->first.c_str(), eik.getText().raw());
8943 }
8944 }
8945 if (FAILED(rc))
8946 rc = S_OK; // do not fail with broken shared folders
8947
8948 /* acquire the lock again */
8949 alock.acquire();
8950 }
8951
8952 /* release the lock before a lengthy operation */
8953 alock.release();
8954
8955 /*
8956 * Capture USB devices.
8957 */
8958 rc = pConsole->captureUSBDevices(pConsole->mpUVM);
8959 if (FAILED(rc))
8960 break;
8961
8962 /* Load saved state? */
8963 if (task->mSavedStateFile.length())
8964 {
8965 LogFlowFunc(("Restoring saved state from '%s'...\n",
8966 task->mSavedStateFile.c_str()));
8967
8968 vrc = VMR3LoadFromFile(pConsole->mpUVM,
8969 task->mSavedStateFile.c_str(),
8970 Console::stateProgressCallback,
8971 static_cast<IProgress *>(task->mProgress));
8972
8973 if (RT_SUCCESS(vrc))
8974 {
8975 if (task->mStartPaused)
8976 /* done */
8977 pConsole->setMachineState(MachineState_Paused);
8978 else
8979 {
8980 /* Start/Resume the VM execution */
8981#ifdef VBOX_WITH_EXTPACK
8982 vrc = pConsole->mptrExtPackManager->callAllVmPowerOnHooks(pConsole, pVM);
8983#endif
8984 if (RT_SUCCESS(vrc))
8985 vrc = VMR3Resume(pConsole->mpUVM);
8986 AssertLogRelRC(vrc);
8987 }
8988 }
8989
8990 /* Power off in case we failed loading or resuming the VM */
8991 if (RT_FAILURE(vrc))
8992 {
8993 int vrc2 = VMR3PowerOff(pConsole->mpUVM); AssertLogRelRC(vrc2);
8994#ifdef VBOX_WITH_EXTPACK
8995 pConsole->mptrExtPackManager->callAllVmPowerOffHooks(pConsole, pVM);
8996#endif
8997 }
8998 }
8999 else if (task->mTeleporterEnabled)
9000 {
9001 /* -> ConsoleImplTeleporter.cpp */
9002 bool fPowerOffOnFailure;
9003 rc = pConsole->teleporterTrg(pConsole->mpUVM, pMachine, &task->mErrorMsg, task->mStartPaused,
9004 task->mProgress, &fPowerOffOnFailure);
9005 if (FAILED(rc) && fPowerOffOnFailure)
9006 {
9007 ErrorInfoKeeper eik;
9008 int vrc2 = VMR3PowerOff(pConsole->mpUVM); AssertLogRelRC(vrc2);
9009#ifdef VBOX_WITH_EXTPACK
9010 pConsole->mptrExtPackManager->callAllVmPowerOffHooks(pConsole, pVM);
9011#endif
9012 }
9013 }
9014 else if (task->mEnmFaultToleranceState != FaultToleranceState_Inactive)
9015 {
9016 /*
9017 * Get the config.
9018 */
9019 ULONG uPort;
9020 ULONG uInterval;
9021 Bstr bstrAddress, bstrPassword;
9022
9023 rc = pMachine->COMGETTER(FaultTolerancePort)(&uPort);
9024 if (SUCCEEDED(rc))
9025 {
9026 rc = pMachine->COMGETTER(FaultToleranceSyncInterval)(&uInterval);
9027 if (SUCCEEDED(rc))
9028 rc = pMachine->COMGETTER(FaultToleranceAddress)(bstrAddress.asOutParam());
9029 if (SUCCEEDED(rc))
9030 rc = pMachine->COMGETTER(FaultTolerancePassword)(bstrPassword.asOutParam());
9031 }
9032 if (task->mProgress->setCancelCallback(faultToleranceProgressCancelCallback, pVM))
9033 {
9034 if (SUCCEEDED(rc))
9035 {
9036 Utf8Str strAddress(bstrAddress);
9037 const char *pszAddress = strAddress.isEmpty() ? NULL : strAddress.c_str();
9038 Utf8Str strPassword(bstrPassword);
9039 const char *pszPassword = strPassword.isEmpty() ? NULL : strPassword.c_str();
9040
9041 /* Power on the FT enabled VM. */
9042#ifdef VBOX_WITH_EXTPACK
9043 vrc = pConsole->mptrExtPackManager->callAllVmPowerOnHooks(pConsole, pVM);
9044#endif
9045 if (RT_SUCCESS(vrc))
9046 vrc = FTMR3PowerOn(pVM,
9047 task->mEnmFaultToleranceState == FaultToleranceState_Master /* fMaster */,
9048 uInterval,
9049 pszAddress,
9050 uPort,
9051 pszPassword);
9052 AssertLogRelRC(vrc);
9053 }
9054 task->mProgress->setCancelCallback(NULL, NULL);
9055 }
9056 else
9057 rc = E_FAIL;
9058 }
9059 else if (task->mStartPaused)
9060 /* done */
9061 pConsole->setMachineState(MachineState_Paused);
9062 else
9063 {
9064 /* Power on the VM (i.e. start executing) */
9065#ifdef VBOX_WITH_EXTPACK
9066 vrc = pConsole->mptrExtPackManager->callAllVmPowerOnHooks(pConsole, pVM);
9067#endif
9068 if (RT_SUCCESS(vrc))
9069 vrc = VMR3PowerOn(pConsole->mpUVM);
9070 AssertLogRelRC(vrc);
9071 }
9072
9073 /* acquire the lock again */
9074 alock.acquire();
9075 }
9076 while (0);
9077
9078 /* On failure, destroy the VM */
9079 if (FAILED(rc) || RT_FAILURE(vrc))
9080 {
9081 /* preserve existing error info */
9082 ErrorInfoKeeper eik;
9083
9084 /* powerDown() will call VMR3Destroy() and do all necessary
9085 * cleanup (VRDP, USB devices) */
9086 alock.release();
9087 HRESULT rc2 = pConsole->powerDown();
9088 alock.acquire();
9089 AssertComRC(rc2);
9090 }
9091 else
9092 {
9093 /*
9094 * Deregister the VMSetError callback. This is necessary as the
9095 * pfnVMAtError() function passed to VMR3Create() is supposed to
9096 * be sticky but our error callback isn't.
9097 */
9098 alock.release();
9099 VMR3AtErrorDeregister(pConsole->mpUVM, Console::genericVMSetErrorCallback, &task->mErrorMsg);
9100 /** @todo register another VMSetError callback? */
9101 alock.acquire();
9102 }
9103 }
9104 else
9105 {
9106 /*
9107 * If VMR3Create() failed it has released the VM memory.
9108 */
9109 VMR3ReleaseUVM(pConsole->mpUVM);
9110 pConsole->mpUVM = NULL;
9111 }
9112
9113 if (SUCCEEDED(rc) && RT_FAILURE(vrc))
9114 {
9115 /* If VMR3Create() or one of the other calls in this function fail,
9116 * an appropriate error message has been set in task->mErrorMsg.
9117 * However since that happens via a callback, the rc status code in
9118 * this function is not updated.
9119 */
9120 if (!task->mErrorMsg.length())
9121 {
9122 /* If the error message is not set but we've got a failure,
9123 * convert the VBox status code into a meaningful error message.
9124 * This becomes unused once all the sources of errors set the
9125 * appropriate error message themselves.
9126 */
9127 AssertMsgFailed(("Missing error message during powerup for status code %Rrc\n", vrc));
9128 task->mErrorMsg = Utf8StrFmt(tr("Failed to start VM execution (%Rrc)"),
9129 vrc);
9130 }
9131
9132 /* Set the error message as the COM error.
9133 * Progress::notifyComplete() will pick it up later. */
9134 throw setErrorStatic(E_FAIL, task->mErrorMsg.c_str());
9135 }
9136 }
9137 catch (HRESULT aRC) { rc = aRC; }
9138
9139 if ( pConsole->mMachineState == MachineState_Starting
9140 || pConsole->mMachineState == MachineState_Restoring
9141 || pConsole->mMachineState == MachineState_TeleportingIn
9142 )
9143 {
9144 /* We are still in the Starting/Restoring state. This means one of:
9145 *
9146 * 1) we failed before VMR3Create() was called;
9147 * 2) VMR3Create() failed.
9148 *
9149 * In both cases, there is no need to call powerDown(), but we still
9150 * need to go back to the PoweredOff/Saved state. Reuse
9151 * vmstateChangeCallback() for that purpose.
9152 */
9153
9154 /* preserve existing error info */
9155 ErrorInfoKeeper eik;
9156
9157 Assert(pConsole->mpUVM == NULL);
9158 vmstateChangeCallback(NULL, VMSTATE_TERMINATED, VMSTATE_CREATING,
9159 pConsole);
9160 }
9161
9162 /*
9163 * Evaluate the final result. Note that the appropriate mMachineState value
9164 * is already set by vmstateChangeCallback() in all cases.
9165 */
9166
9167 /* release the lock, don't need it any more */
9168 alock.release();
9169
9170 if (SUCCEEDED(rc))
9171 {
9172 /* Notify the progress object of the success */
9173 task->mProgress->notifyComplete(S_OK);
9174 }
9175 else
9176 {
9177 /* The progress object will fetch the current error info */
9178 task->mProgress->notifyComplete(rc);
9179 LogRel(("Power up failed (vrc=%Rrc, rc=%Rhrc (%#08X))\n", vrc, rc, rc));
9180 }
9181
9182 /* Notify VBoxSVC and any waiting openRemoteSession progress object. */
9183 pConsole->mControl->EndPowerUp(rc);
9184
9185#if defined(RT_OS_WINDOWS)
9186 /* uninitialize COM */
9187 CoUninitialize();
9188#endif
9189
9190 LogFlowFuncLeave();
9191
9192 return VINF_SUCCESS;
9193}
9194
9195
9196/**
9197 * Reconfigures a medium attachment (part of taking or deleting an online snapshot).
9198 *
9199 * @param pConsole Reference to the console object.
9200 * @param pUVM The VM handle.
9201 * @param lInstance The instance of the controller.
9202 * @param pcszDevice The name of the controller type.
9203 * @param enmBus The storage bus type of the controller.
9204 * @param fSetupMerge Whether to set up a medium merge
9205 * @param uMergeSource Merge source image index
9206 * @param uMergeTarget Merge target image index
9207 * @param aMediumAtt The medium attachment.
9208 * @param aMachineState The current machine state.
9209 * @param phrc Where to store com error - only valid if we return VERR_GENERAL_FAILURE.
9210 * @return VBox status code.
9211 */
9212/* static */
9213DECLCALLBACK(int) Console::reconfigureMediumAttachment(Console *pConsole,
9214 PUVM pUVM,
9215 const char *pcszDevice,
9216 unsigned uInstance,
9217 StorageBus_T enmBus,
9218 bool fUseHostIOCache,
9219 bool fBuiltinIOCache,
9220 bool fSetupMerge,
9221 unsigned uMergeSource,
9222 unsigned uMergeTarget,
9223 IMediumAttachment *aMediumAtt,
9224 MachineState_T aMachineState,
9225 HRESULT *phrc)
9226{
9227 LogFlowFunc(("pUVM=%p aMediumAtt=%p phrc=%p\n", pUVM, aMediumAtt, phrc));
9228
9229 int rc;
9230 HRESULT hrc;
9231 Bstr bstr;
9232 *phrc = S_OK;
9233#define RC_CHECK() do { if (RT_FAILURE(rc)) { AssertMsgFailed(("rc=%Rrc\n", rc)); return rc; } } while (0)
9234#define H() do { if (FAILED(hrc)) { AssertMsgFailed(("hrc=%Rhrc (%#x)\n", hrc, hrc)); *phrc = hrc; return VERR_GENERAL_FAILURE; } } while (0)
9235
9236 /* Ignore attachments other than hard disks, since at the moment they are
9237 * not subject to snapshotting in general. */
9238 DeviceType_T lType;
9239 hrc = aMediumAtt->COMGETTER(Type)(&lType); H();
9240 if (lType != DeviceType_HardDisk)
9241 return VINF_SUCCESS;
9242
9243 /* Determine the base path for the device instance. */
9244 PCFGMNODE pCtlInst;
9245 pCtlInst = CFGMR3GetChildF(CFGMR3GetRootU(pUVM), "Devices/%s/%u/", pcszDevice, uInstance);
9246 AssertReturn(pCtlInst, VERR_INTERNAL_ERROR);
9247
9248 /* Update the device instance configuration. */
9249 rc = pConsole->configMediumAttachment(pCtlInst,
9250 pcszDevice,
9251 uInstance,
9252 enmBus,
9253 fUseHostIOCache,
9254 fBuiltinIOCache,
9255 fSetupMerge,
9256 uMergeSource,
9257 uMergeTarget,
9258 aMediumAtt,
9259 aMachineState,
9260 phrc,
9261 true /* fAttachDetach */,
9262 false /* fForceUnmount */,
9263 false /* fHotplug */,
9264 pUVM,
9265 NULL /* paLedDevType */);
9266 /** @todo this dumps everything attached to this device instance, which
9267 * is more than necessary. Dumping the changed LUN would be enough. */
9268 CFGMR3Dump(pCtlInst);
9269 RC_CHECK();
9270
9271#undef RC_CHECK
9272#undef H
9273
9274 LogFlowFunc(("Returns success\n"));
9275 return VINF_SUCCESS;
9276}
9277
9278/**
9279 * Progress cancelation callback employed by Console::fntTakeSnapshotWorker.
9280 */
9281static void takesnapshotProgressCancelCallback(void *pvUser)
9282{
9283 PUVM pUVM = (PUVM)pvUser;
9284 SSMR3Cancel(pUVM);
9285}
9286
9287/**
9288 * Worker thread created by Console::TakeSnapshot.
9289 * @param Thread The current thread (ignored).
9290 * @param pvUser The task.
9291 * @return VINF_SUCCESS (ignored).
9292 */
9293/*static*/
9294DECLCALLBACK(int) Console::fntTakeSnapshotWorker(RTTHREAD Thread, void *pvUser)
9295{
9296 VMTakeSnapshotTask *pTask = (VMTakeSnapshotTask*)pvUser;
9297
9298 // taking a snapshot consists of the following:
9299
9300 // 1) creating a diff image for each virtual hard disk, into which write operations go after
9301 // the snapshot has been created (done in VBoxSVC, in SessionMachine::BeginTakingSnapshot)
9302 // 2) creating a Snapshot object with the state of the machine (hardware + storage,
9303 // done in VBoxSVC, also in SessionMachine::BeginTakingSnapshot)
9304 // 3) saving the state of the virtual machine (here, in the VM process, if the machine is online)
9305
9306 Console *that = pTask->mConsole;
9307 bool fBeganTakingSnapshot = false;
9308 bool fSuspenededBySave = false;
9309
9310 AutoCaller autoCaller(that);
9311 if (FAILED(autoCaller.rc()))
9312 {
9313 that->mptrCancelableProgress.setNull();
9314 return autoCaller.rc();
9315 }
9316
9317 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
9318
9319 HRESULT rc = S_OK;
9320
9321 try
9322 {
9323 /* STEP 1 + 2:
9324 * request creating the diff images on the server and create the snapshot object
9325 * (this will set the machine state to Saving on the server to block
9326 * others from accessing this machine)
9327 */
9328 rc = that->mControl->BeginTakingSnapshot(that,
9329 pTask->bstrName.raw(),
9330 pTask->bstrDescription.raw(),
9331 pTask->mProgress,
9332 pTask->fTakingSnapshotOnline,
9333 pTask->bstrSavedStateFile.asOutParam());
9334 if (FAILED(rc))
9335 throw rc;
9336
9337 fBeganTakingSnapshot = true;
9338
9339 /* Check sanity: for offline snapshots there must not be a saved state
9340 * file name. All other combinations are valid (even though online
9341 * snapshots without saved state file seems inconsistent - there are
9342 * some exotic use cases, which need to be explicitly enabled, see the
9343 * code of SessionMachine::BeginTakingSnapshot. */
9344 if ( !pTask->fTakingSnapshotOnline
9345 && !pTask->bstrSavedStateFile.isEmpty())
9346 throw setErrorStatic(E_FAIL, "Invalid state of saved state file");
9347
9348 /* sync the state with the server */
9349 if (pTask->lastMachineState == MachineState_Running)
9350 that->setMachineStateLocally(MachineState_LiveSnapshotting);
9351 else
9352 that->setMachineStateLocally(MachineState_Saving);
9353
9354 // STEP 3: save the VM state (if online)
9355 if (pTask->fTakingSnapshotOnline)
9356 {
9357 int vrc;
9358 SafeVMPtr ptrVM(that);
9359 if (!ptrVM.isOk())
9360 throw ptrVM.rc();
9361
9362 pTask->mProgress->SetNextOperation(Bstr(tr("Saving the machine state")).raw(),
9363 pTask->ulMemSize); // operation weight, same as computed when setting up progress object
9364 if (!pTask->bstrSavedStateFile.isEmpty())
9365 {
9366 Utf8Str strSavedStateFile(pTask->bstrSavedStateFile);
9367
9368 pTask->mProgress->setCancelCallback(takesnapshotProgressCancelCallback, ptrVM.rawUVM());
9369
9370 alock.release();
9371 LogFlowFunc(("VMR3Save...\n"));
9372 vrc = VMR3Save(ptrVM.rawUVM(),
9373 strSavedStateFile.c_str(),
9374 true /*fContinueAfterwards*/,
9375 Console::stateProgressCallback,
9376 static_cast<IProgress *>(pTask->mProgress),
9377 &fSuspenededBySave);
9378 alock.acquire();
9379 if (RT_FAILURE(vrc))
9380 throw setErrorStatic(E_FAIL,
9381 tr("Failed to save the machine state to '%s' (%Rrc)"),
9382 strSavedStateFile.c_str(), vrc);
9383
9384 pTask->mProgress->setCancelCallback(NULL, NULL);
9385 }
9386 else
9387 LogRel(("Console: skipped saving state as part of online snapshot\n"));
9388
9389 if (!pTask->mProgress->notifyPointOfNoReturn())
9390 throw setErrorStatic(E_FAIL, tr("Canceled"));
9391 that->mptrCancelableProgress.setNull();
9392
9393 // STEP 4: reattach hard disks
9394 LogFlowFunc(("Reattaching new differencing hard disks...\n"));
9395
9396 pTask->mProgress->SetNextOperation(Bstr(tr("Reconfiguring medium attachments")).raw(),
9397 1); // operation weight, same as computed when setting up progress object
9398
9399 com::SafeIfaceArray<IMediumAttachment> atts;
9400 rc = that->mMachine->COMGETTER(MediumAttachments)(ComSafeArrayAsOutParam(atts));
9401 if (FAILED(rc))
9402 throw rc;
9403
9404 for (size_t i = 0;
9405 i < atts.size();
9406 ++i)
9407 {
9408 ComPtr<IStorageController> pStorageController;
9409 Bstr controllerName;
9410 ULONG lInstance;
9411 StorageControllerType_T enmController;
9412 StorageBus_T enmBus;
9413 BOOL fUseHostIOCache;
9414
9415 /*
9416 * We can't pass a storage controller object directly
9417 * (g++ complains about not being able to pass non POD types through '...')
9418 * so we have to query needed values here and pass them.
9419 */
9420 rc = atts[i]->COMGETTER(Controller)(controllerName.asOutParam());
9421 if (FAILED(rc))
9422 throw rc;
9423
9424 rc = that->mMachine->GetStorageControllerByName(controllerName.raw(),
9425 pStorageController.asOutParam());
9426 if (FAILED(rc))
9427 throw rc;
9428
9429 rc = pStorageController->COMGETTER(ControllerType)(&enmController);
9430 if (FAILED(rc))
9431 throw rc;
9432 rc = pStorageController->COMGETTER(Instance)(&lInstance);
9433 if (FAILED(rc))
9434 throw rc;
9435 rc = pStorageController->COMGETTER(Bus)(&enmBus);
9436 if (FAILED(rc))
9437 throw rc;
9438 rc = pStorageController->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
9439 if (FAILED(rc))
9440 throw rc;
9441
9442 const char *pcszDevice = Console::convertControllerTypeToDev(enmController);
9443
9444 BOOL fBuiltinIOCache;
9445 rc = that->mMachine->COMGETTER(IOCacheEnabled)(&fBuiltinIOCache);
9446 if (FAILED(rc))
9447 throw rc;
9448
9449 /*
9450 * don't release the lock since reconfigureMediumAttachment
9451 * isn't going to need the Console lock.
9452 */
9453 vrc = VMR3ReqCallWaitU(ptrVM.rawUVM(),
9454 VMCPUID_ANY,
9455 (PFNRT)reconfigureMediumAttachment,
9456 13,
9457 that,
9458 ptrVM.rawUVM(),
9459 pcszDevice,
9460 lInstance,
9461 enmBus,
9462 fUseHostIOCache,
9463 fBuiltinIOCache,
9464 false /* fSetupMerge */,
9465 0 /* uMergeSource */,
9466 0 /* uMergeTarget */,
9467 atts[i],
9468 that->mMachineState,
9469 &rc);
9470 if (RT_FAILURE(vrc))
9471 throw setErrorStatic(E_FAIL, Console::tr("%Rrc"), vrc);
9472 if (FAILED(rc))
9473 throw rc;
9474 }
9475 }
9476
9477 /*
9478 * finalize the requested snapshot object.
9479 * This will reset the machine state to the state it had right
9480 * before calling mControl->BeginTakingSnapshot().
9481 */
9482 rc = that->mControl->EndTakingSnapshot(TRUE /*aSuccess*/);
9483 // do not throw rc here because we can't call EndTakingSnapshot() twice
9484 LogFlowFunc(("EndTakingSnapshot -> %Rhrc [mMachineState=%s]\n", rc, Global::stringifyMachineState(that->mMachineState)));
9485 }
9486 catch (HRESULT rcThrown)
9487 {
9488 /* preserve existing error info */
9489 ErrorInfoKeeper eik;
9490
9491 if (fBeganTakingSnapshot)
9492 that->mControl->EndTakingSnapshot(FALSE /*aSuccess*/);
9493
9494 rc = rcThrown;
9495 LogFunc(("Caught %Rhrc [mMachineState=%s]\n", rc, Global::stringifyMachineState(that->mMachineState)));
9496 }
9497 Assert(alock.isWriteLockOnCurrentThread());
9498
9499 if (FAILED(rc)) /* Must come before calling setMachineState. */
9500 pTask->mProgress->notifyComplete(rc);
9501
9502 /*
9503 * Fix up the machine state.
9504 *
9505 * For live snapshots we do all the work, for the two other variations we
9506 * just update the local copy.
9507 */
9508 MachineState_T enmMachineState;
9509 that->mMachine->COMGETTER(State)(&enmMachineState);
9510 if ( that->mMachineState == MachineState_LiveSnapshotting
9511 || that->mMachineState == MachineState_Saving)
9512 {
9513
9514 if (!pTask->fTakingSnapshotOnline)
9515 that->setMachineStateLocally(pTask->lastMachineState);
9516 else if (SUCCEEDED(rc))
9517 {
9518 Assert( pTask->lastMachineState == MachineState_Running
9519 || pTask->lastMachineState == MachineState_Paused);
9520 Assert(that->mMachineState == MachineState_Saving);
9521 if (pTask->lastMachineState == MachineState_Running)
9522 {
9523 LogFlowFunc(("VMR3Resume...\n"));
9524 SafeVMPtr ptrVM(that);
9525 alock.release();
9526 int vrc = VMR3Resume(ptrVM.rawUVM());
9527 alock.acquire();
9528 if (RT_FAILURE(vrc))
9529 {
9530 rc = setErrorStatic(VBOX_E_VM_ERROR, tr("Could not resume the machine execution (%Rrc)"), vrc);
9531 pTask->mProgress->notifyComplete(rc);
9532 if (that->mMachineState == MachineState_Saving)
9533 that->setMachineStateLocally(MachineState_Paused);
9534 }
9535 }
9536 else
9537 that->setMachineStateLocally(MachineState_Paused);
9538 }
9539 else
9540 {
9541 /** @todo this could probably be made more generic and reused elsewhere. */
9542 /* paranoid cleanup on for a failed online snapshot. */
9543 VMSTATE enmVMState = VMR3GetStateU(that->mpUVM);
9544 switch (enmVMState)
9545 {
9546 case VMSTATE_RUNNING:
9547 case VMSTATE_RUNNING_LS:
9548 case VMSTATE_DEBUGGING:
9549 case VMSTATE_DEBUGGING_LS:
9550 case VMSTATE_POWERING_OFF:
9551 case VMSTATE_POWERING_OFF_LS:
9552 case VMSTATE_RESETTING:
9553 case VMSTATE_RESETTING_LS:
9554 Assert(!fSuspenededBySave);
9555 that->setMachineState(MachineState_Running);
9556 break;
9557
9558 case VMSTATE_GURU_MEDITATION:
9559 case VMSTATE_GURU_MEDITATION_LS:
9560 that->setMachineState(MachineState_Stuck);
9561 break;
9562
9563 case VMSTATE_FATAL_ERROR:
9564 case VMSTATE_FATAL_ERROR_LS:
9565 if (pTask->lastMachineState == MachineState_Paused)
9566 that->setMachineStateLocally(pTask->lastMachineState);
9567 else
9568 that->setMachineState(MachineState_Paused);
9569 break;
9570
9571 default:
9572 AssertMsgFailed(("%s\n", VMR3GetStateName(enmVMState)));
9573 case VMSTATE_SUSPENDED:
9574 case VMSTATE_SUSPENDED_LS:
9575 case VMSTATE_SUSPENDING:
9576 case VMSTATE_SUSPENDING_LS:
9577 case VMSTATE_SUSPENDING_EXT_LS:
9578 if (fSuspenededBySave)
9579 {
9580 Assert(pTask->lastMachineState == MachineState_Running);
9581 LogFlowFunc(("VMR3Resume (on failure)...\n"));
9582 SafeVMPtr ptrVM(that);
9583 alock.release();
9584 int vrc = VMR3Resume(ptrVM.rawUVM()); AssertLogRelRC(vrc);
9585 alock.acquire();
9586 if (RT_FAILURE(vrc))
9587 that->setMachineState(MachineState_Paused);
9588 }
9589 else if (pTask->lastMachineState == MachineState_Paused)
9590 that->setMachineStateLocally(pTask->lastMachineState);
9591 else
9592 that->setMachineState(MachineState_Paused);
9593 break;
9594 }
9595
9596 }
9597 }
9598 /*else: somebody else has change the state... Leave it. */
9599
9600 /* check the remote state to see that we got it right. */
9601 that->mMachine->COMGETTER(State)(&enmMachineState);
9602 AssertLogRelMsg(that->mMachineState == enmMachineState,
9603 ("mMachineState=%s enmMachineState=%s\n", Global::stringifyMachineState(that->mMachineState),
9604 Global::stringifyMachineState(enmMachineState) ));
9605
9606
9607 if (SUCCEEDED(rc)) /* The failure cases are handled above. */
9608 pTask->mProgress->notifyComplete(rc);
9609
9610 delete pTask;
9611
9612 LogFlowFuncLeave();
9613 return VINF_SUCCESS;
9614}
9615
9616/**
9617 * Thread for executing the saved state operation.
9618 *
9619 * @param Thread The thread handle.
9620 * @param pvUser Pointer to a VMSaveTask structure.
9621 * @return VINF_SUCCESS (ignored).
9622 *
9623 * @note Locks the Console object for writing.
9624 */
9625/*static*/
9626DECLCALLBACK(int) Console::saveStateThread(RTTHREAD Thread, void *pvUser)
9627{
9628 LogFlowFuncEnter();
9629
9630 std::auto_ptr<VMSaveTask> task(static_cast<VMSaveTask*>(pvUser));
9631 AssertReturn(task.get(), VERR_INVALID_PARAMETER);
9632
9633 Assert(task->mSavedStateFile.length());
9634 Assert(task->mProgress.isNull());
9635 Assert(!task->mServerProgress.isNull());
9636
9637 const ComObjPtr<Console> &that = task->mConsole;
9638 Utf8Str errMsg;
9639 HRESULT rc = S_OK;
9640
9641 LogFlowFunc(("Saving the state to '%s'...\n", task->mSavedStateFile.c_str()));
9642
9643 bool fSuspenededBySave;
9644 int vrc = VMR3Save(task->mpUVM,
9645 task->mSavedStateFile.c_str(),
9646 false, /*fContinueAfterwards*/
9647 Console::stateProgressCallback,
9648 static_cast<IProgress *>(task->mServerProgress),
9649 &fSuspenededBySave);
9650 if (RT_FAILURE(vrc))
9651 {
9652 errMsg = Utf8StrFmt(Console::tr("Failed to save the machine state to '%s' (%Rrc)"),
9653 task->mSavedStateFile.c_str(), vrc);
9654 rc = E_FAIL;
9655 }
9656 Assert(!fSuspenededBySave);
9657
9658 /* lock the console once we're going to access it */
9659 AutoWriteLock thatLock(that COMMA_LOCKVAL_SRC_POS);
9660
9661 /* synchronize the state with the server */
9662 if (SUCCEEDED(rc))
9663 {
9664 /*
9665 * The machine has been successfully saved, so power it down
9666 * (vmstateChangeCallback() will set state to Saved on success).
9667 * Note: we release the task's VM caller, otherwise it will
9668 * deadlock.
9669 */
9670 task->releaseVMCaller();
9671 thatLock.release();
9672 rc = that->powerDown();
9673 thatLock.acquire();
9674 }
9675
9676 /*
9677 * If we failed, reset the local machine state.
9678 */
9679 if (FAILED(rc))
9680 that->setMachineStateLocally(task->mMachineStateBefore);
9681
9682 /*
9683 * Finalize the requested save state procedure. In case of failure it will
9684 * reset the machine state to the state it had right before calling
9685 * mControl->BeginSavingState(). This must be the last thing because it
9686 * will set the progress to completed, and that means that the frontend
9687 * can immediately uninit the associated console object.
9688 */
9689 that->mControl->EndSavingState(rc, Bstr(errMsg).raw());
9690
9691 LogFlowFuncLeave();
9692 return VINF_SUCCESS;
9693}
9694
9695/**
9696 * Thread for powering down the Console.
9697 *
9698 * @param Thread The thread handle.
9699 * @param pvUser Pointer to the VMTask structure.
9700 * @return VINF_SUCCESS (ignored).
9701 *
9702 * @note Locks the Console object for writing.
9703 */
9704/*static*/
9705DECLCALLBACK(int) Console::powerDownThread(RTTHREAD Thread, void *pvUser)
9706{
9707 LogFlowFuncEnter();
9708
9709 std::auto_ptr<VMPowerDownTask> task(static_cast<VMPowerDownTask *>(pvUser));
9710 AssertReturn(task.get(), VERR_INVALID_PARAMETER);
9711
9712 AssertReturn(task->isOk(), VERR_GENERAL_FAILURE);
9713
9714 Assert(task->mProgress.isNull());
9715
9716 const ComObjPtr<Console> &that = task->mConsole;
9717
9718 /* Note: no need to use addCaller() to protect Console because VMTask does
9719 * that */
9720
9721 /* wait until the method tat started us returns */
9722 AutoWriteLock thatLock(that COMMA_LOCKVAL_SRC_POS);
9723
9724 /* release VM caller to avoid the powerDown() deadlock */
9725 task->releaseVMCaller();
9726
9727 thatLock.release();
9728
9729 that->powerDown(task->mServerProgress);
9730
9731 /* complete the operation */
9732 that->mControl->EndPoweringDown(S_OK, Bstr().raw());
9733
9734 LogFlowFuncLeave();
9735 return VINF_SUCCESS;
9736}
9737
9738
9739/**
9740 * @interface_method_impl{VMM2USERMETHODS,pfnSaveState}
9741 */
9742/*static*/ DECLCALLBACK(int)
9743Console::vmm2User_SaveState(PCVMM2USERMETHODS pThis, PUVM pUVM)
9744{
9745 Console *pConsole = ((MYVMM2USERMETHODS *)pThis)->pConsole;
9746 NOREF(pUVM);
9747
9748 /*
9749 * For now, just call SaveState. We should probably try notify the GUI so
9750 * it can pop up a progress object and stuff.
9751 */
9752 HRESULT hrc = pConsole->SaveState(NULL);
9753 return SUCCEEDED(hrc) ? VINF_SUCCESS : Global::vboxStatusCodeFromCOM(hrc);
9754}
9755
9756/**
9757 * @interface_method_impl{VMM2USERMETHODS,pfnNotifyEmtInit}
9758 */
9759/*static*/ DECLCALLBACK(void)
9760Console::vmm2User_NotifyEmtInit(PCVMM2USERMETHODS pThis, PUVM pUVM, PUVMCPU pUVCpu)
9761{
9762 NOREF(pThis); NOREF(pUVM); NOREF(pUVCpu);
9763 VirtualBoxBase::initializeComForThread();
9764}
9765
9766/**
9767 * @interface_method_impl{VMM2USERMETHODS,pfnNotifyEmtTerm}
9768 */
9769/*static*/ DECLCALLBACK(void)
9770Console::vmm2User_NotifyEmtTerm(PCVMM2USERMETHODS pThis, PUVM pUVM, PUVMCPU pUVCpu)
9771{
9772 NOREF(pThis); NOREF(pUVM); NOREF(pUVCpu);
9773 VirtualBoxBase::uninitializeComForThread();
9774}
9775
9776/**
9777 * @interface_method_impl{VMM2USERMETHODS,pfnNotifyPdmtInit}
9778 */
9779/*static*/ DECLCALLBACK(void)
9780Console::vmm2User_NotifyPdmtInit(PCVMM2USERMETHODS pThis, PUVM pUVM)
9781{
9782 NOREF(pThis); NOREF(pUVM);
9783 VirtualBoxBase::initializeComForThread();
9784}
9785
9786/**
9787 * @interface_method_impl{VMM2USERMETHODS,pfnNotifyPdmtTerm}
9788 */
9789/*static*/ DECLCALLBACK(void)
9790Console::vmm2User_NotifyPdmtTerm(PCVMM2USERMETHODS pThis, PUVM pUVM)
9791{
9792 NOREF(pThis); NOREF(pUVM);
9793 VirtualBoxBase::uninitializeComForThread();
9794}
9795
9796
9797
9798
9799/**
9800 * The Main status driver instance data.
9801 */
9802typedef struct DRVMAINSTATUS
9803{
9804 /** The LED connectors. */
9805 PDMILEDCONNECTORS ILedConnectors;
9806 /** Pointer to the LED ports interface above us. */
9807 PPDMILEDPORTS pLedPorts;
9808 /** Pointer to the array of LED pointers. */
9809 PPDMLED *papLeds;
9810 /** The unit number corresponding to the first entry in the LED array. */
9811 RTUINT iFirstLUN;
9812 /** The unit number corresponding to the last entry in the LED array.
9813 * (The size of the LED array is iLastLUN - iFirstLUN + 1.) */
9814 RTUINT iLastLUN;
9815 /** Pointer to the driver instance. */
9816 PPDMDRVINS pDrvIns;
9817 /** The Media Notify interface. */
9818 PDMIMEDIANOTIFY IMediaNotify;
9819 /** Map for translating PDM storage controller/LUN information to
9820 * IMediumAttachment references. */
9821 Console::MediumAttachmentMap *pmapMediumAttachments;
9822 /** Device name+instance for mapping */
9823 char *pszDeviceInstance;
9824 /** Pointer to the Console object, for driver triggered activities. */
9825 Console *pConsole;
9826} DRVMAINSTATUS, *PDRVMAINSTATUS;
9827
9828
9829/**
9830 * Notification about a unit which have been changed.
9831 *
9832 * The driver must discard any pointers to data owned by
9833 * the unit and requery it.
9834 *
9835 * @param pInterface Pointer to the interface structure containing the called function pointer.
9836 * @param iLUN The unit number.
9837 */
9838DECLCALLBACK(void) Console::drvStatus_UnitChanged(PPDMILEDCONNECTORS pInterface, unsigned iLUN)
9839{
9840 PDRVMAINSTATUS pData = (PDRVMAINSTATUS)((uintptr_t)pInterface - RT_OFFSETOF(DRVMAINSTATUS, ILedConnectors));
9841 if (iLUN >= pData->iFirstLUN && iLUN <= pData->iLastLUN)
9842 {
9843 PPDMLED pLed;
9844 int rc = pData->pLedPorts->pfnQueryStatusLed(pData->pLedPorts, iLUN, &pLed);
9845 if (RT_FAILURE(rc))
9846 pLed = NULL;
9847 ASMAtomicWritePtr(&pData->papLeds[iLUN - pData->iFirstLUN], pLed);
9848 Log(("drvStatus_UnitChanged: iLUN=%d pLed=%p\n", iLUN, pLed));
9849 }
9850}
9851
9852
9853/**
9854 * Notification about a medium eject.
9855 *
9856 * @returns VBox status.
9857 * @param pInterface Pointer to the interface structure containing the called function pointer.
9858 * @param uLUN The unit number.
9859 */
9860DECLCALLBACK(int) Console::drvStatus_MediumEjected(PPDMIMEDIANOTIFY pInterface, unsigned uLUN)
9861{
9862 PDRVMAINSTATUS pData = (PDRVMAINSTATUS)((uintptr_t)pInterface - RT_OFFSETOF(DRVMAINSTATUS, IMediaNotify));
9863 PPDMDRVINS pDrvIns = pData->pDrvIns;
9864 LogFunc(("uLUN=%d\n", uLUN));
9865 if (pData->pmapMediumAttachments)
9866 {
9867 AutoWriteLock alock(pData->pConsole COMMA_LOCKVAL_SRC_POS);
9868
9869 ComPtr<IMediumAttachment> pMediumAtt;
9870 Utf8Str devicePath = Utf8StrFmt("%s/LUN#%u", pData->pszDeviceInstance, uLUN);
9871 Console::MediumAttachmentMap::const_iterator end = pData->pmapMediumAttachments->end();
9872 Console::MediumAttachmentMap::const_iterator it = pData->pmapMediumAttachments->find(devicePath);
9873 if (it != end)
9874 pMediumAtt = it->second;
9875 Assert(!pMediumAtt.isNull());
9876 if (!pMediumAtt.isNull())
9877 {
9878 IMedium *pMedium = NULL;
9879 HRESULT rc = pMediumAtt->COMGETTER(Medium)(&pMedium);
9880 AssertComRC(rc);
9881 if (SUCCEEDED(rc) && pMedium)
9882 {
9883 BOOL fHostDrive = FALSE;
9884 rc = pMedium->COMGETTER(HostDrive)(&fHostDrive);
9885 AssertComRC(rc);
9886 if (!fHostDrive)
9887 {
9888 alock.release();
9889
9890 ComPtr<IMediumAttachment> pNewMediumAtt;
9891 rc = pData->pConsole->mControl->EjectMedium(pMediumAtt, pNewMediumAtt.asOutParam());
9892 if (SUCCEEDED(rc))
9893 fireMediumChangedEvent(pData->pConsole->mEventSource, pNewMediumAtt);
9894
9895 alock.acquire();
9896 if (pNewMediumAtt != pMediumAtt)
9897 {
9898 pData->pmapMediumAttachments->erase(devicePath);
9899 pData->pmapMediumAttachments->insert(std::make_pair(devicePath, pNewMediumAtt));
9900 }
9901 }
9902 }
9903 }
9904 }
9905 return VINF_SUCCESS;
9906}
9907
9908
9909/**
9910 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
9911 */
9912DECLCALLBACK(void *) Console::drvStatus_QueryInterface(PPDMIBASE pInterface, const char *pszIID)
9913{
9914 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
9915 PDRVMAINSTATUS pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINSTATUS);
9916 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
9917 PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDCONNECTORS, &pThis->ILedConnectors);
9918 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIANOTIFY, &pThis->IMediaNotify);
9919 return NULL;
9920}
9921
9922
9923/**
9924 * Destruct a status driver instance.
9925 *
9926 * @returns VBox status.
9927 * @param pDrvIns The driver instance data.
9928 */
9929DECLCALLBACK(void) Console::drvStatus_Destruct(PPDMDRVINS pDrvIns)
9930{
9931 PDRVMAINSTATUS pData = PDMINS_2_DATA(pDrvIns, PDRVMAINSTATUS);
9932 LogFlowFunc(("iInstance=%d\n", pDrvIns->iInstance));
9933 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
9934
9935 if (pData->papLeds)
9936 {
9937 unsigned iLed = pData->iLastLUN - pData->iFirstLUN + 1;
9938 while (iLed-- > 0)
9939 ASMAtomicWriteNullPtr(&pData->papLeds[iLed]);
9940 }
9941}
9942
9943
9944/**
9945 * Construct a status driver instance.
9946 *
9947 * @copydoc FNPDMDRVCONSTRUCT
9948 */
9949DECLCALLBACK(int) Console::drvStatus_Construct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
9950{
9951 PDRVMAINSTATUS pData = PDMINS_2_DATA(pDrvIns, PDRVMAINSTATUS);
9952 LogFlowFunc(("iInstance=%d\n", pDrvIns->iInstance));
9953 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
9954
9955 /*
9956 * Validate configuration.
9957 */
9958 if (!CFGMR3AreValuesValid(pCfg, "papLeds\0pmapMediumAttachments\0DeviceInstance\0pConsole\0First\0Last\0"))
9959 return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
9960 AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
9961 ("Configuration error: Not possible to attach anything to this driver!\n"),
9962 VERR_PDM_DRVINS_NO_ATTACH);
9963
9964 /*
9965 * Data.
9966 */
9967 pDrvIns->IBase.pfnQueryInterface = Console::drvStatus_QueryInterface;
9968 pData->ILedConnectors.pfnUnitChanged = Console::drvStatus_UnitChanged;
9969 pData->IMediaNotify.pfnEjected = Console::drvStatus_MediumEjected;
9970 pData->pDrvIns = pDrvIns;
9971 pData->pszDeviceInstance = NULL;
9972
9973 /*
9974 * Read config.
9975 */
9976 int rc = CFGMR3QueryPtr(pCfg, "papLeds", (void **)&pData->papLeds);
9977 if (RT_FAILURE(rc))
9978 {
9979 AssertMsgFailed(("Configuration error: Failed to query the \"papLeds\" value! rc=%Rrc\n", rc));
9980 return rc;
9981 }
9982
9983 rc = CFGMR3QueryPtrDef(pCfg, "pmapMediumAttachments", (void **)&pData->pmapMediumAttachments, NULL);
9984 if (RT_FAILURE(rc))
9985 {
9986 AssertMsgFailed(("Configuration error: Failed to query the \"pmapMediumAttachments\" value! rc=%Rrc\n", rc));
9987 return rc;
9988 }
9989 if (pData->pmapMediumAttachments)
9990 {
9991 rc = CFGMR3QueryStringAlloc(pCfg, "DeviceInstance", &pData->pszDeviceInstance);
9992 if (RT_FAILURE(rc))
9993 {
9994 AssertMsgFailed(("Configuration error: Failed to query the \"DeviceInstance\" value! rc=%Rrc\n", rc));
9995 return rc;
9996 }
9997 rc = CFGMR3QueryPtr(pCfg, "pConsole", (void **)&pData->pConsole);
9998 if (RT_FAILURE(rc))
9999 {
10000 AssertMsgFailed(("Configuration error: Failed to query the \"pConsole\" value! rc=%Rrc\n", rc));
10001 return rc;
10002 }
10003 }
10004
10005 rc = CFGMR3QueryU32(pCfg, "First", &pData->iFirstLUN);
10006 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
10007 pData->iFirstLUN = 0;
10008 else if (RT_FAILURE(rc))
10009 {
10010 AssertMsgFailed(("Configuration error: Failed to query the \"First\" value! rc=%Rrc\n", rc));
10011 return rc;
10012 }
10013
10014 rc = CFGMR3QueryU32(pCfg, "Last", &pData->iLastLUN);
10015 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
10016 pData->iLastLUN = 0;
10017 else if (RT_FAILURE(rc))
10018 {
10019 AssertMsgFailed(("Configuration error: Failed to query the \"Last\" value! rc=%Rrc\n", rc));
10020 return rc;
10021 }
10022 if (pData->iFirstLUN > pData->iLastLUN)
10023 {
10024 AssertMsgFailed(("Configuration error: Invalid unit range %u-%u\n", pData->iFirstLUN, pData->iLastLUN));
10025 return VERR_GENERAL_FAILURE;
10026 }
10027
10028 /*
10029 * Get the ILedPorts interface of the above driver/device and
10030 * query the LEDs we want.
10031 */
10032 pData->pLedPorts = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMILEDPORTS);
10033 AssertMsgReturn(pData->pLedPorts, ("Configuration error: No led ports interface above!\n"),
10034 VERR_PDM_MISSING_INTERFACE_ABOVE);
10035
10036 for (unsigned i = pData->iFirstLUN; i <= pData->iLastLUN; ++i)
10037 Console::drvStatus_UnitChanged(&pData->ILedConnectors, i);
10038
10039 return VINF_SUCCESS;
10040}
10041
10042
10043/**
10044 * Console status driver (LED) registration record.
10045 */
10046const PDMDRVREG Console::DrvStatusReg =
10047{
10048 /* u32Version */
10049 PDM_DRVREG_VERSION,
10050 /* szName */
10051 "MainStatus",
10052 /* szRCMod */
10053 "",
10054 /* szR0Mod */
10055 "",
10056 /* pszDescription */
10057 "Main status driver (Main as in the API).",
10058 /* fFlags */
10059 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
10060 /* fClass. */
10061 PDM_DRVREG_CLASS_STATUS,
10062 /* cMaxInstances */
10063 ~0U,
10064 /* cbInstance */
10065 sizeof(DRVMAINSTATUS),
10066 /* pfnConstruct */
10067 Console::drvStatus_Construct,
10068 /* pfnDestruct */
10069 Console::drvStatus_Destruct,
10070 /* pfnRelocate */
10071 NULL,
10072 /* pfnIOCtl */
10073 NULL,
10074 /* pfnPowerOn */
10075 NULL,
10076 /* pfnReset */
10077 NULL,
10078 /* pfnSuspend */
10079 NULL,
10080 /* pfnResume */
10081 NULL,
10082 /* pfnAttach */
10083 NULL,
10084 /* pfnDetach */
10085 NULL,
10086 /* pfnPowerOff */
10087 NULL,
10088 /* pfnSoftReset */
10089 NULL,
10090 /* u32EndVersion */
10091 PDM_DRVREG_VERSION
10092};
10093
10094/* 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