VirtualBox

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

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

Shared folders: instead of dropping a shared folder which does not have a mapping on the host, include it but make any operation on it fail. This is necessary to keep SSM consistent.

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

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