VirtualBox

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

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

VMM, Main: PCI passthrough work

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

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