VirtualBox

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

Last change on this file since 98088 was 98088, checked in by vboxsync, 2 years ago

Main/ConsoleImpl: Added a dedicated read/write lock to the LED sets to protect potential activity queries during VM construction. bugref:9892

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