VirtualBox

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

Last change on this file since 73768 was 73005, checked in by vboxsync, 6 years ago

Main: Use setErrorBoth when we've got a VBox status code handy. (The COM status codes aren't too specfic and this may help us decode error messages and provide an alternative to strstr for API clients. setErrorBoth isn't new, btw.) [build fix]

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

© 2023 Oracle
ContactPrivacy policyTerms of Use