VirtualBox

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

Last change on this file since 48406 was 48406, checked in by vboxsync, 11 years ago

Main,VBoxManage: Implemented IConsole::EmulatedUSB. Removed IMachine::emulatedUSBWebcameraEnabled.

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

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