VirtualBox

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

Last change on this file since 60952 was 60404, checked in by vboxsync, 9 years ago

VMM,Devices,Main: Implemented soft/warm reset for shutdown status codes 05h, 09h and 0Ah.

This is a shot at adjusting our VM reset handling to handle the ancient way of
getting a 286 out of protected mode and back to real mode. Our exiting reset
code (XXXR3Reset, PDMDEVREG::pfnReset, and so on) is doing a cold reset of the
system and then some additional device & memory initialization that the firmware
is usually responsible for doing. When the guest triggers a reset via the
keyboard controller, system control port A, CPU triple fault, and possibly ACPI,
only the CPU is supposed to be reset. The BIOS would then decide whether memory
and devices needed resetting as well, or if the resetter justed wanted to get out
protected mode and resume executing some real mode code pointed to by 467h.

  • New states SOFT_RESETTING and SOFT_RESETTING_LS. The latter returns to RUNNING_LS, not SUSPENDED_LS like for hard reset.
  • Added a firmware interface so the VMM/PDM can ask it whether we're supposed to do a hard reset or a soft(/warm) one.
  • Implemented firmware interface for the PC BIOS (but not EFI). It indicates soft(/warm) reset when CMOS[0xf] is 5, 9 or 10.
  • Moved the CMOS[0xf] resetting from the RTC device to the PC BIOS since it's firmware thing, not RTC.
  • Added a flag parameter to PDMDevHlpVMReset for specifying the source of the reset operation. One class of sources (GIM) will always trigger hard resets, whereas the others will check with the firmware first.
  • Added PDMR3GetResetInfo for query the flags passed to PDMDevHlpVMReset and for asking the firmware whether it's a hard or soft reset. The latter, however, is only done if only CPU 0 is active. Systems with more than one CPU in a state other than EMSTATE_WAIT_SIPI status will always be hard reset.
  • Added internal VMR3ResetFF and VMR3ResetTripleFault APIs for handling the VM_FF_RESET and VINF_EM_TRIPLE_FAULT conditions.
  • Added PMDR3ResetSoft and had it call pfnSoftReset (which is now defined).

Warning! Major PDM_DEVHLPR3_VERSION change, minor PDM_DEVREG_VERSION change.

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