VirtualBox

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

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

Main: Increased guest property lookup performance by using a map for internal data, leaving lock before posting change event.

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