VirtualBox

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

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

Moved the extension pack release logging from ConsoleImpl.cpp to ExtPackManagerImpl.cpp.

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

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