VirtualBox

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

Last change on this file since 41836 was 41528, checked in by vboxsync, 13 years ago

Main/HostUSBDevice(all platforms)+USBProxyService: redo USB locking, fixes major regression, added lots of assertions to catch locking flaws early, whitespace cleanup
Main/Machine: small USB locking fix to be consistent with the remaining code
Main/Host+glue/AutoLock: replace USB list lock by host lock, small numbering cleanup
Main/Console: redo USB locking, do less in USB callbacks/EMT (addresses long standing todo items), eliminate unsafe iterator parameters

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