VirtualBox

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

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

Main: do not fail loading machine settings if a shared folder path is not absolute (that breaks importing OVF and machine folders from other hosts since the rules about what constitutes an absolute path differ between host OSes). Instead, issue a runtime warning if a shared folder path is not absolute or does not exist on the host when the VM is powered up.

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