VirtualBox

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

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

PDM,++: Change APIs used by Main from PVM to PUVM.

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