VirtualBox

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

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

several warnings

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

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