VirtualBox

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

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

Main/ConsoleImpl.cpp/powerUp: Moved VRDE audio driver attaching and enabling recording to _after_ we've checked that VMR3Create succeeded, as it's begging for deadlocks to do it earlier since both may try talk to EMT (there is no EMT if VMR3Create failed). Also fixed error handling to not use AssertComRCReturnVoid() as that would leave everything dangling in limbo. Completely untested. bugref:9374

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

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