VirtualBox

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

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

Main+Frontends: move release logging setup to the glue code (to eliminate spreading code duplication), various minor fixes and cleanups

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