VirtualBox

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

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

Main, VMM: PCI passthrough work

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