VirtualBox

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

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

VRDP, Main: forward UTCINFO events as guest properties.

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

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