VirtualBox

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

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

Console: define VRDP/ActiveClient guest property.

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