VirtualBox

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

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

compile fixes for VBOX_WITH_EXTPACK disabled

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 323.7 KB
Line 
1/* $Id: ConsoleImpl.cpp 38913 2011-09-30 09:53:47Z 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#ifdef VBOX_WITH_EXTPACK
6431 mptrExtPackManager->dumpAllToReleaseLog();
6432#endif
6433
6434#ifdef RT_OS_SOLARIS
6435 /* setup host core dumper for the VM */
6436 Bstr value;
6437 HRESULT hrc = mMachine->GetExtraData(Bstr("VBoxInternal2/CoreDumpEnabled").raw(), value.asOutParam());
6438 if (SUCCEEDED(hrc) && value == "1")
6439 {
6440 Bstr coreDumpDir, coreDumpReplaceSys, coreDumpLive;
6441 mMachine->GetExtraData(Bstr("VBoxInternal2/CoreDumpDir").raw(), coreDumpDir.asOutParam());
6442 mMachine->GetExtraData(Bstr("VBoxInternal2/CoreDumpReplaceSystemDump").raw(), coreDumpReplaceSys.asOutParam());
6443 mMachine->GetExtraData(Bstr("VBoxInternal2/CoreDumpLive").raw(), coreDumpLive.asOutParam());
6444
6445 uint32_t fCoreFlags = 0;
6446 if ( coreDumpReplaceSys.isEmpty() == false
6447 && Utf8Str(coreDumpReplaceSys).toUInt32() == 1)
6448 {
6449 fCoreFlags |= RTCOREDUMPER_FLAGS_REPLACE_SYSTEM_DUMP;
6450 }
6451
6452 if ( coreDumpLive.isEmpty() == false
6453 && Utf8Str(coreDumpLive).toUInt32() == 1)
6454 {
6455 fCoreFlags |= RTCOREDUMPER_FLAGS_LIVE_CORE;
6456 }
6457
6458 Utf8Str strDumpDir(coreDumpDir);
6459 const char *pszDumpDir = strDumpDir.c_str();
6460 if ( pszDumpDir
6461 && *pszDumpDir == '\0')
6462 pszDumpDir = NULL;
6463
6464 int vrc;
6465 if ( pszDumpDir
6466 && !RTDirExists(pszDumpDir))
6467 {
6468 /*
6469 * Try create the directory.
6470 */
6471 vrc = RTDirCreateFullPath(pszDumpDir, 0777);
6472 if (RT_FAILURE(vrc))
6473 throw setError(E_FAIL, "Failed to setup CoreDumper. Couldn't create dump directory '%s' (%Rrc)\n", pszDumpDir, vrc);
6474 }
6475
6476 vrc = RTCoreDumperSetup(pszDumpDir, fCoreFlags);
6477 if (RT_FAILURE(vrc))
6478 throw setError(E_FAIL, "Failed to setup CoreDumper (%Rrc)", vrc);
6479 else
6480 LogRel(("CoreDumper setup successful. pszDumpDir=%s fFlags=%#x\n", pszDumpDir ? pszDumpDir : ".", fCoreFlags));
6481 }
6482#endif
6483
6484 /* pass the progress object to the caller if requested */
6485 if (aProgress)
6486 {
6487 if (task->hardDiskProgresses.size() == 0)
6488 {
6489 /* there are no other operations to track, return the powerup
6490 * progress only */
6491 pPowerupProgress.queryInterfaceTo(aProgress);
6492 }
6493 else
6494 {
6495 /* create a combined progress object */
6496 ComObjPtr<CombinedProgress> pProgress;
6497 pProgress.createObject();
6498 VMPowerUpTask::ProgressList progresses(task->hardDiskProgresses);
6499 progresses.push_back(ComPtr<IProgress> (pPowerupProgress));
6500 rc = pProgress->init(static_cast<IConsole *>(this),
6501 progressDesc.raw(), progresses.begin(),
6502 progresses.end());
6503 AssertComRCReturnRC(rc);
6504 pProgress.queryInterfaceTo(aProgress);
6505 }
6506 }
6507
6508 int vrc = RTThreadCreate(NULL, Console::powerUpThread,
6509 (void *)task.get(), 0,
6510 RTTHREADTYPE_MAIN_WORKER, 0, "VMPowerUp");
6511 if (RT_FAILURE(vrc))
6512 throw setError(E_FAIL, "Could not create VMPowerUp thread (%Rrc)", vrc);
6513
6514 /* task is now owned by powerUpThread(), so release it */
6515 task.release();
6516
6517 /* finally, set the state: no right to fail in this method afterwards
6518 * since we've already started the thread and it is now responsible for
6519 * any error reporting and appropriate state change! */
6520 if (mMachineState == MachineState_Saved)
6521 setMachineState(MachineState_Restoring);
6522 else if (fTeleporterEnabled)
6523 setMachineState(MachineState_TeleportingIn);
6524 else if (enmFaultToleranceState == FaultToleranceState_Standby)
6525 setMachineState(MachineState_FaultTolerantSyncing);
6526 else
6527 setMachineState(MachineState_Starting);
6528 }
6529 catch (HRESULT aRC) { rc = aRC; }
6530
6531 if (FAILED(rc) && fBeganPoweringUp)
6532 {
6533
6534 /* The progress object will fetch the current error info */
6535 if (!pPowerupProgress.isNull())
6536 pPowerupProgress->notifyComplete(rc);
6537
6538 /* Save the error info across the IPC below. Can't be done before the
6539 * progress notification above, as saving the error info deletes it
6540 * from the current context, and thus the progress object wouldn't be
6541 * updated correctly. */
6542 ErrorInfoKeeper eik;
6543
6544 /* signal end of operation */
6545 mControl->EndPowerUp(rc);
6546 }
6547
6548 LogFlowThisFunc(("mMachineState=%d, rc=%Rhrc\n", mMachineState, rc));
6549 LogFlowThisFuncLeave();
6550 return rc;
6551}
6552
6553/**
6554 * Internal power off worker routine.
6555 *
6556 * This method may be called only at certain places with the following meaning
6557 * as shown below:
6558 *
6559 * - if the machine state is either Running or Paused, a normal
6560 * Console-initiated powerdown takes place (e.g. PowerDown());
6561 * - if the machine state is Saving, saveStateThread() has successfully done its
6562 * job;
6563 * - if the machine state is Starting or Restoring, powerUpThread() has failed
6564 * to start/load the VM;
6565 * - if the machine state is Stopping, the VM has powered itself off (i.e. not
6566 * as a result of the powerDown() call).
6567 *
6568 * Calling it in situations other than the above will cause unexpected behavior.
6569 *
6570 * Note that this method should be the only one that destroys mpVM and sets it
6571 * to NULL.
6572 *
6573 * @param aProgress Progress object to run (may be NULL).
6574 *
6575 * @note Locks this object for writing.
6576 *
6577 * @note Never call this method from a thread that called addVMCaller() or
6578 * instantiated an AutoVMCaller object; first call releaseVMCaller() or
6579 * release(). Otherwise it will deadlock.
6580 */
6581HRESULT Console::powerDown(IProgress *aProgress /*= NULL*/)
6582{
6583 LogFlowThisFuncEnter();
6584
6585 AutoCaller autoCaller(this);
6586 AssertComRCReturnRC(autoCaller.rc());
6587
6588 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6589
6590 /* Total # of steps for the progress object. Must correspond to the
6591 * number of "advance percent count" comments in this method! */
6592 enum { StepCount = 7 };
6593 /* current step */
6594 ULONG step = 0;
6595
6596 HRESULT rc = S_OK;
6597 int vrc = VINF_SUCCESS;
6598
6599 /* sanity */
6600 Assert(mVMDestroying == false);
6601
6602 PUVM pUVM = mpUVM; Assert(pUVM != NULL);
6603 uint32_t cRefs = VMR3RetainUVM(pUVM); Assert(cRefs != UINT32_MAX);
6604
6605 AssertMsg( mMachineState == MachineState_Running
6606 || mMachineState == MachineState_Paused
6607 || mMachineState == MachineState_Stuck
6608 || mMachineState == MachineState_Starting
6609 || mMachineState == MachineState_Stopping
6610 || mMachineState == MachineState_Saving
6611 || mMachineState == MachineState_Restoring
6612 || mMachineState == MachineState_TeleportingPausedVM
6613 || mMachineState == MachineState_FaultTolerantSyncing
6614 || mMachineState == MachineState_TeleportingIn
6615 , ("Invalid machine state: %s\n", Global::stringifyMachineState(mMachineState)));
6616
6617 LogRel(("Console::powerDown(): A request to power off the VM has been issued (mMachineState=%s, InUninit=%d)\n",
6618 Global::stringifyMachineState(mMachineState), autoCaller.state() == InUninit));
6619
6620 /* Check if we need to power off the VM. In case of mVMPoweredOff=true, the
6621 * VM has already powered itself off in vmstateChangeCallback() and is just
6622 * notifying Console about that. In case of Starting or Restoring,
6623 * powerUpThread() is calling us on failure, so the VM is already off at
6624 * that point. */
6625 if ( !mVMPoweredOff
6626 && ( mMachineState == MachineState_Starting
6627 || mMachineState == MachineState_Restoring
6628 || mMachineState == MachineState_FaultTolerantSyncing
6629 || mMachineState == MachineState_TeleportingIn)
6630 )
6631 mVMPoweredOff = true;
6632
6633 /*
6634 * Go to Stopping state if not already there.
6635 *
6636 * Note that we don't go from Saving/Restoring to Stopping because
6637 * vmstateChangeCallback() needs it to set the state to Saved on
6638 * VMSTATE_TERMINATED. In terms of protecting from inappropriate operations
6639 * while leaving the lock below, Saving or Restoring should be fine too.
6640 * Ditto for TeleportingPausedVM -> Teleported.
6641 */
6642 if ( mMachineState != MachineState_Saving
6643 && mMachineState != MachineState_Restoring
6644 && mMachineState != MachineState_Stopping
6645 && mMachineState != MachineState_TeleportingIn
6646 && mMachineState != MachineState_TeleportingPausedVM
6647 && mMachineState != MachineState_FaultTolerantSyncing
6648 )
6649 setMachineState(MachineState_Stopping);
6650
6651 /* ----------------------------------------------------------------------
6652 * DONE with necessary state changes, perform the power down actions (it's
6653 * safe to leave the object lock now if needed)
6654 * ---------------------------------------------------------------------- */
6655
6656 /* Stop the VRDP server to prevent new clients connection while VM is being
6657 * powered off. */
6658 if (mConsoleVRDPServer)
6659 {
6660 LogFlowThisFunc(("Stopping VRDP server...\n"));
6661
6662 /* Leave the lock since EMT will call us back as addVMCaller()
6663 * in updateDisplayData(). */
6664 alock.leave();
6665
6666 mConsoleVRDPServer->Stop();
6667
6668 alock.enter();
6669 }
6670
6671 /* advance percent count */
6672 if (aProgress)
6673 aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
6674
6675
6676 /* ----------------------------------------------------------------------
6677 * Now, wait for all mpVM callers to finish their work if there are still
6678 * some on other threads. NO methods that need mpVM (or initiate other calls
6679 * that need it) may be called after this point
6680 * ---------------------------------------------------------------------- */
6681
6682 /* go to the destroying state to prevent from adding new callers */
6683 mVMDestroying = true;
6684
6685 if (mVMCallers > 0)
6686 {
6687 /* lazy creation */
6688 if (mVMZeroCallersSem == NIL_RTSEMEVENT)
6689 RTSemEventCreate(&mVMZeroCallersSem);
6690
6691 LogFlowThisFunc(("Waiting for mpVM callers (%d) to drop to zero...\n",
6692 mVMCallers));
6693
6694 alock.leave();
6695
6696 RTSemEventWait(mVMZeroCallersSem, RT_INDEFINITE_WAIT);
6697
6698 alock.enter();
6699 }
6700
6701 /* advance percent count */
6702 if (aProgress)
6703 aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
6704
6705 vrc = VINF_SUCCESS;
6706
6707 /*
6708 * Power off the VM if not already done that.
6709 * Leave the lock since EMT will call vmstateChangeCallback.
6710 *
6711 * Note that VMR3PowerOff() may fail here (invalid VMSTATE) if the
6712 * VM-(guest-)initiated power off happened in parallel a ms before this
6713 * call. So far, we let this error pop up on the user's side.
6714 */
6715 if (!mVMPoweredOff)
6716 {
6717 LogFlowThisFunc(("Powering off the VM...\n"));
6718 alock.leave();
6719 vrc = VMR3PowerOff(VMR3GetVM(pUVM));
6720#ifdef VBOX_WITH_EXTPACK
6721 mptrExtPackManager->callAllVmPowerOffHooks(this, VMR3GetVM(pUVM));
6722#endif
6723 alock.enter();
6724 }
6725
6726 /* advance percent count */
6727 if (aProgress)
6728 aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
6729
6730#ifdef VBOX_WITH_HGCM
6731 /* Shutdown HGCM services before destroying the VM. */
6732 if (m_pVMMDev)
6733 {
6734 LogFlowThisFunc(("Shutdown HGCM...\n"));
6735
6736 /* Leave the lock since EMT will call us back as addVMCaller() */
6737 alock.leave();
6738
6739 m_pVMMDev->hgcmShutdown();
6740
6741 alock.enter();
6742 }
6743
6744 /* advance percent count */
6745 if (aProgress)
6746 aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
6747
6748#endif /* VBOX_WITH_HGCM */
6749
6750 LogFlowThisFunc(("Ready for VM destruction.\n"));
6751
6752 /* If we are called from Console::uninit(), then try to destroy the VM even
6753 * on failure (this will most likely fail too, but what to do?..) */
6754 if (RT_SUCCESS(vrc) || autoCaller.state() == InUninit)
6755 {
6756 /* If the machine has an USB controller, release all USB devices
6757 * (symmetric to the code in captureUSBDevices()) */
6758 bool fHasUSBController = false;
6759 {
6760 PPDMIBASE pBase;
6761 vrc = PDMR3QueryLun(VMR3GetVM(pUVM), "usb-ohci", 0, 0, &pBase);
6762 if (RT_SUCCESS(vrc))
6763 {
6764 fHasUSBController = true;
6765 detachAllUSBDevices(false /* aDone */);
6766 }
6767 }
6768
6769 /* Now we've got to destroy the VM as well. (mpVM is not valid beyond
6770 * this point). We leave the lock before calling VMR3Destroy() because
6771 * it will result into calling destructors of drivers associated with
6772 * Console children which may in turn try to lock Console (e.g. by
6773 * instantiating SafeVMPtr to access mpVM). It's safe here because
6774 * mVMDestroying is set which should prevent any activity. */
6775
6776 /* Set mpUVM to NULL early just in case if some old code is not using
6777 * addVMCaller()/releaseVMCaller(). (We have our own ref on pUVM.) */
6778 VMR3ReleaseUVM(mpUVM);
6779 mpUVM = NULL;
6780
6781 LogFlowThisFunc(("Destroying the VM...\n"));
6782
6783 alock.leave();
6784
6785 vrc = VMR3Destroy(VMR3GetVM(pUVM));
6786
6787 /* take the lock again */
6788 alock.enter();
6789
6790 /* advance percent count */
6791 if (aProgress)
6792 aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
6793
6794 if (RT_SUCCESS(vrc))
6795 {
6796 LogFlowThisFunc(("Machine has been destroyed (mMachineState=%d)\n",
6797 mMachineState));
6798 /* Note: the Console-level machine state change happens on the
6799 * VMSTATE_TERMINATE state change in vmstateChangeCallback(). If
6800 * powerDown() is called from EMT (i.e. from vmstateChangeCallback()
6801 * on receiving VM-initiated VMSTATE_OFF), VMSTATE_TERMINATE hasn't
6802 * occurred yet. This is okay, because mMachineState is already
6803 * Stopping in this case, so any other attempt to call PowerDown()
6804 * will be rejected. */
6805 }
6806 else
6807 {
6808 /* bad bad bad, but what to do? (Give Console our UVM ref.) */
6809 mpUVM = pUVM;
6810 pUVM = NULL;
6811 rc = setError(VBOX_E_VM_ERROR,
6812 tr("Could not destroy the machine. (Error: %Rrc)"),
6813 vrc);
6814 }
6815
6816 /* Complete the detaching of the USB devices. */
6817 if (fHasUSBController)
6818 detachAllUSBDevices(true /* aDone */);
6819
6820 /* advance percent count */
6821 if (aProgress)
6822 aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
6823 }
6824 else
6825 {
6826 rc = setError(VBOX_E_VM_ERROR,
6827 tr("Could not power off the machine. (Error: %Rrc)"),
6828 vrc);
6829 }
6830
6831 /*
6832 * Finished with the destruction.
6833 *
6834 * Note that if something impossible happened and we've failed to destroy
6835 * the VM, mVMDestroying will remain true and mMachineState will be
6836 * something like Stopping, so most Console methods will return an error
6837 * to the caller.
6838 */
6839 if (mpUVM != NULL)
6840 VMR3ReleaseUVM(pUVM);
6841 else
6842 mVMDestroying = false;
6843
6844 if (SUCCEEDED(rc))
6845 mCallbackData.clear();
6846
6847 LogFlowThisFuncLeave();
6848 return rc;
6849}
6850
6851/**
6852 * @note Locks this object for writing.
6853 */
6854HRESULT Console::setMachineState(MachineState_T aMachineState,
6855 bool aUpdateServer /* = true */)
6856{
6857 AutoCaller autoCaller(this);
6858 AssertComRCReturnRC(autoCaller.rc());
6859
6860 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6861
6862 HRESULT rc = S_OK;
6863
6864 if (mMachineState != aMachineState)
6865 {
6866 LogThisFunc(("machineState=%s -> %s aUpdateServer=%RTbool\n",
6867 Global::stringifyMachineState(mMachineState), Global::stringifyMachineState(aMachineState), aUpdateServer));
6868 mMachineState = aMachineState;
6869
6870 /// @todo (dmik)
6871 // possibly, we need to redo onStateChange() using the dedicated
6872 // Event thread, like it is done in VirtualBox. This will make it
6873 // much safer (no deadlocks possible if someone tries to use the
6874 // console from the callback), however, listeners will lose the
6875 // ability to synchronously react to state changes (is it really
6876 // necessary??)
6877 LogFlowThisFunc(("Doing onStateChange()...\n"));
6878 onStateChange(aMachineState);
6879 LogFlowThisFunc(("Done onStateChange()\n"));
6880
6881 if (aUpdateServer)
6882 {
6883 /* Server notification MUST be done from under the lock; otherwise
6884 * the machine state here and on the server might go out of sync
6885 * which can lead to various unexpected results (like the machine
6886 * state being >= MachineState_Running on the server, while the
6887 * session state is already SessionState_Unlocked at the same time
6888 * there).
6889 *
6890 * Cross-lock conditions should be carefully watched out: calling
6891 * UpdateState we will require Machine and SessionMachine locks
6892 * (remember that here we're holding the Console lock here, and also
6893 * all locks that have been entered by the thread before calling
6894 * this method).
6895 */
6896 LogFlowThisFunc(("Doing mControl->UpdateState()...\n"));
6897 rc = mControl->UpdateState(aMachineState);
6898 LogFlowThisFunc(("mControl->UpdateState()=%Rhrc\n", rc));
6899 }
6900 }
6901
6902 return rc;
6903}
6904
6905/**
6906 * Searches for a shared folder with the given logical name
6907 * in the collection of shared folders.
6908 *
6909 * @param aName logical name of the shared folder
6910 * @param aSharedFolder where to return the found object
6911 * @param aSetError whether to set the error info if the folder is
6912 * not found
6913 * @return
6914 * S_OK when found or E_INVALIDARG when not found
6915 *
6916 * @note The caller must lock this object for writing.
6917 */
6918HRESULT Console::findSharedFolder(const Utf8Str &strName,
6919 ComObjPtr<SharedFolder> &aSharedFolder,
6920 bool aSetError /* = false */)
6921{
6922 /* sanity check */
6923 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
6924
6925 SharedFolderMap::const_iterator it = m_mapSharedFolders.find(strName);
6926 if (it != m_mapSharedFolders.end())
6927 {
6928 aSharedFolder = it->second;
6929 return S_OK;
6930 }
6931
6932 if (aSetError)
6933 setError(VBOX_E_FILE_ERROR,
6934 tr("Could not find a shared folder named '%s'."),
6935 strName.c_str());
6936
6937 return VBOX_E_FILE_ERROR;
6938}
6939
6940/**
6941 * Fetches the list of global or machine shared folders from the server.
6942 *
6943 * @param aGlobal true to fetch global folders.
6944 *
6945 * @note The caller must lock this object for writing.
6946 */
6947HRESULT Console::fetchSharedFolders(BOOL aGlobal)
6948{
6949 /* sanity check */
6950 AssertReturn(AutoCaller(this).state() == InInit ||
6951 isWriteLockOnCurrentThread(), E_FAIL);
6952
6953 LogFlowThisFunc(("Entering\n"));
6954
6955 /* Check if we're online and keep it that way. */
6956 SafeVMPtrQuiet ptrVM(this);
6957 AutoVMCallerQuietWeak autoVMCaller(this);
6958 bool const online = ptrVM.isOk()
6959 && m_pVMMDev
6960 && m_pVMMDev->isShFlActive();
6961
6962 HRESULT rc = S_OK;
6963
6964 try
6965 {
6966 if (aGlobal)
6967 {
6968 /// @todo grab & process global folders when they are done
6969 }
6970 else
6971 {
6972 SharedFolderDataMap oldFolders;
6973 if (online)
6974 oldFolders = m_mapMachineSharedFolders;
6975
6976 m_mapMachineSharedFolders.clear();
6977
6978 SafeIfaceArray<ISharedFolder> folders;
6979 rc = mMachine->COMGETTER(SharedFolders)(ComSafeArrayAsOutParam(folders));
6980 if (FAILED(rc)) throw rc;
6981
6982 for (size_t i = 0; i < folders.size(); ++i)
6983 {
6984 ComPtr<ISharedFolder> pSharedFolder = folders[i];
6985
6986 Bstr bstrName;
6987 Bstr bstrHostPath;
6988 BOOL writable;
6989 BOOL autoMount;
6990
6991 rc = pSharedFolder->COMGETTER(Name)(bstrName.asOutParam());
6992 if (FAILED(rc)) throw rc;
6993 Utf8Str strName(bstrName);
6994
6995 rc = pSharedFolder->COMGETTER(HostPath)(bstrHostPath.asOutParam());
6996 if (FAILED(rc)) throw rc;
6997 Utf8Str strHostPath(bstrHostPath);
6998
6999 rc = pSharedFolder->COMGETTER(Writable)(&writable);
7000 if (FAILED(rc)) throw rc;
7001
7002 rc = pSharedFolder->COMGETTER(AutoMount)(&autoMount);
7003 if (FAILED(rc)) throw rc;
7004
7005 m_mapMachineSharedFolders.insert(std::make_pair(strName,
7006 SharedFolderData(strHostPath, writable, autoMount)));
7007
7008 /* send changes to HGCM if the VM is running */
7009 if (online)
7010 {
7011 SharedFolderDataMap::iterator it = oldFolders.find(strName);
7012 if ( it == oldFolders.end()
7013 || it->second.m_strHostPath != strHostPath)
7014 {
7015 /* a new machine folder is added or
7016 * the existing machine folder is changed */
7017 if (m_mapSharedFolders.find(strName) != m_mapSharedFolders.end())
7018 ; /* the console folder exists, nothing to do */
7019 else
7020 {
7021 /* remove the old machine folder (when changed)
7022 * or the global folder if any (when new) */
7023 if ( it != oldFolders.end()
7024 || m_mapGlobalSharedFolders.find(strName) != m_mapGlobalSharedFolders.end()
7025 )
7026 {
7027 rc = removeSharedFolder(strName);
7028 if (FAILED(rc)) throw rc;
7029 }
7030
7031 /* create the new machine folder */
7032 rc = createSharedFolder(strName,
7033 SharedFolderData(strHostPath,
7034 writable,
7035 autoMount));
7036 if (FAILED(rc)) throw rc;
7037 }
7038 }
7039 /* forget the processed (or identical) folder */
7040 if (it != oldFolders.end())
7041 oldFolders.erase(it);
7042 }
7043 }
7044
7045 /* process outdated (removed) folders */
7046 if (online)
7047 {
7048 for (SharedFolderDataMap::const_iterator it = oldFolders.begin();
7049 it != oldFolders.end(); ++ it)
7050 {
7051 if (m_mapSharedFolders.find(it->first) != m_mapSharedFolders.end())
7052 ; /* the console folder exists, nothing to do */
7053 else
7054 {
7055 /* remove the outdated machine folder */
7056 rc = removeSharedFolder(it->first);
7057 if (FAILED(rc)) throw rc;
7058
7059 /* create the global folder if there is any */
7060 SharedFolderDataMap::const_iterator git =
7061 m_mapGlobalSharedFolders.find(it->first);
7062 if (git != m_mapGlobalSharedFolders.end())
7063 {
7064 rc = createSharedFolder(git->first, git->second);
7065 if (FAILED(rc)) throw rc;
7066 }
7067 }
7068 }
7069 }
7070 }
7071 }
7072 catch (HRESULT rc2)
7073 {
7074 if (online)
7075 setVMRuntimeErrorCallbackF(ptrVM, this, 0, "BrokenSharedFolder",
7076 N_("Broken shared folder!"));
7077 }
7078
7079 LogFlowThisFunc(("Leaving\n"));
7080
7081 return rc;
7082}
7083
7084/**
7085 * Searches for a shared folder with the given name in the list of machine
7086 * shared folders and then in the list of the global shared folders.
7087 *
7088 * @param aName Name of the folder to search for.
7089 * @param aIt Where to store the pointer to the found folder.
7090 * @return @c true if the folder was found and @c false otherwise.
7091 *
7092 * @note The caller must lock this object for reading.
7093 */
7094bool Console::findOtherSharedFolder(const Utf8Str &strName,
7095 SharedFolderDataMap::const_iterator &aIt)
7096{
7097 /* sanity check */
7098 AssertReturn(isWriteLockOnCurrentThread(), false);
7099
7100 /* first, search machine folders */
7101 aIt = m_mapMachineSharedFolders.find(strName);
7102 if (aIt != m_mapMachineSharedFolders.end())
7103 return true;
7104
7105 /* second, search machine folders */
7106 aIt = m_mapGlobalSharedFolders.find(strName);
7107 if (aIt != m_mapGlobalSharedFolders.end())
7108 return true;
7109
7110 return false;
7111}
7112
7113/**
7114 * Calls the HGCM service to add a shared folder definition.
7115 *
7116 * @param aName Shared folder name.
7117 * @param aHostPath Shared folder path.
7118 *
7119 * @note Must be called from under AutoVMCaller and when mpVM != NULL!
7120 * @note Doesn't lock anything.
7121 */
7122HRESULT Console::createSharedFolder(const Utf8Str &strName, const SharedFolderData &aData)
7123{
7124 ComAssertRet(strName.isNotEmpty(), E_FAIL);
7125 ComAssertRet(aData.m_strHostPath.isNotEmpty(), E_FAIL);
7126
7127 /* sanity checks */
7128 AssertReturn(mpUVM, E_FAIL);
7129 AssertReturn(m_pVMMDev && m_pVMMDev->isShFlActive(), E_FAIL);
7130
7131 VBOXHGCMSVCPARM parms[SHFL_CPARMS_ADD_MAPPING2];
7132 SHFLSTRING *pFolderName, *pMapName;
7133 size_t cbString;
7134
7135 Log(("Adding shared folder '%s' -> '%s'\n", strName.c_str(), aData.m_strHostPath.c_str()));
7136
7137 // check whether the path is valid and exists
7138 char hostPathFull[RTPATH_MAX];
7139 int vrc = RTPathAbsEx(NULL,
7140 aData.m_strHostPath.c_str(),
7141 hostPathFull,
7142 sizeof(hostPathFull));
7143 if (RT_FAILURE(vrc))
7144 return setError(E_INVALIDARG,
7145 tr("Invalid shared folder path: '%s' (%Rrc)"),
7146 aData.m_strHostPath.c_str(), vrc);
7147 if (!RTPathExists(hostPathFull))
7148 return setError(E_INVALIDARG,
7149 tr("Shared folder path '%s' does not exist on the host"),
7150 aData.m_strHostPath.c_str());
7151 /* Check whether the path is full (absolute) */
7152 if (RTPathCompare(aData.m_strHostPath.c_str(), hostPathFull) != 0)
7153 return setError(E_INVALIDARG,
7154 tr("Shared folder path '%s' is not absolute"),
7155 aData.m_strHostPath.c_str());
7156
7157 // now that we know the path is good, give it to HGCM
7158
7159 Bstr bstrName(strName);
7160 Bstr bstrHostPath(aData.m_strHostPath);
7161
7162 cbString = (bstrHostPath.length() + 1) * sizeof(RTUTF16);
7163 if (cbString >= UINT16_MAX)
7164 return setError(E_INVALIDARG, tr("The name is too long"));
7165 pFolderName = (SHFLSTRING*)RTMemAllocZ(sizeof(SHFLSTRING) + cbString);
7166 Assert(pFolderName);
7167 memcpy(pFolderName->String.ucs2, bstrHostPath.raw(), cbString);
7168
7169 pFolderName->u16Size = (uint16_t)cbString;
7170 pFolderName->u16Length = (uint16_t)cbString - sizeof(RTUTF16);
7171
7172 parms[0].type = VBOX_HGCM_SVC_PARM_PTR;
7173 parms[0].u.pointer.addr = pFolderName;
7174 parms[0].u.pointer.size = sizeof(SHFLSTRING) + (uint16_t)cbString;
7175
7176 cbString = (bstrName.length() + 1) * sizeof(RTUTF16);
7177 if (cbString >= UINT16_MAX)
7178 {
7179 RTMemFree(pFolderName);
7180 return setError(E_INVALIDARG, tr("The host path is too long"));
7181 }
7182 pMapName = (SHFLSTRING*)RTMemAllocZ(sizeof(SHFLSTRING) + cbString);
7183 Assert(pMapName);
7184 memcpy(pMapName->String.ucs2, bstrName.raw(), cbString);
7185
7186 pMapName->u16Size = (uint16_t)cbString;
7187 pMapName->u16Length = (uint16_t)cbString - sizeof(RTUTF16);
7188
7189 parms[1].type = VBOX_HGCM_SVC_PARM_PTR;
7190 parms[1].u.pointer.addr = pMapName;
7191 parms[1].u.pointer.size = sizeof(SHFLSTRING) + (uint16_t)cbString;
7192
7193 parms[2].type = VBOX_HGCM_SVC_PARM_32BIT;
7194 parms[2].u.uint32 = aData.m_fWritable;
7195
7196 /*
7197 * Auto-mount flag; is indicated by using the SHFL_CPARMS_ADD_MAPPING2
7198 * define below. This shows the host service that we have supplied
7199 * an additional parameter (auto-mount) and keeps the actual command
7200 * backwards compatible.
7201 */
7202 parms[3].type = VBOX_HGCM_SVC_PARM_32BIT;
7203 parms[3].u.uint32 = aData.m_fAutoMount;
7204
7205 vrc = m_pVMMDev->hgcmHostCall("VBoxSharedFolders",
7206 SHFL_FN_ADD_MAPPING,
7207 SHFL_CPARMS_ADD_MAPPING2, &parms[0]);
7208 RTMemFree(pFolderName);
7209 RTMemFree(pMapName);
7210
7211 if (RT_FAILURE(vrc))
7212 return setError(E_FAIL,
7213 tr("Could not create a shared folder '%s' mapped to '%s' (%Rrc)"),
7214 strName.c_str(), aData.m_strHostPath.c_str(), vrc);
7215
7216 return S_OK;
7217}
7218
7219/**
7220 * Calls the HGCM service to remove the shared folder definition.
7221 *
7222 * @param aName Shared folder name.
7223 *
7224 * @note Must be called from under AutoVMCaller and when mpVM != NULL!
7225 * @note Doesn't lock anything.
7226 */
7227HRESULT Console::removeSharedFolder(const Utf8Str &strName)
7228{
7229 ComAssertRet(strName.isNotEmpty(), E_FAIL);
7230
7231 /* sanity checks */
7232 AssertReturn(mpUVM, E_FAIL);
7233 AssertReturn(m_pVMMDev && m_pVMMDev->isShFlActive(), E_FAIL);
7234
7235 VBOXHGCMSVCPARM parms;
7236 SHFLSTRING *pMapName;
7237 size_t cbString;
7238
7239 Log(("Removing shared folder '%s'\n", strName.c_str()));
7240
7241 Bstr bstrName(strName);
7242 cbString = (bstrName.length() + 1) * sizeof(RTUTF16);
7243 if (cbString >= UINT16_MAX)
7244 return setError(E_INVALIDARG, tr("The name is too long"));
7245 pMapName = (SHFLSTRING *) RTMemAllocZ(sizeof(SHFLSTRING) + cbString);
7246 Assert(pMapName);
7247 memcpy(pMapName->String.ucs2, bstrName.raw(), cbString);
7248
7249 pMapName->u16Size = (uint16_t)cbString;
7250 pMapName->u16Length = (uint16_t)cbString - sizeof(RTUTF16);
7251
7252 parms.type = VBOX_HGCM_SVC_PARM_PTR;
7253 parms.u.pointer.addr = pMapName;
7254 parms.u.pointer.size = sizeof(SHFLSTRING) + (uint16_t)cbString;
7255
7256 int vrc = m_pVMMDev->hgcmHostCall("VBoxSharedFolders",
7257 SHFL_FN_REMOVE_MAPPING,
7258 1, &parms);
7259 RTMemFree(pMapName);
7260 if (RT_FAILURE(vrc))
7261 return setError(E_FAIL,
7262 tr("Could not remove the shared folder '%s' (%Rrc)"),
7263 strName.c_str(), vrc);
7264
7265 return S_OK;
7266}
7267
7268/**
7269 * VM state callback function. Called by the VMM
7270 * using its state machine states.
7271 *
7272 * Primarily used to handle VM initiated power off, suspend and state saving,
7273 * but also for doing termination completed work (VMSTATE_TERMINATE).
7274 *
7275 * In general this function is called in the context of the EMT.
7276 *
7277 * @param aVM The VM handle.
7278 * @param aState The new state.
7279 * @param aOldState The old state.
7280 * @param aUser The user argument (pointer to the Console object).
7281 *
7282 * @note Locks the Console object for writing.
7283 */
7284DECLCALLBACK(void) Console::vmstateChangeCallback(PVM aVM,
7285 VMSTATE aState,
7286 VMSTATE aOldState,
7287 void *aUser)
7288{
7289 LogFlowFunc(("Changing state from %s to %s (aVM=%p)\n",
7290 VMR3GetStateName(aOldState), VMR3GetStateName(aState), aVM));
7291
7292 Console *that = static_cast<Console *>(aUser);
7293 AssertReturnVoid(that);
7294
7295 AutoCaller autoCaller(that);
7296
7297 /* Note that we must let this method proceed even if Console::uninit() has
7298 * been already called. In such case this VMSTATE change is a result of:
7299 * 1) powerDown() called from uninit() itself, or
7300 * 2) VM-(guest-)initiated power off. */
7301 AssertReturnVoid( autoCaller.isOk()
7302 || autoCaller.state() == InUninit);
7303
7304 switch (aState)
7305 {
7306 /*
7307 * The VM has terminated
7308 */
7309 case VMSTATE_OFF:
7310 {
7311 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
7312
7313 if (that->mVMStateChangeCallbackDisabled)
7314 break;
7315
7316 /* Do we still think that it is running? It may happen if this is a
7317 * VM-(guest-)initiated shutdown/poweroff.
7318 */
7319 if ( that->mMachineState != MachineState_Stopping
7320 && that->mMachineState != MachineState_Saving
7321 && that->mMachineState != MachineState_Restoring
7322 && that->mMachineState != MachineState_TeleportingIn
7323 && that->mMachineState != MachineState_FaultTolerantSyncing
7324 && that->mMachineState != MachineState_TeleportingPausedVM
7325 && !that->mVMIsAlreadyPoweringOff
7326 )
7327 {
7328 LogFlowFunc(("VM has powered itself off but Console still thinks it is running. Notifying.\n"));
7329
7330 /* prevent powerDown() from calling VMR3PowerOff() again */
7331 Assert(that->mVMPoweredOff == false);
7332 that->mVMPoweredOff = true;
7333
7334 /*
7335 * request a progress object from the server
7336 * (this will set the machine state to Stopping on the server
7337 * to block others from accessing this machine)
7338 */
7339 ComPtr<IProgress> pProgress;
7340 HRESULT rc = that->mControl->BeginPoweringDown(pProgress.asOutParam());
7341 AssertComRC(rc);
7342
7343 /* sync the state with the server */
7344 that->setMachineStateLocally(MachineState_Stopping);
7345
7346 /* Setup task object and thread to carry out the operation
7347 * asynchronously (if we call powerDown() right here but there
7348 * is one or more mpVM callers (added with addVMCaller()) we'll
7349 * deadlock).
7350 */
7351 std::auto_ptr<VMPowerDownTask> task(new VMPowerDownTask(that,
7352 pProgress));
7353
7354 /* If creating a task failed, this can currently mean one of
7355 * two: either Console::uninit() has been called just a ms
7356 * before (so a powerDown() call is already on the way), or
7357 * powerDown() itself is being already executed. Just do
7358 * nothing.
7359 */
7360 if (!task->isOk())
7361 {
7362 LogFlowFunc(("Console is already being uninitialized.\n"));
7363 break;
7364 }
7365
7366 int vrc = RTThreadCreate(NULL, Console::powerDownThread,
7367 (void *) task.get(), 0,
7368 RTTHREADTYPE_MAIN_WORKER, 0,
7369 "VMPowerDown");
7370 AssertMsgRCBreak(vrc, ("Could not create VMPowerDown thread (%Rrc)\n", vrc));
7371
7372 /* task is now owned by powerDownThread(), so release it */
7373 task.release();
7374 }
7375 break;
7376 }
7377
7378 /* The VM has been completely destroyed.
7379 *
7380 * Note: This state change can happen at two points:
7381 * 1) At the end of VMR3Destroy() if it was not called from EMT.
7382 * 2) At the end of vmR3EmulationThread if VMR3Destroy() was
7383 * called by EMT.
7384 */
7385 case VMSTATE_TERMINATED:
7386 {
7387 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
7388
7389 if (that->mVMStateChangeCallbackDisabled)
7390 break;
7391
7392 /* Terminate host interface networking. If aVM is NULL, we've been
7393 * manually called from powerUpThread() either before calling
7394 * VMR3Create() or after VMR3Create() failed, so no need to touch
7395 * networking.
7396 */
7397 if (aVM)
7398 that->powerDownHostInterfaces();
7399
7400 /* From now on the machine is officially powered down or remains in
7401 * the Saved state.
7402 */
7403 switch (that->mMachineState)
7404 {
7405 default:
7406 AssertFailed();
7407 /* fall through */
7408 case MachineState_Stopping:
7409 /* successfully powered down */
7410 that->setMachineState(MachineState_PoweredOff);
7411 break;
7412 case MachineState_Saving:
7413 /* successfully saved */
7414 that->setMachineState(MachineState_Saved);
7415 break;
7416 case MachineState_Starting:
7417 /* failed to start, but be patient: set back to PoweredOff
7418 * (for similarity with the below) */
7419 that->setMachineState(MachineState_PoweredOff);
7420 break;
7421 case MachineState_Restoring:
7422 /* failed to load the saved state file, but be patient: set
7423 * back to Saved (to preserve the saved state file) */
7424 that->setMachineState(MachineState_Saved);
7425 break;
7426 case MachineState_TeleportingIn:
7427 /* Teleportation failed or was canceled. Back to powered off. */
7428 that->setMachineState(MachineState_PoweredOff);
7429 break;
7430 case MachineState_TeleportingPausedVM:
7431 /* Successfully teleported the VM. */
7432 that->setMachineState(MachineState_Teleported);
7433 break;
7434 case MachineState_FaultTolerantSyncing:
7435 /* Fault tolerant sync failed or was canceled. Back to powered off. */
7436 that->setMachineState(MachineState_PoweredOff);
7437 break;
7438 }
7439 break;
7440 }
7441
7442 case VMSTATE_RESETTING:
7443 {
7444#ifdef VBOX_WITH_GUEST_PROPS
7445 /* Do not take any read/write locks here! */
7446 that->guestPropertiesHandleVMReset();
7447#endif
7448 break;
7449 }
7450
7451 case VMSTATE_SUSPENDED:
7452 {
7453 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
7454
7455 if (that->mVMStateChangeCallbackDisabled)
7456 break;
7457
7458 switch (that->mMachineState)
7459 {
7460 case MachineState_Teleporting:
7461 that->setMachineState(MachineState_TeleportingPausedVM);
7462 break;
7463
7464 case MachineState_LiveSnapshotting:
7465 that->setMachineState(MachineState_Saving);
7466 break;
7467
7468 case MachineState_TeleportingPausedVM:
7469 case MachineState_Saving:
7470 case MachineState_Restoring:
7471 case MachineState_Stopping:
7472 case MachineState_TeleportingIn:
7473 case MachineState_FaultTolerantSyncing:
7474 /* The worker thread handles the transition. */
7475 break;
7476
7477 default:
7478 AssertMsgFailed(("%s\n", Global::stringifyMachineState(that->mMachineState)));
7479 case MachineState_Running:
7480 that->setMachineState(MachineState_Paused);
7481 break;
7482
7483 case MachineState_Paused:
7484 /* Nothing to do. */
7485 break;
7486 }
7487 break;
7488 }
7489
7490 case VMSTATE_SUSPENDED_LS:
7491 case VMSTATE_SUSPENDED_EXT_LS:
7492 {
7493 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
7494 if (that->mVMStateChangeCallbackDisabled)
7495 break;
7496 switch (that->mMachineState)
7497 {
7498 case MachineState_Teleporting:
7499 that->setMachineState(MachineState_TeleportingPausedVM);
7500 break;
7501
7502 case MachineState_LiveSnapshotting:
7503 that->setMachineState(MachineState_Saving);
7504 break;
7505
7506 case MachineState_TeleportingPausedVM:
7507 case MachineState_Saving:
7508 /* ignore */
7509 break;
7510
7511 default:
7512 AssertMsgFailed(("%s/%s -> %s\n", Global::stringifyMachineState(that->mMachineState), VMR3GetStateName(aOldState), VMR3GetStateName(aState) ));
7513 that->setMachineState(MachineState_Paused);
7514 break;
7515 }
7516 break;
7517 }
7518
7519 case VMSTATE_RUNNING:
7520 {
7521 if ( aOldState == VMSTATE_POWERING_ON
7522 || aOldState == VMSTATE_RESUMING
7523 || aOldState == VMSTATE_RUNNING_FT)
7524 {
7525 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
7526
7527 if (that->mVMStateChangeCallbackDisabled)
7528 break;
7529
7530 Assert( ( ( that->mMachineState == MachineState_Starting
7531 || that->mMachineState == MachineState_Paused)
7532 && aOldState == VMSTATE_POWERING_ON)
7533 || ( ( that->mMachineState == MachineState_Restoring
7534 || that->mMachineState == MachineState_TeleportingIn
7535 || that->mMachineState == MachineState_Paused
7536 || that->mMachineState == MachineState_Saving
7537 )
7538 && aOldState == VMSTATE_RESUMING)
7539 || ( that->mMachineState == MachineState_FaultTolerantSyncing
7540 && aOldState == VMSTATE_RUNNING_FT));
7541
7542 that->setMachineState(MachineState_Running);
7543 }
7544
7545 break;
7546 }
7547
7548 case VMSTATE_RUNNING_LS:
7549 AssertMsg( that->mMachineState == MachineState_LiveSnapshotting
7550 || that->mMachineState == MachineState_Teleporting,
7551 ("%s/%s -> %s\n", Global::stringifyMachineState(that->mMachineState), VMR3GetStateName(aOldState), VMR3GetStateName(aState) ));
7552 break;
7553
7554 case VMSTATE_RUNNING_FT:
7555 AssertMsg(that->mMachineState == MachineState_FaultTolerantSyncing,
7556 ("%s/%s -> %s\n", Global::stringifyMachineState(that->mMachineState), VMR3GetStateName(aOldState), VMR3GetStateName(aState) ));
7557 break;
7558
7559 case VMSTATE_FATAL_ERROR:
7560 {
7561 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
7562
7563 if (that->mVMStateChangeCallbackDisabled)
7564 break;
7565
7566 /* Fatal errors are only for running VMs. */
7567 Assert(Global::IsOnline(that->mMachineState));
7568
7569 /* Note! 'Pause' is used here in want of something better. There
7570 * are currently only two places where fatal errors might be
7571 * raised, so it is not worth adding a new externally
7572 * visible state for this yet. */
7573 that->setMachineState(MachineState_Paused);
7574 break;
7575 }
7576
7577 case VMSTATE_GURU_MEDITATION:
7578 {
7579 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
7580
7581 if (that->mVMStateChangeCallbackDisabled)
7582 break;
7583
7584 /* Guru are only for running VMs */
7585 Assert(Global::IsOnline(that->mMachineState));
7586
7587 that->setMachineState(MachineState_Stuck);
7588 break;
7589 }
7590
7591 default: /* shut up gcc */
7592 break;
7593 }
7594}
7595
7596#ifdef VBOX_WITH_USB
7597/**
7598 * Sends a request to VMM to attach the given host device.
7599 * After this method succeeds, the attached device will appear in the
7600 * mUSBDevices collection.
7601 *
7602 * @param aHostDevice device to attach
7603 *
7604 * @note Synchronously calls EMT.
7605 * @note Must be called from under this object's lock.
7606 */
7607HRESULT Console::attachUSBDevice(IUSBDevice *aHostDevice, ULONG aMaskedIfs)
7608{
7609 AssertReturn(aHostDevice, E_FAIL);
7610 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
7611
7612 /* still want a lock object because we need to leave it */
7613 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7614
7615 HRESULT hrc;
7616
7617 /*
7618 * Get the address and the Uuid, and call the pfnCreateProxyDevice roothub
7619 * method in EMT (using usbAttachCallback()).
7620 */
7621 Bstr BstrAddress;
7622 hrc = aHostDevice->COMGETTER(Address)(BstrAddress.asOutParam());
7623 ComAssertComRCRetRC(hrc);
7624
7625 Utf8Str Address(BstrAddress);
7626
7627 Bstr id;
7628 hrc = aHostDevice->COMGETTER(Id)(id.asOutParam());
7629 ComAssertComRCRetRC(hrc);
7630 Guid uuid(id);
7631
7632 BOOL fRemote = FALSE;
7633 hrc = aHostDevice->COMGETTER(Remote)(&fRemote);
7634 ComAssertComRCRetRC(hrc);
7635
7636 /* Get the VM handle. */
7637 SafeVMPtr ptrVM(this);
7638 if (!ptrVM.isOk())
7639 return ptrVM.rc();
7640
7641 LogFlowThisFunc(("Proxying USB device '%s' {%RTuuid}...\n",
7642 Address.c_str(), uuid.raw()));
7643
7644 /* leave the lock before a VMR3* call (EMT will call us back)! */
7645 alock.leave();
7646
7647/** @todo just do everything here and only wrap the PDMR3Usb call. That'll offload some notification stuff from the EMT thread. */
7648 int vrc = VMR3ReqCallWait(ptrVM, VMCPUID_ANY,
7649 (PFNRT)usbAttachCallback, 7,
7650 this, ptrVM.raw(), aHostDevice, uuid.raw(), fRemote, Address.c_str(), aMaskedIfs);
7651
7652 /* restore the lock */
7653 alock.enter();
7654
7655 /* hrc is S_OK here */
7656
7657 if (RT_FAILURE(vrc))
7658 {
7659 LogWarningThisFunc(("Failed to create proxy device for '%s' {%RTuuid} (%Rrc)\n",
7660 Address.c_str(), uuid.raw(), vrc));
7661
7662 switch (vrc)
7663 {
7664 case VERR_VUSB_NO_PORTS:
7665 hrc = setError(E_FAIL,
7666 tr("Failed to attach the USB device. (No available ports on the USB controller)."));
7667 break;
7668 case VERR_VUSB_USBFS_PERMISSION:
7669 hrc = setError(E_FAIL,
7670 tr("Not permitted to open the USB device, check usbfs options"));
7671 break;
7672 default:
7673 hrc = setError(E_FAIL,
7674 tr("Failed to create a proxy device for the USB device. (Error: %Rrc)"),
7675 vrc);
7676 break;
7677 }
7678 }
7679
7680 return hrc;
7681}
7682
7683/**
7684 * USB device attach callback used by AttachUSBDevice().
7685 * Note that AttachUSBDevice() doesn't return until this callback is executed,
7686 * so we don't use AutoCaller and don't care about reference counters of
7687 * interface pointers passed in.
7688 *
7689 * @thread EMT
7690 * @note Locks the console object for writing.
7691 */
7692//static
7693DECLCALLBACK(int)
7694Console::usbAttachCallback(Console *that, PVM pVM, IUSBDevice *aHostDevice, PCRTUUID aUuid, bool aRemote, const char *aAddress, ULONG aMaskedIfs)
7695{
7696 LogFlowFuncEnter();
7697 LogFlowFunc(("that={%p}\n", that));
7698
7699 AssertReturn(that && aUuid, VERR_INVALID_PARAMETER);
7700
7701 void *pvRemoteBackend = NULL;
7702 if (aRemote)
7703 {
7704 RemoteUSBDevice *pRemoteUSBDevice = static_cast<RemoteUSBDevice *>(aHostDevice);
7705 Guid guid(*aUuid);
7706
7707 pvRemoteBackend = that->consoleVRDPServer()->USBBackendRequestPointer(pRemoteUSBDevice->clientId(), &guid);
7708 if (!pvRemoteBackend)
7709 return VERR_INVALID_PARAMETER; /* The clientId is invalid then. */
7710 }
7711
7712 USHORT portVersion = 1;
7713 HRESULT hrc = aHostDevice->COMGETTER(PortVersion)(&portVersion);
7714 AssertComRCReturn(hrc, VERR_GENERAL_FAILURE);
7715 Assert(portVersion == 1 || portVersion == 2);
7716
7717 int vrc = PDMR3USBCreateProxyDevice(pVM, aUuid, aRemote, aAddress, pvRemoteBackend,
7718 portVersion == 1 ? VUSB_STDVER_11 : VUSB_STDVER_20, aMaskedIfs);
7719 if (RT_SUCCESS(vrc))
7720 {
7721 /* Create a OUSBDevice and add it to the device list */
7722 ComObjPtr<OUSBDevice> pUSBDevice;
7723 pUSBDevice.createObject();
7724 hrc = pUSBDevice->init(aHostDevice);
7725 AssertComRC(hrc);
7726
7727 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
7728 that->mUSBDevices.push_back(pUSBDevice);
7729 LogFlowFunc(("Attached device {%RTuuid}\n", pUSBDevice->id().raw()));
7730
7731 /* notify callbacks */
7732 that->onUSBDeviceStateChange(pUSBDevice, true /* aAttached */, NULL);
7733 }
7734
7735 LogFlowFunc(("vrc=%Rrc\n", vrc));
7736 LogFlowFuncLeave();
7737 return vrc;
7738}
7739
7740/**
7741 * Sends a request to VMM to detach the given host device. After this method
7742 * succeeds, the detached device will disappear from the mUSBDevices
7743 * collection.
7744 *
7745 * @param aIt Iterator pointing to the device to detach.
7746 *
7747 * @note Synchronously calls EMT.
7748 * @note Must be called from under this object's lock.
7749 */
7750HRESULT Console::detachUSBDevice(USBDeviceList::iterator &aIt)
7751{
7752 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
7753
7754 /* still want a lock object because we need to leave it */
7755 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7756
7757 /* Get the VM handle. */
7758 SafeVMPtr ptrVM(this);
7759 if (!ptrVM.isOk())
7760 return ptrVM.rc();
7761
7762 /* if the device is attached, then there must at least one USB hub. */
7763 AssertReturn(PDMR3USBHasHub(ptrVM), E_FAIL);
7764
7765 LogFlowThisFunc(("Detaching USB proxy device {%RTuuid}...\n",
7766 (*aIt)->id().raw()));
7767
7768 /* leave the lock before a VMR3* call (EMT will call us back)! */
7769 alock.leave();
7770
7771/** @todo just do everything here and only wrap the PDMR3Usb call. That'll offload some notification stuff from the EMT thread. */
7772 int vrc = VMR3ReqCallWait(ptrVM, VMCPUID_ANY,
7773 (PFNRT)usbDetachCallback, 5,
7774 this, ptrVM.raw(), &aIt, (*aIt)->id().raw());
7775 ComAssertRCRet(vrc, E_FAIL);
7776
7777 return S_OK;
7778}
7779
7780/**
7781 * USB device detach callback used by DetachUSBDevice().
7782 * Note that DetachUSBDevice() doesn't return until this callback is executed,
7783 * so we don't use AutoCaller and don't care about reference counters of
7784 * interface pointers passed in.
7785 *
7786 * @thread EMT
7787 * @note Locks the console object for writing.
7788 */
7789//static
7790DECLCALLBACK(int)
7791Console::usbDetachCallback(Console *that, PVM pVM, USBDeviceList::iterator *aIt, PCRTUUID aUuid)
7792{
7793 LogFlowFuncEnter();
7794 LogFlowFunc(("that={%p}\n", that));
7795
7796 AssertReturn(that && aUuid, VERR_INVALID_PARAMETER);
7797 ComObjPtr<OUSBDevice> pUSBDevice = **aIt;
7798
7799 /*
7800 * If that was a remote device, release the backend pointer.
7801 * The pointer was requested in usbAttachCallback.
7802 */
7803 BOOL fRemote = FALSE;
7804
7805 HRESULT hrc2 = (**aIt)->COMGETTER(Remote)(&fRemote);
7806 if (FAILED(hrc2))
7807 setErrorStatic(hrc2, "GetRemote() failed");
7808
7809 if (fRemote)
7810 {
7811 Guid guid(*aUuid);
7812 that->consoleVRDPServer()->USBBackendReleasePointer(&guid);
7813 }
7814
7815 int vrc = PDMR3USBDetachDevice(pVM, aUuid);
7816
7817 if (RT_SUCCESS(vrc))
7818 {
7819 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
7820
7821 /* Remove the device from the collection */
7822 that->mUSBDevices.erase(*aIt);
7823 LogFlowFunc(("Detached device {%RTuuid}\n", pUSBDevice->id().raw()));
7824
7825 /* notify callbacks */
7826 that->onUSBDeviceStateChange(pUSBDevice, false /* aAttached */, NULL);
7827 }
7828
7829 LogFlowFunc(("vrc=%Rrc\n", vrc));
7830 LogFlowFuncLeave();
7831 return vrc;
7832}
7833#endif /* VBOX_WITH_USB */
7834
7835/* Note: FreeBSD needs this whether netflt is used or not. */
7836#if ((defined(RT_OS_LINUX) && !defined(VBOX_WITH_NETFLT)) || defined(RT_OS_FREEBSD))
7837/**
7838 * Helper function to handle host interface device creation and attachment.
7839 *
7840 * @param networkAdapter the network adapter which attachment should be reset
7841 * @return COM status code
7842 *
7843 * @note The caller must lock this object for writing.
7844 *
7845 * @todo Move this back into the driver!
7846 */
7847HRESULT Console::attachToTapInterface(INetworkAdapter *networkAdapter)
7848{
7849 LogFlowThisFunc(("\n"));
7850 /* sanity check */
7851 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
7852
7853# ifdef VBOX_STRICT
7854 /* paranoia */
7855 NetworkAttachmentType_T attachment;
7856 networkAdapter->COMGETTER(AttachmentType)(&attachment);
7857 Assert(attachment == NetworkAttachmentType_Bridged);
7858# endif /* VBOX_STRICT */
7859
7860 HRESULT rc = S_OK;
7861
7862 ULONG slot = 0;
7863 rc = networkAdapter->COMGETTER(Slot)(&slot);
7864 AssertComRC(rc);
7865
7866# ifdef RT_OS_LINUX
7867 /*
7868 * Allocate a host interface device
7869 */
7870 int rcVBox = RTFileOpen(&maTapFD[slot], "/dev/net/tun",
7871 RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_INHERIT);
7872 if (RT_SUCCESS(rcVBox))
7873 {
7874 /*
7875 * Set/obtain the tap interface.
7876 */
7877 struct ifreq IfReq;
7878 memset(&IfReq, 0, sizeof(IfReq));
7879 /* The name of the TAP interface we are using */
7880 Bstr tapDeviceName;
7881 rc = networkAdapter->COMGETTER(BridgedInterface)(tapDeviceName.asOutParam());
7882 if (FAILED(rc))
7883 tapDeviceName.setNull(); /* Is this necessary? */
7884 if (tapDeviceName.isEmpty())
7885 {
7886 LogRel(("No TAP device name was supplied.\n"));
7887 rc = setError(E_FAIL, tr("No TAP device name was supplied for the host networking interface"));
7888 }
7889
7890 if (SUCCEEDED(rc))
7891 {
7892 /* If we are using a static TAP device then try to open it. */
7893 Utf8Str str(tapDeviceName);
7894 if (str.length() <= sizeof(IfReq.ifr_name))
7895 strcpy(IfReq.ifr_name, str.c_str());
7896 else
7897 memcpy(IfReq.ifr_name, str.c_str(), sizeof(IfReq.ifr_name) - 1); /** @todo bitch about names which are too long... */
7898 IfReq.ifr_flags = IFF_TAP | IFF_NO_PI;
7899 rcVBox = ioctl(maTapFD[slot], TUNSETIFF, &IfReq);
7900 if (rcVBox != 0)
7901 {
7902 LogRel(("Failed to open the host network interface %ls\n", tapDeviceName.raw()));
7903 rc = setError(E_FAIL,
7904 tr("Failed to open the host network interface %ls"),
7905 tapDeviceName.raw());
7906 }
7907 }
7908 if (SUCCEEDED(rc))
7909 {
7910 /*
7911 * Make it pollable.
7912 */
7913 if (fcntl(maTapFD[slot], F_SETFL, O_NONBLOCK) != -1)
7914 {
7915 Log(("attachToTapInterface: %RTfile %ls\n", maTapFD[slot], tapDeviceName.raw()));
7916 /*
7917 * Here is the right place to communicate the TAP file descriptor and
7918 * the host interface name to the server if/when it becomes really
7919 * necessary.
7920 */
7921 maTAPDeviceName[slot] = tapDeviceName;
7922 rcVBox = VINF_SUCCESS;
7923 }
7924 else
7925 {
7926 int iErr = errno;
7927
7928 LogRel(("Configuration error: Failed to configure /dev/net/tun non blocking. Error: %s\n", strerror(iErr)));
7929 rcVBox = VERR_HOSTIF_BLOCKING;
7930 rc = setError(E_FAIL,
7931 tr("could not set up the host networking device for non blocking access: %s"),
7932 strerror(errno));
7933 }
7934 }
7935 }
7936 else
7937 {
7938 LogRel(("Configuration error: Failed to open /dev/net/tun rc=%Rrc\n", rcVBox));
7939 switch (rcVBox)
7940 {
7941 case VERR_ACCESS_DENIED:
7942 /* will be handled by our caller */
7943 rc = rcVBox;
7944 break;
7945 default:
7946 rc = setError(E_FAIL,
7947 tr("Could not set up the host networking device: %Rrc"),
7948 rcVBox);
7949 break;
7950 }
7951 }
7952
7953# elif defined(RT_OS_FREEBSD)
7954 /*
7955 * Set/obtain the tap interface.
7956 */
7957 /* The name of the TAP interface we are using */
7958 Bstr tapDeviceName;
7959 rc = networkAdapter->COMGETTER(BridgedInterface)(tapDeviceName.asOutParam());
7960 if (FAILED(rc))
7961 tapDeviceName.setNull(); /* Is this necessary? */
7962 if (tapDeviceName.isEmpty())
7963 {
7964 LogRel(("No TAP device name was supplied.\n"));
7965 rc = setError(E_FAIL, tr("No TAP device name was supplied for the host networking interface"));
7966 }
7967 char szTapdev[1024] = "/dev/";
7968 /* If we are using a static TAP device then try to open it. */
7969 Utf8Str str(tapDeviceName);
7970 if (str.length() + strlen(szTapdev) <= sizeof(szTapdev))
7971 strcat(szTapdev, str.c_str());
7972 else
7973 memcpy(szTapdev + strlen(szTapdev), str.c_str(),
7974 sizeof(szTapdev) - strlen(szTapdev) - 1); /** @todo bitch about names which are too long... */
7975 int rcVBox = RTFileOpen(&maTapFD[slot], szTapdev,
7976 RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_INHERIT | RTFILE_O_NON_BLOCK);
7977
7978 if (RT_SUCCESS(rcVBox))
7979 maTAPDeviceName[slot] = tapDeviceName;
7980 else
7981 {
7982 switch (rcVBox)
7983 {
7984 case VERR_ACCESS_DENIED:
7985 /* will be handled by our caller */
7986 rc = rcVBox;
7987 break;
7988 default:
7989 rc = setError(E_FAIL,
7990 tr("Failed to open the host network interface %ls"),
7991 tapDeviceName.raw());
7992 break;
7993 }
7994 }
7995# else
7996# error "huh?"
7997# endif
7998 /* in case of failure, cleanup. */
7999 if (RT_FAILURE(rcVBox) && SUCCEEDED(rc))
8000 {
8001 LogRel(("General failure attaching to host interface\n"));
8002 rc = setError(E_FAIL,
8003 tr("General failure attaching to host interface"));
8004 }
8005 LogFlowThisFunc(("rc=%d\n", rc));
8006 return rc;
8007}
8008
8009
8010/**
8011 * Helper function to handle detachment from a host interface
8012 *
8013 * @param networkAdapter the network adapter which attachment should be reset
8014 * @return COM status code
8015 *
8016 * @note The caller must lock this object for writing.
8017 *
8018 * @todo Move this back into the driver!
8019 */
8020HRESULT Console::detachFromTapInterface(INetworkAdapter *networkAdapter)
8021{
8022 /* sanity check */
8023 LogFlowThisFunc(("\n"));
8024 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
8025
8026 HRESULT rc = S_OK;
8027# ifdef VBOX_STRICT
8028 /* paranoia */
8029 NetworkAttachmentType_T attachment;
8030 networkAdapter->COMGETTER(AttachmentType)(&attachment);
8031 Assert(attachment == NetworkAttachmentType_Bridged);
8032# endif /* VBOX_STRICT */
8033
8034 ULONG slot = 0;
8035 rc = networkAdapter->COMGETTER(Slot)(&slot);
8036 AssertComRC(rc);
8037
8038 /* is there an open TAP device? */
8039 if (maTapFD[slot] != NIL_RTFILE)
8040 {
8041 /*
8042 * Close the file handle.
8043 */
8044 Bstr tapDeviceName, tapTerminateApplication;
8045 bool isStatic = true;
8046 rc = networkAdapter->COMGETTER(BridgedInterface)(tapDeviceName.asOutParam());
8047 if (FAILED(rc) || tapDeviceName.isEmpty())
8048 {
8049 /* If the name is empty, this is a dynamic TAP device, so close it now,
8050 so that the termination script can remove the interface. Otherwise we still
8051 need the FD to pass to the termination script. */
8052 isStatic = false;
8053 int rcVBox = RTFileClose(maTapFD[slot]);
8054 AssertRC(rcVBox);
8055 maTapFD[slot] = NIL_RTFILE;
8056 }
8057 if (isStatic)
8058 {
8059 /* If we are using a static TAP device, we close it now, after having called the
8060 termination script. */
8061 int rcVBox = RTFileClose(maTapFD[slot]);
8062 AssertRC(rcVBox);
8063 }
8064 /* the TAP device name and handle are no longer valid */
8065 maTapFD[slot] = NIL_RTFILE;
8066 maTAPDeviceName[slot] = "";
8067 }
8068 LogFlowThisFunc(("returning %d\n", rc));
8069 return rc;
8070}
8071#endif /* (RT_OS_LINUX || RT_OS_FREEBSD) && !VBOX_WITH_NETFLT */
8072
8073/**
8074 * Called at power down to terminate host interface networking.
8075 *
8076 * @note The caller must lock this object for writing.
8077 */
8078HRESULT Console::powerDownHostInterfaces()
8079{
8080 LogFlowThisFunc(("\n"));
8081
8082 /* sanity check */
8083 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
8084
8085 /*
8086 * host interface termination handling
8087 */
8088 HRESULT rc;
8089 for (ULONG slot = 0; slot < SchemaDefs::NetworkAdapterCount; slot ++)
8090 {
8091 ComPtr<INetworkAdapter> pNetworkAdapter;
8092 rc = mMachine->GetNetworkAdapter(slot, pNetworkAdapter.asOutParam());
8093 if (FAILED(rc)) break;
8094
8095 BOOL enabled = FALSE;
8096 pNetworkAdapter->COMGETTER(Enabled)(&enabled);
8097 if (!enabled)
8098 continue;
8099
8100 NetworkAttachmentType_T attachment;
8101 pNetworkAdapter->COMGETTER(AttachmentType)(&attachment);
8102 if (attachment == NetworkAttachmentType_Bridged)
8103 {
8104#if ((defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD)) && !defined(VBOX_WITH_NETFLT))
8105 HRESULT rc2 = detachFromTapInterface(pNetworkAdapter);
8106 if (FAILED(rc2) && SUCCEEDED(rc))
8107 rc = rc2;
8108#endif /* (RT_OS_LINUX || RT_OS_FREEBSD) && !VBOX_WITH_NETFLT */
8109 }
8110 }
8111
8112 return rc;
8113}
8114
8115
8116/**
8117 * Process callback handler for VMR3LoadFromFile, VMR3LoadFromStream, VMR3Save
8118 * and VMR3Teleport.
8119 *
8120 * @param pVM The VM handle.
8121 * @param uPercent Completion percentage (0-100).
8122 * @param pvUser Pointer to an IProgress instance.
8123 * @return VINF_SUCCESS.
8124 */
8125/*static*/
8126DECLCALLBACK(int) Console::stateProgressCallback(PVM pVM, unsigned uPercent, void *pvUser)
8127{
8128 IProgress *pProgress = static_cast<IProgress *>(pvUser);
8129
8130 /* update the progress object */
8131 if (pProgress)
8132 pProgress->SetCurrentOperationProgress(uPercent);
8133
8134 return VINF_SUCCESS;
8135}
8136
8137/**
8138 * @copydoc FNVMATERROR
8139 *
8140 * @remarks Might be some tiny serialization concerns with access to the string
8141 * object here...
8142 */
8143/*static*/ DECLCALLBACK(void)
8144Console::genericVMSetErrorCallback(PVM pVM, void *pvUser, int rc, RT_SRC_POS_DECL,
8145 const char *pszErrorFmt, va_list va)
8146{
8147 Utf8Str *pErrorText = (Utf8Str *)pvUser;
8148 AssertPtr(pErrorText);
8149
8150 /* We ignore RT_SRC_POS_DECL arguments to avoid confusion of end-users. */
8151 va_list va2;
8152 va_copy(va2, va);
8153
8154 /* Append to any the existing error message. */
8155 if (pErrorText->length())
8156 *pErrorText = Utf8StrFmt("%s.\n%N (%Rrc)", pErrorText->c_str(),
8157 pszErrorFmt, &va2, rc, rc);
8158 else
8159 *pErrorText = Utf8StrFmt("%N (%Rrc)", pszErrorFmt, &va2, rc, rc);
8160
8161 va_end(va2);
8162}
8163
8164/**
8165 * VM runtime error callback function.
8166 * See VMSetRuntimeError for the detailed description of parameters.
8167 *
8168 * @param pVM The VM handle.
8169 * @param pvUser The user argument.
8170 * @param fFlags The action flags. See VMSETRTERR_FLAGS_*.
8171 * @param pszErrorId Error ID string.
8172 * @param pszFormat Error message format string.
8173 * @param va Error message arguments.
8174 * @thread EMT.
8175 */
8176/* static */ DECLCALLBACK(void)
8177Console::setVMRuntimeErrorCallback(PVM pVM, void *pvUser, uint32_t fFlags,
8178 const char *pszErrorId,
8179 const char *pszFormat, va_list va)
8180{
8181 bool const fFatal = !!(fFlags & VMSETRTERR_FLAGS_FATAL);
8182 LogFlowFuncEnter();
8183
8184 Console *that = static_cast<Console *>(pvUser);
8185 AssertReturnVoid(that);
8186
8187 Utf8Str message(pszFormat, va);
8188
8189 LogRel(("Console: VM runtime error: fatal=%RTbool, errorID=%s message=\"%s\"\n",
8190 fFatal, pszErrorId, message.c_str()));
8191
8192 that->onRuntimeError(BOOL(fFatal), Bstr(pszErrorId).raw(),
8193 Bstr(message).raw());
8194
8195 LogFlowFuncLeave();
8196}
8197
8198/**
8199 * Captures USB devices that match filters of the VM.
8200 * Called at VM startup.
8201 *
8202 * @param pVM The VM handle.
8203 *
8204 * @note The caller must lock this object for writing.
8205 */
8206HRESULT Console::captureUSBDevices(PVM pVM)
8207{
8208 LogFlowThisFunc(("\n"));
8209
8210 /* sanity check */
8211 ComAssertRet(isWriteLockOnCurrentThread(), E_FAIL);
8212
8213 /* If the machine has an USB controller, ask the USB proxy service to
8214 * capture devices */
8215 PPDMIBASE pBase;
8216 int vrc = PDMR3QueryLun(pVM, "usb-ohci", 0, 0, &pBase);
8217 if (RT_SUCCESS(vrc))
8218 {
8219 /* leave the lock before calling Host in VBoxSVC since Host may call
8220 * us back from under its lock (e.g. onUSBDeviceAttach()) which would
8221 * produce an inter-process dead-lock otherwise. */
8222 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8223 alock.leave();
8224
8225 HRESULT hrc = mControl->AutoCaptureUSBDevices();
8226 ComAssertComRCRetRC(hrc);
8227 }
8228 else if ( vrc == VERR_PDM_DEVICE_NOT_FOUND
8229 || vrc == VERR_PDM_DEVICE_INSTANCE_NOT_FOUND)
8230 vrc = VINF_SUCCESS;
8231 else
8232 AssertRC(vrc);
8233
8234 return RT_SUCCESS(vrc) ? S_OK : E_FAIL;
8235}
8236
8237
8238/**
8239 * Detach all USB device which are attached to the VM for the
8240 * purpose of clean up and such like.
8241 *
8242 * @note The caller must lock this object for writing.
8243 */
8244void Console::detachAllUSBDevices(bool aDone)
8245{
8246 LogFlowThisFunc(("aDone=%RTbool\n", aDone));
8247
8248 /* sanity check */
8249 AssertReturnVoid(isWriteLockOnCurrentThread());
8250
8251 mUSBDevices.clear();
8252
8253 /* leave the lock before calling Host in VBoxSVC since Host may call
8254 * us back from under its lock (e.g. onUSBDeviceAttach()) which would
8255 * produce an inter-process dead-lock otherwise. */
8256 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8257 alock.leave();
8258
8259 mControl->DetachAllUSBDevices(aDone);
8260}
8261
8262/**
8263 * @note Locks this object for writing.
8264 */
8265void Console::processRemoteUSBDevices(uint32_t u32ClientId, VRDEUSBDEVICEDESC *pDevList, uint32_t cbDevList, bool fDescExt)
8266{
8267 LogFlowThisFuncEnter();
8268 LogFlowThisFunc(("u32ClientId = %d, pDevList=%p, cbDevList = %d, fDescExt = %d\n", u32ClientId, pDevList, cbDevList, fDescExt));
8269
8270 AutoCaller autoCaller(this);
8271 if (!autoCaller.isOk())
8272 {
8273 /* Console has been already uninitialized, deny request */
8274 AssertMsgFailed(("Console is already uninitialized\n"));
8275 LogFlowThisFunc(("Console is already uninitialized\n"));
8276 LogFlowThisFuncLeave();
8277 return;
8278 }
8279
8280 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8281
8282 /*
8283 * Mark all existing remote USB devices as dirty.
8284 */
8285 for (RemoteUSBDeviceList::iterator it = mRemoteUSBDevices.begin();
8286 it != mRemoteUSBDevices.end();
8287 ++it)
8288 {
8289 (*it)->dirty(true);
8290 }
8291
8292 /*
8293 * Process the pDevList and add devices those are not already in the mRemoteUSBDevices list.
8294 */
8295 /** @todo (sunlover) REMOTE_USB Strict validation of the pDevList. */
8296 VRDEUSBDEVICEDESC *e = pDevList;
8297
8298 /* The cbDevList condition must be checked first, because the function can
8299 * receive pDevList = NULL and cbDevList = 0 on client disconnect.
8300 */
8301 while (cbDevList >= 2 && e->oNext)
8302 {
8303 /* Sanitize incoming strings in case they aren't valid UTF-8. */
8304 if (e->oManufacturer)
8305 RTStrPurgeEncoding((char *)e + e->oManufacturer);
8306 if (e->oProduct)
8307 RTStrPurgeEncoding((char *)e + e->oProduct);
8308 if (e->oSerialNumber)
8309 RTStrPurgeEncoding((char *)e + e->oSerialNumber);
8310
8311 LogFlowThisFunc(("vendor %04X, product %04X, name = %s\n",
8312 e->idVendor, e->idProduct,
8313 e->oProduct? (char *)e + e->oProduct: ""));
8314
8315 bool fNewDevice = true;
8316
8317 for (RemoteUSBDeviceList::iterator it = mRemoteUSBDevices.begin();
8318 it != mRemoteUSBDevices.end();
8319 ++it)
8320 {
8321 if ((*it)->devId() == e->id
8322 && (*it)->clientId() == u32ClientId)
8323 {
8324 /* The device is already in the list. */
8325 (*it)->dirty(false);
8326 fNewDevice = false;
8327 break;
8328 }
8329 }
8330
8331 if (fNewDevice)
8332 {
8333 LogRel(("Remote USB: ++++ Vendor %04X. Product %04X. Name = [%s].\n",
8334 e->idVendor, e->idProduct, e->oProduct? (char *)e + e->oProduct: ""));
8335
8336 /* Create the device object and add the new device to list. */
8337 ComObjPtr<RemoteUSBDevice> pUSBDevice;
8338 pUSBDevice.createObject();
8339 pUSBDevice->init(u32ClientId, e, fDescExt);
8340
8341 mRemoteUSBDevices.push_back(pUSBDevice);
8342
8343 /* Check if the device is ok for current USB filters. */
8344 BOOL fMatched = FALSE;
8345 ULONG fMaskedIfs = 0;
8346
8347 HRESULT hrc = mControl->RunUSBDeviceFilters(pUSBDevice, &fMatched, &fMaskedIfs);
8348
8349 AssertComRC(hrc);
8350
8351 LogFlowThisFunc(("USB filters return %d %#x\n", fMatched, fMaskedIfs));
8352
8353 if (fMatched)
8354 {
8355 hrc = onUSBDeviceAttach(pUSBDevice, NULL, fMaskedIfs);
8356
8357 /// @todo (r=dmik) warning reporting subsystem
8358
8359 if (hrc == S_OK)
8360 {
8361 LogFlowThisFunc(("Device attached\n"));
8362 pUSBDevice->captured(true);
8363 }
8364 }
8365 }
8366
8367 if (cbDevList < e->oNext)
8368 {
8369 LogWarningThisFunc(("cbDevList %d > oNext %d\n",
8370 cbDevList, e->oNext));
8371 break;
8372 }
8373
8374 cbDevList -= e->oNext;
8375
8376 e = (VRDEUSBDEVICEDESC *)((uint8_t *)e + e->oNext);
8377 }
8378
8379 /*
8380 * Remove dirty devices, that is those which are not reported by the server anymore.
8381 */
8382 for (;;)
8383 {
8384 ComObjPtr<RemoteUSBDevice> pUSBDevice;
8385
8386 RemoteUSBDeviceList::iterator it = mRemoteUSBDevices.begin();
8387 while (it != mRemoteUSBDevices.end())
8388 {
8389 if ((*it)->dirty())
8390 {
8391 pUSBDevice = *it;
8392 break;
8393 }
8394
8395 ++ it;
8396 }
8397
8398 if (!pUSBDevice)
8399 {
8400 break;
8401 }
8402
8403 USHORT vendorId = 0;
8404 pUSBDevice->COMGETTER(VendorId)(&vendorId);
8405
8406 USHORT productId = 0;
8407 pUSBDevice->COMGETTER(ProductId)(&productId);
8408
8409 Bstr product;
8410 pUSBDevice->COMGETTER(Product)(product.asOutParam());
8411
8412 LogRel(("Remote USB: ---- Vendor %04X. Product %04X. Name = [%ls].\n",
8413 vendorId, productId, product.raw()));
8414
8415 /* Detach the device from VM. */
8416 if (pUSBDevice->captured())
8417 {
8418 Bstr uuid;
8419 pUSBDevice->COMGETTER(Id)(uuid.asOutParam());
8420 onUSBDeviceDetach(uuid.raw(), NULL);
8421 }
8422
8423 /* And remove it from the list. */
8424 mRemoteUSBDevices.erase(it);
8425 }
8426
8427 LogFlowThisFuncLeave();
8428}
8429
8430/**
8431 * Progress cancelation callback for fault tolerance VM poweron
8432 */
8433static void faultToleranceProgressCancelCallback(void *pvUser)
8434{
8435 PVM pVM = (PVM)pvUser;
8436
8437 if (pVM)
8438 FTMR3CancelStandby(pVM);
8439}
8440
8441/**
8442 * Thread function which starts the VM (also from saved state) and
8443 * track progress.
8444 *
8445 * @param Thread The thread id.
8446 * @param pvUser Pointer to a VMPowerUpTask structure.
8447 * @return VINF_SUCCESS (ignored).
8448 *
8449 * @note Locks the Console object for writing.
8450 */
8451/*static*/
8452DECLCALLBACK(int) Console::powerUpThread(RTTHREAD Thread, void *pvUser)
8453{
8454 LogFlowFuncEnter();
8455
8456 std::auto_ptr<VMPowerUpTask> task(static_cast<VMPowerUpTask *>(pvUser));
8457 AssertReturn(task.get(), VERR_INVALID_PARAMETER);
8458
8459 AssertReturn(!task->mConsole.isNull(), VERR_INVALID_PARAMETER);
8460 AssertReturn(!task->mProgress.isNull(), VERR_INVALID_PARAMETER);
8461
8462 VirtualBoxBase::initializeComForThread();
8463
8464 HRESULT rc = S_OK;
8465 int vrc = VINF_SUCCESS;
8466
8467 /* Set up a build identifier so that it can be seen from core dumps what
8468 * exact build was used to produce the core. */
8469 static char saBuildID[40];
8470 RTStrPrintf(saBuildID, sizeof(saBuildID), "%s%s%s%s VirtualBox %s r%u %s%s%s%s",
8471 "BU", "IL", "DI", "D", VBOX_VERSION_STRING, RTBldCfgRevision(), "BU", "IL", "DI", "D");
8472
8473 ComObjPtr<Console> pConsole = task->mConsole;
8474
8475 /* Note: no need to use addCaller() because VMPowerUpTask does that */
8476
8477 /* The lock is also used as a signal from the task initiator (which
8478 * releases it only after RTThreadCreate()) that we can start the job */
8479 AutoWriteLock alock(pConsole COMMA_LOCKVAL_SRC_POS);
8480
8481 /* sanity */
8482 Assert(pConsole->mpUVM == NULL);
8483
8484 try
8485 {
8486 // Create the VMM device object, which starts the HGCM thread; do this only
8487 // once for the console, for the pathological case that the same console
8488 // object is used to power up a VM twice. VirtualBox 4.0: we now do that
8489 // here instead of the Console constructor (see Console::init())
8490 if (!pConsole->m_pVMMDev)
8491 {
8492 pConsole->m_pVMMDev = new VMMDev(pConsole);
8493 AssertReturn(pConsole->m_pVMMDev, E_FAIL);
8494 }
8495
8496 /* wait for auto reset ops to complete so that we can successfully lock
8497 * the attached hard disks by calling LockMedia() below */
8498 for (VMPowerUpTask::ProgressList::const_iterator
8499 it = task->hardDiskProgresses.begin();
8500 it != task->hardDiskProgresses.end(); ++ it)
8501 {
8502 HRESULT rc2 = (*it)->WaitForCompletion(-1);
8503 AssertComRC(rc2);
8504 }
8505
8506 /*
8507 * Lock attached media. This method will also check their accessibility.
8508 * If we're a teleporter, we'll have to postpone this action so we can
8509 * migrate between local processes.
8510 *
8511 * Note! The media will be unlocked automatically by
8512 * SessionMachine::setMachineState() when the VM is powered down.
8513 */
8514 if ( !task->mTeleporterEnabled
8515 && task->mEnmFaultToleranceState != FaultToleranceState_Standby)
8516 {
8517 rc = pConsole->mControl->LockMedia();
8518 if (FAILED(rc)) throw rc;
8519 }
8520
8521 /* Create the VRDP server. In case of headless operation, this will
8522 * also create the framebuffer, required at VM creation.
8523 */
8524 ConsoleVRDPServer *server = pConsole->consoleVRDPServer();
8525 Assert(server);
8526
8527 /* Does VRDP server call Console from the other thread?
8528 * Not sure (and can change), so leave the lock just in case.
8529 */
8530 alock.leave();
8531 vrc = server->Launch();
8532 alock.enter();
8533
8534 if (vrc == VERR_NET_ADDRESS_IN_USE)
8535 {
8536 Utf8Str errMsg;
8537 Bstr bstr;
8538 pConsole->mVRDEServer->GetVRDEProperty(Bstr("TCP/Ports").raw(), bstr.asOutParam());
8539 Utf8Str ports = bstr;
8540 errMsg = Utf8StrFmt(tr("VirtualBox Remote Desktop Extension server can't bind to the port: %s"),
8541 ports.c_str());
8542 LogRel(("VRDE: Warning: failed to launch VRDE server (%Rrc): '%s'\n",
8543 vrc, errMsg.c_str()));
8544 }
8545 else if (vrc == VINF_NOT_SUPPORTED)
8546 {
8547 /* This means that the VRDE is not installed. */
8548 LogRel(("VRDE: VirtualBox Remote Desktop Extension is not available.\n"));
8549 }
8550 else if (RT_FAILURE(vrc))
8551 {
8552 /* Fail, if the server is installed but can't start. */
8553 Utf8Str errMsg;
8554 switch (vrc)
8555 {
8556 case VERR_FILE_NOT_FOUND:
8557 {
8558 /* VRDE library file is missing. */
8559 errMsg = Utf8StrFmt(tr("Could not find the VirtualBox Remote Desktop Extension library."));
8560 break;
8561 }
8562 default:
8563 errMsg = Utf8StrFmt(tr("Failed to launch Remote Desktop Extension server (%Rrc)"),
8564 vrc);
8565 }
8566 LogRel(("VRDE: Failed: (%Rrc), error message: '%s'\n",
8567 vrc, errMsg.c_str()));
8568 throw setErrorStatic(E_FAIL, errMsg.c_str());
8569 }
8570
8571 ComPtr<IMachine> pMachine = pConsole->machine();
8572 ULONG cCpus = 1;
8573 pMachine->COMGETTER(CPUCount)(&cCpus);
8574
8575 /*
8576 * Create the VM
8577 */
8578 PVM pVM;
8579 /*
8580 * leave the lock since EMT will call Console. It's safe because
8581 * mMachineState is either Starting or Restoring state here.
8582 */
8583 alock.leave();
8584
8585 vrc = VMR3Create(cCpus,
8586 pConsole->mpVmm2UserMethods,
8587 Console::genericVMSetErrorCallback,
8588 &task->mErrorMsg,
8589 task->mConfigConstructor,
8590 static_cast<Console *>(pConsole),
8591 &pVM);
8592
8593 alock.enter();
8594
8595 /* Enable client connections to the server. */
8596 pConsole->consoleVRDPServer()->EnableConnections();
8597
8598 if (RT_SUCCESS(vrc))
8599 {
8600 do
8601 {
8602 /*
8603 * Register our load/save state file handlers
8604 */
8605 vrc = SSMR3RegisterExternal(pVM, sSSMConsoleUnit, 0 /*iInstance*/, sSSMConsoleVer, 0 /* cbGuess */,
8606 NULL, NULL, NULL,
8607 NULL, saveStateFileExec, NULL,
8608 NULL, loadStateFileExec, NULL,
8609 static_cast<Console *>(pConsole));
8610 AssertRCBreak(vrc);
8611
8612 vrc = static_cast<Console *>(pConsole)->getDisplay()->registerSSM(pVM);
8613 AssertRC(vrc);
8614 if (RT_FAILURE(vrc))
8615 break;
8616
8617 /*
8618 * Synchronize debugger settings
8619 */
8620 MachineDebugger *machineDebugger = pConsole->getMachineDebugger();
8621 if (machineDebugger)
8622 machineDebugger->flushQueuedSettings();
8623
8624 /*
8625 * Shared Folders
8626 */
8627 if (pConsole->m_pVMMDev->isShFlActive())
8628 {
8629 /* Does the code below call Console from the other thread?
8630 * Not sure, so leave the lock just in case. */
8631 alock.leave();
8632
8633 for (SharedFolderDataMap::const_iterator it = task->mSharedFolders.begin();
8634 it != task->mSharedFolders.end();
8635 ++it)
8636 {
8637 const SharedFolderData &d = it->second;
8638 rc = pConsole->createSharedFolder(it->first, d);
8639 if (FAILED(rc))
8640 {
8641 ErrorInfoKeeper eik;
8642 setVMRuntimeErrorCallbackF(pVM, pConsole, 0, "BrokenSharedFolder",
8643 N_("The shared folder '%s' could not be set up: %ls.\n"
8644 "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"),
8645 it->first.c_str(), eik.getText().raw());
8646 }
8647 }
8648 if (FAILED(rc))
8649 rc = S_OK; // do not fail with broken shared folders
8650
8651 /* enter the lock again */
8652 alock.enter();
8653 }
8654
8655 /*
8656 * Capture USB devices.
8657 */
8658 rc = pConsole->captureUSBDevices(pVM);
8659 if (FAILED(rc)) break;
8660
8661 /* leave the lock before a lengthy operation */
8662 alock.leave();
8663
8664 /* Load saved state? */
8665 if (task->mSavedStateFile.length())
8666 {
8667 LogFlowFunc(("Restoring saved state from '%s'...\n",
8668 task->mSavedStateFile.c_str()));
8669
8670 vrc = VMR3LoadFromFile(pVM,
8671 task->mSavedStateFile.c_str(),
8672 Console::stateProgressCallback,
8673 static_cast<IProgress *>(task->mProgress));
8674
8675 if (RT_SUCCESS(vrc))
8676 {
8677 if (task->mStartPaused)
8678 /* done */
8679 pConsole->setMachineState(MachineState_Paused);
8680 else
8681 {
8682 /* Start/Resume the VM execution */
8683#ifdef VBOX_WITH_EXTPACK
8684 vrc = pConsole->mptrExtPackManager->callAllVmPowerOnHooks(pConsole, pVM);
8685#endif
8686 if (RT_SUCCESS(vrc))
8687 vrc = VMR3Resume(pVM);
8688 AssertLogRelRC(vrc);
8689 }
8690 }
8691
8692 /* Power off in case we failed loading or resuming the VM */
8693 if (RT_FAILURE(vrc))
8694 {
8695 int vrc2 = VMR3PowerOff(pVM); AssertLogRelRC(vrc2);
8696#ifdef VBOX_WITH_EXTPACK
8697 pConsole->mptrExtPackManager->callAllVmPowerOffHooks(pConsole, pVM);
8698#endif
8699 }
8700 }
8701 else if (task->mTeleporterEnabled)
8702 {
8703 /* -> ConsoleImplTeleporter.cpp */
8704 bool fPowerOffOnFailure;
8705 rc = pConsole->teleporterTrg(VMR3GetUVM(pVM), pMachine, &task->mErrorMsg, task->mStartPaused,
8706 task->mProgress, &fPowerOffOnFailure);
8707 if (FAILED(rc) && fPowerOffOnFailure)
8708 {
8709 ErrorInfoKeeper eik;
8710 int vrc2 = VMR3PowerOff(pVM); AssertLogRelRC(vrc2);
8711#ifdef VBOX_WITH_EXTPACK
8712 pConsole->mptrExtPackManager->callAllVmPowerOffHooks(pConsole, pVM);
8713#endif
8714 }
8715 }
8716 else if (task->mEnmFaultToleranceState != FaultToleranceState_Inactive)
8717 {
8718 /*
8719 * Get the config.
8720 */
8721 ULONG uPort;
8722 ULONG uInterval;
8723 Bstr bstrAddress, bstrPassword;
8724
8725 rc = pMachine->COMGETTER(FaultTolerancePort)(&uPort);
8726 if (SUCCEEDED(rc))
8727 {
8728 rc = pMachine->COMGETTER(FaultToleranceSyncInterval)(&uInterval);
8729 if (SUCCEEDED(rc))
8730 rc = pMachine->COMGETTER(FaultToleranceAddress)(bstrAddress.asOutParam());
8731 if (SUCCEEDED(rc))
8732 rc = pMachine->COMGETTER(FaultTolerancePassword)(bstrPassword.asOutParam());
8733 }
8734 if (task->mProgress->setCancelCallback(faultToleranceProgressCancelCallback, pVM))
8735 {
8736 if (SUCCEEDED(rc))
8737 {
8738 Utf8Str strAddress(bstrAddress);
8739 const char *pszAddress = strAddress.isEmpty() ? NULL : strAddress.c_str();
8740 Utf8Str strPassword(bstrPassword);
8741 const char *pszPassword = strPassword.isEmpty() ? NULL : strPassword.c_str();
8742
8743 /* Power on the FT enabled VM. */
8744#ifdef VBOX_WITH_EXTPACK
8745 vrc = pConsole->mptrExtPackManager->callAllVmPowerOnHooks(pConsole, pVM);
8746#endif
8747 if (RT_SUCCESS(vrc))
8748 vrc = FTMR3PowerOn(pVM,
8749 task->mEnmFaultToleranceState == FaultToleranceState_Master /* fMaster */,
8750 uInterval,
8751 pszAddress,
8752 uPort,
8753 pszPassword);
8754 AssertLogRelRC(vrc);
8755 }
8756 task->mProgress->setCancelCallback(NULL, NULL);
8757 }
8758 else
8759 rc = E_FAIL;
8760 }
8761 else if (task->mStartPaused)
8762 /* done */
8763 pConsole->setMachineState(MachineState_Paused);
8764 else
8765 {
8766 /* Power on the VM (i.e. start executing) */
8767#ifdef VBOX_WITH_EXTPACK
8768 vrc = pConsole->mptrExtPackManager->callAllVmPowerOnHooks(pConsole, pVM);
8769#endif
8770 if (RT_SUCCESS(vrc))
8771 vrc = VMR3PowerOn(pVM);
8772 AssertLogRelRC(vrc);
8773 }
8774
8775 /* enter the lock again */
8776 alock.enter();
8777 }
8778 while (0);
8779
8780 /* On failure, destroy the VM */
8781 if (FAILED(rc) || RT_FAILURE(vrc))
8782 {
8783 /* preserve existing error info */
8784 ErrorInfoKeeper eik;
8785
8786 /* powerDown() will call VMR3Destroy() and do all necessary
8787 * cleanup (VRDP, USB devices) */
8788 HRESULT rc2 = pConsole->powerDown();
8789 AssertComRC(rc2);
8790 }
8791 else
8792 {
8793 /*
8794 * Deregister the VMSetError callback. This is necessary as the
8795 * pfnVMAtError() function passed to VMR3Create() is supposed to
8796 * be sticky but our error callback isn't.
8797 */
8798 alock.leave();
8799 VMR3AtErrorDeregister(pVM, Console::genericVMSetErrorCallback, &task->mErrorMsg);
8800 /** @todo register another VMSetError callback? */
8801 alock.enter();
8802 }
8803 }
8804 else
8805 {
8806 /*
8807 * If VMR3Create() failed it has released the VM memory.
8808 */
8809 VMR3ReleaseUVM(pConsole->mpUVM);
8810 pConsole->mpUVM = NULL;
8811 }
8812
8813 if (SUCCEEDED(rc) && RT_FAILURE(vrc))
8814 {
8815 /* If VMR3Create() or one of the other calls in this function fail,
8816 * an appropriate error message has been set in task->mErrorMsg.
8817 * However since that happens via a callback, the rc status code in
8818 * this function is not updated.
8819 */
8820 if (!task->mErrorMsg.length())
8821 {
8822 /* If the error message is not set but we've got a failure,
8823 * convert the VBox status code into a meaningful error message.
8824 * This becomes unused once all the sources of errors set the
8825 * appropriate error message themselves.
8826 */
8827 AssertMsgFailed(("Missing error message during powerup for status code %Rrc\n", vrc));
8828 task->mErrorMsg = Utf8StrFmt(tr("Failed to start VM execution (%Rrc)"),
8829 vrc);
8830 }
8831
8832 /* Set the error message as the COM error.
8833 * Progress::notifyComplete() will pick it up later. */
8834 throw setErrorStatic(E_FAIL, task->mErrorMsg.c_str());
8835 }
8836 }
8837 catch (HRESULT aRC) { rc = aRC; }
8838
8839 if ( pConsole->mMachineState == MachineState_Starting
8840 || pConsole->mMachineState == MachineState_Restoring
8841 || pConsole->mMachineState == MachineState_TeleportingIn
8842 )
8843 {
8844 /* We are still in the Starting/Restoring state. This means one of:
8845 *
8846 * 1) we failed before VMR3Create() was called;
8847 * 2) VMR3Create() failed.
8848 *
8849 * In both cases, there is no need to call powerDown(), but we still
8850 * need to go back to the PoweredOff/Saved state. Reuse
8851 * vmstateChangeCallback() for that purpose.
8852 */
8853
8854 /* preserve existing error info */
8855 ErrorInfoKeeper eik;
8856
8857 Assert(pConsole->mpUVM == NULL);
8858 vmstateChangeCallback(NULL, VMSTATE_TERMINATED, VMSTATE_CREATING,
8859 pConsole);
8860 }
8861
8862 /*
8863 * Evaluate the final result. Note that the appropriate mMachineState value
8864 * is already set by vmstateChangeCallback() in all cases.
8865 */
8866
8867 /* leave the lock, don't need it any more */
8868 alock.leave();
8869
8870 if (SUCCEEDED(rc))
8871 {
8872 /* Notify the progress object of the success */
8873 task->mProgress->notifyComplete(S_OK);
8874 }
8875 else
8876 {
8877 /* The progress object will fetch the current error info */
8878 task->mProgress->notifyComplete(rc);
8879 LogRel(("Power up failed (vrc=%Rrc, rc=%Rhrc (%#08X))\n", vrc, rc, rc));
8880 }
8881
8882 /* Notify VBoxSVC and any waiting openRemoteSession progress object. */
8883 pConsole->mControl->EndPowerUp(rc);
8884
8885#if defined(RT_OS_WINDOWS)
8886 /* uninitialize COM */
8887 CoUninitialize();
8888#endif
8889
8890 LogFlowFuncLeave();
8891
8892 return VINF_SUCCESS;
8893}
8894
8895
8896/**
8897 * Reconfigures a medium attachment (part of taking or deleting an online snapshot).
8898 *
8899 * @param pConsole Reference to the console object.
8900 * @param pVM The VM handle.
8901 * @param lInstance The instance of the controller.
8902 * @param pcszDevice The name of the controller type.
8903 * @param enmBus The storage bus type of the controller.
8904 * @param fSetupMerge Whether to set up a medium merge
8905 * @param uMergeSource Merge source image index
8906 * @param uMergeTarget Merge target image index
8907 * @param aMediumAtt The medium attachment.
8908 * @param aMachineState The current machine state.
8909 * @param phrc Where to store com error - only valid if we return VERR_GENERAL_FAILURE.
8910 * @return VBox status code.
8911 */
8912/* static */
8913DECLCALLBACK(int) Console::reconfigureMediumAttachment(Console *pConsole,
8914 PVM pVM,
8915 const char *pcszDevice,
8916 unsigned uInstance,
8917 StorageBus_T enmBus,
8918 bool fUseHostIOCache,
8919 bool fBuiltinIoCache,
8920 bool fSetupMerge,
8921 unsigned uMergeSource,
8922 unsigned uMergeTarget,
8923 IMediumAttachment *aMediumAtt,
8924 MachineState_T aMachineState,
8925 HRESULT *phrc)
8926{
8927 LogFlowFunc(("pVM=%p aMediumAtt=%p phrc=%p\n", pVM, aMediumAtt, phrc));
8928
8929 int rc;
8930 HRESULT hrc;
8931 Bstr bstr;
8932 *phrc = S_OK;
8933#define RC_CHECK() do { if (RT_FAILURE(rc)) { AssertMsgFailed(("rc=%Rrc\n", rc)); return rc; } } while (0)
8934#define H() do { if (FAILED(hrc)) { AssertMsgFailed(("hrc=%Rhrc (%#x)\n", hrc, hrc)); *phrc = hrc; return VERR_GENERAL_FAILURE; } } while (0)
8935
8936 /* Ignore attachments other than hard disks, since at the moment they are
8937 * not subject to snapshotting in general. */
8938 DeviceType_T lType;
8939 hrc = aMediumAtt->COMGETTER(Type)(&lType); H();
8940 if (lType != DeviceType_HardDisk)
8941 return VINF_SUCCESS;
8942
8943 /* Determine the base path for the device instance. */
8944 PCFGMNODE pCtlInst;
8945 pCtlInst = CFGMR3GetChildF(CFGMR3GetRoot(pVM), "Devices/%s/%u/", pcszDevice, uInstance);
8946 AssertReturn(pCtlInst, VERR_INTERNAL_ERROR);
8947
8948 /* Update the device instance configuration. */
8949 rc = pConsole->configMediumAttachment(pCtlInst,
8950 pcszDevice,
8951 uInstance,
8952 enmBus,
8953 fUseHostIOCache,
8954 fBuiltinIoCache,
8955 fSetupMerge,
8956 uMergeSource,
8957 uMergeTarget,
8958 aMediumAtt,
8959 aMachineState,
8960 phrc,
8961 true /* fAttachDetach */,
8962 false /* fForceUnmount */,
8963 false /* fHotplug */,
8964 pVM,
8965 NULL /* paLedDevType */);
8966 /** @todo this dumps everything attached to this device instance, which
8967 * is more than necessary. Dumping the changed LUN would be enough. */
8968 CFGMR3Dump(pCtlInst);
8969 RC_CHECK();
8970
8971#undef RC_CHECK
8972#undef H
8973
8974 LogFlowFunc(("Returns success\n"));
8975 return VINF_SUCCESS;
8976}
8977
8978/**
8979 * Progress cancelation callback employed by Console::fntTakeSnapshotWorker.
8980 */
8981static void takesnapshotProgressCancelCallback(void *pvUser)
8982{
8983 PUVM pUVM = (PUVM)pvUser;
8984 SSMR3Cancel(VMR3GetVM(pUVM));
8985}
8986
8987/**
8988 * Worker thread created by Console::TakeSnapshot.
8989 * @param Thread The current thread (ignored).
8990 * @param pvUser The task.
8991 * @return VINF_SUCCESS (ignored).
8992 */
8993/*static*/
8994DECLCALLBACK(int) Console::fntTakeSnapshotWorker(RTTHREAD Thread, void *pvUser)
8995{
8996 VMTakeSnapshotTask *pTask = (VMTakeSnapshotTask*)pvUser;
8997
8998 // taking a snapshot consists of the following:
8999
9000 // 1) creating a diff image for each virtual hard disk, into which write operations go after
9001 // the snapshot has been created (done in VBoxSVC, in SessionMachine::BeginTakingSnapshot)
9002 // 2) creating a Snapshot object with the state of the machine (hardware + storage,
9003 // done in VBoxSVC, also in SessionMachine::BeginTakingSnapshot)
9004 // 3) saving the state of the virtual machine (here, in the VM process, if the machine is online)
9005
9006 Console *that = pTask->mConsole;
9007 bool fBeganTakingSnapshot = false;
9008 bool fSuspenededBySave = false;
9009
9010 AutoCaller autoCaller(that);
9011 if (FAILED(autoCaller.rc()))
9012 {
9013 that->mptrCancelableProgress.setNull();
9014 return autoCaller.rc();
9015 }
9016
9017 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
9018
9019 HRESULT rc = S_OK;
9020
9021 try
9022 {
9023 /* STEP 1 + 2:
9024 * request creating the diff images on the server and create the snapshot object
9025 * (this will set the machine state to Saving on the server to block
9026 * others from accessing this machine)
9027 */
9028 rc = that->mControl->BeginTakingSnapshot(that,
9029 pTask->bstrName.raw(),
9030 pTask->bstrDescription.raw(),
9031 pTask->mProgress,
9032 pTask->fTakingSnapshotOnline,
9033 pTask->bstrSavedStateFile.asOutParam());
9034 if (FAILED(rc))
9035 throw rc;
9036
9037 fBeganTakingSnapshot = true;
9038
9039 /*
9040 * state file is non-null only when the VM is paused
9041 * (i.e. creating a snapshot online)
9042 */
9043 bool f = (!pTask->bstrSavedStateFile.isEmpty() && pTask->fTakingSnapshotOnline)
9044 || ( pTask->bstrSavedStateFile.isEmpty() && !pTask->fTakingSnapshotOnline);
9045 if (!f)
9046 throw setErrorStatic(E_FAIL, "Invalid state of saved state file");
9047
9048 /* sync the state with the server */
9049 if (pTask->lastMachineState == MachineState_Running)
9050 that->setMachineStateLocally(MachineState_LiveSnapshotting);
9051 else
9052 that->setMachineStateLocally(MachineState_Saving);
9053
9054 // STEP 3: save the VM state (if online)
9055 if (pTask->fTakingSnapshotOnline)
9056 {
9057 Utf8Str strSavedStateFile(pTask->bstrSavedStateFile);
9058
9059 SafeVMPtr ptrVM(that);
9060 if (!ptrVM.isOk())
9061 throw ptrVM.rc();
9062
9063 pTask->mProgress->SetNextOperation(Bstr(tr("Saving the machine state")).raw(),
9064 pTask->ulMemSize); // operation weight, same as computed when setting up progress object
9065 pTask->mProgress->setCancelCallback(takesnapshotProgressCancelCallback, ptrVM.rawUVM());
9066
9067 alock.leave();
9068 LogFlowFunc(("VMR3Save...\n"));
9069 int vrc = VMR3Save(ptrVM,
9070 strSavedStateFile.c_str(),
9071 true /*fContinueAfterwards*/,
9072 Console::stateProgressCallback,
9073 static_cast<IProgress *>(pTask->mProgress),
9074 &fSuspenededBySave);
9075 alock.enter();
9076 if (RT_FAILURE(vrc))
9077 throw setErrorStatic(E_FAIL,
9078 tr("Failed to save the machine state to '%s' (%Rrc)"),
9079 strSavedStateFile.c_str(), vrc);
9080
9081 pTask->mProgress->setCancelCallback(NULL, NULL);
9082 if (!pTask->mProgress->notifyPointOfNoReturn())
9083 throw setErrorStatic(E_FAIL, tr("Canceled"));
9084 that->mptrCancelableProgress.setNull();
9085
9086 // STEP 4: reattach hard disks
9087 LogFlowFunc(("Reattaching new differencing hard disks...\n"));
9088
9089 pTask->mProgress->SetNextOperation(Bstr(tr("Reconfiguring medium attachments")).raw(),
9090 1); // operation weight, same as computed when setting up progress object
9091
9092 com::SafeIfaceArray<IMediumAttachment> atts;
9093 rc = that->mMachine->COMGETTER(MediumAttachments)(ComSafeArrayAsOutParam(atts));
9094 if (FAILED(rc))
9095 throw rc;
9096
9097 for (size_t i = 0;
9098 i < atts.size();
9099 ++i)
9100 {
9101 ComPtr<IStorageController> pStorageController;
9102 Bstr controllerName;
9103 ULONG lInstance;
9104 StorageControllerType_T enmController;
9105 StorageBus_T enmBus;
9106 BOOL fUseHostIOCache;
9107
9108 /*
9109 * We can't pass a storage controller object directly
9110 * (g++ complains about not being able to pass non POD types through '...')
9111 * so we have to query needed values here and pass them.
9112 */
9113 rc = atts[i]->COMGETTER(Controller)(controllerName.asOutParam());
9114 if (FAILED(rc))
9115 throw rc;
9116
9117 rc = that->mMachine->GetStorageControllerByName(controllerName.raw(),
9118 pStorageController.asOutParam());
9119 if (FAILED(rc))
9120 throw rc;
9121
9122 rc = pStorageController->COMGETTER(ControllerType)(&enmController);
9123 if (FAILED(rc))
9124 throw rc;
9125 rc = pStorageController->COMGETTER(Instance)(&lInstance);
9126 if (FAILED(rc))
9127 throw rc;
9128 rc = pStorageController->COMGETTER(Bus)(&enmBus);
9129 if (FAILED(rc))
9130 throw rc;
9131 rc = pStorageController->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
9132 if (FAILED(rc))
9133 throw rc;
9134
9135 const char *pcszDevice = Console::convertControllerTypeToDev(enmController);
9136
9137 BOOL fBuiltinIoCache;
9138 rc = that->mMachine->COMGETTER(IoCacheEnabled)(&fBuiltinIoCache);
9139 if (FAILED(rc))
9140 throw rc;
9141
9142 /*
9143 * don't leave the lock since reconfigureMediumAttachment
9144 * isn't going to need the Console lock.
9145 */
9146 vrc = VMR3ReqCallWait(ptrVM,
9147 VMCPUID_ANY,
9148 (PFNRT)reconfigureMediumAttachment,
9149 13,
9150 that,
9151 ptrVM.raw(),
9152 pcszDevice,
9153 lInstance,
9154 enmBus,
9155 fUseHostIOCache,
9156 fBuiltinIoCache,
9157 false /* fSetupMerge */,
9158 0 /* uMergeSource */,
9159 0 /* uMergeTarget */,
9160 atts[i],
9161 that->mMachineState,
9162 &rc);
9163 if (RT_FAILURE(vrc))
9164 throw setErrorStatic(E_FAIL, Console::tr("%Rrc"), vrc);
9165 if (FAILED(rc))
9166 throw rc;
9167 }
9168 }
9169
9170 /*
9171 * finalize the requested snapshot object.
9172 * This will reset the machine state to the state it had right
9173 * before calling mControl->BeginTakingSnapshot().
9174 */
9175 rc = that->mControl->EndTakingSnapshot(TRUE /*aSuccess*/);
9176 // do not throw rc here because we can't call EndTakingSnapshot() twice
9177 LogFlowFunc(("EndTakingSnapshot -> %Rhrc [mMachineState=%s]\n", rc, Global::stringifyMachineState(that->mMachineState)));
9178 }
9179 catch (HRESULT rcThrown)
9180 {
9181 /* preserve existing error info */
9182 ErrorInfoKeeper eik;
9183
9184 if (fBeganTakingSnapshot)
9185 that->mControl->EndTakingSnapshot(FALSE /*aSuccess*/);
9186
9187 rc = rcThrown;
9188 LogFunc(("Caught %Rhrc [mMachineState=%s]\n", rc, Global::stringifyMachineState(that->mMachineState)));
9189 }
9190 Assert(alock.isWriteLockOnCurrentThread());
9191
9192 if (FAILED(rc)) /* Must come before calling setMachineState. */
9193 pTask->mProgress->notifyComplete(rc);
9194
9195 /*
9196 * Fix up the machine state.
9197 *
9198 * For live snapshots we do all the work, for the two other variations we
9199 * just update the local copy.
9200 */
9201 MachineState_T enmMachineState;
9202 that->mMachine->COMGETTER(State)(&enmMachineState);
9203 if ( that->mMachineState == MachineState_LiveSnapshotting
9204 || that->mMachineState == MachineState_Saving)
9205 {
9206
9207 if (!pTask->fTakingSnapshotOnline)
9208 that->setMachineStateLocally(pTask->lastMachineState);
9209 else if (SUCCEEDED(rc))
9210 {
9211 Assert( pTask->lastMachineState == MachineState_Running
9212 || pTask->lastMachineState == MachineState_Paused);
9213 Assert(that->mMachineState == MachineState_Saving);
9214 if (pTask->lastMachineState == MachineState_Running)
9215 {
9216 LogFlowFunc(("VMR3Resume...\n"));
9217 SafeVMPtr ptrVM(that);
9218 alock.leave();
9219 int vrc = VMR3Resume(ptrVM);
9220 alock.enter();
9221 if (RT_FAILURE(vrc))
9222 {
9223 rc = setErrorStatic(VBOX_E_VM_ERROR, tr("Could not resume the machine execution (%Rrc)"), vrc);
9224 pTask->mProgress->notifyComplete(rc);
9225 if (that->mMachineState == MachineState_Saving)
9226 that->setMachineStateLocally(MachineState_Paused);
9227 }
9228 }
9229 else
9230 that->setMachineStateLocally(MachineState_Paused);
9231 }
9232 else
9233 {
9234 /** @todo this could probably be made more generic and reused elsewhere. */
9235 /* paranoid cleanup on for a failed online snapshot. */
9236 VMSTATE enmVMState = VMR3GetStateU(that->mpUVM);
9237 switch (enmVMState)
9238 {
9239 case VMSTATE_RUNNING:
9240 case VMSTATE_RUNNING_LS:
9241 case VMSTATE_DEBUGGING:
9242 case VMSTATE_DEBUGGING_LS:
9243 case VMSTATE_POWERING_OFF:
9244 case VMSTATE_POWERING_OFF_LS:
9245 case VMSTATE_RESETTING:
9246 case VMSTATE_RESETTING_LS:
9247 Assert(!fSuspenededBySave);
9248 that->setMachineState(MachineState_Running);
9249 break;
9250
9251 case VMSTATE_GURU_MEDITATION:
9252 case VMSTATE_GURU_MEDITATION_LS:
9253 that->setMachineState(MachineState_Stuck);
9254 break;
9255
9256 case VMSTATE_FATAL_ERROR:
9257 case VMSTATE_FATAL_ERROR_LS:
9258 if (pTask->lastMachineState == MachineState_Paused)
9259 that->setMachineStateLocally(pTask->lastMachineState);
9260 else
9261 that->setMachineState(MachineState_Paused);
9262 break;
9263
9264 default:
9265 AssertMsgFailed(("%s\n", VMR3GetStateName(enmVMState)));
9266 case VMSTATE_SUSPENDED:
9267 case VMSTATE_SUSPENDED_LS:
9268 case VMSTATE_SUSPENDING:
9269 case VMSTATE_SUSPENDING_LS:
9270 case VMSTATE_SUSPENDING_EXT_LS:
9271 if (fSuspenededBySave)
9272 {
9273 Assert(pTask->lastMachineState == MachineState_Running);
9274 LogFlowFunc(("VMR3Resume (on failure)...\n"));
9275 SafeVMPtr ptrVM(that);
9276 alock.leave();
9277 int vrc = VMR3Resume(ptrVM); AssertLogRelRC(vrc);
9278 alock.enter();
9279 if (RT_FAILURE(vrc))
9280 that->setMachineState(MachineState_Paused);
9281 }
9282 else if (pTask->lastMachineState == MachineState_Paused)
9283 that->setMachineStateLocally(pTask->lastMachineState);
9284 else
9285 that->setMachineState(MachineState_Paused);
9286 break;
9287 }
9288
9289 }
9290 }
9291 /*else: somebody else has change the state... Leave it. */
9292
9293 /* check the remote state to see that we got it right. */
9294 that->mMachine->COMGETTER(State)(&enmMachineState);
9295 AssertLogRelMsg(that->mMachineState == enmMachineState,
9296 ("mMachineState=%s enmMachineState=%s\n", Global::stringifyMachineState(that->mMachineState),
9297 Global::stringifyMachineState(enmMachineState) ));
9298
9299
9300 if (SUCCEEDED(rc)) /* The failure cases are handled above. */
9301 pTask->mProgress->notifyComplete(rc);
9302
9303 delete pTask;
9304
9305 LogFlowFuncLeave();
9306 return VINF_SUCCESS;
9307}
9308
9309/**
9310 * Thread for executing the saved state operation.
9311 *
9312 * @param Thread The thread handle.
9313 * @param pvUser Pointer to a VMSaveTask structure.
9314 * @return VINF_SUCCESS (ignored).
9315 *
9316 * @note Locks the Console object for writing.
9317 */
9318/*static*/
9319DECLCALLBACK(int) Console::saveStateThread(RTTHREAD Thread, void *pvUser)
9320{
9321 LogFlowFuncEnter();
9322
9323 std::auto_ptr<VMSaveTask> task(static_cast<VMSaveTask*>(pvUser));
9324 AssertReturn(task.get(), VERR_INVALID_PARAMETER);
9325
9326 Assert(task->mSavedStateFile.length());
9327 Assert(task->mProgress.isNull());
9328 Assert(!task->mServerProgress.isNull());
9329
9330 const ComObjPtr<Console> &that = task->mConsole;
9331 Utf8Str errMsg;
9332 HRESULT rc = S_OK;
9333
9334 LogFlowFunc(("Saving the state to '%s'...\n", task->mSavedStateFile.c_str()));
9335
9336 bool fSuspenededBySave;
9337 int vrc = VMR3Save(task->mpVM,
9338 task->mSavedStateFile.c_str(),
9339 false, /*fContinueAfterwards*/
9340 Console::stateProgressCallback,
9341 static_cast<IProgress *>(task->mServerProgress),
9342 &fSuspenededBySave);
9343 if (RT_FAILURE(vrc))
9344 {
9345 errMsg = Utf8StrFmt(Console::tr("Failed to save the machine state to '%s' (%Rrc)"),
9346 task->mSavedStateFile.c_str(), vrc);
9347 rc = E_FAIL;
9348 }
9349 Assert(!fSuspenededBySave);
9350
9351 /* lock the console once we're going to access it */
9352 AutoWriteLock thatLock(that COMMA_LOCKVAL_SRC_POS);
9353
9354 /* synchronize the state with the server */
9355 if (SUCCEEDED(rc))
9356 {
9357 /*
9358 * The machine has been successfully saved, so power it down
9359 * (vmstateChangeCallback() will set state to Saved on success).
9360 * Note: we release the task's VM caller, otherwise it will
9361 * deadlock.
9362 */
9363 task->releaseVMCaller();
9364 rc = that->powerDown();
9365 }
9366
9367 /*
9368 * Finalize the requested save state procedure. In case of failure it will
9369 * reset the machine state to the state it had right before calling
9370 * mControl->BeginSavingState(). This must be the last thing because it
9371 * will set the progress to completed, and that means that the frontend
9372 * can immediately uninit the associated console object.
9373 */
9374 that->mControl->EndSavingState(rc, Bstr(errMsg).raw());
9375
9376 LogFlowFuncLeave();
9377 return VINF_SUCCESS;
9378}
9379
9380/**
9381 * Thread for powering down the Console.
9382 *
9383 * @param Thread The thread handle.
9384 * @param pvUser Pointer to the VMTask structure.
9385 * @return VINF_SUCCESS (ignored).
9386 *
9387 * @note Locks the Console object for writing.
9388 */
9389/*static*/
9390DECLCALLBACK(int) Console::powerDownThread(RTTHREAD Thread, void *pvUser)
9391{
9392 LogFlowFuncEnter();
9393
9394 std::auto_ptr<VMPowerDownTask> task(static_cast<VMPowerDownTask *>(pvUser));
9395 AssertReturn(task.get(), VERR_INVALID_PARAMETER);
9396
9397 AssertReturn(task->isOk(), VERR_GENERAL_FAILURE);
9398
9399 Assert(task->mProgress.isNull());
9400
9401 const ComObjPtr<Console> &that = task->mConsole;
9402
9403 /* Note: no need to use addCaller() to protect Console because VMTask does
9404 * that */
9405
9406 /* wait until the method tat started us returns */
9407 AutoWriteLock thatLock(that COMMA_LOCKVAL_SRC_POS);
9408
9409 /* release VM caller to avoid the powerDown() deadlock */
9410 task->releaseVMCaller();
9411
9412 that->powerDown(task->mServerProgress);
9413
9414 /* complete the operation */
9415 that->mControl->EndPoweringDown(S_OK, Bstr().raw());
9416
9417 LogFlowFuncLeave();
9418 return VINF_SUCCESS;
9419}
9420
9421
9422/**
9423 * @interface_method_impl{VMM2USERMETHODS,pfnSaveState}
9424 */
9425/*static*/ DECLCALLBACK(int)
9426Console::vmm2User_SaveState(PCVMM2USERMETHODS pThis, PUVM pUVM)
9427{
9428 Console *pConsole = ((MYVMM2USERMETHODS *)pThis)->pConsole;
9429 NOREF(pUVM);
9430
9431 /*
9432 * For now, just call SaveState. We should probably try notify the GUI so
9433 * it can pop up a progress object and stuff.
9434 */
9435 HRESULT hrc = pConsole->SaveState(NULL);
9436 return SUCCEEDED(hrc) ? VINF_SUCCESS : Global::vboxStatusCodeFromCOM(hrc);
9437}
9438
9439/**
9440 * @interface_method_impl{VMM2USERMETHODS,pfnNotifyEmtInit}
9441 */
9442/*static*/ DECLCALLBACK(void)
9443Console::vmm2User_NotifyEmtInit(PCVMM2USERMETHODS pThis, PUVM pUVM, PUVMCPU pUVCpu)
9444{
9445 NOREF(pThis); NOREF(pUVM); NOREF(pUVCpu);
9446 VirtualBoxBase::initializeComForThread();
9447}
9448
9449/**
9450 * @interface_method_impl{VMM2USERMETHODS,pfnNotifyEmtTerm}
9451 */
9452/*static*/ DECLCALLBACK(void)
9453Console::vmm2User_NotifyEmtTerm(PCVMM2USERMETHODS pThis, PUVM pUVM, PUVMCPU pUVCpu)
9454{
9455 NOREF(pThis); NOREF(pUVM); NOREF(pUVCpu);
9456 VirtualBoxBase::uninitializeComForThread();
9457}
9458
9459/**
9460 * @interface_method_impl{VMM2USERMETHODS,pfnNotifyPdmtInit}
9461 */
9462/*static*/ DECLCALLBACK(void)
9463Console::vmm2User_NotifyPdmtInit(PCVMM2USERMETHODS pThis, PUVM pUVM)
9464{
9465 NOREF(pThis); NOREF(pUVM);
9466 VirtualBoxBase::initializeComForThread();
9467}
9468
9469/**
9470 * @interface_method_impl{VMM2USERMETHODS,pfnNotifyPdmtTerm}
9471 */
9472/*static*/ DECLCALLBACK(void)
9473Console::vmm2User_NotifyPdmtTerm(PCVMM2USERMETHODS pThis, PUVM pUVM)
9474{
9475 NOREF(pThis); NOREF(pUVM);
9476 VirtualBoxBase::uninitializeComForThread();
9477}
9478
9479
9480
9481
9482/**
9483 * The Main status driver instance data.
9484 */
9485typedef struct DRVMAINSTATUS
9486{
9487 /** The LED connectors. */
9488 PDMILEDCONNECTORS ILedConnectors;
9489 /** Pointer to the LED ports interface above us. */
9490 PPDMILEDPORTS pLedPorts;
9491 /** Pointer to the array of LED pointers. */
9492 PPDMLED *papLeds;
9493 /** The unit number corresponding to the first entry in the LED array. */
9494 RTUINT iFirstLUN;
9495 /** The unit number corresponding to the last entry in the LED array.
9496 * (The size of the LED array is iLastLUN - iFirstLUN + 1.) */
9497 RTUINT iLastLUN;
9498 /** Pointer to the driver instance. */
9499 PPDMDRVINS pDrvIns;
9500 /** The Media Notify interface. */
9501 PDMIMEDIANOTIFY IMediaNotify;
9502 /** Map for translating PDM storage controller/LUN information to
9503 * IMediumAttachment references. */
9504 Console::MediumAttachmentMap *pmapMediumAttachments;
9505 /** Device name+instance for mapping */
9506 char *pszDeviceInstance;
9507 /** Pointer to the Console object, for driver triggered activities. */
9508 Console *pConsole;
9509} DRVMAINSTATUS, *PDRVMAINSTATUS;
9510
9511
9512/**
9513 * Notification about a unit which have been changed.
9514 *
9515 * The driver must discard any pointers to data owned by
9516 * the unit and requery it.
9517 *
9518 * @param pInterface Pointer to the interface structure containing the called function pointer.
9519 * @param iLUN The unit number.
9520 */
9521DECLCALLBACK(void) Console::drvStatus_UnitChanged(PPDMILEDCONNECTORS pInterface, unsigned iLUN)
9522{
9523 PDRVMAINSTATUS pData = (PDRVMAINSTATUS)((uintptr_t)pInterface - RT_OFFSETOF(DRVMAINSTATUS, ILedConnectors));
9524 if (iLUN >= pData->iFirstLUN && iLUN <= pData->iLastLUN)
9525 {
9526 PPDMLED pLed;
9527 int rc = pData->pLedPorts->pfnQueryStatusLed(pData->pLedPorts, iLUN, &pLed);
9528 if (RT_FAILURE(rc))
9529 pLed = NULL;
9530 ASMAtomicWritePtr(&pData->papLeds[iLUN - pData->iFirstLUN], pLed);
9531 Log(("drvStatus_UnitChanged: iLUN=%d pLed=%p\n", iLUN, pLed));
9532 }
9533}
9534
9535
9536/**
9537 * Notification about a medium eject.
9538 *
9539 * @returns VBox status.
9540 * @param pInterface Pointer to the interface structure containing the called function pointer.
9541 * @param uLUN The unit number.
9542 */
9543DECLCALLBACK(int) Console::drvStatus_MediumEjected(PPDMIMEDIANOTIFY pInterface, unsigned uLUN)
9544{
9545 PDRVMAINSTATUS pData = (PDRVMAINSTATUS)((uintptr_t)pInterface - RT_OFFSETOF(DRVMAINSTATUS, IMediaNotify));
9546 PPDMDRVINS pDrvIns = pData->pDrvIns;
9547 LogFunc(("uLUN=%d\n", uLUN));
9548 if (pData->pmapMediumAttachments)
9549 {
9550 AutoWriteLock alock(pData->pConsole COMMA_LOCKVAL_SRC_POS);
9551
9552 ComPtr<IMediumAttachment> pMediumAtt;
9553 Utf8Str devicePath = Utf8StrFmt("%s/LUN#%u", pData->pszDeviceInstance, uLUN);
9554 Console::MediumAttachmentMap::const_iterator end = pData->pmapMediumAttachments->end();
9555 Console::MediumAttachmentMap::const_iterator it = pData->pmapMediumAttachments->find(devicePath);
9556 if (it != end)
9557 pMediumAtt = it->second;
9558 Assert(!pMediumAtt.isNull());
9559 if (!pMediumAtt.isNull())
9560 {
9561 IMedium *pMedium;
9562 HRESULT rc = pMediumAtt->COMGETTER(Medium)(&pMedium);
9563 AssertComRC(rc);
9564 BOOL fHostDrive = FALSE;
9565 rc = pMedium->COMGETTER(HostDrive)(&fHostDrive);
9566 AssertComRC(rc);
9567 if (!fHostDrive)
9568 {
9569 alock.release();
9570
9571 ComPtr<IMediumAttachment> pNewMediumAtt;
9572 rc = pData->pConsole->mControl->EjectMedium(pMediumAtt, pNewMediumAtt.asOutParam());
9573 if (SUCCEEDED(rc))
9574 fireMediumChangedEvent(pData->pConsole->mEventSource, pNewMediumAtt);
9575
9576 alock.acquire();
9577 if (pNewMediumAtt != pMediumAtt)
9578 {
9579 pData->pmapMediumAttachments->erase(devicePath);
9580 pData->pmapMediumAttachments->insert(std::make_pair(devicePath, pNewMediumAtt));
9581 }
9582 }
9583 }
9584 }
9585 return VINF_SUCCESS;
9586}
9587
9588
9589/**
9590 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
9591 */
9592DECLCALLBACK(void *) Console::drvStatus_QueryInterface(PPDMIBASE pInterface, const char *pszIID)
9593{
9594 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
9595 PDRVMAINSTATUS pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINSTATUS);
9596 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
9597 PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDCONNECTORS, &pThis->ILedConnectors);
9598 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIANOTIFY, &pThis->IMediaNotify);
9599 return NULL;
9600}
9601
9602
9603/**
9604 * Destruct a status driver instance.
9605 *
9606 * @returns VBox status.
9607 * @param pDrvIns The driver instance data.
9608 */
9609DECLCALLBACK(void) Console::drvStatus_Destruct(PPDMDRVINS pDrvIns)
9610{
9611 PDRVMAINSTATUS pData = PDMINS_2_DATA(pDrvIns, PDRVMAINSTATUS);
9612 LogFlowFunc(("iInstance=%d\n", pDrvIns->iInstance));
9613 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
9614
9615 if (pData->papLeds)
9616 {
9617 unsigned iLed = pData->iLastLUN - pData->iFirstLUN + 1;
9618 while (iLed-- > 0)
9619 ASMAtomicWriteNullPtr(&pData->papLeds[iLed]);
9620 }
9621}
9622
9623
9624/**
9625 * Construct a status driver instance.
9626 *
9627 * @copydoc FNPDMDRVCONSTRUCT
9628 */
9629DECLCALLBACK(int) Console::drvStatus_Construct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
9630{
9631 PDRVMAINSTATUS pData = PDMINS_2_DATA(pDrvIns, PDRVMAINSTATUS);
9632 LogFlowFunc(("iInstance=%d\n", pDrvIns->iInstance));
9633 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
9634
9635 /*
9636 * Validate configuration.
9637 */
9638 if (!CFGMR3AreValuesValid(pCfg, "papLeds\0pmapMediumAttachments\0DeviceInstance\0pConsole\0First\0Last\0"))
9639 return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
9640 AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
9641 ("Configuration error: Not possible to attach anything to this driver!\n"),
9642 VERR_PDM_DRVINS_NO_ATTACH);
9643
9644 /*
9645 * Data.
9646 */
9647 pDrvIns->IBase.pfnQueryInterface = Console::drvStatus_QueryInterface;
9648 pData->ILedConnectors.pfnUnitChanged = Console::drvStatus_UnitChanged;
9649 pData->IMediaNotify.pfnEjected = Console::drvStatus_MediumEjected;
9650 pData->pDrvIns = pDrvIns;
9651 pData->pszDeviceInstance = NULL;
9652
9653 /*
9654 * Read config.
9655 */
9656 int rc = CFGMR3QueryPtr(pCfg, "papLeds", (void **)&pData->papLeds);
9657 if (RT_FAILURE(rc))
9658 {
9659 AssertMsgFailed(("Configuration error: Failed to query the \"papLeds\" value! rc=%Rrc\n", rc));
9660 return rc;
9661 }
9662
9663 rc = CFGMR3QueryPtrDef(pCfg, "pmapMediumAttachments", (void **)&pData->pmapMediumAttachments, NULL);
9664 if (RT_FAILURE(rc))
9665 {
9666 AssertMsgFailed(("Configuration error: Failed to query the \"pmapMediumAttachments\" value! rc=%Rrc\n", rc));
9667 return rc;
9668 }
9669 if (pData->pmapMediumAttachments)
9670 {
9671 rc = CFGMR3QueryStringAlloc(pCfg, "DeviceInstance", &pData->pszDeviceInstance);
9672 if (RT_FAILURE(rc))
9673 {
9674 AssertMsgFailed(("Configuration error: Failed to query the \"DeviceInstance\" value! rc=%Rrc\n", rc));
9675 return rc;
9676 }
9677 rc = CFGMR3QueryPtr(pCfg, "pConsole", (void **)&pData->pConsole);
9678 if (RT_FAILURE(rc))
9679 {
9680 AssertMsgFailed(("Configuration error: Failed to query the \"pConsole\" value! rc=%Rrc\n", rc));
9681 return rc;
9682 }
9683 }
9684
9685 rc = CFGMR3QueryU32(pCfg, "First", &pData->iFirstLUN);
9686 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
9687 pData->iFirstLUN = 0;
9688 else if (RT_FAILURE(rc))
9689 {
9690 AssertMsgFailed(("Configuration error: Failed to query the \"First\" value! rc=%Rrc\n", rc));
9691 return rc;
9692 }
9693
9694 rc = CFGMR3QueryU32(pCfg, "Last", &pData->iLastLUN);
9695 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
9696 pData->iLastLUN = 0;
9697 else if (RT_FAILURE(rc))
9698 {
9699 AssertMsgFailed(("Configuration error: Failed to query the \"Last\" value! rc=%Rrc\n", rc));
9700 return rc;
9701 }
9702 if (pData->iFirstLUN > pData->iLastLUN)
9703 {
9704 AssertMsgFailed(("Configuration error: Invalid unit range %u-%u\n", pData->iFirstLUN, pData->iLastLUN));
9705 return VERR_GENERAL_FAILURE;
9706 }
9707
9708 /*
9709 * Get the ILedPorts interface of the above driver/device and
9710 * query the LEDs we want.
9711 */
9712 pData->pLedPorts = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMILEDPORTS);
9713 AssertMsgReturn(pData->pLedPorts, ("Configuration error: No led ports interface above!\n"),
9714 VERR_PDM_MISSING_INTERFACE_ABOVE);
9715
9716 for (unsigned i = pData->iFirstLUN; i <= pData->iLastLUN; ++i)
9717 Console::drvStatus_UnitChanged(&pData->ILedConnectors, i);
9718
9719 return VINF_SUCCESS;
9720}
9721
9722
9723/**
9724 * Console status driver (LED) registration record.
9725 */
9726const PDMDRVREG Console::DrvStatusReg =
9727{
9728 /* u32Version */
9729 PDM_DRVREG_VERSION,
9730 /* szName */
9731 "MainStatus",
9732 /* szRCMod */
9733 "",
9734 /* szR0Mod */
9735 "",
9736 /* pszDescription */
9737 "Main status driver (Main as in the API).",
9738 /* fFlags */
9739 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
9740 /* fClass. */
9741 PDM_DRVREG_CLASS_STATUS,
9742 /* cMaxInstances */
9743 ~0,
9744 /* cbInstance */
9745 sizeof(DRVMAINSTATUS),
9746 /* pfnConstruct */
9747 Console::drvStatus_Construct,
9748 /* pfnDestruct */
9749 Console::drvStatus_Destruct,
9750 /* pfnRelocate */
9751 NULL,
9752 /* pfnIOCtl */
9753 NULL,
9754 /* pfnPowerOn */
9755 NULL,
9756 /* pfnReset */
9757 NULL,
9758 /* pfnSuspend */
9759 NULL,
9760 /* pfnResume */
9761 NULL,
9762 /* pfnAttach */
9763 NULL,
9764 /* pfnDetach */
9765 NULL,
9766 /* pfnPowerOff */
9767 NULL,
9768 /* pfnSoftReset */
9769 NULL,
9770 /* u32EndVersion */
9771 PDM_DRVREG_VERSION
9772};
9773
9774/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

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