VirtualBox

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

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

Main/Console: fix various regression from the locking cleanup

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

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