VirtualBox

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

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

Main,ExtPacks/Puel/UsbWebcam: UsbWebcam intergration.

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

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