VirtualBox

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

Last change on this file since 37687 was 37687, checked in by vboxsync, 13 years ago

Main/Console+Machine: add notification for guest triggered eject, which right now results in updating the VM config
Devices/Storage/ATA+AHCI: trigger the eject notification

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

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