VirtualBox

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

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

Main: source re-org.

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

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