VirtualBox

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

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

Console::onNetworkAdapterChange: better error message if a network adapter is not enabled

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 284.0 KB
Line 
1/* $Id: ConsoleImpl.cpp 35421 2011-01-07 10:06:28Z 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 if (RT_SUCCESS(vrc))
3506 {
3507 Assert(pBase);
3508 PPDMINETWORKCONFIG pINetCfg;
3509 pINetCfg = PDMIBASE_QUERY_INTERFACE(pBase, PDMINETWORKCONFIG);
3510 if (pINetCfg)
3511 {
3512 Log(("Console::onNetworkAdapterChange: setting link state to %d\n",
3513 fCableConnected));
3514 vrc = pINetCfg->pfnSetLinkState(pINetCfg,
3515 fCableConnected ? PDMNETWORKLINKSTATE_UP
3516 : PDMNETWORKLINKSTATE_DOWN);
3517 ComAssertRC(vrc);
3518 }
3519 if (RT_SUCCESS(vrc) && changeAdapter)
3520 {
3521 VMSTATE enmVMState = VMR3GetState(mpVM);
3522 if ( enmVMState == VMSTATE_RUNNING /** @todo LiveMigration: Forbid or deal correctly with the _LS variants */
3523 || enmVMState == VMSTATE_SUSPENDED)
3524 {
3525 if (fTraceEnabled && fCableConnected && pINetCfg)
3526 {
3527 vrc = pINetCfg->pfnSetLinkState(pINetCfg, PDMNETWORKLINKSTATE_DOWN);
3528 ComAssertRC(vrc);
3529 }
3530
3531 rc = doNetworkAdapterChange(pszAdapterName, ulInstance, 0, aNetworkAdapter);
3532
3533 if (fTraceEnabled && fCableConnected && pINetCfg)
3534 {
3535 vrc = pINetCfg->pfnSetLinkState(pINetCfg, PDMNETWORKLINKSTATE_UP);
3536 ComAssertRC(vrc);
3537 }
3538 }
3539 }
3540 }
3541 else if (vrc == VERR_PDM_DEVICE_INSTANCE_NOT_FOUND)
3542 {
3543 return setError(E_FAIL,
3544 tr("The network adapter #%u is not enabled"), ulInstance);
3545 }
3546 else
3547 ComAssertRC(vrc);
3548
3549 if (RT_FAILURE(vrc))
3550 rc = E_FAIL;
3551 }
3552 }
3553 }
3554
3555 /* notify console callbacks on success */
3556 if (SUCCEEDED(rc))
3557 fireNetworkAdapterChangedEvent(mEventSource, aNetworkAdapter);
3558
3559 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
3560 return rc;
3561}
3562
3563/**
3564 * Called by IInternalSessionControl::OnNATEngineChange().
3565 *
3566 * @note Locks this object for writing.
3567 */
3568HRESULT Console::onNATRedirectRuleChange(ULONG ulInstance, BOOL aNatRuleRemove,
3569 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort, IN_BSTR aGuestIp, LONG aGuestPort)
3570{
3571 LogFlowThisFunc(("\n"));
3572
3573 AutoCaller autoCaller(this);
3574 AssertComRCReturnRC(autoCaller.rc());
3575
3576 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3577
3578 HRESULT rc = S_OK;
3579 int vrc = VINF_SUCCESS;
3580 PPDMINETWORKNATCONFIG pNetNatCfg = NULL;
3581 /* don't trigger nat engine change if the VM isn't running */
3582 if (mpVM)
3583 {
3584 /* protect mpVM */
3585 AutoVMCaller autoVMCaller(this);
3586 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
3587
3588 ComPtr<INetworkAdapter> pNetworkAdapter;
3589 rc = machine()->GetNetworkAdapter(ulInstance, pNetworkAdapter.asOutParam());
3590 if ( FAILED(rc)
3591 || pNetworkAdapter.isNull())
3592 goto done;
3593
3594 /*
3595 * Find the adapter instance, get the config interface and update
3596 * the link state.
3597 */
3598 NetworkAdapterType_T adapterType;
3599 rc = pNetworkAdapter->COMGETTER(AdapterType)(&adapterType);
3600 AssertComRC(rc);
3601 if (FAILED(rc))
3602 {
3603 rc = E_FAIL;
3604 goto done;
3605 }
3606
3607 const char *pszAdapterName = networkAdapterTypeToName(adapterType);
3608 PPDMIBASE pBase;
3609 vrc = PDMR3QueryLun(mpVM, pszAdapterName, ulInstance, 0, &pBase);
3610 ComAssertRC(vrc);
3611 if (RT_FAILURE(vrc))
3612 {
3613 rc = E_FAIL;
3614 goto done;
3615 }
3616 NetworkAttachmentType_T attachmentType;
3617 vrc = pNetworkAdapter->COMGETTER(AttachmentType)(&attachmentType);
3618
3619 if ( RT_FAILURE(vrc)
3620 || attachmentType != NetworkAttachmentType_NAT)
3621 {
3622 rc = (RT_FAILURE(vrc)) ? E_FAIL: rc;
3623 goto done;
3624 }
3625
3626 /* look down for PDMINETWORKNATCONFIG interface */
3627 while (pBase)
3628 {
3629 if ((pNetNatCfg = (PPDMINETWORKNATCONFIG)pBase->pfnQueryInterface(pBase, PDMINETWORKNATCONFIG_IID)))
3630 break;
3631 PPDMDRVINS drvins = PDMIBASE_2_PDMDRV(pBase);
3632 pBase = drvins->pDownBase;
3633 }
3634 if (!pNetNatCfg)
3635 goto done;
3636 bool fUdp = (aProto == NATProtocol_UDP);
3637 vrc = pNetNatCfg->pfnRedirectRuleCommand(pNetNatCfg, aNatRuleRemove, fUdp,
3638 Utf8Str(aHostIp).c_str(), aHostPort, Utf8Str(aGuestIp).c_str(),
3639 aGuestPort);
3640 if (RT_FAILURE(vrc))
3641 rc = E_FAIL;
3642 }
3643done:
3644 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
3645 return rc;
3646}
3647
3648
3649/**
3650 * Process a network adaptor change.
3651 *
3652 * @returns COM status code.
3653 *
3654 * @param pszDevice The PDM device name.
3655 * @param uInstance The PDM device instance.
3656 * @param uLun The PDM LUN number of the drive.
3657 * @param aNetworkAdapter The network adapter whose attachment needs to be changed
3658 *
3659 * @note Locks this object for writing.
3660 */
3661HRESULT Console::doNetworkAdapterChange(const char *pszDevice,
3662 unsigned uInstance,
3663 unsigned uLun,
3664 INetworkAdapter *aNetworkAdapter)
3665{
3666 LogFlowThisFunc(("pszDevice=%p:{%s} uInstance=%u uLun=%u aNetworkAdapter=%p\n",
3667 pszDevice, pszDevice, uInstance, uLun, aNetworkAdapter));
3668
3669 AutoCaller autoCaller(this);
3670 AssertComRCReturnRC(autoCaller.rc());
3671
3672 /* We will need to release the write lock before calling EMT */
3673 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3674
3675 /* protect mpVM */
3676 AutoVMCaller autoVMCaller(this);
3677 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
3678
3679 /*
3680 * Call worker in EMT, that's faster and safer than doing everything
3681 * using VM3ReqCall. Note that we separate VMR3ReqCall from VMR3ReqWait
3682 * here to make requests from under the lock in order to serialize them.
3683 */
3684 PVMREQ pReq;
3685 int vrc = VMR3ReqCall(mpVM, 0 /*idDstCpu*/, &pReq, 0 /* no wait! */, VMREQFLAGS_VBOX_STATUS,
3686 (PFNRT) Console::changeNetworkAttachment, 5,
3687 this, pszDevice, uInstance, uLun, aNetworkAdapter);
3688
3689 /* leave the lock before waiting for a result (EMT will call us back!) */
3690 alock.leave();
3691
3692 if (vrc == VERR_TIMEOUT || RT_SUCCESS(vrc))
3693 {
3694 vrc = VMR3ReqWait(pReq, RT_INDEFINITE_WAIT);
3695 AssertRC(vrc);
3696 if (RT_SUCCESS(vrc))
3697 vrc = pReq->iStatus;
3698 }
3699 VMR3ReqFree(pReq);
3700
3701 if (RT_SUCCESS(vrc))
3702 {
3703 LogFlowThisFunc(("Returns S_OK\n"));
3704 return S_OK;
3705 }
3706
3707 return setError(E_FAIL,
3708 tr("Could not change the network adaptor attachement type (%Rrc)"),
3709 vrc);
3710}
3711
3712
3713/**
3714 * Performs the Network Adaptor change in EMT.
3715 *
3716 * @returns VBox status code.
3717 *
3718 * @param pThis Pointer to the Console object.
3719 * @param pszDevice The PDM device name.
3720 * @param uInstance The PDM device instance.
3721 * @param uLun The PDM LUN number of the drive.
3722 * @param aNetworkAdapter The network adapter whose attachment needs to be changed
3723 *
3724 * @thread EMT
3725 * @note Locks the Console object for writing.
3726 */
3727DECLCALLBACK(int) Console::changeNetworkAttachment(Console *pThis,
3728 const char *pszDevice,
3729 unsigned uInstance,
3730 unsigned uLun,
3731 INetworkAdapter *aNetworkAdapter)
3732{
3733 LogFlowFunc(("pThis=%p pszDevice=%p:{%s} uInstance=%u uLun=%u aNetworkAdapter=%p\n",
3734 pThis, pszDevice, pszDevice, uInstance, uLun, aNetworkAdapter));
3735
3736 AssertReturn(pThis, VERR_INVALID_PARAMETER);
3737
3738 AssertMsg( ( !strcmp(pszDevice, "pcnet")
3739 || !strcmp(pszDevice, "e1000")
3740 || !strcmp(pszDevice, "virtio-net"))
3741 && (uLun == 0)
3742 && (uInstance < SchemaDefs::NetworkAdapterCount),
3743 ("pszDevice=%s uLun=%d uInstance=%d\n", pszDevice, uLun, uInstance));
3744 Log(("pszDevice=%s uLun=%d uInstance=%d\n", pszDevice, uLun, uInstance));
3745
3746 AutoCaller autoCaller(pThis);
3747 AssertComRCReturn(autoCaller.rc(), VERR_ACCESS_DENIED);
3748
3749 /* protect mpVM */
3750 AutoVMCaller autoVMCaller(pThis);
3751 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
3752
3753 PVM pVM = pThis->mpVM;
3754
3755 /*
3756 * Suspend the VM first.
3757 *
3758 * The VM must not be running since it might have pending I/O to
3759 * the drive which is being changed.
3760 */
3761 bool fResume;
3762 VMSTATE enmVMState = VMR3GetState(pVM);
3763 switch (enmVMState)
3764 {
3765 case VMSTATE_RESETTING:
3766 case VMSTATE_RUNNING:
3767 {
3768 LogFlowFunc(("Suspending the VM...\n"));
3769 /* disable the callback to prevent Console-level state change */
3770 pThis->mVMStateChangeCallbackDisabled = true;
3771 int rc = VMR3Suspend(pVM);
3772 pThis->mVMStateChangeCallbackDisabled = false;
3773 AssertRCReturn(rc, rc);
3774 fResume = true;
3775 break;
3776 }
3777
3778 case VMSTATE_SUSPENDED:
3779 case VMSTATE_CREATED:
3780 case VMSTATE_OFF:
3781 fResume = false;
3782 break;
3783
3784 default:
3785 AssertLogRelMsgFailedReturn(("enmVMState=%d\n", enmVMState), VERR_ACCESS_DENIED);
3786 }
3787
3788 int rc = VINF_SUCCESS;
3789 int rcRet = VINF_SUCCESS;
3790
3791 PCFGMNODE pCfg = NULL; /* /Devices/Dev/.../Config/ */
3792 PCFGMNODE pLunL0 = NULL; /* /Devices/Dev/0/LUN#0/ */
3793 PCFGMNODE pInst = CFGMR3GetChildF(CFGMR3GetRoot(pVM), "Devices/%s/%d/", pszDevice, uInstance);
3794 AssertRelease(pInst);
3795
3796 rcRet = pThis->configNetwork(pszDevice, uInstance, uLun, aNetworkAdapter, pCfg, pLunL0, pInst,
3797 true /*fAttachDetach*/, false /*fIgnoreConnectFailure*/);
3798
3799 /*
3800 * Resume the VM if necessary.
3801 */
3802 if (fResume)
3803 {
3804 LogFlowFunc(("Resuming the VM...\n"));
3805 /* disable the callback to prevent Console-level state change */
3806 pThis->mVMStateChangeCallbackDisabled = true;
3807 rc = VMR3Resume(pVM);
3808 pThis->mVMStateChangeCallbackDisabled = false;
3809 AssertRC(rc);
3810 if (RT_FAILURE(rc))
3811 {
3812 /* too bad, we failed. try to sync the console state with the VMM state */
3813 vmstateChangeCallback(pVM, VMSTATE_SUSPENDED, enmVMState, pThis);
3814 }
3815 /// @todo (r=dmik) if we failed with drive mount, then the VMR3Resume
3816 // error (if any) will be hidden from the caller. For proper reporting
3817 // of such multiple errors to the caller we need to enhance the
3818 // IVirtualBoxError interface. For now, give the first error the higher
3819 // priority.
3820 if (RT_SUCCESS(rcRet))
3821 rcRet = rc;
3822 }
3823
3824 LogFlowFunc(("Returning %Rrc\n", rcRet));
3825 return rcRet;
3826}
3827
3828
3829/**
3830 * Called by IInternalSessionControl::OnSerialPortChange().
3831 *
3832 * @note Locks this object for writing.
3833 */
3834HRESULT Console::onSerialPortChange(ISerialPort *aSerialPort)
3835{
3836 LogFlowThisFunc(("\n"));
3837
3838 AutoCaller autoCaller(this);
3839 AssertComRCReturnRC(autoCaller.rc());
3840
3841 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3842
3843 HRESULT rc = S_OK;
3844
3845 /* don't trigger serial port change if the VM isn't running */
3846 if (mpVM)
3847 {
3848 /* protect mpVM */
3849 AutoVMCaller autoVMCaller(this);
3850 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
3851
3852 /* nothing to do so far */
3853 }
3854
3855 /* notify console callbacks on success */
3856 if (SUCCEEDED(rc))
3857 fireSerialPortChangedEvent(mEventSource, aSerialPort);
3858
3859 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
3860 return rc;
3861}
3862
3863/**
3864 * Called by IInternalSessionControl::OnParallelPortChange().
3865 *
3866 * @note Locks this object for writing.
3867 */
3868HRESULT Console::onParallelPortChange(IParallelPort *aParallelPort)
3869{
3870 LogFlowThisFunc(("\n"));
3871
3872 AutoCaller autoCaller(this);
3873 AssertComRCReturnRC(autoCaller.rc());
3874
3875 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3876
3877 HRESULT rc = S_OK;
3878
3879 /* don't trigger parallel port change if the VM isn't running */
3880 if (mpVM)
3881 {
3882 /* protect mpVM */
3883 AutoVMCaller autoVMCaller(this);
3884 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
3885
3886 /* nothing to do so far */
3887 }
3888
3889 /* notify console callbacks on success */
3890 if (SUCCEEDED(rc))
3891 fireParallelPortChangedEvent(mEventSource, aParallelPort);
3892
3893 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
3894 return rc;
3895}
3896
3897/**
3898 * Called by IInternalSessionControl::OnStorageControllerChange().
3899 *
3900 * @note Locks this object for writing.
3901 */
3902HRESULT Console::onStorageControllerChange()
3903{
3904 LogFlowThisFunc(("\n"));
3905
3906 AutoCaller autoCaller(this);
3907 AssertComRCReturnRC(autoCaller.rc());
3908
3909 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3910
3911 HRESULT rc = S_OK;
3912
3913 /* don't trigger storage controller change if the VM isn't running */
3914 if (mpVM)
3915 {
3916 /* protect mpVM */
3917 AutoVMCaller autoVMCaller(this);
3918 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
3919
3920 /* nothing to do so far */
3921 }
3922
3923 /* notify console callbacks on success */
3924 if (SUCCEEDED(rc))
3925 fireStorageControllerChangedEvent(mEventSource);
3926
3927 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
3928 return rc;
3929}
3930
3931/**
3932 * Called by IInternalSessionControl::OnMediumChange().
3933 *
3934 * @note Locks this object for writing.
3935 */
3936HRESULT Console::onMediumChange(IMediumAttachment *aMediumAttachment, BOOL aForce)
3937{
3938 LogFlowThisFunc(("\n"));
3939
3940 AutoCaller autoCaller(this);
3941 AssertComRCReturnRC(autoCaller.rc());
3942
3943 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3944
3945 HRESULT rc = S_OK;
3946
3947 /* don't trigger medium change if the VM isn't running */
3948 if (mpVM)
3949 {
3950 /* protect mpVM */
3951 AutoVMCaller autoVMCaller(this);
3952 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
3953
3954 rc = doMediumChange(aMediumAttachment, !!aForce);
3955 }
3956
3957 /* notify console callbacks on success */
3958 if (SUCCEEDED(rc))
3959 fireMediumChangedEvent(mEventSource, aMediumAttachment);
3960
3961 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
3962 return rc;
3963}
3964
3965/**
3966 * Called by IInternalSessionControl::OnCPUChange().
3967 *
3968 * @note Locks this object for writing.
3969 */
3970HRESULT Console::onCPUChange(ULONG aCPU, BOOL aRemove)
3971{
3972 LogFlowThisFunc(("\n"));
3973
3974 AutoCaller autoCaller(this);
3975 AssertComRCReturnRC(autoCaller.rc());
3976
3977 HRESULT rc = S_OK;
3978
3979 /* don't trigger CPU change if the VM isn't running */
3980 if (mpVM)
3981 {
3982 if (aRemove)
3983 rc = doCPURemove(aCPU);
3984 else
3985 rc = doCPUAdd(aCPU);
3986 }
3987
3988 /* notify console callbacks on success */
3989 if (SUCCEEDED(rc))
3990 fireCPUChangedEvent(mEventSource, aCPU, aRemove);
3991
3992 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
3993 return rc;
3994}
3995
3996/**
3997 * Called by IInternalSessionControl::OnCpuExecutionCapChange().
3998 *
3999 * @note Locks this object for writing.
4000 */
4001HRESULT Console::onCPUExecutionCapChange(ULONG aExecutionCap)
4002{
4003 LogFlowThisFunc(("\n"));
4004
4005 AutoCaller autoCaller(this);
4006 AssertComRCReturnRC(autoCaller.rc());
4007
4008 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4009
4010 HRESULT rc = S_OK;
4011
4012 /* don't trigger the CPU priority change if the VM isn't running */
4013 if (mpVM)
4014 {
4015 /* protect mpVM */
4016 AutoVMCaller autoVMCaller(this);
4017 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
4018
4019 if ( mMachineState == MachineState_Running
4020 || mMachineState == MachineState_Teleporting
4021 || mMachineState == MachineState_LiveSnapshotting
4022 )
4023 {
4024 /* No need to call in the EMT thread. */
4025 rc = VMR3SetCpuExecutionCap(mpVM, aExecutionCap);
4026 }
4027 else
4028 rc = setInvalidMachineStateError();
4029 }
4030
4031 /* notify console callbacks on success */
4032 if (SUCCEEDED(rc))
4033 fireCPUExecutionCapChangedEvent(mEventSource, aExecutionCap);
4034
4035 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
4036 return rc;
4037}
4038
4039/**
4040 * Called by IInternalSessionControl::OnVRDEServerChange().
4041 *
4042 * @note Locks this object for writing.
4043 */
4044HRESULT Console::onVRDEServerChange(BOOL aRestart)
4045{
4046 AutoCaller autoCaller(this);
4047 AssertComRCReturnRC(autoCaller.rc());
4048
4049 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4050
4051 HRESULT rc = S_OK;
4052
4053 if ( mVRDEServer
4054 && ( mMachineState == MachineState_Running
4055 || mMachineState == MachineState_Teleporting
4056 || mMachineState == MachineState_LiveSnapshotting
4057 )
4058 )
4059 {
4060 BOOL vrdpEnabled = FALSE;
4061
4062 rc = mVRDEServer->COMGETTER(Enabled)(&vrdpEnabled);
4063 ComAssertComRCRetRC(rc);
4064
4065 if (aRestart)
4066 {
4067 /* VRDP server may call this Console object back from other threads (VRDP INPUT or OUTPUT). */
4068 alock.leave();
4069
4070 if (vrdpEnabled)
4071 {
4072 // If there was no VRDP server started the 'stop' will do nothing.
4073 // However if a server was started and this notification was called,
4074 // we have to restart the server.
4075 mConsoleVRDPServer->Stop();
4076
4077 if (RT_FAILURE(mConsoleVRDPServer->Launch()))
4078 rc = E_FAIL;
4079 else
4080 mConsoleVRDPServer->EnableConnections();
4081 }
4082 else
4083 {
4084 mConsoleVRDPServer->Stop();
4085 }
4086
4087 alock.enter();
4088 }
4089 }
4090
4091 /* notify console callbacks on success */
4092 if (SUCCEEDED(rc))
4093 fireVRDEServerChangedEvent(mEventSource);
4094
4095 return rc;
4096}
4097
4098/**
4099 * @note Locks this object for reading.
4100 */
4101void Console::onVRDEServerInfoChange()
4102{
4103 AutoCaller autoCaller(this);
4104 AssertComRCReturnVoid(autoCaller.rc());
4105
4106 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4107
4108 fireVRDEServerInfoChangedEvent(mEventSource);
4109}
4110
4111
4112/**
4113 * Called by IInternalSessionControl::OnUSBControllerChange().
4114 *
4115 * @note Locks this object for writing.
4116 */
4117HRESULT Console::onUSBControllerChange()
4118{
4119 LogFlowThisFunc(("\n"));
4120
4121 AutoCaller autoCaller(this);
4122 AssertComRCReturnRC(autoCaller.rc());
4123
4124 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4125
4126 HRESULT rc = S_OK;
4127
4128 /* don't trigger USB controller change if the VM isn't running */
4129 if (mpVM)
4130 {
4131 /// @todo implement one day.
4132 // Anyway, if we want to query the machine's USB Controller we need
4133 // to cache it to mUSBController in #init() (similar to mDVDDrive).
4134 //
4135 // bird: While the VM supports hot-plugging, I doubt any guest can
4136 // handle it at this time... :-)
4137
4138 /* protect mpVM */
4139 AutoVMCaller autoVMCaller(this);
4140 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
4141
4142 /* nothing to do so far */
4143 }
4144
4145 /* notify console callbacks on success */
4146 if (SUCCEEDED(rc))
4147 fireUSBControllerChangedEvent(mEventSource);
4148
4149 return rc;
4150}
4151
4152/**
4153 * Called by IInternalSessionControl::OnSharedFolderChange().
4154 *
4155 * @note Locks this object for writing.
4156 */
4157HRESULT Console::onSharedFolderChange(BOOL aGlobal)
4158{
4159 LogFlowThisFunc(("aGlobal=%RTbool\n", aGlobal));
4160
4161 AutoCaller autoCaller(this);
4162 AssertComRCReturnRC(autoCaller.rc());
4163
4164 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4165
4166 HRESULT rc = fetchSharedFolders(aGlobal);
4167
4168 /* notify console callbacks on success */
4169 if (SUCCEEDED(rc))
4170 {
4171 fireSharedFolderChangedEvent(mEventSource, aGlobal ? (Scope_T)Scope_Global : (Scope_T)Scope_Machine);
4172 }
4173
4174 return rc;
4175}
4176
4177/**
4178 * Called by IInternalSessionControl::OnUSBDeviceAttach() or locally by
4179 * processRemoteUSBDevices() after IInternalMachineControl::RunUSBDeviceFilters()
4180 * returns TRUE for a given remote USB device.
4181 *
4182 * @return S_OK if the device was attached to the VM.
4183 * @return failure if not attached.
4184 *
4185 * @param aDevice
4186 * The device in question.
4187 * @param aMaskedIfs
4188 * The interfaces to hide from the guest.
4189 *
4190 * @note Locks this object for writing.
4191 */
4192HRESULT Console::onUSBDeviceAttach(IUSBDevice *aDevice, IVirtualBoxErrorInfo *aError, ULONG aMaskedIfs)
4193{
4194#ifdef VBOX_WITH_USB
4195 LogFlowThisFunc(("aDevice=%p aError=%p\n", aDevice, aError));
4196
4197 AutoCaller autoCaller(this);
4198 ComAssertComRCRetRC(autoCaller.rc());
4199
4200 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4201
4202 /* protect mpVM (we don't need error info, since it's a callback) */
4203 AutoVMCallerQuiet autoVMCaller(this);
4204 if (FAILED(autoVMCaller.rc()))
4205 {
4206 /* The VM may be no more operational when this message arrives
4207 * (e.g. it may be Saving or Stopping or just PoweredOff) --
4208 * autoVMCaller.rc() will return a failure in this case. */
4209 LogFlowThisFunc(("Attach request ignored (mMachineState=%d).\n",
4210 mMachineState));
4211 return autoVMCaller.rc();
4212 }
4213
4214 if (aError != NULL)
4215 {
4216 /* notify callbacks about the error */
4217 onUSBDeviceStateChange(aDevice, true /* aAttached */, aError);
4218 return S_OK;
4219 }
4220
4221 /* Don't proceed unless there's at least one USB hub. */
4222 if (!PDMR3USBHasHub(mpVM))
4223 {
4224 LogFlowThisFunc(("Attach request ignored (no USB controller).\n"));
4225 return E_FAIL;
4226 }
4227
4228 HRESULT rc = attachUSBDevice(aDevice, aMaskedIfs);
4229 if (FAILED(rc))
4230 {
4231 /* take the current error info */
4232 com::ErrorInfoKeeper eik;
4233 /* the error must be a VirtualBoxErrorInfo instance */
4234 ComPtr<IVirtualBoxErrorInfo> pError = eik.takeError();
4235 Assert(!pError.isNull());
4236 if (!pError.isNull())
4237 {
4238 /* notify callbacks about the error */
4239 onUSBDeviceStateChange(aDevice, true /* aAttached */, pError);
4240 }
4241 }
4242
4243 return rc;
4244
4245#else /* !VBOX_WITH_USB */
4246 return E_FAIL;
4247#endif /* !VBOX_WITH_USB */
4248}
4249
4250/**
4251 * Called by IInternalSessionControl::OnUSBDeviceDetach() and locally by
4252 * processRemoteUSBDevices().
4253 *
4254 * @note Locks this object for writing.
4255 */
4256HRESULT Console::onUSBDeviceDetach(IN_BSTR aId,
4257 IVirtualBoxErrorInfo *aError)
4258{
4259#ifdef VBOX_WITH_USB
4260 Guid Uuid(aId);
4261 LogFlowThisFunc(("aId={%RTuuid} aError=%p\n", Uuid.raw(), aError));
4262
4263 AutoCaller autoCaller(this);
4264 AssertComRCReturnRC(autoCaller.rc());
4265
4266 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4267
4268 /* Find the device. */
4269 ComObjPtr<OUSBDevice> pUSBDevice;
4270 USBDeviceList::iterator it = mUSBDevices.begin();
4271 while (it != mUSBDevices.end())
4272 {
4273 LogFlowThisFunc(("it={%RTuuid}\n", (*it)->id().raw()));
4274 if ((*it)->id() == Uuid)
4275 {
4276 pUSBDevice = *it;
4277 break;
4278 }
4279 ++ it;
4280 }
4281
4282
4283 if (pUSBDevice.isNull())
4284 {
4285 LogFlowThisFunc(("USB device not found.\n"));
4286
4287 /* The VM may be no more operational when this message arrives
4288 * (e.g. it may be Saving or Stopping or just PoweredOff). Use
4289 * AutoVMCaller to detect it -- AutoVMCaller::rc() will return a
4290 * failure in this case. */
4291
4292 AutoVMCallerQuiet autoVMCaller(this);
4293 if (FAILED(autoVMCaller.rc()))
4294 {
4295 LogFlowThisFunc(("Detach request ignored (mMachineState=%d).\n",
4296 mMachineState));
4297 return autoVMCaller.rc();
4298 }
4299
4300 /* the device must be in the list otherwise */
4301 AssertFailedReturn(E_FAIL);
4302 }
4303
4304 if (aError != NULL)
4305 {
4306 /* notify callback about an error */
4307 onUSBDeviceStateChange(pUSBDevice, false /* aAttached */, aError);
4308 return S_OK;
4309 }
4310
4311 HRESULT rc = detachUSBDevice(it);
4312
4313 if (FAILED(rc))
4314 {
4315 /* take the current error info */
4316 com::ErrorInfoKeeper eik;
4317 /* the error must be a VirtualBoxErrorInfo instance */
4318 ComPtr<IVirtualBoxErrorInfo> pError = eik.takeError();
4319 Assert(!pError.isNull());
4320 if (!pError.isNull())
4321 {
4322 /* notify callbacks about the error */
4323 onUSBDeviceStateChange(pUSBDevice, false /* aAttached */, pError);
4324 }
4325 }
4326
4327 return rc;
4328
4329#else /* !VBOX_WITH_USB */
4330 return E_FAIL;
4331#endif /* !VBOX_WITH_USB */
4332}
4333
4334/**
4335 * Called by IInternalSessionControl::OnBandwidthGroupChange().
4336 *
4337 * @note Locks this object for writing.
4338 */
4339HRESULT Console::onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
4340{
4341 LogFlowThisFunc(("\n"));
4342
4343 AutoCaller autoCaller(this);
4344 AssertComRCReturnRC(autoCaller.rc());
4345
4346 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4347
4348 HRESULT rc = S_OK;
4349
4350 /* don't trigger the CPU priority change if the VM isn't running */
4351 if (mpVM)
4352 {
4353 /* protect mpVM */
4354 AutoVMCaller autoVMCaller(this);
4355 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
4356
4357 if ( mMachineState == MachineState_Running
4358 || mMachineState == MachineState_Teleporting
4359 || mMachineState == MachineState_LiveSnapshotting
4360 )
4361 {
4362 /* No need to call in the EMT thread. */
4363 ULONG cMax;
4364 Bstr strName;
4365 rc = aBandwidthGroup->COMGETTER(Name)(strName.asOutParam());
4366 if (SUCCEEDED(rc))
4367 rc = aBandwidthGroup->COMGETTER(MaxMbPerSec)(&cMax);
4368
4369 if (SUCCEEDED(rc))
4370 {
4371 int vrc;
4372 vrc = PDMR3AsyncCompletionBwMgrSetMaxForFile(mpVM, Utf8Str(strName).c_str(),
4373 cMax * _1M);
4374 AssertRC(vrc);
4375 }
4376 }
4377 else
4378 rc = setInvalidMachineStateError();
4379 }
4380
4381 /* notify console callbacks on success */
4382 if (SUCCEEDED(rc))
4383 fireBandwidthGroupChangedEvent(mEventSource, aBandwidthGroup);
4384
4385 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
4386 return rc;
4387}
4388
4389/**
4390 * @note Temporarily locks this object for writing.
4391 */
4392HRESULT Console::getGuestProperty(IN_BSTR aName, BSTR *aValue,
4393 LONG64 *aTimestamp, BSTR *aFlags)
4394{
4395#ifndef VBOX_WITH_GUEST_PROPS
4396 ReturnComNotImplemented();
4397#else /* VBOX_WITH_GUEST_PROPS */
4398 if (!VALID_PTR(aName))
4399 return E_INVALIDARG;
4400 if (!VALID_PTR(aValue))
4401 return E_POINTER;
4402 if ((aTimestamp != NULL) && !VALID_PTR(aTimestamp))
4403 return E_POINTER;
4404 if ((aFlags != NULL) && !VALID_PTR(aFlags))
4405 return E_POINTER;
4406
4407 AutoCaller autoCaller(this);
4408 AssertComRCReturnRC(autoCaller.rc());
4409
4410 /* protect mpVM (if not NULL) */
4411 AutoVMCallerWeak autoVMCaller(this);
4412 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
4413
4414 /* Note: validity of mVMMDev which is bound to uninit() is guaranteed by
4415 * autoVMCaller, so there is no need to hold a lock of this */
4416
4417 HRESULT rc = E_UNEXPECTED;
4418 using namespace guestProp;
4419
4420 try
4421 {
4422 VBOXHGCMSVCPARM parm[4];
4423 Utf8Str Utf8Name = aName;
4424 char pszBuffer[MAX_VALUE_LEN + MAX_FLAGS_LEN];
4425
4426 parm[0].type = VBOX_HGCM_SVC_PARM_PTR;
4427 parm[0].u.pointer.addr = (void*)Utf8Name.c_str();
4428 /* The + 1 is the null terminator */
4429 parm[0].u.pointer.size = (uint32_t)Utf8Name.length() + 1;
4430 parm[1].type = VBOX_HGCM_SVC_PARM_PTR;
4431 parm[1].u.pointer.addr = pszBuffer;
4432 parm[1].u.pointer.size = sizeof(pszBuffer);
4433 int vrc = m_pVMMDev->hgcmHostCall("VBoxGuestPropSvc", GET_PROP_HOST,
4434 4, &parm[0]);
4435 /* The returned string should never be able to be greater than our buffer */
4436 AssertLogRel(vrc != VERR_BUFFER_OVERFLOW);
4437 AssertLogRel(RT_FAILURE(vrc) || VBOX_HGCM_SVC_PARM_64BIT == parm[2].type);
4438 if (RT_SUCCESS(vrc) || (VERR_NOT_FOUND == vrc))
4439 {
4440 rc = S_OK;
4441 if (vrc != VERR_NOT_FOUND)
4442 {
4443 Utf8Str strBuffer(pszBuffer);
4444 strBuffer.cloneTo(aValue);
4445
4446 if (aTimestamp)
4447 *aTimestamp = parm[2].u.uint64;
4448
4449 if (aFlags)
4450 {
4451 size_t iFlags = strBuffer.length() + 1;
4452 Utf8Str(pszBuffer + iFlags).cloneTo(aFlags);
4453 }
4454 }
4455 else
4456 aValue = NULL;
4457 }
4458 else
4459 rc = setError(E_UNEXPECTED,
4460 tr("The service call failed with the error %Rrc"),
4461 vrc);
4462 }
4463 catch(std::bad_alloc & /*e*/)
4464 {
4465 rc = E_OUTOFMEMORY;
4466 }
4467 return rc;
4468#endif /* VBOX_WITH_GUEST_PROPS */
4469}
4470
4471/**
4472 * @note Temporarily locks this object for writing.
4473 */
4474HRESULT Console::setGuestProperty(IN_BSTR aName, IN_BSTR aValue, IN_BSTR aFlags)
4475{
4476#ifndef VBOX_WITH_GUEST_PROPS
4477 ReturnComNotImplemented();
4478#else /* VBOX_WITH_GUEST_PROPS */
4479 if (!VALID_PTR(aName))
4480 return E_INVALIDARG;
4481 if ((aValue != NULL) && !VALID_PTR(aValue))
4482 return E_INVALIDARG;
4483 if ((aFlags != NULL) && !VALID_PTR(aFlags))
4484 return E_INVALIDARG;
4485
4486 AutoCaller autoCaller(this);
4487 AssertComRCReturnRC(autoCaller.rc());
4488
4489 /* protect mpVM (if not NULL) */
4490 AutoVMCallerWeak autoVMCaller(this);
4491 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
4492
4493 /* Note: validity of mVMMDev which is bound to uninit() is guaranteed by
4494 * autoVMCaller, so there is no need to hold a lock of this */
4495
4496 HRESULT rc = E_UNEXPECTED;
4497 using namespace guestProp;
4498
4499 VBOXHGCMSVCPARM parm[3];
4500 Utf8Str Utf8Name = aName;
4501 int vrc = VINF_SUCCESS;
4502
4503 parm[0].type = VBOX_HGCM_SVC_PARM_PTR;
4504 parm[0].u.pointer.addr = (void*)Utf8Name.c_str();
4505 /* The + 1 is the null terminator */
4506 parm[0].u.pointer.size = (uint32_t)Utf8Name.length() + 1;
4507 Utf8Str Utf8Value = aValue;
4508 if (aValue != NULL)
4509 {
4510 parm[1].type = VBOX_HGCM_SVC_PARM_PTR;
4511 parm[1].u.pointer.addr = (void*)Utf8Value.c_str();
4512 /* The + 1 is the null terminator */
4513 parm[1].u.pointer.size = (uint32_t)Utf8Value.length() + 1;
4514 }
4515 Utf8Str Utf8Flags = aFlags;
4516 if (aFlags != NULL)
4517 {
4518 parm[2].type = VBOX_HGCM_SVC_PARM_PTR;
4519 parm[2].u.pointer.addr = (void*)Utf8Flags.c_str();
4520 /* The + 1 is the null terminator */
4521 parm[2].u.pointer.size = (uint32_t)Utf8Flags.length() + 1;
4522 }
4523 if ((aValue != NULL) && (aFlags != NULL))
4524 vrc = m_pVMMDev->hgcmHostCall("VBoxGuestPropSvc", SET_PROP_HOST,
4525 3, &parm[0]);
4526 else if (aValue != NULL)
4527 vrc = m_pVMMDev->hgcmHostCall("VBoxGuestPropSvc", SET_PROP_VALUE_HOST,
4528 2, &parm[0]);
4529 else
4530 vrc = m_pVMMDev->hgcmHostCall("VBoxGuestPropSvc", DEL_PROP_HOST,
4531 1, &parm[0]);
4532 if (RT_SUCCESS(vrc))
4533 rc = S_OK;
4534 else
4535 rc = setError(E_UNEXPECTED,
4536 tr("The service call failed with the error %Rrc"),
4537 vrc);
4538 return rc;
4539#endif /* VBOX_WITH_GUEST_PROPS */
4540}
4541
4542
4543/**
4544 * @note Temporarily locks this object for writing.
4545 */
4546HRESULT Console::enumerateGuestProperties(IN_BSTR aPatterns,
4547 ComSafeArrayOut(BSTR, aNames),
4548 ComSafeArrayOut(BSTR, aValues),
4549 ComSafeArrayOut(LONG64, aTimestamps),
4550 ComSafeArrayOut(BSTR, aFlags))
4551{
4552#ifndef VBOX_WITH_GUEST_PROPS
4553 ReturnComNotImplemented();
4554#else /* VBOX_WITH_GUEST_PROPS */
4555 if (!VALID_PTR(aPatterns) && (aPatterns != NULL))
4556 return E_POINTER;
4557 if (ComSafeArrayOutIsNull(aNames))
4558 return E_POINTER;
4559 if (ComSafeArrayOutIsNull(aValues))
4560 return E_POINTER;
4561 if (ComSafeArrayOutIsNull(aTimestamps))
4562 return E_POINTER;
4563 if (ComSafeArrayOutIsNull(aFlags))
4564 return E_POINTER;
4565
4566 AutoCaller autoCaller(this);
4567 AssertComRCReturnRC(autoCaller.rc());
4568
4569 /* protect mpVM (if not NULL) */
4570 AutoVMCallerWeak autoVMCaller(this);
4571 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
4572
4573 /* Note: validity of mVMMDev which is bound to uninit() is guaranteed by
4574 * autoVMCaller, so there is no need to hold a lock of this */
4575
4576 return doEnumerateGuestProperties(aPatterns, ComSafeArrayOutArg(aNames),
4577 ComSafeArrayOutArg(aValues),
4578 ComSafeArrayOutArg(aTimestamps),
4579 ComSafeArrayOutArg(aFlags));
4580#endif /* VBOX_WITH_GUEST_PROPS */
4581}
4582
4583
4584/*
4585 * Internal: helper function for connecting progress reporting
4586 */
4587static int onlineMergeMediumProgress(void *pvUser, unsigned uPercentage)
4588{
4589 HRESULT rc = S_OK;
4590 IProgress *pProgress = static_cast<IProgress *>(pvUser);
4591 if (pProgress)
4592 rc = pProgress->SetCurrentOperationProgress(uPercentage);
4593 return SUCCEEDED(rc) ? VINF_SUCCESS : VERR_GENERAL_FAILURE;
4594}
4595
4596/**
4597 * @note Temporarily locks this object for writing.
4598 */
4599HRESULT Console::onlineMergeMedium(IMediumAttachment *aMediumAttachment,
4600 ULONG aSourceIdx, ULONG aTargetIdx,
4601 IMedium *aSource, IMedium *aTarget,
4602 BOOL aMergeForward,
4603 IMedium *aParentForTarget,
4604 ComSafeArrayIn(IMedium *, aChildrenToReparent),
4605 IProgress *aProgress)
4606{
4607 AutoCaller autoCaller(this);
4608 AssertComRCReturnRC(autoCaller.rc());
4609
4610 HRESULT rc = S_OK;
4611 int vrc = VINF_SUCCESS;
4612 PVM pVM = mpVM;
4613
4614 /* We will need to release the lock before doing the actual merge */
4615 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4616
4617 /* paranoia - we don't want merges to happen while teleporting etc. */
4618 switch (mMachineState)
4619 {
4620 case MachineState_DeletingSnapshotOnline:
4621 case MachineState_DeletingSnapshotPaused:
4622 break;
4623
4624 default:
4625 return setInvalidMachineStateError();
4626 }
4627
4628 BOOL fBuiltinIoCache;
4629 rc = mMachine->COMGETTER(IoCacheEnabled)(&fBuiltinIoCache);
4630 AssertComRC(rc);
4631 SafeIfaceArray<IStorageController> ctrls;
4632 rc = mMachine->COMGETTER(StorageControllers)(ComSafeArrayAsOutParam(ctrls));
4633 AssertComRC(rc);
4634 LONG lDev;
4635 rc = aMediumAttachment->COMGETTER(Device)(&lDev);
4636 AssertComRC(rc);
4637 LONG lPort;
4638 rc = aMediumAttachment->COMGETTER(Port)(&lPort);
4639 AssertComRC(rc);
4640 IMedium *pMedium;
4641 rc = aMediumAttachment->COMGETTER(Medium)(&pMedium);
4642 AssertComRC(rc);
4643 Bstr mediumLocation;
4644 if (pMedium)
4645 {
4646 rc = pMedium->COMGETTER(Location)(mediumLocation.asOutParam());
4647 AssertComRC(rc);
4648 }
4649
4650 Bstr attCtrlName;
4651 rc = aMediumAttachment->COMGETTER(Controller)(attCtrlName.asOutParam());
4652 AssertComRC(rc);
4653 ComPtr<IStorageController> pStorageController;
4654 for (size_t i = 0; i < ctrls.size(); ++i)
4655 {
4656 Bstr ctrlName;
4657 rc = ctrls[i]->COMGETTER(Name)(ctrlName.asOutParam());
4658 AssertComRC(rc);
4659 if (attCtrlName == ctrlName)
4660 {
4661 pStorageController = ctrls[i];
4662 break;
4663 }
4664 }
4665 if (pStorageController.isNull())
4666 return setError(E_FAIL,
4667 tr("Could not find storage controller '%ls'"),
4668 attCtrlName.raw());
4669
4670 StorageControllerType_T enmCtrlType;
4671 rc = pStorageController->COMGETTER(ControllerType)(&enmCtrlType);
4672 AssertComRC(rc);
4673 const char *pcszDevice = convertControllerTypeToDev(enmCtrlType);
4674
4675 StorageBus_T enmBus;
4676 rc = pStorageController->COMGETTER(Bus)(&enmBus);
4677 AssertComRC(rc);
4678 ULONG uInstance;
4679 rc = pStorageController->COMGETTER(Instance)(&uInstance);
4680 AssertComRC(rc);
4681 BOOL fUseHostIOCache;
4682 rc = pStorageController->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
4683 AssertComRC(rc);
4684
4685 unsigned uLUN;
4686 rc = Console::convertBusPortDeviceToLun(enmBus, lPort, lDev, uLUN);
4687 AssertComRCReturnRC(rc);
4688
4689 alock.release();
4690
4691 /* Pause the VM, as it might have pending IO on this drive */
4692 VMSTATE enmVMState = VMR3GetState(pVM);
4693 if (mMachineState == MachineState_DeletingSnapshotOnline)
4694 {
4695 LogFlowFunc(("Suspending the VM...\n"));
4696 /* disable the callback to prevent Console-level state change */
4697 mVMStateChangeCallbackDisabled = true;
4698 int vrc2 = VMR3Suspend(pVM);
4699 mVMStateChangeCallbackDisabled = false;
4700 AssertRCReturn(vrc2, E_FAIL);
4701 }
4702
4703 vrc = VMR3ReqCallWait(pVM,
4704 VMCPUID_ANY,
4705 (PFNRT)reconfigureMediumAttachment,
4706 13,
4707 this,
4708 pVM,
4709 pcszDevice,
4710 uInstance,
4711 enmBus,
4712 fUseHostIOCache,
4713 fBuiltinIoCache,
4714 true /* fSetupMerge */,
4715 aSourceIdx,
4716 aTargetIdx,
4717 aMediumAttachment,
4718 mMachineState,
4719 &rc);
4720 /* error handling is after resuming the VM */
4721
4722 if (mMachineState == MachineState_DeletingSnapshotOnline)
4723 {
4724 LogFlowFunc(("Resuming the VM...\n"));
4725 /* disable the callback to prevent Console-level state change */
4726 mVMStateChangeCallbackDisabled = true;
4727 int vrc2 = VMR3Resume(pVM);
4728 mVMStateChangeCallbackDisabled = false;
4729 if (RT_FAILURE(vrc2))
4730 {
4731 /* too bad, we failed. try to sync the console state with the VMM state */
4732 AssertLogRelRC(vrc2);
4733 vmstateChangeCallback(pVM, VMSTATE_SUSPENDED, enmVMState, this);
4734 }
4735 }
4736
4737 if (RT_FAILURE(vrc))
4738 return setError(E_FAIL, tr("%Rrc"), vrc);
4739 if (FAILED(rc))
4740 return rc;
4741
4742 PPDMIBASE pIBase = NULL;
4743 PPDMIMEDIA pIMedium = NULL;
4744 vrc = PDMR3QueryDriverOnLun(pVM, pcszDevice, uInstance, uLUN, "VD", &pIBase);
4745 if (RT_SUCCESS(vrc))
4746 {
4747 if (pIBase)
4748 {
4749 pIMedium = (PPDMIMEDIA)pIBase->pfnQueryInterface(pIBase, PDMIMEDIA_IID);
4750 if (!pIMedium)
4751 return setError(E_FAIL, tr("could not query medium interface of controller"));
4752 }
4753 else
4754 return setError(E_FAIL, tr("could not query base interface of controller"));
4755 }
4756
4757 /* Finally trigger the merge. */
4758 vrc = pIMedium->pfnMerge(pIMedium, onlineMergeMediumProgress, aProgress);
4759 if (RT_FAILURE(vrc))
4760 return setError(E_FAIL, tr("Failed to perform an online medium merge (%Rrc)"), vrc);
4761
4762 /* Pause the VM, as it might have pending IO on this drive */
4763 enmVMState = VMR3GetState(pVM);
4764 if (mMachineState == MachineState_DeletingSnapshotOnline)
4765 {
4766 LogFlowFunc(("Suspending the VM...\n"));
4767 /* disable the callback to prevent Console-level state change */
4768 mVMStateChangeCallbackDisabled = true;
4769 int vrc2 = VMR3Suspend(pVM);
4770 mVMStateChangeCallbackDisabled = false;
4771 AssertRCReturn(vrc2, E_FAIL);
4772 }
4773
4774 /* Update medium chain and state now, so that the VM can continue. */
4775 rc = mControl->FinishOnlineMergeMedium(aMediumAttachment, aSource, aTarget,
4776 aMergeForward, aParentForTarget,
4777 ComSafeArrayInArg(aChildrenToReparent));
4778
4779 vrc = VMR3ReqCallWait(pVM,
4780 VMCPUID_ANY,
4781 (PFNRT)reconfigureMediumAttachment,
4782 13,
4783 this,
4784 pVM,
4785 pcszDevice,
4786 uInstance,
4787 enmBus,
4788 fUseHostIOCache,
4789 fBuiltinIoCache,
4790 false /* fSetupMerge */,
4791 0 /* uMergeSource */,
4792 0 /* uMergeTarget */,
4793 aMediumAttachment,
4794 mMachineState,
4795 &rc);
4796 /* error handling is after resuming the VM */
4797
4798 if (mMachineState == MachineState_DeletingSnapshotOnline)
4799 {
4800 LogFlowFunc(("Resuming the VM...\n"));
4801 /* disable the callback to prevent Console-level state change */
4802 mVMStateChangeCallbackDisabled = true;
4803 int vrc2 = VMR3Resume(pVM);
4804 mVMStateChangeCallbackDisabled = false;
4805 AssertRC(vrc2);
4806 if (RT_FAILURE(vrc2))
4807 {
4808 /* too bad, we failed. try to sync the console state with the VMM state */
4809 vmstateChangeCallback(pVM, VMSTATE_SUSPENDED, enmVMState, this);
4810 }
4811 }
4812
4813 if (RT_FAILURE(vrc))
4814 return setError(E_FAIL, tr("%Rrc"), vrc);
4815 if (FAILED(rc))
4816 return rc;
4817
4818 return rc;
4819}
4820
4821
4822/**
4823 * Gets called by Session::UpdateMachineState()
4824 * (IInternalSessionControl::updateMachineState()).
4825 *
4826 * Must be called only in certain cases (see the implementation).
4827 *
4828 * @note Locks this object for writing.
4829 */
4830HRESULT Console::updateMachineState(MachineState_T aMachineState)
4831{
4832 AutoCaller autoCaller(this);
4833 AssertComRCReturnRC(autoCaller.rc());
4834
4835 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4836
4837 AssertReturn( mMachineState == MachineState_Saving
4838 || mMachineState == MachineState_LiveSnapshotting
4839 || mMachineState == MachineState_RestoringSnapshot
4840 || mMachineState == MachineState_DeletingSnapshot
4841 || mMachineState == MachineState_DeletingSnapshotOnline
4842 || mMachineState == MachineState_DeletingSnapshotPaused
4843 , E_FAIL);
4844
4845 return setMachineStateLocally(aMachineState);
4846}
4847
4848/**
4849 * @note Locks this object for writing.
4850 */
4851void Console::onMousePointerShapeChange(bool fVisible, bool fAlpha,
4852 uint32_t xHot, uint32_t yHot,
4853 uint32_t width, uint32_t height,
4854 ComSafeArrayIn(BYTE,pShape))
4855{
4856#if 0
4857 LogFlowThisFuncEnter();
4858 LogFlowThisFunc(("fVisible=%d, fAlpha=%d, xHot = %d, yHot = %d, width=%d, height=%d, shape=%p\n",
4859 fVisible, fAlpha, xHot, yHot, width, height, pShape));
4860#endif
4861
4862 AutoCaller autoCaller(this);
4863 AssertComRCReturnVoid(autoCaller.rc());
4864
4865 /* We need a write lock because we alter the cached callback data */
4866 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4867
4868 /* Save the callback arguments */
4869 mCallbackData.mpsc.visible = fVisible;
4870 mCallbackData.mpsc.alpha = fAlpha;
4871 mCallbackData.mpsc.xHot = xHot;
4872 mCallbackData.mpsc.yHot = yHot;
4873 mCallbackData.mpsc.width = width;
4874 mCallbackData.mpsc.height = height;
4875
4876 /* start with not valid */
4877 bool wasValid = mCallbackData.mpsc.valid;
4878 mCallbackData.mpsc.valid = false;
4879
4880 com::SafeArray<BYTE> aShape(ComSafeArrayInArg(pShape));
4881 if (aShape.size() != 0)
4882 mCallbackData.mpsc.shape.initFrom(aShape);
4883 else
4884 mCallbackData.mpsc.shape.resize(0);
4885 mCallbackData.mpsc.valid = true;
4886
4887 fireMousePointerShapeChangedEvent(mEventSource, fVisible, fAlpha, xHot, yHot, width, height, ComSafeArrayInArg(pShape));
4888
4889#if 0
4890 LogFlowThisFuncLeave();
4891#endif
4892}
4893
4894/**
4895 * @note Locks this object for writing.
4896 */
4897void Console::onMouseCapabilityChange(BOOL supportsAbsolute, BOOL supportsRelative, BOOL needsHostCursor)
4898{
4899 LogFlowThisFunc(("supportsAbsolute=%d supportsRelative=%d needsHostCursor=%d\n",
4900 supportsAbsolute, supportsRelative, needsHostCursor));
4901
4902 AutoCaller autoCaller(this);
4903 AssertComRCReturnVoid(autoCaller.rc());
4904
4905 /* We need a write lock because we alter the cached callback data */
4906 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4907
4908 /* save the callback arguments */
4909 mCallbackData.mcc.supportsAbsolute = supportsAbsolute;
4910 mCallbackData.mcc.supportsRelative = supportsRelative;
4911 mCallbackData.mcc.needsHostCursor = needsHostCursor;
4912 mCallbackData.mcc.valid = true;
4913
4914 fireMouseCapabilityChangedEvent(mEventSource, supportsAbsolute, supportsRelative, needsHostCursor);
4915}
4916
4917/**
4918 * @note Locks this object for reading.
4919 */
4920void Console::onStateChange(MachineState_T machineState)
4921{
4922 AutoCaller autoCaller(this);
4923 AssertComRCReturnVoid(autoCaller.rc());
4924
4925 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4926 fireStateChangedEvent(mEventSource, machineState);
4927}
4928
4929/**
4930 * @note Locks this object for reading.
4931 */
4932void Console::onAdditionsStateChange()
4933{
4934 AutoCaller autoCaller(this);
4935 AssertComRCReturnVoid(autoCaller.rc());
4936
4937 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4938 fireAdditionsStateChangedEvent(mEventSource);
4939}
4940
4941/**
4942 * @note Locks this object for reading.
4943 * This notification only is for reporting an incompatible
4944 * Guest Additions interface, *not* the Guest Additions version!
4945 *
4946 * The user will be notified inside the guest if new Guest
4947 * Additions are available (via VBoxTray/VBoxClient).
4948 */
4949void Console::onAdditionsOutdated()
4950{
4951 AutoCaller autoCaller(this);
4952 AssertComRCReturnVoid(autoCaller.rc());
4953
4954 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4955}
4956
4957/**
4958 * @note Locks this object for writing.
4959 */
4960void Console::onKeyboardLedsChange(bool fNumLock, bool fCapsLock, bool fScrollLock)
4961{
4962 AutoCaller autoCaller(this);
4963 AssertComRCReturnVoid(autoCaller.rc());
4964
4965 /* We need a write lock because we alter the cached callback data */
4966 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4967
4968 /* save the callback arguments */
4969 mCallbackData.klc.numLock = fNumLock;
4970 mCallbackData.klc.capsLock = fCapsLock;
4971 mCallbackData.klc.scrollLock = fScrollLock;
4972 mCallbackData.klc.valid = true;
4973
4974 fireKeyboardLedsChangedEvent(mEventSource, fNumLock, fCapsLock, fScrollLock);
4975}
4976
4977/**
4978 * @note Locks this object for reading.
4979 */
4980void Console::onUSBDeviceStateChange(IUSBDevice *aDevice, bool aAttached,
4981 IVirtualBoxErrorInfo *aError)
4982{
4983 AutoCaller autoCaller(this);
4984 AssertComRCReturnVoid(autoCaller.rc());
4985
4986 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4987 fireUSBDeviceStateChangedEvent(mEventSource, aDevice, aAttached, aError);
4988}
4989
4990/**
4991 * @note Locks this object for reading.
4992 */
4993void Console::onRuntimeError(BOOL aFatal, IN_BSTR aErrorID, IN_BSTR aMessage)
4994{
4995 AutoCaller autoCaller(this);
4996 AssertComRCReturnVoid(autoCaller.rc());
4997
4998 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4999 fireRuntimeErrorEvent(mEventSource, aFatal, aErrorID, aMessage);
5000}
5001
5002/**
5003 * @note Locks this object for reading.
5004 */
5005HRESULT Console::onShowWindow(BOOL aCheck, BOOL *aCanShow, LONG64 *aWinId)
5006{
5007 AssertReturn(aCanShow, E_POINTER);
5008 AssertReturn(aWinId, E_POINTER);
5009
5010 *aCanShow = FALSE;
5011 *aWinId = 0;
5012
5013 AutoCaller autoCaller(this);
5014 AssertComRCReturnRC(autoCaller.rc());
5015
5016 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5017 VBoxEventDesc evDesc;
5018
5019 if (aCheck)
5020 {
5021 evDesc.init(mEventSource, VBoxEventType_OnCanShowWindow);
5022 BOOL fDelivered = evDesc.fire(5000); /* Wait up to 5 secs for delivery */
5023 //Assert(fDelivered);
5024 if (fDelivered)
5025 {
5026 ComPtr<IEvent> pEvent;
5027 evDesc.getEvent(pEvent.asOutParam());
5028 // bit clumsy
5029 ComPtr<ICanShowWindowEvent> pCanShowEvent = pEvent;
5030 if (pCanShowEvent)
5031 {
5032 BOOL fVetoed = FALSE;
5033 pCanShowEvent->IsVetoed(&fVetoed);
5034 *aCanShow = !fVetoed;
5035 }
5036 else
5037 {
5038 Assert(FALSE);
5039 *aCanShow = TRUE;
5040 }
5041 }
5042 else
5043 *aCanShow = TRUE;
5044 }
5045 else
5046 {
5047 evDesc.init(mEventSource, VBoxEventType_OnShowWindow, INT64_C(0));
5048 BOOL fDelivered = evDesc.fire(5000); /* Wait up to 5 secs for delivery */
5049 //Assert(fDelivered);
5050 if (fDelivered)
5051 {
5052 ComPtr<IEvent> pEvent;
5053 evDesc.getEvent(pEvent.asOutParam());
5054 ComPtr<IShowWindowEvent> pShowEvent = pEvent;
5055 LONG64 aEvWinId = 0;
5056 if (pShowEvent)
5057 {
5058 pShowEvent->COMGETTER(WinId)(&aEvWinId);
5059 if ((aEvWinId != 0) && (*aWinId == 0))
5060 *aWinId = aEvWinId;
5061 }
5062 else
5063 Assert(FALSE);
5064 }
5065 }
5066
5067 return S_OK;
5068}
5069
5070// private methods
5071////////////////////////////////////////////////////////////////////////////////
5072
5073/**
5074 * Increases the usage counter of the mpVM pointer. Guarantees that
5075 * VMR3Destroy() will not be called on it at least until releaseVMCaller()
5076 * is called.
5077 *
5078 * If this method returns a failure, the caller is not allowed to use mpVM
5079 * and may return the failed result code to the upper level. This method sets
5080 * the extended error info on failure if \a aQuiet is false.
5081 *
5082 * Setting \a aQuiet to true is useful for methods that don't want to return
5083 * the failed result code to the caller when this method fails (e.g. need to
5084 * silently check for the mpVM availability).
5085 *
5086 * When mpVM is NULL but \a aAllowNullVM is true, a corresponding error will be
5087 * returned instead of asserting. Having it false is intended as a sanity check
5088 * for methods that have checked mMachineState and expect mpVM *NOT* to be NULL.
5089 *
5090 * @param aQuiet true to suppress setting error info
5091 * @param aAllowNullVM true to accept mpVM being NULL and return a failure
5092 * (otherwise this method will assert if mpVM is NULL)
5093 *
5094 * @note Locks this object for writing.
5095 */
5096HRESULT Console::addVMCaller(bool aQuiet /* = false */,
5097 bool aAllowNullVM /* = false */)
5098{
5099 AutoCaller autoCaller(this);
5100 AssertComRCReturnRC(autoCaller.rc());
5101
5102 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5103
5104 if (mVMDestroying)
5105 {
5106 /* powerDown() is waiting for all callers to finish */
5107 return aQuiet ? E_ACCESSDENIED : setError(E_ACCESSDENIED,
5108 tr("The virtual machine is being powered down"));
5109 }
5110
5111 if (mpVM == NULL)
5112 {
5113 Assert(aAllowNullVM == true);
5114
5115 /* The machine is not powered up */
5116 return aQuiet ? E_ACCESSDENIED : setError(E_ACCESSDENIED,
5117 tr("The virtual machine is not powered up"));
5118 }
5119
5120 ++mVMCallers;
5121
5122 return S_OK;
5123}
5124
5125/**
5126 * Decreases the usage counter of the mpVM pointer. Must always complete
5127 * the addVMCaller() call after the mpVM pointer is no more necessary.
5128 *
5129 * @note Locks this object for writing.
5130 */
5131void Console::releaseVMCaller()
5132{
5133 AutoCaller autoCaller(this);
5134 AssertComRCReturnVoid(autoCaller.rc());
5135
5136 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5137
5138 AssertReturnVoid(mpVM != NULL);
5139
5140 Assert(mVMCallers > 0);
5141 --mVMCallers;
5142
5143 if (mVMCallers == 0 && mVMDestroying)
5144 {
5145 /* inform powerDown() there are no more callers */
5146 RTSemEventSignal(mVMZeroCallersSem);
5147 }
5148}
5149
5150/**
5151 * Initialize the release logging facility. In case something
5152 * goes wrong, there will be no release logging. Maybe in the future
5153 * we can add some logic to use different file names in this case.
5154 * Note that the logic must be in sync with Machine::DeleteSettings().
5155 */
5156HRESULT Console::consoleInitReleaseLog(const ComPtr<IMachine> aMachine)
5157{
5158 HRESULT hrc = S_OK;
5159
5160 Bstr logFolder;
5161 hrc = aMachine->COMGETTER(LogFolder)(logFolder.asOutParam());
5162 if (FAILED(hrc)) return hrc;
5163
5164 Utf8Str logDir = logFolder;
5165
5166 /* make sure the Logs folder exists */
5167 Assert(logDir.length());
5168 if (!RTDirExists(logDir.c_str()))
5169 RTDirCreateFullPath(logDir.c_str(), 0777);
5170
5171 Utf8Str logFile = Utf8StrFmt("%s%cVBox.log",
5172 logDir.c_str(), RTPATH_DELIMITER);
5173 Utf8Str pngFile = Utf8StrFmt("%s%cVBox.png",
5174 logDir.c_str(), RTPATH_DELIMITER);
5175
5176 /*
5177 * Age the old log files
5178 * Rename .(n-1) to .(n), .(n-2) to .(n-1), ..., and the last log file to .1
5179 * Overwrite target files in case they exist.
5180 */
5181 ComPtr<IVirtualBox> pVirtualBox;
5182 aMachine->COMGETTER(Parent)(pVirtualBox.asOutParam());
5183 ComPtr<ISystemProperties> pSystemProperties;
5184 pVirtualBox->COMGETTER(SystemProperties)(pSystemProperties.asOutParam());
5185 ULONG cHistoryFiles = 3;
5186 pSystemProperties->COMGETTER(LogHistoryCount)(&cHistoryFiles);
5187 if (cHistoryFiles)
5188 {
5189 for (int i = cHistoryFiles-1; i >= 0; i--)
5190 {
5191 Utf8Str *files[] = { &logFile, &pngFile };
5192 Utf8Str oldName, newName;
5193
5194 for (unsigned int j = 0; j < RT_ELEMENTS(files); ++ j)
5195 {
5196 if (i > 0)
5197 oldName = Utf8StrFmt("%s.%d", files[j]->c_str(), i);
5198 else
5199 oldName = *files[j];
5200 newName = Utf8StrFmt("%s.%d", files[j]->c_str(), i + 1);
5201 /* If the old file doesn't exist, delete the new file (if it
5202 * exists) to provide correct rotation even if the sequence is
5203 * broken */
5204 if ( RTFileRename(oldName.c_str(), newName.c_str(), RTFILEMOVE_FLAGS_REPLACE)
5205 == VERR_FILE_NOT_FOUND)
5206 RTFileDelete(newName.c_str());
5207 }
5208 }
5209 }
5210
5211 PRTLOGGER loggerRelease;
5212 static const char * const s_apszGroups[] = VBOX_LOGGROUP_NAMES;
5213 RTUINT fFlags = RTLOGFLAGS_PREFIX_TIME_PROG;
5214#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
5215 fFlags |= RTLOGFLAGS_USECRLF;
5216#endif
5217 char szError[RTPATH_MAX + 128] = "";
5218 int vrc = RTLogCreateEx(&loggerRelease, fFlags, "all",
5219 "VBOX_RELEASE_LOG", RT_ELEMENTS(s_apszGroups), s_apszGroups,
5220 RTLOGDEST_FILE, szError, sizeof(szError), logFile.c_str());
5221 if (RT_SUCCESS(vrc))
5222 {
5223 /* some introductory information */
5224 RTTIMESPEC timeSpec;
5225 char szTmp[256];
5226 RTTimeSpecToString(RTTimeNow(&timeSpec), szTmp, sizeof(szTmp));
5227 RTLogRelLogger(loggerRelease, 0, ~0U,
5228 "VirtualBox %s r%u %s (%s %s) release log\n"
5229#ifdef VBOX_BLEEDING_EDGE
5230 "EXPERIMENTAL build " VBOX_BLEEDING_EDGE "\n"
5231#endif
5232 "Log opened %s\n",
5233 VBOX_VERSION_STRING, RTBldCfgRevision(), VBOX_BUILD_TARGET,
5234 __DATE__, __TIME__, szTmp);
5235
5236 vrc = RTSystemQueryOSInfo(RTSYSOSINFO_PRODUCT, szTmp, sizeof(szTmp));
5237 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
5238 RTLogRelLogger(loggerRelease, 0, ~0U, "OS Product: %s\n", szTmp);
5239 vrc = RTSystemQueryOSInfo(RTSYSOSINFO_RELEASE, szTmp, sizeof(szTmp));
5240 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
5241 RTLogRelLogger(loggerRelease, 0, ~0U, "OS Release: %s\n", szTmp);
5242 vrc = RTSystemQueryOSInfo(RTSYSOSINFO_VERSION, szTmp, sizeof(szTmp));
5243 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
5244 RTLogRelLogger(loggerRelease, 0, ~0U, "OS Version: %s\n", szTmp);
5245 vrc = RTSystemQueryOSInfo(RTSYSOSINFO_SERVICE_PACK, szTmp, sizeof(szTmp));
5246 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
5247 RTLogRelLogger(loggerRelease, 0, ~0U, "OS Service Pack: %s\n", szTmp);
5248 vrc = RTSystemQueryDmiString(RTSYSDMISTR_PRODUCT_NAME, szTmp, sizeof(szTmp));
5249 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
5250 RTLogRelLogger(loggerRelease, 0, ~0U, "DMI Product Name: %s\n", szTmp);
5251 vrc = RTSystemQueryDmiString(RTSYSDMISTR_PRODUCT_VERSION, szTmp, sizeof(szTmp));
5252 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
5253 RTLogRelLogger(loggerRelease, 0, ~0U, "DMI Product Version: %s\n", szTmp);
5254
5255 ComPtr<IHost> pHost;
5256 pVirtualBox->COMGETTER(Host)(pHost.asOutParam());
5257 ULONG cMbHostRam = 0;
5258 ULONG cMbHostRamAvail = 0;
5259 pHost->COMGETTER(MemorySize)(&cMbHostRam);
5260 pHost->COMGETTER(MemoryAvailable)(&cMbHostRamAvail);
5261 RTLogRelLogger(loggerRelease, 0, ~0U, "Host RAM: %uMB RAM, available: %uMB\n",
5262 cMbHostRam, cMbHostRamAvail);
5263
5264 /* the package type is interesting for Linux distributions */
5265 char szExecName[RTPATH_MAX];
5266 char *pszExecName = RTProcGetExecutablePath(szExecName, sizeof(szExecName));
5267 RTLogRelLogger(loggerRelease, 0, ~0U,
5268 "Executable: %s\n"
5269 "Process ID: %u\n"
5270 "Package type: %s"
5271#ifdef VBOX_OSE
5272 " (OSE)"
5273#endif
5274 "\n",
5275 pszExecName ? pszExecName : "unknown",
5276 RTProcSelf(),
5277 VBOX_PACKAGE_STRING);
5278
5279 /* register this logger as the release logger */
5280 RTLogRelSetDefaultInstance(loggerRelease);
5281 hrc = S_OK;
5282
5283 /* Explicitly flush the log in case of VBOX_RELEASE_LOG=buffered. */
5284 RTLogFlush(loggerRelease);
5285 }
5286 else
5287 hrc = setError(E_FAIL,
5288 tr("Failed to open release log (%s, %Rrc)"),
5289 szError, vrc);
5290
5291 /* If we've made any directory changes, flush the directory to increase
5292 the likelihood that the log file will be usable after a system panic.
5293
5294 Tip: Try 'export VBOX_RELEASE_LOG_FLAGS=flush' if the last bits of the log
5295 is missing. Just don't have too high hopes for this to help. */
5296 if (SUCCEEDED(hrc) || cHistoryFiles)
5297 RTDirFlush(logDir.c_str());
5298
5299 return hrc;
5300}
5301
5302/**
5303 * Common worker for PowerUp and PowerUpPaused.
5304 *
5305 * @returns COM status code.
5306 *
5307 * @param aProgress Where to return the progress object.
5308 * @param aPaused true if PowerUpPaused called.
5309 *
5310 * @todo move down to powerDown();
5311 */
5312HRESULT Console::powerUp(IProgress **aProgress, bool aPaused)
5313{
5314 if (aProgress == NULL)
5315 return E_POINTER;
5316
5317 LogFlowThisFuncEnter();
5318 LogFlowThisFunc(("mMachineState=%d\n", mMachineState));
5319
5320 AutoCaller autoCaller(this);
5321 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5322
5323 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5324
5325 if (Global::IsOnlineOrTransient(mMachineState))
5326 return setError(VBOX_E_INVALID_VM_STATE,
5327 tr("The virtual machine is already running or busy (machine state: %s)"),
5328 Global::stringifyMachineState(mMachineState));
5329
5330 HRESULT rc = S_OK;
5331
5332 /* the network cards will undergo a quick consistency check */
5333 for (ULONG slot = 0;
5334 slot < SchemaDefs::NetworkAdapterCount;
5335 ++slot)
5336 {
5337 ComPtr<INetworkAdapter> pNetworkAdapter;
5338 mMachine->GetNetworkAdapter(slot, pNetworkAdapter.asOutParam());
5339 BOOL enabled = FALSE;
5340 pNetworkAdapter->COMGETTER(Enabled)(&enabled);
5341 if (!enabled)
5342 continue;
5343
5344 NetworkAttachmentType_T netattach;
5345 pNetworkAdapter->COMGETTER(AttachmentType)(&netattach);
5346 switch (netattach)
5347 {
5348 case NetworkAttachmentType_Bridged:
5349 {
5350#ifdef RT_OS_WINDOWS
5351 /* a valid host interface must have been set */
5352 Bstr hostif;
5353 pNetworkAdapter->COMGETTER(HostInterface)(hostif.asOutParam());
5354 if (hostif.isEmpty())
5355 {
5356 return setError(VBOX_E_HOST_ERROR,
5357 tr("VM cannot start because host interface networking requires a host interface name to be set"));
5358 }
5359 ComPtr<IVirtualBox> pVirtualBox;
5360 mMachine->COMGETTER(Parent)(pVirtualBox.asOutParam());
5361 ComPtr<IHost> pHost;
5362 pVirtualBox->COMGETTER(Host)(pHost.asOutParam());
5363 ComPtr<IHostNetworkInterface> pHostInterface;
5364 if (!SUCCEEDED(pHost->FindHostNetworkInterfaceByName(hostif.raw(),
5365 pHostInterface.asOutParam())))
5366 {
5367 return setError(VBOX_E_HOST_ERROR,
5368 tr("VM cannot start because the host interface '%ls' does not exist"),
5369 hostif.raw());
5370 }
5371#endif /* RT_OS_WINDOWS */
5372 break;
5373 }
5374 default:
5375 break;
5376 }
5377 }
5378
5379 /* Read console data stored in the saved state file (if not yet done) */
5380 rc = loadDataFromSavedState();
5381 if (FAILED(rc)) return rc;
5382
5383 /* Check all types of shared folders and compose a single list */
5384 SharedFolderDataMap sharedFolders;
5385 {
5386 /* first, insert global folders */
5387 for (SharedFolderDataMap::const_iterator it = mGlobalSharedFolders.begin();
5388 it != mGlobalSharedFolders.end(); ++ it)
5389 sharedFolders[it->first] = it->second;
5390
5391 /* second, insert machine folders */
5392 for (SharedFolderDataMap::const_iterator it = mMachineSharedFolders.begin();
5393 it != mMachineSharedFolders.end(); ++ it)
5394 sharedFolders[it->first] = it->second;
5395
5396 /* third, insert console folders */
5397 for (SharedFolderMap::const_iterator it = mSharedFolders.begin();
5398 it != mSharedFolders.end(); ++ it)
5399 sharedFolders[it->first] = SharedFolderData(it->second->getHostPath(),
5400 it->second->isWritable(),
5401 it->second->isAutoMounted());
5402 }
5403
5404 Bstr savedStateFile;
5405
5406 /*
5407 * Saved VMs will have to prove that their saved states seem kosher.
5408 */
5409 if (mMachineState == MachineState_Saved)
5410 {
5411 rc = mMachine->COMGETTER(StateFilePath)(savedStateFile.asOutParam());
5412 if (FAILED(rc)) return rc;
5413 ComAssertRet(!savedStateFile.isEmpty(), E_FAIL);
5414 int vrc = SSMR3ValidateFile(Utf8Str(savedStateFile).c_str(), false /* fChecksumIt */);
5415 if (RT_FAILURE(vrc))
5416 return setError(VBOX_E_FILE_ERROR,
5417 tr("VM cannot start because the saved state file '%ls' is invalid (%Rrc). Delete the saved state prior to starting the VM"),
5418 savedStateFile.raw(), vrc);
5419 }
5420
5421 /* test and clear the TeleporterEnabled property */
5422 BOOL fTeleporterEnabled;
5423 rc = mMachine->COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
5424 if (FAILED(rc)) return rc;
5425#if 0 /** @todo we should save it afterwards, but that isn't necessarily a good idea. Find a better place for this (VBoxSVC). */
5426 if (fTeleporterEnabled)
5427 {
5428 rc = mMachine->COMSETTER(TeleporterEnabled)(FALSE);
5429 if (FAILED(rc)) return rc;
5430 }
5431#endif
5432
5433 /* test the FaultToleranceState property */
5434 FaultToleranceState_T enmFaultToleranceState;
5435 rc = mMachine->COMGETTER(FaultToleranceState)(&enmFaultToleranceState);
5436 if (FAILED(rc)) return rc;
5437 BOOL fFaultToleranceSyncEnabled = (enmFaultToleranceState == FaultToleranceState_Standby);
5438
5439 /* create a progress object to track progress of this operation */
5440 ComObjPtr<Progress> pPowerupProgress;
5441 pPowerupProgress.createObject();
5442 Bstr progressDesc;
5443 if (mMachineState == MachineState_Saved)
5444 progressDesc = tr("Restoring virtual machine");
5445 else if (fTeleporterEnabled)
5446 progressDesc = tr("Teleporting virtual machine");
5447 else if (fFaultToleranceSyncEnabled)
5448 progressDesc = tr("Fault Tolerance syncing of remote virtual machine");
5449 else
5450 progressDesc = tr("Starting virtual machine");
5451 if ( mMachineState == MachineState_Saved
5452 || (!fTeleporterEnabled && !fFaultToleranceSyncEnabled))
5453 rc = pPowerupProgress->init(static_cast<IConsole *>(this),
5454 progressDesc.raw(),
5455 FALSE /* aCancelable */);
5456 else
5457 if (fTeleporterEnabled)
5458 rc = pPowerupProgress->init(static_cast<IConsole *>(this),
5459 progressDesc.raw(),
5460 TRUE /* aCancelable */,
5461 3 /* cOperations */,
5462 10 /* ulTotalOperationsWeight */,
5463 Bstr(tr("Teleporting virtual machine")).raw(),
5464 1 /* ulFirstOperationWeight */,
5465 NULL);
5466 else
5467 if (fFaultToleranceSyncEnabled)
5468 rc = pPowerupProgress->init(static_cast<IConsole *>(this),
5469 progressDesc.raw(),
5470 TRUE /* aCancelable */,
5471 3 /* cOperations */,
5472 10 /* ulTotalOperationsWeight */,
5473 Bstr(tr("Fault Tolerance syncing of remote virtual machine")).raw(),
5474 1 /* ulFirstOperationWeight */,
5475 NULL);
5476
5477 if (FAILED(rc))
5478 return rc;
5479
5480 /* Tell VBoxSVC and Machine about the progress object so they can combine
5481 proxy it to any openRemoteSession caller. */
5482 LogFlowThisFunc(("Calling BeginPowerUp...\n"));
5483 rc = mControl->BeginPowerUp(pPowerupProgress);
5484 if (FAILED(rc))
5485 {
5486 LogFlowThisFunc(("BeginPowerUp failed\n"));
5487 return rc;
5488 }
5489
5490 LogFlowThisFunc(("Checking if canceled...\n"));
5491 BOOL fCanceled;
5492 rc = pPowerupProgress->COMGETTER(Canceled)(&fCanceled);
5493 if (FAILED(rc))
5494 return rc;
5495 if (fCanceled)
5496 {
5497 LogFlowThisFunc(("Canceled in BeginPowerUp\n"));
5498 return setError(E_FAIL, tr("Powerup was canceled"));
5499 }
5500 LogFlowThisFunc(("Not canceled yet.\n"));
5501
5502 /* setup task object and thread to carry out the operation
5503 * asynchronously */
5504
5505 std::auto_ptr<VMPowerUpTask> task(new VMPowerUpTask(this, pPowerupProgress));
5506 ComAssertComRCRetRC(task->rc());
5507
5508 task->mConfigConstructor = configConstructor;
5509 task->mSharedFolders = sharedFolders;
5510 task->mStartPaused = aPaused;
5511 if (mMachineState == MachineState_Saved)
5512 task->mSavedStateFile = savedStateFile;
5513 task->mTeleporterEnabled = fTeleporterEnabled;
5514 task->mEnmFaultToleranceState = enmFaultToleranceState;
5515
5516 /* Reset differencing hard disks for which autoReset is true,
5517 * but only if the machine has no snapshots OR the current snapshot
5518 * is an OFFLINE snapshot; otherwise we would reset the current differencing
5519 * image of an ONLINE snapshot which contains the disk state of the machine
5520 * while it was previously running, but without the corresponding machine
5521 * state, which is equivalent to powering off a running machine and not
5522 * good idea
5523 */
5524 ComPtr<ISnapshot> pCurrentSnapshot;
5525 rc = mMachine->COMGETTER(CurrentSnapshot)(pCurrentSnapshot.asOutParam());
5526 if (FAILED(rc)) return rc;
5527
5528 BOOL fCurrentSnapshotIsOnline = false;
5529 if (pCurrentSnapshot)
5530 {
5531 rc = pCurrentSnapshot->COMGETTER(Online)(&fCurrentSnapshotIsOnline);
5532 if (FAILED(rc)) return rc;
5533 }
5534
5535 if (!fCurrentSnapshotIsOnline)
5536 {
5537 LogFlowThisFunc(("Looking for immutable images to reset\n"));
5538
5539 com::SafeIfaceArray<IMediumAttachment> atts;
5540 rc = mMachine->COMGETTER(MediumAttachments)(ComSafeArrayAsOutParam(atts));
5541 if (FAILED(rc)) return rc;
5542
5543 for (size_t i = 0;
5544 i < atts.size();
5545 ++i)
5546 {
5547 DeviceType_T devType;
5548 rc = atts[i]->COMGETTER(Type)(&devType);
5549 /** @todo later applies to floppies as well */
5550 if (devType == DeviceType_HardDisk)
5551 {
5552 ComPtr<IMedium> pMedium;
5553 rc = atts[i]->COMGETTER(Medium)(pMedium.asOutParam());
5554 if (FAILED(rc)) return rc;
5555
5556 /* needs autoreset? */
5557 BOOL autoReset = FALSE;
5558 rc = pMedium->COMGETTER(AutoReset)(&autoReset);
5559 if (FAILED(rc)) return rc;
5560
5561 if (autoReset)
5562 {
5563 ComPtr<IProgress> pResetProgress;
5564 rc = pMedium->Reset(pResetProgress.asOutParam());
5565 if (FAILED(rc)) return rc;
5566
5567 /* save for later use on the powerup thread */
5568 task->hardDiskProgresses.push_back(pResetProgress);
5569 }
5570 }
5571 }
5572 }
5573 else
5574 LogFlowThisFunc(("Machine has a current snapshot which is online, skipping immutable images reset\n"));
5575
5576 rc = consoleInitReleaseLog(mMachine);
5577 if (FAILED(rc)) return rc;
5578
5579#ifdef RT_OS_SOLARIS
5580 /* setup host core dumper for the VM */
5581 Bstr value;
5582 HRESULT hrc = mMachine->GetExtraData(Bstr("VBoxInternal2/CoreDumpEnabled").raw(), value.asOutParam());
5583 if (SUCCEEDED(hrc) && value == "1")
5584 {
5585 Bstr coreDumpDir, coreDumpReplaceSys, coreDumpLive;
5586 mMachine->GetExtraData(Bstr("VBoxInternal2/CoreDumpDir").raw(), coreDumpDir.asOutParam());
5587 mMachine->GetExtraData(Bstr("VBoxInternal2/CoreDumpReplaceSystemDump").raw(), coreDumpReplaceSys.asOutParam());
5588 mMachine->GetExtraData(Bstr("VBoxInternal2/CoreDumpLive").raw(), coreDumpLive.asOutParam());
5589
5590 uint32_t fCoreFlags = 0;
5591 if ( coreDumpReplaceSys.isEmpty() == false
5592 && Utf8Str(coreDumpReplaceSys).toUInt32() == 1)
5593 {
5594 fCoreFlags |= RTCOREDUMPER_FLAGS_REPLACE_SYSTEM_DUMP;
5595 }
5596
5597 if ( coreDumpLive.isEmpty() == false
5598 && Utf8Str(coreDumpLive).toUInt32() == 1)
5599 {
5600 fCoreFlags |= RTCOREDUMPER_FLAGS_LIVE_CORE;
5601 }
5602
5603 Utf8Str strDumpDir(coreDumpDir);
5604 const char *pszDumpDir = strDumpDir.c_str();
5605 if ( pszDumpDir
5606 && *pszDumpDir == '\0')
5607 pszDumpDir = NULL;
5608
5609 int vrc;
5610 if ( pszDumpDir
5611 && !RTDirExists(pszDumpDir))
5612 {
5613 /*
5614 * Try create the directory.
5615 */
5616 vrc = RTDirCreateFullPath(pszDumpDir, 0777);
5617 if (RT_FAILURE(vrc))
5618 return setError(E_FAIL, "Failed to setup CoreDumper. Couldn't create dump directory '%s' (%Rrc)\n", pszDumpDir, vrc);
5619 }
5620
5621 vrc = RTCoreDumperSetup(pszDumpDir, fCoreFlags);
5622 if (RT_FAILURE(vrc))
5623 return setError(E_FAIL, "Failed to setup CoreDumper (%Rrc)", vrc);
5624 else
5625 LogRel(("CoreDumper setup successful. pszDumpDir=%s fFlags=%#x\n", pszDumpDir ? pszDumpDir : ".", fCoreFlags));
5626 }
5627#endif
5628
5629 /* pass the progress object to the caller if requested */
5630 if (aProgress)
5631 {
5632 if (task->hardDiskProgresses.size() == 0)
5633 {
5634 /* there are no other operations to track, return the powerup
5635 * progress only */
5636 pPowerupProgress.queryInterfaceTo(aProgress);
5637 }
5638 else
5639 {
5640 /* create a combined progress object */
5641 ComObjPtr<CombinedProgress> pProgress;
5642 pProgress.createObject();
5643 VMPowerUpTask::ProgressList progresses(task->hardDiskProgresses);
5644 progresses.push_back(ComPtr<IProgress> (pPowerupProgress));
5645 rc = pProgress->init(static_cast<IConsole *>(this),
5646 progressDesc.raw(), progresses.begin(),
5647 progresses.end());
5648 AssertComRCReturnRC(rc);
5649 pProgress.queryInterfaceTo(aProgress);
5650 }
5651 }
5652
5653 int vrc = RTThreadCreate(NULL, Console::powerUpThread, (void *) task.get(),
5654 0, RTTHREADTYPE_MAIN_WORKER, 0, "VMPowerUp");
5655 if (RT_FAILURE(vrc))
5656 return setError(E_FAIL, "Could not create VMPowerUp thread (%Rrc)", vrc);
5657
5658 /* task is now owned by powerUpThread(), so release it */
5659 task.release();
5660
5661 /* finally, set the state: no right to fail in this method afterwards
5662 * since we've already started the thread and it is now responsible for
5663 * any error reporting and appropriate state change! */
5664
5665 if (mMachineState == MachineState_Saved)
5666 setMachineState(MachineState_Restoring);
5667 else if (fTeleporterEnabled)
5668 setMachineState(MachineState_TeleportingIn);
5669 else if (enmFaultToleranceState == FaultToleranceState_Standby)
5670 setMachineState(MachineState_FaultTolerantSyncing);
5671 else
5672 setMachineState(MachineState_Starting);
5673
5674 LogFlowThisFunc(("mMachineState=%d\n", mMachineState));
5675 LogFlowThisFuncLeave();
5676 return S_OK;
5677}
5678
5679/**
5680 * Internal power off worker routine.
5681 *
5682 * This method may be called only at certain places with the following meaning
5683 * as shown below:
5684 *
5685 * - if the machine state is either Running or Paused, a normal
5686 * Console-initiated powerdown takes place (e.g. PowerDown());
5687 * - if the machine state is Saving, saveStateThread() has successfully done its
5688 * job;
5689 * - if the machine state is Starting or Restoring, powerUpThread() has failed
5690 * to start/load the VM;
5691 * - if the machine state is Stopping, the VM has powered itself off (i.e. not
5692 * as a result of the powerDown() call).
5693 *
5694 * Calling it in situations other than the above will cause unexpected behavior.
5695 *
5696 * Note that this method should be the only one that destroys mpVM and sets it
5697 * to NULL.
5698 *
5699 * @param aProgress Progress object to run (may be NULL).
5700 *
5701 * @note Locks this object for writing.
5702 *
5703 * @note Never call this method from a thread that called addVMCaller() or
5704 * instantiated an AutoVMCaller object; first call releaseVMCaller() or
5705 * release(). Otherwise it will deadlock.
5706 */
5707HRESULT Console::powerDown(Progress *aProgress /*= NULL*/)
5708{
5709 LogFlowThisFuncEnter();
5710
5711 AutoCaller autoCaller(this);
5712 AssertComRCReturnRC(autoCaller.rc());
5713
5714 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5715
5716 /* Total # of steps for the progress object. Must correspond to the
5717 * number of "advance percent count" comments in this method! */
5718 enum { StepCount = 7 };
5719 /* current step */
5720 ULONG step = 0;
5721
5722 HRESULT rc = S_OK;
5723 int vrc = VINF_SUCCESS;
5724
5725 /* sanity */
5726 Assert(mVMDestroying == false);
5727
5728 Assert(mpVM != NULL);
5729
5730 AssertMsg( mMachineState == MachineState_Running
5731 || mMachineState == MachineState_Paused
5732 || mMachineState == MachineState_Stuck
5733 || mMachineState == MachineState_Starting
5734 || mMachineState == MachineState_Stopping
5735 || mMachineState == MachineState_Saving
5736 || mMachineState == MachineState_Restoring
5737 || mMachineState == MachineState_TeleportingPausedVM
5738 || mMachineState == MachineState_FaultTolerantSyncing
5739 || mMachineState == MachineState_TeleportingIn
5740 , ("Invalid machine state: %s\n", Global::stringifyMachineState(mMachineState)));
5741
5742 LogRel(("Console::powerDown(): A request to power off the VM has been issued (mMachineState=%s, InUninit=%d)\n",
5743 Global::stringifyMachineState(mMachineState), autoCaller.state() == InUninit));
5744
5745 /* Check if we need to power off the VM. In case of mVMPoweredOff=true, the
5746 * VM has already powered itself off in vmstateChangeCallback() and is just
5747 * notifying Console about that. In case of Starting or Restoring,
5748 * powerUpThread() is calling us on failure, so the VM is already off at
5749 * that point. */
5750 if ( !mVMPoweredOff
5751 && ( mMachineState == MachineState_Starting
5752 || mMachineState == MachineState_Restoring
5753 || mMachineState == MachineState_FaultTolerantSyncing
5754 || mMachineState == MachineState_TeleportingIn)
5755 )
5756 mVMPoweredOff = true;
5757
5758 /*
5759 * Go to Stopping state if not already there.
5760 *
5761 * Note that we don't go from Saving/Restoring to Stopping because
5762 * vmstateChangeCallback() needs it to set the state to Saved on
5763 * VMSTATE_TERMINATED. In terms of protecting from inappropriate operations
5764 * while leaving the lock below, Saving or Restoring should be fine too.
5765 * Ditto for TeleportingPausedVM -> Teleported.
5766 */
5767 if ( mMachineState != MachineState_Saving
5768 && mMachineState != MachineState_Restoring
5769 && mMachineState != MachineState_Stopping
5770 && mMachineState != MachineState_TeleportingIn
5771 && mMachineState != MachineState_TeleportingPausedVM
5772 && mMachineState != MachineState_FaultTolerantSyncing
5773 )
5774 setMachineState(MachineState_Stopping);
5775
5776 /* ----------------------------------------------------------------------
5777 * DONE with necessary state changes, perform the power down actions (it's
5778 * safe to leave the object lock now if needed)
5779 * ---------------------------------------------------------------------- */
5780
5781 /* Stop the VRDP server to prevent new clients connection while VM is being
5782 * powered off. */
5783 if (mConsoleVRDPServer)
5784 {
5785 LogFlowThisFunc(("Stopping VRDP server...\n"));
5786
5787 /* Leave the lock since EMT will call us back as addVMCaller()
5788 * in updateDisplayData(). */
5789 alock.leave();
5790
5791 mConsoleVRDPServer->Stop();
5792
5793 alock.enter();
5794 }
5795
5796 /* advance percent count */
5797 if (aProgress)
5798 aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
5799
5800
5801 /* ----------------------------------------------------------------------
5802 * Now, wait for all mpVM callers to finish their work if there are still
5803 * some on other threads. NO methods that need mpVM (or initiate other calls
5804 * that need it) may be called after this point
5805 * ---------------------------------------------------------------------- */
5806
5807 /* go to the destroying state to prevent from adding new callers */
5808 mVMDestroying = true;
5809
5810 if (mVMCallers > 0)
5811 {
5812 /* lazy creation */
5813 if (mVMZeroCallersSem == NIL_RTSEMEVENT)
5814 RTSemEventCreate(&mVMZeroCallersSem);
5815
5816 LogFlowThisFunc(("Waiting for mpVM callers (%d) to drop to zero...\n",
5817 mVMCallers));
5818
5819 alock.leave();
5820
5821 RTSemEventWait(mVMZeroCallersSem, RT_INDEFINITE_WAIT);
5822
5823 alock.enter();
5824 }
5825
5826 /* advance percent count */
5827 if (aProgress)
5828 aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
5829
5830 vrc = VINF_SUCCESS;
5831
5832 /*
5833 * Power off the VM if not already done that.
5834 * Leave the lock since EMT will call vmstateChangeCallback.
5835 *
5836 * Note that VMR3PowerOff() may fail here (invalid VMSTATE) if the
5837 * VM-(guest-)initiated power off happened in parallel a ms before this
5838 * call. So far, we let this error pop up on the user's side.
5839 */
5840 if (!mVMPoweredOff)
5841 {
5842 LogFlowThisFunc(("Powering off the VM...\n"));
5843 alock.leave();
5844 vrc = VMR3PowerOff(mpVM);
5845#ifdef VBOX_WITH_EXTPACK
5846 mptrExtPackManager->callAllVmPowerOffHooks(this, mpVM);
5847#endif
5848 alock.enter();
5849 }
5850 else
5851 {
5852 /** @todo r=bird: Doesn't make sense. Please remove after 3.1 has been branched
5853 * off. */
5854 /* reset the flag for future re-use */
5855 mVMPoweredOff = false;
5856 }
5857
5858 /* advance percent count */
5859 if (aProgress)
5860 aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
5861
5862#ifdef VBOX_WITH_HGCM
5863 /* Shutdown HGCM services before destroying the VM. */
5864 if (m_pVMMDev)
5865 {
5866 LogFlowThisFunc(("Shutdown HGCM...\n"));
5867
5868 /* Leave the lock since EMT will call us back as addVMCaller() */
5869 alock.leave();
5870
5871 m_pVMMDev->hgcmShutdown();
5872
5873 alock.enter();
5874 }
5875
5876 /* advance percent count */
5877 if (aProgress)
5878 aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
5879
5880#endif /* VBOX_WITH_HGCM */
5881
5882 LogFlowThisFunc(("Ready for VM destruction.\n"));
5883
5884 /* If we are called from Console::uninit(), then try to destroy the VM even
5885 * on failure (this will most likely fail too, but what to do?..) */
5886 if (RT_SUCCESS(vrc) || autoCaller.state() == InUninit)
5887 {
5888 /* If the machine has an USB controller, release all USB devices
5889 * (symmetric to the code in captureUSBDevices()) */
5890 bool fHasUSBController = false;
5891 {
5892 PPDMIBASE pBase;
5893 vrc = PDMR3QueryLun(mpVM, "usb-ohci", 0, 0, &pBase);
5894 if (RT_SUCCESS(vrc))
5895 {
5896 fHasUSBController = true;
5897 detachAllUSBDevices(false /* aDone */);
5898 }
5899 }
5900
5901 /* Now we've got to destroy the VM as well. (mpVM is not valid beyond
5902 * this point). We leave the lock before calling VMR3Destroy() because
5903 * it will result into calling destructors of drivers associated with
5904 * Console children which may in turn try to lock Console (e.g. by
5905 * instantiating SafeVMPtr to access mpVM). It's safe here because
5906 * mVMDestroying is set which should prevent any activity. */
5907
5908 /* Set mpVM to NULL early just in case if some old code is not using
5909 * addVMCaller()/releaseVMCaller(). */
5910 PVM pVM = mpVM;
5911 mpVM = NULL;
5912
5913 LogFlowThisFunc(("Destroying the VM...\n"));
5914
5915 alock.leave();
5916
5917 vrc = VMR3Destroy(pVM);
5918
5919 /* take the lock again */
5920 alock.enter();
5921
5922 /* advance percent count */
5923 if (aProgress)
5924 aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
5925
5926 if (RT_SUCCESS(vrc))
5927 {
5928 LogFlowThisFunc(("Machine has been destroyed (mMachineState=%d)\n",
5929 mMachineState));
5930 /* Note: the Console-level machine state change happens on the
5931 * VMSTATE_TERMINATE state change in vmstateChangeCallback(). If
5932 * powerDown() is called from EMT (i.e. from vmstateChangeCallback()
5933 * on receiving VM-initiated VMSTATE_OFF), VMSTATE_TERMINATE hasn't
5934 * occurred yet. This is okay, because mMachineState is already
5935 * Stopping in this case, so any other attempt to call PowerDown()
5936 * will be rejected. */
5937 }
5938 else
5939 {
5940 /* bad bad bad, but what to do? */
5941 mpVM = pVM;
5942 rc = setError(VBOX_E_VM_ERROR,
5943 tr("Could not destroy the machine. (Error: %Rrc)"),
5944 vrc);
5945 }
5946
5947 /* Complete the detaching of the USB devices. */
5948 if (fHasUSBController)
5949 detachAllUSBDevices(true /* aDone */);
5950
5951 /* advance percent count */
5952 if (aProgress)
5953 aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
5954 }
5955 else
5956 {
5957 rc = setError(VBOX_E_VM_ERROR,
5958 tr("Could not power off the machine. (Error: %Rrc)"),
5959 vrc);
5960 }
5961
5962 /* Finished with destruction. Note that if something impossible happened and
5963 * we've failed to destroy the VM, mVMDestroying will remain true and
5964 * mMachineState will be something like Stopping, so most Console methods
5965 * will return an error to the caller. */
5966 if (mpVM == NULL)
5967 mVMDestroying = false;
5968
5969 if (SUCCEEDED(rc))
5970 mCallbackData.clear();
5971
5972 /* complete the progress */
5973 if (aProgress)
5974 aProgress->notifyComplete(rc);
5975
5976 LogFlowThisFuncLeave();
5977 return rc;
5978}
5979
5980/**
5981 * @note Locks this object for writing.
5982 */
5983HRESULT Console::setMachineState(MachineState_T aMachineState,
5984 bool aUpdateServer /* = true */)
5985{
5986 AutoCaller autoCaller(this);
5987 AssertComRCReturnRC(autoCaller.rc());
5988
5989 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5990
5991 HRESULT rc = S_OK;
5992
5993 if (mMachineState != aMachineState)
5994 {
5995 LogThisFunc(("machineState=%s -> %s aUpdateServer=%RTbool\n",
5996 Global::stringifyMachineState(mMachineState), Global::stringifyMachineState(aMachineState), aUpdateServer));
5997 mMachineState = aMachineState;
5998
5999 /// @todo (dmik)
6000 // possibly, we need to redo onStateChange() using the dedicated
6001 // Event thread, like it is done in VirtualBox. This will make it
6002 // much safer (no deadlocks possible if someone tries to use the
6003 // console from the callback), however, listeners will lose the
6004 // ability to synchronously react to state changes (is it really
6005 // necessary??)
6006 LogFlowThisFunc(("Doing onStateChange()...\n"));
6007 onStateChange(aMachineState);
6008 LogFlowThisFunc(("Done onStateChange()\n"));
6009
6010 if (aUpdateServer)
6011 {
6012 /* Server notification MUST be done from under the lock; otherwise
6013 * the machine state here and on the server might go out of sync
6014 * which can lead to various unexpected results (like the machine
6015 * state being >= MachineState_Running on the server, while the
6016 * session state is already SessionState_Unlocked at the same time
6017 * there).
6018 *
6019 * Cross-lock conditions should be carefully watched out: calling
6020 * UpdateState we will require Machine and SessionMachine locks
6021 * (remember that here we're holding the Console lock here, and also
6022 * all locks that have been entered by the thread before calling
6023 * this method).
6024 */
6025 LogFlowThisFunc(("Doing mControl->UpdateState()...\n"));
6026 rc = mControl->UpdateState(aMachineState);
6027 LogFlowThisFunc(("mControl->UpdateState()=%08X\n", rc));
6028 }
6029 }
6030
6031 return rc;
6032}
6033
6034/**
6035 * Searches for a shared folder with the given logical name
6036 * in the collection of shared folders.
6037 *
6038 * @param aName logical name of the shared folder
6039 * @param aSharedFolder where to return the found object
6040 * @param aSetError whether to set the error info if the folder is
6041 * not found
6042 * @return
6043 * S_OK when found or E_INVALIDARG when not found
6044 *
6045 * @note The caller must lock this object for writing.
6046 */
6047HRESULT Console::findSharedFolder(CBSTR aName,
6048 ComObjPtr<SharedFolder> &aSharedFolder,
6049 bool aSetError /* = false */)
6050{
6051 /* sanity check */
6052 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
6053
6054 SharedFolderMap::const_iterator it = mSharedFolders.find(aName);
6055 if (it != mSharedFolders.end())
6056 {
6057 aSharedFolder = it->second;
6058 return S_OK;
6059 }
6060
6061 if (aSetError)
6062 setError(VBOX_E_FILE_ERROR,
6063 tr("Could not find a shared folder named '%ls'."),
6064 aName);
6065
6066 return VBOX_E_FILE_ERROR;
6067}
6068
6069/**
6070 * Fetches the list of global or machine shared folders from the server.
6071 *
6072 * @param aGlobal true to fetch global folders.
6073 *
6074 * @note The caller must lock this object for writing.
6075 */
6076HRESULT Console::fetchSharedFolders(BOOL aGlobal)
6077{
6078 /* sanity check */
6079 AssertReturn(AutoCaller(this).state() == InInit ||
6080 isWriteLockOnCurrentThread(), E_FAIL);
6081
6082 /* protect mpVM (if not NULL) */
6083 AutoVMCallerQuietWeak autoVMCaller(this);
6084
6085 HRESULT rc = S_OK;
6086
6087 bool online = mpVM
6088 && autoVMCaller.isOk()
6089 && m_pVMMDev
6090 && m_pVMMDev->isShFlActive();
6091
6092 if (aGlobal)
6093 {
6094 /// @todo grab & process global folders when they are done
6095 }
6096 else
6097 {
6098 SharedFolderDataMap oldFolders;
6099 if (online)
6100 oldFolders = mMachineSharedFolders;
6101
6102 mMachineSharedFolders.clear();
6103
6104 SafeIfaceArray<ISharedFolder> folders;
6105 rc = mMachine->COMGETTER(SharedFolders)(ComSafeArrayAsOutParam(folders));
6106 AssertComRCReturnRC(rc);
6107
6108 for (size_t i = 0; i < folders.size(); ++i)
6109 {
6110 ComPtr<ISharedFolder> pSharedFolder = folders[i];
6111
6112 Bstr name;
6113 Bstr hostPath;
6114 BOOL writable;
6115 BOOL autoMount;
6116
6117 rc = pSharedFolder->COMGETTER(Name)(name.asOutParam());
6118 if (FAILED(rc)) break;
6119 rc = pSharedFolder->COMGETTER(HostPath)(hostPath.asOutParam());
6120 if (FAILED(rc)) break;
6121 rc = pSharedFolder->COMGETTER(Writable)(&writable);
6122 if (FAILED(rc)) break;
6123 rc = pSharedFolder->COMGETTER(AutoMount)(&autoMount);
6124 if (FAILED(rc)) break;
6125
6126 mMachineSharedFolders.insert(std::make_pair(name, SharedFolderData(hostPath, writable, autoMount)));
6127
6128 /* send changes to HGCM if the VM is running */
6129 /// @todo report errors as runtime warnings through VMSetError
6130 if (online)
6131 {
6132 SharedFolderDataMap::iterator it = oldFolders.find(name);
6133 if (it == oldFolders.end() || it->second.mHostPath != hostPath)
6134 {
6135 /* a new machine folder is added or
6136 * the existing machine folder is changed */
6137 if (mSharedFolders.find(name) != mSharedFolders.end())
6138 ; /* the console folder exists, nothing to do */
6139 else
6140 {
6141 /* remove the old machine folder (when changed)
6142 * or the global folder if any (when new) */
6143 if (it != oldFolders.end() ||
6144 mGlobalSharedFolders.find(name) !=
6145 mGlobalSharedFolders.end())
6146 rc = removeSharedFolder(name.raw());
6147 /* create the new machine folder */
6148 rc = createSharedFolder(name.raw(),
6149 SharedFolderData(hostPath,
6150 writable,
6151 autoMount));
6152 }
6153 }
6154 /* forget the processed (or identical) folder */
6155 if (it != oldFolders.end())
6156 oldFolders.erase(it);
6157
6158 rc = S_OK;
6159 }
6160 }
6161
6162 AssertComRCReturnRC(rc);
6163
6164 /* process outdated (removed) folders */
6165 /// @todo report errors as runtime warnings through VMSetError
6166 if (online)
6167 {
6168 for (SharedFolderDataMap::const_iterator it = oldFolders.begin();
6169 it != oldFolders.end(); ++ it)
6170 {
6171 if (mSharedFolders.find(it->first) != mSharedFolders.end())
6172 ; /* the console folder exists, nothing to do */
6173 else
6174 {
6175 /* remove the outdated machine folder */
6176 rc = removeSharedFolder(it->first.raw());
6177 /* create the global folder if there is any */
6178 SharedFolderDataMap::const_iterator git =
6179 mGlobalSharedFolders.find(it->first);
6180 if (git != mGlobalSharedFolders.end())
6181 rc = createSharedFolder(git->first.raw(), git->second);
6182 }
6183 }
6184
6185 rc = S_OK;
6186 }
6187 }
6188
6189 return rc;
6190}
6191
6192/**
6193 * Searches for a shared folder with the given name in the list of machine
6194 * shared folders and then in the list of the global shared folders.
6195 *
6196 * @param aName Name of the folder to search for.
6197 * @param aIt Where to store the pointer to the found folder.
6198 * @return @c true if the folder was found and @c false otherwise.
6199 *
6200 * @note The caller must lock this object for reading.
6201 */
6202bool Console::findOtherSharedFolder(IN_BSTR aName,
6203 SharedFolderDataMap::const_iterator &aIt)
6204{
6205 /* sanity check */
6206 AssertReturn(isWriteLockOnCurrentThread(), false);
6207
6208 /* first, search machine folders */
6209 aIt = mMachineSharedFolders.find(aName);
6210 if (aIt != mMachineSharedFolders.end())
6211 return true;
6212
6213 /* second, search machine folders */
6214 aIt = mGlobalSharedFolders.find(aName);
6215 if (aIt != mGlobalSharedFolders.end())
6216 return true;
6217
6218 return false;
6219}
6220
6221/**
6222 * Calls the HGCM service to add a shared folder definition.
6223 *
6224 * @param aName Shared folder name.
6225 * @param aHostPath Shared folder path.
6226 *
6227 * @note Must be called from under AutoVMCaller and when mpVM != NULL!
6228 * @note Doesn't lock anything.
6229 */
6230HRESULT Console::createSharedFolder(CBSTR aName, SharedFolderData aData)
6231{
6232 ComAssertRet(aName && *aName, E_FAIL);
6233 ComAssertRet(!aData.mHostPath.isEmpty(), E_FAIL);
6234
6235 /* sanity checks */
6236 AssertReturn(mpVM, E_FAIL);
6237 AssertReturn(m_pVMMDev && m_pVMMDev->isShFlActive(), E_FAIL);
6238
6239 VBOXHGCMSVCPARM parms[SHFL_CPARMS_ADD_MAPPING2];
6240 SHFLSTRING *pFolderName, *pMapName;
6241 size_t cbString;
6242
6243 Log(("Adding shared folder '%ls' -> '%ls'\n", aName, aData.mHostPath.raw()));
6244
6245 cbString = (RTUtf16Len(aData.mHostPath.raw()) + 1) * sizeof(RTUTF16);
6246 if (cbString >= UINT16_MAX)
6247 return setError(E_INVALIDARG, tr("The name is too long"));
6248 pFolderName = (SHFLSTRING *) RTMemAllocZ(sizeof(SHFLSTRING) + cbString);
6249 Assert(pFolderName);
6250 memcpy(pFolderName->String.ucs2, aData.mHostPath.raw(), cbString);
6251
6252 pFolderName->u16Size = (uint16_t)cbString;
6253 pFolderName->u16Length = (uint16_t)cbString - sizeof(RTUTF16);
6254
6255 parms[0].type = VBOX_HGCM_SVC_PARM_PTR;
6256 parms[0].u.pointer.addr = pFolderName;
6257 parms[0].u.pointer.size = sizeof(SHFLSTRING) + (uint16_t)cbString;
6258
6259 cbString = (RTUtf16Len(aName) + 1) * sizeof(RTUTF16);
6260 if (cbString >= UINT16_MAX)
6261 {
6262 RTMemFree(pFolderName);
6263 return setError(E_INVALIDARG, tr("The host path is too long"));
6264 }
6265 pMapName = (SHFLSTRING *) RTMemAllocZ(sizeof(SHFLSTRING) + cbString);
6266 Assert(pMapName);
6267 memcpy(pMapName->String.ucs2, aName, cbString);
6268
6269 pMapName->u16Size = (uint16_t)cbString;
6270 pMapName->u16Length = (uint16_t)cbString - sizeof(RTUTF16);
6271
6272 parms[1].type = VBOX_HGCM_SVC_PARM_PTR;
6273 parms[1].u.pointer.addr = pMapName;
6274 parms[1].u.pointer.size = sizeof(SHFLSTRING) + (uint16_t)cbString;
6275
6276 parms[2].type = VBOX_HGCM_SVC_PARM_32BIT;
6277 parms[2].u.uint32 = aData.mWritable;
6278
6279 /*
6280 * Auto-mount flag; is indicated by using the SHFL_CPARMS_ADD_MAPPING2
6281 * define below. This shows the host service that we have supplied
6282 * an additional parameter (auto-mount) and keeps the actual command
6283 * backwards compatible.
6284 */
6285 parms[3].type = VBOX_HGCM_SVC_PARM_32BIT;
6286 parms[3].u.uint32 = aData.mAutoMount;
6287
6288 int vrc = m_pVMMDev->hgcmHostCall("VBoxSharedFolders",
6289 SHFL_FN_ADD_MAPPING,
6290 SHFL_CPARMS_ADD_MAPPING2, &parms[0]);
6291 RTMemFree(pFolderName);
6292 RTMemFree(pMapName);
6293
6294 if (RT_FAILURE(vrc))
6295 return setError(E_FAIL,
6296 tr("Could not create a shared folder '%ls' mapped to '%ls' (%Rrc)"),
6297 aName, aData.mHostPath.raw(), vrc);
6298
6299 return S_OK;
6300}
6301
6302/**
6303 * Calls the HGCM service to remove the shared folder definition.
6304 *
6305 * @param aName Shared folder name.
6306 *
6307 * @note Must be called from under AutoVMCaller and when mpVM != NULL!
6308 * @note Doesn't lock anything.
6309 */
6310HRESULT Console::removeSharedFolder(CBSTR aName)
6311{
6312 ComAssertRet(aName && *aName, E_FAIL);
6313
6314 /* sanity checks */
6315 AssertReturn(mpVM, E_FAIL);
6316 AssertReturn(m_pVMMDev && m_pVMMDev->isShFlActive(), E_FAIL);
6317
6318 VBOXHGCMSVCPARM parms;
6319 SHFLSTRING *pMapName;
6320 size_t cbString;
6321
6322 Log(("Removing shared folder '%ls'\n", aName));
6323
6324 cbString = (RTUtf16Len(aName) + 1) * sizeof(RTUTF16);
6325 if (cbString >= UINT16_MAX)
6326 return setError(E_INVALIDARG, tr("The name is too long"));
6327 pMapName = (SHFLSTRING *) RTMemAllocZ(sizeof(SHFLSTRING) + cbString);
6328 Assert(pMapName);
6329 memcpy(pMapName->String.ucs2, aName, cbString);
6330
6331 pMapName->u16Size = (uint16_t)cbString;
6332 pMapName->u16Length = (uint16_t)cbString - sizeof(RTUTF16);
6333
6334 parms.type = VBOX_HGCM_SVC_PARM_PTR;
6335 parms.u.pointer.addr = pMapName;
6336 parms.u.pointer.size = sizeof(SHFLSTRING) + (uint16_t)cbString;
6337
6338 int vrc = m_pVMMDev->hgcmHostCall("VBoxSharedFolders",
6339 SHFL_FN_REMOVE_MAPPING,
6340 1, &parms);
6341 RTMemFree(pMapName);
6342 if (RT_FAILURE(vrc))
6343 return setError(E_FAIL,
6344 tr("Could not remove the shared folder '%ls' (%Rrc)"),
6345 aName, vrc);
6346
6347 return S_OK;
6348}
6349
6350/**
6351 * VM state callback function. Called by the VMM
6352 * using its state machine states.
6353 *
6354 * Primarily used to handle VM initiated power off, suspend and state saving,
6355 * but also for doing termination completed work (VMSTATE_TERMINATE).
6356 *
6357 * In general this function is called in the context of the EMT.
6358 *
6359 * @param aVM The VM handle.
6360 * @param aState The new state.
6361 * @param aOldState The old state.
6362 * @param aUser The user argument (pointer to the Console object).
6363 *
6364 * @note Locks the Console object for writing.
6365 */
6366DECLCALLBACK(void) Console::vmstateChangeCallback(PVM aVM,
6367 VMSTATE aState,
6368 VMSTATE aOldState,
6369 void *aUser)
6370{
6371 LogFlowFunc(("Changing state from %s to %s (aVM=%p)\n",
6372 VMR3GetStateName(aOldState), VMR3GetStateName(aState), aVM));
6373
6374 Console *that = static_cast<Console *>(aUser);
6375 AssertReturnVoid(that);
6376
6377 AutoCaller autoCaller(that);
6378
6379 /* Note that we must let this method proceed even if Console::uninit() has
6380 * been already called. In such case this VMSTATE change is a result of:
6381 * 1) powerDown() called from uninit() itself, or
6382 * 2) VM-(guest-)initiated power off. */
6383 AssertReturnVoid( autoCaller.isOk()
6384 || autoCaller.state() == InUninit);
6385
6386 switch (aState)
6387 {
6388 /*
6389 * The VM has terminated
6390 */
6391 case VMSTATE_OFF:
6392 {
6393 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6394
6395 if (that->mVMStateChangeCallbackDisabled)
6396 break;
6397
6398 /* Do we still think that it is running? It may happen if this is a
6399 * VM-(guest-)initiated shutdown/poweroff.
6400 */
6401 if ( that->mMachineState != MachineState_Stopping
6402 && that->mMachineState != MachineState_Saving
6403 && that->mMachineState != MachineState_Restoring
6404 && that->mMachineState != MachineState_TeleportingIn
6405 && that->mMachineState != MachineState_FaultTolerantSyncing
6406 && that->mMachineState != MachineState_TeleportingPausedVM
6407 && !that->mVMIsAlreadyPoweringOff
6408 )
6409 {
6410 LogFlowFunc(("VM has powered itself off but Console still thinks it is running. Notifying.\n"));
6411
6412 /* prevent powerDown() from calling VMR3PowerOff() again */
6413 Assert(that->mVMPoweredOff == false);
6414 that->mVMPoweredOff = true;
6415
6416 /* we are stopping now */
6417 that->setMachineState(MachineState_Stopping);
6418
6419 /* Setup task object and thread to carry out the operation
6420 * asynchronously (if we call powerDown() right here but there
6421 * is one or more mpVM callers (added with addVMCaller()) we'll
6422 * deadlock).
6423 */
6424 std::auto_ptr<VMProgressTask> task(new VMProgressTask(that, NULL /* aProgress */,
6425 true /* aUsesVMPtr */));
6426
6427 /* If creating a task failed, this can currently mean one of
6428 * two: either Console::uninit() has been called just a ms
6429 * before (so a powerDown() call is already on the way), or
6430 * powerDown() itself is being already executed. Just do
6431 * nothing.
6432 */
6433 if (!task->isOk())
6434 {
6435 LogFlowFunc(("Console is already being uninitialized.\n"));
6436 break;
6437 }
6438
6439 int vrc = RTThreadCreate(NULL, Console::powerDownThread,
6440 (void *) task.get(), 0,
6441 RTTHREADTYPE_MAIN_WORKER, 0,
6442 "VMPowerDown");
6443 AssertMsgRCBreak(vrc, ("Could not create VMPowerDown thread (%Rrc)\n", vrc));
6444
6445 /* task is now owned by powerDownThread(), so release it */
6446 task.release();
6447 }
6448 break;
6449 }
6450
6451 /* The VM has been completely destroyed.
6452 *
6453 * Note: This state change can happen at two points:
6454 * 1) At the end of VMR3Destroy() if it was not called from EMT.
6455 * 2) At the end of vmR3EmulationThread if VMR3Destroy() was
6456 * called by EMT.
6457 */
6458 case VMSTATE_TERMINATED:
6459 {
6460 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6461
6462 if (that->mVMStateChangeCallbackDisabled)
6463 break;
6464
6465 /* Terminate host interface networking. If aVM is NULL, we've been
6466 * manually called from powerUpThread() either before calling
6467 * VMR3Create() or after VMR3Create() failed, so no need to touch
6468 * networking.
6469 */
6470 if (aVM)
6471 that->powerDownHostInterfaces();
6472
6473 /* From now on the machine is officially powered down or remains in
6474 * the Saved state.
6475 */
6476 switch (that->mMachineState)
6477 {
6478 default:
6479 AssertFailed();
6480 /* fall through */
6481 case MachineState_Stopping:
6482 /* successfully powered down */
6483 that->setMachineState(MachineState_PoweredOff);
6484 break;
6485 case MachineState_Saving:
6486 /* successfully saved */
6487 that->setMachineState(MachineState_Saved);
6488 break;
6489 case MachineState_Starting:
6490 /* failed to start, but be patient: set back to PoweredOff
6491 * (for similarity with the below) */
6492 that->setMachineState(MachineState_PoweredOff);
6493 break;
6494 case MachineState_Restoring:
6495 /* failed to load the saved state file, but be patient: set
6496 * back to Saved (to preserve the saved state file) */
6497 that->setMachineState(MachineState_Saved);
6498 break;
6499 case MachineState_TeleportingIn:
6500 /* Teleportation failed or was canceled. Back to powered off. */
6501 that->setMachineState(MachineState_PoweredOff);
6502 break;
6503 case MachineState_TeleportingPausedVM:
6504 /* Successfully teleported the VM. */
6505 that->setMachineState(MachineState_Teleported);
6506 break;
6507 case MachineState_FaultTolerantSyncing:
6508 /* Fault tolerant sync failed or was canceled. Back to powered off. */
6509 that->setMachineState(MachineState_PoweredOff);
6510 break;
6511 }
6512 break;
6513 }
6514
6515 case VMSTATE_SUSPENDED:
6516 {
6517 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6518
6519 if (that->mVMStateChangeCallbackDisabled)
6520 break;
6521
6522 switch (that->mMachineState)
6523 {
6524 case MachineState_Teleporting:
6525 that->setMachineState(MachineState_TeleportingPausedVM);
6526 break;
6527
6528 case MachineState_LiveSnapshotting:
6529 that->setMachineState(MachineState_Saving);
6530 break;
6531
6532 case MachineState_TeleportingPausedVM:
6533 case MachineState_Saving:
6534 case MachineState_Restoring:
6535 case MachineState_Stopping:
6536 case MachineState_TeleportingIn:
6537 case MachineState_FaultTolerantSyncing:
6538 /* The worker thread handles the transition. */
6539 break;
6540
6541 default:
6542 AssertMsgFailed(("%s\n", Global::stringifyMachineState(that->mMachineState)));
6543 case MachineState_Running:
6544 that->setMachineState(MachineState_Paused);
6545 break;
6546
6547 case MachineState_Paused:
6548 /* Nothing to do. */
6549 break;
6550 }
6551 break;
6552 }
6553
6554 case VMSTATE_SUSPENDED_LS:
6555 case VMSTATE_SUSPENDED_EXT_LS:
6556 {
6557 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6558 if (that->mVMStateChangeCallbackDisabled)
6559 break;
6560 switch (that->mMachineState)
6561 {
6562 case MachineState_Teleporting:
6563 that->setMachineState(MachineState_TeleportingPausedVM);
6564 break;
6565
6566 case MachineState_LiveSnapshotting:
6567 that->setMachineState(MachineState_Saving);
6568 break;
6569
6570 case MachineState_TeleportingPausedVM:
6571 case MachineState_Saving:
6572 /* ignore */
6573 break;
6574
6575 default:
6576 AssertMsgFailed(("%s/%s -> %s\n", Global::stringifyMachineState(that->mMachineState), VMR3GetStateName(aOldState), VMR3GetStateName(aState) ));
6577 that->setMachineState(MachineState_Paused);
6578 break;
6579 }
6580 break;
6581 }
6582
6583 case VMSTATE_RUNNING:
6584 {
6585 if ( aOldState == VMSTATE_POWERING_ON
6586 || aOldState == VMSTATE_RESUMING
6587 || aOldState == VMSTATE_RUNNING_FT)
6588 {
6589 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6590
6591 if (that->mVMStateChangeCallbackDisabled)
6592 break;
6593
6594 Assert( ( ( that->mMachineState == MachineState_Starting
6595 || that->mMachineState == MachineState_Paused)
6596 && aOldState == VMSTATE_POWERING_ON)
6597 || ( ( that->mMachineState == MachineState_Restoring
6598 || that->mMachineState == MachineState_TeleportingIn
6599 || that->mMachineState == MachineState_Paused
6600 || that->mMachineState == MachineState_Saving
6601 )
6602 && aOldState == VMSTATE_RESUMING)
6603 || ( that->mMachineState == MachineState_FaultTolerantSyncing
6604 && aOldState == VMSTATE_RUNNING_FT));
6605
6606 that->setMachineState(MachineState_Running);
6607 }
6608
6609 break;
6610 }
6611
6612 case VMSTATE_RUNNING_LS:
6613 AssertMsg( that->mMachineState == MachineState_LiveSnapshotting
6614 || that->mMachineState == MachineState_Teleporting,
6615 ("%s/%s -> %s\n", Global::stringifyMachineState(that->mMachineState), VMR3GetStateName(aOldState), VMR3GetStateName(aState) ));
6616 break;
6617
6618 case VMSTATE_RUNNING_FT:
6619 AssertMsg(that->mMachineState == MachineState_FaultTolerantSyncing,
6620 ("%s/%s -> %s\n", Global::stringifyMachineState(that->mMachineState), VMR3GetStateName(aOldState), VMR3GetStateName(aState) ));
6621 break;
6622
6623 case VMSTATE_FATAL_ERROR:
6624 {
6625 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6626
6627 if (that->mVMStateChangeCallbackDisabled)
6628 break;
6629
6630 /* Fatal errors are only for running VMs. */
6631 Assert(Global::IsOnline(that->mMachineState));
6632
6633 /* Note! 'Pause' is used here in want of something better. There
6634 * are currently only two places where fatal errors might be
6635 * raised, so it is not worth adding a new externally
6636 * visible state for this yet. */
6637 that->setMachineState(MachineState_Paused);
6638 break;
6639 }
6640
6641 case VMSTATE_GURU_MEDITATION:
6642 {
6643 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6644
6645 if (that->mVMStateChangeCallbackDisabled)
6646 break;
6647
6648 /* Guru are only for running VMs */
6649 Assert(Global::IsOnline(that->mMachineState));
6650
6651 that->setMachineState(MachineState_Stuck);
6652 break;
6653 }
6654
6655 default: /* shut up gcc */
6656 break;
6657 }
6658}
6659
6660#ifdef VBOX_WITH_USB
6661
6662/**
6663 * Sends a request to VMM to attach the given host device.
6664 * After this method succeeds, the attached device will appear in the
6665 * mUSBDevices collection.
6666 *
6667 * @param aHostDevice device to attach
6668 *
6669 * @note Synchronously calls EMT.
6670 * @note Must be called from under this object's lock.
6671 */
6672HRESULT Console::attachUSBDevice(IUSBDevice *aHostDevice, ULONG aMaskedIfs)
6673{
6674 AssertReturn(aHostDevice, E_FAIL);
6675 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
6676
6677 /* still want a lock object because we need to leave it */
6678 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6679
6680 HRESULT hrc;
6681
6682 /*
6683 * Get the address and the Uuid, and call the pfnCreateProxyDevice roothub
6684 * method in EMT (using usbAttachCallback()).
6685 */
6686 Bstr BstrAddress;
6687 hrc = aHostDevice->COMGETTER(Address)(BstrAddress.asOutParam());
6688 ComAssertComRCRetRC(hrc);
6689
6690 Utf8Str Address(BstrAddress);
6691
6692 Bstr id;
6693 hrc = aHostDevice->COMGETTER(Id)(id.asOutParam());
6694 ComAssertComRCRetRC(hrc);
6695 Guid uuid(id);
6696
6697 BOOL fRemote = FALSE;
6698 hrc = aHostDevice->COMGETTER(Remote)(&fRemote);
6699 ComAssertComRCRetRC(hrc);
6700
6701 /* protect mpVM */
6702 AutoVMCaller autoVMCaller(this);
6703 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
6704
6705 LogFlowThisFunc(("Proxying USB device '%s' {%RTuuid}...\n",
6706 Address.c_str(), uuid.raw()));
6707
6708 /* leave the lock before a VMR3* call (EMT will call us back)! */
6709 alock.leave();
6710
6711/** @todo just do everything here and only wrap the PDMR3Usb call. That'll offload some notification stuff from the EMT thread. */
6712 int vrc = VMR3ReqCallWait(mpVM, VMCPUID_ANY,
6713 (PFNRT)usbAttachCallback, 6, this, aHostDevice, uuid.raw(), fRemote, Address.c_str(), aMaskedIfs);
6714
6715 /* restore the lock */
6716 alock.enter();
6717
6718 /* hrc is S_OK here */
6719
6720 if (RT_FAILURE(vrc))
6721 {
6722 LogWarningThisFunc(("Failed to create proxy device for '%s' {%RTuuid} (%Rrc)\n",
6723 Address.c_str(), uuid.raw(), vrc));
6724
6725 switch (vrc)
6726 {
6727 case VERR_VUSB_NO_PORTS:
6728 hrc = setError(E_FAIL,
6729 tr("Failed to attach the USB device. (No available ports on the USB controller)."));
6730 break;
6731 case VERR_VUSB_USBFS_PERMISSION:
6732 hrc = setError(E_FAIL,
6733 tr("Not permitted to open the USB device, check usbfs options"));
6734 break;
6735 default:
6736 hrc = setError(E_FAIL,
6737 tr("Failed to create a proxy device for the USB device. (Error: %Rrc)"),
6738 vrc);
6739 break;
6740 }
6741 }
6742
6743 return hrc;
6744}
6745
6746/**
6747 * USB device attach callback used by AttachUSBDevice().
6748 * Note that AttachUSBDevice() doesn't return until this callback is executed,
6749 * so we don't use AutoCaller and don't care about reference counters of
6750 * interface pointers passed in.
6751 *
6752 * @thread EMT
6753 * @note Locks the console object for writing.
6754 */
6755//static
6756DECLCALLBACK(int)
6757Console::usbAttachCallback(Console *that, IUSBDevice *aHostDevice, PCRTUUID aUuid, bool aRemote, const char *aAddress, ULONG aMaskedIfs)
6758{
6759 LogFlowFuncEnter();
6760 LogFlowFunc(("that={%p}\n", that));
6761
6762 AssertReturn(that && aUuid, VERR_INVALID_PARAMETER);
6763
6764 void *pvRemoteBackend = NULL;
6765 if (aRemote)
6766 {
6767 RemoteUSBDevice *pRemoteUSBDevice = static_cast<RemoteUSBDevice *>(aHostDevice);
6768 Guid guid(*aUuid);
6769
6770 pvRemoteBackend = that->consoleVRDPServer()->USBBackendRequestPointer(pRemoteUSBDevice->clientId(), &guid);
6771 if (!pvRemoteBackend)
6772 return VERR_INVALID_PARAMETER; /* The clientId is invalid then. */
6773 }
6774
6775 USHORT portVersion = 1;
6776 HRESULT hrc = aHostDevice->COMGETTER(PortVersion)(&portVersion);
6777 AssertComRCReturn(hrc, VERR_GENERAL_FAILURE);
6778 Assert(portVersion == 1 || portVersion == 2);
6779
6780 int vrc = PDMR3USBCreateProxyDevice(that->mpVM, aUuid, aRemote, aAddress, pvRemoteBackend,
6781 portVersion == 1 ? VUSB_STDVER_11 : VUSB_STDVER_20, aMaskedIfs);
6782 if (RT_SUCCESS(vrc))
6783 {
6784 /* Create a OUSBDevice and add it to the device list */
6785 ComObjPtr<OUSBDevice> pUSBDevice;
6786 pUSBDevice.createObject();
6787 hrc = pUSBDevice->init(aHostDevice);
6788 AssertComRC(hrc);
6789
6790 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6791 that->mUSBDevices.push_back(pUSBDevice);
6792 LogFlowFunc(("Attached device {%RTuuid}\n", pUSBDevice->id().raw()));
6793
6794 /* notify callbacks */
6795 that->onUSBDeviceStateChange(pUSBDevice, true /* aAttached */, NULL);
6796 }
6797
6798 LogFlowFunc(("vrc=%Rrc\n", vrc));
6799 LogFlowFuncLeave();
6800 return vrc;
6801}
6802
6803/**
6804 * Sends a request to VMM to detach the given host device. After this method
6805 * succeeds, the detached device will disappear from the mUSBDevices
6806 * collection.
6807 *
6808 * @param aIt Iterator pointing to the device to detach.
6809 *
6810 * @note Synchronously calls EMT.
6811 * @note Must be called from under this object's lock.
6812 */
6813HRESULT Console::detachUSBDevice(USBDeviceList::iterator &aIt)
6814{
6815 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
6816
6817 /* still want a lock object because we need to leave it */
6818 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6819
6820 /* protect mpVM */
6821 AutoVMCaller autoVMCaller(this);
6822 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
6823
6824 /* if the device is attached, then there must at least one USB hub. */
6825 AssertReturn(PDMR3USBHasHub(mpVM), E_FAIL);
6826
6827 LogFlowThisFunc(("Detaching USB proxy device {%RTuuid}...\n",
6828 (*aIt)->id().raw()));
6829
6830 /* leave the lock before a VMR3* call (EMT will call us back)! */
6831 alock.leave();
6832
6833/** @todo just do everything here and only wrap the PDMR3Usb call. That'll offload some notification stuff from the EMT thread. */
6834 int vrc = VMR3ReqCallWait(mpVM, VMCPUID_ANY,
6835 (PFNRT) usbDetachCallback, 4, this, &aIt, (*aIt)->id().raw());
6836 ComAssertRCRet(vrc, E_FAIL);
6837
6838 return S_OK;
6839}
6840
6841/**
6842 * USB device detach callback used by DetachUSBDevice().
6843 * Note that DetachUSBDevice() doesn't return until this callback is executed,
6844 * so we don't use AutoCaller and don't care about reference counters of
6845 * interface pointers passed in.
6846 *
6847 * @thread EMT
6848 * @note Locks the console object for writing.
6849 */
6850//static
6851DECLCALLBACK(int)
6852Console::usbDetachCallback(Console *that, USBDeviceList::iterator *aIt, PCRTUUID aUuid)
6853{
6854 LogFlowFuncEnter();
6855 LogFlowFunc(("that={%p}\n", that));
6856
6857 AssertReturn(that && aUuid, VERR_INVALID_PARAMETER);
6858 ComObjPtr<OUSBDevice> pUSBDevice = **aIt;
6859
6860 /*
6861 * If that was a remote device, release the backend pointer.
6862 * The pointer was requested in usbAttachCallback.
6863 */
6864 BOOL fRemote = FALSE;
6865
6866 HRESULT hrc2 = (**aIt)->COMGETTER(Remote)(&fRemote);
6867 if (FAILED(hrc2))
6868 setErrorStatic(hrc2, "GetRemote() failed");
6869
6870 if (fRemote)
6871 {
6872 Guid guid(*aUuid);
6873 that->consoleVRDPServer()->USBBackendReleasePointer(&guid);
6874 }
6875
6876 int vrc = PDMR3USBDetachDevice(that->mpVM, aUuid);
6877
6878 if (RT_SUCCESS(vrc))
6879 {
6880 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6881
6882 /* Remove the device from the collection */
6883 that->mUSBDevices.erase(*aIt);
6884 LogFlowFunc(("Detached device {%RTuuid}\n", pUSBDevice->id().raw()));
6885
6886 /* notify callbacks */
6887 that->onUSBDeviceStateChange(pUSBDevice, false /* aAttached */, NULL);
6888 }
6889
6890 LogFlowFunc(("vrc=%Rrc\n", vrc));
6891 LogFlowFuncLeave();
6892 return vrc;
6893}
6894
6895#endif /* VBOX_WITH_USB */
6896#if ((defined(RT_OS_LINUX) && !defined(VBOX_WITH_NETFLT)) || defined(RT_OS_FREEBSD))
6897
6898/**
6899 * Helper function to handle host interface device creation and attachment.
6900 *
6901 * @param networkAdapter the network adapter which attachment should be reset
6902 * @return COM status code
6903 *
6904 * @note The caller must lock this object for writing.
6905 *
6906 * @todo Move this back into the driver!
6907 */
6908HRESULT Console::attachToTapInterface(INetworkAdapter *networkAdapter)
6909{
6910 LogFlowThisFunc(("\n"));
6911 /* sanity check */
6912 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
6913
6914# ifdef VBOX_STRICT
6915 /* paranoia */
6916 NetworkAttachmentType_T attachment;
6917 networkAdapter->COMGETTER(AttachmentType)(&attachment);
6918 Assert(attachment == NetworkAttachmentType_Bridged);
6919# endif /* VBOX_STRICT */
6920
6921 HRESULT rc = S_OK;
6922
6923 ULONG slot = 0;
6924 rc = networkAdapter->COMGETTER(Slot)(&slot);
6925 AssertComRC(rc);
6926
6927# ifdef RT_OS_LINUX
6928 /*
6929 * Allocate a host interface device
6930 */
6931 int rcVBox = RTFileOpen(&maTapFD[slot], "/dev/net/tun",
6932 RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_INHERIT);
6933 if (RT_SUCCESS(rcVBox))
6934 {
6935 /*
6936 * Set/obtain the tap interface.
6937 */
6938 struct ifreq IfReq;
6939 memset(&IfReq, 0, sizeof(IfReq));
6940 /* The name of the TAP interface we are using */
6941 Bstr tapDeviceName;
6942 rc = networkAdapter->COMGETTER(HostInterface)(tapDeviceName.asOutParam());
6943 if (FAILED(rc))
6944 tapDeviceName.setNull(); /* Is this necessary? */
6945 if (tapDeviceName.isEmpty())
6946 {
6947 LogRel(("No TAP device name was supplied.\n"));
6948 rc = setError(E_FAIL, tr("No TAP device name was supplied for the host networking interface"));
6949 }
6950
6951 if (SUCCEEDED(rc))
6952 {
6953 /* If we are using a static TAP device then try to open it. */
6954 Utf8Str str(tapDeviceName);
6955 if (str.length() <= sizeof(IfReq.ifr_name))
6956 strcpy(IfReq.ifr_name, str.c_str());
6957 else
6958 memcpy(IfReq.ifr_name, str.c_str(), sizeof(IfReq.ifr_name) - 1); /** @todo bitch about names which are too long... */
6959 IfReq.ifr_flags = IFF_TAP | IFF_NO_PI;
6960 rcVBox = ioctl(maTapFD[slot], TUNSETIFF, &IfReq);
6961 if (rcVBox != 0)
6962 {
6963 LogRel(("Failed to open the host network interface %ls\n", tapDeviceName.raw()));
6964 rc = setError(E_FAIL,
6965 tr("Failed to open the host network interface %ls"),
6966 tapDeviceName.raw());
6967 }
6968 }
6969 if (SUCCEEDED(rc))
6970 {
6971 /*
6972 * Make it pollable.
6973 */
6974 if (fcntl(maTapFD[slot], F_SETFL, O_NONBLOCK) != -1)
6975 {
6976 Log(("attachToTapInterface: %RTfile %ls\n", maTapFD[slot], tapDeviceName.raw()));
6977 /*
6978 * Here is the right place to communicate the TAP file descriptor and
6979 * the host interface name to the server if/when it becomes really
6980 * necessary.
6981 */
6982 maTAPDeviceName[slot] = tapDeviceName;
6983 rcVBox = VINF_SUCCESS;
6984 }
6985 else
6986 {
6987 int iErr = errno;
6988
6989 LogRel(("Configuration error: Failed to configure /dev/net/tun non blocking. Error: %s\n", strerror(iErr)));
6990 rcVBox = VERR_HOSTIF_BLOCKING;
6991 rc = setError(E_FAIL,
6992 tr("could not set up the host networking device for non blocking access: %s"),
6993 strerror(errno));
6994 }
6995 }
6996 }
6997 else
6998 {
6999 LogRel(("Configuration error: Failed to open /dev/net/tun rc=%Rrc\n", rcVBox));
7000 switch (rcVBox)
7001 {
7002 case VERR_ACCESS_DENIED:
7003 /* will be handled by our caller */
7004 rc = rcVBox;
7005 break;
7006 default:
7007 rc = setError(E_FAIL,
7008 tr("Could not set up the host networking device: %Rrc"),
7009 rcVBox);
7010 break;
7011 }
7012 }
7013
7014# elif defined(RT_OS_FREEBSD)
7015 /*
7016 * Set/obtain the tap interface.
7017 */
7018 /* The name of the TAP interface we are using */
7019 Bstr tapDeviceName;
7020 rc = networkAdapter->COMGETTER(HostInterface)(tapDeviceName.asOutParam());
7021 if (FAILED(rc))
7022 tapDeviceName.setNull(); /* Is this necessary? */
7023 if (tapDeviceName.isEmpty())
7024 {
7025 LogRel(("No TAP device name was supplied.\n"));
7026 rc = setError(E_FAIL, tr("No TAP device name was supplied for the host networking interface"));
7027 }
7028 char szTapdev[1024] = "/dev/";
7029 /* If we are using a static TAP device then try to open it. */
7030 Utf8Str str(tapDeviceName);
7031 if (str.length() + strlen(szTapdev) <= sizeof(szTapdev))
7032 strcat(szTapdev, str.c_str());
7033 else
7034 memcpy(szTapdev + strlen(szTapdev), str.c_str(),
7035 sizeof(szTapdev) - strlen(szTapdev) - 1); /** @todo bitch about names which are too long... */
7036 int rcVBox = RTFileOpen(&maTapFD[slot], szTapdev,
7037 RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_INHERIT | RTFILE_O_NON_BLOCK);
7038
7039 if (RT_SUCCESS(rcVBox))
7040 maTAPDeviceName[slot] = tapDeviceName;
7041 else
7042 {
7043 switch (rcVBox)
7044 {
7045 case VERR_ACCESS_DENIED:
7046 /* will be handled by our caller */
7047 rc = rcVBox;
7048 break;
7049 default:
7050 rc = setError(E_FAIL,
7051 tr("Failed to open the host network interface %ls"),
7052 tapDeviceName.raw());
7053 break;
7054 }
7055 }
7056# else
7057# error "huh?"
7058# endif
7059 /* in case of failure, cleanup. */
7060 if (RT_FAILURE(rcVBox) && SUCCEEDED(rc))
7061 {
7062 LogRel(("General failure attaching to host interface\n"));
7063 rc = setError(E_FAIL,
7064 tr("General failure attaching to host interface"));
7065 }
7066 LogFlowThisFunc(("rc=%d\n", rc));
7067 return rc;
7068}
7069
7070
7071/**
7072 * Helper function to handle detachment from a host interface
7073 *
7074 * @param networkAdapter the network adapter which attachment should be reset
7075 * @return COM status code
7076 *
7077 * @note The caller must lock this object for writing.
7078 *
7079 * @todo Move this back into the driver!
7080 */
7081HRESULT Console::detachFromTapInterface(INetworkAdapter *networkAdapter)
7082{
7083 /* sanity check */
7084 LogFlowThisFunc(("\n"));
7085 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
7086
7087 HRESULT rc = S_OK;
7088# ifdef VBOX_STRICT
7089 /* paranoia */
7090 NetworkAttachmentType_T attachment;
7091 networkAdapter->COMGETTER(AttachmentType)(&attachment);
7092 Assert(attachment == NetworkAttachmentType_Bridged);
7093# endif /* VBOX_STRICT */
7094
7095 ULONG slot = 0;
7096 rc = networkAdapter->COMGETTER(Slot)(&slot);
7097 AssertComRC(rc);
7098
7099 /* is there an open TAP device? */
7100 if (maTapFD[slot] != NIL_RTFILE)
7101 {
7102 /*
7103 * Close the file handle.
7104 */
7105 Bstr tapDeviceName, tapTerminateApplication;
7106 bool isStatic = true;
7107 rc = networkAdapter->COMGETTER(HostInterface)(tapDeviceName.asOutParam());
7108 if (FAILED(rc) || tapDeviceName.isEmpty())
7109 {
7110 /* If the name is empty, this is a dynamic TAP device, so close it now,
7111 so that the termination script can remove the interface. Otherwise we still
7112 need the FD to pass to the termination script. */
7113 isStatic = false;
7114 int rcVBox = RTFileClose(maTapFD[slot]);
7115 AssertRC(rcVBox);
7116 maTapFD[slot] = NIL_RTFILE;
7117 }
7118 if (isStatic)
7119 {
7120 /* If we are using a static TAP device, we close it now, after having called the
7121 termination script. */
7122 int rcVBox = RTFileClose(maTapFD[slot]);
7123 AssertRC(rcVBox);
7124 }
7125 /* the TAP device name and handle are no longer valid */
7126 maTapFD[slot] = NIL_RTFILE;
7127 maTAPDeviceName[slot] = "";
7128 }
7129 LogFlowThisFunc(("returning %d\n", rc));
7130 return rc;
7131}
7132
7133#endif /* (RT_OS_LINUX || RT_OS_FREEBSD) && !VBOX_WITH_NETFLT */
7134
7135/**
7136 * Called at power down to terminate host interface networking.
7137 *
7138 * @note The caller must lock this object for writing.
7139 */
7140HRESULT Console::powerDownHostInterfaces()
7141{
7142 LogFlowThisFunc(("\n"));
7143
7144 /* sanity check */
7145 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
7146
7147 /*
7148 * host interface termination handling
7149 */
7150 HRESULT rc;
7151 for (ULONG slot = 0; slot < SchemaDefs::NetworkAdapterCount; slot ++)
7152 {
7153 ComPtr<INetworkAdapter> pNetworkAdapter;
7154 rc = mMachine->GetNetworkAdapter(slot, pNetworkAdapter.asOutParam());
7155 if (FAILED(rc)) break;
7156
7157 BOOL enabled = FALSE;
7158 pNetworkAdapter->COMGETTER(Enabled)(&enabled);
7159 if (!enabled)
7160 continue;
7161
7162 NetworkAttachmentType_T attachment;
7163 pNetworkAdapter->COMGETTER(AttachmentType)(&attachment);
7164 if (attachment == NetworkAttachmentType_Bridged)
7165 {
7166#if defined(RT_OS_LINUX) && !defined(VBOX_WITH_NETFLT)
7167 HRESULT rc2 = detachFromTapInterface(pNetworkAdapter);
7168 if (FAILED(rc2) && SUCCEEDED(rc))
7169 rc = rc2;
7170#endif
7171 }
7172 }
7173
7174 return rc;
7175}
7176
7177
7178/**
7179 * Process callback handler for VMR3LoadFromFile, VMR3LoadFromStream, VMR3Save
7180 * and VMR3Teleport.
7181 *
7182 * @param pVM The VM handle.
7183 * @param uPercent Completion percentage (0-100).
7184 * @param pvUser Pointer to an IProgress instance.
7185 * @return VINF_SUCCESS.
7186 */
7187/*static*/
7188DECLCALLBACK(int) Console::stateProgressCallback(PVM pVM, unsigned uPercent, void *pvUser)
7189{
7190 IProgress *pProgress = static_cast<IProgress *>(pvUser);
7191
7192 /* update the progress object */
7193 if (pProgress)
7194 pProgress->SetCurrentOperationProgress(uPercent);
7195
7196 return VINF_SUCCESS;
7197}
7198
7199/**
7200 * @copydoc FNVMATERROR
7201 *
7202 * @remarks Might be some tiny serialization concerns with access to the string
7203 * object here...
7204 */
7205/*static*/ DECLCALLBACK(void)
7206Console::genericVMSetErrorCallback(PVM pVM, void *pvUser, int rc, RT_SRC_POS_DECL,
7207 const char *pszErrorFmt, va_list va)
7208{
7209 Utf8Str *pErrorText = (Utf8Str *)pvUser;
7210 AssertPtr(pErrorText);
7211
7212 /* We ignore RT_SRC_POS_DECL arguments to avoid confusion of end-users. */
7213 va_list va2;
7214 va_copy(va2, va);
7215
7216 /* Append to any the existing error message. */
7217 if (pErrorText->length())
7218 *pErrorText = Utf8StrFmt("%s.\n%N (%Rrc)", pErrorText->c_str(),
7219 pszErrorFmt, &va2, rc, rc);
7220 else
7221 *pErrorText = Utf8StrFmt("%N (%Rrc)", pszErrorFmt, &va2, rc, rc);
7222
7223 va_end(va2);
7224}
7225
7226/**
7227 * VM runtime error callback function.
7228 * See VMSetRuntimeError for the detailed description of parameters.
7229 *
7230 * @param pVM The VM handle.
7231 * @param pvUser The user argument.
7232 * @param fFlags The action flags. See VMSETRTERR_FLAGS_*.
7233 * @param pszErrorId Error ID string.
7234 * @param pszFormat Error message format string.
7235 * @param va Error message arguments.
7236 * @thread EMT.
7237 */
7238/* static */ DECLCALLBACK(void)
7239Console::setVMRuntimeErrorCallback(PVM pVM, void *pvUser, uint32_t fFlags,
7240 const char *pszErrorId,
7241 const char *pszFormat, va_list va)
7242{
7243 bool const fFatal = !!(fFlags & VMSETRTERR_FLAGS_FATAL);
7244 LogFlowFuncEnter();
7245
7246 Console *that = static_cast<Console *>(pvUser);
7247 AssertReturnVoid(that);
7248
7249 Utf8Str message(pszFormat, va);
7250
7251 LogRel(("Console: VM runtime error: fatal=%RTbool, errorID=%s message=\"%s\"\n",
7252 fFatal, pszErrorId, message.c_str()));
7253
7254 that->onRuntimeError(BOOL(fFatal), Bstr(pszErrorId).raw(),
7255 Bstr(message).raw());
7256
7257 LogFlowFuncLeave();
7258}
7259
7260/**
7261 * Captures USB devices that match filters of the VM.
7262 * Called at VM startup.
7263 *
7264 * @param pVM The VM handle.
7265 *
7266 * @note The caller must lock this object for writing.
7267 */
7268HRESULT Console::captureUSBDevices(PVM pVM)
7269{
7270 LogFlowThisFunc(("\n"));
7271
7272 /* sanity check */
7273 ComAssertRet(isWriteLockOnCurrentThread(), E_FAIL);
7274
7275 /* If the machine has an USB controller, ask the USB proxy service to
7276 * capture devices */
7277 PPDMIBASE pBase;
7278 int vrc = PDMR3QueryLun(pVM, "usb-ohci", 0, 0, &pBase);
7279 if (RT_SUCCESS(vrc))
7280 {
7281 /* leave the lock before calling Host in VBoxSVC since Host may call
7282 * us back from under its lock (e.g. onUSBDeviceAttach()) which would
7283 * produce an inter-process dead-lock otherwise. */
7284 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7285 alock.leave();
7286
7287 HRESULT hrc = mControl->AutoCaptureUSBDevices();
7288 ComAssertComRCRetRC(hrc);
7289 }
7290 else if ( vrc == VERR_PDM_DEVICE_NOT_FOUND
7291 || vrc == VERR_PDM_DEVICE_INSTANCE_NOT_FOUND)
7292 vrc = VINF_SUCCESS;
7293 else
7294 AssertRC(vrc);
7295
7296 return RT_SUCCESS(vrc) ? S_OK : E_FAIL;
7297}
7298
7299
7300/**
7301 * Detach all USB device which are attached to the VM for the
7302 * purpose of clean up and such like.
7303 *
7304 * @note The caller must lock this object for writing.
7305 */
7306void Console::detachAllUSBDevices(bool aDone)
7307{
7308 LogFlowThisFunc(("aDone=%RTbool\n", aDone));
7309
7310 /* sanity check */
7311 AssertReturnVoid(isWriteLockOnCurrentThread());
7312
7313 mUSBDevices.clear();
7314
7315 /* leave the lock before calling Host in VBoxSVC since Host may call
7316 * us back from under its lock (e.g. onUSBDeviceAttach()) which would
7317 * produce an inter-process dead-lock otherwise. */
7318 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7319 alock.leave();
7320
7321 mControl->DetachAllUSBDevices(aDone);
7322}
7323
7324/**
7325 * @note Locks this object for writing.
7326 */
7327void Console::processRemoteUSBDevices(uint32_t u32ClientId, VRDEUSBDEVICEDESC *pDevList, uint32_t cbDevList)
7328{
7329 LogFlowThisFuncEnter();
7330 LogFlowThisFunc(("u32ClientId = %d, pDevList=%p, cbDevList = %d\n", u32ClientId, pDevList, cbDevList));
7331
7332 AutoCaller autoCaller(this);
7333 if (!autoCaller.isOk())
7334 {
7335 /* Console has been already uninitialized, deny request */
7336 AssertMsgFailed(("Console is already uninitialized\n"));
7337 LogFlowThisFunc(("Console is already uninitialized\n"));
7338 LogFlowThisFuncLeave();
7339 return;
7340 }
7341
7342 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7343
7344 /*
7345 * Mark all existing remote USB devices as dirty.
7346 */
7347 for (RemoteUSBDeviceList::iterator it = mRemoteUSBDevices.begin();
7348 it != mRemoteUSBDevices.end();
7349 ++it)
7350 {
7351 (*it)->dirty(true);
7352 }
7353
7354 /*
7355 * Process the pDevList and add devices those are not already in the mRemoteUSBDevices list.
7356 */
7357 /** @todo (sunlover) REMOTE_USB Strict validation of the pDevList. */
7358 VRDEUSBDEVICEDESC *e = pDevList;
7359
7360 /* The cbDevList condition must be checked first, because the function can
7361 * receive pDevList = NULL and cbDevList = 0 on client disconnect.
7362 */
7363 while (cbDevList >= 2 && e->oNext)
7364 {
7365 LogFlowThisFunc(("vendor %04X, product %04X, name = %s\n",
7366 e->idVendor, e->idProduct,
7367 e->oProduct? (char *)e + e->oProduct: ""));
7368
7369 bool fNewDevice = true;
7370
7371 for (RemoteUSBDeviceList::iterator it = mRemoteUSBDevices.begin();
7372 it != mRemoteUSBDevices.end();
7373 ++it)
7374 {
7375 if ((*it)->devId() == e->id
7376 && (*it)->clientId() == u32ClientId)
7377 {
7378 /* The device is already in the list. */
7379 (*it)->dirty(false);
7380 fNewDevice = false;
7381 break;
7382 }
7383 }
7384
7385 if (fNewDevice)
7386 {
7387 LogRel(("Remote USB: ++++ Vendor %04X. Product %04X. Name = [%s].\n",
7388 e->idVendor, e->idProduct, e->oProduct? (char *)e + e->oProduct: ""));
7389
7390 /* Create the device object and add the new device to list. */
7391 ComObjPtr<RemoteUSBDevice> pUSBDevice;
7392 pUSBDevice.createObject();
7393 pUSBDevice->init(u32ClientId, e);
7394
7395 mRemoteUSBDevices.push_back(pUSBDevice);
7396
7397 /* Check if the device is ok for current USB filters. */
7398 BOOL fMatched = FALSE;
7399 ULONG fMaskedIfs = 0;
7400
7401 HRESULT hrc = mControl->RunUSBDeviceFilters(pUSBDevice, &fMatched, &fMaskedIfs);
7402
7403 AssertComRC(hrc);
7404
7405 LogFlowThisFunc(("USB filters return %d %#x\n", fMatched, fMaskedIfs));
7406
7407 if (fMatched)
7408 {
7409 hrc = onUSBDeviceAttach(pUSBDevice, NULL, fMaskedIfs);
7410
7411 /// @todo (r=dmik) warning reporting subsystem
7412
7413 if (hrc == S_OK)
7414 {
7415 LogFlowThisFunc(("Device attached\n"));
7416 pUSBDevice->captured(true);
7417 }
7418 }
7419 }
7420
7421 if (cbDevList < e->oNext)
7422 {
7423 LogWarningThisFunc(("cbDevList %d > oNext %d\n",
7424 cbDevList, e->oNext));
7425 break;
7426 }
7427
7428 cbDevList -= e->oNext;
7429
7430 e = (VRDEUSBDEVICEDESC *)((uint8_t *)e + e->oNext);
7431 }
7432
7433 /*
7434 * Remove dirty devices, that is those which are not reported by the server anymore.
7435 */
7436 for (;;)
7437 {
7438 ComObjPtr<RemoteUSBDevice> pUSBDevice;
7439
7440 RemoteUSBDeviceList::iterator it = mRemoteUSBDevices.begin();
7441 while (it != mRemoteUSBDevices.end())
7442 {
7443 if ((*it)->dirty())
7444 {
7445 pUSBDevice = *it;
7446 break;
7447 }
7448
7449 ++ it;
7450 }
7451
7452 if (!pUSBDevice)
7453 {
7454 break;
7455 }
7456
7457 USHORT vendorId = 0;
7458 pUSBDevice->COMGETTER(VendorId)(&vendorId);
7459
7460 USHORT productId = 0;
7461 pUSBDevice->COMGETTER(ProductId)(&productId);
7462
7463 Bstr product;
7464 pUSBDevice->COMGETTER(Product)(product.asOutParam());
7465
7466 LogRel(("Remote USB: ---- Vendor %04X. Product %04X. Name = [%ls].\n",
7467 vendorId, productId, product.raw()));
7468
7469 /* Detach the device from VM. */
7470 if (pUSBDevice->captured())
7471 {
7472 Bstr uuid;
7473 pUSBDevice->COMGETTER(Id)(uuid.asOutParam());
7474 onUSBDeviceDetach(uuid.raw(), NULL);
7475 }
7476
7477 /* And remove it from the list. */
7478 mRemoteUSBDevices.erase(it);
7479 }
7480
7481 LogFlowThisFuncLeave();
7482}
7483
7484/**
7485 * Progress cancelation callback for fault tolerance VM poweron
7486 */
7487static void faultToleranceProgressCancelCallback(void *pvUser)
7488{
7489 PVM pVM = (PVM)pvUser;
7490
7491 if (pVM)
7492 FTMR3CancelStandby(pVM);
7493}
7494
7495/**
7496 * Thread function which starts the VM (also from saved state) and
7497 * track progress.
7498 *
7499 * @param Thread The thread id.
7500 * @param pvUser Pointer to a VMPowerUpTask structure.
7501 * @return VINF_SUCCESS (ignored).
7502 *
7503 * @note Locks the Console object for writing.
7504 */
7505/*static*/
7506DECLCALLBACK(int) Console::powerUpThread(RTTHREAD Thread, void *pvUser)
7507{
7508 LogFlowFuncEnter();
7509
7510 std::auto_ptr<VMPowerUpTask> task(static_cast<VMPowerUpTask *>(pvUser));
7511 AssertReturn(task.get(), VERR_INVALID_PARAMETER);
7512
7513 AssertReturn(!task->mConsole.isNull(), VERR_INVALID_PARAMETER);
7514 AssertReturn(!task->mProgress.isNull(), VERR_INVALID_PARAMETER);
7515
7516#if defined(RT_OS_WINDOWS)
7517 {
7518 /* initialize COM */
7519 HRESULT hrc = CoInitializeEx(NULL,
7520 COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE |
7521 COINIT_SPEED_OVER_MEMORY);
7522 LogFlowFunc(("CoInitializeEx()=%08X\n", hrc));
7523 }
7524#endif
7525
7526 HRESULT rc = S_OK;
7527 int vrc = VINF_SUCCESS;
7528
7529 /* Set up a build identifier so that it can be seen from core dumps what
7530 * exact build was used to produce the core. */
7531 static char saBuildID[40];
7532 RTStrPrintf(saBuildID, sizeof(saBuildID), "%s%s%s%s VirtualBox %s r%u %s%s%s%s",
7533 "BU", "IL", "DI", "D", VBOX_VERSION_STRING, RTBldCfgRevision(), "BU", "IL", "DI", "D");
7534
7535 ComObjPtr<Console> pConsole = task->mConsole;
7536
7537 /* Note: no need to use addCaller() because VMPowerUpTask does that */
7538
7539 /* The lock is also used as a signal from the task initiator (which
7540 * releases it only after RTThreadCreate()) that we can start the job */
7541 AutoWriteLock alock(pConsole COMMA_LOCKVAL_SRC_POS);
7542
7543 /* sanity */
7544 Assert(pConsole->mpVM == NULL);
7545
7546 try
7547 {
7548 // Create the VMM device object, which starts the HGCM thread; do this only
7549 // once for the console, for the pathological case that the same console
7550 // object is used to power up a VM twice. VirtualBox 4.0: we now do that
7551 // here instead of the Console constructor (see Console::init())
7552 if (!pConsole->m_pVMMDev)
7553 {
7554 pConsole->m_pVMMDev = new VMMDev(pConsole);
7555 AssertReturn(pConsole->m_pVMMDev, E_FAIL);
7556 }
7557
7558 /* wait for auto reset ops to complete so that we can successfully lock
7559 * the attached hard disks by calling LockMedia() below */
7560 for (VMPowerUpTask::ProgressList::const_iterator
7561 it = task->hardDiskProgresses.begin();
7562 it != task->hardDiskProgresses.end(); ++ it)
7563 {
7564 HRESULT rc2 = (*it)->WaitForCompletion(-1);
7565 AssertComRC(rc2);
7566 }
7567
7568 /*
7569 * Lock attached media. This method will also check their accessibility.
7570 * If we're a teleporter, we'll have to postpone this action so we can
7571 * migrate between local processes.
7572 *
7573 * Note! The media will be unlocked automatically by
7574 * SessionMachine::setMachineState() when the VM is powered down.
7575 */
7576 if ( !task->mTeleporterEnabled
7577 && task->mEnmFaultToleranceState != FaultToleranceState_Standby)
7578 {
7579 rc = pConsole->mControl->LockMedia();
7580 if (FAILED(rc)) throw rc;
7581 }
7582
7583 /* Create the VRDP server. In case of headless operation, this will
7584 * also create the framebuffer, required at VM creation.
7585 */
7586 ConsoleVRDPServer *server = pConsole->consoleVRDPServer();
7587 Assert(server);
7588
7589 /* Does VRDP server call Console from the other thread?
7590 * Not sure (and can change), so leave the lock just in case.
7591 */
7592 alock.leave();
7593 vrc = server->Launch();
7594 alock.enter();
7595
7596 if (vrc == VERR_NET_ADDRESS_IN_USE)
7597 {
7598 Utf8Str errMsg;
7599 Bstr bstr;
7600 pConsole->mVRDEServer->GetVRDEProperty(Bstr("TCP/Ports").raw(), bstr.asOutParam());
7601 Utf8Str ports = bstr;
7602 errMsg = Utf8StrFmt(tr("VirtualBox Remote Desktop Extension server can't bind to the port: %s"),
7603 ports.c_str());
7604 LogRel(("VRDE: Warning: failed to launch VRDE server (%Rrc): '%s'\n",
7605 vrc, errMsg.c_str()));
7606 }
7607 else if (vrc == VINF_NOT_SUPPORTED)
7608 {
7609 /* This means that the VRDE is not installed. */
7610 LogRel(("VRDE: VirtualBox Remote Desktop Extension is not available.\n"));
7611 }
7612 else if (RT_FAILURE(vrc))
7613 {
7614 /* Fail, if the server is installed but can't start. */
7615 Utf8Str errMsg;
7616 switch (vrc)
7617 {
7618 case VERR_FILE_NOT_FOUND:
7619 {
7620 /* VRDE library file is missing. */
7621 errMsg = Utf8StrFmt(tr("Could not find the VirtualBox Remote Desktop Extension library."));
7622 break;
7623 }
7624 default:
7625 errMsg = Utf8StrFmt(tr("Failed to launch Remote Desktop Extension server (%Rrc)"),
7626 vrc);
7627 }
7628 LogRel(("VRDE: Failed: (%Rrc), error message: '%s'\n",
7629 vrc, errMsg.c_str()));
7630 throw setErrorStatic(E_FAIL, errMsg.c_str());
7631 }
7632
7633 ComPtr<IMachine> pMachine = pConsole->machine();
7634 ULONG cCpus = 1;
7635 pMachine->COMGETTER(CPUCount)(&cCpus);
7636
7637 /*
7638 * Create the VM
7639 */
7640 PVM pVM;
7641 /*
7642 * leave the lock since EMT will call Console. It's safe because
7643 * mMachineState is either Starting or Restoring state here.
7644 */
7645 alock.leave();
7646
7647 vrc = VMR3Create(cCpus,
7648 pConsole->mpVmm2UserMethods,
7649 Console::genericVMSetErrorCallback,
7650 &task->mErrorMsg,
7651 task->mConfigConstructor,
7652 static_cast<Console *>(pConsole),
7653 &pVM);
7654
7655 alock.enter();
7656
7657 /* Enable client connections to the server. */
7658 pConsole->consoleVRDPServer()->EnableConnections();
7659
7660 if (RT_SUCCESS(vrc))
7661 {
7662 do
7663 {
7664 /*
7665 * Register our load/save state file handlers
7666 */
7667 vrc = SSMR3RegisterExternal(pVM, sSSMConsoleUnit, 0 /*iInstance*/, sSSMConsoleVer, 0 /* cbGuess */,
7668 NULL, NULL, NULL,
7669 NULL, saveStateFileExec, NULL,
7670 NULL, loadStateFileExec, NULL,
7671 static_cast<Console *>(pConsole));
7672 AssertRCBreak(vrc);
7673
7674 vrc = static_cast<Console *>(pConsole)->getDisplay()->registerSSM(pVM);
7675 AssertRC(vrc);
7676 if (RT_FAILURE(vrc))
7677 break;
7678
7679 /*
7680 * Synchronize debugger settings
7681 */
7682 MachineDebugger *machineDebugger = pConsole->getMachineDebugger();
7683 if (machineDebugger)
7684 machineDebugger->flushQueuedSettings();
7685
7686 /*
7687 * Shared Folders
7688 */
7689 if (pConsole->m_pVMMDev->isShFlActive())
7690 {
7691 /* Does the code below call Console from the other thread?
7692 * Not sure, so leave the lock just in case. */
7693 alock.leave();
7694
7695 for (SharedFolderDataMap::const_iterator it = task->mSharedFolders.begin();
7696 it != task->mSharedFolders.end();
7697 ++it)
7698 {
7699 rc = pConsole->createSharedFolder((*it).first.raw(),
7700 (*it).second);
7701 if (FAILED(rc)) break;
7702 }
7703 if (FAILED(rc)) break;
7704
7705 /* enter the lock again */
7706 alock.enter();
7707 }
7708
7709 /*
7710 * Capture USB devices.
7711 */
7712 rc = pConsole->captureUSBDevices(pVM);
7713 if (FAILED(rc)) break;
7714
7715 /* leave the lock before a lengthy operation */
7716 alock.leave();
7717
7718 /* Load saved state? */
7719 if (task->mSavedStateFile.length())
7720 {
7721 LogFlowFunc(("Restoring saved state from '%s'...\n",
7722 task->mSavedStateFile.c_str()));
7723
7724 vrc = VMR3LoadFromFile(pVM,
7725 task->mSavedStateFile.c_str(),
7726 Console::stateProgressCallback,
7727 static_cast<IProgress *>(task->mProgress));
7728
7729 if (RT_SUCCESS(vrc))
7730 {
7731 if (task->mStartPaused)
7732 /* done */
7733 pConsole->setMachineState(MachineState_Paused);
7734 else
7735 {
7736 /* Start/Resume the VM execution */
7737#ifdef VBOX_WITH_EXTPACK
7738 vrc = pConsole->mptrExtPackManager->callAllVmPowerOnHooks(pConsole, pVM);
7739#endif
7740 if (RT_SUCCESS(vrc))
7741 vrc = VMR3Resume(pVM);
7742 AssertLogRelRC(vrc);
7743 }
7744 }
7745
7746 /* Power off in case we failed loading or resuming the VM */
7747 if (RT_FAILURE(vrc))
7748 {
7749 int vrc2 = VMR3PowerOff(pVM); AssertLogRelRC(vrc2);
7750#ifdef VBOX_WITH_EXTPACK
7751 pConsole->mptrExtPackManager->callAllVmPowerOffHooks(pConsole, pVM);
7752#endif
7753 }
7754 }
7755 else if (task->mTeleporterEnabled)
7756 {
7757 /* -> ConsoleImplTeleporter.cpp */
7758 bool fPowerOffOnFailure;
7759 rc = pConsole->teleporterTrg(pVM, pMachine, &task->mErrorMsg, task->mStartPaused,
7760 task->mProgress, &fPowerOffOnFailure);
7761 if (FAILED(rc) && fPowerOffOnFailure)
7762 {
7763 ErrorInfoKeeper eik;
7764 int vrc2 = VMR3PowerOff(pVM); AssertLogRelRC(vrc2);
7765#ifdef VBOX_WITH_EXTPACK
7766 pConsole->mptrExtPackManager->callAllVmPowerOffHooks(pConsole, pVM);
7767#endif
7768 }
7769 }
7770 else if (task->mEnmFaultToleranceState != FaultToleranceState_Inactive)
7771 {
7772 /*
7773 * Get the config.
7774 */
7775 ULONG uPort;
7776 ULONG uInterval;
7777 Bstr bstrAddress, bstrPassword;
7778
7779 rc = pMachine->COMGETTER(FaultTolerancePort)(&uPort);
7780 if (SUCCEEDED(rc))
7781 {
7782 rc = pMachine->COMGETTER(FaultToleranceSyncInterval)(&uInterval);
7783 if (SUCCEEDED(rc))
7784 rc = pMachine->COMGETTER(FaultToleranceAddress)(bstrAddress.asOutParam());
7785 if (SUCCEEDED(rc))
7786 rc = pMachine->COMGETTER(FaultTolerancePassword)(bstrPassword.asOutParam());
7787 }
7788 if (task->mProgress->setCancelCallback(faultToleranceProgressCancelCallback, pVM))
7789 {
7790 if (SUCCEEDED(rc))
7791 {
7792 Utf8Str strAddress(bstrAddress);
7793 const char *pszAddress = strAddress.isEmpty() ? NULL : strAddress.c_str();
7794 Utf8Str strPassword(bstrPassword);
7795 const char *pszPassword = strPassword.isEmpty() ? NULL : strPassword.c_str();
7796
7797 /* Power on the FT enabled VM. */
7798#ifdef VBOX_WITH_EXTPACK
7799 vrc = pConsole->mptrExtPackManager->callAllVmPowerOnHooks(pConsole, pVM);
7800#endif
7801 if (RT_SUCCESS(vrc))
7802 vrc = FTMR3PowerOn(pVM,
7803 task->mEnmFaultToleranceState == FaultToleranceState_Master /* fMaster */,
7804 uInterval,
7805 pszAddress,
7806 uPort,
7807 pszPassword);
7808 AssertLogRelRC(vrc);
7809 }
7810 task->mProgress->setCancelCallback(NULL, NULL);
7811 }
7812 else
7813 rc = E_FAIL;
7814 }
7815 else if (task->mStartPaused)
7816 /* done */
7817 pConsole->setMachineState(MachineState_Paused);
7818 else
7819 {
7820 /* Power on the VM (i.e. start executing) */
7821#ifdef VBOX_WITH_EXTPACK
7822 vrc = pConsole->mptrExtPackManager->callAllVmPowerOnHooks(pConsole, pVM);
7823#endif
7824 if (RT_SUCCESS(vrc))
7825 vrc = VMR3PowerOn(pVM);
7826 AssertLogRelRC(vrc);
7827 }
7828
7829 /* enter the lock again */
7830 alock.enter();
7831 }
7832 while (0);
7833
7834 /* On failure, destroy the VM */
7835 if (FAILED(rc) || RT_FAILURE(vrc))
7836 {
7837 /* preserve existing error info */
7838 ErrorInfoKeeper eik;
7839
7840 /* powerDown() will call VMR3Destroy() and do all necessary
7841 * cleanup (VRDP, USB devices) */
7842 HRESULT rc2 = pConsole->powerDown();
7843 AssertComRC(rc2);
7844 }
7845 else
7846 {
7847 /*
7848 * Deregister the VMSetError callback. This is necessary as the
7849 * pfnVMAtError() function passed to VMR3Create() is supposed to
7850 * be sticky but our error callback isn't.
7851 */
7852 alock.leave();
7853 VMR3AtErrorDeregister(pVM, Console::genericVMSetErrorCallback, &task->mErrorMsg);
7854 /** @todo register another VMSetError callback? */
7855 alock.enter();
7856 }
7857 }
7858 else
7859 {
7860 /*
7861 * If VMR3Create() failed it has released the VM memory.
7862 */
7863 pConsole->mpVM = NULL;
7864 }
7865
7866 if (SUCCEEDED(rc) && RT_FAILURE(vrc))
7867 {
7868 /* If VMR3Create() or one of the other calls in this function fail,
7869 * an appropriate error message has been set in task->mErrorMsg.
7870 * However since that happens via a callback, the rc status code in
7871 * this function is not updated.
7872 */
7873 if (!task->mErrorMsg.length())
7874 {
7875 /* If the error message is not set but we've got a failure,
7876 * convert the VBox status code into a meaningful error message.
7877 * This becomes unused once all the sources of errors set the
7878 * appropriate error message themselves.
7879 */
7880 AssertMsgFailed(("Missing error message during powerup for status code %Rrc\n", vrc));
7881 task->mErrorMsg = Utf8StrFmt(tr("Failed to start VM execution (%Rrc)"),
7882 vrc);
7883 }
7884
7885 /* Set the error message as the COM error.
7886 * Progress::notifyComplete() will pick it up later. */
7887 throw setErrorStatic(E_FAIL, task->mErrorMsg.c_str());
7888 }
7889 }
7890 catch (HRESULT aRC) { rc = aRC; }
7891
7892 if ( pConsole->mMachineState == MachineState_Starting
7893 || pConsole->mMachineState == MachineState_Restoring
7894 || pConsole->mMachineState == MachineState_TeleportingIn
7895 )
7896 {
7897 /* We are still in the Starting/Restoring state. This means one of:
7898 *
7899 * 1) we failed before VMR3Create() was called;
7900 * 2) VMR3Create() failed.
7901 *
7902 * In both cases, there is no need to call powerDown(), but we still
7903 * need to go back to the PoweredOff/Saved state. Reuse
7904 * vmstateChangeCallback() for that purpose.
7905 */
7906
7907 /* preserve existing error info */
7908 ErrorInfoKeeper eik;
7909
7910 Assert(pConsole->mpVM == NULL);
7911 vmstateChangeCallback(NULL, VMSTATE_TERMINATED, VMSTATE_CREATING,
7912 pConsole);
7913 }
7914
7915 /*
7916 * Evaluate the final result. Note that the appropriate mMachineState value
7917 * is already set by vmstateChangeCallback() in all cases.
7918 */
7919
7920 /* leave the lock, don't need it any more */
7921 alock.leave();
7922
7923 if (SUCCEEDED(rc))
7924 {
7925 /* Notify the progress object of the success */
7926 task->mProgress->notifyComplete(S_OK);
7927 }
7928 else
7929 {
7930 /* The progress object will fetch the current error info */
7931 task->mProgress->notifyComplete(rc);
7932 LogRel(("Power up failed (vrc=%Rrc, rc=%Rhrc (%#08X))\n", vrc, rc, rc));
7933 }
7934
7935 /* Notify VBoxSVC and any waiting openRemoteSession progress object. */
7936 pConsole->mControl->EndPowerUp(rc);
7937
7938#if defined(RT_OS_WINDOWS)
7939 /* uninitialize COM */
7940 CoUninitialize();
7941#endif
7942
7943 LogFlowFuncLeave();
7944
7945 return VINF_SUCCESS;
7946}
7947
7948
7949/**
7950 * Reconfigures a medium attachment (part of taking or deleting an online snapshot).
7951 *
7952 * @param pConsole Reference to the console object.
7953 * @param pVM The VM handle.
7954 * @param lInstance The instance of the controller.
7955 * @param pcszDevice The name of the controller type.
7956 * @param enmBus The storage bus type of the controller.
7957 * @param fSetupMerge Whether to set up a medium merge
7958 * @param uMergeSource Merge source image index
7959 * @param uMergeTarget Merge target image index
7960 * @param aMediumAtt The medium attachment.
7961 * @param aMachineState The current machine state.
7962 * @param phrc Where to store com error - only valid if we return VERR_GENERAL_FAILURE.
7963 * @return VBox status code.
7964 */
7965/* static */
7966DECLCALLBACK(int) Console::reconfigureMediumAttachment(Console *pConsole,
7967 PVM pVM,
7968 const char *pcszDevice,
7969 unsigned uInstance,
7970 StorageBus_T enmBus,
7971 bool fUseHostIOCache,
7972 bool fBuiltinIoCache,
7973 bool fSetupMerge,
7974 unsigned uMergeSource,
7975 unsigned uMergeTarget,
7976 IMediumAttachment *aMediumAtt,
7977 MachineState_T aMachineState,
7978 HRESULT *phrc)
7979{
7980 LogFlowFunc(("pVM=%p aMediumAtt=%p phrc=%p\n", pVM, aMediumAtt, phrc));
7981
7982 int rc;
7983 HRESULT hrc;
7984 Bstr bstr;
7985 *phrc = S_OK;
7986#define RC_CHECK() do { if (RT_FAILURE(rc)) { AssertMsgFailed(("rc=%Rrc\n", rc)); return rc; } } while (0)
7987#define H() do { if (FAILED(hrc)) { AssertMsgFailed(("hrc=%Rhrc (%#x)\n", hrc, hrc)); *phrc = hrc; return VERR_GENERAL_FAILURE; } } while (0)
7988
7989 /* Ignore attachments other than hard disks, since at the moment they are
7990 * not subject to snapshotting in general. */
7991 DeviceType_T lType;
7992 hrc = aMediumAtt->COMGETTER(Type)(&lType); H();
7993 if (lType != DeviceType_HardDisk)
7994 return VINF_SUCCESS;
7995
7996 /* Determine the base path for the device instance. */
7997 PCFGMNODE pCtlInst;
7998 pCtlInst = CFGMR3GetChildF(CFGMR3GetRoot(pVM), "Devices/%s/%u/", pcszDevice, uInstance);
7999 AssertReturn(pCtlInst, VERR_INTERNAL_ERROR);
8000
8001 /* Update the device instance configuration. */
8002 rc = pConsole->configMediumAttachment(pCtlInst,
8003 pcszDevice,
8004 uInstance,
8005 enmBus,
8006 fUseHostIOCache,
8007 fBuiltinIoCache,
8008 fSetupMerge,
8009 uMergeSource,
8010 uMergeTarget,
8011 aMediumAtt,
8012 aMachineState,
8013 phrc,
8014 true /* fAttachDetach */,
8015 false /* fForceUnmount */,
8016 pVM,
8017 NULL /* paLedDevType */);
8018 /** @todo this dumps everything attached to this device instance, which
8019 * is more than necessary. Dumping the changed LUN would be enough. */
8020 CFGMR3Dump(pCtlInst);
8021 RC_CHECK();
8022
8023#undef RC_CHECK
8024#undef H
8025
8026 LogFlowFunc(("Returns success\n"));
8027 return VINF_SUCCESS;
8028}
8029
8030/**
8031 * Progress cancelation callback employed by Console::fntTakeSnapshotWorker.
8032 */
8033static void takesnapshotProgressCancelCallback(void *pvUser)
8034{
8035 PVM pVM = (PVM)pvUser;
8036 SSMR3Cancel(pVM);
8037}
8038
8039/**
8040 * Worker thread created by Console::TakeSnapshot.
8041 * @param Thread The current thread (ignored).
8042 * @param pvUser The task.
8043 * @return VINF_SUCCESS (ignored).
8044 */
8045/*static*/
8046DECLCALLBACK(int) Console::fntTakeSnapshotWorker(RTTHREAD Thread, void *pvUser)
8047{
8048 VMTakeSnapshotTask *pTask = (VMTakeSnapshotTask*)pvUser;
8049
8050 // taking a snapshot consists of the following:
8051
8052 // 1) creating a diff image for each virtual hard disk, into which write operations go after
8053 // the snapshot has been created (done in VBoxSVC, in SessionMachine::BeginTakingSnapshot)
8054 // 2) creating a Snapshot object with the state of the machine (hardware + storage,
8055 // done in VBoxSVC, also in SessionMachine::BeginTakingSnapshot)
8056 // 3) saving the state of the virtual machine (here, in the VM process, if the machine is online)
8057
8058 Console *that = pTask->mConsole;
8059 bool fBeganTakingSnapshot = false;
8060 bool fSuspenededBySave = false;
8061
8062 AutoCaller autoCaller(that);
8063 if (FAILED(autoCaller.rc()))
8064 {
8065 that->mptrCancelableProgress.setNull();
8066 return autoCaller.rc();
8067 }
8068
8069 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
8070
8071 HRESULT rc = S_OK;
8072
8073 try
8074 {
8075 /* STEP 1 + 2:
8076 * request creating the diff images on the server and create the snapshot object
8077 * (this will set the machine state to Saving on the server to block
8078 * others from accessing this machine)
8079 */
8080 rc = that->mControl->BeginTakingSnapshot(that,
8081 pTask->bstrName.raw(),
8082 pTask->bstrDescription.raw(),
8083 pTask->mProgress,
8084 pTask->fTakingSnapshotOnline,
8085 pTask->bstrSavedStateFile.asOutParam());
8086 if (FAILED(rc))
8087 throw rc;
8088
8089 fBeganTakingSnapshot = true;
8090
8091 /*
8092 * state file is non-null only when the VM is paused
8093 * (i.e. creating a snapshot online)
8094 */
8095 bool f = (!pTask->bstrSavedStateFile.isEmpty() && pTask->fTakingSnapshotOnline)
8096 || ( pTask->bstrSavedStateFile.isEmpty() && !pTask->fTakingSnapshotOnline);
8097 if (!f)
8098 throw setErrorStatic(E_FAIL, "Invalid state of saved state file");
8099
8100 /* sync the state with the server */
8101 if (pTask->lastMachineState == MachineState_Running)
8102 that->setMachineStateLocally(MachineState_LiveSnapshotting);
8103 else
8104 that->setMachineStateLocally(MachineState_Saving);
8105
8106 // STEP 3: save the VM state (if online)
8107 if (pTask->fTakingSnapshotOnline)
8108 {
8109 Utf8Str strSavedStateFile(pTask->bstrSavedStateFile);
8110
8111 pTask->mProgress->SetNextOperation(Bstr(tr("Saving the machine state")).raw(),
8112 pTask->ulMemSize); // operation weight, same as computed when setting up progress object
8113 pTask->mProgress->setCancelCallback(takesnapshotProgressCancelCallback, that->mpVM);
8114
8115 alock.leave();
8116 LogFlowFunc(("VMR3Save...\n"));
8117 int vrc = VMR3Save(that->mpVM,
8118 strSavedStateFile.c_str(),
8119 true /*fContinueAfterwards*/,
8120 Console::stateProgressCallback,
8121 static_cast<IProgress *>(pTask->mProgress),
8122 &fSuspenededBySave);
8123 alock.enter();
8124 if (RT_FAILURE(vrc))
8125 throw setErrorStatic(E_FAIL,
8126 tr("Failed to save the machine state to '%s' (%Rrc)"),
8127 strSavedStateFile.c_str(), vrc);
8128
8129 pTask->mProgress->setCancelCallback(NULL, NULL);
8130 if (!pTask->mProgress->notifyPointOfNoReturn())
8131 throw setErrorStatic(E_FAIL, tr("Canceled"));
8132 that->mptrCancelableProgress.setNull();
8133
8134 // STEP 4: reattach hard disks
8135 LogFlowFunc(("Reattaching new differencing hard disks...\n"));
8136
8137 pTask->mProgress->SetNextOperation(Bstr(tr("Reconfiguring medium attachments")).raw(),
8138 1); // operation weight, same as computed when setting up progress object
8139
8140 com::SafeIfaceArray<IMediumAttachment> atts;
8141 rc = that->mMachine->COMGETTER(MediumAttachments)(ComSafeArrayAsOutParam(atts));
8142 if (FAILED(rc))
8143 throw rc;
8144
8145 for (size_t i = 0;
8146 i < atts.size();
8147 ++i)
8148 {
8149 ComPtr<IStorageController> pStorageController;
8150 Bstr controllerName;
8151 ULONG lInstance;
8152 StorageControllerType_T enmController;
8153 StorageBus_T enmBus;
8154 BOOL fUseHostIOCache;
8155
8156 /*
8157 * We can't pass a storage controller object directly
8158 * (g++ complains about not being able to pass non POD types through '...')
8159 * so we have to query needed values here and pass them.
8160 */
8161 rc = atts[i]->COMGETTER(Controller)(controllerName.asOutParam());
8162 if (FAILED(rc))
8163 throw rc;
8164
8165 rc = that->mMachine->GetStorageControllerByName(controllerName.raw(),
8166 pStorageController.asOutParam());
8167 if (FAILED(rc))
8168 throw rc;
8169
8170 rc = pStorageController->COMGETTER(ControllerType)(&enmController);
8171 if (FAILED(rc))
8172 throw rc;
8173 rc = pStorageController->COMGETTER(Instance)(&lInstance);
8174 if (FAILED(rc))
8175 throw rc;
8176 rc = pStorageController->COMGETTER(Bus)(&enmBus);
8177 if (FAILED(rc))
8178 throw rc;
8179 rc = pStorageController->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
8180 if (FAILED(rc))
8181 throw rc;
8182
8183 const char *pcszDevice = Console::convertControllerTypeToDev(enmController);
8184
8185 BOOL fBuiltinIoCache;
8186 rc = that->mMachine->COMGETTER(IoCacheEnabled)(&fBuiltinIoCache);
8187 if (FAILED(rc))
8188 throw rc;
8189
8190 /*
8191 * don't leave the lock since reconfigureMediumAttachment
8192 * isn't going to need the Console lock.
8193 */
8194 vrc = VMR3ReqCallWait(that->mpVM,
8195 VMCPUID_ANY,
8196 (PFNRT)reconfigureMediumAttachment,
8197 13,
8198 that,
8199 that->mpVM,
8200 pcszDevice,
8201 lInstance,
8202 enmBus,
8203 fUseHostIOCache,
8204 fBuiltinIoCache,
8205 false /* fSetupMerge */,
8206 0 /* uMergeSource */,
8207 0 /* uMergeTarget */,
8208 atts[i],
8209 that->mMachineState,
8210 &rc);
8211 if (RT_FAILURE(vrc))
8212 throw setErrorStatic(E_FAIL, Console::tr("%Rrc"), vrc);
8213 if (FAILED(rc))
8214 throw rc;
8215 }
8216 }
8217
8218 /*
8219 * finalize the requested snapshot object.
8220 * This will reset the machine state to the state it had right
8221 * before calling mControl->BeginTakingSnapshot().
8222 */
8223 rc = that->mControl->EndTakingSnapshot(TRUE /*aSuccess*/);
8224 // do not throw rc here because we can't call EndTakingSnapshot() twice
8225 LogFlowFunc(("EndTakingSnapshot -> %Rhrc [mMachineState=%s]\n", rc, Global::stringifyMachineState(that->mMachineState)));
8226 }
8227 catch (HRESULT rcThrown)
8228 {
8229 /* preserve existing error info */
8230 ErrorInfoKeeper eik;
8231
8232 if (fBeganTakingSnapshot)
8233 that->mControl->EndTakingSnapshot(FALSE /*aSuccess*/);
8234
8235 rc = rcThrown;
8236 LogFunc(("Caught %Rhrc [mMachineState=%s]\n", rc, Global::stringifyMachineState(that->mMachineState)));
8237 }
8238 Assert(alock.isWriteLockOnCurrentThread());
8239
8240 if (FAILED(rc)) /* Must come before calling setMachineState. */
8241 pTask->mProgress->notifyComplete(rc);
8242
8243 /*
8244 * Fix up the machine state.
8245 *
8246 * For live snapshots we do all the work, for the two other variations we
8247 * just update the local copy.
8248 */
8249 MachineState_T enmMachineState;
8250 that->mMachine->COMGETTER(State)(&enmMachineState);
8251 if ( that->mMachineState == MachineState_LiveSnapshotting
8252 || that->mMachineState == MachineState_Saving)
8253 {
8254
8255 if (!pTask->fTakingSnapshotOnline)
8256 that->setMachineStateLocally(pTask->lastMachineState);
8257 else if (SUCCEEDED(rc))
8258 {
8259 Assert( pTask->lastMachineState == MachineState_Running
8260 || pTask->lastMachineState == MachineState_Paused);
8261 Assert(that->mMachineState == MachineState_Saving);
8262 if (pTask->lastMachineState == MachineState_Running)
8263 {
8264 LogFlowFunc(("VMR3Resume...\n"));
8265 alock.leave();
8266 int vrc = VMR3Resume(that->mpVM);
8267 alock.enter();
8268 if (RT_FAILURE(vrc))
8269 {
8270 rc = setErrorStatic(VBOX_E_VM_ERROR, tr("Could not resume the machine execution (%Rrc)"), vrc);
8271 pTask->mProgress->notifyComplete(rc);
8272 if (that->mMachineState == MachineState_Saving)
8273 that->setMachineStateLocally(MachineState_Paused);
8274 }
8275 }
8276 else
8277 that->setMachineStateLocally(MachineState_Paused);
8278 }
8279 else
8280 {
8281 /** @todo this could probably be made more generic and reused elsewhere. */
8282 /* paranoid cleanup on for a failed online snapshot. */
8283 VMSTATE enmVMState = VMR3GetState(that->mpVM);
8284 switch (enmVMState)
8285 {
8286 case VMSTATE_RUNNING:
8287 case VMSTATE_RUNNING_LS:
8288 case VMSTATE_DEBUGGING:
8289 case VMSTATE_DEBUGGING_LS:
8290 case VMSTATE_POWERING_OFF:
8291 case VMSTATE_POWERING_OFF_LS:
8292 case VMSTATE_RESETTING:
8293 case VMSTATE_RESETTING_LS:
8294 Assert(!fSuspenededBySave);
8295 that->setMachineState(MachineState_Running);
8296 break;
8297
8298 case VMSTATE_GURU_MEDITATION:
8299 case VMSTATE_GURU_MEDITATION_LS:
8300 that->setMachineState(MachineState_Stuck);
8301 break;
8302
8303 case VMSTATE_FATAL_ERROR:
8304 case VMSTATE_FATAL_ERROR_LS:
8305 if (pTask->lastMachineState == MachineState_Paused)
8306 that->setMachineStateLocally(pTask->lastMachineState);
8307 else
8308 that->setMachineState(MachineState_Paused);
8309 break;
8310
8311 default:
8312 AssertMsgFailed(("%s\n", VMR3GetStateName(enmVMState)));
8313 case VMSTATE_SUSPENDED:
8314 case VMSTATE_SUSPENDED_LS:
8315 case VMSTATE_SUSPENDING:
8316 case VMSTATE_SUSPENDING_LS:
8317 case VMSTATE_SUSPENDING_EXT_LS:
8318 if (fSuspenededBySave)
8319 {
8320 Assert(pTask->lastMachineState == MachineState_Running);
8321 LogFlowFunc(("VMR3Resume (on failure)...\n"));
8322 alock.leave();
8323 int vrc = VMR3Resume(that->mpVM); AssertLogRelRC(vrc);
8324 alock.enter();
8325 if (RT_FAILURE(vrc))
8326 that->setMachineState(MachineState_Paused);
8327 }
8328 else if (pTask->lastMachineState == MachineState_Paused)
8329 that->setMachineStateLocally(pTask->lastMachineState);
8330 else
8331 that->setMachineState(MachineState_Paused);
8332 break;
8333 }
8334
8335 }
8336 }
8337 /*else: somebody else has change the state... Leave it. */
8338
8339 /* check the remote state to see that we got it right. */
8340 that->mMachine->COMGETTER(State)(&enmMachineState);
8341 AssertLogRelMsg(that->mMachineState == enmMachineState,
8342 ("mMachineState=%s enmMachineState=%s\n", Global::stringifyMachineState(that->mMachineState),
8343 Global::stringifyMachineState(enmMachineState) ));
8344
8345
8346 if (SUCCEEDED(rc)) /* The failure cases are handled above. */
8347 pTask->mProgress->notifyComplete(rc);
8348
8349 delete pTask;
8350
8351 LogFlowFuncLeave();
8352 return VINF_SUCCESS;
8353}
8354
8355/**
8356 * Thread for executing the saved state operation.
8357 *
8358 * @param Thread The thread handle.
8359 * @param pvUser Pointer to a VMSaveTask structure.
8360 * @return VINF_SUCCESS (ignored).
8361 *
8362 * @note Locks the Console object for writing.
8363 */
8364/*static*/
8365DECLCALLBACK(int) Console::saveStateThread(RTTHREAD Thread, void *pvUser)
8366{
8367 LogFlowFuncEnter();
8368
8369 std::auto_ptr<VMSaveTask> task(static_cast<VMSaveTask*>(pvUser));
8370 AssertReturn(task.get(), VERR_INVALID_PARAMETER);
8371
8372 Assert(task->mSavedStateFile.length());
8373 Assert(task->mProgress.isNull());
8374 Assert(!task->mServerProgress.isNull());
8375
8376 const ComObjPtr<Console> &that = task->mConsole;
8377 Utf8Str errMsg;
8378 HRESULT rc = S_OK;
8379
8380 LogFlowFunc(("Saving the state to '%s'...\n", task->mSavedStateFile.c_str()));
8381
8382 bool fSuspenededBySave;
8383 int vrc = VMR3Save(that->mpVM,
8384 task->mSavedStateFile.c_str(),
8385 false, /*fContinueAfterwards*/
8386 Console::stateProgressCallback,
8387 static_cast<IProgress *>(task->mServerProgress),
8388 &fSuspenededBySave);
8389 if (RT_FAILURE(vrc))
8390 {
8391 errMsg = Utf8StrFmt(Console::tr("Failed to save the machine state to '%s' (%Rrc)"),
8392 task->mSavedStateFile.c_str(), vrc);
8393 rc = E_FAIL;
8394 }
8395 Assert(!fSuspenededBySave);
8396
8397 /* lock the console once we're going to access it */
8398 AutoWriteLock thatLock(that COMMA_LOCKVAL_SRC_POS);
8399
8400 /* synchronize the state with the server */
8401 if (SUCCEEDED(rc))
8402 {
8403 /*
8404 * The machine has been successfully saved, so power it down
8405 * (vmstateChangeCallback() will set state to Saved on success).
8406 * Note: we release the task's VM caller, otherwise it will
8407 * deadlock.
8408 */
8409 task->releaseVMCaller();
8410 rc = that->powerDown();
8411 }
8412
8413 /*
8414 * Finalize the requested save state procedure. In case of failure it will
8415 * reset the machine state to the state it had right before calling
8416 * mControl->BeginSavingState(). This must be the last thing because it
8417 * will set the progress to completed, and that means that the frontend
8418 * can immediately uninit the associated console object.
8419 */
8420 that->mControl->EndSavingState(rc, Bstr(errMsg).raw());
8421
8422 LogFlowFuncLeave();
8423 return VINF_SUCCESS;
8424}
8425
8426/**
8427 * Thread for powering down the Console.
8428 *
8429 * @param Thread The thread handle.
8430 * @param pvUser Pointer to the VMTask structure.
8431 * @return VINF_SUCCESS (ignored).
8432 *
8433 * @note Locks the Console object for writing.
8434 */
8435/*static*/
8436DECLCALLBACK(int) Console::powerDownThread(RTTHREAD Thread, void *pvUser)
8437{
8438 LogFlowFuncEnter();
8439
8440 std::auto_ptr<VMProgressTask> task(static_cast<VMProgressTask *>(pvUser));
8441 AssertReturn(task.get(), VERR_INVALID_PARAMETER);
8442
8443 AssertReturn(task->isOk(), VERR_GENERAL_FAILURE);
8444
8445 const ComObjPtr<Console> &that = task->mConsole;
8446
8447 /* Note: no need to use addCaller() to protect Console because VMTask does
8448 * that */
8449
8450 /* wait until the method tat started us returns */
8451 AutoWriteLock thatLock(that COMMA_LOCKVAL_SRC_POS);
8452
8453 /* release VM caller to avoid the powerDown() deadlock */
8454 task->releaseVMCaller();
8455
8456 that->powerDown(task->mProgress);
8457
8458 LogFlowFuncLeave();
8459 return VINF_SUCCESS;
8460}
8461
8462
8463/**
8464 * @interface_method_impl{VMM2USERMETHODS,pfnSaveState}
8465 */
8466/*static*/
8467DECLCALLBACK(int) Console::vmm2User_SaveState(PCVMM2USERMETHODS pThis, PVM pVM)
8468{
8469 Console *pConsole = *(Console **)(pThis + 1); /* lazy bird */
8470
8471 /*
8472 * For now, just call SaveState. We should probably try notify the GUI so
8473 * it can pop up a progress object and stuff.
8474 */
8475 HRESULT hrc = pConsole->SaveState(NULL);
8476 return SUCCEEDED(hrc) ? VINF_SUCCESS : Global::vboxStatusCodeFromCOM(hrc);
8477}
8478
8479
8480
8481/**
8482 * The Main status driver instance data.
8483 */
8484typedef struct DRVMAINSTATUS
8485{
8486 /** The LED connectors. */
8487 PDMILEDCONNECTORS ILedConnectors;
8488 /** Pointer to the LED ports interface above us. */
8489 PPDMILEDPORTS pLedPorts;
8490 /** Pointer to the array of LED pointers. */
8491 PPDMLED *papLeds;
8492 /** The unit number corresponding to the first entry in the LED array. */
8493 RTUINT iFirstLUN;
8494 /** The unit number corresponding to the last entry in the LED array.
8495 * (The size of the LED array is iLastLUN - iFirstLUN + 1.) */
8496 RTUINT iLastLUN;
8497} DRVMAINSTATUS, *PDRVMAINSTATUS;
8498
8499
8500/**
8501 * Notification about a unit which have been changed.
8502 *
8503 * The driver must discard any pointers to data owned by
8504 * the unit and requery it.
8505 *
8506 * @param pInterface Pointer to the interface structure containing the called function pointer.
8507 * @param iLUN The unit number.
8508 */
8509DECLCALLBACK(void) Console::drvStatus_UnitChanged(PPDMILEDCONNECTORS pInterface, unsigned iLUN)
8510{
8511 PDRVMAINSTATUS pData = (PDRVMAINSTATUS)(void *)pInterface;
8512 if (iLUN >= pData->iFirstLUN && iLUN <= pData->iLastLUN)
8513 {
8514 PPDMLED pLed;
8515 int rc = pData->pLedPorts->pfnQueryStatusLed(pData->pLedPorts, iLUN, &pLed);
8516 if (RT_FAILURE(rc))
8517 pLed = NULL;
8518 ASMAtomicWritePtr(&pData->papLeds[iLUN - pData->iFirstLUN], pLed);
8519 Log(("drvStatus_UnitChanged: iLUN=%d pLed=%p\n", iLUN, pLed));
8520 }
8521}
8522
8523
8524/**
8525 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
8526 */
8527DECLCALLBACK(void *) Console::drvStatus_QueryInterface(PPDMIBASE pInterface, const char *pszIID)
8528{
8529 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
8530 PDRVMAINSTATUS pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINSTATUS);
8531 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
8532 PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDCONNECTORS, &pThis->ILedConnectors);
8533 return NULL;
8534}
8535
8536
8537/**
8538 * Destruct a status driver instance.
8539 *
8540 * @returns VBox status.
8541 * @param pDrvIns The driver instance data.
8542 */
8543DECLCALLBACK(void) Console::drvStatus_Destruct(PPDMDRVINS pDrvIns)
8544{
8545 PDRVMAINSTATUS pData = PDMINS_2_DATA(pDrvIns, PDRVMAINSTATUS);
8546 LogFlowFunc(("iInstance=%d\n", pDrvIns->iInstance));
8547 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
8548
8549 if (pData->papLeds)
8550 {
8551 unsigned iLed = pData->iLastLUN - pData->iFirstLUN + 1;
8552 while (iLed-- > 0)
8553 ASMAtomicWriteNullPtr(&pData->papLeds[iLed]);
8554 }
8555}
8556
8557
8558/**
8559 * Construct a status driver instance.
8560 *
8561 * @copydoc FNPDMDRVCONSTRUCT
8562 */
8563DECLCALLBACK(int) Console::drvStatus_Construct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
8564{
8565 PDRVMAINSTATUS pData = PDMINS_2_DATA(pDrvIns, PDRVMAINSTATUS);
8566 LogFlowFunc(("iInstance=%d\n", pDrvIns->iInstance));
8567 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
8568
8569 /*
8570 * Validate configuration.
8571 */
8572 if (!CFGMR3AreValuesValid(pCfg, "papLeds\0First\0Last\0"))
8573 return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
8574 AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
8575 ("Configuration error: Not possible to attach anything to this driver!\n"),
8576 VERR_PDM_DRVINS_NO_ATTACH);
8577
8578 /*
8579 * Data.
8580 */
8581 pDrvIns->IBase.pfnQueryInterface = Console::drvStatus_QueryInterface;
8582 pData->ILedConnectors.pfnUnitChanged = Console::drvStatus_UnitChanged;
8583
8584 /*
8585 * Read config.
8586 */
8587 int rc = CFGMR3QueryPtr(pCfg, "papLeds", (void **)&pData->papLeds);
8588 if (RT_FAILURE(rc))
8589 {
8590 AssertMsgFailed(("Configuration error: Failed to query the \"papLeds\" value! rc=%Rrc\n", rc));
8591 return rc;
8592 }
8593
8594 rc = CFGMR3QueryU32(pCfg, "First", &pData->iFirstLUN);
8595 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
8596 pData->iFirstLUN = 0;
8597 else if (RT_FAILURE(rc))
8598 {
8599 AssertMsgFailed(("Configuration error: Failed to query the \"First\" value! rc=%Rrc\n", rc));
8600 return rc;
8601 }
8602
8603 rc = CFGMR3QueryU32(pCfg, "Last", &pData->iLastLUN);
8604 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
8605 pData->iLastLUN = 0;
8606 else if (RT_FAILURE(rc))
8607 {
8608 AssertMsgFailed(("Configuration error: Failed to query the \"Last\" value! rc=%Rrc\n", rc));
8609 return rc;
8610 }
8611 if (pData->iFirstLUN > pData->iLastLUN)
8612 {
8613 AssertMsgFailed(("Configuration error: Invalid unit range %u-%u\n", pData->iFirstLUN, pData->iLastLUN));
8614 return VERR_GENERAL_FAILURE;
8615 }
8616
8617 /*
8618 * Get the ILedPorts interface of the above driver/device and
8619 * query the LEDs we want.
8620 */
8621 pData->pLedPorts = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMILEDPORTS);
8622 AssertMsgReturn(pData->pLedPorts, ("Configuration error: No led ports interface above!\n"),
8623 VERR_PDM_MISSING_INTERFACE_ABOVE);
8624
8625 for (unsigned i = pData->iFirstLUN; i <= pData->iLastLUN; ++i)
8626 Console::drvStatus_UnitChanged(&pData->ILedConnectors, i);
8627
8628 return VINF_SUCCESS;
8629}
8630
8631
8632/**
8633 * Keyboard driver registration record.
8634 */
8635const PDMDRVREG Console::DrvStatusReg =
8636{
8637 /* u32Version */
8638 PDM_DRVREG_VERSION,
8639 /* szName */
8640 "MainStatus",
8641 /* szRCMod */
8642 "",
8643 /* szR0Mod */
8644 "",
8645 /* pszDescription */
8646 "Main status driver (Main as in the API).",
8647 /* fFlags */
8648 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
8649 /* fClass. */
8650 PDM_DRVREG_CLASS_STATUS,
8651 /* cMaxInstances */
8652 ~0,
8653 /* cbInstance */
8654 sizeof(DRVMAINSTATUS),
8655 /* pfnConstruct */
8656 Console::drvStatus_Construct,
8657 /* pfnDestruct */
8658 Console::drvStatus_Destruct,
8659 /* pfnRelocate */
8660 NULL,
8661 /* pfnIOCtl */
8662 NULL,
8663 /* pfnPowerOn */
8664 NULL,
8665 /* pfnReset */
8666 NULL,
8667 /* pfnSuspend */
8668 NULL,
8669 /* pfnResume */
8670 NULL,
8671 /* pfnAttach */
8672 NULL,
8673 /* pfnDetach */
8674 NULL,
8675 /* pfnPowerOff */
8676 NULL,
8677 /* pfnSoftReset */
8678 NULL,
8679 /* u32EndVersion */
8680 PDM_DRVREG_VERSION
8681};
8682
8683/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

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