VirtualBox

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

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

VRDP, Main: update RDP client name guest property.

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

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