VirtualBox

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

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

Main/ConsoleImpl: reset the local machine state if VMR3SaveState failed for some reason (e.g. disk full)

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