VirtualBox

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

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

burn fix, typo

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