VirtualBox

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

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

Switch to the new block cache and disable the old one for now

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