VirtualBox

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

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

Storage/DrvHostBase: add generic unmount implementation, so that host floppy unmount works
Main/Console: fix swapped error messages for medium change

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 324.2 KB
Line 
1/* $Id: ConsoleImpl.cpp 37780 2011-07-05 12:55:13Z 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
6407#if 0
6408 LogRel(("Installed Extension Packs:\n"));
6409 com::SafeIfaceArray<IExtPack> extPacks;
6410 HRESULT rc2 = mptrExtPackManager->COMGETTER(InstalledExtPacks)(ComSafeArrayAsOutParam(extPacks));
6411 if (SUCCEEDED(rc2))
6412 {
6413 for (size_t i = 0; i < extPacks.size(); i++)
6414 {
6415 /* Read all the properties. */
6416 Bstr bstrName;
6417 rc2 = extPacks[i]->COMGETTER(Name)(bstrName.asOutParam());
6418 Bstr bstrVersion;
6419 rc2 = extPacks[i]->COMGETTER(Version)(bstrVersion.asOutParam());
6420 ULONG uRevision = 0;
6421 rc2 = extPacks[i]->COMGETTER(Revision)(&uRevision);
6422 Bstr bstrVrdeModule;
6423 rc2 = extPacks[i]->COMGETTER(VRDEModule)(bstrVrdeModule.asOutParam());
6424 BOOL fUsable;
6425 rc2 = extPacks[i]->COMGETTER(Usable)(&fUsable);
6426 Bstr bstrWhy;
6427 rc2 = extPacks[i]->COMGETTER(WhyUnusable)(bstrWhy.asOutParam());
6428
6429 /* Display them. */
6430 if (i)
6431 LogRel(("\n"));
6432 LogRel((" %lS (Version: %lS r%u; VRDE Module: %lS",
6433 bstrName.raw(), bstrVersion.raw(), uRevision, bstrVrdeModule.raw()));
6434 if (!fUsable)
6435 LogRel(("; unusable because of '%s'", bstrWhy.raw()));
6436 LogRel((")\n"));
6437 }
6438 if (!extPacks.size())
6439 LogRel((" None inststalled!\n"));
6440 }
6441 else
6442 LogRel((" Cannot retrieve this information (%Rhrc)!\n", rc2));
6443#endif
6444
6445#ifdef RT_OS_SOLARIS
6446 /* setup host core dumper for the VM */
6447 Bstr value;
6448 HRESULT hrc = mMachine->GetExtraData(Bstr("VBoxInternal2/CoreDumpEnabled").raw(), value.asOutParam());
6449 if (SUCCEEDED(hrc) && value == "1")
6450 {
6451 Bstr coreDumpDir, coreDumpReplaceSys, coreDumpLive;
6452 mMachine->GetExtraData(Bstr("VBoxInternal2/CoreDumpDir").raw(), coreDumpDir.asOutParam());
6453 mMachine->GetExtraData(Bstr("VBoxInternal2/CoreDumpReplaceSystemDump").raw(), coreDumpReplaceSys.asOutParam());
6454 mMachine->GetExtraData(Bstr("VBoxInternal2/CoreDumpLive").raw(), coreDumpLive.asOutParam());
6455
6456 uint32_t fCoreFlags = 0;
6457 if ( coreDumpReplaceSys.isEmpty() == false
6458 && Utf8Str(coreDumpReplaceSys).toUInt32() == 1)
6459 {
6460 fCoreFlags |= RTCOREDUMPER_FLAGS_REPLACE_SYSTEM_DUMP;
6461 }
6462
6463 if ( coreDumpLive.isEmpty() == false
6464 && Utf8Str(coreDumpLive).toUInt32() == 1)
6465 {
6466 fCoreFlags |= RTCOREDUMPER_FLAGS_LIVE_CORE;
6467 }
6468
6469 Utf8Str strDumpDir(coreDumpDir);
6470 const char *pszDumpDir = strDumpDir.c_str();
6471 if ( pszDumpDir
6472 && *pszDumpDir == '\0')
6473 pszDumpDir = NULL;
6474
6475 int vrc;
6476 if ( pszDumpDir
6477 && !RTDirExists(pszDumpDir))
6478 {
6479 /*
6480 * Try create the directory.
6481 */
6482 vrc = RTDirCreateFullPath(pszDumpDir, 0777);
6483 if (RT_FAILURE(vrc))
6484 throw setError(E_FAIL, "Failed to setup CoreDumper. Couldn't create dump directory '%s' (%Rrc)\n", pszDumpDir, vrc);
6485 }
6486
6487 vrc = RTCoreDumperSetup(pszDumpDir, fCoreFlags);
6488 if (RT_FAILURE(vrc))
6489 throw setError(E_FAIL, "Failed to setup CoreDumper (%Rrc)", vrc);
6490 else
6491 LogRel(("CoreDumper setup successful. pszDumpDir=%s fFlags=%#x\n", pszDumpDir ? pszDumpDir : ".", fCoreFlags));
6492 }
6493#endif
6494
6495 /* pass the progress object to the caller if requested */
6496 if (aProgress)
6497 {
6498 if (task->hardDiskProgresses.size() == 0)
6499 {
6500 /* there are no other operations to track, return the powerup
6501 * progress only */
6502 pPowerupProgress.queryInterfaceTo(aProgress);
6503 }
6504 else
6505 {
6506 /* create a combined progress object */
6507 ComObjPtr<CombinedProgress> pProgress;
6508 pProgress.createObject();
6509 VMPowerUpTask::ProgressList progresses(task->hardDiskProgresses);
6510 progresses.push_back(ComPtr<IProgress> (pPowerupProgress));
6511 rc = pProgress->init(static_cast<IConsole *>(this),
6512 progressDesc.raw(), progresses.begin(),
6513 progresses.end());
6514 AssertComRCReturnRC(rc);
6515 pProgress.queryInterfaceTo(aProgress);
6516 }
6517 }
6518
6519 int vrc = RTThreadCreate(NULL, Console::powerUpThread,
6520 (void *)task.get(), 0,
6521 RTTHREADTYPE_MAIN_WORKER, 0, "VMPowerUp");
6522 if (RT_FAILURE(vrc))
6523 throw setError(E_FAIL, "Could not create VMPowerUp thread (%Rrc)", vrc);
6524
6525 /* task is now owned by powerUpThread(), so release it */
6526 task.release();
6527
6528 /* finally, set the state: no right to fail in this method afterwards
6529 * since we've already started the thread and it is now responsible for
6530 * any error reporting and appropriate state change! */
6531 if (mMachineState == MachineState_Saved)
6532 setMachineState(MachineState_Restoring);
6533 else if (fTeleporterEnabled)
6534 setMachineState(MachineState_TeleportingIn);
6535 else if (enmFaultToleranceState == FaultToleranceState_Standby)
6536 setMachineState(MachineState_FaultTolerantSyncing);
6537 else
6538 setMachineState(MachineState_Starting);
6539 }
6540 catch (HRESULT aRC) { rc = aRC; }
6541
6542 if (FAILED(rc) && fBeganPoweringUp)
6543 {
6544
6545 /* The progress object will fetch the current error info */
6546 if (!pPowerupProgress.isNull())
6547 pPowerupProgress->notifyComplete(rc);
6548
6549 /* Save the error info across the IPC below. Can't be done before the
6550 * progress notification above, as saving the error info deletes it
6551 * from the current context, and thus the progress object wouldn't be
6552 * updated correctly. */
6553 ErrorInfoKeeper eik;
6554
6555 /* signal end of operation */
6556 mControl->EndPowerUp(rc);
6557 }
6558
6559 LogFlowThisFunc(("mMachineState=%d, rc=%Rhrc\n", mMachineState, rc));
6560 LogFlowThisFuncLeave();
6561 return rc;
6562}
6563
6564/**
6565 * Internal power off worker routine.
6566 *
6567 * This method may be called only at certain places with the following meaning
6568 * as shown below:
6569 *
6570 * - if the machine state is either Running or Paused, a normal
6571 * Console-initiated powerdown takes place (e.g. PowerDown());
6572 * - if the machine state is Saving, saveStateThread() has successfully done its
6573 * job;
6574 * - if the machine state is Starting or Restoring, powerUpThread() has failed
6575 * to start/load the VM;
6576 * - if the machine state is Stopping, the VM has powered itself off (i.e. not
6577 * as a result of the powerDown() call).
6578 *
6579 * Calling it in situations other than the above will cause unexpected behavior.
6580 *
6581 * Note that this method should be the only one that destroys mpVM and sets it
6582 * to NULL.
6583 *
6584 * @param aProgress Progress object to run (may be NULL).
6585 *
6586 * @note Locks this object for writing.
6587 *
6588 * @note Never call this method from a thread that called addVMCaller() or
6589 * instantiated an AutoVMCaller object; first call releaseVMCaller() or
6590 * release(). Otherwise it will deadlock.
6591 */
6592HRESULT Console::powerDown(IProgress *aProgress /*= NULL*/)
6593{
6594 LogFlowThisFuncEnter();
6595
6596 AutoCaller autoCaller(this);
6597 AssertComRCReturnRC(autoCaller.rc());
6598
6599 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6600
6601 /* Total # of steps for the progress object. Must correspond to the
6602 * number of "advance percent count" comments in this method! */
6603 enum { StepCount = 7 };
6604 /* current step */
6605 ULONG step = 0;
6606
6607 HRESULT rc = S_OK;
6608 int vrc = VINF_SUCCESS;
6609
6610 /* sanity */
6611 Assert(mVMDestroying == false);
6612
6613 PUVM pUVM = mpUVM; Assert(pUVM != NULL);
6614 uint32_t cRefs = VMR3RetainUVM(pUVM); Assert(cRefs != UINT32_MAX);
6615
6616 AssertMsg( mMachineState == MachineState_Running
6617 || mMachineState == MachineState_Paused
6618 || mMachineState == MachineState_Stuck
6619 || mMachineState == MachineState_Starting
6620 || mMachineState == MachineState_Stopping
6621 || mMachineState == MachineState_Saving
6622 || mMachineState == MachineState_Restoring
6623 || mMachineState == MachineState_TeleportingPausedVM
6624 || mMachineState == MachineState_FaultTolerantSyncing
6625 || mMachineState == MachineState_TeleportingIn
6626 , ("Invalid machine state: %s\n", Global::stringifyMachineState(mMachineState)));
6627
6628 LogRel(("Console::powerDown(): A request to power off the VM has been issued (mMachineState=%s, InUninit=%d)\n",
6629 Global::stringifyMachineState(mMachineState), autoCaller.state() == InUninit));
6630
6631 /* Check if we need to power off the VM. In case of mVMPoweredOff=true, the
6632 * VM has already powered itself off in vmstateChangeCallback() and is just
6633 * notifying Console about that. In case of Starting or Restoring,
6634 * powerUpThread() is calling us on failure, so the VM is already off at
6635 * that point. */
6636 if ( !mVMPoweredOff
6637 && ( mMachineState == MachineState_Starting
6638 || mMachineState == MachineState_Restoring
6639 || mMachineState == MachineState_FaultTolerantSyncing
6640 || mMachineState == MachineState_TeleportingIn)
6641 )
6642 mVMPoweredOff = true;
6643
6644 /*
6645 * Go to Stopping state if not already there.
6646 *
6647 * Note that we don't go from Saving/Restoring to Stopping because
6648 * vmstateChangeCallback() needs it to set the state to Saved on
6649 * VMSTATE_TERMINATED. In terms of protecting from inappropriate operations
6650 * while leaving the lock below, Saving or Restoring should be fine too.
6651 * Ditto for TeleportingPausedVM -> Teleported.
6652 */
6653 if ( mMachineState != MachineState_Saving
6654 && mMachineState != MachineState_Restoring
6655 && mMachineState != MachineState_Stopping
6656 && mMachineState != MachineState_TeleportingIn
6657 && mMachineState != MachineState_TeleportingPausedVM
6658 && mMachineState != MachineState_FaultTolerantSyncing
6659 )
6660 setMachineState(MachineState_Stopping);
6661
6662 /* ----------------------------------------------------------------------
6663 * DONE with necessary state changes, perform the power down actions (it's
6664 * safe to leave the object lock now if needed)
6665 * ---------------------------------------------------------------------- */
6666
6667 /* Stop the VRDP server to prevent new clients connection while VM is being
6668 * powered off. */
6669 if (mConsoleVRDPServer)
6670 {
6671 LogFlowThisFunc(("Stopping VRDP server...\n"));
6672
6673 /* Leave the lock since EMT will call us back as addVMCaller()
6674 * in updateDisplayData(). */
6675 alock.leave();
6676
6677 mConsoleVRDPServer->Stop();
6678
6679 alock.enter();
6680 }
6681
6682 /* advance percent count */
6683 if (aProgress)
6684 aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
6685
6686
6687 /* ----------------------------------------------------------------------
6688 * Now, wait for all mpVM callers to finish their work if there are still
6689 * some on other threads. NO methods that need mpVM (or initiate other calls
6690 * that need it) may be called after this point
6691 * ---------------------------------------------------------------------- */
6692
6693 /* go to the destroying state to prevent from adding new callers */
6694 mVMDestroying = true;
6695
6696 if (mVMCallers > 0)
6697 {
6698 /* lazy creation */
6699 if (mVMZeroCallersSem == NIL_RTSEMEVENT)
6700 RTSemEventCreate(&mVMZeroCallersSem);
6701
6702 LogFlowThisFunc(("Waiting for mpVM callers (%d) to drop to zero...\n",
6703 mVMCallers));
6704
6705 alock.leave();
6706
6707 RTSemEventWait(mVMZeroCallersSem, RT_INDEFINITE_WAIT);
6708
6709 alock.enter();
6710 }
6711
6712 /* advance percent count */
6713 if (aProgress)
6714 aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
6715
6716 vrc = VINF_SUCCESS;
6717
6718 /*
6719 * Power off the VM if not already done that.
6720 * Leave the lock since EMT will call vmstateChangeCallback.
6721 *
6722 * Note that VMR3PowerOff() may fail here (invalid VMSTATE) if the
6723 * VM-(guest-)initiated power off happened in parallel a ms before this
6724 * call. So far, we let this error pop up on the user's side.
6725 */
6726 if (!mVMPoweredOff)
6727 {
6728 LogFlowThisFunc(("Powering off the VM...\n"));
6729 alock.leave();
6730 vrc = VMR3PowerOff(VMR3GetVM(pUVM));
6731#ifdef VBOX_WITH_EXTPACK
6732 mptrExtPackManager->callAllVmPowerOffHooks(this, VMR3GetVM(pUVM));
6733#endif
6734 alock.enter();
6735 }
6736
6737 /* advance percent count */
6738 if (aProgress)
6739 aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
6740
6741#ifdef VBOX_WITH_HGCM
6742 /* Shutdown HGCM services before destroying the VM. */
6743 if (m_pVMMDev)
6744 {
6745 LogFlowThisFunc(("Shutdown HGCM...\n"));
6746
6747 /* Leave the lock since EMT will call us back as addVMCaller() */
6748 alock.leave();
6749
6750 m_pVMMDev->hgcmShutdown();
6751
6752 alock.enter();
6753 }
6754
6755 /* advance percent count */
6756 if (aProgress)
6757 aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
6758
6759#endif /* VBOX_WITH_HGCM */
6760
6761 LogFlowThisFunc(("Ready for VM destruction.\n"));
6762
6763 /* If we are called from Console::uninit(), then try to destroy the VM even
6764 * on failure (this will most likely fail too, but what to do?..) */
6765 if (RT_SUCCESS(vrc) || autoCaller.state() == InUninit)
6766 {
6767 /* If the machine has an USB controller, release all USB devices
6768 * (symmetric to the code in captureUSBDevices()) */
6769 bool fHasUSBController = false;
6770 {
6771 PPDMIBASE pBase;
6772 vrc = PDMR3QueryLun(VMR3GetVM(pUVM), "usb-ohci", 0, 0, &pBase);
6773 if (RT_SUCCESS(vrc))
6774 {
6775 fHasUSBController = true;
6776 detachAllUSBDevices(false /* aDone */);
6777 }
6778 }
6779
6780 /* Now we've got to destroy the VM as well. (mpVM is not valid beyond
6781 * this point). We leave the lock before calling VMR3Destroy() because
6782 * it will result into calling destructors of drivers associated with
6783 * Console children which may in turn try to lock Console (e.g. by
6784 * instantiating SafeVMPtr to access mpVM). It's safe here because
6785 * mVMDestroying is set which should prevent any activity. */
6786
6787 /* Set mpUVM to NULL early just in case if some old code is not using
6788 * addVMCaller()/releaseVMCaller(). (We have our own ref on pUVM.) */
6789 VMR3ReleaseUVM(mpUVM);
6790 mpUVM = NULL;
6791
6792 LogFlowThisFunc(("Destroying the VM...\n"));
6793
6794 alock.leave();
6795
6796 vrc = VMR3Destroy(VMR3GetVM(pUVM));
6797
6798 /* take the lock again */
6799 alock.enter();
6800
6801 /* advance percent count */
6802 if (aProgress)
6803 aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
6804
6805 if (RT_SUCCESS(vrc))
6806 {
6807 LogFlowThisFunc(("Machine has been destroyed (mMachineState=%d)\n",
6808 mMachineState));
6809 /* Note: the Console-level machine state change happens on the
6810 * VMSTATE_TERMINATE state change in vmstateChangeCallback(). If
6811 * powerDown() is called from EMT (i.e. from vmstateChangeCallback()
6812 * on receiving VM-initiated VMSTATE_OFF), VMSTATE_TERMINATE hasn't
6813 * occurred yet. This is okay, because mMachineState is already
6814 * Stopping in this case, so any other attempt to call PowerDown()
6815 * will be rejected. */
6816 }
6817 else
6818 {
6819 /* bad bad bad, but what to do? (Give Console our UVM ref.) */
6820 mpUVM = pUVM;
6821 pUVM = NULL;
6822 rc = setError(VBOX_E_VM_ERROR,
6823 tr("Could not destroy the machine. (Error: %Rrc)"),
6824 vrc);
6825 }
6826
6827 /* Complete the detaching of the USB devices. */
6828 if (fHasUSBController)
6829 detachAllUSBDevices(true /* aDone */);
6830
6831 /* advance percent count */
6832 if (aProgress)
6833 aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
6834 }
6835 else
6836 {
6837 rc = setError(VBOX_E_VM_ERROR,
6838 tr("Could not power off the machine. (Error: %Rrc)"),
6839 vrc);
6840 }
6841
6842 /*
6843 * Finished with the destruction.
6844 *
6845 * Note that if something impossible happened and we've failed to destroy
6846 * the VM, mVMDestroying will remain true and mMachineState will be
6847 * something like Stopping, so most Console methods will return an error
6848 * to the caller.
6849 */
6850 if (mpUVM != NULL)
6851 VMR3ReleaseUVM(pUVM);
6852 else
6853 mVMDestroying = false;
6854
6855 if (SUCCEEDED(rc))
6856 mCallbackData.clear();
6857
6858 LogFlowThisFuncLeave();
6859 return rc;
6860}
6861
6862/**
6863 * @note Locks this object for writing.
6864 */
6865HRESULT Console::setMachineState(MachineState_T aMachineState,
6866 bool aUpdateServer /* = true */)
6867{
6868 AutoCaller autoCaller(this);
6869 AssertComRCReturnRC(autoCaller.rc());
6870
6871 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6872
6873 HRESULT rc = S_OK;
6874
6875 if (mMachineState != aMachineState)
6876 {
6877 LogThisFunc(("machineState=%s -> %s aUpdateServer=%RTbool\n",
6878 Global::stringifyMachineState(mMachineState), Global::stringifyMachineState(aMachineState), aUpdateServer));
6879 mMachineState = aMachineState;
6880
6881 /// @todo (dmik)
6882 // possibly, we need to redo onStateChange() using the dedicated
6883 // Event thread, like it is done in VirtualBox. This will make it
6884 // much safer (no deadlocks possible if someone tries to use the
6885 // console from the callback), however, listeners will lose the
6886 // ability to synchronously react to state changes (is it really
6887 // necessary??)
6888 LogFlowThisFunc(("Doing onStateChange()...\n"));
6889 onStateChange(aMachineState);
6890 LogFlowThisFunc(("Done onStateChange()\n"));
6891
6892 if (aUpdateServer)
6893 {
6894 /* Server notification MUST be done from under the lock; otherwise
6895 * the machine state here and on the server might go out of sync
6896 * which can lead to various unexpected results (like the machine
6897 * state being >= MachineState_Running on the server, while the
6898 * session state is already SessionState_Unlocked at the same time
6899 * there).
6900 *
6901 * Cross-lock conditions should be carefully watched out: calling
6902 * UpdateState we will require Machine and SessionMachine locks
6903 * (remember that here we're holding the Console lock here, and also
6904 * all locks that have been entered by the thread before calling
6905 * this method).
6906 */
6907 LogFlowThisFunc(("Doing mControl->UpdateState()...\n"));
6908 rc = mControl->UpdateState(aMachineState);
6909 LogFlowThisFunc(("mControl->UpdateState()=%Rhrc\n", rc));
6910 }
6911 }
6912
6913 return rc;
6914}
6915
6916/**
6917 * Searches for a shared folder with the given logical name
6918 * in the collection of shared folders.
6919 *
6920 * @param aName logical name of the shared folder
6921 * @param aSharedFolder where to return the found object
6922 * @param aSetError whether to set the error info if the folder is
6923 * not found
6924 * @return
6925 * S_OK when found or E_INVALIDARG when not found
6926 *
6927 * @note The caller must lock this object for writing.
6928 */
6929HRESULT Console::findSharedFolder(const Utf8Str &strName,
6930 ComObjPtr<SharedFolder> &aSharedFolder,
6931 bool aSetError /* = false */)
6932{
6933 /* sanity check */
6934 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
6935
6936 SharedFolderMap::const_iterator it = m_mapSharedFolders.find(strName);
6937 if (it != m_mapSharedFolders.end())
6938 {
6939 aSharedFolder = it->second;
6940 return S_OK;
6941 }
6942
6943 if (aSetError)
6944 setError(VBOX_E_FILE_ERROR,
6945 tr("Could not find a shared folder named '%s'."),
6946 strName.c_str());
6947
6948 return VBOX_E_FILE_ERROR;
6949}
6950
6951/**
6952 * Fetches the list of global or machine shared folders from the server.
6953 *
6954 * @param aGlobal true to fetch global folders.
6955 *
6956 * @note The caller must lock this object for writing.
6957 */
6958HRESULT Console::fetchSharedFolders(BOOL aGlobal)
6959{
6960 /* sanity check */
6961 AssertReturn(AutoCaller(this).state() == InInit ||
6962 isWriteLockOnCurrentThread(), E_FAIL);
6963
6964 LogFlowThisFunc(("Entering\n"));
6965
6966 /* Check if we're online and keep it that way. */
6967 SafeVMPtrQuiet ptrVM(this);
6968 AutoVMCallerQuietWeak autoVMCaller(this);
6969 bool const online = ptrVM.isOk()
6970 && m_pVMMDev
6971 && m_pVMMDev->isShFlActive();
6972
6973 HRESULT rc = S_OK;
6974
6975 try
6976 {
6977 if (aGlobal)
6978 {
6979 /// @todo grab & process global folders when they are done
6980 }
6981 else
6982 {
6983 SharedFolderDataMap oldFolders;
6984 if (online)
6985 oldFolders = m_mapMachineSharedFolders;
6986
6987 m_mapMachineSharedFolders.clear();
6988
6989 SafeIfaceArray<ISharedFolder> folders;
6990 rc = mMachine->COMGETTER(SharedFolders)(ComSafeArrayAsOutParam(folders));
6991 if (FAILED(rc)) throw rc;
6992
6993 for (size_t i = 0; i < folders.size(); ++i)
6994 {
6995 ComPtr<ISharedFolder> pSharedFolder = folders[i];
6996
6997 Bstr bstrName;
6998 Bstr bstrHostPath;
6999 BOOL writable;
7000 BOOL autoMount;
7001
7002 rc = pSharedFolder->COMGETTER(Name)(bstrName.asOutParam());
7003 if (FAILED(rc)) throw rc;
7004 Utf8Str strName(bstrName);
7005
7006 rc = pSharedFolder->COMGETTER(HostPath)(bstrHostPath.asOutParam());
7007 if (FAILED(rc)) throw rc;
7008 Utf8Str strHostPath(bstrHostPath);
7009
7010 rc = pSharedFolder->COMGETTER(Writable)(&writable);
7011 if (FAILED(rc)) throw rc;
7012
7013 rc = pSharedFolder->COMGETTER(AutoMount)(&autoMount);
7014 if (FAILED(rc)) throw rc;
7015
7016 m_mapMachineSharedFolders.insert(std::make_pair(strName,
7017 SharedFolderData(strHostPath, writable, autoMount)));
7018
7019 /* send changes to HGCM if the VM is running */
7020 if (online)
7021 {
7022 SharedFolderDataMap::iterator it = oldFolders.find(strName);
7023 if ( it == oldFolders.end()
7024 || it->second.m_strHostPath != strHostPath)
7025 {
7026 /* a new machine folder is added or
7027 * the existing machine folder is changed */
7028 if (m_mapSharedFolders.find(strName) != m_mapSharedFolders.end())
7029 ; /* the console folder exists, nothing to do */
7030 else
7031 {
7032 /* remove the old machine folder (when changed)
7033 * or the global folder if any (when new) */
7034 if ( it != oldFolders.end()
7035 || m_mapGlobalSharedFolders.find(strName) != m_mapGlobalSharedFolders.end()
7036 )
7037 {
7038 rc = removeSharedFolder(strName);
7039 if (FAILED(rc)) throw rc;
7040 }
7041
7042 /* create the new machine folder */
7043 rc = createSharedFolder(strName,
7044 SharedFolderData(strHostPath,
7045 writable,
7046 autoMount));
7047 if (FAILED(rc)) throw rc;
7048 }
7049 }
7050 /* forget the processed (or identical) folder */
7051 if (it != oldFolders.end())
7052 oldFolders.erase(it);
7053 }
7054 }
7055
7056 /* process outdated (removed) folders */
7057 if (online)
7058 {
7059 for (SharedFolderDataMap::const_iterator it = oldFolders.begin();
7060 it != oldFolders.end(); ++ it)
7061 {
7062 if (m_mapSharedFolders.find(it->first) != m_mapSharedFolders.end())
7063 ; /* the console folder exists, nothing to do */
7064 else
7065 {
7066 /* remove the outdated machine folder */
7067 rc = removeSharedFolder(it->first);
7068 if (FAILED(rc)) throw rc;
7069
7070 /* create the global folder if there is any */
7071 SharedFolderDataMap::const_iterator git =
7072 m_mapGlobalSharedFolders.find(it->first);
7073 if (git != m_mapGlobalSharedFolders.end())
7074 {
7075 rc = createSharedFolder(git->first, git->second);
7076 if (FAILED(rc)) throw rc;
7077 }
7078 }
7079 }
7080 }
7081 }
7082 }
7083 catch (HRESULT rc2)
7084 {
7085 if (online)
7086 setVMRuntimeErrorCallbackF(ptrVM, this, 0, "BrokenSharedFolder",
7087 N_("Broken shared folder!"));
7088 }
7089
7090 LogFlowThisFunc(("Leaving\n"));
7091
7092 return rc;
7093}
7094
7095/**
7096 * Searches for a shared folder with the given name in the list of machine
7097 * shared folders and then in the list of the global shared folders.
7098 *
7099 * @param aName Name of the folder to search for.
7100 * @param aIt Where to store the pointer to the found folder.
7101 * @return @c true if the folder was found and @c false otherwise.
7102 *
7103 * @note The caller must lock this object for reading.
7104 */
7105bool Console::findOtherSharedFolder(const Utf8Str &strName,
7106 SharedFolderDataMap::const_iterator &aIt)
7107{
7108 /* sanity check */
7109 AssertReturn(isWriteLockOnCurrentThread(), false);
7110
7111 /* first, search machine folders */
7112 aIt = m_mapMachineSharedFolders.find(strName);
7113 if (aIt != m_mapMachineSharedFolders.end())
7114 return true;
7115
7116 /* second, search machine folders */
7117 aIt = m_mapGlobalSharedFolders.find(strName);
7118 if (aIt != m_mapGlobalSharedFolders.end())
7119 return true;
7120
7121 return false;
7122}
7123
7124/**
7125 * Calls the HGCM service to add a shared folder definition.
7126 *
7127 * @param aName Shared folder name.
7128 * @param aHostPath Shared folder path.
7129 *
7130 * @note Must be called from under AutoVMCaller and when mpVM != NULL!
7131 * @note Doesn't lock anything.
7132 */
7133HRESULT Console::createSharedFolder(const Utf8Str &strName, const SharedFolderData &aData)
7134{
7135 ComAssertRet(strName.isNotEmpty(), E_FAIL);
7136 ComAssertRet(aData.m_strHostPath.isNotEmpty(), E_FAIL);
7137
7138 /* sanity checks */
7139 AssertReturn(mpUVM, E_FAIL);
7140 AssertReturn(m_pVMMDev && m_pVMMDev->isShFlActive(), E_FAIL);
7141
7142 VBOXHGCMSVCPARM parms[SHFL_CPARMS_ADD_MAPPING2];
7143 SHFLSTRING *pFolderName, *pMapName;
7144 size_t cbString;
7145
7146 Log(("Adding shared folder '%s' -> '%s'\n", strName.c_str(), aData.m_strHostPath.c_str()));
7147
7148 // check whether the path is valid and exists
7149 /* Check whether the path is full (absolute) */
7150 char hostPathFull[RTPATH_MAX];
7151 int vrc = RTPathAbsEx(NULL,
7152 aData.m_strHostPath.c_str(),
7153 hostPathFull,
7154 sizeof(hostPathFull));
7155 if (RT_FAILURE(vrc))
7156 return setError(E_INVALIDARG,
7157 tr("Invalid shared folder path: '%s' (%Rrc)"),
7158 aData.m_strHostPath.c_str(), vrc);
7159
7160 if (RTPathCompare(aData.m_strHostPath.c_str(), hostPathFull) != 0)
7161 return setError(E_INVALIDARG,
7162 tr("Shared folder path '%s' is not absolute"),
7163 aData.m_strHostPath.c_str());
7164 if (!RTPathExists(hostPathFull))
7165 return setError(E_INVALIDARG,
7166 tr("Shared folder path '%s' does not exist on the host"),
7167 aData.m_strHostPath.c_str());
7168
7169 // now that we know the path is good, give it to HGCM
7170
7171 Bstr bstrName(strName);
7172 Bstr bstrHostPath(aData.m_strHostPath);
7173
7174 cbString = (bstrHostPath.length() + 1) * sizeof(RTUTF16);
7175 if (cbString >= UINT16_MAX)
7176 return setError(E_INVALIDARG, tr("The name is too long"));
7177 pFolderName = (SHFLSTRING*)RTMemAllocZ(sizeof(SHFLSTRING) + cbString);
7178 Assert(pFolderName);
7179 memcpy(pFolderName->String.ucs2, bstrHostPath.raw(), cbString);
7180
7181 pFolderName->u16Size = (uint16_t)cbString;
7182 pFolderName->u16Length = (uint16_t)cbString - sizeof(RTUTF16);
7183
7184 parms[0].type = VBOX_HGCM_SVC_PARM_PTR;
7185 parms[0].u.pointer.addr = pFolderName;
7186 parms[0].u.pointer.size = sizeof(SHFLSTRING) + (uint16_t)cbString;
7187
7188 cbString = (bstrName.length() + 1) * sizeof(RTUTF16);
7189 if (cbString >= UINT16_MAX)
7190 {
7191 RTMemFree(pFolderName);
7192 return setError(E_INVALIDARG, tr("The host path is too long"));
7193 }
7194 pMapName = (SHFLSTRING*)RTMemAllocZ(sizeof(SHFLSTRING) + cbString);
7195 Assert(pMapName);
7196 memcpy(pMapName->String.ucs2, bstrName.raw(), cbString);
7197
7198 pMapName->u16Size = (uint16_t)cbString;
7199 pMapName->u16Length = (uint16_t)cbString - sizeof(RTUTF16);
7200
7201 parms[1].type = VBOX_HGCM_SVC_PARM_PTR;
7202 parms[1].u.pointer.addr = pMapName;
7203 parms[1].u.pointer.size = sizeof(SHFLSTRING) + (uint16_t)cbString;
7204
7205 parms[2].type = VBOX_HGCM_SVC_PARM_32BIT;
7206 parms[2].u.uint32 = aData.m_fWritable;
7207
7208 /*
7209 * Auto-mount flag; is indicated by using the SHFL_CPARMS_ADD_MAPPING2
7210 * define below. This shows the host service that we have supplied
7211 * an additional parameter (auto-mount) and keeps the actual command
7212 * backwards compatible.
7213 */
7214 parms[3].type = VBOX_HGCM_SVC_PARM_32BIT;
7215 parms[3].u.uint32 = aData.m_fAutoMount;
7216
7217 vrc = m_pVMMDev->hgcmHostCall("VBoxSharedFolders",
7218 SHFL_FN_ADD_MAPPING,
7219 SHFL_CPARMS_ADD_MAPPING2, &parms[0]);
7220 RTMemFree(pFolderName);
7221 RTMemFree(pMapName);
7222
7223 if (RT_FAILURE(vrc))
7224 return setError(E_FAIL,
7225 tr("Could not create a shared folder '%s' mapped to '%s' (%Rrc)"),
7226 strName.c_str(), aData.m_strHostPath.c_str(), vrc);
7227
7228 return S_OK;
7229}
7230
7231/**
7232 * Calls the HGCM service to remove the shared folder definition.
7233 *
7234 * @param aName Shared folder name.
7235 *
7236 * @note Must be called from under AutoVMCaller and when mpVM != NULL!
7237 * @note Doesn't lock anything.
7238 */
7239HRESULT Console::removeSharedFolder(const Utf8Str &strName)
7240{
7241 ComAssertRet(strName.isNotEmpty(), E_FAIL);
7242
7243 /* sanity checks */
7244 AssertReturn(mpUVM, E_FAIL);
7245 AssertReturn(m_pVMMDev && m_pVMMDev->isShFlActive(), E_FAIL);
7246
7247 VBOXHGCMSVCPARM parms;
7248 SHFLSTRING *pMapName;
7249 size_t cbString;
7250
7251 Log(("Removing shared folder '%s'\n", strName.c_str()));
7252
7253 Bstr bstrName(strName);
7254 cbString = (bstrName.length() + 1) * sizeof(RTUTF16);
7255 if (cbString >= UINT16_MAX)
7256 return setError(E_INVALIDARG, tr("The name is too long"));
7257 pMapName = (SHFLSTRING *) RTMemAllocZ(sizeof(SHFLSTRING) + cbString);
7258 Assert(pMapName);
7259 memcpy(pMapName->String.ucs2, bstrName.raw(), cbString);
7260
7261 pMapName->u16Size = (uint16_t)cbString;
7262 pMapName->u16Length = (uint16_t)cbString - sizeof(RTUTF16);
7263
7264 parms.type = VBOX_HGCM_SVC_PARM_PTR;
7265 parms.u.pointer.addr = pMapName;
7266 parms.u.pointer.size = sizeof(SHFLSTRING) + (uint16_t)cbString;
7267
7268 int vrc = m_pVMMDev->hgcmHostCall("VBoxSharedFolders",
7269 SHFL_FN_REMOVE_MAPPING,
7270 1, &parms);
7271 RTMemFree(pMapName);
7272 if (RT_FAILURE(vrc))
7273 return setError(E_FAIL,
7274 tr("Could not remove the shared folder '%s' (%Rrc)"),
7275 strName.c_str(), vrc);
7276
7277 return S_OK;
7278}
7279
7280/**
7281 * VM state callback function. Called by the VMM
7282 * using its state machine states.
7283 *
7284 * Primarily used to handle VM initiated power off, suspend and state saving,
7285 * but also for doing termination completed work (VMSTATE_TERMINATE).
7286 *
7287 * In general this function is called in the context of the EMT.
7288 *
7289 * @param aVM The VM handle.
7290 * @param aState The new state.
7291 * @param aOldState The old state.
7292 * @param aUser The user argument (pointer to the Console object).
7293 *
7294 * @note Locks the Console object for writing.
7295 */
7296DECLCALLBACK(void) Console::vmstateChangeCallback(PVM aVM,
7297 VMSTATE aState,
7298 VMSTATE aOldState,
7299 void *aUser)
7300{
7301 LogFlowFunc(("Changing state from %s to %s (aVM=%p)\n",
7302 VMR3GetStateName(aOldState), VMR3GetStateName(aState), aVM));
7303
7304 Console *that = static_cast<Console *>(aUser);
7305 AssertReturnVoid(that);
7306
7307 AutoCaller autoCaller(that);
7308
7309 /* Note that we must let this method proceed even if Console::uninit() has
7310 * been already called. In such case this VMSTATE change is a result of:
7311 * 1) powerDown() called from uninit() itself, or
7312 * 2) VM-(guest-)initiated power off. */
7313 AssertReturnVoid( autoCaller.isOk()
7314 || autoCaller.state() == InUninit);
7315
7316 switch (aState)
7317 {
7318 /*
7319 * The VM has terminated
7320 */
7321 case VMSTATE_OFF:
7322 {
7323 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
7324
7325 if (that->mVMStateChangeCallbackDisabled)
7326 break;
7327
7328 /* Do we still think that it is running? It may happen if this is a
7329 * VM-(guest-)initiated shutdown/poweroff.
7330 */
7331 if ( that->mMachineState != MachineState_Stopping
7332 && that->mMachineState != MachineState_Saving
7333 && that->mMachineState != MachineState_Restoring
7334 && that->mMachineState != MachineState_TeleportingIn
7335 && that->mMachineState != MachineState_FaultTolerantSyncing
7336 && that->mMachineState != MachineState_TeleportingPausedVM
7337 && !that->mVMIsAlreadyPoweringOff
7338 )
7339 {
7340 LogFlowFunc(("VM has powered itself off but Console still thinks it is running. Notifying.\n"));
7341
7342 /* prevent powerDown() from calling VMR3PowerOff() again */
7343 Assert(that->mVMPoweredOff == false);
7344 that->mVMPoweredOff = true;
7345
7346 /*
7347 * request a progress object from the server
7348 * (this will set the machine state to Stopping on the server
7349 * to block others from accessing this machine)
7350 */
7351 ComPtr<IProgress> pProgress;
7352 HRESULT rc = that->mControl->BeginPoweringDown(pProgress.asOutParam());
7353 AssertComRC(rc);
7354
7355 /* sync the state with the server */
7356 that->setMachineStateLocally(MachineState_Stopping);
7357
7358 /* Setup task object and thread to carry out the operation
7359 * asynchronously (if we call powerDown() right here but there
7360 * is one or more mpVM callers (added with addVMCaller()) we'll
7361 * deadlock).
7362 */
7363 std::auto_ptr<VMPowerDownTask> task(new VMPowerDownTask(that,
7364 pProgress));
7365
7366 /* If creating a task failed, this can currently mean one of
7367 * two: either Console::uninit() has been called just a ms
7368 * before (so a powerDown() call is already on the way), or
7369 * powerDown() itself is being already executed. Just do
7370 * nothing.
7371 */
7372 if (!task->isOk())
7373 {
7374 LogFlowFunc(("Console is already being uninitialized.\n"));
7375 break;
7376 }
7377
7378 int vrc = RTThreadCreate(NULL, Console::powerDownThread,
7379 (void *) task.get(), 0,
7380 RTTHREADTYPE_MAIN_WORKER, 0,
7381 "VMPowerDown");
7382 AssertMsgRCBreak(vrc, ("Could not create VMPowerDown thread (%Rrc)\n", vrc));
7383
7384 /* task is now owned by powerDownThread(), so release it */
7385 task.release();
7386 }
7387 break;
7388 }
7389
7390 /* The VM has been completely destroyed.
7391 *
7392 * Note: This state change can happen at two points:
7393 * 1) At the end of VMR3Destroy() if it was not called from EMT.
7394 * 2) At the end of vmR3EmulationThread if VMR3Destroy() was
7395 * called by EMT.
7396 */
7397 case VMSTATE_TERMINATED:
7398 {
7399 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
7400
7401 if (that->mVMStateChangeCallbackDisabled)
7402 break;
7403
7404 /* Terminate host interface networking. If aVM is NULL, we've been
7405 * manually called from powerUpThread() either before calling
7406 * VMR3Create() or after VMR3Create() failed, so no need to touch
7407 * networking.
7408 */
7409 if (aVM)
7410 that->powerDownHostInterfaces();
7411
7412 /* From now on the machine is officially powered down or remains in
7413 * the Saved state.
7414 */
7415 switch (that->mMachineState)
7416 {
7417 default:
7418 AssertFailed();
7419 /* fall through */
7420 case MachineState_Stopping:
7421 /* successfully powered down */
7422 that->setMachineState(MachineState_PoweredOff);
7423 break;
7424 case MachineState_Saving:
7425 /* successfully saved */
7426 that->setMachineState(MachineState_Saved);
7427 break;
7428 case MachineState_Starting:
7429 /* failed to start, but be patient: set back to PoweredOff
7430 * (for similarity with the below) */
7431 that->setMachineState(MachineState_PoweredOff);
7432 break;
7433 case MachineState_Restoring:
7434 /* failed to load the saved state file, but be patient: set
7435 * back to Saved (to preserve the saved state file) */
7436 that->setMachineState(MachineState_Saved);
7437 break;
7438 case MachineState_TeleportingIn:
7439 /* Teleportation failed or was canceled. Back to powered off. */
7440 that->setMachineState(MachineState_PoweredOff);
7441 break;
7442 case MachineState_TeleportingPausedVM:
7443 /* Successfully teleported the VM. */
7444 that->setMachineState(MachineState_Teleported);
7445 break;
7446 case MachineState_FaultTolerantSyncing:
7447 /* Fault tolerant sync failed or was canceled. Back to powered off. */
7448 that->setMachineState(MachineState_PoweredOff);
7449 break;
7450 }
7451 break;
7452 }
7453
7454 case VMSTATE_RESETTING:
7455 {
7456#ifdef VBOX_WITH_GUEST_PROPS
7457 /* Do not take any read/write locks here! */
7458 that->guestPropertiesHandleVMReset();
7459#endif
7460 break;
7461 }
7462
7463 case VMSTATE_SUSPENDED:
7464 {
7465 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
7466
7467 if (that->mVMStateChangeCallbackDisabled)
7468 break;
7469
7470 switch (that->mMachineState)
7471 {
7472 case MachineState_Teleporting:
7473 that->setMachineState(MachineState_TeleportingPausedVM);
7474 break;
7475
7476 case MachineState_LiveSnapshotting:
7477 that->setMachineState(MachineState_Saving);
7478 break;
7479
7480 case MachineState_TeleportingPausedVM:
7481 case MachineState_Saving:
7482 case MachineState_Restoring:
7483 case MachineState_Stopping:
7484 case MachineState_TeleportingIn:
7485 case MachineState_FaultTolerantSyncing:
7486 /* The worker thread handles the transition. */
7487 break;
7488
7489 default:
7490 AssertMsgFailed(("%s\n", Global::stringifyMachineState(that->mMachineState)));
7491 case MachineState_Running:
7492 that->setMachineState(MachineState_Paused);
7493 break;
7494
7495 case MachineState_Paused:
7496 /* Nothing to do. */
7497 break;
7498 }
7499 break;
7500 }
7501
7502 case VMSTATE_SUSPENDED_LS:
7503 case VMSTATE_SUSPENDED_EXT_LS:
7504 {
7505 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
7506 if (that->mVMStateChangeCallbackDisabled)
7507 break;
7508 switch (that->mMachineState)
7509 {
7510 case MachineState_Teleporting:
7511 that->setMachineState(MachineState_TeleportingPausedVM);
7512 break;
7513
7514 case MachineState_LiveSnapshotting:
7515 that->setMachineState(MachineState_Saving);
7516 break;
7517
7518 case MachineState_TeleportingPausedVM:
7519 case MachineState_Saving:
7520 /* ignore */
7521 break;
7522
7523 default:
7524 AssertMsgFailed(("%s/%s -> %s\n", Global::stringifyMachineState(that->mMachineState), VMR3GetStateName(aOldState), VMR3GetStateName(aState) ));
7525 that->setMachineState(MachineState_Paused);
7526 break;
7527 }
7528 break;
7529 }
7530
7531 case VMSTATE_RUNNING:
7532 {
7533 if ( aOldState == VMSTATE_POWERING_ON
7534 || aOldState == VMSTATE_RESUMING
7535 || aOldState == VMSTATE_RUNNING_FT)
7536 {
7537 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
7538
7539 if (that->mVMStateChangeCallbackDisabled)
7540 break;
7541
7542 Assert( ( ( that->mMachineState == MachineState_Starting
7543 || that->mMachineState == MachineState_Paused)
7544 && aOldState == VMSTATE_POWERING_ON)
7545 || ( ( that->mMachineState == MachineState_Restoring
7546 || that->mMachineState == MachineState_TeleportingIn
7547 || that->mMachineState == MachineState_Paused
7548 || that->mMachineState == MachineState_Saving
7549 )
7550 && aOldState == VMSTATE_RESUMING)
7551 || ( that->mMachineState == MachineState_FaultTolerantSyncing
7552 && aOldState == VMSTATE_RUNNING_FT));
7553
7554 that->setMachineState(MachineState_Running);
7555 }
7556
7557 break;
7558 }
7559
7560 case VMSTATE_RUNNING_LS:
7561 AssertMsg( that->mMachineState == MachineState_LiveSnapshotting
7562 || that->mMachineState == MachineState_Teleporting,
7563 ("%s/%s -> %s\n", Global::stringifyMachineState(that->mMachineState), VMR3GetStateName(aOldState), VMR3GetStateName(aState) ));
7564 break;
7565
7566 case VMSTATE_RUNNING_FT:
7567 AssertMsg(that->mMachineState == MachineState_FaultTolerantSyncing,
7568 ("%s/%s -> %s\n", Global::stringifyMachineState(that->mMachineState), VMR3GetStateName(aOldState), VMR3GetStateName(aState) ));
7569 break;
7570
7571 case VMSTATE_FATAL_ERROR:
7572 {
7573 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
7574
7575 if (that->mVMStateChangeCallbackDisabled)
7576 break;
7577
7578 /* Fatal errors are only for running VMs. */
7579 Assert(Global::IsOnline(that->mMachineState));
7580
7581 /* Note! 'Pause' is used here in want of something better. There
7582 * are currently only two places where fatal errors might be
7583 * raised, so it is not worth adding a new externally
7584 * visible state for this yet. */
7585 that->setMachineState(MachineState_Paused);
7586 break;
7587 }
7588
7589 case VMSTATE_GURU_MEDITATION:
7590 {
7591 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
7592
7593 if (that->mVMStateChangeCallbackDisabled)
7594 break;
7595
7596 /* Guru are only for running VMs */
7597 Assert(Global::IsOnline(that->mMachineState));
7598
7599 that->setMachineState(MachineState_Stuck);
7600 break;
7601 }
7602
7603 default: /* shut up gcc */
7604 break;
7605 }
7606}
7607
7608#ifdef VBOX_WITH_USB
7609/**
7610 * Sends a request to VMM to attach the given host device.
7611 * After this method succeeds, the attached device will appear in the
7612 * mUSBDevices collection.
7613 *
7614 * @param aHostDevice device to attach
7615 *
7616 * @note Synchronously calls EMT.
7617 * @note Must be called from under this object's lock.
7618 */
7619HRESULT Console::attachUSBDevice(IUSBDevice *aHostDevice, ULONG aMaskedIfs)
7620{
7621 AssertReturn(aHostDevice, E_FAIL);
7622 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
7623
7624 /* still want a lock object because we need to leave it */
7625 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7626
7627 HRESULT hrc;
7628
7629 /*
7630 * Get the address and the Uuid, and call the pfnCreateProxyDevice roothub
7631 * method in EMT (using usbAttachCallback()).
7632 */
7633 Bstr BstrAddress;
7634 hrc = aHostDevice->COMGETTER(Address)(BstrAddress.asOutParam());
7635 ComAssertComRCRetRC(hrc);
7636
7637 Utf8Str Address(BstrAddress);
7638
7639 Bstr id;
7640 hrc = aHostDevice->COMGETTER(Id)(id.asOutParam());
7641 ComAssertComRCRetRC(hrc);
7642 Guid uuid(id);
7643
7644 BOOL fRemote = FALSE;
7645 hrc = aHostDevice->COMGETTER(Remote)(&fRemote);
7646 ComAssertComRCRetRC(hrc);
7647
7648 /* Get the VM handle. */
7649 SafeVMPtr ptrVM(this);
7650 if (!ptrVM.isOk())
7651 return ptrVM.rc();
7652
7653 LogFlowThisFunc(("Proxying USB device '%s' {%RTuuid}...\n",
7654 Address.c_str(), uuid.raw()));
7655
7656 /* leave the lock before a VMR3* call (EMT will call us back)! */
7657 alock.leave();
7658
7659/** @todo just do everything here and only wrap the PDMR3Usb call. That'll offload some notification stuff from the EMT thread. */
7660 int vrc = VMR3ReqCallWait(ptrVM, VMCPUID_ANY,
7661 (PFNRT)usbAttachCallback, 7,
7662 this, ptrVM.raw(), aHostDevice, uuid.raw(), fRemote, Address.c_str(), aMaskedIfs);
7663
7664 /* restore the lock */
7665 alock.enter();
7666
7667 /* hrc is S_OK here */
7668
7669 if (RT_FAILURE(vrc))
7670 {
7671 LogWarningThisFunc(("Failed to create proxy device for '%s' {%RTuuid} (%Rrc)\n",
7672 Address.c_str(), uuid.raw(), vrc));
7673
7674 switch (vrc)
7675 {
7676 case VERR_VUSB_NO_PORTS:
7677 hrc = setError(E_FAIL,
7678 tr("Failed to attach the USB device. (No available ports on the USB controller)."));
7679 break;
7680 case VERR_VUSB_USBFS_PERMISSION:
7681 hrc = setError(E_FAIL,
7682 tr("Not permitted to open the USB device, check usbfs options"));
7683 break;
7684 default:
7685 hrc = setError(E_FAIL,
7686 tr("Failed to create a proxy device for the USB device. (Error: %Rrc)"),
7687 vrc);
7688 break;
7689 }
7690 }
7691
7692 return hrc;
7693}
7694
7695/**
7696 * USB device attach callback used by AttachUSBDevice().
7697 * Note that AttachUSBDevice() doesn't return until this callback is executed,
7698 * so we don't use AutoCaller and don't care about reference counters of
7699 * interface pointers passed in.
7700 *
7701 * @thread EMT
7702 * @note Locks the console object for writing.
7703 */
7704//static
7705DECLCALLBACK(int)
7706Console::usbAttachCallback(Console *that, PVM pVM, IUSBDevice *aHostDevice, PCRTUUID aUuid, bool aRemote, const char *aAddress, ULONG aMaskedIfs)
7707{
7708 LogFlowFuncEnter();
7709 LogFlowFunc(("that={%p}\n", that));
7710
7711 AssertReturn(that && aUuid, VERR_INVALID_PARAMETER);
7712
7713 void *pvRemoteBackend = NULL;
7714 if (aRemote)
7715 {
7716 RemoteUSBDevice *pRemoteUSBDevice = static_cast<RemoteUSBDevice *>(aHostDevice);
7717 Guid guid(*aUuid);
7718
7719 pvRemoteBackend = that->consoleVRDPServer()->USBBackendRequestPointer(pRemoteUSBDevice->clientId(), &guid);
7720 if (!pvRemoteBackend)
7721 return VERR_INVALID_PARAMETER; /* The clientId is invalid then. */
7722 }
7723
7724 USHORT portVersion = 1;
7725 HRESULT hrc = aHostDevice->COMGETTER(PortVersion)(&portVersion);
7726 AssertComRCReturn(hrc, VERR_GENERAL_FAILURE);
7727 Assert(portVersion == 1 || portVersion == 2);
7728
7729 int vrc = PDMR3USBCreateProxyDevice(pVM, aUuid, aRemote, aAddress, pvRemoteBackend,
7730 portVersion == 1 ? VUSB_STDVER_11 : VUSB_STDVER_20, aMaskedIfs);
7731 if (RT_SUCCESS(vrc))
7732 {
7733 /* Create a OUSBDevice and add it to the device list */
7734 ComObjPtr<OUSBDevice> pUSBDevice;
7735 pUSBDevice.createObject();
7736 hrc = pUSBDevice->init(aHostDevice);
7737 AssertComRC(hrc);
7738
7739 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
7740 that->mUSBDevices.push_back(pUSBDevice);
7741 LogFlowFunc(("Attached device {%RTuuid}\n", pUSBDevice->id().raw()));
7742
7743 /* notify callbacks */
7744 that->onUSBDeviceStateChange(pUSBDevice, true /* aAttached */, NULL);
7745 }
7746
7747 LogFlowFunc(("vrc=%Rrc\n", vrc));
7748 LogFlowFuncLeave();
7749 return vrc;
7750}
7751
7752/**
7753 * Sends a request to VMM to detach the given host device. After this method
7754 * succeeds, the detached device will disappear from the mUSBDevices
7755 * collection.
7756 *
7757 * @param aIt Iterator pointing to the device to detach.
7758 *
7759 * @note Synchronously calls EMT.
7760 * @note Must be called from under this object's lock.
7761 */
7762HRESULT Console::detachUSBDevice(USBDeviceList::iterator &aIt)
7763{
7764 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
7765
7766 /* still want a lock object because we need to leave it */
7767 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7768
7769 /* Get the VM handle. */
7770 SafeVMPtr ptrVM(this);
7771 if (!ptrVM.isOk())
7772 return ptrVM.rc();
7773
7774 /* if the device is attached, then there must at least one USB hub. */
7775 AssertReturn(PDMR3USBHasHub(ptrVM), E_FAIL);
7776
7777 LogFlowThisFunc(("Detaching USB proxy device {%RTuuid}...\n",
7778 (*aIt)->id().raw()));
7779
7780 /* leave the lock before a VMR3* call (EMT will call us back)! */
7781 alock.leave();
7782
7783/** @todo just do everything here and only wrap the PDMR3Usb call. That'll offload some notification stuff from the EMT thread. */
7784 int vrc = VMR3ReqCallWait(ptrVM, VMCPUID_ANY,
7785 (PFNRT)usbDetachCallback, 5,
7786 this, ptrVM.raw(), &aIt, (*aIt)->id().raw());
7787 ComAssertRCRet(vrc, E_FAIL);
7788
7789 return S_OK;
7790}
7791
7792/**
7793 * USB device detach callback used by DetachUSBDevice().
7794 * Note that DetachUSBDevice() doesn't return until this callback is executed,
7795 * so we don't use AutoCaller and don't care about reference counters of
7796 * interface pointers passed in.
7797 *
7798 * @thread EMT
7799 * @note Locks the console object for writing.
7800 */
7801//static
7802DECLCALLBACK(int)
7803Console::usbDetachCallback(Console *that, PVM pVM, USBDeviceList::iterator *aIt, PCRTUUID aUuid)
7804{
7805 LogFlowFuncEnter();
7806 LogFlowFunc(("that={%p}\n", that));
7807
7808 AssertReturn(that && aUuid, VERR_INVALID_PARAMETER);
7809 ComObjPtr<OUSBDevice> pUSBDevice = **aIt;
7810
7811 /*
7812 * If that was a remote device, release the backend pointer.
7813 * The pointer was requested in usbAttachCallback.
7814 */
7815 BOOL fRemote = FALSE;
7816
7817 HRESULT hrc2 = (**aIt)->COMGETTER(Remote)(&fRemote);
7818 if (FAILED(hrc2))
7819 setErrorStatic(hrc2, "GetRemote() failed");
7820
7821 if (fRemote)
7822 {
7823 Guid guid(*aUuid);
7824 that->consoleVRDPServer()->USBBackendReleasePointer(&guid);
7825 }
7826
7827 int vrc = PDMR3USBDetachDevice(pVM, aUuid);
7828
7829 if (RT_SUCCESS(vrc))
7830 {
7831 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
7832
7833 /* Remove the device from the collection */
7834 that->mUSBDevices.erase(*aIt);
7835 LogFlowFunc(("Detached device {%RTuuid}\n", pUSBDevice->id().raw()));
7836
7837 /* notify callbacks */
7838 that->onUSBDeviceStateChange(pUSBDevice, false /* aAttached */, NULL);
7839 }
7840
7841 LogFlowFunc(("vrc=%Rrc\n", vrc));
7842 LogFlowFuncLeave();
7843 return vrc;
7844}
7845#endif /* VBOX_WITH_USB */
7846
7847/* Note: FreeBSD needs this whether netflt is used or not. */
7848#if ((defined(RT_OS_LINUX) && !defined(VBOX_WITH_NETFLT)) || defined(RT_OS_FREEBSD))
7849/**
7850 * Helper function to handle host interface device creation and attachment.
7851 *
7852 * @param networkAdapter the network adapter which attachment should be reset
7853 * @return COM status code
7854 *
7855 * @note The caller must lock this object for writing.
7856 *
7857 * @todo Move this back into the driver!
7858 */
7859HRESULT Console::attachToTapInterface(INetworkAdapter *networkAdapter)
7860{
7861 LogFlowThisFunc(("\n"));
7862 /* sanity check */
7863 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
7864
7865# ifdef VBOX_STRICT
7866 /* paranoia */
7867 NetworkAttachmentType_T attachment;
7868 networkAdapter->COMGETTER(AttachmentType)(&attachment);
7869 Assert(attachment == NetworkAttachmentType_Bridged);
7870# endif /* VBOX_STRICT */
7871
7872 HRESULT rc = S_OK;
7873
7874 ULONG slot = 0;
7875 rc = networkAdapter->COMGETTER(Slot)(&slot);
7876 AssertComRC(rc);
7877
7878# ifdef RT_OS_LINUX
7879 /*
7880 * Allocate a host interface device
7881 */
7882 int rcVBox = RTFileOpen(&maTapFD[slot], "/dev/net/tun",
7883 RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_INHERIT);
7884 if (RT_SUCCESS(rcVBox))
7885 {
7886 /*
7887 * Set/obtain the tap interface.
7888 */
7889 struct ifreq IfReq;
7890 memset(&IfReq, 0, sizeof(IfReq));
7891 /* The name of the TAP interface we are using */
7892 Bstr tapDeviceName;
7893 rc = networkAdapter->COMGETTER(BridgedInterface)(tapDeviceName.asOutParam());
7894 if (FAILED(rc))
7895 tapDeviceName.setNull(); /* Is this necessary? */
7896 if (tapDeviceName.isEmpty())
7897 {
7898 LogRel(("No TAP device name was supplied.\n"));
7899 rc = setError(E_FAIL, tr("No TAP device name was supplied for the host networking interface"));
7900 }
7901
7902 if (SUCCEEDED(rc))
7903 {
7904 /* If we are using a static TAP device then try to open it. */
7905 Utf8Str str(tapDeviceName);
7906 if (str.length() <= sizeof(IfReq.ifr_name))
7907 strcpy(IfReq.ifr_name, str.c_str());
7908 else
7909 memcpy(IfReq.ifr_name, str.c_str(), sizeof(IfReq.ifr_name) - 1); /** @todo bitch about names which are too long... */
7910 IfReq.ifr_flags = IFF_TAP | IFF_NO_PI;
7911 rcVBox = ioctl(maTapFD[slot], TUNSETIFF, &IfReq);
7912 if (rcVBox != 0)
7913 {
7914 LogRel(("Failed to open the host network interface %ls\n", tapDeviceName.raw()));
7915 rc = setError(E_FAIL,
7916 tr("Failed to open the host network interface %ls"),
7917 tapDeviceName.raw());
7918 }
7919 }
7920 if (SUCCEEDED(rc))
7921 {
7922 /*
7923 * Make it pollable.
7924 */
7925 if (fcntl(maTapFD[slot], F_SETFL, O_NONBLOCK) != -1)
7926 {
7927 Log(("attachToTapInterface: %RTfile %ls\n", maTapFD[slot], tapDeviceName.raw()));
7928 /*
7929 * Here is the right place to communicate the TAP file descriptor and
7930 * the host interface name to the server if/when it becomes really
7931 * necessary.
7932 */
7933 maTAPDeviceName[slot] = tapDeviceName;
7934 rcVBox = VINF_SUCCESS;
7935 }
7936 else
7937 {
7938 int iErr = errno;
7939
7940 LogRel(("Configuration error: Failed to configure /dev/net/tun non blocking. Error: %s\n", strerror(iErr)));
7941 rcVBox = VERR_HOSTIF_BLOCKING;
7942 rc = setError(E_FAIL,
7943 tr("could not set up the host networking device for non blocking access: %s"),
7944 strerror(errno));
7945 }
7946 }
7947 }
7948 else
7949 {
7950 LogRel(("Configuration error: Failed to open /dev/net/tun rc=%Rrc\n", rcVBox));
7951 switch (rcVBox)
7952 {
7953 case VERR_ACCESS_DENIED:
7954 /* will be handled by our caller */
7955 rc = rcVBox;
7956 break;
7957 default:
7958 rc = setError(E_FAIL,
7959 tr("Could not set up the host networking device: %Rrc"),
7960 rcVBox);
7961 break;
7962 }
7963 }
7964
7965# elif defined(RT_OS_FREEBSD)
7966 /*
7967 * Set/obtain the tap interface.
7968 */
7969 /* The name of the TAP interface we are using */
7970 Bstr tapDeviceName;
7971 rc = networkAdapter->COMGETTER(BridgedInterface)(tapDeviceName.asOutParam());
7972 if (FAILED(rc))
7973 tapDeviceName.setNull(); /* Is this necessary? */
7974 if (tapDeviceName.isEmpty())
7975 {
7976 LogRel(("No TAP device name was supplied.\n"));
7977 rc = setError(E_FAIL, tr("No TAP device name was supplied for the host networking interface"));
7978 }
7979 char szTapdev[1024] = "/dev/";
7980 /* If we are using a static TAP device then try to open it. */
7981 Utf8Str str(tapDeviceName);
7982 if (str.length() + strlen(szTapdev) <= sizeof(szTapdev))
7983 strcat(szTapdev, str.c_str());
7984 else
7985 memcpy(szTapdev + strlen(szTapdev), str.c_str(),
7986 sizeof(szTapdev) - strlen(szTapdev) - 1); /** @todo bitch about names which are too long... */
7987 int rcVBox = RTFileOpen(&maTapFD[slot], szTapdev,
7988 RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_INHERIT | RTFILE_O_NON_BLOCK);
7989
7990 if (RT_SUCCESS(rcVBox))
7991 maTAPDeviceName[slot] = tapDeviceName;
7992 else
7993 {
7994 switch (rcVBox)
7995 {
7996 case VERR_ACCESS_DENIED:
7997 /* will be handled by our caller */
7998 rc = rcVBox;
7999 break;
8000 default:
8001 rc = setError(E_FAIL,
8002 tr("Failed to open the host network interface %ls"),
8003 tapDeviceName.raw());
8004 break;
8005 }
8006 }
8007# else
8008# error "huh?"
8009# endif
8010 /* in case of failure, cleanup. */
8011 if (RT_FAILURE(rcVBox) && SUCCEEDED(rc))
8012 {
8013 LogRel(("General failure attaching to host interface\n"));
8014 rc = setError(E_FAIL,
8015 tr("General failure attaching to host interface"));
8016 }
8017 LogFlowThisFunc(("rc=%d\n", rc));
8018 return rc;
8019}
8020
8021
8022/**
8023 * Helper function to handle detachment from a host interface
8024 *
8025 * @param networkAdapter the network adapter which attachment should be reset
8026 * @return COM status code
8027 *
8028 * @note The caller must lock this object for writing.
8029 *
8030 * @todo Move this back into the driver!
8031 */
8032HRESULT Console::detachFromTapInterface(INetworkAdapter *networkAdapter)
8033{
8034 /* sanity check */
8035 LogFlowThisFunc(("\n"));
8036 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
8037
8038 HRESULT rc = S_OK;
8039# ifdef VBOX_STRICT
8040 /* paranoia */
8041 NetworkAttachmentType_T attachment;
8042 networkAdapter->COMGETTER(AttachmentType)(&attachment);
8043 Assert(attachment == NetworkAttachmentType_Bridged);
8044# endif /* VBOX_STRICT */
8045
8046 ULONG slot = 0;
8047 rc = networkAdapter->COMGETTER(Slot)(&slot);
8048 AssertComRC(rc);
8049
8050 /* is there an open TAP device? */
8051 if (maTapFD[slot] != NIL_RTFILE)
8052 {
8053 /*
8054 * Close the file handle.
8055 */
8056 Bstr tapDeviceName, tapTerminateApplication;
8057 bool isStatic = true;
8058 rc = networkAdapter->COMGETTER(BridgedInterface)(tapDeviceName.asOutParam());
8059 if (FAILED(rc) || tapDeviceName.isEmpty())
8060 {
8061 /* If the name is empty, this is a dynamic TAP device, so close it now,
8062 so that the termination script can remove the interface. Otherwise we still
8063 need the FD to pass to the termination script. */
8064 isStatic = false;
8065 int rcVBox = RTFileClose(maTapFD[slot]);
8066 AssertRC(rcVBox);
8067 maTapFD[slot] = NIL_RTFILE;
8068 }
8069 if (isStatic)
8070 {
8071 /* If we are using a static TAP device, we close it now, after having called the
8072 termination script. */
8073 int rcVBox = RTFileClose(maTapFD[slot]);
8074 AssertRC(rcVBox);
8075 }
8076 /* the TAP device name and handle are no longer valid */
8077 maTapFD[slot] = NIL_RTFILE;
8078 maTAPDeviceName[slot] = "";
8079 }
8080 LogFlowThisFunc(("returning %d\n", rc));
8081 return rc;
8082}
8083#endif /* (RT_OS_LINUX || RT_OS_FREEBSD) && !VBOX_WITH_NETFLT */
8084
8085/**
8086 * Called at power down to terminate host interface networking.
8087 *
8088 * @note The caller must lock this object for writing.
8089 */
8090HRESULT Console::powerDownHostInterfaces()
8091{
8092 LogFlowThisFunc(("\n"));
8093
8094 /* sanity check */
8095 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
8096
8097 /*
8098 * host interface termination handling
8099 */
8100 HRESULT rc;
8101 for (ULONG slot = 0; slot < SchemaDefs::NetworkAdapterCount; slot ++)
8102 {
8103 ComPtr<INetworkAdapter> pNetworkAdapter;
8104 rc = mMachine->GetNetworkAdapter(slot, pNetworkAdapter.asOutParam());
8105 if (FAILED(rc)) break;
8106
8107 BOOL enabled = FALSE;
8108 pNetworkAdapter->COMGETTER(Enabled)(&enabled);
8109 if (!enabled)
8110 continue;
8111
8112 NetworkAttachmentType_T attachment;
8113 pNetworkAdapter->COMGETTER(AttachmentType)(&attachment);
8114 if (attachment == NetworkAttachmentType_Bridged)
8115 {
8116#if ((defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD)) && !defined(VBOX_WITH_NETFLT))
8117 HRESULT rc2 = detachFromTapInterface(pNetworkAdapter);
8118 if (FAILED(rc2) && SUCCEEDED(rc))
8119 rc = rc2;
8120#endif /* (RT_OS_LINUX || RT_OS_FREEBSD) && !VBOX_WITH_NETFLT */
8121 }
8122 }
8123
8124 return rc;
8125}
8126
8127
8128/**
8129 * Process callback handler for VMR3LoadFromFile, VMR3LoadFromStream, VMR3Save
8130 * and VMR3Teleport.
8131 *
8132 * @param pVM The VM handle.
8133 * @param uPercent Completion percentage (0-100).
8134 * @param pvUser Pointer to an IProgress instance.
8135 * @return VINF_SUCCESS.
8136 */
8137/*static*/
8138DECLCALLBACK(int) Console::stateProgressCallback(PVM pVM, unsigned uPercent, void *pvUser)
8139{
8140 IProgress *pProgress = static_cast<IProgress *>(pvUser);
8141
8142 /* update the progress object */
8143 if (pProgress)
8144 pProgress->SetCurrentOperationProgress(uPercent);
8145
8146 return VINF_SUCCESS;
8147}
8148
8149/**
8150 * @copydoc FNVMATERROR
8151 *
8152 * @remarks Might be some tiny serialization concerns with access to the string
8153 * object here...
8154 */
8155/*static*/ DECLCALLBACK(void)
8156Console::genericVMSetErrorCallback(PVM pVM, void *pvUser, int rc, RT_SRC_POS_DECL,
8157 const char *pszErrorFmt, va_list va)
8158{
8159 Utf8Str *pErrorText = (Utf8Str *)pvUser;
8160 AssertPtr(pErrorText);
8161
8162 /* We ignore RT_SRC_POS_DECL arguments to avoid confusion of end-users. */
8163 va_list va2;
8164 va_copy(va2, va);
8165
8166 /* Append to any the existing error message. */
8167 if (pErrorText->length())
8168 *pErrorText = Utf8StrFmt("%s.\n%N (%Rrc)", pErrorText->c_str(),
8169 pszErrorFmt, &va2, rc, rc);
8170 else
8171 *pErrorText = Utf8StrFmt("%N (%Rrc)", pszErrorFmt, &va2, rc, rc);
8172
8173 va_end(va2);
8174}
8175
8176/**
8177 * VM runtime error callback function.
8178 * See VMSetRuntimeError for the detailed description of parameters.
8179 *
8180 * @param pVM The VM handle.
8181 * @param pvUser The user argument.
8182 * @param fFlags The action flags. See VMSETRTERR_FLAGS_*.
8183 * @param pszErrorId Error ID string.
8184 * @param pszFormat Error message format string.
8185 * @param va Error message arguments.
8186 * @thread EMT.
8187 */
8188/* static */ DECLCALLBACK(void)
8189Console::setVMRuntimeErrorCallback(PVM pVM, void *pvUser, uint32_t fFlags,
8190 const char *pszErrorId,
8191 const char *pszFormat, va_list va)
8192{
8193 bool const fFatal = !!(fFlags & VMSETRTERR_FLAGS_FATAL);
8194 LogFlowFuncEnter();
8195
8196 Console *that = static_cast<Console *>(pvUser);
8197 AssertReturnVoid(that);
8198
8199 Utf8Str message(pszFormat, va);
8200
8201 LogRel(("Console: VM runtime error: fatal=%RTbool, errorID=%s message=\"%s\"\n",
8202 fFatal, pszErrorId, message.c_str()));
8203
8204 that->onRuntimeError(BOOL(fFatal), Bstr(pszErrorId).raw(),
8205 Bstr(message).raw());
8206
8207 LogFlowFuncLeave();
8208}
8209
8210/**
8211 * Captures USB devices that match filters of the VM.
8212 * Called at VM startup.
8213 *
8214 * @param pVM The VM handle.
8215 *
8216 * @note The caller must lock this object for writing.
8217 */
8218HRESULT Console::captureUSBDevices(PVM pVM)
8219{
8220 LogFlowThisFunc(("\n"));
8221
8222 /* sanity check */
8223 ComAssertRet(isWriteLockOnCurrentThread(), E_FAIL);
8224
8225 /* If the machine has an USB controller, ask the USB proxy service to
8226 * capture devices */
8227 PPDMIBASE pBase;
8228 int vrc = PDMR3QueryLun(pVM, "usb-ohci", 0, 0, &pBase);
8229 if (RT_SUCCESS(vrc))
8230 {
8231 /* leave the lock before calling Host in VBoxSVC since Host may call
8232 * us back from under its lock (e.g. onUSBDeviceAttach()) which would
8233 * produce an inter-process dead-lock otherwise. */
8234 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8235 alock.leave();
8236
8237 HRESULT hrc = mControl->AutoCaptureUSBDevices();
8238 ComAssertComRCRetRC(hrc);
8239 }
8240 else if ( vrc == VERR_PDM_DEVICE_NOT_FOUND
8241 || vrc == VERR_PDM_DEVICE_INSTANCE_NOT_FOUND)
8242 vrc = VINF_SUCCESS;
8243 else
8244 AssertRC(vrc);
8245
8246 return RT_SUCCESS(vrc) ? S_OK : E_FAIL;
8247}
8248
8249
8250/**
8251 * Detach all USB device which are attached to the VM for the
8252 * purpose of clean up and such like.
8253 *
8254 * @note The caller must lock this object for writing.
8255 */
8256void Console::detachAllUSBDevices(bool aDone)
8257{
8258 LogFlowThisFunc(("aDone=%RTbool\n", aDone));
8259
8260 /* sanity check */
8261 AssertReturnVoid(isWriteLockOnCurrentThread());
8262
8263 mUSBDevices.clear();
8264
8265 /* leave the lock before calling Host in VBoxSVC since Host may call
8266 * us back from under its lock (e.g. onUSBDeviceAttach()) which would
8267 * produce an inter-process dead-lock otherwise. */
8268 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8269 alock.leave();
8270
8271 mControl->DetachAllUSBDevices(aDone);
8272}
8273
8274/**
8275 * @note Locks this object for writing.
8276 */
8277void Console::processRemoteUSBDevices(uint32_t u32ClientId, VRDEUSBDEVICEDESC *pDevList, uint32_t cbDevList)
8278{
8279 LogFlowThisFuncEnter();
8280 LogFlowThisFunc(("u32ClientId = %d, pDevList=%p, cbDevList = %d\n", u32ClientId, pDevList, cbDevList));
8281
8282 AutoCaller autoCaller(this);
8283 if (!autoCaller.isOk())
8284 {
8285 /* Console has been already uninitialized, deny request */
8286 AssertMsgFailed(("Console is already uninitialized\n"));
8287 LogFlowThisFunc(("Console is already uninitialized\n"));
8288 LogFlowThisFuncLeave();
8289 return;
8290 }
8291
8292 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8293
8294 /*
8295 * Mark all existing remote USB devices as dirty.
8296 */
8297 for (RemoteUSBDeviceList::iterator it = mRemoteUSBDevices.begin();
8298 it != mRemoteUSBDevices.end();
8299 ++it)
8300 {
8301 (*it)->dirty(true);
8302 }
8303
8304 /*
8305 * Process the pDevList and add devices those are not already in the mRemoteUSBDevices list.
8306 */
8307 /** @todo (sunlover) REMOTE_USB Strict validation of the pDevList. */
8308 VRDEUSBDEVICEDESC *e = pDevList;
8309
8310 /* The cbDevList condition must be checked first, because the function can
8311 * receive pDevList = NULL and cbDevList = 0 on client disconnect.
8312 */
8313 while (cbDevList >= 2 && e->oNext)
8314 {
8315 /* Sanitize incoming strings in case they aren't valid UTF-8. */
8316 if (e->oManufacturer)
8317 RTStrPurgeEncoding((char *)e + e->oManufacturer);
8318 if (e->oProduct)
8319 RTStrPurgeEncoding((char *)e + e->oProduct);
8320 if (e->oSerialNumber)
8321 RTStrPurgeEncoding((char *)e + e->oSerialNumber);
8322
8323 LogFlowThisFunc(("vendor %04X, product %04X, name = %s\n",
8324 e->idVendor, e->idProduct,
8325 e->oProduct? (char *)e + e->oProduct: ""));
8326
8327 bool fNewDevice = true;
8328
8329 for (RemoteUSBDeviceList::iterator it = mRemoteUSBDevices.begin();
8330 it != mRemoteUSBDevices.end();
8331 ++it)
8332 {
8333 if ((*it)->devId() == e->id
8334 && (*it)->clientId() == u32ClientId)
8335 {
8336 /* The device is already in the list. */
8337 (*it)->dirty(false);
8338 fNewDevice = false;
8339 break;
8340 }
8341 }
8342
8343 if (fNewDevice)
8344 {
8345 LogRel(("Remote USB: ++++ Vendor %04X. Product %04X. Name = [%s].\n",
8346 e->idVendor, e->idProduct, e->oProduct? (char *)e + e->oProduct: ""));
8347
8348 /* Create the device object and add the new device to list. */
8349 ComObjPtr<RemoteUSBDevice> pUSBDevice;
8350 pUSBDevice.createObject();
8351 pUSBDevice->init(u32ClientId, e);
8352
8353 mRemoteUSBDevices.push_back(pUSBDevice);
8354
8355 /* Check if the device is ok for current USB filters. */
8356 BOOL fMatched = FALSE;
8357 ULONG fMaskedIfs = 0;
8358
8359 HRESULT hrc = mControl->RunUSBDeviceFilters(pUSBDevice, &fMatched, &fMaskedIfs);
8360
8361 AssertComRC(hrc);
8362
8363 LogFlowThisFunc(("USB filters return %d %#x\n", fMatched, fMaskedIfs));
8364
8365 if (fMatched)
8366 {
8367 hrc = onUSBDeviceAttach(pUSBDevice, NULL, fMaskedIfs);
8368
8369 /// @todo (r=dmik) warning reporting subsystem
8370
8371 if (hrc == S_OK)
8372 {
8373 LogFlowThisFunc(("Device attached\n"));
8374 pUSBDevice->captured(true);
8375 }
8376 }
8377 }
8378
8379 if (cbDevList < e->oNext)
8380 {
8381 LogWarningThisFunc(("cbDevList %d > oNext %d\n",
8382 cbDevList, e->oNext));
8383 break;
8384 }
8385
8386 cbDevList -= e->oNext;
8387
8388 e = (VRDEUSBDEVICEDESC *)((uint8_t *)e + e->oNext);
8389 }
8390
8391 /*
8392 * Remove dirty devices, that is those which are not reported by the server anymore.
8393 */
8394 for (;;)
8395 {
8396 ComObjPtr<RemoteUSBDevice> pUSBDevice;
8397
8398 RemoteUSBDeviceList::iterator it = mRemoteUSBDevices.begin();
8399 while (it != mRemoteUSBDevices.end())
8400 {
8401 if ((*it)->dirty())
8402 {
8403 pUSBDevice = *it;
8404 break;
8405 }
8406
8407 ++ it;
8408 }
8409
8410 if (!pUSBDevice)
8411 {
8412 break;
8413 }
8414
8415 USHORT vendorId = 0;
8416 pUSBDevice->COMGETTER(VendorId)(&vendorId);
8417
8418 USHORT productId = 0;
8419 pUSBDevice->COMGETTER(ProductId)(&productId);
8420
8421 Bstr product;
8422 pUSBDevice->COMGETTER(Product)(product.asOutParam());
8423
8424 LogRel(("Remote USB: ---- Vendor %04X. Product %04X. Name = [%ls].\n",
8425 vendorId, productId, product.raw()));
8426
8427 /* Detach the device from VM. */
8428 if (pUSBDevice->captured())
8429 {
8430 Bstr uuid;
8431 pUSBDevice->COMGETTER(Id)(uuid.asOutParam());
8432 onUSBDeviceDetach(uuid.raw(), NULL);
8433 }
8434
8435 /* And remove it from the list. */
8436 mRemoteUSBDevices.erase(it);
8437 }
8438
8439 LogFlowThisFuncLeave();
8440}
8441
8442/**
8443 * Progress cancelation callback for fault tolerance VM poweron
8444 */
8445static void faultToleranceProgressCancelCallback(void *pvUser)
8446{
8447 PVM pVM = (PVM)pvUser;
8448
8449 if (pVM)
8450 FTMR3CancelStandby(pVM);
8451}
8452
8453/**
8454 * Thread function which starts the VM (also from saved state) and
8455 * track progress.
8456 *
8457 * @param Thread The thread id.
8458 * @param pvUser Pointer to a VMPowerUpTask structure.
8459 * @return VINF_SUCCESS (ignored).
8460 *
8461 * @note Locks the Console object for writing.
8462 */
8463/*static*/
8464DECLCALLBACK(int) Console::powerUpThread(RTTHREAD Thread, void *pvUser)
8465{
8466 LogFlowFuncEnter();
8467
8468 std::auto_ptr<VMPowerUpTask> task(static_cast<VMPowerUpTask *>(pvUser));
8469 AssertReturn(task.get(), VERR_INVALID_PARAMETER);
8470
8471 AssertReturn(!task->mConsole.isNull(), VERR_INVALID_PARAMETER);
8472 AssertReturn(!task->mProgress.isNull(), VERR_INVALID_PARAMETER);
8473
8474 VirtualBoxBase::initializeComForThread();
8475
8476 HRESULT rc = S_OK;
8477 int vrc = VINF_SUCCESS;
8478
8479 /* Set up a build identifier so that it can be seen from core dumps what
8480 * exact build was used to produce the core. */
8481 static char saBuildID[40];
8482 RTStrPrintf(saBuildID, sizeof(saBuildID), "%s%s%s%s VirtualBox %s r%u %s%s%s%s",
8483 "BU", "IL", "DI", "D", VBOX_VERSION_STRING, RTBldCfgRevision(), "BU", "IL", "DI", "D");
8484
8485 ComObjPtr<Console> pConsole = task->mConsole;
8486
8487 /* Note: no need to use addCaller() because VMPowerUpTask does that */
8488
8489 /* The lock is also used as a signal from the task initiator (which
8490 * releases it only after RTThreadCreate()) that we can start the job */
8491 AutoWriteLock alock(pConsole COMMA_LOCKVAL_SRC_POS);
8492
8493 /* sanity */
8494 Assert(pConsole->mpUVM == NULL);
8495
8496 try
8497 {
8498 // Create the VMM device object, which starts the HGCM thread; do this only
8499 // once for the console, for the pathological case that the same console
8500 // object is used to power up a VM twice. VirtualBox 4.0: we now do that
8501 // here instead of the Console constructor (see Console::init())
8502 if (!pConsole->m_pVMMDev)
8503 {
8504 pConsole->m_pVMMDev = new VMMDev(pConsole);
8505 AssertReturn(pConsole->m_pVMMDev, E_FAIL);
8506 }
8507
8508 /* wait for auto reset ops to complete so that we can successfully lock
8509 * the attached hard disks by calling LockMedia() below */
8510 for (VMPowerUpTask::ProgressList::const_iterator
8511 it = task->hardDiskProgresses.begin();
8512 it != task->hardDiskProgresses.end(); ++ it)
8513 {
8514 HRESULT rc2 = (*it)->WaitForCompletion(-1);
8515 AssertComRC(rc2);
8516 }
8517
8518 /*
8519 * Lock attached media. This method will also check their accessibility.
8520 * If we're a teleporter, we'll have to postpone this action so we can
8521 * migrate between local processes.
8522 *
8523 * Note! The media will be unlocked automatically by
8524 * SessionMachine::setMachineState() when the VM is powered down.
8525 */
8526 if ( !task->mTeleporterEnabled
8527 && task->mEnmFaultToleranceState != FaultToleranceState_Standby)
8528 {
8529 rc = pConsole->mControl->LockMedia();
8530 if (FAILED(rc)) throw rc;
8531 }
8532
8533 /* Create the VRDP server. In case of headless operation, this will
8534 * also create the framebuffer, required at VM creation.
8535 */
8536 ConsoleVRDPServer *server = pConsole->consoleVRDPServer();
8537 Assert(server);
8538
8539 /* Does VRDP server call Console from the other thread?
8540 * Not sure (and can change), so leave the lock just in case.
8541 */
8542 alock.leave();
8543 vrc = server->Launch();
8544 alock.enter();
8545
8546 if (vrc == VERR_NET_ADDRESS_IN_USE)
8547 {
8548 Utf8Str errMsg;
8549 Bstr bstr;
8550 pConsole->mVRDEServer->GetVRDEProperty(Bstr("TCP/Ports").raw(), bstr.asOutParam());
8551 Utf8Str ports = bstr;
8552 errMsg = Utf8StrFmt(tr("VirtualBox Remote Desktop Extension server can't bind to the port: %s"),
8553 ports.c_str());
8554 LogRel(("VRDE: Warning: failed to launch VRDE server (%Rrc): '%s'\n",
8555 vrc, errMsg.c_str()));
8556 }
8557 else if (vrc == VINF_NOT_SUPPORTED)
8558 {
8559 /* This means that the VRDE is not installed. */
8560 LogRel(("VRDE: VirtualBox Remote Desktop Extension is not available.\n"));
8561 }
8562 else if (RT_FAILURE(vrc))
8563 {
8564 /* Fail, if the server is installed but can't start. */
8565 Utf8Str errMsg;
8566 switch (vrc)
8567 {
8568 case VERR_FILE_NOT_FOUND:
8569 {
8570 /* VRDE library file is missing. */
8571 errMsg = Utf8StrFmt(tr("Could not find the VirtualBox Remote Desktop Extension library."));
8572 break;
8573 }
8574 default:
8575 errMsg = Utf8StrFmt(tr("Failed to launch Remote Desktop Extension server (%Rrc)"),
8576 vrc);
8577 }
8578 LogRel(("VRDE: Failed: (%Rrc), error message: '%s'\n",
8579 vrc, errMsg.c_str()));
8580 throw setErrorStatic(E_FAIL, errMsg.c_str());
8581 }
8582
8583 ComPtr<IMachine> pMachine = pConsole->machine();
8584 ULONG cCpus = 1;
8585 pMachine->COMGETTER(CPUCount)(&cCpus);
8586
8587 /*
8588 * Create the VM
8589 */
8590 PVM pVM;
8591 /*
8592 * leave the lock since EMT will call Console. It's safe because
8593 * mMachineState is either Starting or Restoring state here.
8594 */
8595 alock.leave();
8596
8597 vrc = VMR3Create(cCpus,
8598 pConsole->mpVmm2UserMethods,
8599 Console::genericVMSetErrorCallback,
8600 &task->mErrorMsg,
8601 task->mConfigConstructor,
8602 static_cast<Console *>(pConsole),
8603 &pVM);
8604
8605 alock.enter();
8606
8607 /* Enable client connections to the server. */
8608 pConsole->consoleVRDPServer()->EnableConnections();
8609
8610 if (RT_SUCCESS(vrc))
8611 {
8612 do
8613 {
8614 /*
8615 * Register our load/save state file handlers
8616 */
8617 vrc = SSMR3RegisterExternal(pVM, sSSMConsoleUnit, 0 /*iInstance*/, sSSMConsoleVer, 0 /* cbGuess */,
8618 NULL, NULL, NULL,
8619 NULL, saveStateFileExec, NULL,
8620 NULL, loadStateFileExec, NULL,
8621 static_cast<Console *>(pConsole));
8622 AssertRCBreak(vrc);
8623
8624 vrc = static_cast<Console *>(pConsole)->getDisplay()->registerSSM(pVM);
8625 AssertRC(vrc);
8626 if (RT_FAILURE(vrc))
8627 break;
8628
8629 /*
8630 * Synchronize debugger settings
8631 */
8632 MachineDebugger *machineDebugger = pConsole->getMachineDebugger();
8633 if (machineDebugger)
8634 machineDebugger->flushQueuedSettings();
8635
8636 /*
8637 * Shared Folders
8638 */
8639 if (pConsole->m_pVMMDev->isShFlActive())
8640 {
8641 /* Does the code below call Console from the other thread?
8642 * Not sure, so leave the lock just in case. */
8643 alock.leave();
8644
8645 for (SharedFolderDataMap::const_iterator it = task->mSharedFolders.begin();
8646 it != task->mSharedFolders.end();
8647 ++it)
8648 {
8649 const SharedFolderData &d = it->second;
8650 rc = pConsole->createSharedFolder(it->first, d);
8651 if (FAILED(rc))
8652 {
8653 ErrorInfoKeeper eik;
8654 setVMRuntimeErrorCallbackF(pVM, pConsole, 0, "BrokenSharedFolder",
8655 N_("The shared folder '%s' could not be set up: %ls.\n"
8656 "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"),
8657 it->first.c_str(), eik.getText().raw());
8658 }
8659 }
8660 if (FAILED(rc))
8661 rc = S_OK; // do not fail with broken shared folders
8662
8663 /* enter the lock again */
8664 alock.enter();
8665 }
8666
8667 /*
8668 * Capture USB devices.
8669 */
8670 rc = pConsole->captureUSBDevices(pVM);
8671 if (FAILED(rc)) break;
8672
8673 /* leave the lock before a lengthy operation */
8674 alock.leave();
8675
8676 /* Load saved state? */
8677 if (task->mSavedStateFile.length())
8678 {
8679 LogFlowFunc(("Restoring saved state from '%s'...\n",
8680 task->mSavedStateFile.c_str()));
8681
8682 vrc = VMR3LoadFromFile(pVM,
8683 task->mSavedStateFile.c_str(),
8684 Console::stateProgressCallback,
8685 static_cast<IProgress *>(task->mProgress));
8686
8687 if (RT_SUCCESS(vrc))
8688 {
8689 if (task->mStartPaused)
8690 /* done */
8691 pConsole->setMachineState(MachineState_Paused);
8692 else
8693 {
8694 /* Start/Resume the VM execution */
8695#ifdef VBOX_WITH_EXTPACK
8696 vrc = pConsole->mptrExtPackManager->callAllVmPowerOnHooks(pConsole, pVM);
8697#endif
8698 if (RT_SUCCESS(vrc))
8699 vrc = VMR3Resume(pVM);
8700 AssertLogRelRC(vrc);
8701 }
8702 }
8703
8704 /* Power off in case we failed loading or resuming the VM */
8705 if (RT_FAILURE(vrc))
8706 {
8707 int vrc2 = VMR3PowerOff(pVM); AssertLogRelRC(vrc2);
8708#ifdef VBOX_WITH_EXTPACK
8709 pConsole->mptrExtPackManager->callAllVmPowerOffHooks(pConsole, pVM);
8710#endif
8711 }
8712 }
8713 else if (task->mTeleporterEnabled)
8714 {
8715 /* -> ConsoleImplTeleporter.cpp */
8716 bool fPowerOffOnFailure;
8717 rc = pConsole->teleporterTrg(VMR3GetUVM(pVM), pMachine, &task->mErrorMsg, task->mStartPaused,
8718 task->mProgress, &fPowerOffOnFailure);
8719 if (FAILED(rc) && fPowerOffOnFailure)
8720 {
8721 ErrorInfoKeeper eik;
8722 int vrc2 = VMR3PowerOff(pVM); AssertLogRelRC(vrc2);
8723#ifdef VBOX_WITH_EXTPACK
8724 pConsole->mptrExtPackManager->callAllVmPowerOffHooks(pConsole, pVM);
8725#endif
8726 }
8727 }
8728 else if (task->mEnmFaultToleranceState != FaultToleranceState_Inactive)
8729 {
8730 /*
8731 * Get the config.
8732 */
8733 ULONG uPort;
8734 ULONG uInterval;
8735 Bstr bstrAddress, bstrPassword;
8736
8737 rc = pMachine->COMGETTER(FaultTolerancePort)(&uPort);
8738 if (SUCCEEDED(rc))
8739 {
8740 rc = pMachine->COMGETTER(FaultToleranceSyncInterval)(&uInterval);
8741 if (SUCCEEDED(rc))
8742 rc = pMachine->COMGETTER(FaultToleranceAddress)(bstrAddress.asOutParam());
8743 if (SUCCEEDED(rc))
8744 rc = pMachine->COMGETTER(FaultTolerancePassword)(bstrPassword.asOutParam());
8745 }
8746 if (task->mProgress->setCancelCallback(faultToleranceProgressCancelCallback, pVM))
8747 {
8748 if (SUCCEEDED(rc))
8749 {
8750 Utf8Str strAddress(bstrAddress);
8751 const char *pszAddress = strAddress.isEmpty() ? NULL : strAddress.c_str();
8752 Utf8Str strPassword(bstrPassword);
8753 const char *pszPassword = strPassword.isEmpty() ? NULL : strPassword.c_str();
8754
8755 /* Power on the FT enabled VM. */
8756#ifdef VBOX_WITH_EXTPACK
8757 vrc = pConsole->mptrExtPackManager->callAllVmPowerOnHooks(pConsole, pVM);
8758#endif
8759 if (RT_SUCCESS(vrc))
8760 vrc = FTMR3PowerOn(pVM,
8761 task->mEnmFaultToleranceState == FaultToleranceState_Master /* fMaster */,
8762 uInterval,
8763 pszAddress,
8764 uPort,
8765 pszPassword);
8766 AssertLogRelRC(vrc);
8767 }
8768 task->mProgress->setCancelCallback(NULL, NULL);
8769 }
8770 else
8771 rc = E_FAIL;
8772 }
8773 else if (task->mStartPaused)
8774 /* done */
8775 pConsole->setMachineState(MachineState_Paused);
8776 else
8777 {
8778 /* Power on the VM (i.e. start executing) */
8779#ifdef VBOX_WITH_EXTPACK
8780 vrc = pConsole->mptrExtPackManager->callAllVmPowerOnHooks(pConsole, pVM);
8781#endif
8782 if (RT_SUCCESS(vrc))
8783 vrc = VMR3PowerOn(pVM);
8784 AssertLogRelRC(vrc);
8785 }
8786
8787 /* enter the lock again */
8788 alock.enter();
8789 }
8790 while (0);
8791
8792 /* On failure, destroy the VM */
8793 if (FAILED(rc) || RT_FAILURE(vrc))
8794 {
8795 /* preserve existing error info */
8796 ErrorInfoKeeper eik;
8797
8798 /* powerDown() will call VMR3Destroy() and do all necessary
8799 * cleanup (VRDP, USB devices) */
8800 HRESULT rc2 = pConsole->powerDown();
8801 AssertComRC(rc2);
8802 }
8803 else
8804 {
8805 /*
8806 * Deregister the VMSetError callback. This is necessary as the
8807 * pfnVMAtError() function passed to VMR3Create() is supposed to
8808 * be sticky but our error callback isn't.
8809 */
8810 alock.leave();
8811 VMR3AtErrorDeregister(pVM, Console::genericVMSetErrorCallback, &task->mErrorMsg);
8812 /** @todo register another VMSetError callback? */
8813 alock.enter();
8814 }
8815 }
8816 else
8817 {
8818 /*
8819 * If VMR3Create() failed it has released the VM memory.
8820 */
8821 VMR3ReleaseUVM(pConsole->mpUVM);
8822 pConsole->mpUVM = NULL;
8823 }
8824
8825 if (SUCCEEDED(rc) && RT_FAILURE(vrc))
8826 {
8827 /* If VMR3Create() or one of the other calls in this function fail,
8828 * an appropriate error message has been set in task->mErrorMsg.
8829 * However since that happens via a callback, the rc status code in
8830 * this function is not updated.
8831 */
8832 if (!task->mErrorMsg.length())
8833 {
8834 /* If the error message is not set but we've got a failure,
8835 * convert the VBox status code into a meaningful error message.
8836 * This becomes unused once all the sources of errors set the
8837 * appropriate error message themselves.
8838 */
8839 AssertMsgFailed(("Missing error message during powerup for status code %Rrc\n", vrc));
8840 task->mErrorMsg = Utf8StrFmt(tr("Failed to start VM execution (%Rrc)"),
8841 vrc);
8842 }
8843
8844 /* Set the error message as the COM error.
8845 * Progress::notifyComplete() will pick it up later. */
8846 throw setErrorStatic(E_FAIL, task->mErrorMsg.c_str());
8847 }
8848 }
8849 catch (HRESULT aRC) { rc = aRC; }
8850
8851 if ( pConsole->mMachineState == MachineState_Starting
8852 || pConsole->mMachineState == MachineState_Restoring
8853 || pConsole->mMachineState == MachineState_TeleportingIn
8854 )
8855 {
8856 /* We are still in the Starting/Restoring state. This means one of:
8857 *
8858 * 1) we failed before VMR3Create() was called;
8859 * 2) VMR3Create() failed.
8860 *
8861 * In both cases, there is no need to call powerDown(), but we still
8862 * need to go back to the PoweredOff/Saved state. Reuse
8863 * vmstateChangeCallback() for that purpose.
8864 */
8865
8866 /* preserve existing error info */
8867 ErrorInfoKeeper eik;
8868
8869 Assert(pConsole->mpUVM == NULL);
8870 vmstateChangeCallback(NULL, VMSTATE_TERMINATED, VMSTATE_CREATING,
8871 pConsole);
8872 }
8873
8874 /*
8875 * Evaluate the final result. Note that the appropriate mMachineState value
8876 * is already set by vmstateChangeCallback() in all cases.
8877 */
8878
8879 /* leave the lock, don't need it any more */
8880 alock.leave();
8881
8882 if (SUCCEEDED(rc))
8883 {
8884 /* Notify the progress object of the success */
8885 task->mProgress->notifyComplete(S_OK);
8886 }
8887 else
8888 {
8889 /* The progress object will fetch the current error info */
8890 task->mProgress->notifyComplete(rc);
8891 LogRel(("Power up failed (vrc=%Rrc, rc=%Rhrc (%#08X))\n", vrc, rc, rc));
8892 }
8893
8894 /* Notify VBoxSVC and any waiting openRemoteSession progress object. */
8895 pConsole->mControl->EndPowerUp(rc);
8896
8897#if defined(RT_OS_WINDOWS)
8898 /* uninitialize COM */
8899 CoUninitialize();
8900#endif
8901
8902 LogFlowFuncLeave();
8903
8904 return VINF_SUCCESS;
8905}
8906
8907
8908/**
8909 * Reconfigures a medium attachment (part of taking or deleting an online snapshot).
8910 *
8911 * @param pConsole Reference to the console object.
8912 * @param pVM The VM handle.
8913 * @param lInstance The instance of the controller.
8914 * @param pcszDevice The name of the controller type.
8915 * @param enmBus The storage bus type of the controller.
8916 * @param fSetupMerge Whether to set up a medium merge
8917 * @param uMergeSource Merge source image index
8918 * @param uMergeTarget Merge target image index
8919 * @param aMediumAtt The medium attachment.
8920 * @param aMachineState The current machine state.
8921 * @param phrc Where to store com error - only valid if we return VERR_GENERAL_FAILURE.
8922 * @return VBox status code.
8923 */
8924/* static */
8925DECLCALLBACK(int) Console::reconfigureMediumAttachment(Console *pConsole,
8926 PVM pVM,
8927 const char *pcszDevice,
8928 unsigned uInstance,
8929 StorageBus_T enmBus,
8930 bool fUseHostIOCache,
8931 bool fBuiltinIoCache,
8932 bool fSetupMerge,
8933 unsigned uMergeSource,
8934 unsigned uMergeTarget,
8935 IMediumAttachment *aMediumAtt,
8936 MachineState_T aMachineState,
8937 HRESULT *phrc)
8938{
8939 LogFlowFunc(("pVM=%p aMediumAtt=%p phrc=%p\n", pVM, aMediumAtt, phrc));
8940
8941 int rc;
8942 HRESULT hrc;
8943 Bstr bstr;
8944 *phrc = S_OK;
8945#define RC_CHECK() do { if (RT_FAILURE(rc)) { AssertMsgFailed(("rc=%Rrc\n", rc)); return rc; } } while (0)
8946#define H() do { if (FAILED(hrc)) { AssertMsgFailed(("hrc=%Rhrc (%#x)\n", hrc, hrc)); *phrc = hrc; return VERR_GENERAL_FAILURE; } } while (0)
8947
8948 /* Ignore attachments other than hard disks, since at the moment they are
8949 * not subject to snapshotting in general. */
8950 DeviceType_T lType;
8951 hrc = aMediumAtt->COMGETTER(Type)(&lType); H();
8952 if (lType != DeviceType_HardDisk)
8953 return VINF_SUCCESS;
8954
8955 /* Determine the base path for the device instance. */
8956 PCFGMNODE pCtlInst;
8957 pCtlInst = CFGMR3GetChildF(CFGMR3GetRoot(pVM), "Devices/%s/%u/", pcszDevice, uInstance);
8958 AssertReturn(pCtlInst, VERR_INTERNAL_ERROR);
8959
8960 /* Update the device instance configuration. */
8961 rc = pConsole->configMediumAttachment(pCtlInst,
8962 pcszDevice,
8963 uInstance,
8964 enmBus,
8965 fUseHostIOCache,
8966 fBuiltinIoCache,
8967 fSetupMerge,
8968 uMergeSource,
8969 uMergeTarget,
8970 aMediumAtt,
8971 aMachineState,
8972 phrc,
8973 true /* fAttachDetach */,
8974 false /* fForceUnmount */,
8975 false /* fHotplug */,
8976 pVM,
8977 NULL /* paLedDevType */);
8978 /** @todo this dumps everything attached to this device instance, which
8979 * is more than necessary. Dumping the changed LUN would be enough. */
8980 CFGMR3Dump(pCtlInst);
8981 RC_CHECK();
8982
8983#undef RC_CHECK
8984#undef H
8985
8986 LogFlowFunc(("Returns success\n"));
8987 return VINF_SUCCESS;
8988}
8989
8990/**
8991 * Progress cancelation callback employed by Console::fntTakeSnapshotWorker.
8992 */
8993static void takesnapshotProgressCancelCallback(void *pvUser)
8994{
8995 PUVM pUVM = (PUVM)pvUser;
8996 SSMR3Cancel(VMR3GetVM(pUVM));
8997}
8998
8999/**
9000 * Worker thread created by Console::TakeSnapshot.
9001 * @param Thread The current thread (ignored).
9002 * @param pvUser The task.
9003 * @return VINF_SUCCESS (ignored).
9004 */
9005/*static*/
9006DECLCALLBACK(int) Console::fntTakeSnapshotWorker(RTTHREAD Thread, void *pvUser)
9007{
9008 VMTakeSnapshotTask *pTask = (VMTakeSnapshotTask*)pvUser;
9009
9010 // taking a snapshot consists of the following:
9011
9012 // 1) creating a diff image for each virtual hard disk, into which write operations go after
9013 // the snapshot has been created (done in VBoxSVC, in SessionMachine::BeginTakingSnapshot)
9014 // 2) creating a Snapshot object with the state of the machine (hardware + storage,
9015 // done in VBoxSVC, also in SessionMachine::BeginTakingSnapshot)
9016 // 3) saving the state of the virtual machine (here, in the VM process, if the machine is online)
9017
9018 Console *that = pTask->mConsole;
9019 bool fBeganTakingSnapshot = false;
9020 bool fSuspenededBySave = false;
9021
9022 AutoCaller autoCaller(that);
9023 if (FAILED(autoCaller.rc()))
9024 {
9025 that->mptrCancelableProgress.setNull();
9026 return autoCaller.rc();
9027 }
9028
9029 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
9030
9031 HRESULT rc = S_OK;
9032
9033 try
9034 {
9035 /* STEP 1 + 2:
9036 * request creating the diff images on the server and create the snapshot object
9037 * (this will set the machine state to Saving on the server to block
9038 * others from accessing this machine)
9039 */
9040 rc = that->mControl->BeginTakingSnapshot(that,
9041 pTask->bstrName.raw(),
9042 pTask->bstrDescription.raw(),
9043 pTask->mProgress,
9044 pTask->fTakingSnapshotOnline,
9045 pTask->bstrSavedStateFile.asOutParam());
9046 if (FAILED(rc))
9047 throw rc;
9048
9049 fBeganTakingSnapshot = true;
9050
9051 /*
9052 * state file is non-null only when the VM is paused
9053 * (i.e. creating a snapshot online)
9054 */
9055 bool f = (!pTask->bstrSavedStateFile.isEmpty() && pTask->fTakingSnapshotOnline)
9056 || ( pTask->bstrSavedStateFile.isEmpty() && !pTask->fTakingSnapshotOnline);
9057 if (!f)
9058 throw setErrorStatic(E_FAIL, "Invalid state of saved state file");
9059
9060 /* sync the state with the server */
9061 if (pTask->lastMachineState == MachineState_Running)
9062 that->setMachineStateLocally(MachineState_LiveSnapshotting);
9063 else
9064 that->setMachineStateLocally(MachineState_Saving);
9065
9066 // STEP 3: save the VM state (if online)
9067 if (pTask->fTakingSnapshotOnline)
9068 {
9069 Utf8Str strSavedStateFile(pTask->bstrSavedStateFile);
9070
9071 SafeVMPtr ptrVM(that);
9072 if (!ptrVM.isOk())
9073 throw ptrVM.rc();
9074
9075 pTask->mProgress->SetNextOperation(Bstr(tr("Saving the machine state")).raw(),
9076 pTask->ulMemSize); // operation weight, same as computed when setting up progress object
9077 pTask->mProgress->setCancelCallback(takesnapshotProgressCancelCallback, ptrVM.rawUVM());
9078
9079 alock.leave();
9080 LogFlowFunc(("VMR3Save...\n"));
9081 int vrc = VMR3Save(ptrVM,
9082 strSavedStateFile.c_str(),
9083 true /*fContinueAfterwards*/,
9084 Console::stateProgressCallback,
9085 static_cast<IProgress *>(pTask->mProgress),
9086 &fSuspenededBySave);
9087 alock.enter();
9088 if (RT_FAILURE(vrc))
9089 throw setErrorStatic(E_FAIL,
9090 tr("Failed to save the machine state to '%s' (%Rrc)"),
9091 strSavedStateFile.c_str(), vrc);
9092
9093 pTask->mProgress->setCancelCallback(NULL, NULL);
9094 if (!pTask->mProgress->notifyPointOfNoReturn())
9095 throw setErrorStatic(E_FAIL, tr("Canceled"));
9096 that->mptrCancelableProgress.setNull();
9097
9098 // STEP 4: reattach hard disks
9099 LogFlowFunc(("Reattaching new differencing hard disks...\n"));
9100
9101 pTask->mProgress->SetNextOperation(Bstr(tr("Reconfiguring medium attachments")).raw(),
9102 1); // operation weight, same as computed when setting up progress object
9103
9104 com::SafeIfaceArray<IMediumAttachment> atts;
9105 rc = that->mMachine->COMGETTER(MediumAttachments)(ComSafeArrayAsOutParam(atts));
9106 if (FAILED(rc))
9107 throw rc;
9108
9109 for (size_t i = 0;
9110 i < atts.size();
9111 ++i)
9112 {
9113 ComPtr<IStorageController> pStorageController;
9114 Bstr controllerName;
9115 ULONG lInstance;
9116 StorageControllerType_T enmController;
9117 StorageBus_T enmBus;
9118 BOOL fUseHostIOCache;
9119
9120 /*
9121 * We can't pass a storage controller object directly
9122 * (g++ complains about not being able to pass non POD types through '...')
9123 * so we have to query needed values here and pass them.
9124 */
9125 rc = atts[i]->COMGETTER(Controller)(controllerName.asOutParam());
9126 if (FAILED(rc))
9127 throw rc;
9128
9129 rc = that->mMachine->GetStorageControllerByName(controllerName.raw(),
9130 pStorageController.asOutParam());
9131 if (FAILED(rc))
9132 throw rc;
9133
9134 rc = pStorageController->COMGETTER(ControllerType)(&enmController);
9135 if (FAILED(rc))
9136 throw rc;
9137 rc = pStorageController->COMGETTER(Instance)(&lInstance);
9138 if (FAILED(rc))
9139 throw rc;
9140 rc = pStorageController->COMGETTER(Bus)(&enmBus);
9141 if (FAILED(rc))
9142 throw rc;
9143 rc = pStorageController->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
9144 if (FAILED(rc))
9145 throw rc;
9146
9147 const char *pcszDevice = Console::convertControllerTypeToDev(enmController);
9148
9149 BOOL fBuiltinIoCache;
9150 rc = that->mMachine->COMGETTER(IoCacheEnabled)(&fBuiltinIoCache);
9151 if (FAILED(rc))
9152 throw rc;
9153
9154 /*
9155 * don't leave the lock since reconfigureMediumAttachment
9156 * isn't going to need the Console lock.
9157 */
9158 vrc = VMR3ReqCallWait(ptrVM,
9159 VMCPUID_ANY,
9160 (PFNRT)reconfigureMediumAttachment,
9161 13,
9162 that,
9163 ptrVM.raw(),
9164 pcszDevice,
9165 lInstance,
9166 enmBus,
9167 fUseHostIOCache,
9168 fBuiltinIoCache,
9169 false /* fSetupMerge */,
9170 0 /* uMergeSource */,
9171 0 /* uMergeTarget */,
9172 atts[i],
9173 that->mMachineState,
9174 &rc);
9175 if (RT_FAILURE(vrc))
9176 throw setErrorStatic(E_FAIL, Console::tr("%Rrc"), vrc);
9177 if (FAILED(rc))
9178 throw rc;
9179 }
9180 }
9181
9182 /*
9183 * finalize the requested snapshot object.
9184 * This will reset the machine state to the state it had right
9185 * before calling mControl->BeginTakingSnapshot().
9186 */
9187 rc = that->mControl->EndTakingSnapshot(TRUE /*aSuccess*/);
9188 // do not throw rc here because we can't call EndTakingSnapshot() twice
9189 LogFlowFunc(("EndTakingSnapshot -> %Rhrc [mMachineState=%s]\n", rc, Global::stringifyMachineState(that->mMachineState)));
9190 }
9191 catch (HRESULT rcThrown)
9192 {
9193 /* preserve existing error info */
9194 ErrorInfoKeeper eik;
9195
9196 if (fBeganTakingSnapshot)
9197 that->mControl->EndTakingSnapshot(FALSE /*aSuccess*/);
9198
9199 rc = rcThrown;
9200 LogFunc(("Caught %Rhrc [mMachineState=%s]\n", rc, Global::stringifyMachineState(that->mMachineState)));
9201 }
9202 Assert(alock.isWriteLockOnCurrentThread());
9203
9204 if (FAILED(rc)) /* Must come before calling setMachineState. */
9205 pTask->mProgress->notifyComplete(rc);
9206
9207 /*
9208 * Fix up the machine state.
9209 *
9210 * For live snapshots we do all the work, for the two other variations we
9211 * just update the local copy.
9212 */
9213 MachineState_T enmMachineState;
9214 that->mMachine->COMGETTER(State)(&enmMachineState);
9215 if ( that->mMachineState == MachineState_LiveSnapshotting
9216 || that->mMachineState == MachineState_Saving)
9217 {
9218
9219 if (!pTask->fTakingSnapshotOnline)
9220 that->setMachineStateLocally(pTask->lastMachineState);
9221 else if (SUCCEEDED(rc))
9222 {
9223 Assert( pTask->lastMachineState == MachineState_Running
9224 || pTask->lastMachineState == MachineState_Paused);
9225 Assert(that->mMachineState == MachineState_Saving);
9226 if (pTask->lastMachineState == MachineState_Running)
9227 {
9228 LogFlowFunc(("VMR3Resume...\n"));
9229 SafeVMPtr ptrVM(that);
9230 alock.leave();
9231 int vrc = VMR3Resume(ptrVM);
9232 alock.enter();
9233 if (RT_FAILURE(vrc))
9234 {
9235 rc = setErrorStatic(VBOX_E_VM_ERROR, tr("Could not resume the machine execution (%Rrc)"), vrc);
9236 pTask->mProgress->notifyComplete(rc);
9237 if (that->mMachineState == MachineState_Saving)
9238 that->setMachineStateLocally(MachineState_Paused);
9239 }
9240 }
9241 else
9242 that->setMachineStateLocally(MachineState_Paused);
9243 }
9244 else
9245 {
9246 /** @todo this could probably be made more generic and reused elsewhere. */
9247 /* paranoid cleanup on for a failed online snapshot. */
9248 VMSTATE enmVMState = VMR3GetStateU(that->mpUVM);
9249 switch (enmVMState)
9250 {
9251 case VMSTATE_RUNNING:
9252 case VMSTATE_RUNNING_LS:
9253 case VMSTATE_DEBUGGING:
9254 case VMSTATE_DEBUGGING_LS:
9255 case VMSTATE_POWERING_OFF:
9256 case VMSTATE_POWERING_OFF_LS:
9257 case VMSTATE_RESETTING:
9258 case VMSTATE_RESETTING_LS:
9259 Assert(!fSuspenededBySave);
9260 that->setMachineState(MachineState_Running);
9261 break;
9262
9263 case VMSTATE_GURU_MEDITATION:
9264 case VMSTATE_GURU_MEDITATION_LS:
9265 that->setMachineState(MachineState_Stuck);
9266 break;
9267
9268 case VMSTATE_FATAL_ERROR:
9269 case VMSTATE_FATAL_ERROR_LS:
9270 if (pTask->lastMachineState == MachineState_Paused)
9271 that->setMachineStateLocally(pTask->lastMachineState);
9272 else
9273 that->setMachineState(MachineState_Paused);
9274 break;
9275
9276 default:
9277 AssertMsgFailed(("%s\n", VMR3GetStateName(enmVMState)));
9278 case VMSTATE_SUSPENDED:
9279 case VMSTATE_SUSPENDED_LS:
9280 case VMSTATE_SUSPENDING:
9281 case VMSTATE_SUSPENDING_LS:
9282 case VMSTATE_SUSPENDING_EXT_LS:
9283 if (fSuspenededBySave)
9284 {
9285 Assert(pTask->lastMachineState == MachineState_Running);
9286 LogFlowFunc(("VMR3Resume (on failure)...\n"));
9287 SafeVMPtr ptrVM(that);
9288 alock.leave();
9289 int vrc = VMR3Resume(ptrVM); AssertLogRelRC(vrc);
9290 alock.enter();
9291 if (RT_FAILURE(vrc))
9292 that->setMachineState(MachineState_Paused);
9293 }
9294 else if (pTask->lastMachineState == MachineState_Paused)
9295 that->setMachineStateLocally(pTask->lastMachineState);
9296 else
9297 that->setMachineState(MachineState_Paused);
9298 break;
9299 }
9300
9301 }
9302 }
9303 /*else: somebody else has change the state... Leave it. */
9304
9305 /* check the remote state to see that we got it right. */
9306 that->mMachine->COMGETTER(State)(&enmMachineState);
9307 AssertLogRelMsg(that->mMachineState == enmMachineState,
9308 ("mMachineState=%s enmMachineState=%s\n", Global::stringifyMachineState(that->mMachineState),
9309 Global::stringifyMachineState(enmMachineState) ));
9310
9311
9312 if (SUCCEEDED(rc)) /* The failure cases are handled above. */
9313 pTask->mProgress->notifyComplete(rc);
9314
9315 delete pTask;
9316
9317 LogFlowFuncLeave();
9318 return VINF_SUCCESS;
9319}
9320
9321/**
9322 * Thread for executing the saved state operation.
9323 *
9324 * @param Thread The thread handle.
9325 * @param pvUser Pointer to a VMSaveTask structure.
9326 * @return VINF_SUCCESS (ignored).
9327 *
9328 * @note Locks the Console object for writing.
9329 */
9330/*static*/
9331DECLCALLBACK(int) Console::saveStateThread(RTTHREAD Thread, void *pvUser)
9332{
9333 LogFlowFuncEnter();
9334
9335 std::auto_ptr<VMSaveTask> task(static_cast<VMSaveTask*>(pvUser));
9336 AssertReturn(task.get(), VERR_INVALID_PARAMETER);
9337
9338 Assert(task->mSavedStateFile.length());
9339 Assert(task->mProgress.isNull());
9340 Assert(!task->mServerProgress.isNull());
9341
9342 const ComObjPtr<Console> &that = task->mConsole;
9343 Utf8Str errMsg;
9344 HRESULT rc = S_OK;
9345
9346 LogFlowFunc(("Saving the state to '%s'...\n", task->mSavedStateFile.c_str()));
9347
9348 bool fSuspenededBySave;
9349 int vrc = VMR3Save(task->mpVM,
9350 task->mSavedStateFile.c_str(),
9351 false, /*fContinueAfterwards*/
9352 Console::stateProgressCallback,
9353 static_cast<IProgress *>(task->mServerProgress),
9354 &fSuspenededBySave);
9355 if (RT_FAILURE(vrc))
9356 {
9357 errMsg = Utf8StrFmt(Console::tr("Failed to save the machine state to '%s' (%Rrc)"),
9358 task->mSavedStateFile.c_str(), vrc);
9359 rc = E_FAIL;
9360 }
9361 Assert(!fSuspenededBySave);
9362
9363 /* lock the console once we're going to access it */
9364 AutoWriteLock thatLock(that COMMA_LOCKVAL_SRC_POS);
9365
9366 /* synchronize the state with the server */
9367 if (SUCCEEDED(rc))
9368 {
9369 /*
9370 * The machine has been successfully saved, so power it down
9371 * (vmstateChangeCallback() will set state to Saved on success).
9372 * Note: we release the task's VM caller, otherwise it will
9373 * deadlock.
9374 */
9375 task->releaseVMCaller();
9376 rc = that->powerDown();
9377 }
9378
9379 /*
9380 * Finalize the requested save state procedure. In case of failure it will
9381 * reset the machine state to the state it had right before calling
9382 * mControl->BeginSavingState(). This must be the last thing because it
9383 * will set the progress to completed, and that means that the frontend
9384 * can immediately uninit the associated console object.
9385 */
9386 that->mControl->EndSavingState(rc, Bstr(errMsg).raw());
9387
9388 LogFlowFuncLeave();
9389 return VINF_SUCCESS;
9390}
9391
9392/**
9393 * Thread for powering down the Console.
9394 *
9395 * @param Thread The thread handle.
9396 * @param pvUser Pointer to the VMTask structure.
9397 * @return VINF_SUCCESS (ignored).
9398 *
9399 * @note Locks the Console object for writing.
9400 */
9401/*static*/
9402DECLCALLBACK(int) Console::powerDownThread(RTTHREAD Thread, void *pvUser)
9403{
9404 LogFlowFuncEnter();
9405
9406 std::auto_ptr<VMPowerDownTask> task(static_cast<VMPowerDownTask *>(pvUser));
9407 AssertReturn(task.get(), VERR_INVALID_PARAMETER);
9408
9409 AssertReturn(task->isOk(), VERR_GENERAL_FAILURE);
9410
9411 Assert(task->mProgress.isNull());
9412
9413 const ComObjPtr<Console> &that = task->mConsole;
9414
9415 /* Note: no need to use addCaller() to protect Console because VMTask does
9416 * that */
9417
9418 /* wait until the method tat started us returns */
9419 AutoWriteLock thatLock(that COMMA_LOCKVAL_SRC_POS);
9420
9421 /* release VM caller to avoid the powerDown() deadlock */
9422 task->releaseVMCaller();
9423
9424 that->powerDown(task->mServerProgress);
9425
9426 /* complete the operation */
9427 that->mControl->EndPoweringDown(S_OK, Bstr().raw());
9428
9429 LogFlowFuncLeave();
9430 return VINF_SUCCESS;
9431}
9432
9433
9434/**
9435 * @interface_method_impl{VMM2USERMETHODS,pfnSaveState}
9436 */
9437/*static*/ DECLCALLBACK(int)
9438Console::vmm2User_SaveState(PCVMM2USERMETHODS pThis, PUVM pUVM)
9439{
9440 Console *pConsole = ((MYVMM2USERMETHODS *)pThis)->pConsole;
9441 NOREF(pUVM);
9442
9443 /*
9444 * For now, just call SaveState. We should probably try notify the GUI so
9445 * it can pop up a progress object and stuff.
9446 */
9447 HRESULT hrc = pConsole->SaveState(NULL);
9448 return SUCCEEDED(hrc) ? VINF_SUCCESS : Global::vboxStatusCodeFromCOM(hrc);
9449}
9450
9451/**
9452 * @interface_method_impl{VMM2USERMETHODS,pfnNotifyEmtInit}
9453 */
9454/*static*/ DECLCALLBACK(void)
9455Console::vmm2User_NotifyEmtInit(PCVMM2USERMETHODS pThis, PUVM pUVM, PUVMCPU pUVCpu)
9456{
9457 NOREF(pThis); NOREF(pUVM); NOREF(pUVCpu);
9458 VirtualBoxBase::initializeComForThread();
9459}
9460
9461/**
9462 * @interface_method_impl{VMM2USERMETHODS,pfnNotifyEmtTerm}
9463 */
9464/*static*/ DECLCALLBACK(void)
9465Console::vmm2User_NotifyEmtTerm(PCVMM2USERMETHODS pThis, PUVM pUVM, PUVMCPU pUVCpu)
9466{
9467 NOREF(pThis); NOREF(pUVM); NOREF(pUVCpu);
9468 VirtualBoxBase::uninitializeComForThread();
9469}
9470
9471/**
9472 * @interface_method_impl{VMM2USERMETHODS,pfnNotifyPdmtInit}
9473 */
9474/*static*/ DECLCALLBACK(void)
9475Console::vmm2User_NotifyPdmtInit(PCVMM2USERMETHODS pThis, PUVM pUVM)
9476{
9477 NOREF(pThis); NOREF(pUVM);
9478 VirtualBoxBase::initializeComForThread();
9479}
9480
9481/**
9482 * @interface_method_impl{VMM2USERMETHODS,pfnNotifyPdmtTerm}
9483 */
9484/*static*/ DECLCALLBACK(void)
9485Console::vmm2User_NotifyPdmtTerm(PCVMM2USERMETHODS pThis, PUVM pUVM)
9486{
9487 NOREF(pThis); NOREF(pUVM);
9488 VirtualBoxBase::uninitializeComForThread();
9489}
9490
9491
9492
9493
9494/**
9495 * The Main status driver instance data.
9496 */
9497typedef struct DRVMAINSTATUS
9498{
9499 /** The LED connectors. */
9500 PDMILEDCONNECTORS ILedConnectors;
9501 /** Pointer to the LED ports interface above us. */
9502 PPDMILEDPORTS pLedPorts;
9503 /** Pointer to the array of LED pointers. */
9504 PPDMLED *papLeds;
9505 /** The unit number corresponding to the first entry in the LED array. */
9506 RTUINT iFirstLUN;
9507 /** The unit number corresponding to the last entry in the LED array.
9508 * (The size of the LED array is iLastLUN - iFirstLUN + 1.) */
9509 RTUINT iLastLUN;
9510 /** Pointer to the driver instance. */
9511 PPDMDRVINS pDrvIns;
9512 /** The Media Notify interface. */
9513 PDMIMEDIANOTIFY IMediaNotify;
9514 /** Map for translating PDM storage controller/LUN information to
9515 * IMediumAttachment references. */
9516 Console::MediumAttachmentMap *pmapMediumAttachments;
9517 /** Device name+instance for mapping */
9518 char *pszDeviceInstance;
9519 /** Pointer to the Console object, for driver triggered activities. */
9520 Console *pConsole;
9521} DRVMAINSTATUS, *PDRVMAINSTATUS;
9522
9523
9524/**
9525 * Notification about a unit which have been changed.
9526 *
9527 * The driver must discard any pointers to data owned by
9528 * the unit and requery it.
9529 *
9530 * @param pInterface Pointer to the interface structure containing the called function pointer.
9531 * @param iLUN The unit number.
9532 */
9533DECLCALLBACK(void) Console::drvStatus_UnitChanged(PPDMILEDCONNECTORS pInterface, unsigned iLUN)
9534{
9535 PDRVMAINSTATUS pData = (PDRVMAINSTATUS)((uintptr_t)pInterface - RT_OFFSETOF(DRVMAINSTATUS, ILedConnectors));
9536 if (iLUN >= pData->iFirstLUN && iLUN <= pData->iLastLUN)
9537 {
9538 PPDMLED pLed;
9539 int rc = pData->pLedPorts->pfnQueryStatusLed(pData->pLedPorts, iLUN, &pLed);
9540 if (RT_FAILURE(rc))
9541 pLed = NULL;
9542 ASMAtomicWritePtr(&pData->papLeds[iLUN - pData->iFirstLUN], pLed);
9543 Log(("drvStatus_UnitChanged: iLUN=%d pLed=%p\n", iLUN, pLed));
9544 }
9545}
9546
9547
9548/**
9549 * Notification about a medium eject.
9550 *
9551 * @returns VBox status.
9552 * @param pInterface Pointer to the interface structure containing the called function pointer.
9553 * @param uLUN The unit number.
9554 */
9555DECLCALLBACK(int) Console::drvStatus_MediumEjected(PPDMIMEDIANOTIFY pInterface, unsigned uLUN)
9556{
9557 PDRVMAINSTATUS pData = (PDRVMAINSTATUS)((uintptr_t)pInterface - RT_OFFSETOF(DRVMAINSTATUS, IMediaNotify));
9558 PPDMDRVINS pDrvIns = pData->pDrvIns;
9559 LogFunc(("uLUN=%d\n", uLUN));
9560 if (pData->pmapMediumAttachments)
9561 {
9562 AutoWriteLock alock(pData->pConsole COMMA_LOCKVAL_SRC_POS);
9563
9564 ComPtr<IMediumAttachment> pMediumAtt;
9565 Utf8Str devicePath = Utf8StrFmt("%s/LUN#%u", pData->pszDeviceInstance, uLUN);
9566 Console::MediumAttachmentMap::const_iterator end = pData->pmapMediumAttachments->end();
9567 Console::MediumAttachmentMap::const_iterator it = pData->pmapMediumAttachments->find(devicePath);
9568 if (it != end)
9569 pMediumAtt = it->second;
9570 Assert(!pMediumAtt.isNull());
9571 if (!pMediumAtt.isNull())
9572 {
9573 IMedium *pMedium;
9574 HRESULT rc = pMediumAtt->COMGETTER(Medium)(&pMedium);
9575 AssertComRC(rc);
9576 BOOL fHostDrive = FALSE;
9577 rc = pMedium->COMGETTER(HostDrive)(&fHostDrive);
9578 AssertComRC(rc);
9579 if (!fHostDrive)
9580 {
9581 alock.release();
9582
9583 ComPtr<IMediumAttachment> pNewMediumAtt;
9584 rc = pData->pConsole->mControl->EjectMedium(pMediumAtt, pNewMediumAtt.asOutParam());
9585 if (SUCCEEDED(rc))
9586 fireMediumChangedEvent(pData->pConsole->mEventSource, pNewMediumAtt);
9587
9588 alock.acquire();
9589 if (pNewMediumAtt != pMediumAtt)
9590 {
9591 pData->pmapMediumAttachments->erase(devicePath);
9592 pData->pmapMediumAttachments->insert(std::make_pair(devicePath, pNewMediumAtt));
9593 }
9594 }
9595 }
9596 }
9597 return VINF_SUCCESS;
9598}
9599
9600
9601/**
9602 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
9603 */
9604DECLCALLBACK(void *) Console::drvStatus_QueryInterface(PPDMIBASE pInterface, const char *pszIID)
9605{
9606 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
9607 PDRVMAINSTATUS pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINSTATUS);
9608 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
9609 PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDCONNECTORS, &pThis->ILedConnectors);
9610 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIANOTIFY, &pThis->IMediaNotify);
9611 return NULL;
9612}
9613
9614
9615/**
9616 * Destruct a status driver instance.
9617 *
9618 * @returns VBox status.
9619 * @param pDrvIns The driver instance data.
9620 */
9621DECLCALLBACK(void) Console::drvStatus_Destruct(PPDMDRVINS pDrvIns)
9622{
9623 PDRVMAINSTATUS pData = PDMINS_2_DATA(pDrvIns, PDRVMAINSTATUS);
9624 LogFlowFunc(("iInstance=%d\n", pDrvIns->iInstance));
9625 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
9626
9627 if (pData->papLeds)
9628 {
9629 unsigned iLed = pData->iLastLUN - pData->iFirstLUN + 1;
9630 while (iLed-- > 0)
9631 ASMAtomicWriteNullPtr(&pData->papLeds[iLed]);
9632 }
9633}
9634
9635
9636/**
9637 * Construct a status driver instance.
9638 *
9639 * @copydoc FNPDMDRVCONSTRUCT
9640 */
9641DECLCALLBACK(int) Console::drvStatus_Construct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
9642{
9643 PDRVMAINSTATUS pData = PDMINS_2_DATA(pDrvIns, PDRVMAINSTATUS);
9644 LogFlowFunc(("iInstance=%d\n", pDrvIns->iInstance));
9645 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
9646
9647 /*
9648 * Validate configuration.
9649 */
9650 if (!CFGMR3AreValuesValid(pCfg, "papLeds\0pmapMediumAttachments\0DeviceInstance\0pConsole\0First\0Last\0"))
9651 return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
9652 AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
9653 ("Configuration error: Not possible to attach anything to this driver!\n"),
9654 VERR_PDM_DRVINS_NO_ATTACH);
9655
9656 /*
9657 * Data.
9658 */
9659 pDrvIns->IBase.pfnQueryInterface = Console::drvStatus_QueryInterface;
9660 pData->ILedConnectors.pfnUnitChanged = Console::drvStatus_UnitChanged;
9661 pData->IMediaNotify.pfnEjected = Console::drvStatus_MediumEjected;
9662 pData->pDrvIns = pDrvIns;
9663 pData->pszDeviceInstance = NULL;
9664
9665 /*
9666 * Read config.
9667 */
9668 int rc = CFGMR3QueryPtr(pCfg, "papLeds", (void **)&pData->papLeds);
9669 if (RT_FAILURE(rc))
9670 {
9671 AssertMsgFailed(("Configuration error: Failed to query the \"papLeds\" value! rc=%Rrc\n", rc));
9672 return rc;
9673 }
9674
9675 rc = CFGMR3QueryPtrDef(pCfg, "pmapMediumAttachments", (void **)&pData->pmapMediumAttachments, NULL);
9676 if (RT_FAILURE(rc))
9677 {
9678 AssertMsgFailed(("Configuration error: Failed to query the \"pmapMediumAttachments\" value! rc=%Rrc\n", rc));
9679 return rc;
9680 }
9681 if (pData->pmapMediumAttachments)
9682 {
9683 rc = CFGMR3QueryStringAlloc(pCfg, "DeviceInstance", &pData->pszDeviceInstance);
9684 if (RT_FAILURE(rc))
9685 {
9686 AssertMsgFailed(("Configuration error: Failed to query the \"DeviceInstance\" value! rc=%Rrc\n", rc));
9687 return rc;
9688 }
9689 rc = CFGMR3QueryPtr(pCfg, "pConsole", (void **)&pData->pConsole);
9690 if (RT_FAILURE(rc))
9691 {
9692 AssertMsgFailed(("Configuration error: Failed to query the \"pConsole\" value! rc=%Rrc\n", rc));
9693 return rc;
9694 }
9695 }
9696
9697 rc = CFGMR3QueryU32(pCfg, "First", &pData->iFirstLUN);
9698 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
9699 pData->iFirstLUN = 0;
9700 else if (RT_FAILURE(rc))
9701 {
9702 AssertMsgFailed(("Configuration error: Failed to query the \"First\" value! rc=%Rrc\n", rc));
9703 return rc;
9704 }
9705
9706 rc = CFGMR3QueryU32(pCfg, "Last", &pData->iLastLUN);
9707 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
9708 pData->iLastLUN = 0;
9709 else if (RT_FAILURE(rc))
9710 {
9711 AssertMsgFailed(("Configuration error: Failed to query the \"Last\" value! rc=%Rrc\n", rc));
9712 return rc;
9713 }
9714 if (pData->iFirstLUN > pData->iLastLUN)
9715 {
9716 AssertMsgFailed(("Configuration error: Invalid unit range %u-%u\n", pData->iFirstLUN, pData->iLastLUN));
9717 return VERR_GENERAL_FAILURE;
9718 }
9719
9720 /*
9721 * Get the ILedPorts interface of the above driver/device and
9722 * query the LEDs we want.
9723 */
9724 pData->pLedPorts = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMILEDPORTS);
9725 AssertMsgReturn(pData->pLedPorts, ("Configuration error: No led ports interface above!\n"),
9726 VERR_PDM_MISSING_INTERFACE_ABOVE);
9727
9728 for (unsigned i = pData->iFirstLUN; i <= pData->iLastLUN; ++i)
9729 Console::drvStatus_UnitChanged(&pData->ILedConnectors, i);
9730
9731 return VINF_SUCCESS;
9732}
9733
9734
9735/**
9736 * Console status driver (LED) registration record.
9737 */
9738const PDMDRVREG Console::DrvStatusReg =
9739{
9740 /* u32Version */
9741 PDM_DRVREG_VERSION,
9742 /* szName */
9743 "MainStatus",
9744 /* szRCMod */
9745 "",
9746 /* szR0Mod */
9747 "",
9748 /* pszDescription */
9749 "Main status driver (Main as in the API).",
9750 /* fFlags */
9751 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
9752 /* fClass. */
9753 PDM_DRVREG_CLASS_STATUS,
9754 /* cMaxInstances */
9755 ~0,
9756 /* cbInstance */
9757 sizeof(DRVMAINSTATUS),
9758 /* pfnConstruct */
9759 Console::drvStatus_Construct,
9760 /* pfnDestruct */
9761 Console::drvStatus_Destruct,
9762 /* pfnRelocate */
9763 NULL,
9764 /* pfnIOCtl */
9765 NULL,
9766 /* pfnPowerOn */
9767 NULL,
9768 /* pfnReset */
9769 NULL,
9770 /* pfnSuspend */
9771 NULL,
9772 /* pfnResume */
9773 NULL,
9774 /* pfnAttach */
9775 NULL,
9776 /* pfnDetach */
9777 NULL,
9778 /* pfnPowerOff */
9779 NULL,
9780 /* pfnSoftReset */
9781 NULL,
9782 /* u32EndVersion */
9783 PDM_DRVREG_VERSION
9784};
9785
9786/* 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