VirtualBox

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

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

Main/Medium: rework locking scheme to solve lock order violations and long GUI start up time caused by too much locking
Main/all: Remove the enter and leave methods from write locks, they cause hard to find locking problems. Better solve them explicitly.

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