VirtualBox

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

Last change on this file since 55437 was 55437, checked in by vboxsync, 10 years ago

Main/Console+Machine: deliver OnGuestPropertyChanged for the current VM also through the Console event source, to make event delivery more efficient (less broadcasting/filtering compared to using the VirtualBox event source), which of course only works within VM processes.
VBoxHeadless: make use of the more efficient event delivery path, which as an additional bonus often allows VBoxSVC to skip queueing the guest property changed events, which for fast delivery rates needs quite some memory.

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

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