VirtualBox

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

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

cleanup

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 292.5 KB
Line 
1/* $Id: ConsoleImpl.cpp 35758 2011-01-28 12:54:56Z 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 /* first, insert global folders */
5553 for (SharedFolderDataMap::const_iterator it = m_mapGlobalSharedFolders.begin();
5554 it != m_mapGlobalSharedFolders.end();
5555 ++it)
5556 {
5557 const SharedFolderData &d = it->second;
5558 sharedFolders[it->first] = d;
5559 }
5560
5561 /* second, insert machine folders */
5562 for (SharedFolderDataMap::const_iterator it = m_mapMachineSharedFolders.begin();
5563 it != m_mapMachineSharedFolders.end();
5564 ++it)
5565 {
5566 const SharedFolderData &d = it->second;
5567 sharedFolders[it->first] = d;
5568 }
5569
5570 /* third, insert console folders */
5571 for (SharedFolderMap::const_iterator it = m_mapSharedFolders.begin();
5572 it != m_mapSharedFolders.end();
5573 ++it)
5574 {
5575 SharedFolder *pSF = it->second;
5576 AutoCaller sfCaller(pSF);
5577 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
5578 sharedFolders[it->first] = SharedFolderData(pSF->getHostPath(),
5579 pSF->isWritable(),
5580 pSF->isAutoMounted());
5581 }
5582 }
5583
5584 Bstr savedStateFile;
5585
5586 /*
5587 * Saved VMs will have to prove that their saved states seem kosher.
5588 */
5589 if (mMachineState == MachineState_Saved)
5590 {
5591 rc = mMachine->COMGETTER(StateFilePath)(savedStateFile.asOutParam());
5592 if (FAILED(rc))
5593 throw rc;
5594 ComAssertRet(!savedStateFile.isEmpty(), E_FAIL);
5595 int vrc = SSMR3ValidateFile(Utf8Str(savedStateFile).c_str(), false /* fChecksumIt */);
5596 if (RT_FAILURE(vrc))
5597 throw setError(VBOX_E_FILE_ERROR,
5598 tr("VM cannot start because the saved state file '%ls' is invalid (%Rrc). Delete the saved state prior to starting the VM"),
5599 savedStateFile.raw(), vrc);
5600 }
5601
5602 LogFlowThisFunc(("Checking if canceled...\n"));
5603 BOOL fCanceled;
5604 rc = pPowerupProgress->COMGETTER(Canceled)(&fCanceled);
5605 if (FAILED(rc))
5606 throw rc;
5607 if (fCanceled)
5608 {
5609 LogFlowThisFunc(("Canceled in BeginPowerUp\n"));
5610 throw setError(E_FAIL, tr("Powerup was canceled"));
5611 }
5612 LogFlowThisFunc(("Not canceled yet.\n"));
5613
5614 /* setup task object and thread to carry out the operation
5615 * asynchronously */
5616
5617 std::auto_ptr<VMPowerUpTask> task(new VMPowerUpTask(this, pPowerupProgress));
5618 ComAssertComRCRetRC(task->rc());
5619
5620 task->mConfigConstructor = configConstructor;
5621 task->mSharedFolders = sharedFolders;
5622 task->mStartPaused = aPaused;
5623 if (mMachineState == MachineState_Saved)
5624 task->mSavedStateFile = savedStateFile;
5625 task->mTeleporterEnabled = fTeleporterEnabled;
5626 task->mEnmFaultToleranceState = enmFaultToleranceState;
5627
5628 /* Reset differencing hard disks for which autoReset is true,
5629 * but only if the machine has no snapshots OR the current snapshot
5630 * is an OFFLINE snapshot; otherwise we would reset the current
5631 * differencing image of an ONLINE snapshot which contains the disk
5632 * state of the machine while it was previously running, but without
5633 * the corresponding machine state, which is equivalent to powering
5634 * off a running machine and not good idea
5635 */
5636 ComPtr<ISnapshot> pCurrentSnapshot;
5637 rc = mMachine->COMGETTER(CurrentSnapshot)(pCurrentSnapshot.asOutParam());
5638 if (FAILED(rc))
5639 throw rc;
5640
5641 BOOL fCurrentSnapshotIsOnline = false;
5642 if (pCurrentSnapshot)
5643 {
5644 rc = pCurrentSnapshot->COMGETTER(Online)(&fCurrentSnapshotIsOnline);
5645 if (FAILED(rc))
5646 throw rc;
5647 }
5648
5649 if (!fCurrentSnapshotIsOnline)
5650 {
5651 LogFlowThisFunc(("Looking for immutable images to reset\n"));
5652
5653 com::SafeIfaceArray<IMediumAttachment> atts;
5654 rc = mMachine->COMGETTER(MediumAttachments)(ComSafeArrayAsOutParam(atts));
5655 if (FAILED(rc))
5656 throw rc;
5657
5658 for (size_t i = 0;
5659 i < atts.size();
5660 ++i)
5661 {
5662 DeviceType_T devType;
5663 rc = atts[i]->COMGETTER(Type)(&devType);
5664 /** @todo later applies to floppies as well */
5665 if (devType == DeviceType_HardDisk)
5666 {
5667 ComPtr<IMedium> pMedium;
5668 rc = atts[i]->COMGETTER(Medium)(pMedium.asOutParam());
5669 if (FAILED(rc))
5670 throw rc;
5671
5672 /* needs autoreset? */
5673 BOOL autoReset = FALSE;
5674 rc = pMedium->COMGETTER(AutoReset)(&autoReset);
5675 if (FAILED(rc))
5676 throw rc;
5677
5678 if (autoReset)
5679 {
5680 ComPtr<IProgress> pResetProgress;
5681 rc = pMedium->Reset(pResetProgress.asOutParam());
5682 if (FAILED(rc))
5683 throw rc;
5684
5685 /* save for later use on the powerup thread */
5686 task->hardDiskProgresses.push_back(pResetProgress);
5687 }
5688 }
5689 }
5690 }
5691 else
5692 LogFlowThisFunc(("Machine has a current snapshot which is online, skipping immutable images reset\n"));
5693
5694 rc = consoleInitReleaseLog(mMachine);
5695 if (FAILED(rc))
5696 throw rc;
5697
5698#ifdef RT_OS_SOLARIS
5699 /* setup host core dumper for the VM */
5700 Bstr value;
5701 HRESULT hrc = mMachine->GetExtraData(Bstr("VBoxInternal2/CoreDumpEnabled").raw(), value.asOutParam());
5702 if (SUCCEEDED(hrc) && value == "1")
5703 {
5704 Bstr coreDumpDir, coreDumpReplaceSys, coreDumpLive;
5705 mMachine->GetExtraData(Bstr("VBoxInternal2/CoreDumpDir").raw(), coreDumpDir.asOutParam());
5706 mMachine->GetExtraData(Bstr("VBoxInternal2/CoreDumpReplaceSystemDump").raw(), coreDumpReplaceSys.asOutParam());
5707 mMachine->GetExtraData(Bstr("VBoxInternal2/CoreDumpLive").raw(), coreDumpLive.asOutParam());
5708
5709 uint32_t fCoreFlags = 0;
5710 if ( coreDumpReplaceSys.isEmpty() == false
5711 && Utf8Str(coreDumpReplaceSys).toUInt32() == 1)
5712 {
5713 fCoreFlags |= RTCOREDUMPER_FLAGS_REPLACE_SYSTEM_DUMP;
5714 }
5715
5716 if ( coreDumpLive.isEmpty() == false
5717 && Utf8Str(coreDumpLive).toUInt32() == 1)
5718 {
5719 fCoreFlags |= RTCOREDUMPER_FLAGS_LIVE_CORE;
5720 }
5721
5722 Utf8Str strDumpDir(coreDumpDir);
5723 const char *pszDumpDir = strDumpDir.c_str();
5724 if ( pszDumpDir
5725 && *pszDumpDir == '\0')
5726 pszDumpDir = NULL;
5727
5728 int vrc;
5729 if ( pszDumpDir
5730 && !RTDirExists(pszDumpDir))
5731 {
5732 /*
5733 * Try create the directory.
5734 */
5735 vrc = RTDirCreateFullPath(pszDumpDir, 0777);
5736 if (RT_FAILURE(vrc))
5737 throw setError(E_FAIL, "Failed to setup CoreDumper. Couldn't create dump directory '%s' (%Rrc)\n", pszDumpDir, vrc);
5738 }
5739
5740 vrc = RTCoreDumperSetup(pszDumpDir, fCoreFlags);
5741 if (RT_FAILURE(vrc))
5742 throw setError(E_FAIL, "Failed to setup CoreDumper (%Rrc)", vrc);
5743 else
5744 LogRel(("CoreDumper setup successful. pszDumpDir=%s fFlags=%#x\n", pszDumpDir ? pszDumpDir : ".", fCoreFlags));
5745 }
5746#endif
5747
5748 /* pass the progress object to the caller if requested */
5749 if (aProgress)
5750 {
5751 if (task->hardDiskProgresses.size() == 0)
5752 {
5753 /* there are no other operations to track, return the powerup
5754 * progress only */
5755 pPowerupProgress.queryInterfaceTo(aProgress);
5756 }
5757 else
5758 {
5759 /* create a combined progress object */
5760 ComObjPtr<CombinedProgress> pProgress;
5761 pProgress.createObject();
5762 VMPowerUpTask::ProgressList progresses(task->hardDiskProgresses);
5763 progresses.push_back(ComPtr<IProgress> (pPowerupProgress));
5764 rc = pProgress->init(static_cast<IConsole *>(this),
5765 progressDesc.raw(), progresses.begin(),
5766 progresses.end());
5767 AssertComRCReturnRC(rc);
5768 pProgress.queryInterfaceTo(aProgress);
5769 }
5770 }
5771
5772 int vrc = RTThreadCreate(NULL, Console::powerUpThread,
5773 (void *)task.get(), 0,
5774 RTTHREADTYPE_MAIN_WORKER, 0, "VMPowerUp");
5775 if (RT_FAILURE(vrc))
5776 throw setError(E_FAIL, "Could not create VMPowerUp thread (%Rrc)", vrc);
5777
5778 /* task is now owned by powerUpThread(), so release it */
5779 task.release();
5780
5781 /* finally, set the state: no right to fail in this method afterwards
5782 * since we've already started the thread and it is now responsible for
5783 * any error reporting and appropriate state change! */
5784 if (mMachineState == MachineState_Saved)
5785 setMachineState(MachineState_Restoring);
5786 else if (fTeleporterEnabled)
5787 setMachineState(MachineState_TeleportingIn);
5788 else if (enmFaultToleranceState == FaultToleranceState_Standby)
5789 setMachineState(MachineState_FaultTolerantSyncing);
5790 else
5791 setMachineState(MachineState_Starting);
5792 }
5793 catch (HRESULT aRC) { rc = aRC; }
5794
5795 if (FAILED(rc) && fBeganPoweringUp)
5796 {
5797
5798 /* The progress object will fetch the current error info */
5799 if (!pPowerupProgress.isNull())
5800 pPowerupProgress->notifyComplete(rc);
5801
5802 /* Save the error info across the IPC below. Can't be done before the
5803 * progress notification above, as saving the error info deletes it
5804 * from the current context, and thus the progress object wouldn't be
5805 * updated correctly. */
5806 ErrorInfoKeeper eik;
5807
5808 /* signal end of operation */
5809 mControl->EndPowerUp(rc);
5810 }
5811
5812 LogFlowThisFunc(("mMachineState=%d, rc=%Rhrc\n", mMachineState, rc));
5813 LogFlowThisFuncLeave();
5814 return rc;
5815}
5816
5817/**
5818 * Internal power off worker routine.
5819 *
5820 * This method may be called only at certain places with the following meaning
5821 * as shown below:
5822 *
5823 * - if the machine state is either Running or Paused, a normal
5824 * Console-initiated powerdown takes place (e.g. PowerDown());
5825 * - if the machine state is Saving, saveStateThread() has successfully done its
5826 * job;
5827 * - if the machine state is Starting or Restoring, powerUpThread() has failed
5828 * to start/load the VM;
5829 * - if the machine state is Stopping, the VM has powered itself off (i.e. not
5830 * as a result of the powerDown() call).
5831 *
5832 * Calling it in situations other than the above will cause unexpected behavior.
5833 *
5834 * Note that this method should be the only one that destroys mpVM and sets it
5835 * to NULL.
5836 *
5837 * @param aProgress Progress object to run (may be NULL).
5838 *
5839 * @note Locks this object for writing.
5840 *
5841 * @note Never call this method from a thread that called addVMCaller() or
5842 * instantiated an AutoVMCaller object; first call releaseVMCaller() or
5843 * release(). Otherwise it will deadlock.
5844 */
5845HRESULT Console::powerDown(IProgress *aProgress /*= NULL*/)
5846{
5847 LogFlowThisFuncEnter();
5848
5849 AutoCaller autoCaller(this);
5850 AssertComRCReturnRC(autoCaller.rc());
5851
5852 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5853
5854 /* Total # of steps for the progress object. Must correspond to the
5855 * number of "advance percent count" comments in this method! */
5856 enum { StepCount = 7 };
5857 /* current step */
5858 ULONG step = 0;
5859
5860 HRESULT rc = S_OK;
5861 int vrc = VINF_SUCCESS;
5862
5863 /* sanity */
5864 Assert(mVMDestroying == false);
5865
5866 Assert(mpVM != NULL);
5867
5868 AssertMsg( mMachineState == MachineState_Running
5869 || mMachineState == MachineState_Paused
5870 || mMachineState == MachineState_Stuck
5871 || mMachineState == MachineState_Starting
5872 || mMachineState == MachineState_Stopping
5873 || mMachineState == MachineState_Saving
5874 || mMachineState == MachineState_Restoring
5875 || mMachineState == MachineState_TeleportingPausedVM
5876 || mMachineState == MachineState_FaultTolerantSyncing
5877 || mMachineState == MachineState_TeleportingIn
5878 , ("Invalid machine state: %s\n", Global::stringifyMachineState(mMachineState)));
5879
5880 LogRel(("Console::powerDown(): A request to power off the VM has been issued (mMachineState=%s, InUninit=%d)\n",
5881 Global::stringifyMachineState(mMachineState), autoCaller.state() == InUninit));
5882
5883 /* Check if we need to power off the VM. In case of mVMPoweredOff=true, the
5884 * VM has already powered itself off in vmstateChangeCallback() and is just
5885 * notifying Console about that. In case of Starting or Restoring,
5886 * powerUpThread() is calling us on failure, so the VM is already off at
5887 * that point. */
5888 if ( !mVMPoweredOff
5889 && ( mMachineState == MachineState_Starting
5890 || mMachineState == MachineState_Restoring
5891 || mMachineState == MachineState_FaultTolerantSyncing
5892 || mMachineState == MachineState_TeleportingIn)
5893 )
5894 mVMPoweredOff = true;
5895
5896 /*
5897 * Go to Stopping state if not already there.
5898 *
5899 * Note that we don't go from Saving/Restoring to Stopping because
5900 * vmstateChangeCallback() needs it to set the state to Saved on
5901 * VMSTATE_TERMINATED. In terms of protecting from inappropriate operations
5902 * while leaving the lock below, Saving or Restoring should be fine too.
5903 * Ditto for TeleportingPausedVM -> Teleported.
5904 */
5905 if ( mMachineState != MachineState_Saving
5906 && mMachineState != MachineState_Restoring
5907 && mMachineState != MachineState_Stopping
5908 && mMachineState != MachineState_TeleportingIn
5909 && mMachineState != MachineState_TeleportingPausedVM
5910 && mMachineState != MachineState_FaultTolerantSyncing
5911 )
5912 setMachineState(MachineState_Stopping);
5913
5914 /* ----------------------------------------------------------------------
5915 * DONE with necessary state changes, perform the power down actions (it's
5916 * safe to leave the object lock now if needed)
5917 * ---------------------------------------------------------------------- */
5918
5919 /* Stop the VRDP server to prevent new clients connection while VM is being
5920 * powered off. */
5921 if (mConsoleVRDPServer)
5922 {
5923 LogFlowThisFunc(("Stopping VRDP server...\n"));
5924
5925 /* Leave the lock since EMT will call us back as addVMCaller()
5926 * in updateDisplayData(). */
5927 alock.leave();
5928
5929 mConsoleVRDPServer->Stop();
5930
5931 alock.enter();
5932 }
5933
5934 /* advance percent count */
5935 if (aProgress)
5936 aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
5937
5938
5939 /* ----------------------------------------------------------------------
5940 * Now, wait for all mpVM callers to finish their work if there are still
5941 * some on other threads. NO methods that need mpVM (or initiate other calls
5942 * that need it) may be called after this point
5943 * ---------------------------------------------------------------------- */
5944
5945 /* go to the destroying state to prevent from adding new callers */
5946 mVMDestroying = true;
5947
5948 if (mVMCallers > 0)
5949 {
5950 /* lazy creation */
5951 if (mVMZeroCallersSem == NIL_RTSEMEVENT)
5952 RTSemEventCreate(&mVMZeroCallersSem);
5953
5954 LogFlowThisFunc(("Waiting for mpVM callers (%d) to drop to zero...\n",
5955 mVMCallers));
5956
5957 alock.leave();
5958
5959 RTSemEventWait(mVMZeroCallersSem, RT_INDEFINITE_WAIT);
5960
5961 alock.enter();
5962 }
5963
5964 /* advance percent count */
5965 if (aProgress)
5966 aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
5967
5968 vrc = VINF_SUCCESS;
5969
5970 /*
5971 * Power off the VM if not already done that.
5972 * Leave the lock since EMT will call vmstateChangeCallback.
5973 *
5974 * Note that VMR3PowerOff() may fail here (invalid VMSTATE) if the
5975 * VM-(guest-)initiated power off happened in parallel a ms before this
5976 * call. So far, we let this error pop up on the user's side.
5977 */
5978 if (!mVMPoweredOff)
5979 {
5980 LogFlowThisFunc(("Powering off the VM...\n"));
5981 alock.leave();
5982 vrc = VMR3PowerOff(mpVM);
5983#ifdef VBOX_WITH_EXTPACK
5984 mptrExtPackManager->callAllVmPowerOffHooks(this, mpVM);
5985#endif
5986 alock.enter();
5987 }
5988
5989 /* advance percent count */
5990 if (aProgress)
5991 aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
5992
5993#ifdef VBOX_WITH_HGCM
5994 /* Shutdown HGCM services before destroying the VM. */
5995 if (m_pVMMDev)
5996 {
5997 LogFlowThisFunc(("Shutdown HGCM...\n"));
5998
5999 /* Leave the lock since EMT will call us back as addVMCaller() */
6000 alock.leave();
6001
6002 m_pVMMDev->hgcmShutdown();
6003
6004 alock.enter();
6005 }
6006
6007 /* advance percent count */
6008 if (aProgress)
6009 aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
6010
6011#endif /* VBOX_WITH_HGCM */
6012
6013 LogFlowThisFunc(("Ready for VM destruction.\n"));
6014
6015 /* If we are called from Console::uninit(), then try to destroy the VM even
6016 * on failure (this will most likely fail too, but what to do?..) */
6017 if (RT_SUCCESS(vrc) || autoCaller.state() == InUninit)
6018 {
6019 /* If the machine has an USB controller, release all USB devices
6020 * (symmetric to the code in captureUSBDevices()) */
6021 bool fHasUSBController = false;
6022 {
6023 PPDMIBASE pBase;
6024 vrc = PDMR3QueryLun(mpVM, "usb-ohci", 0, 0, &pBase);
6025 if (RT_SUCCESS(vrc))
6026 {
6027 fHasUSBController = true;
6028 detachAllUSBDevices(false /* aDone */);
6029 }
6030 }
6031
6032 /* Now we've got to destroy the VM as well. (mpVM is not valid beyond
6033 * this point). We leave the lock before calling VMR3Destroy() because
6034 * it will result into calling destructors of drivers associated with
6035 * Console children which may in turn try to lock Console (e.g. by
6036 * instantiating SafeVMPtr to access mpVM). It's safe here because
6037 * mVMDestroying is set which should prevent any activity. */
6038
6039 /* Set mpVM to NULL early just in case if some old code is not using
6040 * addVMCaller()/releaseVMCaller(). */
6041 PVM pVM = mpVM;
6042 mpVM = NULL;
6043
6044 LogFlowThisFunc(("Destroying the VM...\n"));
6045
6046 alock.leave();
6047
6048 vrc = VMR3Destroy(pVM);
6049
6050 /* take the lock again */
6051 alock.enter();
6052
6053 /* advance percent count */
6054 if (aProgress)
6055 aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
6056
6057 if (RT_SUCCESS(vrc))
6058 {
6059 LogFlowThisFunc(("Machine has been destroyed (mMachineState=%d)\n",
6060 mMachineState));
6061 /* Note: the Console-level machine state change happens on the
6062 * VMSTATE_TERMINATE state change in vmstateChangeCallback(). If
6063 * powerDown() is called from EMT (i.e. from vmstateChangeCallback()
6064 * on receiving VM-initiated VMSTATE_OFF), VMSTATE_TERMINATE hasn't
6065 * occurred yet. This is okay, because mMachineState is already
6066 * Stopping in this case, so any other attempt to call PowerDown()
6067 * will be rejected. */
6068 }
6069 else
6070 {
6071 /* bad bad bad, but what to do? */
6072 mpVM = pVM;
6073 rc = setError(VBOX_E_VM_ERROR,
6074 tr("Could not destroy the machine. (Error: %Rrc)"),
6075 vrc);
6076 }
6077
6078 /* Complete the detaching of the USB devices. */
6079 if (fHasUSBController)
6080 detachAllUSBDevices(true /* aDone */);
6081
6082 /* advance percent count */
6083 if (aProgress)
6084 aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
6085 }
6086 else
6087 {
6088 rc = setError(VBOX_E_VM_ERROR,
6089 tr("Could not power off the machine. (Error: %Rrc)"),
6090 vrc);
6091 }
6092
6093 /* Finished with destruction. Note that if something impossible happened and
6094 * we've failed to destroy the VM, mVMDestroying will remain true and
6095 * mMachineState will be something like Stopping, so most Console methods
6096 * will return an error to the caller. */
6097 if (mpVM == NULL)
6098 mVMDestroying = false;
6099
6100 if (SUCCEEDED(rc))
6101 mCallbackData.clear();
6102
6103 LogFlowThisFuncLeave();
6104 return rc;
6105}
6106
6107/**
6108 * @note Locks this object for writing.
6109 */
6110HRESULT Console::setMachineState(MachineState_T aMachineState,
6111 bool aUpdateServer /* = true */)
6112{
6113 AutoCaller autoCaller(this);
6114 AssertComRCReturnRC(autoCaller.rc());
6115
6116 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6117
6118 HRESULT rc = S_OK;
6119
6120 if (mMachineState != aMachineState)
6121 {
6122 LogThisFunc(("machineState=%s -> %s aUpdateServer=%RTbool\n",
6123 Global::stringifyMachineState(mMachineState), Global::stringifyMachineState(aMachineState), aUpdateServer));
6124 mMachineState = aMachineState;
6125
6126 /// @todo (dmik)
6127 // possibly, we need to redo onStateChange() using the dedicated
6128 // Event thread, like it is done in VirtualBox. This will make it
6129 // much safer (no deadlocks possible if someone tries to use the
6130 // console from the callback), however, listeners will lose the
6131 // ability to synchronously react to state changes (is it really
6132 // necessary??)
6133 LogFlowThisFunc(("Doing onStateChange()...\n"));
6134 onStateChange(aMachineState);
6135 LogFlowThisFunc(("Done onStateChange()\n"));
6136
6137 if (aUpdateServer)
6138 {
6139 /* Server notification MUST be done from under the lock; otherwise
6140 * the machine state here and on the server might go out of sync
6141 * which can lead to various unexpected results (like the machine
6142 * state being >= MachineState_Running on the server, while the
6143 * session state is already SessionState_Unlocked at the same time
6144 * there).
6145 *
6146 * Cross-lock conditions should be carefully watched out: calling
6147 * UpdateState we will require Machine and SessionMachine locks
6148 * (remember that here we're holding the Console lock here, and also
6149 * all locks that have been entered by the thread before calling
6150 * this method).
6151 */
6152 LogFlowThisFunc(("Doing mControl->UpdateState()...\n"));
6153 rc = mControl->UpdateState(aMachineState);
6154 LogFlowThisFunc(("mControl->UpdateState()=%Rhrc\n", rc));
6155 }
6156 }
6157
6158 return rc;
6159}
6160
6161/**
6162 * Searches for a shared folder with the given logical name
6163 * in the collection of shared folders.
6164 *
6165 * @param aName logical name of the shared folder
6166 * @param aSharedFolder where to return the found object
6167 * @param aSetError whether to set the error info if the folder is
6168 * not found
6169 * @return
6170 * S_OK when found or E_INVALIDARG when not found
6171 *
6172 * @note The caller must lock this object for writing.
6173 */
6174HRESULT Console::findSharedFolder(const Utf8Str &strName,
6175 ComObjPtr<SharedFolder> &aSharedFolder,
6176 bool aSetError /* = false */)
6177{
6178 /* sanity check */
6179 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
6180
6181 SharedFolderMap::const_iterator it = m_mapSharedFolders.find(strName);
6182 if (it != m_mapSharedFolders.end())
6183 {
6184 aSharedFolder = it->second;
6185 return S_OK;
6186 }
6187
6188 if (aSetError)
6189 setError(VBOX_E_FILE_ERROR,
6190 tr("Could not find a shared folder named '%s'."),
6191 strName.c_str());
6192
6193 return VBOX_E_FILE_ERROR;
6194}
6195
6196/**
6197 * Fetches the list of global or machine shared folders from the server.
6198 *
6199 * @param aGlobal true to fetch global folders.
6200 *
6201 * @note The caller must lock this object for writing.
6202 */
6203HRESULT Console::fetchSharedFolders(BOOL aGlobal)
6204{
6205 /* sanity check */
6206 AssertReturn(AutoCaller(this).state() == InInit ||
6207 isWriteLockOnCurrentThread(), E_FAIL);
6208
6209 LogFlowThisFunc(("Entering\n"));
6210
6211 /* protect mpVM (if not NULL) */
6212 AutoVMCallerQuietWeak autoVMCaller(this);
6213
6214 HRESULT rc = S_OK;
6215
6216 bool online = mpVM
6217 && autoVMCaller.isOk()
6218 && m_pVMMDev
6219 && m_pVMMDev->isShFlActive();
6220
6221 try
6222 {
6223 if (aGlobal)
6224 {
6225 /// @todo grab & process global folders when they are done
6226 }
6227 else
6228 {
6229 SharedFolderDataMap oldFolders;
6230 if (online)
6231 oldFolders = m_mapMachineSharedFolders;
6232
6233 m_mapMachineSharedFolders.clear();
6234
6235 SafeIfaceArray<ISharedFolder> folders;
6236 rc = mMachine->COMGETTER(SharedFolders)(ComSafeArrayAsOutParam(folders));
6237 if (FAILED(rc)) throw rc;
6238
6239 for (size_t i = 0; i < folders.size(); ++i)
6240 {
6241 ComPtr<ISharedFolder> pSharedFolder = folders[i];
6242
6243 Bstr bstrName;
6244 Bstr bstrHostPath;
6245 BOOL writable;
6246 BOOL autoMount;
6247
6248 rc = pSharedFolder->COMGETTER(Name)(bstrName.asOutParam());
6249 if (FAILED(rc)) throw rc;
6250 Utf8Str strName(bstrName);
6251
6252 rc = pSharedFolder->COMGETTER(HostPath)(bstrHostPath.asOutParam());
6253 if (FAILED(rc)) throw rc;
6254 Utf8Str strHostPath(bstrHostPath);
6255
6256 rc = pSharedFolder->COMGETTER(Writable)(&writable);
6257 if (FAILED(rc)) throw rc;
6258
6259 rc = pSharedFolder->COMGETTER(AutoMount)(&autoMount);
6260 if (FAILED(rc)) throw rc;
6261
6262 m_mapMachineSharedFolders.insert(std::make_pair(strName,
6263 SharedFolderData(strHostPath, writable, autoMount)));
6264
6265 /* send changes to HGCM if the VM is running */
6266 if (online)
6267 {
6268 SharedFolderDataMap::iterator it = oldFolders.find(strName);
6269 if ( it == oldFolders.end()
6270 || it->second.m_strHostPath != strHostPath)
6271 {
6272 /* a new machine folder is added or
6273 * the existing machine folder is changed */
6274 if (m_mapSharedFolders.find(strName) != m_mapSharedFolders.end())
6275 ; /* the console folder exists, nothing to do */
6276 else
6277 {
6278 /* remove the old machine folder (when changed)
6279 * or the global folder if any (when new) */
6280 if ( it != oldFolders.end()
6281 || m_mapGlobalSharedFolders.find(strName) != m_mapGlobalSharedFolders.end()
6282 )
6283 {
6284 rc = removeSharedFolder(strName);
6285 if (FAILED(rc)) throw rc;
6286 }
6287
6288 /* create the new machine folder */
6289 rc = createSharedFolder(strName,
6290 SharedFolderData(strHostPath,
6291 writable,
6292 autoMount));
6293 if (FAILED(rc)) throw rc;
6294 }
6295 }
6296 /* forget the processed (or identical) folder */
6297 if (it != oldFolders.end())
6298 oldFolders.erase(it);
6299 }
6300 }
6301
6302 /* process outdated (removed) folders */
6303 if (online)
6304 {
6305 for (SharedFolderDataMap::const_iterator it = oldFolders.begin();
6306 it != oldFolders.end(); ++ it)
6307 {
6308 if (m_mapSharedFolders.find(it->first) != m_mapSharedFolders.end())
6309 ; /* the console folder exists, nothing to do */
6310 else
6311 {
6312 /* remove the outdated machine folder */
6313 rc = removeSharedFolder(it->first);
6314 if (FAILED(rc)) throw rc;
6315
6316 /* create the global folder if there is any */
6317 SharedFolderDataMap::const_iterator git =
6318 m_mapGlobalSharedFolders.find(it->first);
6319 if (git != m_mapGlobalSharedFolders.end())
6320 {
6321 rc = createSharedFolder(git->first, git->second);
6322 if (FAILED(rc)) throw rc;
6323 }
6324 }
6325 }
6326 }
6327 }
6328 }
6329 catch (HRESULT rc2)
6330 {
6331 if (online)
6332 setVMRuntimeErrorCallbackF(mpVM, this, 0, "BrokenSharedFolder",
6333 N_("Broken shared folder!"));
6334 }
6335
6336 LogFlowThisFunc(("Leaving\n"));
6337
6338 return rc;
6339}
6340
6341/**
6342 * Searches for a shared folder with the given name in the list of machine
6343 * shared folders and then in the list of the global shared folders.
6344 *
6345 * @param aName Name of the folder to search for.
6346 * @param aIt Where to store the pointer to the found folder.
6347 * @return @c true if the folder was found and @c false otherwise.
6348 *
6349 * @note The caller must lock this object for reading.
6350 */
6351bool Console::findOtherSharedFolder(const Utf8Str &strName,
6352 SharedFolderDataMap::const_iterator &aIt)
6353{
6354 /* sanity check */
6355 AssertReturn(isWriteLockOnCurrentThread(), false);
6356
6357 /* first, search machine folders */
6358 aIt = m_mapMachineSharedFolders.find(strName);
6359 if (aIt != m_mapMachineSharedFolders.end())
6360 return true;
6361
6362 /* second, search machine folders */
6363 aIt = m_mapGlobalSharedFolders.find(strName);
6364 if (aIt != m_mapGlobalSharedFolders.end())
6365 return true;
6366
6367 return false;
6368}
6369
6370/**
6371 * Calls the HGCM service to add a shared folder definition.
6372 *
6373 * @param aName Shared folder name.
6374 * @param aHostPath Shared folder path.
6375 *
6376 * @note Must be called from under AutoVMCaller and when mpVM != NULL!
6377 * @note Doesn't lock anything.
6378 */
6379HRESULT Console::createSharedFolder(const Utf8Str &strName, const SharedFolderData &aData)
6380{
6381 ComAssertRet(strName.isNotEmpty(), E_FAIL);
6382 ComAssertRet(aData.m_strHostPath.isNotEmpty(), E_FAIL);
6383
6384 /* sanity checks */
6385 AssertReturn(mpVM, E_FAIL);
6386 AssertReturn(m_pVMMDev && m_pVMMDev->isShFlActive(), E_FAIL);
6387
6388 VBOXHGCMSVCPARM parms[SHFL_CPARMS_ADD_MAPPING2];
6389 SHFLSTRING *pFolderName, *pMapName;
6390 size_t cbString;
6391
6392 Log(("Adding shared folder '%s' -> '%s'\n", strName.c_str(), aData.m_strHostPath.c_str()));
6393
6394 // check whether the path is valid and exists
6395 /* Check whether the path is full (absolute) */
6396 char hostPathFull[RTPATH_MAX];
6397 int vrc = RTPathAbsEx(NULL,
6398 aData.m_strHostPath.c_str(),
6399 hostPathFull,
6400 sizeof(hostPathFull));
6401 if (RT_FAILURE(vrc))
6402 return setError(E_INVALIDARG,
6403 tr("Invalid shared folder path: '%s' (%Rrc)"),
6404 aData.m_strHostPath.c_str(), vrc);
6405
6406 if (RTPathCompare(aData.m_strHostPath.c_str(), hostPathFull) != 0)
6407 return setError(E_INVALIDARG,
6408 tr("Shared folder path '%s' is not absolute"),
6409 aData.m_strHostPath.c_str());
6410 if (!RTPathExists(hostPathFull))
6411 return setError(E_INVALIDARG,
6412 tr("Shared folder path '%s' does not exist on the host"),
6413 aData.m_strHostPath.c_str());
6414
6415 // now that we know the path is good, give it to HGCM
6416
6417 Bstr bstrName(strName);
6418 Bstr bstrHostPath(aData.m_strHostPath);
6419
6420 cbString = (bstrHostPath.length() + 1) * sizeof(RTUTF16);
6421 if (cbString >= UINT16_MAX)
6422 return setError(E_INVALIDARG, tr("The name is too long"));
6423 pFolderName = (SHFLSTRING*)RTMemAllocZ(sizeof(SHFLSTRING) + cbString);
6424 Assert(pFolderName);
6425 memcpy(pFolderName->String.ucs2, bstrHostPath.raw(), cbString);
6426
6427 pFolderName->u16Size = (uint16_t)cbString;
6428 pFolderName->u16Length = (uint16_t)cbString - sizeof(RTUTF16);
6429
6430 parms[0].type = VBOX_HGCM_SVC_PARM_PTR;
6431 parms[0].u.pointer.addr = pFolderName;
6432 parms[0].u.pointer.size = sizeof(SHFLSTRING) + (uint16_t)cbString;
6433
6434 cbString = (bstrName.length() + 1) * sizeof(RTUTF16);
6435 if (cbString >= UINT16_MAX)
6436 {
6437 RTMemFree(pFolderName);
6438 return setError(E_INVALIDARG, tr("The host path is too long"));
6439 }
6440 pMapName = (SHFLSTRING*)RTMemAllocZ(sizeof(SHFLSTRING) + cbString);
6441 Assert(pMapName);
6442 memcpy(pMapName->String.ucs2, bstrName.raw(), cbString);
6443
6444 pMapName->u16Size = (uint16_t)cbString;
6445 pMapName->u16Length = (uint16_t)cbString - sizeof(RTUTF16);
6446
6447 parms[1].type = VBOX_HGCM_SVC_PARM_PTR;
6448 parms[1].u.pointer.addr = pMapName;
6449 parms[1].u.pointer.size = sizeof(SHFLSTRING) + (uint16_t)cbString;
6450
6451 parms[2].type = VBOX_HGCM_SVC_PARM_32BIT;
6452 parms[2].u.uint32 = aData.m_fWritable;
6453
6454 /*
6455 * Auto-mount flag; is indicated by using the SHFL_CPARMS_ADD_MAPPING2
6456 * define below. This shows the host service that we have supplied
6457 * an additional parameter (auto-mount) and keeps the actual command
6458 * backwards compatible.
6459 */
6460 parms[3].type = VBOX_HGCM_SVC_PARM_32BIT;
6461 parms[3].u.uint32 = aData.m_fAutoMount;
6462
6463 vrc = m_pVMMDev->hgcmHostCall("VBoxSharedFolders",
6464 SHFL_FN_ADD_MAPPING,
6465 SHFL_CPARMS_ADD_MAPPING2, &parms[0]);
6466 RTMemFree(pFolderName);
6467 RTMemFree(pMapName);
6468
6469 if (RT_FAILURE(vrc))
6470 return setError(E_FAIL,
6471 tr("Could not create a shared folder '%s' mapped to '%s' (%Rrc)"),
6472 strName.c_str(), aData.m_strHostPath.c_str(), vrc);
6473
6474 return S_OK;
6475}
6476
6477/**
6478 * Calls the HGCM service to remove the shared folder definition.
6479 *
6480 * @param aName Shared folder name.
6481 *
6482 * @note Must be called from under AutoVMCaller and when mpVM != NULL!
6483 * @note Doesn't lock anything.
6484 */
6485HRESULT Console::removeSharedFolder(const Utf8Str &strName)
6486{
6487 ComAssertRet(strName.isNotEmpty(), E_FAIL);
6488
6489 /* sanity checks */
6490 AssertReturn(mpVM, E_FAIL);
6491 AssertReturn(m_pVMMDev && m_pVMMDev->isShFlActive(), E_FAIL);
6492
6493 VBOXHGCMSVCPARM parms;
6494 SHFLSTRING *pMapName;
6495 size_t cbString;
6496
6497 Log(("Removing shared folder '%s'\n", strName.c_str()));
6498
6499 Bstr bstrName(strName);
6500 cbString = (bstrName.length() + 1) * sizeof(RTUTF16);
6501 if (cbString >= UINT16_MAX)
6502 return setError(E_INVALIDARG, tr("The name is too long"));
6503 pMapName = (SHFLSTRING *) RTMemAllocZ(sizeof(SHFLSTRING) + cbString);
6504 Assert(pMapName);
6505 memcpy(pMapName->String.ucs2, bstrName.raw(), cbString);
6506
6507 pMapName->u16Size = (uint16_t)cbString;
6508 pMapName->u16Length = (uint16_t)cbString - sizeof(RTUTF16);
6509
6510 parms.type = VBOX_HGCM_SVC_PARM_PTR;
6511 parms.u.pointer.addr = pMapName;
6512 parms.u.pointer.size = sizeof(SHFLSTRING) + (uint16_t)cbString;
6513
6514 int vrc = m_pVMMDev->hgcmHostCall("VBoxSharedFolders",
6515 SHFL_FN_REMOVE_MAPPING,
6516 1, &parms);
6517 RTMemFree(pMapName);
6518 if (RT_FAILURE(vrc))
6519 return setError(E_FAIL,
6520 tr("Could not remove the shared folder '%s' (%Rrc)"),
6521 strName.c_str(), vrc);
6522
6523 return S_OK;
6524}
6525
6526/**
6527 * VM state callback function. Called by the VMM
6528 * using its state machine states.
6529 *
6530 * Primarily used to handle VM initiated power off, suspend and state saving,
6531 * but also for doing termination completed work (VMSTATE_TERMINATE).
6532 *
6533 * In general this function is called in the context of the EMT.
6534 *
6535 * @param aVM The VM handle.
6536 * @param aState The new state.
6537 * @param aOldState The old state.
6538 * @param aUser The user argument (pointer to the Console object).
6539 *
6540 * @note Locks the Console object for writing.
6541 */
6542DECLCALLBACK(void) Console::vmstateChangeCallback(PVM aVM,
6543 VMSTATE aState,
6544 VMSTATE aOldState,
6545 void *aUser)
6546{
6547 LogFlowFunc(("Changing state from %s to %s (aVM=%p)\n",
6548 VMR3GetStateName(aOldState), VMR3GetStateName(aState), aVM));
6549
6550 Console *that = static_cast<Console *>(aUser);
6551 AssertReturnVoid(that);
6552
6553 AutoCaller autoCaller(that);
6554
6555 /* Note that we must let this method proceed even if Console::uninit() has
6556 * been already called. In such case this VMSTATE change is a result of:
6557 * 1) powerDown() called from uninit() itself, or
6558 * 2) VM-(guest-)initiated power off. */
6559 AssertReturnVoid( autoCaller.isOk()
6560 || autoCaller.state() == InUninit);
6561
6562 switch (aState)
6563 {
6564 /*
6565 * The VM has terminated
6566 */
6567 case VMSTATE_OFF:
6568 {
6569 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6570
6571 if (that->mVMStateChangeCallbackDisabled)
6572 break;
6573
6574 /* Do we still think that it is running? It may happen if this is a
6575 * VM-(guest-)initiated shutdown/poweroff.
6576 */
6577 if ( that->mMachineState != MachineState_Stopping
6578 && that->mMachineState != MachineState_Saving
6579 && that->mMachineState != MachineState_Restoring
6580 && that->mMachineState != MachineState_TeleportingIn
6581 && that->mMachineState != MachineState_FaultTolerantSyncing
6582 && that->mMachineState != MachineState_TeleportingPausedVM
6583 && !that->mVMIsAlreadyPoweringOff
6584 )
6585 {
6586 LogFlowFunc(("VM has powered itself off but Console still thinks it is running. Notifying.\n"));
6587
6588 /* prevent powerDown() from calling VMR3PowerOff() again */
6589 Assert(that->mVMPoweredOff == false);
6590 that->mVMPoweredOff = true;
6591
6592 /*
6593 * request a progress object from the server
6594 * (this will set the machine state to Stopping on the server
6595 * to block others from accessing this machine)
6596 */
6597 ComPtr<IProgress> pProgress;
6598 HRESULT rc = that->mControl->BeginPoweringDown(pProgress.asOutParam());
6599 AssertComRC(rc);
6600
6601 /* sync the state with the server */
6602 that->setMachineStateLocally(MachineState_Stopping);
6603
6604 /* Setup task object and thread to carry out the operation
6605 * asynchronously (if we call powerDown() right here but there
6606 * is one or more mpVM callers (added with addVMCaller()) we'll
6607 * deadlock).
6608 */
6609 std::auto_ptr<VMPowerDownTask> task(new VMPowerDownTask(that,
6610 pProgress));
6611
6612 /* If creating a task failed, this can currently mean one of
6613 * two: either Console::uninit() has been called just a ms
6614 * before (so a powerDown() call is already on the way), or
6615 * powerDown() itself is being already executed. Just do
6616 * nothing.
6617 */
6618 if (!task->isOk())
6619 {
6620 LogFlowFunc(("Console is already being uninitialized.\n"));
6621 break;
6622 }
6623
6624 int vrc = RTThreadCreate(NULL, Console::powerDownThread,
6625 (void *) task.get(), 0,
6626 RTTHREADTYPE_MAIN_WORKER, 0,
6627 "VMPowerDown");
6628 AssertMsgRCBreak(vrc, ("Could not create VMPowerDown thread (%Rrc)\n", vrc));
6629
6630 /* task is now owned by powerDownThread(), so release it */
6631 task.release();
6632 }
6633 break;
6634 }
6635
6636 /* The VM has been completely destroyed.
6637 *
6638 * Note: This state change can happen at two points:
6639 * 1) At the end of VMR3Destroy() if it was not called from EMT.
6640 * 2) At the end of vmR3EmulationThread if VMR3Destroy() was
6641 * called by EMT.
6642 */
6643 case VMSTATE_TERMINATED:
6644 {
6645 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6646
6647 if (that->mVMStateChangeCallbackDisabled)
6648 break;
6649
6650 /* Terminate host interface networking. If aVM is NULL, we've been
6651 * manually called from powerUpThread() either before calling
6652 * VMR3Create() or after VMR3Create() failed, so no need to touch
6653 * networking.
6654 */
6655 if (aVM)
6656 that->powerDownHostInterfaces();
6657
6658 /* From now on the machine is officially powered down or remains in
6659 * the Saved state.
6660 */
6661 switch (that->mMachineState)
6662 {
6663 default:
6664 AssertFailed();
6665 /* fall through */
6666 case MachineState_Stopping:
6667 /* successfully powered down */
6668 that->setMachineState(MachineState_PoweredOff);
6669 break;
6670 case MachineState_Saving:
6671 /* successfully saved */
6672 that->setMachineState(MachineState_Saved);
6673 break;
6674 case MachineState_Starting:
6675 /* failed to start, but be patient: set back to PoweredOff
6676 * (for similarity with the below) */
6677 that->setMachineState(MachineState_PoweredOff);
6678 break;
6679 case MachineState_Restoring:
6680 /* failed to load the saved state file, but be patient: set
6681 * back to Saved (to preserve the saved state file) */
6682 that->setMachineState(MachineState_Saved);
6683 break;
6684 case MachineState_TeleportingIn:
6685 /* Teleportation failed or was canceled. Back to powered off. */
6686 that->setMachineState(MachineState_PoweredOff);
6687 break;
6688 case MachineState_TeleportingPausedVM:
6689 /* Successfully teleported the VM. */
6690 that->setMachineState(MachineState_Teleported);
6691 break;
6692 case MachineState_FaultTolerantSyncing:
6693 /* Fault tolerant sync failed or was canceled. Back to powered off. */
6694 that->setMachineState(MachineState_PoweredOff);
6695 break;
6696 }
6697 break;
6698 }
6699
6700 case VMSTATE_SUSPENDED:
6701 {
6702 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6703
6704 if (that->mVMStateChangeCallbackDisabled)
6705 break;
6706
6707 switch (that->mMachineState)
6708 {
6709 case MachineState_Teleporting:
6710 that->setMachineState(MachineState_TeleportingPausedVM);
6711 break;
6712
6713 case MachineState_LiveSnapshotting:
6714 that->setMachineState(MachineState_Saving);
6715 break;
6716
6717 case MachineState_TeleportingPausedVM:
6718 case MachineState_Saving:
6719 case MachineState_Restoring:
6720 case MachineState_Stopping:
6721 case MachineState_TeleportingIn:
6722 case MachineState_FaultTolerantSyncing:
6723 /* The worker thread handles the transition. */
6724 break;
6725
6726 default:
6727 AssertMsgFailed(("%s\n", Global::stringifyMachineState(that->mMachineState)));
6728 case MachineState_Running:
6729 that->setMachineState(MachineState_Paused);
6730 break;
6731
6732 case MachineState_Paused:
6733 /* Nothing to do. */
6734 break;
6735 }
6736 break;
6737 }
6738
6739 case VMSTATE_SUSPENDED_LS:
6740 case VMSTATE_SUSPENDED_EXT_LS:
6741 {
6742 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6743 if (that->mVMStateChangeCallbackDisabled)
6744 break;
6745 switch (that->mMachineState)
6746 {
6747 case MachineState_Teleporting:
6748 that->setMachineState(MachineState_TeleportingPausedVM);
6749 break;
6750
6751 case MachineState_LiveSnapshotting:
6752 that->setMachineState(MachineState_Saving);
6753 break;
6754
6755 case MachineState_TeleportingPausedVM:
6756 case MachineState_Saving:
6757 /* ignore */
6758 break;
6759
6760 default:
6761 AssertMsgFailed(("%s/%s -> %s\n", Global::stringifyMachineState(that->mMachineState), VMR3GetStateName(aOldState), VMR3GetStateName(aState) ));
6762 that->setMachineState(MachineState_Paused);
6763 break;
6764 }
6765 break;
6766 }
6767
6768 case VMSTATE_RUNNING:
6769 {
6770 if ( aOldState == VMSTATE_POWERING_ON
6771 || aOldState == VMSTATE_RESUMING
6772 || aOldState == VMSTATE_RUNNING_FT)
6773 {
6774 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6775
6776 if (that->mVMStateChangeCallbackDisabled)
6777 break;
6778
6779 Assert( ( ( that->mMachineState == MachineState_Starting
6780 || that->mMachineState == MachineState_Paused)
6781 && aOldState == VMSTATE_POWERING_ON)
6782 || ( ( that->mMachineState == MachineState_Restoring
6783 || that->mMachineState == MachineState_TeleportingIn
6784 || that->mMachineState == MachineState_Paused
6785 || that->mMachineState == MachineState_Saving
6786 )
6787 && aOldState == VMSTATE_RESUMING)
6788 || ( that->mMachineState == MachineState_FaultTolerantSyncing
6789 && aOldState == VMSTATE_RUNNING_FT));
6790
6791 that->setMachineState(MachineState_Running);
6792 }
6793
6794 break;
6795 }
6796
6797 case VMSTATE_RUNNING_LS:
6798 AssertMsg( that->mMachineState == MachineState_LiveSnapshotting
6799 || that->mMachineState == MachineState_Teleporting,
6800 ("%s/%s -> %s\n", Global::stringifyMachineState(that->mMachineState), VMR3GetStateName(aOldState), VMR3GetStateName(aState) ));
6801 break;
6802
6803 case VMSTATE_RUNNING_FT:
6804 AssertMsg(that->mMachineState == MachineState_FaultTolerantSyncing,
6805 ("%s/%s -> %s\n", Global::stringifyMachineState(that->mMachineState), VMR3GetStateName(aOldState), VMR3GetStateName(aState) ));
6806 break;
6807
6808 case VMSTATE_FATAL_ERROR:
6809 {
6810 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6811
6812 if (that->mVMStateChangeCallbackDisabled)
6813 break;
6814
6815 /* Fatal errors are only for running VMs. */
6816 Assert(Global::IsOnline(that->mMachineState));
6817
6818 /* Note! 'Pause' is used here in want of something better. There
6819 * are currently only two places where fatal errors might be
6820 * raised, so it is not worth adding a new externally
6821 * visible state for this yet. */
6822 that->setMachineState(MachineState_Paused);
6823 break;
6824 }
6825
6826 case VMSTATE_GURU_MEDITATION:
6827 {
6828 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6829
6830 if (that->mVMStateChangeCallbackDisabled)
6831 break;
6832
6833 /* Guru are only for running VMs */
6834 Assert(Global::IsOnline(that->mMachineState));
6835
6836 that->setMachineState(MachineState_Stuck);
6837 break;
6838 }
6839
6840 default: /* shut up gcc */
6841 break;
6842 }
6843}
6844
6845#ifdef VBOX_WITH_USB
6846/**
6847 * Sends a request to VMM to attach the given host device.
6848 * After this method succeeds, the attached device will appear in the
6849 * mUSBDevices collection.
6850 *
6851 * @param aHostDevice device to attach
6852 *
6853 * @note Synchronously calls EMT.
6854 * @note Must be called from under this object's lock.
6855 */
6856HRESULT Console::attachUSBDevice(IUSBDevice *aHostDevice, ULONG aMaskedIfs)
6857{
6858 AssertReturn(aHostDevice, E_FAIL);
6859 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
6860
6861 /* still want a lock object because we need to leave it */
6862 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6863
6864 HRESULT hrc;
6865
6866 /*
6867 * Get the address and the Uuid, and call the pfnCreateProxyDevice roothub
6868 * method in EMT (using usbAttachCallback()).
6869 */
6870 Bstr BstrAddress;
6871 hrc = aHostDevice->COMGETTER(Address)(BstrAddress.asOutParam());
6872 ComAssertComRCRetRC(hrc);
6873
6874 Utf8Str Address(BstrAddress);
6875
6876 Bstr id;
6877 hrc = aHostDevice->COMGETTER(Id)(id.asOutParam());
6878 ComAssertComRCRetRC(hrc);
6879 Guid uuid(id);
6880
6881 BOOL fRemote = FALSE;
6882 hrc = aHostDevice->COMGETTER(Remote)(&fRemote);
6883 ComAssertComRCRetRC(hrc);
6884
6885 /* protect mpVM */
6886 AutoVMCaller autoVMCaller(this);
6887 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
6888
6889 LogFlowThisFunc(("Proxying USB device '%s' {%RTuuid}...\n",
6890 Address.c_str(), uuid.raw()));
6891
6892 /* leave the lock before a VMR3* call (EMT will call us back)! */
6893 alock.leave();
6894
6895/** @todo just do everything here and only wrap the PDMR3Usb call. That'll offload some notification stuff from the EMT thread. */
6896 int vrc = VMR3ReqCallWait(mpVM, VMCPUID_ANY,
6897 (PFNRT)usbAttachCallback, 6, this, aHostDevice, uuid.raw(), fRemote, Address.c_str(), aMaskedIfs);
6898
6899 /* restore the lock */
6900 alock.enter();
6901
6902 /* hrc is S_OK here */
6903
6904 if (RT_FAILURE(vrc))
6905 {
6906 LogWarningThisFunc(("Failed to create proxy device for '%s' {%RTuuid} (%Rrc)\n",
6907 Address.c_str(), uuid.raw(), vrc));
6908
6909 switch (vrc)
6910 {
6911 case VERR_VUSB_NO_PORTS:
6912 hrc = setError(E_FAIL,
6913 tr("Failed to attach the USB device. (No available ports on the USB controller)."));
6914 break;
6915 case VERR_VUSB_USBFS_PERMISSION:
6916 hrc = setError(E_FAIL,
6917 tr("Not permitted to open the USB device, check usbfs options"));
6918 break;
6919 default:
6920 hrc = setError(E_FAIL,
6921 tr("Failed to create a proxy device for the USB device. (Error: %Rrc)"),
6922 vrc);
6923 break;
6924 }
6925 }
6926
6927 return hrc;
6928}
6929
6930/**
6931 * USB device attach callback used by AttachUSBDevice().
6932 * Note that AttachUSBDevice() doesn't return until this callback is executed,
6933 * so we don't use AutoCaller and don't care about reference counters of
6934 * interface pointers passed in.
6935 *
6936 * @thread EMT
6937 * @note Locks the console object for writing.
6938 */
6939//static
6940DECLCALLBACK(int)
6941Console::usbAttachCallback(Console *that, IUSBDevice *aHostDevice, PCRTUUID aUuid, bool aRemote, const char *aAddress, ULONG aMaskedIfs)
6942{
6943 LogFlowFuncEnter();
6944 LogFlowFunc(("that={%p}\n", that));
6945
6946 AssertReturn(that && aUuid, VERR_INVALID_PARAMETER);
6947
6948 void *pvRemoteBackend = NULL;
6949 if (aRemote)
6950 {
6951 RemoteUSBDevice *pRemoteUSBDevice = static_cast<RemoteUSBDevice *>(aHostDevice);
6952 Guid guid(*aUuid);
6953
6954 pvRemoteBackend = that->consoleVRDPServer()->USBBackendRequestPointer(pRemoteUSBDevice->clientId(), &guid);
6955 if (!pvRemoteBackend)
6956 return VERR_INVALID_PARAMETER; /* The clientId is invalid then. */
6957 }
6958
6959 USHORT portVersion = 1;
6960 HRESULT hrc = aHostDevice->COMGETTER(PortVersion)(&portVersion);
6961 AssertComRCReturn(hrc, VERR_GENERAL_FAILURE);
6962 Assert(portVersion == 1 || portVersion == 2);
6963
6964 int vrc = PDMR3USBCreateProxyDevice(that->mpVM, aUuid, aRemote, aAddress, pvRemoteBackend,
6965 portVersion == 1 ? VUSB_STDVER_11 : VUSB_STDVER_20, aMaskedIfs);
6966 if (RT_SUCCESS(vrc))
6967 {
6968 /* Create a OUSBDevice and add it to the device list */
6969 ComObjPtr<OUSBDevice> pUSBDevice;
6970 pUSBDevice.createObject();
6971 hrc = pUSBDevice->init(aHostDevice);
6972 AssertComRC(hrc);
6973
6974 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6975 that->mUSBDevices.push_back(pUSBDevice);
6976 LogFlowFunc(("Attached device {%RTuuid}\n", pUSBDevice->id().raw()));
6977
6978 /* notify callbacks */
6979 that->onUSBDeviceStateChange(pUSBDevice, true /* aAttached */, NULL);
6980 }
6981
6982 LogFlowFunc(("vrc=%Rrc\n", vrc));
6983 LogFlowFuncLeave();
6984 return vrc;
6985}
6986
6987/**
6988 * Sends a request to VMM to detach the given host device. After this method
6989 * succeeds, the detached device will disappear from the mUSBDevices
6990 * collection.
6991 *
6992 * @param aIt Iterator pointing to the device to detach.
6993 *
6994 * @note Synchronously calls EMT.
6995 * @note Must be called from under this object's lock.
6996 */
6997HRESULT Console::detachUSBDevice(USBDeviceList::iterator &aIt)
6998{
6999 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
7000
7001 /* still want a lock object because we need to leave it */
7002 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7003
7004 /* protect mpVM */
7005 AutoVMCaller autoVMCaller(this);
7006 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
7007
7008 /* if the device is attached, then there must at least one USB hub. */
7009 AssertReturn(PDMR3USBHasHub(mpVM), E_FAIL);
7010
7011 LogFlowThisFunc(("Detaching USB proxy device {%RTuuid}...\n",
7012 (*aIt)->id().raw()));
7013
7014 /* leave the lock before a VMR3* call (EMT will call us back)! */
7015 alock.leave();
7016
7017/** @todo just do everything here and only wrap the PDMR3Usb call. That'll offload some notification stuff from the EMT thread. */
7018 int vrc = VMR3ReqCallWait(mpVM, VMCPUID_ANY,
7019 (PFNRT) usbDetachCallback, 4, this, &aIt, (*aIt)->id().raw());
7020 ComAssertRCRet(vrc, E_FAIL);
7021
7022 return S_OK;
7023}
7024
7025/**
7026 * USB device detach callback used by DetachUSBDevice().
7027 * Note that DetachUSBDevice() doesn't return until this callback is executed,
7028 * so we don't use AutoCaller and don't care about reference counters of
7029 * interface pointers passed in.
7030 *
7031 * @thread EMT
7032 * @note Locks the console object for writing.
7033 */
7034//static
7035DECLCALLBACK(int)
7036Console::usbDetachCallback(Console *that, USBDeviceList::iterator *aIt, PCRTUUID aUuid)
7037{
7038 LogFlowFuncEnter();
7039 LogFlowFunc(("that={%p}\n", that));
7040
7041 AssertReturn(that && aUuid, VERR_INVALID_PARAMETER);
7042 ComObjPtr<OUSBDevice> pUSBDevice = **aIt;
7043
7044 /*
7045 * If that was a remote device, release the backend pointer.
7046 * The pointer was requested in usbAttachCallback.
7047 */
7048 BOOL fRemote = FALSE;
7049
7050 HRESULT hrc2 = (**aIt)->COMGETTER(Remote)(&fRemote);
7051 if (FAILED(hrc2))
7052 setErrorStatic(hrc2, "GetRemote() failed");
7053
7054 if (fRemote)
7055 {
7056 Guid guid(*aUuid);
7057 that->consoleVRDPServer()->USBBackendReleasePointer(&guid);
7058 }
7059
7060 int vrc = PDMR3USBDetachDevice(that->mpVM, aUuid);
7061
7062 if (RT_SUCCESS(vrc))
7063 {
7064 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
7065
7066 /* Remove the device from the collection */
7067 that->mUSBDevices.erase(*aIt);
7068 LogFlowFunc(("Detached device {%RTuuid}\n", pUSBDevice->id().raw()));
7069
7070 /* notify callbacks */
7071 that->onUSBDeviceStateChange(pUSBDevice, false /* aAttached */, NULL);
7072 }
7073
7074 LogFlowFunc(("vrc=%Rrc\n", vrc));
7075 LogFlowFuncLeave();
7076 return vrc;
7077}
7078#endif /* VBOX_WITH_USB */
7079
7080#if ((defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD)) && !defined(VBOX_WITH_NETFLT))
7081/**
7082 * Helper function to handle host interface device creation and attachment.
7083 *
7084 * @param networkAdapter the network adapter which attachment should be reset
7085 * @return COM status code
7086 *
7087 * @note The caller must lock this object for writing.
7088 *
7089 * @todo Move this back into the driver!
7090 */
7091HRESULT Console::attachToTapInterface(INetworkAdapter *networkAdapter)
7092{
7093 LogFlowThisFunc(("\n"));
7094 /* sanity check */
7095 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
7096
7097# ifdef VBOX_STRICT
7098 /* paranoia */
7099 NetworkAttachmentType_T attachment;
7100 networkAdapter->COMGETTER(AttachmentType)(&attachment);
7101 Assert(attachment == NetworkAttachmentType_Bridged);
7102# endif /* VBOX_STRICT */
7103
7104 HRESULT rc = S_OK;
7105
7106 ULONG slot = 0;
7107 rc = networkAdapter->COMGETTER(Slot)(&slot);
7108 AssertComRC(rc);
7109
7110# ifdef RT_OS_LINUX
7111 /*
7112 * Allocate a host interface device
7113 */
7114 int rcVBox = RTFileOpen(&maTapFD[slot], "/dev/net/tun",
7115 RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_INHERIT);
7116 if (RT_SUCCESS(rcVBox))
7117 {
7118 /*
7119 * Set/obtain the tap interface.
7120 */
7121 struct ifreq IfReq;
7122 memset(&IfReq, 0, sizeof(IfReq));
7123 /* The name of the TAP interface we are using */
7124 Bstr tapDeviceName;
7125 rc = networkAdapter->COMGETTER(HostInterface)(tapDeviceName.asOutParam());
7126 if (FAILED(rc))
7127 tapDeviceName.setNull(); /* Is this necessary? */
7128 if (tapDeviceName.isEmpty())
7129 {
7130 LogRel(("No TAP device name was supplied.\n"));
7131 rc = setError(E_FAIL, tr("No TAP device name was supplied for the host networking interface"));
7132 }
7133
7134 if (SUCCEEDED(rc))
7135 {
7136 /* If we are using a static TAP device then try to open it. */
7137 Utf8Str str(tapDeviceName);
7138 if (str.length() <= sizeof(IfReq.ifr_name))
7139 strcpy(IfReq.ifr_name, str.c_str());
7140 else
7141 memcpy(IfReq.ifr_name, str.c_str(), sizeof(IfReq.ifr_name) - 1); /** @todo bitch about names which are too long... */
7142 IfReq.ifr_flags = IFF_TAP | IFF_NO_PI;
7143 rcVBox = ioctl(maTapFD[slot], TUNSETIFF, &IfReq);
7144 if (rcVBox != 0)
7145 {
7146 LogRel(("Failed to open the host network interface %ls\n", tapDeviceName.raw()));
7147 rc = setError(E_FAIL,
7148 tr("Failed to open the host network interface %ls"),
7149 tapDeviceName.raw());
7150 }
7151 }
7152 if (SUCCEEDED(rc))
7153 {
7154 /*
7155 * Make it pollable.
7156 */
7157 if (fcntl(maTapFD[slot], F_SETFL, O_NONBLOCK) != -1)
7158 {
7159 Log(("attachToTapInterface: %RTfile %ls\n", maTapFD[slot], tapDeviceName.raw()));
7160 /*
7161 * Here is the right place to communicate the TAP file descriptor and
7162 * the host interface name to the server if/when it becomes really
7163 * necessary.
7164 */
7165 maTAPDeviceName[slot] = tapDeviceName;
7166 rcVBox = VINF_SUCCESS;
7167 }
7168 else
7169 {
7170 int iErr = errno;
7171
7172 LogRel(("Configuration error: Failed to configure /dev/net/tun non blocking. Error: %s\n", strerror(iErr)));
7173 rcVBox = VERR_HOSTIF_BLOCKING;
7174 rc = setError(E_FAIL,
7175 tr("could not set up the host networking device for non blocking access: %s"),
7176 strerror(errno));
7177 }
7178 }
7179 }
7180 else
7181 {
7182 LogRel(("Configuration error: Failed to open /dev/net/tun rc=%Rrc\n", rcVBox));
7183 switch (rcVBox)
7184 {
7185 case VERR_ACCESS_DENIED:
7186 /* will be handled by our caller */
7187 rc = rcVBox;
7188 break;
7189 default:
7190 rc = setError(E_FAIL,
7191 tr("Could not set up the host networking device: %Rrc"),
7192 rcVBox);
7193 break;
7194 }
7195 }
7196
7197# elif defined(RT_OS_FREEBSD)
7198 /*
7199 * Set/obtain the tap interface.
7200 */
7201 /* The name of the TAP interface we are using */
7202 Bstr tapDeviceName;
7203 rc = networkAdapter->COMGETTER(HostInterface)(tapDeviceName.asOutParam());
7204 if (FAILED(rc))
7205 tapDeviceName.setNull(); /* Is this necessary? */
7206 if (tapDeviceName.isEmpty())
7207 {
7208 LogRel(("No TAP device name was supplied.\n"));
7209 rc = setError(E_FAIL, tr("No TAP device name was supplied for the host networking interface"));
7210 }
7211 char szTapdev[1024] = "/dev/";
7212 /* If we are using a static TAP device then try to open it. */
7213 Utf8Str str(tapDeviceName);
7214 if (str.length() + strlen(szTapdev) <= sizeof(szTapdev))
7215 strcat(szTapdev, str.c_str());
7216 else
7217 memcpy(szTapdev + strlen(szTapdev), str.c_str(),
7218 sizeof(szTapdev) - strlen(szTapdev) - 1); /** @todo bitch about names which are too long... */
7219 int rcVBox = RTFileOpen(&maTapFD[slot], szTapdev,
7220 RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_INHERIT | RTFILE_O_NON_BLOCK);
7221
7222 if (RT_SUCCESS(rcVBox))
7223 maTAPDeviceName[slot] = tapDeviceName;
7224 else
7225 {
7226 switch (rcVBox)
7227 {
7228 case VERR_ACCESS_DENIED:
7229 /* will be handled by our caller */
7230 rc = rcVBox;
7231 break;
7232 default:
7233 rc = setError(E_FAIL,
7234 tr("Failed to open the host network interface %ls"),
7235 tapDeviceName.raw());
7236 break;
7237 }
7238 }
7239# else
7240# error "huh?"
7241# endif
7242 /* in case of failure, cleanup. */
7243 if (RT_FAILURE(rcVBox) && SUCCEEDED(rc))
7244 {
7245 LogRel(("General failure attaching to host interface\n"));
7246 rc = setError(E_FAIL,
7247 tr("General failure attaching to host interface"));
7248 }
7249 LogFlowThisFunc(("rc=%d\n", rc));
7250 return rc;
7251}
7252
7253
7254/**
7255 * Helper function to handle detachment from a host interface
7256 *
7257 * @param networkAdapter the network adapter which attachment should be reset
7258 * @return COM status code
7259 *
7260 * @note The caller must lock this object for writing.
7261 *
7262 * @todo Move this back into the driver!
7263 */
7264HRESULT Console::detachFromTapInterface(INetworkAdapter *networkAdapter)
7265{
7266 /* sanity check */
7267 LogFlowThisFunc(("\n"));
7268 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
7269
7270 HRESULT rc = S_OK;
7271# ifdef VBOX_STRICT
7272 /* paranoia */
7273 NetworkAttachmentType_T attachment;
7274 networkAdapter->COMGETTER(AttachmentType)(&attachment);
7275 Assert(attachment == NetworkAttachmentType_Bridged);
7276# endif /* VBOX_STRICT */
7277
7278 ULONG slot = 0;
7279 rc = networkAdapter->COMGETTER(Slot)(&slot);
7280 AssertComRC(rc);
7281
7282 /* is there an open TAP device? */
7283 if (maTapFD[slot] != NIL_RTFILE)
7284 {
7285 /*
7286 * Close the file handle.
7287 */
7288 Bstr tapDeviceName, tapTerminateApplication;
7289 bool isStatic = true;
7290 rc = networkAdapter->COMGETTER(HostInterface)(tapDeviceName.asOutParam());
7291 if (FAILED(rc) || tapDeviceName.isEmpty())
7292 {
7293 /* If the name is empty, this is a dynamic TAP device, so close it now,
7294 so that the termination script can remove the interface. Otherwise we still
7295 need the FD to pass to the termination script. */
7296 isStatic = false;
7297 int rcVBox = RTFileClose(maTapFD[slot]);
7298 AssertRC(rcVBox);
7299 maTapFD[slot] = NIL_RTFILE;
7300 }
7301 if (isStatic)
7302 {
7303 /* If we are using a static TAP device, we close it now, after having called the
7304 termination script. */
7305 int rcVBox = RTFileClose(maTapFD[slot]);
7306 AssertRC(rcVBox);
7307 }
7308 /* the TAP device name and handle are no longer valid */
7309 maTapFD[slot] = NIL_RTFILE;
7310 maTAPDeviceName[slot] = "";
7311 }
7312 LogFlowThisFunc(("returning %d\n", rc));
7313 return rc;
7314}
7315#endif /* (RT_OS_LINUX || RT_OS_FREEBSD) && !VBOX_WITH_NETFLT */
7316
7317/**
7318 * Called at power down to terminate host interface networking.
7319 *
7320 * @note The caller must lock this object for writing.
7321 */
7322HRESULT Console::powerDownHostInterfaces()
7323{
7324 LogFlowThisFunc(("\n"));
7325
7326 /* sanity check */
7327 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
7328
7329 /*
7330 * host interface termination handling
7331 */
7332 HRESULT rc;
7333 for (ULONG slot = 0; slot < SchemaDefs::NetworkAdapterCount; slot ++)
7334 {
7335 ComPtr<INetworkAdapter> pNetworkAdapter;
7336 rc = mMachine->GetNetworkAdapter(slot, pNetworkAdapter.asOutParam());
7337 if (FAILED(rc)) break;
7338
7339 BOOL enabled = FALSE;
7340 pNetworkAdapter->COMGETTER(Enabled)(&enabled);
7341 if (!enabled)
7342 continue;
7343
7344 NetworkAttachmentType_T attachment;
7345 pNetworkAdapter->COMGETTER(AttachmentType)(&attachment);
7346 if (attachment == NetworkAttachmentType_Bridged)
7347 {
7348#if ((defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD)) && !defined(VBOX_WITH_NETFLT))
7349 HRESULT rc2 = detachFromTapInterface(pNetworkAdapter);
7350 if (FAILED(rc2) && SUCCEEDED(rc))
7351 rc = rc2;
7352#endif /* (RT_OS_LINUX || RT_OS_FREEBSD) && !VBOX_WITH_NETFLT */
7353 }
7354 }
7355
7356 return rc;
7357}
7358
7359
7360/**
7361 * Process callback handler for VMR3LoadFromFile, VMR3LoadFromStream, VMR3Save
7362 * and VMR3Teleport.
7363 *
7364 * @param pVM The VM handle.
7365 * @param uPercent Completion percentage (0-100).
7366 * @param pvUser Pointer to an IProgress instance.
7367 * @return VINF_SUCCESS.
7368 */
7369/*static*/
7370DECLCALLBACK(int) Console::stateProgressCallback(PVM pVM, unsigned uPercent, void *pvUser)
7371{
7372 IProgress *pProgress = static_cast<IProgress *>(pvUser);
7373
7374 /* update the progress object */
7375 if (pProgress)
7376 pProgress->SetCurrentOperationProgress(uPercent);
7377
7378 return VINF_SUCCESS;
7379}
7380
7381/**
7382 * @copydoc FNVMATERROR
7383 *
7384 * @remarks Might be some tiny serialization concerns with access to the string
7385 * object here...
7386 */
7387/*static*/ DECLCALLBACK(void)
7388Console::genericVMSetErrorCallback(PVM pVM, void *pvUser, int rc, RT_SRC_POS_DECL,
7389 const char *pszErrorFmt, va_list va)
7390{
7391 Utf8Str *pErrorText = (Utf8Str *)pvUser;
7392 AssertPtr(pErrorText);
7393
7394 /* We ignore RT_SRC_POS_DECL arguments to avoid confusion of end-users. */
7395 va_list va2;
7396 va_copy(va2, va);
7397
7398 /* Append to any the existing error message. */
7399 if (pErrorText->length())
7400 *pErrorText = Utf8StrFmt("%s.\n%N (%Rrc)", pErrorText->c_str(),
7401 pszErrorFmt, &va2, rc, rc);
7402 else
7403 *pErrorText = Utf8StrFmt("%N (%Rrc)", pszErrorFmt, &va2, rc, rc);
7404
7405 va_end(va2);
7406}
7407
7408/**
7409 * VM runtime error callback function.
7410 * See VMSetRuntimeError for the detailed description of parameters.
7411 *
7412 * @param pVM The VM handle.
7413 * @param pvUser The user argument.
7414 * @param fFlags The action flags. See VMSETRTERR_FLAGS_*.
7415 * @param pszErrorId Error ID string.
7416 * @param pszFormat Error message format string.
7417 * @param va Error message arguments.
7418 * @thread EMT.
7419 */
7420/* static */ DECLCALLBACK(void)
7421Console::setVMRuntimeErrorCallback(PVM pVM, void *pvUser, uint32_t fFlags,
7422 const char *pszErrorId,
7423 const char *pszFormat, va_list va)
7424{
7425 bool const fFatal = !!(fFlags & VMSETRTERR_FLAGS_FATAL);
7426 LogFlowFuncEnter();
7427
7428 Console *that = static_cast<Console *>(pvUser);
7429 AssertReturnVoid(that);
7430
7431 Utf8Str message(pszFormat, va);
7432
7433 LogRel(("Console: VM runtime error: fatal=%RTbool, errorID=%s message=\"%s\"\n",
7434 fFatal, pszErrorId, message.c_str()));
7435
7436 that->onRuntimeError(BOOL(fFatal), Bstr(pszErrorId).raw(),
7437 Bstr(message).raw());
7438
7439 LogFlowFuncLeave();
7440}
7441
7442/**
7443 * Captures USB devices that match filters of the VM.
7444 * Called at VM startup.
7445 *
7446 * @param pVM The VM handle.
7447 *
7448 * @note The caller must lock this object for writing.
7449 */
7450HRESULT Console::captureUSBDevices(PVM pVM)
7451{
7452 LogFlowThisFunc(("\n"));
7453
7454 /* sanity check */
7455 ComAssertRet(isWriteLockOnCurrentThread(), E_FAIL);
7456
7457 /* If the machine has an USB controller, ask the USB proxy service to
7458 * capture devices */
7459 PPDMIBASE pBase;
7460 int vrc = PDMR3QueryLun(pVM, "usb-ohci", 0, 0, &pBase);
7461 if (RT_SUCCESS(vrc))
7462 {
7463 /* leave the lock before calling Host in VBoxSVC since Host may call
7464 * us back from under its lock (e.g. onUSBDeviceAttach()) which would
7465 * produce an inter-process dead-lock otherwise. */
7466 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7467 alock.leave();
7468
7469 HRESULT hrc = mControl->AutoCaptureUSBDevices();
7470 ComAssertComRCRetRC(hrc);
7471 }
7472 else if ( vrc == VERR_PDM_DEVICE_NOT_FOUND
7473 || vrc == VERR_PDM_DEVICE_INSTANCE_NOT_FOUND)
7474 vrc = VINF_SUCCESS;
7475 else
7476 AssertRC(vrc);
7477
7478 return RT_SUCCESS(vrc) ? S_OK : E_FAIL;
7479}
7480
7481
7482/**
7483 * Detach all USB device which are attached to the VM for the
7484 * purpose of clean up and such like.
7485 *
7486 * @note The caller must lock this object for writing.
7487 */
7488void Console::detachAllUSBDevices(bool aDone)
7489{
7490 LogFlowThisFunc(("aDone=%RTbool\n", aDone));
7491
7492 /* sanity check */
7493 AssertReturnVoid(isWriteLockOnCurrentThread());
7494
7495 mUSBDevices.clear();
7496
7497 /* leave the lock before calling Host in VBoxSVC since Host may call
7498 * us back from under its lock (e.g. onUSBDeviceAttach()) which would
7499 * produce an inter-process dead-lock otherwise. */
7500 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7501 alock.leave();
7502
7503 mControl->DetachAllUSBDevices(aDone);
7504}
7505
7506/**
7507 * @note Locks this object for writing.
7508 */
7509void Console::processRemoteUSBDevices(uint32_t u32ClientId, VRDEUSBDEVICEDESC *pDevList, uint32_t cbDevList)
7510{
7511 LogFlowThisFuncEnter();
7512 LogFlowThisFunc(("u32ClientId = %d, pDevList=%p, cbDevList = %d\n", u32ClientId, pDevList, cbDevList));
7513
7514 AutoCaller autoCaller(this);
7515 if (!autoCaller.isOk())
7516 {
7517 /* Console has been already uninitialized, deny request */
7518 AssertMsgFailed(("Console is already uninitialized\n"));
7519 LogFlowThisFunc(("Console is already uninitialized\n"));
7520 LogFlowThisFuncLeave();
7521 return;
7522 }
7523
7524 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7525
7526 /*
7527 * Mark all existing remote USB devices as dirty.
7528 */
7529 for (RemoteUSBDeviceList::iterator it = mRemoteUSBDevices.begin();
7530 it != mRemoteUSBDevices.end();
7531 ++it)
7532 {
7533 (*it)->dirty(true);
7534 }
7535
7536 /*
7537 * Process the pDevList and add devices those are not already in the mRemoteUSBDevices list.
7538 */
7539 /** @todo (sunlover) REMOTE_USB Strict validation of the pDevList. */
7540 VRDEUSBDEVICEDESC *e = pDevList;
7541
7542 /* The cbDevList condition must be checked first, because the function can
7543 * receive pDevList = NULL and cbDevList = 0 on client disconnect.
7544 */
7545 while (cbDevList >= 2 && e->oNext)
7546 {
7547 LogFlowThisFunc(("vendor %04X, product %04X, name = %s\n",
7548 e->idVendor, e->idProduct,
7549 e->oProduct? (char *)e + e->oProduct: ""));
7550
7551 bool fNewDevice = true;
7552
7553 for (RemoteUSBDeviceList::iterator it = mRemoteUSBDevices.begin();
7554 it != mRemoteUSBDevices.end();
7555 ++it)
7556 {
7557 if ((*it)->devId() == e->id
7558 && (*it)->clientId() == u32ClientId)
7559 {
7560 /* The device is already in the list. */
7561 (*it)->dirty(false);
7562 fNewDevice = false;
7563 break;
7564 }
7565 }
7566
7567 if (fNewDevice)
7568 {
7569 LogRel(("Remote USB: ++++ Vendor %04X. Product %04X. Name = [%s].\n",
7570 e->idVendor, e->idProduct, e->oProduct? (char *)e + e->oProduct: ""));
7571
7572 /* Create the device object and add the new device to list. */
7573 ComObjPtr<RemoteUSBDevice> pUSBDevice;
7574 pUSBDevice.createObject();
7575 pUSBDevice->init(u32ClientId, e);
7576
7577 mRemoteUSBDevices.push_back(pUSBDevice);
7578
7579 /* Check if the device is ok for current USB filters. */
7580 BOOL fMatched = FALSE;
7581 ULONG fMaskedIfs = 0;
7582
7583 HRESULT hrc = mControl->RunUSBDeviceFilters(pUSBDevice, &fMatched, &fMaskedIfs);
7584
7585 AssertComRC(hrc);
7586
7587 LogFlowThisFunc(("USB filters return %d %#x\n", fMatched, fMaskedIfs));
7588
7589 if (fMatched)
7590 {
7591 hrc = onUSBDeviceAttach(pUSBDevice, NULL, fMaskedIfs);
7592
7593 /// @todo (r=dmik) warning reporting subsystem
7594
7595 if (hrc == S_OK)
7596 {
7597 LogFlowThisFunc(("Device attached\n"));
7598 pUSBDevice->captured(true);
7599 }
7600 }
7601 }
7602
7603 if (cbDevList < e->oNext)
7604 {
7605 LogWarningThisFunc(("cbDevList %d > oNext %d\n",
7606 cbDevList, e->oNext));
7607 break;
7608 }
7609
7610 cbDevList -= e->oNext;
7611
7612 e = (VRDEUSBDEVICEDESC *)((uint8_t *)e + e->oNext);
7613 }
7614
7615 /*
7616 * Remove dirty devices, that is those which are not reported by the server anymore.
7617 */
7618 for (;;)
7619 {
7620 ComObjPtr<RemoteUSBDevice> pUSBDevice;
7621
7622 RemoteUSBDeviceList::iterator it = mRemoteUSBDevices.begin();
7623 while (it != mRemoteUSBDevices.end())
7624 {
7625 if ((*it)->dirty())
7626 {
7627 pUSBDevice = *it;
7628 break;
7629 }
7630
7631 ++ it;
7632 }
7633
7634 if (!pUSBDevice)
7635 {
7636 break;
7637 }
7638
7639 USHORT vendorId = 0;
7640 pUSBDevice->COMGETTER(VendorId)(&vendorId);
7641
7642 USHORT productId = 0;
7643 pUSBDevice->COMGETTER(ProductId)(&productId);
7644
7645 Bstr product;
7646 pUSBDevice->COMGETTER(Product)(product.asOutParam());
7647
7648 LogRel(("Remote USB: ---- Vendor %04X. Product %04X. Name = [%ls].\n",
7649 vendorId, productId, product.raw()));
7650
7651 /* Detach the device from VM. */
7652 if (pUSBDevice->captured())
7653 {
7654 Bstr uuid;
7655 pUSBDevice->COMGETTER(Id)(uuid.asOutParam());
7656 onUSBDeviceDetach(uuid.raw(), NULL);
7657 }
7658
7659 /* And remove it from the list. */
7660 mRemoteUSBDevices.erase(it);
7661 }
7662
7663 LogFlowThisFuncLeave();
7664}
7665
7666/**
7667 * Progress cancelation callback for fault tolerance VM poweron
7668 */
7669static void faultToleranceProgressCancelCallback(void *pvUser)
7670{
7671 PVM pVM = (PVM)pvUser;
7672
7673 if (pVM)
7674 FTMR3CancelStandby(pVM);
7675}
7676
7677/**
7678 * Thread function which starts the VM (also from saved state) and
7679 * track progress.
7680 *
7681 * @param Thread The thread id.
7682 * @param pvUser Pointer to a VMPowerUpTask structure.
7683 * @return VINF_SUCCESS (ignored).
7684 *
7685 * @note Locks the Console object for writing.
7686 */
7687/*static*/
7688DECLCALLBACK(int) Console::powerUpThread(RTTHREAD Thread, void *pvUser)
7689{
7690 LogFlowFuncEnter();
7691
7692 std::auto_ptr<VMPowerUpTask> task(static_cast<VMPowerUpTask *>(pvUser));
7693 AssertReturn(task.get(), VERR_INVALID_PARAMETER);
7694
7695 AssertReturn(!task->mConsole.isNull(), VERR_INVALID_PARAMETER);
7696 AssertReturn(!task->mProgress.isNull(), VERR_INVALID_PARAMETER);
7697
7698#if defined(RT_OS_WINDOWS)
7699 {
7700 /* initialize COM */
7701 HRESULT hrc = CoInitializeEx(NULL,
7702 COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE |
7703 COINIT_SPEED_OVER_MEMORY);
7704 LogFlowFunc(("CoInitializeEx()=%Rhrc\n", hrc));
7705 }
7706#endif
7707
7708 HRESULT rc = S_OK;
7709 int vrc = VINF_SUCCESS;
7710
7711 /* Set up a build identifier so that it can be seen from core dumps what
7712 * exact build was used to produce the core. */
7713 static char saBuildID[40];
7714 RTStrPrintf(saBuildID, sizeof(saBuildID), "%s%s%s%s VirtualBox %s r%u %s%s%s%s",
7715 "BU", "IL", "DI", "D", VBOX_VERSION_STRING, RTBldCfgRevision(), "BU", "IL", "DI", "D");
7716
7717 ComObjPtr<Console> pConsole = task->mConsole;
7718
7719 /* Note: no need to use addCaller() because VMPowerUpTask does that */
7720
7721 /* The lock is also used as a signal from the task initiator (which
7722 * releases it only after RTThreadCreate()) that we can start the job */
7723 AutoWriteLock alock(pConsole COMMA_LOCKVAL_SRC_POS);
7724
7725 /* sanity */
7726 Assert(pConsole->mpVM == NULL);
7727
7728 try
7729 {
7730 // Create the VMM device object, which starts the HGCM thread; do this only
7731 // once for the console, for the pathological case that the same console
7732 // object is used to power up a VM twice. VirtualBox 4.0: we now do that
7733 // here instead of the Console constructor (see Console::init())
7734 if (!pConsole->m_pVMMDev)
7735 {
7736 pConsole->m_pVMMDev = new VMMDev(pConsole);
7737 AssertReturn(pConsole->m_pVMMDev, E_FAIL);
7738 }
7739
7740 /* wait for auto reset ops to complete so that we can successfully lock
7741 * the attached hard disks by calling LockMedia() below */
7742 for (VMPowerUpTask::ProgressList::const_iterator
7743 it = task->hardDiskProgresses.begin();
7744 it != task->hardDiskProgresses.end(); ++ it)
7745 {
7746 HRESULT rc2 = (*it)->WaitForCompletion(-1);
7747 AssertComRC(rc2);
7748 }
7749
7750 /*
7751 * Lock attached media. This method will also check their accessibility.
7752 * If we're a teleporter, we'll have to postpone this action so we can
7753 * migrate between local processes.
7754 *
7755 * Note! The media will be unlocked automatically by
7756 * SessionMachine::setMachineState() when the VM is powered down.
7757 */
7758 if ( !task->mTeleporterEnabled
7759 && task->mEnmFaultToleranceState != FaultToleranceState_Standby)
7760 {
7761 rc = pConsole->mControl->LockMedia();
7762 if (FAILED(rc)) throw rc;
7763 }
7764
7765 /* Create the VRDP server. In case of headless operation, this will
7766 * also create the framebuffer, required at VM creation.
7767 */
7768 ConsoleVRDPServer *server = pConsole->consoleVRDPServer();
7769 Assert(server);
7770
7771 /* Does VRDP server call Console from the other thread?
7772 * Not sure (and can change), so leave the lock just in case.
7773 */
7774 alock.leave();
7775 vrc = server->Launch();
7776 alock.enter();
7777
7778 if (vrc == VERR_NET_ADDRESS_IN_USE)
7779 {
7780 Utf8Str errMsg;
7781 Bstr bstr;
7782 pConsole->mVRDEServer->GetVRDEProperty(Bstr("TCP/Ports").raw(), bstr.asOutParam());
7783 Utf8Str ports = bstr;
7784 errMsg = Utf8StrFmt(tr("VirtualBox Remote Desktop Extension server can't bind to the port: %s"),
7785 ports.c_str());
7786 LogRel(("VRDE: Warning: failed to launch VRDE server (%Rrc): '%s'\n",
7787 vrc, errMsg.c_str()));
7788 }
7789 else if (vrc == VINF_NOT_SUPPORTED)
7790 {
7791 /* This means that the VRDE is not installed. */
7792 LogRel(("VRDE: VirtualBox Remote Desktop Extension is not available.\n"));
7793 }
7794 else if (RT_FAILURE(vrc))
7795 {
7796 /* Fail, if the server is installed but can't start. */
7797 Utf8Str errMsg;
7798 switch (vrc)
7799 {
7800 case VERR_FILE_NOT_FOUND:
7801 {
7802 /* VRDE library file is missing. */
7803 errMsg = Utf8StrFmt(tr("Could not find the VirtualBox Remote Desktop Extension library."));
7804 break;
7805 }
7806 default:
7807 errMsg = Utf8StrFmt(tr("Failed to launch Remote Desktop Extension server (%Rrc)"),
7808 vrc);
7809 }
7810 LogRel(("VRDE: Failed: (%Rrc), error message: '%s'\n",
7811 vrc, errMsg.c_str()));
7812 throw setErrorStatic(E_FAIL, errMsg.c_str());
7813 }
7814
7815 ComPtr<IMachine> pMachine = pConsole->machine();
7816 ULONG cCpus = 1;
7817 pMachine->COMGETTER(CPUCount)(&cCpus);
7818
7819 /*
7820 * Create the VM
7821 */
7822 PVM pVM;
7823 /*
7824 * leave the lock since EMT will call Console. It's safe because
7825 * mMachineState is either Starting or Restoring state here.
7826 */
7827 alock.leave();
7828
7829 vrc = VMR3Create(cCpus,
7830 pConsole->mpVmm2UserMethods,
7831 Console::genericVMSetErrorCallback,
7832 &task->mErrorMsg,
7833 task->mConfigConstructor,
7834 static_cast<Console *>(pConsole),
7835 &pVM);
7836
7837 alock.enter();
7838
7839 /* Enable client connections to the server. */
7840 pConsole->consoleVRDPServer()->EnableConnections();
7841
7842 if (RT_SUCCESS(vrc))
7843 {
7844 do
7845 {
7846 /*
7847 * Register our load/save state file handlers
7848 */
7849 vrc = SSMR3RegisterExternal(pVM, sSSMConsoleUnit, 0 /*iInstance*/, sSSMConsoleVer, 0 /* cbGuess */,
7850 NULL, NULL, NULL,
7851 NULL, saveStateFileExec, NULL,
7852 NULL, loadStateFileExec, NULL,
7853 static_cast<Console *>(pConsole));
7854 AssertRCBreak(vrc);
7855
7856 vrc = static_cast<Console *>(pConsole)->getDisplay()->registerSSM(pVM);
7857 AssertRC(vrc);
7858 if (RT_FAILURE(vrc))
7859 break;
7860
7861 /*
7862 * Synchronize debugger settings
7863 */
7864 MachineDebugger *machineDebugger = pConsole->getMachineDebugger();
7865 if (machineDebugger)
7866 machineDebugger->flushQueuedSettings();
7867
7868 /*
7869 * Shared Folders
7870 */
7871 if (pConsole->m_pVMMDev->isShFlActive())
7872 {
7873 /* Does the code below call Console from the other thread?
7874 * Not sure, so leave the lock just in case. */
7875 alock.leave();
7876
7877 for (SharedFolderDataMap::const_iterator it = task->mSharedFolders.begin();
7878 it != task->mSharedFolders.end();
7879 ++it)
7880 {
7881 const SharedFolderData &d = it->second;
7882 rc = pConsole->createSharedFolder(it->first, d);
7883 if (FAILED(rc))
7884 {
7885 ErrorInfoKeeper eik;
7886 setVMRuntimeErrorCallbackF(pVM, pConsole, 0, "BrokenSharedFolder",
7887 N_("The shared folder '%s' could not be set up: %ls.\n"
7888 "The shared folder setup will not be complete. It is recommended to power down the virtual machine and "
7889 "fix the shared folder settings while the machine is not running."),
7890 it->first.c_str(), eik.getText().raw());
7891 break;
7892 }
7893 }
7894 if (FAILED(rc))
7895 rc = S_OK; // do not fail with broken shared folders
7896
7897 /* enter the lock again */
7898 alock.enter();
7899 }
7900
7901 /*
7902 * Capture USB devices.
7903 */
7904 rc = pConsole->captureUSBDevices(pVM);
7905 if (FAILED(rc)) break;
7906
7907 /* leave the lock before a lengthy operation */
7908 alock.leave();
7909
7910 /* Load saved state? */
7911 if (task->mSavedStateFile.length())
7912 {
7913 LogFlowFunc(("Restoring saved state from '%s'...\n",
7914 task->mSavedStateFile.c_str()));
7915
7916 vrc = VMR3LoadFromFile(pVM,
7917 task->mSavedStateFile.c_str(),
7918 Console::stateProgressCallback,
7919 static_cast<IProgress *>(task->mProgress));
7920
7921 if (RT_SUCCESS(vrc))
7922 {
7923 if (task->mStartPaused)
7924 /* done */
7925 pConsole->setMachineState(MachineState_Paused);
7926 else
7927 {
7928 /* Start/Resume the VM execution */
7929#ifdef VBOX_WITH_EXTPACK
7930 vrc = pConsole->mptrExtPackManager->callAllVmPowerOnHooks(pConsole, pVM);
7931#endif
7932 if (RT_SUCCESS(vrc))
7933 vrc = VMR3Resume(pVM);
7934 AssertLogRelRC(vrc);
7935 }
7936 }
7937
7938 /* Power off in case we failed loading or resuming the VM */
7939 if (RT_FAILURE(vrc))
7940 {
7941 int vrc2 = VMR3PowerOff(pVM); AssertLogRelRC(vrc2);
7942#ifdef VBOX_WITH_EXTPACK
7943 pConsole->mptrExtPackManager->callAllVmPowerOffHooks(pConsole, pVM);
7944#endif
7945 }
7946 }
7947 else if (task->mTeleporterEnabled)
7948 {
7949 /* -> ConsoleImplTeleporter.cpp */
7950 bool fPowerOffOnFailure;
7951 rc = pConsole->teleporterTrg(pVM, pMachine, &task->mErrorMsg, task->mStartPaused,
7952 task->mProgress, &fPowerOffOnFailure);
7953 if (FAILED(rc) && fPowerOffOnFailure)
7954 {
7955 ErrorInfoKeeper eik;
7956 int vrc2 = VMR3PowerOff(pVM); AssertLogRelRC(vrc2);
7957#ifdef VBOX_WITH_EXTPACK
7958 pConsole->mptrExtPackManager->callAllVmPowerOffHooks(pConsole, pVM);
7959#endif
7960 }
7961 }
7962 else if (task->mEnmFaultToleranceState != FaultToleranceState_Inactive)
7963 {
7964 /*
7965 * Get the config.
7966 */
7967 ULONG uPort;
7968 ULONG uInterval;
7969 Bstr bstrAddress, bstrPassword;
7970
7971 rc = pMachine->COMGETTER(FaultTolerancePort)(&uPort);
7972 if (SUCCEEDED(rc))
7973 {
7974 rc = pMachine->COMGETTER(FaultToleranceSyncInterval)(&uInterval);
7975 if (SUCCEEDED(rc))
7976 rc = pMachine->COMGETTER(FaultToleranceAddress)(bstrAddress.asOutParam());
7977 if (SUCCEEDED(rc))
7978 rc = pMachine->COMGETTER(FaultTolerancePassword)(bstrPassword.asOutParam());
7979 }
7980 if (task->mProgress->setCancelCallback(faultToleranceProgressCancelCallback, pVM))
7981 {
7982 if (SUCCEEDED(rc))
7983 {
7984 Utf8Str strAddress(bstrAddress);
7985 const char *pszAddress = strAddress.isEmpty() ? NULL : strAddress.c_str();
7986 Utf8Str strPassword(bstrPassword);
7987 const char *pszPassword = strPassword.isEmpty() ? NULL : strPassword.c_str();
7988
7989 /* Power on the FT enabled VM. */
7990#ifdef VBOX_WITH_EXTPACK
7991 vrc = pConsole->mptrExtPackManager->callAllVmPowerOnHooks(pConsole, pVM);
7992#endif
7993 if (RT_SUCCESS(vrc))
7994 vrc = FTMR3PowerOn(pVM,
7995 task->mEnmFaultToleranceState == FaultToleranceState_Master /* fMaster */,
7996 uInterval,
7997 pszAddress,
7998 uPort,
7999 pszPassword);
8000 AssertLogRelRC(vrc);
8001 }
8002 task->mProgress->setCancelCallback(NULL, NULL);
8003 }
8004 else
8005 rc = E_FAIL;
8006 }
8007 else if (task->mStartPaused)
8008 /* done */
8009 pConsole->setMachineState(MachineState_Paused);
8010 else
8011 {
8012 /* Power on the VM (i.e. start executing) */
8013#ifdef VBOX_WITH_EXTPACK
8014 vrc = pConsole->mptrExtPackManager->callAllVmPowerOnHooks(pConsole, pVM);
8015#endif
8016 if (RT_SUCCESS(vrc))
8017 vrc = VMR3PowerOn(pVM);
8018 AssertLogRelRC(vrc);
8019 }
8020
8021 /* enter the lock again */
8022 alock.enter();
8023 }
8024 while (0);
8025
8026 /* On failure, destroy the VM */
8027 if (FAILED(rc) || RT_FAILURE(vrc))
8028 {
8029 /* preserve existing error info */
8030 ErrorInfoKeeper eik;
8031
8032 /* powerDown() will call VMR3Destroy() and do all necessary
8033 * cleanup (VRDP, USB devices) */
8034 HRESULT rc2 = pConsole->powerDown();
8035 AssertComRC(rc2);
8036 }
8037 else
8038 {
8039 /*
8040 * Deregister the VMSetError callback. This is necessary as the
8041 * pfnVMAtError() function passed to VMR3Create() is supposed to
8042 * be sticky but our error callback isn't.
8043 */
8044 alock.leave();
8045 VMR3AtErrorDeregister(pVM, Console::genericVMSetErrorCallback, &task->mErrorMsg);
8046 /** @todo register another VMSetError callback? */
8047 alock.enter();
8048 }
8049 }
8050 else
8051 {
8052 /*
8053 * If VMR3Create() failed it has released the VM memory.
8054 */
8055 pConsole->mpVM = NULL;
8056 }
8057
8058 if (SUCCEEDED(rc) && RT_FAILURE(vrc))
8059 {
8060 /* If VMR3Create() or one of the other calls in this function fail,
8061 * an appropriate error message has been set in task->mErrorMsg.
8062 * However since that happens via a callback, the rc status code in
8063 * this function is not updated.
8064 */
8065 if (!task->mErrorMsg.length())
8066 {
8067 /* If the error message is not set but we've got a failure,
8068 * convert the VBox status code into a meaningful error message.
8069 * This becomes unused once all the sources of errors set the
8070 * appropriate error message themselves.
8071 */
8072 AssertMsgFailed(("Missing error message during powerup for status code %Rrc\n", vrc));
8073 task->mErrorMsg = Utf8StrFmt(tr("Failed to start VM execution (%Rrc)"),
8074 vrc);
8075 }
8076
8077 /* Set the error message as the COM error.
8078 * Progress::notifyComplete() will pick it up later. */
8079 throw setErrorStatic(E_FAIL, task->mErrorMsg.c_str());
8080 }
8081 }
8082 catch (HRESULT aRC) { rc = aRC; }
8083
8084 if ( pConsole->mMachineState == MachineState_Starting
8085 || pConsole->mMachineState == MachineState_Restoring
8086 || pConsole->mMachineState == MachineState_TeleportingIn
8087 )
8088 {
8089 /* We are still in the Starting/Restoring state. This means one of:
8090 *
8091 * 1) we failed before VMR3Create() was called;
8092 * 2) VMR3Create() failed.
8093 *
8094 * In both cases, there is no need to call powerDown(), but we still
8095 * need to go back to the PoweredOff/Saved state. Reuse
8096 * vmstateChangeCallback() for that purpose.
8097 */
8098
8099 /* preserve existing error info */
8100 ErrorInfoKeeper eik;
8101
8102 Assert(pConsole->mpVM == NULL);
8103 vmstateChangeCallback(NULL, VMSTATE_TERMINATED, VMSTATE_CREATING,
8104 pConsole);
8105 }
8106
8107 /*
8108 * Evaluate the final result. Note that the appropriate mMachineState value
8109 * is already set by vmstateChangeCallback() in all cases.
8110 */
8111
8112 /* leave the lock, don't need it any more */
8113 alock.leave();
8114
8115 if (SUCCEEDED(rc))
8116 {
8117 /* Notify the progress object of the success */
8118 task->mProgress->notifyComplete(S_OK);
8119 }
8120 else
8121 {
8122 /* The progress object will fetch the current error info */
8123 task->mProgress->notifyComplete(rc);
8124 LogRel(("Power up failed (vrc=%Rrc, rc=%Rhrc (%#08X))\n", vrc, rc, rc));
8125 }
8126
8127 /* Notify VBoxSVC and any waiting openRemoteSession progress object. */
8128 pConsole->mControl->EndPowerUp(rc);
8129
8130#if defined(RT_OS_WINDOWS)
8131 /* uninitialize COM */
8132 CoUninitialize();
8133#endif
8134
8135 LogFlowFuncLeave();
8136
8137 return VINF_SUCCESS;
8138}
8139
8140
8141/**
8142 * Reconfigures a medium attachment (part of taking or deleting an online snapshot).
8143 *
8144 * @param pConsole Reference to the console object.
8145 * @param pVM The VM handle.
8146 * @param lInstance The instance of the controller.
8147 * @param pcszDevice The name of the controller type.
8148 * @param enmBus The storage bus type of the controller.
8149 * @param fSetupMerge Whether to set up a medium merge
8150 * @param uMergeSource Merge source image index
8151 * @param uMergeTarget Merge target image index
8152 * @param aMediumAtt The medium attachment.
8153 * @param aMachineState The current machine state.
8154 * @param phrc Where to store com error - only valid if we return VERR_GENERAL_FAILURE.
8155 * @return VBox status code.
8156 */
8157/* static */
8158DECLCALLBACK(int) Console::reconfigureMediumAttachment(Console *pConsole,
8159 PVM pVM,
8160 const char *pcszDevice,
8161 unsigned uInstance,
8162 StorageBus_T enmBus,
8163 bool fUseHostIOCache,
8164 bool fBuiltinIoCache,
8165 bool fSetupMerge,
8166 unsigned uMergeSource,
8167 unsigned uMergeTarget,
8168 IMediumAttachment *aMediumAtt,
8169 MachineState_T aMachineState,
8170 HRESULT *phrc)
8171{
8172 LogFlowFunc(("pVM=%p aMediumAtt=%p phrc=%p\n", pVM, aMediumAtt, phrc));
8173
8174 int rc;
8175 HRESULT hrc;
8176 Bstr bstr;
8177 *phrc = S_OK;
8178#define RC_CHECK() do { if (RT_FAILURE(rc)) { AssertMsgFailed(("rc=%Rrc\n", rc)); return rc; } } while (0)
8179#define H() do { if (FAILED(hrc)) { AssertMsgFailed(("hrc=%Rhrc (%#x)\n", hrc, hrc)); *phrc = hrc; return VERR_GENERAL_FAILURE; } } while (0)
8180
8181 /* Ignore attachments other than hard disks, since at the moment they are
8182 * not subject to snapshotting in general. */
8183 DeviceType_T lType;
8184 hrc = aMediumAtt->COMGETTER(Type)(&lType); H();
8185 if (lType != DeviceType_HardDisk)
8186 return VINF_SUCCESS;
8187
8188 /* Determine the base path for the device instance. */
8189 PCFGMNODE pCtlInst;
8190 pCtlInst = CFGMR3GetChildF(CFGMR3GetRoot(pVM), "Devices/%s/%u/", pcszDevice, uInstance);
8191 AssertReturn(pCtlInst, VERR_INTERNAL_ERROR);
8192
8193 /* Update the device instance configuration. */
8194 rc = pConsole->configMediumAttachment(pCtlInst,
8195 pcszDevice,
8196 uInstance,
8197 enmBus,
8198 fUseHostIOCache,
8199 fBuiltinIoCache,
8200 fSetupMerge,
8201 uMergeSource,
8202 uMergeTarget,
8203 aMediumAtt,
8204 aMachineState,
8205 phrc,
8206 true /* fAttachDetach */,
8207 false /* fForceUnmount */,
8208 pVM,
8209 NULL /* paLedDevType */);
8210 /** @todo this dumps everything attached to this device instance, which
8211 * is more than necessary. Dumping the changed LUN would be enough. */
8212 CFGMR3Dump(pCtlInst);
8213 RC_CHECK();
8214
8215#undef RC_CHECK
8216#undef H
8217
8218 LogFlowFunc(("Returns success\n"));
8219 return VINF_SUCCESS;
8220}
8221
8222/**
8223 * Progress cancelation callback employed by Console::fntTakeSnapshotWorker.
8224 */
8225static void takesnapshotProgressCancelCallback(void *pvUser)
8226{
8227 PVM pVM = (PVM)pvUser;
8228 SSMR3Cancel(pVM);
8229}
8230
8231/**
8232 * Worker thread created by Console::TakeSnapshot.
8233 * @param Thread The current thread (ignored).
8234 * @param pvUser The task.
8235 * @return VINF_SUCCESS (ignored).
8236 */
8237/*static*/
8238DECLCALLBACK(int) Console::fntTakeSnapshotWorker(RTTHREAD Thread, void *pvUser)
8239{
8240 VMTakeSnapshotTask *pTask = (VMTakeSnapshotTask*)pvUser;
8241
8242 // taking a snapshot consists of the following:
8243
8244 // 1) creating a diff image for each virtual hard disk, into which write operations go after
8245 // the snapshot has been created (done in VBoxSVC, in SessionMachine::BeginTakingSnapshot)
8246 // 2) creating a Snapshot object with the state of the machine (hardware + storage,
8247 // done in VBoxSVC, also in SessionMachine::BeginTakingSnapshot)
8248 // 3) saving the state of the virtual machine (here, in the VM process, if the machine is online)
8249
8250 Console *that = pTask->mConsole;
8251 bool fBeganTakingSnapshot = false;
8252 bool fSuspenededBySave = false;
8253
8254 AutoCaller autoCaller(that);
8255 if (FAILED(autoCaller.rc()))
8256 {
8257 that->mptrCancelableProgress.setNull();
8258 return autoCaller.rc();
8259 }
8260
8261 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
8262
8263 HRESULT rc = S_OK;
8264
8265 try
8266 {
8267 /* STEP 1 + 2:
8268 * request creating the diff images on the server and create the snapshot object
8269 * (this will set the machine state to Saving on the server to block
8270 * others from accessing this machine)
8271 */
8272 rc = that->mControl->BeginTakingSnapshot(that,
8273 pTask->bstrName.raw(),
8274 pTask->bstrDescription.raw(),
8275 pTask->mProgress,
8276 pTask->fTakingSnapshotOnline,
8277 pTask->bstrSavedStateFile.asOutParam());
8278 if (FAILED(rc))
8279 throw rc;
8280
8281 fBeganTakingSnapshot = true;
8282
8283 /*
8284 * state file is non-null only when the VM is paused
8285 * (i.e. creating a snapshot online)
8286 */
8287 bool f = (!pTask->bstrSavedStateFile.isEmpty() && pTask->fTakingSnapshotOnline)
8288 || ( pTask->bstrSavedStateFile.isEmpty() && !pTask->fTakingSnapshotOnline);
8289 if (!f)
8290 throw setErrorStatic(E_FAIL, "Invalid state of saved state file");
8291
8292 /* sync the state with the server */
8293 if (pTask->lastMachineState == MachineState_Running)
8294 that->setMachineStateLocally(MachineState_LiveSnapshotting);
8295 else
8296 that->setMachineStateLocally(MachineState_Saving);
8297
8298 // STEP 3: save the VM state (if online)
8299 if (pTask->fTakingSnapshotOnline)
8300 {
8301 Utf8Str strSavedStateFile(pTask->bstrSavedStateFile);
8302
8303 pTask->mProgress->SetNextOperation(Bstr(tr("Saving the machine state")).raw(),
8304 pTask->ulMemSize); // operation weight, same as computed when setting up progress object
8305 pTask->mProgress->setCancelCallback(takesnapshotProgressCancelCallback, that->mpVM);
8306
8307 alock.leave();
8308 LogFlowFunc(("VMR3Save...\n"));
8309 int vrc = VMR3Save(that->mpVM,
8310 strSavedStateFile.c_str(),
8311 true /*fContinueAfterwards*/,
8312 Console::stateProgressCallback,
8313 static_cast<IProgress *>(pTask->mProgress),
8314 &fSuspenededBySave);
8315 alock.enter();
8316 if (RT_FAILURE(vrc))
8317 throw setErrorStatic(E_FAIL,
8318 tr("Failed to save the machine state to '%s' (%Rrc)"),
8319 strSavedStateFile.c_str(), vrc);
8320
8321 pTask->mProgress->setCancelCallback(NULL, NULL);
8322 if (!pTask->mProgress->notifyPointOfNoReturn())
8323 throw setErrorStatic(E_FAIL, tr("Canceled"));
8324 that->mptrCancelableProgress.setNull();
8325
8326 // STEP 4: reattach hard disks
8327 LogFlowFunc(("Reattaching new differencing hard disks...\n"));
8328
8329 pTask->mProgress->SetNextOperation(Bstr(tr("Reconfiguring medium attachments")).raw(),
8330 1); // operation weight, same as computed when setting up progress object
8331
8332 com::SafeIfaceArray<IMediumAttachment> atts;
8333 rc = that->mMachine->COMGETTER(MediumAttachments)(ComSafeArrayAsOutParam(atts));
8334 if (FAILED(rc))
8335 throw rc;
8336
8337 for (size_t i = 0;
8338 i < atts.size();
8339 ++i)
8340 {
8341 ComPtr<IStorageController> pStorageController;
8342 Bstr controllerName;
8343 ULONG lInstance;
8344 StorageControllerType_T enmController;
8345 StorageBus_T enmBus;
8346 BOOL fUseHostIOCache;
8347
8348 /*
8349 * We can't pass a storage controller object directly
8350 * (g++ complains about not being able to pass non POD types through '...')
8351 * so we have to query needed values here and pass them.
8352 */
8353 rc = atts[i]->COMGETTER(Controller)(controllerName.asOutParam());
8354 if (FAILED(rc))
8355 throw rc;
8356
8357 rc = that->mMachine->GetStorageControllerByName(controllerName.raw(),
8358 pStorageController.asOutParam());
8359 if (FAILED(rc))
8360 throw rc;
8361
8362 rc = pStorageController->COMGETTER(ControllerType)(&enmController);
8363 if (FAILED(rc))
8364 throw rc;
8365 rc = pStorageController->COMGETTER(Instance)(&lInstance);
8366 if (FAILED(rc))
8367 throw rc;
8368 rc = pStorageController->COMGETTER(Bus)(&enmBus);
8369 if (FAILED(rc))
8370 throw rc;
8371 rc = pStorageController->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
8372 if (FAILED(rc))
8373 throw rc;
8374
8375 const char *pcszDevice = Console::convertControllerTypeToDev(enmController);
8376
8377 BOOL fBuiltinIoCache;
8378 rc = that->mMachine->COMGETTER(IoCacheEnabled)(&fBuiltinIoCache);
8379 if (FAILED(rc))
8380 throw rc;
8381
8382 /*
8383 * don't leave the lock since reconfigureMediumAttachment
8384 * isn't going to need the Console lock.
8385 */
8386 vrc = VMR3ReqCallWait(that->mpVM,
8387 VMCPUID_ANY,
8388 (PFNRT)reconfigureMediumAttachment,
8389 13,
8390 that,
8391 that->mpVM,
8392 pcszDevice,
8393 lInstance,
8394 enmBus,
8395 fUseHostIOCache,
8396 fBuiltinIoCache,
8397 false /* fSetupMerge */,
8398 0 /* uMergeSource */,
8399 0 /* uMergeTarget */,
8400 atts[i],
8401 that->mMachineState,
8402 &rc);
8403 if (RT_FAILURE(vrc))
8404 throw setErrorStatic(E_FAIL, Console::tr("%Rrc"), vrc);
8405 if (FAILED(rc))
8406 throw rc;
8407 }
8408 }
8409
8410 /*
8411 * finalize the requested snapshot object.
8412 * This will reset the machine state to the state it had right
8413 * before calling mControl->BeginTakingSnapshot().
8414 */
8415 rc = that->mControl->EndTakingSnapshot(TRUE /*aSuccess*/);
8416 // do not throw rc here because we can't call EndTakingSnapshot() twice
8417 LogFlowFunc(("EndTakingSnapshot -> %Rhrc [mMachineState=%s]\n", rc, Global::stringifyMachineState(that->mMachineState)));
8418 }
8419 catch (HRESULT rcThrown)
8420 {
8421 /* preserve existing error info */
8422 ErrorInfoKeeper eik;
8423
8424 if (fBeganTakingSnapshot)
8425 that->mControl->EndTakingSnapshot(FALSE /*aSuccess*/);
8426
8427 rc = rcThrown;
8428 LogFunc(("Caught %Rhrc [mMachineState=%s]\n", rc, Global::stringifyMachineState(that->mMachineState)));
8429 }
8430 Assert(alock.isWriteLockOnCurrentThread());
8431
8432 if (FAILED(rc)) /* Must come before calling setMachineState. */
8433 pTask->mProgress->notifyComplete(rc);
8434
8435 /*
8436 * Fix up the machine state.
8437 *
8438 * For live snapshots we do all the work, for the two other variations we
8439 * just update the local copy.
8440 */
8441 MachineState_T enmMachineState;
8442 that->mMachine->COMGETTER(State)(&enmMachineState);
8443 if ( that->mMachineState == MachineState_LiveSnapshotting
8444 || that->mMachineState == MachineState_Saving)
8445 {
8446
8447 if (!pTask->fTakingSnapshotOnline)
8448 that->setMachineStateLocally(pTask->lastMachineState);
8449 else if (SUCCEEDED(rc))
8450 {
8451 Assert( pTask->lastMachineState == MachineState_Running
8452 || pTask->lastMachineState == MachineState_Paused);
8453 Assert(that->mMachineState == MachineState_Saving);
8454 if (pTask->lastMachineState == MachineState_Running)
8455 {
8456 LogFlowFunc(("VMR3Resume...\n"));
8457 alock.leave();
8458 int vrc = VMR3Resume(that->mpVM);
8459 alock.enter();
8460 if (RT_FAILURE(vrc))
8461 {
8462 rc = setErrorStatic(VBOX_E_VM_ERROR, tr("Could not resume the machine execution (%Rrc)"), vrc);
8463 pTask->mProgress->notifyComplete(rc);
8464 if (that->mMachineState == MachineState_Saving)
8465 that->setMachineStateLocally(MachineState_Paused);
8466 }
8467 }
8468 else
8469 that->setMachineStateLocally(MachineState_Paused);
8470 }
8471 else
8472 {
8473 /** @todo this could probably be made more generic and reused elsewhere. */
8474 /* paranoid cleanup on for a failed online snapshot. */
8475 VMSTATE enmVMState = VMR3GetState(that->mpVM);
8476 switch (enmVMState)
8477 {
8478 case VMSTATE_RUNNING:
8479 case VMSTATE_RUNNING_LS:
8480 case VMSTATE_DEBUGGING:
8481 case VMSTATE_DEBUGGING_LS:
8482 case VMSTATE_POWERING_OFF:
8483 case VMSTATE_POWERING_OFF_LS:
8484 case VMSTATE_RESETTING:
8485 case VMSTATE_RESETTING_LS:
8486 Assert(!fSuspenededBySave);
8487 that->setMachineState(MachineState_Running);
8488 break;
8489
8490 case VMSTATE_GURU_MEDITATION:
8491 case VMSTATE_GURU_MEDITATION_LS:
8492 that->setMachineState(MachineState_Stuck);
8493 break;
8494
8495 case VMSTATE_FATAL_ERROR:
8496 case VMSTATE_FATAL_ERROR_LS:
8497 if (pTask->lastMachineState == MachineState_Paused)
8498 that->setMachineStateLocally(pTask->lastMachineState);
8499 else
8500 that->setMachineState(MachineState_Paused);
8501 break;
8502
8503 default:
8504 AssertMsgFailed(("%s\n", VMR3GetStateName(enmVMState)));
8505 case VMSTATE_SUSPENDED:
8506 case VMSTATE_SUSPENDED_LS:
8507 case VMSTATE_SUSPENDING:
8508 case VMSTATE_SUSPENDING_LS:
8509 case VMSTATE_SUSPENDING_EXT_LS:
8510 if (fSuspenededBySave)
8511 {
8512 Assert(pTask->lastMachineState == MachineState_Running);
8513 LogFlowFunc(("VMR3Resume (on failure)...\n"));
8514 alock.leave();
8515 int vrc = VMR3Resume(that->mpVM); AssertLogRelRC(vrc);
8516 alock.enter();
8517 if (RT_FAILURE(vrc))
8518 that->setMachineState(MachineState_Paused);
8519 }
8520 else if (pTask->lastMachineState == MachineState_Paused)
8521 that->setMachineStateLocally(pTask->lastMachineState);
8522 else
8523 that->setMachineState(MachineState_Paused);
8524 break;
8525 }
8526
8527 }
8528 }
8529 /*else: somebody else has change the state... Leave it. */
8530
8531 /* check the remote state to see that we got it right. */
8532 that->mMachine->COMGETTER(State)(&enmMachineState);
8533 AssertLogRelMsg(that->mMachineState == enmMachineState,
8534 ("mMachineState=%s enmMachineState=%s\n", Global::stringifyMachineState(that->mMachineState),
8535 Global::stringifyMachineState(enmMachineState) ));
8536
8537
8538 if (SUCCEEDED(rc)) /* The failure cases are handled above. */
8539 pTask->mProgress->notifyComplete(rc);
8540
8541 delete pTask;
8542
8543 LogFlowFuncLeave();
8544 return VINF_SUCCESS;
8545}
8546
8547/**
8548 * Thread for executing the saved state operation.
8549 *
8550 * @param Thread The thread handle.
8551 * @param pvUser Pointer to a VMSaveTask structure.
8552 * @return VINF_SUCCESS (ignored).
8553 *
8554 * @note Locks the Console object for writing.
8555 */
8556/*static*/
8557DECLCALLBACK(int) Console::saveStateThread(RTTHREAD Thread, void *pvUser)
8558{
8559 LogFlowFuncEnter();
8560
8561 std::auto_ptr<VMSaveTask> task(static_cast<VMSaveTask*>(pvUser));
8562 AssertReturn(task.get(), VERR_INVALID_PARAMETER);
8563
8564 Assert(task->mSavedStateFile.length());
8565 Assert(task->mProgress.isNull());
8566 Assert(!task->mServerProgress.isNull());
8567
8568 const ComObjPtr<Console> &that = task->mConsole;
8569 Utf8Str errMsg;
8570 HRESULT rc = S_OK;
8571
8572 LogFlowFunc(("Saving the state to '%s'...\n", task->mSavedStateFile.c_str()));
8573
8574 bool fSuspenededBySave;
8575 int vrc = VMR3Save(that->mpVM,
8576 task->mSavedStateFile.c_str(),
8577 false, /*fContinueAfterwards*/
8578 Console::stateProgressCallback,
8579 static_cast<IProgress *>(task->mServerProgress),
8580 &fSuspenededBySave);
8581 if (RT_FAILURE(vrc))
8582 {
8583 errMsg = Utf8StrFmt(Console::tr("Failed to save the machine state to '%s' (%Rrc)"),
8584 task->mSavedStateFile.c_str(), vrc);
8585 rc = E_FAIL;
8586 }
8587 Assert(!fSuspenededBySave);
8588
8589 /* lock the console once we're going to access it */
8590 AutoWriteLock thatLock(that COMMA_LOCKVAL_SRC_POS);
8591
8592 /* synchronize the state with the server */
8593 if (SUCCEEDED(rc))
8594 {
8595 /*
8596 * The machine has been successfully saved, so power it down
8597 * (vmstateChangeCallback() will set state to Saved on success).
8598 * Note: we release the task's VM caller, otherwise it will
8599 * deadlock.
8600 */
8601 task->releaseVMCaller();
8602 rc = that->powerDown();
8603 }
8604
8605 /*
8606 * Finalize the requested save state procedure. In case of failure it will
8607 * reset the machine state to the state it had right before calling
8608 * mControl->BeginSavingState(). This must be the last thing because it
8609 * will set the progress to completed, and that means that the frontend
8610 * can immediately uninit the associated console object.
8611 */
8612 that->mControl->EndSavingState(rc, Bstr(errMsg).raw());
8613
8614 LogFlowFuncLeave();
8615 return VINF_SUCCESS;
8616}
8617
8618/**
8619 * Thread for powering down the Console.
8620 *
8621 * @param Thread The thread handle.
8622 * @param pvUser Pointer to the VMTask structure.
8623 * @return VINF_SUCCESS (ignored).
8624 *
8625 * @note Locks the Console object for writing.
8626 */
8627/*static*/
8628DECLCALLBACK(int) Console::powerDownThread(RTTHREAD Thread, void *pvUser)
8629{
8630 LogFlowFuncEnter();
8631
8632 std::auto_ptr<VMPowerDownTask> task(static_cast<VMPowerDownTask *>(pvUser));
8633 AssertReturn(task.get(), VERR_INVALID_PARAMETER);
8634
8635 AssertReturn(task->isOk(), VERR_GENERAL_FAILURE);
8636
8637 Assert(task->mProgress.isNull());
8638
8639 const ComObjPtr<Console> &that = task->mConsole;
8640
8641 /* Note: no need to use addCaller() to protect Console because VMTask does
8642 * that */
8643
8644 /* wait until the method tat started us returns */
8645 AutoWriteLock thatLock(that COMMA_LOCKVAL_SRC_POS);
8646
8647 /* release VM caller to avoid the powerDown() deadlock */
8648 task->releaseVMCaller();
8649
8650 that->powerDown(task->mServerProgress);
8651
8652 /* complete the operation */
8653 that->mControl->EndPoweringDown(S_OK, Bstr().raw());
8654
8655 LogFlowFuncLeave();
8656 return VINF_SUCCESS;
8657}
8658
8659
8660/**
8661 * @interface_method_impl{VMM2USERMETHODS,pfnSaveState}
8662 */
8663/*static*/
8664DECLCALLBACK(int) Console::vmm2User_SaveState(PCVMM2USERMETHODS pThis, PVM pVM)
8665{
8666 Console *pConsole = *(Console **)(pThis + 1); /* lazy bird */
8667
8668 /*
8669 * For now, just call SaveState. We should probably try notify the GUI so
8670 * it can pop up a progress object and stuff.
8671 */
8672 HRESULT hrc = pConsole->SaveState(NULL);
8673 return SUCCEEDED(hrc) ? VINF_SUCCESS : Global::vboxStatusCodeFromCOM(hrc);
8674}
8675
8676
8677
8678/**
8679 * The Main status driver instance data.
8680 */
8681typedef struct DRVMAINSTATUS
8682{
8683 /** The LED connectors. */
8684 PDMILEDCONNECTORS ILedConnectors;
8685 /** Pointer to the LED ports interface above us. */
8686 PPDMILEDPORTS pLedPorts;
8687 /** Pointer to the array of LED pointers. */
8688 PPDMLED *papLeds;
8689 /** The unit number corresponding to the first entry in the LED array. */
8690 RTUINT iFirstLUN;
8691 /** The unit number corresponding to the last entry in the LED array.
8692 * (The size of the LED array is iLastLUN - iFirstLUN + 1.) */
8693 RTUINT iLastLUN;
8694} DRVMAINSTATUS, *PDRVMAINSTATUS;
8695
8696
8697/**
8698 * Notification about a unit which have been changed.
8699 *
8700 * The driver must discard any pointers to data owned by
8701 * the unit and requery it.
8702 *
8703 * @param pInterface Pointer to the interface structure containing the called function pointer.
8704 * @param iLUN The unit number.
8705 */
8706DECLCALLBACK(void) Console::drvStatus_UnitChanged(PPDMILEDCONNECTORS pInterface, unsigned iLUN)
8707{
8708 PDRVMAINSTATUS pData = (PDRVMAINSTATUS)(void *)pInterface;
8709 if (iLUN >= pData->iFirstLUN && iLUN <= pData->iLastLUN)
8710 {
8711 PPDMLED pLed;
8712 int rc = pData->pLedPorts->pfnQueryStatusLed(pData->pLedPorts, iLUN, &pLed);
8713 if (RT_FAILURE(rc))
8714 pLed = NULL;
8715 ASMAtomicWritePtr(&pData->papLeds[iLUN - pData->iFirstLUN], pLed);
8716 Log(("drvStatus_UnitChanged: iLUN=%d pLed=%p\n", iLUN, pLed));
8717 }
8718}
8719
8720
8721/**
8722 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
8723 */
8724DECLCALLBACK(void *) Console::drvStatus_QueryInterface(PPDMIBASE pInterface, const char *pszIID)
8725{
8726 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
8727 PDRVMAINSTATUS pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINSTATUS);
8728 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
8729 PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDCONNECTORS, &pThis->ILedConnectors);
8730 return NULL;
8731}
8732
8733
8734/**
8735 * Destruct a status driver instance.
8736 *
8737 * @returns VBox status.
8738 * @param pDrvIns The driver instance data.
8739 */
8740DECLCALLBACK(void) Console::drvStatus_Destruct(PPDMDRVINS pDrvIns)
8741{
8742 PDRVMAINSTATUS pData = PDMINS_2_DATA(pDrvIns, PDRVMAINSTATUS);
8743 LogFlowFunc(("iInstance=%d\n", pDrvIns->iInstance));
8744 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
8745
8746 if (pData->papLeds)
8747 {
8748 unsigned iLed = pData->iLastLUN - pData->iFirstLUN + 1;
8749 while (iLed-- > 0)
8750 ASMAtomicWriteNullPtr(&pData->papLeds[iLed]);
8751 }
8752}
8753
8754
8755/**
8756 * Construct a status driver instance.
8757 *
8758 * @copydoc FNPDMDRVCONSTRUCT
8759 */
8760DECLCALLBACK(int) Console::drvStatus_Construct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
8761{
8762 PDRVMAINSTATUS pData = PDMINS_2_DATA(pDrvIns, PDRVMAINSTATUS);
8763 LogFlowFunc(("iInstance=%d\n", pDrvIns->iInstance));
8764 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
8765
8766 /*
8767 * Validate configuration.
8768 */
8769 if (!CFGMR3AreValuesValid(pCfg, "papLeds\0First\0Last\0"))
8770 return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
8771 AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
8772 ("Configuration error: Not possible to attach anything to this driver!\n"),
8773 VERR_PDM_DRVINS_NO_ATTACH);
8774
8775 /*
8776 * Data.
8777 */
8778 pDrvIns->IBase.pfnQueryInterface = Console::drvStatus_QueryInterface;
8779 pData->ILedConnectors.pfnUnitChanged = Console::drvStatus_UnitChanged;
8780
8781 /*
8782 * Read config.
8783 */
8784 int rc = CFGMR3QueryPtr(pCfg, "papLeds", (void **)&pData->papLeds);
8785 if (RT_FAILURE(rc))
8786 {
8787 AssertMsgFailed(("Configuration error: Failed to query the \"papLeds\" value! rc=%Rrc\n", rc));
8788 return rc;
8789 }
8790
8791 rc = CFGMR3QueryU32(pCfg, "First", &pData->iFirstLUN);
8792 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
8793 pData->iFirstLUN = 0;
8794 else if (RT_FAILURE(rc))
8795 {
8796 AssertMsgFailed(("Configuration error: Failed to query the \"First\" value! rc=%Rrc\n", rc));
8797 return rc;
8798 }
8799
8800 rc = CFGMR3QueryU32(pCfg, "Last", &pData->iLastLUN);
8801 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
8802 pData->iLastLUN = 0;
8803 else if (RT_FAILURE(rc))
8804 {
8805 AssertMsgFailed(("Configuration error: Failed to query the \"Last\" value! rc=%Rrc\n", rc));
8806 return rc;
8807 }
8808 if (pData->iFirstLUN > pData->iLastLUN)
8809 {
8810 AssertMsgFailed(("Configuration error: Invalid unit range %u-%u\n", pData->iFirstLUN, pData->iLastLUN));
8811 return VERR_GENERAL_FAILURE;
8812 }
8813
8814 /*
8815 * Get the ILedPorts interface of the above driver/device and
8816 * query the LEDs we want.
8817 */
8818 pData->pLedPorts = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMILEDPORTS);
8819 AssertMsgReturn(pData->pLedPorts, ("Configuration error: No led ports interface above!\n"),
8820 VERR_PDM_MISSING_INTERFACE_ABOVE);
8821
8822 for (unsigned i = pData->iFirstLUN; i <= pData->iLastLUN; ++i)
8823 Console::drvStatus_UnitChanged(&pData->ILedConnectors, i);
8824
8825 return VINF_SUCCESS;
8826}
8827
8828
8829/**
8830 * Keyboard driver registration record.
8831 */
8832const PDMDRVREG Console::DrvStatusReg =
8833{
8834 /* u32Version */
8835 PDM_DRVREG_VERSION,
8836 /* szName */
8837 "MainStatus",
8838 /* szRCMod */
8839 "",
8840 /* szR0Mod */
8841 "",
8842 /* pszDescription */
8843 "Main status driver (Main as in the API).",
8844 /* fFlags */
8845 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
8846 /* fClass. */
8847 PDM_DRVREG_CLASS_STATUS,
8848 /* cMaxInstances */
8849 ~0,
8850 /* cbInstance */
8851 sizeof(DRVMAINSTATUS),
8852 /* pfnConstruct */
8853 Console::drvStatus_Construct,
8854 /* pfnDestruct */
8855 Console::drvStatus_Destruct,
8856 /* pfnRelocate */
8857 NULL,
8858 /* pfnIOCtl */
8859 NULL,
8860 /* pfnPowerOn */
8861 NULL,
8862 /* pfnReset */
8863 NULL,
8864 /* pfnSuspend */
8865 NULL,
8866 /* pfnResume */
8867 NULL,
8868 /* pfnAttach */
8869 NULL,
8870 /* pfnDetach */
8871 NULL,
8872 /* pfnPowerOff */
8873 NULL,
8874 /* pfnSoftReset */
8875 NULL,
8876 /* u32EndVersion */
8877 PDM_DRVREG_VERSION
8878};
8879
8880/* 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