VirtualBox

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

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

EFI: permanent NVRAM storage.

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

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