VirtualBox

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

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

Main/Console: dead code/whitespace

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

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