VirtualBox

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

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

compile fix for VBOX_WITH_GUEST_PROPS disabled

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

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