VirtualBox

source: vbox/trunk/src/VBox/Main/ConsoleImpl.cpp@ 35171

最後變更 在這個檔案從35171是 35171,由 vboxsync 提交於 14 年 前

Main/Console: log the file system of the snapshot folder as well

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

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