VirtualBox

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

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

Main: reworked listener objects creation, fixes Win problems with events, few cleanups

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

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