VirtualBox

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

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

Main/VMM: Use UVM w/ refcounting - part 1.

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

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