VirtualBox

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

Last change on this file since 94763 was 94763, checked in by vboxsync, 3 years ago

VMM/SSM: Allow SSMR3Open and SSMR3ValidateFile to take a stream operations callback table, bugref:9955

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

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