VirtualBox

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

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

Main: remote smartcard

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

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