VirtualBox

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

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

Main/Console: fix deadlock in DVD change, many more such fixes to come

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 326.6 KB
Line 
1/* $Id: ConsoleImpl.cpp 40302 2012-02-29 18:30:21Z 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/** @todo release the console lock? */
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/** @todo release the console lock? */
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/** @todo release the console lock? */
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/** @todo release the console lock? */
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 PPDMIBASE pBase;
4326 int vrc = PDMR3QueryDeviceLun(ptrVM, pszAdapterName, ulInstance, 0, &pBase);
4327 if (RT_SUCCESS(vrc))
4328 {
4329 Assert(pBase);
4330 PPDMINETWORKCONFIG pINetCfg;
4331 pINetCfg = PDMIBASE_QUERY_INTERFACE(pBase, PDMINETWORKCONFIG);
4332 if (pINetCfg)
4333 {
4334 Log(("Console::onNetworkAdapterChange: setting link state to %d\n",
4335 fCableConnected));
4336 vrc = pINetCfg->pfnSetLinkState(pINetCfg,
4337 fCableConnected ? PDMNETWORKLINKSTATE_UP
4338 : PDMNETWORKLINKSTATE_DOWN);
4339 ComAssertRC(vrc);
4340 }
4341 if (RT_SUCCESS(vrc) && changeAdapter)
4342 {
4343 VMSTATE enmVMState = VMR3GetState(ptrVM);
4344 if ( enmVMState == VMSTATE_RUNNING /** @todo LiveMigration: Forbid or deal correctly with the _LS variants */
4345 || enmVMState == VMSTATE_SUSPENDED)
4346 {
4347 if (fTraceEnabled && fCableConnected && pINetCfg)
4348 {
4349 vrc = pINetCfg->pfnSetLinkState(pINetCfg, PDMNETWORKLINKSTATE_DOWN);
4350 ComAssertRC(vrc);
4351 }
4352
4353 rc = doNetworkAdapterChange(ptrVM, pszAdapterName, ulInstance, 0, aNetworkAdapter);
4354
4355 if (fTraceEnabled && fCableConnected && pINetCfg)
4356 {
4357 vrc = pINetCfg->pfnSetLinkState(pINetCfg, PDMNETWORKLINKSTATE_UP);
4358 ComAssertRC(vrc);
4359 }
4360 }
4361 }
4362 }
4363 else if (vrc == VERR_PDM_DEVICE_INSTANCE_NOT_FOUND)
4364 return setError(E_FAIL,
4365 tr("The network adapter #%u is not enabled"), ulInstance);
4366 else
4367 ComAssertRC(vrc);
4368
4369 if (RT_FAILURE(vrc))
4370 rc = E_FAIL;
4371 }
4372 }
4373 ptrVM.release();
4374 }
4375
4376 /* notify console callbacks on success */
4377 if (SUCCEEDED(rc))
4378 {
4379 alock.release(); /** @todo 101% safe? */
4380 fireNetworkAdapterChangedEvent(mEventSource, aNetworkAdapter);
4381 }
4382
4383 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
4384 return rc;
4385}
4386
4387/**
4388 * Called by IInternalSessionControl::OnNATEngineChange().
4389 *
4390 * @note Locks this object for writing.
4391 */
4392HRESULT Console::onNATRedirectRuleChange(ULONG ulInstance, BOOL aNatRuleRemove,
4393 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort, IN_BSTR aGuestIp, LONG aGuestPort)
4394{
4395 LogFlowThisFunc(("\n"));
4396
4397 AutoCaller autoCaller(this);
4398 AssertComRCReturnRC(autoCaller.rc());
4399
4400 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4401
4402 HRESULT rc = S_OK;
4403
4404 /* don't trigger nat engine change if the VM isn't running */
4405 SafeVMPtrQuiet ptrVM(this);
4406 if (ptrVM.isOk())
4407 {
4408 do
4409 {
4410 ComPtr<INetworkAdapter> pNetworkAdapter;
4411 rc = machine()->GetNetworkAdapter(ulInstance, pNetworkAdapter.asOutParam());
4412 if ( FAILED(rc)
4413 || pNetworkAdapter.isNull())
4414 break;
4415
4416 /*
4417 * Find the adapter instance, get the config interface and update
4418 * the link state.
4419 */
4420 NetworkAdapterType_T adapterType;
4421 rc = pNetworkAdapter->COMGETTER(AdapterType)(&adapterType);
4422 if (FAILED(rc))
4423 {
4424 AssertComRC(rc);
4425 rc = E_FAIL;
4426 break;
4427 }
4428
4429 const char *pszAdapterName = networkAdapterTypeToName(adapterType);
4430 PPDMIBASE pBase;
4431 int vrc = PDMR3QueryLun(ptrVM, pszAdapterName, ulInstance, 0, &pBase);
4432 if (RT_FAILURE(vrc))
4433 {
4434 ComAssertRC(vrc);
4435 rc = E_FAIL;
4436 break;
4437 }
4438
4439 NetworkAttachmentType_T attachmentType;
4440 rc = pNetworkAdapter->COMGETTER(AttachmentType)(&attachmentType);
4441 if ( FAILED(rc)
4442 || attachmentType != NetworkAttachmentType_NAT)
4443 {
4444 rc = E_FAIL;
4445 break;
4446 }
4447
4448 /* look down for PDMINETWORKNATCONFIG interface */
4449 PPDMINETWORKNATCONFIG pNetNatCfg = NULL;
4450 while (pBase)
4451 {
4452 pNetNatCfg = (PPDMINETWORKNATCONFIG)pBase->pfnQueryInterface(pBase, PDMINETWORKNATCONFIG_IID);
4453 if (pNetNatCfg)
4454 break;
4455 /** @todo r=bird: This stinks! */
4456 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pBase);
4457 pBase = pDrvIns->pDownBase;
4458 }
4459 if (!pNetNatCfg)
4460 break;
4461
4462 bool fUdp = aProto == NATProtocol_UDP;
4463 vrc = pNetNatCfg->pfnRedirectRuleCommand(pNetNatCfg, aNatRuleRemove, fUdp,
4464 Utf8Str(aHostIp).c_str(), aHostPort, Utf8Str(aGuestIp).c_str(),
4465 aGuestPort);
4466 if (RT_FAILURE(vrc))
4467 rc = E_FAIL;
4468 } while (0); /* break loop */
4469 ptrVM.release();
4470 }
4471
4472 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
4473 return rc;
4474}
4475
4476
4477/**
4478 * Process a network adaptor change.
4479 *
4480 * @returns COM status code.
4481 *
4482 * @parma pVM The VM handle (caller hold this safely).
4483 * @param pszDevice The PDM device name.
4484 * @param uInstance The PDM device instance.
4485 * @param uLun The PDM LUN number of the drive.
4486 * @param aNetworkAdapter The network adapter whose attachment needs to be changed
4487 *
4488 * @note Locks this object for writing.
4489 */
4490HRESULT Console::doNetworkAdapterChange(PVM pVM,
4491 const char *pszDevice,
4492 unsigned uInstance,
4493 unsigned uLun,
4494 INetworkAdapter *aNetworkAdapter)
4495{
4496 LogFlowThisFunc(("pszDevice=%p:{%s} uInstance=%u uLun=%u aNetworkAdapter=%p\n",
4497 pszDevice, pszDevice, uInstance, uLun, aNetworkAdapter));
4498
4499 AutoCaller autoCaller(this);
4500 AssertComRCReturnRC(autoCaller.rc());
4501
4502 /* We will need to release the write lock before calling EMT */
4503 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4504
4505 /* Get the VM handle. */
4506 SafeVMPtr ptrVM(this);
4507 if (!ptrVM.isOk())
4508 return ptrVM.rc();
4509
4510 /*
4511 * Call worker in EMT, that's faster and safer than doing everything
4512 * using VM3ReqCall. Note that we separate VMR3ReqCall from VMR3ReqWait
4513 * here to make requests from under the lock in order to serialize them.
4514 */
4515 PVMREQ pReq;
4516 int vrc = VMR3ReqCall(pVM, 0 /*idDstCpu*/, &pReq, 0 /* no wait! */, VMREQFLAGS_VBOX_STATUS,
4517 (PFNRT) Console::changeNetworkAttachment, 6,
4518 this, ptrVM.raw(), pszDevice, uInstance, uLun, aNetworkAdapter);
4519
4520 /* release the lock before waiting for a result (EMT will call us back!) */
4521 alock.release();
4522
4523 if (vrc == VERR_TIMEOUT || RT_SUCCESS(vrc))
4524 {
4525 vrc = VMR3ReqWait(pReq, RT_INDEFINITE_WAIT);
4526 AssertRC(vrc);
4527 if (RT_SUCCESS(vrc))
4528 vrc = pReq->iStatus;
4529 }
4530 VMR3ReqFree(pReq);
4531
4532 if (RT_SUCCESS(vrc))
4533 {
4534 LogFlowThisFunc(("Returns S_OK\n"));
4535 return S_OK;
4536 }
4537
4538 return setError(E_FAIL,
4539 tr("Could not change the network adaptor attachement type (%Rrc)"),
4540 vrc);
4541}
4542
4543
4544/**
4545 * Performs the Network Adaptor change in EMT.
4546 *
4547 * @returns VBox status code.
4548 *
4549 * @param pThis Pointer to the Console object.
4550 * @param pVM The VM handle.
4551 * @param pszDevice The PDM device name.
4552 * @param uInstance The PDM device instance.
4553 * @param uLun The PDM LUN number of the drive.
4554 * @param aNetworkAdapter The network adapter whose attachment needs to be changed
4555 *
4556 * @thread EMT
4557 * @note Locks the Console object for writing.
4558 */
4559DECLCALLBACK(int) Console::changeNetworkAttachment(Console *pThis,
4560 PVM pVM,
4561 const char *pszDevice,
4562 unsigned uInstance,
4563 unsigned uLun,
4564 INetworkAdapter *aNetworkAdapter)
4565{
4566 LogFlowFunc(("pThis=%p pszDevice=%p:{%s} uInstance=%u uLun=%u aNetworkAdapter=%p\n",
4567 pThis, pszDevice, pszDevice, uInstance, uLun, aNetworkAdapter));
4568
4569 AssertReturn(pThis, VERR_INVALID_PARAMETER);
4570
4571 AutoCaller autoCaller(pThis);
4572 AssertComRCReturn(autoCaller.rc(), VERR_ACCESS_DENIED);
4573
4574 ComPtr<IVirtualBox> pVirtualBox;
4575 pThis->mMachine->COMGETTER(Parent)(pVirtualBox.asOutParam());
4576 ComPtr<ISystemProperties> pSystemProperties;
4577 if (pVirtualBox)
4578 pVirtualBox->COMGETTER(SystemProperties)(pSystemProperties.asOutParam());
4579 ChipsetType_T chipsetType = ChipsetType_PIIX3;
4580 pThis->mMachine->COMGETTER(ChipsetType)(&chipsetType);
4581 ULONG maxNetworkAdapters = 0;
4582 if (pSystemProperties)
4583 pSystemProperties->GetMaxNetworkAdapters(chipsetType, &maxNetworkAdapters);
4584 AssertMsg( ( !strcmp(pszDevice, "pcnet")
4585 || !strcmp(pszDevice, "e1000")
4586 || !strcmp(pszDevice, "virtio-net"))
4587 && uLun == 0
4588 && uInstance < maxNetworkAdapters,
4589 ("pszDevice=%s uLun=%d uInstance=%d\n", pszDevice, uLun, uInstance));
4590 Log(("pszDevice=%s uLun=%d uInstance=%d\n", pszDevice, uLun, uInstance));
4591
4592 /*
4593 * Suspend the VM first.
4594 *
4595 * The VM must not be running since it might have pending I/O to
4596 * the drive which is being changed.
4597 */
4598 bool fResume;
4599 VMSTATE enmVMState = VMR3GetState(pVM);
4600 switch (enmVMState)
4601 {
4602 case VMSTATE_RESETTING:
4603 case VMSTATE_RUNNING:
4604 {
4605 LogFlowFunc(("Suspending the VM...\n"));
4606 /* disable the callback to prevent Console-level state change */
4607 pThis->mVMStateChangeCallbackDisabled = true;
4608 int rc = VMR3Suspend(pVM);
4609 pThis->mVMStateChangeCallbackDisabled = false;
4610 AssertRCReturn(rc, rc);
4611 fResume = true;
4612 break;
4613 }
4614
4615 case VMSTATE_SUSPENDED:
4616 case VMSTATE_CREATED:
4617 case VMSTATE_OFF:
4618 fResume = false;
4619 break;
4620
4621 default:
4622 AssertLogRelMsgFailedReturn(("enmVMState=%d\n", enmVMState), VERR_ACCESS_DENIED);
4623 }
4624
4625 int rc = VINF_SUCCESS;
4626 int rcRet = VINF_SUCCESS;
4627
4628 PCFGMNODE pCfg = NULL; /* /Devices/Dev/.../Config/ */
4629 PCFGMNODE pLunL0 = NULL; /* /Devices/Dev/0/LUN#0/ */
4630 PCFGMNODE pInst = CFGMR3GetChildF(CFGMR3GetRoot(pVM), "Devices/%s/%d/", pszDevice, uInstance);
4631 AssertRelease(pInst);
4632
4633 rcRet = pThis->configNetwork(pszDevice, uInstance, uLun, aNetworkAdapter, pCfg, pLunL0, pInst,
4634 true /*fAttachDetach*/, false /*fIgnoreConnectFailure*/);
4635
4636 /*
4637 * Resume the VM if necessary.
4638 */
4639 if (fResume)
4640 {
4641 LogFlowFunc(("Resuming the VM...\n"));
4642 /* disable the callback to prevent Console-level state change */
4643 pThis->mVMStateChangeCallbackDisabled = true;
4644 rc = VMR3Resume(pVM);
4645 pThis->mVMStateChangeCallbackDisabled = false;
4646 AssertRC(rc);
4647 if (RT_FAILURE(rc))
4648 {
4649 /* too bad, we failed. try to sync the console state with the VMM state */
4650 vmstateChangeCallback(pVM, VMSTATE_SUSPENDED, enmVMState, pThis);
4651 }
4652 /// @todo (r=dmik) if we failed with drive mount, then the VMR3Resume
4653 // error (if any) will be hidden from the caller. For proper reporting
4654 // of such multiple errors to the caller we need to enhance the
4655 // IVirtualBoxError interface. For now, give the first error the higher
4656 // priority.
4657 if (RT_SUCCESS(rcRet))
4658 rcRet = rc;
4659 }
4660
4661 LogFlowFunc(("Returning %Rrc\n", rcRet));
4662 return rcRet;
4663}
4664
4665
4666/**
4667 * Called by IInternalSessionControl::OnSerialPortChange().
4668 */
4669HRESULT Console::onSerialPortChange(ISerialPort *aSerialPort)
4670{
4671 LogFlowThisFunc(("\n"));
4672
4673 AutoCaller autoCaller(this);
4674 AssertComRCReturnRC(autoCaller.rc());
4675
4676 fireSerialPortChangedEvent(mEventSource, aSerialPort);
4677
4678 LogFlowThisFunc(("Leaving rc=%#x\n", S_OK));
4679 return S_OK;
4680}
4681
4682/**
4683 * Called by IInternalSessionControl::OnParallelPortChange().
4684 */
4685HRESULT Console::onParallelPortChange(IParallelPort *aParallelPort)
4686{
4687 LogFlowThisFunc(("\n"));
4688
4689 AutoCaller autoCaller(this);
4690 AssertComRCReturnRC(autoCaller.rc());
4691
4692 fireParallelPortChangedEvent(mEventSource, aParallelPort);
4693
4694 LogFlowThisFunc(("Leaving rc=%#x\n", S_OK));
4695 return S_OK;
4696}
4697
4698/**
4699 * Called by IInternalSessionControl::OnStorageControllerChange().
4700 */
4701HRESULT Console::onStorageControllerChange()
4702{
4703 LogFlowThisFunc(("\n"));
4704
4705 AutoCaller autoCaller(this);
4706 AssertComRCReturnRC(autoCaller.rc());
4707
4708 fireStorageControllerChangedEvent(mEventSource);
4709
4710 LogFlowThisFunc(("Leaving rc=%#x\n", S_OK));
4711 return S_OK;
4712}
4713
4714/**
4715 * Called by IInternalSessionControl::OnMediumChange().
4716 */
4717HRESULT Console::onMediumChange(IMediumAttachment *aMediumAttachment, BOOL aForce)
4718{
4719 LogFlowThisFunc(("\n"));
4720
4721 AutoCaller autoCaller(this);
4722 AssertComRCReturnRC(autoCaller.rc());
4723
4724 HRESULT rc = S_OK;
4725
4726 /* don't trigger medium change if the VM isn't running */
4727 SafeVMPtrQuiet ptrVM(this);
4728 if (ptrVM.isOk())
4729 {
4730 rc = doMediumChange(aMediumAttachment, !!aForce, ptrVM);
4731 ptrVM.release();
4732 }
4733
4734 /* notify console callbacks on success */
4735 if (SUCCEEDED(rc))
4736 fireMediumChangedEvent(mEventSource, aMediumAttachment);
4737
4738 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
4739 return rc;
4740}
4741
4742/**
4743 * Called by IInternalSessionControl::OnCPUChange().
4744 *
4745 * @note Locks this object for writing.
4746 */
4747HRESULT Console::onCPUChange(ULONG aCPU, BOOL aRemove)
4748{
4749 LogFlowThisFunc(("\n"));
4750
4751 AutoCaller autoCaller(this);
4752 AssertComRCReturnRC(autoCaller.rc());
4753
4754 HRESULT rc = S_OK;
4755
4756 /* don't trigger CPU change if the VM isn't running */
4757 SafeVMPtrQuiet ptrVM(this);
4758 if (ptrVM.isOk())
4759 {
4760 if (aRemove)
4761 rc = doCPURemove(aCPU, ptrVM);
4762 else
4763 rc = doCPUAdd(aCPU, ptrVM);
4764 ptrVM.release();
4765 }
4766
4767 /* notify console callbacks on success */
4768 if (SUCCEEDED(rc))
4769 fireCPUChangedEvent(mEventSource, aCPU, aRemove);
4770
4771 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
4772 return rc;
4773}
4774
4775/**
4776 * Called by IInternalSessionControl::OnCpuExecutionCapChange().
4777 *
4778 * @note Locks this object for writing.
4779 */
4780HRESULT Console::onCPUExecutionCapChange(ULONG aExecutionCap)
4781{
4782 LogFlowThisFunc(("\n"));
4783
4784 AutoCaller autoCaller(this);
4785 AssertComRCReturnRC(autoCaller.rc());
4786
4787 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4788
4789 HRESULT rc = S_OK;
4790
4791 /* don't trigger the CPU priority change if the VM isn't running */
4792 SafeVMPtrQuiet ptrVM(this);
4793 if (ptrVM.isOk())
4794 {
4795 if ( mMachineState == MachineState_Running
4796 || mMachineState == MachineState_Teleporting
4797 || mMachineState == MachineState_LiveSnapshotting
4798 )
4799 {
4800 /* No need to call in the EMT thread. */
4801 rc = VMR3SetCpuExecutionCap(ptrVM, aExecutionCap);
4802 }
4803 else
4804 rc = setInvalidMachineStateError();
4805 ptrVM.release();
4806 }
4807
4808 /* notify console callbacks on success */
4809 if (SUCCEEDED(rc))
4810 {
4811 alock.release();
4812 fireCPUExecutionCapChangedEvent(mEventSource, aExecutionCap);
4813 }
4814
4815 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
4816 return rc;
4817}
4818
4819/**
4820 * Called by IInternalSessionControl::OnVRDEServerChange().
4821 *
4822 * @note Locks this object for writing.
4823 */
4824HRESULT Console::onVRDEServerChange(BOOL aRestart)
4825{
4826 AutoCaller autoCaller(this);
4827 AssertComRCReturnRC(autoCaller.rc());
4828
4829 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4830
4831 HRESULT rc = S_OK;
4832
4833 if ( mVRDEServer
4834 && ( mMachineState == MachineState_Running
4835 || mMachineState == MachineState_Teleporting
4836 || mMachineState == MachineState_LiveSnapshotting
4837 )
4838 )
4839 {
4840 BOOL vrdpEnabled = FALSE;
4841
4842 rc = mVRDEServer->COMGETTER(Enabled)(&vrdpEnabled);
4843 ComAssertComRCRetRC(rc);
4844
4845 if (aRestart)
4846 {
4847 /* VRDP server may call this Console object back from other threads (VRDP INPUT or OUTPUT). */
4848 alock.release();
4849
4850 if (vrdpEnabled)
4851 {
4852 // If there was no VRDP server started the 'stop' will do nothing.
4853 // However if a server was started and this notification was called,
4854 // we have to restart the server.
4855 mConsoleVRDPServer->Stop();
4856
4857 if (RT_FAILURE(mConsoleVRDPServer->Launch()))
4858 rc = E_FAIL;
4859 else
4860 mConsoleVRDPServer->EnableConnections();
4861 }
4862 else
4863 {
4864 mConsoleVRDPServer->Stop();
4865 }
4866
4867 alock.acquire();
4868 }
4869 }
4870
4871 /* notify console callbacks on success */
4872 if (SUCCEEDED(rc))
4873 {
4874 alock.release();
4875 fireVRDEServerChangedEvent(mEventSource);
4876 }
4877
4878 return rc;
4879}
4880
4881void Console::onVRDEServerInfoChange()
4882{
4883 AutoCaller autoCaller(this);
4884 AssertComRCReturnVoid(autoCaller.rc());
4885
4886 fireVRDEServerInfoChangedEvent(mEventSource);
4887}
4888
4889
4890/**
4891 * Called by IInternalSessionControl::OnUSBControllerChange().
4892 */
4893HRESULT Console::onUSBControllerChange()
4894{
4895 LogFlowThisFunc(("\n"));
4896
4897 AutoCaller autoCaller(this);
4898 AssertComRCReturnRC(autoCaller.rc());
4899
4900 fireUSBControllerChangedEvent(mEventSource);
4901
4902 return S_OK;
4903}
4904
4905/**
4906 * Called by IInternalSessionControl::OnSharedFolderChange().
4907 *
4908 * @note Locks this object for writing.
4909 */
4910HRESULT Console::onSharedFolderChange(BOOL aGlobal)
4911{
4912 LogFlowThisFunc(("aGlobal=%RTbool\n", aGlobal));
4913
4914 AutoCaller autoCaller(this);
4915 AssertComRCReturnRC(autoCaller.rc());
4916
4917 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4918
4919 HRESULT rc = fetchSharedFolders(aGlobal);
4920
4921 /* notify console callbacks on success */
4922 if (SUCCEEDED(rc))
4923 {
4924 alock.release();
4925 fireSharedFolderChangedEvent(mEventSource, aGlobal ? (Scope_T)Scope_Global : (Scope_T)Scope_Machine);
4926 }
4927
4928 return rc;
4929}
4930
4931/**
4932 * Called by IInternalSessionControl::OnUSBDeviceAttach() or locally by
4933 * processRemoteUSBDevices() after IInternalMachineControl::RunUSBDeviceFilters()
4934 * returns TRUE for a given remote USB device.
4935 *
4936 * @return S_OK if the device was attached to the VM.
4937 * @return failure if not attached.
4938 *
4939 * @param aDevice
4940 * The device in question.
4941 * @param aMaskedIfs
4942 * The interfaces to hide from the guest.
4943 *
4944 * @note Locks this object for writing.
4945 */
4946HRESULT Console::onUSBDeviceAttach(IUSBDevice *aDevice, IVirtualBoxErrorInfo *aError, ULONG aMaskedIfs)
4947{
4948#ifdef VBOX_WITH_USB
4949 LogFlowThisFunc(("aDevice=%p aError=%p\n", aDevice, aError));
4950
4951 AutoCaller autoCaller(this);
4952 ComAssertComRCRetRC(autoCaller.rc());
4953
4954 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4955
4956 /* Get the VM pointer (we don't need error info, since it's a callback). */
4957 SafeVMPtrQuiet ptrVM(this);
4958 if (!ptrVM.isOk())
4959 {
4960 /* The VM may be no more operational when this message arrives
4961 * (e.g. it may be Saving or Stopping or just PoweredOff) --
4962 * autoVMCaller.rc() will return a failure in this case. */
4963 LogFlowThisFunc(("Attach request ignored (mMachineState=%d).\n",
4964 mMachineState));
4965 return ptrVM.rc();
4966 }
4967
4968 if (aError != NULL)
4969 {
4970 /* notify callbacks about the error */
4971 onUSBDeviceStateChange(aDevice, true /* aAttached */, aError);
4972 return S_OK;
4973 }
4974
4975 /* Don't proceed unless there's at least one USB hub. */
4976 if (!PDMR3USBHasHub(ptrVM))
4977 {
4978 LogFlowThisFunc(("Attach request ignored (no USB controller).\n"));
4979 return E_FAIL;
4980 }
4981
4982 HRESULT rc = attachUSBDevice(aDevice, aMaskedIfs);
4983 if (FAILED(rc))
4984 {
4985 /* take the current error info */
4986 com::ErrorInfoKeeper eik;
4987 /* the error must be a VirtualBoxErrorInfo instance */
4988 ComPtr<IVirtualBoxErrorInfo> pError = eik.takeError();
4989 Assert(!pError.isNull());
4990 if (!pError.isNull())
4991 {
4992 /* notify callbacks about the error */
4993 onUSBDeviceStateChange(aDevice, true /* aAttached */, pError);
4994 }
4995 }
4996
4997 return rc;
4998
4999#else /* !VBOX_WITH_USB */
5000 return E_FAIL;
5001#endif /* !VBOX_WITH_USB */
5002}
5003
5004/**
5005 * Called by IInternalSessionControl::OnUSBDeviceDetach() and locally by
5006 * processRemoteUSBDevices().
5007 *
5008 * @note Locks this object for writing.
5009 */
5010HRESULT Console::onUSBDeviceDetach(IN_BSTR aId,
5011 IVirtualBoxErrorInfo *aError)
5012{
5013#ifdef VBOX_WITH_USB
5014 Guid Uuid(aId);
5015 LogFlowThisFunc(("aId={%RTuuid} aError=%p\n", Uuid.raw(), aError));
5016
5017 AutoCaller autoCaller(this);
5018 AssertComRCReturnRC(autoCaller.rc());
5019
5020 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5021
5022 /* Find the device. */
5023 ComObjPtr<OUSBDevice> pUSBDevice;
5024 USBDeviceList::iterator it = mUSBDevices.begin();
5025 while (it != mUSBDevices.end())
5026 {
5027 LogFlowThisFunc(("it={%RTuuid}\n", (*it)->id().raw()));
5028 if ((*it)->id() == Uuid)
5029 {
5030 pUSBDevice = *it;
5031 break;
5032 }
5033 ++it;
5034 }
5035
5036
5037 if (pUSBDevice.isNull())
5038 {
5039 LogFlowThisFunc(("USB device not found.\n"));
5040
5041 /* The VM may be no more operational when this message arrives
5042 * (e.g. it may be Saving or Stopping or just PoweredOff). Use
5043 * AutoVMCaller to detect it -- AutoVMCaller::rc() will return a
5044 * failure in this case. */
5045
5046 AutoVMCallerQuiet autoVMCaller(this);
5047 if (FAILED(autoVMCaller.rc()))
5048 {
5049 LogFlowThisFunc(("Detach request ignored (mMachineState=%d).\n",
5050 mMachineState));
5051 return autoVMCaller.rc();
5052 }
5053
5054 /* the device must be in the list otherwise */
5055 AssertFailedReturn(E_FAIL);
5056 }
5057
5058 if (aError != NULL)
5059 {
5060 /* notify callback about an error */
5061 onUSBDeviceStateChange(pUSBDevice, false /* aAttached */, aError);
5062 return S_OK;
5063 }
5064
5065 HRESULT rc = detachUSBDevice(it);
5066
5067 if (FAILED(rc))
5068 {
5069 /* take the current error info */
5070 com::ErrorInfoKeeper eik;
5071 /* the error must be a VirtualBoxErrorInfo instance */
5072 ComPtr<IVirtualBoxErrorInfo> pError = eik.takeError();
5073 Assert(!pError.isNull());
5074 if (!pError.isNull())
5075 {
5076 /* notify callbacks about the error */
5077 onUSBDeviceStateChange(pUSBDevice, false /* aAttached */, pError);
5078 }
5079 }
5080
5081 return rc;
5082
5083#else /* !VBOX_WITH_USB */
5084 return E_FAIL;
5085#endif /* !VBOX_WITH_USB */
5086}
5087
5088/**
5089 * Called by IInternalSessionControl::OnBandwidthGroupChange().
5090 *
5091 * @note Locks this object for writing.
5092 */
5093HRESULT Console::onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
5094{
5095 LogFlowThisFunc(("\n"));
5096
5097 AutoCaller autoCaller(this);
5098 AssertComRCReturnRC(autoCaller.rc());
5099
5100 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5101
5102 HRESULT rc = S_OK;
5103
5104 /* don't trigger the CPU priority change if the VM isn't running */
5105 SafeVMPtrQuiet ptrVM(this);
5106 if (ptrVM.isOk())
5107 {
5108 if ( mMachineState == MachineState_Running
5109 || mMachineState == MachineState_Teleporting
5110 || mMachineState == MachineState_LiveSnapshotting
5111 )
5112 {
5113 /* No need to call in the EMT thread. */
5114 ULONG cMax;
5115 Bstr strName;
5116 rc = aBandwidthGroup->COMGETTER(Name)(strName.asOutParam());
5117 if (SUCCEEDED(rc))
5118 rc = aBandwidthGroup->COMGETTER(MaxMbPerSec)(&cMax);
5119
5120 if (SUCCEEDED(rc))
5121 {
5122 int vrc;
5123 vrc = PDMR3AsyncCompletionBwMgrSetMaxForFile(ptrVM, Utf8Str(strName).c_str(),
5124 cMax * _1M);
5125 AssertRC(vrc);
5126 }
5127 }
5128 else
5129 rc = setInvalidMachineStateError();
5130 ptrVM.release();
5131 }
5132
5133 /* notify console callbacks on success */
5134 if (SUCCEEDED(rc))
5135 {
5136 alock.release();
5137 fireBandwidthGroupChangedEvent(mEventSource, aBandwidthGroup);
5138 }
5139
5140 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
5141 return rc;
5142}
5143
5144/**
5145 * Called by IInternalSessionControl::OnStorageDeviceChange().
5146 *
5147 * @note Locks this object for writing.
5148 */
5149HRESULT Console::onStorageDeviceChange(IMediumAttachment *aMediumAttachment, BOOL aRemove)
5150{
5151 LogFlowThisFunc(("\n"));
5152
5153 AutoCaller autoCaller(this);
5154 AssertComRCReturnRC(autoCaller.rc());
5155
5156 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5157
5158 HRESULT rc = S_OK;
5159
5160 /* don't trigger medium change if the VM isn't running */
5161 SafeVMPtrQuiet ptrVM(this);
5162 if (ptrVM.isOk())
5163 {
5164 if (aRemove)
5165 rc = doStorageDeviceDetach(aMediumAttachment, ptrVM);
5166 else
5167 rc = doStorageDeviceAttach(aMediumAttachment, ptrVM);
5168 ptrVM.release();
5169 }
5170
5171 /* notify console callbacks on success */
5172 if (SUCCEEDED(rc))
5173 {
5174 alock.release(); /** @todo 101% safe? */
5175 fireStorageDeviceChangedEvent(mEventSource, aMediumAttachment, aRemove);
5176 }
5177
5178 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
5179 return rc;
5180}
5181
5182/**
5183 * @note Temporarily locks this object for writing.
5184 */
5185HRESULT Console::getGuestProperty(IN_BSTR aName, BSTR *aValue,
5186 LONG64 *aTimestamp, BSTR *aFlags)
5187{
5188#ifndef VBOX_WITH_GUEST_PROPS
5189 ReturnComNotImplemented();
5190#else /* VBOX_WITH_GUEST_PROPS */
5191 if (!VALID_PTR(aName))
5192 return E_INVALIDARG;
5193 if (!VALID_PTR(aValue))
5194 return E_POINTER;
5195 if ((aTimestamp != NULL) && !VALID_PTR(aTimestamp))
5196 return E_POINTER;
5197 if ((aFlags != NULL) && !VALID_PTR(aFlags))
5198 return E_POINTER;
5199
5200 AutoCaller autoCaller(this);
5201 AssertComRCReturnRC(autoCaller.rc());
5202
5203 /* protect mpVM (if not NULL) */
5204 AutoVMCallerWeak autoVMCaller(this);
5205 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
5206
5207 /* Note: validity of mVMMDev which is bound to uninit() is guaranteed by
5208 * autoVMCaller, so there is no need to hold a lock of this */
5209
5210 HRESULT rc = E_UNEXPECTED;
5211 using namespace guestProp;
5212
5213 try
5214 {
5215 VBOXHGCMSVCPARM parm[4];
5216 Utf8Str Utf8Name = aName;
5217 char szBuffer[MAX_VALUE_LEN + MAX_FLAGS_LEN];
5218
5219 parm[0].type = VBOX_HGCM_SVC_PARM_PTR;
5220 parm[0].u.pointer.addr = (void*)Utf8Name.c_str();
5221 /* The + 1 is the null terminator */
5222 parm[0].u.pointer.size = (uint32_t)Utf8Name.length() + 1;
5223 parm[1].type = VBOX_HGCM_SVC_PARM_PTR;
5224 parm[1].u.pointer.addr = szBuffer;
5225 parm[1].u.pointer.size = sizeof(szBuffer);
5226 int vrc = m_pVMMDev->hgcmHostCall("VBoxGuestPropSvc", GET_PROP_HOST,
5227 4, &parm[0]);
5228 /* The returned string should never be able to be greater than our buffer */
5229 AssertLogRel(vrc != VERR_BUFFER_OVERFLOW);
5230 AssertLogRel(RT_FAILURE(vrc) || VBOX_HGCM_SVC_PARM_64BIT == parm[2].type);
5231 if (RT_SUCCESS(vrc) || (VERR_NOT_FOUND == vrc))
5232 {
5233 rc = S_OK;
5234 if (vrc != VERR_NOT_FOUND)
5235 {
5236 Utf8Str strBuffer(szBuffer);
5237 strBuffer.cloneTo(aValue);
5238
5239 if (aTimestamp)
5240 *aTimestamp = parm[2].u.uint64;
5241
5242 if (aFlags)
5243 {
5244 size_t iFlags = strBuffer.length() + 1;
5245 Utf8Str(szBuffer + iFlags).cloneTo(aFlags);
5246 }
5247 }
5248 else
5249 aValue = NULL;
5250 }
5251 else
5252 rc = setError(E_UNEXPECTED,
5253 tr("The service call failed with the error %Rrc"),
5254 vrc);
5255 }
5256 catch(std::bad_alloc & /*e*/)
5257 {
5258 rc = E_OUTOFMEMORY;
5259 }
5260 return rc;
5261#endif /* VBOX_WITH_GUEST_PROPS */
5262}
5263
5264/**
5265 * @note Temporarily locks this object for writing.
5266 */
5267HRESULT Console::setGuestProperty(IN_BSTR aName, IN_BSTR aValue, IN_BSTR aFlags)
5268{
5269#ifndef VBOX_WITH_GUEST_PROPS
5270 ReturnComNotImplemented();
5271#else /* VBOX_WITH_GUEST_PROPS */
5272 if (!VALID_PTR(aName))
5273 return E_INVALIDARG;
5274 if ((aValue != NULL) && !VALID_PTR(aValue))
5275 return E_INVALIDARG;
5276 if ((aFlags != NULL) && !VALID_PTR(aFlags))
5277 return E_INVALIDARG;
5278
5279 AutoCaller autoCaller(this);
5280 AssertComRCReturnRC(autoCaller.rc());
5281
5282 /* protect mpVM (if not NULL) */
5283 AutoVMCallerWeak autoVMCaller(this);
5284 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
5285
5286 /* Note: validity of mVMMDev which is bound to uninit() is guaranteed by
5287 * autoVMCaller, so there is no need to hold a lock of this */
5288
5289 HRESULT rc = E_UNEXPECTED;
5290 using namespace guestProp;
5291
5292 VBOXHGCMSVCPARM parm[3];
5293 Utf8Str Utf8Name = aName;
5294 int vrc = VINF_SUCCESS;
5295
5296 parm[0].type = VBOX_HGCM_SVC_PARM_PTR;
5297 parm[0].u.pointer.addr = (void*)Utf8Name.c_str();
5298 /* The + 1 is the null terminator */
5299 parm[0].u.pointer.size = (uint32_t)Utf8Name.length() + 1;
5300 Utf8Str Utf8Value = aValue;
5301 if (aValue != NULL)
5302 {
5303 parm[1].type = VBOX_HGCM_SVC_PARM_PTR;
5304 parm[1].u.pointer.addr = (void*)Utf8Value.c_str();
5305 /* The + 1 is the null terminator */
5306 parm[1].u.pointer.size = (uint32_t)Utf8Value.length() + 1;
5307 }
5308 Utf8Str Utf8Flags = aFlags;
5309 if (aFlags != NULL)
5310 {
5311 parm[2].type = VBOX_HGCM_SVC_PARM_PTR;
5312 parm[2].u.pointer.addr = (void*)Utf8Flags.c_str();
5313 /* The + 1 is the null terminator */
5314 parm[2].u.pointer.size = (uint32_t)Utf8Flags.length() + 1;
5315 }
5316 if ((aValue != NULL) && (aFlags != NULL))
5317 vrc = m_pVMMDev->hgcmHostCall("VBoxGuestPropSvc", SET_PROP_HOST,
5318 3, &parm[0]);
5319 else if (aValue != NULL)
5320 vrc = m_pVMMDev->hgcmHostCall("VBoxGuestPropSvc", SET_PROP_VALUE_HOST,
5321 2, &parm[0]);
5322 else
5323 vrc = m_pVMMDev->hgcmHostCall("VBoxGuestPropSvc", DEL_PROP_HOST,
5324 1, &parm[0]);
5325 if (RT_SUCCESS(vrc))
5326 rc = S_OK;
5327 else
5328 rc = setError(E_UNEXPECTED,
5329 tr("The service call failed with the error %Rrc"),
5330 vrc);
5331 return rc;
5332#endif /* VBOX_WITH_GUEST_PROPS */
5333}
5334
5335
5336/**
5337 * @note Temporarily locks this object for writing.
5338 */
5339HRESULT Console::enumerateGuestProperties(IN_BSTR aPatterns,
5340 ComSafeArrayOut(BSTR, aNames),
5341 ComSafeArrayOut(BSTR, aValues),
5342 ComSafeArrayOut(LONG64, aTimestamps),
5343 ComSafeArrayOut(BSTR, aFlags))
5344{
5345#ifndef VBOX_WITH_GUEST_PROPS
5346 ReturnComNotImplemented();
5347#else /* VBOX_WITH_GUEST_PROPS */
5348 if (!VALID_PTR(aPatterns) && (aPatterns != NULL))
5349 return E_POINTER;
5350 if (ComSafeArrayOutIsNull(aNames))
5351 return E_POINTER;
5352 if (ComSafeArrayOutIsNull(aValues))
5353 return E_POINTER;
5354 if (ComSafeArrayOutIsNull(aTimestamps))
5355 return E_POINTER;
5356 if (ComSafeArrayOutIsNull(aFlags))
5357 return E_POINTER;
5358
5359 AutoCaller autoCaller(this);
5360 AssertComRCReturnRC(autoCaller.rc());
5361
5362 /* protect mpVM (if not NULL) */
5363 AutoVMCallerWeak autoVMCaller(this);
5364 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
5365
5366 /* Note: validity of mVMMDev which is bound to uninit() is guaranteed by
5367 * autoVMCaller, so there is no need to hold a lock of this */
5368
5369 return doEnumerateGuestProperties(aPatterns, ComSafeArrayOutArg(aNames),
5370 ComSafeArrayOutArg(aValues),
5371 ComSafeArrayOutArg(aTimestamps),
5372 ComSafeArrayOutArg(aFlags));
5373#endif /* VBOX_WITH_GUEST_PROPS */
5374}
5375
5376
5377/*
5378 * Internal: helper function for connecting progress reporting
5379 */
5380static int onlineMergeMediumProgress(void *pvUser, unsigned uPercentage)
5381{
5382 HRESULT rc = S_OK;
5383 IProgress *pProgress = static_cast<IProgress *>(pvUser);
5384 if (pProgress)
5385 rc = pProgress->SetCurrentOperationProgress(uPercentage);
5386 return SUCCEEDED(rc) ? VINF_SUCCESS : VERR_GENERAL_FAILURE;
5387}
5388
5389/**
5390 * @note Temporarily locks this object for writing. bird: And/or reading?
5391 */
5392HRESULT Console::onlineMergeMedium(IMediumAttachment *aMediumAttachment,
5393 ULONG aSourceIdx, ULONG aTargetIdx,
5394 IMedium *aSource, IMedium *aTarget,
5395 BOOL aMergeForward,
5396 IMedium *aParentForTarget,
5397 ComSafeArrayIn(IMedium *, aChildrenToReparent),
5398 IProgress *aProgress)
5399{
5400 AutoCaller autoCaller(this);
5401 AssertComRCReturnRC(autoCaller.rc());
5402
5403 HRESULT rc = S_OK;
5404 int vrc = VINF_SUCCESS;
5405
5406 /* Get the VM - must be done before the read-locking. */
5407 SafeVMPtr ptrVM(this);
5408 if (!ptrVM.isOk())
5409 return ptrVM.rc();
5410
5411 /* We will need to release the lock before doing the actual merge */
5412 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5413
5414 /* paranoia - we don't want merges to happen while teleporting etc. */
5415 switch (mMachineState)
5416 {
5417 case MachineState_DeletingSnapshotOnline:
5418 case MachineState_DeletingSnapshotPaused:
5419 break;
5420
5421 default:
5422 return setInvalidMachineStateError();
5423 }
5424
5425 /** @todo AssertComRC -> AssertComRCReturn! Could potentially end up
5426 * using uninitialized variables here. */
5427 BOOL fBuiltinIoCache;
5428 rc = mMachine->COMGETTER(IoCacheEnabled)(&fBuiltinIoCache);
5429 AssertComRC(rc);
5430 SafeIfaceArray<IStorageController> ctrls;
5431 rc = mMachine->COMGETTER(StorageControllers)(ComSafeArrayAsOutParam(ctrls));
5432 AssertComRC(rc);
5433 LONG lDev;
5434 rc = aMediumAttachment->COMGETTER(Device)(&lDev);
5435 AssertComRC(rc);
5436 LONG lPort;
5437 rc = aMediumAttachment->COMGETTER(Port)(&lPort);
5438 AssertComRC(rc);
5439 IMedium *pMedium;
5440 rc = aMediumAttachment->COMGETTER(Medium)(&pMedium);
5441 AssertComRC(rc);
5442 Bstr mediumLocation;
5443 if (pMedium)
5444 {
5445 rc = pMedium->COMGETTER(Location)(mediumLocation.asOutParam());
5446 AssertComRC(rc);
5447 }
5448
5449 Bstr attCtrlName;
5450 rc = aMediumAttachment->COMGETTER(Controller)(attCtrlName.asOutParam());
5451 AssertComRC(rc);
5452 ComPtr<IStorageController> pStorageController;
5453 for (size_t i = 0; i < ctrls.size(); ++i)
5454 {
5455 Bstr ctrlName;
5456 rc = ctrls[i]->COMGETTER(Name)(ctrlName.asOutParam());
5457 AssertComRC(rc);
5458 if (attCtrlName == ctrlName)
5459 {
5460 pStorageController = ctrls[i];
5461 break;
5462 }
5463 }
5464 if (pStorageController.isNull())
5465 return setError(E_FAIL,
5466 tr("Could not find storage controller '%ls'"),
5467 attCtrlName.raw());
5468
5469 StorageControllerType_T enmCtrlType;
5470 rc = pStorageController->COMGETTER(ControllerType)(&enmCtrlType);
5471 AssertComRC(rc);
5472 const char *pcszDevice = convertControllerTypeToDev(enmCtrlType);
5473
5474 StorageBus_T enmBus;
5475 rc = pStorageController->COMGETTER(Bus)(&enmBus);
5476 AssertComRC(rc);
5477 ULONG uInstance;
5478 rc = pStorageController->COMGETTER(Instance)(&uInstance);
5479 AssertComRC(rc);
5480 BOOL fUseHostIOCache;
5481 rc = pStorageController->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
5482 AssertComRC(rc);
5483
5484 unsigned uLUN;
5485 rc = Console::convertBusPortDeviceToLun(enmBus, lPort, lDev, uLUN);
5486 AssertComRCReturnRC(rc);
5487
5488 alock.release();
5489
5490 /* Pause the VM, as it might have pending IO on this drive */
5491 VMSTATE enmVMState = VMR3GetState(ptrVM);
5492 if (mMachineState == MachineState_DeletingSnapshotOnline)
5493 {
5494 LogFlowFunc(("Suspending the VM...\n"));
5495 /* disable the callback to prevent Console-level state change */
5496 mVMStateChangeCallbackDisabled = true;
5497 int vrc2 = VMR3Suspend(ptrVM);
5498 mVMStateChangeCallbackDisabled = false;
5499 AssertRCReturn(vrc2, E_FAIL);
5500 }
5501
5502 vrc = VMR3ReqCallWait(ptrVM,
5503 VMCPUID_ANY,
5504 (PFNRT)reconfigureMediumAttachment,
5505 13,
5506 this,
5507 ptrVM.raw(),
5508 pcszDevice,
5509 uInstance,
5510 enmBus,
5511 fUseHostIOCache,
5512 fBuiltinIoCache,
5513 true /* fSetupMerge */,
5514 aSourceIdx,
5515 aTargetIdx,
5516 aMediumAttachment,
5517 mMachineState,
5518 &rc);
5519 /* error handling is after resuming the VM */
5520
5521 if (mMachineState == MachineState_DeletingSnapshotOnline)
5522 {
5523 LogFlowFunc(("Resuming the VM...\n"));
5524 /* disable the callback to prevent Console-level state change */
5525 mVMStateChangeCallbackDisabled = true;
5526 int vrc2 = VMR3Resume(ptrVM);
5527 mVMStateChangeCallbackDisabled = false;
5528 if (RT_FAILURE(vrc2))
5529 {
5530 /* too bad, we failed. try to sync the console state with the VMM state */
5531 AssertLogRelRC(vrc2);
5532 vmstateChangeCallback(ptrVM, VMSTATE_SUSPENDED, enmVMState, this);
5533 }
5534 }
5535
5536 if (RT_FAILURE(vrc))
5537 return setError(E_FAIL, tr("%Rrc"), vrc);
5538 if (FAILED(rc))
5539 return rc;
5540
5541 PPDMIBASE pIBase = NULL;
5542 PPDMIMEDIA pIMedium = NULL;
5543 vrc = PDMR3QueryDriverOnLun(ptrVM, pcszDevice, uInstance, uLUN, "VD", &pIBase);
5544 if (RT_SUCCESS(vrc))
5545 {
5546 if (pIBase)
5547 {
5548 pIMedium = (PPDMIMEDIA)pIBase->pfnQueryInterface(pIBase, PDMIMEDIA_IID);
5549 if (!pIMedium)
5550 return setError(E_FAIL, tr("could not query medium interface of controller"));
5551 }
5552 else
5553 return setError(E_FAIL, tr("could not query base interface of controller"));
5554 }
5555
5556 /* Finally trigger the merge. */
5557 vrc = pIMedium->pfnMerge(pIMedium, onlineMergeMediumProgress, aProgress);
5558 if (RT_FAILURE(vrc))
5559 return setError(E_FAIL, tr("Failed to perform an online medium merge (%Rrc)"), vrc);
5560
5561 /* Pause the VM, as it might have pending IO on this drive */
5562 enmVMState = VMR3GetState(ptrVM);
5563 if (mMachineState == MachineState_DeletingSnapshotOnline)
5564 {
5565 LogFlowFunc(("Suspending the VM...\n"));
5566 /* disable the callback to prevent Console-level state change */
5567 mVMStateChangeCallbackDisabled = true;
5568 int vrc2 = VMR3Suspend(ptrVM);
5569 mVMStateChangeCallbackDisabled = false;
5570 AssertRCReturn(vrc2, E_FAIL);
5571 }
5572
5573 /* Update medium chain and state now, so that the VM can continue. */
5574 rc = mControl->FinishOnlineMergeMedium(aMediumAttachment, aSource, aTarget,
5575 aMergeForward, aParentForTarget,
5576 ComSafeArrayInArg(aChildrenToReparent));
5577
5578 vrc = VMR3ReqCallWait(ptrVM,
5579 VMCPUID_ANY,
5580 (PFNRT)reconfigureMediumAttachment,
5581 13,
5582 this,
5583 ptrVM.raw(),
5584 pcszDevice,
5585 uInstance,
5586 enmBus,
5587 fUseHostIOCache,
5588 fBuiltinIoCache,
5589 false /* fSetupMerge */,
5590 0 /* uMergeSource */,
5591 0 /* uMergeTarget */,
5592 aMediumAttachment,
5593 mMachineState,
5594 &rc);
5595 /* error handling is after resuming the VM */
5596
5597 if (mMachineState == MachineState_DeletingSnapshotOnline)
5598 {
5599 LogFlowFunc(("Resuming the VM...\n"));
5600 /* disable the callback to prevent Console-level state change */
5601 mVMStateChangeCallbackDisabled = true;
5602 int vrc2 = VMR3Resume(ptrVM);
5603 mVMStateChangeCallbackDisabled = false;
5604 AssertRC(vrc2);
5605 if (RT_FAILURE(vrc2))
5606 {
5607 /* too bad, we failed. try to sync the console state with the VMM state */
5608 vmstateChangeCallback(ptrVM, VMSTATE_SUSPENDED, enmVMState, this);
5609 }
5610 }
5611
5612 if (RT_FAILURE(vrc))
5613 return setError(E_FAIL, tr("%Rrc"), vrc);
5614 if (FAILED(rc))
5615 return rc;
5616
5617 return rc;
5618}
5619
5620
5621/**
5622 * Merely passes the call to Guest::enableVMMStatistics().
5623 */
5624void Console::enableVMMStatistics(BOOL aEnable)
5625{
5626 if (mGuest)
5627 mGuest->enableVMMStatistics(aEnable);
5628}
5629
5630/**
5631 * Gets called by Session::UpdateMachineState()
5632 * (IInternalSessionControl::updateMachineState()).
5633 *
5634 * Must be called only in certain cases (see the implementation).
5635 *
5636 * @note Locks this object for writing.
5637 */
5638HRESULT Console::updateMachineState(MachineState_T aMachineState)
5639{
5640 AutoCaller autoCaller(this);
5641 AssertComRCReturnRC(autoCaller.rc());
5642
5643 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5644
5645 AssertReturn( mMachineState == MachineState_Saving
5646 || mMachineState == MachineState_LiveSnapshotting
5647 || mMachineState == MachineState_RestoringSnapshot
5648 || mMachineState == MachineState_DeletingSnapshot
5649 || mMachineState == MachineState_DeletingSnapshotOnline
5650 || mMachineState == MachineState_DeletingSnapshotPaused
5651 , E_FAIL);
5652
5653 return setMachineStateLocally(aMachineState);
5654}
5655
5656#ifdef CONSOLE_WITH_EVENT_CACHE
5657/**
5658 * @note Locks this object for writing.
5659 */
5660#endif
5661void Console::onMousePointerShapeChange(bool fVisible, bool fAlpha,
5662 uint32_t xHot, uint32_t yHot,
5663 uint32_t width, uint32_t height,
5664 ComSafeArrayIn(BYTE,pShape))
5665{
5666#if 0
5667 LogFlowThisFuncEnter();
5668 LogFlowThisFunc(("fVisible=%d, fAlpha=%d, xHot = %d, yHot = %d, width=%d, height=%d, shape=%p\n",
5669 fVisible, fAlpha, xHot, yHot, width, height, pShape));
5670#endif
5671
5672 AutoCaller autoCaller(this);
5673 AssertComRCReturnVoid(autoCaller.rc());
5674
5675#ifdef CONSOLE_WITH_EVENT_CACHE
5676 {
5677 /* We need a write lock because we alter the cached callback data */
5678 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5679
5680 /* Save the callback arguments */
5681 mCallbackData.mpsc.visible = fVisible;
5682 mCallbackData.mpsc.alpha = fAlpha;
5683 mCallbackData.mpsc.xHot = xHot;
5684 mCallbackData.mpsc.yHot = yHot;
5685 mCallbackData.mpsc.width = width;
5686 mCallbackData.mpsc.height = height;
5687
5688 /* start with not valid */
5689 bool wasValid = mCallbackData.mpsc.valid;
5690 mCallbackData.mpsc.valid = false;
5691
5692 com::SafeArray<BYTE> aShape(ComSafeArrayInArg(pShape));
5693 if (aShape.size() != 0)
5694 mCallbackData.mpsc.shape.initFrom(aShape);
5695 else
5696 mCallbackData.mpsc.shape.resize(0);
5697 mCallbackData.mpsc.valid = true;
5698 }
5699#endif
5700
5701 fireMousePointerShapeChangedEvent(mEventSource, fVisible, fAlpha, xHot, yHot, width, height, ComSafeArrayInArg(pShape));
5702
5703#if 0
5704 LogFlowThisFuncLeave();
5705#endif
5706}
5707
5708#ifdef CONSOLE_WITH_EVENT_CACHE
5709/**
5710 * @note Locks this object for writing.
5711 */
5712#endif
5713void Console::onMouseCapabilityChange(BOOL supportsAbsolute, BOOL supportsRelative, BOOL needsHostCursor)
5714{
5715 LogFlowThisFunc(("supportsAbsolute=%d supportsRelative=%d needsHostCursor=%d\n",
5716 supportsAbsolute, supportsRelative, needsHostCursor));
5717
5718 AutoCaller autoCaller(this);
5719 AssertComRCReturnVoid(autoCaller.rc());
5720
5721#ifdef CONSOLE_WITH_EVENT_CACHE
5722 {
5723 /* We need a write lock because we alter the cached callback data */
5724 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5725
5726 /* save the callback arguments */
5727 mCallbackData.mcc.supportsAbsolute = supportsAbsolute;
5728 mCallbackData.mcc.supportsRelative = supportsRelative;
5729 mCallbackData.mcc.needsHostCursor = needsHostCursor;
5730 mCallbackData.mcc.valid = true;
5731 }
5732#endif
5733
5734 fireMouseCapabilityChangedEvent(mEventSource, supportsAbsolute, supportsRelative, needsHostCursor);
5735}
5736
5737void Console::onStateChange(MachineState_T machineState)
5738{
5739 AutoCaller autoCaller(this);
5740 AssertComRCReturnVoid(autoCaller.rc());
5741
5742 fireStateChangedEvent(mEventSource, machineState);
5743}
5744
5745void Console::onAdditionsStateChange()
5746{
5747 AutoCaller autoCaller(this);
5748 AssertComRCReturnVoid(autoCaller.rc());
5749
5750 fireAdditionsStateChangedEvent(mEventSource);
5751}
5752
5753/**
5754 * @remarks This notification only is for reporting an incompatible
5755 * Guest Additions interface, *not* the Guest Additions version!
5756 *
5757 * The user will be notified inside the guest if new Guest
5758 * Additions are available (via VBoxTray/VBoxClient).
5759 */
5760void Console::onAdditionsOutdated()
5761{
5762 AutoCaller autoCaller(this);
5763 AssertComRCReturnVoid(autoCaller.rc());
5764
5765 /** @todo implement this */
5766}
5767
5768#ifdef CONSOLE_WITH_EVENT_CACHE
5769/**
5770 * @note Locks this object for writing.
5771 */
5772#endif
5773void Console::onKeyboardLedsChange(bool fNumLock, bool fCapsLock, bool fScrollLock)
5774{
5775 AutoCaller autoCaller(this);
5776 AssertComRCReturnVoid(autoCaller.rc());
5777
5778#ifdef CONSOLE_WITH_EVENT_CACHE
5779 {
5780 /* We need a write lock because we alter the cached callback data */
5781 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5782
5783 /* save the callback arguments */
5784 mCallbackData.klc.numLock = fNumLock;
5785 mCallbackData.klc.capsLock = fCapsLock;
5786 mCallbackData.klc.scrollLock = fScrollLock;
5787 mCallbackData.klc.valid = true;
5788 }
5789#endif
5790
5791 fireKeyboardLedsChangedEvent(mEventSource, fNumLock, fCapsLock, fScrollLock);
5792}
5793
5794void Console::onUSBDeviceStateChange(IUSBDevice *aDevice, bool aAttached,
5795 IVirtualBoxErrorInfo *aError)
5796{
5797 AutoCaller autoCaller(this);
5798 AssertComRCReturnVoid(autoCaller.rc());
5799
5800 fireUSBDeviceStateChangedEvent(mEventSource, aDevice, aAttached, aError);
5801}
5802
5803void Console::onRuntimeError(BOOL aFatal, IN_BSTR aErrorID, IN_BSTR aMessage)
5804{
5805 AutoCaller autoCaller(this);
5806 AssertComRCReturnVoid(autoCaller.rc());
5807
5808 fireRuntimeErrorEvent(mEventSource, aFatal, aErrorID, aMessage);
5809}
5810
5811HRESULT Console::onShowWindow(BOOL aCheck, BOOL *aCanShow, LONG64 *aWinId)
5812{
5813 AssertReturn(aCanShow, E_POINTER);
5814 AssertReturn(aWinId, E_POINTER);
5815
5816 *aCanShow = FALSE;
5817 *aWinId = 0;
5818
5819 AutoCaller autoCaller(this);
5820 AssertComRCReturnRC(autoCaller.rc());
5821
5822 VBoxEventDesc evDesc;
5823 if (aCheck)
5824 {
5825 evDesc.init(mEventSource, VBoxEventType_OnCanShowWindow);
5826 BOOL fDelivered = evDesc.fire(5000); /* Wait up to 5 secs for delivery */
5827 //Assert(fDelivered);
5828 if (fDelivered)
5829 {
5830 ComPtr<IEvent> pEvent;
5831 evDesc.getEvent(pEvent.asOutParam());
5832 // bit clumsy
5833 ComPtr<ICanShowWindowEvent> pCanShowEvent = pEvent;
5834 if (pCanShowEvent)
5835 {
5836 BOOL fVetoed = FALSE;
5837 pCanShowEvent->IsVetoed(&fVetoed);
5838 *aCanShow = !fVetoed;
5839 }
5840 else
5841 {
5842 AssertFailed();
5843 *aCanShow = TRUE;
5844 }
5845 }
5846 else
5847 *aCanShow = TRUE;
5848 }
5849 else
5850 {
5851 evDesc.init(mEventSource, VBoxEventType_OnShowWindow, INT64_C(0));
5852 BOOL fDelivered = evDesc.fire(5000); /* Wait up to 5 secs for delivery */
5853 //Assert(fDelivered);
5854 if (fDelivered)
5855 {
5856 ComPtr<IEvent> pEvent;
5857 evDesc.getEvent(pEvent.asOutParam());
5858 ComPtr<IShowWindowEvent> pShowEvent = pEvent;
5859 if (pShowEvent)
5860 {
5861 LONG64 iEvWinId = 0;
5862 pShowEvent->COMGETTER(WinId)(&iEvWinId);
5863 if (iEvWinId != 0 && *aWinId == 0)
5864 *aWinId = iEvWinId;
5865 }
5866 else
5867 AssertFailed();
5868 }
5869 }
5870
5871 return S_OK;
5872}
5873
5874// private methods
5875////////////////////////////////////////////////////////////////////////////////
5876
5877/**
5878 * Increases the usage counter of the mpVM pointer. Guarantees that
5879 * VMR3Destroy() will not be called on it at least until releaseVMCaller()
5880 * is called.
5881 *
5882 * If this method returns a failure, the caller is not allowed to use mpVM
5883 * and may return the failed result code to the upper level. This method sets
5884 * the extended error info on failure if \a aQuiet is false.
5885 *
5886 * Setting \a aQuiet to true is useful for methods that don't want to return
5887 * the failed result code to the caller when this method fails (e.g. need to
5888 * silently check for the mpVM availability).
5889 *
5890 * When mpVM is NULL but \a aAllowNullVM is true, a corresponding error will be
5891 * returned instead of asserting. Having it false is intended as a sanity check
5892 * for methods that have checked mMachineState and expect mpVM *NOT* to be NULL.
5893 *
5894 * @param aQuiet true to suppress setting error info
5895 * @param aAllowNullVM true to accept mpVM being NULL and return a failure
5896 * (otherwise this method will assert if mpVM is NULL)
5897 *
5898 * @note Locks this object for writing.
5899 */
5900HRESULT Console::addVMCaller(bool aQuiet /* = false */,
5901 bool aAllowNullVM /* = false */)
5902{
5903 AutoCaller autoCaller(this);
5904 AssertComRCReturnRC(autoCaller.rc());
5905
5906 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5907
5908 if (mVMDestroying)
5909 {
5910 /* powerDown() is waiting for all callers to finish */
5911 return aQuiet ? E_ACCESSDENIED : setError(E_ACCESSDENIED,
5912 tr("The virtual machine is being powered down"));
5913 }
5914
5915 if (mpUVM == NULL)
5916 {
5917 Assert(aAllowNullVM == true);
5918
5919 /* The machine is not powered up */
5920 return aQuiet ? E_ACCESSDENIED : setError(E_ACCESSDENIED,
5921 tr("The virtual machine is not powered up"));
5922 }
5923
5924 ++mVMCallers;
5925
5926 return S_OK;
5927}
5928
5929/**
5930 * Decreases the usage counter of the mpVM pointer. Must always complete
5931 * the addVMCaller() call after the mpVM pointer is no more necessary.
5932 *
5933 * @note Locks this object for writing.
5934 */
5935void Console::releaseVMCaller()
5936{
5937 AutoCaller autoCaller(this);
5938 AssertComRCReturnVoid(autoCaller.rc());
5939
5940 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5941
5942 AssertReturnVoid(mpUVM != NULL);
5943
5944 Assert(mVMCallers > 0);
5945 --mVMCallers;
5946
5947 if (mVMCallers == 0 && mVMDestroying)
5948 {
5949 /* inform powerDown() there are no more callers */
5950 RTSemEventSignal(mVMZeroCallersSem);
5951 }
5952}
5953
5954
5955HRESULT Console::safeVMPtrRetainer(PVM *a_ppVM, PUVM *a_ppUVM, bool a_Quiet)
5956{
5957 *a_ppVM = NULL;
5958 *a_ppUVM = NULL;
5959
5960 AutoCaller autoCaller(this);
5961 AssertComRCReturnRC(autoCaller.rc());
5962 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5963
5964 /*
5965 * Repeat the checks done by addVMCaller.
5966 */
5967 if (mVMDestroying) /* powerDown() is waiting for all callers to finish */
5968 return a_Quiet
5969 ? E_ACCESSDENIED
5970 : setError(E_ACCESSDENIED, tr("The virtual machine is being powered down"));
5971 PUVM pUVM = mpUVM;
5972 if (!pUVM)
5973 return a_Quiet
5974 ? E_ACCESSDENIED
5975 : setError(E_ACCESSDENIED, tr("The virtual machine is was powered off"));
5976
5977 /*
5978 * Retain a reference to the user mode VM handle and get the global handle.
5979 */
5980 uint32_t cRefs = VMR3RetainUVM(pUVM);
5981 if (cRefs == UINT32_MAX)
5982 return a_Quiet
5983 ? E_ACCESSDENIED
5984 : setError(E_ACCESSDENIED, tr("The virtual machine is was powered off"));
5985
5986 PVM pVM = VMR3GetVM(pUVM);
5987 if (!pVM)
5988 {
5989 VMR3ReleaseUVM(pUVM);
5990 return a_Quiet
5991 ? E_ACCESSDENIED
5992 : setError(E_ACCESSDENIED, tr("The virtual machine is was powered off"));
5993 }
5994
5995 /* done */
5996 *a_ppVM = pVM;
5997 *a_ppUVM = pUVM;
5998 return S_OK;
5999}
6000
6001void Console::safeVMPtrReleaser(PVM *a_ppVM, PUVM *a_ppUVM)
6002{
6003 if (*a_ppVM && *a_ppUVM)
6004 VMR3ReleaseUVM(*a_ppUVM);
6005 *a_ppVM = NULL;
6006 *a_ppUVM = NULL;
6007}
6008
6009
6010/**
6011 * Initialize the release logging facility. In case something
6012 * goes wrong, there will be no release logging. Maybe in the future
6013 * we can add some logic to use different file names in this case.
6014 * Note that the logic must be in sync with Machine::DeleteSettings().
6015 */
6016HRESULT Console::consoleInitReleaseLog(const ComPtr<IMachine> aMachine)
6017{
6018 HRESULT hrc = S_OK;
6019
6020 Bstr logFolder;
6021 hrc = aMachine->COMGETTER(LogFolder)(logFolder.asOutParam());
6022 if (FAILED(hrc)) return hrc;
6023
6024 Utf8Str logDir = logFolder;
6025
6026 /* make sure the Logs folder exists */
6027 Assert(logDir.length());
6028 if (!RTDirExists(logDir.c_str()))
6029 RTDirCreateFullPath(logDir.c_str(), 0700);
6030
6031 Utf8Str logFile = Utf8StrFmt("%s%cVBox.log",
6032 logDir.c_str(), RTPATH_DELIMITER);
6033 Utf8Str pngFile = Utf8StrFmt("%s%cVBox.png",
6034 logDir.c_str(), RTPATH_DELIMITER);
6035
6036 /*
6037 * Age the old log files
6038 * Rename .(n-1) to .(n), .(n-2) to .(n-1), ..., and the last log file to .1
6039 * Overwrite target files in case they exist.
6040 */
6041 ComPtr<IVirtualBox> pVirtualBox;
6042 aMachine->COMGETTER(Parent)(pVirtualBox.asOutParam());
6043 ComPtr<ISystemProperties> pSystemProperties;
6044 pVirtualBox->COMGETTER(SystemProperties)(pSystemProperties.asOutParam());
6045 ULONG cHistoryFiles = 3;
6046 pSystemProperties->COMGETTER(LogHistoryCount)(&cHistoryFiles);
6047 if (cHistoryFiles)
6048 {
6049 for (int i = cHistoryFiles-1; i >= 0; i--)
6050 {
6051 Utf8Str *files[] = { &logFile, &pngFile };
6052 Utf8Str oldName, newName;
6053
6054 for (unsigned int j = 0; j < RT_ELEMENTS(files); ++j)
6055 {
6056 if (i > 0)
6057 oldName = Utf8StrFmt("%s.%d", files[j]->c_str(), i);
6058 else
6059 oldName = *files[j];
6060 newName = Utf8StrFmt("%s.%d", files[j]->c_str(), i + 1);
6061 /* If the old file doesn't exist, delete the new file (if it
6062 * exists) to provide correct rotation even if the sequence is
6063 * broken */
6064 if ( RTFileRename(oldName.c_str(), newName.c_str(), RTFILEMOVE_FLAGS_REPLACE)
6065 == VERR_FILE_NOT_FOUND)
6066 RTFileDelete(newName.c_str());
6067 }
6068 }
6069 }
6070
6071 static const char * const s_apszGroups[] = VBOX_LOGGROUP_NAMES;
6072 char szError[RTPATH_MAX + 128] = "";
6073 PRTLOGGER pReleaseLogger;
6074 uint32_t fFlags = RTLOGFLAGS_PREFIX_TIME_PROG | RTLOGFLAGS_RESTRICT_GROUPS;
6075#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
6076 fFlags |= RTLOGFLAGS_USECRLF;
6077#endif
6078 int vrc = RTLogCreateEx(&pReleaseLogger, fFlags, "all all.restrict default.unrestricted",
6079 "VBOX_RELEASE_LOG", RT_ELEMENTS(s_apszGroups), s_apszGroups, RTLOGDEST_FILE,
6080 NULL /* pfnBeginEnd */, 0 /* cHistory */, 0 /* cbHistoryFileMax */, 0 /* uHistoryTimeMax */,
6081 szError, sizeof(szError), logFile.c_str());
6082 if (RT_SUCCESS(vrc))
6083 {
6084 RTLogSetGroupLimit(pReleaseLogger, 32768);
6085 bool fOldBuffered = RTLogRelSetBuffering(true /*fBuffered*/);
6086
6087 /* some introductory information */
6088 RTTIMESPEC timeSpec;
6089 char szTmp[256];
6090 RTTimeSpecToString(RTTimeNow(&timeSpec), szTmp, sizeof(szTmp));
6091 RTLogRelLogger(pReleaseLogger, 0, ~0U,
6092 "VirtualBox %s r%u %s (%s %s) release log\n"
6093#ifdef VBOX_BLEEDING_EDGE
6094 "EXPERIMENTAL build " VBOX_BLEEDING_EDGE "\n"
6095#endif
6096 "Log opened %s\n",
6097 VBOX_VERSION_STRING, RTBldCfgRevision(), VBOX_BUILD_TARGET,
6098 __DATE__, __TIME__, szTmp);
6099
6100 vrc = RTSystemQueryOSInfo(RTSYSOSINFO_PRODUCT, szTmp, sizeof(szTmp));
6101 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
6102 RTLogRelLogger(pReleaseLogger, 0, ~0U, "OS Product: %s\n", szTmp);
6103 vrc = RTSystemQueryOSInfo(RTSYSOSINFO_RELEASE, szTmp, sizeof(szTmp));
6104 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
6105 RTLogRelLogger(pReleaseLogger, 0, ~0U, "OS Release: %s\n", szTmp);
6106 vrc = RTSystemQueryOSInfo(RTSYSOSINFO_VERSION, szTmp, sizeof(szTmp));
6107 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
6108 RTLogRelLogger(pReleaseLogger, 0, ~0U, "OS Version: %s\n", szTmp);
6109 vrc = RTSystemQueryOSInfo(RTSYSOSINFO_SERVICE_PACK, szTmp, sizeof(szTmp));
6110 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
6111 RTLogRelLogger(pReleaseLogger, 0, ~0U, "OS Service Pack: %s\n", szTmp);
6112 vrc = RTSystemQueryDmiString(RTSYSDMISTR_PRODUCT_NAME, szTmp, sizeof(szTmp));
6113 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
6114 RTLogRelLogger(pReleaseLogger, 0, ~0U, "DMI Product Name: %s\n", szTmp);
6115 vrc = RTSystemQueryDmiString(RTSYSDMISTR_PRODUCT_VERSION, szTmp, sizeof(szTmp));
6116 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
6117 RTLogRelLogger(pReleaseLogger, 0, ~0U, "DMI Product Version: %s\n", szTmp);
6118
6119 ComPtr<IHost> pHost;
6120 pVirtualBox->COMGETTER(Host)(pHost.asOutParam());
6121 ULONG cMbHostRam = 0;
6122 ULONG cMbHostRamAvail = 0;
6123 pHost->COMGETTER(MemorySize)(&cMbHostRam);
6124 pHost->COMGETTER(MemoryAvailable)(&cMbHostRamAvail);
6125 RTLogRelLogger(pReleaseLogger, 0, ~0U, "Host RAM: %uMB RAM, available: %uMB\n",
6126 cMbHostRam, cMbHostRamAvail);
6127
6128 /* the package type is interesting for Linux distributions */
6129 char szExecName[RTPATH_MAX];
6130 char *pszExecName = RTProcGetExecutablePath(szExecName, sizeof(szExecName));
6131 RTLogRelLogger(pReleaseLogger, 0, ~0U,
6132 "Executable: %s\n"
6133 "Process ID: %u\n"
6134 "Package type: %s"
6135#ifdef VBOX_OSE
6136 " (OSE)"
6137#endif
6138 "\n",
6139 pszExecName ? pszExecName : "unknown",
6140 RTProcSelf(),
6141 VBOX_PACKAGE_STRING);
6142
6143 /* register this logger as the release logger */
6144 RTLogRelSetDefaultInstance(pReleaseLogger);
6145 hrc = S_OK;
6146
6147 /* Restore the buffering setting and xplicitly flush the log. */
6148 RTLogRelSetBuffering(fOldBuffered);
6149 RTLogFlush(pReleaseLogger);
6150 }
6151 else
6152 hrc = setError(E_FAIL,
6153 tr("Failed to open release log (%s, %Rrc)"),
6154 szError, vrc);
6155
6156 /* If we've made any directory changes, flush the directory to increase
6157 the likelihood that the log file will be usable after a system panic.
6158
6159 Tip: Try 'export VBOX_RELEASE_LOG_FLAGS=flush' if the last bits of the log
6160 is missing. Just don't have too high hopes for this to help. */
6161 if (SUCCEEDED(hrc) || cHistoryFiles)
6162 RTDirFlush(logDir.c_str());
6163
6164 return hrc;
6165}
6166
6167/**
6168 * Common worker for PowerUp and PowerUpPaused.
6169 *
6170 * @returns COM status code.
6171 *
6172 * @param aProgress Where to return the progress object.
6173 * @param aPaused true if PowerUpPaused called.
6174 */
6175HRESULT Console::powerUp(IProgress **aProgress, bool aPaused)
6176{
6177 LogFlowThisFuncEnter();
6178 LogFlowThisFunc(("mMachineState=%d\n", mMachineState));
6179
6180 CheckComArgOutPointerValid(aProgress);
6181
6182 AutoCaller autoCaller(this);
6183 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6184
6185 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6186
6187 HRESULT rc = S_OK;
6188 ComObjPtr<Progress> pPowerupProgress;
6189 bool fBeganPoweringUp = false;
6190
6191 try
6192 {
6193 if (Global::IsOnlineOrTransient(mMachineState))
6194 throw setError(VBOX_E_INVALID_VM_STATE,
6195 tr("The virtual machine is already running or busy (machine state: %s)"),
6196 Global::stringifyMachineState(mMachineState));
6197
6198 /* test and clear the TeleporterEnabled property */
6199 BOOL fTeleporterEnabled;
6200 rc = mMachine->COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
6201 if (FAILED(rc))
6202 throw rc;
6203#if 0 /** @todo we should save it afterwards, but that isn't necessarily a good idea. Find a better place for this (VBoxSVC). */
6204 if (fTeleporterEnabled)
6205 {
6206 rc = mMachine->COMSETTER(TeleporterEnabled)(FALSE);
6207 if (FAILED(rc))
6208 throw rc;
6209 }
6210#endif
6211
6212 /* test the FaultToleranceState property */
6213 FaultToleranceState_T enmFaultToleranceState;
6214 rc = mMachine->COMGETTER(FaultToleranceState)(&enmFaultToleranceState);
6215 if (FAILED(rc))
6216 throw rc;
6217 BOOL fFaultToleranceSyncEnabled = (enmFaultToleranceState == FaultToleranceState_Standby);
6218
6219 /* Create a progress object to track progress of this operation. Must
6220 * be done as early as possible (together with BeginPowerUp()) as this
6221 * is vital for communicating as much as possible early powerup
6222 * failure information to the API caller */
6223 pPowerupProgress.createObject();
6224 Bstr progressDesc;
6225 if (mMachineState == MachineState_Saved)
6226 progressDesc = tr("Restoring virtual machine");
6227 else if (fTeleporterEnabled)
6228 progressDesc = tr("Teleporting virtual machine");
6229 else if (fFaultToleranceSyncEnabled)
6230 progressDesc = tr("Fault Tolerance syncing of remote virtual machine");
6231 else
6232 progressDesc = tr("Starting virtual machine");
6233 if ( mMachineState == MachineState_Saved
6234 || (!fTeleporterEnabled && !fFaultToleranceSyncEnabled))
6235 rc = pPowerupProgress->init(static_cast<IConsole *>(this),
6236 progressDesc.raw(),
6237 FALSE /* aCancelable */);
6238 else
6239 if (fTeleporterEnabled)
6240 rc = pPowerupProgress->init(static_cast<IConsole *>(this),
6241 progressDesc.raw(),
6242 TRUE /* aCancelable */,
6243 3 /* cOperations */,
6244 10 /* ulTotalOperationsWeight */,
6245 Bstr(tr("Teleporting virtual machine")).raw(),
6246 1 /* ulFirstOperationWeight */,
6247 NULL);
6248 else
6249 if (fFaultToleranceSyncEnabled)
6250 rc = pPowerupProgress->init(static_cast<IConsole *>(this),
6251 progressDesc.raw(),
6252 TRUE /* aCancelable */,
6253 3 /* cOperations */,
6254 10 /* ulTotalOperationsWeight */,
6255 Bstr(tr("Fault Tolerance syncing of remote virtual machine")).raw(),
6256 1 /* ulFirstOperationWeight */,
6257 NULL);
6258
6259 if (FAILED(rc))
6260 throw rc;
6261
6262 /* Tell VBoxSVC and Machine about the progress object so they can
6263 combine/proxy it to any openRemoteSession caller. */
6264 LogFlowThisFunc(("Calling BeginPowerUp...\n"));
6265 rc = mControl->BeginPowerUp(pPowerupProgress);
6266 if (FAILED(rc))
6267 {
6268 LogFlowThisFunc(("BeginPowerUp failed\n"));
6269 throw rc;
6270 }
6271 fBeganPoweringUp = true;
6272
6273 /** @todo this code prevents starting a VM with unavailable bridged
6274 * networking interface. The only benefit is a slightly better error
6275 * message, which should be moved to the driver code. This is the
6276 * only reason why I left the code in for now. The driver allows
6277 * unavailable bridged networking interfaces in certain circumstances,
6278 * and this is sabotaged by this check. The VM will initially have no
6279 * network connectivity, but the user can fix this at runtime. */
6280#if 0
6281 /* the network cards will undergo a quick consistency check */
6282 for (ULONG slot = 0;
6283 slot < maxNetworkAdapters;
6284 ++slot)
6285 {
6286 ComPtr<INetworkAdapter> pNetworkAdapter;
6287 mMachine->GetNetworkAdapter(slot, pNetworkAdapter.asOutParam());
6288 BOOL enabled = FALSE;
6289 pNetworkAdapter->COMGETTER(Enabled)(&enabled);
6290 if (!enabled)
6291 continue;
6292
6293 NetworkAttachmentType_T netattach;
6294 pNetworkAdapter->COMGETTER(AttachmentType)(&netattach);
6295 switch (netattach)
6296 {
6297 case NetworkAttachmentType_Bridged:
6298 {
6299 /* a valid host interface must have been set */
6300 Bstr hostif;
6301 pNetworkAdapter->COMGETTER(HostInterface)(hostif.asOutParam());
6302 if (hostif.isEmpty())
6303 {
6304 throw setError(VBOX_E_HOST_ERROR,
6305 tr("VM cannot start because host interface networking requires a host interface name to be set"));
6306 }
6307 ComPtr<IVirtualBox> pVirtualBox;
6308 mMachine->COMGETTER(Parent)(pVirtualBox.asOutParam());
6309 ComPtr<IHost> pHost;
6310 pVirtualBox->COMGETTER(Host)(pHost.asOutParam());
6311 ComPtr<IHostNetworkInterface> pHostInterface;
6312 if (!SUCCEEDED(pHost->FindHostNetworkInterfaceByName(hostif.raw(),
6313 pHostInterface.asOutParam())))
6314 {
6315 throw setError(VBOX_E_HOST_ERROR,
6316 tr("VM cannot start because the host interface '%ls' does not exist"),
6317 hostif.raw());
6318 }
6319 break;
6320 }
6321 default:
6322 break;
6323 }
6324 }
6325#endif // 0
6326
6327 /* Read console data stored in the saved state file (if not yet done) */
6328 rc = loadDataFromSavedState();
6329 if (FAILED(rc))
6330 throw rc;
6331
6332 /* Check all types of shared folders and compose a single list */
6333 SharedFolderDataMap sharedFolders;
6334 {
6335 /* first, insert global folders */
6336 for (SharedFolderDataMap::const_iterator it = m_mapGlobalSharedFolders.begin();
6337 it != m_mapGlobalSharedFolders.end();
6338 ++it)
6339 {
6340 const SharedFolderData &d = it->second;
6341 sharedFolders[it->first] = d;
6342 }
6343
6344 /* second, insert machine folders */
6345 for (SharedFolderDataMap::const_iterator it = m_mapMachineSharedFolders.begin();
6346 it != m_mapMachineSharedFolders.end();
6347 ++it)
6348 {
6349 const SharedFolderData &d = it->second;
6350 sharedFolders[it->first] = d;
6351 }
6352
6353 /* third, insert console folders */
6354 for (SharedFolderMap::const_iterator it = m_mapSharedFolders.begin();
6355 it != m_mapSharedFolders.end();
6356 ++it)
6357 {
6358 SharedFolder *pSF = it->second;
6359 AutoCaller sfCaller(pSF);
6360 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
6361 sharedFolders[it->first] = SharedFolderData(pSF->getHostPath(),
6362 pSF->isWritable(),
6363 pSF->isAutoMounted());
6364 }
6365 }
6366
6367 Bstr savedStateFile;
6368
6369 /*
6370 * Saved VMs will have to prove that their saved states seem kosher.
6371 */
6372 if (mMachineState == MachineState_Saved)
6373 {
6374 rc = mMachine->COMGETTER(StateFilePath)(savedStateFile.asOutParam());
6375 if (FAILED(rc))
6376 throw rc;
6377 ComAssertRet(!savedStateFile.isEmpty(), E_FAIL);
6378 int vrc = SSMR3ValidateFile(Utf8Str(savedStateFile).c_str(), false /* fChecksumIt */);
6379 if (RT_FAILURE(vrc))
6380 throw setError(VBOX_E_FILE_ERROR,
6381 tr("VM cannot start because the saved state file '%ls' is invalid (%Rrc). Delete the saved state prior to starting the VM"),
6382 savedStateFile.raw(), vrc);
6383 }
6384
6385 LogFlowThisFunc(("Checking if canceled...\n"));
6386 BOOL fCanceled;
6387 rc = pPowerupProgress->COMGETTER(Canceled)(&fCanceled);
6388 if (FAILED(rc))
6389 throw rc;
6390 if (fCanceled)
6391 {
6392 LogFlowThisFunc(("Canceled in BeginPowerUp\n"));
6393 throw setError(E_FAIL, tr("Powerup was canceled"));
6394 }
6395 LogFlowThisFunc(("Not canceled yet.\n"));
6396
6397 /* setup task object and thread to carry out the operation
6398 * asynchronously */
6399
6400 std::auto_ptr<VMPowerUpTask> task(new VMPowerUpTask(this, pPowerupProgress));
6401 ComAssertComRCRetRC(task->rc());
6402
6403 task->mConfigConstructor = configConstructor;
6404 task->mSharedFolders = sharedFolders;
6405 task->mStartPaused = aPaused;
6406 if (mMachineState == MachineState_Saved)
6407 task->mSavedStateFile = savedStateFile;
6408 task->mTeleporterEnabled = fTeleporterEnabled;
6409 task->mEnmFaultToleranceState = enmFaultToleranceState;
6410
6411 /* Reset differencing hard disks for which autoReset is true,
6412 * but only if the machine has no snapshots OR the current snapshot
6413 * is an OFFLINE snapshot; otherwise we would reset the current
6414 * differencing image of an ONLINE snapshot which contains the disk
6415 * state of the machine while it was previously running, but without
6416 * the corresponding machine state, which is equivalent to powering
6417 * off a running machine and not good idea
6418 */
6419 ComPtr<ISnapshot> pCurrentSnapshot;
6420 rc = mMachine->COMGETTER(CurrentSnapshot)(pCurrentSnapshot.asOutParam());
6421 if (FAILED(rc))
6422 throw rc;
6423
6424 BOOL fCurrentSnapshotIsOnline = false;
6425 if (pCurrentSnapshot)
6426 {
6427 rc = pCurrentSnapshot->COMGETTER(Online)(&fCurrentSnapshotIsOnline);
6428 if (FAILED(rc))
6429 throw rc;
6430 }
6431
6432 if (!fCurrentSnapshotIsOnline)
6433 {
6434 LogFlowThisFunc(("Looking for immutable images to reset\n"));
6435
6436 com::SafeIfaceArray<IMediumAttachment> atts;
6437 rc = mMachine->COMGETTER(MediumAttachments)(ComSafeArrayAsOutParam(atts));
6438 if (FAILED(rc))
6439 throw rc;
6440
6441 for (size_t i = 0;
6442 i < atts.size();
6443 ++i)
6444 {
6445 DeviceType_T devType;
6446 rc = atts[i]->COMGETTER(Type)(&devType);
6447 /** @todo later applies to floppies as well */
6448 if (devType == DeviceType_HardDisk)
6449 {
6450 ComPtr<IMedium> pMedium;
6451 rc = atts[i]->COMGETTER(Medium)(pMedium.asOutParam());
6452 if (FAILED(rc))
6453 throw rc;
6454
6455 /* needs autoreset? */
6456 BOOL autoReset = FALSE;
6457 rc = pMedium->COMGETTER(AutoReset)(&autoReset);
6458 if (FAILED(rc))
6459 throw rc;
6460
6461 if (autoReset)
6462 {
6463 ComPtr<IProgress> pResetProgress;
6464 rc = pMedium->Reset(pResetProgress.asOutParam());
6465 if (FAILED(rc))
6466 throw rc;
6467
6468 /* save for later use on the powerup thread */
6469 task->hardDiskProgresses.push_back(pResetProgress);
6470 }
6471 }
6472 }
6473 }
6474 else
6475 LogFlowThisFunc(("Machine has a current snapshot which is online, skipping immutable images reset\n"));
6476
6477 rc = consoleInitReleaseLog(mMachine);
6478 if (FAILED(rc))
6479 throw rc;
6480#ifdef VBOX_WITH_EXTPACK
6481 mptrExtPackManager->dumpAllToReleaseLog();
6482#endif
6483
6484#ifdef RT_OS_SOLARIS
6485 /* setup host core dumper for the VM */
6486 Bstr value;
6487 HRESULT hrc = mMachine->GetExtraData(Bstr("VBoxInternal2/CoreDumpEnabled").raw(), value.asOutParam());
6488 if (SUCCEEDED(hrc) && value == "1")
6489 {
6490 Bstr coreDumpDir, coreDumpReplaceSys, coreDumpLive;
6491 mMachine->GetExtraData(Bstr("VBoxInternal2/CoreDumpDir").raw(), coreDumpDir.asOutParam());
6492 mMachine->GetExtraData(Bstr("VBoxInternal2/CoreDumpReplaceSystemDump").raw(), coreDumpReplaceSys.asOutParam());
6493 mMachine->GetExtraData(Bstr("VBoxInternal2/CoreDumpLive").raw(), coreDumpLive.asOutParam());
6494
6495 uint32_t fCoreFlags = 0;
6496 if ( coreDumpReplaceSys.isEmpty() == false
6497 && Utf8Str(coreDumpReplaceSys).toUInt32() == 1)
6498 {
6499 fCoreFlags |= RTCOREDUMPER_FLAGS_REPLACE_SYSTEM_DUMP;
6500 }
6501
6502 if ( coreDumpLive.isEmpty() == false
6503 && Utf8Str(coreDumpLive).toUInt32() == 1)
6504 {
6505 fCoreFlags |= RTCOREDUMPER_FLAGS_LIVE_CORE;
6506 }
6507
6508 Utf8Str strDumpDir(coreDumpDir);
6509 const char *pszDumpDir = strDumpDir.c_str();
6510 if ( pszDumpDir
6511 && *pszDumpDir == '\0')
6512 pszDumpDir = NULL;
6513
6514 int vrc;
6515 if ( pszDumpDir
6516 && !RTDirExists(pszDumpDir))
6517 {
6518 /*
6519 * Try create the directory.
6520 */
6521 vrc = RTDirCreateFullPath(pszDumpDir, 0700);
6522 if (RT_FAILURE(vrc))
6523 throw setError(E_FAIL, "Failed to setup CoreDumper. Couldn't create dump directory '%s' (%Rrc)\n", pszDumpDir, vrc);
6524 }
6525
6526 vrc = RTCoreDumperSetup(pszDumpDir, fCoreFlags);
6527 if (RT_FAILURE(vrc))
6528 throw setError(E_FAIL, "Failed to setup CoreDumper (%Rrc)", vrc);
6529 else
6530 LogRel(("CoreDumper setup successful. pszDumpDir=%s fFlags=%#x\n", pszDumpDir ? pszDumpDir : ".", fCoreFlags));
6531 }
6532#endif
6533
6534 /* pass the progress object to the caller if requested */
6535 if (aProgress)
6536 {
6537 if (task->hardDiskProgresses.size() == 0)
6538 {
6539 /* there are no other operations to track, return the powerup
6540 * progress only */
6541 pPowerupProgress.queryInterfaceTo(aProgress);
6542 }
6543 else
6544 {
6545 /* create a combined progress object */
6546 ComObjPtr<CombinedProgress> pProgress;
6547 pProgress.createObject();
6548 VMPowerUpTask::ProgressList progresses(task->hardDiskProgresses);
6549 progresses.push_back(ComPtr<IProgress> (pPowerupProgress));
6550 rc = pProgress->init(static_cast<IConsole *>(this),
6551 progressDesc.raw(), progresses.begin(),
6552 progresses.end());
6553 AssertComRCReturnRC(rc);
6554 pProgress.queryInterfaceTo(aProgress);
6555 }
6556 }
6557
6558 int vrc = RTThreadCreate(NULL, Console::powerUpThread,
6559 (void *)task.get(), 0,
6560 RTTHREADTYPE_MAIN_WORKER, 0, "VMPwrUp");
6561 if (RT_FAILURE(vrc))
6562 throw setError(E_FAIL, "Could not create VMPowerUp thread (%Rrc)", vrc);
6563
6564 /* task is now owned by powerUpThread(), so release it */
6565 task.release();
6566
6567 /* finally, set the state: no right to fail in this method afterwards
6568 * since we've already started the thread and it is now responsible for
6569 * any error reporting and appropriate state change! */
6570 if (mMachineState == MachineState_Saved)
6571 setMachineState(MachineState_Restoring);
6572 else if (fTeleporterEnabled)
6573 setMachineState(MachineState_TeleportingIn);
6574 else if (enmFaultToleranceState == FaultToleranceState_Standby)
6575 setMachineState(MachineState_FaultTolerantSyncing);
6576 else
6577 setMachineState(MachineState_Starting);
6578 }
6579 catch (HRESULT aRC) { rc = aRC; }
6580
6581 if (FAILED(rc) && fBeganPoweringUp)
6582 {
6583
6584 /* The progress object will fetch the current error info */
6585 if (!pPowerupProgress.isNull())
6586 pPowerupProgress->notifyComplete(rc);
6587
6588 /* Save the error info across the IPC below. Can't be done before the
6589 * progress notification above, as saving the error info deletes it
6590 * from the current context, and thus the progress object wouldn't be
6591 * updated correctly. */
6592 ErrorInfoKeeper eik;
6593
6594 /* signal end of operation */
6595 mControl->EndPowerUp(rc);
6596 }
6597
6598 LogFlowThisFunc(("mMachineState=%d, rc=%Rhrc\n", mMachineState, rc));
6599 LogFlowThisFuncLeave();
6600 return rc;
6601}
6602
6603/**
6604 * Internal power off worker routine.
6605 *
6606 * This method may be called only at certain places with the following meaning
6607 * as shown below:
6608 *
6609 * - if the machine state is either Running or Paused, a normal
6610 * Console-initiated powerdown takes place (e.g. PowerDown());
6611 * - if the machine state is Saving, saveStateThread() has successfully done its
6612 * job;
6613 * - if the machine state is Starting or Restoring, powerUpThread() has failed
6614 * to start/load the VM;
6615 * - if the machine state is Stopping, the VM has powered itself off (i.e. not
6616 * as a result of the powerDown() call).
6617 *
6618 * Calling it in situations other than the above will cause unexpected behavior.
6619 *
6620 * Note that this method should be the only one that destroys mpVM and sets it
6621 * to NULL.
6622 *
6623 * @param aProgress Progress object to run (may be NULL).
6624 *
6625 * @note Locks this object for writing.
6626 *
6627 * @note Never call this method from a thread that called addVMCaller() or
6628 * instantiated an AutoVMCaller object; first call releaseVMCaller() or
6629 * release(). Otherwise it will deadlock.
6630 */
6631HRESULT Console::powerDown(IProgress *aProgress /*= NULL*/)
6632{
6633 LogFlowThisFuncEnter();
6634
6635 AutoCaller autoCaller(this);
6636 AssertComRCReturnRC(autoCaller.rc());
6637
6638 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6639
6640 /* Total # of steps for the progress object. Must correspond to the
6641 * number of "advance percent count" comments in this method! */
6642 enum { StepCount = 7 };
6643 /* current step */
6644 ULONG step = 0;
6645
6646 HRESULT rc = S_OK;
6647 int vrc = VINF_SUCCESS;
6648
6649 /* sanity */
6650 Assert(mVMDestroying == false);
6651
6652 PUVM pUVM = mpUVM; Assert(pUVM != NULL);
6653 uint32_t cRefs = VMR3RetainUVM(pUVM); Assert(cRefs != UINT32_MAX);
6654
6655 AssertMsg( mMachineState == MachineState_Running
6656 || mMachineState == MachineState_Paused
6657 || mMachineState == MachineState_Stuck
6658 || mMachineState == MachineState_Starting
6659 || mMachineState == MachineState_Stopping
6660 || mMachineState == MachineState_Saving
6661 || mMachineState == MachineState_Restoring
6662 || mMachineState == MachineState_TeleportingPausedVM
6663 || mMachineState == MachineState_FaultTolerantSyncing
6664 || mMachineState == MachineState_TeleportingIn
6665 , ("Invalid machine state: %s\n", Global::stringifyMachineState(mMachineState)));
6666
6667 LogRel(("Console::powerDown(): A request to power off the VM has been issued (mMachineState=%s, InUninit=%d)\n",
6668 Global::stringifyMachineState(mMachineState), autoCaller.state() == InUninit));
6669
6670 /* Check if we need to power off the VM. In case of mVMPoweredOff=true, the
6671 * VM has already powered itself off in vmstateChangeCallback() and is just
6672 * notifying Console about that. In case of Starting or Restoring,
6673 * powerUpThread() is calling us on failure, so the VM is already off at
6674 * that point. */
6675 if ( !mVMPoweredOff
6676 && ( mMachineState == MachineState_Starting
6677 || mMachineState == MachineState_Restoring
6678 || mMachineState == MachineState_FaultTolerantSyncing
6679 || mMachineState == MachineState_TeleportingIn)
6680 )
6681 mVMPoweredOff = true;
6682
6683 /*
6684 * Go to Stopping state if not already there.
6685 *
6686 * Note that we don't go from Saving/Restoring to Stopping because
6687 * vmstateChangeCallback() needs it to set the state to Saved on
6688 * VMSTATE_TERMINATED. In terms of protecting from inappropriate operations
6689 * while leaving the lock below, Saving or Restoring should be fine too.
6690 * Ditto for TeleportingPausedVM -> Teleported.
6691 */
6692 if ( mMachineState != MachineState_Saving
6693 && mMachineState != MachineState_Restoring
6694 && mMachineState != MachineState_Stopping
6695 && mMachineState != MachineState_TeleportingIn
6696 && mMachineState != MachineState_TeleportingPausedVM
6697 && mMachineState != MachineState_FaultTolerantSyncing
6698 )
6699 setMachineState(MachineState_Stopping);
6700
6701 /* ----------------------------------------------------------------------
6702 * DONE with necessary state changes, perform the power down actions (it's
6703 * safe to release the object lock now if needed)
6704 * ---------------------------------------------------------------------- */
6705
6706 /* Stop the VRDP server to prevent new clients connection while VM is being
6707 * powered off. */
6708 if (mConsoleVRDPServer)
6709 {
6710 LogFlowThisFunc(("Stopping VRDP server...\n"));
6711
6712 /* Leave the lock since EMT will call us back as addVMCaller()
6713 * in updateDisplayData(). */
6714 alock.release();
6715
6716 mConsoleVRDPServer->Stop();
6717
6718 alock.acquire();
6719 }
6720
6721 /* advance percent count */
6722 if (aProgress)
6723 aProgress->SetCurrentOperationProgress(99 * (++step) / StepCount );
6724
6725
6726 /* ----------------------------------------------------------------------
6727 * Now, wait for all mpVM callers to finish their work if there are still
6728 * some on other threads. NO methods that need mpVM (or initiate other calls
6729 * that need it) may be called after this point
6730 * ---------------------------------------------------------------------- */
6731
6732 /* go to the destroying state to prevent from adding new callers */
6733 mVMDestroying = true;
6734
6735 if (mVMCallers > 0)
6736 {
6737 /* lazy creation */
6738 if (mVMZeroCallersSem == NIL_RTSEMEVENT)
6739 RTSemEventCreate(&mVMZeroCallersSem);
6740
6741 LogFlowThisFunc(("Waiting for mpVM callers (%d) to drop to zero...\n",
6742 mVMCallers));
6743
6744 alock.release();
6745
6746 RTSemEventWait(mVMZeroCallersSem, RT_INDEFINITE_WAIT);
6747
6748 alock.acquire();
6749 }
6750
6751 /* advance percent count */
6752 if (aProgress)
6753 aProgress->SetCurrentOperationProgress(99 * (++step) / StepCount );
6754
6755 vrc = VINF_SUCCESS;
6756
6757 /*
6758 * Power off the VM if not already done that.
6759 * Leave the lock since EMT will call vmstateChangeCallback.
6760 *
6761 * Note that VMR3PowerOff() may fail here (invalid VMSTATE) if the
6762 * VM-(guest-)initiated power off happened in parallel a ms before this
6763 * call. So far, we let this error pop up on the user's side.
6764 */
6765 if (!mVMPoweredOff)
6766 {
6767 LogFlowThisFunc(("Powering off the VM...\n"));
6768 alock.release();
6769 vrc = VMR3PowerOff(VMR3GetVM(pUVM));
6770#ifdef VBOX_WITH_EXTPACK
6771 mptrExtPackManager->callAllVmPowerOffHooks(this, VMR3GetVM(pUVM));
6772#endif
6773 alock.acquire();
6774 }
6775
6776 /* advance percent count */
6777 if (aProgress)
6778 aProgress->SetCurrentOperationProgress(99 * (++step) / StepCount );
6779
6780#ifdef VBOX_WITH_HGCM
6781 /* Shutdown HGCM services before destroying the VM. */
6782 if (m_pVMMDev)
6783 {
6784 LogFlowThisFunc(("Shutdown HGCM...\n"));
6785
6786 /* Leave the lock since EMT will call us back as addVMCaller() */
6787 alock.release();
6788
6789 m_pVMMDev->hgcmShutdown();
6790
6791 alock.acquire();
6792 }
6793
6794 /* advance percent count */
6795 if (aProgress)
6796 aProgress->SetCurrentOperationProgress(99 * (++step) / StepCount);
6797
6798#endif /* VBOX_WITH_HGCM */
6799
6800 LogFlowThisFunc(("Ready for VM destruction.\n"));
6801
6802 /* If we are called from Console::uninit(), then try to destroy the VM even
6803 * on failure (this will most likely fail too, but what to do?..) */
6804 if (RT_SUCCESS(vrc) || autoCaller.state() == InUninit)
6805 {
6806 /* If the machine has an USB controller, release all USB devices
6807 * (symmetric to the code in captureUSBDevices()) */
6808 bool fHasUSBController = false;
6809 {
6810 PPDMIBASE pBase;
6811 vrc = PDMR3QueryLun(VMR3GetVM(pUVM), "usb-ohci", 0, 0, &pBase);
6812 if (RT_SUCCESS(vrc))
6813 {
6814 fHasUSBController = true;
6815 detachAllUSBDevices(false /* aDone */);
6816 }
6817 }
6818
6819 /* Now we've got to destroy the VM as well. (mpVM is not valid beyond
6820 * this point). We release the lock before calling VMR3Destroy() because
6821 * it will result into calling destructors of drivers associated with
6822 * Console children which may in turn try to lock Console (e.g. by
6823 * instantiating SafeVMPtr to access mpVM). It's safe here because
6824 * mVMDestroying is set which should prevent any activity. */
6825
6826 /* Set mpUVM to NULL early just in case if some old code is not using
6827 * addVMCaller()/releaseVMCaller(). (We have our own ref on pUVM.) */
6828 VMR3ReleaseUVM(mpUVM);
6829 mpUVM = NULL;
6830
6831 LogFlowThisFunc(("Destroying the VM...\n"));
6832
6833 alock.release();
6834
6835 vrc = VMR3Destroy(VMR3GetVM(pUVM));
6836
6837 /* take the lock again */
6838 alock.acquire();
6839
6840 /* advance percent count */
6841 if (aProgress)
6842 aProgress->SetCurrentOperationProgress(99 * (++step) / StepCount);
6843
6844 if (RT_SUCCESS(vrc))
6845 {
6846 LogFlowThisFunc(("Machine has been destroyed (mMachineState=%d)\n",
6847 mMachineState));
6848 /* Note: the Console-level machine state change happens on the
6849 * VMSTATE_TERMINATE state change in vmstateChangeCallback(). If
6850 * powerDown() is called from EMT (i.e. from vmstateChangeCallback()
6851 * on receiving VM-initiated VMSTATE_OFF), VMSTATE_TERMINATE hasn't
6852 * occurred yet. This is okay, because mMachineState is already
6853 * Stopping in this case, so any other attempt to call PowerDown()
6854 * will be rejected. */
6855 }
6856 else
6857 {
6858 /* bad bad bad, but what to do? (Give Console our UVM ref.) */
6859 mpUVM = pUVM;
6860 pUVM = NULL;
6861 rc = setError(VBOX_E_VM_ERROR,
6862 tr("Could not destroy the machine. (Error: %Rrc)"),
6863 vrc);
6864 }
6865
6866 /* Complete the detaching of the USB devices. */
6867 if (fHasUSBController)
6868 detachAllUSBDevices(true /* aDone */);
6869
6870 /* advance percent count */
6871 if (aProgress)
6872 aProgress->SetCurrentOperationProgress(99 * (++step) / StepCount);
6873 }
6874 else
6875 {
6876 rc = setError(VBOX_E_VM_ERROR,
6877 tr("Could not power off the machine. (Error: %Rrc)"),
6878 vrc);
6879 }
6880
6881 /*
6882 * Finished with the destruction.
6883 *
6884 * Note that if something impossible happened and we've failed to destroy
6885 * the VM, mVMDestroying will remain true and mMachineState will be
6886 * something like Stopping, so most Console methods will return an error
6887 * to the caller.
6888 */
6889 if (mpUVM != NULL)
6890 VMR3ReleaseUVM(pUVM);
6891 else
6892 mVMDestroying = false;
6893
6894#ifdef CONSOLE_WITH_EVENT_CACHE
6895 if (SUCCEEDED(rc))
6896 mCallbackData.clear();
6897#endif
6898
6899 LogFlowThisFuncLeave();
6900 return rc;
6901}
6902
6903/**
6904 * @note Locks this object for writing.
6905 */
6906HRESULT Console::setMachineState(MachineState_T aMachineState,
6907 bool aUpdateServer /* = true */)
6908{
6909 AutoCaller autoCaller(this);
6910 AssertComRCReturnRC(autoCaller.rc());
6911
6912 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6913
6914 HRESULT rc = S_OK;
6915
6916 if (mMachineState != aMachineState)
6917 {
6918 LogThisFunc(("machineState=%s -> %s aUpdateServer=%RTbool\n",
6919 Global::stringifyMachineState(mMachineState), Global::stringifyMachineState(aMachineState), aUpdateServer));
6920 mMachineState = aMachineState;
6921
6922 /// @todo (dmik)
6923 // possibly, we need to redo onStateChange() using the dedicated
6924 // Event thread, like it is done in VirtualBox. This will make it
6925 // much safer (no deadlocks possible if someone tries to use the
6926 // console from the callback), however, listeners will lose the
6927 // ability to synchronously react to state changes (is it really
6928 // necessary??)
6929 LogFlowThisFunc(("Doing onStateChange()...\n"));
6930 onStateChange(aMachineState);
6931 LogFlowThisFunc(("Done onStateChange()\n"));
6932
6933 if (aUpdateServer)
6934 {
6935 /* Server notification MUST be done from under the lock; otherwise
6936 * the machine state here and on the server might go out of sync
6937 * which can lead to various unexpected results (like the machine
6938 * state being >= MachineState_Running on the server, while the
6939 * session state is already SessionState_Unlocked at the same time
6940 * there).
6941 *
6942 * Cross-lock conditions should be carefully watched out: calling
6943 * UpdateState we will require Machine and SessionMachine locks
6944 * (remember that here we're holding the Console lock here, and also
6945 * all locks that have been acquire by the thread before calling
6946 * this method).
6947 */
6948 LogFlowThisFunc(("Doing mControl->UpdateState()...\n"));
6949 rc = mControl->UpdateState(aMachineState);
6950 LogFlowThisFunc(("mControl->UpdateState()=%Rhrc\n", rc));
6951 }
6952 }
6953
6954 return rc;
6955}
6956
6957/**
6958 * Searches for a shared folder with the given logical name
6959 * in the collection of shared folders.
6960 *
6961 * @param aName logical name of the shared folder
6962 * @param aSharedFolder where to return the found object
6963 * @param aSetError whether to set the error info if the folder is
6964 * not found
6965 * @return
6966 * S_OK when found or E_INVALIDARG when not found
6967 *
6968 * @note The caller must lock this object for writing.
6969 */
6970HRESULT Console::findSharedFolder(const Utf8Str &strName,
6971 ComObjPtr<SharedFolder> &aSharedFolder,
6972 bool aSetError /* = false */)
6973{
6974 /* sanity check */
6975 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
6976
6977 SharedFolderMap::const_iterator it = m_mapSharedFolders.find(strName);
6978 if (it != m_mapSharedFolders.end())
6979 {
6980 aSharedFolder = it->second;
6981 return S_OK;
6982 }
6983
6984 if (aSetError)
6985 setError(VBOX_E_FILE_ERROR,
6986 tr("Could not find a shared folder named '%s'."),
6987 strName.c_str());
6988
6989 return VBOX_E_FILE_ERROR;
6990}
6991
6992/**
6993 * Fetches the list of global or machine shared folders from the server.
6994 *
6995 * @param aGlobal true to fetch global folders.
6996 *
6997 * @note The caller must lock this object for writing.
6998 */
6999HRESULT Console::fetchSharedFolders(BOOL aGlobal)
7000{
7001 /* sanity check */
7002 AssertReturn(AutoCaller(this).state() == InInit ||
7003 isWriteLockOnCurrentThread(), E_FAIL);
7004
7005 LogFlowThisFunc(("Entering\n"));
7006
7007 /* Check if we're online and keep it that way. */
7008 SafeVMPtrQuiet ptrVM(this);
7009 AutoVMCallerQuietWeak autoVMCaller(this);
7010 bool const online = ptrVM.isOk()
7011 && m_pVMMDev
7012 && m_pVMMDev->isShFlActive();
7013
7014 HRESULT rc = S_OK;
7015
7016 try
7017 {
7018 if (aGlobal)
7019 {
7020 /// @todo grab & process global folders when they are done
7021 }
7022 else
7023 {
7024 SharedFolderDataMap oldFolders;
7025 if (online)
7026 oldFolders = m_mapMachineSharedFolders;
7027
7028 m_mapMachineSharedFolders.clear();
7029
7030 SafeIfaceArray<ISharedFolder> folders;
7031 rc = mMachine->COMGETTER(SharedFolders)(ComSafeArrayAsOutParam(folders));
7032 if (FAILED(rc)) throw rc;
7033
7034 for (size_t i = 0; i < folders.size(); ++i)
7035 {
7036 ComPtr<ISharedFolder> pSharedFolder = folders[i];
7037
7038 Bstr bstrName;
7039 Bstr bstrHostPath;
7040 BOOL writable;
7041 BOOL autoMount;
7042
7043 rc = pSharedFolder->COMGETTER(Name)(bstrName.asOutParam());
7044 if (FAILED(rc)) throw rc;
7045 Utf8Str strName(bstrName);
7046
7047 rc = pSharedFolder->COMGETTER(HostPath)(bstrHostPath.asOutParam());
7048 if (FAILED(rc)) throw rc;
7049 Utf8Str strHostPath(bstrHostPath);
7050
7051 rc = pSharedFolder->COMGETTER(Writable)(&writable);
7052 if (FAILED(rc)) throw rc;
7053
7054 rc = pSharedFolder->COMGETTER(AutoMount)(&autoMount);
7055 if (FAILED(rc)) throw rc;
7056
7057 m_mapMachineSharedFolders.insert(std::make_pair(strName,
7058 SharedFolderData(strHostPath, writable, autoMount)));
7059
7060 /* send changes to HGCM if the VM is running */
7061 if (online)
7062 {
7063 SharedFolderDataMap::iterator it = oldFolders.find(strName);
7064 if ( it == oldFolders.end()
7065 || it->second.m_strHostPath != strHostPath)
7066 {
7067 /* a new machine folder is added or
7068 * the existing machine folder is changed */
7069 if (m_mapSharedFolders.find(strName) != m_mapSharedFolders.end())
7070 ; /* the console folder exists, nothing to do */
7071 else
7072 {
7073 /* remove the old machine folder (when changed)
7074 * or the global folder if any (when new) */
7075 if ( it != oldFolders.end()
7076 || m_mapGlobalSharedFolders.find(strName) != m_mapGlobalSharedFolders.end()
7077 )
7078 {
7079 rc = removeSharedFolder(strName);
7080 if (FAILED(rc)) throw rc;
7081 }
7082
7083 /* create the new machine folder */
7084 rc = createSharedFolder(strName,
7085 SharedFolderData(strHostPath,
7086 writable,
7087 autoMount));
7088 if (FAILED(rc)) throw rc;
7089 }
7090 }
7091 /* forget the processed (or identical) folder */
7092 if (it != oldFolders.end())
7093 oldFolders.erase(it);
7094 }
7095 }
7096
7097 /* process outdated (removed) folders */
7098 if (online)
7099 {
7100 for (SharedFolderDataMap::const_iterator it = oldFolders.begin();
7101 it != oldFolders.end(); ++it)
7102 {
7103 if (m_mapSharedFolders.find(it->first) != m_mapSharedFolders.end())
7104 ; /* the console folder exists, nothing to do */
7105 else
7106 {
7107 /* remove the outdated machine folder */
7108 rc = removeSharedFolder(it->first);
7109 if (FAILED(rc)) throw rc;
7110
7111 /* create the global folder if there is any */
7112 SharedFolderDataMap::const_iterator git =
7113 m_mapGlobalSharedFolders.find(it->first);
7114 if (git != m_mapGlobalSharedFolders.end())
7115 {
7116 rc = createSharedFolder(git->first, git->second);
7117 if (FAILED(rc)) throw rc;
7118 }
7119 }
7120 }
7121 }
7122 }
7123 }
7124 catch (HRESULT rc2)
7125 {
7126 if (online)
7127 setVMRuntimeErrorCallbackF(ptrVM, this, 0, "BrokenSharedFolder",
7128 N_("Broken shared folder!"));
7129 }
7130
7131 LogFlowThisFunc(("Leaving\n"));
7132
7133 return rc;
7134}
7135
7136/**
7137 * Searches for a shared folder with the given name in the list of machine
7138 * shared folders and then in the list of the global shared folders.
7139 *
7140 * @param aName Name of the folder to search for.
7141 * @param aIt Where to store the pointer to the found folder.
7142 * @return @c true if the folder was found and @c false otherwise.
7143 *
7144 * @note The caller must lock this object for reading.
7145 */
7146bool Console::findOtherSharedFolder(const Utf8Str &strName,
7147 SharedFolderDataMap::const_iterator &aIt)
7148{
7149 /* sanity check */
7150 AssertReturn(isWriteLockOnCurrentThread(), false);
7151
7152 /* first, search machine folders */
7153 aIt = m_mapMachineSharedFolders.find(strName);
7154 if (aIt != m_mapMachineSharedFolders.end())
7155 return true;
7156
7157 /* second, search machine folders */
7158 aIt = m_mapGlobalSharedFolders.find(strName);
7159 if (aIt != m_mapGlobalSharedFolders.end())
7160 return true;
7161
7162 return false;
7163}
7164
7165/**
7166 * Calls the HGCM service to add a shared folder definition.
7167 *
7168 * @param aName Shared folder name.
7169 * @param aHostPath Shared folder path.
7170 *
7171 * @note Must be called from under AutoVMCaller and when mpVM != NULL!
7172 * @note Doesn't lock anything.
7173 */
7174HRESULT Console::createSharedFolder(const Utf8Str &strName, const SharedFolderData &aData)
7175{
7176 ComAssertRet(strName.isNotEmpty(), E_FAIL);
7177 ComAssertRet(aData.m_strHostPath.isNotEmpty(), E_FAIL);
7178
7179 /* sanity checks */
7180 AssertReturn(mpUVM, E_FAIL);
7181 AssertReturn(m_pVMMDev && m_pVMMDev->isShFlActive(), E_FAIL);
7182
7183 VBOXHGCMSVCPARM parms[SHFL_CPARMS_ADD_MAPPING];
7184 SHFLSTRING *pFolderName, *pMapName;
7185 size_t cbString;
7186
7187 Bstr value;
7188 HRESULT hrc = mMachine->GetExtraData(BstrFmt("VBoxInternal2/SharedFoldersEnableSymlinksCreate/%s",
7189 strName.c_str()).raw(),
7190 value.asOutParam());
7191 bool fSymlinksCreate = hrc == S_OK && value == "1";
7192
7193 Log(("Adding shared folder '%s' -> '%s'\n", strName.c_str(), aData.m_strHostPath.c_str()));
7194
7195 // check whether the path is valid and exists
7196 char hostPathFull[RTPATH_MAX];
7197 int vrc = RTPathAbsEx(NULL,
7198 aData.m_strHostPath.c_str(),
7199 hostPathFull,
7200 sizeof(hostPathFull));
7201 if (RT_FAILURE(vrc))
7202 return setError(E_INVALIDARG,
7203 tr("Invalid shared folder path: '%s' (%Rrc)"),
7204 aData.m_strHostPath.c_str(), vrc);
7205 if (!RTPathExists(hostPathFull))
7206 return setError(E_INVALIDARG,
7207 tr("Shared folder path '%s' does not exist on the host"),
7208 aData.m_strHostPath.c_str());
7209 /* Check whether the path is full (absolute) */
7210 if (RTPathCompare(aData.m_strHostPath.c_str(), hostPathFull) != 0)
7211 return setError(E_INVALIDARG,
7212 tr("Shared folder path '%s' is not absolute"),
7213 aData.m_strHostPath.c_str());
7214
7215 // now that we know the path is good, give it to HGCM
7216
7217 Bstr bstrName(strName);
7218 Bstr bstrHostPath(aData.m_strHostPath);
7219
7220 cbString = (bstrHostPath.length() + 1) * sizeof(RTUTF16);
7221 if (cbString >= UINT16_MAX)
7222 return setError(E_INVALIDARG, tr("The name is too long"));
7223 pFolderName = (SHFLSTRING*)RTMemAllocZ(sizeof(SHFLSTRING) + cbString);
7224 Assert(pFolderName);
7225 memcpy(pFolderName->String.ucs2, bstrHostPath.raw(), cbString);
7226
7227 pFolderName->u16Size = (uint16_t)cbString;
7228 pFolderName->u16Length = (uint16_t)cbString - sizeof(RTUTF16);
7229
7230 parms[0].type = VBOX_HGCM_SVC_PARM_PTR;
7231 parms[0].u.pointer.addr = pFolderName;
7232 parms[0].u.pointer.size = sizeof(SHFLSTRING) + (uint16_t)cbString;
7233
7234 cbString = (bstrName.length() + 1) * sizeof(RTUTF16);
7235 if (cbString >= UINT16_MAX)
7236 {
7237 RTMemFree(pFolderName);
7238 return setError(E_INVALIDARG, tr("The host path is too long"));
7239 }
7240 pMapName = (SHFLSTRING*)RTMemAllocZ(sizeof(SHFLSTRING) + cbString);
7241 Assert(pMapName);
7242 memcpy(pMapName->String.ucs2, bstrName.raw(), cbString);
7243
7244 pMapName->u16Size = (uint16_t)cbString;
7245 pMapName->u16Length = (uint16_t)cbString - sizeof(RTUTF16);
7246
7247 parms[1].type = VBOX_HGCM_SVC_PARM_PTR;
7248 parms[1].u.pointer.addr = pMapName;
7249 parms[1].u.pointer.size = sizeof(SHFLSTRING) + (uint16_t)cbString;
7250
7251 parms[2].type = VBOX_HGCM_SVC_PARM_32BIT;
7252 parms[2].u.uint32 = (aData.m_fWritable ? SHFL_ADD_MAPPING_F_WRITABLE : 0)
7253 | (aData.m_fAutoMount ? SHFL_ADD_MAPPING_F_AUTOMOUNT : 0)
7254 | (fSymlinksCreate ? SHFL_ADD_MAPPING_F_CREATE_SYMLINKS : 0);
7255
7256 vrc = m_pVMMDev->hgcmHostCall("VBoxSharedFolders",
7257 SHFL_FN_ADD_MAPPING,
7258 SHFL_CPARMS_ADD_MAPPING, &parms[0]);
7259 RTMemFree(pFolderName);
7260 RTMemFree(pMapName);
7261
7262 if (RT_FAILURE(vrc))
7263 return setError(E_FAIL,
7264 tr("Could not create a shared folder '%s' mapped to '%s' (%Rrc)"),
7265 strName.c_str(), aData.m_strHostPath.c_str(), vrc);
7266
7267 return S_OK;
7268}
7269
7270/**
7271 * Calls the HGCM service to remove the shared folder definition.
7272 *
7273 * @param aName Shared folder name.
7274 *
7275 * @note Must be called from under AutoVMCaller and when mpVM != NULL!
7276 * @note Doesn't lock anything.
7277 */
7278HRESULT Console::removeSharedFolder(const Utf8Str &strName)
7279{
7280 ComAssertRet(strName.isNotEmpty(), E_FAIL);
7281
7282 /* sanity checks */
7283 AssertReturn(mpUVM, E_FAIL);
7284 AssertReturn(m_pVMMDev && m_pVMMDev->isShFlActive(), E_FAIL);
7285
7286 VBOXHGCMSVCPARM parms;
7287 SHFLSTRING *pMapName;
7288 size_t cbString;
7289
7290 Log(("Removing shared folder '%s'\n", strName.c_str()));
7291
7292 Bstr bstrName(strName);
7293 cbString = (bstrName.length() + 1) * sizeof(RTUTF16);
7294 if (cbString >= UINT16_MAX)
7295 return setError(E_INVALIDARG, tr("The name is too long"));
7296 pMapName = (SHFLSTRING *) RTMemAllocZ(sizeof(SHFLSTRING) + cbString);
7297 Assert(pMapName);
7298 memcpy(pMapName->String.ucs2, bstrName.raw(), cbString);
7299
7300 pMapName->u16Size = (uint16_t)cbString;
7301 pMapName->u16Length = (uint16_t)cbString - sizeof(RTUTF16);
7302
7303 parms.type = VBOX_HGCM_SVC_PARM_PTR;
7304 parms.u.pointer.addr = pMapName;
7305 parms.u.pointer.size = sizeof(SHFLSTRING) + (uint16_t)cbString;
7306
7307 int vrc = m_pVMMDev->hgcmHostCall("VBoxSharedFolders",
7308 SHFL_FN_REMOVE_MAPPING,
7309 1, &parms);
7310 RTMemFree(pMapName);
7311 if (RT_FAILURE(vrc))
7312 return setError(E_FAIL,
7313 tr("Could not remove the shared folder '%s' (%Rrc)"),
7314 strName.c_str(), vrc);
7315
7316 return S_OK;
7317}
7318
7319/**
7320 * VM state callback function. Called by the VMM
7321 * using its state machine states.
7322 *
7323 * Primarily used to handle VM initiated power off, suspend and state saving,
7324 * but also for doing termination completed work (VMSTATE_TERMINATE).
7325 *
7326 * In general this function is called in the context of the EMT.
7327 *
7328 * @param aVM The VM handle.
7329 * @param aState The new state.
7330 * @param aOldState The old state.
7331 * @param aUser The user argument (pointer to the Console object).
7332 *
7333 * @note Locks the Console object for writing.
7334 */
7335DECLCALLBACK(void) Console::vmstateChangeCallback(PVM aVM,
7336 VMSTATE aState,
7337 VMSTATE aOldState,
7338 void *aUser)
7339{
7340 LogFlowFunc(("Changing state from %s to %s (aVM=%p)\n",
7341 VMR3GetStateName(aOldState), VMR3GetStateName(aState), aVM));
7342
7343 Console *that = static_cast<Console *>(aUser);
7344 AssertReturnVoid(that);
7345
7346 AutoCaller autoCaller(that);
7347
7348 /* Note that we must let this method proceed even if Console::uninit() has
7349 * been already called. In such case this VMSTATE change is a result of:
7350 * 1) powerDown() called from uninit() itself, or
7351 * 2) VM-(guest-)initiated power off. */
7352 AssertReturnVoid( autoCaller.isOk()
7353 || autoCaller.state() == InUninit);
7354
7355 switch (aState)
7356 {
7357 /*
7358 * The VM has terminated
7359 */
7360 case VMSTATE_OFF:
7361 {
7362 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
7363
7364 if (that->mVMStateChangeCallbackDisabled)
7365 break;
7366
7367 /* Do we still think that it is running? It may happen if this is a
7368 * VM-(guest-)initiated shutdown/poweroff.
7369 */
7370 if ( that->mMachineState != MachineState_Stopping
7371 && that->mMachineState != MachineState_Saving
7372 && that->mMachineState != MachineState_Restoring
7373 && that->mMachineState != MachineState_TeleportingIn
7374 && that->mMachineState != MachineState_FaultTolerantSyncing
7375 && that->mMachineState != MachineState_TeleportingPausedVM
7376 && !that->mVMIsAlreadyPoweringOff
7377 )
7378 {
7379 LogFlowFunc(("VM has powered itself off but Console still thinks it is running. Notifying.\n"));
7380
7381 /* prevent powerDown() from calling VMR3PowerOff() again */
7382 Assert(that->mVMPoweredOff == false);
7383 that->mVMPoweredOff = true;
7384
7385 /*
7386 * request a progress object from the server
7387 * (this will set the machine state to Stopping on the server
7388 * to block others from accessing this machine)
7389 */
7390 ComPtr<IProgress> pProgress;
7391 HRESULT rc = that->mControl->BeginPoweringDown(pProgress.asOutParam());
7392 AssertComRC(rc);
7393
7394 /* sync the state with the server */
7395 that->setMachineStateLocally(MachineState_Stopping);
7396
7397 /* Setup task object and thread to carry out the operation
7398 * asynchronously (if we call powerDown() right here but there
7399 * is one or more mpVM callers (added with addVMCaller()) we'll
7400 * deadlock).
7401 */
7402 std::auto_ptr<VMPowerDownTask> task(new VMPowerDownTask(that,
7403 pProgress));
7404
7405 /* If creating a task failed, this can currently mean one of
7406 * two: either Console::uninit() has been called just a ms
7407 * before (so a powerDown() call is already on the way), or
7408 * powerDown() itself is being already executed. Just do
7409 * nothing.
7410 */
7411 if (!task->isOk())
7412 {
7413 LogFlowFunc(("Console is already being uninitialized.\n"));
7414 break;
7415 }
7416
7417 int vrc = RTThreadCreate(NULL, Console::powerDownThread,
7418 (void *) task.get(), 0,
7419 RTTHREADTYPE_MAIN_WORKER, 0,
7420 "VMPwrDwn");
7421 AssertMsgRCBreak(vrc, ("Could not create VMPowerDown thread (%Rrc)\n", vrc));
7422
7423 /* task is now owned by powerDownThread(), so release it */
7424 task.release();
7425 }
7426 break;
7427 }
7428
7429 /* The VM has been completely destroyed.
7430 *
7431 * Note: This state change can happen at two points:
7432 * 1) At the end of VMR3Destroy() if it was not called from EMT.
7433 * 2) At the end of vmR3EmulationThread if VMR3Destroy() was
7434 * called by EMT.
7435 */
7436 case VMSTATE_TERMINATED:
7437 {
7438 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
7439
7440 if (that->mVMStateChangeCallbackDisabled)
7441 break;
7442
7443 /* Terminate host interface networking. If aVM is NULL, we've been
7444 * manually called from powerUpThread() either before calling
7445 * VMR3Create() or after VMR3Create() failed, so no need to touch
7446 * networking.
7447 */
7448 if (aVM)
7449 that->powerDownHostInterfaces();
7450
7451 /* From now on the machine is officially powered down or remains in
7452 * the Saved state.
7453 */
7454 switch (that->mMachineState)
7455 {
7456 default:
7457 AssertFailed();
7458 /* fall through */
7459 case MachineState_Stopping:
7460 /* successfully powered down */
7461 that->setMachineState(MachineState_PoweredOff);
7462 break;
7463 case MachineState_Saving:
7464 /* successfully saved */
7465 that->setMachineState(MachineState_Saved);
7466 break;
7467 case MachineState_Starting:
7468 /* failed to start, but be patient: set back to PoweredOff
7469 * (for similarity with the below) */
7470 that->setMachineState(MachineState_PoweredOff);
7471 break;
7472 case MachineState_Restoring:
7473 /* failed to load the saved state file, but be patient: set
7474 * back to Saved (to preserve the saved state file) */
7475 that->setMachineState(MachineState_Saved);
7476 break;
7477 case MachineState_TeleportingIn:
7478 /* Teleportation failed or was canceled. Back to powered off. */
7479 that->setMachineState(MachineState_PoweredOff);
7480 break;
7481 case MachineState_TeleportingPausedVM:
7482 /* Successfully teleported the VM. */
7483 that->setMachineState(MachineState_Teleported);
7484 break;
7485 case MachineState_FaultTolerantSyncing:
7486 /* Fault tolerant sync failed or was canceled. Back to powered off. */
7487 that->setMachineState(MachineState_PoweredOff);
7488 break;
7489 }
7490 break;
7491 }
7492
7493 case VMSTATE_RESETTING:
7494 {
7495#ifdef VBOX_WITH_GUEST_PROPS
7496 /* Do not take any read/write locks here! */
7497 that->guestPropertiesHandleVMReset();
7498#endif
7499 break;
7500 }
7501
7502 case VMSTATE_SUSPENDED:
7503 {
7504 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
7505
7506 if (that->mVMStateChangeCallbackDisabled)
7507 break;
7508
7509 switch (that->mMachineState)
7510 {
7511 case MachineState_Teleporting:
7512 that->setMachineState(MachineState_TeleportingPausedVM);
7513 break;
7514
7515 case MachineState_LiveSnapshotting:
7516 that->setMachineState(MachineState_Saving);
7517 break;
7518
7519 case MachineState_TeleportingPausedVM:
7520 case MachineState_Saving:
7521 case MachineState_Restoring:
7522 case MachineState_Stopping:
7523 case MachineState_TeleportingIn:
7524 case MachineState_FaultTolerantSyncing:
7525 /* The worker thread handles the transition. */
7526 break;
7527
7528 default:
7529 AssertMsgFailed(("%s\n", Global::stringifyMachineState(that->mMachineState)));
7530 case MachineState_Running:
7531 that->setMachineState(MachineState_Paused);
7532 break;
7533
7534 case MachineState_Paused:
7535 /* Nothing to do. */
7536 break;
7537 }
7538 break;
7539 }
7540
7541 case VMSTATE_SUSPENDED_LS:
7542 case VMSTATE_SUSPENDED_EXT_LS:
7543 {
7544 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
7545 if (that->mVMStateChangeCallbackDisabled)
7546 break;
7547 switch (that->mMachineState)
7548 {
7549 case MachineState_Teleporting:
7550 that->setMachineState(MachineState_TeleportingPausedVM);
7551 break;
7552
7553 case MachineState_LiveSnapshotting:
7554 that->setMachineState(MachineState_Saving);
7555 break;
7556
7557 case MachineState_TeleportingPausedVM:
7558 case MachineState_Saving:
7559 /* ignore */
7560 break;
7561
7562 default:
7563 AssertMsgFailed(("%s/%s -> %s\n", Global::stringifyMachineState(that->mMachineState), VMR3GetStateName(aOldState), VMR3GetStateName(aState) ));
7564 that->setMachineState(MachineState_Paused);
7565 break;
7566 }
7567 break;
7568 }
7569
7570 case VMSTATE_RUNNING:
7571 {
7572 if ( aOldState == VMSTATE_POWERING_ON
7573 || aOldState == VMSTATE_RESUMING
7574 || aOldState == VMSTATE_RUNNING_FT)
7575 {
7576 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
7577
7578 if (that->mVMStateChangeCallbackDisabled)
7579 break;
7580
7581 Assert( ( ( that->mMachineState == MachineState_Starting
7582 || that->mMachineState == MachineState_Paused)
7583 && aOldState == VMSTATE_POWERING_ON)
7584 || ( ( that->mMachineState == MachineState_Restoring
7585 || that->mMachineState == MachineState_TeleportingIn
7586 || that->mMachineState == MachineState_Paused
7587 || that->mMachineState == MachineState_Saving
7588 )
7589 && aOldState == VMSTATE_RESUMING)
7590 || ( that->mMachineState == MachineState_FaultTolerantSyncing
7591 && aOldState == VMSTATE_RUNNING_FT));
7592
7593 that->setMachineState(MachineState_Running);
7594 }
7595
7596 break;
7597 }
7598
7599 case VMSTATE_RUNNING_LS:
7600 AssertMsg( that->mMachineState == MachineState_LiveSnapshotting
7601 || that->mMachineState == MachineState_Teleporting,
7602 ("%s/%s -> %s\n", Global::stringifyMachineState(that->mMachineState), VMR3GetStateName(aOldState), VMR3GetStateName(aState) ));
7603 break;
7604
7605 case VMSTATE_RUNNING_FT:
7606 AssertMsg(that->mMachineState == MachineState_FaultTolerantSyncing,
7607 ("%s/%s -> %s\n", Global::stringifyMachineState(that->mMachineState), VMR3GetStateName(aOldState), VMR3GetStateName(aState) ));
7608 break;
7609
7610 case VMSTATE_FATAL_ERROR:
7611 {
7612 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
7613
7614 if (that->mVMStateChangeCallbackDisabled)
7615 break;
7616
7617 /* Fatal errors are only for running VMs. */
7618 Assert(Global::IsOnline(that->mMachineState));
7619
7620 /* Note! 'Pause' is used here in want of something better. There
7621 * are currently only two places where fatal errors might be
7622 * raised, so it is not worth adding a new externally
7623 * visible state for this yet. */
7624 that->setMachineState(MachineState_Paused);
7625 break;
7626 }
7627
7628 case VMSTATE_GURU_MEDITATION:
7629 {
7630 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
7631
7632 if (that->mVMStateChangeCallbackDisabled)
7633 break;
7634
7635 /* Guru are only for running VMs */
7636 Assert(Global::IsOnline(that->mMachineState));
7637
7638 that->setMachineState(MachineState_Stuck);
7639 break;
7640 }
7641
7642 default: /* shut up gcc */
7643 break;
7644 }
7645}
7646
7647#ifdef VBOX_WITH_USB
7648/**
7649 * Sends a request to VMM to attach the given host device.
7650 * After this method succeeds, the attached device will appear in the
7651 * mUSBDevices collection.
7652 *
7653 * @param aHostDevice device to attach
7654 *
7655 * @note Synchronously calls EMT.
7656 * @note Must be called from under this object's lock.
7657 */
7658HRESULT Console::attachUSBDevice(IUSBDevice *aHostDevice, ULONG aMaskedIfs)
7659{
7660 AssertReturn(aHostDevice, E_FAIL);
7661 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
7662
7663 /* still want a lock object because we need to release it */
7664 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7665
7666 HRESULT hrc;
7667
7668 /*
7669 * Get the address and the Uuid, and call the pfnCreateProxyDevice roothub
7670 * method in EMT (using usbAttachCallback()).
7671 */
7672 Bstr BstrAddress;
7673 hrc = aHostDevice->COMGETTER(Address)(BstrAddress.asOutParam());
7674 ComAssertComRCRetRC(hrc);
7675
7676 Utf8Str Address(BstrAddress);
7677
7678 Bstr id;
7679 hrc = aHostDevice->COMGETTER(Id)(id.asOutParam());
7680 ComAssertComRCRetRC(hrc);
7681 Guid uuid(id);
7682
7683 BOOL fRemote = FALSE;
7684 hrc = aHostDevice->COMGETTER(Remote)(&fRemote);
7685 ComAssertComRCRetRC(hrc);
7686
7687 /* Get the VM handle. */
7688 SafeVMPtr ptrVM(this);
7689 if (!ptrVM.isOk())
7690 return ptrVM.rc();
7691
7692 LogFlowThisFunc(("Proxying USB device '%s' {%RTuuid}...\n",
7693 Address.c_str(), uuid.raw()));
7694
7695 /* release the lock before a VMR3* call (EMT will call us back)! */
7696 alock.release();
7697
7698/** @todo just do everything here and only wrap the PDMR3Usb call. That'll offload some notification stuff from the EMT thread. */
7699 int vrc = VMR3ReqCallWait(ptrVM, VMCPUID_ANY,
7700 (PFNRT)usbAttachCallback, 7,
7701 this, ptrVM.raw(), aHostDevice, uuid.raw(), fRemote, Address.c_str(), aMaskedIfs);
7702
7703 /* restore the lock */
7704 alock.acquire();
7705
7706 /* hrc is S_OK here */
7707
7708 if (RT_FAILURE(vrc))
7709 {
7710 LogWarningThisFunc(("Failed to create proxy device for '%s' {%RTuuid} (%Rrc)\n",
7711 Address.c_str(), uuid.raw(), vrc));
7712
7713 switch (vrc)
7714 {
7715 case VERR_VUSB_NO_PORTS:
7716 hrc = setError(E_FAIL,
7717 tr("Failed to attach the USB device. (No available ports on the USB controller)."));
7718 break;
7719 case VERR_VUSB_USBFS_PERMISSION:
7720 hrc = setError(E_FAIL,
7721 tr("Not permitted to open the USB device, check usbfs options"));
7722 break;
7723 default:
7724 hrc = setError(E_FAIL,
7725 tr("Failed to create a proxy device for the USB device. (Error: %Rrc)"),
7726 vrc);
7727 break;
7728 }
7729 }
7730
7731 return hrc;
7732}
7733
7734/**
7735 * USB device attach callback used by AttachUSBDevice().
7736 * Note that AttachUSBDevice() doesn't return until this callback is executed,
7737 * so we don't use AutoCaller and don't care about reference counters of
7738 * interface pointers passed in.
7739 *
7740 * @thread EMT
7741 * @note Locks the console object for writing.
7742 */
7743//static
7744DECLCALLBACK(int)
7745Console::usbAttachCallback(Console *that, PVM pVM, IUSBDevice *aHostDevice, PCRTUUID aUuid, bool aRemote, const char *aAddress, ULONG aMaskedIfs)
7746{
7747 LogFlowFuncEnter();
7748 LogFlowFunc(("that={%p}\n", that));
7749
7750 AssertReturn(that && aUuid, VERR_INVALID_PARAMETER);
7751
7752 void *pvRemoteBackend = NULL;
7753 if (aRemote)
7754 {
7755 RemoteUSBDevice *pRemoteUSBDevice = static_cast<RemoteUSBDevice *>(aHostDevice);
7756 Guid guid(*aUuid);
7757
7758 pvRemoteBackend = that->consoleVRDPServer()->USBBackendRequestPointer(pRemoteUSBDevice->clientId(), &guid);
7759 if (!pvRemoteBackend)
7760 return VERR_INVALID_PARAMETER; /* The clientId is invalid then. */
7761 }
7762
7763 USHORT portVersion = 1;
7764 HRESULT hrc = aHostDevice->COMGETTER(PortVersion)(&portVersion);
7765 AssertComRCReturn(hrc, VERR_GENERAL_FAILURE);
7766 Assert(portVersion == 1 || portVersion == 2);
7767
7768 int vrc = PDMR3USBCreateProxyDevice(pVM, aUuid, aRemote, aAddress, pvRemoteBackend,
7769 portVersion == 1 ? VUSB_STDVER_11 : VUSB_STDVER_20, aMaskedIfs);
7770 if (RT_SUCCESS(vrc))
7771 {
7772 /* Create a OUSBDevice and add it to the device list */
7773 ComObjPtr<OUSBDevice> pUSBDevice;
7774 pUSBDevice.createObject();
7775 hrc = pUSBDevice->init(aHostDevice);
7776 AssertComRC(hrc);
7777
7778 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
7779 that->mUSBDevices.push_back(pUSBDevice);
7780 LogFlowFunc(("Attached device {%RTuuid}\n", pUSBDevice->id().raw()));
7781
7782 /* notify callbacks */
7783 that->onUSBDeviceStateChange(pUSBDevice, true /* aAttached */, NULL);
7784 }
7785
7786 LogFlowFunc(("vrc=%Rrc\n", vrc));
7787 LogFlowFuncLeave();
7788 return vrc;
7789}
7790
7791/**
7792 * Sends a request to VMM to detach the given host device. After this method
7793 * succeeds, the detached device will disappear from the mUSBDevices
7794 * collection.
7795 *
7796 * @param aIt Iterator pointing to the device to detach.
7797 *
7798 * @note Synchronously calls EMT.
7799 * @note Must be called from under this object's lock.
7800 */
7801HRESULT Console::detachUSBDevice(USBDeviceList::iterator &aIt)
7802{
7803 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
7804
7805 /* still want a lock object because we need to release it */
7806 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7807
7808 /* Get the VM handle. */
7809 SafeVMPtr ptrVM(this);
7810 if (!ptrVM.isOk())
7811 return ptrVM.rc();
7812
7813 /* if the device is attached, then there must at least one USB hub. */
7814 AssertReturn(PDMR3USBHasHub(ptrVM), E_FAIL);
7815
7816 LogFlowThisFunc(("Detaching USB proxy device {%RTuuid}...\n",
7817 (*aIt)->id().raw()));
7818
7819 /* release the lock before a VMR3* call (EMT will call us back)! */
7820 alock.release();
7821
7822/** @todo just do everything here and only wrap the PDMR3Usb call. That'll offload some notification stuff from the EMT thread. */
7823 int vrc = VMR3ReqCallWait(ptrVM, VMCPUID_ANY,
7824 (PFNRT)usbDetachCallback, 5,
7825 this, ptrVM.raw(), &aIt, (*aIt)->id().raw());
7826 ComAssertRCRet(vrc, E_FAIL);
7827
7828 return S_OK;
7829}
7830
7831/**
7832 * USB device detach callback used by DetachUSBDevice().
7833 * Note that DetachUSBDevice() doesn't return until this callback is executed,
7834 * so we don't use AutoCaller and don't care about reference counters of
7835 * interface pointers passed in.
7836 *
7837 * @thread EMT
7838 * @note Locks the console object for writing.
7839 */
7840//static
7841DECLCALLBACK(int)
7842Console::usbDetachCallback(Console *that, PVM pVM, USBDeviceList::iterator *aIt, PCRTUUID aUuid)
7843{
7844 LogFlowFuncEnter();
7845 LogFlowFunc(("that={%p}\n", that));
7846
7847 AssertReturn(that && aUuid, VERR_INVALID_PARAMETER);
7848 ComObjPtr<OUSBDevice> pUSBDevice = **aIt;
7849
7850 /*
7851 * If that was a remote device, release the backend pointer.
7852 * The pointer was requested in usbAttachCallback.
7853 */
7854 BOOL fRemote = FALSE;
7855
7856 HRESULT hrc2 = (**aIt)->COMGETTER(Remote)(&fRemote);
7857 if (FAILED(hrc2))
7858 setErrorStatic(hrc2, "GetRemote() failed");
7859
7860 if (fRemote)
7861 {
7862 Guid guid(*aUuid);
7863 that->consoleVRDPServer()->USBBackendReleasePointer(&guid);
7864 }
7865
7866 int vrc = PDMR3USBDetachDevice(pVM, aUuid);
7867
7868 if (RT_SUCCESS(vrc))
7869 {
7870 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
7871
7872 /* Remove the device from the collection */
7873 that->mUSBDevices.erase(*aIt);
7874 LogFlowFunc(("Detached device {%RTuuid}\n", pUSBDevice->id().raw()));
7875
7876 /* notify callbacks */
7877 that->onUSBDeviceStateChange(pUSBDevice, false /* aAttached */, NULL);
7878 }
7879
7880 LogFlowFunc(("vrc=%Rrc\n", vrc));
7881 LogFlowFuncLeave();
7882 return vrc;
7883}
7884#endif /* VBOX_WITH_USB */
7885
7886/* Note: FreeBSD needs this whether netflt is used or not. */
7887#if ((defined(RT_OS_LINUX) && !defined(VBOX_WITH_NETFLT)) || defined(RT_OS_FREEBSD))
7888/**
7889 * Helper function to handle host interface device creation and attachment.
7890 *
7891 * @param networkAdapter the network adapter which attachment should be reset
7892 * @return COM status code
7893 *
7894 * @note The caller must lock this object for writing.
7895 *
7896 * @todo Move this back into the driver!
7897 */
7898HRESULT Console::attachToTapInterface(INetworkAdapter *networkAdapter)
7899{
7900 LogFlowThisFunc(("\n"));
7901 /* sanity check */
7902 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
7903
7904# ifdef VBOX_STRICT
7905 /* paranoia */
7906 NetworkAttachmentType_T attachment;
7907 networkAdapter->COMGETTER(AttachmentType)(&attachment);
7908 Assert(attachment == NetworkAttachmentType_Bridged);
7909# endif /* VBOX_STRICT */
7910
7911 HRESULT rc = S_OK;
7912
7913 ULONG slot = 0;
7914 rc = networkAdapter->COMGETTER(Slot)(&slot);
7915 AssertComRC(rc);
7916
7917# ifdef RT_OS_LINUX
7918 /*
7919 * Allocate a host interface device
7920 */
7921 int rcVBox = RTFileOpen(&maTapFD[slot], "/dev/net/tun",
7922 RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_INHERIT);
7923 if (RT_SUCCESS(rcVBox))
7924 {
7925 /*
7926 * Set/obtain the tap interface.
7927 */
7928 struct ifreq IfReq;
7929 memset(&IfReq, 0, sizeof(IfReq));
7930 /* The name of the TAP interface we are using */
7931 Bstr tapDeviceName;
7932 rc = networkAdapter->COMGETTER(BridgedInterface)(tapDeviceName.asOutParam());
7933 if (FAILED(rc))
7934 tapDeviceName.setNull(); /* Is this necessary? */
7935 if (tapDeviceName.isEmpty())
7936 {
7937 LogRel(("No TAP device name was supplied.\n"));
7938 rc = setError(E_FAIL, tr("No TAP device name was supplied for the host networking interface"));
7939 }
7940
7941 if (SUCCEEDED(rc))
7942 {
7943 /* If we are using a static TAP device then try to open it. */
7944 Utf8Str str(tapDeviceName);
7945 if (str.length() <= sizeof(IfReq.ifr_name))
7946 strcpy(IfReq.ifr_name, str.c_str());
7947 else
7948 memcpy(IfReq.ifr_name, str.c_str(), sizeof(IfReq.ifr_name) - 1); /** @todo bitch about names which are too long... */
7949 IfReq.ifr_flags = IFF_TAP | IFF_NO_PI;
7950 rcVBox = ioctl(maTapFD[slot], TUNSETIFF, &IfReq);
7951 if (rcVBox != 0)
7952 {
7953 LogRel(("Failed to open the host network interface %ls\n", tapDeviceName.raw()));
7954 rc = setError(E_FAIL,
7955 tr("Failed to open the host network interface %ls"),
7956 tapDeviceName.raw());
7957 }
7958 }
7959 if (SUCCEEDED(rc))
7960 {
7961 /*
7962 * Make it pollable.
7963 */
7964 if (fcntl(maTapFD[slot], F_SETFL, O_NONBLOCK) != -1)
7965 {
7966 Log(("attachToTapInterface: %RTfile %ls\n", maTapFD[slot], tapDeviceName.raw()));
7967 /*
7968 * Here is the right place to communicate the TAP file descriptor and
7969 * the host interface name to the server if/when it becomes really
7970 * necessary.
7971 */
7972 maTAPDeviceName[slot] = tapDeviceName;
7973 rcVBox = VINF_SUCCESS;
7974 }
7975 else
7976 {
7977 int iErr = errno;
7978
7979 LogRel(("Configuration error: Failed to configure /dev/net/tun non blocking. Error: %s\n", strerror(iErr)));
7980 rcVBox = VERR_HOSTIF_BLOCKING;
7981 rc = setError(E_FAIL,
7982 tr("could not set up the host networking device for non blocking access: %s"),
7983 strerror(errno));
7984 }
7985 }
7986 }
7987 else
7988 {
7989 LogRel(("Configuration error: Failed to open /dev/net/tun rc=%Rrc\n", rcVBox));
7990 switch (rcVBox)
7991 {
7992 case VERR_ACCESS_DENIED:
7993 /* will be handled by our caller */
7994 rc = rcVBox;
7995 break;
7996 default:
7997 rc = setError(E_FAIL,
7998 tr("Could not set up the host networking device: %Rrc"),
7999 rcVBox);
8000 break;
8001 }
8002 }
8003
8004# elif defined(RT_OS_FREEBSD)
8005 /*
8006 * Set/obtain the tap interface.
8007 */
8008 /* The name of the TAP interface we are using */
8009 Bstr tapDeviceName;
8010 rc = networkAdapter->COMGETTER(BridgedInterface)(tapDeviceName.asOutParam());
8011 if (FAILED(rc))
8012 tapDeviceName.setNull(); /* Is this necessary? */
8013 if (tapDeviceName.isEmpty())
8014 {
8015 LogRel(("No TAP device name was supplied.\n"));
8016 rc = setError(E_FAIL, tr("No TAP device name was supplied for the host networking interface"));
8017 }
8018 char szTapdev[1024] = "/dev/";
8019 /* If we are using a static TAP device then try to open it. */
8020 Utf8Str str(tapDeviceName);
8021 if (str.length() + strlen(szTapdev) <= sizeof(szTapdev))
8022 strcat(szTapdev, str.c_str());
8023 else
8024 memcpy(szTapdev + strlen(szTapdev), str.c_str(),
8025 sizeof(szTapdev) - strlen(szTapdev) - 1); /** @todo bitch about names which are too long... */
8026 int rcVBox = RTFileOpen(&maTapFD[slot], szTapdev,
8027 RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_INHERIT | RTFILE_O_NON_BLOCK);
8028
8029 if (RT_SUCCESS(rcVBox))
8030 maTAPDeviceName[slot] = tapDeviceName;
8031 else
8032 {
8033 switch (rcVBox)
8034 {
8035 case VERR_ACCESS_DENIED:
8036 /* will be handled by our caller */
8037 rc = rcVBox;
8038 break;
8039 default:
8040 rc = setError(E_FAIL,
8041 tr("Failed to open the host network interface %ls"),
8042 tapDeviceName.raw());
8043 break;
8044 }
8045 }
8046# else
8047# error "huh?"
8048# endif
8049 /* in case of failure, cleanup. */
8050 if (RT_FAILURE(rcVBox) && SUCCEEDED(rc))
8051 {
8052 LogRel(("General failure attaching to host interface\n"));
8053 rc = setError(E_FAIL,
8054 tr("General failure attaching to host interface"));
8055 }
8056 LogFlowThisFunc(("rc=%d\n", rc));
8057 return rc;
8058}
8059
8060
8061/**
8062 * Helper function to handle detachment from a host interface
8063 *
8064 * @param networkAdapter the network adapter which attachment should be reset
8065 * @return COM status code
8066 *
8067 * @note The caller must lock this object for writing.
8068 *
8069 * @todo Move this back into the driver!
8070 */
8071HRESULT Console::detachFromTapInterface(INetworkAdapter *networkAdapter)
8072{
8073 /* sanity check */
8074 LogFlowThisFunc(("\n"));
8075 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
8076
8077 HRESULT rc = S_OK;
8078# ifdef VBOX_STRICT
8079 /* paranoia */
8080 NetworkAttachmentType_T attachment;
8081 networkAdapter->COMGETTER(AttachmentType)(&attachment);
8082 Assert(attachment == NetworkAttachmentType_Bridged);
8083# endif /* VBOX_STRICT */
8084
8085 ULONG slot = 0;
8086 rc = networkAdapter->COMGETTER(Slot)(&slot);
8087 AssertComRC(rc);
8088
8089 /* is there an open TAP device? */
8090 if (maTapFD[slot] != NIL_RTFILE)
8091 {
8092 /*
8093 * Close the file handle.
8094 */
8095 Bstr tapDeviceName, tapTerminateApplication;
8096 bool isStatic = true;
8097 rc = networkAdapter->COMGETTER(BridgedInterface)(tapDeviceName.asOutParam());
8098 if (FAILED(rc) || tapDeviceName.isEmpty())
8099 {
8100 /* If the name is empty, this is a dynamic TAP device, so close it now,
8101 so that the termination script can remove the interface. Otherwise we still
8102 need the FD to pass to the termination script. */
8103 isStatic = false;
8104 int rcVBox = RTFileClose(maTapFD[slot]);
8105 AssertRC(rcVBox);
8106 maTapFD[slot] = NIL_RTFILE;
8107 }
8108 if (isStatic)
8109 {
8110 /* If we are using a static TAP device, we close it now, after having called the
8111 termination script. */
8112 int rcVBox = RTFileClose(maTapFD[slot]);
8113 AssertRC(rcVBox);
8114 }
8115 /* the TAP device name and handle are no longer valid */
8116 maTapFD[slot] = NIL_RTFILE;
8117 maTAPDeviceName[slot] = "";
8118 }
8119 LogFlowThisFunc(("returning %d\n", rc));
8120 return rc;
8121}
8122#endif /* (RT_OS_LINUX || RT_OS_FREEBSD) && !VBOX_WITH_NETFLT */
8123
8124/**
8125 * Called at power down to terminate host interface networking.
8126 *
8127 * @note The caller must lock this object for writing.
8128 */
8129HRESULT Console::powerDownHostInterfaces()
8130{
8131 LogFlowThisFunc(("\n"));
8132
8133 /* sanity check */
8134 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
8135
8136 /*
8137 * host interface termination handling
8138 */
8139 HRESULT rc = S_OK;
8140 ComPtr<IVirtualBox> pVirtualBox;
8141 mMachine->COMGETTER(Parent)(pVirtualBox.asOutParam());
8142 ComPtr<ISystemProperties> pSystemProperties;
8143 if (pVirtualBox)
8144 pVirtualBox->COMGETTER(SystemProperties)(pSystemProperties.asOutParam());
8145 ChipsetType_T chipsetType = ChipsetType_PIIX3;
8146 mMachine->COMGETTER(ChipsetType)(&chipsetType);
8147 ULONG maxNetworkAdapters = 0;
8148 if (pSystemProperties)
8149 pSystemProperties->GetMaxNetworkAdapters(chipsetType, &maxNetworkAdapters);
8150
8151 for (ULONG slot = 0; slot < maxNetworkAdapters; slot++)
8152 {
8153 ComPtr<INetworkAdapter> pNetworkAdapter;
8154 rc = mMachine->GetNetworkAdapter(slot, pNetworkAdapter.asOutParam());
8155 if (FAILED(rc)) break;
8156
8157 BOOL enabled = FALSE;
8158 pNetworkAdapter->COMGETTER(Enabled)(&enabled);
8159 if (!enabled)
8160 continue;
8161
8162 NetworkAttachmentType_T attachment;
8163 pNetworkAdapter->COMGETTER(AttachmentType)(&attachment);
8164 if (attachment == NetworkAttachmentType_Bridged)
8165 {
8166#if ((defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD)) && !defined(VBOX_WITH_NETFLT))
8167 HRESULT rc2 = detachFromTapInterface(pNetworkAdapter);
8168 if (FAILED(rc2) && SUCCEEDED(rc))
8169 rc = rc2;
8170#endif /* (RT_OS_LINUX || RT_OS_FREEBSD) && !VBOX_WITH_NETFLT */
8171 }
8172 }
8173
8174 return rc;
8175}
8176
8177
8178/**
8179 * Process callback handler for VMR3LoadFromFile, VMR3LoadFromStream, VMR3Save
8180 * and VMR3Teleport.
8181 *
8182 * @param pVM The VM handle.
8183 * @param uPercent Completion percentage (0-100).
8184 * @param pvUser Pointer to an IProgress instance.
8185 * @return VINF_SUCCESS.
8186 */
8187/*static*/
8188DECLCALLBACK(int) Console::stateProgressCallback(PVM pVM, unsigned uPercent, void *pvUser)
8189{
8190 IProgress *pProgress = static_cast<IProgress *>(pvUser);
8191
8192 /* update the progress object */
8193 if (pProgress)
8194 pProgress->SetCurrentOperationProgress(uPercent);
8195
8196 return VINF_SUCCESS;
8197}
8198
8199/**
8200 * @copydoc FNVMATERROR
8201 *
8202 * @remarks Might be some tiny serialization concerns with access to the string
8203 * object here...
8204 */
8205/*static*/ DECLCALLBACK(void)
8206Console::genericVMSetErrorCallback(PVM pVM, void *pvUser, int rc, RT_SRC_POS_DECL,
8207 const char *pszErrorFmt, va_list va)
8208{
8209 Utf8Str *pErrorText = (Utf8Str *)pvUser;
8210 AssertPtr(pErrorText);
8211
8212 /* We ignore RT_SRC_POS_DECL arguments to avoid confusion of end-users. */
8213 va_list va2;
8214 va_copy(va2, va);
8215
8216 /* Append to any the existing error message. */
8217 if (pErrorText->length())
8218 *pErrorText = Utf8StrFmt("%s.\n%N (%Rrc)", pErrorText->c_str(),
8219 pszErrorFmt, &va2, rc, rc);
8220 else
8221 *pErrorText = Utf8StrFmt("%N (%Rrc)", pszErrorFmt, &va2, rc, rc);
8222
8223 va_end(va2);
8224}
8225
8226/**
8227 * VM runtime error callback function.
8228 * See VMSetRuntimeError for the detailed description of parameters.
8229 *
8230 * @param pVM The VM handle.
8231 * @param pvUser The user argument.
8232 * @param fFlags The action flags. See VMSETRTERR_FLAGS_*.
8233 * @param pszErrorId Error ID string.
8234 * @param pszFormat Error message format string.
8235 * @param va Error message arguments.
8236 * @thread EMT.
8237 */
8238/* static */ DECLCALLBACK(void)
8239Console::setVMRuntimeErrorCallback(PVM pVM, void *pvUser, uint32_t fFlags,
8240 const char *pszErrorId,
8241 const char *pszFormat, va_list va)
8242{
8243 bool const fFatal = !!(fFlags & VMSETRTERR_FLAGS_FATAL);
8244 LogFlowFuncEnter();
8245
8246 Console *that = static_cast<Console *>(pvUser);
8247 AssertReturnVoid(that);
8248
8249 Utf8Str message(pszFormat, va);
8250
8251 LogRel(("Console: VM runtime error: fatal=%RTbool, errorID=%s message=\"%s\"\n",
8252 fFatal, pszErrorId, message.c_str()));
8253
8254 that->onRuntimeError(BOOL(fFatal), Bstr(pszErrorId).raw(),
8255 Bstr(message).raw());
8256
8257 LogFlowFuncLeave();
8258}
8259
8260/**
8261 * Captures USB devices that match filters of the VM.
8262 * Called at VM startup.
8263 *
8264 * @param pVM The VM handle.
8265 *
8266 * @note The caller must lock this object for writing.
8267 */
8268HRESULT Console::captureUSBDevices(PVM pVM)
8269{
8270 LogFlowThisFunc(("\n"));
8271
8272 /* sanity check */
8273 ComAssertRet(isWriteLockOnCurrentThread(), E_FAIL);
8274
8275 /* If the machine has an USB controller, ask the USB proxy service to
8276 * capture devices */
8277 PPDMIBASE pBase;
8278 int vrc = PDMR3QueryLun(pVM, "usb-ohci", 0, 0, &pBase);
8279 if (RT_SUCCESS(vrc))
8280 {
8281 /* release the lock before calling Host in VBoxSVC since Host may call
8282 * us back from under its lock (e.g. onUSBDeviceAttach()) which would
8283 * produce an inter-process dead-lock otherwise. */
8284 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8285 alock.release();
8286
8287 HRESULT hrc = mControl->AutoCaptureUSBDevices();
8288 ComAssertComRCRetRC(hrc);
8289 }
8290 else if ( vrc == VERR_PDM_DEVICE_NOT_FOUND
8291 || vrc == VERR_PDM_DEVICE_INSTANCE_NOT_FOUND)
8292 vrc = VINF_SUCCESS;
8293 else
8294 AssertRC(vrc);
8295
8296 return RT_SUCCESS(vrc) ? S_OK : E_FAIL;
8297}
8298
8299
8300/**
8301 * Detach all USB device which are attached to the VM for the
8302 * purpose of clean up and such like.
8303 *
8304 * @note The caller must lock this object for writing.
8305 */
8306void Console::detachAllUSBDevices(bool aDone)
8307{
8308 LogFlowThisFunc(("aDone=%RTbool\n", aDone));
8309
8310 /* sanity check */
8311 AssertReturnVoid(isWriteLockOnCurrentThread());
8312
8313 mUSBDevices.clear();
8314
8315 /* release the lock before calling Host in VBoxSVC since Host may call
8316 * us back from under its lock (e.g. onUSBDeviceAttach()) which would
8317 * produce an inter-process dead-lock otherwise. */
8318 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8319 alock.release();
8320
8321 mControl->DetachAllUSBDevices(aDone);
8322}
8323
8324/**
8325 * @note Locks this object for writing.
8326 */
8327void Console::processRemoteUSBDevices(uint32_t u32ClientId, VRDEUSBDEVICEDESC *pDevList, uint32_t cbDevList, bool fDescExt)
8328{
8329 LogFlowThisFuncEnter();
8330 LogFlowThisFunc(("u32ClientId = %d, pDevList=%p, cbDevList = %d, fDescExt = %d\n", u32ClientId, pDevList, cbDevList, fDescExt));
8331
8332 AutoCaller autoCaller(this);
8333 if (!autoCaller.isOk())
8334 {
8335 /* Console has been already uninitialized, deny request */
8336 AssertMsgFailed(("Console is already uninitialized\n"));
8337 LogFlowThisFunc(("Console is already uninitialized\n"));
8338 LogFlowThisFuncLeave();
8339 return;
8340 }
8341
8342 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8343
8344 /*
8345 * Mark all existing remote USB devices as dirty.
8346 */
8347 for (RemoteUSBDeviceList::iterator it = mRemoteUSBDevices.begin();
8348 it != mRemoteUSBDevices.end();
8349 ++it)
8350 {
8351 (*it)->dirty(true);
8352 }
8353
8354 /*
8355 * Process the pDevList and add devices those are not already in the mRemoteUSBDevices list.
8356 */
8357 /** @todo (sunlover) REMOTE_USB Strict validation of the pDevList. */
8358 VRDEUSBDEVICEDESC *e = pDevList;
8359
8360 /* The cbDevList condition must be checked first, because the function can
8361 * receive pDevList = NULL and cbDevList = 0 on client disconnect.
8362 */
8363 while (cbDevList >= 2 && e->oNext)
8364 {
8365 /* Sanitize incoming strings in case they aren't valid UTF-8. */
8366 if (e->oManufacturer)
8367 RTStrPurgeEncoding((char *)e + e->oManufacturer);
8368 if (e->oProduct)
8369 RTStrPurgeEncoding((char *)e + e->oProduct);
8370 if (e->oSerialNumber)
8371 RTStrPurgeEncoding((char *)e + e->oSerialNumber);
8372
8373 LogFlowThisFunc(("vendor %04X, product %04X, name = %s\n",
8374 e->idVendor, e->idProduct,
8375 e->oProduct? (char *)e + e->oProduct: ""));
8376
8377 bool fNewDevice = true;
8378
8379 for (RemoteUSBDeviceList::iterator it = mRemoteUSBDevices.begin();
8380 it != mRemoteUSBDevices.end();
8381 ++it)
8382 {
8383 if ((*it)->devId() == e->id
8384 && (*it)->clientId() == u32ClientId)
8385 {
8386 /* The device is already in the list. */
8387 (*it)->dirty(false);
8388 fNewDevice = false;
8389 break;
8390 }
8391 }
8392
8393 if (fNewDevice)
8394 {
8395 LogRel(("Remote USB: ++++ Vendor %04X. Product %04X. Name = [%s].\n",
8396 e->idVendor, e->idProduct, e->oProduct? (char *)e + e->oProduct: ""));
8397
8398 /* Create the device object and add the new device to list. */
8399 ComObjPtr<RemoteUSBDevice> pUSBDevice;
8400 pUSBDevice.createObject();
8401 pUSBDevice->init(u32ClientId, e, fDescExt);
8402
8403 mRemoteUSBDevices.push_back(pUSBDevice);
8404
8405 /* Check if the device is ok for current USB filters. */
8406 BOOL fMatched = FALSE;
8407 ULONG fMaskedIfs = 0;
8408
8409 HRESULT hrc = mControl->RunUSBDeviceFilters(pUSBDevice, &fMatched, &fMaskedIfs);
8410
8411 AssertComRC(hrc);
8412
8413 LogFlowThisFunc(("USB filters return %d %#x\n", fMatched, fMaskedIfs));
8414
8415 if (fMatched)
8416 {
8417 hrc = onUSBDeviceAttach(pUSBDevice, NULL, fMaskedIfs);
8418
8419 /// @todo (r=dmik) warning reporting subsystem
8420
8421 if (hrc == S_OK)
8422 {
8423 LogFlowThisFunc(("Device attached\n"));
8424 pUSBDevice->captured(true);
8425 }
8426 }
8427 }
8428
8429 if (cbDevList < e->oNext)
8430 {
8431 LogWarningThisFunc(("cbDevList %d > oNext %d\n",
8432 cbDevList, e->oNext));
8433 break;
8434 }
8435
8436 cbDevList -= e->oNext;
8437
8438 e = (VRDEUSBDEVICEDESC *)((uint8_t *)e + e->oNext);
8439 }
8440
8441 /*
8442 * Remove dirty devices, that is those which are not reported by the server anymore.
8443 */
8444 for (;;)
8445 {
8446 ComObjPtr<RemoteUSBDevice> pUSBDevice;
8447
8448 RemoteUSBDeviceList::iterator it = mRemoteUSBDevices.begin();
8449 while (it != mRemoteUSBDevices.end())
8450 {
8451 if ((*it)->dirty())
8452 {
8453 pUSBDevice = *it;
8454 break;
8455 }
8456
8457 ++it;
8458 }
8459
8460 if (!pUSBDevice)
8461 {
8462 break;
8463 }
8464
8465 USHORT vendorId = 0;
8466 pUSBDevice->COMGETTER(VendorId)(&vendorId);
8467
8468 USHORT productId = 0;
8469 pUSBDevice->COMGETTER(ProductId)(&productId);
8470
8471 Bstr product;
8472 pUSBDevice->COMGETTER(Product)(product.asOutParam());
8473
8474 LogRel(("Remote USB: ---- Vendor %04X. Product %04X. Name = [%ls].\n",
8475 vendorId, productId, product.raw()));
8476
8477 /* Detach the device from VM. */
8478 if (pUSBDevice->captured())
8479 {
8480 Bstr uuid;
8481 pUSBDevice->COMGETTER(Id)(uuid.asOutParam());
8482 onUSBDeviceDetach(uuid.raw(), NULL);
8483 }
8484
8485 /* And remove it from the list. */
8486 mRemoteUSBDevices.erase(it);
8487 }
8488
8489 LogFlowThisFuncLeave();
8490}
8491
8492/**
8493 * Progress cancelation callback for fault tolerance VM poweron
8494 */
8495static void faultToleranceProgressCancelCallback(void *pvUser)
8496{
8497 PVM pVM = (PVM)pvUser;
8498
8499 if (pVM)
8500 FTMR3CancelStandby(pVM);
8501}
8502
8503/**
8504 * Thread function which starts the VM (also from saved state) and
8505 * track progress.
8506 *
8507 * @param Thread The thread id.
8508 * @param pvUser Pointer to a VMPowerUpTask structure.
8509 * @return VINF_SUCCESS (ignored).
8510 *
8511 * @note Locks the Console object for writing.
8512 */
8513/*static*/
8514DECLCALLBACK(int) Console::powerUpThread(RTTHREAD Thread, void *pvUser)
8515{
8516 LogFlowFuncEnter();
8517
8518 std::auto_ptr<VMPowerUpTask> task(static_cast<VMPowerUpTask *>(pvUser));
8519 AssertReturn(task.get(), VERR_INVALID_PARAMETER);
8520
8521 AssertReturn(!task->mConsole.isNull(), VERR_INVALID_PARAMETER);
8522 AssertReturn(!task->mProgress.isNull(), VERR_INVALID_PARAMETER);
8523
8524 VirtualBoxBase::initializeComForThread();
8525
8526 HRESULT rc = S_OK;
8527 int vrc = VINF_SUCCESS;
8528
8529 /* Set up a build identifier so that it can be seen from core dumps what
8530 * exact build was used to produce the core. */
8531 static char saBuildID[40];
8532 RTStrPrintf(saBuildID, sizeof(saBuildID), "%s%s%s%s VirtualBox %s r%u %s%s%s%s",
8533 "BU", "IL", "DI", "D", VBOX_VERSION_STRING, RTBldCfgRevision(), "BU", "IL", "DI", "D");
8534
8535 ComObjPtr<Console> pConsole = task->mConsole;
8536
8537 /* Note: no need to use addCaller() because VMPowerUpTask does that */
8538
8539 /* The lock is also used as a signal from the task initiator (which
8540 * releases it only after RTThreadCreate()) that we can start the job */
8541 AutoWriteLock alock(pConsole COMMA_LOCKVAL_SRC_POS);
8542
8543 /* sanity */
8544 Assert(pConsole->mpUVM == NULL);
8545
8546 try
8547 {
8548 // Create the VMM device object, which starts the HGCM thread; do this only
8549 // once for the console, for the pathological case that the same console
8550 // object is used to power up a VM twice. VirtualBox 4.0: we now do that
8551 // here instead of the Console constructor (see Console::init())
8552 if (!pConsole->m_pVMMDev)
8553 {
8554 pConsole->m_pVMMDev = new VMMDev(pConsole);
8555 AssertReturn(pConsole->m_pVMMDev, E_FAIL);
8556 }
8557
8558 /* wait for auto reset ops to complete so that we can successfully lock
8559 * the attached hard disks by calling LockMedia() below */
8560 for (VMPowerUpTask::ProgressList::const_iterator
8561 it = task->hardDiskProgresses.begin();
8562 it != task->hardDiskProgresses.end(); ++it)
8563 {
8564 HRESULT rc2 = (*it)->WaitForCompletion(-1);
8565 AssertComRC(rc2);
8566 }
8567
8568 /*
8569 * Lock attached media. This method will also check their accessibility.
8570 * If we're a teleporter, we'll have to postpone this action so we can
8571 * migrate between local processes.
8572 *
8573 * Note! The media will be unlocked automatically by
8574 * SessionMachine::setMachineState() when the VM is powered down.
8575 */
8576 if ( !task->mTeleporterEnabled
8577 && task->mEnmFaultToleranceState != FaultToleranceState_Standby)
8578 {
8579 rc = pConsole->mControl->LockMedia();
8580 if (FAILED(rc)) throw rc;
8581 }
8582
8583 /* Create the VRDP server. In case of headless operation, this will
8584 * also create the framebuffer, required at VM creation.
8585 */
8586 ConsoleVRDPServer *server = pConsole->consoleVRDPServer();
8587 Assert(server);
8588
8589 /* Does VRDP server call Console from the other thread?
8590 * Not sure (and can change), so release the lock just in case.
8591 */
8592 alock.release();
8593 vrc = server->Launch();
8594 alock.acquire();
8595
8596 if (vrc == VERR_NET_ADDRESS_IN_USE)
8597 {
8598 Utf8Str errMsg;
8599 Bstr bstr;
8600 pConsole->mVRDEServer->GetVRDEProperty(Bstr("TCP/Ports").raw(), bstr.asOutParam());
8601 Utf8Str ports = bstr;
8602 errMsg = Utf8StrFmt(tr("VirtualBox Remote Desktop Extension server can't bind to the port: %s"),
8603 ports.c_str());
8604 LogRel(("VRDE: Warning: failed to launch VRDE server (%Rrc): '%s'\n",
8605 vrc, errMsg.c_str()));
8606 }
8607 else if (vrc == VINF_NOT_SUPPORTED)
8608 {
8609 /* This means that the VRDE is not installed. */
8610 LogRel(("VRDE: VirtualBox Remote Desktop Extension is not available.\n"));
8611 }
8612 else if (RT_FAILURE(vrc))
8613 {
8614 /* Fail, if the server is installed but can't start. */
8615 Utf8Str errMsg;
8616 switch (vrc)
8617 {
8618 case VERR_FILE_NOT_FOUND:
8619 {
8620 /* VRDE library file is missing. */
8621 errMsg = Utf8StrFmt(tr("Could not find the VirtualBox Remote Desktop Extension library."));
8622 break;
8623 }
8624 default:
8625 errMsg = Utf8StrFmt(tr("Failed to launch Remote Desktop Extension server (%Rrc)"),
8626 vrc);
8627 }
8628 LogRel(("VRDE: Failed: (%Rrc), error message: '%s'\n",
8629 vrc, errMsg.c_str()));
8630 throw setErrorStatic(E_FAIL, errMsg.c_str());
8631 }
8632
8633 ComPtr<IMachine> pMachine = pConsole->machine();
8634 ULONG cCpus = 1;
8635 pMachine->COMGETTER(CPUCount)(&cCpus);
8636
8637 /*
8638 * Create the VM
8639 */
8640 PVM pVM;
8641 /*
8642 * release the lock since EMT will call Console. It's safe because
8643 * mMachineState is either Starting or Restoring state here.
8644 */
8645 alock.release();
8646
8647 vrc = VMR3Create(cCpus,
8648 pConsole->mpVmm2UserMethods,
8649 Console::genericVMSetErrorCallback,
8650 &task->mErrorMsg,
8651 task->mConfigConstructor,
8652 static_cast<Console *>(pConsole),
8653 &pVM);
8654
8655 alock.acquire();
8656
8657 /* Enable client connections to the server. */
8658 pConsole->consoleVRDPServer()->EnableConnections();
8659
8660 if (RT_SUCCESS(vrc))
8661 {
8662 do
8663 {
8664 /*
8665 * Register our load/save state file handlers
8666 */
8667 vrc = SSMR3RegisterExternal(pVM, sSSMConsoleUnit, 0 /*iInstance*/, sSSMConsoleVer, 0 /* cbGuess */,
8668 NULL, NULL, NULL,
8669 NULL, saveStateFileExec, NULL,
8670 NULL, loadStateFileExec, NULL,
8671 static_cast<Console *>(pConsole));
8672 AssertRCBreak(vrc);
8673
8674 vrc = static_cast<Console *>(pConsole)->getDisplay()->registerSSM(pVM);
8675 AssertRC(vrc);
8676 if (RT_FAILURE(vrc))
8677 break;
8678
8679 /*
8680 * Synchronize debugger settings
8681 */
8682 MachineDebugger *machineDebugger = pConsole->getMachineDebugger();
8683 if (machineDebugger)
8684 machineDebugger->flushQueuedSettings();
8685
8686 /*
8687 * Shared Folders
8688 */
8689 if (pConsole->m_pVMMDev->isShFlActive())
8690 {
8691 /* Does the code below call Console from the other thread?
8692 * Not sure, so release the lock just in case. */
8693 alock.release();
8694
8695 for (SharedFolderDataMap::const_iterator it = task->mSharedFolders.begin();
8696 it != task->mSharedFolders.end();
8697 ++it)
8698 {
8699 const SharedFolderData &d = it->second;
8700 rc = pConsole->createSharedFolder(it->first, d);
8701 if (FAILED(rc))
8702 {
8703 ErrorInfoKeeper eik;
8704 setVMRuntimeErrorCallbackF(pVM, pConsole, 0, "BrokenSharedFolder",
8705 N_("The shared folder '%s' could not be set up: %ls.\n"
8706 "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"),
8707 it->first.c_str(), eik.getText().raw());
8708 }
8709 }
8710 if (FAILED(rc))
8711 rc = S_OK; // do not fail with broken shared folders
8712
8713 /* acquire the lock again */
8714 alock.acquire();
8715 }
8716
8717 /*
8718 * Capture USB devices.
8719 */
8720 rc = pConsole->captureUSBDevices(pVM);
8721 if (FAILED(rc)) break;
8722
8723 /* release the lock before a lengthy operation */
8724 alock.release();
8725
8726 /* Load saved state? */
8727 if (task->mSavedStateFile.length())
8728 {
8729 LogFlowFunc(("Restoring saved state from '%s'...\n",
8730 task->mSavedStateFile.c_str()));
8731
8732 vrc = VMR3LoadFromFile(pVM,
8733 task->mSavedStateFile.c_str(),
8734 Console::stateProgressCallback,
8735 static_cast<IProgress *>(task->mProgress));
8736
8737 if (RT_SUCCESS(vrc))
8738 {
8739 if (task->mStartPaused)
8740 /* done */
8741 pConsole->setMachineState(MachineState_Paused);
8742 else
8743 {
8744 /* Start/Resume the VM execution */
8745#ifdef VBOX_WITH_EXTPACK
8746 vrc = pConsole->mptrExtPackManager->callAllVmPowerOnHooks(pConsole, pVM);
8747#endif
8748 if (RT_SUCCESS(vrc))
8749 vrc = VMR3Resume(pVM);
8750 AssertLogRelRC(vrc);
8751 }
8752 }
8753
8754 /* Power off in case we failed loading or resuming the VM */
8755 if (RT_FAILURE(vrc))
8756 {
8757 int vrc2 = VMR3PowerOff(pVM); AssertLogRelRC(vrc2);
8758#ifdef VBOX_WITH_EXTPACK
8759 pConsole->mptrExtPackManager->callAllVmPowerOffHooks(pConsole, pVM);
8760#endif
8761 }
8762 }
8763 else if (task->mTeleporterEnabled)
8764 {
8765 /* -> ConsoleImplTeleporter.cpp */
8766 bool fPowerOffOnFailure;
8767 rc = pConsole->teleporterTrg(VMR3GetUVM(pVM), pMachine, &task->mErrorMsg, task->mStartPaused,
8768 task->mProgress, &fPowerOffOnFailure);
8769 if (FAILED(rc) && fPowerOffOnFailure)
8770 {
8771 ErrorInfoKeeper eik;
8772 int vrc2 = VMR3PowerOff(pVM); AssertLogRelRC(vrc2);
8773#ifdef VBOX_WITH_EXTPACK
8774 pConsole->mptrExtPackManager->callAllVmPowerOffHooks(pConsole, pVM);
8775#endif
8776 }
8777 }
8778 else if (task->mEnmFaultToleranceState != FaultToleranceState_Inactive)
8779 {
8780 /*
8781 * Get the config.
8782 */
8783 ULONG uPort;
8784 ULONG uInterval;
8785 Bstr bstrAddress, bstrPassword;
8786
8787 rc = pMachine->COMGETTER(FaultTolerancePort)(&uPort);
8788 if (SUCCEEDED(rc))
8789 {
8790 rc = pMachine->COMGETTER(FaultToleranceSyncInterval)(&uInterval);
8791 if (SUCCEEDED(rc))
8792 rc = pMachine->COMGETTER(FaultToleranceAddress)(bstrAddress.asOutParam());
8793 if (SUCCEEDED(rc))
8794 rc = pMachine->COMGETTER(FaultTolerancePassword)(bstrPassword.asOutParam());
8795 }
8796 if (task->mProgress->setCancelCallback(faultToleranceProgressCancelCallback, pVM))
8797 {
8798 if (SUCCEEDED(rc))
8799 {
8800 Utf8Str strAddress(bstrAddress);
8801 const char *pszAddress = strAddress.isEmpty() ? NULL : strAddress.c_str();
8802 Utf8Str strPassword(bstrPassword);
8803 const char *pszPassword = strPassword.isEmpty() ? NULL : strPassword.c_str();
8804
8805 /* Power on the FT enabled VM. */
8806#ifdef VBOX_WITH_EXTPACK
8807 vrc = pConsole->mptrExtPackManager->callAllVmPowerOnHooks(pConsole, pVM);
8808#endif
8809 if (RT_SUCCESS(vrc))
8810 vrc = FTMR3PowerOn(pVM,
8811 task->mEnmFaultToleranceState == FaultToleranceState_Master /* fMaster */,
8812 uInterval,
8813 pszAddress,
8814 uPort,
8815 pszPassword);
8816 AssertLogRelRC(vrc);
8817 }
8818 task->mProgress->setCancelCallback(NULL, NULL);
8819 }
8820 else
8821 rc = E_FAIL;
8822 }
8823 else if (task->mStartPaused)
8824 /* done */
8825 pConsole->setMachineState(MachineState_Paused);
8826 else
8827 {
8828 /* Power on the VM (i.e. start executing) */
8829#ifdef VBOX_WITH_EXTPACK
8830 vrc = pConsole->mptrExtPackManager->callAllVmPowerOnHooks(pConsole, pVM);
8831#endif
8832 if (RT_SUCCESS(vrc))
8833 vrc = VMR3PowerOn(pVM);
8834 AssertLogRelRC(vrc);
8835 }
8836
8837 /* acquire the lock again */
8838 alock.acquire();
8839 }
8840 while (0);
8841
8842 /* On failure, destroy the VM */
8843 if (FAILED(rc) || RT_FAILURE(vrc))
8844 {
8845 /* preserve existing error info */
8846 ErrorInfoKeeper eik;
8847
8848 /* powerDown() will call VMR3Destroy() and do all necessary
8849 * cleanup (VRDP, USB devices) */
8850 alock.release();
8851 HRESULT rc2 = pConsole->powerDown();
8852 alock.acquire();
8853 AssertComRC(rc2);
8854 }
8855 else
8856 {
8857 /*
8858 * Deregister the VMSetError callback. This is necessary as the
8859 * pfnVMAtError() function passed to VMR3Create() is supposed to
8860 * be sticky but our error callback isn't.
8861 */
8862 alock.release();
8863 VMR3AtErrorDeregister(pVM, Console::genericVMSetErrorCallback, &task->mErrorMsg);
8864 /** @todo register another VMSetError callback? */
8865 alock.acquire();
8866 }
8867 }
8868 else
8869 {
8870 /*
8871 * If VMR3Create() failed it has released the VM memory.
8872 */
8873 VMR3ReleaseUVM(pConsole->mpUVM);
8874 pConsole->mpUVM = NULL;
8875 }
8876
8877 if (SUCCEEDED(rc) && RT_FAILURE(vrc))
8878 {
8879 /* If VMR3Create() or one of the other calls in this function fail,
8880 * an appropriate error message has been set in task->mErrorMsg.
8881 * However since that happens via a callback, the rc status code in
8882 * this function is not updated.
8883 */
8884 if (!task->mErrorMsg.length())
8885 {
8886 /* If the error message is not set but we've got a failure,
8887 * convert the VBox status code into a meaningful error message.
8888 * This becomes unused once all the sources of errors set the
8889 * appropriate error message themselves.
8890 */
8891 AssertMsgFailed(("Missing error message during powerup for status code %Rrc\n", vrc));
8892 task->mErrorMsg = Utf8StrFmt(tr("Failed to start VM execution (%Rrc)"),
8893 vrc);
8894 }
8895
8896 /* Set the error message as the COM error.
8897 * Progress::notifyComplete() will pick it up later. */
8898 throw setErrorStatic(E_FAIL, task->mErrorMsg.c_str());
8899 }
8900 }
8901 catch (HRESULT aRC) { rc = aRC; }
8902
8903 if ( pConsole->mMachineState == MachineState_Starting
8904 || pConsole->mMachineState == MachineState_Restoring
8905 || pConsole->mMachineState == MachineState_TeleportingIn
8906 )
8907 {
8908 /* We are still in the Starting/Restoring state. This means one of:
8909 *
8910 * 1) we failed before VMR3Create() was called;
8911 * 2) VMR3Create() failed.
8912 *
8913 * In both cases, there is no need to call powerDown(), but we still
8914 * need to go back to the PoweredOff/Saved state. Reuse
8915 * vmstateChangeCallback() for that purpose.
8916 */
8917
8918 /* preserve existing error info */
8919 ErrorInfoKeeper eik;
8920
8921 Assert(pConsole->mpUVM == NULL);
8922 vmstateChangeCallback(NULL, VMSTATE_TERMINATED, VMSTATE_CREATING,
8923 pConsole);
8924 }
8925
8926 /*
8927 * Evaluate the final result. Note that the appropriate mMachineState value
8928 * is already set by vmstateChangeCallback() in all cases.
8929 */
8930
8931 /* release the lock, don't need it any more */
8932 alock.release();
8933
8934 if (SUCCEEDED(rc))
8935 {
8936 /* Notify the progress object of the success */
8937 task->mProgress->notifyComplete(S_OK);
8938 }
8939 else
8940 {
8941 /* The progress object will fetch the current error info */
8942 task->mProgress->notifyComplete(rc);
8943 LogRel(("Power up failed (vrc=%Rrc, rc=%Rhrc (%#08X))\n", vrc, rc, rc));
8944 }
8945
8946 /* Notify VBoxSVC and any waiting openRemoteSession progress object. */
8947 pConsole->mControl->EndPowerUp(rc);
8948
8949#if defined(RT_OS_WINDOWS)
8950 /* uninitialize COM */
8951 CoUninitialize();
8952#endif
8953
8954 LogFlowFuncLeave();
8955
8956 return VINF_SUCCESS;
8957}
8958
8959
8960/**
8961 * Reconfigures a medium attachment (part of taking or deleting an online snapshot).
8962 *
8963 * @param pConsole Reference to the console object.
8964 * @param pVM The VM handle.
8965 * @param lInstance The instance of the controller.
8966 * @param pcszDevice The name of the controller type.
8967 * @param enmBus The storage bus type of the controller.
8968 * @param fSetupMerge Whether to set up a medium merge
8969 * @param uMergeSource Merge source image index
8970 * @param uMergeTarget Merge target image index
8971 * @param aMediumAtt The medium attachment.
8972 * @param aMachineState The current machine state.
8973 * @param phrc Where to store com error - only valid if we return VERR_GENERAL_FAILURE.
8974 * @return VBox status code.
8975 */
8976/* static */
8977DECLCALLBACK(int) Console::reconfigureMediumAttachment(Console *pConsole,
8978 PVM pVM,
8979 const char *pcszDevice,
8980 unsigned uInstance,
8981 StorageBus_T enmBus,
8982 bool fUseHostIOCache,
8983 bool fBuiltinIoCache,
8984 bool fSetupMerge,
8985 unsigned uMergeSource,
8986 unsigned uMergeTarget,
8987 IMediumAttachment *aMediumAtt,
8988 MachineState_T aMachineState,
8989 HRESULT *phrc)
8990{
8991 LogFlowFunc(("pVM=%p aMediumAtt=%p phrc=%p\n", pVM, aMediumAtt, phrc));
8992
8993 int rc;
8994 HRESULT hrc;
8995 Bstr bstr;
8996 *phrc = S_OK;
8997#define RC_CHECK() do { if (RT_FAILURE(rc)) { AssertMsgFailed(("rc=%Rrc\n", rc)); return rc; } } while (0)
8998#define H() do { if (FAILED(hrc)) { AssertMsgFailed(("hrc=%Rhrc (%#x)\n", hrc, hrc)); *phrc = hrc; return VERR_GENERAL_FAILURE; } } while (0)
8999
9000 /* Ignore attachments other than hard disks, since at the moment they are
9001 * not subject to snapshotting in general. */
9002 DeviceType_T lType;
9003 hrc = aMediumAtt->COMGETTER(Type)(&lType); H();
9004 if (lType != DeviceType_HardDisk)
9005 return VINF_SUCCESS;
9006
9007 /* Determine the base path for the device instance. */
9008 PCFGMNODE pCtlInst;
9009 pCtlInst = CFGMR3GetChildF(CFGMR3GetRoot(pVM), "Devices/%s/%u/", pcszDevice, uInstance);
9010 AssertReturn(pCtlInst, VERR_INTERNAL_ERROR);
9011
9012 /* Update the device instance configuration. */
9013 rc = pConsole->configMediumAttachment(pCtlInst,
9014 pcszDevice,
9015 uInstance,
9016 enmBus,
9017 fUseHostIOCache,
9018 fBuiltinIoCache,
9019 fSetupMerge,
9020 uMergeSource,
9021 uMergeTarget,
9022 aMediumAtt,
9023 aMachineState,
9024 phrc,
9025 true /* fAttachDetach */,
9026 false /* fForceUnmount */,
9027 false /* fHotplug */,
9028 pVM,
9029 NULL /* paLedDevType */);
9030 /** @todo this dumps everything attached to this device instance, which
9031 * is more than necessary. Dumping the changed LUN would be enough. */
9032 CFGMR3Dump(pCtlInst);
9033 RC_CHECK();
9034
9035#undef RC_CHECK
9036#undef H
9037
9038 LogFlowFunc(("Returns success\n"));
9039 return VINF_SUCCESS;
9040}
9041
9042/**
9043 * Progress cancelation callback employed by Console::fntTakeSnapshotWorker.
9044 */
9045static void takesnapshotProgressCancelCallback(void *pvUser)
9046{
9047 PUVM pUVM = (PUVM)pvUser;
9048 SSMR3Cancel(VMR3GetVM(pUVM));
9049}
9050
9051/**
9052 * Worker thread created by Console::TakeSnapshot.
9053 * @param Thread The current thread (ignored).
9054 * @param pvUser The task.
9055 * @return VINF_SUCCESS (ignored).
9056 */
9057/*static*/
9058DECLCALLBACK(int) Console::fntTakeSnapshotWorker(RTTHREAD Thread, void *pvUser)
9059{
9060 VMTakeSnapshotTask *pTask = (VMTakeSnapshotTask*)pvUser;
9061
9062 // taking a snapshot consists of the following:
9063
9064 // 1) creating a diff image for each virtual hard disk, into which write operations go after
9065 // the snapshot has been created (done in VBoxSVC, in SessionMachine::BeginTakingSnapshot)
9066 // 2) creating a Snapshot object with the state of the machine (hardware + storage,
9067 // done in VBoxSVC, also in SessionMachine::BeginTakingSnapshot)
9068 // 3) saving the state of the virtual machine (here, in the VM process, if the machine is online)
9069
9070 Console *that = pTask->mConsole;
9071 bool fBeganTakingSnapshot = false;
9072 bool fSuspenededBySave = false;
9073
9074 AutoCaller autoCaller(that);
9075 if (FAILED(autoCaller.rc()))
9076 {
9077 that->mptrCancelableProgress.setNull();
9078 return autoCaller.rc();
9079 }
9080
9081 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
9082
9083 HRESULT rc = S_OK;
9084
9085 try
9086 {
9087 /* STEP 1 + 2:
9088 * request creating the diff images on the server and create the snapshot object
9089 * (this will set the machine state to Saving on the server to block
9090 * others from accessing this machine)
9091 */
9092 rc = that->mControl->BeginTakingSnapshot(that,
9093 pTask->bstrName.raw(),
9094 pTask->bstrDescription.raw(),
9095 pTask->mProgress,
9096 pTask->fTakingSnapshotOnline,
9097 pTask->bstrSavedStateFile.asOutParam());
9098 if (FAILED(rc))
9099 throw rc;
9100
9101 fBeganTakingSnapshot = true;
9102
9103 /*
9104 * state file is non-null only when the VM is paused
9105 * (i.e. creating a snapshot online)
9106 */
9107 bool f = (!pTask->bstrSavedStateFile.isEmpty() && pTask->fTakingSnapshotOnline)
9108 || ( pTask->bstrSavedStateFile.isEmpty() && !pTask->fTakingSnapshotOnline);
9109 if (!f)
9110 throw setErrorStatic(E_FAIL, "Invalid state of saved state file");
9111
9112 /* sync the state with the server */
9113 if (pTask->lastMachineState == MachineState_Running)
9114 that->setMachineStateLocally(MachineState_LiveSnapshotting);
9115 else
9116 that->setMachineStateLocally(MachineState_Saving);
9117
9118 // STEP 3: save the VM state (if online)
9119 if (pTask->fTakingSnapshotOnline)
9120 {
9121 Utf8Str strSavedStateFile(pTask->bstrSavedStateFile);
9122
9123 SafeVMPtr ptrVM(that);
9124 if (!ptrVM.isOk())
9125 throw ptrVM.rc();
9126
9127 pTask->mProgress->SetNextOperation(Bstr(tr("Saving the machine state")).raw(),
9128 pTask->ulMemSize); // operation weight, same as computed when setting up progress object
9129 pTask->mProgress->setCancelCallback(takesnapshotProgressCancelCallback, ptrVM.rawUVM());
9130
9131 alock.release();
9132 LogFlowFunc(("VMR3Save...\n"));
9133 int vrc = VMR3Save(ptrVM,
9134 strSavedStateFile.c_str(),
9135 true /*fContinueAfterwards*/,
9136 Console::stateProgressCallback,
9137 static_cast<IProgress *>(pTask->mProgress),
9138 &fSuspenededBySave);
9139 alock.acquire();
9140 if (RT_FAILURE(vrc))
9141 throw setErrorStatic(E_FAIL,
9142 tr("Failed to save the machine state to '%s' (%Rrc)"),
9143 strSavedStateFile.c_str(), vrc);
9144
9145 pTask->mProgress->setCancelCallback(NULL, NULL);
9146 if (!pTask->mProgress->notifyPointOfNoReturn())
9147 throw setErrorStatic(E_FAIL, tr("Canceled"));
9148 that->mptrCancelableProgress.setNull();
9149
9150 // STEP 4: reattach hard disks
9151 LogFlowFunc(("Reattaching new differencing hard disks...\n"));
9152
9153 pTask->mProgress->SetNextOperation(Bstr(tr("Reconfiguring medium attachments")).raw(),
9154 1); // operation weight, same as computed when setting up progress object
9155
9156 com::SafeIfaceArray<IMediumAttachment> atts;
9157 rc = that->mMachine->COMGETTER(MediumAttachments)(ComSafeArrayAsOutParam(atts));
9158 if (FAILED(rc))
9159 throw rc;
9160
9161 for (size_t i = 0;
9162 i < atts.size();
9163 ++i)
9164 {
9165 ComPtr<IStorageController> pStorageController;
9166 Bstr controllerName;
9167 ULONG lInstance;
9168 StorageControllerType_T enmController;
9169 StorageBus_T enmBus;
9170 BOOL fUseHostIOCache;
9171
9172 /*
9173 * We can't pass a storage controller object directly
9174 * (g++ complains about not being able to pass non POD types through '...')
9175 * so we have to query needed values here and pass them.
9176 */
9177 rc = atts[i]->COMGETTER(Controller)(controllerName.asOutParam());
9178 if (FAILED(rc))
9179 throw rc;
9180
9181 rc = that->mMachine->GetStorageControllerByName(controllerName.raw(),
9182 pStorageController.asOutParam());
9183 if (FAILED(rc))
9184 throw rc;
9185
9186 rc = pStorageController->COMGETTER(ControllerType)(&enmController);
9187 if (FAILED(rc))
9188 throw rc;
9189 rc = pStorageController->COMGETTER(Instance)(&lInstance);
9190 if (FAILED(rc))
9191 throw rc;
9192 rc = pStorageController->COMGETTER(Bus)(&enmBus);
9193 if (FAILED(rc))
9194 throw rc;
9195 rc = pStorageController->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
9196 if (FAILED(rc))
9197 throw rc;
9198
9199 const char *pcszDevice = Console::convertControllerTypeToDev(enmController);
9200
9201 BOOL fBuiltinIoCache;
9202 rc = that->mMachine->COMGETTER(IoCacheEnabled)(&fBuiltinIoCache);
9203 if (FAILED(rc))
9204 throw rc;
9205
9206 /*
9207 * don't release the lock since reconfigureMediumAttachment
9208 * isn't going to need the Console lock.
9209 */
9210 vrc = VMR3ReqCallWait(ptrVM,
9211 VMCPUID_ANY,
9212 (PFNRT)reconfigureMediumAttachment,
9213 13,
9214 that,
9215 ptrVM.raw(),
9216 pcszDevice,
9217 lInstance,
9218 enmBus,
9219 fUseHostIOCache,
9220 fBuiltinIoCache,
9221 false /* fSetupMerge */,
9222 0 /* uMergeSource */,
9223 0 /* uMergeTarget */,
9224 atts[i],
9225 that->mMachineState,
9226 &rc);
9227 if (RT_FAILURE(vrc))
9228 throw setErrorStatic(E_FAIL, Console::tr("%Rrc"), vrc);
9229 if (FAILED(rc))
9230 throw rc;
9231 }
9232 }
9233
9234 /*
9235 * finalize the requested snapshot object.
9236 * This will reset the machine state to the state it had right
9237 * before calling mControl->BeginTakingSnapshot().
9238 */
9239 rc = that->mControl->EndTakingSnapshot(TRUE /*aSuccess*/);
9240 // do not throw rc here because we can't call EndTakingSnapshot() twice
9241 LogFlowFunc(("EndTakingSnapshot -> %Rhrc [mMachineState=%s]\n", rc, Global::stringifyMachineState(that->mMachineState)));
9242 }
9243 catch (HRESULT rcThrown)
9244 {
9245 /* preserve existing error info */
9246 ErrorInfoKeeper eik;
9247
9248 if (fBeganTakingSnapshot)
9249 that->mControl->EndTakingSnapshot(FALSE /*aSuccess*/);
9250
9251 rc = rcThrown;
9252 LogFunc(("Caught %Rhrc [mMachineState=%s]\n", rc, Global::stringifyMachineState(that->mMachineState)));
9253 }
9254 Assert(alock.isWriteLockOnCurrentThread());
9255
9256 if (FAILED(rc)) /* Must come before calling setMachineState. */
9257 pTask->mProgress->notifyComplete(rc);
9258
9259 /*
9260 * Fix up the machine state.
9261 *
9262 * For live snapshots we do all the work, for the two other variations we
9263 * just update the local copy.
9264 */
9265 MachineState_T enmMachineState;
9266 that->mMachine->COMGETTER(State)(&enmMachineState);
9267 if ( that->mMachineState == MachineState_LiveSnapshotting
9268 || that->mMachineState == MachineState_Saving)
9269 {
9270
9271 if (!pTask->fTakingSnapshotOnline)
9272 that->setMachineStateLocally(pTask->lastMachineState);
9273 else if (SUCCEEDED(rc))
9274 {
9275 Assert( pTask->lastMachineState == MachineState_Running
9276 || pTask->lastMachineState == MachineState_Paused);
9277 Assert(that->mMachineState == MachineState_Saving);
9278 if (pTask->lastMachineState == MachineState_Running)
9279 {
9280 LogFlowFunc(("VMR3Resume...\n"));
9281 SafeVMPtr ptrVM(that);
9282 alock.release();
9283 int vrc = VMR3Resume(ptrVM);
9284 alock.acquire();
9285 if (RT_FAILURE(vrc))
9286 {
9287 rc = setErrorStatic(VBOX_E_VM_ERROR, tr("Could not resume the machine execution (%Rrc)"), vrc);
9288 pTask->mProgress->notifyComplete(rc);
9289 if (that->mMachineState == MachineState_Saving)
9290 that->setMachineStateLocally(MachineState_Paused);
9291 }
9292 }
9293 else
9294 that->setMachineStateLocally(MachineState_Paused);
9295 }
9296 else
9297 {
9298 /** @todo this could probably be made more generic and reused elsewhere. */
9299 /* paranoid cleanup on for a failed online snapshot. */
9300 VMSTATE enmVMState = VMR3GetStateU(that->mpUVM);
9301 switch (enmVMState)
9302 {
9303 case VMSTATE_RUNNING:
9304 case VMSTATE_RUNNING_LS:
9305 case VMSTATE_DEBUGGING:
9306 case VMSTATE_DEBUGGING_LS:
9307 case VMSTATE_POWERING_OFF:
9308 case VMSTATE_POWERING_OFF_LS:
9309 case VMSTATE_RESETTING:
9310 case VMSTATE_RESETTING_LS:
9311 Assert(!fSuspenededBySave);
9312 that->setMachineState(MachineState_Running);
9313 break;
9314
9315 case VMSTATE_GURU_MEDITATION:
9316 case VMSTATE_GURU_MEDITATION_LS:
9317 that->setMachineState(MachineState_Stuck);
9318 break;
9319
9320 case VMSTATE_FATAL_ERROR:
9321 case VMSTATE_FATAL_ERROR_LS:
9322 if (pTask->lastMachineState == MachineState_Paused)
9323 that->setMachineStateLocally(pTask->lastMachineState);
9324 else
9325 that->setMachineState(MachineState_Paused);
9326 break;
9327
9328 default:
9329 AssertMsgFailed(("%s\n", VMR3GetStateName(enmVMState)));
9330 case VMSTATE_SUSPENDED:
9331 case VMSTATE_SUSPENDED_LS:
9332 case VMSTATE_SUSPENDING:
9333 case VMSTATE_SUSPENDING_LS:
9334 case VMSTATE_SUSPENDING_EXT_LS:
9335 if (fSuspenededBySave)
9336 {
9337 Assert(pTask->lastMachineState == MachineState_Running);
9338 LogFlowFunc(("VMR3Resume (on failure)...\n"));
9339 SafeVMPtr ptrVM(that);
9340 alock.release();
9341 int vrc = VMR3Resume(ptrVM); AssertLogRelRC(vrc);
9342 alock.acquire();
9343 if (RT_FAILURE(vrc))
9344 that->setMachineState(MachineState_Paused);
9345 }
9346 else if (pTask->lastMachineState == MachineState_Paused)
9347 that->setMachineStateLocally(pTask->lastMachineState);
9348 else
9349 that->setMachineState(MachineState_Paused);
9350 break;
9351 }
9352
9353 }
9354 }
9355 /*else: somebody else has change the state... Leave it. */
9356
9357 /* check the remote state to see that we got it right. */
9358 that->mMachine->COMGETTER(State)(&enmMachineState);
9359 AssertLogRelMsg(that->mMachineState == enmMachineState,
9360 ("mMachineState=%s enmMachineState=%s\n", Global::stringifyMachineState(that->mMachineState),
9361 Global::stringifyMachineState(enmMachineState) ));
9362
9363
9364 if (SUCCEEDED(rc)) /* The failure cases are handled above. */
9365 pTask->mProgress->notifyComplete(rc);
9366
9367 delete pTask;
9368
9369 LogFlowFuncLeave();
9370 return VINF_SUCCESS;
9371}
9372
9373/**
9374 * Thread for executing the saved state operation.
9375 *
9376 * @param Thread The thread handle.
9377 * @param pvUser Pointer to a VMSaveTask structure.
9378 * @return VINF_SUCCESS (ignored).
9379 *
9380 * @note Locks the Console object for writing.
9381 */
9382/*static*/
9383DECLCALLBACK(int) Console::saveStateThread(RTTHREAD Thread, void *pvUser)
9384{
9385 LogFlowFuncEnter();
9386
9387 std::auto_ptr<VMSaveTask> task(static_cast<VMSaveTask*>(pvUser));
9388 AssertReturn(task.get(), VERR_INVALID_PARAMETER);
9389
9390 Assert(task->mSavedStateFile.length());
9391 Assert(task->mProgress.isNull());
9392 Assert(!task->mServerProgress.isNull());
9393
9394 const ComObjPtr<Console> &that = task->mConsole;
9395 Utf8Str errMsg;
9396 HRESULT rc = S_OK;
9397
9398 LogFlowFunc(("Saving the state to '%s'...\n", task->mSavedStateFile.c_str()));
9399
9400 bool fSuspenededBySave;
9401 int vrc = VMR3Save(task->mpVM,
9402 task->mSavedStateFile.c_str(),
9403 false, /*fContinueAfterwards*/
9404 Console::stateProgressCallback,
9405 static_cast<IProgress *>(task->mServerProgress),
9406 &fSuspenededBySave);
9407 if (RT_FAILURE(vrc))
9408 {
9409 errMsg = Utf8StrFmt(Console::tr("Failed to save the machine state to '%s' (%Rrc)"),
9410 task->mSavedStateFile.c_str(), vrc);
9411 rc = E_FAIL;
9412 }
9413 Assert(!fSuspenededBySave);
9414
9415 /* lock the console once we're going to access it */
9416 AutoWriteLock thatLock(that COMMA_LOCKVAL_SRC_POS);
9417
9418 /* synchronize the state with the server */
9419 if (SUCCEEDED(rc))
9420 {
9421 /*
9422 * The machine has been successfully saved, so power it down
9423 * (vmstateChangeCallback() will set state to Saved on success).
9424 * Note: we release the task's VM caller, otherwise it will
9425 * deadlock.
9426 */
9427 task->releaseVMCaller();
9428 thatLock.release();
9429 rc = that->powerDown();
9430 thatLock.acquire();
9431 }
9432
9433 /*
9434 * Finalize the requested save state procedure. In case of failure it will
9435 * reset the machine state to the state it had right before calling
9436 * mControl->BeginSavingState(). This must be the last thing because it
9437 * will set the progress to completed, and that means that the frontend
9438 * can immediately uninit the associated console object.
9439 */
9440 that->mControl->EndSavingState(rc, Bstr(errMsg).raw());
9441
9442 LogFlowFuncLeave();
9443 return VINF_SUCCESS;
9444}
9445
9446/**
9447 * Thread for powering down the Console.
9448 *
9449 * @param Thread The thread handle.
9450 * @param pvUser Pointer to the VMTask structure.
9451 * @return VINF_SUCCESS (ignored).
9452 *
9453 * @note Locks the Console object for writing.
9454 */
9455/*static*/
9456DECLCALLBACK(int) Console::powerDownThread(RTTHREAD Thread, void *pvUser)
9457{
9458 LogFlowFuncEnter();
9459
9460 std::auto_ptr<VMPowerDownTask> task(static_cast<VMPowerDownTask *>(pvUser));
9461 AssertReturn(task.get(), VERR_INVALID_PARAMETER);
9462
9463 AssertReturn(task->isOk(), VERR_GENERAL_FAILURE);
9464
9465 Assert(task->mProgress.isNull());
9466
9467 const ComObjPtr<Console> &that = task->mConsole;
9468
9469 /* Note: no need to use addCaller() to protect Console because VMTask does
9470 * that */
9471
9472 /* wait until the method tat started us returns */
9473 AutoWriteLock thatLock(that COMMA_LOCKVAL_SRC_POS);
9474
9475 /* release VM caller to avoid the powerDown() deadlock */
9476 task->releaseVMCaller();
9477
9478 thatLock.release();
9479
9480 that->powerDown(task->mServerProgress);
9481
9482 /* complete the operation */
9483 that->mControl->EndPoweringDown(S_OK, Bstr().raw());
9484
9485 LogFlowFuncLeave();
9486 return VINF_SUCCESS;
9487}
9488
9489
9490/**
9491 * @interface_method_impl{VMM2USERMETHODS,pfnSaveState}
9492 */
9493/*static*/ DECLCALLBACK(int)
9494Console::vmm2User_SaveState(PCVMM2USERMETHODS pThis, PUVM pUVM)
9495{
9496 Console *pConsole = ((MYVMM2USERMETHODS *)pThis)->pConsole;
9497 NOREF(pUVM);
9498
9499 /*
9500 * For now, just call SaveState. We should probably try notify the GUI so
9501 * it can pop up a progress object and stuff.
9502 */
9503 HRESULT hrc = pConsole->SaveState(NULL);
9504 return SUCCEEDED(hrc) ? VINF_SUCCESS : Global::vboxStatusCodeFromCOM(hrc);
9505}
9506
9507/**
9508 * @interface_method_impl{VMM2USERMETHODS,pfnNotifyEmtInit}
9509 */
9510/*static*/ DECLCALLBACK(void)
9511Console::vmm2User_NotifyEmtInit(PCVMM2USERMETHODS pThis, PUVM pUVM, PUVMCPU pUVCpu)
9512{
9513 NOREF(pThis); NOREF(pUVM); NOREF(pUVCpu);
9514 VirtualBoxBase::initializeComForThread();
9515}
9516
9517/**
9518 * @interface_method_impl{VMM2USERMETHODS,pfnNotifyEmtTerm}
9519 */
9520/*static*/ DECLCALLBACK(void)
9521Console::vmm2User_NotifyEmtTerm(PCVMM2USERMETHODS pThis, PUVM pUVM, PUVMCPU pUVCpu)
9522{
9523 NOREF(pThis); NOREF(pUVM); NOREF(pUVCpu);
9524 VirtualBoxBase::uninitializeComForThread();
9525}
9526
9527/**
9528 * @interface_method_impl{VMM2USERMETHODS,pfnNotifyPdmtInit}
9529 */
9530/*static*/ DECLCALLBACK(void)
9531Console::vmm2User_NotifyPdmtInit(PCVMM2USERMETHODS pThis, PUVM pUVM)
9532{
9533 NOREF(pThis); NOREF(pUVM);
9534 VirtualBoxBase::initializeComForThread();
9535}
9536
9537/**
9538 * @interface_method_impl{VMM2USERMETHODS,pfnNotifyPdmtTerm}
9539 */
9540/*static*/ DECLCALLBACK(void)
9541Console::vmm2User_NotifyPdmtTerm(PCVMM2USERMETHODS pThis, PUVM pUVM)
9542{
9543 NOREF(pThis); NOREF(pUVM);
9544 VirtualBoxBase::uninitializeComForThread();
9545}
9546
9547
9548
9549
9550/**
9551 * The Main status driver instance data.
9552 */
9553typedef struct DRVMAINSTATUS
9554{
9555 /** The LED connectors. */
9556 PDMILEDCONNECTORS ILedConnectors;
9557 /** Pointer to the LED ports interface above us. */
9558 PPDMILEDPORTS pLedPorts;
9559 /** Pointer to the array of LED pointers. */
9560 PPDMLED *papLeds;
9561 /** The unit number corresponding to the first entry in the LED array. */
9562 RTUINT iFirstLUN;
9563 /** The unit number corresponding to the last entry in the LED array.
9564 * (The size of the LED array is iLastLUN - iFirstLUN + 1.) */
9565 RTUINT iLastLUN;
9566 /** Pointer to the driver instance. */
9567 PPDMDRVINS pDrvIns;
9568 /** The Media Notify interface. */
9569 PDMIMEDIANOTIFY IMediaNotify;
9570 /** Map for translating PDM storage controller/LUN information to
9571 * IMediumAttachment references. */
9572 Console::MediumAttachmentMap *pmapMediumAttachments;
9573 /** Device name+instance for mapping */
9574 char *pszDeviceInstance;
9575 /** Pointer to the Console object, for driver triggered activities. */
9576 Console *pConsole;
9577} DRVMAINSTATUS, *PDRVMAINSTATUS;
9578
9579
9580/**
9581 * Notification about a unit which have been changed.
9582 *
9583 * The driver must discard any pointers to data owned by
9584 * the unit and requery it.
9585 *
9586 * @param pInterface Pointer to the interface structure containing the called function pointer.
9587 * @param iLUN The unit number.
9588 */
9589DECLCALLBACK(void) Console::drvStatus_UnitChanged(PPDMILEDCONNECTORS pInterface, unsigned iLUN)
9590{
9591 PDRVMAINSTATUS pData = (PDRVMAINSTATUS)((uintptr_t)pInterface - RT_OFFSETOF(DRVMAINSTATUS, ILedConnectors));
9592 if (iLUN >= pData->iFirstLUN && iLUN <= pData->iLastLUN)
9593 {
9594 PPDMLED pLed;
9595 int rc = pData->pLedPorts->pfnQueryStatusLed(pData->pLedPorts, iLUN, &pLed);
9596 if (RT_FAILURE(rc))
9597 pLed = NULL;
9598 ASMAtomicWritePtr(&pData->papLeds[iLUN - pData->iFirstLUN], pLed);
9599 Log(("drvStatus_UnitChanged: iLUN=%d pLed=%p\n", iLUN, pLed));
9600 }
9601}
9602
9603
9604/**
9605 * Notification about a medium eject.
9606 *
9607 * @returns VBox status.
9608 * @param pInterface Pointer to the interface structure containing the called function pointer.
9609 * @param uLUN The unit number.
9610 */
9611DECLCALLBACK(int) Console::drvStatus_MediumEjected(PPDMIMEDIANOTIFY pInterface, unsigned uLUN)
9612{
9613 PDRVMAINSTATUS pData = (PDRVMAINSTATUS)((uintptr_t)pInterface - RT_OFFSETOF(DRVMAINSTATUS, IMediaNotify));
9614 PPDMDRVINS pDrvIns = pData->pDrvIns;
9615 LogFunc(("uLUN=%d\n", uLUN));
9616 if (pData->pmapMediumAttachments)
9617 {
9618 AutoWriteLock alock(pData->pConsole COMMA_LOCKVAL_SRC_POS);
9619
9620 ComPtr<IMediumAttachment> pMediumAtt;
9621 Utf8Str devicePath = Utf8StrFmt("%s/LUN#%u", pData->pszDeviceInstance, uLUN);
9622 Console::MediumAttachmentMap::const_iterator end = pData->pmapMediumAttachments->end();
9623 Console::MediumAttachmentMap::const_iterator it = pData->pmapMediumAttachments->find(devicePath);
9624 if (it != end)
9625 pMediumAtt = it->second;
9626 Assert(!pMediumAtt.isNull());
9627 if (!pMediumAtt.isNull())
9628 {
9629 IMedium *pMedium = NULL;
9630 HRESULT rc = pMediumAtt->COMGETTER(Medium)(&pMedium);
9631 AssertComRC(rc);
9632 if (SUCCEEDED(rc) && pMedium)
9633 {
9634 BOOL fHostDrive = FALSE;
9635 rc = pMedium->COMGETTER(HostDrive)(&fHostDrive);
9636 AssertComRC(rc);
9637 if (!fHostDrive)
9638 {
9639 alock.release();
9640
9641 ComPtr<IMediumAttachment> pNewMediumAtt;
9642 rc = pData->pConsole->mControl->EjectMedium(pMediumAtt, pNewMediumAtt.asOutParam());
9643 if (SUCCEEDED(rc))
9644 fireMediumChangedEvent(pData->pConsole->mEventSource, pNewMediumAtt);
9645
9646 alock.acquire();
9647 if (pNewMediumAtt != pMediumAtt)
9648 {
9649 pData->pmapMediumAttachments->erase(devicePath);
9650 pData->pmapMediumAttachments->insert(std::make_pair(devicePath, pNewMediumAtt));
9651 }
9652 }
9653 }
9654 }
9655 }
9656 return VINF_SUCCESS;
9657}
9658
9659
9660/**
9661 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
9662 */
9663DECLCALLBACK(void *) Console::drvStatus_QueryInterface(PPDMIBASE pInterface, const char *pszIID)
9664{
9665 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
9666 PDRVMAINSTATUS pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINSTATUS);
9667 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
9668 PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDCONNECTORS, &pThis->ILedConnectors);
9669 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIANOTIFY, &pThis->IMediaNotify);
9670 return NULL;
9671}
9672
9673
9674/**
9675 * Destruct a status driver instance.
9676 *
9677 * @returns VBox status.
9678 * @param pDrvIns The driver instance data.
9679 */
9680DECLCALLBACK(void) Console::drvStatus_Destruct(PPDMDRVINS pDrvIns)
9681{
9682 PDRVMAINSTATUS pData = PDMINS_2_DATA(pDrvIns, PDRVMAINSTATUS);
9683 LogFlowFunc(("iInstance=%d\n", pDrvIns->iInstance));
9684 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
9685
9686 if (pData->papLeds)
9687 {
9688 unsigned iLed = pData->iLastLUN - pData->iFirstLUN + 1;
9689 while (iLed-- > 0)
9690 ASMAtomicWriteNullPtr(&pData->papLeds[iLed]);
9691 }
9692}
9693
9694
9695/**
9696 * Construct a status driver instance.
9697 *
9698 * @copydoc FNPDMDRVCONSTRUCT
9699 */
9700DECLCALLBACK(int) Console::drvStatus_Construct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
9701{
9702 PDRVMAINSTATUS pData = PDMINS_2_DATA(pDrvIns, PDRVMAINSTATUS);
9703 LogFlowFunc(("iInstance=%d\n", pDrvIns->iInstance));
9704 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
9705
9706 /*
9707 * Validate configuration.
9708 */
9709 if (!CFGMR3AreValuesValid(pCfg, "papLeds\0pmapMediumAttachments\0DeviceInstance\0pConsole\0First\0Last\0"))
9710 return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
9711 AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
9712 ("Configuration error: Not possible to attach anything to this driver!\n"),
9713 VERR_PDM_DRVINS_NO_ATTACH);
9714
9715 /*
9716 * Data.
9717 */
9718 pDrvIns->IBase.pfnQueryInterface = Console::drvStatus_QueryInterface;
9719 pData->ILedConnectors.pfnUnitChanged = Console::drvStatus_UnitChanged;
9720 pData->IMediaNotify.pfnEjected = Console::drvStatus_MediumEjected;
9721 pData->pDrvIns = pDrvIns;
9722 pData->pszDeviceInstance = NULL;
9723
9724 /*
9725 * Read config.
9726 */
9727 int rc = CFGMR3QueryPtr(pCfg, "papLeds", (void **)&pData->papLeds);
9728 if (RT_FAILURE(rc))
9729 {
9730 AssertMsgFailed(("Configuration error: Failed to query the \"papLeds\" value! rc=%Rrc\n", rc));
9731 return rc;
9732 }
9733
9734 rc = CFGMR3QueryPtrDef(pCfg, "pmapMediumAttachments", (void **)&pData->pmapMediumAttachments, NULL);
9735 if (RT_FAILURE(rc))
9736 {
9737 AssertMsgFailed(("Configuration error: Failed to query the \"pmapMediumAttachments\" value! rc=%Rrc\n", rc));
9738 return rc;
9739 }
9740 if (pData->pmapMediumAttachments)
9741 {
9742 rc = CFGMR3QueryStringAlloc(pCfg, "DeviceInstance", &pData->pszDeviceInstance);
9743 if (RT_FAILURE(rc))
9744 {
9745 AssertMsgFailed(("Configuration error: Failed to query the \"DeviceInstance\" value! rc=%Rrc\n", rc));
9746 return rc;
9747 }
9748 rc = CFGMR3QueryPtr(pCfg, "pConsole", (void **)&pData->pConsole);
9749 if (RT_FAILURE(rc))
9750 {
9751 AssertMsgFailed(("Configuration error: Failed to query the \"pConsole\" value! rc=%Rrc\n", rc));
9752 return rc;
9753 }
9754 }
9755
9756 rc = CFGMR3QueryU32(pCfg, "First", &pData->iFirstLUN);
9757 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
9758 pData->iFirstLUN = 0;
9759 else if (RT_FAILURE(rc))
9760 {
9761 AssertMsgFailed(("Configuration error: Failed to query the \"First\" value! rc=%Rrc\n", rc));
9762 return rc;
9763 }
9764
9765 rc = CFGMR3QueryU32(pCfg, "Last", &pData->iLastLUN);
9766 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
9767 pData->iLastLUN = 0;
9768 else if (RT_FAILURE(rc))
9769 {
9770 AssertMsgFailed(("Configuration error: Failed to query the \"Last\" value! rc=%Rrc\n", rc));
9771 return rc;
9772 }
9773 if (pData->iFirstLUN > pData->iLastLUN)
9774 {
9775 AssertMsgFailed(("Configuration error: Invalid unit range %u-%u\n", pData->iFirstLUN, pData->iLastLUN));
9776 return VERR_GENERAL_FAILURE;
9777 }
9778
9779 /*
9780 * Get the ILedPorts interface of the above driver/device and
9781 * query the LEDs we want.
9782 */
9783 pData->pLedPorts = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMILEDPORTS);
9784 AssertMsgReturn(pData->pLedPorts, ("Configuration error: No led ports interface above!\n"),
9785 VERR_PDM_MISSING_INTERFACE_ABOVE);
9786
9787 for (unsigned i = pData->iFirstLUN; i <= pData->iLastLUN; ++i)
9788 Console::drvStatus_UnitChanged(&pData->ILedConnectors, i);
9789
9790 return VINF_SUCCESS;
9791}
9792
9793
9794/**
9795 * Console status driver (LED) registration record.
9796 */
9797const PDMDRVREG Console::DrvStatusReg =
9798{
9799 /* u32Version */
9800 PDM_DRVREG_VERSION,
9801 /* szName */
9802 "MainStatus",
9803 /* szRCMod */
9804 "",
9805 /* szR0Mod */
9806 "",
9807 /* pszDescription */
9808 "Main status driver (Main as in the API).",
9809 /* fFlags */
9810 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
9811 /* fClass. */
9812 PDM_DRVREG_CLASS_STATUS,
9813 /* cMaxInstances */
9814 ~0U,
9815 /* cbInstance */
9816 sizeof(DRVMAINSTATUS),
9817 /* pfnConstruct */
9818 Console::drvStatus_Construct,
9819 /* pfnDestruct */
9820 Console::drvStatus_Destruct,
9821 /* pfnRelocate */
9822 NULL,
9823 /* pfnIOCtl */
9824 NULL,
9825 /* pfnPowerOn */
9826 NULL,
9827 /* pfnReset */
9828 NULL,
9829 /* pfnSuspend */
9830 NULL,
9831 /* pfnResume */
9832 NULL,
9833 /* pfnAttach */
9834 NULL,
9835 /* pfnDetach */
9836 NULL,
9837 /* pfnPowerOff */
9838 NULL,
9839 /* pfnSoftReset */
9840 NULL,
9841 /* u32EndVersion */
9842 PDM_DRVREG_VERSION
9843};
9844
9845/* 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