VirtualBox

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

Last change on this file since 36381 was 36381, checked in by vboxsync, 14 years ago

todo

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

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