VirtualBox

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

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

Main/Console+Machine: new method for deleting a range of snapshot, right now not implemented

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