VirtualBox

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

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

Updated VBox authentication library interface, removed references to VRDP.

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