VirtualBox

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

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

Main/ConsoleImpl: avoid debug assertion while shutting down many VMs in parallel.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 329.9 KB
Line 
1/* $Id: ConsoleImpl.cpp 43168 2012-09-04 15:13:54Z 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 /** @todo Fix race during console/VM reference destruction, refer @bugref{6318}
6059 * comment 25. */
6060 if (FAILED(autoCaller.rc()))
6061 return autoCaller.rc();
6062
6063 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6064
6065 if (mVMDestroying)
6066 {
6067 /* powerDown() is waiting for all callers to finish */
6068 return aQuiet ? E_ACCESSDENIED : setError(E_ACCESSDENIED,
6069 tr("The virtual machine is being powered down"));
6070 }
6071
6072 if (mpUVM == NULL)
6073 {
6074 Assert(aAllowNullVM == true);
6075
6076 /* The machine is not powered up */
6077 return aQuiet ? E_ACCESSDENIED : setError(E_ACCESSDENIED,
6078 tr("The virtual machine is not powered up"));
6079 }
6080
6081 ++mVMCallers;
6082
6083 return S_OK;
6084}
6085
6086/**
6087 * Decreases the usage counter of the mpVM pointer. Must always complete
6088 * the addVMCaller() call after the mpVM pointer is no more necessary.
6089 *
6090 * @note Locks this object for writing.
6091 */
6092void Console::releaseVMCaller()
6093{
6094 AutoCaller autoCaller(this);
6095 AssertComRCReturnVoid(autoCaller.rc());
6096
6097 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6098
6099 AssertReturnVoid(mpUVM != NULL);
6100
6101 Assert(mVMCallers > 0);
6102 --mVMCallers;
6103
6104 if (mVMCallers == 0 && mVMDestroying)
6105 {
6106 /* inform powerDown() there are no more callers */
6107 RTSemEventSignal(mVMZeroCallersSem);
6108 }
6109}
6110
6111
6112HRESULT Console::safeVMPtrRetainer(PVM *a_ppVM, PUVM *a_ppUVM, bool a_Quiet)
6113{
6114 *a_ppVM = NULL;
6115 *a_ppUVM = NULL;
6116
6117 AutoCaller autoCaller(this);
6118 AssertComRCReturnRC(autoCaller.rc());
6119 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6120
6121 /*
6122 * Repeat the checks done by addVMCaller.
6123 */
6124 if (mVMDestroying) /* powerDown() is waiting for all callers to finish */
6125 return a_Quiet
6126 ? E_ACCESSDENIED
6127 : setError(E_ACCESSDENIED, tr("The virtual machine is being powered down"));
6128 PUVM pUVM = mpUVM;
6129 if (!pUVM)
6130 return a_Quiet
6131 ? E_ACCESSDENIED
6132 : setError(E_ACCESSDENIED, tr("The virtual machine is was powered off"));
6133
6134 /*
6135 * Retain a reference to the user mode VM handle and get the global handle.
6136 */
6137 uint32_t cRefs = VMR3RetainUVM(pUVM);
6138 if (cRefs == UINT32_MAX)
6139 return a_Quiet
6140 ? E_ACCESSDENIED
6141 : setError(E_ACCESSDENIED, tr("The virtual machine is was powered off"));
6142
6143 PVM pVM = VMR3GetVM(pUVM);
6144 if (!pVM)
6145 {
6146 VMR3ReleaseUVM(pUVM);
6147 return a_Quiet
6148 ? E_ACCESSDENIED
6149 : setError(E_ACCESSDENIED, tr("The virtual machine is was powered off"));
6150 }
6151
6152 /* done */
6153 *a_ppVM = pVM;
6154 *a_ppUVM = pUVM;
6155 return S_OK;
6156}
6157
6158void Console::safeVMPtrReleaser(PVM *a_ppVM, PUVM *a_ppUVM)
6159{
6160 if (*a_ppVM && *a_ppUVM)
6161 VMR3ReleaseUVM(*a_ppUVM);
6162 *a_ppVM = NULL;
6163 *a_ppUVM = NULL;
6164}
6165
6166
6167/**
6168 * Initialize the release logging facility. In case something
6169 * goes wrong, there will be no release logging. Maybe in the future
6170 * we can add some logic to use different file names in this case.
6171 * Note that the logic must be in sync with Machine::DeleteSettings().
6172 */
6173HRESULT Console::consoleInitReleaseLog(const ComPtr<IMachine> aMachine)
6174{
6175 HRESULT hrc = S_OK;
6176
6177 Bstr logFolder;
6178 hrc = aMachine->COMGETTER(LogFolder)(logFolder.asOutParam());
6179 if (FAILED(hrc))
6180 return hrc;
6181
6182 Utf8Str logDir = logFolder;
6183
6184 /* make sure the Logs folder exists */
6185 Assert(logDir.length());
6186 if (!RTDirExists(logDir.c_str()))
6187 RTDirCreateFullPath(logDir.c_str(), 0700);
6188
6189 Utf8Str logFile = Utf8StrFmt("%s%cVBox.log",
6190 logDir.c_str(), RTPATH_DELIMITER);
6191 Utf8Str pngFile = Utf8StrFmt("%s%cVBox.png",
6192 logDir.c_str(), RTPATH_DELIMITER);
6193
6194 /*
6195 * Age the old log files
6196 * Rename .(n-1) to .(n), .(n-2) to .(n-1), ..., and the last log file to .1
6197 * Overwrite target files in case they exist.
6198 */
6199 ComPtr<IVirtualBox> pVirtualBox;
6200 aMachine->COMGETTER(Parent)(pVirtualBox.asOutParam());
6201 ComPtr<ISystemProperties> pSystemProperties;
6202 pVirtualBox->COMGETTER(SystemProperties)(pSystemProperties.asOutParam());
6203 ULONG cHistoryFiles = 3;
6204 pSystemProperties->COMGETTER(LogHistoryCount)(&cHistoryFiles);
6205 if (cHistoryFiles)
6206 {
6207 for (int i = cHistoryFiles-1; i >= 0; i--)
6208 {
6209 Utf8Str *files[] = { &logFile, &pngFile };
6210 Utf8Str oldName, newName;
6211
6212 for (unsigned int j = 0; j < RT_ELEMENTS(files); ++j)
6213 {
6214 if (i > 0)
6215 oldName = Utf8StrFmt("%s.%d", files[j]->c_str(), i);
6216 else
6217 oldName = *files[j];
6218 newName = Utf8StrFmt("%s.%d", files[j]->c_str(), i + 1);
6219 /* If the old file doesn't exist, delete the new file (if it
6220 * exists) to provide correct rotation even if the sequence is
6221 * broken */
6222 if ( RTFileRename(oldName.c_str(), newName.c_str(), RTFILEMOVE_FLAGS_REPLACE)
6223 == VERR_FILE_NOT_FOUND)
6224 RTFileDelete(newName.c_str());
6225 }
6226 }
6227 }
6228
6229 char szError[RTPATH_MAX + 128];
6230 int vrc = com::VBoxLogRelCreate("VM", logFile.c_str(),
6231 RTLOGFLAGS_PREFIX_TIME_PROG | RTLOGFLAGS_RESTRICT_GROUPS,
6232 "all all.restrict default.unrestricted",
6233 "VBOX_RELEASE_LOG", RTLOGDEST_FILE,
6234 32768 /* cMaxEntriesPerGroup */,
6235 0 /* cHistory */, 0 /* uHistoryFileTime */,
6236 0 /* uHistoryFileSize */, szError, sizeof(szError));
6237 if (RT_FAILURE(vrc))
6238 hrc = setError(E_FAIL, tr("Failed to open release log (%s, %Rrc)"),
6239 szError, vrc);
6240
6241 /* If we've made any directory changes, flush the directory to increase
6242 the likelihood that the log file will be usable after a system panic.
6243
6244 Tip: Try 'export VBOX_RELEASE_LOG_FLAGS=flush' if the last bits of the log
6245 is missing. Just don't have too high hopes for this to help. */
6246 if (SUCCEEDED(hrc) || cHistoryFiles)
6247 RTDirFlush(logDir.c_str());
6248
6249 return hrc;
6250}
6251
6252/**
6253 * Common worker for PowerUp and PowerUpPaused.
6254 *
6255 * @returns COM status code.
6256 *
6257 * @param aProgress Where to return the progress object.
6258 * @param aPaused true if PowerUpPaused called.
6259 */
6260HRESULT Console::powerUp(IProgress **aProgress, bool aPaused)
6261{
6262 LogFlowThisFuncEnter();
6263 LogFlowThisFunc(("mMachineState=%d\n", mMachineState));
6264
6265 CheckComArgOutPointerValid(aProgress);
6266
6267 AutoCaller autoCaller(this);
6268 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6269
6270 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6271
6272 HRESULT rc = S_OK;
6273 ComObjPtr<Progress> pPowerupProgress;
6274 bool fBeganPoweringUp = false;
6275
6276 try
6277 {
6278 if (Global::IsOnlineOrTransient(mMachineState))
6279 throw setError(VBOX_E_INVALID_VM_STATE,
6280 tr("The virtual machine is already running or busy (machine state: %s)"),
6281 Global::stringifyMachineState(mMachineState));
6282
6283 /* test and clear the TeleporterEnabled property */
6284 BOOL fTeleporterEnabled;
6285 rc = mMachine->COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
6286 if (FAILED(rc))
6287 throw rc;
6288#if 0 /** @todo we should save it afterwards, but that isn't necessarily a good idea. Find a better place for this (VBoxSVC). */
6289 if (fTeleporterEnabled)
6290 {
6291 rc = mMachine->COMSETTER(TeleporterEnabled)(FALSE);
6292 if (FAILED(rc))
6293 throw rc;
6294 }
6295#endif
6296
6297 /* test the FaultToleranceState property */
6298 FaultToleranceState_T enmFaultToleranceState;
6299 rc = mMachine->COMGETTER(FaultToleranceState)(&enmFaultToleranceState);
6300 if (FAILED(rc))
6301 throw rc;
6302 BOOL fFaultToleranceSyncEnabled = (enmFaultToleranceState == FaultToleranceState_Standby);
6303
6304 /* Create a progress object to track progress of this operation. Must
6305 * be done as early as possible (together with BeginPowerUp()) as this
6306 * is vital for communicating as much as possible early powerup
6307 * failure information to the API caller */
6308 pPowerupProgress.createObject();
6309 Bstr progressDesc;
6310 if (mMachineState == MachineState_Saved)
6311 progressDesc = tr("Restoring virtual machine");
6312 else if (fTeleporterEnabled)
6313 progressDesc = tr("Teleporting virtual machine");
6314 else if (fFaultToleranceSyncEnabled)
6315 progressDesc = tr("Fault Tolerance syncing of remote virtual machine");
6316 else
6317 progressDesc = tr("Starting virtual machine");
6318 if ( mMachineState == MachineState_Saved
6319 || (!fTeleporterEnabled && !fFaultToleranceSyncEnabled))
6320 rc = pPowerupProgress->init(static_cast<IConsole *>(this),
6321 progressDesc.raw(),
6322 FALSE /* aCancelable */);
6323 else
6324 if (fTeleporterEnabled)
6325 rc = pPowerupProgress->init(static_cast<IConsole *>(this),
6326 progressDesc.raw(),
6327 TRUE /* aCancelable */,
6328 3 /* cOperations */,
6329 10 /* ulTotalOperationsWeight */,
6330 Bstr(tr("Teleporting virtual machine")).raw(),
6331 1 /* ulFirstOperationWeight */,
6332 NULL);
6333 else
6334 if (fFaultToleranceSyncEnabled)
6335 rc = pPowerupProgress->init(static_cast<IConsole *>(this),
6336 progressDesc.raw(),
6337 TRUE /* aCancelable */,
6338 3 /* cOperations */,
6339 10 /* ulTotalOperationsWeight */,
6340 Bstr(tr("Fault Tolerance syncing of remote virtual machine")).raw(),
6341 1 /* ulFirstOperationWeight */,
6342 NULL);
6343
6344 if (FAILED(rc))
6345 throw rc;
6346
6347 /* Tell VBoxSVC and Machine about the progress object so they can
6348 combine/proxy it to any openRemoteSession caller. */
6349 LogFlowThisFunc(("Calling BeginPowerUp...\n"));
6350 rc = mControl->BeginPowerUp(pPowerupProgress);
6351 if (FAILED(rc))
6352 {
6353 LogFlowThisFunc(("BeginPowerUp failed\n"));
6354 throw rc;
6355 }
6356 fBeganPoweringUp = true;
6357
6358 /** @todo this code prevents starting a VM with unavailable bridged
6359 * networking interface. The only benefit is a slightly better error
6360 * message, which should be moved to the driver code. This is the
6361 * only reason why I left the code in for now. The driver allows
6362 * unavailable bridged networking interfaces in certain circumstances,
6363 * and this is sabotaged by this check. The VM will initially have no
6364 * network connectivity, but the user can fix this at runtime. */
6365#if 0
6366 /* the network cards will undergo a quick consistency check */
6367 for (ULONG slot = 0;
6368 slot < maxNetworkAdapters;
6369 ++slot)
6370 {
6371 ComPtr<INetworkAdapter> pNetworkAdapter;
6372 mMachine->GetNetworkAdapter(slot, pNetworkAdapter.asOutParam());
6373 BOOL enabled = FALSE;
6374 pNetworkAdapter->COMGETTER(Enabled)(&enabled);
6375 if (!enabled)
6376 continue;
6377
6378 NetworkAttachmentType_T netattach;
6379 pNetworkAdapter->COMGETTER(AttachmentType)(&netattach);
6380 switch (netattach)
6381 {
6382 case NetworkAttachmentType_Bridged:
6383 {
6384 /* a valid host interface must have been set */
6385 Bstr hostif;
6386 pNetworkAdapter->COMGETTER(HostInterface)(hostif.asOutParam());
6387 if (hostif.isEmpty())
6388 {
6389 throw setError(VBOX_E_HOST_ERROR,
6390 tr("VM cannot start because host interface networking requires a host interface name to be set"));
6391 }
6392 ComPtr<IVirtualBox> pVirtualBox;
6393 mMachine->COMGETTER(Parent)(pVirtualBox.asOutParam());
6394 ComPtr<IHost> pHost;
6395 pVirtualBox->COMGETTER(Host)(pHost.asOutParam());
6396 ComPtr<IHostNetworkInterface> pHostInterface;
6397 if (!SUCCEEDED(pHost->FindHostNetworkInterfaceByName(hostif.raw(),
6398 pHostInterface.asOutParam())))
6399 {
6400 throw setError(VBOX_E_HOST_ERROR,
6401 tr("VM cannot start because the host interface '%ls' does not exist"),
6402 hostif.raw());
6403 }
6404 break;
6405 }
6406 default:
6407 break;
6408 }
6409 }
6410#endif // 0
6411
6412 /* Read console data stored in the saved state file (if not yet done) */
6413 rc = loadDataFromSavedState();
6414 if (FAILED(rc))
6415 throw rc;
6416
6417 /* Check all types of shared folders and compose a single list */
6418 SharedFolderDataMap sharedFolders;
6419 {
6420 /* first, insert global folders */
6421 for (SharedFolderDataMap::const_iterator it = m_mapGlobalSharedFolders.begin();
6422 it != m_mapGlobalSharedFolders.end();
6423 ++it)
6424 {
6425 const SharedFolderData &d = it->second;
6426 sharedFolders[it->first] = d;
6427 }
6428
6429 /* second, insert machine folders */
6430 for (SharedFolderDataMap::const_iterator it = m_mapMachineSharedFolders.begin();
6431 it != m_mapMachineSharedFolders.end();
6432 ++it)
6433 {
6434 const SharedFolderData &d = it->second;
6435 sharedFolders[it->first] = d;
6436 }
6437
6438 /* third, insert console folders */
6439 for (SharedFolderMap::const_iterator it = m_mapSharedFolders.begin();
6440 it != m_mapSharedFolders.end();
6441 ++it)
6442 {
6443 SharedFolder *pSF = it->second;
6444 AutoCaller sfCaller(pSF);
6445 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
6446 sharedFolders[it->first] = SharedFolderData(pSF->getHostPath(),
6447 pSF->isWritable(),
6448 pSF->isAutoMounted());
6449 }
6450 }
6451
6452 Bstr savedStateFile;
6453
6454 /*
6455 * Saved VMs will have to prove that their saved states seem kosher.
6456 */
6457 if (mMachineState == MachineState_Saved)
6458 {
6459 rc = mMachine->COMGETTER(StateFilePath)(savedStateFile.asOutParam());
6460 if (FAILED(rc))
6461 throw rc;
6462 ComAssertRet(!savedStateFile.isEmpty(), E_FAIL);
6463 int vrc = SSMR3ValidateFile(Utf8Str(savedStateFile).c_str(), false /* fChecksumIt */);
6464 if (RT_FAILURE(vrc))
6465 throw setError(VBOX_E_FILE_ERROR,
6466 tr("VM cannot start because the saved state file '%ls' is invalid (%Rrc). Delete the saved state prior to starting the VM"),
6467 savedStateFile.raw(), vrc);
6468 }
6469
6470 LogFlowThisFunc(("Checking if canceled...\n"));
6471 BOOL fCanceled;
6472 rc = pPowerupProgress->COMGETTER(Canceled)(&fCanceled);
6473 if (FAILED(rc))
6474 throw rc;
6475 if (fCanceled)
6476 {
6477 LogFlowThisFunc(("Canceled in BeginPowerUp\n"));
6478 throw setError(E_FAIL, tr("Powerup was canceled"));
6479 }
6480 LogFlowThisFunc(("Not canceled yet.\n"));
6481
6482 /* setup task object and thread to carry out the operation
6483 * asynchronously */
6484
6485 std::auto_ptr<VMPowerUpTask> task(new VMPowerUpTask(this, pPowerupProgress));
6486 ComAssertComRCRetRC(task->rc());
6487
6488 task->mConfigConstructor = configConstructor;
6489 task->mSharedFolders = sharedFolders;
6490 task->mStartPaused = aPaused;
6491 if (mMachineState == MachineState_Saved)
6492 task->mSavedStateFile = savedStateFile;
6493 task->mTeleporterEnabled = fTeleporterEnabled;
6494 task->mEnmFaultToleranceState = enmFaultToleranceState;
6495
6496 /* Reset differencing hard disks for which autoReset is true,
6497 * but only if the machine has no snapshots OR the current snapshot
6498 * is an OFFLINE snapshot; otherwise we would reset the current
6499 * differencing image of an ONLINE snapshot which contains the disk
6500 * state of the machine while it was previously running, but without
6501 * the corresponding machine state, which is equivalent to powering
6502 * off a running machine and not good idea
6503 */
6504 ComPtr<ISnapshot> pCurrentSnapshot;
6505 rc = mMachine->COMGETTER(CurrentSnapshot)(pCurrentSnapshot.asOutParam());
6506 if (FAILED(rc))
6507 throw rc;
6508
6509 BOOL fCurrentSnapshotIsOnline = false;
6510 if (pCurrentSnapshot)
6511 {
6512 rc = pCurrentSnapshot->COMGETTER(Online)(&fCurrentSnapshotIsOnline);
6513 if (FAILED(rc))
6514 throw rc;
6515 }
6516
6517 if (!fCurrentSnapshotIsOnline)
6518 {
6519 LogFlowThisFunc(("Looking for immutable images to reset\n"));
6520
6521 com::SafeIfaceArray<IMediumAttachment> atts;
6522 rc = mMachine->COMGETTER(MediumAttachments)(ComSafeArrayAsOutParam(atts));
6523 if (FAILED(rc))
6524 throw rc;
6525
6526 for (size_t i = 0;
6527 i < atts.size();
6528 ++i)
6529 {
6530 DeviceType_T devType;
6531 rc = atts[i]->COMGETTER(Type)(&devType);
6532 /** @todo later applies to floppies as well */
6533 if (devType == DeviceType_HardDisk)
6534 {
6535 ComPtr<IMedium> pMedium;
6536 rc = atts[i]->COMGETTER(Medium)(pMedium.asOutParam());
6537 if (FAILED(rc))
6538 throw rc;
6539
6540 /* needs autoreset? */
6541 BOOL autoReset = FALSE;
6542 rc = pMedium->COMGETTER(AutoReset)(&autoReset);
6543 if (FAILED(rc))
6544 throw rc;
6545
6546 if (autoReset)
6547 {
6548 ComPtr<IProgress> pResetProgress;
6549 rc = pMedium->Reset(pResetProgress.asOutParam());
6550 if (FAILED(rc))
6551 throw rc;
6552
6553 /* save for later use on the powerup thread */
6554 task->hardDiskProgresses.push_back(pResetProgress);
6555 }
6556 }
6557 }
6558 }
6559 else
6560 LogFlowThisFunc(("Machine has a current snapshot which is online, skipping immutable images reset\n"));
6561
6562 rc = consoleInitReleaseLog(mMachine);
6563 if (FAILED(rc))
6564 throw rc;
6565#ifdef VBOX_WITH_EXTPACK
6566 mptrExtPackManager->dumpAllToReleaseLog();
6567#endif
6568
6569#ifdef RT_OS_SOLARIS
6570 /* setup host core dumper for the VM */
6571 Bstr value;
6572 HRESULT hrc = mMachine->GetExtraData(Bstr("VBoxInternal2/CoreDumpEnabled").raw(), value.asOutParam());
6573 if (SUCCEEDED(hrc) && value == "1")
6574 {
6575 Bstr coreDumpDir, coreDumpReplaceSys, coreDumpLive;
6576 mMachine->GetExtraData(Bstr("VBoxInternal2/CoreDumpDir").raw(), coreDumpDir.asOutParam());
6577 mMachine->GetExtraData(Bstr("VBoxInternal2/CoreDumpReplaceSystemDump").raw(), coreDumpReplaceSys.asOutParam());
6578 mMachine->GetExtraData(Bstr("VBoxInternal2/CoreDumpLive").raw(), coreDumpLive.asOutParam());
6579
6580 uint32_t fCoreFlags = 0;
6581 if ( coreDumpReplaceSys.isEmpty() == false
6582 && Utf8Str(coreDumpReplaceSys).toUInt32() == 1)
6583 {
6584 fCoreFlags |= RTCOREDUMPER_FLAGS_REPLACE_SYSTEM_DUMP;
6585 }
6586
6587 if ( coreDumpLive.isEmpty() == false
6588 && Utf8Str(coreDumpLive).toUInt32() == 1)
6589 {
6590 fCoreFlags |= RTCOREDUMPER_FLAGS_LIVE_CORE;
6591 }
6592
6593 Utf8Str strDumpDir(coreDumpDir);
6594 const char *pszDumpDir = strDumpDir.c_str();
6595 if ( pszDumpDir
6596 && *pszDumpDir == '\0')
6597 pszDumpDir = NULL;
6598
6599 int vrc;
6600 if ( pszDumpDir
6601 && !RTDirExists(pszDumpDir))
6602 {
6603 /*
6604 * Try create the directory.
6605 */
6606 vrc = RTDirCreateFullPath(pszDumpDir, 0700);
6607 if (RT_FAILURE(vrc))
6608 throw setError(E_FAIL, "Failed to setup CoreDumper. Couldn't create dump directory '%s' (%Rrc)\n", pszDumpDir, vrc);
6609 }
6610
6611 vrc = RTCoreDumperSetup(pszDumpDir, fCoreFlags);
6612 if (RT_FAILURE(vrc))
6613 throw setError(E_FAIL, "Failed to setup CoreDumper (%Rrc)", vrc);
6614 else
6615 LogRel(("CoreDumper setup successful. pszDumpDir=%s fFlags=%#x\n", pszDumpDir ? pszDumpDir : ".", fCoreFlags));
6616 }
6617#endif
6618
6619 /* pass the progress object to the caller if requested */
6620 if (aProgress)
6621 {
6622 if (task->hardDiskProgresses.size() == 0)
6623 {
6624 /* there are no other operations to track, return the powerup
6625 * progress only */
6626 pPowerupProgress.queryInterfaceTo(aProgress);
6627 }
6628 else
6629 {
6630 /* create a combined progress object */
6631 ComObjPtr<CombinedProgress> pProgress;
6632 pProgress.createObject();
6633 VMPowerUpTask::ProgressList progresses(task->hardDiskProgresses);
6634 progresses.push_back(ComPtr<IProgress> (pPowerupProgress));
6635 rc = pProgress->init(static_cast<IConsole *>(this),
6636 progressDesc.raw(), progresses.begin(),
6637 progresses.end());
6638 AssertComRCReturnRC(rc);
6639 pProgress.queryInterfaceTo(aProgress);
6640 }
6641 }
6642
6643 int vrc = RTThreadCreate(NULL, Console::powerUpThread,
6644 (void *)task.get(), 0,
6645 RTTHREADTYPE_MAIN_WORKER, 0, "VMPwrUp");
6646 if (RT_FAILURE(vrc))
6647 throw setError(E_FAIL, "Could not create VMPowerUp thread (%Rrc)", vrc);
6648
6649 /* task is now owned by powerUpThread(), so release it */
6650 task.release();
6651
6652 /* finally, set the state: no right to fail in this method afterwards
6653 * since we've already started the thread and it is now responsible for
6654 * any error reporting and appropriate state change! */
6655 if (mMachineState == MachineState_Saved)
6656 setMachineState(MachineState_Restoring);
6657 else if (fTeleporterEnabled)
6658 setMachineState(MachineState_TeleportingIn);
6659 else if (enmFaultToleranceState == FaultToleranceState_Standby)
6660 setMachineState(MachineState_FaultTolerantSyncing);
6661 else
6662 setMachineState(MachineState_Starting);
6663 }
6664 catch (HRESULT aRC) { rc = aRC; }
6665
6666 if (FAILED(rc) && fBeganPoweringUp)
6667 {
6668
6669 /* The progress object will fetch the current error info */
6670 if (!pPowerupProgress.isNull())
6671 pPowerupProgress->notifyComplete(rc);
6672
6673 /* Save the error info across the IPC below. Can't be done before the
6674 * progress notification above, as saving the error info deletes it
6675 * from the current context, and thus the progress object wouldn't be
6676 * updated correctly. */
6677 ErrorInfoKeeper eik;
6678
6679 /* signal end of operation */
6680 mControl->EndPowerUp(rc);
6681 }
6682
6683 LogFlowThisFunc(("mMachineState=%d, rc=%Rhrc\n", mMachineState, rc));
6684 LogFlowThisFuncLeave();
6685 return rc;
6686}
6687
6688/**
6689 * Internal power off worker routine.
6690 *
6691 * This method may be called only at certain places with the following meaning
6692 * as shown below:
6693 *
6694 * - if the machine state is either Running or Paused, a normal
6695 * Console-initiated powerdown takes place (e.g. PowerDown());
6696 * - if the machine state is Saving, saveStateThread() has successfully done its
6697 * job;
6698 * - if the machine state is Starting or Restoring, powerUpThread() has failed
6699 * to start/load the VM;
6700 * - if the machine state is Stopping, the VM has powered itself off (i.e. not
6701 * as a result of the powerDown() call).
6702 *
6703 * Calling it in situations other than the above will cause unexpected behavior.
6704 *
6705 * Note that this method should be the only one that destroys mpVM and sets it
6706 * to NULL.
6707 *
6708 * @param aProgress Progress object to run (may be NULL).
6709 *
6710 * @note Locks this object for writing.
6711 *
6712 * @note Never call this method from a thread that called addVMCaller() or
6713 * instantiated an AutoVMCaller object; first call releaseVMCaller() or
6714 * release(). Otherwise it will deadlock.
6715 */
6716HRESULT Console::powerDown(IProgress *aProgress /*= NULL*/)
6717{
6718 LogFlowThisFuncEnter();
6719
6720 AutoCaller autoCaller(this);
6721 AssertComRCReturnRC(autoCaller.rc());
6722
6723 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6724
6725 /* Total # of steps for the progress object. Must correspond to the
6726 * number of "advance percent count" comments in this method! */
6727 enum { StepCount = 7 };
6728 /* current step */
6729 ULONG step = 0;
6730
6731 HRESULT rc = S_OK;
6732 int vrc = VINF_SUCCESS;
6733
6734 /* sanity */
6735 Assert(mVMDestroying == false);
6736
6737 PUVM pUVM = mpUVM; Assert(pUVM != NULL);
6738 uint32_t cRefs = VMR3RetainUVM(pUVM); Assert(cRefs != UINT32_MAX);
6739
6740 AssertMsg( mMachineState == MachineState_Running
6741 || mMachineState == MachineState_Paused
6742 || mMachineState == MachineState_Stuck
6743 || mMachineState == MachineState_Starting
6744 || mMachineState == MachineState_Stopping
6745 || mMachineState == MachineState_Saving
6746 || mMachineState == MachineState_Restoring
6747 || mMachineState == MachineState_TeleportingPausedVM
6748 || mMachineState == MachineState_FaultTolerantSyncing
6749 || mMachineState == MachineState_TeleportingIn
6750 , ("Invalid machine state: %s\n", Global::stringifyMachineState(mMachineState)));
6751
6752 LogRel(("Console::powerDown(): A request to power off the VM has been issued (mMachineState=%s, InUninit=%d)\n",
6753 Global::stringifyMachineState(mMachineState), autoCaller.state() == InUninit));
6754
6755 /* Check if we need to power off the VM. In case of mVMPoweredOff=true, the
6756 * VM has already powered itself off in vmstateChangeCallback() and is just
6757 * notifying Console about that. In case of Starting or Restoring,
6758 * powerUpThread() is calling us on failure, so the VM is already off at
6759 * that point. */
6760 if ( !mVMPoweredOff
6761 && ( mMachineState == MachineState_Starting
6762 || mMachineState == MachineState_Restoring
6763 || mMachineState == MachineState_FaultTolerantSyncing
6764 || mMachineState == MachineState_TeleportingIn)
6765 )
6766 mVMPoweredOff = true;
6767
6768 /*
6769 * Go to Stopping state if not already there.
6770 *
6771 * Note that we don't go from Saving/Restoring to Stopping because
6772 * vmstateChangeCallback() needs it to set the state to Saved on
6773 * VMSTATE_TERMINATED. In terms of protecting from inappropriate operations
6774 * while leaving the lock below, Saving or Restoring should be fine too.
6775 * Ditto for TeleportingPausedVM -> Teleported.
6776 */
6777 if ( mMachineState != MachineState_Saving
6778 && mMachineState != MachineState_Restoring
6779 && mMachineState != MachineState_Stopping
6780 && mMachineState != MachineState_TeleportingIn
6781 && mMachineState != MachineState_TeleportingPausedVM
6782 && mMachineState != MachineState_FaultTolerantSyncing
6783 )
6784 setMachineState(MachineState_Stopping);
6785
6786 /* ----------------------------------------------------------------------
6787 * DONE with necessary state changes, perform the power down actions (it's
6788 * safe to release the object lock now if needed)
6789 * ---------------------------------------------------------------------- */
6790
6791 /* Stop the VRDP server to prevent new clients connection while VM is being
6792 * powered off. */
6793 if (mConsoleVRDPServer)
6794 {
6795 LogFlowThisFunc(("Stopping VRDP server...\n"));
6796
6797 /* Leave the lock since EMT will call us back as addVMCaller()
6798 * in updateDisplayData(). */
6799 alock.release();
6800
6801 mConsoleVRDPServer->Stop();
6802
6803 alock.acquire();
6804 }
6805
6806 /* advance percent count */
6807 if (aProgress)
6808 aProgress->SetCurrentOperationProgress(99 * (++step) / StepCount );
6809
6810
6811 /* ----------------------------------------------------------------------
6812 * Now, wait for all mpVM callers to finish their work if there are still
6813 * some on other threads. NO methods that need mpVM (or initiate other calls
6814 * that need it) may be called after this point
6815 * ---------------------------------------------------------------------- */
6816
6817 /* go to the destroying state to prevent from adding new callers */
6818 mVMDestroying = true;
6819
6820 if (mVMCallers > 0)
6821 {
6822 /* lazy creation */
6823 if (mVMZeroCallersSem == NIL_RTSEMEVENT)
6824 RTSemEventCreate(&mVMZeroCallersSem);
6825
6826 LogFlowThisFunc(("Waiting for mpVM callers (%d) to drop to zero...\n",
6827 mVMCallers));
6828
6829 alock.release();
6830
6831 RTSemEventWait(mVMZeroCallersSem, RT_INDEFINITE_WAIT);
6832
6833 alock.acquire();
6834 }
6835
6836 /* advance percent count */
6837 if (aProgress)
6838 aProgress->SetCurrentOperationProgress(99 * (++step) / StepCount );
6839
6840 vrc = VINF_SUCCESS;
6841
6842 /*
6843 * Power off the VM if not already done that.
6844 * Leave the lock since EMT will call vmstateChangeCallback.
6845 *
6846 * Note that VMR3PowerOff() may fail here (invalid VMSTATE) if the
6847 * VM-(guest-)initiated power off happened in parallel a ms before this
6848 * call. So far, we let this error pop up on the user's side.
6849 */
6850 if (!mVMPoweredOff)
6851 {
6852 LogFlowThisFunc(("Powering off the VM...\n"));
6853 alock.release();
6854 vrc = VMR3PowerOff(VMR3GetVM(pUVM));
6855#ifdef VBOX_WITH_EXTPACK
6856 mptrExtPackManager->callAllVmPowerOffHooks(this, VMR3GetVM(pUVM));
6857#endif
6858 alock.acquire();
6859 }
6860
6861 /* advance percent count */
6862 if (aProgress)
6863 aProgress->SetCurrentOperationProgress(99 * (++step) / StepCount );
6864
6865#ifdef VBOX_WITH_HGCM
6866 /* Shutdown HGCM services before destroying the VM. */
6867 if (m_pVMMDev)
6868 {
6869 LogFlowThisFunc(("Shutdown HGCM...\n"));
6870
6871 /* Leave the lock since EMT will call us back as addVMCaller() */
6872 alock.release();
6873
6874 m_pVMMDev->hgcmShutdown();
6875
6876 alock.acquire();
6877 }
6878
6879 /* advance percent count */
6880 if (aProgress)
6881 aProgress->SetCurrentOperationProgress(99 * (++step) / StepCount);
6882
6883#endif /* VBOX_WITH_HGCM */
6884
6885 LogFlowThisFunc(("Ready for VM destruction.\n"));
6886
6887 /* If we are called from Console::uninit(), then try to destroy the VM even
6888 * on failure (this will most likely fail too, but what to do?..) */
6889 if (RT_SUCCESS(vrc) || autoCaller.state() == InUninit)
6890 {
6891 /* If the machine has an USB controller, release all USB devices
6892 * (symmetric to the code in captureUSBDevices()) */
6893 bool fHasUSBController = false;
6894 {
6895 PPDMIBASE pBase;
6896 vrc = PDMR3QueryLun(VMR3GetVM(pUVM), "usb-ohci", 0, 0, &pBase);
6897 if (RT_SUCCESS(vrc))
6898 {
6899 fHasUSBController = true;
6900 alock.release();
6901 detachAllUSBDevices(false /* aDone */);
6902 alock.acquire();
6903 }
6904 }
6905
6906 /* Now we've got to destroy the VM as well. (mpVM is not valid beyond
6907 * this point). We release the lock before calling VMR3Destroy() because
6908 * it will result into calling destructors of drivers associated with
6909 * Console children which may in turn try to lock Console (e.g. by
6910 * instantiating SafeVMPtr to access mpVM). It's safe here because
6911 * mVMDestroying is set which should prevent any activity. */
6912
6913 /* Set mpUVM to NULL early just in case if some old code is not using
6914 * addVMCaller()/releaseVMCaller(). (We have our own ref on pUVM.) */
6915 VMR3ReleaseUVM(mpUVM);
6916 mpUVM = NULL;
6917
6918 LogFlowThisFunc(("Destroying the VM...\n"));
6919
6920 alock.release();
6921
6922 vrc = VMR3Destroy(VMR3GetVM(pUVM));
6923
6924 /* take the lock again */
6925 alock.acquire();
6926
6927 /* advance percent count */
6928 if (aProgress)
6929 aProgress->SetCurrentOperationProgress(99 * (++step) / StepCount);
6930
6931 if (RT_SUCCESS(vrc))
6932 {
6933 LogFlowThisFunc(("Machine has been destroyed (mMachineState=%d)\n",
6934 mMachineState));
6935 /* Note: the Console-level machine state change happens on the
6936 * VMSTATE_TERMINATE state change in vmstateChangeCallback(). If
6937 * powerDown() is called from EMT (i.e. from vmstateChangeCallback()
6938 * on receiving VM-initiated VMSTATE_OFF), VMSTATE_TERMINATE hasn't
6939 * occurred yet. This is okay, because mMachineState is already
6940 * Stopping in this case, so any other attempt to call PowerDown()
6941 * will be rejected. */
6942 }
6943 else
6944 {
6945 /* bad bad bad, but what to do? (Give Console our UVM ref.) */
6946 mpUVM = pUVM;
6947 pUVM = NULL;
6948 rc = setError(VBOX_E_VM_ERROR,
6949 tr("Could not destroy the machine. (Error: %Rrc)"),
6950 vrc);
6951 }
6952
6953 /* Complete the detaching of the USB devices. */
6954 if (fHasUSBController)
6955 {
6956 alock.release();
6957 detachAllUSBDevices(true /* aDone */);
6958 alock.acquire();
6959 }
6960
6961 /* advance percent count */
6962 if (aProgress)
6963 aProgress->SetCurrentOperationProgress(99 * (++step) / StepCount);
6964 }
6965 else
6966 {
6967 rc = setError(VBOX_E_VM_ERROR,
6968 tr("Could not power off the machine. (Error: %Rrc)"),
6969 vrc);
6970 }
6971
6972 /*
6973 * Finished with the destruction.
6974 *
6975 * Note that if something impossible happened and we've failed to destroy
6976 * the VM, mVMDestroying will remain true and mMachineState will be
6977 * something like Stopping, so most Console methods will return an error
6978 * to the caller.
6979 */
6980 if (mpUVM != NULL)
6981 VMR3ReleaseUVM(pUVM);
6982 else
6983 mVMDestroying = false;
6984
6985#ifdef CONSOLE_WITH_EVENT_CACHE
6986 if (SUCCEEDED(rc))
6987 mCallbackData.clear();
6988#endif
6989
6990 LogFlowThisFuncLeave();
6991 return rc;
6992}
6993
6994/**
6995 * @note Locks this object for writing.
6996 */
6997HRESULT Console::setMachineState(MachineState_T aMachineState,
6998 bool aUpdateServer /* = true */)
6999{
7000 AutoCaller autoCaller(this);
7001 AssertComRCReturnRC(autoCaller.rc());
7002
7003 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7004
7005 HRESULT rc = S_OK;
7006
7007 if (mMachineState != aMachineState)
7008 {
7009 LogThisFunc(("machineState=%s -> %s aUpdateServer=%RTbool\n",
7010 Global::stringifyMachineState(mMachineState), Global::stringifyMachineState(aMachineState), aUpdateServer));
7011 mMachineState = aMachineState;
7012
7013 /// @todo (dmik)
7014 // possibly, we need to redo onStateChange() using the dedicated
7015 // Event thread, like it is done in VirtualBox. This will make it
7016 // much safer (no deadlocks possible if someone tries to use the
7017 // console from the callback), however, listeners will lose the
7018 // ability to synchronously react to state changes (is it really
7019 // necessary??)
7020 LogFlowThisFunc(("Doing onStateChange()...\n"));
7021 onStateChange(aMachineState);
7022 LogFlowThisFunc(("Done onStateChange()\n"));
7023
7024 if (aUpdateServer)
7025 {
7026 /* Server notification MUST be done from under the lock; otherwise
7027 * the machine state here and on the server might go out of sync
7028 * which can lead to various unexpected results (like the machine
7029 * state being >= MachineState_Running on the server, while the
7030 * session state is already SessionState_Unlocked at the same time
7031 * there).
7032 *
7033 * Cross-lock conditions should be carefully watched out: calling
7034 * UpdateState we will require Machine and SessionMachine locks
7035 * (remember that here we're holding the Console lock here, and also
7036 * all locks that have been acquire by the thread before calling
7037 * this method).
7038 */
7039 LogFlowThisFunc(("Doing mControl->UpdateState()...\n"));
7040 rc = mControl->UpdateState(aMachineState);
7041 LogFlowThisFunc(("mControl->UpdateState()=%Rhrc\n", rc));
7042 }
7043 }
7044
7045 return rc;
7046}
7047
7048/**
7049 * Searches for a shared folder with the given logical name
7050 * in the collection of shared folders.
7051 *
7052 * @param aName logical name of the shared folder
7053 * @param aSharedFolder where to return the found object
7054 * @param aSetError whether to set the error info if the folder is
7055 * not found
7056 * @return
7057 * S_OK when found or E_INVALIDARG when not found
7058 *
7059 * @note The caller must lock this object for writing.
7060 */
7061HRESULT Console::findSharedFolder(const Utf8Str &strName,
7062 ComObjPtr<SharedFolder> &aSharedFolder,
7063 bool aSetError /* = false */)
7064{
7065 /* sanity check */
7066 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
7067
7068 SharedFolderMap::const_iterator it = m_mapSharedFolders.find(strName);
7069 if (it != m_mapSharedFolders.end())
7070 {
7071 aSharedFolder = it->second;
7072 return S_OK;
7073 }
7074
7075 if (aSetError)
7076 setError(VBOX_E_FILE_ERROR,
7077 tr("Could not find a shared folder named '%s'."),
7078 strName.c_str());
7079
7080 return VBOX_E_FILE_ERROR;
7081}
7082
7083/**
7084 * Fetches the list of global or machine shared folders from the server.
7085 *
7086 * @param aGlobal true to fetch global folders.
7087 *
7088 * @note The caller must lock this object for writing.
7089 */
7090HRESULT Console::fetchSharedFolders(BOOL aGlobal)
7091{
7092 /* sanity check */
7093 AssertReturn(AutoCaller(this).state() == InInit ||
7094 isWriteLockOnCurrentThread(), E_FAIL);
7095
7096 LogFlowThisFunc(("Entering\n"));
7097
7098 /* Check if we're online and keep it that way. */
7099 SafeVMPtrQuiet ptrVM(this);
7100 AutoVMCallerQuietWeak autoVMCaller(this);
7101 bool const online = ptrVM.isOk()
7102 && m_pVMMDev
7103 && m_pVMMDev->isShFlActive();
7104
7105 HRESULT rc = S_OK;
7106
7107 try
7108 {
7109 if (aGlobal)
7110 {
7111 /// @todo grab & process global folders when they are done
7112 }
7113 else
7114 {
7115 SharedFolderDataMap oldFolders;
7116 if (online)
7117 oldFolders = m_mapMachineSharedFolders;
7118
7119 m_mapMachineSharedFolders.clear();
7120
7121 SafeIfaceArray<ISharedFolder> folders;
7122 rc = mMachine->COMGETTER(SharedFolders)(ComSafeArrayAsOutParam(folders));
7123 if (FAILED(rc)) throw rc;
7124
7125 for (size_t i = 0; i < folders.size(); ++i)
7126 {
7127 ComPtr<ISharedFolder> pSharedFolder = folders[i];
7128
7129 Bstr bstrName;
7130 Bstr bstrHostPath;
7131 BOOL writable;
7132 BOOL autoMount;
7133
7134 rc = pSharedFolder->COMGETTER(Name)(bstrName.asOutParam());
7135 if (FAILED(rc)) throw rc;
7136 Utf8Str strName(bstrName);
7137
7138 rc = pSharedFolder->COMGETTER(HostPath)(bstrHostPath.asOutParam());
7139 if (FAILED(rc)) throw rc;
7140 Utf8Str strHostPath(bstrHostPath);
7141
7142 rc = pSharedFolder->COMGETTER(Writable)(&writable);
7143 if (FAILED(rc)) throw rc;
7144
7145 rc = pSharedFolder->COMGETTER(AutoMount)(&autoMount);
7146 if (FAILED(rc)) throw rc;
7147
7148 m_mapMachineSharedFolders.insert(std::make_pair(strName,
7149 SharedFolderData(strHostPath, !!writable, !!autoMount)));
7150
7151 /* send changes to HGCM if the VM is running */
7152 if (online)
7153 {
7154 SharedFolderDataMap::iterator it = oldFolders.find(strName);
7155 if ( it == oldFolders.end()
7156 || it->second.m_strHostPath != strHostPath)
7157 {
7158 /* a new machine folder is added or
7159 * the existing machine folder is changed */
7160 if (m_mapSharedFolders.find(strName) != m_mapSharedFolders.end())
7161 ; /* the console folder exists, nothing to do */
7162 else
7163 {
7164 /* remove the old machine folder (when changed)
7165 * or the global folder if any (when new) */
7166 if ( it != oldFolders.end()
7167 || m_mapGlobalSharedFolders.find(strName) != m_mapGlobalSharedFolders.end()
7168 )
7169 {
7170 rc = removeSharedFolder(strName);
7171 if (FAILED(rc)) throw rc;
7172 }
7173
7174 /* create the new machine folder */
7175 rc = createSharedFolder(strName,
7176 SharedFolderData(strHostPath, !!writable, !!autoMount));
7177 if (FAILED(rc)) throw rc;
7178 }
7179 }
7180 /* forget the processed (or identical) folder */
7181 if (it != oldFolders.end())
7182 oldFolders.erase(it);
7183 }
7184 }
7185
7186 /* process outdated (removed) folders */
7187 if (online)
7188 {
7189 for (SharedFolderDataMap::const_iterator it = oldFolders.begin();
7190 it != oldFolders.end(); ++it)
7191 {
7192 if (m_mapSharedFolders.find(it->first) != m_mapSharedFolders.end())
7193 ; /* the console folder exists, nothing to do */
7194 else
7195 {
7196 /* remove the outdated machine folder */
7197 rc = removeSharedFolder(it->first);
7198 if (FAILED(rc)) throw rc;
7199
7200 /* create the global folder if there is any */
7201 SharedFolderDataMap::const_iterator git =
7202 m_mapGlobalSharedFolders.find(it->first);
7203 if (git != m_mapGlobalSharedFolders.end())
7204 {
7205 rc = createSharedFolder(git->first, git->second);
7206 if (FAILED(rc)) throw rc;
7207 }
7208 }
7209 }
7210 }
7211 }
7212 }
7213 catch (HRESULT rc2)
7214 {
7215 if (online)
7216 setVMRuntimeErrorCallbackF(ptrVM, this, 0, "BrokenSharedFolder",
7217 N_("Broken shared folder!"));
7218 }
7219
7220 LogFlowThisFunc(("Leaving\n"));
7221
7222 return rc;
7223}
7224
7225/**
7226 * Searches for a shared folder with the given name in the list of machine
7227 * shared folders and then in the list of the global shared folders.
7228 *
7229 * @param aName Name of the folder to search for.
7230 * @param aIt Where to store the pointer to the found folder.
7231 * @return @c true if the folder was found and @c false otherwise.
7232 *
7233 * @note The caller must lock this object for reading.
7234 */
7235bool Console::findOtherSharedFolder(const Utf8Str &strName,
7236 SharedFolderDataMap::const_iterator &aIt)
7237{
7238 /* sanity check */
7239 AssertReturn(isWriteLockOnCurrentThread(), false);
7240
7241 /* first, search machine folders */
7242 aIt = m_mapMachineSharedFolders.find(strName);
7243 if (aIt != m_mapMachineSharedFolders.end())
7244 return true;
7245
7246 /* second, search machine folders */
7247 aIt = m_mapGlobalSharedFolders.find(strName);
7248 if (aIt != m_mapGlobalSharedFolders.end())
7249 return true;
7250
7251 return false;
7252}
7253
7254/**
7255 * Calls the HGCM service to add a shared folder definition.
7256 *
7257 * @param aName Shared folder name.
7258 * @param aHostPath Shared folder path.
7259 *
7260 * @note Must be called from under AutoVMCaller and when mpVM != NULL!
7261 * @note Doesn't lock anything.
7262 */
7263HRESULT Console::createSharedFolder(const Utf8Str &strName, const SharedFolderData &aData)
7264{
7265 ComAssertRet(strName.isNotEmpty(), E_FAIL);
7266 ComAssertRet(aData.m_strHostPath.isNotEmpty(), E_FAIL);
7267
7268 /* sanity checks */
7269 AssertReturn(mpUVM, E_FAIL);
7270 AssertReturn(m_pVMMDev && m_pVMMDev->isShFlActive(), E_FAIL);
7271
7272 VBOXHGCMSVCPARM parms[SHFL_CPARMS_ADD_MAPPING];
7273 SHFLSTRING *pFolderName, *pMapName;
7274 size_t cbString;
7275
7276 Bstr value;
7277 HRESULT hrc = mMachine->GetExtraData(BstrFmt("VBoxInternal2/SharedFoldersEnableSymlinksCreate/%s",
7278 strName.c_str()).raw(),
7279 value.asOutParam());
7280 bool fSymlinksCreate = hrc == S_OK && value == "1";
7281
7282 Log(("Adding shared folder '%s' -> '%s'\n", strName.c_str(), aData.m_strHostPath.c_str()));
7283
7284 // check whether the path is valid and exists
7285 char hostPathFull[RTPATH_MAX];
7286 int vrc = RTPathAbsEx(NULL,
7287 aData.m_strHostPath.c_str(),
7288 hostPathFull,
7289 sizeof(hostPathFull));
7290 if (RT_FAILURE(vrc))
7291 return setError(E_INVALIDARG,
7292 tr("Invalid shared folder path: '%s' (%Rrc)"),
7293 aData.m_strHostPath.c_str(), vrc);
7294 if (!RTPathExists(hostPathFull))
7295 return setError(E_INVALIDARG,
7296 tr("Shared folder path '%s' does not exist on the host"),
7297 aData.m_strHostPath.c_str());
7298 /* Check whether the path is full (absolute) */
7299 if (RTPathCompare(aData.m_strHostPath.c_str(), hostPathFull) != 0)
7300 return setError(E_INVALIDARG,
7301 tr("Shared folder path '%s' is not absolute"),
7302 aData.m_strHostPath.c_str());
7303
7304 // now that we know the path is good, give it to HGCM
7305
7306 Bstr bstrName(strName);
7307 Bstr bstrHostPath(aData.m_strHostPath);
7308
7309 cbString = (bstrHostPath.length() + 1) * sizeof(RTUTF16);
7310 if (cbString >= UINT16_MAX)
7311 return setError(E_INVALIDARG, tr("The name is too long"));
7312 pFolderName = (SHFLSTRING*)RTMemAllocZ(sizeof(SHFLSTRING) + cbString);
7313 Assert(pFolderName);
7314 memcpy(pFolderName->String.ucs2, bstrHostPath.raw(), cbString);
7315
7316 pFolderName->u16Size = (uint16_t)cbString;
7317 pFolderName->u16Length = (uint16_t)cbString - sizeof(RTUTF16);
7318
7319 parms[0].type = VBOX_HGCM_SVC_PARM_PTR;
7320 parms[0].u.pointer.addr = pFolderName;
7321 parms[0].u.pointer.size = sizeof(SHFLSTRING) + (uint16_t)cbString;
7322
7323 cbString = (bstrName.length() + 1) * sizeof(RTUTF16);
7324 if (cbString >= UINT16_MAX)
7325 {
7326 RTMemFree(pFolderName);
7327 return setError(E_INVALIDARG, tr("The host path is too long"));
7328 }
7329 pMapName = (SHFLSTRING*)RTMemAllocZ(sizeof(SHFLSTRING) + cbString);
7330 Assert(pMapName);
7331 memcpy(pMapName->String.ucs2, bstrName.raw(), cbString);
7332
7333 pMapName->u16Size = (uint16_t)cbString;
7334 pMapName->u16Length = (uint16_t)cbString - sizeof(RTUTF16);
7335
7336 parms[1].type = VBOX_HGCM_SVC_PARM_PTR;
7337 parms[1].u.pointer.addr = pMapName;
7338 parms[1].u.pointer.size = sizeof(SHFLSTRING) + (uint16_t)cbString;
7339
7340 parms[2].type = VBOX_HGCM_SVC_PARM_32BIT;
7341 parms[2].u.uint32 = (aData.m_fWritable ? SHFL_ADD_MAPPING_F_WRITABLE : 0)
7342 | (aData.m_fAutoMount ? SHFL_ADD_MAPPING_F_AUTOMOUNT : 0)
7343 | (fSymlinksCreate ? SHFL_ADD_MAPPING_F_CREATE_SYMLINKS : 0);
7344
7345 vrc = m_pVMMDev->hgcmHostCall("VBoxSharedFolders",
7346 SHFL_FN_ADD_MAPPING,
7347 SHFL_CPARMS_ADD_MAPPING, &parms[0]);
7348 RTMemFree(pFolderName);
7349 RTMemFree(pMapName);
7350
7351 if (RT_FAILURE(vrc))
7352 return setError(E_FAIL,
7353 tr("Could not create a shared folder '%s' mapped to '%s' (%Rrc)"),
7354 strName.c_str(), aData.m_strHostPath.c_str(), vrc);
7355
7356 return S_OK;
7357}
7358
7359/**
7360 * Calls the HGCM service to remove the shared folder definition.
7361 *
7362 * @param aName Shared folder name.
7363 *
7364 * @note Must be called from under AutoVMCaller and when mpVM != NULL!
7365 * @note Doesn't lock anything.
7366 */
7367HRESULT Console::removeSharedFolder(const Utf8Str &strName)
7368{
7369 ComAssertRet(strName.isNotEmpty(), E_FAIL);
7370
7371 /* sanity checks */
7372 AssertReturn(mpUVM, E_FAIL);
7373 AssertReturn(m_pVMMDev && m_pVMMDev->isShFlActive(), E_FAIL);
7374
7375 VBOXHGCMSVCPARM parms;
7376 SHFLSTRING *pMapName;
7377 size_t cbString;
7378
7379 Log(("Removing shared folder '%s'\n", strName.c_str()));
7380
7381 Bstr bstrName(strName);
7382 cbString = (bstrName.length() + 1) * sizeof(RTUTF16);
7383 if (cbString >= UINT16_MAX)
7384 return setError(E_INVALIDARG, tr("The name is too long"));
7385 pMapName = (SHFLSTRING *) RTMemAllocZ(sizeof(SHFLSTRING) + cbString);
7386 Assert(pMapName);
7387 memcpy(pMapName->String.ucs2, bstrName.raw(), cbString);
7388
7389 pMapName->u16Size = (uint16_t)cbString;
7390 pMapName->u16Length = (uint16_t)cbString - sizeof(RTUTF16);
7391
7392 parms.type = VBOX_HGCM_SVC_PARM_PTR;
7393 parms.u.pointer.addr = pMapName;
7394 parms.u.pointer.size = sizeof(SHFLSTRING) + (uint16_t)cbString;
7395
7396 int vrc = m_pVMMDev->hgcmHostCall("VBoxSharedFolders",
7397 SHFL_FN_REMOVE_MAPPING,
7398 1, &parms);
7399 RTMemFree(pMapName);
7400 if (RT_FAILURE(vrc))
7401 return setError(E_FAIL,
7402 tr("Could not remove the shared folder '%s' (%Rrc)"),
7403 strName.c_str(), vrc);
7404
7405 return S_OK;
7406}
7407
7408/**
7409 * VM state callback function. Called by the VMM
7410 * using its state machine states.
7411 *
7412 * Primarily used to handle VM initiated power off, suspend and state saving,
7413 * but also for doing termination completed work (VMSTATE_TERMINATE).
7414 *
7415 * In general this function is called in the context of the EMT.
7416 *
7417 * @param aVM The VM handle.
7418 * @param aState The new state.
7419 * @param aOldState The old state.
7420 * @param aUser The user argument (pointer to the Console object).
7421 *
7422 * @note Locks the Console object for writing.
7423 */
7424DECLCALLBACK(void) Console::vmstateChangeCallback(PVM aVM,
7425 VMSTATE aState,
7426 VMSTATE aOldState,
7427 void *aUser)
7428{
7429 LogFlowFunc(("Changing state from %s to %s (aVM=%p)\n",
7430 VMR3GetStateName(aOldState), VMR3GetStateName(aState), aVM));
7431
7432 Console *that = static_cast<Console *>(aUser);
7433 AssertReturnVoid(that);
7434
7435 AutoCaller autoCaller(that);
7436
7437 /* Note that we must let this method proceed even if Console::uninit() has
7438 * been already called. In such case this VMSTATE change is a result of:
7439 * 1) powerDown() called from uninit() itself, or
7440 * 2) VM-(guest-)initiated power off. */
7441 AssertReturnVoid( autoCaller.isOk()
7442 || autoCaller.state() == InUninit);
7443
7444 switch (aState)
7445 {
7446 /*
7447 * The VM has terminated
7448 */
7449 case VMSTATE_OFF:
7450 {
7451 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
7452
7453 if (that->mVMStateChangeCallbackDisabled)
7454 break;
7455
7456 /* Do we still think that it is running? It may happen if this is a
7457 * VM-(guest-)initiated shutdown/poweroff.
7458 */
7459 if ( that->mMachineState != MachineState_Stopping
7460 && that->mMachineState != MachineState_Saving
7461 && that->mMachineState != MachineState_Restoring
7462 && that->mMachineState != MachineState_TeleportingIn
7463 && that->mMachineState != MachineState_FaultTolerantSyncing
7464 && that->mMachineState != MachineState_TeleportingPausedVM
7465 && !that->mVMIsAlreadyPoweringOff
7466 )
7467 {
7468 LogFlowFunc(("VM has powered itself off but Console still thinks it is running. Notifying.\n"));
7469
7470 /* prevent powerDown() from calling VMR3PowerOff() again */
7471 Assert(that->mVMPoweredOff == false);
7472 that->mVMPoweredOff = true;
7473
7474 /*
7475 * request a progress object from the server
7476 * (this will set the machine state to Stopping on the server
7477 * to block others from accessing this machine)
7478 */
7479 ComPtr<IProgress> pProgress;
7480 HRESULT rc = that->mControl->BeginPoweringDown(pProgress.asOutParam());
7481 AssertComRC(rc);
7482
7483 /* sync the state with the server */
7484 that->setMachineStateLocally(MachineState_Stopping);
7485
7486 /* Setup task object and thread to carry out the operation
7487 * asynchronously (if we call powerDown() right here but there
7488 * is one or more mpVM callers (added with addVMCaller()) we'll
7489 * deadlock).
7490 */
7491 std::auto_ptr<VMPowerDownTask> task(new VMPowerDownTask(that,
7492 pProgress));
7493
7494 /* If creating a task failed, this can currently mean one of
7495 * two: either Console::uninit() has been called just a ms
7496 * before (so a powerDown() call is already on the way), or
7497 * powerDown() itself is being already executed. Just do
7498 * nothing.
7499 */
7500 if (!task->isOk())
7501 {
7502 LogFlowFunc(("Console is already being uninitialized.\n"));
7503 break;
7504 }
7505
7506 int vrc = RTThreadCreate(NULL, Console::powerDownThread,
7507 (void *) task.get(), 0,
7508 RTTHREADTYPE_MAIN_WORKER, 0,
7509 "VMPwrDwn");
7510 AssertMsgRCBreak(vrc, ("Could not create VMPowerDown thread (%Rrc)\n", vrc));
7511
7512 /* task is now owned by powerDownThread(), so release it */
7513 task.release();
7514 }
7515 break;
7516 }
7517
7518 /* The VM has been completely destroyed.
7519 *
7520 * Note: This state change can happen at two points:
7521 * 1) At the end of VMR3Destroy() if it was not called from EMT.
7522 * 2) At the end of vmR3EmulationThread if VMR3Destroy() was
7523 * called by EMT.
7524 */
7525 case VMSTATE_TERMINATED:
7526 {
7527 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
7528
7529 if (that->mVMStateChangeCallbackDisabled)
7530 break;
7531
7532 /* Terminate host interface networking. If aVM is NULL, we've been
7533 * manually called from powerUpThread() either before calling
7534 * VMR3Create() or after VMR3Create() failed, so no need to touch
7535 * networking.
7536 */
7537 if (aVM)
7538 that->powerDownHostInterfaces();
7539
7540 /* From now on the machine is officially powered down or remains in
7541 * the Saved state.
7542 */
7543 switch (that->mMachineState)
7544 {
7545 default:
7546 AssertFailed();
7547 /* fall through */
7548 case MachineState_Stopping:
7549 /* successfully powered down */
7550 that->setMachineState(MachineState_PoweredOff);
7551 break;
7552 case MachineState_Saving:
7553 /* successfully saved */
7554 that->setMachineState(MachineState_Saved);
7555 break;
7556 case MachineState_Starting:
7557 /* failed to start, but be patient: set back to PoweredOff
7558 * (for similarity with the below) */
7559 that->setMachineState(MachineState_PoweredOff);
7560 break;
7561 case MachineState_Restoring:
7562 /* failed to load the saved state file, but be patient: set
7563 * back to Saved (to preserve the saved state file) */
7564 that->setMachineState(MachineState_Saved);
7565 break;
7566 case MachineState_TeleportingIn:
7567 /* Teleportation failed or was canceled. Back to powered off. */
7568 that->setMachineState(MachineState_PoweredOff);
7569 break;
7570 case MachineState_TeleportingPausedVM:
7571 /* Successfully teleported the VM. */
7572 that->setMachineState(MachineState_Teleported);
7573 break;
7574 case MachineState_FaultTolerantSyncing:
7575 /* Fault tolerant sync failed or was canceled. Back to powered off. */
7576 that->setMachineState(MachineState_PoweredOff);
7577 break;
7578 }
7579 break;
7580 }
7581
7582 case VMSTATE_RESETTING:
7583 {
7584#ifdef VBOX_WITH_GUEST_PROPS
7585 /* Do not take any read/write locks here! */
7586 that->guestPropertiesHandleVMReset();
7587#endif
7588 break;
7589 }
7590
7591 case VMSTATE_SUSPENDED:
7592 {
7593 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
7594
7595 if (that->mVMStateChangeCallbackDisabled)
7596 break;
7597
7598 switch (that->mMachineState)
7599 {
7600 case MachineState_Teleporting:
7601 that->setMachineState(MachineState_TeleportingPausedVM);
7602 break;
7603
7604 case MachineState_LiveSnapshotting:
7605 that->setMachineState(MachineState_Saving);
7606 break;
7607
7608 case MachineState_TeleportingPausedVM:
7609 case MachineState_Saving:
7610 case MachineState_Restoring:
7611 case MachineState_Stopping:
7612 case MachineState_TeleportingIn:
7613 case MachineState_FaultTolerantSyncing:
7614 /* The worker thread handles the transition. */
7615 break;
7616
7617 default:
7618 AssertMsgFailed(("%s\n", Global::stringifyMachineState(that->mMachineState)));
7619 case MachineState_Running:
7620 that->setMachineState(MachineState_Paused);
7621 break;
7622
7623 case MachineState_Paused:
7624 /* Nothing to do. */
7625 break;
7626 }
7627 break;
7628 }
7629
7630 case VMSTATE_SUSPENDED_LS:
7631 case VMSTATE_SUSPENDED_EXT_LS:
7632 {
7633 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
7634 if (that->mVMStateChangeCallbackDisabled)
7635 break;
7636 switch (that->mMachineState)
7637 {
7638 case MachineState_Teleporting:
7639 that->setMachineState(MachineState_TeleportingPausedVM);
7640 break;
7641
7642 case MachineState_LiveSnapshotting:
7643 that->setMachineState(MachineState_Saving);
7644 break;
7645
7646 case MachineState_TeleportingPausedVM:
7647 case MachineState_Saving:
7648 /* ignore */
7649 break;
7650
7651 default:
7652 AssertMsgFailed(("%s/%s -> %s\n", Global::stringifyMachineState(that->mMachineState), VMR3GetStateName(aOldState), VMR3GetStateName(aState) ));
7653 that->setMachineState(MachineState_Paused);
7654 break;
7655 }
7656 break;
7657 }
7658
7659 case VMSTATE_RUNNING:
7660 {
7661 if ( aOldState == VMSTATE_POWERING_ON
7662 || aOldState == VMSTATE_RESUMING
7663 || aOldState == VMSTATE_RUNNING_FT)
7664 {
7665 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
7666
7667 if (that->mVMStateChangeCallbackDisabled)
7668 break;
7669
7670 Assert( ( ( that->mMachineState == MachineState_Starting
7671 || that->mMachineState == MachineState_Paused)
7672 && aOldState == VMSTATE_POWERING_ON)
7673 || ( ( that->mMachineState == MachineState_Restoring
7674 || that->mMachineState == MachineState_TeleportingIn
7675 || that->mMachineState == MachineState_Paused
7676 || that->mMachineState == MachineState_Saving
7677 )
7678 && aOldState == VMSTATE_RESUMING)
7679 || ( that->mMachineState == MachineState_FaultTolerantSyncing
7680 && aOldState == VMSTATE_RUNNING_FT));
7681
7682 that->setMachineState(MachineState_Running);
7683 }
7684
7685 break;
7686 }
7687
7688 case VMSTATE_RUNNING_LS:
7689 AssertMsg( that->mMachineState == MachineState_LiveSnapshotting
7690 || that->mMachineState == MachineState_Teleporting,
7691 ("%s/%s -> %s\n", Global::stringifyMachineState(that->mMachineState), VMR3GetStateName(aOldState), VMR3GetStateName(aState) ));
7692 break;
7693
7694 case VMSTATE_RUNNING_FT:
7695 AssertMsg(that->mMachineState == MachineState_FaultTolerantSyncing,
7696 ("%s/%s -> %s\n", Global::stringifyMachineState(that->mMachineState), VMR3GetStateName(aOldState), VMR3GetStateName(aState) ));
7697 break;
7698
7699 case VMSTATE_FATAL_ERROR:
7700 {
7701 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
7702
7703 if (that->mVMStateChangeCallbackDisabled)
7704 break;
7705
7706 /* Fatal errors are only for running VMs. */
7707 Assert(Global::IsOnline(that->mMachineState));
7708
7709 /* Note! 'Pause' is used here in want of something better. There
7710 * are currently only two places where fatal errors might be
7711 * raised, so it is not worth adding a new externally
7712 * visible state for this yet. */
7713 that->setMachineState(MachineState_Paused);
7714 break;
7715 }
7716
7717 case VMSTATE_GURU_MEDITATION:
7718 {
7719 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
7720
7721 if (that->mVMStateChangeCallbackDisabled)
7722 break;
7723
7724 /* Guru are only for running VMs */
7725 Assert(Global::IsOnline(that->mMachineState));
7726
7727 that->setMachineState(MachineState_Stuck);
7728 break;
7729 }
7730
7731 default: /* shut up gcc */
7732 break;
7733 }
7734}
7735
7736/**
7737 * Changes the clipboard mode.
7738 *
7739 * @param aClipboardMode new clipboard mode.
7740 */
7741void Console::changeClipboardMode(ClipboardMode_T aClipboardMode)
7742{
7743 VMMDev *pVMMDev = m_pVMMDev;
7744 Assert(pVMMDev);
7745
7746 VBOXHGCMSVCPARM parm;
7747 parm.type = VBOX_HGCM_SVC_PARM_32BIT;
7748
7749 switch (aClipboardMode)
7750 {
7751 default:
7752 case ClipboardMode_Disabled:
7753 LogRel(("Shared clipboard mode: Off\n"));
7754 parm.u.uint32 = VBOX_SHARED_CLIPBOARD_MODE_OFF;
7755 break;
7756 case ClipboardMode_GuestToHost:
7757 LogRel(("Shared clipboard mode: Guest to Host\n"));
7758 parm.u.uint32 = VBOX_SHARED_CLIPBOARD_MODE_GUEST_TO_HOST;
7759 break;
7760 case ClipboardMode_HostToGuest:
7761 LogRel(("Shared clipboard mode: Host to Guest\n"));
7762 parm.u.uint32 = VBOX_SHARED_CLIPBOARD_MODE_HOST_TO_GUEST;
7763 break;
7764 case ClipboardMode_Bidirectional:
7765 LogRel(("Shared clipboard mode: Bidirectional\n"));
7766 parm.u.uint32 = VBOX_SHARED_CLIPBOARD_MODE_BIDIRECTIONAL;
7767 break;
7768 }
7769
7770 pVMMDev->hgcmHostCall("VBoxSharedClipboard", VBOX_SHARED_CLIPBOARD_HOST_FN_SET_MODE, 1, &parm);
7771}
7772
7773/**
7774 * Changes the drag'n_drop mode.
7775 *
7776 * @param aDragAndDropMode new drag'n'drop mode.
7777 */
7778void Console::changeDragAndDropMode(DragAndDropMode_T aDragAndDropMode)
7779{
7780 VMMDev *pVMMDev = m_pVMMDev;
7781 Assert(pVMMDev);
7782
7783 VBOXHGCMSVCPARM parm;
7784 parm.type = VBOX_HGCM_SVC_PARM_32BIT;
7785
7786 switch (aDragAndDropMode)
7787 {
7788 default:
7789 case DragAndDropMode_Disabled:
7790 LogRel(("Drag'n'drop mode: Off\n"));
7791 parm.u.uint32 = VBOX_DRAG_AND_DROP_MODE_OFF;
7792 break;
7793 case ClipboardMode_GuestToHost:
7794 LogRel(("Drag'n'drop mode: Guest to Host\n"));
7795 parm.u.uint32 = VBOX_DRAG_AND_DROP_MODE_GUEST_TO_HOST;
7796 break;
7797 case ClipboardMode_HostToGuest:
7798 LogRel(("Drag'n'drop mode: Host to Guest\n"));
7799 parm.u.uint32 = VBOX_DRAG_AND_DROP_MODE_HOST_TO_GUEST;
7800 break;
7801 case ClipboardMode_Bidirectional:
7802 LogRel(("Drag'n'drop mode: Bidirectional\n"));
7803 parm.u.uint32 = VBOX_DRAG_AND_DROP_MODE_BIDIRECTIONAL;
7804 break;
7805 }
7806
7807 pVMMDev->hgcmHostCall("VBoxDragAndDropSvc", DragAndDropSvc::HOST_DND_SET_MODE, 1, &parm);
7808}
7809
7810#ifdef VBOX_WITH_USB
7811/**
7812 * Sends a request to VMM to attach the given host device.
7813 * After this method succeeds, the attached device will appear in the
7814 * mUSBDevices collection.
7815 *
7816 * @param aHostDevice device to attach
7817 *
7818 * @note Synchronously calls EMT.
7819 */
7820HRESULT Console::attachUSBDevice(IUSBDevice *aHostDevice, ULONG aMaskedIfs)
7821{
7822 AssertReturn(aHostDevice, E_FAIL);
7823 AssertReturn(!isWriteLockOnCurrentThread(), E_FAIL);
7824
7825 HRESULT hrc;
7826
7827 /*
7828 * Get the address and the Uuid, and call the pfnCreateProxyDevice roothub
7829 * method in EMT (using usbAttachCallback()).
7830 */
7831 Bstr BstrAddress;
7832 hrc = aHostDevice->COMGETTER(Address)(BstrAddress.asOutParam());
7833 ComAssertComRCRetRC(hrc);
7834
7835 Utf8Str Address(BstrAddress);
7836
7837 Bstr id;
7838 hrc = aHostDevice->COMGETTER(Id)(id.asOutParam());
7839 ComAssertComRCRetRC(hrc);
7840 Guid uuid(id);
7841
7842 BOOL fRemote = FALSE;
7843 hrc = aHostDevice->COMGETTER(Remote)(&fRemote);
7844 ComAssertComRCRetRC(hrc);
7845
7846 /* Get the VM handle. */
7847 SafeVMPtr ptrVM(this);
7848 if (!ptrVM.isOk())
7849 return ptrVM.rc();
7850
7851 LogFlowThisFunc(("Proxying USB device '%s' {%RTuuid}...\n",
7852 Address.c_str(), uuid.raw()));
7853
7854 void *pvRemoteBackend = NULL;
7855 if (fRemote)
7856 {
7857 RemoteUSBDevice *pRemoteUSBDevice = static_cast<RemoteUSBDevice *>(aHostDevice);
7858 pvRemoteBackend = consoleVRDPServer()->USBBackendRequestPointer(pRemoteUSBDevice->clientId(), &uuid);
7859 if (!pvRemoteBackend)
7860 return E_INVALIDARG; /* The clientId is invalid then. */
7861 }
7862
7863 USHORT portVersion = 1;
7864 hrc = aHostDevice->COMGETTER(PortVersion)(&portVersion);
7865 AssertComRCReturnRC(hrc);
7866 Assert(portVersion == 1 || portVersion == 2);
7867
7868 int vrc = VMR3ReqCallWait(ptrVM, 0 /* idDstCpu (saved state, see #6232) */,
7869 (PFNRT)usbAttachCallback, 9,
7870 this, ptrVM.raw(), aHostDevice, uuid.raw(), fRemote, Address.c_str(), pvRemoteBackend, portVersion, aMaskedIfs);
7871
7872 if (RT_SUCCESS(vrc))
7873 {
7874 /* Create a OUSBDevice and add it to the device list */
7875 ComObjPtr<OUSBDevice> pUSBDevice;
7876 pUSBDevice.createObject();
7877 hrc = pUSBDevice->init(aHostDevice);
7878 AssertComRC(hrc);
7879
7880 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7881 mUSBDevices.push_back(pUSBDevice);
7882 LogFlowFunc(("Attached device {%RTuuid}\n", pUSBDevice->id().raw()));
7883
7884 /* notify callbacks */
7885 alock.release();
7886 onUSBDeviceStateChange(pUSBDevice, true /* aAttached */, NULL);
7887 }
7888 else
7889 {
7890 LogWarningThisFunc(("Failed to create proxy device for '%s' {%RTuuid} (%Rrc)\n",
7891 Address.c_str(), uuid.raw(), vrc));
7892
7893 switch (vrc)
7894 {
7895 case VERR_VUSB_NO_PORTS:
7896 hrc = setError(E_FAIL,
7897 tr("Failed to attach the USB device. (No available ports on the USB controller)."));
7898 break;
7899 case VERR_VUSB_USBFS_PERMISSION:
7900 hrc = setError(E_FAIL,
7901 tr("Not permitted to open the USB device, check usbfs options"));
7902 break;
7903 default:
7904 hrc = setError(E_FAIL,
7905 tr("Failed to create a proxy device for the USB device. (Error: %Rrc)"),
7906 vrc);
7907 break;
7908 }
7909 }
7910
7911 return hrc;
7912}
7913
7914/**
7915 * USB device attach callback used by AttachUSBDevice().
7916 * Note that AttachUSBDevice() doesn't return until this callback is executed,
7917 * so we don't use AutoCaller and don't care about reference counters of
7918 * interface pointers passed in.
7919 *
7920 * @thread EMT
7921 * @note Locks the console object for writing.
7922 */
7923//static
7924DECLCALLBACK(int)
7925Console::usbAttachCallback(Console *that, PVM pVM, IUSBDevice *aHostDevice, PCRTUUID aUuid, bool aRemote, const char *aAddress, void *pvRemoteBackend, USHORT aPortVersion, ULONG aMaskedIfs)
7926{
7927 LogFlowFuncEnter();
7928 LogFlowFunc(("that={%p} aUuid={%RTuuid}\n", that, aUuid));
7929
7930 AssertReturn(that && aUuid, VERR_INVALID_PARAMETER);
7931 AssertReturn(!that->isWriteLockOnCurrentThread(), VERR_GENERAL_FAILURE);
7932
7933 int vrc = PDMR3USBCreateProxyDevice(pVM, aUuid, aRemote, aAddress, pvRemoteBackend,
7934 aPortVersion == 1 ? VUSB_STDVER_11 : VUSB_STDVER_20, aMaskedIfs);
7935 LogFlowFunc(("vrc=%Rrc\n", vrc));
7936 LogFlowFuncLeave();
7937 return vrc;
7938}
7939
7940/**
7941 * Sends a request to VMM to detach the given host device. After this method
7942 * succeeds, the detached device will disappear from the mUSBDevices
7943 * collection.
7944 *
7945 * @param aHostDevice device to attach
7946 *
7947 * @note Synchronously calls EMT.
7948 */
7949HRESULT Console::detachUSBDevice(const ComObjPtr<OUSBDevice> &aHostDevice)
7950{
7951 AssertReturn(!isWriteLockOnCurrentThread(), E_FAIL);
7952
7953 /* Get the VM handle. */
7954 SafeVMPtr ptrVM(this);
7955 if (!ptrVM.isOk())
7956 return ptrVM.rc();
7957
7958 /* if the device is attached, then there must at least one USB hub. */
7959 AssertReturn(PDMR3USBHasHub(ptrVM), E_FAIL);
7960
7961 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7962 LogFlowThisFunc(("Detaching USB proxy device {%RTuuid}...\n",
7963 aHostDevice->id().raw()));
7964
7965 /*
7966 * If this was a remote device, release the backend pointer.
7967 * The pointer was requested in usbAttachCallback.
7968 */
7969 BOOL fRemote = FALSE;
7970
7971 HRESULT hrc2 = aHostDevice->COMGETTER(Remote)(&fRemote);
7972 if (FAILED(hrc2))
7973 setErrorStatic(hrc2, "GetRemote() failed");
7974
7975 PCRTUUID pUuid = aHostDevice->id().raw();
7976 if (fRemote)
7977 {
7978 Guid guid(*pUuid);
7979 consoleVRDPServer()->USBBackendReleasePointer(&guid);
7980 }
7981
7982 alock.release();
7983 int vrc = VMR3ReqCallWait(ptrVM, 0 /* idDstCpu (saved state, see #6232) */,
7984 (PFNRT)usbDetachCallback, 5,
7985 this, ptrVM.raw(), pUuid);
7986 if (RT_SUCCESS(vrc))
7987 {
7988 LogFlowFunc(("Detached device {%RTuuid}\n", pUuid));
7989
7990 /* notify callbacks */
7991 onUSBDeviceStateChange(aHostDevice, false /* aAttached */, NULL);
7992 }
7993
7994 ComAssertRCRet(vrc, E_FAIL);
7995
7996 return S_OK;
7997}
7998
7999/**
8000 * USB device detach callback used by DetachUSBDevice().
8001 * Note that DetachUSBDevice() doesn't return until this callback is executed,
8002 * so we don't use AutoCaller and don't care about reference counters of
8003 * interface pointers passed in.
8004 *
8005 * @thread EMT
8006 */
8007//static
8008DECLCALLBACK(int)
8009Console::usbDetachCallback(Console *that, PVM pVM, PCRTUUID aUuid)
8010{
8011 LogFlowFuncEnter();
8012 LogFlowFunc(("that={%p} aUuid={%RTuuid}\n", that, aUuid));
8013
8014 AssertReturn(that && aUuid, VERR_INVALID_PARAMETER);
8015 AssertReturn(!that->isWriteLockOnCurrentThread(), VERR_GENERAL_FAILURE);
8016
8017 int vrc = PDMR3USBDetachDevice(pVM, aUuid);
8018
8019 LogFlowFunc(("vrc=%Rrc\n", vrc));
8020 LogFlowFuncLeave();
8021 return vrc;
8022}
8023#endif /* VBOX_WITH_USB */
8024
8025/* Note: FreeBSD needs this whether netflt is used or not. */
8026#if ((defined(RT_OS_LINUX) && !defined(VBOX_WITH_NETFLT)) || defined(RT_OS_FREEBSD))
8027/**
8028 * Helper function to handle host interface device creation and attachment.
8029 *
8030 * @param networkAdapter the network adapter which attachment should be reset
8031 * @return COM status code
8032 *
8033 * @note The caller must lock this object for writing.
8034 *
8035 * @todo Move this back into the driver!
8036 */
8037HRESULT Console::attachToTapInterface(INetworkAdapter *networkAdapter)
8038{
8039 LogFlowThisFunc(("\n"));
8040 /* sanity check */
8041 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
8042
8043# ifdef VBOX_STRICT
8044 /* paranoia */
8045 NetworkAttachmentType_T attachment;
8046 networkAdapter->COMGETTER(AttachmentType)(&attachment);
8047 Assert(attachment == NetworkAttachmentType_Bridged);
8048# endif /* VBOX_STRICT */
8049
8050 HRESULT rc = S_OK;
8051
8052 ULONG slot = 0;
8053 rc = networkAdapter->COMGETTER(Slot)(&slot);
8054 AssertComRC(rc);
8055
8056# ifdef RT_OS_LINUX
8057 /*
8058 * Allocate a host interface device
8059 */
8060 int rcVBox = RTFileOpen(&maTapFD[slot], "/dev/net/tun",
8061 RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_INHERIT);
8062 if (RT_SUCCESS(rcVBox))
8063 {
8064 /*
8065 * Set/obtain the tap interface.
8066 */
8067 struct ifreq IfReq;
8068 memset(&IfReq, 0, sizeof(IfReq));
8069 /* The name of the TAP interface we are using */
8070 Bstr tapDeviceName;
8071 rc = networkAdapter->COMGETTER(BridgedInterface)(tapDeviceName.asOutParam());
8072 if (FAILED(rc))
8073 tapDeviceName.setNull(); /* Is this necessary? */
8074 if (tapDeviceName.isEmpty())
8075 {
8076 LogRel(("No TAP device name was supplied.\n"));
8077 rc = setError(E_FAIL, tr("No TAP device name was supplied for the host networking interface"));
8078 }
8079
8080 if (SUCCEEDED(rc))
8081 {
8082 /* If we are using a static TAP device then try to open it. */
8083 Utf8Str str(tapDeviceName);
8084 if (str.length() <= sizeof(IfReq.ifr_name))
8085 strcpy(IfReq.ifr_name, str.c_str());
8086 else
8087 memcpy(IfReq.ifr_name, str.c_str(), sizeof(IfReq.ifr_name) - 1); /** @todo bitch about names which are too long... */
8088 IfReq.ifr_flags = IFF_TAP | IFF_NO_PI;
8089 rcVBox = ioctl(maTapFD[slot], TUNSETIFF, &IfReq);
8090 if (rcVBox != 0)
8091 {
8092 LogRel(("Failed to open the host network interface %ls\n", tapDeviceName.raw()));
8093 rc = setError(E_FAIL,
8094 tr("Failed to open the host network interface %ls"),
8095 tapDeviceName.raw());
8096 }
8097 }
8098 if (SUCCEEDED(rc))
8099 {
8100 /*
8101 * Make it pollable.
8102 */
8103 if (fcntl(maTapFD[slot], F_SETFL, O_NONBLOCK) != -1)
8104 {
8105 Log(("attachToTapInterface: %RTfile %ls\n", maTapFD[slot], tapDeviceName.raw()));
8106 /*
8107 * Here is the right place to communicate the TAP file descriptor and
8108 * the host interface name to the server if/when it becomes really
8109 * necessary.
8110 */
8111 maTAPDeviceName[slot] = tapDeviceName;
8112 rcVBox = VINF_SUCCESS;
8113 }
8114 else
8115 {
8116 int iErr = errno;
8117
8118 LogRel(("Configuration error: Failed to configure /dev/net/tun non blocking. Error: %s\n", strerror(iErr)));
8119 rcVBox = VERR_HOSTIF_BLOCKING;
8120 rc = setError(E_FAIL,
8121 tr("could not set up the host networking device for non blocking access: %s"),
8122 strerror(errno));
8123 }
8124 }
8125 }
8126 else
8127 {
8128 LogRel(("Configuration error: Failed to open /dev/net/tun rc=%Rrc\n", rcVBox));
8129 switch (rcVBox)
8130 {
8131 case VERR_ACCESS_DENIED:
8132 /* will be handled by our caller */
8133 rc = rcVBox;
8134 break;
8135 default:
8136 rc = setError(E_FAIL,
8137 tr("Could not set up the host networking device: %Rrc"),
8138 rcVBox);
8139 break;
8140 }
8141 }
8142
8143# elif defined(RT_OS_FREEBSD)
8144 /*
8145 * Set/obtain the tap interface.
8146 */
8147 /* The name of the TAP interface we are using */
8148 Bstr tapDeviceName;
8149 rc = networkAdapter->COMGETTER(BridgedInterface)(tapDeviceName.asOutParam());
8150 if (FAILED(rc))
8151 tapDeviceName.setNull(); /* Is this necessary? */
8152 if (tapDeviceName.isEmpty())
8153 {
8154 LogRel(("No TAP device name was supplied.\n"));
8155 rc = setError(E_FAIL, tr("No TAP device name was supplied for the host networking interface"));
8156 }
8157 char szTapdev[1024] = "/dev/";
8158 /* If we are using a static TAP device then try to open it. */
8159 Utf8Str str(tapDeviceName);
8160 if (str.length() + strlen(szTapdev) <= sizeof(szTapdev))
8161 strcat(szTapdev, str.c_str());
8162 else
8163 memcpy(szTapdev + strlen(szTapdev), str.c_str(),
8164 sizeof(szTapdev) - strlen(szTapdev) - 1); /** @todo bitch about names which are too long... */
8165 int rcVBox = RTFileOpen(&maTapFD[slot], szTapdev,
8166 RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_INHERIT | RTFILE_O_NON_BLOCK);
8167
8168 if (RT_SUCCESS(rcVBox))
8169 maTAPDeviceName[slot] = tapDeviceName;
8170 else
8171 {
8172 switch (rcVBox)
8173 {
8174 case VERR_ACCESS_DENIED:
8175 /* will be handled by our caller */
8176 rc = rcVBox;
8177 break;
8178 default:
8179 rc = setError(E_FAIL,
8180 tr("Failed to open the host network interface %ls"),
8181 tapDeviceName.raw());
8182 break;
8183 }
8184 }
8185# else
8186# error "huh?"
8187# endif
8188 /* in case of failure, cleanup. */
8189 if (RT_FAILURE(rcVBox) && SUCCEEDED(rc))
8190 {
8191 LogRel(("General failure attaching to host interface\n"));
8192 rc = setError(E_FAIL,
8193 tr("General failure attaching to host interface"));
8194 }
8195 LogFlowThisFunc(("rc=%d\n", rc));
8196 return rc;
8197}
8198
8199
8200/**
8201 * Helper function to handle detachment from a host interface
8202 *
8203 * @param networkAdapter the network adapter which attachment should be reset
8204 * @return COM status code
8205 *
8206 * @note The caller must lock this object for writing.
8207 *
8208 * @todo Move this back into the driver!
8209 */
8210HRESULT Console::detachFromTapInterface(INetworkAdapter *networkAdapter)
8211{
8212 /* sanity check */
8213 LogFlowThisFunc(("\n"));
8214 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
8215
8216 HRESULT rc = S_OK;
8217# ifdef VBOX_STRICT
8218 /* paranoia */
8219 NetworkAttachmentType_T attachment;
8220 networkAdapter->COMGETTER(AttachmentType)(&attachment);
8221 Assert(attachment == NetworkAttachmentType_Bridged);
8222# endif /* VBOX_STRICT */
8223
8224 ULONG slot = 0;
8225 rc = networkAdapter->COMGETTER(Slot)(&slot);
8226 AssertComRC(rc);
8227
8228 /* is there an open TAP device? */
8229 if (maTapFD[slot] != NIL_RTFILE)
8230 {
8231 /*
8232 * Close the file handle.
8233 */
8234 Bstr tapDeviceName, tapTerminateApplication;
8235 bool isStatic = true;
8236 rc = networkAdapter->COMGETTER(BridgedInterface)(tapDeviceName.asOutParam());
8237 if (FAILED(rc) || tapDeviceName.isEmpty())
8238 {
8239 /* If the name is empty, this is a dynamic TAP device, so close it now,
8240 so that the termination script can remove the interface. Otherwise we still
8241 need the FD to pass to the termination script. */
8242 isStatic = false;
8243 int rcVBox = RTFileClose(maTapFD[slot]);
8244 AssertRC(rcVBox);
8245 maTapFD[slot] = NIL_RTFILE;
8246 }
8247 if (isStatic)
8248 {
8249 /* If we are using a static TAP device, we close it now, after having called the
8250 termination script. */
8251 int rcVBox = RTFileClose(maTapFD[slot]);
8252 AssertRC(rcVBox);
8253 }
8254 /* the TAP device name and handle are no longer valid */
8255 maTapFD[slot] = NIL_RTFILE;
8256 maTAPDeviceName[slot] = "";
8257 }
8258 LogFlowThisFunc(("returning %d\n", rc));
8259 return rc;
8260}
8261#endif /* (RT_OS_LINUX || RT_OS_FREEBSD) && !VBOX_WITH_NETFLT */
8262
8263/**
8264 * Called at power down to terminate host interface networking.
8265 *
8266 * @note The caller must lock this object for writing.
8267 */
8268HRESULT Console::powerDownHostInterfaces()
8269{
8270 LogFlowThisFunc(("\n"));
8271
8272 /* sanity check */
8273 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
8274
8275 /*
8276 * host interface termination handling
8277 */
8278 HRESULT rc = S_OK;
8279 ComPtr<IVirtualBox> pVirtualBox;
8280 mMachine->COMGETTER(Parent)(pVirtualBox.asOutParam());
8281 ComPtr<ISystemProperties> pSystemProperties;
8282 if (pVirtualBox)
8283 pVirtualBox->COMGETTER(SystemProperties)(pSystemProperties.asOutParam());
8284 ChipsetType_T chipsetType = ChipsetType_PIIX3;
8285 mMachine->COMGETTER(ChipsetType)(&chipsetType);
8286 ULONG maxNetworkAdapters = 0;
8287 if (pSystemProperties)
8288 pSystemProperties->GetMaxNetworkAdapters(chipsetType, &maxNetworkAdapters);
8289
8290 for (ULONG slot = 0; slot < maxNetworkAdapters; slot++)
8291 {
8292 ComPtr<INetworkAdapter> pNetworkAdapter;
8293 rc = mMachine->GetNetworkAdapter(slot, pNetworkAdapter.asOutParam());
8294 if (FAILED(rc)) break;
8295
8296 BOOL enabled = FALSE;
8297 pNetworkAdapter->COMGETTER(Enabled)(&enabled);
8298 if (!enabled)
8299 continue;
8300
8301 NetworkAttachmentType_T attachment;
8302 pNetworkAdapter->COMGETTER(AttachmentType)(&attachment);
8303 if (attachment == NetworkAttachmentType_Bridged)
8304 {
8305#if ((defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD)) && !defined(VBOX_WITH_NETFLT))
8306 HRESULT rc2 = detachFromTapInterface(pNetworkAdapter);
8307 if (FAILED(rc2) && SUCCEEDED(rc))
8308 rc = rc2;
8309#endif /* (RT_OS_LINUX || RT_OS_FREEBSD) && !VBOX_WITH_NETFLT */
8310 }
8311 }
8312
8313 return rc;
8314}
8315
8316
8317/**
8318 * Process callback handler for VMR3LoadFromFile, VMR3LoadFromStream, VMR3Save
8319 * and VMR3Teleport.
8320 *
8321 * @param pVM The VM handle.
8322 * @param uPercent Completion percentage (0-100).
8323 * @param pvUser Pointer to an IProgress instance.
8324 * @return VINF_SUCCESS.
8325 */
8326/*static*/
8327DECLCALLBACK(int) Console::stateProgressCallback(PVM pVM, unsigned uPercent, void *pvUser)
8328{
8329 IProgress *pProgress = static_cast<IProgress *>(pvUser);
8330
8331 /* update the progress object */
8332 if (pProgress)
8333 pProgress->SetCurrentOperationProgress(uPercent);
8334
8335 return VINF_SUCCESS;
8336}
8337
8338/**
8339 * @copydoc FNVMATERROR
8340 *
8341 * @remarks Might be some tiny serialization concerns with access to the string
8342 * object here...
8343 */
8344/*static*/ DECLCALLBACK(void)
8345Console::genericVMSetErrorCallback(PVM pVM, void *pvUser, int rc, RT_SRC_POS_DECL,
8346 const char *pszErrorFmt, va_list va)
8347{
8348 Utf8Str *pErrorText = (Utf8Str *)pvUser;
8349 AssertPtr(pErrorText);
8350
8351 /* We ignore RT_SRC_POS_DECL arguments to avoid confusion of end-users. */
8352 va_list va2;
8353 va_copy(va2, va);
8354
8355 /* Append to any the existing error message. */
8356 if (pErrorText->length())
8357 *pErrorText = Utf8StrFmt("%s.\n%N (%Rrc)", pErrorText->c_str(),
8358 pszErrorFmt, &va2, rc, rc);
8359 else
8360 *pErrorText = Utf8StrFmt("%N (%Rrc)", pszErrorFmt, &va2, rc, rc);
8361
8362 va_end(va2);
8363}
8364
8365/**
8366 * VM runtime error callback function.
8367 * See VMSetRuntimeError for the detailed description of parameters.
8368 *
8369 * @param pVM The VM handle.
8370 * @param pvUser The user argument.
8371 * @param fFlags The action flags. See VMSETRTERR_FLAGS_*.
8372 * @param pszErrorId Error ID string.
8373 * @param pszFormat Error message format string.
8374 * @param va Error message arguments.
8375 * @thread EMT.
8376 */
8377/* static */ DECLCALLBACK(void)
8378Console::setVMRuntimeErrorCallback(PVM pVM, void *pvUser, uint32_t fFlags,
8379 const char *pszErrorId,
8380 const char *pszFormat, va_list va)
8381{
8382 bool const fFatal = !!(fFlags & VMSETRTERR_FLAGS_FATAL);
8383 LogFlowFuncEnter();
8384
8385 Console *that = static_cast<Console *>(pvUser);
8386 AssertReturnVoid(that);
8387
8388 Utf8Str message(pszFormat, va);
8389
8390 LogRel(("Console: VM runtime error: fatal=%RTbool, errorID=%s message=\"%s\"\n",
8391 fFatal, pszErrorId, message.c_str()));
8392
8393 that->onRuntimeError(BOOL(fFatal), Bstr(pszErrorId).raw(),
8394 Bstr(message).raw());
8395
8396 LogFlowFuncLeave();
8397}
8398
8399/**
8400 * Captures USB devices that match filters of the VM.
8401 * Called at VM startup.
8402 *
8403 * @param pVM The VM handle.
8404 */
8405HRESULT Console::captureUSBDevices(PVM pVM)
8406{
8407 LogFlowThisFunc(("\n"));
8408
8409 /* sanity check */
8410 AssertReturn(!isWriteLockOnCurrentThread(), E_FAIL);
8411 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8412
8413 /* If the machine has an USB controller, ask the USB proxy service to
8414 * capture devices */
8415 PPDMIBASE pBase;
8416 int vrc = PDMR3QueryLun(pVM, "usb-ohci", 0, 0, &pBase);
8417 if (RT_SUCCESS(vrc))
8418 {
8419 /* release the lock before calling Host in VBoxSVC since Host may call
8420 * us back from under its lock (e.g. onUSBDeviceAttach()) which would
8421 * produce an inter-process dead-lock otherwise. */
8422 alock.release();
8423
8424 HRESULT hrc = mControl->AutoCaptureUSBDevices();
8425 ComAssertComRCRetRC(hrc);
8426 }
8427 else if ( vrc == VERR_PDM_DEVICE_NOT_FOUND
8428 || vrc == VERR_PDM_DEVICE_INSTANCE_NOT_FOUND)
8429 vrc = VINF_SUCCESS;
8430 else
8431 AssertRC(vrc);
8432
8433 return RT_SUCCESS(vrc) ? S_OK : E_FAIL;
8434}
8435
8436
8437/**
8438 * Detach all USB device which are attached to the VM for the
8439 * purpose of clean up and such like.
8440 */
8441void Console::detachAllUSBDevices(bool aDone)
8442{
8443 LogFlowThisFunc(("aDone=%RTbool\n", aDone));
8444
8445 /* sanity check */
8446 AssertReturnVoid(!isWriteLockOnCurrentThread());
8447 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8448
8449 mUSBDevices.clear();
8450
8451 /* release the lock before calling Host in VBoxSVC since Host may call
8452 * us back from under its lock (e.g. onUSBDeviceAttach()) which would
8453 * produce an inter-process dead-lock otherwise. */
8454 alock.release();
8455
8456 mControl->DetachAllUSBDevices(aDone);
8457}
8458
8459/**
8460 * @note Locks this object for writing.
8461 */
8462void Console::processRemoteUSBDevices(uint32_t u32ClientId, VRDEUSBDEVICEDESC *pDevList, uint32_t cbDevList, bool fDescExt)
8463{
8464 LogFlowThisFuncEnter();
8465 LogFlowThisFunc(("u32ClientId = %d, pDevList=%p, cbDevList = %d, fDescExt = %d\n", u32ClientId, pDevList, cbDevList, fDescExt));
8466
8467 AutoCaller autoCaller(this);
8468 if (!autoCaller.isOk())
8469 {
8470 /* Console has been already uninitialized, deny request */
8471 AssertMsgFailed(("Console is already uninitialized\n"));
8472 LogFlowThisFunc(("Console is already uninitialized\n"));
8473 LogFlowThisFuncLeave();
8474 return;
8475 }
8476
8477 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8478
8479 /*
8480 * Mark all existing remote USB devices as dirty.
8481 */
8482 for (RemoteUSBDeviceList::iterator it = mRemoteUSBDevices.begin();
8483 it != mRemoteUSBDevices.end();
8484 ++it)
8485 {
8486 (*it)->dirty(true);
8487 }
8488
8489 /*
8490 * Process the pDevList and add devices those are not already in the mRemoteUSBDevices list.
8491 */
8492 /** @todo (sunlover) REMOTE_USB Strict validation of the pDevList. */
8493 VRDEUSBDEVICEDESC *e = pDevList;
8494
8495 /* The cbDevList condition must be checked first, because the function can
8496 * receive pDevList = NULL and cbDevList = 0 on client disconnect.
8497 */
8498 while (cbDevList >= 2 && e->oNext)
8499 {
8500 /* Sanitize incoming strings in case they aren't valid UTF-8. */
8501 if (e->oManufacturer)
8502 RTStrPurgeEncoding((char *)e + e->oManufacturer);
8503 if (e->oProduct)
8504 RTStrPurgeEncoding((char *)e + e->oProduct);
8505 if (e->oSerialNumber)
8506 RTStrPurgeEncoding((char *)e + e->oSerialNumber);
8507
8508 LogFlowThisFunc(("vendor %04X, product %04X, name = %s\n",
8509 e->idVendor, e->idProduct,
8510 e->oProduct? (char *)e + e->oProduct: ""));
8511
8512 bool fNewDevice = true;
8513
8514 for (RemoteUSBDeviceList::iterator it = mRemoteUSBDevices.begin();
8515 it != mRemoteUSBDevices.end();
8516 ++it)
8517 {
8518 if ((*it)->devId() == e->id
8519 && (*it)->clientId() == u32ClientId)
8520 {
8521 /* The device is already in the list. */
8522 (*it)->dirty(false);
8523 fNewDevice = false;
8524 break;
8525 }
8526 }
8527
8528 if (fNewDevice)
8529 {
8530 LogRel(("Remote USB: ++++ Vendor %04X. Product %04X. Name = [%s].\n",
8531 e->idVendor, e->idProduct, e->oProduct? (char *)e + e->oProduct: ""));
8532
8533 /* Create the device object and add the new device to list. */
8534 ComObjPtr<RemoteUSBDevice> pUSBDevice;
8535 pUSBDevice.createObject();
8536 pUSBDevice->init(u32ClientId, e, fDescExt);
8537
8538 mRemoteUSBDevices.push_back(pUSBDevice);
8539
8540 /* Check if the device is ok for current USB filters. */
8541 BOOL fMatched = FALSE;
8542 ULONG fMaskedIfs = 0;
8543
8544 HRESULT hrc = mControl->RunUSBDeviceFilters(pUSBDevice, &fMatched, &fMaskedIfs);
8545
8546 AssertComRC(hrc);
8547
8548 LogFlowThisFunc(("USB filters return %d %#x\n", fMatched, fMaskedIfs));
8549
8550 if (fMatched)
8551 {
8552 alock.release();
8553 hrc = onUSBDeviceAttach(pUSBDevice, NULL, fMaskedIfs);
8554 alock.acquire();
8555
8556 /// @todo (r=dmik) warning reporting subsystem
8557
8558 if (hrc == S_OK)
8559 {
8560 LogFlowThisFunc(("Device attached\n"));
8561 pUSBDevice->captured(true);
8562 }
8563 }
8564 }
8565
8566 if (cbDevList < e->oNext)
8567 {
8568 LogWarningThisFunc(("cbDevList %d > oNext %d\n",
8569 cbDevList, e->oNext));
8570 break;
8571 }
8572
8573 cbDevList -= e->oNext;
8574
8575 e = (VRDEUSBDEVICEDESC *)((uint8_t *)e + e->oNext);
8576 }
8577
8578 /*
8579 * Remove dirty devices, that is those which are not reported by the server anymore.
8580 */
8581 for (;;)
8582 {
8583 ComObjPtr<RemoteUSBDevice> pUSBDevice;
8584
8585 RemoteUSBDeviceList::iterator it = mRemoteUSBDevices.begin();
8586 while (it != mRemoteUSBDevices.end())
8587 {
8588 if ((*it)->dirty())
8589 {
8590 pUSBDevice = *it;
8591 break;
8592 }
8593
8594 ++it;
8595 }
8596
8597 if (!pUSBDevice)
8598 {
8599 break;
8600 }
8601
8602 USHORT vendorId = 0;
8603 pUSBDevice->COMGETTER(VendorId)(&vendorId);
8604
8605 USHORT productId = 0;
8606 pUSBDevice->COMGETTER(ProductId)(&productId);
8607
8608 Bstr product;
8609 pUSBDevice->COMGETTER(Product)(product.asOutParam());
8610
8611 LogRel(("Remote USB: ---- Vendor %04X. Product %04X. Name = [%ls].\n",
8612 vendorId, productId, product.raw()));
8613
8614 /* Detach the device from VM. */
8615 if (pUSBDevice->captured())
8616 {
8617 Bstr uuid;
8618 pUSBDevice->COMGETTER(Id)(uuid.asOutParam());
8619 alock.release();
8620 onUSBDeviceDetach(uuid.raw(), NULL);
8621 alock.acquire();
8622 }
8623
8624 /* And remove it from the list. */
8625 mRemoteUSBDevices.erase(it);
8626 }
8627
8628 LogFlowThisFuncLeave();
8629}
8630
8631/**
8632 * Progress cancelation callback for fault tolerance VM poweron
8633 */
8634static void faultToleranceProgressCancelCallback(void *pvUser)
8635{
8636 PVM pVM = (PVM)pvUser;
8637
8638 if (pVM)
8639 FTMR3CancelStandby(pVM);
8640}
8641
8642/**
8643 * Thread function which starts the VM (also from saved state) and
8644 * track progress.
8645 *
8646 * @param Thread The thread id.
8647 * @param pvUser Pointer to a VMPowerUpTask structure.
8648 * @return VINF_SUCCESS (ignored).
8649 *
8650 * @note Locks the Console object for writing.
8651 */
8652/*static*/
8653DECLCALLBACK(int) Console::powerUpThread(RTTHREAD Thread, void *pvUser)
8654{
8655 LogFlowFuncEnter();
8656
8657 std::auto_ptr<VMPowerUpTask> task(static_cast<VMPowerUpTask *>(pvUser));
8658 AssertReturn(task.get(), VERR_INVALID_PARAMETER);
8659
8660 AssertReturn(!task->mConsole.isNull(), VERR_INVALID_PARAMETER);
8661 AssertReturn(!task->mProgress.isNull(), VERR_INVALID_PARAMETER);
8662
8663 VirtualBoxBase::initializeComForThread();
8664
8665 HRESULT rc = S_OK;
8666 int vrc = VINF_SUCCESS;
8667
8668 /* Set up a build identifier so that it can be seen from core dumps what
8669 * exact build was used to produce the core. */
8670 static char saBuildID[40];
8671 RTStrPrintf(saBuildID, sizeof(saBuildID), "%s%s%s%s VirtualBox %s r%u %s%s%s%s",
8672 "BU", "IL", "DI", "D", RTBldCfgVersion(), RTBldCfgRevision(), "BU", "IL", "DI", "D");
8673
8674 ComObjPtr<Console> pConsole = task->mConsole;
8675
8676 /* Note: no need to use addCaller() because VMPowerUpTask does that */
8677
8678 /* The lock is also used as a signal from the task initiator (which
8679 * releases it only after RTThreadCreate()) that we can start the job */
8680 AutoWriteLock alock(pConsole COMMA_LOCKVAL_SRC_POS);
8681
8682 /* sanity */
8683 Assert(pConsole->mpUVM == NULL);
8684
8685 try
8686 {
8687 // Create the VMM device object, which starts the HGCM thread; do this only
8688 // once for the console, for the pathological case that the same console
8689 // object is used to power up a VM twice. VirtualBox 4.0: we now do that
8690 // here instead of the Console constructor (see Console::init())
8691 if (!pConsole->m_pVMMDev)
8692 {
8693 pConsole->m_pVMMDev = new VMMDev(pConsole);
8694 AssertReturn(pConsole->m_pVMMDev, E_FAIL);
8695 }
8696
8697 /* wait for auto reset ops to complete so that we can successfully lock
8698 * the attached hard disks by calling LockMedia() below */
8699 for (VMPowerUpTask::ProgressList::const_iterator
8700 it = task->hardDiskProgresses.begin();
8701 it != task->hardDiskProgresses.end(); ++it)
8702 {
8703 HRESULT rc2 = (*it)->WaitForCompletion(-1);
8704 AssertComRC(rc2);
8705 }
8706
8707 /*
8708 * Lock attached media. This method will also check their accessibility.
8709 * If we're a teleporter, we'll have to postpone this action so we can
8710 * migrate between local processes.
8711 *
8712 * Note! The media will be unlocked automatically by
8713 * SessionMachine::setMachineState() when the VM is powered down.
8714 */
8715 if ( !task->mTeleporterEnabled
8716 && task->mEnmFaultToleranceState != FaultToleranceState_Standby)
8717 {
8718 rc = pConsole->mControl->LockMedia();
8719 if (FAILED(rc)) throw rc;
8720 }
8721
8722 /* Create the VRDP server. In case of headless operation, this will
8723 * also create the framebuffer, required at VM creation.
8724 */
8725 ConsoleVRDPServer *server = pConsole->consoleVRDPServer();
8726 Assert(server);
8727
8728 /* Does VRDP server call Console from the other thread?
8729 * Not sure (and can change), so release the lock just in case.
8730 */
8731 alock.release();
8732 vrc = server->Launch();
8733 alock.acquire();
8734
8735 if (vrc == VERR_NET_ADDRESS_IN_USE)
8736 {
8737 Utf8Str errMsg;
8738 Bstr bstr;
8739 pConsole->mVRDEServer->GetVRDEProperty(Bstr("TCP/Ports").raw(), bstr.asOutParam());
8740 Utf8Str ports = bstr;
8741 errMsg = Utf8StrFmt(tr("VirtualBox Remote Desktop Extension server can't bind to the port: %s"),
8742 ports.c_str());
8743 LogRel(("VRDE: Warning: failed to launch VRDE server (%Rrc): '%s'\n",
8744 vrc, errMsg.c_str()));
8745 }
8746 else if (vrc == VINF_NOT_SUPPORTED)
8747 {
8748 /* This means that the VRDE is not installed. */
8749 LogRel(("VRDE: VirtualBox Remote Desktop Extension is not available.\n"));
8750 }
8751 else if (RT_FAILURE(vrc))
8752 {
8753 /* Fail, if the server is installed but can't start. */
8754 Utf8Str errMsg;
8755 switch (vrc)
8756 {
8757 case VERR_FILE_NOT_FOUND:
8758 {
8759 /* VRDE library file is missing. */
8760 errMsg = Utf8StrFmt(tr("Could not find the VirtualBox Remote Desktop Extension library."));
8761 break;
8762 }
8763 default:
8764 errMsg = Utf8StrFmt(tr("Failed to launch Remote Desktop Extension server (%Rrc)"),
8765 vrc);
8766 }
8767 LogRel(("VRDE: Failed: (%Rrc), error message: '%s'\n",
8768 vrc, errMsg.c_str()));
8769 throw setErrorStatic(E_FAIL, errMsg.c_str());
8770 }
8771
8772 ComPtr<IMachine> pMachine = pConsole->machine();
8773 ULONG cCpus = 1;
8774 pMachine->COMGETTER(CPUCount)(&cCpus);
8775
8776 /*
8777 * Create the VM
8778 */
8779 PVM pVM;
8780 /*
8781 * release the lock since EMT will call Console. It's safe because
8782 * mMachineState is either Starting or Restoring state here.
8783 */
8784 alock.release();
8785
8786 vrc = VMR3Create(cCpus,
8787 pConsole->mpVmm2UserMethods,
8788 Console::genericVMSetErrorCallback,
8789 &task->mErrorMsg,
8790 task->mConfigConstructor,
8791 static_cast<Console *>(pConsole),
8792 &pVM);
8793
8794 alock.acquire();
8795
8796 /* Enable client connections to the server. */
8797 pConsole->consoleVRDPServer()->EnableConnections();
8798
8799 if (RT_SUCCESS(vrc))
8800 {
8801 do
8802 {
8803 /*
8804 * Register our load/save state file handlers
8805 */
8806 vrc = SSMR3RegisterExternal(pVM, sSSMConsoleUnit, 0 /*iInstance*/, sSSMConsoleVer, 0 /* cbGuess */,
8807 NULL, NULL, NULL,
8808 NULL, saveStateFileExec, NULL,
8809 NULL, loadStateFileExec, NULL,
8810 static_cast<Console *>(pConsole));
8811 AssertRCBreak(vrc);
8812
8813 vrc = static_cast<Console *>(pConsole)->getDisplay()->registerSSM(pVM);
8814 AssertRC(vrc);
8815 if (RT_FAILURE(vrc))
8816 break;
8817
8818 /*
8819 * Synchronize debugger settings
8820 */
8821 MachineDebugger *machineDebugger = pConsole->getMachineDebugger();
8822 if (machineDebugger)
8823 machineDebugger->flushQueuedSettings();
8824
8825 /*
8826 * Shared Folders
8827 */
8828 if (pConsole->m_pVMMDev->isShFlActive())
8829 {
8830 /* Does the code below call Console from the other thread?
8831 * Not sure, so release the lock just in case. */
8832 alock.release();
8833
8834 for (SharedFolderDataMap::const_iterator it = task->mSharedFolders.begin();
8835 it != task->mSharedFolders.end();
8836 ++it)
8837 {
8838 const SharedFolderData &d = it->second;
8839 rc = pConsole->createSharedFolder(it->first, d);
8840 if (FAILED(rc))
8841 {
8842 ErrorInfoKeeper eik;
8843 setVMRuntimeErrorCallbackF(pVM, pConsole, 0, "BrokenSharedFolder",
8844 N_("The shared folder '%s' could not be set up: %ls.\n"
8845 "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"),
8846 it->first.c_str(), eik.getText().raw());
8847 }
8848 }
8849 if (FAILED(rc))
8850 rc = S_OK; // do not fail with broken shared folders
8851
8852 /* acquire the lock again */
8853 alock.acquire();
8854 }
8855
8856 /* release the lock before a lengthy operation */
8857 alock.release();
8858
8859 /*
8860 * Capture USB devices.
8861 */
8862 rc = pConsole->captureUSBDevices(pVM);
8863 if (FAILED(rc)) break;
8864
8865 /* Load saved state? */
8866 if (task->mSavedStateFile.length())
8867 {
8868 LogFlowFunc(("Restoring saved state from '%s'...\n",
8869 task->mSavedStateFile.c_str()));
8870
8871 vrc = VMR3LoadFromFile(pVM,
8872 task->mSavedStateFile.c_str(),
8873 Console::stateProgressCallback,
8874 static_cast<IProgress *>(task->mProgress));
8875
8876 if (RT_SUCCESS(vrc))
8877 {
8878 if (task->mStartPaused)
8879 /* done */
8880 pConsole->setMachineState(MachineState_Paused);
8881 else
8882 {
8883 /* Start/Resume the VM execution */
8884#ifdef VBOX_WITH_EXTPACK
8885 vrc = pConsole->mptrExtPackManager->callAllVmPowerOnHooks(pConsole, pVM);
8886#endif
8887 if (RT_SUCCESS(vrc))
8888 vrc = VMR3Resume(pVM);
8889 AssertLogRelRC(vrc);
8890 }
8891 }
8892
8893 /* Power off in case we failed loading or resuming the VM */
8894 if (RT_FAILURE(vrc))
8895 {
8896 int vrc2 = VMR3PowerOff(pVM); AssertLogRelRC(vrc2);
8897#ifdef VBOX_WITH_EXTPACK
8898 pConsole->mptrExtPackManager->callAllVmPowerOffHooks(pConsole, pVM);
8899#endif
8900 }
8901 }
8902 else if (task->mTeleporterEnabled)
8903 {
8904 /* -> ConsoleImplTeleporter.cpp */
8905 bool fPowerOffOnFailure;
8906 rc = pConsole->teleporterTrg(VMR3GetUVM(pVM), pMachine, &task->mErrorMsg, task->mStartPaused,
8907 task->mProgress, &fPowerOffOnFailure);
8908 if (FAILED(rc) && fPowerOffOnFailure)
8909 {
8910 ErrorInfoKeeper eik;
8911 int vrc2 = VMR3PowerOff(pVM); AssertLogRelRC(vrc2);
8912#ifdef VBOX_WITH_EXTPACK
8913 pConsole->mptrExtPackManager->callAllVmPowerOffHooks(pConsole, pVM);
8914#endif
8915 }
8916 }
8917 else if (task->mEnmFaultToleranceState != FaultToleranceState_Inactive)
8918 {
8919 /*
8920 * Get the config.
8921 */
8922 ULONG uPort;
8923 ULONG uInterval;
8924 Bstr bstrAddress, bstrPassword;
8925
8926 rc = pMachine->COMGETTER(FaultTolerancePort)(&uPort);
8927 if (SUCCEEDED(rc))
8928 {
8929 rc = pMachine->COMGETTER(FaultToleranceSyncInterval)(&uInterval);
8930 if (SUCCEEDED(rc))
8931 rc = pMachine->COMGETTER(FaultToleranceAddress)(bstrAddress.asOutParam());
8932 if (SUCCEEDED(rc))
8933 rc = pMachine->COMGETTER(FaultTolerancePassword)(bstrPassword.asOutParam());
8934 }
8935 if (task->mProgress->setCancelCallback(faultToleranceProgressCancelCallback, pVM))
8936 {
8937 if (SUCCEEDED(rc))
8938 {
8939 Utf8Str strAddress(bstrAddress);
8940 const char *pszAddress = strAddress.isEmpty() ? NULL : strAddress.c_str();
8941 Utf8Str strPassword(bstrPassword);
8942 const char *pszPassword = strPassword.isEmpty() ? NULL : strPassword.c_str();
8943
8944 /* Power on the FT enabled VM. */
8945#ifdef VBOX_WITH_EXTPACK
8946 vrc = pConsole->mptrExtPackManager->callAllVmPowerOnHooks(pConsole, pVM);
8947#endif
8948 if (RT_SUCCESS(vrc))
8949 vrc = FTMR3PowerOn(pVM,
8950 task->mEnmFaultToleranceState == FaultToleranceState_Master /* fMaster */,
8951 uInterval,
8952 pszAddress,
8953 uPort,
8954 pszPassword);
8955 AssertLogRelRC(vrc);
8956 }
8957 task->mProgress->setCancelCallback(NULL, NULL);
8958 }
8959 else
8960 rc = E_FAIL;
8961 }
8962 else if (task->mStartPaused)
8963 /* done */
8964 pConsole->setMachineState(MachineState_Paused);
8965 else
8966 {
8967 /* Power on the VM (i.e. start executing) */
8968#ifdef VBOX_WITH_EXTPACK
8969 vrc = pConsole->mptrExtPackManager->callAllVmPowerOnHooks(pConsole, pVM);
8970#endif
8971 if (RT_SUCCESS(vrc))
8972 vrc = VMR3PowerOn(pVM);
8973 AssertLogRelRC(vrc);
8974 }
8975
8976 /* acquire the lock again */
8977 alock.acquire();
8978 }
8979 while (0);
8980
8981 /* On failure, destroy the VM */
8982 if (FAILED(rc) || RT_FAILURE(vrc))
8983 {
8984 /* preserve existing error info */
8985 ErrorInfoKeeper eik;
8986
8987 /* powerDown() will call VMR3Destroy() and do all necessary
8988 * cleanup (VRDP, USB devices) */
8989 alock.release();
8990 HRESULT rc2 = pConsole->powerDown();
8991 alock.acquire();
8992 AssertComRC(rc2);
8993 }
8994 else
8995 {
8996 /*
8997 * Deregister the VMSetError callback. This is necessary as the
8998 * pfnVMAtError() function passed to VMR3Create() is supposed to
8999 * be sticky but our error callback isn't.
9000 */
9001 alock.release();
9002 VMR3AtErrorDeregister(pVM, Console::genericVMSetErrorCallback, &task->mErrorMsg);
9003 /** @todo register another VMSetError callback? */
9004 alock.acquire();
9005 }
9006 }
9007 else
9008 {
9009 /*
9010 * If VMR3Create() failed it has released the VM memory.
9011 */
9012 VMR3ReleaseUVM(pConsole->mpUVM);
9013 pConsole->mpUVM = NULL;
9014 }
9015
9016 if (SUCCEEDED(rc) && RT_FAILURE(vrc))
9017 {
9018 /* If VMR3Create() or one of the other calls in this function fail,
9019 * an appropriate error message has been set in task->mErrorMsg.
9020 * However since that happens via a callback, the rc status code in
9021 * this function is not updated.
9022 */
9023 if (!task->mErrorMsg.length())
9024 {
9025 /* If the error message is not set but we've got a failure,
9026 * convert the VBox status code into a meaningful error message.
9027 * This becomes unused once all the sources of errors set the
9028 * appropriate error message themselves.
9029 */
9030 AssertMsgFailed(("Missing error message during powerup for status code %Rrc\n", vrc));
9031 task->mErrorMsg = Utf8StrFmt(tr("Failed to start VM execution (%Rrc)"),
9032 vrc);
9033 }
9034
9035 /* Set the error message as the COM error.
9036 * Progress::notifyComplete() will pick it up later. */
9037 throw setErrorStatic(E_FAIL, task->mErrorMsg.c_str());
9038 }
9039 }
9040 catch (HRESULT aRC) { rc = aRC; }
9041
9042 if ( pConsole->mMachineState == MachineState_Starting
9043 || pConsole->mMachineState == MachineState_Restoring
9044 || pConsole->mMachineState == MachineState_TeleportingIn
9045 )
9046 {
9047 /* We are still in the Starting/Restoring state. This means one of:
9048 *
9049 * 1) we failed before VMR3Create() was called;
9050 * 2) VMR3Create() failed.
9051 *
9052 * In both cases, there is no need to call powerDown(), but we still
9053 * need to go back to the PoweredOff/Saved state. Reuse
9054 * vmstateChangeCallback() for that purpose.
9055 */
9056
9057 /* preserve existing error info */
9058 ErrorInfoKeeper eik;
9059
9060 Assert(pConsole->mpUVM == NULL);
9061 vmstateChangeCallback(NULL, VMSTATE_TERMINATED, VMSTATE_CREATING,
9062 pConsole);
9063 }
9064
9065 /*
9066 * Evaluate the final result. Note that the appropriate mMachineState value
9067 * is already set by vmstateChangeCallback() in all cases.
9068 */
9069
9070 /* release the lock, don't need it any more */
9071 alock.release();
9072
9073 if (SUCCEEDED(rc))
9074 {
9075 /* Notify the progress object of the success */
9076 task->mProgress->notifyComplete(S_OK);
9077 }
9078 else
9079 {
9080 /* The progress object will fetch the current error info */
9081 task->mProgress->notifyComplete(rc);
9082 LogRel(("Power up failed (vrc=%Rrc, rc=%Rhrc (%#08X))\n", vrc, rc, rc));
9083 }
9084
9085 /* Notify VBoxSVC and any waiting openRemoteSession progress object. */
9086 pConsole->mControl->EndPowerUp(rc);
9087
9088#if defined(RT_OS_WINDOWS)
9089 /* uninitialize COM */
9090 CoUninitialize();
9091#endif
9092
9093 LogFlowFuncLeave();
9094
9095 return VINF_SUCCESS;
9096}
9097
9098
9099/**
9100 * Reconfigures a medium attachment (part of taking or deleting an online snapshot).
9101 *
9102 * @param pConsole Reference to the console object.
9103 * @param pVM The VM handle.
9104 * @param lInstance The instance of the controller.
9105 * @param pcszDevice The name of the controller type.
9106 * @param enmBus The storage bus type of the controller.
9107 * @param fSetupMerge Whether to set up a medium merge
9108 * @param uMergeSource Merge source image index
9109 * @param uMergeTarget Merge target image index
9110 * @param aMediumAtt The medium attachment.
9111 * @param aMachineState The current machine state.
9112 * @param phrc Where to store com error - only valid if we return VERR_GENERAL_FAILURE.
9113 * @return VBox status code.
9114 */
9115/* static */
9116DECLCALLBACK(int) Console::reconfigureMediumAttachment(Console *pConsole,
9117 PVM pVM,
9118 const char *pcszDevice,
9119 unsigned uInstance,
9120 StorageBus_T enmBus,
9121 bool fUseHostIOCache,
9122 bool fBuiltinIOCache,
9123 bool fSetupMerge,
9124 unsigned uMergeSource,
9125 unsigned uMergeTarget,
9126 IMediumAttachment *aMediumAtt,
9127 MachineState_T aMachineState,
9128 HRESULT *phrc)
9129{
9130 LogFlowFunc(("pVM=%p aMediumAtt=%p phrc=%p\n", pVM, aMediumAtt, phrc));
9131
9132 int rc;
9133 HRESULT hrc;
9134 Bstr bstr;
9135 *phrc = S_OK;
9136#define RC_CHECK() do { if (RT_FAILURE(rc)) { AssertMsgFailed(("rc=%Rrc\n", rc)); return rc; } } while (0)
9137#define H() do { if (FAILED(hrc)) { AssertMsgFailed(("hrc=%Rhrc (%#x)\n", hrc, hrc)); *phrc = hrc; return VERR_GENERAL_FAILURE; } } while (0)
9138
9139 /* Ignore attachments other than hard disks, since at the moment they are
9140 * not subject to snapshotting in general. */
9141 DeviceType_T lType;
9142 hrc = aMediumAtt->COMGETTER(Type)(&lType); H();
9143 if (lType != DeviceType_HardDisk)
9144 return VINF_SUCCESS;
9145
9146 /* Determine the base path for the device instance. */
9147 PCFGMNODE pCtlInst;
9148 pCtlInst = CFGMR3GetChildF(CFGMR3GetRoot(pVM), "Devices/%s/%u/", pcszDevice, uInstance);
9149 AssertReturn(pCtlInst, VERR_INTERNAL_ERROR);
9150
9151 /* Update the device instance configuration. */
9152 rc = pConsole->configMediumAttachment(pCtlInst,
9153 pcszDevice,
9154 uInstance,
9155 enmBus,
9156 fUseHostIOCache,
9157 fBuiltinIOCache,
9158 fSetupMerge,
9159 uMergeSource,
9160 uMergeTarget,
9161 aMediumAtt,
9162 aMachineState,
9163 phrc,
9164 true /* fAttachDetach */,
9165 false /* fForceUnmount */,
9166 false /* fHotplug */,
9167 pVM,
9168 NULL /* paLedDevType */);
9169 /** @todo this dumps everything attached to this device instance, which
9170 * is more than necessary. Dumping the changed LUN would be enough. */
9171 CFGMR3Dump(pCtlInst);
9172 RC_CHECK();
9173
9174#undef RC_CHECK
9175#undef H
9176
9177 LogFlowFunc(("Returns success\n"));
9178 return VINF_SUCCESS;
9179}
9180
9181/**
9182 * Progress cancelation callback employed by Console::fntTakeSnapshotWorker.
9183 */
9184static void takesnapshotProgressCancelCallback(void *pvUser)
9185{
9186 PUVM pUVM = (PUVM)pvUser;
9187 SSMR3Cancel(VMR3GetVM(pUVM));
9188}
9189
9190/**
9191 * Worker thread created by Console::TakeSnapshot.
9192 * @param Thread The current thread (ignored).
9193 * @param pvUser The task.
9194 * @return VINF_SUCCESS (ignored).
9195 */
9196/*static*/
9197DECLCALLBACK(int) Console::fntTakeSnapshotWorker(RTTHREAD Thread, void *pvUser)
9198{
9199 VMTakeSnapshotTask *pTask = (VMTakeSnapshotTask*)pvUser;
9200
9201 // taking a snapshot consists of the following:
9202
9203 // 1) creating a diff image for each virtual hard disk, into which write operations go after
9204 // the snapshot has been created (done in VBoxSVC, in SessionMachine::BeginTakingSnapshot)
9205 // 2) creating a Snapshot object with the state of the machine (hardware + storage,
9206 // done in VBoxSVC, also in SessionMachine::BeginTakingSnapshot)
9207 // 3) saving the state of the virtual machine (here, in the VM process, if the machine is online)
9208
9209 Console *that = pTask->mConsole;
9210 bool fBeganTakingSnapshot = false;
9211 bool fSuspenededBySave = false;
9212
9213 AutoCaller autoCaller(that);
9214 if (FAILED(autoCaller.rc()))
9215 {
9216 that->mptrCancelableProgress.setNull();
9217 return autoCaller.rc();
9218 }
9219
9220 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
9221
9222 HRESULT rc = S_OK;
9223
9224 try
9225 {
9226 /* STEP 1 + 2:
9227 * request creating the diff images on the server and create the snapshot object
9228 * (this will set the machine state to Saving on the server to block
9229 * others from accessing this machine)
9230 */
9231 rc = that->mControl->BeginTakingSnapshot(that,
9232 pTask->bstrName.raw(),
9233 pTask->bstrDescription.raw(),
9234 pTask->mProgress,
9235 pTask->fTakingSnapshotOnline,
9236 pTask->bstrSavedStateFile.asOutParam());
9237 if (FAILED(rc))
9238 throw rc;
9239
9240 fBeganTakingSnapshot = true;
9241
9242 /*
9243 * state file is non-null only when the VM is paused
9244 * (i.e. creating a snapshot online)
9245 */
9246 bool f = (!pTask->bstrSavedStateFile.isEmpty() && pTask->fTakingSnapshotOnline)
9247 || ( pTask->bstrSavedStateFile.isEmpty() && !pTask->fTakingSnapshotOnline);
9248 if (!f)
9249 throw setErrorStatic(E_FAIL, "Invalid state of saved state file");
9250
9251 /* sync the state with the server */
9252 if (pTask->lastMachineState == MachineState_Running)
9253 that->setMachineStateLocally(MachineState_LiveSnapshotting);
9254 else
9255 that->setMachineStateLocally(MachineState_Saving);
9256
9257 // STEP 3: save the VM state (if online)
9258 if (pTask->fTakingSnapshotOnline)
9259 {
9260 Utf8Str strSavedStateFile(pTask->bstrSavedStateFile);
9261
9262 SafeVMPtr ptrVM(that);
9263 if (!ptrVM.isOk())
9264 throw ptrVM.rc();
9265
9266 pTask->mProgress->SetNextOperation(Bstr(tr("Saving the machine state")).raw(),
9267 pTask->ulMemSize); // operation weight, same as computed when setting up progress object
9268 pTask->mProgress->setCancelCallback(takesnapshotProgressCancelCallback, ptrVM.rawUVM());
9269
9270 alock.release();
9271 LogFlowFunc(("VMR3Save...\n"));
9272 int vrc = VMR3Save(ptrVM,
9273 strSavedStateFile.c_str(),
9274 true /*fContinueAfterwards*/,
9275 Console::stateProgressCallback,
9276 static_cast<IProgress *>(pTask->mProgress),
9277 &fSuspenededBySave);
9278 alock.acquire();
9279 if (RT_FAILURE(vrc))
9280 throw setErrorStatic(E_FAIL,
9281 tr("Failed to save the machine state to '%s' (%Rrc)"),
9282 strSavedStateFile.c_str(), vrc);
9283
9284 pTask->mProgress->setCancelCallback(NULL, NULL);
9285 if (!pTask->mProgress->notifyPointOfNoReturn())
9286 throw setErrorStatic(E_FAIL, tr("Canceled"));
9287 that->mptrCancelableProgress.setNull();
9288
9289 // STEP 4: reattach hard disks
9290 LogFlowFunc(("Reattaching new differencing hard disks...\n"));
9291
9292 pTask->mProgress->SetNextOperation(Bstr(tr("Reconfiguring medium attachments")).raw(),
9293 1); // operation weight, same as computed when setting up progress object
9294
9295 com::SafeIfaceArray<IMediumAttachment> atts;
9296 rc = that->mMachine->COMGETTER(MediumAttachments)(ComSafeArrayAsOutParam(atts));
9297 if (FAILED(rc))
9298 throw rc;
9299
9300 for (size_t i = 0;
9301 i < atts.size();
9302 ++i)
9303 {
9304 ComPtr<IStorageController> pStorageController;
9305 Bstr controllerName;
9306 ULONG lInstance;
9307 StorageControllerType_T enmController;
9308 StorageBus_T enmBus;
9309 BOOL fUseHostIOCache;
9310
9311 /*
9312 * We can't pass a storage controller object directly
9313 * (g++ complains about not being able to pass non POD types through '...')
9314 * so we have to query needed values here and pass them.
9315 */
9316 rc = atts[i]->COMGETTER(Controller)(controllerName.asOutParam());
9317 if (FAILED(rc))
9318 throw rc;
9319
9320 rc = that->mMachine->GetStorageControllerByName(controllerName.raw(),
9321 pStorageController.asOutParam());
9322 if (FAILED(rc))
9323 throw rc;
9324
9325 rc = pStorageController->COMGETTER(ControllerType)(&enmController);
9326 if (FAILED(rc))
9327 throw rc;
9328 rc = pStorageController->COMGETTER(Instance)(&lInstance);
9329 if (FAILED(rc))
9330 throw rc;
9331 rc = pStorageController->COMGETTER(Bus)(&enmBus);
9332 if (FAILED(rc))
9333 throw rc;
9334 rc = pStorageController->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
9335 if (FAILED(rc))
9336 throw rc;
9337
9338 const char *pcszDevice = Console::convertControllerTypeToDev(enmController);
9339
9340 BOOL fBuiltinIOCache;
9341 rc = that->mMachine->COMGETTER(IOCacheEnabled)(&fBuiltinIOCache);
9342 if (FAILED(rc))
9343 throw rc;
9344
9345 /*
9346 * don't release the lock since reconfigureMediumAttachment
9347 * isn't going to need the Console lock.
9348 */
9349 vrc = VMR3ReqCallWait(ptrVM,
9350 VMCPUID_ANY,
9351 (PFNRT)reconfigureMediumAttachment,
9352 13,
9353 that,
9354 ptrVM.raw(),
9355 pcszDevice,
9356 lInstance,
9357 enmBus,
9358 fUseHostIOCache,
9359 fBuiltinIOCache,
9360 false /* fSetupMerge */,
9361 0 /* uMergeSource */,
9362 0 /* uMergeTarget */,
9363 atts[i],
9364 that->mMachineState,
9365 &rc);
9366 if (RT_FAILURE(vrc))
9367 throw setErrorStatic(E_FAIL, Console::tr("%Rrc"), vrc);
9368 if (FAILED(rc))
9369 throw rc;
9370 }
9371 }
9372
9373 /*
9374 * finalize the requested snapshot object.
9375 * This will reset the machine state to the state it had right
9376 * before calling mControl->BeginTakingSnapshot().
9377 */
9378 rc = that->mControl->EndTakingSnapshot(TRUE /*aSuccess*/);
9379 // do not throw rc here because we can't call EndTakingSnapshot() twice
9380 LogFlowFunc(("EndTakingSnapshot -> %Rhrc [mMachineState=%s]\n", rc, Global::stringifyMachineState(that->mMachineState)));
9381 }
9382 catch (HRESULT rcThrown)
9383 {
9384 /* preserve existing error info */
9385 ErrorInfoKeeper eik;
9386
9387 if (fBeganTakingSnapshot)
9388 that->mControl->EndTakingSnapshot(FALSE /*aSuccess*/);
9389
9390 rc = rcThrown;
9391 LogFunc(("Caught %Rhrc [mMachineState=%s]\n", rc, Global::stringifyMachineState(that->mMachineState)));
9392 }
9393 Assert(alock.isWriteLockOnCurrentThread());
9394
9395 if (FAILED(rc)) /* Must come before calling setMachineState. */
9396 pTask->mProgress->notifyComplete(rc);
9397
9398 /*
9399 * Fix up the machine state.
9400 *
9401 * For live snapshots we do all the work, for the two other variations we
9402 * just update the local copy.
9403 */
9404 MachineState_T enmMachineState;
9405 that->mMachine->COMGETTER(State)(&enmMachineState);
9406 if ( that->mMachineState == MachineState_LiveSnapshotting
9407 || that->mMachineState == MachineState_Saving)
9408 {
9409
9410 if (!pTask->fTakingSnapshotOnline)
9411 that->setMachineStateLocally(pTask->lastMachineState);
9412 else if (SUCCEEDED(rc))
9413 {
9414 Assert( pTask->lastMachineState == MachineState_Running
9415 || pTask->lastMachineState == MachineState_Paused);
9416 Assert(that->mMachineState == MachineState_Saving);
9417 if (pTask->lastMachineState == MachineState_Running)
9418 {
9419 LogFlowFunc(("VMR3Resume...\n"));
9420 SafeVMPtr ptrVM(that);
9421 alock.release();
9422 int vrc = VMR3Resume(ptrVM);
9423 alock.acquire();
9424 if (RT_FAILURE(vrc))
9425 {
9426 rc = setErrorStatic(VBOX_E_VM_ERROR, tr("Could not resume the machine execution (%Rrc)"), vrc);
9427 pTask->mProgress->notifyComplete(rc);
9428 if (that->mMachineState == MachineState_Saving)
9429 that->setMachineStateLocally(MachineState_Paused);
9430 }
9431 }
9432 else
9433 that->setMachineStateLocally(MachineState_Paused);
9434 }
9435 else
9436 {
9437 /** @todo this could probably be made more generic and reused elsewhere. */
9438 /* paranoid cleanup on for a failed online snapshot. */
9439 VMSTATE enmVMState = VMR3GetStateU(that->mpUVM);
9440 switch (enmVMState)
9441 {
9442 case VMSTATE_RUNNING:
9443 case VMSTATE_RUNNING_LS:
9444 case VMSTATE_DEBUGGING:
9445 case VMSTATE_DEBUGGING_LS:
9446 case VMSTATE_POWERING_OFF:
9447 case VMSTATE_POWERING_OFF_LS:
9448 case VMSTATE_RESETTING:
9449 case VMSTATE_RESETTING_LS:
9450 Assert(!fSuspenededBySave);
9451 that->setMachineState(MachineState_Running);
9452 break;
9453
9454 case VMSTATE_GURU_MEDITATION:
9455 case VMSTATE_GURU_MEDITATION_LS:
9456 that->setMachineState(MachineState_Stuck);
9457 break;
9458
9459 case VMSTATE_FATAL_ERROR:
9460 case VMSTATE_FATAL_ERROR_LS:
9461 if (pTask->lastMachineState == MachineState_Paused)
9462 that->setMachineStateLocally(pTask->lastMachineState);
9463 else
9464 that->setMachineState(MachineState_Paused);
9465 break;
9466
9467 default:
9468 AssertMsgFailed(("%s\n", VMR3GetStateName(enmVMState)));
9469 case VMSTATE_SUSPENDED:
9470 case VMSTATE_SUSPENDED_LS:
9471 case VMSTATE_SUSPENDING:
9472 case VMSTATE_SUSPENDING_LS:
9473 case VMSTATE_SUSPENDING_EXT_LS:
9474 if (fSuspenededBySave)
9475 {
9476 Assert(pTask->lastMachineState == MachineState_Running);
9477 LogFlowFunc(("VMR3Resume (on failure)...\n"));
9478 SafeVMPtr ptrVM(that);
9479 alock.release();
9480 int vrc = VMR3Resume(ptrVM); AssertLogRelRC(vrc);
9481 alock.acquire();
9482 if (RT_FAILURE(vrc))
9483 that->setMachineState(MachineState_Paused);
9484 }
9485 else if (pTask->lastMachineState == MachineState_Paused)
9486 that->setMachineStateLocally(pTask->lastMachineState);
9487 else
9488 that->setMachineState(MachineState_Paused);
9489 break;
9490 }
9491
9492 }
9493 }
9494 /*else: somebody else has change the state... Leave it. */
9495
9496 /* check the remote state to see that we got it right. */
9497 that->mMachine->COMGETTER(State)(&enmMachineState);
9498 AssertLogRelMsg(that->mMachineState == enmMachineState,
9499 ("mMachineState=%s enmMachineState=%s\n", Global::stringifyMachineState(that->mMachineState),
9500 Global::stringifyMachineState(enmMachineState) ));
9501
9502
9503 if (SUCCEEDED(rc)) /* The failure cases are handled above. */
9504 pTask->mProgress->notifyComplete(rc);
9505
9506 delete pTask;
9507
9508 LogFlowFuncLeave();
9509 return VINF_SUCCESS;
9510}
9511
9512/**
9513 * Thread for executing the saved state operation.
9514 *
9515 * @param Thread The thread handle.
9516 * @param pvUser Pointer to a VMSaveTask structure.
9517 * @return VINF_SUCCESS (ignored).
9518 *
9519 * @note Locks the Console object for writing.
9520 */
9521/*static*/
9522DECLCALLBACK(int) Console::saveStateThread(RTTHREAD Thread, void *pvUser)
9523{
9524 LogFlowFuncEnter();
9525
9526 std::auto_ptr<VMSaveTask> task(static_cast<VMSaveTask*>(pvUser));
9527 AssertReturn(task.get(), VERR_INVALID_PARAMETER);
9528
9529 Assert(task->mSavedStateFile.length());
9530 Assert(task->mProgress.isNull());
9531 Assert(!task->mServerProgress.isNull());
9532
9533 const ComObjPtr<Console> &that = task->mConsole;
9534 Utf8Str errMsg;
9535 HRESULT rc = S_OK;
9536
9537 LogFlowFunc(("Saving the state to '%s'...\n", task->mSavedStateFile.c_str()));
9538
9539 bool fSuspenededBySave;
9540 int vrc = VMR3Save(task->mpVM,
9541 task->mSavedStateFile.c_str(),
9542 false, /*fContinueAfterwards*/
9543 Console::stateProgressCallback,
9544 static_cast<IProgress *>(task->mServerProgress),
9545 &fSuspenededBySave);
9546 if (RT_FAILURE(vrc))
9547 {
9548 errMsg = Utf8StrFmt(Console::tr("Failed to save the machine state to '%s' (%Rrc)"),
9549 task->mSavedStateFile.c_str(), vrc);
9550 rc = E_FAIL;
9551 }
9552 Assert(!fSuspenededBySave);
9553
9554 /* lock the console once we're going to access it */
9555 AutoWriteLock thatLock(that COMMA_LOCKVAL_SRC_POS);
9556
9557 /* synchronize the state with the server */
9558 if (SUCCEEDED(rc))
9559 {
9560 /*
9561 * The machine has been successfully saved, so power it down
9562 * (vmstateChangeCallback() will set state to Saved on success).
9563 * Note: we release the task's VM caller, otherwise it will
9564 * deadlock.
9565 */
9566 task->releaseVMCaller();
9567 thatLock.release();
9568 rc = that->powerDown();
9569 thatLock.acquire();
9570 }
9571
9572 /*
9573 * If we failed, reset the local machine state.
9574 */
9575 if (FAILED(rc))
9576 that->setMachineStateLocally(task->mMachineStateBefore);
9577
9578 /*
9579 * Finalize the requested save state procedure. In case of failure it will
9580 * reset the machine state to the state it had right before calling
9581 * mControl->BeginSavingState(). This must be the last thing because it
9582 * will set the progress to completed, and that means that the frontend
9583 * can immediately uninit the associated console object.
9584 */
9585 that->mControl->EndSavingState(rc, Bstr(errMsg).raw());
9586
9587 LogFlowFuncLeave();
9588 return VINF_SUCCESS;
9589}
9590
9591/**
9592 * Thread for powering down the Console.
9593 *
9594 * @param Thread The thread handle.
9595 * @param pvUser Pointer to the VMTask structure.
9596 * @return VINF_SUCCESS (ignored).
9597 *
9598 * @note Locks the Console object for writing.
9599 */
9600/*static*/
9601DECLCALLBACK(int) Console::powerDownThread(RTTHREAD Thread, void *pvUser)
9602{
9603 LogFlowFuncEnter();
9604
9605 std::auto_ptr<VMPowerDownTask> task(static_cast<VMPowerDownTask *>(pvUser));
9606 AssertReturn(task.get(), VERR_INVALID_PARAMETER);
9607
9608 AssertReturn(task->isOk(), VERR_GENERAL_FAILURE);
9609
9610 Assert(task->mProgress.isNull());
9611
9612 const ComObjPtr<Console> &that = task->mConsole;
9613
9614 /* Note: no need to use addCaller() to protect Console because VMTask does
9615 * that */
9616
9617 /* wait until the method tat started us returns */
9618 AutoWriteLock thatLock(that COMMA_LOCKVAL_SRC_POS);
9619
9620 /* release VM caller to avoid the powerDown() deadlock */
9621 task->releaseVMCaller();
9622
9623 thatLock.release();
9624
9625 that->powerDown(task->mServerProgress);
9626
9627 /* complete the operation */
9628 that->mControl->EndPoweringDown(S_OK, Bstr().raw());
9629
9630 LogFlowFuncLeave();
9631 return VINF_SUCCESS;
9632}
9633
9634
9635/**
9636 * @interface_method_impl{VMM2USERMETHODS,pfnSaveState}
9637 */
9638/*static*/ DECLCALLBACK(int)
9639Console::vmm2User_SaveState(PCVMM2USERMETHODS pThis, PUVM pUVM)
9640{
9641 Console *pConsole = ((MYVMM2USERMETHODS *)pThis)->pConsole;
9642 NOREF(pUVM);
9643
9644 /*
9645 * For now, just call SaveState. We should probably try notify the GUI so
9646 * it can pop up a progress object and stuff.
9647 */
9648 HRESULT hrc = pConsole->SaveState(NULL);
9649 return SUCCEEDED(hrc) ? VINF_SUCCESS : Global::vboxStatusCodeFromCOM(hrc);
9650}
9651
9652/**
9653 * @interface_method_impl{VMM2USERMETHODS,pfnNotifyEmtInit}
9654 */
9655/*static*/ DECLCALLBACK(void)
9656Console::vmm2User_NotifyEmtInit(PCVMM2USERMETHODS pThis, PUVM pUVM, PUVMCPU pUVCpu)
9657{
9658 NOREF(pThis); NOREF(pUVM); NOREF(pUVCpu);
9659 VirtualBoxBase::initializeComForThread();
9660}
9661
9662/**
9663 * @interface_method_impl{VMM2USERMETHODS,pfnNotifyEmtTerm}
9664 */
9665/*static*/ DECLCALLBACK(void)
9666Console::vmm2User_NotifyEmtTerm(PCVMM2USERMETHODS pThis, PUVM pUVM, PUVMCPU pUVCpu)
9667{
9668 NOREF(pThis); NOREF(pUVM); NOREF(pUVCpu);
9669 VirtualBoxBase::uninitializeComForThread();
9670}
9671
9672/**
9673 * @interface_method_impl{VMM2USERMETHODS,pfnNotifyPdmtInit}
9674 */
9675/*static*/ DECLCALLBACK(void)
9676Console::vmm2User_NotifyPdmtInit(PCVMM2USERMETHODS pThis, PUVM pUVM)
9677{
9678 NOREF(pThis); NOREF(pUVM);
9679 VirtualBoxBase::initializeComForThread();
9680}
9681
9682/**
9683 * @interface_method_impl{VMM2USERMETHODS,pfnNotifyPdmtTerm}
9684 */
9685/*static*/ DECLCALLBACK(void)
9686Console::vmm2User_NotifyPdmtTerm(PCVMM2USERMETHODS pThis, PUVM pUVM)
9687{
9688 NOREF(pThis); NOREF(pUVM);
9689 VirtualBoxBase::uninitializeComForThread();
9690}
9691
9692
9693
9694
9695/**
9696 * The Main status driver instance data.
9697 */
9698typedef struct DRVMAINSTATUS
9699{
9700 /** The LED connectors. */
9701 PDMILEDCONNECTORS ILedConnectors;
9702 /** Pointer to the LED ports interface above us. */
9703 PPDMILEDPORTS pLedPorts;
9704 /** Pointer to the array of LED pointers. */
9705 PPDMLED *papLeds;
9706 /** The unit number corresponding to the first entry in the LED array. */
9707 RTUINT iFirstLUN;
9708 /** The unit number corresponding to the last entry in the LED array.
9709 * (The size of the LED array is iLastLUN - iFirstLUN + 1.) */
9710 RTUINT iLastLUN;
9711 /** Pointer to the driver instance. */
9712 PPDMDRVINS pDrvIns;
9713 /** The Media Notify interface. */
9714 PDMIMEDIANOTIFY IMediaNotify;
9715 /** Map for translating PDM storage controller/LUN information to
9716 * IMediumAttachment references. */
9717 Console::MediumAttachmentMap *pmapMediumAttachments;
9718 /** Device name+instance for mapping */
9719 char *pszDeviceInstance;
9720 /** Pointer to the Console object, for driver triggered activities. */
9721 Console *pConsole;
9722} DRVMAINSTATUS, *PDRVMAINSTATUS;
9723
9724
9725/**
9726 * Notification about a unit which have been changed.
9727 *
9728 * The driver must discard any pointers to data owned by
9729 * the unit and requery it.
9730 *
9731 * @param pInterface Pointer to the interface structure containing the called function pointer.
9732 * @param iLUN The unit number.
9733 */
9734DECLCALLBACK(void) Console::drvStatus_UnitChanged(PPDMILEDCONNECTORS pInterface, unsigned iLUN)
9735{
9736 PDRVMAINSTATUS pData = (PDRVMAINSTATUS)((uintptr_t)pInterface - RT_OFFSETOF(DRVMAINSTATUS, ILedConnectors));
9737 if (iLUN >= pData->iFirstLUN && iLUN <= pData->iLastLUN)
9738 {
9739 PPDMLED pLed;
9740 int rc = pData->pLedPorts->pfnQueryStatusLed(pData->pLedPorts, iLUN, &pLed);
9741 if (RT_FAILURE(rc))
9742 pLed = NULL;
9743 ASMAtomicWritePtr(&pData->papLeds[iLUN - pData->iFirstLUN], pLed);
9744 Log(("drvStatus_UnitChanged: iLUN=%d pLed=%p\n", iLUN, pLed));
9745 }
9746}
9747
9748
9749/**
9750 * Notification about a medium eject.
9751 *
9752 * @returns VBox status.
9753 * @param pInterface Pointer to the interface structure containing the called function pointer.
9754 * @param uLUN The unit number.
9755 */
9756DECLCALLBACK(int) Console::drvStatus_MediumEjected(PPDMIMEDIANOTIFY pInterface, unsigned uLUN)
9757{
9758 PDRVMAINSTATUS pData = (PDRVMAINSTATUS)((uintptr_t)pInterface - RT_OFFSETOF(DRVMAINSTATUS, IMediaNotify));
9759 PPDMDRVINS pDrvIns = pData->pDrvIns;
9760 LogFunc(("uLUN=%d\n", uLUN));
9761 if (pData->pmapMediumAttachments)
9762 {
9763 AutoWriteLock alock(pData->pConsole COMMA_LOCKVAL_SRC_POS);
9764
9765 ComPtr<IMediumAttachment> pMediumAtt;
9766 Utf8Str devicePath = Utf8StrFmt("%s/LUN#%u", pData->pszDeviceInstance, uLUN);
9767 Console::MediumAttachmentMap::const_iterator end = pData->pmapMediumAttachments->end();
9768 Console::MediumAttachmentMap::const_iterator it = pData->pmapMediumAttachments->find(devicePath);
9769 if (it != end)
9770 pMediumAtt = it->second;
9771 Assert(!pMediumAtt.isNull());
9772 if (!pMediumAtt.isNull())
9773 {
9774 IMedium *pMedium = NULL;
9775 HRESULT rc = pMediumAtt->COMGETTER(Medium)(&pMedium);
9776 AssertComRC(rc);
9777 if (SUCCEEDED(rc) && pMedium)
9778 {
9779 BOOL fHostDrive = FALSE;
9780 rc = pMedium->COMGETTER(HostDrive)(&fHostDrive);
9781 AssertComRC(rc);
9782 if (!fHostDrive)
9783 {
9784 alock.release();
9785
9786 ComPtr<IMediumAttachment> pNewMediumAtt;
9787 rc = pData->pConsole->mControl->EjectMedium(pMediumAtt, pNewMediumAtt.asOutParam());
9788 if (SUCCEEDED(rc))
9789 fireMediumChangedEvent(pData->pConsole->mEventSource, pNewMediumAtt);
9790
9791 alock.acquire();
9792 if (pNewMediumAtt != pMediumAtt)
9793 {
9794 pData->pmapMediumAttachments->erase(devicePath);
9795 pData->pmapMediumAttachments->insert(std::make_pair(devicePath, pNewMediumAtt));
9796 }
9797 }
9798 }
9799 }
9800 }
9801 return VINF_SUCCESS;
9802}
9803
9804
9805/**
9806 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
9807 */
9808DECLCALLBACK(void *) Console::drvStatus_QueryInterface(PPDMIBASE pInterface, const char *pszIID)
9809{
9810 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
9811 PDRVMAINSTATUS pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINSTATUS);
9812 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
9813 PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDCONNECTORS, &pThis->ILedConnectors);
9814 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIANOTIFY, &pThis->IMediaNotify);
9815 return NULL;
9816}
9817
9818
9819/**
9820 * Destruct a status driver instance.
9821 *
9822 * @returns VBox status.
9823 * @param pDrvIns The driver instance data.
9824 */
9825DECLCALLBACK(void) Console::drvStatus_Destruct(PPDMDRVINS pDrvIns)
9826{
9827 PDRVMAINSTATUS pData = PDMINS_2_DATA(pDrvIns, PDRVMAINSTATUS);
9828 LogFlowFunc(("iInstance=%d\n", pDrvIns->iInstance));
9829 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
9830
9831 if (pData->papLeds)
9832 {
9833 unsigned iLed = pData->iLastLUN - pData->iFirstLUN + 1;
9834 while (iLed-- > 0)
9835 ASMAtomicWriteNullPtr(&pData->papLeds[iLed]);
9836 }
9837}
9838
9839
9840/**
9841 * Construct a status driver instance.
9842 *
9843 * @copydoc FNPDMDRVCONSTRUCT
9844 */
9845DECLCALLBACK(int) Console::drvStatus_Construct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
9846{
9847 PDRVMAINSTATUS pData = PDMINS_2_DATA(pDrvIns, PDRVMAINSTATUS);
9848 LogFlowFunc(("iInstance=%d\n", pDrvIns->iInstance));
9849 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
9850
9851 /*
9852 * Validate configuration.
9853 */
9854 if (!CFGMR3AreValuesValid(pCfg, "papLeds\0pmapMediumAttachments\0DeviceInstance\0pConsole\0First\0Last\0"))
9855 return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
9856 AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
9857 ("Configuration error: Not possible to attach anything to this driver!\n"),
9858 VERR_PDM_DRVINS_NO_ATTACH);
9859
9860 /*
9861 * Data.
9862 */
9863 pDrvIns->IBase.pfnQueryInterface = Console::drvStatus_QueryInterface;
9864 pData->ILedConnectors.pfnUnitChanged = Console::drvStatus_UnitChanged;
9865 pData->IMediaNotify.pfnEjected = Console::drvStatus_MediumEjected;
9866 pData->pDrvIns = pDrvIns;
9867 pData->pszDeviceInstance = NULL;
9868
9869 /*
9870 * Read config.
9871 */
9872 int rc = CFGMR3QueryPtr(pCfg, "papLeds", (void **)&pData->papLeds);
9873 if (RT_FAILURE(rc))
9874 {
9875 AssertMsgFailed(("Configuration error: Failed to query the \"papLeds\" value! rc=%Rrc\n", rc));
9876 return rc;
9877 }
9878
9879 rc = CFGMR3QueryPtrDef(pCfg, "pmapMediumAttachments", (void **)&pData->pmapMediumAttachments, NULL);
9880 if (RT_FAILURE(rc))
9881 {
9882 AssertMsgFailed(("Configuration error: Failed to query the \"pmapMediumAttachments\" value! rc=%Rrc\n", rc));
9883 return rc;
9884 }
9885 if (pData->pmapMediumAttachments)
9886 {
9887 rc = CFGMR3QueryStringAlloc(pCfg, "DeviceInstance", &pData->pszDeviceInstance);
9888 if (RT_FAILURE(rc))
9889 {
9890 AssertMsgFailed(("Configuration error: Failed to query the \"DeviceInstance\" value! rc=%Rrc\n", rc));
9891 return rc;
9892 }
9893 rc = CFGMR3QueryPtr(pCfg, "pConsole", (void **)&pData->pConsole);
9894 if (RT_FAILURE(rc))
9895 {
9896 AssertMsgFailed(("Configuration error: Failed to query the \"pConsole\" value! rc=%Rrc\n", rc));
9897 return rc;
9898 }
9899 }
9900
9901 rc = CFGMR3QueryU32(pCfg, "First", &pData->iFirstLUN);
9902 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
9903 pData->iFirstLUN = 0;
9904 else if (RT_FAILURE(rc))
9905 {
9906 AssertMsgFailed(("Configuration error: Failed to query the \"First\" value! rc=%Rrc\n", rc));
9907 return rc;
9908 }
9909
9910 rc = CFGMR3QueryU32(pCfg, "Last", &pData->iLastLUN);
9911 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
9912 pData->iLastLUN = 0;
9913 else if (RT_FAILURE(rc))
9914 {
9915 AssertMsgFailed(("Configuration error: Failed to query the \"Last\" value! rc=%Rrc\n", rc));
9916 return rc;
9917 }
9918 if (pData->iFirstLUN > pData->iLastLUN)
9919 {
9920 AssertMsgFailed(("Configuration error: Invalid unit range %u-%u\n", pData->iFirstLUN, pData->iLastLUN));
9921 return VERR_GENERAL_FAILURE;
9922 }
9923
9924 /*
9925 * Get the ILedPorts interface of the above driver/device and
9926 * query the LEDs we want.
9927 */
9928 pData->pLedPorts = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMILEDPORTS);
9929 AssertMsgReturn(pData->pLedPorts, ("Configuration error: No led ports interface above!\n"),
9930 VERR_PDM_MISSING_INTERFACE_ABOVE);
9931
9932 for (unsigned i = pData->iFirstLUN; i <= pData->iLastLUN; ++i)
9933 Console::drvStatus_UnitChanged(&pData->ILedConnectors, i);
9934
9935 return VINF_SUCCESS;
9936}
9937
9938
9939/**
9940 * Console status driver (LED) registration record.
9941 */
9942const PDMDRVREG Console::DrvStatusReg =
9943{
9944 /* u32Version */
9945 PDM_DRVREG_VERSION,
9946 /* szName */
9947 "MainStatus",
9948 /* szRCMod */
9949 "",
9950 /* szR0Mod */
9951 "",
9952 /* pszDescription */
9953 "Main status driver (Main as in the API).",
9954 /* fFlags */
9955 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
9956 /* fClass. */
9957 PDM_DRVREG_CLASS_STATUS,
9958 /* cMaxInstances */
9959 ~0U,
9960 /* cbInstance */
9961 sizeof(DRVMAINSTATUS),
9962 /* pfnConstruct */
9963 Console::drvStatus_Construct,
9964 /* pfnDestruct */
9965 Console::drvStatus_Destruct,
9966 /* pfnRelocate */
9967 NULL,
9968 /* pfnIOCtl */
9969 NULL,
9970 /* pfnPowerOn */
9971 NULL,
9972 /* pfnReset */
9973 NULL,
9974 /* pfnSuspend */
9975 NULL,
9976 /* pfnResume */
9977 NULL,
9978 /* pfnAttach */
9979 NULL,
9980 /* pfnDetach */
9981 NULL,
9982 /* pfnPowerOff */
9983 NULL,
9984 /* pfnSoftReset */
9985 NULL,
9986 /* u32EndVersion */
9987 PDM_DRVREG_VERSION
9988};
9989
9990/* 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