VirtualBox

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

Last change on this file since 82580 was 82502, checked in by vboxsync, 5 years ago

Main/Console: Reverted VRDE clipboard breakage introduced in r131016 when using the HGCM extension feature for doing clipboard area coordination via VBoxSVC. Removed the SharedClipboardPrivate bits as they seem to have no real purpose. The clipboard area stuff will no longer get IVirtualBox/VBoxSVC, that has to be replumbed if deemed necessary (I don't think it is). bugref:9437

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

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