VirtualBox

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

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

Main: typos

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