VirtualBox

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

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

Main/Console: fix poweroff regression introduced when moving the progress object to VBoxSVC

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

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