VirtualBox

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

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

Main,Frontends: poll more than one device in IConsole::GetDeviceActivity.

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