VirtualBox

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

Last change on this file since 66870 was 66870, checked in by vboxsync, 8 years ago

Main/ConsoleImpl: bugref:8855: don't ignore the error code when powering up a VM

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

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