VirtualBox

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

Last change on this file since 53297 was 53066, checked in by vboxsync, 10 years ago

Fix typo introduced with r96513 which causes USB 2.0 devices being attached to the OHCI root hub

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

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