VirtualBox

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

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

PVM -> PUVM and make setVMRuntimeErrorCallbackF non-static and drop pVM.

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