VirtualBox

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

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

ExtPackManager: Drop the drop zone idea.

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

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