VirtualBox

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

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

Main: Use shorter thread names.

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