VirtualBox

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

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

Main/ConsoleImpl: when doing VMR3ReqCall requests, do always drop the object lock while waiting on the result. EMT will not only call us back but, more important, it might wait for the lock at another place! It might be simpler to do alock.release() first and then do VMR3ReqCallWait() but for now don't do invasive changes. And do only wait if the VMR3Req request did not yet succeed (in rare cases), otherwise we will wait forever. See also ticket #13722 and xtracker 7648.

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