VirtualBox

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

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

Main: shared folder setup: reorder the path checks; if a shared folder not exists this should be reported first (otherwise the error is very misleading)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 323.6 KB
Line 
1/* $Id: ConsoleImpl.cpp 38495 2011-08-19 09:06:33Z 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, 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, aId, TRUE /* fDeleteAllChildren */, &machineState, aProgress);
3273 if (FAILED(rc)) return rc;
3274
3275 setMachineStateLocally(machineState);
3276 return S_OK;
3277}
3278
3279STDMETHODIMP Console::DeleteSnapshotRange(IN_BSTR aStartId, IN_BSTR aEndId, IProgress **aProgress)
3280{
3281 CheckComArgExpr(aStartId, Guid(aStartId).isEmpty() == false);
3282 CheckComArgExpr(aEndId, Guid(aEndId).isEmpty() == false);
3283 CheckComArgOutPointerValid(aProgress);
3284
3285 AutoCaller autoCaller(this);
3286 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3287
3288 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3289
3290 if (Global::IsTransient(mMachineState))
3291 return setError(VBOX_E_INVALID_VM_STATE,
3292 tr("Cannot delete a snapshot of the machine while it is changing the state (machine state: %s)"),
3293 Global::stringifyMachineState(mMachineState));
3294
3295 MachineState_T machineState = MachineState_Null;
3296 HRESULT rc = mControl->DeleteSnapshot(this, aStartId, aEndId, FALSE /* fDeleteAllChildren */, &machineState, aProgress);
3297 if (FAILED(rc)) return rc;
3298
3299 setMachineStateLocally(machineState);
3300 return S_OK;
3301}
3302
3303STDMETHODIMP Console::RestoreSnapshot(ISnapshot *aSnapshot, IProgress **aProgress)
3304{
3305 AutoCaller autoCaller(this);
3306 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3307
3308 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3309
3310 if (Global::IsOnlineOrTransient(mMachineState))
3311 return setError(VBOX_E_INVALID_VM_STATE,
3312 tr("Cannot delete the current state of the running machine (machine state: %s)"),
3313 Global::stringifyMachineState(mMachineState));
3314
3315 MachineState_T machineState = MachineState_Null;
3316 HRESULT rc = mControl->RestoreSnapshot(this, aSnapshot, &machineState, aProgress);
3317 if (FAILED(rc)) return rc;
3318
3319 setMachineStateLocally(machineState);
3320 return S_OK;
3321}
3322
3323// Non-interface public methods
3324/////////////////////////////////////////////////////////////////////////////
3325
3326/*static*/
3327HRESULT Console::setErrorStatic(HRESULT aResultCode, const char *pcsz, ...)
3328{
3329 va_list args;
3330 va_start(args, pcsz);
3331 HRESULT rc = setErrorInternal(aResultCode,
3332 getStaticClassIID(),
3333 getStaticComponentName(),
3334 Utf8Str(pcsz, args),
3335 false /* aWarning */,
3336 true /* aLogIt */);
3337 va_end(args);
3338 return rc;
3339}
3340
3341HRESULT Console::setInvalidMachineStateError()
3342{
3343 return setError(VBOX_E_INVALID_VM_STATE,
3344 tr("Invalid machine state: %s"),
3345 Global::stringifyMachineState(mMachineState));
3346}
3347
3348
3349/**
3350 * @copydoc VirtualBox::handleUnexpectedExceptions
3351 */
3352/* static */
3353HRESULT Console::handleUnexpectedExceptions(RT_SRC_POS_DECL)
3354{
3355 try
3356 {
3357 /* re-throw the current exception */
3358 throw;
3359 }
3360 catch (const std::exception &err)
3361 {
3362 return setErrorStatic(E_FAIL,
3363 tr("Unexpected exception: %s [%s]\n%s[%d] (%s)"),
3364 err.what(), typeid(err).name(),
3365 pszFile, iLine, pszFunction);
3366 }
3367 catch (...)
3368 {
3369 return setErrorStatic(E_FAIL,
3370 tr("Unknown exception\n%s[%d] (%s)"),
3371 pszFile, iLine, pszFunction);
3372 }
3373
3374 /* should not get here */
3375 AssertFailed();
3376 return E_FAIL;
3377}
3378
3379/* static */
3380const char *Console::convertControllerTypeToDev(StorageControllerType_T enmCtrlType)
3381{
3382 switch (enmCtrlType)
3383 {
3384 case StorageControllerType_LsiLogic:
3385 return "lsilogicscsi";
3386 case StorageControllerType_BusLogic:
3387 return "buslogic";
3388 case StorageControllerType_LsiLogicSas:
3389 return "lsilogicsas";
3390 case StorageControllerType_IntelAhci:
3391 return "ahci";
3392 case StorageControllerType_PIIX3:
3393 case StorageControllerType_PIIX4:
3394 case StorageControllerType_ICH6:
3395 return "piix3ide";
3396 case StorageControllerType_I82078:
3397 return "i82078";
3398 default:
3399 return NULL;
3400 }
3401}
3402
3403HRESULT Console::convertBusPortDeviceToLun(StorageBus_T enmBus, LONG port, LONG device, unsigned &uLun)
3404{
3405 switch (enmBus)
3406 {
3407 case StorageBus_IDE:
3408 case StorageBus_Floppy:
3409 {
3410 AssertMsgReturn(port < 2 && port >= 0, ("%d\n", port), E_INVALIDARG);
3411 AssertMsgReturn(device < 2 && device >= 0, ("%d\n", device), E_INVALIDARG);
3412 uLun = 2 * port + device;
3413 return S_OK;
3414 }
3415 case StorageBus_SATA:
3416 case StorageBus_SCSI:
3417 case StorageBus_SAS:
3418 {
3419 uLun = port;
3420 return S_OK;
3421 }
3422 default:
3423 uLun = 0;
3424 AssertMsgFailedReturn(("%d\n", enmBus), E_INVALIDARG);
3425 }
3426}
3427
3428// private methods
3429/////////////////////////////////////////////////////////////////////////////
3430
3431/**
3432 * Process a medium change.
3433 *
3434 * @param aMediumAttachment The medium attachment with the new medium state.
3435 * @param fForce Force medium chance, if it is locked or not.
3436 * @param pVM Safe VM handle.
3437 *
3438 * @note Locks this object for writing.
3439 */
3440HRESULT Console::doMediumChange(IMediumAttachment *aMediumAttachment, bool fForce, PVM pVM)
3441{
3442 AutoCaller autoCaller(this);
3443 AssertComRCReturnRC(autoCaller.rc());
3444
3445 /* We will need to release the write lock before calling EMT */
3446 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3447
3448 HRESULT rc = S_OK;
3449 const char *pszDevice = NULL;
3450
3451 SafeIfaceArray<IStorageController> ctrls;
3452 rc = mMachine->COMGETTER(StorageControllers)(ComSafeArrayAsOutParam(ctrls));
3453 AssertComRC(rc);
3454 IMedium *pMedium;
3455 rc = aMediumAttachment->COMGETTER(Medium)(&pMedium);
3456 AssertComRC(rc);
3457 Bstr mediumLocation;
3458 if (pMedium)
3459 {
3460 rc = pMedium->COMGETTER(Location)(mediumLocation.asOutParam());
3461 AssertComRC(rc);
3462 }
3463
3464 Bstr attCtrlName;
3465 rc = aMediumAttachment->COMGETTER(Controller)(attCtrlName.asOutParam());
3466 AssertComRC(rc);
3467 ComPtr<IStorageController> pStorageController;
3468 for (size_t i = 0; i < ctrls.size(); ++i)
3469 {
3470 Bstr ctrlName;
3471 rc = ctrls[i]->COMGETTER(Name)(ctrlName.asOutParam());
3472 AssertComRC(rc);
3473 if (attCtrlName == ctrlName)
3474 {
3475 pStorageController = ctrls[i];
3476 break;
3477 }
3478 }
3479 if (pStorageController.isNull())
3480 return setError(E_FAIL,
3481 tr("Could not find storage controller '%ls'"), attCtrlName.raw());
3482
3483 StorageControllerType_T enmCtrlType;
3484 rc = pStorageController->COMGETTER(ControllerType)(&enmCtrlType);
3485 AssertComRC(rc);
3486 pszDevice = convertControllerTypeToDev(enmCtrlType);
3487
3488 StorageBus_T enmBus;
3489 rc = pStorageController->COMGETTER(Bus)(&enmBus);
3490 AssertComRC(rc);
3491 ULONG uInstance;
3492 rc = pStorageController->COMGETTER(Instance)(&uInstance);
3493 AssertComRC(rc);
3494 BOOL fUseHostIOCache;
3495 rc = pStorageController->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
3496 AssertComRC(rc);
3497
3498 /*
3499 * Call worker in EMT, that's faster and safer than doing everything
3500 * using VMR3ReqCall. Note that we separate VMR3ReqCall from VMR3ReqWait
3501 * here to make requests from under the lock in order to serialize them.
3502 */
3503 PVMREQ pReq;
3504 int vrc = VMR3ReqCall(pVM,
3505 VMCPUID_ANY,
3506 &pReq,
3507 0 /* no wait! */,
3508 VMREQFLAGS_VBOX_STATUS,
3509 (PFNRT)Console::changeRemovableMedium,
3510 8,
3511 this,
3512 pVM,
3513 pszDevice,
3514 uInstance,
3515 enmBus,
3516 fUseHostIOCache,
3517 aMediumAttachment,
3518 fForce);
3519
3520 /* leave the lock before waiting for a result (EMT will call us back!) */
3521 alock.leave();
3522
3523 if (vrc == VERR_TIMEOUT || RT_SUCCESS(vrc))
3524 {
3525 vrc = VMR3ReqWait(pReq, RT_INDEFINITE_WAIT);
3526 AssertRC(vrc);
3527 if (RT_SUCCESS(vrc))
3528 vrc = pReq->iStatus;
3529 }
3530 VMR3ReqFree(pReq);
3531
3532 if (RT_SUCCESS(vrc))
3533 {
3534 LogFlowThisFunc(("Returns S_OK\n"));
3535 return S_OK;
3536 }
3537
3538 if (pMedium)
3539 return setError(E_FAIL,
3540 tr("Could not mount the media/drive '%ls' (%Rrc)"),
3541 mediumLocation.raw(), vrc);
3542
3543 return setError(E_FAIL,
3544 tr("Could not unmount the currently mounted media/drive (%Rrc)"),
3545 vrc);
3546}
3547
3548/**
3549 * Performs the medium change in EMT.
3550 *
3551 * @returns VBox status code.
3552 *
3553 * @param pThis Pointer to the Console object.
3554 * @param pVM The VM handle.
3555 * @param pcszDevice The PDM device name.
3556 * @param uInstance The PDM device instance.
3557 * @param uLun The PDM LUN number of the drive.
3558 * @param fHostDrive True if this is a host drive attachment.
3559 * @param pszPath The path to the media / drive which is now being mounted / captured.
3560 * If NULL no media or drive is attached and the LUN will be configured with
3561 * the default block driver with no media. This will also be the state if
3562 * mounting / capturing the specified media / drive fails.
3563 * @param pszFormat Medium format string, usually "RAW".
3564 * @param fPassthrough Enables using passthrough mode of the host DVD drive if applicable.
3565 *
3566 * @thread EMT
3567 */
3568DECLCALLBACK(int) Console::changeRemovableMedium(Console *pConsole,
3569 PVM pVM,
3570 const char *pcszDevice,
3571 unsigned uInstance,
3572 StorageBus_T enmBus,
3573 bool fUseHostIOCache,
3574 IMediumAttachment *aMediumAtt,
3575 bool fForce)
3576{
3577 LogFlowFunc(("pConsole=%p uInstance=%u pszDevice=%p:{%s} enmBus=%u, aMediumAtt=%p, fForce=%d\n",
3578 pConsole, uInstance, pcszDevice, pcszDevice, enmBus, aMediumAtt, fForce));
3579
3580 AssertReturn(pConsole, VERR_INVALID_PARAMETER);
3581
3582 AutoCaller autoCaller(pConsole);
3583 AssertComRCReturn(autoCaller.rc(), VERR_ACCESS_DENIED);
3584
3585 /*
3586 * Suspend the VM first.
3587 *
3588 * The VM must not be running since it might have pending I/O to
3589 * the drive which is being changed.
3590 */
3591 bool fResume;
3592 VMSTATE enmVMState = VMR3GetState(pVM);
3593 switch (enmVMState)
3594 {
3595 case VMSTATE_RESETTING:
3596 case VMSTATE_RUNNING:
3597 {
3598 LogFlowFunc(("Suspending the VM...\n"));
3599 /* disable the callback to prevent Console-level state change */
3600 pConsole->mVMStateChangeCallbackDisabled = true;
3601 int rc = VMR3Suspend(pVM);
3602 pConsole->mVMStateChangeCallbackDisabled = false;
3603 AssertRCReturn(rc, rc);
3604 fResume = true;
3605 break;
3606 }
3607
3608 case VMSTATE_SUSPENDED:
3609 case VMSTATE_CREATED:
3610 case VMSTATE_OFF:
3611 fResume = false;
3612 break;
3613
3614 case VMSTATE_RUNNING_LS:
3615 case VMSTATE_RUNNING_FT:
3616 return setErrorInternal(VBOX_E_INVALID_VM_STATE,
3617 COM_IIDOF(IConsole),
3618 getStaticComponentName(),
3619 (enmVMState == VMSTATE_RUNNING_LS) ? Utf8Str(tr("Cannot change drive during live migration")) : Utf8Str(tr("Cannot change drive during fault tolerant syncing")),
3620 false /*aWarning*/,
3621 true /*aLogIt*/);
3622
3623 default:
3624 AssertMsgFailedReturn(("enmVMState=%d\n", enmVMState), VERR_ACCESS_DENIED);
3625 }
3626
3627 /* Determine the base path for the device instance. */
3628 PCFGMNODE pCtlInst;
3629 pCtlInst = CFGMR3GetChildF(CFGMR3GetRoot(pVM), "Devices/%s/%u/", pcszDevice, uInstance);
3630 AssertReturn(pCtlInst, VERR_INTERNAL_ERROR);
3631
3632 int rc = VINF_SUCCESS;
3633 int rcRet = VINF_SUCCESS;
3634
3635 rcRet = pConsole->configMediumAttachment(pCtlInst,
3636 pcszDevice,
3637 uInstance,
3638 enmBus,
3639 fUseHostIOCache,
3640 false /* fSetupMerge */,
3641 false /* fBuiltinIoCache */,
3642 0 /* uMergeSource */,
3643 0 /* uMergeTarget */,
3644 aMediumAtt,
3645 pConsole->mMachineState,
3646 NULL /* phrc */,
3647 true /* fAttachDetach */,
3648 fForce /* fForceUnmount */,
3649 false /* fHotplug */,
3650 pVM,
3651 NULL /* paLedDevType */);
3652 /** @todo this dumps everything attached to this device instance, which
3653 * is more than necessary. Dumping the changed LUN would be enough. */
3654 CFGMR3Dump(pCtlInst);
3655
3656 /*
3657 * Resume the VM if necessary.
3658 */
3659 if (fResume)
3660 {
3661 LogFlowFunc(("Resuming the VM...\n"));
3662 /* disable the callback to prevent Console-level state change */
3663 pConsole->mVMStateChangeCallbackDisabled = true;
3664 rc = VMR3Resume(pVM);
3665 pConsole->mVMStateChangeCallbackDisabled = false;
3666 AssertRC(rc);
3667 if (RT_FAILURE(rc))
3668 {
3669 /* too bad, we failed. try to sync the console state with the VMM state */
3670 vmstateChangeCallback(pVM, VMSTATE_SUSPENDED, enmVMState, pConsole);
3671 }
3672 /// @todo (r=dmik) if we failed with drive mount, then the VMR3Resume
3673 // error (if any) will be hidden from the caller. For proper reporting
3674 // of such multiple errors to the caller we need to enhance the
3675 // IVirtualBoxError interface. For now, give the first error the higher
3676 // priority.
3677 if (RT_SUCCESS(rcRet))
3678 rcRet = rc;
3679 }
3680
3681 LogFlowFunc(("Returning %Rrc\n", rcRet));
3682 return rcRet;
3683}
3684
3685
3686/**
3687 * Attach a new storage device to the VM.
3688 *
3689 * @param aMediumAttachment The medium attachment which is added.
3690 * @param pVM Safe VM handle.
3691 *
3692 * @note Locks this object for writing.
3693 */
3694HRESULT Console::doStorageDeviceAttach(IMediumAttachment *aMediumAttachment, PVM pVM)
3695{
3696 AutoCaller autoCaller(this);
3697 AssertComRCReturnRC(autoCaller.rc());
3698
3699 /* We will need to release the write lock before calling EMT */
3700 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3701
3702 HRESULT rc = S_OK;
3703 const char *pszDevice = NULL;
3704
3705 SafeIfaceArray<IStorageController> ctrls;
3706 rc = mMachine->COMGETTER(StorageControllers)(ComSafeArrayAsOutParam(ctrls));
3707 AssertComRC(rc);
3708 IMedium *pMedium;
3709 rc = aMediumAttachment->COMGETTER(Medium)(&pMedium);
3710 AssertComRC(rc);
3711 Bstr mediumLocation;
3712 if (pMedium)
3713 {
3714 rc = pMedium->COMGETTER(Location)(mediumLocation.asOutParam());
3715 AssertComRC(rc);
3716 }
3717
3718 Bstr attCtrlName;
3719 rc = aMediumAttachment->COMGETTER(Controller)(attCtrlName.asOutParam());
3720 AssertComRC(rc);
3721 ComPtr<IStorageController> pStorageController;
3722 for (size_t i = 0; i < ctrls.size(); ++i)
3723 {
3724 Bstr ctrlName;
3725 rc = ctrls[i]->COMGETTER(Name)(ctrlName.asOutParam());
3726 AssertComRC(rc);
3727 if (attCtrlName == ctrlName)
3728 {
3729 pStorageController = ctrls[i];
3730 break;
3731 }
3732 }
3733 if (pStorageController.isNull())
3734 return setError(E_FAIL,
3735 tr("Could not find storage controller '%ls'"), attCtrlName.raw());
3736
3737 StorageControllerType_T enmCtrlType;
3738 rc = pStorageController->COMGETTER(ControllerType)(&enmCtrlType);
3739 AssertComRC(rc);
3740 pszDevice = convertControllerTypeToDev(enmCtrlType);
3741
3742 StorageBus_T enmBus;
3743 rc = pStorageController->COMGETTER(Bus)(&enmBus);
3744 AssertComRC(rc);
3745 ULONG uInstance;
3746 rc = pStorageController->COMGETTER(Instance)(&uInstance);
3747 AssertComRC(rc);
3748 BOOL fUseHostIOCache;
3749 rc = pStorageController->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
3750 AssertComRC(rc);
3751
3752 /*
3753 * Call worker in EMT, that's faster and safer than doing everything
3754 * using VMR3ReqCall. Note that we separate VMR3ReqCall from VMR3ReqWait
3755 * here to make requests from under the lock in order to serialize them.
3756 */
3757 PVMREQ pReq;
3758 int vrc = VMR3ReqCall(pVM,
3759 VMCPUID_ANY,
3760 &pReq,
3761 0 /* no wait! */,
3762 VMREQFLAGS_VBOX_STATUS,
3763 (PFNRT)Console::attachStorageDevice,
3764 7,
3765 this,
3766 pVM,
3767 pszDevice,
3768 uInstance,
3769 enmBus,
3770 fUseHostIOCache,
3771 aMediumAttachment);
3772
3773 /* leave the lock before waiting for a result (EMT will call us back!) */
3774 alock.leave();
3775
3776 if (vrc == VERR_TIMEOUT || RT_SUCCESS(vrc))
3777 {
3778 vrc = VMR3ReqWait(pReq, RT_INDEFINITE_WAIT);
3779 AssertRC(vrc);
3780 if (RT_SUCCESS(vrc))
3781 vrc = pReq->iStatus;
3782 }
3783 VMR3ReqFree(pReq);
3784
3785 if (RT_SUCCESS(vrc))
3786 {
3787 LogFlowThisFunc(("Returns S_OK\n"));
3788 return S_OK;
3789 }
3790
3791 if (!pMedium)
3792 return setError(E_FAIL,
3793 tr("Could not mount the media/drive '%ls' (%Rrc)"),
3794 mediumLocation.raw(), vrc);
3795
3796 return setError(E_FAIL,
3797 tr("Could not unmount the currently mounted media/drive (%Rrc)"),
3798 vrc);
3799}
3800
3801
3802/**
3803 * Performs the storage attach operation in EMT.
3804 *
3805 * @returns VBox status code.
3806 *
3807 * @param pThis Pointer to the Console object.
3808 * @param pVM The VM handle.
3809 * @param pcszDevice The PDM device name.
3810 * @param uInstance The PDM device instance.
3811 *
3812 * @thread EMT
3813 */
3814DECLCALLBACK(int) Console::attachStorageDevice(Console *pConsole,
3815 PVM pVM,
3816 const char *pcszDevice,
3817 unsigned uInstance,
3818 StorageBus_T enmBus,
3819 bool fUseHostIOCache,
3820 IMediumAttachment *aMediumAtt)
3821{
3822 LogFlowFunc(("pConsole=%p uInstance=%u pszDevice=%p:{%s} enmBus=%u, aMediumAtt=%p\n",
3823 pConsole, uInstance, pcszDevice, pcszDevice, enmBus, aMediumAtt));
3824
3825 AssertReturn(pConsole, VERR_INVALID_PARAMETER);
3826
3827 AutoCaller autoCaller(pConsole);
3828 AssertComRCReturn(autoCaller.rc(), VERR_ACCESS_DENIED);
3829
3830 /*
3831 * Suspend the VM first.
3832 *
3833 * The VM must not be running since it might have pending I/O to
3834 * the drive which is being changed.
3835 */
3836 bool fResume;
3837 VMSTATE enmVMState = VMR3GetState(pVM);
3838 switch (enmVMState)
3839 {
3840 case VMSTATE_RESETTING:
3841 case VMSTATE_RUNNING:
3842 {
3843 LogFlowFunc(("Suspending the VM...\n"));
3844 /* disable the callback to prevent Console-level state change */
3845 pConsole->mVMStateChangeCallbackDisabled = true;
3846 int rc = VMR3Suspend(pVM);
3847 pConsole->mVMStateChangeCallbackDisabled = false;
3848 AssertRCReturn(rc, rc);
3849 fResume = true;
3850 break;
3851 }
3852
3853 case VMSTATE_SUSPENDED:
3854 case VMSTATE_CREATED:
3855 case VMSTATE_OFF:
3856 fResume = false;
3857 break;
3858
3859 case VMSTATE_RUNNING_LS:
3860 case VMSTATE_RUNNING_FT:
3861 return setErrorInternal(VBOX_E_INVALID_VM_STATE,
3862 COM_IIDOF(IConsole),
3863 getStaticComponentName(),
3864 (enmVMState == VMSTATE_RUNNING_LS) ? Utf8Str(tr("Cannot change drive during live migration")) : Utf8Str(tr("Cannot change drive during fault tolerant syncing")),
3865 false /*aWarning*/,
3866 true /*aLogIt*/);
3867
3868 default:
3869 AssertMsgFailedReturn(("enmVMState=%d\n", enmVMState), VERR_ACCESS_DENIED);
3870 }
3871
3872 /* Determine the base path for the device instance. */
3873 PCFGMNODE pCtlInst;
3874 pCtlInst = CFGMR3GetChildF(CFGMR3GetRoot(pVM), "Devices/%s/%u/", pcszDevice, uInstance);
3875 AssertReturn(pCtlInst, VERR_INTERNAL_ERROR);
3876
3877 int rc = VINF_SUCCESS;
3878 int rcRet = VINF_SUCCESS;
3879
3880 rcRet = pConsole->configMediumAttachment(pCtlInst,
3881 pcszDevice,
3882 uInstance,
3883 enmBus,
3884 fUseHostIOCache,
3885 false /* fSetupMerge */,
3886 false /* fBuiltinIoCache */,
3887 0 /* uMergeSource */,
3888 0 /* uMergeTarget */,
3889 aMediumAtt,
3890 pConsole->mMachineState,
3891 NULL /* phrc */,
3892 true /* fAttachDetach */,
3893 false /* fForceUnmount */,
3894 true /* fHotplug */,
3895 pVM,
3896 NULL /* paLedDevType */);
3897 /** @todo this dumps everything attached to this device instance, which
3898 * is more than necessary. Dumping the changed LUN would be enough. */
3899 CFGMR3Dump(pCtlInst);
3900
3901 /*
3902 * Resume the VM if necessary.
3903 */
3904 if (fResume)
3905 {
3906 LogFlowFunc(("Resuming the VM...\n"));
3907 /* disable the callback to prevent Console-level state change */
3908 pConsole->mVMStateChangeCallbackDisabled = true;
3909 rc = VMR3Resume(pVM);
3910 pConsole->mVMStateChangeCallbackDisabled = false;
3911 AssertRC(rc);
3912 if (RT_FAILURE(rc))
3913 {
3914 /* too bad, we failed. try to sync the console state with the VMM state */
3915 vmstateChangeCallback(pVM, VMSTATE_SUSPENDED, enmVMState, pConsole);
3916 }
3917 /** @todo: if we failed with drive mount, then the VMR3Resume
3918 * error (if any) will be hidden from the caller. For proper reporting
3919 * of such multiple errors to the caller we need to enhance the
3920 * IVirtualBoxError interface. For now, give the first error the higher
3921 * priority.
3922 */
3923 if (RT_SUCCESS(rcRet))
3924 rcRet = rc;
3925 }
3926
3927 LogFlowFunc(("Returning %Rrc\n", rcRet));
3928 return rcRet;
3929}
3930
3931/**
3932 * Attach a new storage device to the VM.
3933 *
3934 * @param aMediumAttachment The medium attachment which is added.
3935 * @param pVM Safe VM handle.
3936 *
3937 * @note Locks this object for writing.
3938 */
3939HRESULT Console::doStorageDeviceDetach(IMediumAttachment *aMediumAttachment, PVM pVM)
3940{
3941 AutoCaller autoCaller(this);
3942 AssertComRCReturnRC(autoCaller.rc());
3943
3944 /* We will need to release the write lock before calling EMT */
3945 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3946
3947 HRESULT rc = S_OK;
3948 const char *pszDevice = NULL;
3949
3950 SafeIfaceArray<IStorageController> ctrls;
3951 rc = mMachine->COMGETTER(StorageControllers)(ComSafeArrayAsOutParam(ctrls));
3952 AssertComRC(rc);
3953 IMedium *pMedium;
3954 rc = aMediumAttachment->COMGETTER(Medium)(&pMedium);
3955 AssertComRC(rc);
3956 Bstr mediumLocation;
3957 if (pMedium)
3958 {
3959 rc = pMedium->COMGETTER(Location)(mediumLocation.asOutParam());
3960 AssertComRC(rc);
3961 }
3962
3963 Bstr attCtrlName;
3964 rc = aMediumAttachment->COMGETTER(Controller)(attCtrlName.asOutParam());
3965 AssertComRC(rc);
3966 ComPtr<IStorageController> pStorageController;
3967 for (size_t i = 0; i < ctrls.size(); ++i)
3968 {
3969 Bstr ctrlName;
3970 rc = ctrls[i]->COMGETTER(Name)(ctrlName.asOutParam());
3971 AssertComRC(rc);
3972 if (attCtrlName == ctrlName)
3973 {
3974 pStorageController = ctrls[i];
3975 break;
3976 }
3977 }
3978 if (pStorageController.isNull())
3979 return setError(E_FAIL,
3980 tr("Could not find storage controller '%ls'"), attCtrlName.raw());
3981
3982 StorageControllerType_T enmCtrlType;
3983 rc = pStorageController->COMGETTER(ControllerType)(&enmCtrlType);
3984 AssertComRC(rc);
3985 pszDevice = convertControllerTypeToDev(enmCtrlType);
3986
3987 StorageBus_T enmBus;
3988 rc = pStorageController->COMGETTER(Bus)(&enmBus);
3989 AssertComRC(rc);
3990 ULONG uInstance;
3991 rc = pStorageController->COMGETTER(Instance)(&uInstance);
3992 AssertComRC(rc);
3993
3994 /*
3995 * Call worker in EMT, that's faster and safer than doing everything
3996 * using VMR3ReqCall. Note that we separate VMR3ReqCall from VMR3ReqWait
3997 * here to make requests from under the lock in order to serialize them.
3998 */
3999 PVMREQ pReq;
4000 int vrc = VMR3ReqCall(pVM,
4001 VMCPUID_ANY,
4002 &pReq,
4003 0 /* no wait! */,
4004 VMREQFLAGS_VBOX_STATUS,
4005 (PFNRT)Console::detachStorageDevice,
4006 6,
4007 this,
4008 pVM,
4009 pszDevice,
4010 uInstance,
4011 enmBus,
4012 aMediumAttachment);
4013
4014 /* leave the lock before waiting for a result (EMT will call us back!) */
4015 alock.leave();
4016
4017 if (vrc == VERR_TIMEOUT || RT_SUCCESS(vrc))
4018 {
4019 vrc = VMR3ReqWait(pReq, RT_INDEFINITE_WAIT);
4020 AssertRC(vrc);
4021 if (RT_SUCCESS(vrc))
4022 vrc = pReq->iStatus;
4023 }
4024 VMR3ReqFree(pReq);
4025
4026 if (RT_SUCCESS(vrc))
4027 {
4028 LogFlowThisFunc(("Returns S_OK\n"));
4029 return S_OK;
4030 }
4031
4032 if (!pMedium)
4033 return setError(E_FAIL,
4034 tr("Could not mount the media/drive '%ls' (%Rrc)"),
4035 mediumLocation.raw(), vrc);
4036
4037 return setError(E_FAIL,
4038 tr("Could not unmount the currently mounted media/drive (%Rrc)"),
4039 vrc);
4040}
4041
4042/**
4043 * Performs the storage detach operation in EMT.
4044 *
4045 * @returns VBox status code.
4046 *
4047 * @param pThis Pointer to the Console object.
4048 * @param pVM The VM handle.
4049 * @param pcszDevice The PDM device name.
4050 * @param uInstance The PDM device instance.
4051 *
4052 * @thread EMT
4053 */
4054DECLCALLBACK(int) Console::detachStorageDevice(Console *pConsole,
4055 PVM pVM,
4056 const char *pcszDevice,
4057 unsigned uInstance,
4058 StorageBus_T enmBus,
4059 IMediumAttachment *pMediumAtt)
4060{
4061 LogFlowFunc(("pConsole=%p uInstance=%u pszDevice=%p:{%s} enmBus=%u, pMediumAtt=%p\n",
4062 pConsole, uInstance, pcszDevice, pcszDevice, enmBus, pMediumAtt));
4063
4064 AssertReturn(pConsole, VERR_INVALID_PARAMETER);
4065
4066 AutoCaller autoCaller(pConsole);
4067 AssertComRCReturn(autoCaller.rc(), VERR_ACCESS_DENIED);
4068
4069 /*
4070 * Suspend the VM first.
4071 *
4072 * The VM must not be running since it might have pending I/O to
4073 * the drive which is being changed.
4074 */
4075 bool fResume;
4076 VMSTATE enmVMState = VMR3GetState(pVM);
4077 switch (enmVMState)
4078 {
4079 case VMSTATE_RESETTING:
4080 case VMSTATE_RUNNING:
4081 {
4082 LogFlowFunc(("Suspending the VM...\n"));
4083 /* disable the callback to prevent Console-level state change */
4084 pConsole->mVMStateChangeCallbackDisabled = true;
4085 int rc = VMR3Suspend(pVM);
4086 pConsole->mVMStateChangeCallbackDisabled = false;
4087 AssertRCReturn(rc, rc);
4088 fResume = true;
4089 break;
4090 }
4091
4092 case VMSTATE_SUSPENDED:
4093 case VMSTATE_CREATED:
4094 case VMSTATE_OFF:
4095 fResume = false;
4096 break;
4097
4098 case VMSTATE_RUNNING_LS:
4099 case VMSTATE_RUNNING_FT:
4100 return setErrorInternal(VBOX_E_INVALID_VM_STATE,
4101 COM_IIDOF(IConsole),
4102 getStaticComponentName(),
4103 (enmVMState == VMSTATE_RUNNING_LS) ? Utf8Str(tr("Cannot change drive during live migration")) : Utf8Str(tr("Cannot change drive during fault tolerant syncing")),
4104 false /*aWarning*/,
4105 true /*aLogIt*/);
4106
4107 default:
4108 AssertMsgFailedReturn(("enmVMState=%d\n", enmVMState), VERR_ACCESS_DENIED);
4109 }
4110
4111 /* Determine the base path for the device instance. */
4112 PCFGMNODE pCtlInst;
4113 pCtlInst = CFGMR3GetChildF(CFGMR3GetRoot(pVM), "Devices/%s/%u/", pcszDevice, uInstance);
4114 AssertReturn(pCtlInst, VERR_INTERNAL_ERROR);
4115
4116#define H() AssertMsgReturn(!FAILED(hrc), ("hrc=%Rhrc\n", hrc), VERR_GENERAL_FAILURE)
4117
4118 HRESULT hrc;
4119 int rc = VINF_SUCCESS;
4120 int rcRet = VINF_SUCCESS;
4121 unsigned uLUN;
4122 LONG lDev;
4123 LONG lPort;
4124 DeviceType_T lType;
4125 PCFGMNODE pLunL0 = NULL;
4126 PCFGMNODE pCfg = NULL;
4127
4128 hrc = pMediumAtt->COMGETTER(Device)(&lDev); H();
4129 hrc = pMediumAtt->COMGETTER(Port)(&lPort); H();
4130 hrc = pMediumAtt->COMGETTER(Type)(&lType); H();
4131 hrc = Console::convertBusPortDeviceToLun(enmBus, lPort, lDev, uLUN); H();
4132
4133#undef H
4134
4135 /* First check if the LUN really exists. */
4136 pLunL0 = CFGMR3GetChildF(pCtlInst, "LUN#%u", uLUN);
4137 if (pLunL0)
4138 {
4139 rc = PDMR3DeviceDetach(pVM, pcszDevice, uInstance, uLUN, 0);
4140 if (rc == VERR_PDM_NO_DRIVER_ATTACHED_TO_LUN)
4141 rc = VINF_SUCCESS;
4142 AssertRCReturn(rc, rc);
4143 CFGMR3RemoveNode(pLunL0);
4144
4145 Utf8Str devicePath = Utf8StrFmt("%s/%u/LUN#%u", pcszDevice, uInstance, uLUN);
4146 pConsole->mapMediumAttachments.erase(devicePath);
4147
4148 }
4149 else
4150 AssertFailedReturn(VERR_INTERNAL_ERROR);
4151
4152 CFGMR3Dump(pCtlInst);
4153
4154 /*
4155 * Resume the VM if necessary.
4156 */
4157 if (fResume)
4158 {
4159 LogFlowFunc(("Resuming the VM...\n"));
4160 /* disable the callback to prevent Console-level state change */
4161 pConsole->mVMStateChangeCallbackDisabled = true;
4162 rc = VMR3Resume(pVM);
4163 pConsole->mVMStateChangeCallbackDisabled = false;
4164 AssertRC(rc);
4165 if (RT_FAILURE(rc))
4166 {
4167 /* too bad, we failed. try to sync the console state with the VMM state */
4168 vmstateChangeCallback(pVM, VMSTATE_SUSPENDED, enmVMState, pConsole);
4169 }
4170 /** @todo: if we failed with drive mount, then the VMR3Resume
4171 * error (if any) will be hidden from the caller. For proper reporting
4172 * of such multiple errors to the caller we need to enhance the
4173 * IVirtualBoxError interface. For now, give the first error the higher
4174 * priority.
4175 */
4176 if (RT_SUCCESS(rcRet))
4177 rcRet = rc;
4178 }
4179
4180 LogFlowFunc(("Returning %Rrc\n", rcRet));
4181 return rcRet;
4182}
4183
4184/**
4185 * Called by IInternalSessionControl::OnNetworkAdapterChange().
4186 *
4187 * @note Locks this object for writing.
4188 */
4189HRESULT Console::onNetworkAdapterChange(INetworkAdapter *aNetworkAdapter, BOOL changeAdapter)
4190{
4191 LogFlowThisFunc(("\n"));
4192
4193 AutoCaller autoCaller(this);
4194 AssertComRCReturnRC(autoCaller.rc());
4195
4196 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4197
4198 HRESULT rc = S_OK;
4199
4200 /* don't trigger network change if the VM isn't running */
4201 SafeVMPtrQuiet ptrVM(this);
4202 if (ptrVM.isOk())
4203 {
4204 /* Get the properties we need from the adapter */
4205 BOOL fCableConnected, fTraceEnabled;
4206 rc = aNetworkAdapter->COMGETTER(CableConnected)(&fCableConnected);
4207 AssertComRC(rc);
4208 if (SUCCEEDED(rc))
4209 {
4210 rc = aNetworkAdapter->COMGETTER(TraceEnabled)(&fTraceEnabled);
4211 AssertComRC(rc);
4212 }
4213 if (SUCCEEDED(rc))
4214 {
4215 ULONG ulInstance;
4216 rc = aNetworkAdapter->COMGETTER(Slot)(&ulInstance);
4217 AssertComRC(rc);
4218 if (SUCCEEDED(rc))
4219 {
4220 /*
4221 * Find the adapter instance, get the config interface and update
4222 * the link state.
4223 */
4224 NetworkAdapterType_T adapterType;
4225 rc = aNetworkAdapter->COMGETTER(AdapterType)(&adapterType);
4226 AssertComRC(rc);
4227 const char *pszAdapterName = networkAdapterTypeToName(adapterType);
4228 PPDMIBASE pBase;
4229 int vrc = PDMR3QueryDeviceLun(ptrVM, pszAdapterName, ulInstance, 0, &pBase);
4230 if (RT_SUCCESS(vrc))
4231 {
4232 Assert(pBase);
4233 PPDMINETWORKCONFIG pINetCfg;
4234 pINetCfg = PDMIBASE_QUERY_INTERFACE(pBase, PDMINETWORKCONFIG);
4235 if (pINetCfg)
4236 {
4237 Log(("Console::onNetworkAdapterChange: setting link state to %d\n",
4238 fCableConnected));
4239 vrc = pINetCfg->pfnSetLinkState(pINetCfg,
4240 fCableConnected ? PDMNETWORKLINKSTATE_UP
4241 : PDMNETWORKLINKSTATE_DOWN);
4242 ComAssertRC(vrc);
4243 }
4244 if (RT_SUCCESS(vrc) && changeAdapter)
4245 {
4246 VMSTATE enmVMState = VMR3GetState(ptrVM);
4247 if ( enmVMState == VMSTATE_RUNNING /** @todo LiveMigration: Forbid or deal correctly with the _LS variants */
4248 || enmVMState == VMSTATE_SUSPENDED)
4249 {
4250 if (fTraceEnabled && fCableConnected && pINetCfg)
4251 {
4252 vrc = pINetCfg->pfnSetLinkState(pINetCfg, PDMNETWORKLINKSTATE_DOWN);
4253 ComAssertRC(vrc);
4254 }
4255
4256 rc = doNetworkAdapterChange(ptrVM, pszAdapterName, ulInstance, 0, aNetworkAdapter);
4257
4258 if (fTraceEnabled && fCableConnected && pINetCfg)
4259 {
4260 vrc = pINetCfg->pfnSetLinkState(pINetCfg, PDMNETWORKLINKSTATE_UP);
4261 ComAssertRC(vrc);
4262 }
4263 }
4264 }
4265 }
4266 else if (vrc == VERR_PDM_DEVICE_INSTANCE_NOT_FOUND)
4267 return setError(E_FAIL,
4268 tr("The network adapter #%u is not enabled"), ulInstance);
4269 else
4270 ComAssertRC(vrc);
4271
4272 if (RT_FAILURE(vrc))
4273 rc = E_FAIL;
4274 }
4275 }
4276 ptrVM.release();
4277 }
4278
4279 /* notify console callbacks on success */
4280 if (SUCCEEDED(rc))
4281 fireNetworkAdapterChangedEvent(mEventSource, aNetworkAdapter);
4282
4283 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
4284 return rc;
4285}
4286
4287/**
4288 * Called by IInternalSessionControl::OnNATEngineChange().
4289 *
4290 * @note Locks this object for writing.
4291 */
4292HRESULT Console::onNATRedirectRuleChange(ULONG ulInstance, BOOL aNatRuleRemove,
4293 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort, IN_BSTR aGuestIp, LONG aGuestPort)
4294{
4295 LogFlowThisFunc(("\n"));
4296
4297 AutoCaller autoCaller(this);
4298 AssertComRCReturnRC(autoCaller.rc());
4299
4300 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4301
4302 HRESULT rc = S_OK;
4303
4304 /* don't trigger nat engine change if the VM isn't running */
4305 SafeVMPtrQuiet ptrVM(this);
4306 if (ptrVM.isOk())
4307 {
4308 do
4309 {
4310 ComPtr<INetworkAdapter> pNetworkAdapter;
4311 rc = machine()->GetNetworkAdapter(ulInstance, pNetworkAdapter.asOutParam());
4312 if ( FAILED(rc)
4313 || pNetworkAdapter.isNull())
4314 break;
4315
4316 /*
4317 * Find the adapter instance, get the config interface and update
4318 * the link state.
4319 */
4320 NetworkAdapterType_T adapterType;
4321 rc = pNetworkAdapter->COMGETTER(AdapterType)(&adapterType);
4322 if (FAILED(rc))
4323 {
4324 AssertComRC(rc);
4325 rc = E_FAIL;
4326 break;
4327 }
4328
4329 const char *pszAdapterName = networkAdapterTypeToName(adapterType);
4330 PPDMIBASE pBase;
4331 int vrc = PDMR3QueryLun(ptrVM, pszAdapterName, ulInstance, 0, &pBase);
4332 if (RT_FAILURE(vrc))
4333 {
4334 ComAssertRC(vrc);
4335 rc = E_FAIL;
4336 break;
4337 }
4338
4339 NetworkAttachmentType_T attachmentType;
4340 rc = pNetworkAdapter->COMGETTER(AttachmentType)(&attachmentType);
4341 if ( FAILED(rc)
4342 || attachmentType != NetworkAttachmentType_NAT)
4343 {
4344 rc = E_FAIL;
4345 break;
4346 }
4347
4348 /* look down for PDMINETWORKNATCONFIG interface */
4349 PPDMINETWORKNATCONFIG pNetNatCfg = NULL;
4350 while (pBase)
4351 {
4352 pNetNatCfg = (PPDMINETWORKNATCONFIG)pBase->pfnQueryInterface(pBase, PDMINETWORKNATCONFIG_IID);
4353 if (pNetNatCfg)
4354 break;
4355 /** @todo r=bird: This stinks! */
4356 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pBase);
4357 pBase = pDrvIns->pDownBase;
4358 }
4359 if (!pNetNatCfg)
4360 break;
4361
4362 bool fUdp = aProto == NATProtocol_UDP;
4363 vrc = pNetNatCfg->pfnRedirectRuleCommand(pNetNatCfg, aNatRuleRemove, fUdp,
4364 Utf8Str(aHostIp).c_str(), aHostPort, Utf8Str(aGuestIp).c_str(),
4365 aGuestPort);
4366 if (RT_FAILURE(vrc))
4367 rc = E_FAIL;
4368 } while (0); /* break loop */
4369 ptrVM.release();
4370 }
4371
4372 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
4373 return rc;
4374}
4375
4376
4377/**
4378 * Process a network adaptor change.
4379 *
4380 * @returns COM status code.
4381 *
4382 * @parma pVM The VM handle (caller hold this safely).
4383 * @param pszDevice The PDM device name.
4384 * @param uInstance The PDM device instance.
4385 * @param uLun The PDM LUN number of the drive.
4386 * @param aNetworkAdapter The network adapter whose attachment needs to be changed
4387 *
4388 * @note Locks this object for writing.
4389 */
4390HRESULT Console::doNetworkAdapterChange(PVM pVM,
4391 const char *pszDevice,
4392 unsigned uInstance,
4393 unsigned uLun,
4394 INetworkAdapter *aNetworkAdapter)
4395{
4396 LogFlowThisFunc(("pszDevice=%p:{%s} uInstance=%u uLun=%u aNetworkAdapter=%p\n",
4397 pszDevice, pszDevice, uInstance, uLun, aNetworkAdapter));
4398
4399 AutoCaller autoCaller(this);
4400 AssertComRCReturnRC(autoCaller.rc());
4401
4402 /* We will need to release the write lock before calling EMT */
4403 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4404
4405 /* Get the VM handle. */
4406 SafeVMPtr ptrVM(this);
4407 if (!ptrVM.isOk())
4408 return ptrVM.rc();
4409
4410 /*
4411 * Call worker in EMT, that's faster and safer than doing everything
4412 * using VM3ReqCall. Note that we separate VMR3ReqCall from VMR3ReqWait
4413 * here to make requests from under the lock in order to serialize them.
4414 */
4415 PVMREQ pReq;
4416 int vrc = VMR3ReqCall(pVM, 0 /*idDstCpu*/, &pReq, 0 /* no wait! */, VMREQFLAGS_VBOX_STATUS,
4417 (PFNRT) Console::changeNetworkAttachment, 6,
4418 this, ptrVM.raw(), pszDevice, uInstance, uLun, aNetworkAdapter);
4419
4420 /* leave the lock before waiting for a result (EMT will call us back!) */
4421 alock.leave();
4422
4423 if (vrc == VERR_TIMEOUT || RT_SUCCESS(vrc))
4424 {
4425 vrc = VMR3ReqWait(pReq, RT_INDEFINITE_WAIT);
4426 AssertRC(vrc);
4427 if (RT_SUCCESS(vrc))
4428 vrc = pReq->iStatus;
4429 }
4430 VMR3ReqFree(pReq);
4431
4432 if (RT_SUCCESS(vrc))
4433 {
4434 LogFlowThisFunc(("Returns S_OK\n"));
4435 return S_OK;
4436 }
4437
4438 return setError(E_FAIL,
4439 tr("Could not change the network adaptor attachement type (%Rrc)"),
4440 vrc);
4441}
4442
4443
4444/**
4445 * Performs the Network Adaptor change in EMT.
4446 *
4447 * @returns VBox status code.
4448 *
4449 * @param pThis Pointer to the Console object.
4450 * @param pVM The VM handle.
4451 * @param pszDevice The PDM device name.
4452 * @param uInstance The PDM device instance.
4453 * @param uLun The PDM LUN number of the drive.
4454 * @param aNetworkAdapter The network adapter whose attachment needs to be changed
4455 *
4456 * @thread EMT
4457 * @note Locks the Console object for writing.
4458 */
4459DECLCALLBACK(int) Console::changeNetworkAttachment(Console *pThis,
4460 PVM pVM,
4461 const char *pszDevice,
4462 unsigned uInstance,
4463 unsigned uLun,
4464 INetworkAdapter *aNetworkAdapter)
4465{
4466 LogFlowFunc(("pThis=%p pszDevice=%p:{%s} uInstance=%u uLun=%u aNetworkAdapter=%p\n",
4467 pThis, pszDevice, pszDevice, uInstance, uLun, aNetworkAdapter));
4468
4469 AssertReturn(pThis, VERR_INVALID_PARAMETER);
4470
4471 AssertMsg( ( !strcmp(pszDevice, "pcnet")
4472 || !strcmp(pszDevice, "e1000")
4473 || !strcmp(pszDevice, "virtio-net"))
4474 && uLun == 0
4475 && uInstance < SchemaDefs::NetworkAdapterCount,
4476 ("pszDevice=%s uLun=%d uInstance=%d\n", pszDevice, uLun, uInstance));
4477 Log(("pszDevice=%s uLun=%d uInstance=%d\n", pszDevice, uLun, uInstance));
4478
4479 AutoCaller autoCaller(pThis);
4480 AssertComRCReturn(autoCaller.rc(), VERR_ACCESS_DENIED);
4481
4482 /*
4483 * Suspend the VM first.
4484 *
4485 * The VM must not be running since it might have pending I/O to
4486 * the drive which is being changed.
4487 */
4488 bool fResume;
4489 VMSTATE enmVMState = VMR3GetState(pVM);
4490 switch (enmVMState)
4491 {
4492 case VMSTATE_RESETTING:
4493 case VMSTATE_RUNNING:
4494 {
4495 LogFlowFunc(("Suspending the VM...\n"));
4496 /* disable the callback to prevent Console-level state change */
4497 pThis->mVMStateChangeCallbackDisabled = true;
4498 int rc = VMR3Suspend(pVM);
4499 pThis->mVMStateChangeCallbackDisabled = false;
4500 AssertRCReturn(rc, rc);
4501 fResume = true;
4502 break;
4503 }
4504
4505 case VMSTATE_SUSPENDED:
4506 case VMSTATE_CREATED:
4507 case VMSTATE_OFF:
4508 fResume = false;
4509 break;
4510
4511 default:
4512 AssertLogRelMsgFailedReturn(("enmVMState=%d\n", enmVMState), VERR_ACCESS_DENIED);
4513 }
4514
4515 int rc = VINF_SUCCESS;
4516 int rcRet = VINF_SUCCESS;
4517
4518 PCFGMNODE pCfg = NULL; /* /Devices/Dev/.../Config/ */
4519 PCFGMNODE pLunL0 = NULL; /* /Devices/Dev/0/LUN#0/ */
4520 PCFGMNODE pInst = CFGMR3GetChildF(CFGMR3GetRoot(pVM), "Devices/%s/%d/", pszDevice, uInstance);
4521 AssertRelease(pInst);
4522
4523 rcRet = pThis->configNetwork(pszDevice, uInstance, uLun, aNetworkAdapter, pCfg, pLunL0, pInst,
4524 true /*fAttachDetach*/, false /*fIgnoreConnectFailure*/);
4525
4526 /*
4527 * Resume the VM if necessary.
4528 */
4529 if (fResume)
4530 {
4531 LogFlowFunc(("Resuming the VM...\n"));
4532 /* disable the callback to prevent Console-level state change */
4533 pThis->mVMStateChangeCallbackDisabled = true;
4534 rc = VMR3Resume(pVM);
4535 pThis->mVMStateChangeCallbackDisabled = false;
4536 AssertRC(rc);
4537 if (RT_FAILURE(rc))
4538 {
4539 /* too bad, we failed. try to sync the console state with the VMM state */
4540 vmstateChangeCallback(pVM, VMSTATE_SUSPENDED, enmVMState, pThis);
4541 }
4542 /// @todo (r=dmik) if we failed with drive mount, then the VMR3Resume
4543 // error (if any) will be hidden from the caller. For proper reporting
4544 // of such multiple errors to the caller we need to enhance the
4545 // IVirtualBoxError interface. For now, give the first error the higher
4546 // priority.
4547 if (RT_SUCCESS(rcRet))
4548 rcRet = rc;
4549 }
4550
4551 LogFlowFunc(("Returning %Rrc\n", rcRet));
4552 return rcRet;
4553}
4554
4555
4556/**
4557 * Called by IInternalSessionControl::OnSerialPortChange().
4558 *
4559 * @note Locks this object for writing.
4560 */
4561HRESULT Console::onSerialPortChange(ISerialPort *aSerialPort)
4562{
4563 LogFlowThisFunc(("\n"));
4564
4565 AutoCaller autoCaller(this);
4566 AssertComRCReturnRC(autoCaller.rc());
4567
4568 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4569
4570 HRESULT rc = S_OK;
4571
4572 /* don't trigger serial port change if the VM isn't running */
4573 SafeVMPtrQuiet ptrVM(this);
4574 if (ptrVM.isOk())
4575 {
4576 /* nothing to do so far */
4577 ptrVM.release();
4578 }
4579
4580 /* notify console callbacks on success */
4581 if (SUCCEEDED(rc))
4582 fireSerialPortChangedEvent(mEventSource, aSerialPort);
4583
4584 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
4585 return rc;
4586}
4587
4588/**
4589 * Called by IInternalSessionControl::OnParallelPortChange().
4590 *
4591 * @note Locks this object for writing.
4592 */
4593HRESULT Console::onParallelPortChange(IParallelPort *aParallelPort)
4594{
4595 LogFlowThisFunc(("\n"));
4596
4597 AutoCaller autoCaller(this);
4598 AssertComRCReturnRC(autoCaller.rc());
4599
4600 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4601
4602 HRESULT rc = S_OK;
4603
4604 /* don't trigger parallel port change if the VM isn't running */
4605 SafeVMPtrQuiet ptrVM(this);
4606 if (ptrVM.isOk())
4607 {
4608 /* nothing to do so far */
4609 ptrVM.release();
4610 }
4611
4612 /* notify console callbacks on success */
4613 if (SUCCEEDED(rc))
4614 fireParallelPortChangedEvent(mEventSource, aParallelPort);
4615
4616 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
4617 return rc;
4618}
4619
4620/**
4621 * Called by IInternalSessionControl::OnStorageControllerChange().
4622 *
4623 * @note Locks this object for writing.
4624 */
4625HRESULT Console::onStorageControllerChange()
4626{
4627 LogFlowThisFunc(("\n"));
4628
4629 AutoCaller autoCaller(this);
4630 AssertComRCReturnRC(autoCaller.rc());
4631
4632 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4633
4634 HRESULT rc = S_OK;
4635
4636 /* don't trigger storage controller change if the VM isn't running */
4637 SafeVMPtrQuiet ptrVM(this);
4638 if (ptrVM.isOk())
4639 {
4640 /* nothing to do so far */
4641 ptrVM.release();
4642 }
4643
4644 /* notify console callbacks on success */
4645 if (SUCCEEDED(rc))
4646 fireStorageControllerChangedEvent(mEventSource);
4647
4648 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
4649 return rc;
4650}
4651
4652/**
4653 * Called by IInternalSessionControl::OnMediumChange().
4654 *
4655 * @note Locks this object for writing.
4656 */
4657HRESULT Console::onMediumChange(IMediumAttachment *aMediumAttachment, BOOL aForce)
4658{
4659 LogFlowThisFunc(("\n"));
4660
4661 AutoCaller autoCaller(this);
4662 AssertComRCReturnRC(autoCaller.rc());
4663
4664 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4665
4666 HRESULT rc = S_OK;
4667
4668 /* don't trigger medium change if the VM isn't running */
4669 SafeVMPtrQuiet ptrVM(this);
4670 if (ptrVM.isOk())
4671 {
4672 rc = doMediumChange(aMediumAttachment, !!aForce, ptrVM);
4673 ptrVM.release();
4674 }
4675
4676 /* notify console callbacks on success */
4677 if (SUCCEEDED(rc))
4678 fireMediumChangedEvent(mEventSource, aMediumAttachment);
4679
4680 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
4681 return rc;
4682}
4683
4684/**
4685 * Called by IInternalSessionControl::OnCPUChange().
4686 *
4687 * @note Locks this object for writing.
4688 */
4689HRESULT Console::onCPUChange(ULONG aCPU, BOOL aRemove)
4690{
4691 LogFlowThisFunc(("\n"));
4692
4693 AutoCaller autoCaller(this);
4694 AssertComRCReturnRC(autoCaller.rc());
4695
4696 HRESULT rc = S_OK;
4697
4698 /* don't trigger CPU change if the VM isn't running */
4699 SafeVMPtrQuiet ptrVM(this);
4700 if (ptrVM.isOk())
4701 {
4702 if (aRemove)
4703 rc = doCPURemove(aCPU, ptrVM);
4704 else
4705 rc = doCPUAdd(aCPU, ptrVM);
4706 ptrVM.release();
4707 }
4708
4709 /* notify console callbacks on success */
4710 if (SUCCEEDED(rc))
4711 fireCPUChangedEvent(mEventSource, aCPU, aRemove);
4712
4713 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
4714 return rc;
4715}
4716
4717/**
4718 * Called by IInternalSessionControl::OnCpuExecutionCapChange().
4719 *
4720 * @note Locks this object for writing.
4721 */
4722HRESULT Console::onCPUExecutionCapChange(ULONG aExecutionCap)
4723{
4724 LogFlowThisFunc(("\n"));
4725
4726 AutoCaller autoCaller(this);
4727 AssertComRCReturnRC(autoCaller.rc());
4728
4729 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4730
4731 HRESULT rc = S_OK;
4732
4733 /* don't trigger the CPU priority change if the VM isn't running */
4734 SafeVMPtrQuiet ptrVM(this);
4735 if (ptrVM.isOk())
4736 {
4737 if ( mMachineState == MachineState_Running
4738 || mMachineState == MachineState_Teleporting
4739 || mMachineState == MachineState_LiveSnapshotting
4740 )
4741 {
4742 /* No need to call in the EMT thread. */
4743 rc = VMR3SetCpuExecutionCap(ptrVM, aExecutionCap);
4744 }
4745 else
4746 rc = setInvalidMachineStateError();
4747 ptrVM.release();
4748 }
4749
4750 /* notify console callbacks on success */
4751 if (SUCCEEDED(rc))
4752 fireCPUExecutionCapChangedEvent(mEventSource, aExecutionCap);
4753
4754 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
4755 return rc;
4756}
4757
4758/**
4759 * Called by IInternalSessionControl::OnVRDEServerChange().
4760 *
4761 * @note Locks this object for writing.
4762 */
4763HRESULT Console::onVRDEServerChange(BOOL aRestart)
4764{
4765 AutoCaller autoCaller(this);
4766 AssertComRCReturnRC(autoCaller.rc());
4767
4768 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4769
4770 HRESULT rc = S_OK;
4771
4772 if ( mVRDEServer
4773 && ( mMachineState == MachineState_Running
4774 || mMachineState == MachineState_Teleporting
4775 || mMachineState == MachineState_LiveSnapshotting
4776 )
4777 )
4778 {
4779 BOOL vrdpEnabled = FALSE;
4780
4781 rc = mVRDEServer->COMGETTER(Enabled)(&vrdpEnabled);
4782 ComAssertComRCRetRC(rc);
4783
4784 if (aRestart)
4785 {
4786 /* VRDP server may call this Console object back from other threads (VRDP INPUT or OUTPUT). */
4787 alock.leave();
4788
4789 if (vrdpEnabled)
4790 {
4791 // If there was no VRDP server started the 'stop' will do nothing.
4792 // However if a server was started and this notification was called,
4793 // we have to restart the server.
4794 mConsoleVRDPServer->Stop();
4795
4796 if (RT_FAILURE(mConsoleVRDPServer->Launch()))
4797 rc = E_FAIL;
4798 else
4799 mConsoleVRDPServer->EnableConnections();
4800 }
4801 else
4802 {
4803 mConsoleVRDPServer->Stop();
4804 }
4805
4806 alock.enter();
4807 }
4808 }
4809
4810 /* notify console callbacks on success */
4811 if (SUCCEEDED(rc))
4812 fireVRDEServerChangedEvent(mEventSource);
4813
4814 return rc;
4815}
4816
4817/**
4818 * @note Locks this object for reading.
4819 */
4820void Console::onVRDEServerInfoChange()
4821{
4822 AutoCaller autoCaller(this);
4823 AssertComRCReturnVoid(autoCaller.rc());
4824
4825 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4826
4827 fireVRDEServerInfoChangedEvent(mEventSource);
4828}
4829
4830
4831/**
4832 * Called by IInternalSessionControl::OnUSBControllerChange().
4833 *
4834 * @note Locks this object for writing.
4835 */
4836HRESULT Console::onUSBControllerChange()
4837{
4838 LogFlowThisFunc(("\n"));
4839
4840 AutoCaller autoCaller(this);
4841 AssertComRCReturnRC(autoCaller.rc());
4842
4843 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4844
4845 HRESULT rc = S_OK;
4846
4847 /* don't trigger USB controller change if the VM isn't running */
4848 SafeVMPtrQuiet ptrVM(this);
4849 if (ptrVM.isOk())
4850 {
4851 /// @todo implement one day.
4852 // Anyway, if we want to query the machine's USB Controller we need
4853 // to cache it to mUSBController in #init() (similar to mDVDDrive).
4854 //
4855 // bird: While the VM supports hot-plugging, I doubt any guest can
4856 // handle it at this time... :-)
4857
4858 /* nothing to do so far */
4859 ptrVM.release();
4860 }
4861
4862 /* notify console callbacks on success */
4863 if (SUCCEEDED(rc))
4864 fireUSBControllerChangedEvent(mEventSource);
4865
4866 return rc;
4867}
4868
4869/**
4870 * Called by IInternalSessionControl::OnSharedFolderChange().
4871 *
4872 * @note Locks this object for writing.
4873 */
4874HRESULT Console::onSharedFolderChange(BOOL aGlobal)
4875{
4876 LogFlowThisFunc(("aGlobal=%RTbool\n", aGlobal));
4877
4878 AutoCaller autoCaller(this);
4879 AssertComRCReturnRC(autoCaller.rc());
4880
4881 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4882
4883 HRESULT rc = fetchSharedFolders(aGlobal);
4884
4885 /* notify console callbacks on success */
4886 if (SUCCEEDED(rc))
4887 {
4888 fireSharedFolderChangedEvent(mEventSource, aGlobal ? (Scope_T)Scope_Global : (Scope_T)Scope_Machine);
4889 }
4890
4891 return rc;
4892}
4893
4894/**
4895 * Called by IInternalSessionControl::OnUSBDeviceAttach() or locally by
4896 * processRemoteUSBDevices() after IInternalMachineControl::RunUSBDeviceFilters()
4897 * returns TRUE for a given remote USB device.
4898 *
4899 * @return S_OK if the device was attached to the VM.
4900 * @return failure if not attached.
4901 *
4902 * @param aDevice
4903 * The device in question.
4904 * @param aMaskedIfs
4905 * The interfaces to hide from the guest.
4906 *
4907 * @note Locks this object for writing.
4908 */
4909HRESULT Console::onUSBDeviceAttach(IUSBDevice *aDevice, IVirtualBoxErrorInfo *aError, ULONG aMaskedIfs)
4910{
4911#ifdef VBOX_WITH_USB
4912 LogFlowThisFunc(("aDevice=%p aError=%p\n", aDevice, aError));
4913
4914 AutoCaller autoCaller(this);
4915 ComAssertComRCRetRC(autoCaller.rc());
4916
4917 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4918
4919 /* Get the VM pointer (we don't need error info, since it's a callback). */
4920 SafeVMPtrQuiet ptrVM(this);
4921 if (!ptrVM.isOk())
4922 {
4923 /* The VM may be no more operational when this message arrives
4924 * (e.g. it may be Saving or Stopping or just PoweredOff) --
4925 * autoVMCaller.rc() will return a failure in this case. */
4926 LogFlowThisFunc(("Attach request ignored (mMachineState=%d).\n",
4927 mMachineState));
4928 return ptrVM.rc();
4929 }
4930
4931 if (aError != NULL)
4932 {
4933 /* notify callbacks about the error */
4934 onUSBDeviceStateChange(aDevice, true /* aAttached */, aError);
4935 return S_OK;
4936 }
4937
4938 /* Don't proceed unless there's at least one USB hub. */
4939 if (!PDMR3USBHasHub(ptrVM))
4940 {
4941 LogFlowThisFunc(("Attach request ignored (no USB controller).\n"));
4942 return E_FAIL;
4943 }
4944
4945 HRESULT rc = attachUSBDevice(aDevice, aMaskedIfs);
4946 if (FAILED(rc))
4947 {
4948 /* take the current error info */
4949 com::ErrorInfoKeeper eik;
4950 /* the error must be a VirtualBoxErrorInfo instance */
4951 ComPtr<IVirtualBoxErrorInfo> pError = eik.takeError();
4952 Assert(!pError.isNull());
4953 if (!pError.isNull())
4954 {
4955 /* notify callbacks about the error */
4956 onUSBDeviceStateChange(aDevice, true /* aAttached */, pError);
4957 }
4958 }
4959
4960 return rc;
4961
4962#else /* !VBOX_WITH_USB */
4963 return E_FAIL;
4964#endif /* !VBOX_WITH_USB */
4965}
4966
4967/**
4968 * Called by IInternalSessionControl::OnUSBDeviceDetach() and locally by
4969 * processRemoteUSBDevices().
4970 *
4971 * @note Locks this object for writing.
4972 */
4973HRESULT Console::onUSBDeviceDetach(IN_BSTR aId,
4974 IVirtualBoxErrorInfo *aError)
4975{
4976#ifdef VBOX_WITH_USB
4977 Guid Uuid(aId);
4978 LogFlowThisFunc(("aId={%RTuuid} aError=%p\n", Uuid.raw(), aError));
4979
4980 AutoCaller autoCaller(this);
4981 AssertComRCReturnRC(autoCaller.rc());
4982
4983 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4984
4985 /* Find the device. */
4986 ComObjPtr<OUSBDevice> pUSBDevice;
4987 USBDeviceList::iterator it = mUSBDevices.begin();
4988 while (it != mUSBDevices.end())
4989 {
4990 LogFlowThisFunc(("it={%RTuuid}\n", (*it)->id().raw()));
4991 if ((*it)->id() == Uuid)
4992 {
4993 pUSBDevice = *it;
4994 break;
4995 }
4996 ++ it;
4997 }
4998
4999
5000 if (pUSBDevice.isNull())
5001 {
5002 LogFlowThisFunc(("USB device not found.\n"));
5003
5004 /* The VM may be no more operational when this message arrives
5005 * (e.g. it may be Saving or Stopping or just PoweredOff). Use
5006 * AutoVMCaller to detect it -- AutoVMCaller::rc() will return a
5007 * failure in this case. */
5008
5009 AutoVMCallerQuiet autoVMCaller(this);
5010 if (FAILED(autoVMCaller.rc()))
5011 {
5012 LogFlowThisFunc(("Detach request ignored (mMachineState=%d).\n",
5013 mMachineState));
5014 return autoVMCaller.rc();
5015 }
5016
5017 /* the device must be in the list otherwise */
5018 AssertFailedReturn(E_FAIL);
5019 }
5020
5021 if (aError != NULL)
5022 {
5023 /* notify callback about an error */
5024 onUSBDeviceStateChange(pUSBDevice, false /* aAttached */, aError);
5025 return S_OK;
5026 }
5027
5028 HRESULT rc = detachUSBDevice(it);
5029
5030 if (FAILED(rc))
5031 {
5032 /* take the current error info */
5033 com::ErrorInfoKeeper eik;
5034 /* the error must be a VirtualBoxErrorInfo instance */
5035 ComPtr<IVirtualBoxErrorInfo> pError = eik.takeError();
5036 Assert(!pError.isNull());
5037 if (!pError.isNull())
5038 {
5039 /* notify callbacks about the error */
5040 onUSBDeviceStateChange(pUSBDevice, false /* aAttached */, pError);
5041 }
5042 }
5043
5044 return rc;
5045
5046#else /* !VBOX_WITH_USB */
5047 return E_FAIL;
5048#endif /* !VBOX_WITH_USB */
5049}
5050
5051/**
5052 * Called by IInternalSessionControl::OnBandwidthGroupChange().
5053 *
5054 * @note Locks this object for writing.
5055 */
5056HRESULT Console::onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
5057{
5058 LogFlowThisFunc(("\n"));
5059
5060 AutoCaller autoCaller(this);
5061 AssertComRCReturnRC(autoCaller.rc());
5062
5063 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5064
5065 HRESULT rc = S_OK;
5066
5067 /* don't trigger the CPU priority change if the VM isn't running */
5068 SafeVMPtrQuiet ptrVM(this);
5069 if (ptrVM.isOk())
5070 {
5071 if ( mMachineState == MachineState_Running
5072 || mMachineState == MachineState_Teleporting
5073 || mMachineState == MachineState_LiveSnapshotting
5074 )
5075 {
5076 /* No need to call in the EMT thread. */
5077 ULONG cMax;
5078 Bstr strName;
5079 rc = aBandwidthGroup->COMGETTER(Name)(strName.asOutParam());
5080 if (SUCCEEDED(rc))
5081 rc = aBandwidthGroup->COMGETTER(MaxMbPerSec)(&cMax);
5082
5083 if (SUCCEEDED(rc))
5084 {
5085 int vrc;
5086 vrc = PDMR3AsyncCompletionBwMgrSetMaxForFile(ptrVM, Utf8Str(strName).c_str(),
5087 cMax * _1M);
5088 AssertRC(vrc);
5089 }
5090 }
5091 else
5092 rc = setInvalidMachineStateError();
5093 ptrVM.release();
5094 }
5095
5096 /* notify console callbacks on success */
5097 if (SUCCEEDED(rc))
5098 fireBandwidthGroupChangedEvent(mEventSource, aBandwidthGroup);
5099
5100 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
5101 return rc;
5102}
5103
5104/**
5105 * Called by IInternalSessionControl::OnStorageDeviceChange().
5106 *
5107 * @note Locks this object for writing.
5108 */
5109HRESULT Console::onStorageDeviceChange(IMediumAttachment *aMediumAttachment, BOOL aRemove)
5110{
5111 LogFlowThisFunc(("\n"));
5112
5113 AutoCaller autoCaller(this);
5114 AssertComRCReturnRC(autoCaller.rc());
5115
5116 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5117
5118 HRESULT rc = S_OK;
5119
5120 /* don't trigger medium change if the VM isn't running */
5121 SafeVMPtrQuiet ptrVM(this);
5122 if (ptrVM.isOk())
5123 {
5124 if (aRemove)
5125 rc = doStorageDeviceDetach(aMediumAttachment, ptrVM);
5126 else
5127 rc = doStorageDeviceAttach(aMediumAttachment, ptrVM);
5128 ptrVM.release();
5129 }
5130
5131 /* notify console callbacks on success */
5132 if (SUCCEEDED(rc))
5133 fireStorageDeviceChangedEvent(mEventSource, aMediumAttachment, aRemove);
5134
5135 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
5136 return rc;
5137}
5138
5139/**
5140 * @note Temporarily locks this object for writing.
5141 */
5142HRESULT Console::getGuestProperty(IN_BSTR aName, BSTR *aValue,
5143 LONG64 *aTimestamp, BSTR *aFlags)
5144{
5145#ifndef VBOX_WITH_GUEST_PROPS
5146 ReturnComNotImplemented();
5147#else /* VBOX_WITH_GUEST_PROPS */
5148 if (!VALID_PTR(aName))
5149 return E_INVALIDARG;
5150 if (!VALID_PTR(aValue))
5151 return E_POINTER;
5152 if ((aTimestamp != NULL) && !VALID_PTR(aTimestamp))
5153 return E_POINTER;
5154 if ((aFlags != NULL) && !VALID_PTR(aFlags))
5155 return E_POINTER;
5156
5157 AutoCaller autoCaller(this);
5158 AssertComRCReturnRC(autoCaller.rc());
5159
5160 /* protect mpVM (if not NULL) */
5161 AutoVMCallerWeak autoVMCaller(this);
5162 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
5163
5164 /* Note: validity of mVMMDev which is bound to uninit() is guaranteed by
5165 * autoVMCaller, so there is no need to hold a lock of this */
5166
5167 HRESULT rc = E_UNEXPECTED;
5168 using namespace guestProp;
5169
5170 try
5171 {
5172 VBOXHGCMSVCPARM parm[4];
5173 Utf8Str Utf8Name = aName;
5174 char pszBuffer[MAX_VALUE_LEN + MAX_FLAGS_LEN];
5175
5176 parm[0].type = VBOX_HGCM_SVC_PARM_PTR;
5177 parm[0].u.pointer.addr = (void*)Utf8Name.c_str();
5178 /* The + 1 is the null terminator */
5179 parm[0].u.pointer.size = (uint32_t)Utf8Name.length() + 1;
5180 parm[1].type = VBOX_HGCM_SVC_PARM_PTR;
5181 parm[1].u.pointer.addr = pszBuffer;
5182 parm[1].u.pointer.size = sizeof(pszBuffer);
5183 int vrc = m_pVMMDev->hgcmHostCall("VBoxGuestPropSvc", GET_PROP_HOST,
5184 4, &parm[0]);
5185 /* The returned string should never be able to be greater than our buffer */
5186 AssertLogRel(vrc != VERR_BUFFER_OVERFLOW);
5187 AssertLogRel(RT_FAILURE(vrc) || VBOX_HGCM_SVC_PARM_64BIT == parm[2].type);
5188 if (RT_SUCCESS(vrc) || (VERR_NOT_FOUND == vrc))
5189 {
5190 rc = S_OK;
5191 if (vrc != VERR_NOT_FOUND)
5192 {
5193 Utf8Str strBuffer(pszBuffer);
5194 strBuffer.cloneTo(aValue);
5195
5196 if (aTimestamp)
5197 *aTimestamp = parm[2].u.uint64;
5198
5199 if (aFlags)
5200 {
5201 size_t iFlags = strBuffer.length() + 1;
5202 Utf8Str(pszBuffer + iFlags).cloneTo(aFlags);
5203 }
5204 }
5205 else
5206 aValue = NULL;
5207 }
5208 else
5209 rc = setError(E_UNEXPECTED,
5210 tr("The service call failed with the error %Rrc"),
5211 vrc);
5212 }
5213 catch(std::bad_alloc & /*e*/)
5214 {
5215 rc = E_OUTOFMEMORY;
5216 }
5217 return rc;
5218#endif /* VBOX_WITH_GUEST_PROPS */
5219}
5220
5221/**
5222 * @note Temporarily locks this object for writing.
5223 */
5224HRESULT Console::setGuestProperty(IN_BSTR aName, IN_BSTR aValue, IN_BSTR aFlags)
5225{
5226#ifndef VBOX_WITH_GUEST_PROPS
5227 ReturnComNotImplemented();
5228#else /* VBOX_WITH_GUEST_PROPS */
5229 if (!VALID_PTR(aName))
5230 return E_INVALIDARG;
5231 if ((aValue != NULL) && !VALID_PTR(aValue))
5232 return E_INVALIDARG;
5233 if ((aFlags != NULL) && !VALID_PTR(aFlags))
5234 return E_INVALIDARG;
5235
5236 AutoCaller autoCaller(this);
5237 AssertComRCReturnRC(autoCaller.rc());
5238
5239 /* protect mpVM (if not NULL) */
5240 AutoVMCallerWeak autoVMCaller(this);
5241 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
5242
5243 /* Note: validity of mVMMDev which is bound to uninit() is guaranteed by
5244 * autoVMCaller, so there is no need to hold a lock of this */
5245
5246 HRESULT rc = E_UNEXPECTED;
5247 using namespace guestProp;
5248
5249 VBOXHGCMSVCPARM parm[3];
5250 Utf8Str Utf8Name = aName;
5251 int vrc = VINF_SUCCESS;
5252
5253 parm[0].type = VBOX_HGCM_SVC_PARM_PTR;
5254 parm[0].u.pointer.addr = (void*)Utf8Name.c_str();
5255 /* The + 1 is the null terminator */
5256 parm[0].u.pointer.size = (uint32_t)Utf8Name.length() + 1;
5257 Utf8Str Utf8Value = aValue;
5258 if (aValue != NULL)
5259 {
5260 parm[1].type = VBOX_HGCM_SVC_PARM_PTR;
5261 parm[1].u.pointer.addr = (void*)Utf8Value.c_str();
5262 /* The + 1 is the null terminator */
5263 parm[1].u.pointer.size = (uint32_t)Utf8Value.length() + 1;
5264 }
5265 Utf8Str Utf8Flags = aFlags;
5266 if (aFlags != NULL)
5267 {
5268 parm[2].type = VBOX_HGCM_SVC_PARM_PTR;
5269 parm[2].u.pointer.addr = (void*)Utf8Flags.c_str();
5270 /* The + 1 is the null terminator */
5271 parm[2].u.pointer.size = (uint32_t)Utf8Flags.length() + 1;
5272 }
5273 if ((aValue != NULL) && (aFlags != NULL))
5274 vrc = m_pVMMDev->hgcmHostCall("VBoxGuestPropSvc", SET_PROP_HOST,
5275 3, &parm[0]);
5276 else if (aValue != NULL)
5277 vrc = m_pVMMDev->hgcmHostCall("VBoxGuestPropSvc", SET_PROP_VALUE_HOST,
5278 2, &parm[0]);
5279 else
5280 vrc = m_pVMMDev->hgcmHostCall("VBoxGuestPropSvc", DEL_PROP_HOST,
5281 1, &parm[0]);
5282 if (RT_SUCCESS(vrc))
5283 rc = S_OK;
5284 else
5285 rc = setError(E_UNEXPECTED,
5286 tr("The service call failed with the error %Rrc"),
5287 vrc);
5288 return rc;
5289#endif /* VBOX_WITH_GUEST_PROPS */
5290}
5291
5292
5293/**
5294 * @note Temporarily locks this object for writing.
5295 */
5296HRESULT Console::enumerateGuestProperties(IN_BSTR aPatterns,
5297 ComSafeArrayOut(BSTR, aNames),
5298 ComSafeArrayOut(BSTR, aValues),
5299 ComSafeArrayOut(LONG64, aTimestamps),
5300 ComSafeArrayOut(BSTR, aFlags))
5301{
5302#ifndef VBOX_WITH_GUEST_PROPS
5303 ReturnComNotImplemented();
5304#else /* VBOX_WITH_GUEST_PROPS */
5305 if (!VALID_PTR(aPatterns) && (aPatterns != NULL))
5306 return E_POINTER;
5307 if (ComSafeArrayOutIsNull(aNames))
5308 return E_POINTER;
5309 if (ComSafeArrayOutIsNull(aValues))
5310 return E_POINTER;
5311 if (ComSafeArrayOutIsNull(aTimestamps))
5312 return E_POINTER;
5313 if (ComSafeArrayOutIsNull(aFlags))
5314 return E_POINTER;
5315
5316 AutoCaller autoCaller(this);
5317 AssertComRCReturnRC(autoCaller.rc());
5318
5319 /* protect mpVM (if not NULL) */
5320 AutoVMCallerWeak autoVMCaller(this);
5321 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
5322
5323 /* Note: validity of mVMMDev which is bound to uninit() is guaranteed by
5324 * autoVMCaller, so there is no need to hold a lock of this */
5325
5326 return doEnumerateGuestProperties(aPatterns, ComSafeArrayOutArg(aNames),
5327 ComSafeArrayOutArg(aValues),
5328 ComSafeArrayOutArg(aTimestamps),
5329 ComSafeArrayOutArg(aFlags));
5330#endif /* VBOX_WITH_GUEST_PROPS */
5331}
5332
5333
5334/*
5335 * Internal: helper function for connecting progress reporting
5336 */
5337static int onlineMergeMediumProgress(void *pvUser, unsigned uPercentage)
5338{
5339 HRESULT rc = S_OK;
5340 IProgress *pProgress = static_cast<IProgress *>(pvUser);
5341 if (pProgress)
5342 rc = pProgress->SetCurrentOperationProgress(uPercentage);
5343 return SUCCEEDED(rc) ? VINF_SUCCESS : VERR_GENERAL_FAILURE;
5344}
5345
5346/**
5347 * @note Temporarily locks this object for writing. bird: And/or reading?
5348 */
5349HRESULT Console::onlineMergeMedium(IMediumAttachment *aMediumAttachment,
5350 ULONG aSourceIdx, ULONG aTargetIdx,
5351 IMedium *aSource, IMedium *aTarget,
5352 BOOL aMergeForward,
5353 IMedium *aParentForTarget,
5354 ComSafeArrayIn(IMedium *, aChildrenToReparent),
5355 IProgress *aProgress)
5356{
5357 AutoCaller autoCaller(this);
5358 AssertComRCReturnRC(autoCaller.rc());
5359
5360 HRESULT rc = S_OK;
5361 int vrc = VINF_SUCCESS;
5362
5363 /* Get the VM - must be done before the read-locking. */
5364 SafeVMPtr ptrVM(this);
5365 if (!ptrVM.isOk())
5366 return ptrVM.rc();
5367
5368 /* We will need to release the lock before doing the actual merge */
5369 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5370
5371 /* paranoia - we don't want merges to happen while teleporting etc. */
5372 switch (mMachineState)
5373 {
5374 case MachineState_DeletingSnapshotOnline:
5375 case MachineState_DeletingSnapshotPaused:
5376 break;
5377
5378 default:
5379 return setInvalidMachineStateError();
5380 }
5381
5382 /** @todo AssertComRC -> AssertComRCReturn! Could potentially end up
5383 * using uninitialized variables here. */
5384 BOOL fBuiltinIoCache;
5385 rc = mMachine->COMGETTER(IoCacheEnabled)(&fBuiltinIoCache);
5386 AssertComRC(rc);
5387 SafeIfaceArray<IStorageController> ctrls;
5388 rc = mMachine->COMGETTER(StorageControllers)(ComSafeArrayAsOutParam(ctrls));
5389 AssertComRC(rc);
5390 LONG lDev;
5391 rc = aMediumAttachment->COMGETTER(Device)(&lDev);
5392 AssertComRC(rc);
5393 LONG lPort;
5394 rc = aMediumAttachment->COMGETTER(Port)(&lPort);
5395 AssertComRC(rc);
5396 IMedium *pMedium;
5397 rc = aMediumAttachment->COMGETTER(Medium)(&pMedium);
5398 AssertComRC(rc);
5399 Bstr mediumLocation;
5400 if (pMedium)
5401 {
5402 rc = pMedium->COMGETTER(Location)(mediumLocation.asOutParam());
5403 AssertComRC(rc);
5404 }
5405
5406 Bstr attCtrlName;
5407 rc = aMediumAttachment->COMGETTER(Controller)(attCtrlName.asOutParam());
5408 AssertComRC(rc);
5409 ComPtr<IStorageController> pStorageController;
5410 for (size_t i = 0; i < ctrls.size(); ++i)
5411 {
5412 Bstr ctrlName;
5413 rc = ctrls[i]->COMGETTER(Name)(ctrlName.asOutParam());
5414 AssertComRC(rc);
5415 if (attCtrlName == ctrlName)
5416 {
5417 pStorageController = ctrls[i];
5418 break;
5419 }
5420 }
5421 if (pStorageController.isNull())
5422 return setError(E_FAIL,
5423 tr("Could not find storage controller '%ls'"),
5424 attCtrlName.raw());
5425
5426 StorageControllerType_T enmCtrlType;
5427 rc = pStorageController->COMGETTER(ControllerType)(&enmCtrlType);
5428 AssertComRC(rc);
5429 const char *pcszDevice = convertControllerTypeToDev(enmCtrlType);
5430
5431 StorageBus_T enmBus;
5432 rc = pStorageController->COMGETTER(Bus)(&enmBus);
5433 AssertComRC(rc);
5434 ULONG uInstance;
5435 rc = pStorageController->COMGETTER(Instance)(&uInstance);
5436 AssertComRC(rc);
5437 BOOL fUseHostIOCache;
5438 rc = pStorageController->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
5439 AssertComRC(rc);
5440
5441 unsigned uLUN;
5442 rc = Console::convertBusPortDeviceToLun(enmBus, lPort, lDev, uLUN);
5443 AssertComRCReturnRC(rc);
5444
5445 alock.release();
5446
5447 /* Pause the VM, as it might have pending IO on this drive */
5448 VMSTATE enmVMState = VMR3GetState(ptrVM);
5449 if (mMachineState == MachineState_DeletingSnapshotOnline)
5450 {
5451 LogFlowFunc(("Suspending the VM...\n"));
5452 /* disable the callback to prevent Console-level state change */
5453 mVMStateChangeCallbackDisabled = true;
5454 int vrc2 = VMR3Suspend(ptrVM);
5455 mVMStateChangeCallbackDisabled = false;
5456 AssertRCReturn(vrc2, E_FAIL);
5457 }
5458
5459 vrc = VMR3ReqCallWait(ptrVM,
5460 VMCPUID_ANY,
5461 (PFNRT)reconfigureMediumAttachment,
5462 13,
5463 this,
5464 ptrVM.raw(),
5465 pcszDevice,
5466 uInstance,
5467 enmBus,
5468 fUseHostIOCache,
5469 fBuiltinIoCache,
5470 true /* fSetupMerge */,
5471 aSourceIdx,
5472 aTargetIdx,
5473 aMediumAttachment,
5474 mMachineState,
5475 &rc);
5476 /* error handling is after resuming the VM */
5477
5478 if (mMachineState == MachineState_DeletingSnapshotOnline)
5479 {
5480 LogFlowFunc(("Resuming the VM...\n"));
5481 /* disable the callback to prevent Console-level state change */
5482 mVMStateChangeCallbackDisabled = true;
5483 int vrc2 = VMR3Resume(ptrVM);
5484 mVMStateChangeCallbackDisabled = false;
5485 if (RT_FAILURE(vrc2))
5486 {
5487 /* too bad, we failed. try to sync the console state with the VMM state */
5488 AssertLogRelRC(vrc2);
5489 vmstateChangeCallback(ptrVM, VMSTATE_SUSPENDED, enmVMState, this);
5490 }
5491 }
5492
5493 if (RT_FAILURE(vrc))
5494 return setError(E_FAIL, tr("%Rrc"), vrc);
5495 if (FAILED(rc))
5496 return rc;
5497
5498 PPDMIBASE pIBase = NULL;
5499 PPDMIMEDIA pIMedium = NULL;
5500 vrc = PDMR3QueryDriverOnLun(ptrVM, pcszDevice, uInstance, uLUN, "VD", &pIBase);
5501 if (RT_SUCCESS(vrc))
5502 {
5503 if (pIBase)
5504 {
5505 pIMedium = (PPDMIMEDIA)pIBase->pfnQueryInterface(pIBase, PDMIMEDIA_IID);
5506 if (!pIMedium)
5507 return setError(E_FAIL, tr("could not query medium interface of controller"));
5508 }
5509 else
5510 return setError(E_FAIL, tr("could not query base interface of controller"));
5511 }
5512
5513 /* Finally trigger the merge. */
5514 vrc = pIMedium->pfnMerge(pIMedium, onlineMergeMediumProgress, aProgress);
5515 if (RT_FAILURE(vrc))
5516 return setError(E_FAIL, tr("Failed to perform an online medium merge (%Rrc)"), vrc);
5517
5518 /* Pause the VM, as it might have pending IO on this drive */
5519 enmVMState = VMR3GetState(ptrVM);
5520 if (mMachineState == MachineState_DeletingSnapshotOnline)
5521 {
5522 LogFlowFunc(("Suspending the VM...\n"));
5523 /* disable the callback to prevent Console-level state change */
5524 mVMStateChangeCallbackDisabled = true;
5525 int vrc2 = VMR3Suspend(ptrVM);
5526 mVMStateChangeCallbackDisabled = false;
5527 AssertRCReturn(vrc2, E_FAIL);
5528 }
5529
5530 /* Update medium chain and state now, so that the VM can continue. */
5531 rc = mControl->FinishOnlineMergeMedium(aMediumAttachment, aSource, aTarget,
5532 aMergeForward, aParentForTarget,
5533 ComSafeArrayInArg(aChildrenToReparent));
5534
5535 vrc = VMR3ReqCallWait(ptrVM,
5536 VMCPUID_ANY,
5537 (PFNRT)reconfigureMediumAttachment,
5538 13,
5539 this,
5540 ptrVM.raw(),
5541 pcszDevice,
5542 uInstance,
5543 enmBus,
5544 fUseHostIOCache,
5545 fBuiltinIoCache,
5546 false /* fSetupMerge */,
5547 0 /* uMergeSource */,
5548 0 /* uMergeTarget */,
5549 aMediumAttachment,
5550 mMachineState,
5551 &rc);
5552 /* error handling is after resuming the VM */
5553
5554 if (mMachineState == MachineState_DeletingSnapshotOnline)
5555 {
5556 LogFlowFunc(("Resuming the VM...\n"));
5557 /* disable the callback to prevent Console-level state change */
5558 mVMStateChangeCallbackDisabled = true;
5559 int vrc2 = VMR3Resume(ptrVM);
5560 mVMStateChangeCallbackDisabled = false;
5561 AssertRC(vrc2);
5562 if (RT_FAILURE(vrc2))
5563 {
5564 /* too bad, we failed. try to sync the console state with the VMM state */
5565 vmstateChangeCallback(ptrVM, VMSTATE_SUSPENDED, enmVMState, this);
5566 }
5567 }
5568
5569 if (RT_FAILURE(vrc))
5570 return setError(E_FAIL, tr("%Rrc"), vrc);
5571 if (FAILED(rc))
5572 return rc;
5573
5574 return rc;
5575}
5576
5577
5578/**
5579 * Gets called by Session::UpdateMachineState()
5580 * (IInternalSessionControl::updateMachineState()).
5581 *
5582 * Must be called only in certain cases (see the implementation).
5583 *
5584 * @note Locks this object for writing.
5585 */
5586HRESULT Console::updateMachineState(MachineState_T aMachineState)
5587{
5588 AutoCaller autoCaller(this);
5589 AssertComRCReturnRC(autoCaller.rc());
5590
5591 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5592
5593 AssertReturn( mMachineState == MachineState_Saving
5594 || mMachineState == MachineState_LiveSnapshotting
5595 || mMachineState == MachineState_RestoringSnapshot
5596 || mMachineState == MachineState_DeletingSnapshot
5597 || mMachineState == MachineState_DeletingSnapshotOnline
5598 || mMachineState == MachineState_DeletingSnapshotPaused
5599 , E_FAIL);
5600
5601 return setMachineStateLocally(aMachineState);
5602}
5603
5604/**
5605 * @note Locks this object for writing.
5606 */
5607void Console::onMousePointerShapeChange(bool fVisible, bool fAlpha,
5608 uint32_t xHot, uint32_t yHot,
5609 uint32_t width, uint32_t height,
5610 ComSafeArrayIn(BYTE,pShape))
5611{
5612#if 0
5613 LogFlowThisFuncEnter();
5614 LogFlowThisFunc(("fVisible=%d, fAlpha=%d, xHot = %d, yHot = %d, width=%d, height=%d, shape=%p\n",
5615 fVisible, fAlpha, xHot, yHot, width, height, pShape));
5616#endif
5617
5618 AutoCaller autoCaller(this);
5619 AssertComRCReturnVoid(autoCaller.rc());
5620
5621 /* We need a write lock because we alter the cached callback data */
5622 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5623
5624 /* Save the callback arguments */
5625 mCallbackData.mpsc.visible = fVisible;
5626 mCallbackData.mpsc.alpha = fAlpha;
5627 mCallbackData.mpsc.xHot = xHot;
5628 mCallbackData.mpsc.yHot = yHot;
5629 mCallbackData.mpsc.width = width;
5630 mCallbackData.mpsc.height = height;
5631
5632 /* start with not valid */
5633 bool wasValid = mCallbackData.mpsc.valid;
5634 mCallbackData.mpsc.valid = false;
5635
5636 com::SafeArray<BYTE> aShape(ComSafeArrayInArg(pShape));
5637 if (aShape.size() != 0)
5638 mCallbackData.mpsc.shape.initFrom(aShape);
5639 else
5640 mCallbackData.mpsc.shape.resize(0);
5641 mCallbackData.mpsc.valid = true;
5642
5643 fireMousePointerShapeChangedEvent(mEventSource, fVisible, fAlpha, xHot, yHot, width, height, ComSafeArrayInArg(pShape));
5644
5645#if 0
5646 LogFlowThisFuncLeave();
5647#endif
5648}
5649
5650/**
5651 * @note Locks this object for writing.
5652 */
5653void Console::onMouseCapabilityChange(BOOL supportsAbsolute, BOOL supportsRelative, BOOL needsHostCursor)
5654{
5655 LogFlowThisFunc(("supportsAbsolute=%d supportsRelative=%d needsHostCursor=%d\n",
5656 supportsAbsolute, supportsRelative, needsHostCursor));
5657
5658 AutoCaller autoCaller(this);
5659 AssertComRCReturnVoid(autoCaller.rc());
5660
5661 /* We need a write lock because we alter the cached callback data */
5662 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5663
5664 /* save the callback arguments */
5665 mCallbackData.mcc.supportsAbsolute = supportsAbsolute;
5666 mCallbackData.mcc.supportsRelative = supportsRelative;
5667 mCallbackData.mcc.needsHostCursor = needsHostCursor;
5668 mCallbackData.mcc.valid = true;
5669
5670 fireMouseCapabilityChangedEvent(mEventSource, supportsAbsolute, supportsRelative, needsHostCursor);
5671}
5672
5673/**
5674 * @note Locks this object for reading.
5675 */
5676void Console::onStateChange(MachineState_T machineState)
5677{
5678 AutoCaller autoCaller(this);
5679 AssertComRCReturnVoid(autoCaller.rc());
5680
5681 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5682 fireStateChangedEvent(mEventSource, machineState);
5683}
5684
5685/**
5686 * @note Locks this object for reading.
5687 */
5688void Console::onAdditionsStateChange()
5689{
5690 AutoCaller autoCaller(this);
5691 AssertComRCReturnVoid(autoCaller.rc());
5692
5693 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5694 fireAdditionsStateChangedEvent(mEventSource);
5695}
5696
5697/**
5698 * @note Locks this object for reading.
5699 * This notification only is for reporting an incompatible
5700 * Guest Additions interface, *not* the Guest Additions version!
5701 *
5702 * The user will be notified inside the guest if new Guest
5703 * Additions are available (via VBoxTray/VBoxClient).
5704 */
5705void Console::onAdditionsOutdated()
5706{
5707 AutoCaller autoCaller(this);
5708 AssertComRCReturnVoid(autoCaller.rc());
5709
5710 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5711}
5712
5713/**
5714 * @note Locks this object for writing.
5715 */
5716void Console::onKeyboardLedsChange(bool fNumLock, bool fCapsLock, bool fScrollLock)
5717{
5718 AutoCaller autoCaller(this);
5719 AssertComRCReturnVoid(autoCaller.rc());
5720
5721 /* We need a write lock because we alter the cached callback data */
5722 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5723
5724 /* save the callback arguments */
5725 mCallbackData.klc.numLock = fNumLock;
5726 mCallbackData.klc.capsLock = fCapsLock;
5727 mCallbackData.klc.scrollLock = fScrollLock;
5728 mCallbackData.klc.valid = true;
5729
5730 fireKeyboardLedsChangedEvent(mEventSource, fNumLock, fCapsLock, fScrollLock);
5731}
5732
5733/**
5734 * @note Locks this object for reading.
5735 */
5736void Console::onUSBDeviceStateChange(IUSBDevice *aDevice, bool aAttached,
5737 IVirtualBoxErrorInfo *aError)
5738{
5739 AutoCaller autoCaller(this);
5740 AssertComRCReturnVoid(autoCaller.rc());
5741
5742 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5743 fireUSBDeviceStateChangedEvent(mEventSource, aDevice, aAttached, aError);
5744}
5745
5746/**
5747 * @note Locks this object for reading.
5748 */
5749void Console::onRuntimeError(BOOL aFatal, IN_BSTR aErrorID, IN_BSTR aMessage)
5750{
5751 AutoCaller autoCaller(this);
5752 AssertComRCReturnVoid(autoCaller.rc());
5753
5754 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5755 fireRuntimeErrorEvent(mEventSource, aFatal, aErrorID, aMessage);
5756}
5757
5758/**
5759 * @note Locks this object for reading.
5760 */
5761HRESULT Console::onShowWindow(BOOL aCheck, BOOL *aCanShow, LONG64 *aWinId)
5762{
5763 AssertReturn(aCanShow, E_POINTER);
5764 AssertReturn(aWinId, E_POINTER);
5765
5766 *aCanShow = FALSE;
5767 *aWinId = 0;
5768
5769 AutoCaller autoCaller(this);
5770 AssertComRCReturnRC(autoCaller.rc());
5771
5772 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5773 VBoxEventDesc evDesc;
5774
5775 if (aCheck)
5776 {
5777 evDesc.init(mEventSource, VBoxEventType_OnCanShowWindow);
5778 BOOL fDelivered = evDesc.fire(5000); /* Wait up to 5 secs for delivery */
5779 //Assert(fDelivered);
5780 if (fDelivered)
5781 {
5782 ComPtr<IEvent> pEvent;
5783 evDesc.getEvent(pEvent.asOutParam());
5784 // bit clumsy
5785 ComPtr<ICanShowWindowEvent> pCanShowEvent = pEvent;
5786 if (pCanShowEvent)
5787 {
5788 BOOL fVetoed = FALSE;
5789 pCanShowEvent->IsVetoed(&fVetoed);
5790 *aCanShow = !fVetoed;
5791 }
5792 else
5793 {
5794 Assert(FALSE);
5795 *aCanShow = TRUE;
5796 }
5797 }
5798 else
5799 *aCanShow = TRUE;
5800 }
5801 else
5802 {
5803 evDesc.init(mEventSource, VBoxEventType_OnShowWindow, INT64_C(0));
5804 BOOL fDelivered = evDesc.fire(5000); /* Wait up to 5 secs for delivery */
5805 //Assert(fDelivered);
5806 if (fDelivered)
5807 {
5808 ComPtr<IEvent> pEvent;
5809 evDesc.getEvent(pEvent.asOutParam());
5810 ComPtr<IShowWindowEvent> pShowEvent = pEvent;
5811 LONG64 aEvWinId = 0;
5812 if (pShowEvent)
5813 {
5814 pShowEvent->COMGETTER(WinId)(&aEvWinId);
5815 if ((aEvWinId != 0) && (*aWinId == 0))
5816 *aWinId = aEvWinId;
5817 }
5818 else
5819 Assert(FALSE);
5820 }
5821 }
5822
5823 return S_OK;
5824}
5825
5826// private methods
5827////////////////////////////////////////////////////////////////////////////////
5828
5829/**
5830 * Increases the usage counter of the mpVM pointer. Guarantees that
5831 * VMR3Destroy() will not be called on it at least until releaseVMCaller()
5832 * is called.
5833 *
5834 * If this method returns a failure, the caller is not allowed to use mpVM
5835 * and may return the failed result code to the upper level. This method sets
5836 * the extended error info on failure if \a aQuiet is false.
5837 *
5838 * Setting \a aQuiet to true is useful for methods that don't want to return
5839 * the failed result code to the caller when this method fails (e.g. need to
5840 * silently check for the mpVM availability).
5841 *
5842 * When mpVM is NULL but \a aAllowNullVM is true, a corresponding error will be
5843 * returned instead of asserting. Having it false is intended as a sanity check
5844 * for methods that have checked mMachineState and expect mpVM *NOT* to be NULL.
5845 *
5846 * @param aQuiet true to suppress setting error info
5847 * @param aAllowNullVM true to accept mpVM being NULL and return a failure
5848 * (otherwise this method will assert if mpVM is NULL)
5849 *
5850 * @note Locks this object for writing.
5851 */
5852HRESULT Console::addVMCaller(bool aQuiet /* = false */,
5853 bool aAllowNullVM /* = false */)
5854{
5855 AutoCaller autoCaller(this);
5856 AssertComRCReturnRC(autoCaller.rc());
5857
5858 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5859
5860 if (mVMDestroying)
5861 {
5862 /* powerDown() is waiting for all callers to finish */
5863 return aQuiet ? E_ACCESSDENIED : setError(E_ACCESSDENIED,
5864 tr("The virtual machine is being powered down"));
5865 }
5866
5867 if (mpUVM == NULL)
5868 {
5869 Assert(aAllowNullVM == true);
5870
5871 /* The machine is not powered up */
5872 return aQuiet ? E_ACCESSDENIED : setError(E_ACCESSDENIED,
5873 tr("The virtual machine is not powered up"));
5874 }
5875
5876 ++mVMCallers;
5877
5878 return S_OK;
5879}
5880
5881/**
5882 * Decreases the usage counter of the mpVM pointer. Must always complete
5883 * the addVMCaller() call after the mpVM pointer is no more necessary.
5884 *
5885 * @note Locks this object for writing.
5886 */
5887void Console::releaseVMCaller()
5888{
5889 AutoCaller autoCaller(this);
5890 AssertComRCReturnVoid(autoCaller.rc());
5891
5892 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5893
5894 AssertReturnVoid(mpUVM != NULL);
5895
5896 Assert(mVMCallers > 0);
5897 --mVMCallers;
5898
5899 if (mVMCallers == 0 && mVMDestroying)
5900 {
5901 /* inform powerDown() there are no more callers */
5902 RTSemEventSignal(mVMZeroCallersSem);
5903 }
5904}
5905
5906
5907HRESULT Console::safeVMPtrRetainer(PVM *a_ppVM, PUVM *a_ppUVM, bool a_Quiet)
5908{
5909 *a_ppVM = NULL;
5910 *a_ppUVM = NULL;
5911
5912 AutoCaller autoCaller(this);
5913 AssertComRCReturnRC(autoCaller.rc());
5914 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5915
5916 /*
5917 * Repeat the checks done by addVMCaller.
5918 */
5919 if (mVMDestroying) /* powerDown() is waiting for all callers to finish */
5920 return a_Quiet
5921 ? E_ACCESSDENIED
5922 : setError(E_ACCESSDENIED, tr("The virtual machine is being powered down"));
5923 PUVM pUVM = mpUVM;
5924 if (!pUVM)
5925 return a_Quiet
5926 ? E_ACCESSDENIED
5927 : setError(E_ACCESSDENIED, tr("The virtual machine is was powered off"));
5928
5929 /*
5930 * Retain a reference to the user mode VM handle and get the global handle.
5931 */
5932 uint32_t cRefs = VMR3RetainUVM(pUVM);
5933 if (cRefs == UINT32_MAX)
5934 return a_Quiet
5935 ? E_ACCESSDENIED
5936 : setError(E_ACCESSDENIED, tr("The virtual machine is was powered off"));
5937
5938 PVM pVM = VMR3GetVM(pUVM);
5939 if (!pVM)
5940 {
5941 VMR3ReleaseUVM(pUVM);
5942 return a_Quiet
5943 ? E_ACCESSDENIED
5944 : setError(E_ACCESSDENIED, tr("The virtual machine is was powered off"));
5945 }
5946
5947 /* done */
5948 *a_ppVM = pVM;
5949 *a_ppUVM = pUVM;
5950 return S_OK;
5951}
5952
5953void Console::safeVMPtrReleaser(PVM *a_ppVM, PUVM *a_ppUVM)
5954{
5955 if (*a_ppVM && *a_ppUVM)
5956 VMR3ReleaseUVM(*a_ppUVM);
5957 *a_ppVM = NULL;
5958 *a_ppUVM = NULL;
5959}
5960
5961
5962/**
5963 * Initialize the release logging facility. In case something
5964 * goes wrong, there will be no release logging. Maybe in the future
5965 * we can add some logic to use different file names in this case.
5966 * Note that the logic must be in sync with Machine::DeleteSettings().
5967 */
5968HRESULT Console::consoleInitReleaseLog(const ComPtr<IMachine> aMachine)
5969{
5970 HRESULT hrc = S_OK;
5971
5972 Bstr logFolder;
5973 hrc = aMachine->COMGETTER(LogFolder)(logFolder.asOutParam());
5974 if (FAILED(hrc)) return hrc;
5975
5976 Utf8Str logDir = logFolder;
5977
5978 /* make sure the Logs folder exists */
5979 Assert(logDir.length());
5980 if (!RTDirExists(logDir.c_str()))
5981 RTDirCreateFullPath(logDir.c_str(), 0777);
5982
5983 Utf8Str logFile = Utf8StrFmt("%s%cVBox.log",
5984 logDir.c_str(), RTPATH_DELIMITER);
5985 Utf8Str pngFile = Utf8StrFmt("%s%cVBox.png",
5986 logDir.c_str(), RTPATH_DELIMITER);
5987
5988 /*
5989 * Age the old log files
5990 * Rename .(n-1) to .(n), .(n-2) to .(n-1), ..., and the last log file to .1
5991 * Overwrite target files in case they exist.
5992 */
5993 ComPtr<IVirtualBox> pVirtualBox;
5994 aMachine->COMGETTER(Parent)(pVirtualBox.asOutParam());
5995 ComPtr<ISystemProperties> pSystemProperties;
5996 pVirtualBox->COMGETTER(SystemProperties)(pSystemProperties.asOutParam());
5997 ULONG cHistoryFiles = 3;
5998 pSystemProperties->COMGETTER(LogHistoryCount)(&cHistoryFiles);
5999 if (cHistoryFiles)
6000 {
6001 for (int i = cHistoryFiles-1; i >= 0; i--)
6002 {
6003 Utf8Str *files[] = { &logFile, &pngFile };
6004 Utf8Str oldName, newName;
6005
6006 for (unsigned int j = 0; j < RT_ELEMENTS(files); ++ j)
6007 {
6008 if (i > 0)
6009 oldName = Utf8StrFmt("%s.%d", files[j]->c_str(), i);
6010 else
6011 oldName = *files[j];
6012 newName = Utf8StrFmt("%s.%d", files[j]->c_str(), i + 1);
6013 /* If the old file doesn't exist, delete the new file (if it
6014 * exists) to provide correct rotation even if the sequence is
6015 * broken */
6016 if ( RTFileRename(oldName.c_str(), newName.c_str(), RTFILEMOVE_FLAGS_REPLACE)
6017 == VERR_FILE_NOT_FOUND)
6018 RTFileDelete(newName.c_str());
6019 }
6020 }
6021 }
6022
6023 static const char * const s_apszGroups[] = VBOX_LOGGROUP_NAMES;
6024 char szError[RTPATH_MAX + 128] = "";
6025 PRTLOGGER pReleaseLogger;
6026 uint32_t fFlags = RTLOGFLAGS_PREFIX_TIME_PROG | RTLOGFLAGS_RESTRICT_GROUPS;
6027#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
6028 fFlags |= RTLOGFLAGS_USECRLF;
6029#endif
6030 int vrc = RTLogCreateEx(&pReleaseLogger, fFlags, "all all.restrict default.unrestricted",
6031 "VBOX_RELEASE_LOG", RT_ELEMENTS(s_apszGroups), s_apszGroups, RTLOGDEST_FILE,
6032 NULL /* pfnBeginEnd */, 0 /* cHistory */, 0 /* cbHistoryFileMax */, 0 /* uHistoryTimeMax */,
6033 szError, sizeof(szError), logFile.c_str());
6034 if (RT_SUCCESS(vrc))
6035 {
6036 RTLogSetGroupLimit(pReleaseLogger, 32768);
6037
6038 /* some introductory information */
6039 RTTIMESPEC timeSpec;
6040 char szTmp[256];
6041 RTTimeSpecToString(RTTimeNow(&timeSpec), szTmp, sizeof(szTmp));
6042 RTLogRelLogger(pReleaseLogger, 0, ~0U,
6043 "VirtualBox %s r%u %s (%s %s) release log\n"
6044#ifdef VBOX_BLEEDING_EDGE
6045 "EXPERIMENTAL build " VBOX_BLEEDING_EDGE "\n"
6046#endif
6047 "Log opened %s\n",
6048 VBOX_VERSION_STRING, RTBldCfgRevision(), VBOX_BUILD_TARGET,
6049 __DATE__, __TIME__, szTmp);
6050
6051 vrc = RTSystemQueryOSInfo(RTSYSOSINFO_PRODUCT, szTmp, sizeof(szTmp));
6052 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
6053 RTLogRelLogger(pReleaseLogger, 0, ~0U, "OS Product: %s\n", szTmp);
6054 vrc = RTSystemQueryOSInfo(RTSYSOSINFO_RELEASE, szTmp, sizeof(szTmp));
6055 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
6056 RTLogRelLogger(pReleaseLogger, 0, ~0U, "OS Release: %s\n", szTmp);
6057 vrc = RTSystemQueryOSInfo(RTSYSOSINFO_VERSION, szTmp, sizeof(szTmp));
6058 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
6059 RTLogRelLogger(pReleaseLogger, 0, ~0U, "OS Version: %s\n", szTmp);
6060 vrc = RTSystemQueryOSInfo(RTSYSOSINFO_SERVICE_PACK, szTmp, sizeof(szTmp));
6061 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
6062 RTLogRelLogger(pReleaseLogger, 0, ~0U, "OS Service Pack: %s\n", szTmp);
6063 vrc = RTSystemQueryDmiString(RTSYSDMISTR_PRODUCT_NAME, szTmp, sizeof(szTmp));
6064 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
6065 RTLogRelLogger(pReleaseLogger, 0, ~0U, "DMI Product Name: %s\n", szTmp);
6066 vrc = RTSystemQueryDmiString(RTSYSDMISTR_PRODUCT_VERSION, szTmp, sizeof(szTmp));
6067 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
6068 RTLogRelLogger(pReleaseLogger, 0, ~0U, "DMI Product Version: %s\n", szTmp);
6069
6070 ComPtr<IHost> pHost;
6071 pVirtualBox->COMGETTER(Host)(pHost.asOutParam());
6072 ULONG cMbHostRam = 0;
6073 ULONG cMbHostRamAvail = 0;
6074 pHost->COMGETTER(MemorySize)(&cMbHostRam);
6075 pHost->COMGETTER(MemoryAvailable)(&cMbHostRamAvail);
6076 RTLogRelLogger(pReleaseLogger, 0, ~0U, "Host RAM: %uMB RAM, available: %uMB\n",
6077 cMbHostRam, cMbHostRamAvail);
6078
6079 /* the package type is interesting for Linux distributions */
6080 char szExecName[RTPATH_MAX];
6081 char *pszExecName = RTProcGetExecutablePath(szExecName, sizeof(szExecName));
6082 RTLogRelLogger(pReleaseLogger, 0, ~0U,
6083 "Executable: %s\n"
6084 "Process ID: %u\n"
6085 "Package type: %s"
6086#ifdef VBOX_OSE
6087 " (OSE)"
6088#endif
6089 "\n",
6090 pszExecName ? pszExecName : "unknown",
6091 RTProcSelf(),
6092 VBOX_PACKAGE_STRING);
6093
6094 /* register this logger as the release logger */
6095 RTLogRelSetDefaultInstance(pReleaseLogger);
6096 hrc = S_OK;
6097
6098 /* Explicitly flush the log in case of VBOX_RELEASE_LOG=buffered. */
6099 RTLogFlush(pReleaseLogger);
6100 }
6101 else
6102 hrc = setError(E_FAIL,
6103 tr("Failed to open release log (%s, %Rrc)"),
6104 szError, vrc);
6105
6106 /* If we've made any directory changes, flush the directory to increase
6107 the likelihood that the log file will be usable after a system panic.
6108
6109 Tip: Try 'export VBOX_RELEASE_LOG_FLAGS=flush' if the last bits of the log
6110 is missing. Just don't have too high hopes for this to help. */
6111 if (SUCCEEDED(hrc) || cHistoryFiles)
6112 RTDirFlush(logDir.c_str());
6113
6114 return hrc;
6115}
6116
6117/**
6118 * Common worker for PowerUp and PowerUpPaused.
6119 *
6120 * @returns COM status code.
6121 *
6122 * @param aProgress Where to return the progress object.
6123 * @param aPaused true if PowerUpPaused called.
6124 */
6125HRESULT Console::powerUp(IProgress **aProgress, bool aPaused)
6126{
6127 LogFlowThisFuncEnter();
6128 LogFlowThisFunc(("mMachineState=%d\n", mMachineState));
6129
6130 CheckComArgOutPointerValid(aProgress);
6131
6132 AutoCaller autoCaller(this);
6133 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6134
6135 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6136
6137 HRESULT rc = S_OK;
6138 ComObjPtr<Progress> pPowerupProgress;
6139 bool fBeganPoweringUp = false;
6140
6141 try
6142 {
6143 if (Global::IsOnlineOrTransient(mMachineState))
6144 throw setError(VBOX_E_INVALID_VM_STATE,
6145 tr("The virtual machine is already running or busy (machine state: %s)"),
6146 Global::stringifyMachineState(mMachineState));
6147
6148 /* test and clear the TeleporterEnabled property */
6149 BOOL fTeleporterEnabled;
6150 rc = mMachine->COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
6151 if (FAILED(rc))
6152 throw rc;
6153#if 0 /** @todo we should save it afterwards, but that isn't necessarily a good idea. Find a better place for this (VBoxSVC). */
6154 if (fTeleporterEnabled)
6155 {
6156 rc = mMachine->COMSETTER(TeleporterEnabled)(FALSE);
6157 if (FAILED(rc))
6158 throw rc;
6159 }
6160#endif
6161
6162 /* test the FaultToleranceState property */
6163 FaultToleranceState_T enmFaultToleranceState;
6164 rc = mMachine->COMGETTER(FaultToleranceState)(&enmFaultToleranceState);
6165 if (FAILED(rc))
6166 throw rc;
6167 BOOL fFaultToleranceSyncEnabled = (enmFaultToleranceState == FaultToleranceState_Standby);
6168
6169 /* Create a progress object to track progress of this operation. Must
6170 * be done as early as possible (together with BeginPowerUp()) as this
6171 * is vital for communicating as much as possible early powerup
6172 * failure information to the API caller */
6173 pPowerupProgress.createObject();
6174 Bstr progressDesc;
6175 if (mMachineState == MachineState_Saved)
6176 progressDesc = tr("Restoring virtual machine");
6177 else if (fTeleporterEnabled)
6178 progressDesc = tr("Teleporting virtual machine");
6179 else if (fFaultToleranceSyncEnabled)
6180 progressDesc = tr("Fault Tolerance syncing of remote virtual machine");
6181 else
6182 progressDesc = tr("Starting virtual machine");
6183 if ( mMachineState == MachineState_Saved
6184 || (!fTeleporterEnabled && !fFaultToleranceSyncEnabled))
6185 rc = pPowerupProgress->init(static_cast<IConsole *>(this),
6186 progressDesc.raw(),
6187 FALSE /* aCancelable */);
6188 else
6189 if (fTeleporterEnabled)
6190 rc = pPowerupProgress->init(static_cast<IConsole *>(this),
6191 progressDesc.raw(),
6192 TRUE /* aCancelable */,
6193 3 /* cOperations */,
6194 10 /* ulTotalOperationsWeight */,
6195 Bstr(tr("Teleporting virtual machine")).raw(),
6196 1 /* ulFirstOperationWeight */,
6197 NULL);
6198 else
6199 if (fFaultToleranceSyncEnabled)
6200 rc = pPowerupProgress->init(static_cast<IConsole *>(this),
6201 progressDesc.raw(),
6202 TRUE /* aCancelable */,
6203 3 /* cOperations */,
6204 10 /* ulTotalOperationsWeight */,
6205 Bstr(tr("Fault Tolerance syncing of remote virtual machine")).raw(),
6206 1 /* ulFirstOperationWeight */,
6207 NULL);
6208
6209 if (FAILED(rc))
6210 throw rc;
6211
6212 /* Tell VBoxSVC and Machine about the progress object so they can
6213 combine/proxy it to any openRemoteSession caller. */
6214 LogFlowThisFunc(("Calling BeginPowerUp...\n"));
6215 rc = mControl->BeginPowerUp(pPowerupProgress);
6216 if (FAILED(rc))
6217 {
6218 LogFlowThisFunc(("BeginPowerUp failed\n"));
6219 throw rc;
6220 }
6221 fBeganPoweringUp = true;
6222
6223 /** @todo this code prevents starting a VM with unavailable bridged
6224 * networking interface. The only benefit is a slightly better error
6225 * message, which should be moved to the driver code. This is the
6226 * only reason why I left the code in for now. The driver allows
6227 * unavailable bridged networking interfaces in certain circumstances,
6228 * and this is sabotaged by this check. The VM will initially have no
6229 * network connectivity, but the user can fix this at runtime. */
6230#if 0
6231 /* the network cards will undergo a quick consistency check */
6232 for (ULONG slot = 0;
6233 slot < SchemaDefs::NetworkAdapterCount;
6234 ++slot)
6235 {
6236 ComPtr<INetworkAdapter> pNetworkAdapter;
6237 mMachine->GetNetworkAdapter(slot, pNetworkAdapter.asOutParam());
6238 BOOL enabled = FALSE;
6239 pNetworkAdapter->COMGETTER(Enabled)(&enabled);
6240 if (!enabled)
6241 continue;
6242
6243 NetworkAttachmentType_T netattach;
6244 pNetworkAdapter->COMGETTER(AttachmentType)(&netattach);
6245 switch (netattach)
6246 {
6247 case NetworkAttachmentType_Bridged:
6248 {
6249 /* a valid host interface must have been set */
6250 Bstr hostif;
6251 pNetworkAdapter->COMGETTER(HostInterface)(hostif.asOutParam());
6252 if (hostif.isEmpty())
6253 {
6254 throw setError(VBOX_E_HOST_ERROR,
6255 tr("VM cannot start because host interface networking requires a host interface name to be set"));
6256 }
6257 ComPtr<IVirtualBox> pVirtualBox;
6258 mMachine->COMGETTER(Parent)(pVirtualBox.asOutParam());
6259 ComPtr<IHost> pHost;
6260 pVirtualBox->COMGETTER(Host)(pHost.asOutParam());
6261 ComPtr<IHostNetworkInterface> pHostInterface;
6262 if (!SUCCEEDED(pHost->FindHostNetworkInterfaceByName(hostif.raw(),
6263 pHostInterface.asOutParam())))
6264 {
6265 throw setError(VBOX_E_HOST_ERROR,
6266 tr("VM cannot start because the host interface '%ls' does not exist"),
6267 hostif.raw());
6268 }
6269 break;
6270 }
6271 default:
6272 break;
6273 }
6274 }
6275#endif // 0
6276
6277 /* Read console data stored in the saved state file (if not yet done) */
6278 rc = loadDataFromSavedState();
6279 if (FAILED(rc))
6280 throw rc;
6281
6282 /* Check all types of shared folders and compose a single list */
6283 SharedFolderDataMap sharedFolders;
6284 {
6285 /* first, insert global folders */
6286 for (SharedFolderDataMap::const_iterator it = m_mapGlobalSharedFolders.begin();
6287 it != m_mapGlobalSharedFolders.end();
6288 ++it)
6289 {
6290 const SharedFolderData &d = it->second;
6291 sharedFolders[it->first] = d;
6292 }
6293
6294 /* second, insert machine folders */
6295 for (SharedFolderDataMap::const_iterator it = m_mapMachineSharedFolders.begin();
6296 it != m_mapMachineSharedFolders.end();
6297 ++it)
6298 {
6299 const SharedFolderData &d = it->second;
6300 sharedFolders[it->first] = d;
6301 }
6302
6303 /* third, insert console folders */
6304 for (SharedFolderMap::const_iterator it = m_mapSharedFolders.begin();
6305 it != m_mapSharedFolders.end();
6306 ++it)
6307 {
6308 SharedFolder *pSF = it->second;
6309 AutoCaller sfCaller(pSF);
6310 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
6311 sharedFolders[it->first] = SharedFolderData(pSF->getHostPath(),
6312 pSF->isWritable(),
6313 pSF->isAutoMounted());
6314 }
6315 }
6316
6317 Bstr savedStateFile;
6318
6319 /*
6320 * Saved VMs will have to prove that their saved states seem kosher.
6321 */
6322 if (mMachineState == MachineState_Saved)
6323 {
6324 rc = mMachine->COMGETTER(StateFilePath)(savedStateFile.asOutParam());
6325 if (FAILED(rc))
6326 throw rc;
6327 ComAssertRet(!savedStateFile.isEmpty(), E_FAIL);
6328 int vrc = SSMR3ValidateFile(Utf8Str(savedStateFile).c_str(), false /* fChecksumIt */);
6329 if (RT_FAILURE(vrc))
6330 throw setError(VBOX_E_FILE_ERROR,
6331 tr("VM cannot start because the saved state file '%ls' is invalid (%Rrc). Delete the saved state prior to starting the VM"),
6332 savedStateFile.raw(), vrc);
6333 }
6334
6335 LogFlowThisFunc(("Checking if canceled...\n"));
6336 BOOL fCanceled;
6337 rc = pPowerupProgress->COMGETTER(Canceled)(&fCanceled);
6338 if (FAILED(rc))
6339 throw rc;
6340 if (fCanceled)
6341 {
6342 LogFlowThisFunc(("Canceled in BeginPowerUp\n"));
6343 throw setError(E_FAIL, tr("Powerup was canceled"));
6344 }
6345 LogFlowThisFunc(("Not canceled yet.\n"));
6346
6347 /* setup task object and thread to carry out the operation
6348 * asynchronously */
6349
6350 std::auto_ptr<VMPowerUpTask> task(new VMPowerUpTask(this, pPowerupProgress));
6351 ComAssertComRCRetRC(task->rc());
6352
6353 task->mConfigConstructor = configConstructor;
6354 task->mSharedFolders = sharedFolders;
6355 task->mStartPaused = aPaused;
6356 if (mMachineState == MachineState_Saved)
6357 task->mSavedStateFile = savedStateFile;
6358 task->mTeleporterEnabled = fTeleporterEnabled;
6359 task->mEnmFaultToleranceState = enmFaultToleranceState;
6360
6361 /* Reset differencing hard disks for which autoReset is true,
6362 * but only if the machine has no snapshots OR the current snapshot
6363 * is an OFFLINE snapshot; otherwise we would reset the current
6364 * differencing image of an ONLINE snapshot which contains the disk
6365 * state of the machine while it was previously running, but without
6366 * the corresponding machine state, which is equivalent to powering
6367 * off a running machine and not good idea
6368 */
6369 ComPtr<ISnapshot> pCurrentSnapshot;
6370 rc = mMachine->COMGETTER(CurrentSnapshot)(pCurrentSnapshot.asOutParam());
6371 if (FAILED(rc))
6372 throw rc;
6373
6374 BOOL fCurrentSnapshotIsOnline = false;
6375 if (pCurrentSnapshot)
6376 {
6377 rc = pCurrentSnapshot->COMGETTER(Online)(&fCurrentSnapshotIsOnline);
6378 if (FAILED(rc))
6379 throw rc;
6380 }
6381
6382 if (!fCurrentSnapshotIsOnline)
6383 {
6384 LogFlowThisFunc(("Looking for immutable images to reset\n"));
6385
6386 com::SafeIfaceArray<IMediumAttachment> atts;
6387 rc = mMachine->COMGETTER(MediumAttachments)(ComSafeArrayAsOutParam(atts));
6388 if (FAILED(rc))
6389 throw rc;
6390
6391 for (size_t i = 0;
6392 i < atts.size();
6393 ++i)
6394 {
6395 DeviceType_T devType;
6396 rc = atts[i]->COMGETTER(Type)(&devType);
6397 /** @todo later applies to floppies as well */
6398 if (devType == DeviceType_HardDisk)
6399 {
6400 ComPtr<IMedium> pMedium;
6401 rc = atts[i]->COMGETTER(Medium)(pMedium.asOutParam());
6402 if (FAILED(rc))
6403 throw rc;
6404
6405 /* needs autoreset? */
6406 BOOL autoReset = FALSE;
6407 rc = pMedium->COMGETTER(AutoReset)(&autoReset);
6408 if (FAILED(rc))
6409 throw rc;
6410
6411 if (autoReset)
6412 {
6413 ComPtr<IProgress> pResetProgress;
6414 rc = pMedium->Reset(pResetProgress.asOutParam());
6415 if (FAILED(rc))
6416 throw rc;
6417
6418 /* save for later use on the powerup thread */
6419 task->hardDiskProgresses.push_back(pResetProgress);
6420 }
6421 }
6422 }
6423 }
6424 else
6425 LogFlowThisFunc(("Machine has a current snapshot which is online, skipping immutable images reset\n"));
6426
6427 rc = consoleInitReleaseLog(mMachine);
6428 if (FAILED(rc))
6429 throw rc;
6430 mptrExtPackManager->dumpAllToReleaseLog();
6431
6432#ifdef RT_OS_SOLARIS
6433 /* setup host core dumper for the VM */
6434 Bstr value;
6435 HRESULT hrc = mMachine->GetExtraData(Bstr("VBoxInternal2/CoreDumpEnabled").raw(), value.asOutParam());
6436 if (SUCCEEDED(hrc) && value == "1")
6437 {
6438 Bstr coreDumpDir, coreDumpReplaceSys, coreDumpLive;
6439 mMachine->GetExtraData(Bstr("VBoxInternal2/CoreDumpDir").raw(), coreDumpDir.asOutParam());
6440 mMachine->GetExtraData(Bstr("VBoxInternal2/CoreDumpReplaceSystemDump").raw(), coreDumpReplaceSys.asOutParam());
6441 mMachine->GetExtraData(Bstr("VBoxInternal2/CoreDumpLive").raw(), coreDumpLive.asOutParam());
6442
6443 uint32_t fCoreFlags = 0;
6444 if ( coreDumpReplaceSys.isEmpty() == false
6445 && Utf8Str(coreDumpReplaceSys).toUInt32() == 1)
6446 {
6447 fCoreFlags |= RTCOREDUMPER_FLAGS_REPLACE_SYSTEM_DUMP;
6448 }
6449
6450 if ( coreDumpLive.isEmpty() == false
6451 && Utf8Str(coreDumpLive).toUInt32() == 1)
6452 {
6453 fCoreFlags |= RTCOREDUMPER_FLAGS_LIVE_CORE;
6454 }
6455
6456 Utf8Str strDumpDir(coreDumpDir);
6457 const char *pszDumpDir = strDumpDir.c_str();
6458 if ( pszDumpDir
6459 && *pszDumpDir == '\0')
6460 pszDumpDir = NULL;
6461
6462 int vrc;
6463 if ( pszDumpDir
6464 && !RTDirExists(pszDumpDir))
6465 {
6466 /*
6467 * Try create the directory.
6468 */
6469 vrc = RTDirCreateFullPath(pszDumpDir, 0777);
6470 if (RT_FAILURE(vrc))
6471 throw setError(E_FAIL, "Failed to setup CoreDumper. Couldn't create dump directory '%s' (%Rrc)\n", pszDumpDir, vrc);
6472 }
6473
6474 vrc = RTCoreDumperSetup(pszDumpDir, fCoreFlags);
6475 if (RT_FAILURE(vrc))
6476 throw setError(E_FAIL, "Failed to setup CoreDumper (%Rrc)", vrc);
6477 else
6478 LogRel(("CoreDumper setup successful. pszDumpDir=%s fFlags=%#x\n", pszDumpDir ? pszDumpDir : ".", fCoreFlags));
6479 }
6480#endif
6481
6482 /* pass the progress object to the caller if requested */
6483 if (aProgress)
6484 {
6485 if (task->hardDiskProgresses.size() == 0)
6486 {
6487 /* there are no other operations to track, return the powerup
6488 * progress only */
6489 pPowerupProgress.queryInterfaceTo(aProgress);
6490 }
6491 else
6492 {
6493 /* create a combined progress object */
6494 ComObjPtr<CombinedProgress> pProgress;
6495 pProgress.createObject();
6496 VMPowerUpTask::ProgressList progresses(task->hardDiskProgresses);
6497 progresses.push_back(ComPtr<IProgress> (pPowerupProgress));
6498 rc = pProgress->init(static_cast<IConsole *>(this),
6499 progressDesc.raw(), progresses.begin(),
6500 progresses.end());
6501 AssertComRCReturnRC(rc);
6502 pProgress.queryInterfaceTo(aProgress);
6503 }
6504 }
6505
6506 int vrc = RTThreadCreate(NULL, Console::powerUpThread,
6507 (void *)task.get(), 0,
6508 RTTHREADTYPE_MAIN_WORKER, 0, "VMPowerUp");
6509 if (RT_FAILURE(vrc))
6510 throw setError(E_FAIL, "Could not create VMPowerUp thread (%Rrc)", vrc);
6511
6512 /* task is now owned by powerUpThread(), so release it */
6513 task.release();
6514
6515 /* finally, set the state: no right to fail in this method afterwards
6516 * since we've already started the thread and it is now responsible for
6517 * any error reporting and appropriate state change! */
6518 if (mMachineState == MachineState_Saved)
6519 setMachineState(MachineState_Restoring);
6520 else if (fTeleporterEnabled)
6521 setMachineState(MachineState_TeleportingIn);
6522 else if (enmFaultToleranceState == FaultToleranceState_Standby)
6523 setMachineState(MachineState_FaultTolerantSyncing);
6524 else
6525 setMachineState(MachineState_Starting);
6526 }
6527 catch (HRESULT aRC) { rc = aRC; }
6528
6529 if (FAILED(rc) && fBeganPoweringUp)
6530 {
6531
6532 /* The progress object will fetch the current error info */
6533 if (!pPowerupProgress.isNull())
6534 pPowerupProgress->notifyComplete(rc);
6535
6536 /* Save the error info across the IPC below. Can't be done before the
6537 * progress notification above, as saving the error info deletes it
6538 * from the current context, and thus the progress object wouldn't be
6539 * updated correctly. */
6540 ErrorInfoKeeper eik;
6541
6542 /* signal end of operation */
6543 mControl->EndPowerUp(rc);
6544 }
6545
6546 LogFlowThisFunc(("mMachineState=%d, rc=%Rhrc\n", mMachineState, rc));
6547 LogFlowThisFuncLeave();
6548 return rc;
6549}
6550
6551/**
6552 * Internal power off worker routine.
6553 *
6554 * This method may be called only at certain places with the following meaning
6555 * as shown below:
6556 *
6557 * - if the machine state is either Running or Paused, a normal
6558 * Console-initiated powerdown takes place (e.g. PowerDown());
6559 * - if the machine state is Saving, saveStateThread() has successfully done its
6560 * job;
6561 * - if the machine state is Starting or Restoring, powerUpThread() has failed
6562 * to start/load the VM;
6563 * - if the machine state is Stopping, the VM has powered itself off (i.e. not
6564 * as a result of the powerDown() call).
6565 *
6566 * Calling it in situations other than the above will cause unexpected behavior.
6567 *
6568 * Note that this method should be the only one that destroys mpVM and sets it
6569 * to NULL.
6570 *
6571 * @param aProgress Progress object to run (may be NULL).
6572 *
6573 * @note Locks this object for writing.
6574 *
6575 * @note Never call this method from a thread that called addVMCaller() or
6576 * instantiated an AutoVMCaller object; first call releaseVMCaller() or
6577 * release(). Otherwise it will deadlock.
6578 */
6579HRESULT Console::powerDown(IProgress *aProgress /*= NULL*/)
6580{
6581 LogFlowThisFuncEnter();
6582
6583 AutoCaller autoCaller(this);
6584 AssertComRCReturnRC(autoCaller.rc());
6585
6586 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6587
6588 /* Total # of steps for the progress object. Must correspond to the
6589 * number of "advance percent count" comments in this method! */
6590 enum { StepCount = 7 };
6591 /* current step */
6592 ULONG step = 0;
6593
6594 HRESULT rc = S_OK;
6595 int vrc = VINF_SUCCESS;
6596
6597 /* sanity */
6598 Assert(mVMDestroying == false);
6599
6600 PUVM pUVM = mpUVM; Assert(pUVM != NULL);
6601 uint32_t cRefs = VMR3RetainUVM(pUVM); Assert(cRefs != UINT32_MAX);
6602
6603 AssertMsg( mMachineState == MachineState_Running
6604 || mMachineState == MachineState_Paused
6605 || mMachineState == MachineState_Stuck
6606 || mMachineState == MachineState_Starting
6607 || mMachineState == MachineState_Stopping
6608 || mMachineState == MachineState_Saving
6609 || mMachineState == MachineState_Restoring
6610 || mMachineState == MachineState_TeleportingPausedVM
6611 || mMachineState == MachineState_FaultTolerantSyncing
6612 || mMachineState == MachineState_TeleportingIn
6613 , ("Invalid machine state: %s\n", Global::stringifyMachineState(mMachineState)));
6614
6615 LogRel(("Console::powerDown(): A request to power off the VM has been issued (mMachineState=%s, InUninit=%d)\n",
6616 Global::stringifyMachineState(mMachineState), autoCaller.state() == InUninit));
6617
6618 /* Check if we need to power off the VM. In case of mVMPoweredOff=true, the
6619 * VM has already powered itself off in vmstateChangeCallback() and is just
6620 * notifying Console about that. In case of Starting or Restoring,
6621 * powerUpThread() is calling us on failure, so the VM is already off at
6622 * that point. */
6623 if ( !mVMPoweredOff
6624 && ( mMachineState == MachineState_Starting
6625 || mMachineState == MachineState_Restoring
6626 || mMachineState == MachineState_FaultTolerantSyncing
6627 || mMachineState == MachineState_TeleportingIn)
6628 )
6629 mVMPoweredOff = true;
6630
6631 /*
6632 * Go to Stopping state if not already there.
6633 *
6634 * Note that we don't go from Saving/Restoring to Stopping because
6635 * vmstateChangeCallback() needs it to set the state to Saved on
6636 * VMSTATE_TERMINATED. In terms of protecting from inappropriate operations
6637 * while leaving the lock below, Saving or Restoring should be fine too.
6638 * Ditto for TeleportingPausedVM -> Teleported.
6639 */
6640 if ( mMachineState != MachineState_Saving
6641 && mMachineState != MachineState_Restoring
6642 && mMachineState != MachineState_Stopping
6643 && mMachineState != MachineState_TeleportingIn
6644 && mMachineState != MachineState_TeleportingPausedVM
6645 && mMachineState != MachineState_FaultTolerantSyncing
6646 )
6647 setMachineState(MachineState_Stopping);
6648
6649 /* ----------------------------------------------------------------------
6650 * DONE with necessary state changes, perform the power down actions (it's
6651 * safe to leave the object lock now if needed)
6652 * ---------------------------------------------------------------------- */
6653
6654 /* Stop the VRDP server to prevent new clients connection while VM is being
6655 * powered off. */
6656 if (mConsoleVRDPServer)
6657 {
6658 LogFlowThisFunc(("Stopping VRDP server...\n"));
6659
6660 /* Leave the lock since EMT will call us back as addVMCaller()
6661 * in updateDisplayData(). */
6662 alock.leave();
6663
6664 mConsoleVRDPServer->Stop();
6665
6666 alock.enter();
6667 }
6668
6669 /* advance percent count */
6670 if (aProgress)
6671 aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
6672
6673
6674 /* ----------------------------------------------------------------------
6675 * Now, wait for all mpVM callers to finish their work if there are still
6676 * some on other threads. NO methods that need mpVM (or initiate other calls
6677 * that need it) may be called after this point
6678 * ---------------------------------------------------------------------- */
6679
6680 /* go to the destroying state to prevent from adding new callers */
6681 mVMDestroying = true;
6682
6683 if (mVMCallers > 0)
6684 {
6685 /* lazy creation */
6686 if (mVMZeroCallersSem == NIL_RTSEMEVENT)
6687 RTSemEventCreate(&mVMZeroCallersSem);
6688
6689 LogFlowThisFunc(("Waiting for mpVM callers (%d) to drop to zero...\n",
6690 mVMCallers));
6691
6692 alock.leave();
6693
6694 RTSemEventWait(mVMZeroCallersSem, RT_INDEFINITE_WAIT);
6695
6696 alock.enter();
6697 }
6698
6699 /* advance percent count */
6700 if (aProgress)
6701 aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
6702
6703 vrc = VINF_SUCCESS;
6704
6705 /*
6706 * Power off the VM if not already done that.
6707 * Leave the lock since EMT will call vmstateChangeCallback.
6708 *
6709 * Note that VMR3PowerOff() may fail here (invalid VMSTATE) if the
6710 * VM-(guest-)initiated power off happened in parallel a ms before this
6711 * call. So far, we let this error pop up on the user's side.
6712 */
6713 if (!mVMPoweredOff)
6714 {
6715 LogFlowThisFunc(("Powering off the VM...\n"));
6716 alock.leave();
6717 vrc = VMR3PowerOff(VMR3GetVM(pUVM));
6718#ifdef VBOX_WITH_EXTPACK
6719 mptrExtPackManager->callAllVmPowerOffHooks(this, VMR3GetVM(pUVM));
6720#endif
6721 alock.enter();
6722 }
6723
6724 /* advance percent count */
6725 if (aProgress)
6726 aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
6727
6728#ifdef VBOX_WITH_HGCM
6729 /* Shutdown HGCM services before destroying the VM. */
6730 if (m_pVMMDev)
6731 {
6732 LogFlowThisFunc(("Shutdown HGCM...\n"));
6733
6734 /* Leave the lock since EMT will call us back as addVMCaller() */
6735 alock.leave();
6736
6737 m_pVMMDev->hgcmShutdown();
6738
6739 alock.enter();
6740 }
6741
6742 /* advance percent count */
6743 if (aProgress)
6744 aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
6745
6746#endif /* VBOX_WITH_HGCM */
6747
6748 LogFlowThisFunc(("Ready for VM destruction.\n"));
6749
6750 /* If we are called from Console::uninit(), then try to destroy the VM even
6751 * on failure (this will most likely fail too, but what to do?..) */
6752 if (RT_SUCCESS(vrc) || autoCaller.state() == InUninit)
6753 {
6754 /* If the machine has an USB controller, release all USB devices
6755 * (symmetric to the code in captureUSBDevices()) */
6756 bool fHasUSBController = false;
6757 {
6758 PPDMIBASE pBase;
6759 vrc = PDMR3QueryLun(VMR3GetVM(pUVM), "usb-ohci", 0, 0, &pBase);
6760 if (RT_SUCCESS(vrc))
6761 {
6762 fHasUSBController = true;
6763 detachAllUSBDevices(false /* aDone */);
6764 }
6765 }
6766
6767 /* Now we've got to destroy the VM as well. (mpVM is not valid beyond
6768 * this point). We leave the lock before calling VMR3Destroy() because
6769 * it will result into calling destructors of drivers associated with
6770 * Console children which may in turn try to lock Console (e.g. by
6771 * instantiating SafeVMPtr to access mpVM). It's safe here because
6772 * mVMDestroying is set which should prevent any activity. */
6773
6774 /* Set mpUVM to NULL early just in case if some old code is not using
6775 * addVMCaller()/releaseVMCaller(). (We have our own ref on pUVM.) */
6776 VMR3ReleaseUVM(mpUVM);
6777 mpUVM = NULL;
6778
6779 LogFlowThisFunc(("Destroying the VM...\n"));
6780
6781 alock.leave();
6782
6783 vrc = VMR3Destroy(VMR3GetVM(pUVM));
6784
6785 /* take the lock again */
6786 alock.enter();
6787
6788 /* advance percent count */
6789 if (aProgress)
6790 aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
6791
6792 if (RT_SUCCESS(vrc))
6793 {
6794 LogFlowThisFunc(("Machine has been destroyed (mMachineState=%d)\n",
6795 mMachineState));
6796 /* Note: the Console-level machine state change happens on the
6797 * VMSTATE_TERMINATE state change in vmstateChangeCallback(). If
6798 * powerDown() is called from EMT (i.e. from vmstateChangeCallback()
6799 * on receiving VM-initiated VMSTATE_OFF), VMSTATE_TERMINATE hasn't
6800 * occurred yet. This is okay, because mMachineState is already
6801 * Stopping in this case, so any other attempt to call PowerDown()
6802 * will be rejected. */
6803 }
6804 else
6805 {
6806 /* bad bad bad, but what to do? (Give Console our UVM ref.) */
6807 mpUVM = pUVM;
6808 pUVM = NULL;
6809 rc = setError(VBOX_E_VM_ERROR,
6810 tr("Could not destroy the machine. (Error: %Rrc)"),
6811 vrc);
6812 }
6813
6814 /* Complete the detaching of the USB devices. */
6815 if (fHasUSBController)
6816 detachAllUSBDevices(true /* aDone */);
6817
6818 /* advance percent count */
6819 if (aProgress)
6820 aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
6821 }
6822 else
6823 {
6824 rc = setError(VBOX_E_VM_ERROR,
6825 tr("Could not power off the machine. (Error: %Rrc)"),
6826 vrc);
6827 }
6828
6829 /*
6830 * Finished with the destruction.
6831 *
6832 * Note that if something impossible happened and we've failed to destroy
6833 * the VM, mVMDestroying will remain true and mMachineState will be
6834 * something like Stopping, so most Console methods will return an error
6835 * to the caller.
6836 */
6837 if (mpUVM != NULL)
6838 VMR3ReleaseUVM(pUVM);
6839 else
6840 mVMDestroying = false;
6841
6842 if (SUCCEEDED(rc))
6843 mCallbackData.clear();
6844
6845 LogFlowThisFuncLeave();
6846 return rc;
6847}
6848
6849/**
6850 * @note Locks this object for writing.
6851 */
6852HRESULT Console::setMachineState(MachineState_T aMachineState,
6853 bool aUpdateServer /* = true */)
6854{
6855 AutoCaller autoCaller(this);
6856 AssertComRCReturnRC(autoCaller.rc());
6857
6858 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6859
6860 HRESULT rc = S_OK;
6861
6862 if (mMachineState != aMachineState)
6863 {
6864 LogThisFunc(("machineState=%s -> %s aUpdateServer=%RTbool\n",
6865 Global::stringifyMachineState(mMachineState), Global::stringifyMachineState(aMachineState), aUpdateServer));
6866 mMachineState = aMachineState;
6867
6868 /// @todo (dmik)
6869 // possibly, we need to redo onStateChange() using the dedicated
6870 // Event thread, like it is done in VirtualBox. This will make it
6871 // much safer (no deadlocks possible if someone tries to use the
6872 // console from the callback), however, listeners will lose the
6873 // ability to synchronously react to state changes (is it really
6874 // necessary??)
6875 LogFlowThisFunc(("Doing onStateChange()...\n"));
6876 onStateChange(aMachineState);
6877 LogFlowThisFunc(("Done onStateChange()\n"));
6878
6879 if (aUpdateServer)
6880 {
6881 /* Server notification MUST be done from under the lock; otherwise
6882 * the machine state here and on the server might go out of sync
6883 * which can lead to various unexpected results (like the machine
6884 * state being >= MachineState_Running on the server, while the
6885 * session state is already SessionState_Unlocked at the same time
6886 * there).
6887 *
6888 * Cross-lock conditions should be carefully watched out: calling
6889 * UpdateState we will require Machine and SessionMachine locks
6890 * (remember that here we're holding the Console lock here, and also
6891 * all locks that have been entered by the thread before calling
6892 * this method).
6893 */
6894 LogFlowThisFunc(("Doing mControl->UpdateState()...\n"));
6895 rc = mControl->UpdateState(aMachineState);
6896 LogFlowThisFunc(("mControl->UpdateState()=%Rhrc\n", rc));
6897 }
6898 }
6899
6900 return rc;
6901}
6902
6903/**
6904 * Searches for a shared folder with the given logical name
6905 * in the collection of shared folders.
6906 *
6907 * @param aName logical name of the shared folder
6908 * @param aSharedFolder where to return the found object
6909 * @param aSetError whether to set the error info if the folder is
6910 * not found
6911 * @return
6912 * S_OK when found or E_INVALIDARG when not found
6913 *
6914 * @note The caller must lock this object for writing.
6915 */
6916HRESULT Console::findSharedFolder(const Utf8Str &strName,
6917 ComObjPtr<SharedFolder> &aSharedFolder,
6918 bool aSetError /* = false */)
6919{
6920 /* sanity check */
6921 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
6922
6923 SharedFolderMap::const_iterator it = m_mapSharedFolders.find(strName);
6924 if (it != m_mapSharedFolders.end())
6925 {
6926 aSharedFolder = it->second;
6927 return S_OK;
6928 }
6929
6930 if (aSetError)
6931 setError(VBOX_E_FILE_ERROR,
6932 tr("Could not find a shared folder named '%s'."),
6933 strName.c_str());
6934
6935 return VBOX_E_FILE_ERROR;
6936}
6937
6938/**
6939 * Fetches the list of global or machine shared folders from the server.
6940 *
6941 * @param aGlobal true to fetch global folders.
6942 *
6943 * @note The caller must lock this object for writing.
6944 */
6945HRESULT Console::fetchSharedFolders(BOOL aGlobal)
6946{
6947 /* sanity check */
6948 AssertReturn(AutoCaller(this).state() == InInit ||
6949 isWriteLockOnCurrentThread(), E_FAIL);
6950
6951 LogFlowThisFunc(("Entering\n"));
6952
6953 /* Check if we're online and keep it that way. */
6954 SafeVMPtrQuiet ptrVM(this);
6955 AutoVMCallerQuietWeak autoVMCaller(this);
6956 bool const online = ptrVM.isOk()
6957 && m_pVMMDev
6958 && m_pVMMDev->isShFlActive();
6959
6960 HRESULT rc = S_OK;
6961
6962 try
6963 {
6964 if (aGlobal)
6965 {
6966 /// @todo grab & process global folders when they are done
6967 }
6968 else
6969 {
6970 SharedFolderDataMap oldFolders;
6971 if (online)
6972 oldFolders = m_mapMachineSharedFolders;
6973
6974 m_mapMachineSharedFolders.clear();
6975
6976 SafeIfaceArray<ISharedFolder> folders;
6977 rc = mMachine->COMGETTER(SharedFolders)(ComSafeArrayAsOutParam(folders));
6978 if (FAILED(rc)) throw rc;
6979
6980 for (size_t i = 0; i < folders.size(); ++i)
6981 {
6982 ComPtr<ISharedFolder> pSharedFolder = folders[i];
6983
6984 Bstr bstrName;
6985 Bstr bstrHostPath;
6986 BOOL writable;
6987 BOOL autoMount;
6988
6989 rc = pSharedFolder->COMGETTER(Name)(bstrName.asOutParam());
6990 if (FAILED(rc)) throw rc;
6991 Utf8Str strName(bstrName);
6992
6993 rc = pSharedFolder->COMGETTER(HostPath)(bstrHostPath.asOutParam());
6994 if (FAILED(rc)) throw rc;
6995 Utf8Str strHostPath(bstrHostPath);
6996
6997 rc = pSharedFolder->COMGETTER(Writable)(&writable);
6998 if (FAILED(rc)) throw rc;
6999
7000 rc = pSharedFolder->COMGETTER(AutoMount)(&autoMount);
7001 if (FAILED(rc)) throw rc;
7002
7003 m_mapMachineSharedFolders.insert(std::make_pair(strName,
7004 SharedFolderData(strHostPath, writable, autoMount)));
7005
7006 /* send changes to HGCM if the VM is running */
7007 if (online)
7008 {
7009 SharedFolderDataMap::iterator it = oldFolders.find(strName);
7010 if ( it == oldFolders.end()
7011 || it->second.m_strHostPath != strHostPath)
7012 {
7013 /* a new machine folder is added or
7014 * the existing machine folder is changed */
7015 if (m_mapSharedFolders.find(strName) != m_mapSharedFolders.end())
7016 ; /* the console folder exists, nothing to do */
7017 else
7018 {
7019 /* remove the old machine folder (when changed)
7020 * or the global folder if any (when new) */
7021 if ( it != oldFolders.end()
7022 || m_mapGlobalSharedFolders.find(strName) != m_mapGlobalSharedFolders.end()
7023 )
7024 {
7025 rc = removeSharedFolder(strName);
7026 if (FAILED(rc)) throw rc;
7027 }
7028
7029 /* create the new machine folder */
7030 rc = createSharedFolder(strName,
7031 SharedFolderData(strHostPath,
7032 writable,
7033 autoMount));
7034 if (FAILED(rc)) throw rc;
7035 }
7036 }
7037 /* forget the processed (or identical) folder */
7038 if (it != oldFolders.end())
7039 oldFolders.erase(it);
7040 }
7041 }
7042
7043 /* process outdated (removed) folders */
7044 if (online)
7045 {
7046 for (SharedFolderDataMap::const_iterator it = oldFolders.begin();
7047 it != oldFolders.end(); ++ it)
7048 {
7049 if (m_mapSharedFolders.find(it->first) != m_mapSharedFolders.end())
7050 ; /* the console folder exists, nothing to do */
7051 else
7052 {
7053 /* remove the outdated machine folder */
7054 rc = removeSharedFolder(it->first);
7055 if (FAILED(rc)) throw rc;
7056
7057 /* create the global folder if there is any */
7058 SharedFolderDataMap::const_iterator git =
7059 m_mapGlobalSharedFolders.find(it->first);
7060 if (git != m_mapGlobalSharedFolders.end())
7061 {
7062 rc = createSharedFolder(git->first, git->second);
7063 if (FAILED(rc)) throw rc;
7064 }
7065 }
7066 }
7067 }
7068 }
7069 }
7070 catch (HRESULT rc2)
7071 {
7072 if (online)
7073 setVMRuntimeErrorCallbackF(ptrVM, this, 0, "BrokenSharedFolder",
7074 N_("Broken shared folder!"));
7075 }
7076
7077 LogFlowThisFunc(("Leaving\n"));
7078
7079 return rc;
7080}
7081
7082/**
7083 * Searches for a shared folder with the given name in the list of machine
7084 * shared folders and then in the list of the global shared folders.
7085 *
7086 * @param aName Name of the folder to search for.
7087 * @param aIt Where to store the pointer to the found folder.
7088 * @return @c true if the folder was found and @c false otherwise.
7089 *
7090 * @note The caller must lock this object for reading.
7091 */
7092bool Console::findOtherSharedFolder(const Utf8Str &strName,
7093 SharedFolderDataMap::const_iterator &aIt)
7094{
7095 /* sanity check */
7096 AssertReturn(isWriteLockOnCurrentThread(), false);
7097
7098 /* first, search machine folders */
7099 aIt = m_mapMachineSharedFolders.find(strName);
7100 if (aIt != m_mapMachineSharedFolders.end())
7101 return true;
7102
7103 /* second, search machine folders */
7104 aIt = m_mapGlobalSharedFolders.find(strName);
7105 if (aIt != m_mapGlobalSharedFolders.end())
7106 return true;
7107
7108 return false;
7109}
7110
7111/**
7112 * Calls the HGCM service to add a shared folder definition.
7113 *
7114 * @param aName Shared folder name.
7115 * @param aHostPath Shared folder path.
7116 *
7117 * @note Must be called from under AutoVMCaller and when mpVM != NULL!
7118 * @note Doesn't lock anything.
7119 */
7120HRESULT Console::createSharedFolder(const Utf8Str &strName, const SharedFolderData &aData)
7121{
7122 ComAssertRet(strName.isNotEmpty(), E_FAIL);
7123 ComAssertRet(aData.m_strHostPath.isNotEmpty(), E_FAIL);
7124
7125 /* sanity checks */
7126 AssertReturn(mpUVM, E_FAIL);
7127 AssertReturn(m_pVMMDev && m_pVMMDev->isShFlActive(), E_FAIL);
7128
7129 VBOXHGCMSVCPARM parms[SHFL_CPARMS_ADD_MAPPING2];
7130 SHFLSTRING *pFolderName, *pMapName;
7131 size_t cbString;
7132
7133 Log(("Adding shared folder '%s' -> '%s'\n", strName.c_str(), aData.m_strHostPath.c_str()));
7134
7135 // check whether the path is valid and exists
7136 char hostPathFull[RTPATH_MAX];
7137 int vrc = RTPathAbsEx(NULL,
7138 aData.m_strHostPath.c_str(),
7139 hostPathFull,
7140 sizeof(hostPathFull));
7141 if (RT_FAILURE(vrc))
7142 return setError(E_INVALIDARG,
7143 tr("Invalid shared folder path: '%s' (%Rrc)"),
7144 aData.m_strHostPath.c_str(), vrc);
7145 if (!RTPathExists(hostPathFull))
7146 return setError(E_INVALIDARG,
7147 tr("Shared folder path '%s' does not exist on the host"),
7148 aData.m_strHostPath.c_str());
7149 /* Check whether the path is full (absolute) */
7150 if (RTPathCompare(aData.m_strHostPath.c_str(), hostPathFull) != 0)
7151 return setError(E_INVALIDARG,
7152 tr("Shared folder path '%s' is not absolute"),
7153 aData.m_strHostPath.c_str());
7154
7155 // now that we know the path is good, give it to HGCM
7156
7157 Bstr bstrName(strName);
7158 Bstr bstrHostPath(aData.m_strHostPath);
7159
7160 cbString = (bstrHostPath.length() + 1) * sizeof(RTUTF16);
7161 if (cbString >= UINT16_MAX)
7162 return setError(E_INVALIDARG, tr("The name is too long"));
7163 pFolderName = (SHFLSTRING*)RTMemAllocZ(sizeof(SHFLSTRING) + cbString);
7164 Assert(pFolderName);
7165 memcpy(pFolderName->String.ucs2, bstrHostPath.raw(), cbString);
7166
7167 pFolderName->u16Size = (uint16_t)cbString;
7168 pFolderName->u16Length = (uint16_t)cbString - sizeof(RTUTF16);
7169
7170 parms[0].type = VBOX_HGCM_SVC_PARM_PTR;
7171 parms[0].u.pointer.addr = pFolderName;
7172 parms[0].u.pointer.size = sizeof(SHFLSTRING) + (uint16_t)cbString;
7173
7174 cbString = (bstrName.length() + 1) * sizeof(RTUTF16);
7175 if (cbString >= UINT16_MAX)
7176 {
7177 RTMemFree(pFolderName);
7178 return setError(E_INVALIDARG, tr("The host path is too long"));
7179 }
7180 pMapName = (SHFLSTRING*)RTMemAllocZ(sizeof(SHFLSTRING) + cbString);
7181 Assert(pMapName);
7182 memcpy(pMapName->String.ucs2, bstrName.raw(), cbString);
7183
7184 pMapName->u16Size = (uint16_t)cbString;
7185 pMapName->u16Length = (uint16_t)cbString - sizeof(RTUTF16);
7186
7187 parms[1].type = VBOX_HGCM_SVC_PARM_PTR;
7188 parms[1].u.pointer.addr = pMapName;
7189 parms[1].u.pointer.size = sizeof(SHFLSTRING) + (uint16_t)cbString;
7190
7191 parms[2].type = VBOX_HGCM_SVC_PARM_32BIT;
7192 parms[2].u.uint32 = aData.m_fWritable;
7193
7194 /*
7195 * Auto-mount flag; is indicated by using the SHFL_CPARMS_ADD_MAPPING2
7196 * define below. This shows the host service that we have supplied
7197 * an additional parameter (auto-mount) and keeps the actual command
7198 * backwards compatible.
7199 */
7200 parms[3].type = VBOX_HGCM_SVC_PARM_32BIT;
7201 parms[3].u.uint32 = aData.m_fAutoMount;
7202
7203 vrc = m_pVMMDev->hgcmHostCall("VBoxSharedFolders",
7204 SHFL_FN_ADD_MAPPING,
7205 SHFL_CPARMS_ADD_MAPPING2, &parms[0]);
7206 RTMemFree(pFolderName);
7207 RTMemFree(pMapName);
7208
7209 if (RT_FAILURE(vrc))
7210 return setError(E_FAIL,
7211 tr("Could not create a shared folder '%s' mapped to '%s' (%Rrc)"),
7212 strName.c_str(), aData.m_strHostPath.c_str(), vrc);
7213
7214 return S_OK;
7215}
7216
7217/**
7218 * Calls the HGCM service to remove the shared folder definition.
7219 *
7220 * @param aName Shared folder name.
7221 *
7222 * @note Must be called from under AutoVMCaller and when mpVM != NULL!
7223 * @note Doesn't lock anything.
7224 */
7225HRESULT Console::removeSharedFolder(const Utf8Str &strName)
7226{
7227 ComAssertRet(strName.isNotEmpty(), E_FAIL);
7228
7229 /* sanity checks */
7230 AssertReturn(mpUVM, E_FAIL);
7231 AssertReturn(m_pVMMDev && m_pVMMDev->isShFlActive(), E_FAIL);
7232
7233 VBOXHGCMSVCPARM parms;
7234 SHFLSTRING *pMapName;
7235 size_t cbString;
7236
7237 Log(("Removing shared folder '%s'\n", strName.c_str()));
7238
7239 Bstr bstrName(strName);
7240 cbString = (bstrName.length() + 1) * sizeof(RTUTF16);
7241 if (cbString >= UINT16_MAX)
7242 return setError(E_INVALIDARG, tr("The name is too long"));
7243 pMapName = (SHFLSTRING *) RTMemAllocZ(sizeof(SHFLSTRING) + cbString);
7244 Assert(pMapName);
7245 memcpy(pMapName->String.ucs2, bstrName.raw(), cbString);
7246
7247 pMapName->u16Size = (uint16_t)cbString;
7248 pMapName->u16Length = (uint16_t)cbString - sizeof(RTUTF16);
7249
7250 parms.type = VBOX_HGCM_SVC_PARM_PTR;
7251 parms.u.pointer.addr = pMapName;
7252 parms.u.pointer.size = sizeof(SHFLSTRING) + (uint16_t)cbString;
7253
7254 int vrc = m_pVMMDev->hgcmHostCall("VBoxSharedFolders",
7255 SHFL_FN_REMOVE_MAPPING,
7256 1, &parms);
7257 RTMemFree(pMapName);
7258 if (RT_FAILURE(vrc))
7259 return setError(E_FAIL,
7260 tr("Could not remove the shared folder '%s' (%Rrc)"),
7261 strName.c_str(), vrc);
7262
7263 return S_OK;
7264}
7265
7266/**
7267 * VM state callback function. Called by the VMM
7268 * using its state machine states.
7269 *
7270 * Primarily used to handle VM initiated power off, suspend and state saving,
7271 * but also for doing termination completed work (VMSTATE_TERMINATE).
7272 *
7273 * In general this function is called in the context of the EMT.
7274 *
7275 * @param aVM The VM handle.
7276 * @param aState The new state.
7277 * @param aOldState The old state.
7278 * @param aUser The user argument (pointer to the Console object).
7279 *
7280 * @note Locks the Console object for writing.
7281 */
7282DECLCALLBACK(void) Console::vmstateChangeCallback(PVM aVM,
7283 VMSTATE aState,
7284 VMSTATE aOldState,
7285 void *aUser)
7286{
7287 LogFlowFunc(("Changing state from %s to %s (aVM=%p)\n",
7288 VMR3GetStateName(aOldState), VMR3GetStateName(aState), aVM));
7289
7290 Console *that = static_cast<Console *>(aUser);
7291 AssertReturnVoid(that);
7292
7293 AutoCaller autoCaller(that);
7294
7295 /* Note that we must let this method proceed even if Console::uninit() has
7296 * been already called. In such case this VMSTATE change is a result of:
7297 * 1) powerDown() called from uninit() itself, or
7298 * 2) VM-(guest-)initiated power off. */
7299 AssertReturnVoid( autoCaller.isOk()
7300 || autoCaller.state() == InUninit);
7301
7302 switch (aState)
7303 {
7304 /*
7305 * The VM has terminated
7306 */
7307 case VMSTATE_OFF:
7308 {
7309 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
7310
7311 if (that->mVMStateChangeCallbackDisabled)
7312 break;
7313
7314 /* Do we still think that it is running? It may happen if this is a
7315 * VM-(guest-)initiated shutdown/poweroff.
7316 */
7317 if ( that->mMachineState != MachineState_Stopping
7318 && that->mMachineState != MachineState_Saving
7319 && that->mMachineState != MachineState_Restoring
7320 && that->mMachineState != MachineState_TeleportingIn
7321 && that->mMachineState != MachineState_FaultTolerantSyncing
7322 && that->mMachineState != MachineState_TeleportingPausedVM
7323 && !that->mVMIsAlreadyPoweringOff
7324 )
7325 {
7326 LogFlowFunc(("VM has powered itself off but Console still thinks it is running. Notifying.\n"));
7327
7328 /* prevent powerDown() from calling VMR3PowerOff() again */
7329 Assert(that->mVMPoweredOff == false);
7330 that->mVMPoweredOff = true;
7331
7332 /*
7333 * request a progress object from the server
7334 * (this will set the machine state to Stopping on the server
7335 * to block others from accessing this machine)
7336 */
7337 ComPtr<IProgress> pProgress;
7338 HRESULT rc = that->mControl->BeginPoweringDown(pProgress.asOutParam());
7339 AssertComRC(rc);
7340
7341 /* sync the state with the server */
7342 that->setMachineStateLocally(MachineState_Stopping);
7343
7344 /* Setup task object and thread to carry out the operation
7345 * asynchronously (if we call powerDown() right here but there
7346 * is one or more mpVM callers (added with addVMCaller()) we'll
7347 * deadlock).
7348 */
7349 std::auto_ptr<VMPowerDownTask> task(new VMPowerDownTask(that,
7350 pProgress));
7351
7352 /* If creating a task failed, this can currently mean one of
7353 * two: either Console::uninit() has been called just a ms
7354 * before (so a powerDown() call is already on the way), or
7355 * powerDown() itself is being already executed. Just do
7356 * nothing.
7357 */
7358 if (!task->isOk())
7359 {
7360 LogFlowFunc(("Console is already being uninitialized.\n"));
7361 break;
7362 }
7363
7364 int vrc = RTThreadCreate(NULL, Console::powerDownThread,
7365 (void *) task.get(), 0,
7366 RTTHREADTYPE_MAIN_WORKER, 0,
7367 "VMPowerDown");
7368 AssertMsgRCBreak(vrc, ("Could not create VMPowerDown thread (%Rrc)\n", vrc));
7369
7370 /* task is now owned by powerDownThread(), so release it */
7371 task.release();
7372 }
7373 break;
7374 }
7375
7376 /* The VM has been completely destroyed.
7377 *
7378 * Note: This state change can happen at two points:
7379 * 1) At the end of VMR3Destroy() if it was not called from EMT.
7380 * 2) At the end of vmR3EmulationThread if VMR3Destroy() was
7381 * called by EMT.
7382 */
7383 case VMSTATE_TERMINATED:
7384 {
7385 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
7386
7387 if (that->mVMStateChangeCallbackDisabled)
7388 break;
7389
7390 /* Terminate host interface networking. If aVM is NULL, we've been
7391 * manually called from powerUpThread() either before calling
7392 * VMR3Create() or after VMR3Create() failed, so no need to touch
7393 * networking.
7394 */
7395 if (aVM)
7396 that->powerDownHostInterfaces();
7397
7398 /* From now on the machine is officially powered down or remains in
7399 * the Saved state.
7400 */
7401 switch (that->mMachineState)
7402 {
7403 default:
7404 AssertFailed();
7405 /* fall through */
7406 case MachineState_Stopping:
7407 /* successfully powered down */
7408 that->setMachineState(MachineState_PoweredOff);
7409 break;
7410 case MachineState_Saving:
7411 /* successfully saved */
7412 that->setMachineState(MachineState_Saved);
7413 break;
7414 case MachineState_Starting:
7415 /* failed to start, but be patient: set back to PoweredOff
7416 * (for similarity with the below) */
7417 that->setMachineState(MachineState_PoweredOff);
7418 break;
7419 case MachineState_Restoring:
7420 /* failed to load the saved state file, but be patient: set
7421 * back to Saved (to preserve the saved state file) */
7422 that->setMachineState(MachineState_Saved);
7423 break;
7424 case MachineState_TeleportingIn:
7425 /* Teleportation failed or was canceled. Back to powered off. */
7426 that->setMachineState(MachineState_PoweredOff);
7427 break;
7428 case MachineState_TeleportingPausedVM:
7429 /* Successfully teleported the VM. */
7430 that->setMachineState(MachineState_Teleported);
7431 break;
7432 case MachineState_FaultTolerantSyncing:
7433 /* Fault tolerant sync failed or was canceled. Back to powered off. */
7434 that->setMachineState(MachineState_PoweredOff);
7435 break;
7436 }
7437 break;
7438 }
7439
7440 case VMSTATE_RESETTING:
7441 {
7442#ifdef VBOX_WITH_GUEST_PROPS
7443 /* Do not take any read/write locks here! */
7444 that->guestPropertiesHandleVMReset();
7445#endif
7446 break;
7447 }
7448
7449 case VMSTATE_SUSPENDED:
7450 {
7451 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
7452
7453 if (that->mVMStateChangeCallbackDisabled)
7454 break;
7455
7456 switch (that->mMachineState)
7457 {
7458 case MachineState_Teleporting:
7459 that->setMachineState(MachineState_TeleportingPausedVM);
7460 break;
7461
7462 case MachineState_LiveSnapshotting:
7463 that->setMachineState(MachineState_Saving);
7464 break;
7465
7466 case MachineState_TeleportingPausedVM:
7467 case MachineState_Saving:
7468 case MachineState_Restoring:
7469 case MachineState_Stopping:
7470 case MachineState_TeleportingIn:
7471 case MachineState_FaultTolerantSyncing:
7472 /* The worker thread handles the transition. */
7473 break;
7474
7475 default:
7476 AssertMsgFailed(("%s\n", Global::stringifyMachineState(that->mMachineState)));
7477 case MachineState_Running:
7478 that->setMachineState(MachineState_Paused);
7479 break;
7480
7481 case MachineState_Paused:
7482 /* Nothing to do. */
7483 break;
7484 }
7485 break;
7486 }
7487
7488 case VMSTATE_SUSPENDED_LS:
7489 case VMSTATE_SUSPENDED_EXT_LS:
7490 {
7491 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
7492 if (that->mVMStateChangeCallbackDisabled)
7493 break;
7494 switch (that->mMachineState)
7495 {
7496 case MachineState_Teleporting:
7497 that->setMachineState(MachineState_TeleportingPausedVM);
7498 break;
7499
7500 case MachineState_LiveSnapshotting:
7501 that->setMachineState(MachineState_Saving);
7502 break;
7503
7504 case MachineState_TeleportingPausedVM:
7505 case MachineState_Saving:
7506 /* ignore */
7507 break;
7508
7509 default:
7510 AssertMsgFailed(("%s/%s -> %s\n", Global::stringifyMachineState(that->mMachineState), VMR3GetStateName(aOldState), VMR3GetStateName(aState) ));
7511 that->setMachineState(MachineState_Paused);
7512 break;
7513 }
7514 break;
7515 }
7516
7517 case VMSTATE_RUNNING:
7518 {
7519 if ( aOldState == VMSTATE_POWERING_ON
7520 || aOldState == VMSTATE_RESUMING
7521 || aOldState == VMSTATE_RUNNING_FT)
7522 {
7523 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
7524
7525 if (that->mVMStateChangeCallbackDisabled)
7526 break;
7527
7528 Assert( ( ( that->mMachineState == MachineState_Starting
7529 || that->mMachineState == MachineState_Paused)
7530 && aOldState == VMSTATE_POWERING_ON)
7531 || ( ( that->mMachineState == MachineState_Restoring
7532 || that->mMachineState == MachineState_TeleportingIn
7533 || that->mMachineState == MachineState_Paused
7534 || that->mMachineState == MachineState_Saving
7535 )
7536 && aOldState == VMSTATE_RESUMING)
7537 || ( that->mMachineState == MachineState_FaultTolerantSyncing
7538 && aOldState == VMSTATE_RUNNING_FT));
7539
7540 that->setMachineState(MachineState_Running);
7541 }
7542
7543 break;
7544 }
7545
7546 case VMSTATE_RUNNING_LS:
7547 AssertMsg( that->mMachineState == MachineState_LiveSnapshotting
7548 || that->mMachineState == MachineState_Teleporting,
7549 ("%s/%s -> %s\n", Global::stringifyMachineState(that->mMachineState), VMR3GetStateName(aOldState), VMR3GetStateName(aState) ));
7550 break;
7551
7552 case VMSTATE_RUNNING_FT:
7553 AssertMsg(that->mMachineState == MachineState_FaultTolerantSyncing,
7554 ("%s/%s -> %s\n", Global::stringifyMachineState(that->mMachineState), VMR3GetStateName(aOldState), VMR3GetStateName(aState) ));
7555 break;
7556
7557 case VMSTATE_FATAL_ERROR:
7558 {
7559 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
7560
7561 if (that->mVMStateChangeCallbackDisabled)
7562 break;
7563
7564 /* Fatal errors are only for running VMs. */
7565 Assert(Global::IsOnline(that->mMachineState));
7566
7567 /* Note! 'Pause' is used here in want of something better. There
7568 * are currently only two places where fatal errors might be
7569 * raised, so it is not worth adding a new externally
7570 * visible state for this yet. */
7571 that->setMachineState(MachineState_Paused);
7572 break;
7573 }
7574
7575 case VMSTATE_GURU_MEDITATION:
7576 {
7577 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
7578
7579 if (that->mVMStateChangeCallbackDisabled)
7580 break;
7581
7582 /* Guru are only for running VMs */
7583 Assert(Global::IsOnline(that->mMachineState));
7584
7585 that->setMachineState(MachineState_Stuck);
7586 break;
7587 }
7588
7589 default: /* shut up gcc */
7590 break;
7591 }
7592}
7593
7594#ifdef VBOX_WITH_USB
7595/**
7596 * Sends a request to VMM to attach the given host device.
7597 * After this method succeeds, the attached device will appear in the
7598 * mUSBDevices collection.
7599 *
7600 * @param aHostDevice device to attach
7601 *
7602 * @note Synchronously calls EMT.
7603 * @note Must be called from under this object's lock.
7604 */
7605HRESULT Console::attachUSBDevice(IUSBDevice *aHostDevice, ULONG aMaskedIfs)
7606{
7607 AssertReturn(aHostDevice, E_FAIL);
7608 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
7609
7610 /* still want a lock object because we need to leave it */
7611 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7612
7613 HRESULT hrc;
7614
7615 /*
7616 * Get the address and the Uuid, and call the pfnCreateProxyDevice roothub
7617 * method in EMT (using usbAttachCallback()).
7618 */
7619 Bstr BstrAddress;
7620 hrc = aHostDevice->COMGETTER(Address)(BstrAddress.asOutParam());
7621 ComAssertComRCRetRC(hrc);
7622
7623 Utf8Str Address(BstrAddress);
7624
7625 Bstr id;
7626 hrc = aHostDevice->COMGETTER(Id)(id.asOutParam());
7627 ComAssertComRCRetRC(hrc);
7628 Guid uuid(id);
7629
7630 BOOL fRemote = FALSE;
7631 hrc = aHostDevice->COMGETTER(Remote)(&fRemote);
7632 ComAssertComRCRetRC(hrc);
7633
7634 /* Get the VM handle. */
7635 SafeVMPtr ptrVM(this);
7636 if (!ptrVM.isOk())
7637 return ptrVM.rc();
7638
7639 LogFlowThisFunc(("Proxying USB device '%s' {%RTuuid}...\n",
7640 Address.c_str(), uuid.raw()));
7641
7642 /* leave the lock before a VMR3* call (EMT will call us back)! */
7643 alock.leave();
7644
7645/** @todo just do everything here and only wrap the PDMR3Usb call. That'll offload some notification stuff from the EMT thread. */
7646 int vrc = VMR3ReqCallWait(ptrVM, VMCPUID_ANY,
7647 (PFNRT)usbAttachCallback, 7,
7648 this, ptrVM.raw(), aHostDevice, uuid.raw(), fRemote, Address.c_str(), aMaskedIfs);
7649
7650 /* restore the lock */
7651 alock.enter();
7652
7653 /* hrc is S_OK here */
7654
7655 if (RT_FAILURE(vrc))
7656 {
7657 LogWarningThisFunc(("Failed to create proxy device for '%s' {%RTuuid} (%Rrc)\n",
7658 Address.c_str(), uuid.raw(), vrc));
7659
7660 switch (vrc)
7661 {
7662 case VERR_VUSB_NO_PORTS:
7663 hrc = setError(E_FAIL,
7664 tr("Failed to attach the USB device. (No available ports on the USB controller)."));
7665 break;
7666 case VERR_VUSB_USBFS_PERMISSION:
7667 hrc = setError(E_FAIL,
7668 tr("Not permitted to open the USB device, check usbfs options"));
7669 break;
7670 default:
7671 hrc = setError(E_FAIL,
7672 tr("Failed to create a proxy device for the USB device. (Error: %Rrc)"),
7673 vrc);
7674 break;
7675 }
7676 }
7677
7678 return hrc;
7679}
7680
7681/**
7682 * USB device attach callback used by AttachUSBDevice().
7683 * Note that AttachUSBDevice() doesn't return until this callback is executed,
7684 * so we don't use AutoCaller and don't care about reference counters of
7685 * interface pointers passed in.
7686 *
7687 * @thread EMT
7688 * @note Locks the console object for writing.
7689 */
7690//static
7691DECLCALLBACK(int)
7692Console::usbAttachCallback(Console *that, PVM pVM, IUSBDevice *aHostDevice, PCRTUUID aUuid, bool aRemote, const char *aAddress, ULONG aMaskedIfs)
7693{
7694 LogFlowFuncEnter();
7695 LogFlowFunc(("that={%p}\n", that));
7696
7697 AssertReturn(that && aUuid, VERR_INVALID_PARAMETER);
7698
7699 void *pvRemoteBackend = NULL;
7700 if (aRemote)
7701 {
7702 RemoteUSBDevice *pRemoteUSBDevice = static_cast<RemoteUSBDevice *>(aHostDevice);
7703 Guid guid(*aUuid);
7704
7705 pvRemoteBackend = that->consoleVRDPServer()->USBBackendRequestPointer(pRemoteUSBDevice->clientId(), &guid);
7706 if (!pvRemoteBackend)
7707 return VERR_INVALID_PARAMETER; /* The clientId is invalid then. */
7708 }
7709
7710 USHORT portVersion = 1;
7711 HRESULT hrc = aHostDevice->COMGETTER(PortVersion)(&portVersion);
7712 AssertComRCReturn(hrc, VERR_GENERAL_FAILURE);
7713 Assert(portVersion == 1 || portVersion == 2);
7714
7715 int vrc = PDMR3USBCreateProxyDevice(pVM, aUuid, aRemote, aAddress, pvRemoteBackend,
7716 portVersion == 1 ? VUSB_STDVER_11 : VUSB_STDVER_20, aMaskedIfs);
7717 if (RT_SUCCESS(vrc))
7718 {
7719 /* Create a OUSBDevice and add it to the device list */
7720 ComObjPtr<OUSBDevice> pUSBDevice;
7721 pUSBDevice.createObject();
7722 hrc = pUSBDevice->init(aHostDevice);
7723 AssertComRC(hrc);
7724
7725 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
7726 that->mUSBDevices.push_back(pUSBDevice);
7727 LogFlowFunc(("Attached device {%RTuuid}\n", pUSBDevice->id().raw()));
7728
7729 /* notify callbacks */
7730 that->onUSBDeviceStateChange(pUSBDevice, true /* aAttached */, NULL);
7731 }
7732
7733 LogFlowFunc(("vrc=%Rrc\n", vrc));
7734 LogFlowFuncLeave();
7735 return vrc;
7736}
7737
7738/**
7739 * Sends a request to VMM to detach the given host device. After this method
7740 * succeeds, the detached device will disappear from the mUSBDevices
7741 * collection.
7742 *
7743 * @param aIt Iterator pointing to the device to detach.
7744 *
7745 * @note Synchronously calls EMT.
7746 * @note Must be called from under this object's lock.
7747 */
7748HRESULT Console::detachUSBDevice(USBDeviceList::iterator &aIt)
7749{
7750 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
7751
7752 /* still want a lock object because we need to leave it */
7753 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7754
7755 /* Get the VM handle. */
7756 SafeVMPtr ptrVM(this);
7757 if (!ptrVM.isOk())
7758 return ptrVM.rc();
7759
7760 /* if the device is attached, then there must at least one USB hub. */
7761 AssertReturn(PDMR3USBHasHub(ptrVM), E_FAIL);
7762
7763 LogFlowThisFunc(("Detaching USB proxy device {%RTuuid}...\n",
7764 (*aIt)->id().raw()));
7765
7766 /* leave the lock before a VMR3* call (EMT will call us back)! */
7767 alock.leave();
7768
7769/** @todo just do everything here and only wrap the PDMR3Usb call. That'll offload some notification stuff from the EMT thread. */
7770 int vrc = VMR3ReqCallWait(ptrVM, VMCPUID_ANY,
7771 (PFNRT)usbDetachCallback, 5,
7772 this, ptrVM.raw(), &aIt, (*aIt)->id().raw());
7773 ComAssertRCRet(vrc, E_FAIL);
7774
7775 return S_OK;
7776}
7777
7778/**
7779 * USB device detach callback used by DetachUSBDevice().
7780 * Note that DetachUSBDevice() doesn't return until this callback is executed,
7781 * so we don't use AutoCaller and don't care about reference counters of
7782 * interface pointers passed in.
7783 *
7784 * @thread EMT
7785 * @note Locks the console object for writing.
7786 */
7787//static
7788DECLCALLBACK(int)
7789Console::usbDetachCallback(Console *that, PVM pVM, USBDeviceList::iterator *aIt, PCRTUUID aUuid)
7790{
7791 LogFlowFuncEnter();
7792 LogFlowFunc(("that={%p}\n", that));
7793
7794 AssertReturn(that && aUuid, VERR_INVALID_PARAMETER);
7795 ComObjPtr<OUSBDevice> pUSBDevice = **aIt;
7796
7797 /*
7798 * If that was a remote device, release the backend pointer.
7799 * The pointer was requested in usbAttachCallback.
7800 */
7801 BOOL fRemote = FALSE;
7802
7803 HRESULT hrc2 = (**aIt)->COMGETTER(Remote)(&fRemote);
7804 if (FAILED(hrc2))
7805 setErrorStatic(hrc2, "GetRemote() failed");
7806
7807 if (fRemote)
7808 {
7809 Guid guid(*aUuid);
7810 that->consoleVRDPServer()->USBBackendReleasePointer(&guid);
7811 }
7812
7813 int vrc = PDMR3USBDetachDevice(pVM, aUuid);
7814
7815 if (RT_SUCCESS(vrc))
7816 {
7817 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
7818
7819 /* Remove the device from the collection */
7820 that->mUSBDevices.erase(*aIt);
7821 LogFlowFunc(("Detached device {%RTuuid}\n", pUSBDevice->id().raw()));
7822
7823 /* notify callbacks */
7824 that->onUSBDeviceStateChange(pUSBDevice, false /* aAttached */, NULL);
7825 }
7826
7827 LogFlowFunc(("vrc=%Rrc\n", vrc));
7828 LogFlowFuncLeave();
7829 return vrc;
7830}
7831#endif /* VBOX_WITH_USB */
7832
7833/* Note: FreeBSD needs this whether netflt is used or not. */
7834#if ((defined(RT_OS_LINUX) && !defined(VBOX_WITH_NETFLT)) || defined(RT_OS_FREEBSD))
7835/**
7836 * Helper function to handle host interface device creation and attachment.
7837 *
7838 * @param networkAdapter the network adapter which attachment should be reset
7839 * @return COM status code
7840 *
7841 * @note The caller must lock this object for writing.
7842 *
7843 * @todo Move this back into the driver!
7844 */
7845HRESULT Console::attachToTapInterface(INetworkAdapter *networkAdapter)
7846{
7847 LogFlowThisFunc(("\n"));
7848 /* sanity check */
7849 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
7850
7851# ifdef VBOX_STRICT
7852 /* paranoia */
7853 NetworkAttachmentType_T attachment;
7854 networkAdapter->COMGETTER(AttachmentType)(&attachment);
7855 Assert(attachment == NetworkAttachmentType_Bridged);
7856# endif /* VBOX_STRICT */
7857
7858 HRESULT rc = S_OK;
7859
7860 ULONG slot = 0;
7861 rc = networkAdapter->COMGETTER(Slot)(&slot);
7862 AssertComRC(rc);
7863
7864# ifdef RT_OS_LINUX
7865 /*
7866 * Allocate a host interface device
7867 */
7868 int rcVBox = RTFileOpen(&maTapFD[slot], "/dev/net/tun",
7869 RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_INHERIT);
7870 if (RT_SUCCESS(rcVBox))
7871 {
7872 /*
7873 * Set/obtain the tap interface.
7874 */
7875 struct ifreq IfReq;
7876 memset(&IfReq, 0, sizeof(IfReq));
7877 /* The name of the TAP interface we are using */
7878 Bstr tapDeviceName;
7879 rc = networkAdapter->COMGETTER(BridgedInterface)(tapDeviceName.asOutParam());
7880 if (FAILED(rc))
7881 tapDeviceName.setNull(); /* Is this necessary? */
7882 if (tapDeviceName.isEmpty())
7883 {
7884 LogRel(("No TAP device name was supplied.\n"));
7885 rc = setError(E_FAIL, tr("No TAP device name was supplied for the host networking interface"));
7886 }
7887
7888 if (SUCCEEDED(rc))
7889 {
7890 /* If we are using a static TAP device then try to open it. */
7891 Utf8Str str(tapDeviceName);
7892 if (str.length() <= sizeof(IfReq.ifr_name))
7893 strcpy(IfReq.ifr_name, str.c_str());
7894 else
7895 memcpy(IfReq.ifr_name, str.c_str(), sizeof(IfReq.ifr_name) - 1); /** @todo bitch about names which are too long... */
7896 IfReq.ifr_flags = IFF_TAP | IFF_NO_PI;
7897 rcVBox = ioctl(maTapFD[slot], TUNSETIFF, &IfReq);
7898 if (rcVBox != 0)
7899 {
7900 LogRel(("Failed to open the host network interface %ls\n", tapDeviceName.raw()));
7901 rc = setError(E_FAIL,
7902 tr("Failed to open the host network interface %ls"),
7903 tapDeviceName.raw());
7904 }
7905 }
7906 if (SUCCEEDED(rc))
7907 {
7908 /*
7909 * Make it pollable.
7910 */
7911 if (fcntl(maTapFD[slot], F_SETFL, O_NONBLOCK) != -1)
7912 {
7913 Log(("attachToTapInterface: %RTfile %ls\n", maTapFD[slot], tapDeviceName.raw()));
7914 /*
7915 * Here is the right place to communicate the TAP file descriptor and
7916 * the host interface name to the server if/when it becomes really
7917 * necessary.
7918 */
7919 maTAPDeviceName[slot] = tapDeviceName;
7920 rcVBox = VINF_SUCCESS;
7921 }
7922 else
7923 {
7924 int iErr = errno;
7925
7926 LogRel(("Configuration error: Failed to configure /dev/net/tun non blocking. Error: %s\n", strerror(iErr)));
7927 rcVBox = VERR_HOSTIF_BLOCKING;
7928 rc = setError(E_FAIL,
7929 tr("could not set up the host networking device for non blocking access: %s"),
7930 strerror(errno));
7931 }
7932 }
7933 }
7934 else
7935 {
7936 LogRel(("Configuration error: Failed to open /dev/net/tun rc=%Rrc\n", rcVBox));
7937 switch (rcVBox)
7938 {
7939 case VERR_ACCESS_DENIED:
7940 /* will be handled by our caller */
7941 rc = rcVBox;
7942 break;
7943 default:
7944 rc = setError(E_FAIL,
7945 tr("Could not set up the host networking device: %Rrc"),
7946 rcVBox);
7947 break;
7948 }
7949 }
7950
7951# elif defined(RT_OS_FREEBSD)
7952 /*
7953 * Set/obtain the tap interface.
7954 */
7955 /* The name of the TAP interface we are using */
7956 Bstr tapDeviceName;
7957 rc = networkAdapter->COMGETTER(BridgedInterface)(tapDeviceName.asOutParam());
7958 if (FAILED(rc))
7959 tapDeviceName.setNull(); /* Is this necessary? */
7960 if (tapDeviceName.isEmpty())
7961 {
7962 LogRel(("No TAP device name was supplied.\n"));
7963 rc = setError(E_FAIL, tr("No TAP device name was supplied for the host networking interface"));
7964 }
7965 char szTapdev[1024] = "/dev/";
7966 /* If we are using a static TAP device then try to open it. */
7967 Utf8Str str(tapDeviceName);
7968 if (str.length() + strlen(szTapdev) <= sizeof(szTapdev))
7969 strcat(szTapdev, str.c_str());
7970 else
7971 memcpy(szTapdev + strlen(szTapdev), str.c_str(),
7972 sizeof(szTapdev) - strlen(szTapdev) - 1); /** @todo bitch about names which are too long... */
7973 int rcVBox = RTFileOpen(&maTapFD[slot], szTapdev,
7974 RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_INHERIT | RTFILE_O_NON_BLOCK);
7975
7976 if (RT_SUCCESS(rcVBox))
7977 maTAPDeviceName[slot] = tapDeviceName;
7978 else
7979 {
7980 switch (rcVBox)
7981 {
7982 case VERR_ACCESS_DENIED:
7983 /* will be handled by our caller */
7984 rc = rcVBox;
7985 break;
7986 default:
7987 rc = setError(E_FAIL,
7988 tr("Failed to open the host network interface %ls"),
7989 tapDeviceName.raw());
7990 break;
7991 }
7992 }
7993# else
7994# error "huh?"
7995# endif
7996 /* in case of failure, cleanup. */
7997 if (RT_FAILURE(rcVBox) && SUCCEEDED(rc))
7998 {
7999 LogRel(("General failure attaching to host interface\n"));
8000 rc = setError(E_FAIL,
8001 tr("General failure attaching to host interface"));
8002 }
8003 LogFlowThisFunc(("rc=%d\n", rc));
8004 return rc;
8005}
8006
8007
8008/**
8009 * Helper function to handle detachment from a host interface
8010 *
8011 * @param networkAdapter the network adapter which attachment should be reset
8012 * @return COM status code
8013 *
8014 * @note The caller must lock this object for writing.
8015 *
8016 * @todo Move this back into the driver!
8017 */
8018HRESULT Console::detachFromTapInterface(INetworkAdapter *networkAdapter)
8019{
8020 /* sanity check */
8021 LogFlowThisFunc(("\n"));
8022 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
8023
8024 HRESULT rc = S_OK;
8025# ifdef VBOX_STRICT
8026 /* paranoia */
8027 NetworkAttachmentType_T attachment;
8028 networkAdapter->COMGETTER(AttachmentType)(&attachment);
8029 Assert(attachment == NetworkAttachmentType_Bridged);
8030# endif /* VBOX_STRICT */
8031
8032 ULONG slot = 0;
8033 rc = networkAdapter->COMGETTER(Slot)(&slot);
8034 AssertComRC(rc);
8035
8036 /* is there an open TAP device? */
8037 if (maTapFD[slot] != NIL_RTFILE)
8038 {
8039 /*
8040 * Close the file handle.
8041 */
8042 Bstr tapDeviceName, tapTerminateApplication;
8043 bool isStatic = true;
8044 rc = networkAdapter->COMGETTER(BridgedInterface)(tapDeviceName.asOutParam());
8045 if (FAILED(rc) || tapDeviceName.isEmpty())
8046 {
8047 /* If the name is empty, this is a dynamic TAP device, so close it now,
8048 so that the termination script can remove the interface. Otherwise we still
8049 need the FD to pass to the termination script. */
8050 isStatic = false;
8051 int rcVBox = RTFileClose(maTapFD[slot]);
8052 AssertRC(rcVBox);
8053 maTapFD[slot] = NIL_RTFILE;
8054 }
8055 if (isStatic)
8056 {
8057 /* If we are using a static TAP device, we close it now, after having called the
8058 termination script. */
8059 int rcVBox = RTFileClose(maTapFD[slot]);
8060 AssertRC(rcVBox);
8061 }
8062 /* the TAP device name and handle are no longer valid */
8063 maTapFD[slot] = NIL_RTFILE;
8064 maTAPDeviceName[slot] = "";
8065 }
8066 LogFlowThisFunc(("returning %d\n", rc));
8067 return rc;
8068}
8069#endif /* (RT_OS_LINUX || RT_OS_FREEBSD) && !VBOX_WITH_NETFLT */
8070
8071/**
8072 * Called at power down to terminate host interface networking.
8073 *
8074 * @note The caller must lock this object for writing.
8075 */
8076HRESULT Console::powerDownHostInterfaces()
8077{
8078 LogFlowThisFunc(("\n"));
8079
8080 /* sanity check */
8081 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
8082
8083 /*
8084 * host interface termination handling
8085 */
8086 HRESULT rc;
8087 for (ULONG slot = 0; slot < SchemaDefs::NetworkAdapterCount; slot ++)
8088 {
8089 ComPtr<INetworkAdapter> pNetworkAdapter;
8090 rc = mMachine->GetNetworkAdapter(slot, pNetworkAdapter.asOutParam());
8091 if (FAILED(rc)) break;
8092
8093 BOOL enabled = FALSE;
8094 pNetworkAdapter->COMGETTER(Enabled)(&enabled);
8095 if (!enabled)
8096 continue;
8097
8098 NetworkAttachmentType_T attachment;
8099 pNetworkAdapter->COMGETTER(AttachmentType)(&attachment);
8100 if (attachment == NetworkAttachmentType_Bridged)
8101 {
8102#if ((defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD)) && !defined(VBOX_WITH_NETFLT))
8103 HRESULT rc2 = detachFromTapInterface(pNetworkAdapter);
8104 if (FAILED(rc2) && SUCCEEDED(rc))
8105 rc = rc2;
8106#endif /* (RT_OS_LINUX || RT_OS_FREEBSD) && !VBOX_WITH_NETFLT */
8107 }
8108 }
8109
8110 return rc;
8111}
8112
8113
8114/**
8115 * Process callback handler for VMR3LoadFromFile, VMR3LoadFromStream, VMR3Save
8116 * and VMR3Teleport.
8117 *
8118 * @param pVM The VM handle.
8119 * @param uPercent Completion percentage (0-100).
8120 * @param pvUser Pointer to an IProgress instance.
8121 * @return VINF_SUCCESS.
8122 */
8123/*static*/
8124DECLCALLBACK(int) Console::stateProgressCallback(PVM pVM, unsigned uPercent, void *pvUser)
8125{
8126 IProgress *pProgress = static_cast<IProgress *>(pvUser);
8127
8128 /* update the progress object */
8129 if (pProgress)
8130 pProgress->SetCurrentOperationProgress(uPercent);
8131
8132 return VINF_SUCCESS;
8133}
8134
8135/**
8136 * @copydoc FNVMATERROR
8137 *
8138 * @remarks Might be some tiny serialization concerns with access to the string
8139 * object here...
8140 */
8141/*static*/ DECLCALLBACK(void)
8142Console::genericVMSetErrorCallback(PVM pVM, void *pvUser, int rc, RT_SRC_POS_DECL,
8143 const char *pszErrorFmt, va_list va)
8144{
8145 Utf8Str *pErrorText = (Utf8Str *)pvUser;
8146 AssertPtr(pErrorText);
8147
8148 /* We ignore RT_SRC_POS_DECL arguments to avoid confusion of end-users. */
8149 va_list va2;
8150 va_copy(va2, va);
8151
8152 /* Append to any the existing error message. */
8153 if (pErrorText->length())
8154 *pErrorText = Utf8StrFmt("%s.\n%N (%Rrc)", pErrorText->c_str(),
8155 pszErrorFmt, &va2, rc, rc);
8156 else
8157 *pErrorText = Utf8StrFmt("%N (%Rrc)", pszErrorFmt, &va2, rc, rc);
8158
8159 va_end(va2);
8160}
8161
8162/**
8163 * VM runtime error callback function.
8164 * See VMSetRuntimeError for the detailed description of parameters.
8165 *
8166 * @param pVM The VM handle.
8167 * @param pvUser The user argument.
8168 * @param fFlags The action flags. See VMSETRTERR_FLAGS_*.
8169 * @param pszErrorId Error ID string.
8170 * @param pszFormat Error message format string.
8171 * @param va Error message arguments.
8172 * @thread EMT.
8173 */
8174/* static */ DECLCALLBACK(void)
8175Console::setVMRuntimeErrorCallback(PVM pVM, void *pvUser, uint32_t fFlags,
8176 const char *pszErrorId,
8177 const char *pszFormat, va_list va)
8178{
8179 bool const fFatal = !!(fFlags & VMSETRTERR_FLAGS_FATAL);
8180 LogFlowFuncEnter();
8181
8182 Console *that = static_cast<Console *>(pvUser);
8183 AssertReturnVoid(that);
8184
8185 Utf8Str message(pszFormat, va);
8186
8187 LogRel(("Console: VM runtime error: fatal=%RTbool, errorID=%s message=\"%s\"\n",
8188 fFatal, pszErrorId, message.c_str()));
8189
8190 that->onRuntimeError(BOOL(fFatal), Bstr(pszErrorId).raw(),
8191 Bstr(message).raw());
8192
8193 LogFlowFuncLeave();
8194}
8195
8196/**
8197 * Captures USB devices that match filters of the VM.
8198 * Called at VM startup.
8199 *
8200 * @param pVM The VM handle.
8201 *
8202 * @note The caller must lock this object for writing.
8203 */
8204HRESULT Console::captureUSBDevices(PVM pVM)
8205{
8206 LogFlowThisFunc(("\n"));
8207
8208 /* sanity check */
8209 ComAssertRet(isWriteLockOnCurrentThread(), E_FAIL);
8210
8211 /* If the machine has an USB controller, ask the USB proxy service to
8212 * capture devices */
8213 PPDMIBASE pBase;
8214 int vrc = PDMR3QueryLun(pVM, "usb-ohci", 0, 0, &pBase);
8215 if (RT_SUCCESS(vrc))
8216 {
8217 /* leave the lock before calling Host in VBoxSVC since Host may call
8218 * us back from under its lock (e.g. onUSBDeviceAttach()) which would
8219 * produce an inter-process dead-lock otherwise. */
8220 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8221 alock.leave();
8222
8223 HRESULT hrc = mControl->AutoCaptureUSBDevices();
8224 ComAssertComRCRetRC(hrc);
8225 }
8226 else if ( vrc == VERR_PDM_DEVICE_NOT_FOUND
8227 || vrc == VERR_PDM_DEVICE_INSTANCE_NOT_FOUND)
8228 vrc = VINF_SUCCESS;
8229 else
8230 AssertRC(vrc);
8231
8232 return RT_SUCCESS(vrc) ? S_OK : E_FAIL;
8233}
8234
8235
8236/**
8237 * Detach all USB device which are attached to the VM for the
8238 * purpose of clean up and such like.
8239 *
8240 * @note The caller must lock this object for writing.
8241 */
8242void Console::detachAllUSBDevices(bool aDone)
8243{
8244 LogFlowThisFunc(("aDone=%RTbool\n", aDone));
8245
8246 /* sanity check */
8247 AssertReturnVoid(isWriteLockOnCurrentThread());
8248
8249 mUSBDevices.clear();
8250
8251 /* leave the lock before calling Host in VBoxSVC since Host may call
8252 * us back from under its lock (e.g. onUSBDeviceAttach()) which would
8253 * produce an inter-process dead-lock otherwise. */
8254 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8255 alock.leave();
8256
8257 mControl->DetachAllUSBDevices(aDone);
8258}
8259
8260/**
8261 * @note Locks this object for writing.
8262 */
8263void Console::processRemoteUSBDevices(uint32_t u32ClientId, VRDEUSBDEVICEDESC *pDevList, uint32_t cbDevList)
8264{
8265 LogFlowThisFuncEnter();
8266 LogFlowThisFunc(("u32ClientId = %d, pDevList=%p, cbDevList = %d\n", u32ClientId, pDevList, cbDevList));
8267
8268 AutoCaller autoCaller(this);
8269 if (!autoCaller.isOk())
8270 {
8271 /* Console has been already uninitialized, deny request */
8272 AssertMsgFailed(("Console is already uninitialized\n"));
8273 LogFlowThisFunc(("Console is already uninitialized\n"));
8274 LogFlowThisFuncLeave();
8275 return;
8276 }
8277
8278 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8279
8280 /*
8281 * Mark all existing remote USB devices as dirty.
8282 */
8283 for (RemoteUSBDeviceList::iterator it = mRemoteUSBDevices.begin();
8284 it != mRemoteUSBDevices.end();
8285 ++it)
8286 {
8287 (*it)->dirty(true);
8288 }
8289
8290 /*
8291 * Process the pDevList and add devices those are not already in the mRemoteUSBDevices list.
8292 */
8293 /** @todo (sunlover) REMOTE_USB Strict validation of the pDevList. */
8294 VRDEUSBDEVICEDESC *e = pDevList;
8295
8296 /* The cbDevList condition must be checked first, because the function can
8297 * receive pDevList = NULL and cbDevList = 0 on client disconnect.
8298 */
8299 while (cbDevList >= 2 && e->oNext)
8300 {
8301 /* Sanitize incoming strings in case they aren't valid UTF-8. */
8302 if (e->oManufacturer)
8303 RTStrPurgeEncoding((char *)e + e->oManufacturer);
8304 if (e->oProduct)
8305 RTStrPurgeEncoding((char *)e + e->oProduct);
8306 if (e->oSerialNumber)
8307 RTStrPurgeEncoding((char *)e + e->oSerialNumber);
8308
8309 LogFlowThisFunc(("vendor %04X, product %04X, name = %s\n",
8310 e->idVendor, e->idProduct,
8311 e->oProduct? (char *)e + e->oProduct: ""));
8312
8313 bool fNewDevice = true;
8314
8315 for (RemoteUSBDeviceList::iterator it = mRemoteUSBDevices.begin();
8316 it != mRemoteUSBDevices.end();
8317 ++it)
8318 {
8319 if ((*it)->devId() == e->id
8320 && (*it)->clientId() == u32ClientId)
8321 {
8322 /* The device is already in the list. */
8323 (*it)->dirty(false);
8324 fNewDevice = false;
8325 break;
8326 }
8327 }
8328
8329 if (fNewDevice)
8330 {
8331 LogRel(("Remote USB: ++++ Vendor %04X. Product %04X. Name = [%s].\n",
8332 e->idVendor, e->idProduct, e->oProduct? (char *)e + e->oProduct: ""));
8333
8334 /* Create the device object and add the new device to list. */
8335 ComObjPtr<RemoteUSBDevice> pUSBDevice;
8336 pUSBDevice.createObject();
8337 pUSBDevice->init(u32ClientId, e);
8338
8339 mRemoteUSBDevices.push_back(pUSBDevice);
8340
8341 /* Check if the device is ok for current USB filters. */
8342 BOOL fMatched = FALSE;
8343 ULONG fMaskedIfs = 0;
8344
8345 HRESULT hrc = mControl->RunUSBDeviceFilters(pUSBDevice, &fMatched, &fMaskedIfs);
8346
8347 AssertComRC(hrc);
8348
8349 LogFlowThisFunc(("USB filters return %d %#x\n", fMatched, fMaskedIfs));
8350
8351 if (fMatched)
8352 {
8353 hrc = onUSBDeviceAttach(pUSBDevice, NULL, fMaskedIfs);
8354
8355 /// @todo (r=dmik) warning reporting subsystem
8356
8357 if (hrc == S_OK)
8358 {
8359 LogFlowThisFunc(("Device attached\n"));
8360 pUSBDevice->captured(true);
8361 }
8362 }
8363 }
8364
8365 if (cbDevList < e->oNext)
8366 {
8367 LogWarningThisFunc(("cbDevList %d > oNext %d\n",
8368 cbDevList, e->oNext));
8369 break;
8370 }
8371
8372 cbDevList -= e->oNext;
8373
8374 e = (VRDEUSBDEVICEDESC *)((uint8_t *)e + e->oNext);
8375 }
8376
8377 /*
8378 * Remove dirty devices, that is those which are not reported by the server anymore.
8379 */
8380 for (;;)
8381 {
8382 ComObjPtr<RemoteUSBDevice> pUSBDevice;
8383
8384 RemoteUSBDeviceList::iterator it = mRemoteUSBDevices.begin();
8385 while (it != mRemoteUSBDevices.end())
8386 {
8387 if ((*it)->dirty())
8388 {
8389 pUSBDevice = *it;
8390 break;
8391 }
8392
8393 ++ it;
8394 }
8395
8396 if (!pUSBDevice)
8397 {
8398 break;
8399 }
8400
8401 USHORT vendorId = 0;
8402 pUSBDevice->COMGETTER(VendorId)(&vendorId);
8403
8404 USHORT productId = 0;
8405 pUSBDevice->COMGETTER(ProductId)(&productId);
8406
8407 Bstr product;
8408 pUSBDevice->COMGETTER(Product)(product.asOutParam());
8409
8410 LogRel(("Remote USB: ---- Vendor %04X. Product %04X. Name = [%ls].\n",
8411 vendorId, productId, product.raw()));
8412
8413 /* Detach the device from VM. */
8414 if (pUSBDevice->captured())
8415 {
8416 Bstr uuid;
8417 pUSBDevice->COMGETTER(Id)(uuid.asOutParam());
8418 onUSBDeviceDetach(uuid.raw(), NULL);
8419 }
8420
8421 /* And remove it from the list. */
8422 mRemoteUSBDevices.erase(it);
8423 }
8424
8425 LogFlowThisFuncLeave();
8426}
8427
8428/**
8429 * Progress cancelation callback for fault tolerance VM poweron
8430 */
8431static void faultToleranceProgressCancelCallback(void *pvUser)
8432{
8433 PVM pVM = (PVM)pvUser;
8434
8435 if (pVM)
8436 FTMR3CancelStandby(pVM);
8437}
8438
8439/**
8440 * Thread function which starts the VM (also from saved state) and
8441 * track progress.
8442 *
8443 * @param Thread The thread id.
8444 * @param pvUser Pointer to a VMPowerUpTask structure.
8445 * @return VINF_SUCCESS (ignored).
8446 *
8447 * @note Locks the Console object for writing.
8448 */
8449/*static*/
8450DECLCALLBACK(int) Console::powerUpThread(RTTHREAD Thread, void *pvUser)
8451{
8452 LogFlowFuncEnter();
8453
8454 std::auto_ptr<VMPowerUpTask> task(static_cast<VMPowerUpTask *>(pvUser));
8455 AssertReturn(task.get(), VERR_INVALID_PARAMETER);
8456
8457 AssertReturn(!task->mConsole.isNull(), VERR_INVALID_PARAMETER);
8458 AssertReturn(!task->mProgress.isNull(), VERR_INVALID_PARAMETER);
8459
8460 VirtualBoxBase::initializeComForThread();
8461
8462 HRESULT rc = S_OK;
8463 int vrc = VINF_SUCCESS;
8464
8465 /* Set up a build identifier so that it can be seen from core dumps what
8466 * exact build was used to produce the core. */
8467 static char saBuildID[40];
8468 RTStrPrintf(saBuildID, sizeof(saBuildID), "%s%s%s%s VirtualBox %s r%u %s%s%s%s",
8469 "BU", "IL", "DI", "D", VBOX_VERSION_STRING, RTBldCfgRevision(), "BU", "IL", "DI", "D");
8470
8471 ComObjPtr<Console> pConsole = task->mConsole;
8472
8473 /* Note: no need to use addCaller() because VMPowerUpTask does that */
8474
8475 /* The lock is also used as a signal from the task initiator (which
8476 * releases it only after RTThreadCreate()) that we can start the job */
8477 AutoWriteLock alock(pConsole COMMA_LOCKVAL_SRC_POS);
8478
8479 /* sanity */
8480 Assert(pConsole->mpUVM == NULL);
8481
8482 try
8483 {
8484 // Create the VMM device object, which starts the HGCM thread; do this only
8485 // once for the console, for the pathological case that the same console
8486 // object is used to power up a VM twice. VirtualBox 4.0: we now do that
8487 // here instead of the Console constructor (see Console::init())
8488 if (!pConsole->m_pVMMDev)
8489 {
8490 pConsole->m_pVMMDev = new VMMDev(pConsole);
8491 AssertReturn(pConsole->m_pVMMDev, E_FAIL);
8492 }
8493
8494 /* wait for auto reset ops to complete so that we can successfully lock
8495 * the attached hard disks by calling LockMedia() below */
8496 for (VMPowerUpTask::ProgressList::const_iterator
8497 it = task->hardDiskProgresses.begin();
8498 it != task->hardDiskProgresses.end(); ++ it)
8499 {
8500 HRESULT rc2 = (*it)->WaitForCompletion(-1);
8501 AssertComRC(rc2);
8502 }
8503
8504 /*
8505 * Lock attached media. This method will also check their accessibility.
8506 * If we're a teleporter, we'll have to postpone this action so we can
8507 * migrate between local processes.
8508 *
8509 * Note! The media will be unlocked automatically by
8510 * SessionMachine::setMachineState() when the VM is powered down.
8511 */
8512 if ( !task->mTeleporterEnabled
8513 && task->mEnmFaultToleranceState != FaultToleranceState_Standby)
8514 {
8515 rc = pConsole->mControl->LockMedia();
8516 if (FAILED(rc)) throw rc;
8517 }
8518
8519 /* Create the VRDP server. In case of headless operation, this will
8520 * also create the framebuffer, required at VM creation.
8521 */
8522 ConsoleVRDPServer *server = pConsole->consoleVRDPServer();
8523 Assert(server);
8524
8525 /* Does VRDP server call Console from the other thread?
8526 * Not sure (and can change), so leave the lock just in case.
8527 */
8528 alock.leave();
8529 vrc = server->Launch();
8530 alock.enter();
8531
8532 if (vrc == VERR_NET_ADDRESS_IN_USE)
8533 {
8534 Utf8Str errMsg;
8535 Bstr bstr;
8536 pConsole->mVRDEServer->GetVRDEProperty(Bstr("TCP/Ports").raw(), bstr.asOutParam());
8537 Utf8Str ports = bstr;
8538 errMsg = Utf8StrFmt(tr("VirtualBox Remote Desktop Extension server can't bind to the port: %s"),
8539 ports.c_str());
8540 LogRel(("VRDE: Warning: failed to launch VRDE server (%Rrc): '%s'\n",
8541 vrc, errMsg.c_str()));
8542 }
8543 else if (vrc == VINF_NOT_SUPPORTED)
8544 {
8545 /* This means that the VRDE is not installed. */
8546 LogRel(("VRDE: VirtualBox Remote Desktop Extension is not available.\n"));
8547 }
8548 else if (RT_FAILURE(vrc))
8549 {
8550 /* Fail, if the server is installed but can't start. */
8551 Utf8Str errMsg;
8552 switch (vrc)
8553 {
8554 case VERR_FILE_NOT_FOUND:
8555 {
8556 /* VRDE library file is missing. */
8557 errMsg = Utf8StrFmt(tr("Could not find the VirtualBox Remote Desktop Extension library."));
8558 break;
8559 }
8560 default:
8561 errMsg = Utf8StrFmt(tr("Failed to launch Remote Desktop Extension server (%Rrc)"),
8562 vrc);
8563 }
8564 LogRel(("VRDE: Failed: (%Rrc), error message: '%s'\n",
8565 vrc, errMsg.c_str()));
8566 throw setErrorStatic(E_FAIL, errMsg.c_str());
8567 }
8568
8569 ComPtr<IMachine> pMachine = pConsole->machine();
8570 ULONG cCpus = 1;
8571 pMachine->COMGETTER(CPUCount)(&cCpus);
8572
8573 /*
8574 * Create the VM
8575 */
8576 PVM pVM;
8577 /*
8578 * leave the lock since EMT will call Console. It's safe because
8579 * mMachineState is either Starting or Restoring state here.
8580 */
8581 alock.leave();
8582
8583 vrc = VMR3Create(cCpus,
8584 pConsole->mpVmm2UserMethods,
8585 Console::genericVMSetErrorCallback,
8586 &task->mErrorMsg,
8587 task->mConfigConstructor,
8588 static_cast<Console *>(pConsole),
8589 &pVM);
8590
8591 alock.enter();
8592
8593 /* Enable client connections to the server. */
8594 pConsole->consoleVRDPServer()->EnableConnections();
8595
8596 if (RT_SUCCESS(vrc))
8597 {
8598 do
8599 {
8600 /*
8601 * Register our load/save state file handlers
8602 */
8603 vrc = SSMR3RegisterExternal(pVM, sSSMConsoleUnit, 0 /*iInstance*/, sSSMConsoleVer, 0 /* cbGuess */,
8604 NULL, NULL, NULL,
8605 NULL, saveStateFileExec, NULL,
8606 NULL, loadStateFileExec, NULL,
8607 static_cast<Console *>(pConsole));
8608 AssertRCBreak(vrc);
8609
8610 vrc = static_cast<Console *>(pConsole)->getDisplay()->registerSSM(pVM);
8611 AssertRC(vrc);
8612 if (RT_FAILURE(vrc))
8613 break;
8614
8615 /*
8616 * Synchronize debugger settings
8617 */
8618 MachineDebugger *machineDebugger = pConsole->getMachineDebugger();
8619 if (machineDebugger)
8620 machineDebugger->flushQueuedSettings();
8621
8622 /*
8623 * Shared Folders
8624 */
8625 if (pConsole->m_pVMMDev->isShFlActive())
8626 {
8627 /* Does the code below call Console from the other thread?
8628 * Not sure, so leave the lock just in case. */
8629 alock.leave();
8630
8631 for (SharedFolderDataMap::const_iterator it = task->mSharedFolders.begin();
8632 it != task->mSharedFolders.end();
8633 ++it)
8634 {
8635 const SharedFolderData &d = it->second;
8636 rc = pConsole->createSharedFolder(it->first, d);
8637 if (FAILED(rc))
8638 {
8639 ErrorInfoKeeper eik;
8640 setVMRuntimeErrorCallbackF(pVM, pConsole, 0, "BrokenSharedFolder",
8641 N_("The shared folder '%s' could not be set up: %ls.\n"
8642 "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"),
8643 it->first.c_str(), eik.getText().raw());
8644 }
8645 }
8646 if (FAILED(rc))
8647 rc = S_OK; // do not fail with broken shared folders
8648
8649 /* enter the lock again */
8650 alock.enter();
8651 }
8652
8653 /*
8654 * Capture USB devices.
8655 */
8656 rc = pConsole->captureUSBDevices(pVM);
8657 if (FAILED(rc)) break;
8658
8659 /* leave the lock before a lengthy operation */
8660 alock.leave();
8661
8662 /* Load saved state? */
8663 if (task->mSavedStateFile.length())
8664 {
8665 LogFlowFunc(("Restoring saved state from '%s'...\n",
8666 task->mSavedStateFile.c_str()));
8667
8668 vrc = VMR3LoadFromFile(pVM,
8669 task->mSavedStateFile.c_str(),
8670 Console::stateProgressCallback,
8671 static_cast<IProgress *>(task->mProgress));
8672
8673 if (RT_SUCCESS(vrc))
8674 {
8675 if (task->mStartPaused)
8676 /* done */
8677 pConsole->setMachineState(MachineState_Paused);
8678 else
8679 {
8680 /* Start/Resume the VM execution */
8681#ifdef VBOX_WITH_EXTPACK
8682 vrc = pConsole->mptrExtPackManager->callAllVmPowerOnHooks(pConsole, pVM);
8683#endif
8684 if (RT_SUCCESS(vrc))
8685 vrc = VMR3Resume(pVM);
8686 AssertLogRelRC(vrc);
8687 }
8688 }
8689
8690 /* Power off in case we failed loading or resuming the VM */
8691 if (RT_FAILURE(vrc))
8692 {
8693 int vrc2 = VMR3PowerOff(pVM); AssertLogRelRC(vrc2);
8694#ifdef VBOX_WITH_EXTPACK
8695 pConsole->mptrExtPackManager->callAllVmPowerOffHooks(pConsole, pVM);
8696#endif
8697 }
8698 }
8699 else if (task->mTeleporterEnabled)
8700 {
8701 /* -> ConsoleImplTeleporter.cpp */
8702 bool fPowerOffOnFailure;
8703 rc = pConsole->teleporterTrg(VMR3GetUVM(pVM), pMachine, &task->mErrorMsg, task->mStartPaused,
8704 task->mProgress, &fPowerOffOnFailure);
8705 if (FAILED(rc) && fPowerOffOnFailure)
8706 {
8707 ErrorInfoKeeper eik;
8708 int vrc2 = VMR3PowerOff(pVM); AssertLogRelRC(vrc2);
8709#ifdef VBOX_WITH_EXTPACK
8710 pConsole->mptrExtPackManager->callAllVmPowerOffHooks(pConsole, pVM);
8711#endif
8712 }
8713 }
8714 else if (task->mEnmFaultToleranceState != FaultToleranceState_Inactive)
8715 {
8716 /*
8717 * Get the config.
8718 */
8719 ULONG uPort;
8720 ULONG uInterval;
8721 Bstr bstrAddress, bstrPassword;
8722
8723 rc = pMachine->COMGETTER(FaultTolerancePort)(&uPort);
8724 if (SUCCEEDED(rc))
8725 {
8726 rc = pMachine->COMGETTER(FaultToleranceSyncInterval)(&uInterval);
8727 if (SUCCEEDED(rc))
8728 rc = pMachine->COMGETTER(FaultToleranceAddress)(bstrAddress.asOutParam());
8729 if (SUCCEEDED(rc))
8730 rc = pMachine->COMGETTER(FaultTolerancePassword)(bstrPassword.asOutParam());
8731 }
8732 if (task->mProgress->setCancelCallback(faultToleranceProgressCancelCallback, pVM))
8733 {
8734 if (SUCCEEDED(rc))
8735 {
8736 Utf8Str strAddress(bstrAddress);
8737 const char *pszAddress = strAddress.isEmpty() ? NULL : strAddress.c_str();
8738 Utf8Str strPassword(bstrPassword);
8739 const char *pszPassword = strPassword.isEmpty() ? NULL : strPassword.c_str();
8740
8741 /* Power on the FT enabled VM. */
8742#ifdef VBOX_WITH_EXTPACK
8743 vrc = pConsole->mptrExtPackManager->callAllVmPowerOnHooks(pConsole, pVM);
8744#endif
8745 if (RT_SUCCESS(vrc))
8746 vrc = FTMR3PowerOn(pVM,
8747 task->mEnmFaultToleranceState == FaultToleranceState_Master /* fMaster */,
8748 uInterval,
8749 pszAddress,
8750 uPort,
8751 pszPassword);
8752 AssertLogRelRC(vrc);
8753 }
8754 task->mProgress->setCancelCallback(NULL, NULL);
8755 }
8756 else
8757 rc = E_FAIL;
8758 }
8759 else if (task->mStartPaused)
8760 /* done */
8761 pConsole->setMachineState(MachineState_Paused);
8762 else
8763 {
8764 /* Power on the VM (i.e. start executing) */
8765#ifdef VBOX_WITH_EXTPACK
8766 vrc = pConsole->mptrExtPackManager->callAllVmPowerOnHooks(pConsole, pVM);
8767#endif
8768 if (RT_SUCCESS(vrc))
8769 vrc = VMR3PowerOn(pVM);
8770 AssertLogRelRC(vrc);
8771 }
8772
8773 /* enter the lock again */
8774 alock.enter();
8775 }
8776 while (0);
8777
8778 /* On failure, destroy the VM */
8779 if (FAILED(rc) || RT_FAILURE(vrc))
8780 {
8781 /* preserve existing error info */
8782 ErrorInfoKeeper eik;
8783
8784 /* powerDown() will call VMR3Destroy() and do all necessary
8785 * cleanup (VRDP, USB devices) */
8786 HRESULT rc2 = pConsole->powerDown();
8787 AssertComRC(rc2);
8788 }
8789 else
8790 {
8791 /*
8792 * Deregister the VMSetError callback. This is necessary as the
8793 * pfnVMAtError() function passed to VMR3Create() is supposed to
8794 * be sticky but our error callback isn't.
8795 */
8796 alock.leave();
8797 VMR3AtErrorDeregister(pVM, Console::genericVMSetErrorCallback, &task->mErrorMsg);
8798 /** @todo register another VMSetError callback? */
8799 alock.enter();
8800 }
8801 }
8802 else
8803 {
8804 /*
8805 * If VMR3Create() failed it has released the VM memory.
8806 */
8807 VMR3ReleaseUVM(pConsole->mpUVM);
8808 pConsole->mpUVM = NULL;
8809 }
8810
8811 if (SUCCEEDED(rc) && RT_FAILURE(vrc))
8812 {
8813 /* If VMR3Create() or one of the other calls in this function fail,
8814 * an appropriate error message has been set in task->mErrorMsg.
8815 * However since that happens via a callback, the rc status code in
8816 * this function is not updated.
8817 */
8818 if (!task->mErrorMsg.length())
8819 {
8820 /* If the error message is not set but we've got a failure,
8821 * convert the VBox status code into a meaningful error message.
8822 * This becomes unused once all the sources of errors set the
8823 * appropriate error message themselves.
8824 */
8825 AssertMsgFailed(("Missing error message during powerup for status code %Rrc\n", vrc));
8826 task->mErrorMsg = Utf8StrFmt(tr("Failed to start VM execution (%Rrc)"),
8827 vrc);
8828 }
8829
8830 /* Set the error message as the COM error.
8831 * Progress::notifyComplete() will pick it up later. */
8832 throw setErrorStatic(E_FAIL, task->mErrorMsg.c_str());
8833 }
8834 }
8835 catch (HRESULT aRC) { rc = aRC; }
8836
8837 if ( pConsole->mMachineState == MachineState_Starting
8838 || pConsole->mMachineState == MachineState_Restoring
8839 || pConsole->mMachineState == MachineState_TeleportingIn
8840 )
8841 {
8842 /* We are still in the Starting/Restoring state. This means one of:
8843 *
8844 * 1) we failed before VMR3Create() was called;
8845 * 2) VMR3Create() failed.
8846 *
8847 * In both cases, there is no need to call powerDown(), but we still
8848 * need to go back to the PoweredOff/Saved state. Reuse
8849 * vmstateChangeCallback() for that purpose.
8850 */
8851
8852 /* preserve existing error info */
8853 ErrorInfoKeeper eik;
8854
8855 Assert(pConsole->mpUVM == NULL);
8856 vmstateChangeCallback(NULL, VMSTATE_TERMINATED, VMSTATE_CREATING,
8857 pConsole);
8858 }
8859
8860 /*
8861 * Evaluate the final result. Note that the appropriate mMachineState value
8862 * is already set by vmstateChangeCallback() in all cases.
8863 */
8864
8865 /* leave the lock, don't need it any more */
8866 alock.leave();
8867
8868 if (SUCCEEDED(rc))
8869 {
8870 /* Notify the progress object of the success */
8871 task->mProgress->notifyComplete(S_OK);
8872 }
8873 else
8874 {
8875 /* The progress object will fetch the current error info */
8876 task->mProgress->notifyComplete(rc);
8877 LogRel(("Power up failed (vrc=%Rrc, rc=%Rhrc (%#08X))\n", vrc, rc, rc));
8878 }
8879
8880 /* Notify VBoxSVC and any waiting openRemoteSession progress object. */
8881 pConsole->mControl->EndPowerUp(rc);
8882
8883#if defined(RT_OS_WINDOWS)
8884 /* uninitialize COM */
8885 CoUninitialize();
8886#endif
8887
8888 LogFlowFuncLeave();
8889
8890 return VINF_SUCCESS;
8891}
8892
8893
8894/**
8895 * Reconfigures a medium attachment (part of taking or deleting an online snapshot).
8896 *
8897 * @param pConsole Reference to the console object.
8898 * @param pVM The VM handle.
8899 * @param lInstance The instance of the controller.
8900 * @param pcszDevice The name of the controller type.
8901 * @param enmBus The storage bus type of the controller.
8902 * @param fSetupMerge Whether to set up a medium merge
8903 * @param uMergeSource Merge source image index
8904 * @param uMergeTarget Merge target image index
8905 * @param aMediumAtt The medium attachment.
8906 * @param aMachineState The current machine state.
8907 * @param phrc Where to store com error - only valid if we return VERR_GENERAL_FAILURE.
8908 * @return VBox status code.
8909 */
8910/* static */
8911DECLCALLBACK(int) Console::reconfigureMediumAttachment(Console *pConsole,
8912 PVM pVM,
8913 const char *pcszDevice,
8914 unsigned uInstance,
8915 StorageBus_T enmBus,
8916 bool fUseHostIOCache,
8917 bool fBuiltinIoCache,
8918 bool fSetupMerge,
8919 unsigned uMergeSource,
8920 unsigned uMergeTarget,
8921 IMediumAttachment *aMediumAtt,
8922 MachineState_T aMachineState,
8923 HRESULT *phrc)
8924{
8925 LogFlowFunc(("pVM=%p aMediumAtt=%p phrc=%p\n", pVM, aMediumAtt, phrc));
8926
8927 int rc;
8928 HRESULT hrc;
8929 Bstr bstr;
8930 *phrc = S_OK;
8931#define RC_CHECK() do { if (RT_FAILURE(rc)) { AssertMsgFailed(("rc=%Rrc\n", rc)); return rc; } } while (0)
8932#define H() do { if (FAILED(hrc)) { AssertMsgFailed(("hrc=%Rhrc (%#x)\n", hrc, hrc)); *phrc = hrc; return VERR_GENERAL_FAILURE; } } while (0)
8933
8934 /* Ignore attachments other than hard disks, since at the moment they are
8935 * not subject to snapshotting in general. */
8936 DeviceType_T lType;
8937 hrc = aMediumAtt->COMGETTER(Type)(&lType); H();
8938 if (lType != DeviceType_HardDisk)
8939 return VINF_SUCCESS;
8940
8941 /* Determine the base path for the device instance. */
8942 PCFGMNODE pCtlInst;
8943 pCtlInst = CFGMR3GetChildF(CFGMR3GetRoot(pVM), "Devices/%s/%u/", pcszDevice, uInstance);
8944 AssertReturn(pCtlInst, VERR_INTERNAL_ERROR);
8945
8946 /* Update the device instance configuration. */
8947 rc = pConsole->configMediumAttachment(pCtlInst,
8948 pcszDevice,
8949 uInstance,
8950 enmBus,
8951 fUseHostIOCache,
8952 fBuiltinIoCache,
8953 fSetupMerge,
8954 uMergeSource,
8955 uMergeTarget,
8956 aMediumAtt,
8957 aMachineState,
8958 phrc,
8959 true /* fAttachDetach */,
8960 false /* fForceUnmount */,
8961 false /* fHotplug */,
8962 pVM,
8963 NULL /* paLedDevType */);
8964 /** @todo this dumps everything attached to this device instance, which
8965 * is more than necessary. Dumping the changed LUN would be enough. */
8966 CFGMR3Dump(pCtlInst);
8967 RC_CHECK();
8968
8969#undef RC_CHECK
8970#undef H
8971
8972 LogFlowFunc(("Returns success\n"));
8973 return VINF_SUCCESS;
8974}
8975
8976/**
8977 * Progress cancelation callback employed by Console::fntTakeSnapshotWorker.
8978 */
8979static void takesnapshotProgressCancelCallback(void *pvUser)
8980{
8981 PUVM pUVM = (PUVM)pvUser;
8982 SSMR3Cancel(VMR3GetVM(pUVM));
8983}
8984
8985/**
8986 * Worker thread created by Console::TakeSnapshot.
8987 * @param Thread The current thread (ignored).
8988 * @param pvUser The task.
8989 * @return VINF_SUCCESS (ignored).
8990 */
8991/*static*/
8992DECLCALLBACK(int) Console::fntTakeSnapshotWorker(RTTHREAD Thread, void *pvUser)
8993{
8994 VMTakeSnapshotTask *pTask = (VMTakeSnapshotTask*)pvUser;
8995
8996 // taking a snapshot consists of the following:
8997
8998 // 1) creating a diff image for each virtual hard disk, into which write operations go after
8999 // the snapshot has been created (done in VBoxSVC, in SessionMachine::BeginTakingSnapshot)
9000 // 2) creating a Snapshot object with the state of the machine (hardware + storage,
9001 // done in VBoxSVC, also in SessionMachine::BeginTakingSnapshot)
9002 // 3) saving the state of the virtual machine (here, in the VM process, if the machine is online)
9003
9004 Console *that = pTask->mConsole;
9005 bool fBeganTakingSnapshot = false;
9006 bool fSuspenededBySave = false;
9007
9008 AutoCaller autoCaller(that);
9009 if (FAILED(autoCaller.rc()))
9010 {
9011 that->mptrCancelableProgress.setNull();
9012 return autoCaller.rc();
9013 }
9014
9015 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
9016
9017 HRESULT rc = S_OK;
9018
9019 try
9020 {
9021 /* STEP 1 + 2:
9022 * request creating the diff images on the server and create the snapshot object
9023 * (this will set the machine state to Saving on the server to block
9024 * others from accessing this machine)
9025 */
9026 rc = that->mControl->BeginTakingSnapshot(that,
9027 pTask->bstrName.raw(),
9028 pTask->bstrDescription.raw(),
9029 pTask->mProgress,
9030 pTask->fTakingSnapshotOnline,
9031 pTask->bstrSavedStateFile.asOutParam());
9032 if (FAILED(rc))
9033 throw rc;
9034
9035 fBeganTakingSnapshot = true;
9036
9037 /*
9038 * state file is non-null only when the VM is paused
9039 * (i.e. creating a snapshot online)
9040 */
9041 bool f = (!pTask->bstrSavedStateFile.isEmpty() && pTask->fTakingSnapshotOnline)
9042 || ( pTask->bstrSavedStateFile.isEmpty() && !pTask->fTakingSnapshotOnline);
9043 if (!f)
9044 throw setErrorStatic(E_FAIL, "Invalid state of saved state file");
9045
9046 /* sync the state with the server */
9047 if (pTask->lastMachineState == MachineState_Running)
9048 that->setMachineStateLocally(MachineState_LiveSnapshotting);
9049 else
9050 that->setMachineStateLocally(MachineState_Saving);
9051
9052 // STEP 3: save the VM state (if online)
9053 if (pTask->fTakingSnapshotOnline)
9054 {
9055 Utf8Str strSavedStateFile(pTask->bstrSavedStateFile);
9056
9057 SafeVMPtr ptrVM(that);
9058 if (!ptrVM.isOk())
9059 throw ptrVM.rc();
9060
9061 pTask->mProgress->SetNextOperation(Bstr(tr("Saving the machine state")).raw(),
9062 pTask->ulMemSize); // operation weight, same as computed when setting up progress object
9063 pTask->mProgress->setCancelCallback(takesnapshotProgressCancelCallback, ptrVM.rawUVM());
9064
9065 alock.leave();
9066 LogFlowFunc(("VMR3Save...\n"));
9067 int vrc = VMR3Save(ptrVM,
9068 strSavedStateFile.c_str(),
9069 true /*fContinueAfterwards*/,
9070 Console::stateProgressCallback,
9071 static_cast<IProgress *>(pTask->mProgress),
9072 &fSuspenededBySave);
9073 alock.enter();
9074 if (RT_FAILURE(vrc))
9075 throw setErrorStatic(E_FAIL,
9076 tr("Failed to save the machine state to '%s' (%Rrc)"),
9077 strSavedStateFile.c_str(), vrc);
9078
9079 pTask->mProgress->setCancelCallback(NULL, NULL);
9080 if (!pTask->mProgress->notifyPointOfNoReturn())
9081 throw setErrorStatic(E_FAIL, tr("Canceled"));
9082 that->mptrCancelableProgress.setNull();
9083
9084 // STEP 4: reattach hard disks
9085 LogFlowFunc(("Reattaching new differencing hard disks...\n"));
9086
9087 pTask->mProgress->SetNextOperation(Bstr(tr("Reconfiguring medium attachments")).raw(),
9088 1); // operation weight, same as computed when setting up progress object
9089
9090 com::SafeIfaceArray<IMediumAttachment> atts;
9091 rc = that->mMachine->COMGETTER(MediumAttachments)(ComSafeArrayAsOutParam(atts));
9092 if (FAILED(rc))
9093 throw rc;
9094
9095 for (size_t i = 0;
9096 i < atts.size();
9097 ++i)
9098 {
9099 ComPtr<IStorageController> pStorageController;
9100 Bstr controllerName;
9101 ULONG lInstance;
9102 StorageControllerType_T enmController;
9103 StorageBus_T enmBus;
9104 BOOL fUseHostIOCache;
9105
9106 /*
9107 * We can't pass a storage controller object directly
9108 * (g++ complains about not being able to pass non POD types through '...')
9109 * so we have to query needed values here and pass them.
9110 */
9111 rc = atts[i]->COMGETTER(Controller)(controllerName.asOutParam());
9112 if (FAILED(rc))
9113 throw rc;
9114
9115 rc = that->mMachine->GetStorageControllerByName(controllerName.raw(),
9116 pStorageController.asOutParam());
9117 if (FAILED(rc))
9118 throw rc;
9119
9120 rc = pStorageController->COMGETTER(ControllerType)(&enmController);
9121 if (FAILED(rc))
9122 throw rc;
9123 rc = pStorageController->COMGETTER(Instance)(&lInstance);
9124 if (FAILED(rc))
9125 throw rc;
9126 rc = pStorageController->COMGETTER(Bus)(&enmBus);
9127 if (FAILED(rc))
9128 throw rc;
9129 rc = pStorageController->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
9130 if (FAILED(rc))
9131 throw rc;
9132
9133 const char *pcszDevice = Console::convertControllerTypeToDev(enmController);
9134
9135 BOOL fBuiltinIoCache;
9136 rc = that->mMachine->COMGETTER(IoCacheEnabled)(&fBuiltinIoCache);
9137 if (FAILED(rc))
9138 throw rc;
9139
9140 /*
9141 * don't leave the lock since reconfigureMediumAttachment
9142 * isn't going to need the Console lock.
9143 */
9144 vrc = VMR3ReqCallWait(ptrVM,
9145 VMCPUID_ANY,
9146 (PFNRT)reconfigureMediumAttachment,
9147 13,
9148 that,
9149 ptrVM.raw(),
9150 pcszDevice,
9151 lInstance,
9152 enmBus,
9153 fUseHostIOCache,
9154 fBuiltinIoCache,
9155 false /* fSetupMerge */,
9156 0 /* uMergeSource */,
9157 0 /* uMergeTarget */,
9158 atts[i],
9159 that->mMachineState,
9160 &rc);
9161 if (RT_FAILURE(vrc))
9162 throw setErrorStatic(E_FAIL, Console::tr("%Rrc"), vrc);
9163 if (FAILED(rc))
9164 throw rc;
9165 }
9166 }
9167
9168 /*
9169 * finalize the requested snapshot object.
9170 * This will reset the machine state to the state it had right
9171 * before calling mControl->BeginTakingSnapshot().
9172 */
9173 rc = that->mControl->EndTakingSnapshot(TRUE /*aSuccess*/);
9174 // do not throw rc here because we can't call EndTakingSnapshot() twice
9175 LogFlowFunc(("EndTakingSnapshot -> %Rhrc [mMachineState=%s]\n", rc, Global::stringifyMachineState(that->mMachineState)));
9176 }
9177 catch (HRESULT rcThrown)
9178 {
9179 /* preserve existing error info */
9180 ErrorInfoKeeper eik;
9181
9182 if (fBeganTakingSnapshot)
9183 that->mControl->EndTakingSnapshot(FALSE /*aSuccess*/);
9184
9185 rc = rcThrown;
9186 LogFunc(("Caught %Rhrc [mMachineState=%s]\n", rc, Global::stringifyMachineState(that->mMachineState)));
9187 }
9188 Assert(alock.isWriteLockOnCurrentThread());
9189
9190 if (FAILED(rc)) /* Must come before calling setMachineState. */
9191 pTask->mProgress->notifyComplete(rc);
9192
9193 /*
9194 * Fix up the machine state.
9195 *
9196 * For live snapshots we do all the work, for the two other variations we
9197 * just update the local copy.
9198 */
9199 MachineState_T enmMachineState;
9200 that->mMachine->COMGETTER(State)(&enmMachineState);
9201 if ( that->mMachineState == MachineState_LiveSnapshotting
9202 || that->mMachineState == MachineState_Saving)
9203 {
9204
9205 if (!pTask->fTakingSnapshotOnline)
9206 that->setMachineStateLocally(pTask->lastMachineState);
9207 else if (SUCCEEDED(rc))
9208 {
9209 Assert( pTask->lastMachineState == MachineState_Running
9210 || pTask->lastMachineState == MachineState_Paused);
9211 Assert(that->mMachineState == MachineState_Saving);
9212 if (pTask->lastMachineState == MachineState_Running)
9213 {
9214 LogFlowFunc(("VMR3Resume...\n"));
9215 SafeVMPtr ptrVM(that);
9216 alock.leave();
9217 int vrc = VMR3Resume(ptrVM);
9218 alock.enter();
9219 if (RT_FAILURE(vrc))
9220 {
9221 rc = setErrorStatic(VBOX_E_VM_ERROR, tr("Could not resume the machine execution (%Rrc)"), vrc);
9222 pTask->mProgress->notifyComplete(rc);
9223 if (that->mMachineState == MachineState_Saving)
9224 that->setMachineStateLocally(MachineState_Paused);
9225 }
9226 }
9227 else
9228 that->setMachineStateLocally(MachineState_Paused);
9229 }
9230 else
9231 {
9232 /** @todo this could probably be made more generic and reused elsewhere. */
9233 /* paranoid cleanup on for a failed online snapshot. */
9234 VMSTATE enmVMState = VMR3GetStateU(that->mpUVM);
9235 switch (enmVMState)
9236 {
9237 case VMSTATE_RUNNING:
9238 case VMSTATE_RUNNING_LS:
9239 case VMSTATE_DEBUGGING:
9240 case VMSTATE_DEBUGGING_LS:
9241 case VMSTATE_POWERING_OFF:
9242 case VMSTATE_POWERING_OFF_LS:
9243 case VMSTATE_RESETTING:
9244 case VMSTATE_RESETTING_LS:
9245 Assert(!fSuspenededBySave);
9246 that->setMachineState(MachineState_Running);
9247 break;
9248
9249 case VMSTATE_GURU_MEDITATION:
9250 case VMSTATE_GURU_MEDITATION_LS:
9251 that->setMachineState(MachineState_Stuck);
9252 break;
9253
9254 case VMSTATE_FATAL_ERROR:
9255 case VMSTATE_FATAL_ERROR_LS:
9256 if (pTask->lastMachineState == MachineState_Paused)
9257 that->setMachineStateLocally(pTask->lastMachineState);
9258 else
9259 that->setMachineState(MachineState_Paused);
9260 break;
9261
9262 default:
9263 AssertMsgFailed(("%s\n", VMR3GetStateName(enmVMState)));
9264 case VMSTATE_SUSPENDED:
9265 case VMSTATE_SUSPENDED_LS:
9266 case VMSTATE_SUSPENDING:
9267 case VMSTATE_SUSPENDING_LS:
9268 case VMSTATE_SUSPENDING_EXT_LS:
9269 if (fSuspenededBySave)
9270 {
9271 Assert(pTask->lastMachineState == MachineState_Running);
9272 LogFlowFunc(("VMR3Resume (on failure)...\n"));
9273 SafeVMPtr ptrVM(that);
9274 alock.leave();
9275 int vrc = VMR3Resume(ptrVM); AssertLogRelRC(vrc);
9276 alock.enter();
9277 if (RT_FAILURE(vrc))
9278 that->setMachineState(MachineState_Paused);
9279 }
9280 else if (pTask->lastMachineState == MachineState_Paused)
9281 that->setMachineStateLocally(pTask->lastMachineState);
9282 else
9283 that->setMachineState(MachineState_Paused);
9284 break;
9285 }
9286
9287 }
9288 }
9289 /*else: somebody else has change the state... Leave it. */
9290
9291 /* check the remote state to see that we got it right. */
9292 that->mMachine->COMGETTER(State)(&enmMachineState);
9293 AssertLogRelMsg(that->mMachineState == enmMachineState,
9294 ("mMachineState=%s enmMachineState=%s\n", Global::stringifyMachineState(that->mMachineState),
9295 Global::stringifyMachineState(enmMachineState) ));
9296
9297
9298 if (SUCCEEDED(rc)) /* The failure cases are handled above. */
9299 pTask->mProgress->notifyComplete(rc);
9300
9301 delete pTask;
9302
9303 LogFlowFuncLeave();
9304 return VINF_SUCCESS;
9305}
9306
9307/**
9308 * Thread for executing the saved state operation.
9309 *
9310 * @param Thread The thread handle.
9311 * @param pvUser Pointer to a VMSaveTask structure.
9312 * @return VINF_SUCCESS (ignored).
9313 *
9314 * @note Locks the Console object for writing.
9315 */
9316/*static*/
9317DECLCALLBACK(int) Console::saveStateThread(RTTHREAD Thread, void *pvUser)
9318{
9319 LogFlowFuncEnter();
9320
9321 std::auto_ptr<VMSaveTask> task(static_cast<VMSaveTask*>(pvUser));
9322 AssertReturn(task.get(), VERR_INVALID_PARAMETER);
9323
9324 Assert(task->mSavedStateFile.length());
9325 Assert(task->mProgress.isNull());
9326 Assert(!task->mServerProgress.isNull());
9327
9328 const ComObjPtr<Console> &that = task->mConsole;
9329 Utf8Str errMsg;
9330 HRESULT rc = S_OK;
9331
9332 LogFlowFunc(("Saving the state to '%s'...\n", task->mSavedStateFile.c_str()));
9333
9334 bool fSuspenededBySave;
9335 int vrc = VMR3Save(task->mpVM,
9336 task->mSavedStateFile.c_str(),
9337 false, /*fContinueAfterwards*/
9338 Console::stateProgressCallback,
9339 static_cast<IProgress *>(task->mServerProgress),
9340 &fSuspenededBySave);
9341 if (RT_FAILURE(vrc))
9342 {
9343 errMsg = Utf8StrFmt(Console::tr("Failed to save the machine state to '%s' (%Rrc)"),
9344 task->mSavedStateFile.c_str(), vrc);
9345 rc = E_FAIL;
9346 }
9347 Assert(!fSuspenededBySave);
9348
9349 /* lock the console once we're going to access it */
9350 AutoWriteLock thatLock(that COMMA_LOCKVAL_SRC_POS);
9351
9352 /* synchronize the state with the server */
9353 if (SUCCEEDED(rc))
9354 {
9355 /*
9356 * The machine has been successfully saved, so power it down
9357 * (vmstateChangeCallback() will set state to Saved on success).
9358 * Note: we release the task's VM caller, otherwise it will
9359 * deadlock.
9360 */
9361 task->releaseVMCaller();
9362 rc = that->powerDown();
9363 }
9364
9365 /*
9366 * Finalize the requested save state procedure. In case of failure it will
9367 * reset the machine state to the state it had right before calling
9368 * mControl->BeginSavingState(). This must be the last thing because it
9369 * will set the progress to completed, and that means that the frontend
9370 * can immediately uninit the associated console object.
9371 */
9372 that->mControl->EndSavingState(rc, Bstr(errMsg).raw());
9373
9374 LogFlowFuncLeave();
9375 return VINF_SUCCESS;
9376}
9377
9378/**
9379 * Thread for powering down the Console.
9380 *
9381 * @param Thread The thread handle.
9382 * @param pvUser Pointer to the VMTask structure.
9383 * @return VINF_SUCCESS (ignored).
9384 *
9385 * @note Locks the Console object for writing.
9386 */
9387/*static*/
9388DECLCALLBACK(int) Console::powerDownThread(RTTHREAD Thread, void *pvUser)
9389{
9390 LogFlowFuncEnter();
9391
9392 std::auto_ptr<VMPowerDownTask> task(static_cast<VMPowerDownTask *>(pvUser));
9393 AssertReturn(task.get(), VERR_INVALID_PARAMETER);
9394
9395 AssertReturn(task->isOk(), VERR_GENERAL_FAILURE);
9396
9397 Assert(task->mProgress.isNull());
9398
9399 const ComObjPtr<Console> &that = task->mConsole;
9400
9401 /* Note: no need to use addCaller() to protect Console because VMTask does
9402 * that */
9403
9404 /* wait until the method tat started us returns */
9405 AutoWriteLock thatLock(that COMMA_LOCKVAL_SRC_POS);
9406
9407 /* release VM caller to avoid the powerDown() deadlock */
9408 task->releaseVMCaller();
9409
9410 that->powerDown(task->mServerProgress);
9411
9412 /* complete the operation */
9413 that->mControl->EndPoweringDown(S_OK, Bstr().raw());
9414
9415 LogFlowFuncLeave();
9416 return VINF_SUCCESS;
9417}
9418
9419
9420/**
9421 * @interface_method_impl{VMM2USERMETHODS,pfnSaveState}
9422 */
9423/*static*/ DECLCALLBACK(int)
9424Console::vmm2User_SaveState(PCVMM2USERMETHODS pThis, PUVM pUVM)
9425{
9426 Console *pConsole = ((MYVMM2USERMETHODS *)pThis)->pConsole;
9427 NOREF(pUVM);
9428
9429 /*
9430 * For now, just call SaveState. We should probably try notify the GUI so
9431 * it can pop up a progress object and stuff.
9432 */
9433 HRESULT hrc = pConsole->SaveState(NULL);
9434 return SUCCEEDED(hrc) ? VINF_SUCCESS : Global::vboxStatusCodeFromCOM(hrc);
9435}
9436
9437/**
9438 * @interface_method_impl{VMM2USERMETHODS,pfnNotifyEmtInit}
9439 */
9440/*static*/ DECLCALLBACK(void)
9441Console::vmm2User_NotifyEmtInit(PCVMM2USERMETHODS pThis, PUVM pUVM, PUVMCPU pUVCpu)
9442{
9443 NOREF(pThis); NOREF(pUVM); NOREF(pUVCpu);
9444 VirtualBoxBase::initializeComForThread();
9445}
9446
9447/**
9448 * @interface_method_impl{VMM2USERMETHODS,pfnNotifyEmtTerm}
9449 */
9450/*static*/ DECLCALLBACK(void)
9451Console::vmm2User_NotifyEmtTerm(PCVMM2USERMETHODS pThis, PUVM pUVM, PUVMCPU pUVCpu)
9452{
9453 NOREF(pThis); NOREF(pUVM); NOREF(pUVCpu);
9454 VirtualBoxBase::uninitializeComForThread();
9455}
9456
9457/**
9458 * @interface_method_impl{VMM2USERMETHODS,pfnNotifyPdmtInit}
9459 */
9460/*static*/ DECLCALLBACK(void)
9461Console::vmm2User_NotifyPdmtInit(PCVMM2USERMETHODS pThis, PUVM pUVM)
9462{
9463 NOREF(pThis); NOREF(pUVM);
9464 VirtualBoxBase::initializeComForThread();
9465}
9466
9467/**
9468 * @interface_method_impl{VMM2USERMETHODS,pfnNotifyPdmtTerm}
9469 */
9470/*static*/ DECLCALLBACK(void)
9471Console::vmm2User_NotifyPdmtTerm(PCVMM2USERMETHODS pThis, PUVM pUVM)
9472{
9473 NOREF(pThis); NOREF(pUVM);
9474 VirtualBoxBase::uninitializeComForThread();
9475}
9476
9477
9478
9479
9480/**
9481 * The Main status driver instance data.
9482 */
9483typedef struct DRVMAINSTATUS
9484{
9485 /** The LED connectors. */
9486 PDMILEDCONNECTORS ILedConnectors;
9487 /** Pointer to the LED ports interface above us. */
9488 PPDMILEDPORTS pLedPorts;
9489 /** Pointer to the array of LED pointers. */
9490 PPDMLED *papLeds;
9491 /** The unit number corresponding to the first entry in the LED array. */
9492 RTUINT iFirstLUN;
9493 /** The unit number corresponding to the last entry in the LED array.
9494 * (The size of the LED array is iLastLUN - iFirstLUN + 1.) */
9495 RTUINT iLastLUN;
9496 /** Pointer to the driver instance. */
9497 PPDMDRVINS pDrvIns;
9498 /** The Media Notify interface. */
9499 PDMIMEDIANOTIFY IMediaNotify;
9500 /** Map for translating PDM storage controller/LUN information to
9501 * IMediumAttachment references. */
9502 Console::MediumAttachmentMap *pmapMediumAttachments;
9503 /** Device name+instance for mapping */
9504 char *pszDeviceInstance;
9505 /** Pointer to the Console object, for driver triggered activities. */
9506 Console *pConsole;
9507} DRVMAINSTATUS, *PDRVMAINSTATUS;
9508
9509
9510/**
9511 * Notification about a unit which have been changed.
9512 *
9513 * The driver must discard any pointers to data owned by
9514 * the unit and requery it.
9515 *
9516 * @param pInterface Pointer to the interface structure containing the called function pointer.
9517 * @param iLUN The unit number.
9518 */
9519DECLCALLBACK(void) Console::drvStatus_UnitChanged(PPDMILEDCONNECTORS pInterface, unsigned iLUN)
9520{
9521 PDRVMAINSTATUS pData = (PDRVMAINSTATUS)((uintptr_t)pInterface - RT_OFFSETOF(DRVMAINSTATUS, ILedConnectors));
9522 if (iLUN >= pData->iFirstLUN && iLUN <= pData->iLastLUN)
9523 {
9524 PPDMLED pLed;
9525 int rc = pData->pLedPorts->pfnQueryStatusLed(pData->pLedPorts, iLUN, &pLed);
9526 if (RT_FAILURE(rc))
9527 pLed = NULL;
9528 ASMAtomicWritePtr(&pData->papLeds[iLUN - pData->iFirstLUN], pLed);
9529 Log(("drvStatus_UnitChanged: iLUN=%d pLed=%p\n", iLUN, pLed));
9530 }
9531}
9532
9533
9534/**
9535 * Notification about a medium eject.
9536 *
9537 * @returns VBox status.
9538 * @param pInterface Pointer to the interface structure containing the called function pointer.
9539 * @param uLUN The unit number.
9540 */
9541DECLCALLBACK(int) Console::drvStatus_MediumEjected(PPDMIMEDIANOTIFY pInterface, unsigned uLUN)
9542{
9543 PDRVMAINSTATUS pData = (PDRVMAINSTATUS)((uintptr_t)pInterface - RT_OFFSETOF(DRVMAINSTATUS, IMediaNotify));
9544 PPDMDRVINS pDrvIns = pData->pDrvIns;
9545 LogFunc(("uLUN=%d\n", uLUN));
9546 if (pData->pmapMediumAttachments)
9547 {
9548 AutoWriteLock alock(pData->pConsole COMMA_LOCKVAL_SRC_POS);
9549
9550 ComPtr<IMediumAttachment> pMediumAtt;
9551 Utf8Str devicePath = Utf8StrFmt("%s/LUN#%u", pData->pszDeviceInstance, uLUN);
9552 Console::MediumAttachmentMap::const_iterator end = pData->pmapMediumAttachments->end();
9553 Console::MediumAttachmentMap::const_iterator it = pData->pmapMediumAttachments->find(devicePath);
9554 if (it != end)
9555 pMediumAtt = it->second;
9556 Assert(!pMediumAtt.isNull());
9557 if (!pMediumAtt.isNull())
9558 {
9559 IMedium *pMedium;
9560 HRESULT rc = pMediumAtt->COMGETTER(Medium)(&pMedium);
9561 AssertComRC(rc);
9562 BOOL fHostDrive = FALSE;
9563 rc = pMedium->COMGETTER(HostDrive)(&fHostDrive);
9564 AssertComRC(rc);
9565 if (!fHostDrive)
9566 {
9567 alock.release();
9568
9569 ComPtr<IMediumAttachment> pNewMediumAtt;
9570 rc = pData->pConsole->mControl->EjectMedium(pMediumAtt, pNewMediumAtt.asOutParam());
9571 if (SUCCEEDED(rc))
9572 fireMediumChangedEvent(pData->pConsole->mEventSource, pNewMediumAtt);
9573
9574 alock.acquire();
9575 if (pNewMediumAtt != pMediumAtt)
9576 {
9577 pData->pmapMediumAttachments->erase(devicePath);
9578 pData->pmapMediumAttachments->insert(std::make_pair(devicePath, pNewMediumAtt));
9579 }
9580 }
9581 }
9582 }
9583 return VINF_SUCCESS;
9584}
9585
9586
9587/**
9588 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
9589 */
9590DECLCALLBACK(void *) Console::drvStatus_QueryInterface(PPDMIBASE pInterface, const char *pszIID)
9591{
9592 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
9593 PDRVMAINSTATUS pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINSTATUS);
9594 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
9595 PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDCONNECTORS, &pThis->ILedConnectors);
9596 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIANOTIFY, &pThis->IMediaNotify);
9597 return NULL;
9598}
9599
9600
9601/**
9602 * Destruct a status driver instance.
9603 *
9604 * @returns VBox status.
9605 * @param pDrvIns The driver instance data.
9606 */
9607DECLCALLBACK(void) Console::drvStatus_Destruct(PPDMDRVINS pDrvIns)
9608{
9609 PDRVMAINSTATUS pData = PDMINS_2_DATA(pDrvIns, PDRVMAINSTATUS);
9610 LogFlowFunc(("iInstance=%d\n", pDrvIns->iInstance));
9611 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
9612
9613 if (pData->papLeds)
9614 {
9615 unsigned iLed = pData->iLastLUN - pData->iFirstLUN + 1;
9616 while (iLed-- > 0)
9617 ASMAtomicWriteNullPtr(&pData->papLeds[iLed]);
9618 }
9619}
9620
9621
9622/**
9623 * Construct a status driver instance.
9624 *
9625 * @copydoc FNPDMDRVCONSTRUCT
9626 */
9627DECLCALLBACK(int) Console::drvStatus_Construct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
9628{
9629 PDRVMAINSTATUS pData = PDMINS_2_DATA(pDrvIns, PDRVMAINSTATUS);
9630 LogFlowFunc(("iInstance=%d\n", pDrvIns->iInstance));
9631 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
9632
9633 /*
9634 * Validate configuration.
9635 */
9636 if (!CFGMR3AreValuesValid(pCfg, "papLeds\0pmapMediumAttachments\0DeviceInstance\0pConsole\0First\0Last\0"))
9637 return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
9638 AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
9639 ("Configuration error: Not possible to attach anything to this driver!\n"),
9640 VERR_PDM_DRVINS_NO_ATTACH);
9641
9642 /*
9643 * Data.
9644 */
9645 pDrvIns->IBase.pfnQueryInterface = Console::drvStatus_QueryInterface;
9646 pData->ILedConnectors.pfnUnitChanged = Console::drvStatus_UnitChanged;
9647 pData->IMediaNotify.pfnEjected = Console::drvStatus_MediumEjected;
9648 pData->pDrvIns = pDrvIns;
9649 pData->pszDeviceInstance = NULL;
9650
9651 /*
9652 * Read config.
9653 */
9654 int rc = CFGMR3QueryPtr(pCfg, "papLeds", (void **)&pData->papLeds);
9655 if (RT_FAILURE(rc))
9656 {
9657 AssertMsgFailed(("Configuration error: Failed to query the \"papLeds\" value! rc=%Rrc\n", rc));
9658 return rc;
9659 }
9660
9661 rc = CFGMR3QueryPtrDef(pCfg, "pmapMediumAttachments", (void **)&pData->pmapMediumAttachments, NULL);
9662 if (RT_FAILURE(rc))
9663 {
9664 AssertMsgFailed(("Configuration error: Failed to query the \"pmapMediumAttachments\" value! rc=%Rrc\n", rc));
9665 return rc;
9666 }
9667 if (pData->pmapMediumAttachments)
9668 {
9669 rc = CFGMR3QueryStringAlloc(pCfg, "DeviceInstance", &pData->pszDeviceInstance);
9670 if (RT_FAILURE(rc))
9671 {
9672 AssertMsgFailed(("Configuration error: Failed to query the \"DeviceInstance\" value! rc=%Rrc\n", rc));
9673 return rc;
9674 }
9675 rc = CFGMR3QueryPtr(pCfg, "pConsole", (void **)&pData->pConsole);
9676 if (RT_FAILURE(rc))
9677 {
9678 AssertMsgFailed(("Configuration error: Failed to query the \"pConsole\" value! rc=%Rrc\n", rc));
9679 return rc;
9680 }
9681 }
9682
9683 rc = CFGMR3QueryU32(pCfg, "First", &pData->iFirstLUN);
9684 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
9685 pData->iFirstLUN = 0;
9686 else if (RT_FAILURE(rc))
9687 {
9688 AssertMsgFailed(("Configuration error: Failed to query the \"First\" value! rc=%Rrc\n", rc));
9689 return rc;
9690 }
9691
9692 rc = CFGMR3QueryU32(pCfg, "Last", &pData->iLastLUN);
9693 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
9694 pData->iLastLUN = 0;
9695 else if (RT_FAILURE(rc))
9696 {
9697 AssertMsgFailed(("Configuration error: Failed to query the \"Last\" value! rc=%Rrc\n", rc));
9698 return rc;
9699 }
9700 if (pData->iFirstLUN > pData->iLastLUN)
9701 {
9702 AssertMsgFailed(("Configuration error: Invalid unit range %u-%u\n", pData->iFirstLUN, pData->iLastLUN));
9703 return VERR_GENERAL_FAILURE;
9704 }
9705
9706 /*
9707 * Get the ILedPorts interface of the above driver/device and
9708 * query the LEDs we want.
9709 */
9710 pData->pLedPorts = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMILEDPORTS);
9711 AssertMsgReturn(pData->pLedPorts, ("Configuration error: No led ports interface above!\n"),
9712 VERR_PDM_MISSING_INTERFACE_ABOVE);
9713
9714 for (unsigned i = pData->iFirstLUN; i <= pData->iLastLUN; ++i)
9715 Console::drvStatus_UnitChanged(&pData->ILedConnectors, i);
9716
9717 return VINF_SUCCESS;
9718}
9719
9720
9721/**
9722 * Console status driver (LED) registration record.
9723 */
9724const PDMDRVREG Console::DrvStatusReg =
9725{
9726 /* u32Version */
9727 PDM_DRVREG_VERSION,
9728 /* szName */
9729 "MainStatus",
9730 /* szRCMod */
9731 "",
9732 /* szR0Mod */
9733 "",
9734 /* pszDescription */
9735 "Main status driver (Main as in the API).",
9736 /* fFlags */
9737 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
9738 /* fClass. */
9739 PDM_DRVREG_CLASS_STATUS,
9740 /* cMaxInstances */
9741 ~0,
9742 /* cbInstance */
9743 sizeof(DRVMAINSTATUS),
9744 /* pfnConstruct */
9745 Console::drvStatus_Construct,
9746 /* pfnDestruct */
9747 Console::drvStatus_Destruct,
9748 /* pfnRelocate */
9749 NULL,
9750 /* pfnIOCtl */
9751 NULL,
9752 /* pfnPowerOn */
9753 NULL,
9754 /* pfnReset */
9755 NULL,
9756 /* pfnSuspend */
9757 NULL,
9758 /* pfnResume */
9759 NULL,
9760 /* pfnAttach */
9761 NULL,
9762 /* pfnDetach */
9763 NULL,
9764 /* pfnPowerOff */
9765 NULL,
9766 /* pfnSoftReset */
9767 NULL,
9768 /* u32EndVersion */
9769 PDM_DRVREG_VERSION
9770};
9771
9772/* 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