VirtualBox

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

Last change on this file since 44288 was 44288, checked in by vboxsync, 12 years ago

Eliminate last use of CombinedProgress class etc xtracker id id 6167

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